Postgres95 1.01 Distribution - Virgin Sources PG95_DIST
authorMarc G. Fournier <[email protected]>
Tue, 9 Jul 1996 06:22:35 +0000 (06:22 +0000)
committerMarc G. Fournier <[email protected]>
Tue, 9 Jul 1996 06:22:35 +0000 (06:22 +0000)
868 files changed:
src/Makefile [new file with mode: 0644]
src/Makefile.global [new file with mode: 0644]
src/backend/Makefile [new file with mode: 0644]
src/backend/access/Makefile.inc [new file with mode: 0644]
src/backend/access/attnum.h [new file with mode: 0644]
src/backend/access/common/Makefile.inc [new file with mode: 0644]
src/backend/access/common/heaptuple.c [new file with mode: 0644]
src/backend/access/common/heapvalid.c [new file with mode: 0644]
src/backend/access/common/indextuple.c [new file with mode: 0644]
src/backend/access/common/indexvalid.c [new file with mode: 0644]
src/backend/access/common/printtup.c [new file with mode: 0644]
src/backend/access/common/scankey.c [new file with mode: 0644]
src/backend/access/common/tupdesc.c [new file with mode: 0644]
src/backend/access/funcindex.h [new file with mode: 0644]
src/backend/access/genam.h [new file with mode: 0644]
src/backend/access/hash.h [new file with mode: 0644]
src/backend/access/hash/Makefile.inc [new file with mode: 0644]
src/backend/access/hash/hash.c [new file with mode: 0644]
src/backend/access/hash/hashfunc.c [new file with mode: 0644]
src/backend/access/hash/hashinsert.c [new file with mode: 0644]
src/backend/access/hash/hashovfl.c [new file with mode: 0644]
src/backend/access/hash/hashpage.c [new file with mode: 0644]
src/backend/access/hash/hashscan.c [new file with mode: 0644]
src/backend/access/hash/hashsearch.c [new file with mode: 0644]
src/backend/access/hash/hashstrat.c [new file with mode: 0644]
src/backend/access/hash/hashutil.c [new file with mode: 0644]
src/backend/access/heap/Makefile.inc [new file with mode: 0644]
src/backend/access/heap/heapam.c [new file with mode: 0644]
src/backend/access/heap/hio.c [new file with mode: 0644]
src/backend/access/heap/stats.c [new file with mode: 0644]
src/backend/access/heapam.h [new file with mode: 0644]
src/backend/access/hio.h [new file with mode: 0644]
src/backend/access/htup.h [new file with mode: 0644]
src/backend/access/ibit.h [new file with mode: 0644]
src/backend/access/index/Makefile.inc [new file with mode: 0644]
src/backend/access/index/genam.c [new file with mode: 0644]
src/backend/access/index/indexam.c [new file with mode: 0644]
src/backend/access/index/istrat.c [new file with mode: 0644]
src/backend/access/iqual.h [new file with mode: 0644]
src/backend/access/istrat.h [new file with mode: 0644]
src/backend/access/itup.h [new file with mode: 0644]
src/backend/access/nbtree.h [new file with mode: 0644]
src/backend/access/nbtree/Makefile.inc [new file with mode: 0644]
src/backend/access/nbtree/README [new file with mode: 0644]
src/backend/access/nbtree/nbtcompare.c [new file with mode: 0644]
src/backend/access/nbtree/nbtinsert.c [new file with mode: 0644]
src/backend/access/nbtree/nbtpage.c [new file with mode: 0644]
src/backend/access/nbtree/nbtree.c [new file with mode: 0644]
src/backend/access/nbtree/nbtscan.c [new file with mode: 0644]
src/backend/access/nbtree/nbtsearch.c [new file with mode: 0644]
src/backend/access/nbtree/nbtsort.c [new file with mode: 0644]
src/backend/access/nbtree/nbtstrat.c [new file with mode: 0644]
src/backend/access/nbtree/nbtutils.c [new file with mode: 0644]
src/backend/access/printtup.h [new file with mode: 0644]
src/backend/access/relscan.h [new file with mode: 0644]
src/backend/access/rtree.h [new file with mode: 0644]
src/backend/access/rtree/Makefile.inc [new file with mode: 0644]
src/backend/access/rtree/rtget.c [new file with mode: 0644]
src/backend/access/rtree/rtproc.c [new file with mode: 0644]
src/backend/access/rtree/rtree.c [new file with mode: 0644]
src/backend/access/rtree/rtscan.c [new file with mode: 0644]
src/backend/access/rtree/rtstrat.c [new file with mode: 0644]
src/backend/access/rtscan.h [new file with mode: 0644]
src/backend/access/rtstrat.h [new file with mode: 0644]
src/backend/access/sdir.h [new file with mode: 0644]
src/backend/access/skey.h [new file with mode: 0644]
src/backend/access/strat.h [new file with mode: 0644]
src/backend/access/transam.h [new file with mode: 0644]
src/backend/access/transam/Makefile.inc [new file with mode: 0644]
src/backend/access/transam/transam.c [new file with mode: 0644]
src/backend/access/transam/transsup.c [new file with mode: 0644]
src/backend/access/transam/varsup.c [new file with mode: 0644]
src/backend/access/transam/xact.c [new file with mode: 0644]
src/backend/access/transam/xid.c [new file with mode: 0644]
src/backend/access/tupdesc.h [new file with mode: 0644]
src/backend/access/tupmacs.h [new file with mode: 0644]
src/backend/access/valid.h [new file with mode: 0644]
src/backend/access/xact.h [new file with mode: 0644]
src/backend/bootstrap/Makefile.inc [new file with mode: 0644]
src/backend/bootstrap/boot.sed [new file with mode: 0644]
src/backend/bootstrap/bootparse.y [new file with mode: 0644]
src/backend/bootstrap/bootscanner.l [new file with mode: 0644]
src/backend/bootstrap/bootstrap.c [new file with mode: 0644]
src/backend/bootstrap/bootstrap.h [new file with mode: 0644]
src/backend/catalog/Makefile.inc [new file with mode: 0644]
src/backend/catalog/README [new file with mode: 0644]
src/backend/catalog/catalog.c [new file with mode: 0644]
src/backend/catalog/catalog.h [new file with mode: 0644]
src/backend/catalog/catname.h [new file with mode: 0644]
src/backend/catalog/genbki.sh [new file with mode: 0644]
src/backend/catalog/heap.c [new file with mode: 0644]
src/backend/catalog/heap.h [new file with mode: 0644]
src/backend/catalog/index.c [new file with mode: 0644]
src/backend/catalog/index.h [new file with mode: 0644]
src/backend/catalog/indexing.c [new file with mode: 0644]
src/backend/catalog/indexing.h [new file with mode: 0644]
src/backend/catalog/pg_aggregate.c [new file with mode: 0644]
src/backend/catalog/pg_aggregate.h [new file with mode: 0644]
src/backend/catalog/pg_am.h [new file with mode: 0644]
src/backend/catalog/pg_amop.h [new file with mode: 0644]
src/backend/catalog/pg_amproc.h [new file with mode: 0644]
src/backend/catalog/pg_attribute.h [new file with mode: 0644]
src/backend/catalog/pg_class.h [new file with mode: 0644]
src/backend/catalog/pg_database.h [new file with mode: 0644]
src/backend/catalog/pg_defaults.h [new file with mode: 0644]
src/backend/catalog/pg_demon.h [new file with mode: 0644]
src/backend/catalog/pg_group.h [new file with mode: 0644]
src/backend/catalog/pg_hosts.h [new file with mode: 0644]
src/backend/catalog/pg_index.h [new file with mode: 0644]
src/backend/catalog/pg_inheritproc.h [new file with mode: 0644]
src/backend/catalog/pg_inherits.h [new file with mode: 0644]
src/backend/catalog/pg_ipl.h [new file with mode: 0644]
src/backend/catalog/pg_language.h [new file with mode: 0644]
src/backend/catalog/pg_listener.h [new file with mode: 0644]
src/backend/catalog/pg_log.h [new file with mode: 0644]
src/backend/catalog/pg_magic.h [new file with mode: 0644]
src/backend/catalog/pg_opclass.h [new file with mode: 0644]
src/backend/catalog/pg_operator.c [new file with mode: 0644]
src/backend/catalog/pg_operator.h [new file with mode: 0644]
src/backend/catalog/pg_parg.h [new file with mode: 0644]
src/backend/catalog/pg_proc.c [new file with mode: 0644]
src/backend/catalog/pg_proc.h [new file with mode: 0644]
src/backend/catalog/pg_rewrite.h [new file with mode: 0644]
src/backend/catalog/pg_server.h [new file with mode: 0644]
src/backend/catalog/pg_statistic.h [new file with mode: 0644]
src/backend/catalog/pg_time.h [new file with mode: 0644]
src/backend/catalog/pg_type.c [new file with mode: 0644]
src/backend/catalog/pg_type.h [new file with mode: 0644]
src/backend/catalog/pg_user.h [new file with mode: 0644]
src/backend/catalog/pg_variable.h [new file with mode: 0644]
src/backend/catalog/pg_version.h [new file with mode: 0644]
src/backend/catalog/unused_oids [new file with mode: 0644]
src/backend/commands/Makefile.inc [new file with mode: 0644]
src/backend/commands/_deadcode/version.c [new file with mode: 0644]
src/backend/commands/async.c [new file with mode: 0644]
src/backend/commands/async.h [new file with mode: 0644]
src/backend/commands/cluster.c [new file with mode: 0644]
src/backend/commands/cluster.h [new file with mode: 0644]
src/backend/commands/command.c [new file with mode: 0644]
src/backend/commands/command.h [new file with mode: 0644]
src/backend/commands/copy.c [new file with mode: 0644]
src/backend/commands/copy.h [new file with mode: 0644]
src/backend/commands/creatinh.c [new file with mode: 0644]
src/backend/commands/creatinh.h [new file with mode: 0644]
src/backend/commands/defind.c [new file with mode: 0644]
src/backend/commands/define.c [new file with mode: 0644]
src/backend/commands/defrem.h [new file with mode: 0644]
src/backend/commands/explain.c [new file with mode: 0644]
src/backend/commands/explain.h [new file with mode: 0644]
src/backend/commands/purge.c [new file with mode: 0644]
src/backend/commands/purge.h [new file with mode: 0644]
src/backend/commands/recipe.c [new file with mode: 0644]
src/backend/commands/recipe.h [new file with mode: 0644]
src/backend/commands/remove.c [new file with mode: 0644]
src/backend/commands/rename.c [new file with mode: 0644]
src/backend/commands/rename.h [new file with mode: 0644]
src/backend/commands/vacuum.c [new file with mode: 0644]
src/backend/commands/vacuum.h [new file with mode: 0644]
src/backend/commands/version.h [new file with mode: 0644]
src/backend/commands/view.c [new file with mode: 0644]
src/backend/commands/view.h [new file with mode: 0644]
src/backend/executor/Makefile.inc [new file with mode: 0644]
src/backend/executor/execAmi.c [new file with mode: 0644]
src/backend/executor/execFlatten.c [new file with mode: 0644]
src/backend/executor/execFlatten.h [new file with mode: 0644]
src/backend/executor/execJunk.c [new file with mode: 0644]
src/backend/executor/execMain.c [new file with mode: 0644]
src/backend/executor/execProcnode.c [new file with mode: 0644]
src/backend/executor/execQual.c [new file with mode: 0644]
src/backend/executor/execScan.c [new file with mode: 0644]
src/backend/executor/execTuples.c [new file with mode: 0644]
src/backend/executor/execUtils.c [new file with mode: 0644]
src/backend/executor/execdebug.h [new file with mode: 0644]
src/backend/executor/execdefs.h [new file with mode: 0644]
src/backend/executor/execdesc.h [new file with mode: 0644]
src/backend/executor/executor.h [new file with mode: 0644]
src/backend/executor/functions.c [new file with mode: 0644]
src/backend/executor/functions.h [new file with mode: 0644]
src/backend/executor/hashjoin.h [new file with mode: 0644]
src/backend/executor/nodeAgg.c [new file with mode: 0644]
src/backend/executor/nodeAgg.h [new file with mode: 0644]
src/backend/executor/nodeAppend.c [new file with mode: 0644]
src/backend/executor/nodeAppend.h [new file with mode: 0644]
src/backend/executor/nodeGroup.c [new file with mode: 0644]
src/backend/executor/nodeGroup.h [new file with mode: 0644]
src/backend/executor/nodeHash.c [new file with mode: 0644]
src/backend/executor/nodeHash.h [new file with mode: 0644]
src/backend/executor/nodeHashjoin.c [new file with mode: 0644]
src/backend/executor/nodeHashjoin.h [new file with mode: 0644]
src/backend/executor/nodeIndexscan.c [new file with mode: 0644]
src/backend/executor/nodeIndexscan.h [new file with mode: 0644]
src/backend/executor/nodeMaterial.c [new file with mode: 0644]
src/backend/executor/nodeMaterial.h [new file with mode: 0644]
src/backend/executor/nodeMergejoin.c [new file with mode: 0644]
src/backend/executor/nodeMergejoin.h [new file with mode: 0644]
src/backend/executor/nodeNestloop.c [new file with mode: 0644]
src/backend/executor/nodeNestloop.h [new file with mode: 0644]
src/backend/executor/nodeResult.c [new file with mode: 0644]
src/backend/executor/nodeResult.h [new file with mode: 0644]
src/backend/executor/nodeSeqscan.c [new file with mode: 0644]
src/backend/executor/nodeSeqscan.h [new file with mode: 0644]
src/backend/executor/nodeSort.c [new file with mode: 0644]
src/backend/executor/nodeSort.h [new file with mode: 0644]
src/backend/executor/nodeTee.c [new file with mode: 0644]
src/backend/executor/nodeTee.h [new file with mode: 0644]
src/backend/executor/nodeUnique.c [new file with mode: 0644]
src/backend/executor/nodeUnique.h [new file with mode: 0644]
src/backend/executor/tuptable.h [new file with mode: 0644]
src/backend/include/Makefile.inc [new file with mode: 0644]
src/backend/include/c.h [new file with mode: 0644]
src/backend/include/miscadmin.h [new file with mode: 0644]
src/backend/include/postgres.h [new file with mode: 0644]
src/backend/lib/Makefile.inc [new file with mode: 0644]
src/backend/lib/bit.c [new file with mode: 0644]
src/backend/lib/dllist.c [new file with mode: 0644]
src/backend/lib/dllist.h [new file with mode: 0644]
src/backend/lib/fstack.c [new file with mode: 0644]
src/backend/lib/fstack.h [new file with mode: 0644]
src/backend/lib/hasht.c [new file with mode: 0644]
src/backend/lib/hasht.h [new file with mode: 0644]
src/backend/lib/lispsort.c [new file with mode: 0644]
src/backend/lib/lispsort.h [new file with mode: 0644]
src/backend/lib/qsort.c [new file with mode: 0644]
src/backend/lib/qsort.h [new file with mode: 0644]
src/backend/lib/stringinfo.c [new file with mode: 0644]
src/backend/lib/stringinfo.h [new file with mode: 0644]
src/backend/libpq/Makefile.inc [new file with mode: 0644]
src/backend/libpq/auth.c [new file with mode: 0644]
src/backend/libpq/auth.h [new file with mode: 0644]
src/backend/libpq/be-dumpdata.c [new file with mode: 0644]
src/backend/libpq/be-fsstubs.c [new file with mode: 0644]
src/backend/libpq/be-fsstubs.h [new file with mode: 0644]
src/backend/libpq/be-pqexec.c [new file with mode: 0644]
src/backend/libpq/libpq-be.h [new file with mode: 0644]
src/backend/libpq/libpq-fs.h [new file with mode: 0644]
src/backend/libpq/libpq.h [new file with mode: 0644]
src/backend/libpq/portal.c [new file with mode: 0644]
src/backend/libpq/portalbuf.c [new file with mode: 0644]
src/backend/libpq/pqcomm.c [new file with mode: 0644]
src/backend/libpq/pqcomm.h [new file with mode: 0644]
src/backend/libpq/pqpacket.c [new file with mode: 0644]
src/backend/libpq/pqsignal.c [new file with mode: 0644]
src/backend/libpq/pqsignal.h [new file with mode: 0644]
src/backend/main/Makefile.inc [new file with mode: 0644]
src/backend/main/main.c [new file with mode: 0644]
src/backend/makeID [new file with mode: 0644]
src/backend/nodes/Makefile.inc [new file with mode: 0644]
src/backend/nodes/README [new file with mode: 0644]
src/backend/nodes/copyfuncs.c [new file with mode: 0644]
src/backend/nodes/equalfuncs.c [new file with mode: 0644]
src/backend/nodes/execnodes.h [new file with mode: 0644]
src/backend/nodes/list.c [new file with mode: 0644]
src/backend/nodes/makefuncs.c [new file with mode: 0644]
src/backend/nodes/makefuncs.h [new file with mode: 0644]
src/backend/nodes/memnodes.h [new file with mode: 0644]
src/backend/nodes/nodeFuncs.c [new file with mode: 0644]
src/backend/nodes/nodeFuncs.h [new file with mode: 0644]
src/backend/nodes/nodes.c [new file with mode: 0644]
src/backend/nodes/nodes.h [new file with mode: 0644]
src/backend/nodes/outfuncs.c [new file with mode: 0644]
src/backend/nodes/params.h [new file with mode: 0644]
src/backend/nodes/parsenodes.h [new file with mode: 0644]
src/backend/nodes/pg_list.h [new file with mode: 0644]
src/backend/nodes/plannodes.h [new file with mode: 0644]
src/backend/nodes/primnodes.h [new file with mode: 0644]
src/backend/nodes/print.c [new file with mode: 0644]
src/backend/nodes/read.c [new file with mode: 0644]
src/backend/nodes/readfuncs.c [new file with mode: 0644]
src/backend/nodes/readfuncs.h [new file with mode: 0644]
src/backend/nodes/relation.h [new file with mode: 0644]
src/backend/optimizer/Makefile.inc [new file with mode: 0644]
src/backend/optimizer/clauseinfo.h [new file with mode: 0644]
src/backend/optimizer/clauses.h [new file with mode: 0644]
src/backend/optimizer/cost.h [new file with mode: 0644]
src/backend/optimizer/internal.h [new file with mode: 0644]
src/backend/optimizer/joininfo.h [new file with mode: 0644]
src/backend/optimizer/keys.h [new file with mode: 0644]
src/backend/optimizer/ordering.h [new file with mode: 0644]
src/backend/optimizer/path/Makefile.inc [new file with mode: 0644]
src/backend/optimizer/path/allpaths.c [new file with mode: 0644]
src/backend/optimizer/path/clausesel.c [new file with mode: 0644]
src/backend/optimizer/path/costsize.c [new file with mode: 0644]
src/backend/optimizer/path/hashutils.c [new file with mode: 0644]
src/backend/optimizer/path/indxpath.c [new file with mode: 0644]
src/backend/optimizer/path/joinpath.c [new file with mode: 0644]
src/backend/optimizer/path/joinrels.c [new file with mode: 0644]
src/backend/optimizer/path/joinutils.c [new file with mode: 0644]
src/backend/optimizer/path/mergeutils.c [new file with mode: 0644]
src/backend/optimizer/path/orindxpath.c [new file with mode: 0644]
src/backend/optimizer/path/predmig.c [new file with mode: 0644]
src/backend/optimizer/path/prune.c [new file with mode: 0644]
src/backend/optimizer/path/xfunc.c [new file with mode: 0644]
src/backend/optimizer/pathnode.h [new file with mode: 0644]
src/backend/optimizer/paths.h [new file with mode: 0644]
src/backend/optimizer/plan/Makefile.inc [new file with mode: 0644]
src/backend/optimizer/plan/createplan.c [new file with mode: 0644]
src/backend/optimizer/plan/initsplan.c [new file with mode: 0644]
src/backend/optimizer/plan/planmain.c [new file with mode: 0644]
src/backend/optimizer/plan/planner.c [new file with mode: 0644]
src/backend/optimizer/plan/setrefs.c [new file with mode: 0644]
src/backend/optimizer/plancat.h [new file with mode: 0644]
src/backend/optimizer/planmain.h [new file with mode: 0644]
src/backend/optimizer/planner.h [new file with mode: 0644]
src/backend/optimizer/prep.h [new file with mode: 0644]
src/backend/optimizer/prep/Makefile.inc [new file with mode: 0644]
src/backend/optimizer/prep/archive.c [new file with mode: 0644]
src/backend/optimizer/prep/prepqual.c [new file with mode: 0644]
src/backend/optimizer/prep/preptlist.c [new file with mode: 0644]
src/backend/optimizer/prep/prepunion.c [new file with mode: 0644]
src/backend/optimizer/tlist.h [new file with mode: 0644]
src/backend/optimizer/util/Makefile.inc [new file with mode: 0644]
src/backend/optimizer/util/clauseinfo.c [new file with mode: 0644]
src/backend/optimizer/util/clauses.c [new file with mode: 0644]
src/backend/optimizer/util/indexnode.c [new file with mode: 0644]
src/backend/optimizer/util/internal.c [new file with mode: 0644]
src/backend/optimizer/util/joininfo.c [new file with mode: 0644]
src/backend/optimizer/util/keys.c [new file with mode: 0644]
src/backend/optimizer/util/ordering.c [new file with mode: 0644]
src/backend/optimizer/util/pathnode.c [new file with mode: 0644]
src/backend/optimizer/util/plancat.c [new file with mode: 0644]
src/backend/optimizer/util/relnode.c [new file with mode: 0644]
src/backend/optimizer/util/tlist.c [new file with mode: 0644]
src/backend/optimizer/util/var.c [new file with mode: 0644]
src/backend/optimizer/var.h [new file with mode: 0644]
src/backend/optimizer/xfunc.h [new file with mode: 0644]
src/backend/parser/Makefile.inc [new file with mode: 0644]
src/backend/parser/analyze.c [new file with mode: 0644]
src/backend/parser/catalog_utils.c [new file with mode: 0644]
src/backend/parser/catalog_utils.h [new file with mode: 0644]
src/backend/parser/dbcommands.c [new file with mode: 0644]
src/backend/parser/dbcommands.h [new file with mode: 0644]
src/backend/parser/gram.y [new file with mode: 0644]
src/backend/parser/keywords.c [new file with mode: 0644]
src/backend/parser/keywords.h [new file with mode: 0644]
src/backend/parser/parse_query.c [new file with mode: 0644]
src/backend/parser/parse_query.h [new file with mode: 0644]
src/backend/parser/parse_state.h [new file with mode: 0644]
src/backend/parser/parser.c [new file with mode: 0644]
src/backend/parser/parsetree.h [new file with mode: 0644]
src/backend/parser/scan.l [new file with mode: 0644]
src/backend/parser/scansup.c [new file with mode: 0644]
src/backend/parser/scansup.h [new file with mode: 0644]
src/backend/port/BSD44_derived/Makefile.inc [new file with mode: 0644]
src/backend/port/BSD44_derived/README [new file with mode: 0644]
src/backend/port/BSD44_derived/dl.c [new file with mode: 0644]
src/backend/port/BSD44_derived/float.h [new file with mode: 0644]
src/backend/port/BSD44_derived/machine.h [new file with mode: 0644]
src/backend/port/BSD44_derived/port-protos.h [new file with mode: 0644]
src/backend/port/Makefile.inc [new file with mode: 0644]
src/backend/port/aix/Makefile.inc [new file with mode: 0644]
src/backend/port/aix/README.dlfcn [new file with mode: 0644]
src/backend/port/aix/dlfcn.c [new file with mode: 0644]
src/backend/port/aix/dlfcn.h [new file with mode: 0644]
src/backend/port/aix/machine.h [new file with mode: 0644]
src/backend/port/aix/mkldexport.sh [new file with mode: 0755]
src/backend/port/aix/port-protos.h [new file with mode: 0644]
src/backend/port/alpha/Makefile.inc [new file with mode: 0644]
src/backend/port/alpha/machine.h [new file with mode: 0644]
src/backend/port/alpha/port-protos.h [new file with mode: 0644]
src/backend/port/alpha/port.c [new file with mode: 0644]
src/backend/port/bsdi/Makefile.inc [new file with mode: 0644]
src/backend/port/bsdi/dynloader.c [new file with mode: 0644]
src/backend/port/bsdi/machine.h [new file with mode: 0644]
src/backend/port/bsdi/port-protos.h [new file with mode: 0644]
src/backend/port/bsdi/port.c [new file with mode: 0644]
src/backend/port/hpux/Makefile.inc [new file with mode: 0644]
src/backend/port/hpux/dynloader.c [new file with mode: 0644]
src/backend/port/hpux/fixade.h [new file with mode: 0644]
src/backend/port/hpux/machine.h [new file with mode: 0644]
src/backend/port/hpux/port-protos.h [new file with mode: 0644]
src/backend/port/hpux/port.c [new file with mode: 0644]
src/backend/port/hpux/tas.c.template [new file with mode: 0644]
src/backend/port/hpux/tas.s [new file with mode: 0644]
src/backend/port/irix5/Makefile.inc [new file with mode: 0644]
src/backend/port/irix5/README [new file with mode: 0644]
src/backend/port/irix5/machine.h [new file with mode: 0644]
src/backend/port/irix5/port-protos.h [new file with mode: 0644]
src/backend/port/irix5/port.c [new file with mode: 0644]
src/backend/port/linux/Makefile.inc [new file with mode: 0644]
src/backend/port/linux/dynloader.c [new file with mode: 0644]
src/backend/port/linux/machine.h [new file with mode: 0644]
src/backend/port/linux/port-protos.h [new file with mode: 0644]
src/backend/port/linux/port.c [new file with mode: 0644]
src/backend/port/sparc/Makefile.inc [new file with mode: 0644]
src/backend/port/sparc/float.h [new file with mode: 0644]
src/backend/port/sparc/machine.h [new file with mode: 0644]
src/backend/port/sparc/port-protos.h [new file with mode: 0644]
src/backend/port/sparc/strtol.c [new file with mode: 0644]
src/backend/port/sparc_solaris/Makefile.inc [new file with mode: 0644]
src/backend/port/sparc_solaris/machine.h [new file with mode: 0644]
src/backend/port/sparc_solaris/port-protos.h [new file with mode: 0644]
src/backend/port/sparc_solaris/port.c [new file with mode: 0644]
src/backend/port/sparc_solaris/rusagestub.h [new file with mode: 0644]
src/backend/port/sparc_solaris/tas.s [new file with mode: 0644]
src/backend/port/ultrix4/Makefile.inc [new file with mode: 0644]
src/backend/port/ultrix4/dl.h [new file with mode: 0644]
src/backend/port/ultrix4/dynloader.c [new file with mode: 0644]
src/backend/port/ultrix4/machine.h [new file with mode: 0644]
src/backend/port/ultrix4/port-protos.h [new file with mode: 0644]
src/backend/port/ultrix4/port.c [new file with mode: 0644]
src/backend/port/ultrix4/strdup.c [new file with mode: 0644]
src/backend/port/win32/machine.h [new file with mode: 0644]
src/backend/port/win32/nt.c [new file with mode: 0644]
src/backend/port/win32/nt.h [new file with mode: 0644]
src/backend/port/win32/pglite.mak [new file with mode: 0644]
src/backend/port/win32/port-protos.h [new file with mode: 0644]
src/backend/port/win32/pwd.h [new file with mode: 0644]
src/backend/port/win32/regex/COPYRIGHT [new file with mode: 0644]
src/backend/port/win32/regex/Makefile.inc [new file with mode: 0644]
src/backend/port/win32/regex/WHATSNEW [new file with mode: 0644]
src/backend/port/win32/regex/cclass.h [new file with mode: 0644]
src/backend/port/win32/regex/cname.h [new file with mode: 0644]
src/backend/port/win32/regex/engine.c [new file with mode: 0644]
src/backend/port/win32/regex/re_format.7 [new file with mode: 0644]
src/backend/port/win32/regex/regcomp.c [new file with mode: 0644]
src/backend/port/win32/regex/regerror.c [new file with mode: 0644]
src/backend/port/win32/regex/regex.3 [new file with mode: 0644]
src/backend/port/win32/regex/regex.h [new file with mode: 0644]
src/backend/port/win32/regex/regex2.h [new file with mode: 0644]
src/backend/port/win32/regex/regexec.c [new file with mode: 0644]
src/backend/port/win32/regex/regexp.h [new file with mode: 0644]
src/backend/port/win32/regex/regfree.c [new file with mode: 0644]
src/backend/port/win32/regex/utils.h [new file with mode: 0644]
src/backend/port/win32/rusagestub.h [new file with mode: 0644]
src/backend/port/win32/sys/cdefs.h [new file with mode: 0644]
src/backend/port/win32/sys/file.h [new file with mode: 0644]
src/backend/port/win32/sys/ipc.h [new file with mode: 0644]
src/backend/port/win32/sys/param.h [new file with mode: 0644]
src/backend/port/win32/sys/sem.h [new file with mode: 0644]
src/backend/port/win32/sys/shm.h [new file with mode: 0644]
src/backend/port/win32/sys/time.h [new file with mode: 0644]
src/backend/postmaster/Makefile.inc [new file with mode: 0644]
src/backend/postmaster/postmaster.c [new file with mode: 0644]
src/backend/regex/COPYRIGHT [new file with mode: 0644]
src/backend/regex/Makefile.inc [new file with mode: 0644]
src/backend/regex/WHATSNEW [new file with mode: 0644]
src/backend/regex/cclass.h [new file with mode: 0644]
src/backend/regex/cdefs.h [new file with mode: 0644]
src/backend/regex/cname.h [new file with mode: 0644]
src/backend/regex/engine.c [new file with mode: 0644]
src/backend/regex/re_format.7 [new file with mode: 0644]
src/backend/regex/regcomp.c [new file with mode: 0644]
src/backend/regex/regerror.c [new file with mode: 0644]
src/backend/regex/regex.3 [new file with mode: 0644]
src/backend/regex/regex.h [new file with mode: 0644]
src/backend/regex/regex2.h [new file with mode: 0644]
src/backend/regex/regexec.c [new file with mode: 0644]
src/backend/regex/regexp.h [new file with mode: 0644]
src/backend/regex/regfree.c [new file with mode: 0644]
src/backend/regex/utils.h [new file with mode: 0644]
src/backend/rewrite/Makefile.inc [new file with mode: 0644]
src/backend/rewrite/locks.c [new file with mode: 0644]
src/backend/rewrite/locks.h [new file with mode: 0644]
src/backend/rewrite/prs2lock.h [new file with mode: 0644]
src/backend/rewrite/rewriteDefine.c [new file with mode: 0644]
src/backend/rewrite/rewriteDefine.h [new file with mode: 0644]
src/backend/rewrite/rewriteHandler.c [new file with mode: 0644]
src/backend/rewrite/rewriteHandler.h [new file with mode: 0644]
src/backend/rewrite/rewriteManip.c [new file with mode: 0644]
src/backend/rewrite/rewriteManip.h [new file with mode: 0644]
src/backend/rewrite/rewriteRemove.c [new file with mode: 0644]
src/backend/rewrite/rewriteRemove.h [new file with mode: 0644]
src/backend/rewrite/rewriteSupport.c [new file with mode: 0644]
src/backend/rewrite/rewriteSupport.h [new file with mode: 0644]
src/backend/storage/Makefile.inc [new file with mode: 0644]
src/backend/storage/backendid.h [new file with mode: 0644]
src/backend/storage/block.h [new file with mode: 0644]
src/backend/storage/buf.h [new file with mode: 0644]
src/backend/storage/buf_internals.h [new file with mode: 0644]
src/backend/storage/buffer/Makefile.inc [new file with mode: 0644]
src/backend/storage/buffer/buf_init.c [new file with mode: 0644]
src/backend/storage/buffer/buf_table.c [new file with mode: 0644]
src/backend/storage/buffer/bufmgr.c [new file with mode: 0644]
src/backend/storage/buffer/freelist.c [new file with mode: 0644]
src/backend/storage/buffer/localbuf.c [new file with mode: 0644]
src/backend/storage/bufmgr.h [new file with mode: 0644]
src/backend/storage/bufpage.h [new file with mode: 0644]
src/backend/storage/fd.h [new file with mode: 0644]
src/backend/storage/file/Makefile.inc [new file with mode: 0644]
src/backend/storage/file/fd.c [new file with mode: 0644]
src/backend/storage/ipc.h [new file with mode: 0644]
src/backend/storage/ipc/Makefile.inc [new file with mode: 0644]
src/backend/storage/ipc/README [new file with mode: 0644]
src/backend/storage/ipc/ipc.c [new file with mode: 0644]
src/backend/storage/ipc/ipci.c [new file with mode: 0644]
src/backend/storage/ipc/s_lock.c [new file with mode: 0644]
src/backend/storage/ipc/shmem.c [new file with mode: 0644]
src/backend/storage/ipc/shmqueue.c [new file with mode: 0644]
src/backend/storage/ipc/sinval.c [new file with mode: 0644]
src/backend/storage/ipc/sinvaladt.c [new file with mode: 0644]
src/backend/storage/ipc/spin.c [new file with mode: 0644]
src/backend/storage/item.h [new file with mode: 0644]
src/backend/storage/itemid.h [new file with mode: 0644]
src/backend/storage/itempos.h [new file with mode: 0644]
src/backend/storage/itemptr.h [new file with mode: 0644]
src/backend/storage/large_object.h [new file with mode: 0644]
src/backend/storage/large_object/Makefile.inc [new file with mode: 0644]
src/backend/storage/large_object/inv_api.c [new file with mode: 0644]
src/backend/storage/lmgr.h [new file with mode: 0644]
src/backend/storage/lmgr/Makefile.inc [new file with mode: 0644]
src/backend/storage/lmgr/README [new file with mode: 0644]
src/backend/storage/lmgr/lmgr.c [new file with mode: 0644]
src/backend/storage/lmgr/lock.c [new file with mode: 0644]
src/backend/storage/lmgr/multi.c [new file with mode: 0644]
src/backend/storage/lmgr/proc.c [new file with mode: 0644]
src/backend/storage/lmgr/single.c [new file with mode: 0644]
src/backend/storage/lock.h [new file with mode: 0644]
src/backend/storage/multilev.h [new file with mode: 0644]
src/backend/storage/off.h [new file with mode: 0644]
src/backend/storage/page.h [new file with mode: 0644]
src/backend/storage/page/Makefile.inc [new file with mode: 0644]
src/backend/storage/page/bufpage.c [new file with mode: 0644]
src/backend/storage/page/itemptr.c [new file with mode: 0644]
src/backend/storage/pagenum.h [new file with mode: 0644]
src/backend/storage/pos.h [new file with mode: 0644]
src/backend/storage/proc.h [new file with mode: 0644]
src/backend/storage/shmem.h [new file with mode: 0644]
src/backend/storage/sinval.h [new file with mode: 0644]
src/backend/storage/sinvaladt.h [new file with mode: 0644]
src/backend/storage/smgr.h [new file with mode: 0644]
src/backend/storage/smgr/Makefile.inc [new file with mode: 0644]
src/backend/storage/smgr/README [new file with mode: 0644]
src/backend/storage/smgr/md.c [new file with mode: 0644]
src/backend/storage/smgr/mm.c [new file with mode: 0644]
src/backend/storage/smgr/smgr.c [new file with mode: 0644]
src/backend/storage/smgr/smgrtype.c [new file with mode: 0644]
src/backend/storage/spin.h [new file with mode: 0644]
src/backend/tcop/Makefile.inc [new file with mode: 0644]
src/backend/tcop/aclchk.c [new file with mode: 0644]
src/backend/tcop/dest.c [new file with mode: 0644]
src/backend/tcop/dest.h [new file with mode: 0644]
src/backend/tcop/fastpath.c [new file with mode: 0644]
src/backend/tcop/fastpath.h [new file with mode: 0644]
src/backend/tcop/postgres.c [new file with mode: 0644]
src/backend/tcop/pquery.c [new file with mode: 0644]
src/backend/tcop/pquery.h [new file with mode: 0644]
src/backend/tcop/tcopdebug.h [new file with mode: 0644]
src/backend/tcop/tcopprot.h [new file with mode: 0644]
src/backend/tcop/utility.c [new file with mode: 0644]
src/backend/tcop/utility.h [new file with mode: 0644]
src/backend/tioga/Arr_TgRecipe.h [new file with mode: 0644]
src/backend/tioga/Makefile.inc [new file with mode: 0644]
src/backend/tioga/Varray.c [new file with mode: 0644]
src/backend/tioga/Varray.h [new file with mode: 0644]
src/backend/tioga/tgRecipe.c [new file with mode: 0644]
src/backend/tioga/tgRecipe.h [new file with mode: 0644]
src/backend/utils/Gen_fmgrtab.sh [new file with mode: 0644]
src/backend/utils/Makefile.inc [new file with mode: 0644]
src/backend/utils/acl.h [new file with mode: 0644]
src/backend/utils/adt/Makefile.inc [new file with mode: 0644]
src/backend/utils/adt/acl.c [new file with mode: 0644]
src/backend/utils/adt/arrayfuncs.c [new file with mode: 0644]
src/backend/utils/adt/arrayutils.c [new file with mode: 0644]
src/backend/utils/adt/bool.c [new file with mode: 0644]
src/backend/utils/adt/char.c [new file with mode: 0644]
src/backend/utils/adt/chunk.c [new file with mode: 0644]
src/backend/utils/adt/date.c [new file with mode: 0644]
src/backend/utils/adt/datetimes.c [new file with mode: 0644]
src/backend/utils/adt/datum.c [new file with mode: 0644]
src/backend/utils/adt/dt.c [new file with mode: 0644]
src/backend/utils/adt/filename.c [new file with mode: 0644]
src/backend/utils/adt/float.c [new file with mode: 0644]
src/backend/utils/adt/geo-ops.c [new file with mode: 0644]
src/backend/utils/adt/geo-selfuncs.c [new file with mode: 0644]
src/backend/utils/adt/int.c [new file with mode: 0644]
src/backend/utils/adt/like.c [new file with mode: 0644]
src/backend/utils/adt/misc.c [new file with mode: 0644]
src/backend/utils/adt/nabstime.c [new file with mode: 0644]
src/backend/utils/adt/name.c [new file with mode: 0644]
src/backend/utils/adt/not_in.c [new file with mode: 0644]
src/backend/utils/adt/numutils.c [new file with mode: 0644]
src/backend/utils/adt/oid.c [new file with mode: 0644]
src/backend/utils/adt/oidint2.c [new file with mode: 0644]
src/backend/utils/adt/oidint4.c [new file with mode: 0644]
src/backend/utils/adt/oidname.c [new file with mode: 0644]
src/backend/utils/adt/regexp.c [new file with mode: 0644]
src/backend/utils/adt/regproc.c [new file with mode: 0644]
src/backend/utils/adt/selfuncs.c [new file with mode: 0644]
src/backend/utils/adt/sets.c [new file with mode: 0644]
src/backend/utils/adt/tid.c [new file with mode: 0644]
src/backend/utils/adt/varchar.c [new file with mode: 0644]
src/backend/utils/adt/varlena.c [new file with mode: 0644]
src/backend/utils/array.h [new file with mode: 0644]
src/backend/utils/bit.h [new file with mode: 0644]
src/backend/utils/builtins.h [new file with mode: 0644]
src/backend/utils/cache/Makefile.inc [new file with mode: 0644]
src/backend/utils/cache/catcache.c [new file with mode: 0644]
src/backend/utils/cache/fcache.c [new file with mode: 0644]
src/backend/utils/cache/inval.c [new file with mode: 0644]
src/backend/utils/cache/lsyscache.c [new file with mode: 0644]
src/backend/utils/cache/rel.c [new file with mode: 0644]
src/backend/utils/cache/relcache.c [new file with mode: 0644]
src/backend/utils/cache/syscache.c [new file with mode: 0644]
src/backend/utils/catcache.h [new file with mode: 0644]
src/backend/utils/datum.h [new file with mode: 0644]
src/backend/utils/dynamic_loader.h [new file with mode: 0644]
src/backend/utils/elog.h [new file with mode: 0644]
src/backend/utils/error/Makefile.inc [new file with mode: 0644]
src/backend/utils/error/assert.c [new file with mode: 0644]
src/backend/utils/error/elog.c [new file with mode: 0644]
src/backend/utils/error/exc.c [new file with mode: 0644]
src/backend/utils/error/excabort.c [new file with mode: 0644]
src/backend/utils/error/excid.c [new file with mode: 0644]
src/backend/utils/error/format.c [new file with mode: 0644]
src/backend/utils/exc.h [new file with mode: 0644]
src/backend/utils/excid.h [new file with mode: 0644]
src/backend/utils/fcache.h [new file with mode: 0644]
src/backend/utils/fcache2.h [new file with mode: 0644]
src/backend/utils/fmgr/Makefile.inc [new file with mode: 0644]
src/backend/utils/fmgr/dfmgr.c [new file with mode: 0644]
src/backend/utils/fmgr/fmgr.c [new file with mode: 0644]
src/backend/utils/fmgrtab.h [new file with mode: 0644]
src/backend/utils/geo-decls.h [new file with mode: 0644]
src/backend/utils/hash/Makefile.inc [new file with mode: 0644]
src/backend/utils/hash/dynahash.c [new file with mode: 0644]
src/backend/utils/hash/hashfn.c [new file with mode: 0644]
src/backend/utils/hsearch.h [new file with mode: 0644]
src/backend/utils/init/Makefile.inc [new file with mode: 0644]
src/backend/utils/init/enbl.c [new file with mode: 0644]
src/backend/utils/init/findbe.c [new file with mode: 0644]
src/backend/utils/init/globals.c [new file with mode: 0644]
src/backend/utils/init/magic.c [new file with mode: 0644]
src/backend/utils/init/miscinit.c [new file with mode: 0644]
src/backend/utils/init/postinit.c [new file with mode: 0644]
src/backend/utils/inval.h [new file with mode: 0644]
src/backend/utils/lselect.h [new file with mode: 0644]
src/backend/utils/lsyscache.h [new file with mode: 0644]
src/backend/utils/mcxt.h [new file with mode: 0644]
src/backend/utils/memutils.h [new file with mode: 0644]
src/backend/utils/mmgr/Makefile.inc [new file with mode: 0644]
src/backend/utils/mmgr/aset.c [new file with mode: 0644]
src/backend/utils/mmgr/mcxt.c [new file with mode: 0644]
src/backend/utils/mmgr/oset.c [new file with mode: 0644]
src/backend/utils/mmgr/palloc.c [new file with mode: 0644]
src/backend/utils/mmgr/portalmem.c [new file with mode: 0644]
src/backend/utils/module.h [new file with mode: 0644]
src/backend/utils/nabstime.h [new file with mode: 0644]
src/backend/utils/oidcompos.h [new file with mode: 0644]
src/backend/utils/palloc.h [new file with mode: 0644]
src/backend/utils/portal.h [new file with mode: 0644]
src/backend/utils/psort.h [new file with mode: 0644]
src/backend/utils/rel.h [new file with mode: 0644]
src/backend/utils/rel2.h [new file with mode: 0644]
src/backend/utils/relcache.h [new file with mode: 0644]
src/backend/utils/sets.h [new file with mode: 0644]
src/backend/utils/sort/Makefile.inc [new file with mode: 0644]
src/backend/utils/sort/lselect.c [new file with mode: 0644]
src/backend/utils/sort/psort.c [new file with mode: 0644]
src/backend/utils/syscache.h [new file with mode: 0644]
src/backend/utils/time/Makefile.inc [new file with mode: 0644]
src/backend/utils/time/tqual.c [new file with mode: 0644]
src/backend/utils/tqual.h [new file with mode: 0644]
src/bin/Makefile [new file with mode: 0644]
src/bin/Makefile.global [new file with mode: 0644]
src/bin/cleardbdir/Makefile [new file with mode: 0644]
src/bin/cleardbdir/cleardbdir.sh [new file with mode: 0644]
src/bin/createdb/Makefile [new file with mode: 0644]
src/bin/createdb/createdb.sh [new file with mode: 0644]
src/bin/createuser/Makefile [new file with mode: 0644]
src/bin/createuser/createuser.sh [new file with mode: 0644]
src/bin/destroydb/Makefile [new file with mode: 0644]
src/bin/destroydb/destroydb.sh [new file with mode: 0644]
src/bin/destroyuser/Makefile [new file with mode: 0644]
src/bin/destroyuser/destroyuser.sh [new file with mode: 0644]
src/bin/initdb/Makefile [new file with mode: 0644]
src/bin/initdb/initdb.sh [new file with mode: 0644]
src/bin/ipcclean/Makefile [new file with mode: 0644]
src/bin/ipcclean/ipcclean.sh [new file with mode: 0644]
src/bin/monitor/Makefile [new file with mode: 0644]
src/bin/monitor/monitor.c [new file with mode: 0644]
src/bin/pg4_dump/Makefile [new file with mode: 0644]
src/bin/pg4_dump/README [new file with mode: 0644]
src/bin/pg4_dump/common.c [new file with mode: 0644]
src/bin/pg4_dump/pg4_dump.c [new file with mode: 0644]
src/bin/pg4_dump/pg_dump.h [new file with mode: 0644]
src/bin/pg_dump/Makefile [new file with mode: 0644]
src/bin/pg_dump/README [new file with mode: 0644]
src/bin/pg_dump/common.c [new file with mode: 0644]
src/bin/pg_dump/pg_dump.c [new file with mode: 0644]
src/bin/pg_dump/pg_dump.h [new file with mode: 0644]
src/bin/pg_id/Makefile [new file with mode: 0644]
src/bin/pg_id/pg_id.c [new file with mode: 0644]
src/bin/pg_version/Makefile [new file with mode: 0644]
src/bin/pg_version/pg_version.c [new file with mode: 0644]
src/bin/pgtclsh/Makefile [new file with mode: 0644]
src/bin/pgtclsh/README [new file with mode: 0644]
src/bin/pgtclsh/pgtclAppInit.c [new file with mode: 0644]
src/bin/pgtclsh/pgtclUtils.tcl [new file with mode: 0644]
src/bin/pgtclsh/pgtkAppInit.c [new file with mode: 0644]
src/bin/pgtclsh/updateStats.tcl [new file with mode: 0644]
src/bin/psql/Makefile [new file with mode: 0644]
src/bin/psql/psql.c [new file with mode: 0644]
src/bin/psql/psqlHelp.h [new file with mode: 0644]
src/bin/psql/rlstubs.c [new file with mode: 0644]
src/bin/psql/stringutils.c [new file with mode: 0644]
src/bin/psql/stringutils.h [new file with mode: 0644]
src/interfaces/libpgtcl/Makefile [new file with mode: 0644]
src/interfaces/libpgtcl/README [new file with mode: 0644]
src/interfaces/libpgtcl/libpgtcl.h [new file with mode: 0644]
src/interfaces/libpgtcl/pgtcl.c [new file with mode: 0644]
src/interfaces/libpgtcl/pgtclCmds.c [new file with mode: 0644]
src/interfaces/libpgtcl/pgtclCmds.h [new file with mode: 0644]
src/interfaces/libpgtcl/pgtclId.c [new file with mode: 0644]
src/interfaces/libpgtcl/pgtclId.h [new file with mode: 0644]
src/interfaces/libpq++/Makefile [new file with mode: 0644]
src/interfaces/libpq++/README [new file with mode: 0644]
src/interfaces/libpq++/examples/Makefile [new file with mode: 0644]
src/interfaces/libpq++/examples/testlibpq0.cc [new file with mode: 0644]
src/interfaces/libpq++/examples/testlibpq1.cc [new file with mode: 0644]
src/interfaces/libpq++/examples/testlibpq2.cc [new file with mode: 0644]
src/interfaces/libpq++/examples/testlibpq2.sql [new file with mode: 0644]
src/interfaces/libpq++/examples/testlibpq3.cc [new file with mode: 0644]
src/interfaces/libpq++/examples/testlibpq3.sql [new file with mode: 0644]
src/interfaces/libpq++/examples/testlibpq4.cc [new file with mode: 0644]
src/interfaces/libpq++/examples/testlo.cc [new file with mode: 0644]
src/interfaces/libpq++/libpq++.H [new file with mode: 0644]
src/interfaces/libpq++/man/libpq++.3 [new file with mode: 0644]
src/interfaces/libpq++/pgconnection.cc [new file with mode: 0644]
src/interfaces/libpq++/pgenv.cc [new file with mode: 0644]
src/interfaces/libpq++/pglobject.cc [new file with mode: 0644]
src/interfaces/libpq/Makefile [new file with mode: 0644]
src/interfaces/libpq/README [new file with mode: 0644]
src/interfaces/libpq/fe-auth.c [new file with mode: 0644]
src/interfaces/libpq/fe-auth.h [new file with mode: 0644]
src/interfaces/libpq/fe-connect.c [new file with mode: 0644]
src/interfaces/libpq/fe-exec.c [new file with mode: 0644]
src/interfaces/libpq/fe-lobj.c [new file with mode: 0644]
src/interfaces/libpq/fe-misc.c [new file with mode: 0644]
src/interfaces/libpq/libpq-fe.h [new file with mode: 0644]
src/interfaces/libpq/pg_hba [new file with mode: 0644]
src/interfaces/libpq/pqsignal.c [new file with mode: 0644]
src/interfaces/libpq/pqsignal.h [new file with mode: 0644]
src/mk/port/postgres.mk.BSD44_derived [new file with mode: 0644]
src/mk/port/postgres.mk.aix [new file with mode: 0644]
src/mk/port/postgres.mk.alpha [new file with mode: 0644]
src/mk/port/postgres.mk.bsdi [new file with mode: 0644]
src/mk/port/postgres.mk.hpux [new file with mode: 0644]
src/mk/port/postgres.mk.irix5 [new file with mode: 0644]
src/mk/port/postgres.mk.linux [new file with mode: 0644]
src/mk/port/postgres.mk.sparc [new file with mode: 0644]
src/mk/port/postgres.mk.sparc_solaris [new file with mode: 0644]
src/mk/port/postgres.mk.svr4 [new file with mode: 0644]
src/mk/port/postgres.mk.ultrix4 [new file with mode: 0644]
src/mk/postgres.lib.mk [new file with mode: 0644]
src/mk/postgres.mk [new file with mode: 0644]
src/mk/postgres.prog.mk [new file with mode: 0644]
src/mk/postgres.shell.mk [new file with mode: 0644]
src/mk/postgres.subdir.mk [new file with mode: 0644]
src/mk/postgres.user.mk [new file with mode: 0644]
src/test/Makefile [new file with mode: 0644]
src/test/bench/Makefile [new file with mode: 0644]
src/test/bench/WISC-README [new file with mode: 0644]
src/test/bench/create.sh [new file with mode: 0755]
src/test/bench/create.source [new file with mode: 0644]
src/test/bench/perquery [new file with mode: 0644]
src/test/bench/query01 [new file with mode: 0644]
src/test/bench/query02 [new file with mode: 0644]
src/test/bench/query03 [new file with mode: 0644]
src/test/bench/query04 [new file with mode: 0644]
src/test/bench/query05 [new file with mode: 0644]
src/test/bench/query06 [new file with mode: 0644]
src/test/bench/query07 [new file with mode: 0644]
src/test/bench/query08 [new file with mode: 0644]
src/test/bench/query09 [new file with mode: 0644]
src/test/bench/query10 [new file with mode: 0644]
src/test/bench/query11 [new file with mode: 0644]
src/test/bench/query12 [new file with mode: 0644]
src/test/bench/query13 [new file with mode: 0644]
src/test/bench/query14 [new file with mode: 0644]
src/test/bench/query15 [new file with mode: 0644]
src/test/bench/query16 [new file with mode: 0644]
src/test/bench/query17 [new file with mode: 0644]
src/test/bench/query18 [new file with mode: 0644]
src/test/bench/query19 [new file with mode: 0644]
src/test/bench/query20 [new file with mode: 0644]
src/test/bench/query21 [new file with mode: 0644]
src/test/bench/query22 [new file with mode: 0644]
src/test/bench/query23 [new file with mode: 0644]
src/test/bench/query24 [new file with mode: 0644]
src/test/bench/query25 [new file with mode: 0644]
src/test/bench/query26 [new file with mode: 0644]
src/test/bench/query27 [new file with mode: 0644]
src/test/bench/query28 [new file with mode: 0644]
src/test/bench/query29 [new file with mode: 0644]
src/test/bench/query30 [new file with mode: 0644]
src/test/bench/query31 [new file with mode: 0644]
src/test/bench/query32 [new file with mode: 0644]
src/test/bench/runwisc.sh [new file with mode: 0755]
src/test/bench/wholebench.sh [new file with mode: 0755]
src/test/examples/Makefile [new file with mode: 0644]
src/test/examples/testlibpq.c [new file with mode: 0644]
src/test/examples/testlibpq2.c [new file with mode: 0644]
src/test/examples/testlibpq2.sql [new file with mode: 0644]
src/test/examples/testlibpq3.c [new file with mode: 0644]
src/test/examples/testlibpq3.sql [new file with mode: 0644]
src/test/examples/testlibpq4.c [new file with mode: 0644]
src/test/examples/testlo.c [new file with mode: 0644]
src/test/examples/testlo2.c [new file with mode: 0644]
src/test/regress/Makefile [new file with mode: 0644]
src/test/regress/create.source [new file with mode: 0644]
src/test/regress/data/dept.data [new file with mode: 0644]
src/test/regress/data/desc.data [new file with mode: 0644]
src/test/regress/data/emp.data [new file with mode: 0644]
src/test/regress/data/hash.data [new file with mode: 0644]
src/test/regress/data/onek.data [new file with mode: 0644]
src/test/regress/data/person.data [new file with mode: 0644]
src/test/regress/data/real_city.data [new file with mode: 0644]
src/test/regress/data/rect.data [new file with mode: 0644]
src/test/regress/data/streets.data [new file with mode: 0644]
src/test/regress/data/stud_emp.data [new file with mode: 0644]
src/test/regress/data/student.data [new file with mode: 0644]
src/test/regress/data/tenk.data [new file with mode: 0644]
src/test/regress/destroy.source [new file with mode: 0644]
src/test/regress/errors.source [new file with mode: 0644]
src/test/regress/queries.source [new file with mode: 0644]
src/test/regress/regress.c [new file with mode: 0644]
src/test/regress/regress.sh [new file with mode: 0755]
src/test/regress/sample.regress.out [new file with mode: 0644]
src/test/regress/security.source [new file with mode: 0644]
src/test/suite/README [new file with mode: 0644]
src/test/suite/agg.sql [new file with mode: 0644]
src/test/suite/date.sql [new file with mode: 0644]
src/test/suite/float.sql [new file with mode: 0644]
src/test/suite/group.sql [new file with mode: 0644]
src/test/suite/group_err.sql [new file with mode: 0644]
src/test/suite/inh.sql [new file with mode: 0644]
src/test/suite/join.sql [new file with mode: 0644]
src/test/suite/oper.sql [new file with mode: 0644]
src/test/suite/parse.sql [new file with mode: 0644]
src/test/suite/quote.sql [new file with mode: 0644]
src/test/suite/results/agg.sql.out [new file with mode: 0644]
src/test/suite/results/date.sql.out [new file with mode: 0644]
src/test/suite/results/float.sql.out [new file with mode: 0644]
src/test/suite/results/group.sql.out [new file with mode: 0644]
src/test/suite/results/group_err.sql.out [new file with mode: 0644]
src/test/suite/results/inh.sql.out [new file with mode: 0644]
src/test/suite/results/join.sql.out [new file with mode: 0644]
src/test/suite/results/oper.sql.out [new file with mode: 0644]
src/test/suite/results/parse.sql.out [new file with mode: 0644]
src/test/suite/results/quote.sql.out [new file with mode: 0644]
src/test/suite/results/rules.sql.out [new file with mode: 0644]
src/test/suite/results/select.sql.out [new file with mode: 0644]
src/test/suite/results/sort.sql.out [new file with mode: 0644]
src/test/suite/results/sqlcompat.sql.out [new file with mode: 0644]
src/test/suite/results/time.sql.out [new file with mode: 0644]
src/test/suite/results/varchar.sql.out [new file with mode: 0644]
src/test/suite/results/views.sql.out [new file with mode: 0644]
src/test/suite/rules.sql [new file with mode: 0644]
src/test/suite/runall [new file with mode: 0755]
src/test/suite/select.sql [new file with mode: 0644]
src/test/suite/sort.sql [new file with mode: 0644]
src/test/suite/sqlcompat.sql [new file with mode: 0644]
src/test/suite/time.sql [new file with mode: 0644]
src/test/suite/varchar.sql [new file with mode: 0644]
src/test/suite/views.sql [new file with mode: 0644]
src/tools/mkldexport/Makefile [new file with mode: 0644]
src/tools/mkldexport/README [new file with mode: 0644]
src/tools/mkldexport/mkldexport.sh [new file with mode: 0644]
src/tutorial/C-code/beard.c [new file with mode: 0644]
src/tutorial/C-code/complex.c [new file with mode: 0644]
src/tutorial/C-code/funcs.c [new file with mode: 0644]
src/tutorial/Makefile [new file with mode: 0644]
src/tutorial/README [new file with mode: 0644]
src/tutorial/advanced.source [new file with mode: 0644]
src/tutorial/basics.source [new file with mode: 0644]
src/tutorial/complex.source [new file with mode: 0644]
src/tutorial/funcs.source [new file with mode: 0644]
src/tutorial/syscat.source [new file with mode: 0644]

diff --git a/src/Makefile b/src/Makefile
new file mode 100644 (file)
index 0000000..be536c7
--- /dev/null
@@ -0,0 +1,48 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Build and install postgres.
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+# NOTES
+#      objdir  - location of the objects and generated files (eg. obj)
+#
+#-------------------------------------------------------------------------
+
+SUBDIR= backend libpq bin
+
+FIND = find
+# assuming gnu tar and split here
+TAR  = tar
+SPLIT = split
+
+ETAGS = etags
+XARGS = xargs
+
+ifeq ($(USE_TCL), true)
+SUBDIR += libpgtcl
+endif
+
+include mk/postgres.subdir.mk
+
+TAGS:
+       rm -f TAGS; \
+       for i in backend libpq bin; do \
+         $(FIND) $$i -name '*.[chyl]' -print | $(XARGS) $(ETAGS) -a ; \
+       done
+
+# target to generate a backup tar file and split files that can be 
+# saved to 1.44M floppy
+BACKUP:
+       rm -f BACKUP.filelist BACKUP.tgz; \
+       $(FIND) . -not -path '*obj/*' -not -path '*data/*' -type f -print > BACKUP.filelist; \
+       $(TAR) --files-from BACKUP.filelist -c -z -v -f BACKUP.tgz
+       $(SPLIT) --bytes=1400k BACKUP.tgz pgBACKUP.     
+
+.PHONY: TAGS
+.PHONY: BACKUP
diff --git a/src/Makefile.global b/src/Makefile.global
new file mode 100644 (file)
index 0000000..2dd3ab5
--- /dev/null
@@ -0,0 +1,306 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.global--
+#    global configuration for the Makefiles
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+# NOTES
+#    This is seen by any Makefiles that include mk/postgres.mk. To
+#    override the default setting, create a Makefile.custom in this
+#    directory and put your defines there. (Makefile.custom is included
+#    at the end of this file.)
+#
+#    If you change any of these defines you probably have to 
+#      gmake clean; gmake
+#    since no dependecies are created for these. (of course you can 
+#    be crafty and check what files really depend on them and just remake
+#    those).
+#
+#-------------------------------------------------------------------------
+
+
+##############################################################################
+#
+# CONFIGURATION SECTION
+#
+# Following are settings pertaining to the postgres build and 
+# installation.  The most important one is obviously the name 
+# of the port.
+
+#  The name of the port.  Valid choices are:
+#      alpha           -       DEC Alpha AXP on OSF/1 2.0
+#      hpux            -       HP PA-RISC on HP-UX 9.0
+#      sparc_solaris   -       SUN SPARC on Solaris 2.4
+#      sparc           -       SUN SPARC on SunOS 4.1.3
+#      ultrix4         -       DEC MIPS on Ultrix 4.4
+#      linux           -       Intel x86 on Linux 1.2 and Linux ELF
+#                              (For non-ELF Linux, you need to comment out 
+#                              "LINUX_ELF=1" in src/mk/port/postgres.mk.linux)
+#      BSD44_derived   -       OSs derived from 4.4-lite BSD (NetBSD, FreeBSD)
+#       bsdi            -       BSD/OS 2.0 and 2.01
+#      aix             -       IBM on AIX 3.2.5
+#      irix5           -       SGI MIPS on IRIX 5.3
+#  Some hooks are provided for
+#      svr4            -       Intel x86 on Intel SVR4
+#      next            -       Motorola MC68K or Intel x86 on NeXTSTEP 3.2
+#  but these are guaranteed not to work as of yet.
+#
+#  XXX Note that you MUST set PORTNAME here (or on the command line) so 
+#      that port-dependent variables are correctly set within this file.
+#      Makefile.custom does not take effect (for ifeq purposes) 
+#      until after this file is processed!
+#  make sure that you have no whitespaces after the PORTNAME setting
+#  or the makefiles can get confused
+PORTNAME=      alpha
+
+# POSTGRESLOGIN is the login name of the user who gets special
+# privileges within the database.  By default it is "postgres", but
+# you can change it to any existing login name (such as your own 
+# login if you are compiling a private version or don't have root
+# access).
+POSTGRESLOGIN= postgres
+
+# For convenience, POSTGRESDIR is where DATADIR, BINDIR, and LIBDIR 
+# and other target destinations are rooted.  Of course, each of these is 
+# changable separately.
+POSTGRESDIR=   /private/postgres95
+
+# SRCDIR specifies where the source files are.
+SRCDIR=                $(POSTGRESDIR)/src
+
+# DATADIR specifies where the postmaster expects to find its database.
+# This may be overridden by command line options or the PGDATA environment
+# variable.
+DATADIR=       $(POSTGRESDIR)/data
+
+# Where the postgres executables live (changeable by just putting them
+# somewhere else and putting that directory in your shell PATH)
+BINDIR=                $(POSTGRESDIR)/bin
+
+# Where libpq.a gets installed.  You must put it where your loader will
+# look for it if you wish to use the -lpq convention.  Otherwise you
+# can just put the absolute pathname to the library at the end of your
+# command line.
+LIBDIR=                $(POSTGRESDIR)/lib
+
+# This is the directory where IPC utilities ipcs and ipcrm are located
+#
+IPCSDIR=       /usr/bin
+
+# Where the man pages (suitable for use with "man") get installed.
+POSTMANDIR=    $(POSTGRESDIR)/man
+
+# Where the formatted documents (e.g., the reference manual) get installed.
+POSTDOCDIR=    $(POSTGRESDIR)/doc
+
+# Where the header files necessary to build frontend programs get installed.
+HEADERDIR=     $(POSTGRESDIR)/include
+
+# NAMEDATALEN is the max length for system identifiers (e.g. table names, 
+# attribute names, function names, etc.)  
+#
+# These MUST be set here.  DO NOT COMMENT THESE OUT
+# Setting these too high will result in excess space usage for system catalogs
+# Setting them too low will make the system unusable.
+# values between 16 and 64 that are multiples of four are recommended.
+#
+# NOTE also that databases with different NAMEDATALEN's cannot interoperate!
+#
+NAMEDATALEN = 32
+# OIDNAMELEN should be set to NAMEDATALEN + sizeof(Oid)
+OIDNAMELEN = 36
+
+CFLAGS+= -DNAMEDATALEN=$(NAMEDATALEN) -DOIDNAMELEN=$(OIDNAMELEN)
+
+##############################################################################
+#
+# FEATURES 
+#
+# To disable a feature, comment out the entire definition
+# (that is, prepend '#', don't set it to "0" or "no").
+
+# Comment out ENFORCE_ALIGNMENT if you do NOT want unaligned access to
+# multi-byte types to generate a bus error.
+ENFORCE_ALIGNMENT= true
+
+# Comment out CDEBUG to turn off debugging and sanity-checking.
+#
+#      XXX on MIPS, use -g3 if you want to compile with -O
+CDEBUG= -g
+
+# turn this on if you prefer European style dates instead of American
+# style dates
+# EUROPEAN_DATES = 1
+
+# Comment out PROFILE to disable profiling.
+#
+#      XXX define on MIPS if you want to be able to use pixie.
+#          note that this disables dynamic loading!
+#PROFILE= -p -non_shared
+
+# About the use of readline in psql:
+#    psql does not require the GNU readline and history libraries. Hence, we
+#    do not compile with them by default. However, there are hooks in the
+#    program which supports the use of GNU readline and history. Should you
+#    decide to use them, change USE_READLINE to true and change READLINE_INCDIR
+#    and READLINE_LIBDIR to reflect the location of the readline and histroy
+#    headers and libraries.
+#
+#USE_READLINE= true
+
+# directories for the readline and history libraries.
+READLINE_INCDIR=  /usr/local/include
+HISTORY_INCDIR=   /usr/local/include
+READLINE_LIBDIR=  /usr/local/lib
+HISTORY_LIBDIR=   /usr/local/lib
+
+# If you do not plan to use Host based authentication,
+# comment out the following line
+HBA = 1
+ifdef HBA
+HBAFLAGS= -DHBA
+endif
+
+
+
+# If you plan to use Kerberos for authentication...
+#
+# Comment out KRBVERS if you do not use Kerberos.
+#      Set KRBVERS to "4" for Kerberos v4, "5" for Kerberos v5.
+#      XXX Edit the default Kerberos variables below!
+#
+#KRBVERS=      5
+
+
+# Globally pass Kerberos file locations.
+#      these are used in the postmaster and all libpq applications.
+#
+#      Adjust KRBINCS and KRBLIBS to reflect where you have Kerberos
+#              include files and libraries installed.
+#      PG_KRB_SRVNAM is the name under which POSTGRES is registered in
+#              the Kerberos database (KDC).
+#      PG_KRB_SRVTAB is the location of the server's keytab file.
+#
+ifdef KRBVERS
+KRBINCS= -I/usr/athena/include
+KRBLIBS= -L/usr/athena/lib
+KRBFLAGS+= $(KRBINCS) -DPG_KRB_SRVNAM='"postgres_dbms"'
+   ifeq ($(KRBVERS), 4)
+KRBFLAGS+= -DKRB4
+KRBFLAGS+= -DPG_KRB_SRVTAB='"/etc/srvtab"'
+KRBLIBS+= -lkrb -ldes
+   else
+   ifeq ($(KRBVERS), 5)
+KRBFLAGS+= -DKRB5
+KRBFLAGS+= -DPG_KRB_SRVTAB='"FILE:/krb5/srvtab.postgres"'
+KRBLIBS+= -lkrb5 -lcrypto -lcom_err -lisode
+   endif
+   endif
+endif
+
+#
+# location of Tcl/Tk headers and libraries
+#
+# Uncomment this to build the tcl utilities.
+USE_TCL= true
+# customize these to your site's needs
+#
+TCL_INCDIR= /usr/local/devel/tcl7.4/include
+TCL_LIBDIR= /usr/local/devel/tcl7.4/lib
+TCL_LIB = -ltcl7.4
+TK_INCDIR=  /usr/local/devel/tk4.0/include
+TK_LIBDIR=  /usr/local/devel/tk4.0/lib
+TK_LIB = -ltk4.0
+
+#
+# include port specific rules and variables. For instance:
+#
+# signal(2) handling - this is here because it affects some of 
+# the frontend commands as well as the backend server.
+#
+# Ultrix and SunOS provide BSD signal(2) semantics by default.
+#
+# SVID2 and POSIX signal(2) semantics differ from BSD signal(2) 
+# semantics.  We can use the POSIX sigaction(2) on systems that
+# allow us to request restartable signals (SA_RESTART).
+#
+# Some systems don't allow restartable signals at all unless we 
+# link to a special BSD library.
+#
+# We devoutly hope that there aren't any systems that provide
+# neither POSIX signals nor BSD signals.  The alternative 
+# is to do signal-handler reinstallation, which doesn't work well 
+# at all.
+#
+-include $(MKDIR)/port/postgres.mk.$(PORTNAME)
+
+##############################################################################
+#
+# Flags for CC and LD. (depend on CDEBUG and PROFILE)
+#
+
+# Globally pass debugging/optimization/profiling flags based
+# on the options selected above.
+ifdef CDEBUG
+   CFLAGS+= $(CDEBUG)
+   LDFLAGS+= $(CDEBUG)
+else
+   ifndef CFLAGS_OPT
+      CFLAGS_OPT= -O
+   endif
+   CFLAGS+= $(CFLAGS_OPT)
+#
+# Uncommenting this will make things go a LOT faster, but you will
+# also lose a lot of useful error-checking.
+#
+   CFLAGS+= -DNO_ASSERT_CHECKING
+endif
+
+ifdef PROFILE
+CFLAGS+= $(PROFILE)
+LDFLAGS+= $(PROFILE)
+endif
+
+# Globally pass PORTNAME
+CFLAGS+= -DPORTNAME_$(PORTNAME)
+
+# Globally pass the default TCP port for postmaster(1).
+CFLAGS+= -DPOSTPORT='"5432"'
+
+# include flags from mk/port/postgres.mk.$(PORTNAME)
+CFLAGS+= $(CFLAGS_BE)
+LDADD+= $(LDADD_BE)
+LDFLAGS+= $(LDFLAGS_BE)
+
+
+##############################################################################
+#
+# Miscellaneous configuration
+#
+
+# This is the time, in seconds, at which a given backend server
+# will wait on a lock before deciding to abort the transaction
+# (this is what we do in lieu of deadlock detection).
+#
+# Low numbers are not recommended as they will tend to cause
+# false aborts if many transactions are long-lived.
+CFLAGS+= -DDEADLOCK_TIMEOUT=60
+
+srcdir=                $(SRCDIR)
+includedir=    $(HEADERDIR)
+objdir=                obj
+
+
+##############################################################################
+#
+# Customization.
+#
+-include $(MKDIR)/../Makefile.custom
+
+
diff --git a/src/backend/Makefile b/src/backend/Makefile
new file mode 100644 (file)
index 0000000..d3972ec
--- /dev/null
@@ -0,0 +1,289 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for the postgres backend (and the postmaster)
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+#
+# The following turns on intermediate linking of partial objects to speed
+# the link cycle during development. (To turn this off, put "BIGOBJS=false"
+# in your custom makefile, ../Makefile.custom.)
+BIGOBJS= true
+
+
+PROG=  postgres
+
+MKDIR= ../mk
+include $(MKDIR)/postgres.mk
+
+
+include $(CURDIR)/access/Makefile.inc
+include $(CURDIR)/bootstrap/Makefile.inc
+include $(CURDIR)/catalog/Makefile.inc
+include $(CURDIR)/commands/Makefile.inc
+include $(CURDIR)/executor/Makefile.inc
+include $(CURDIR)/include/Makefile.inc
+include $(CURDIR)/lib/Makefile.inc
+include $(CURDIR)/libpq/Makefile.inc
+include $(CURDIR)/main/Makefile.inc
+include $(CURDIR)/nodes/Makefile.inc
+include $(CURDIR)/optimizer/Makefile.inc
+include $(CURDIR)/parser/Makefile.inc
+include $(CURDIR)/port/Makefile.inc
+include $(CURDIR)/postmaster/Makefile.inc
+include $(CURDIR)/regex/Makefile.inc
+include $(CURDIR)/rewrite/Makefile.inc
+include $(CURDIR)/storage/Makefile.inc
+include $(CURDIR)/tcop/Makefile.inc
+include $(CURDIR)/tioga/Makefile.inc
+include $(CURDIR)/utils/Makefile.inc
+
+SRCS:= ${SRCS_ACCESS} ${SRCS_BOOTSTRAP} $(SRCS_CATALOG) ${SRCS_COMMANDS} \
+       ${SRCS_EXECUTOR} $(SRCS_LIB) $(SRCS_LIBPQ) ${SRCS_MAIN} \
+       ${SRCS_NODES} ${SRCS_OPTIMIZER} ${SRCS_PARSER} ${SRCS_PORT} \
+       $(SRCS_POSTMASTER) ${SRCS_REGEX} ${SRCS_REWRITE} ${SRCS_STORAGE} \
+       ${SRCS_TCOP} ${SRCS_UTILS} 
+
+ifeq ($(BIGOBJS), true)
+OBJS= ACCESS.o BOOTSTRAP.o COMMANDS.o EXECUTOR.o MAIN.o MISC.o NODES.o \
+       PARSER.o OPTIMIZER.o REGEX.o REWRITE.o STORAGE.o TCOP.o UTILS.o
+CLEANFILES+= $(subst .s,.o,$(SRCS:.c=.o)) $(OBJS)
+else
+OBJS:= $(subst .s,.o,$(SRCS:%.c=$(objdir)/%.o))
+CLEANFILES+= $(notdir $(OBJS))
+endif
+
+#############################################################################
+#
+# TIOGA stuff
+#
+ifdef TIOGA
+SRCS+= $(SRCS_TIOGA) 
+   ifeq ($(BIGOBJS), true)
+TIOGA.o:       $(SRCS_TIOGA:%.c=$(objdir)/%.o)
+       $(make_partial)
+OBJS+= TIOGA.o
+CLEANFILES+= $(SRCS_TIOGA:%.c=%.o) TIOGA.o
+   else
+OBJS+= $(SRCS_TIOGA:%.c=$(objdir)/%.o)
+   endif
+endif
+
+
+#############################################################################
+#
+# Compiling the postgres backend.
+#
+CFLAGS+=  -DPOSTGRESDIR='"$(POSTGRESDIR)"' \
+       -DPGDATADIR='"$(DATADIR)"' \
+       -I$(CURDIR)/. -I$(CURDIR)/$(objdir) \
+       -I$(CURDIR)/include \
+       -I$(CURDIR)/port/$(PORTNAME)
+
+# turn this on if you prefer European style dates instead of American
+# style dates
+ifdef EUROPEAN_DATES
+CFLAGS += -DEUROPEAN_STYLE
+endif
+
+# kerberos flags
+ifdef KRBVERS
+CFLAGS+= $(KRBFLAGS)
+LDADD+= $(KRBLIBS)
+endif
+
+# host based access flags
+ifdef HBA
+CFLAGS+= $(HBAFLAGS)
+endif
+
+
+#
+# All systems except NEXTSTEP require the math library.
+# Loader flags for system-dependent libraries are appended in
+#      src/backend/port/$(PORTNAME)/Makefile.inc
+#
+ifneq ($(PORTNAME), next)
+LDADD+=        -lm
+endif
+
+# statically link in libc for linux
+ifeq ($(PORTNAME), linux)
+LDADD+= -lc
+endif
+
+postgres: $(POSTGRES_DEPEND) $(OBJS) $(EXPORTS)
+       $(CC) $(LDFLAGS) -o $(objdir)/$(@F) $(addprefix $(objdir)/,$(notdir $(OBJS))) $(LDADD)
+
+# Make this target first if you are doing a parallel make.
+# The targets in 'first' need to be made sequentially because of dependencies.
+# Then, you can make 'all' with parallelism turned on.
+first: $(POSTGRES_DEPEND)
+
+
+#############################################################################
+#
+# Partial objects for platforms with slow linkers.
+#
+ifeq ($(BIGOBJS), true)
+
+OBJS_ACCESS:=     $(SRCS_ACCESS:%.c=$(objdir)/%.o)
+OBJS_BOOTSTRAP:=   $(SRCS_BOOTSTRAP:%.c=$(objdir)/%.o)
+OBJS_CATALOG:=    $(SRCS_CATALOG:%.c=$(objdir)/%.o)
+OBJS_COMMANDS:=           $(SRCS_COMMANDS:%.c=$(objdir)/%.o)
+OBJS_EXECUTOR:=           $(SRCS_EXECUTOR:%.c=$(objdir)/%.o)
+OBJS_MAIN:=       $(SRCS_MAIN:%.c=$(objdir)/%.o)
+OBJS_POSTMASTER:=  $(SRCS_POSTMASTER:%.c=$(objdir)/%.o)
+OBJS_LIB:=        $(SRCS_LIB:%.c=$(objdir)/%.o)
+OBJS_LIBPQ:=      $(SRCS_LIBPQ:%.c=$(objdir)/%.o)
+OBJS_PORT:=       $(addprefix $(objdir)/,$(subst .s,.o,$(SRCS_PORT:.c=.o)))
+OBJS_NODES:=      $(SRCS_NODES:%.c=$(objdir)/%.o)
+OBJS_PARSER:=     $(SRCS_PARSER:%.c=$(objdir)/%.o)
+OBJS_OPTIMIZER:=   $(SRCS_OPTIMIZER:%.c=$(objdir)/%.o)
+OBJS_REGEX:=      $(SRCS_REGEX:%.c=$(objdir)/%.o)
+OBJS_REWRITE:=    $(SRCS_REWRITE:%.c=$(objdir)/%.o)
+OBJS_STORAGE:=    $(SRCS_STORAGE:%.c=$(objdir)/%.o)
+OBJS_TCOP:=       $(SRCS_TCOP:%.c=$(objdir)/%.o)
+OBJS_UTILS:=      $(SRCS_UTILS:%.c=$(objdir)/%.o)
+
+ACCESS.o:      $(OBJS_ACCESS)
+       $(make_partial)
+BOOTSTRAP.o:   $(OBJS_BOOTSTRAP)
+       $(make_partial)
+COMMANDS.o:    $(OBJS_COMMANDS)
+       $(make_partial)
+EXECUTOR.o:    $(OBJS_EXECUTOR)
+       $(make_partial)
+MAIN.o:                $(OBJS_MAIN) $(OBJS_POSTMASTER)
+       $(make_partial)
+MISC.o:                $(OBJS_CATALOG) $(OBJS_LIB) $(OBJS_LIBPQ) $(OBJS_PORT)
+       $(make_partial)
+NODES.o:       $(OBJS_NODES)
+       $(make_partial)
+PARSER.o:      $(OBJS_PARSER)
+       $(make_partial)
+OPTIMIZER.o:   $(OBJS_OPTIMIZER)
+       $(make_partial)
+REGEX.o:       $(OBJS_REGEX)
+       $(make_partial)
+REWRITE.o:     $(OBJS_REWRITE)
+       $(make_partial)
+STORAGE.o:     $(OBJS_STORAGE)
+       $(make_partial)
+TCOP.o:                $(OBJS_TCOP)
+       $(make_partial)
+UTILS.o:       $(OBJS_UTILS)
+       $(make_partial)
+endif
+
+#############################################################################
+#
+# Installation.
+#
+# Install the bki files to the data directory.  We also copy a version
+# of them that has "PGUID" intact, so one can change the value of the
+# postgres userid before running initdb in the case of customizing the
+# binary release (i.e., fixing up PGUID w/o recompiling the system).
+# Those files are copied out as foo.source.  The program newbki(1) can
+# be run later to reset the postgres login id (but it must be run before
+# initdb is run, or after clearing the data directory with
+# cleardbdir(1)). [newbki distributed with v4r2 but not with Postgres95.]
+#
+
+#      NAMEDATALEN=`egrep "^#define NAMEDATALEN" $(CURDIR)/include/postgres.h | awk '{print $$3}'`; \
+#      OIDNAMELEN=`egrep "^#define OIDNAMELEN" $(CURDIR)/include/postgres.h | awk '{print $$3}'`; \
+
+install: beforeinstall pg_id $(BKIFILES) postgres
+       $(INSTALL) $(INSTL_EXE_OPTS) $(objdir)/postgres $(DESTDIR)$(BINDIR)/postgres
+       @rm -f $(DESTDIR)$(BINDIR)/postmaster
+       cd $(DESTDIR)$(BINDIR); ln -s postgres postmaster
+       @cd $(objdir); \
+       PG_UID=`./pg_id $(POSTGRESLOGIN)`; \
+       POSTGRESLOGIN=$(POSTGRESLOGIN);\
+       echo "NAMEDATALEN = $(NAMEDATALEN)"; \
+       echo "OIDNAMELEN = $(OIDNAMELEN)"; \
+       case $$PG_UID in "NOUSER") \
+               echo "Warning: no account named $(POSTGRESLOGIN), using yours";\
+               POSTGRESLOGIN=`whoami`; \
+               PG_UID=`./pg_id`;; \
+       esac ;\
+       for bki in $(BKIFILES); do \
+               sed \
+                   -e "s/postgres PGUID/$$POSTGRESLOGIN $$PG_UID/" \
+                   -e "s/NAMEDATALEN/$(NAMEDATALEN)/g" \
+                   -e "s/OIDNAMELEN/$(OIDNAMELEN)/g" \
+                   -e "s/PGUID/$$PG_UID/" \
+                   < $$bki > $$bki.sed ; \
+               echo "Installing $(DESTDIR)$(DATADIR)/files/$$bki."; \
+               $(INSTALL) $(INSTLOPTS) \
+                   $$bki.sed $(DESTDIR)$(DATADIR)/files/$$bki; \
+               rm -f $$bki.sed; \
+               echo "Installing $(DESTDIR)$(DATADIR)/files/$$bki.source."; \
+               $(INSTALL) $(INSTLOPTS) \
+                   $$bki $(DESTDIR)$(DATADIR)/files/$$bki.source; \
+       done;
+       @echo "Installing $(DATADIR)/pg_hba";
+       @cp $(srcdir)/libpq/pg_hba $(DATADIR)
+       @chmod 644 $(DATADIR)/pg_hba
+
+
+# so we can get the UID of the postgres owner (w/o moving pg_id to
+# src/tools). We just want the vanilla LDFLAGS for pg_id
+IDLDFLAGS:= $(LDFLAGS)
+ifeq ($(PORTNAME), hpux)
+ifeq ($(CC), cc)
+IDLDFLAGS+= -Aa -D_HPUX_SOURCE
+endif
+endif
+pg_id: $(srcdir)/bin/pg_id/pg_id.c
+       $(CC) $(IDLDFLAGS) -o $(objdir)/$(@F) $<
+
+CLEANFILES+= pg_id postgres
+
+
+#############################################################################
+#
+# Support for code development.
+#
+
+#
+# Build the file, "./ID", used by the "gid" (grep-for-identifier) tool
+#
+IDFILE=        ID
+.PHONY: $(IDFILE)
+$(IDFILE):
+       $(CURDIR)/makeID $(PORTNAME)
+
+#
+# Special rule to generate cpp'd version of a .c file.  This is
+# especially useful given all the hellish macro processing going on.
+# The cpp'd version has a .C suffix.  To create foo.C from foo.c, just
+# type
+#      bmake foo.C
+#
+%.cpp: %.c
+       $(CC) -E $(CFLAGS) $(<:.C=.c) | cat -s | cb | tr -s '\012*' '\012' > $(objdir)/$(@F)
+
+cppall: $(SRCS:.c=.cpp)
+
+#
+# To use Purify (SunOS only), define PURIFY to be the path (and
+# options) with which to invoke the Purify loader.  Only the executable
+# needs to be loaded with Purify.
+#
+# PURIFY = /usr/sww/bin/purify -cache-dir=/usr/local/postgres/src/backend/purify-cache
+#.if defined(PURIFY)
+#${PROG}: $(POSTGRES_DEPEND) $(OBJS) $(EXPORTS)
+#      ${PURIFY} ${CC} ${LDFLAGS} -o $(objdir)/$(@F) $(addprefix $(objdir)/,$(notdir $(OBJS))) $(LDADD)
+#
+#CLEANFILES+= .purify* .pure .lock.*.o *_pure_*.o *.pure_*link*
+#.endif
+
diff --git a/src/backend/access/Makefile.inc b/src/backend/access/Makefile.inc
new file mode 100644 (file)
index 0000000..940ed7c
--- /dev/null
@@ -0,0 +1,35 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the access methods module
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+accdir=$(CURDIR)/access
+VPATH:=$(VPATH):$(accdir):\
+       $(accdir)/common:$(accdir)/hash:$(accdir)/heap:$(accdir)/index:\
+       $(accdir)/rtree:$(accdir)/nbtree:$(accdir)/transam
+
+
+SUBSRCS=
+include $(accdir)/common/Makefile.inc
+include $(accdir)/hash/Makefile.inc
+include $(accdir)/heap/Makefile.inc
+include $(accdir)/index/Makefile.inc
+include $(accdir)/rtree/Makefile.inc
+include $(accdir)/nbtree/Makefile.inc
+include $(accdir)/transam/Makefile.inc
+SRCS_ACCESS:= $(SUBSRCS)
+
+HEADERS+= attnum.h funcindex.h genam.h hash.h \
+       heapam.h hio.h htup.h ibit.h iqual.h istrat.h \
+       itup.h nbtree.h printtup.h relscan.h rtree.h \
+       sdir.h skey.h strat.h transam.h tupdesc.h tupmacs.h \
+       valid.h xact.h
+
diff --git a/src/backend/access/attnum.h b/src/backend/access/attnum.h
new file mode 100644 (file)
index 0000000..94a0730
--- /dev/null
@@ -0,0 +1,61 @@
+/*-------------------------------------------------------------------------
+ *
+ * attnum.h--
+ *    POSTGRES attribute number definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        ATTNUM_H
+#define ATTNUM_H
+
+#include "c.h"
+
+/*
+ * user defined attribute numbers start at 1.  -ay 2/95
+ */
+typedef int16          AttrNumber;
+
+#define InvalidAttrNumber      0
+
+/* ----------------
+ *     support macros
+ * ----------------
+ */
+/*
+ * AttributeNumberIsValid --
+ *     True iff the attribute number is valid.
+ */
+#define AttributeNumberIsValid(attributeNumber) \
+    ((bool) ((attributeNumber) != InvalidAttrNumber))
+
+/*
+ * AttrNumberIsForUserDefinedAttr --
+ *     True iff the attribute number corresponds to an user defined attribute.
+ */
+#define AttrNumberIsForUserDefinedAttr(attributeNumber) \
+    ((bool) ((attributeNumber) > 0))
+
+/*
+ * AttrNumberGetAttrOffset --
+ *     Returns the attribute offset for an attribute number.
+ *
+ * Note:
+ *     Assumes the attribute number is for an user defined attribute.
+ */
+#define AttrNumberGetAttrOffset(attNum) \
+     (AssertMacro(AttrNumberIsForUserDefinedAttr(attNum)) ? \
+      ((attNum - 1)) : 0)
+
+/*
+ * AttributeOffsetGetAttributeNumber --
+ *     Returns the attribute number for an attribute offset.
+ */
+#define AttrOffsetGetAttrNumber(attributeOffset) \
+     ((AttrNumber) (1 + attributeOffset))
+
+#endif /* ATTNUM_H */
diff --git a/src/backend/access/common/Makefile.inc b/src/backend/access/common/Makefile.inc
new file mode 100644 (file)
index 0000000..cc3c408
--- /dev/null
@@ -0,0 +1,16 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for access/common
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= heaptuple.c heapvalid.c indextuple.c indexvalid.c printtup.c \
+       scankey.c tupdesc.c
+
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
new file mode 100644 (file)
index 0000000..5668311
--- /dev/null
@@ -0,0 +1,1011 @@
+/*-------------------------------------------------------------------------
+ *
+ * heaptuple.c--
+ *    This file contains heap tuple accessor and mutator routines, as well
+ *    as a few various tuple utilities.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    The old interface functions have been converted to macros
+ *    and moved to heapam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+
+#include "postgres.h"
+
+#include "access/htup.h"
+#include "access/itup.h"
+#include "access/tupmacs.h"
+#include "access/skey.h"
+#include "storage/ipc.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "access/transam.h"
+#include "storage/bufpage.h"           /* for MAXTUPLEN */
+#include "storage/itemptr.h"
+#include "utils/memutils.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+#include "utils/nabstime.h"
+
+/* this is so the sparcstation debugger works */
+
+#ifndef NO_ASSERT_CHECKING
+#ifdef sparc
+#define register
+#endif /* sparc */
+#endif /* NO_ASSERT_CHECKING */
+
+/* ----------------------------------------------------------------
+ *                     misc support routines
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     ComputeDataSize
+ * ----------------
+ */
+Size
+ComputeDataSize(TupleDesc tupleDesc,
+               Datum value[],
+               char nulls[])
+{
+    uint32 length;
+    int i;
+    int numberOfAttributes = tupleDesc->natts;
+    AttributeTupleForm *att = tupleDesc->attrs;
+    
+    for (length = 0, i = 0; i < numberOfAttributes; i++) {
+       if (nulls[i] != ' ') continue;
+           
+       switch (att[i]->attlen) {
+       case -1:
+           /*
+            * This is the size of the disk representation and so
+            * must include the additional sizeof long.
+            */
+           if (att[i]->attalign == 'd') {
+               length = DOUBLEALIGN(length)
+                   + VARSIZE(DatumGetPointer(value[i]));
+           } else {
+               length = INTALIGN(length)
+                   + VARSIZE(DatumGetPointer(value[i]));
+           }
+           break;
+       case sizeof(char):
+           length++;
+           break;
+       case sizeof(short):
+           length = SHORTALIGN(length + sizeof(short));
+           break;
+       case sizeof(int32):
+           length = INTALIGN(length + sizeof(int32));
+           break;
+       default:
+           if (att[i]->attlen < sizeof(int32))
+               elog(WARN, "ComputeDataSize: attribute %d has len %d",
+                    i, att[i]->attlen);
+           if (att[i]->attalign == 'd')
+               length = DOUBLEALIGN(length) + att[i]->attlen;
+           else
+               length = LONGALIGN(length) + att[i]->attlen;
+           break;
+       }
+    }
+    
+    return length;
+}
+
+/* ----------------
+ *     DataFill
+ * ----------------
+ */
+void
+DataFill(char *data,
+        TupleDesc tupleDesc,
+        Datum value[],
+        char nulls[],
+        char *infomask,
+        bits8 bit[])
+{
+    bits8      *bitP;
+    int                bitmask;
+    uint32     length;
+    int                i;
+    int         numberOfAttributes = tupleDesc->natts;
+    AttributeTupleForm* att = tupleDesc->attrs;
+    
+    if (bit != NULL) {
+       bitP = &bit[-1];
+       bitmask = CSIGNBIT;
+    }
+    
+    *infomask = 0;
+    
+    for (i = 0; i < numberOfAttributes; i++) {
+       if (bit != NULL) {
+           if (bitmask != CSIGNBIT) {
+               bitmask <<= 1;
+           } else {
+               bitP += 1;
+               *bitP = 0x0;
+               bitmask = 1;
+           }
+           
+           if (nulls[i] == 'n') {
+               *infomask |= HEAP_HASNULL;
+               continue;
+           }
+           
+           *bitP |= bitmask;
+       }
+           
+       switch (att[i]->attlen) {
+       case -1:
+           *infomask |= HEAP_HASVARLENA;
+           if (att[i]->attalign=='d') {
+               data = (char *) DOUBLEALIGN(data);
+           } else {
+               data = (char *) INTALIGN(data);
+           }
+           length = VARSIZE(DatumGetPointer(value[i]));
+           memmove(data, DatumGetPointer(value[i]),length);
+           data += length;
+           break;
+       case sizeof(char):
+           *data = att[i]->attbyval ?
+               DatumGetChar(value[i]) : *((char *) value[i]);
+           data += sizeof(char);
+           break;
+       case sizeof(int16):
+           data = (char *) SHORTALIGN(data);
+           * (short *) data = (att[i]->attbyval ?
+                               DatumGetInt16(value[i]) :
+                               *((short *) value[i]));
+           data += sizeof(short);
+           break;
+       case sizeof(int32):
+           data = (char *) INTALIGN(data);
+           * (int32 *) data = (att[i]->attbyval ?
+                               DatumGetInt32(value[i]) :
+                               *((int32 *) value[i]));
+           data += sizeof(int32);
+           break;
+       default:
+           if (att[i]->attlen < sizeof(int32))
+               elog(WARN, "DataFill: attribute %d has len %d",
+                    i, att[i]->attlen);
+           if (att[i]->attalign == 'd') {
+               data = (char *) DOUBLEALIGN(data);
+               memmove(data, DatumGetPointer(value[i]),
+                       att[i]->attlen);
+               data += att[i]->attlen;
+           } else {
+               data = (char *) LONGALIGN(data);
+               memmove(data, DatumGetPointer(value[i]),
+                       att[i]->attlen);
+               data += att[i]->attlen;
+           }
+                   
+       }
+    }
+}
+
+/* ----------------------------------------------------------------
+ *                     heap tuple interface
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     heap_attisnull  - returns 1 iff tuple attribute is not present
+ * ----------------
+ */
+int
+heap_attisnull(HeapTuple tup, int attnum)
+{
+    if (attnum > (int)tup->t_natts)
+       return (1);
+    
+    if (HeapTupleNoNulls(tup)) return(0);
+    
+    if (attnum > 0) {
+       return(att_isnull(attnum - 1, tup->t_bits));
+    } else
+       switch (attnum) {
+       case SelfItemPointerAttributeNumber:
+       case ObjectIdAttributeNumber:
+       case MinTransactionIdAttributeNumber:
+       case MinCommandIdAttributeNumber:
+       case MaxTransactionIdAttributeNumber:
+       case MaxCommandIdAttributeNumber:
+       case ChainItemPointerAttributeNumber:
+       case AnchorItemPointerAttributeNumber:
+       case MinAbsoluteTimeAttributeNumber:
+       case MaxAbsoluteTimeAttributeNumber:
+       case VersionTypeAttributeNumber:
+           break;
+           
+       case 0:
+           elog(WARN, "heap_attisnull: zero attnum disallowed");
+           
+       default:
+           elog(WARN, "heap_attisnull: undefined negative attnum");
+       }
+    
+    return (0);
+}
+
+/* ----------------------------------------------------------------
+ *              system attribute heap tuple support
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     heap_sysattrlen
+ *
+ *     This routine returns the length of a system attribute.
+ * ----------------
+ */
+int
+heap_sysattrlen(AttrNumber attno)
+{
+    HeapTupleData      *f = NULL;
+    int                        len;
+
+    switch (attno) {
+    case SelfItemPointerAttributeNumber:
+       len = sizeof f->t_ctid;
+       break;
+    case ObjectIdAttributeNumber:
+       len = sizeof f->t_oid;
+       break;
+    case MinTransactionIdAttributeNumber:
+       len = sizeof f->t_xmin;
+       break;
+    case MinCommandIdAttributeNumber:
+       len = sizeof f->t_cmin;
+       break;
+    case MaxTransactionIdAttributeNumber:
+       len = sizeof f->t_xmax;
+       break;
+    case MaxCommandIdAttributeNumber:
+       len = sizeof f->t_cmax;
+       break;
+    case ChainItemPointerAttributeNumber:
+       len = sizeof f->t_chain;
+       break;
+    case AnchorItemPointerAttributeNumber:
+       elog(WARN, "heap_sysattrlen: field t_anchor does not exist!");
+       break;
+    case MinAbsoluteTimeAttributeNumber:
+       len = sizeof f->t_tmin;
+       break;
+    case MaxAbsoluteTimeAttributeNumber:
+       len = sizeof f->t_tmax;
+       break;
+    case VersionTypeAttributeNumber:
+       len = sizeof f->t_vtype;
+       break;
+    default:
+       elog(WARN, "sysattrlen: System attribute number %d unknown.",
+            attno);
+       len = 0;
+       break;
+    }
+    return (len);
+}
+
+/* ----------------
+ *     heap_sysattrbyval
+ *
+ *     This routine returns the "by-value" property of a system attribute.
+ * ----------------
+ */
+bool
+heap_sysattrbyval(AttrNumber attno)
+{
+    bool               byval;
+    
+    switch (attno) {
+    case SelfItemPointerAttributeNumber:
+       byval = false;
+       break;
+    case ObjectIdAttributeNumber:
+       byval = true;
+       break;
+    case MinTransactionIdAttributeNumber:
+       byval = true;
+       break;
+    case MinCommandIdAttributeNumber:
+       byval = true;
+       break;
+    case MaxTransactionIdAttributeNumber:
+       byval = true;
+       break;
+    case MaxCommandIdAttributeNumber:
+       byval = true;
+       break;
+    case ChainItemPointerAttributeNumber:
+       byval = false;
+       break;
+    case AnchorItemPointerAttributeNumber:
+       byval = false;
+       break;
+    case MinAbsoluteTimeAttributeNumber:
+       byval = true;
+       break;
+    case MaxAbsoluteTimeAttributeNumber:
+       byval = true;
+       break;
+    case VersionTypeAttributeNumber:
+       byval = true;
+       break;
+    default:
+       byval = true;
+       elog(WARN, "sysattrbyval: System attribute number %d unknown.",
+            attno);
+       break;
+    }
+    
+    return byval;
+}
+
+/* ----------------
+ *     heap_getsysattr
+ * ----------------
+ */
+char *
+heap_getsysattr(HeapTuple tup, Buffer b, int attnum)
+{
+    switch (attnum) {
+    case SelfItemPointerAttributeNumber:
+       return ((char *)&tup->t_ctid);
+    case ObjectIdAttributeNumber:
+       return ((char *) (long) tup->t_oid);
+    case MinTransactionIdAttributeNumber:
+       return ((char *) (long) tup->t_xmin);
+    case MinCommandIdAttributeNumber:
+       return ((char *) (long) tup->t_cmin);
+    case MaxTransactionIdAttributeNumber:
+       return ((char *) (long) tup->t_xmax);
+    case MaxCommandIdAttributeNumber:
+       return ((char *) (long) tup->t_cmax);
+    case ChainItemPointerAttributeNumber:
+       return ((char *) &tup->t_chain);
+    case AnchorItemPointerAttributeNumber:
+       elog(WARN, "heap_getsysattr: t_anchor does not exist!");
+       break;
+       
+       /*
+        *  For tmin and tmax, we need to do some extra work.  These don't
+        *  get filled in until the vacuum cleaner runs (or we manage to flush
+        *  a page after setting the value correctly below).  If the vacuum
+        *  cleaner hasn't run yet, then the times stored in the tuple are
+        *  wrong, and we need to look up the commit time of the transaction.
+        *  We cache this value in the tuple to avoid doing the work more than
+        *  once.
+        */
+       
+    case MinAbsoluteTimeAttributeNumber:
+       if (!AbsoluteTimeIsBackwardCompatiblyValid(tup->t_tmin) &&
+           TransactionIdDidCommit(tup->t_xmin))
+           tup->t_tmin = TransactionIdGetCommitTime(tup->t_xmin);
+       return ((char *) (long) tup->t_tmin);
+    case MaxAbsoluteTimeAttributeNumber:
+       if (!AbsoluteTimeIsBackwardCompatiblyReal(tup->t_tmax)) {
+           if (TransactionIdDidCommit(tup->t_xmax))
+               tup->t_tmax = TransactionIdGetCommitTime(tup->t_xmax);
+           else
+               tup->t_tmax = CURRENT_ABSTIME;
+       }
+       return ((char *) (long) tup->t_tmax);
+    case VersionTypeAttributeNumber:
+       return ((char *) (long) tup->t_vtype);
+    default:
+       elog(WARN, "heap_getsysattr: undefined attnum %d", attnum);
+    }
+    return(NULL);
+}
+
+/* ----------------
+ *     fastgetattr
+ *
+ *     This is a newer version of fastgetattr which attempts to be
+ *     faster by caching attribute offsets in the attribute descriptor.
+ *
+ *     an alternate way to speed things up would be to cache offsets
+ *     with the tuple, but that seems more difficult unless you take
+ *     the storage hit of actually putting those offsets into the
+ *     tuple you send to disk.  Yuck.
+ *
+ *     This scheme will be slightly slower than that, but should
+ *     preform well for queries which hit large #'s of tuples.  After
+ *     you cache the offsets once, examining all the other tuples using
+ *     the same attribute descriptor will go much quicker. -cim 5/4/91
+ * ----------------
+ */
+char *
+fastgetattr(HeapTuple tup,
+           int attnum,
+           TupleDesc tupleDesc,
+           bool *isnull)
+{
+    char *tp;          /* ptr to att in tuple */
+    bits8  *bp;                /* ptr to att in tuple */
+    int slow;          /* do we have to walk nulls? */
+    AttributeTupleForm *att = tupleDesc->attrs;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    
+    Assert(PointerIsValid(isnull));
+    Assert(attnum > 0);
+    
+    /* ----------------
+     *   Three cases:
+     * 
+     *   1: No nulls and no variable length attributes.
+     *   2: Has a null or a varlena AFTER att.
+     *   3: Has nulls or varlenas BEFORE att.
+     * ----------------
+     */
+    
+    *isnull =  false;
+    
+    if (HeapTupleNoNulls(tup)) {
+       attnum--;
+       if (att[attnum]->attcacheoff > 0) {
+           return (char *)
+               fetchatt( &(att[attnum]),
+                        (char *)tup + tup->t_hoff + att[attnum]->attcacheoff);
+       } else if (attnum == 0) {
+           /*
+            * first attribute is always at position zero
+            */
+           return((char *) fetchatt(&(att[0]), (char *) tup + tup->t_hoff));
+       }
+           
+       tp = (char *) tup + tup->t_hoff;
+           
+       slow = 0;
+    } else {
+       /*
+        * there's a null somewhere in the tuple
+        */
+
+       bp = tup->t_bits;
+       tp = (char *) tup + tup->t_hoff;
+       slow = 0;
+       attnum--;
+           
+       /* ----------------
+        *      check to see if desired att is null
+        * ----------------
+        */
+       
+       if (att_isnull(attnum, bp)) {
+           *isnull = true;
+           return NULL;
+       }
+
+       /* ----------------
+        *      Now check to see if any preceeding bits are null...
+        * ----------------
+        */
+       
+       {
+           register int  i = 0; /* current offset in bp */
+               
+           for (i = 0; i < attnum && !slow; i++) {
+               if (att_isnull(i, bp)) slow = 1;
+           }
+       }
+    }
+    
+    /*
+     * now check for any non-fixed length attrs before our attribute
+     */
+    if (!slow) {
+       if (att[attnum]->attcacheoff > 0) {
+           return (char *)
+               fetchatt(&(att[attnum]),
+                        tp + att[attnum]->attcacheoff);
+       } else if (attnum == 0) {
+           return (char *)
+               fetchatt(&(att[0]), (char *) tup + tup->t_hoff);
+       } else if (!HeapTupleAllFixed(tup)) {
+           register int j = 0;
+                   
+           for (j = 0; j < attnum && !slow; j++)
+               if (att[j]->attlen < 1) slow = 1;
+       }
+    }
+    
+    /*
+     * if slow is zero, and we got here, we know that we have a tuple with
+     * no nulls.  We also have to initialize the remainder of
+     * the attribute cached offset values.
+     */
+    if (!slow) {
+       register int j = 1;
+       register long off;
+           
+       /*
+        * need to set cache for some atts
+        */
+           
+       att[0]->attcacheoff = 0;
+       
+       while (att[j]->attcacheoff > 0) j++;
+       
+       off = att[j-1]->attcacheoff + att[j-1]->attlen;
+       
+       for (; j < attnum + 1; j++) {
+           switch(att[j]->attlen) {
+           case -1:
+               off = (att[j]->attalign=='d') ?
+                   DOUBLEALIGN(off) : INTALIGN(off);
+               break;
+           case sizeof(char):
+               break;
+           case sizeof(short):
+               off = SHORTALIGN(off);
+               break;
+           case sizeof(int32):
+               off = INTALIGN(off);
+               break;
+           default:
+               if (att[j]->attlen < sizeof(int32)) {
+                   elog(WARN,
+                        "fastgetattr: attribute %d has len %d",
+                        j, att[j]->attlen);
+               }
+               if (att[j]->attalign == 'd')
+                   off = DOUBLEALIGN(off);
+               else
+                   off = LONGALIGN(off);
+               break;
+           }
+                   
+           att[j]->attcacheoff = off;
+           off += att[j]->attlen;
+       }
+       
+       return
+           (char *)fetchatt(&(att[attnum]), tp + att[attnum]->attcacheoff);
+    } else {
+       register bool usecache = true;
+       register int off = 0;
+       register int i;
+       
+       /*
+        * Now we know that we have to walk the tuple CAREFULLY.
+        *
+        * Note - This loop is a little tricky.  On iteration i we
+        * first set the offset for attribute i and figure out how much
+        * the offset should be incremented.  Finally, we need to align the
+        * offset based on the size of attribute i+1 (for which the offset
+        * has been computed). -mer 12 Dec 1991
+        */
+       
+       for (i = 0; i < attnum; i++) {
+           if (!HeapTupleNoNulls(tup)) {
+               if (att_isnull(i, bp)) {
+                   usecache = false;
+                   continue;
+               }
+           }
+           switch (att[i]->attlen) {
+           case -1:
+               off = (att[i]->attalign=='d') ?
+                   DOUBLEALIGN(off) : INTALIGN(off);
+               break;
+           case sizeof(char):
+               break;
+           case sizeof(short):
+               off = SHORTALIGN(off);
+               break;
+           case sizeof(int32):
+               off = INTALIGN(off);
+               break;
+           default:
+               if (att[i]->attlen < sizeof(int32))
+                   elog(WARN,
+                        "fastgetattr2: attribute %d has len %d",
+                        i, att[i]->attlen);
+               if (att[i]->attalign == 'd')
+                   off = DOUBLEALIGN(off);
+               else
+                   off = LONGALIGN(off);
+               break;
+           }
+           if (usecache && att[i]->attcacheoff > 0) {
+               off = att[i]->attcacheoff;
+               if (att[i]->attlen == -1) {
+                   usecache = false;
+               }
+           } else {
+               if (usecache) att[i]->attcacheoff = off;
+           }
+                   
+           switch(att[i]->attlen) {
+           case sizeof(char):
+               off++;
+               break;
+           case sizeof(int16):
+               off += sizeof(int16);
+               break;
+           case sizeof(int32):
+               off += sizeof(int32);
+               break;
+           case -1:
+               usecache = false;
+               off += VARSIZE(tp + off);
+               break;
+           default:
+               off += att[i]->attlen;
+               break;
+           }
+       }
+       switch (att[attnum]->attlen) {
+       case -1:
+           off = (att[attnum]->attalign=='d')?
+               DOUBLEALIGN(off) : INTALIGN(off);
+           break;
+       case sizeof(char):
+           break;
+       case sizeof(short):
+           off = SHORTALIGN(off);
+           break;
+       case sizeof(int32):
+           off = INTALIGN(off);
+           break;
+       default:
+           if (att[attnum]->attlen < sizeof(int32))
+               elog(WARN, "fastgetattr3: attribute %d has len %d",
+                    attnum, att[attnum]->attlen);
+           if (att[attnum]->attalign == 'd')
+               off = DOUBLEALIGN(off);
+           else
+               off = LONGALIGN(off);
+           break;
+       }
+       return((char *) fetchatt(&(att[attnum]), tp + off));
+    }
+}
+
+/* ----------------
+ *     heap_getattr
+ *
+ *     returns an attribute from a heap tuple.  uses 
+ * ----------------
+ */
+char *
+heap_getattr(HeapTuple tup,
+            Buffer b,
+            int attnum,
+            TupleDesc tupleDesc,
+            bool *isnull)
+{
+    bool       localIsNull;
+
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(tup != NULL);
+    
+    if (! PointerIsValid(isnull))
+       isnull = &localIsNull;
+    
+    if (attnum > (int) tup->t_natts) {
+       *isnull = true;
+       return ((char *) NULL);
+    }
+    
+    /* ----------------
+     * take care of user defined attributes
+     * ----------------
+     */
+    if (attnum > 0) {
+       char  *datum;
+       datum = fastgetattr(tup, attnum, tupleDesc, isnull);
+       
+       return (datum);
+    }
+    
+    /* ----------------
+     * take care of system attributes
+     * ----------------
+     */
+    *isnull = false;
+    return
+       heap_getsysattr(tup, b, attnum);
+}
+
+/* ----------------
+ *     heap_copytuple
+ *
+ *     returns a copy of an entire tuple
+ * ----------------
+ */
+HeapTuple
+heap_copytuple(HeapTuple tuple)
+{
+    HeapTuple  newTuple;
+
+    if (! HeapTupleIsValid(tuple))
+       return (NULL);
+    
+    /* XXX For now, just prevent an undetectable executor related error */
+    if (tuple->t_len > MAXTUPLEN) {
+       elog(WARN, "palloctup: cannot handle length %d tuples",
+            tuple->t_len);
+    }
+    
+    newTuple = (HeapTuple) palloc(tuple->t_len);
+    memmove((char *) newTuple, (char *) tuple, (int) tuple->t_len);
+    return(newTuple);
+}
+
+/* ----------------
+ *     heap_deformtuple
+ *
+ *     the inverse of heap_formtuple (see below)
+ * ----------------
+ */
+void
+heap_deformtuple(HeapTuple tuple,
+                TupleDesc tdesc,
+                Datum values[],
+                char nulls[])
+{
+    int i;
+    int natts;
+    
+    Assert(HeapTupleIsValid(tuple));
+    
+    natts = tuple->t_natts;
+    for (i = 0; i<natts; i++) {
+       bool isnull;
+           
+       values[i] = (Datum)heap_getattr(tuple,
+                                       InvalidBuffer,
+                                       i+1,
+                                       tdesc,
+                                       &isnull);
+       if (isnull)
+           nulls[i] = 'n';
+       else
+           nulls[i] = ' ';
+    }
+}
+
+/* ----------------
+ *     heap_formtuple 
+ *
+ *     constructs a tuple from the given value[] and null[] arrays
+ *
+ * old comments
+ *     Handles alignment by aligning 2 byte attributes on short boundries
+ *     and 3 or 4 byte attributes on long word boundries on a vax; and
+ *     aligning non-byte attributes on short boundries on a sun.  Does
+ *     not properly align fixed length arrays of 1 or 2 byte types (yet).
+ *
+ *     Null attributes are indicated by a 'n' in the appropriate byte
+ *     of the null[].  Non-null attributes are indicated by a ' ' (space).
+ *
+ *     Fix me.  (Figure that must keep context if debug--allow give oid.)
+ *     Assumes in order.
+ * ----------------
+ */
+HeapTuple
+heap_formtuple(TupleDesc tupleDescriptor,
+              Datum value[],
+              char nulls[])
+{
+    char       *tp;    /* tuple pointer */
+    HeapTuple  tuple;  /* return tuple */
+    int                bitmaplen;
+    long       len;
+    int                hoff;
+    bool       hasnull = false;
+    int                i;
+    int         numberOfAttributes = tupleDescriptor->natts;    
+
+    len = sizeof *tuple - sizeof tuple->t_bits;
+    
+    for (i = 0; i < numberOfAttributes && !hasnull; i++) {
+       if (nulls[i] != ' ') hasnull = true;
+    }
+    
+    if (numberOfAttributes > MaxHeapAttributeNumber)
+       elog(WARN, "heap_formtuple: numberOfAttributes of %d > %d",
+            numberOfAttributes, MaxHeapAttributeNumber);
+    
+    if (hasnull) {
+       bitmaplen = BITMAPLEN(numberOfAttributes);
+       len       += bitmaplen;
+    }
+
+    hoff = len = DOUBLEALIGN(len);     /* be conservative here */
+
+    len += ComputeDataSize(tupleDescriptor, value, nulls);
+    
+    tp = (char *) palloc(len);
+    tuple = (HeapTuple) tp;
+
+    memset(tp, 0, (int)len);
+    
+    tuple->t_len =     len;
+    tuple->t_natts =   numberOfAttributes;
+    tuple->t_hoff = hoff;
+    tuple->t_tmin = INVALID_ABSTIME;
+    tuple->t_tmax = CURRENT_ABSTIME;
+    
+    DataFill((char *)tuple + tuple->t_hoff,
+            tupleDescriptor,
+            value,
+            nulls,
+             &tuple->t_infomask,
+            (hasnull ? tuple->t_bits : NULL));
+    
+    return (tuple);
+}
+
+/* ----------------
+ *     heap_modifytuple
+ *
+ *     forms a new tuple from an old tuple and a set of replacement values.
+ * ----------------
+ */
+HeapTuple
+heap_modifytuple(HeapTuple tuple,
+                Buffer buffer,
+                Relation relation,
+                Datum replValue[],
+                char replNull[],
+                char repl[])
+{
+    int                attoff;
+    int                numberOfAttributes;
+    Datum      *value;
+    char       *nulls;
+    bool       isNull;
+    HeapTuple  newTuple;
+    int                madecopy;
+    uint8      infomask;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(HeapTupleIsValid(tuple));
+    Assert(BufferIsValid(buffer) || RelationIsValid(relation));
+    Assert(HeapTupleIsValid(tuple));
+    Assert(PointerIsValid(replValue));
+    Assert(PointerIsValid(replNull));
+    Assert(PointerIsValid(repl));
+    
+    /* ----------------
+     * if we're pointing to a disk page, then first
+     *  make a copy of our tuple so that all the attributes
+     *  are available.  XXX this is inefficient -cim
+     * ----------------
+     */
+    madecopy = 0;
+    if (BufferIsValid(buffer) == true) {
+       relation =      (Relation) BufferGetRelation(buffer);
+       tuple =         heap_copytuple(tuple);
+       madecopy = 1;
+    }
+    
+    numberOfAttributes = RelationGetRelationTupleForm(relation)->relnatts;
+    
+    /* ----------------
+     * allocate and fill value[] and nulls[] arrays from either
+     *  the tuple or the repl information, as appropriate.
+     * ----------------
+     */
+    value = (Datum *)  palloc(numberOfAttributes * sizeof *value);
+    nulls =  (char *)  palloc(numberOfAttributes * sizeof *nulls);
+    
+    for (attoff = 0;
+        attoff < numberOfAttributes;
+        attoff += 1) {
+       
+       if (repl[attoff] == ' ') {
+           char *attr;
+
+           attr =
+               heap_getattr(tuple,
+                            InvalidBuffer, 
+                            AttrOffsetGetAttrNumber(attoff),
+                            RelationGetTupleDescriptor(relation),
+                            &isNull) ;
+           value[attoff] = PointerGetDatum(attr);
+           nulls[attoff] = (isNull) ? 'n' : ' ';
+           
+       } else if (repl[attoff] != 'r') {
+           elog(WARN, "heap_modifytuple: repl is \\%3d", repl[attoff]);
+           
+       } else { /* == 'r' */
+           value[attoff] = replValue[attoff];
+           nulls[attoff] =  replNull[attoff];
+       }
+    }
+    
+    /* ----------------
+     * create a new tuple from the values[] and nulls[] arrays
+     * ----------------
+     */
+    newTuple = heap_formtuple(RelationGetTupleDescriptor(relation),
+                             value,
+                             nulls);
+    
+    /* ----------------
+     * copy the header except for t_len, t_natts, t_hoff, t_bits, t_infomask
+     * ----------------
+     */
+    infomask = newTuple->t_infomask;
+    memmove((char *) &newTuple->t_ctid,        /*XXX*/
+           (char *) &tuple->t_ctid,
+           ((char *) &tuple->t_hoff - (char *) &tuple->t_ctid)); /*XXX*/
+    newTuple->t_infomask = infomask;
+    newTuple->t_natts = numberOfAttributes;    /* fix t_natts just in case */
+    
+    /* ----------------
+     * if we made a copy of the tuple, then free it.
+     * ----------------
+     */
+    if (madecopy)
+       pfree(tuple);
+    
+    return
+       newTuple;
+}
+
+/* ----------------------------------------------------------------
+ *                     other misc functions
+ * ----------------------------------------------------------------
+ */
+
+HeapTuple
+heap_addheader(uint32 natts,   /* max domain index */
+              int structlen,   /* its length */
+              char *structure) /* pointer to the struct */
+{
+    register char      *tp;    /* tuple data pointer */
+    HeapTuple          tup;
+    long               len;
+    int                        hoff;
+    
+    AssertArg(natts > 0);
+    
+    len = sizeof (HeapTupleData) - sizeof (tup->t_bits);
+    
+    hoff = len = DOUBLEALIGN(len);     /* be conservative */
+    len += structlen;
+    tp = (char *) palloc(len);
+    tup = (HeapTuple) tp;
+    memset((char*)tup, 0, len);
+    
+    tup->t_len = (short) len;                  /* XXX */
+    tp += tup->t_hoff = hoff;
+    tup->t_natts = natts;
+    tup->t_infomask = 0;
+    
+    memmove(tp, structure, structlen);
+    
+    return (tup);
+}
diff --git a/src/backend/access/common/heapvalid.c b/src/backend/access/common/heapvalid.c
new file mode 100644 (file)
index 0000000..c806242
--- /dev/null
@@ -0,0 +1,134 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapvalid.c--
+ *    heap tuple qualification validity checking code
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+
+#include "access/htup.h"
+#include "access/skey.h"
+#include "access/heapam.h"
+#include "utils/tqual.h"
+#include "access/valid.h"      /* where the declarations go */
+#include "access/xact.h"
+
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+#include "storage/itemid.h"
+#include "fmgr.h"
+#include "utils/elog.h"
+#include "utils/rel.h"
+
+/* ----------------
+ *     heap_keytest
+ *
+ *     Test a heap tuple with respect to a scan key.
+ * ----------------
+ */
+bool
+heap_keytest(HeapTuple t,
+            TupleDesc tupdesc,
+            int nkeys,
+            ScanKey keys)
+{
+    bool       isnull;
+    Datum      atp;
+    int                test;
+    
+    for (; nkeys--; keys++) {
+       atp = (Datum)heap_getattr(t, InvalidBuffer,
+                                 keys->sk_attno, 
+                                 tupdesc,
+                                 &isnull);
+       
+       if (isnull)
+           /* XXX eventually should check if SK_ISNULL */
+           return false;
+       
+       if (keys->sk_flags & SK_COMMUTE)
+           test = (long) FMGR_PTR2(keys->sk_func, keys->sk_procedure,
+                                   keys->sk_argument, atp);
+       else
+           test = (long) FMGR_PTR2(keys->sk_func, keys->sk_procedure,
+                                   atp, keys->sk_argument);
+       
+       if (!test == !(keys->sk_flags & SK_NEGATE))
+           return false;
+    }
+    
+    return true;
+}
+
+/* ----------------
+ *     heap_tuple_satisfies
+ *
+ *  Returns a valid HeapTuple if it satisfies the timequal and keytest.
+ *  Returns NULL otherwise.  Used to be heap_satisifies (sic) which
+ *  returned a boolean.  It now returns a tuple so that we can avoid doing two
+ *  PageGetItem's per tuple.
+ *
+ *     Complete check of validity including LP_CTUP and keytest.
+ *     This should perhaps be combined with valid somehow in the
+ *     future.  (Also, additional rule tests/time range tests.)
+ *
+ *  on 8/21/92 mao says:  i rearranged the tests here to do keytest before
+ *  SatisfiesTimeQual.  profiling indicated that even for vacuumed relations,
+ *  time qual checking was more expensive than key testing.  time qual is
+ *  least likely to fail, too.  we should really add the time qual test to
+ *  the restriction and optimize it in the normal way.  this has interactions
+ *  with joey's expensive function work.
+ * ----------------
+ */
+HeapTuple
+heap_tuple_satisfies(ItemId itemId,
+                    Relation relation,
+                    PageHeader disk_page,
+                    TimeQual   qual,
+                    int nKeys,
+                    ScanKey key)
+{
+    HeapTuple  tuple;
+    bool res;
+    
+    if (! ItemIdIsUsed(itemId))
+       return NULL;
+    
+    tuple = (HeapTuple) PageGetItem((Page) disk_page, itemId);
+    
+    if (key != NULL)
+       res = heap_keytest(tuple, RelationGetTupleDescriptor(relation), 
+                          nKeys, key);
+    else
+       res = TRUE;
+    
+    if (res && (relation->rd_rel->relkind == RELKIND_UNCATALOGED
+               || HeapTupleSatisfiesTimeQual(tuple,qual)))
+       return tuple;
+    
+    return (HeapTuple) NULL;
+}
+
+/*
+ *  TupleUpdatedByCurXactAndCmd() -- Returns true if this tuple has
+ *     already been updated once by the current transaction/command
+ *     pair.
+ */
+bool
+TupleUpdatedByCurXactAndCmd(HeapTuple t)
+{
+    if (TransactionIdEquals(t->t_xmax,
+                           GetCurrentTransactionId()) &&
+       t->t_cmax == GetCurrentCommandId())
+       return true;
+    
+    return false;
+}
diff --git a/src/backend/access/common/indextuple.c b/src/backend/access/common/indextuple.c
new file mode 100644 (file)
index 0000000..ea38757
--- /dev/null
@@ -0,0 +1,427 @@
+/*-------------------------------------------------------------------------
+ *
+ * indextuple.c--
+ *     This file contains index tuple accessor and mutator routines,
+ *     as well as a few various tuple utilities.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+
+#include "c.h"
+#include "access/ibit.h"
+#include "access/itup.h"       /* where the declarations go */
+#include "access/heapam.h"
+#include "access/genam.h"      
+#include "access/tupdesc.h"
+#include "access/tupmacs.h"
+
+#include "storage/itemptr.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+static Size IndexInfoFindDataOffset(unsigned short t_info);
+
+/* ----------------------------------------------------------------
+ *               index_ tuple interface routines
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     index_formtuple
+ * ----------------
+ */
+IndexTuple
+index_formtuple(TupleDesc tupleDescriptor,
+               Datum value[],
+               char null[])
+{
+    register char      *tp;    /* tuple pointer */
+    IndexTuple         tuple;  /* return tuple */
+    Size               size, hoff;
+    int                i;
+    unsigned short      infomask = 0;
+    bool               hasnull = false;
+    char               tupmask = 0;
+    int                 numberOfAttributes = tupleDescriptor->natts;
+    
+    if (numberOfAttributes > MaxIndexAttributeNumber)
+       elog(WARN, "index_formtuple: numberOfAttributes of %d > %d",
+            numberOfAttributes, MaxIndexAttributeNumber);
+    
+    
+    for (i = 0; i < numberOfAttributes && !hasnull; i++) {
+       if (null[i] != ' ') hasnull = true;
+    }
+    
+    if (hasnull) infomask |= INDEX_NULL_MASK;
+    
+    hoff = IndexInfoFindDataOffset(infomask);
+    size = hoff
+       + ComputeDataSize(tupleDescriptor,
+                         value, null);
+    size = DOUBLEALIGN(size);  /* be conservative */
+    
+    tp = (char *) palloc(size);
+    tuple = (IndexTuple) tp;
+    memset(tp,0,(int)size);
+    
+    DataFill((char *)tp + hoff,
+            tupleDescriptor,
+            value,
+            null,
+            &tupmask,
+            (hasnull ? (bits8*)tp + sizeof(*tuple) : NULL));
+    
+    /*
+     * We do this because DataFill wants to initialize a "tupmask" which
+     * is used for HeapTuples, but we want an indextuple infomask.  The only
+     * "relevent" info is the "has variable attributes" field, which is in
+     * mask position 0x02.  We have already set the null mask above.
+     */
+    
+    if (tupmask & 0x02) infomask |= INDEX_VAR_MASK;
+    
+    /*
+     * Here we make sure that we can actually hold the size.  We also want
+     * to make sure that size is not aligned oddly.  This actually is a
+     * rather odd way to make sure the size is not too large overall.
+     */
+    
+    if (size & 0xE000)
+       elog(WARN, "index_formtuple: data takes %d bytes: too big", size);
+
+    
+    infomask |= size;
+    
+    /* ----------------
+     * initialize metadata
+     * ----------------
+     */
+    tuple->t_info = infomask;
+    return (tuple);
+}
+
+/* ----------------
+ *     fastgetiattr
+ *
+ *     This is a newer version of fastgetiattr which attempts to be
+ *     faster by caching attribute offsets in the attribute descriptor.
+ *
+ *     an alternate way to speed things up would be to cache offsets
+ *     with the tuple, but that seems more difficult unless you take
+ *     the storage hit of actually putting those offsets into the
+ *     tuple you send to disk.  Yuck.
+ *
+ *     This scheme will be slightly slower than that, but should
+ *     preform well for queries which hit large #'s of tuples.  After
+ *     you cache the offsets once, examining all the other tuples using
+ *     the same attribute descriptor will go much quicker. -cim 5/4/91
+ * ----------------
+ */
+char *
+fastgetiattr(IndexTuple tup,
+            int attnum,
+            TupleDesc tupleDesc,
+            bool *isnull)
+{
+    register char              *tp;            /* ptr to att in tuple */
+    register char              *bp;            /* ptr to att in tuple */
+    int                        slow;           /* do we have to walk nulls? */
+    register int               data_off;       /* tuple data offset */
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    
+    Assert(PointerIsValid(isnull));
+    Assert(attnum > 0);
+    
+    /* ----------------
+     *   Three cases:
+     * 
+     *   1: No nulls and no variable length attributes.
+     *   2: Has a null or a varlena AFTER att.
+     *   3: Has nulls or varlenas BEFORE att.
+     * ----------------
+     */
+    
+    *isnull =  false;
+    data_off = IndexTupleHasMinHeader(tup) ? sizeof *tup : 
+       IndexInfoFindDataOffset(tup->t_info);
+    
+    if (IndexTupleNoNulls(tup)) {
+       
+       /* first attribute is always at position zero */
+       
+       if (attnum == 1) {
+           return(fetchatt(&(tupleDesc->attrs[0]), (char *) tup + data_off));
+       }
+       attnum--;
+       
+       if (tupleDesc->attrs[attnum]->attcacheoff > 0) {
+           return(fetchatt(&(tupleDesc->attrs[attnum]),
+                           (char *) tup + data_off + 
+                           tupleDesc->attrs[attnum]->attcacheoff));
+       }
+       
+       tp = (char *) tup + data_off;
+       
+       slow = 0;
+    }else { /* there's a null somewhere in the tuple */
+       
+       bp = (char *) tup + sizeof(*tup); /* "knows" t_bits are here! */
+       slow = 0;
+       /* ----------------
+        *      check to see if desired att is null
+        * ----------------
+        */
+       
+       attnum--;
+       {
+           if (att_isnull(attnum, bp)) {
+               *isnull = true;
+               return NULL;
+           }
+       }
+       /* ----------------
+        *      Now check to see if any preceeding bits are null...
+        * ----------------
+        */
+       {
+           register int  i = 0; /* current offset in bp */
+           register int  mask;  /* bit in byte we're looking at */
+           register char n;     /* current byte in bp */
+           register int byte, finalbit;
+           
+           byte = attnum >> 3;
+           finalbit = attnum & 0x07;
+           
+           for (; i <= byte; i++) {
+               n = bp[i];
+               if (i < byte) {
+                   /* check for nulls in any "earlier" bytes */
+                   if ((~n) != 0) {
+                       slow++;
+                       break;
+                   }
+               } else {
+                   /* check for nulls "before" final bit of last byte*/
+                   mask = (finalbit << 1) - 1;
+                   if ((~n) & mask)
+                       slow++;
+               }
+           }
+       }
+       tp = (char *) tup + data_off;
+    }
+    
+    /* now check for any non-fixed length attrs before our attribute */
+    
+    if (!slow) {
+       if (tupleDesc->attrs[attnum]->attcacheoff > 0) {
+           return(fetchatt(&(tupleDesc->attrs[attnum]), 
+                           tp + tupleDesc->attrs[attnum]->attcacheoff));
+       }else if (!IndexTupleAllFixed(tup)) {
+           register int j = 0;
+           
+           for (j = 0; j < attnum && !slow; j++)
+               if (tupleDesc->attrs[j]->attlen < 1) slow = 1;
+       }
+    }
+    
+    /*
+     * if slow is zero, and we got here, we know that we have a tuple with
+     * no nulls.  We also know that we have to initialize the remainder of
+     * the attribute cached offset values.
+     */
+    
+    if (!slow) {
+       register int j = 1;
+       register long off;
+       
+       /*
+        * need to set cache for some atts
+        */
+       
+       tupleDesc->attrs[0]->attcacheoff = 0;
+       
+       while (tupleDesc->attrs[j]->attcacheoff > 0) j++;
+       
+       off = tupleDesc->attrs[j-1]->attcacheoff + 
+             tupleDesc->attrs[j-1]->attlen;
+       
+       for (; j < attnum + 1; j++) {
+           /*
+            * Fix me when going to a machine with more than a four-byte
+            * word!
+            */
+           
+           switch(tupleDesc->attrs[j]->attlen)
+               {
+               case -1:
+                   off = (tupleDesc->attrs[j]->attalign=='d')?
+                       DOUBLEALIGN(off):INTALIGN(off);
+                   break;
+               case sizeof(char):
+                   break;
+               case sizeof(short):
+                   off = SHORTALIGN(off);
+                   break;
+               case sizeof(int32):
+                   off = INTALIGN(off);
+                   break;
+               default:
+                   if (tupleDesc->attrs[j]->attlen > sizeof(int32))
+                       off = (tupleDesc->attrs[j]->attalign=='d')?
+                           DOUBLEALIGN(off) : LONGALIGN(off);
+                   else
+                       elog(WARN, "fastgetiattr: attribute %d has len %d",
+                            j, tupleDesc->attrs[j]->attlen);
+                   break;
+                   
+               }
+           
+           tupleDesc->attrs[j]->attcacheoff = off;
+           off += tupleDesc->attrs[j]->attlen;
+       }
+       
+       return(fetchatt( &(tupleDesc->attrs[attnum]), 
+                       tp + tupleDesc->attrs[attnum]->attcacheoff));
+    }else {
+       register bool usecache = true;
+       register int off = 0;
+       register int i;
+       
+       /*
+        * Now we know that we have to walk the tuple CAREFULLY.
+        */
+       
+       for (i = 0; i < attnum; i++) {
+           if (!IndexTupleNoNulls(tup)) {
+               if (att_isnull(i, bp)) {
+                   usecache = false;
+                   continue;
+               }
+           }
+               
+           if (usecache && tupleDesc->attrs[i]->attcacheoff > 0) {
+               off = tupleDesc->attrs[i]->attcacheoff;
+               if (tupleDesc->attrs[i]->attlen == -1) 
+                   usecache = false;
+               else
+                   continue;
+           }
+                   
+           if (usecache) tupleDesc->attrs[i]->attcacheoff = off;
+           switch(tupleDesc->attrs[i]->attlen)
+               {
+               case sizeof(char):
+                   off++;
+                   break;
+               case sizeof(short):
+                   off = SHORTALIGN(off) + sizeof(short);
+                   break;
+               case -1:
+                   usecache = false;
+                   off = (tupleDesc->attrs[i]->attalign=='d')?
+                       DOUBLEALIGN(off):INTALIGN(off);
+                   off += VARSIZE(tp + off);
+                   break;
+               default:
+                   if (tupleDesc->attrs[i]->attlen > sizeof(int32))
+                       off = (tupleDesc->attrs[i]->attalign=='d') ?
+                           DOUBLEALIGN(off) + tupleDesc->attrs[i]->attlen :
+                           LONGALIGN(off) + tupleDesc->attrs[i]->attlen;
+                   else
+                       elog(WARN, "fastgetiattr2: attribute %d has len %d",
+                            i, tupleDesc->attrs[i]->attlen);
+                   
+                   break;
+               }
+       }
+       
+       return(fetchatt(&tupleDesc->attrs[attnum], tp + off));
+    }
+}
+
+/* ----------------
+ *     index_getattr
+ * ----------------
+ */
+Datum
+index_getattr(IndexTuple tuple,
+             AttrNumber attNum,
+             TupleDesc tupDesc,
+             bool *isNullOutP)
+{
+    Assert (attNum > 0);
+
+    return (Datum)
+       fastgetiattr(tuple, attNum, tupDesc, isNullOutP);
+}
+
+RetrieveIndexResult
+FormRetrieveIndexResult(ItemPointer indexItemPointer,
+                       ItemPointer heapItemPointer)
+{
+    RetrieveIndexResult        result;
+    
+    Assert(ItemPointerIsValid(indexItemPointer));
+    Assert(ItemPointerIsValid(heapItemPointer));
+    
+    result = (RetrieveIndexResult) palloc(sizeof *result);
+    
+    result->index_iptr = *indexItemPointer;
+    result->heap_iptr = *heapItemPointer;
+    
+    return (result);
+}
+
+/*
+ * Takes an infomask as argument (primarily because this needs to be usable
+ * at index_formtuple time so enough space is allocated).
+ *
+ * Change me if adding an attribute to IndexTuples!!!!!!!!!!!
+ */
+static Size
+IndexInfoFindDataOffset(unsigned short t_info)
+{
+    if (!(t_info & INDEX_NULL_MASK))
+       return((Size) sizeof(IndexTupleData));
+    else {
+       Size size = sizeof(IndexTupleData);
+       
+       if (t_info & INDEX_NULL_MASK) {
+           size += sizeof(IndexAttributeBitMapData);
+       }
+       return DOUBLEALIGN(size);       /* be conservative */
+    }
+}
+
+/*
+ * Copies source into target.  If *target == NULL, we palloc space; otherwise
+ * we assume we have space that is already palloc'ed.
+ */
+void
+CopyIndexTuple(IndexTuple source, IndexTuple *target)
+{
+    Size size;
+    IndexTuple ret;
+    
+    size = IndexTupleSize(source);
+    if (*target == NULL) {
+       *target = (IndexTuple) palloc(size);
+    }
+    
+    ret = *target;
+    memmove((char*)ret, (char*)source, size);
+}
+
diff --git a/src/backend/access/common/indexvalid.c b/src/backend/access/common/indexvalid.c
new file mode 100644 (file)
index 0000000..64b6b25
--- /dev/null
@@ -0,0 +1,84 @@
+/*-------------------------------------------------------------------------
+ *
+ * indexvalid.c--
+ *    index tuple qualification validity checking code
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "executor/execdebug.h"
+#include "access/genam.h"
+#include "access/iqual.h"      /* where the declarations go */
+#include "access/itup.h"
+#include "access/skey.h"
+
+#include "storage/buf.h"
+#include "storage/bufpage.h"
+#include "storage/itemid.h"
+#include "utils/rel.h"
+
+/* ----------------------------------------------------------------
+ *               index scan key qualification code
+ * ----------------------------------------------------------------
+ */
+int    NIndexTupleProcessed;
+
+/* ----------------
+ *     index_keytest
+ *
+ * old comments
+ *     May eventually combine with other tests (like timeranges)?
+ *     Should have Buffer buffer; as an argument and pass it to amgetattr.
+ * ----------------
+ */
+bool
+index_keytest(IndexTuple tuple,
+             TupleDesc tupdesc,
+             int scanKeySize,
+             ScanKey key)
+{
+    bool           isNull;
+    Datum          datum;
+    int                    test;
+    
+    IncrIndexProcessed();
+    
+    while (scanKeySize > 0) {
+       datum = index_getattr(tuple,
+                             1,
+                             tupdesc,
+                             &isNull);
+       
+       if (isNull) {
+           /* XXX eventually should check if SK_ISNULL */
+           return (false);
+       }
+       
+       if (key[0].sk_flags & SK_COMMUTE) {
+           test = (int) (*(key[0].sk_func))
+               (DatumGetPointer(key[0].sk_argument),
+                datum);
+       } else {
+           test = (int) (*(key[0].sk_func))
+               (datum,
+                DatumGetPointer(key[0].sk_argument));
+       }
+       
+       if (!test == !(key[0].sk_flags & SK_NEGATE)) {
+           return (false);
+       }
+       
+       scanKeySize -= 1;
+       key++;
+    }
+    
+    return (true);
+}
+
diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
new file mode 100644 (file)
index 0000000..93e1fbf
--- /dev/null
@@ -0,0 +1,306 @@
+/*-------------------------------------------------------------------------
+ *
+ * printtup.c--
+ *    Routines to print out tuples to the destination (binary or non-binary
+ *    portals, frontend/interactive backend, etc.).
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <sys/file.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/skey.h"
+#include "access/printtup.h"
+#include "access/tupdesc.h"
+#include "storage/buf.h"
+#include "utils/memutils.h"
+#include "utils/palloc.h"
+#include "fmgr.h"
+#include "utils/elog.h"
+
+#include "utils/syscache.h"
+#include "catalog/pg_type.h"
+
+#include "libpq/libpq.h"
+
+/* ----------------------------------------------------------------
+ *     printtup / debugtup support
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     typtoout - used by printtup and debugtup
+ * ----------------
+ */
+Oid
+typtoout(Oid type)
+{
+    HeapTuple  typeTuple;
+    
+    typeTuple = SearchSysCacheTuple(TYPOID,
+                                   ObjectIdGetDatum(type),
+                                   0, 0, 0);
+    
+    if (HeapTupleIsValid(typeTuple))
+       return((Oid)
+              ((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput);
+    
+    elog(WARN, "typtoout: Cache lookup of type %d failed", type);
+    return(InvalidOid);
+}
+
+Oid
+gettypelem(Oid type)
+{
+    HeapTuple  typeTuple;
+    
+    typeTuple = SearchSysCacheTuple(TYPOID,
+                                   ObjectIdGetDatum(type),
+                                   0,0,0);
+    
+    if (HeapTupleIsValid(typeTuple))
+       return((Oid)
+              ((TypeTupleForm) GETSTRUCT(typeTuple))->typelem);
+    
+    elog(WARN, "typtoout: Cache lookup of type %d failed", type);
+    return(InvalidOid);
+}
+
+/* ----------------
+ *     printtup
+ * ----------------
+ */
+void
+printtup(HeapTuple tuple, TupleDesc typeinfo)
+{
+    int                i, j, k;
+    char       *outputstr, *attr;
+    bool       isnull;
+    Oid        typoutput;
+    
+    /* ----------------
+     * tell the frontend to expect new tuple data
+     * ----------------
+     */
+    pq_putnchar("D", 1);
+    
+    /* ----------------
+     * send a bitmap of which attributes are null
+     * ----------------
+     */
+    j = 0;
+    k = 1 << 7;
+    for (i = 0; i < tuple->t_natts; ) {
+       attr = heap_getattr(tuple, InvalidBuffer, ++i, typeinfo, &isnull);
+       if (!isnull)
+           j |= k;
+       k >>= 1;
+       if (!(i & 7)) {
+           pq_putint(j, 1);
+           j = 0;
+           k = 1 << 7;
+       }
+    }
+    if (i & 7)
+       pq_putint(j, 1);
+    
+    /* ----------------
+     * send the attributes of this tuple
+     * ----------------
+     */
+    for (i = 0; i < tuple->t_natts; ++i) {
+       attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
+       typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid);
+       
+       if (!isnull && OidIsValid(typoutput)) {
+           outputstr = fmgr(typoutput, attr, 
+                            gettypelem(typeinfo->attrs[i]->atttypid));
+           pq_putint(strlen(outputstr)+4, 4);
+           pq_putnchar(outputstr, strlen(outputstr));
+           pfree(outputstr);
+       }
+    }
+}
+
+/* ----------------
+ *     printatt
+ * ----------------
+ */
+static void
+printatt(unsigned attributeId,
+        AttributeTupleForm attributeP,
+        char *value)
+{
+    printf("\t%2d: %.*s%s%s%s\t(typeid = %u, len = %d, byval = %c)\n",
+          attributeId,
+          NAMEDATALEN,         /* attname is a char16 */
+          attributeP->attname.data,
+          value != NULL ? " = \"" : "",
+          value != NULL ? value : "",
+          value != NULL ? "\"" : "",
+          (unsigned int) (attributeP->atttypid),
+          attributeP->attlen,
+          attributeP->attbyval ? 't' : 'f');
+}
+
+/* ----------------
+ *     showatts
+ * ----------------
+ */
+void
+showatts(char *name, TupleDesc tupleDesc)
+{
+    int        i;
+    int natts = tupleDesc->natts;
+    AttributeTupleForm *attinfo = tupleDesc->attrs;
+
+    puts(name);
+    for (i = 0; i < natts; ++i)
+       printatt((unsigned) i+1, attinfo[i], (char *) NULL);
+    printf("\t----\n");
+}
+
+/* ----------------
+ *     debugtup
+ * ----------------
+ */
+void
+debugtup(HeapTuple tuple, TupleDesc typeinfo)
+{
+    register int       i;
+    char               *attr, *value;
+    bool               isnull;
+    Oid                typoutput;
+    
+    for (i = 0; i < tuple->t_natts; ++i) {
+       attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
+       typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid);
+       
+       if (!isnull && OidIsValid(typoutput)) {
+           value = fmgr(typoutput, attr, 
+                        gettypelem(typeinfo->attrs[i]->atttypid));
+           printatt((unsigned) i+1, typeinfo->attrs[i], value);
+           pfree(value);
+       }
+    }
+    printf("\t----\n");
+}
+
+/*#define IPORTAL_DEBUG*/
+
+/* ----------------
+ *     printtup_internal
+ *      Protocol expects either T, D, C, E, or N.
+ *      We use a different data prefix, e.g. 'B' instead of 'D' to
+ *      indicate a tuple in internal (binary) form.
+ *
+ *      This is same as printtup, except we don't use the typout func.
+ * ----------------
+ */
+void
+printtup_internal(HeapTuple tuple, TupleDesc typeinfo)
+{
+    int                i, j, k;
+    char       *attr;
+    bool       isnull;
+    
+    /* ----------------
+     * tell the frontend to expect new tuple data
+     * ----------------
+     */
+    pq_putnchar("B", 1);
+    
+    /* ----------------
+     * send a bitmap of which attributes are null
+     * ----------------
+     */
+    j = 0;
+    k = 1 << 7;
+    for (i = 0; i < tuple->t_natts; ) {
+       attr = heap_getattr(tuple, InvalidBuffer, ++i, typeinfo, &isnull);
+       if (!isnull)
+           j |= k;
+       k >>= 1;
+       if (!(i & 7)) {
+           pq_putint(j, 1);
+           j = 0;
+           k = 1 << 7;
+       }
+    }
+    if (i & 7)
+       pq_putint(j, 1);
+    
+    /* ----------------
+     * send the attributes of this tuple
+     * ----------------
+     */
+#ifdef IPORTAL_DEBUG
+    fprintf(stderr, "sending tuple with %d atts\n", tuple->t_natts);
+#endif
+    for (i = 0; i < tuple->t_natts; ++i) {
+       int32 len = typeinfo->attrs[i]->attlen;
+       
+       attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
+       if (!isnull) {
+           /* # of bytes, and opaque data */
+           if (len == -1) {
+               /* variable length, assume a varlena structure */
+               len = VARSIZE(attr) - VARHDRSZ;
+               
+               pq_putint(len, sizeof(int32));
+               pq_putnchar(VARDATA(attr), len);
+#ifdef IPORTAL_DEBUG
+               {
+                   char *d = VARDATA(attr);
+                   
+                   fprintf(stderr, "length %d data %x%x%x%x\n",
+                           len, *d, *(d+1), *(d+2), *(d+3));
+               }
+#endif
+           } else {
+               /* fixed size */
+               if (typeinfo->attrs[i]->attbyval) {
+                   int8 i8;
+                   int16 i16;
+                   int32 i32;
+                   
+                   pq_putint(len, sizeof(int32));
+                   switch (len) {
+                   case sizeof(int8):
+                       i8 = DatumGetChar(attr);
+                       pq_putnchar((char *) &i8, len);
+                       break;
+                   case sizeof(int16):
+                       i16 = DatumGetInt16(attr);
+                       pq_putnchar((char *) &i16, len);
+                       break;
+                   case sizeof(int32):
+                       i32 = DatumGetInt32(attr);
+                       pq_putnchar((char *) &i32, len);
+                       break;
+                   }
+#ifdef IPORTAL_DEBUG
+                   fprintf(stderr, "byval length %d data %d\n", len, attr);
+#endif
+               } else {
+                   pq_putint(len, sizeof(int32));
+                   pq_putnchar(attr, len);
+#ifdef IPORTAL_DEBUG
+                   fprintf(stderr, "byref length %d data %x\n", len, attr);
+#endif
+               }
+           }
+       }
+    }
+}
diff --git a/src/backend/access/common/scankey.c b/src/backend/access/common/scankey.c
new file mode 100644 (file)
index 0000000..3e7269d
--- /dev/null
@@ -0,0 +1,68 @@
+/*-------------------------------------------------------------------------
+ *
+ * scan.c--
+ *    scan direction and key code
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+#include "access/sdir.h"
+#include "access/attnum.h"
+#include "access/skey.h"
+
+#include "fmgr.h"
+
+/*
+ * ScanKeyEntryIsLegal --
+ *     True iff the scan key entry is legal.
+ */
+#define ScanKeyEntryIsLegal(entry) \
+    ((bool) (AssertMacro(PointerIsValid(entry)) && \
+            AttributeNumberIsValid(entry->sk_attno)))
+
+/*
+ * ScanKeyEntrySetIllegal --
+ *     Marks a scan key entry as illegal.
+ */
+void
+ScanKeyEntrySetIllegal(ScanKey entry)
+{
+
+    Assert(PointerIsValid(entry));
+    
+    entry->sk_flags = 0;       /* just in case... */
+    entry->sk_attno = InvalidAttrNumber;
+    entry->sk_procedure = 0;   /* should be InvalidRegProcedure */
+}
+
+/*
+ * ScanKeyEntryInitialize --
+ *     Initializes an scan key entry.
+ *
+ * Note:
+ *     Assumes the scan key entry is valid.
+ *     Assumes the intialized scan key entry will be legal.
+ */
+void
+ScanKeyEntryInitialize(ScanKey entry,
+                      bits16 flags,
+                      AttrNumber attributeNumber,
+                      RegProcedure procedure,
+                      Datum argument)
+{
+    Assert(PointerIsValid(entry));
+    
+    entry->sk_flags = flags;
+    entry->sk_attno = attributeNumber;
+    entry->sk_procedure = procedure;
+    entry->sk_argument = argument;
+    fmgr_info(procedure, &entry->sk_func, &entry->sk_nargs);
+    
+    Assert(ScanKeyEntryIsLegal(entry));
+}
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
new file mode 100644 (file)
index 0000000..7ea4346
--- /dev/null
@@ -0,0 +1,398 @@
+/*-------------------------------------------------------------------------
+ *
+ * tupdesc.c--
+ *    POSTGRES tuple descriptor support code
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    some of the executor utility code such as "ExecTypeFromTL" should be
+ *    moved here.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>     /* for sprintf() */
+#include <ctype.h>
+#include <string.h>
+
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+
+#include "access/attnum.h"
+#include "access/htup.h"
+#include "access/tupdesc.h"
+
+#include "utils/builtins.h"
+#include "utils/elog.h"                /* XXX generate exceptions instead */
+#include "utils/palloc.h"
+
+#include "utils/syscache.h"
+#include "catalog/pg_type.h"
+
+#include "nodes/primnodes.h"
+
+#include "parser/catalog_utils.h"
+
+/* ----------------------------------------------------------------
+ *     CreateTemplateTupleDesc
+ *
+ *     This function allocates and zeros a tuple descriptor structure.
+ * ----------------------------------------------------------------
+ */
+TupleDesc
+CreateTemplateTupleDesc(int natts)
+{
+    uint32     size;
+    TupleDesc desc;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    AssertArg(natts >= 1);
+    
+    /* ----------------
+     *  allocate enough memory for the tuple descriptor and
+     *  zero it as TupleDescInitEntry assumes that the descriptor
+     *  is filled with NULL pointers.
+     * ----------------
+     */
+    size = natts * sizeof (AttributeTupleForm);
+    desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
+    desc->attrs = (AttributeTupleForm*) palloc(size);
+    memset(desc->attrs, 0, size);
+
+    desc->natts = natts;
+
+    return (desc);
+}
+
+/* ----------------------------------------------------------------
+ *     CreateTupleDesc
+ *
+ *     This function allocates a new TupleDesc from AttributeTupleForm array
+ * ----------------------------------------------------------------
+ */
+TupleDesc
+CreateTupleDesc(int natts, AttributeTupleForm* attrs)
+{
+    TupleDesc desc;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    AssertArg(natts >= 1);
+    
+    desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
+    desc->attrs = attrs;
+    desc->natts = natts;    
+
+
+    return (desc);
+}
+
+/* ----------------------------------------------------------------
+ *     CreateTupleDescCopy
+ *
+ *     This function creates a new TupleDesc by copying from an existing
+ *      TupleDesc
+ * 
+ * ----------------------------------------------------------------
+ */
+TupleDesc
+CreateTupleDescCopy(TupleDesc tupdesc)
+{
+    TupleDesc desc;
+    int i, size;
+
+    desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
+    desc->natts = tupdesc->natts;
+    size = desc->natts * sizeof (AttributeTupleForm);
+    desc->attrs = (AttributeTupleForm*) palloc(size);
+    for (i=0;i<desc->natts;i++) {
+       desc->attrs[i] = 
+           (AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE);
+       memmove(desc->attrs[i],
+               tupdesc->attrs[i],
+               ATTRIBUTE_TUPLE_SIZE);
+    }
+    return desc;
+}
+
+/* ----------------------------------------------------------------
+ *     TupleDescInitEntry
+ *
+ *     This function initializes a single attribute structure in
+ *     a preallocated tuple descriptor.
+ * ----------------------------------------------------------------
+ */
+bool
+TupleDescInitEntry(TupleDesc desc,
+                  AttrNumber attributeNumber,
+                  char *attributeName,
+                  char *typeName,
+                  int attdim,
+                  bool attisset)
+{
+    HeapTuple          tuple;
+    TypeTupleForm      typeForm;
+    AttributeTupleForm att;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    AssertArg(PointerIsValid(desc));
+    AssertArg(attributeNumber >= 1);
+    /* attributeName's are sometimes NULL, 
+       from resdom's.  I don't know why that is, though -- Jolly */
+/*    AssertArg(NameIsValid(attributeName));*/
+/*    AssertArg(NameIsValid(typeName));*/
+    
+    AssertArg(!PointerIsValid(desc->attrs[attributeNumber - 1]));
+    
+
+    /* ----------------
+     * allocate storage for this attribute
+     * ----------------
+     */
+
+    att = (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
+    desc->attrs[attributeNumber - 1] = att;
+
+    /* ----------------
+     * initialize some of the attribute fields
+     * ----------------
+     */
+    att->attrelid  = 0;                                /* dummy value */
+    
+    if (attributeName != NULL)
+       namestrcpy(&(att->attname), attributeName);
+    else
+       memset(att->attname.data,0,NAMEDATALEN);
+
+    
+    att->attdefrel =   0;                      /* dummy value */
+    att->attnvals  =   0;                      /* dummy value */
+    att->atttyparg =   0;                      /* dummy value */
+    att->attbound =    0;                      /* dummy value */
+    att->attcanindex =         0;                      /* dummy value */
+    att->attproc =     0;                      /* dummy value */
+    att->attcacheoff =         -1;
+    
+    att->attnum = attributeNumber;
+    att->attnelems = attdim;
+    att->attisset = attisset;
+    
+    /* ----------------
+     * search the system cache for the type tuple of the attribute
+     *  we are creating so that we can get the typeid and some other
+     *  stuff.
+     *
+     *  Note: in the special case of 
+     *
+     *     create EMP (name = char16, manager = EMP)
+     *
+     *  RelationNameCreateHeapRelation() calls BuildDesc() which
+     *  calls this routine and since EMP does not exist yet, the
+     *  system cache lookup below fails.  That's fine, but rather
+     *  then doing a elog(WARN) we just leave that information
+     *  uninitialized, return false, then fix things up later.
+     *  -cim 6/14/90
+     * ----------------
+     */
+    tuple = SearchSysCacheTuple(TYPNAME, PointerGetDatum(typeName),
+                               0,0,0);
+    if (! HeapTupleIsValid(tuple)) {
+       /* ----------------
+        *   here type info does not exist yet so we just fill
+        *   the attribute with dummy information and return false.
+        * ----------------
+        */
+       att->atttypid = InvalidOid;
+       att->attlen   = (int16) 0;
+       att->attbyval = (bool) 0;
+       att->attalign = 'i';
+       return false;
+    }
+    
+    /* ----------------
+     * type info exists so we initialize our attribute
+     *  information from the type tuple we found..
+     * ----------------
+     */
+    typeForm = (TypeTupleForm) GETSTRUCT(tuple);
+    
+    att->atttypid = tuple->t_oid;
+    att->attalign = typeForm->typalign;
+    
+    /* ------------------------
+       If this attribute is a set, what is really stored in the
+       attribute is the OID of a tuple in the pg_proc catalog.
+       The pg_proc tuple contains the query string which defines
+       this set - i.e., the query to run to get the set.
+       So the atttypid (just assigned above) refers to the type returned
+       by this query, but the actual length of this attribute is the
+       length (size) of an OID.
+       
+       Why not just make the atttypid point to the OID type, instead
+       of the type the query returns?  Because the executor uses the atttypid
+       to tell the front end what type will be returned (in BeginCommand),
+       and in the end the type returned will be the result of the query, not
+       an OID.
+       
+       Why not wait until the return type of the set is known (i.e., the
+       recursive call to the executor to execute the set has returned) 
+       before telling the front end what the return type will be?  Because
+       the executor is a delicate thing, and making sure that the correct
+       order of front-end commands is maintained is messy, especially 
+       considering that target lists may change as inherited attributes
+       are considered, etc.  Ugh.
+       -----------------------------------------
+       */
+    if (attisset) {
+       Type t = type("oid");
+       att->attlen = tlen(t);
+       att->attbyval = tbyval(t);
+    } else {
+       att->attlen   = typeForm->typlen;
+       att->attbyval = typeForm->typbyval;
+    }
+    
+    
+    return true;
+}
+
+
+/* ----------------------------------------------------------------
+ *     TupleDescMakeSelfReference
+ *
+ *     This function initializes a "self-referential" attribute like
+ *      manager in "create EMP (name=text, manager = EMP)".
+ *     It calls TypeShellMake() which inserts a "shell" type
+ *     tuple into pg_type.  A self-reference is one kind of set, so
+ *      its size and byval are the same as for a set.  See the comments
+ *      above in TupleDescInitEntry.
+ * ----------------------------------------------------------------
+ */
+static void
+TupleDescMakeSelfReference(TupleDesc desc,
+                          AttrNumber attnum,
+                          char *relname)
+{
+    AttributeTupleForm att;
+    Type t = type("oid");
+    
+    att = desc->attrs[attnum-1];
+    att->atttypid = TypeShellMake(relname);
+    att->attlen   = tlen(t);
+    att->attbyval = tbyval(t);
+    att->attnelems = 0;
+}
+
+/* ----------------------------------------------------------------
+ *     BuildDescForRelation
+ *
+ *     This is a general purpose function identical to BuildDesc
+ *     but is used by the DefineRelation() code to catch the
+ *     special case where you
+ *
+ *             create FOO ( ..., x = FOO )
+ *
+ *     here, the initial type lookup for "x = FOO" will fail
+ *     because FOO isn't in the catalogs yet.  But since we
+ *     are creating FOO, instead of doing an elog() we add
+ *     a shell type tuple to pg_type and fix things later
+ *     in amcreate().
+ * ----------------------------------------------------------------
+ */
+TupleDesc
+BuildDescForRelation(List *schema, char *relname)
+{
+    int                        natts;
+    AttrNumber         attnum;
+    List               *p;
+    TupleDesc          desc;
+    char               *attname;
+    char               *typename;
+    int                        attdim;
+    bool                attisset;
+    
+    /* ----------------
+     * allocate a new tuple descriptor
+     * ----------------
+     */
+    natts =    length(schema);
+    desc =     CreateTemplateTupleDesc(natts);
+    
+    attnum = 0;
+    
+    typename = palloc(NAMEDATALEN+1);
+
+    foreach(p, schema) {
+       ColumnDef *entry;
+       List    *arry;
+
+       /* ----------------
+        *      for each entry in the list, get the name and type
+        *      information from the list and have TupleDescInitEntry
+        *      fill in the attribute information we need.
+        * ----------------
+        */     
+       attnum++;
+       
+       entry =         lfirst(p);
+       attname =       entry->colname;
+       arry = entry->typename->arrayBounds;
+       attisset = entry->typename->setof;
+
+       if (arry != NIL) {
+           char buf[20];
+           
+           attdim = length(arry);
+           
+           /* array of XXX is _XXX (inherited from release 3) */
+           sprintf(buf, "_%.*s", NAMEDATALEN, entry->typename->name);
+           strcpy(typename, buf);
+       } else {
+           strcpy(typename, entry->typename->name);
+           attdim = 0;
+       }
+       
+       if (! TupleDescInitEntry(desc, attnum, attname, 
+                                typename, attdim, attisset)) {
+           /* ----------------
+            *  if TupleDescInitEntry() fails, it means there is
+            *  no type in the system catalogs.  So now we check if
+            *  the type name equals the relation name.  If so we
+            *  have a self reference, otherwise it's an error.
+            * ----------------
+            */
+           if (!strcmp(typename, relname)) {
+               TupleDescMakeSelfReference(desc, attnum, relname);
+           } else
+               elog(WARN, "DefineRelation: no such type %.*s", 
+                    NAMEDATALEN, typename);
+       }
+
+       /*
+        * this is for char() and varchar(). When an entry is of type
+        * char() or varchar(), typlen is set to the appropriate length,
+        * which we'll use here instead. (The catalog lookup only returns
+        * the length of bpchar and varchar which is not what we want!)
+        *                                              - ay 6/95
+        */
+       if (entry->typename->typlen > 0) {
+           desc->attrs[attnum - 1]->attlen = entry->typename->typlen;
+       }
+    }
+    return desc;
+}
+
diff --git a/src/backend/access/funcindex.h b/src/backend/access/funcindex.h
new file mode 100644 (file)
index 0000000..3d62099
--- /dev/null
@@ -0,0 +1,43 @@
+/*-------------------------------------------------------------------------
+ *
+ * funcindex.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _FUNC_INDEX_INCLUDED_
+#define _FUNC_INDEX_INCLUDED_
+
+#include "postgres.h"
+
+typedef struct {
+       int     nargs;
+       Oid     arglist[8];
+       Oid     procOid;
+       NameData funcName;
+} FuncIndexInfo;
+
+typedef FuncIndexInfo  *FuncIndexInfoPtr;
+
+/*
+ * some marginally useful macro definitions
+ */
+/* #define FIgetname(FINFO) (&((FINFO)->funcName.data[0]))*/
+#define FIgetname(FINFO) (FINFO)->funcName.data
+#define FIgetnArgs(FINFO) (FINFO)->nargs
+#define FIgetProcOid(FINFO) (FINFO)->procOid
+#define FIgetArg(FINFO, argnum) (FINFO)->arglist[argnum]
+#define FIgetArglist(FINFO) (FINFO)->arglist
+
+#define FIsetnArgs(FINFO, numargs) ((FINFO)->nargs = numargs)
+#define FIsetProcOid(FINFO, id) ((FINFO)->procOid = id)
+#define FIsetArg(FINFO, argnum, argtype) ((FINFO)->arglist[argnum] = argtype)
+
+#define FIisFunctionalIndex(FINFO) (FINFO->procOid != InvalidOid)
+
+#endif /* FUNCINDEX_H */
diff --git a/src/backend/access/genam.h b/src/backend/access/genam.h
new file mode 100644 (file)
index 0000000..0e7e28d
--- /dev/null
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+ *
+ * genam.h--
+ *    POSTGRES general access method definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        GENAM_H
+#define GENAM_H
+
+#include "postgres.h"
+
+#include "access/attnum.h"
+#include "access/htup.h"
+#include "access/istrat.h"
+#include "access/itup.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "access/sdir.h"
+#include "access/funcindex.h"
+
+/* ----------------
+ *     generalized index_ interface routines
+ * ----------------
+ */
+extern Relation index_open(Oid relationId);
+extern Relation index_openr(char *relationName);
+extern void index_close(Relation relation);
+extern InsertIndexResult index_insert(Relation relation,
+                                     IndexTuple indexTuple);
+extern void index_delete(Relation relation, ItemPointer indexItem);
+extern IndexScanDesc index_beginscan(Relation relation, bool scanFromEnd,
+     uint16 numberOfKeys, ScanKey key);
+extern void index_rescan(IndexScanDesc scan, bool scanFromEnd, ScanKey key);
+extern void index_endscan(IndexScanDesc scan);
+extern void index_markpos(IndexScanDesc scan);
+extern void index_restrpos(IndexScanDesc scan);
+extern RetrieveIndexResult index_getnext(IndexScanDesc scan,
+                                        ScanDirection direction);
+extern RegProcedure index_getprocid(Relation irel, AttrNumber attnum,
+                                   uint16 procnum);
+extern Datum GetIndexValue(HeapTuple tuple, TupleDesc hTupDesc,
+     int attOff, AttrNumber attrNums[], FuncIndexInfo *fInfo,
+     bool *attNull, Buffer buffer);
+
+/* in genam.c */
+extern IndexScanDesc RelationGetIndexScan(Relation relation, bool scanFromEnd,
+                                         uint16 numberOfKeys, ScanKey key);
+extern void IndexScanRestart(IndexScanDesc scan, bool scanFromEnd,
+                            ScanKey key);
+extern void IndexScanEnd(IndexScanDesc scan);
+extern void IndexScanMarkPosition(IndexScanDesc scan);
+extern void IndexScanRestorePosition(IndexScanDesc scan);
+
+#endif /* GENAM_H */
diff --git a/src/backend/access/hash.h b/src/backend/access/hash.h
new file mode 100644 (file)
index 0000000..daa9c7e
--- /dev/null
@@ -0,0 +1,336 @@
+/*-------------------------------------------------------------------------
+ *
+ * hash.h--
+ *    header file for postgres hash access method implementation 
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *     modeled after Margo Seltzer's hash implementation for unix. 
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef HASH_H
+#define HASH_H
+
+#include "access/itup.h"
+
+/* 
+ * An overflow page is a spare page allocated for storing data whose 
+ * bucket doesn't have room to store it. We use overflow pages rather
+ * than just splitting the bucket because there is a linear order in
+ * the way we split buckets. In other words, if there isn't enough space
+ * in the bucket itself, put it in an overflow page. 
+ *
+ * Overflow page addresses are stored in form: (Splitnumber, Page offset).
+ *
+ * A splitnumber is the number of the generation where the table doubles
+ * in size. The ovflpage's offset within the splitnumber; offsets start
+ * at 1. 
+ * 
+ * We convert the stored bitmap address into a page address with the
+ * macro OADDR_OF(S, O) where S is the splitnumber and O is the page 
+ * offset. 
+ */
+typedef uint32         Bucket;
+typedef bits16 OverflowPageAddress;
+typedef uint32 SplitNumber;
+typedef uint32  PageOffset;
+
+/* A valid overflow address will always have a page offset >= 1 */
+#define InvalidOvflAddress     0       
+                                          
+#define SPLITSHIFT     11
+#define SPLITMASK      0x7FF
+#define SPLITNUM(N)    ((SplitNumber)(((uint32)(N)) >> SPLITSHIFT))
+#define OPAGENUM(N)    ((PageOffset)((N) & SPLITMASK))
+#define        OADDR_OF(S,O)   ((OverflowPageAddress)((uint32)((uint32)(S) << SPLITSHIFT) + (O)))
+
+#define BUCKET_TO_BLKNO(B) \
+       ((Bucket) ((B) + ((B) ? metap->SPARES[_hash_log2((B)+1)-1] : 0)) + 1)
+#define OADDR_TO_BLKNO(B)       \
+       ((BlockNumber) \
+        (BUCKET_TO_BLKNO ( (1 << SPLITNUM((B))) -1 ) + OPAGENUM((B))));
+
+/* 
+ * hasho_flag tells us which type of page we're looking at.  For
+ * example, knowing overflow pages from bucket pages is necessary
+ * information when you're deleting tuples from a page. If all the
+ * tuples are deleted from an overflow page, the overflow is made
+ * available to other buckets by calling _hash_freeovflpage(). If all
+ * the tuples are deleted from a bucket page, no additional action is
+ * necessary.
+ */
+
+#define        LH_UNUSED_PAGE          (0)
+#define LH_OVERFLOW_PAGE       (1 << 0)
+#define LH_BUCKET_PAGE         (1 << 1)
+#define        LH_BITMAP_PAGE          (1 << 2)
+#define        LH_META_PAGE            (1 << 3)
+
+typedef struct HashPageOpaqueData {
+    bits16 hasho_flag;                 /* is this page a bucket or ovfl */
+    Bucket hasho_bucket;               /* bucket number this pg belongs to */
+    OverflowPageAddress hasho_oaddr;   /* ovfl address of this ovfl pg */
+    BlockNumber hasho_nextblkno;       /* next ovfl blkno */
+    BlockNumber        hasho_prevblkno;        /* previous ovfl (or bucket) blkno */
+} HashPageOpaqueData;
+
+typedef HashPageOpaqueData        *HashPageOpaque;
+
+/*
+ *  ScanOpaqueData is used to remember which buffers we're currently
+ *  examining in the scan.  We keep these buffers locked and pinned and
+ *  recorded in the opaque entry of the scan in order to avoid doing a
+ *  ReadBuffer() for every tuple in the index.  This avoids semop() calls,
+ *  which are expensive.
+ */
+
+typedef struct HashScanOpaqueData {
+    Buffer      hashso_curbuf;
+    Buffer      hashso_mrkbuf;
+} HashScanOpaqueData;
+
+typedef HashScanOpaqueData        *HashScanOpaque;
+
+/* 
+ * Definitions for metapage.
+ */
+
+#define HASH_METAPAGE  0               /* metapage is always block 0 */
+
+#define HASH_MAGIC     0x6440640
+#define HASH_VERSION   0
+
+/*
+ * NCACHED is used to set the array sizeof spares[] & bitmaps[].
+ *
+ * Spares[] is used to hold the number overflow pages currently
+ * allocated at a certain splitpoint. For example, if spares[3] = 7
+ * then there are a maximum of 7 ovflpages available at splitpoint 3.
+ * The value in spares[] will change as ovflpages are added within
+ * a splitpoint. 
+ * 
+ * Within a splitpoint, one can find which ovflpages are available and
+ * which are used by looking at a bitmaps that are stored on the ovfl
+ * pages themselves. There is at least one bitmap for every splitpoint's
+ * ovflpages. Bitmaps[] contains the ovflpage addresses of the ovflpages 
+ * that hold the ovflpage bitmaps. 
+ *
+ * The reason that the size is restricted to NCACHED (32) is because
+ * the bitmaps are 16 bits: upper 5 represent the splitpoint, lower 11
+ * indicate the page number within the splitpoint. Since there are 
+ * only 5 bits to store the splitpoint, there can only be 32 splitpoints. 
+ * Both spares[] and bitmaps[] use splitpoints as there indices, so there
+ * can only be 32 of them. 
+ */
+
+#define        NCACHED         32      
+
+
+typedef struct HashMetaPageData {
+    PageHeaderData     hashm_phdr;             /* pad for page header
+                                                  (do not use) */
+    uint32             hashm_magic;            /* magic no. for hash tables */
+    uint32             hashm_version;          /* version ID */
+    uint32             hashm_nkeys;            /* number of keys stored in
+                                                  the table */
+    uint16             hashm_ffactor;          /* fill factor */
+    uint16             hashm_bsize;            /* bucket size (bytes) -
+                                                  must be a power of 2 */
+    uint16             hashm_bshift;           /* bucket shift */
+    uint16             hashm_bmsize;           /* bitmap array size (bytes) -
+                                                  must be a power of 2 */
+    uint32             hashm_maxbucket;        /* ID of maximum bucket
+                                                  in use */
+    uint32             hashm_highmask;         /* mask to modulo into
+                                                  entire table */
+    uint32             hashm_lowmask;          /* mask to modulo into lower
+                                                  half of table */
+    uint32             hashm_ovflpoint;        /* pageno. from which ovflpgs
+                                                  being allocated */
+    uint32             hashm_lastfreed;        /* last ovflpage freed */
+    uint32             hashm_nmaps;            /* Initial number of bitmaps */
+    uint32             hashm_spares[NCACHED];  /* spare pages available at
+                                                  splitpoints */
+    BlockNumber                hashm_mapp[NCACHED];    /* blknumbers of ovfl page
+                                                  maps */
+    RegProcedure       hashm_procid;           /* hash procedure id from
+                                                  pg_proc */
+} HashMetaPageData;
+
+typedef HashMetaPageData *HashMetaPage;
+
+/* Short hands for accessing structure */
+#define BSHIFT         hashm_bshift
+#define OVFL_POINT     hashm_ovflpoint
+#define        LAST_FREED      hashm_lastfreed
+#define MAX_BUCKET     hashm_maxbucket
+#define FFACTOR                hashm_ffactor
+#define HIGH_MASK      hashm_highmask
+#define LOW_MASK       hashm_lowmask
+#define NKEYS          hashm_nkeys
+#define SPARES         hashm_spares
+
+extern bool    BuildingHash;
+
+typedef struct HashItemData {
+    IndexTupleData          hash_itup;
+} HashItemData;
+
+typedef HashItemData      *HashItem;
+
+/*
+ * Constants
+ */
+#define DEFAULT_FFACTOR                300
+#define SPLITMAX               8
+#define BYTE_TO_BIT            3       /* 2^3 bits/byte */
+#define INT_TO_BYTE            2       /* 2^2 bytes/int */
+#define INT_TO_BIT             5       /* 2^5 bits/int */
+#define ALL_SET                        ((uint32) ~0)
+
+/*
+ * bitmap pages do not contain tuples.  they do contain the standard
+ * page headers and trailers; however, everything in between is a
+ * giant bit array.  the number of bits that fit on a page obviously
+ * depends on the page size and the header/trailer overhead.
+ */
+#define        BMPGSZ_BYTE(metap)      ((metap)->hashm_bmsize)
+#define        BMPGSZ_BIT(metap)       ((metap)->hashm_bmsize << BYTE_TO_BIT)
+#define        HashPageGetBitmap(pg) \
+    ((uint32 *) (((char *) (pg)) + DOUBLEALIGN(sizeof(PageHeaderData))))
+
+/*
+ * The number of bits in an ovflpage bitmap which
+ * tells which ovflpages are empty versus in use (NOT the number of
+ * bits in an overflow page *address* bitmap). 
+ */
+#define BITS_PER_MAP   32      /* Number of bits in ovflpage bitmap */
+
+/* Given the address of the beginning of a big map, clear/set the nth bit */
+#define CLRBIT(A, N)   ((A)[(N)/BITS_PER_MAP] &= ~(1<<((N)%BITS_PER_MAP)))
+#define SETBIT(A, N)   ((A)[(N)/BITS_PER_MAP] |= (1<<((N)%BITS_PER_MAP)))
+#define ISSET(A, N)    ((A)[(N)/BITS_PER_MAP] & (1<<((N)%BITS_PER_MAP)))
+
+/*
+ * page locking modes
+ */
+#define        HASH_READ       0
+#define        HASH_WRITE      1
+
+/*  
+ *  In general, the hash code tries to localize its knowledge about page
+ *  layout to a couple of routines.  However, we need a special value to
+ *  indicate "no page number" in those places where we expect page numbers.
+ */
+
+#define P_NONE         0
+
+/*
+ *  Strategy number. There's only one valid strategy for hashing: equality.
+ */
+
+#define HTEqualStrategyNumber          1
+#define HTMaxStrategyNumber            1
+
+/*
+ *  When a new operator class is declared, we require that the user supply
+ *  us with an amproc procudure for hashing a key of the new type.
+ *  Since we only have one such proc in amproc, it's number 1.
+ */
+
+#define HASHPROC       1
+
+/* public routines */
+
+extern void hashbuild(Relation heap, Relation index, int natts,
+       AttrNumber *attnum, IndexStrategy istrat, uint16 pcount,
+       Datum *params, FuncIndexInfo *finfo, PredInfo *predInfo);
+extern InsertIndexResult hashinsert(Relation rel, IndexTuple itup);
+extern char *hashgettuple(IndexScanDesc scan, ScanDirection dir);
+extern char *hashbeginscan(Relation rel, bool fromEnd, uint16 keysz,
+                          ScanKey scankey);
+extern void hashrescan(IndexScanDesc scan, bool fromEnd, ScanKey scankey);
+extern void hashendscan(IndexScanDesc scan);
+extern void hashmarkpos(IndexScanDesc scan);
+extern void hashrestrpos(IndexScanDesc scan);
+extern void hashdelete(Relation rel, ItemPointer tid);
+
+/* hashfunc.c */
+extern uint32 hashint2(int16 key);
+extern uint32 hashint4(uint32 key);
+extern uint32 hashfloat4(float32 keyp);
+extern uint32 hashfloat8(float64 keyp);
+extern uint32 hashoid(Oid key);
+extern uint32 hashchar(char key);
+extern uint32 hashchar2(uint16 intkey);
+extern uint32 hashchar4(uint32 intkey);
+extern uint32 hashchar8(char *key);
+extern uint32 hashchar16(char *key);
+extern uint32 hashtext(struct varlena *key);
+
+/* private routines */
+
+/* hashinsert.c */
+extern InsertIndexResult _hash_doinsert(Relation rel, HashItem hitem);
+
+
+/* hashovfl.c */
+extern Buffer _hash_addovflpage(Relation rel, Buffer *metabufp, Buffer buf);
+extern Buffer _hash_freeovflpage(Relation rel, Buffer ovflbuf);
+extern int32 _hash_initbitmap(Relation rel, HashMetaPage metap, int32 pnum,
+                             int32 nbits, int32 ndx);
+extern void _hash_squeezebucket(Relation rel, HashMetaPage metap,
+                               Bucket bucket);
+
+
+/* hashpage.c */
+extern void _hash_metapinit(Relation rel);
+extern Buffer _hash_getbuf(Relation rel, BlockNumber blkno, int access);
+extern void _hash_relbuf(Relation rel, Buffer buf, int access);
+extern void _hash_wrtbuf(Relation rel, Buffer buf);
+extern void _hash_wrtnorelbuf(Relation rel, Buffer buf);
+extern Page _hash_chgbufaccess(Relation rel, Buffer *bufp, int from_access,
+                              int to_access);
+extern void _hash_pageinit(Page page, Size size);
+extern void _hash_pagedel(Relation rel, ItemPointer tid);
+extern void _hash_expandtable(Relation rel, Buffer metabuf);
+
+
+/* hashscan.c */
+extern void _hash_regscan(IndexScanDesc scan);
+extern void _hash_dropscan(IndexScanDesc scan);
+extern void _hash_adjscans(Relation rel, ItemPointer tid);
+
+
+/* hashsearch.c */
+extern void _hash_search(Relation rel, int keysz, ScanKey scankey,
+                        Buffer *bufP, HashMetaPage metap);
+extern RetrieveIndexResult _hash_next(IndexScanDesc scan, ScanDirection dir);
+extern RetrieveIndexResult _hash_first(IndexScanDesc scan, ScanDirection dir);
+extern bool _hash_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir,
+                      Buffer metabuf);
+
+
+/* hashstrat.c */
+extern StrategyNumber _hash_getstrat(Relation rel, AttrNumber attno,
+                                    RegProcedure proc);
+extern bool _hash_invokestrat(Relation rel, AttrNumber attno,
+                             StrategyNumber strat, Datum left, Datum right);
+
+
+/* hashutil.c */
+extern ScanKey _hash_mkscankey(Relation rel, IndexTuple itup,
+                              HashMetaPage metap);
+extern void _hash_freeskey(ScanKey skey);
+extern bool _hash_checkqual(IndexScanDesc scan, IndexTuple itup);
+extern HashItem _hash_formitem(IndexTuple itup);
+extern Bucket _hash_call(Relation rel, HashMetaPage metap, Datum key);
+extern uint32 _hash_log2(uint32 num);
+extern void _hash_checkpage(Page page, int flags);
+
+#endif /* HASH_H */
diff --git a/src/backend/access/hash/Makefile.inc b/src/backend/access/hash/Makefile.inc
new file mode 100644 (file)
index 0000000..74920d6
--- /dev/null
@@ -0,0 +1,18 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for access/hash (hash access method)
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= hash.c hashfunc.c hashinsert.c hashovfl.c hashpage.c hashscan.c \
+       hashsearch.c hashstrat.c hashutil.c
+
+
+
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
new file mode 100644 (file)
index 0000000..c6b570d
--- /dev/null
@@ -0,0 +1,467 @@
+/*-------------------------------------------------------------------------
+ *
+ * hash.c--
+ *    Implementation of Margo Seltzer's Hashing package for postgres. 
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    This file contains only the public interface routines.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+#include "utils/excid.h"
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/sdir.h"
+#include "access/hash.h"
+#include "access/funcindex.h"
+#include "nodes/execnodes.h"
+#include "nodes/plannodes.h"
+#include "executor/executor.h"
+#include "executor/tuptable.h"
+#include "catalog/index.h"
+
+
+bool   BuildingHash = false;
+
+/*
+ *  hashbuild() -- build a new hash index.
+ *
+ *     We use a global variable to record the fact that we're creating
+ *     a new index.  This is used to avoid high-concurrency locking,
+ *     since the index won't be visible until this transaction commits
+ *     and since building is guaranteed to be single-threaded.
+ */
+void
+hashbuild(Relation heap,
+         Relation index,
+         int natts,
+         AttrNumber *attnum,
+         IndexStrategy istrat,
+         uint16 pcount,
+         Datum *params,
+         FuncIndexInfo *finfo,
+         PredInfo *predInfo)
+{
+    HeapScanDesc hscan;
+    Buffer buffer;
+    HeapTuple htup;
+    IndexTuple itup;
+    TupleDesc htupdesc, itupdesc;
+    Datum *attdata;
+    bool *nulls;
+    InsertIndexResult res;
+    int nhtups, nitups;
+    int i;
+    HashItem hitem;
+    ExprContext *econtext;
+    TupleTable tupleTable;
+    TupleTableSlot *slot;
+    Oid hrelid, irelid;
+    Node *pred, *oldPred;
+    
+    /* note that this is a new btree */
+    BuildingHash = true;
+    
+    pred = predInfo->pred;
+    oldPred = predInfo->oldPred;
+    
+    /*  initialize the hash index metadata page (if this is a new index) */
+    if (oldPred == NULL)
+       _hash_metapinit(index);
+    
+    /* get tuple descriptors for heap and index relations */
+    htupdesc = RelationGetTupleDescriptor(heap);
+    itupdesc = RelationGetTupleDescriptor(index);
+    
+    /* get space for data items that'll appear in the index tuple */
+    attdata = (Datum *) palloc(natts * sizeof(Datum));
+    nulls = (bool *) palloc(natts * sizeof(bool));
+    
+    /*
+     * If this is a predicate (partial) index, we will need to evaluate the
+     * predicate using ExecQual, which requires the current tuple to be in a
+     * slot of a TupleTable.  In addition, ExecQual must have an ExprContext
+     * referring to that slot.  Here, we initialize dummy TupleTable and
+     * ExprContext objects for this purpose. --Nels, Feb '92
+     */
+#ifndef OMIT_PARTIAL_INDEX
+    if (pred != NULL || oldPred != NULL) {
+       tupleTable = ExecCreateTupleTable(1);
+       slot = ExecAllocTableSlot(tupleTable);
+       econtext = makeNode(ExprContext);
+       FillDummyExprContext(econtext, slot, htupdesc, buffer);
+    }
+#endif /* OMIT_PARTIAL_INDEX */
+    
+    /* start a heap scan */
+    hscan = heap_beginscan(heap, 0, NowTimeQual, 0, (ScanKey) NULL);
+    htup = heap_getnext(hscan, 0, &buffer);
+    
+    /* build the index */
+    nhtups = nitups = 0;
+    
+    for (; HeapTupleIsValid(htup); htup = heap_getnext(hscan, 0, &buffer)) {
+       
+       nhtups++;
+       
+       /*
+        * If oldPred != NULL, this is an EXTEND INDEX command, so skip
+        * this tuple if it was already in the existing partial index
+        */
+       if (oldPred != NULL) {
+           /*SetSlotContents(slot, htup); */
+#ifndef OMIT_PARTIAL_INDEX
+           slot->val = htup;
+           if (ExecQual((List*)oldPred, econtext) == true) {
+               nitups++;
+               continue;
+           }
+#endif /* OMIT_PARTIAL_INDEX */    
+       }
+       
+       /* Skip this tuple if it doesn't satisfy the partial-index predicate */
+       if (pred != NULL) {
+#ifndef OMIT_PARTIAL_INDEX
+           /*SetSlotContents(slot, htup); */
+           slot->val = htup;
+           if (ExecQual((List*)pred, econtext) == false)
+               continue;
+#endif /* OMIT_PARTIAL_INDEX */        
+}
+       
+       nitups++;
+       
+       /*
+        *  For the current heap tuple, extract all the attributes
+        *  we use in this index, and note which are null.
+        */
+       for (i = 1; i <= natts; i++) {
+           int attoff;
+           bool attnull;
+           
+           /*
+            *  Offsets are from the start of the tuple, and are
+            *  zero-based; indices are one-based.  The next call
+            *  returns i - 1.  That's data hiding for you.
+            */
+           
+           /* attoff = i - 1 */
+           attoff = AttrNumberGetAttrOffset(i);
+           
+           /* below, attdata[attoff] set to equal some datum &
+            * attnull is changed to indicate whether or not the attribute 
+            * is null for this tuple
+            */
+           attdata[attoff] = GetIndexValue(htup, 
+                                           htupdesc,
+                                           attoff, 
+                                           attnum, 
+                                           finfo, 
+                                           &attnull,
+                                           buffer);
+           nulls[attoff] = (attnull ? 'n' : ' ');
+       }
+       
+       /* form an index tuple and point it at the heap tuple */
+       itup = index_formtuple(itupdesc, attdata, nulls);
+       
+       /*
+        *  If the single index key is null, we don't insert it into
+        *  the index.  Hash tables support scans on '='.
+        *  Relational algebra says that A = B
+        *  returns null if either A or B is null.  This
+        *  means that no qualification used in an index scan could ever
+        *  return true on a null attribute.  It also means that indices
+        *  can't be used by ISNULL or NOTNULL scans, but that's an
+        *  artifact of the strategy map architecture chosen in 1986, not
+        *  of the way nulls are handled here.
+        */
+       
+       if (itup->t_info & INDEX_NULL_MASK) {
+           pfree(itup);
+           continue;
+       }
+       
+       itup->t_tid = htup->t_ctid;
+       hitem = _hash_formitem(itup);
+       res = _hash_doinsert(index, hitem);
+       pfree(hitem);
+       pfree(itup);
+       pfree(res);
+    }
+    
+    /* okay, all heap tuples are indexed */
+    heap_endscan(hscan);
+    
+    if (pred != NULL || oldPred != NULL) {
+#ifndef OMIT_PARTIAL_INDEX
+       ExecDestroyTupleTable(tupleTable, true);
+       pfree(econtext);
+#endif /* OMIT_PARTIAL_INDEX */        
+    }
+    
+    /*
+     *  Since we just counted the tuples in the heap, we update its
+     *  stats in pg_class to guarantee that the planner takes advantage
+     *  of the index we just created. Finally, only update statistics
+     *  during normal index definitions, not for indices on system catalogs
+     *  created during bootstrap processing.  We must close the relations
+     *  before updatings statistics to guarantee that the relcache entries
+     *  are flushed when we increment the command counter in UpdateStats().
+     */
+    if (IsNormalProcessingMode())
+       {
+           hrelid = heap->rd_id;
+           irelid = index->rd_id;
+           heap_close(heap);
+           index_close(index);
+           UpdateStats(hrelid, nhtups, true);
+           UpdateStats(irelid, nitups, false);
+           if (oldPred != NULL) {
+               if (nitups == nhtups) pred = NULL;
+               UpdateIndexPredicate(irelid, oldPred, pred);
+           }  
+       }
+    
+    /* be tidy */
+    pfree(nulls);
+    pfree(attdata);
+    
+    /* all done */
+    BuildingHash = false;
+}
+
+/*
+ *  hashinsert() -- insert an index tuple into a hash table. 
+ *
+ *  Hash on the index tuple's key, find the appropriate location 
+ *  for the new tuple, put it there, and return an InsertIndexResult
+ *  to the caller. 
+ */
+InsertIndexResult
+hashinsert(Relation rel, IndexTuple itup)
+{
+    HashItem hitem;
+    InsertIndexResult res;
+    
+    if (itup->t_info & INDEX_NULL_MASK)
+       return ((InsertIndexResult) NULL);
+    
+    hitem = _hash_formitem(itup);
+    
+    res = _hash_doinsert(rel, hitem);
+    
+    pfree(hitem);
+    
+    return (res);
+}
+
+
+/*
+ *  hashgettuple() -- Get the next tuple in the scan.
+ */
+char *
+hashgettuple(IndexScanDesc scan, ScanDirection dir)
+{
+    RetrieveIndexResult res;
+    
+    /*
+     *  If we've already initialized this scan, we can just advance it
+     *  in the appropriate direction.  If we haven't done so yet, we
+     *  call a routine to get the first item in the scan.
+     */
+    
+    if (ItemPointerIsValid(&(scan->currentItemData)))
+       res = _hash_next(scan, dir);
+    else
+       res = _hash_first(scan, dir);
+    
+    return ((char *) res);
+}
+
+
+/*
+ *  hashbeginscan() -- start a scan on a hash index
+ */
+char *
+hashbeginscan(Relation rel,
+             bool fromEnd,
+             uint16 keysz,
+             ScanKey scankey)
+{
+    IndexScanDesc scan;
+    HashScanOpaque so;
+    
+    scan = RelationGetIndexScan(rel, fromEnd, keysz, scankey);
+    so = (HashScanOpaque) palloc(sizeof(HashScanOpaqueData)); 
+    so->hashso_curbuf = so->hashso_mrkbuf = InvalidBuffer;
+    scan->opaque = so; 
+    scan->flags = 0x0;
+    
+    /* register scan in case we change pages it's using */
+    _hash_regscan(scan);
+    
+    return ((char *) scan);
+}
+
+/*
+ *  hashrescan() -- rescan an index relation
+ */
+void
+hashrescan(IndexScanDesc scan, bool fromEnd, ScanKey scankey)
+{
+    ItemPointer iptr;
+    HashScanOpaque so;
+    
+    so = (HashScanOpaque) scan->opaque;
+    
+    /* we hold a read lock on the current page in the scan */
+    if (ItemPointerIsValid(iptr = &(scan->currentItemData))) {
+       _hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
+       so->hashso_curbuf = InvalidBuffer;
+       ItemPointerSetInvalid(iptr);
+    }
+    if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) {
+       _hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
+       so->hashso_mrkbuf = InvalidBuffer;
+       ItemPointerSetInvalid(iptr);
+    }
+    
+    /* reset the scan key */
+    if (scan->numberOfKeys > 0) {
+       memmove(scan->keyData,
+               scankey,
+               scan->numberOfKeys * sizeof(ScanKeyData));
+    }
+}
+
+/*
+ *  hashendscan() -- close down a scan
+ */
+void
+hashendscan(IndexScanDesc scan)
+{
+    
+    ItemPointer iptr;
+    HashScanOpaque so;
+    
+    so = (HashScanOpaque) scan->opaque;
+    
+    /* release any locks we still hold */
+    if (ItemPointerIsValid(iptr = &(scan->currentItemData))) {
+       _hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
+       so->hashso_curbuf = InvalidBuffer;
+       ItemPointerSetInvalid(iptr);
+    }
+    
+    if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) {
+       if (BufferIsValid(so->hashso_mrkbuf))
+           _hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
+       so->hashso_mrkbuf = InvalidBuffer;
+       ItemPointerSetInvalid(iptr);
+    }
+    
+    /* don't need scan registered anymore */
+    _hash_dropscan(scan);
+    
+    /* be tidy */
+#ifdef PERFECT_MMGR
+    pfree (scan->opaque);
+#endif /* PERFECT_MMGR */
+}
+
+/*
+ *  hashmarkpos() -- save current scan position
+ *
+ */
+void
+hashmarkpos(IndexScanDesc scan)
+{
+    ItemPointer iptr;
+    HashScanOpaque so;
+    
+    /*  see if we ever call this code. if we do, then so_mrkbuf a
+     *  useful element in the scan->opaque structure. if this procedure
+     *  is never called, so_mrkbuf should be removed from the scan->opaque
+     *  structure. 
+     */
+    elog(NOTICE, "Hashmarkpos() called.");
+    
+    so = (HashScanOpaque) scan->opaque;
+    
+    /* release lock on old marked data, if any */
+    if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) {
+       _hash_relbuf(scan->relation, so->hashso_mrkbuf, HASH_READ);
+       so->hashso_mrkbuf = InvalidBuffer;
+       ItemPointerSetInvalid(iptr);
+    }
+    
+    /* bump lock on currentItemData and copy to currentMarkData */
+    if (ItemPointerIsValid(&(scan->currentItemData))) {
+       so->hashso_mrkbuf = _hash_getbuf(scan->relation,
+                                        BufferGetBlockNumber(so->hashso_curbuf),
+                                        HASH_READ);
+       scan->currentMarkData = scan->currentItemData;
+    }
+}
+
+/*
+ *  hashrestrpos() -- restore scan to last saved position
+ */
+void
+hashrestrpos(IndexScanDesc scan)
+{
+    ItemPointer iptr;
+    HashScanOpaque so;
+    
+    /*  see if we ever call this code. if we do, then so_mrkbuf a
+     *  useful element in the scan->opaque structure. if this procedure
+     *  is never called, so_mrkbuf should be removed from the scan->opaque
+     *  structure. 
+     */
+    elog(NOTICE, "Hashrestrpos() called.");
+    
+    so = (HashScanOpaque) scan->opaque;
+    
+    /* release lock on current data, if any */
+    if (ItemPointerIsValid(iptr = &(scan->currentItemData))) {
+       _hash_relbuf(scan->relation, so->hashso_curbuf, HASH_READ);
+       so->hashso_curbuf = InvalidBuffer;
+       ItemPointerSetInvalid(iptr);
+    }
+    
+    /* bump lock on currentMarkData and copy to currentItemData */
+    if (ItemPointerIsValid(&(scan->currentMarkData))) {
+       so->hashso_curbuf =
+           _hash_getbuf(scan->relation,
+                        BufferGetBlockNumber(so->hashso_mrkbuf),
+                        HASH_READ);
+       
+       scan->currentItemData = scan->currentMarkData;
+    }
+}
+
+/* stubs */
+void
+hashdelete(Relation rel, ItemPointer tid)
+{
+    /* adjust any active scans that will be affected by this deletion */
+    _hash_adjscans(rel, tid);
+    
+    /* delete the data from the page */
+    _hash_pagedel(rel, tid);
+}
+
diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c
new file mode 100644 (file)
index 0000000..f1084c4
--- /dev/null
@@ -0,0 +1,276 @@
+/*-------------------------------------------------------------------------
+ *
+ * hashfunc.c--
+ *    Comparison functions for hash access method.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    These functions are stored in pg_amproc.  For each operator class
+ *    defined on hash tables, they compute the hash value of the argument.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "utils/nabstime.h"
+
+uint32 hashint2(int16 key)
+{
+    return ((uint32) ~key);
+}
+
+uint32 hashint4(uint32 key)
+{
+    return (~key);
+}
+
+/* Hash function from Chris Torek. */
+uint32 hashfloat4(float32 keyp)
+{
+    int len;
+    int loop;
+    uint32 h;
+    char *kp = (char *) keyp;
+
+    len = sizeof(float32data);
+
+#define HASH4a   h = (h << 5) - h + *kp++;
+#define HASH4b   h = (h << 5) + h + *kp++;
+#define HASH4 HASH4b
+
+
+    h = 0;
+    if (len > 0) {
+       loop = (len + 8 - 1) >> 3;
+       
+       switch (len & (8 - 1)) {
+       case 0:
+           do {        /* All fall throughs */
+               HASH4;
+           case 7:
+               HASH4;
+           case 6:
+               HASH4;
+           case 5:
+               HASH4;
+           case 4:
+               HASH4;
+           case 3:
+               HASH4;
+           case 2:
+               HASH4;
+           case 1:
+               HASH4;
+           } while (--loop);
+       }
+    }
+    return (h);
+}      
+
+
+uint32 hashfloat8(float64 keyp)
+{
+    int len;
+    int loop;
+    uint32 h;
+    char *kp = (char *) keyp;
+
+    len = sizeof(float64data);
+
+#define HASH4a   h = (h << 5) - h + *kp++;
+#define HASH4b   h = (h << 5) + h + *kp++;
+#define HASH4 HASH4b
+
+
+    h = 0;
+    if (len > 0) {
+       loop = (len + 8 - 1) >> 3;
+       
+       switch (len & (8 - 1)) {
+       case 0:
+           do {        /* All fall throughs */
+               HASH4;
+           case 7:
+               HASH4;
+           case 6:
+               HASH4;
+           case 5:
+               HASH4;
+           case 4:
+               HASH4;
+           case 3:
+               HASH4;
+           case 2:
+               HASH4;
+           case 1:
+               HASH4;
+           } while (--loop);
+       }
+    }
+    return (h);
+}      
+
+
+uint32 hashoid(Oid key)
+{
+    return ((uint32) ~key);
+}
+
+
+uint32 hashchar(char key)
+{
+    int len;
+    uint32 h;
+
+    len = sizeof(char);
+
+#define PRIME1         37
+#define PRIME2         1048583
+
+    h = 0;
+    /* Convert char to integer */
+    h = h * PRIME1 ^ (key - ' ');
+    h %= PRIME2;
+    
+    return (h);
+}
+
+uint32 hashchar2(uint16 intkey)
+{
+    uint32 h;
+    int len;
+    char *key = (char *) &intkey;
+    h = 0;
+    len = sizeof(uint16);
+    /* Convert string to integer */
+    while (len--)
+       h = h * PRIME1 ^ (*key++ - ' ');
+    h %= PRIME2;
+       
+    return (h);
+}
+
+uint32 hashchar4(uint32 intkey)
+{
+    uint32 h;
+    int len;
+    char *key = (char *) &intkey;
+    h = 0;
+    len = sizeof(uint32);
+    /* Convert string to integer */
+    while (len--)
+       h = h * PRIME1 ^ (*key++ - ' ');
+    h %= PRIME2;
+       
+    return (h);
+}
+
+uint32 hashchar8(char *key)
+{
+    uint32 h;
+    int len;
+    h = 0;
+    len = sizeof(char8);
+    /* Convert string to integer */
+    while (len--)
+       h = h * PRIME1 ^ (*key++ - ' ');
+    h %= PRIME2;
+       
+    return (h);
+}
+
+uint32 hashname(NameData *n)
+{
+    uint32 h;
+    int len;
+    char *key;
+
+    key = n->data;
+    h = 0;
+    len = NAMEDATALEN;
+    /* Convert string to integer */
+    while (len--)
+       h = h * PRIME1 ^ (*key++ - ' ');
+    h %= PRIME2;
+       
+    return (h);
+}
+
+
+uint32 hashchar16(char *key)
+{
+    uint32 h;
+    int len;
+    h = 0;
+    len = sizeof(char16);
+    /* Convert string to integer */
+    while (len--)
+       h = h * PRIME1 ^ (*key++ - ' ');
+    h %= PRIME2;
+       
+    return (h);
+}
+
+
+/*
+ * (Comment from the original db3 hashing code: )
+ *
+ * "This is INCREDIBLY ugly, but fast.  We break the string up into 8 byte
+ * units.  On the first time through the loop we get the 'leftover bytes'
+ * (strlen % 8).  On every other iteration, we perform 8 HASHC's so we handle
+ * all 8 bytes.  Essentially, this saves us 7 cmp & branch instructions.  If
+ * this routine is heavily used enough, it's worth the ugly coding.
+ *
+ * "OZ's original sdbm hash"
+ */
+uint32 hashtext(struct varlena *key)
+{
+    int keylen;
+    char *keydata;
+    uint32 n;
+    int loop;
+
+    keydata = VARDATA(key);
+    keylen = VARSIZE(key);
+
+    /* keylen includes the four bytes in which string keylength is stored */
+    keylen -= sizeof(VARSIZE(key));
+
+#define HASHC   n = *keydata++ + 65599 * n
+
+    n = 0;
+    if (keylen > 0) {
+       loop = (keylen + 8 - 1) >> 3;
+       
+       switch (keylen & (8 - 1)) {
+       case 0:
+           do {        /* All fall throughs */
+               HASHC;
+           case 7:
+               HASHC;
+           case 6:
+               HASHC;
+           case 5:
+               HASHC;
+           case 4:
+               HASHC;
+           case 3:
+               HASHC;
+           case 2:
+               HASHC;
+           case 1:
+               HASHC;
+           } while (--loop);
+       }
+    }
+    return (n);
+}      
diff --git a/src/backend/access/hash/hashinsert.c b/src/backend/access/hash/hashinsert.c
new file mode 100644 (file)
index 0000000..4c04682
--- /dev/null
@@ -0,0 +1,239 @@
+/*-------------------------------------------------------------------------
+ *
+ * hashinsert.c--
+ *    Item insertion in hash tables for Postgres.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+#include "utils/excid.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/hash.h"
+
+static InsertIndexResult _hash_insertonpg(Relation rel, Buffer buf, int keysz, ScanKey scankey, HashItem hitem, Buffer metabuf);
+static OffsetNumber _hash_pgaddtup(Relation rel, Buffer buf, int keysz, ScanKey itup_scankey, Size itemsize, HashItem hitem);
+
+/*
+ *  _hash_doinsert() -- Handle insertion of a single HashItem in the table.
+ *
+ *     This routine is called by the public interface routines, hashbuild
+ *     and hashinsert.  By here, hashitem is filled in, and has a unique
+ *     (xid, seqno) pair. The datum to be used as a "key" is in the
+ *     hashitem. 
+ */
+InsertIndexResult
+_hash_doinsert(Relation rel, HashItem hitem)
+{
+    Buffer buf;
+    Buffer metabuf;
+    BlockNumber blkno;
+    HashMetaPage metap;
+    IndexTuple itup;
+    InsertIndexResult res;
+    ScanKey itup_scankey;
+    int natts;
+    Page page;
+    
+    metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
+    metap = (HashMetaPage) BufferGetPage(metabuf);
+    _hash_checkpage((Page) metap, LH_META_PAGE);
+    
+    /* we need a scan key to do our search, so build one */
+    itup = &(hitem->hash_itup);
+    if ((natts = rel->rd_rel->relnatts) != 1)
+       elog(WARN, "Hash indices valid for only one index key.");
+    itup_scankey = _hash_mkscankey(rel, itup, metap);
+    
+    /* 
+     * find the first page in the bucket chain containing this key and
+     * place it in buf.  _hash_search obtains a read lock for us.
+     */
+    _hash_search(rel, natts, itup_scankey, &buf, metap);
+    page = BufferGetPage(buf);
+    _hash_checkpage(page, LH_BUCKET_PAGE);
+
+    /*
+     * trade in our read lock for a write lock so that we can do the
+     * insertion.
+     */
+    blkno = BufferGetBlockNumber(buf);
+    _hash_relbuf(rel, buf, HASH_READ);
+    buf = _hash_getbuf(rel, blkno, HASH_WRITE);
+    
+    
+    /*
+     * XXX btree comment (haven't decided what to do in hash): don't
+     * think the bucket can be split while we're reading the metapage.
+     *
+     * If the page was split between the time that we surrendered our
+     * read lock and acquired our write lock, then this page may no
+     * longer be the right place for the key we want to insert.
+     */
+    
+    /* do the insertion */
+    res = _hash_insertonpg(rel, buf, natts, itup_scankey,
+                          hitem, metabuf);
+    
+    /* be tidy */
+    _hash_freeskey(itup_scankey);
+    
+    return (res);
+}
+
+/*
+ *  _hash_insertonpg() -- Insert a tuple on a particular page in the table.
+ *
+ *     This recursive procedure does the following things:
+ *
+ *         +  if necessary, splits the target page.  
+ *         +  inserts the tuple.
+ *
+ *     On entry, we must have the right buffer on which to do the
+ *     insertion, and the buffer must be pinned and locked.  On return,
+ *     we will have dropped both the pin and the write lock on the buffer.
+ *
+ */
+static InsertIndexResult
+_hash_insertonpg(Relation rel,
+                Buffer buf,
+                int keysz,
+                ScanKey scankey,
+                HashItem hitem,
+                Buffer metabuf)
+{
+    InsertIndexResult res; 
+    Page page;
+    BlockNumber itup_blkno;
+    OffsetNumber itup_off;
+    int itemsz;
+    HashPageOpaque pageopaque;
+    bool do_expand = false;     
+    Buffer ovflbuf;
+    HashMetaPage metap;
+    Bucket bucket;
+    
+    metap = (HashMetaPage) BufferGetPage(metabuf);
+    _hash_checkpage((Page) metap, LH_META_PAGE);
+    
+    page = BufferGetPage(buf);
+    _hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
+    pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
+    bucket = pageopaque->hasho_bucket;
+
+    itemsz = IndexTupleDSize(hitem->hash_itup)
+       + (sizeof(HashItemData) - sizeof(IndexTupleData));
+    itemsz = DOUBLEALIGN(itemsz);
+    
+    while (PageGetFreeSpace(page) < itemsz) {
+       /* 
+         * no space on this page; check for an overflow page 
+        */
+       if (BlockNumberIsValid(pageopaque->hasho_nextblkno)) {
+           /* 
+            * ovfl page exists; go get it.  if it doesn't have room,
+            * we'll find out next pass through the loop test above.
+            */
+           ovflbuf = _hash_getbuf(rel, pageopaque->hasho_nextblkno,
+                                  HASH_WRITE);
+           _hash_relbuf(rel, buf, HASH_WRITE);
+           buf = ovflbuf;
+           page = BufferGetPage(buf);
+       } else {
+           /* 
+            * we're at the end of the bucket chain and we haven't
+            * found a page with enough room.  allocate a new overflow
+            * page.
+            */
+           do_expand = true;
+           ovflbuf = _hash_addovflpage(rel, &metabuf, buf);
+           _hash_relbuf(rel, buf, HASH_WRITE);
+           buf = ovflbuf;
+           page = BufferGetPage(buf);
+
+           if (PageGetFreeSpace(page) < itemsz) {
+               /* it doesn't fit on an empty page -- give up */
+               elog(WARN, "hash item too large");
+           }
+       }
+       _hash_checkpage(page, LH_OVERFLOW_PAGE);
+       pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
+       Assert(pageopaque->hasho_bucket == bucket);
+    }
+
+    itup_off = _hash_pgaddtup(rel, buf, keysz, scankey, itemsz, hitem);
+    itup_blkno = BufferGetBlockNumber(buf);
+    
+    /* by here, the new tuple is inserted */
+    res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
+    
+    ItemPointerSet(&(res->pointerData), itup_blkno, itup_off);
+    
+    if (res != NULL) {
+       /* 
+        * Increment the number of keys in the table.
+        * We switch lock access type just for a moment
+        * to allow greater accessibility to the metapage. 
+        */
+       metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf,
+                                                 HASH_READ, HASH_WRITE);
+       metap->hashm_nkeys += 1;
+       metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf,
+                                                 HASH_WRITE, HASH_READ);
+       
+    }
+    
+    _hash_wrtbuf(rel, buf);
+    
+    if (do_expand || 
+       (metap->hashm_nkeys / (metap->hashm_maxbucket + 1))
+       > metap->hashm_ffactor) {
+       _hash_expandtable(rel, metabuf);
+    }
+    _hash_relbuf(rel, metabuf, HASH_READ);
+    return (res);
+}      
+
+/*
+ *  _hash_pgaddtup() -- add a tuple to a particular page in the index.
+ *
+ *     This routine adds the tuple to the page as requested, and keeps the
+ *     write lock and reference associated with the page's buffer.  It is
+ *     an error to call pgaddtup() without a write lock and reference.
+ */
+static OffsetNumber
+_hash_pgaddtup(Relation rel,
+              Buffer buf,
+              int keysz,
+              ScanKey itup_scankey,
+              Size itemsize,
+              HashItem hitem)
+{
+    OffsetNumber itup_off;
+    Page page;
+    
+    page = BufferGetPage(buf);
+    _hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
+
+    itup_off = OffsetNumberNext(PageGetMaxOffsetNumber(page));
+    (void) PageAddItem(page, (Item) hitem, itemsize, itup_off, LP_USED);
+    
+    /* write the buffer, but hold our lock */
+    _hash_wrtnorelbuf(rel, buf);
+    
+    return (itup_off);
+}
diff --git a/src/backend/access/hash/hashovfl.c b/src/backend/access/hash/hashovfl.c
new file mode 100644 (file)
index 0000000..ece1669
--- /dev/null
@@ -0,0 +1,614 @@
+/*-------------------------------------------------------------------------
+ *
+ * hashovfl.c--
+ *    Overflow page management code for the Postgres hash access method
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    Overflow pages look like ordinary relation pages.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+
+#include "utils/elog.h"
+#include "utils/rel.h"
+#include "utils/excid.h"
+
+#include "access/genam.h"
+#include "access/hash.h"
+
+static OverflowPageAddress _hash_getovfladdr(Relation rel, Buffer *metabufp);
+static uint32 _hash_firstfreebit(uint32 map);
+
+/*
+ *  _hash_addovflpage
+ *
+ *  Add an overflow page to the page currently pointed to by the buffer 
+ *  argument 'buf'. 
+ *
+ *  *Metabufp has a read lock upon entering the function; buf has a 
+ *  write lock. 
+ *  
+ */
+Buffer
+_hash_addovflpage(Relation rel, Buffer *metabufp, Buffer buf)
+{
+    
+    OverflowPageAddress oaddr;
+    BlockNumber ovflblkno;
+    Buffer ovflbuf;
+    HashMetaPage metap;
+    HashPageOpaque ovflopaque;
+    HashPageOpaque pageopaque;
+    Page page;
+    Page ovflpage;
+    
+    /* this had better be the last page in a bucket chain */
+    page = BufferGetPage(buf);
+    _hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
+    pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
+    Assert(!BlockNumberIsValid(pageopaque->hasho_nextblkno));
+    
+    metap = (HashMetaPage) BufferGetPage(*metabufp);
+    _hash_checkpage((Page) metap, LH_META_PAGE);
+
+    /* allocate an empty overflow page */
+    oaddr = _hash_getovfladdr(rel, metabufp);
+    if (oaddr == InvalidOvflAddress) {
+       elog(WARN, "_hash_addovflpage: problem with _hash_getovfladdr.");
+    }
+    ovflblkno = OADDR_TO_BLKNO(OADDR_OF(SPLITNUM(oaddr), OPAGENUM(oaddr)));
+    Assert(BlockNumberIsValid(ovflblkno));
+    ovflbuf = _hash_getbuf(rel, ovflblkno, HASH_WRITE);
+    Assert(BufferIsValid(ovflbuf));
+    ovflpage = BufferGetPage(ovflbuf);
+
+    /* initialize the new overflow page */
+    _hash_pageinit(ovflpage, BufferGetPageSize(ovflbuf));
+    ovflopaque = (HashPageOpaque) PageGetSpecialPointer(ovflpage);
+    ovflopaque->hasho_prevblkno = BufferGetBlockNumber(buf);
+    ovflopaque->hasho_nextblkno = InvalidBlockNumber;
+    ovflopaque->hasho_flag = LH_OVERFLOW_PAGE;
+    ovflopaque->hasho_oaddr = oaddr;
+    ovflopaque->hasho_bucket = pageopaque->hasho_bucket;
+    _hash_wrtnorelbuf(rel, ovflbuf);
+    
+    /* logically chain overflow page to previous page */
+    pageopaque->hasho_nextblkno = ovflblkno;
+    _hash_wrtnorelbuf(rel, buf);
+    return (ovflbuf);
+}
+
+/*
+ *  _hash_getovfladdr()
+ *
+ *  Find an available overflow page and return its address. 
+ *
+ *  When we enter this function, we have a read lock on *metabufp which
+ *  we change to a write lock immediately. Before exiting, the write lock
+ *  is exchanged for a read lock. 
+ *
+ */
+static OverflowPageAddress
+_hash_getovfladdr(Relation rel, Buffer *metabufp)
+{
+    HashMetaPage metap;
+    Buffer mapbuf;
+    BlockNumber blkno;
+    PageOffset offset;
+    OverflowPageAddress oaddr;
+    SplitNumber splitnum;
+    uint32 *freep;
+    uint32 max_free; 
+    uint32 bit;
+    uint32 first_page; 
+    uint32 free_bit; 
+    uint32 free_page; 
+    uint32 in_use_bits;
+    uint32 i, j;
+    
+    metap = (HashMetaPage) _hash_chgbufaccess(rel, metabufp, HASH_READ, HASH_WRITE);
+    
+    splitnum = metap->OVFL_POINT;
+    max_free = metap->SPARES[splitnum];
+    
+    free_page = (max_free - 1) >> (metap->BSHIFT + BYTE_TO_BIT);
+    free_bit = (max_free - 1) & (BMPGSZ_BIT(metap) - 1);
+    
+    /* Look through all the free maps to find the first free block */
+    first_page = metap->LAST_FREED >> (metap->BSHIFT + BYTE_TO_BIT);
+    for ( i = first_page; i <= free_page; i++ ) {
+       Page mappage;
+
+       blkno = metap->hashm_mapp[i];
+       mapbuf = _hash_getbuf(rel, blkno, HASH_WRITE);
+       mappage = BufferGetPage(mapbuf);
+       _hash_checkpage(mappage, LH_BITMAP_PAGE);
+       freep = HashPageGetBitmap(mappage);
+       Assert(freep);
+       
+       if (i == free_page)
+           in_use_bits = free_bit;
+       else
+           in_use_bits = BMPGSZ_BIT(metap) - 1;
+       
+       if (i == first_page) {
+           bit = metap->LAST_FREED & (BMPGSZ_BIT(metap) - 1);
+           j = bit / BITS_PER_MAP;
+           bit = bit & ~(BITS_PER_MAP - 1);
+       } else {
+           bit = 0;
+           j = 0;
+       }
+       for (; bit <= in_use_bits; j++, bit += BITS_PER_MAP)
+           if (freep[j] != ALL_SET)
+               goto found;
+    }
+    
+    /* No Free Page Found - have to allocate a new page */
+    metap->LAST_FREED = metap->SPARES[splitnum];
+    metap->SPARES[splitnum]++;
+    offset = metap->SPARES[splitnum] -
+       (splitnum ? metap->SPARES[splitnum - 1] : 0);
+    
+#define        OVMSG   "HASH: Out of overflow pages.  Out of luck.\n"
+    
+    if (offset > SPLITMASK) {
+       if (++splitnum >= NCACHED) {
+           elog(WARN, OVMSG);
+       }
+       metap->OVFL_POINT = splitnum;
+       metap->SPARES[splitnum] = metap->SPARES[splitnum-1];
+       metap->SPARES[splitnum-1]--;
+       offset = 0;
+    }
+    
+    /* Check if we need to allocate a new bitmap page */
+    if (free_bit == BMPGSZ_BIT(metap) - 1) {
+       /* won't be needing old map page */
+
+       _hash_relbuf(rel, mapbuf, HASH_WRITE);
+
+       free_page++;
+       if (free_page >= NCACHED) {
+           elog(WARN, OVMSG);
+       }
+       
+       /*
+        * This is tricky.  The 1 indicates that you want the new page
+        * allocated with 1 clear bit.  Actually, you are going to
+        * allocate 2 pages from this map.  The first is going to be
+        * the map page, the second is the overflow page we were
+        * looking for.  The init_bitmap routine automatically, sets
+        * the first bit of itself to indicate that the bitmap itself
+        * is in use.  We would explicitly set the second bit, but
+        * don't have to if we tell init_bitmap not to leave it clear
+        * in the first place.
+        */
+       if (_hash_initbitmap(rel, metap, OADDR_OF(splitnum, offset),
+                            1, free_page)) {
+           elog(WARN, "overflow_page: problem with _hash_initbitmap.");
+       }
+       metap->SPARES[splitnum]++;
+       offset++;
+       if (offset > SPLITMASK) {
+           if (++splitnum >= NCACHED) {
+               elog(WARN, OVMSG);
+           }
+           metap->OVFL_POINT = splitnum;
+           metap->SPARES[splitnum] = metap->SPARES[splitnum-1];
+           metap->SPARES[splitnum-1]--;
+           offset = 0;
+       }
+    } else {
+       
+       /*
+        * Free_bit addresses the last used bit.  Bump it to address
+        * the first available bit.
+        */
+       free_bit++;
+       SETBIT(freep, free_bit);
+       _hash_wrtbuf(rel, mapbuf);
+    }
+    
+    /* Calculate address of the new overflow page */
+    oaddr = OADDR_OF(splitnum, offset);
+    _hash_chgbufaccess(rel, metabufp, HASH_WRITE, HASH_READ);
+    return (oaddr);
+    
+ found:
+    bit = bit + _hash_firstfreebit(freep[j]);
+    SETBIT(freep, bit);
+    _hash_wrtbuf(rel, mapbuf);
+    
+    /*
+     * Bits are addressed starting with 0, but overflow pages are addressed
+     * beginning at 1. Bit is a bit addressnumber, so we need to increment
+     * it to convert it to a page number.
+     */
+    
+    bit = 1 + bit + (i * BMPGSZ_BIT(metap));
+    if (bit >= metap->LAST_FREED) {
+       metap->LAST_FREED = bit - 1;
+    }
+    
+    /* Calculate the split number for this page */
+    for (i = 0; (i < splitnum) && (bit > metap->SPARES[i]); i++)
+       ;
+    offset = (i ? bit - metap->SPARES[i - 1] : bit);
+    if (offset >= SPLITMASK) {
+       elog(WARN, OVMSG);
+    }
+    
+    /* initialize this page */
+    oaddr = OADDR_OF(i, offset);
+    _hash_chgbufaccess(rel, metabufp, HASH_WRITE, HASH_READ);
+    return (oaddr);
+}
+
+/*
+ *  _hash_firstfreebit()
+ *
+ *  Return the first bit that is not set in the argument 'map'. This
+ *  function is used to find an available overflow page within a
+ *  splitnumber. 
+ * 
+ */
+static uint32
+_hash_firstfreebit(uint32 map)
+{
+    uint32 i, mask;
+    
+    mask = 0x1;
+    for (i = 0; i < BITS_PER_MAP; i++) {
+       if (!(mask & map))
+           return (i);
+       mask = mask << 1;
+    }
+    return (i);
+}
+
+/*
+ *  _hash_freeovflpage() - 
+ *
+ *  Mark this overflow page as free and return a buffer with 
+ *  the page that follows it (which may be defined as
+ *  InvalidBuffer). 
+ *
+ */
+Buffer
+_hash_freeovflpage(Relation rel, Buffer ovflbuf)
+{
+    HashMetaPage metap;
+    Buffer metabuf;
+    Buffer mapbuf;
+    BlockNumber prevblkno;
+    BlockNumber blkno;
+    BlockNumber nextblkno;
+    HashPageOpaque ovflopaque;
+    Page ovflpage;
+    Page mappage;
+    OverflowPageAddress addr;
+    SplitNumber splitnum;
+    uint32 *freep;
+    uint32 ovflpgno;
+    int32 bitmappage, bitmapbit;
+    Bucket bucket;
+    
+    metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_WRITE);
+    metap = (HashMetaPage) BufferGetPage(metabuf);
+    _hash_checkpage((Page) metap, LH_META_PAGE);
+    
+    ovflpage = BufferGetPage(ovflbuf);
+    _hash_checkpage(ovflpage, LH_OVERFLOW_PAGE);
+    ovflopaque = (HashPageOpaque) PageGetSpecialPointer(ovflpage);
+    addr = ovflopaque->hasho_oaddr;
+    nextblkno = ovflopaque->hasho_nextblkno;
+    prevblkno = ovflopaque->hasho_prevblkno;
+    bucket = ovflopaque->hasho_bucket;
+    (void) memset(ovflpage, 0, BufferGetPageSize(ovflbuf));
+    _hash_wrtbuf(rel, ovflbuf);
+    
+    /* 
+     * fix up the bucket chain.  this is a doubly-linked list, so we
+     * must fix up the bucket chain members behind and ahead of the
+     * overflow page being deleted.
+     *
+     * XXX this should look like:
+     * - lock prev/next
+     * - modify/write prev/next (how to do write ordering with a
+     * doubly-linked list???)
+     * - unlock prev/next
+     */
+    if (BlockNumberIsValid(prevblkno)) {
+       Buffer prevbuf = _hash_getbuf(rel, prevblkno, HASH_WRITE);
+       Page prevpage = BufferGetPage(prevbuf);
+       HashPageOpaque prevopaque =
+           (HashPageOpaque) PageGetSpecialPointer(prevpage);
+
+       _hash_checkpage(prevpage, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
+       Assert(prevopaque->hasho_bucket == bucket);
+       prevopaque->hasho_nextblkno = nextblkno;
+       _hash_wrtbuf(rel, prevbuf);
+    }
+    if (BlockNumberIsValid(nextblkno)) {
+       Buffer nextbuf = _hash_getbuf(rel, nextblkno, HASH_WRITE);
+       Page nextpage = BufferGetPage(nextbuf);
+       HashPageOpaque nextopaque =
+           (HashPageOpaque) PageGetSpecialPointer(nextpage);
+       
+       _hash_checkpage(nextpage, LH_OVERFLOW_PAGE);
+       Assert(nextopaque->hasho_bucket == bucket);
+       nextopaque->hasho_prevblkno = prevblkno;
+       _hash_wrtbuf(rel, nextbuf);
+    }
+    
+    /* 
+     * Fix up the overflow page bitmap that tracks this particular
+     * overflow page. The bitmap can be found in the MetaPageData
+     * array element hashm_mapp[bitmappage].
+     */
+    splitnum = (addr >> SPLITSHIFT);
+    ovflpgno =
+       (splitnum ? metap->SPARES[splitnum - 1] : 0) + (addr & SPLITMASK) - 1;
+    
+    if (ovflpgno < metap->LAST_FREED) {
+       metap->LAST_FREED = ovflpgno;
+    }
+    
+    bitmappage = (ovflpgno >> (metap->BSHIFT + BYTE_TO_BIT));
+    bitmapbit = ovflpgno & (BMPGSZ_BIT(metap) - 1);
+    
+    blkno = metap->hashm_mapp[bitmappage];
+    mapbuf = _hash_getbuf(rel, blkno, HASH_WRITE);
+    mappage = BufferGetPage(mapbuf);
+    _hash_checkpage(mappage, LH_BITMAP_PAGE);
+    freep = HashPageGetBitmap(mappage);
+    CLRBIT(freep, bitmapbit);
+    _hash_wrtbuf(rel, mapbuf);
+    
+    _hash_relbuf(rel, metabuf, HASH_WRITE);
+    
+    /* 
+     * now instantiate the page that replaced this one, 
+     * if it exists, and return that buffer with a write lock.
+     */
+    if (BlockNumberIsValid(nextblkno)) {
+       return (_hash_getbuf(rel, nextblkno, HASH_WRITE));
+    } else {
+       return (InvalidBuffer);
+    }
+}
+
+
+/*
+ *  _hash_initbitmap()
+ *  
+ *   Initialize a new bitmap page.  The metapage has a write-lock upon
+ *   entering the function.
+ *
+ * 'pnum' is the OverflowPageAddress of the new bitmap page.
+ * 'nbits' is how many bits to clear (i.e., make available) in the new
+ * bitmap page.  the remainder of the bits (as well as the first bit,
+ * representing the bitmap page itself) will be set.
+ * 'ndx' is the 0-based offset of the new bitmap page within the
+ * metapage's array of bitmap page OverflowPageAddresses.
+ */
+
+#define INT_MASK       ((1 << INT_TO_BIT) -1)
+
+int32
+_hash_initbitmap(Relation rel,
+                HashMetaPage metap,
+                int32 pnum,
+                int32 nbits,
+                int32 ndx)
+{
+    Buffer buf;
+    BlockNumber blkno;
+    Page pg;
+    HashPageOpaque op;
+    uint32 *freep;
+    int clearbytes, clearints;
+    
+    blkno = OADDR_TO_BLKNO(pnum);
+    buf = _hash_getbuf(rel, blkno, HASH_WRITE);
+    pg = BufferGetPage(buf);
+    _hash_pageinit(pg, BufferGetPageSize(buf));
+    op = (HashPageOpaque) PageGetSpecialPointer(pg);
+    op->hasho_oaddr = InvalidOvflAddress;
+    op->hasho_prevblkno = InvalidBlockNumber;
+    op->hasho_nextblkno = InvalidBlockNumber;
+    op->hasho_flag = LH_BITMAP_PAGE;
+    op->hasho_bucket = -1;
+
+    freep = HashPageGetBitmap(pg);
+
+    /* set all of the bits above 'nbits' to 1 */
+    clearints = ((nbits - 1) >> INT_TO_BIT) + 1;
+    clearbytes = clearints << INT_TO_BYTE;
+    (void) memset((char *) freep, 0, clearbytes);
+    (void) memset(((char *) freep) + clearbytes, 0xFF,
+                 BMPGSZ_BYTE(metap) - clearbytes);
+    freep[clearints - 1] = ALL_SET << (nbits & INT_MASK);
+
+    /* bit 0 represents the new bitmap page */
+    SETBIT(freep, 0);
+        
+    /* metapage already has a write lock */
+    metap->hashm_nmaps++;
+    metap->hashm_mapp[ndx] = blkno;
+    
+    /* write out the new bitmap page (releasing its locks) */
+    _hash_wrtbuf(rel, buf);
+
+    return (0);
+}
+
+
+/*
+ *  _hash_squeezebucket(rel, bucket)
+ *
+ *  Try to squeeze the tuples onto pages occuring earlier in the
+ *  bucket chain in an attempt to free overflow pages. When we start
+ *  the "squeezing", the page from which we start taking tuples (the
+ *  "read" page) is the last bucket in the bucket chain and the page
+ *  onto which we start squeezing tuples (the "write" page) is the
+ *  first page in the bucket chain.  The read page works backward and
+ *  the write page works forward; the procedure terminates when the
+ *  read page and write page are the same page.
+ */
+void
+_hash_squeezebucket(Relation rel,
+                   HashMetaPage metap, 
+                   Bucket bucket)
+{
+    Buffer wbuf;
+    Buffer rbuf;
+    BlockNumber wblkno;                
+    BlockNumber rblkno;                
+    Page wpage;
+    Page rpage;
+    HashPageOpaque wopaque;
+    HashPageOpaque ropaque;
+    OffsetNumber woffnum;
+    OffsetNumber roffnum;
+    HashItem hitem;
+    int itemsz;
+    
+/*    elog(DEBUG, "_hash_squeezebucket: squeezing bucket %d", bucket); */
+
+    /*
+     * start squeezing into the base bucket page.
+     */
+    wblkno = BUCKET_TO_BLKNO(bucket);
+    wbuf = _hash_getbuf(rel, wblkno, HASH_WRITE);
+    wpage = BufferGetPage(wbuf);
+    _hash_checkpage(wpage, LH_BUCKET_PAGE);
+    wopaque = (HashPageOpaque) PageGetSpecialPointer(wpage);
+    
+    /*
+     * if there aren't any overflow pages, there's nothing to squeeze.
+     */
+    if (!BlockNumberIsValid(wopaque->hasho_nextblkno)) {
+       _hash_relbuf(rel, wbuf, HASH_WRITE);
+       return;
+    }
+    
+    /*
+     * find the last page in the bucket chain by starting at the base
+     * bucket page and working forward.
+     *
+     * XXX if chains tend to be long, we should probably move forward
+     * using HASH_READ and then _hash_chgbufaccess to HASH_WRITE when
+     * we reach the end.  if they are short we probably don't care
+     * very much.  if the hash function is working at all, they had
+     * better be short..
+     */
+    ropaque = wopaque;
+    do {
+       rblkno = ropaque->hasho_nextblkno;
+       if (ropaque != wopaque) {
+           _hash_relbuf(rel, rbuf, HASH_WRITE);
+       }
+       rbuf = _hash_getbuf(rel, rblkno, HASH_WRITE);
+       rpage = BufferGetPage(rbuf);
+       _hash_checkpage(rpage, LH_OVERFLOW_PAGE);
+       Assert(!PageIsEmpty(rpage));
+       ropaque = (HashPageOpaque) PageGetSpecialPointer(rpage);
+       Assert(ropaque->hasho_bucket == bucket);
+    } while (BlockNumberIsValid(ropaque->hasho_nextblkno));
+
+    /*
+     * squeeze the tuples.
+     */
+    roffnum = FirstOffsetNumber;
+    for(;;) {
+       hitem = (HashItem) PageGetItem(rpage, PageGetItemId(rpage, roffnum));
+       itemsz = IndexTupleDSize(hitem->hash_itup) 
+           + (sizeof(HashItemData) - sizeof(IndexTupleData));
+       itemsz = DOUBLEALIGN(itemsz);
+       
+       /*
+        * walk up the bucket chain, looking for a page big enough for
+        * this item.
+        */
+       while (PageGetFreeSpace(wpage) < itemsz) {
+           wblkno = wopaque->hasho_nextblkno;
+
+           _hash_wrtbuf(rel, wbuf);
+
+           if (!BlockNumberIsValid(wblkno) || (rblkno == wblkno)) {
+               _hash_wrtbuf(rel, rbuf);
+               /* wbuf is already released */
+               return;
+           }
+           
+           wbuf = _hash_getbuf(rel, wblkno, HASH_WRITE);
+           wpage = BufferGetPage(wbuf);
+           _hash_checkpage(wpage, LH_OVERFLOW_PAGE);
+           Assert(!PageIsEmpty(wpage));
+           wopaque = (HashPageOpaque) PageGetSpecialPointer(wpage);
+           Assert(wopaque->hasho_bucket == bucket);
+       }
+       
+       /* 
+        * if we're here, we have found room so insert on the "write"
+        * page.
+        */
+       woffnum = OffsetNumberNext(PageGetMaxOffsetNumber(wpage));
+       (void) PageAddItem(wpage, (Item) hitem, itemsz, woffnum, LP_USED);
+       
+       /* 
+        * delete the tuple from the "read" page.
+        * PageIndexTupleDelete repacks the ItemId array, so 'roffnum'
+        * will be "advanced" to the "next" ItemId.
+        */
+       PageIndexTupleDelete(rpage, roffnum);
+       _hash_wrtnorelbuf(rel, rbuf);
+       
+       /*
+        * if the "read" page is now empty because of the deletion,
+        * free it.
+        */
+       if (PageIsEmpty(rpage) && (ropaque->hasho_flag & LH_OVERFLOW_PAGE)) {
+           rblkno = ropaque->hasho_prevblkno;
+           Assert(BlockNumberIsValid(rblkno));
+
+           /*
+            * free this overflow page.  the extra _hash_relbuf is
+            * because _hash_freeovflpage gratuitously returns the
+            * next page (we want the previous page and will get it
+            * ourselves later).
+            */
+           rbuf = _hash_freeovflpage(rel, rbuf);
+           if (BufferIsValid(rbuf)) {
+               _hash_relbuf(rel, rbuf, HASH_WRITE);
+           }
+           
+           if (rblkno == wblkno) {
+               /* rbuf is already released */
+               _hash_wrtbuf(rel, wbuf);
+               return;
+           }
+           
+           rbuf = _hash_getbuf(rel, rblkno, HASH_WRITE);
+           rpage = BufferGetPage(rbuf);
+           _hash_checkpage(rpage, LH_OVERFLOW_PAGE);
+           Assert(!PageIsEmpty(rpage));
+           ropaque = (HashPageOpaque) PageGetSpecialPointer(rpage);
+           Assert(ropaque->hasho_bucket == bucket);
+
+           roffnum = FirstOffsetNumber;
+       }
+    }
+}
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
new file mode 100644 (file)
index 0000000..2b5dbae
--- /dev/null
@@ -0,0 +1,669 @@
+/*-------------------------------------------------------------------------
+ *
+ * hashpage.c--
+ *    Hash table page management code for the Postgres hash access method
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    Postgres hash pages look like ordinary relation pages.  The opaque
+ *    data at high addresses includes information about the page including
+ *    whether a page is an overflow page or a true bucket, the block 
+ *    numbers of the preceding and following pages, and the overflow
+ *    address of the page if it is an overflow page.
+ *
+ *    The first page in a hash relation, page zero, is special -- it stores
+ *    information describing the hash table; it is referred to as teh
+ *    "meta page." Pages one and higher store the actual data. 
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+
+#include "utils/elog.h"
+#include "utils/rel.h"
+#include "utils/excid.h"
+
+#include "access/genam.h"
+#include "access/hash.h"
+
+static void _hash_setpagelock(Relation rel, BlockNumber blkno, int access);
+static void _hash_unsetpagelock(Relation rel, BlockNumber blkno, int access);
+static void _hash_splitpage(Relation rel, Buffer metabuf, Bucket obucket, Bucket nbucket);
+
+/*  
+ *  We use high-concurrency locking on hash indices.  There are two cases in
+ *  which we don't do locking.  One is when we're building the index.
+ *  Since the creating transaction has not committed, no one can see
+ *  the index, and there's no reason to share locks.  The second case
+ *  is when we're just starting up the database system.  We use some
+ *  special-purpose initialization code in the relation cache manager
+ *  (see utils/cache/relcache.c) to allow us to do indexed scans on
+ *  the system catalogs before we'd normally be able to.  This happens
+ *  before the lock table is fully initialized, so we can't use it.
+ *  Strictly speaking, this violates 2pl, but we don't do 2pl on the
+ *  system catalogs anyway.
+ */
+
+
+#define USELOCKING     (!BuildingHash && !IsInitProcessingMode())
+
+
+/*
+ *  _hash_metapinit() -- Initialize the metadata page of a hash index,
+ *             the two buckets that we begin with and the initial
+ *             bitmap page.
+ */
+void
+_hash_metapinit(Relation rel)
+{
+    HashMetaPage metap;
+    HashPageOpaque pageopaque;
+    Buffer metabuf;
+    Buffer buf;
+    Page pg;
+    int nbuckets;
+    uint32 nelem;                      /* number elements */
+    uint32 lg2nelem;                   /* _hash_log2(nelem)   */
+    uint32 nblocks;
+    uint16 i;
+    
+    /* can't be sharing this with anyone, now... */
+    if (USELOCKING)
+       RelationSetLockForWrite(rel);
+    
+    if ((nblocks = RelationGetNumberOfBlocks(rel)) != 0) {
+       elog(WARN, "Cannot initialize non-empty hash table %s",
+            RelationGetRelationName(rel));
+    }
+    
+    metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_WRITE);
+    pg = BufferGetPage(metabuf);
+    metap = (HashMetaPage) pg;
+    _hash_pageinit(pg, BufferGetPageSize(metabuf));
+    
+    metap->hashm_magic                 = HASH_MAGIC;
+    metap->hashm_version       = HASH_VERSION;
+    metap->hashm_nkeys                 = 0;
+    metap->hashm_nmaps                 = 0;
+    metap->hashm_ffactor       = DEFAULT_FFACTOR;
+    metap->hashm_bsize                 = BufferGetPageSize(metabuf);
+    metap->hashm_bshift                = _hash_log2(metap->hashm_bsize);
+    for (i = metap->hashm_bshift; i > 0; --i) {
+       if ((1 << i) < (metap->hashm_bsize -
+                       (DOUBLEALIGN(sizeof(PageHeaderData)) +
+                        DOUBLEALIGN(sizeof(HashPageOpaqueData))))) {
+           break;
+       }
+    }
+    Assert(i);
+    metap->hashm_bmsize                = 1 << i;
+    metap->hashm_procid                = index_getprocid(rel, 1, HASHPROC);
+    
+    /* 
+     * Make nelem = 2 rather than 0 so that we end up allocating space 
+     * for the next greater power of two number of buckets. 
+     */
+    nelem = 2;
+    lg2nelem = 1;              /*_hash_log2(MAX(nelem, 2)) */
+    nbuckets = 2;              /*1 << lg2nelem */
+    
+    memset((char *) metap->hashm_spares, 0, sizeof(metap->hashm_spares));
+    memset((char *) metap->hashm_mapp, 0, sizeof(metap->hashm_mapp));
+    
+    metap->hashm_spares[lg2nelem]     = 2;     /* lg2nelem + 1 */
+    metap->hashm_spares[lg2nelem + 1] = 2;     /* lg2nelem + 1 */
+    metap->hashm_ovflpoint            = 1;     /* lg2nelem */
+    metap->hashm_lastfreed            = 2;
+    
+    metap->hashm_maxbucket = metap->hashm_lowmask = 1;         /* nbuckets - 1 */
+    metap->hashm_highmask  = 3;                         /* (nbuckets << 1) - 1 */
+    
+    pageopaque = (HashPageOpaque) PageGetSpecialPointer(pg);
+    pageopaque->hasho_oaddr = InvalidOvflAddress;
+    pageopaque->hasho_prevblkno = InvalidBlockNumber;
+    pageopaque->hasho_nextblkno = InvalidBlockNumber;
+    pageopaque->hasho_flag = LH_META_PAGE;
+    pageopaque->hasho_bucket = -1;
+
+    /* 
+     * First bitmap page is at: splitpoint lg2nelem page offset 1 which
+     * turns out to be page 3. Couldn't initialize page 3  until we created
+     * the first two buckets above. 
+     */
+    if (_hash_initbitmap(rel, metap, OADDR_OF(lg2nelem, 1), lg2nelem + 1, 0))
+       elog(WARN, "Problem with _hash_initbitmap.");
+
+    /* all done */
+    _hash_wrtnorelbuf(rel, metabuf);
+    
+    /* 
+     * initialize the first two buckets 
+     */
+    for (i = 0; i <= 1; i++) {
+       buf = _hash_getbuf(rel, BUCKET_TO_BLKNO(i), HASH_WRITE);
+       pg = BufferGetPage(buf);
+       _hash_pageinit(pg, BufferGetPageSize(buf));
+       pageopaque = (HashPageOpaque) PageGetSpecialPointer(pg);
+       pageopaque->hasho_oaddr = InvalidOvflAddress;
+       pageopaque->hasho_prevblkno = InvalidBlockNumber;
+       pageopaque->hasho_nextblkno = InvalidBlockNumber;
+       pageopaque->hasho_flag = LH_BUCKET_PAGE;
+       pageopaque->hasho_bucket = i;
+       _hash_wrtbuf(rel, buf);
+    }
+    
+    _hash_relbuf(rel, metabuf, HASH_WRITE);
+    
+    if (USELOCKING)
+       RelationUnsetLockForWrite(rel);
+}
+
+/*
+ *  _hash_getbuf() -- Get a buffer by block number for read or write.
+ *
+ *     When this routine returns, the appropriate lock is set on the
+ *     requested buffer its reference count is correct.
+ *
+ *     XXX P_NEW is not used because, unlike the tree structures, we
+ *     need the bucket blocks to be at certain block numbers.  we must
+ *     depend on the caller to call _hash_pageinit on the block if it
+ *     knows that this is a new block.
+ */
+Buffer
+_hash_getbuf(Relation rel, BlockNumber blkno, int access)
+{
+    Buffer buf;
+    
+    if (blkno == P_NEW) {
+       elog(WARN, "_hash_getbuf: internal error: hash AM does not use P_NEW");
+    }
+    switch (access) {
+    case HASH_WRITE:
+    case HASH_READ:
+       _hash_setpagelock(rel, blkno, access);
+       break;
+    default:
+       elog(WARN, "_hash_getbuf: invalid access (%d) on new blk: %.*s",
+            access, NAMEDATALEN, RelationGetRelationName(rel));
+       break;
+    }
+    buf = ReadBuffer(rel, blkno);
+    
+    /* ref count and lock type are correct */
+    return (buf);
+}
+
+/*
+ *  _hash_relbuf() -- release a locked buffer.
+ */
+void
+_hash_relbuf(Relation rel, Buffer buf, int access)
+{
+    BlockNumber blkno;
+    
+    blkno = BufferGetBlockNumber(buf);
+    
+    switch (access) {
+    case HASH_WRITE:
+    case HASH_READ:
+       _hash_unsetpagelock(rel, blkno, access);
+       break;
+    default:
+       elog(WARN, "_hash_relbuf: invalid access (%d) on blk %x: %.*s",
+            access, blkno, NAMEDATALEN, RelationGetRelationName(rel));
+    }
+    
+    ReleaseBuffer(buf);
+}
+
+/*
+ *  _hash_wrtbuf() -- write a hash page to disk.
+ *
+ *     This routine releases the lock held on the buffer and our reference
+ *     to it.  It is an error to call _hash_wrtbuf() without a write lock
+ *     or a reference to the buffer.
+ */
+void
+_hash_wrtbuf(Relation rel, Buffer buf)
+{
+    BlockNumber blkno;
+    
+    blkno = BufferGetBlockNumber(buf);
+    WriteBuffer(buf);
+    _hash_unsetpagelock(rel, blkno, HASH_WRITE);
+}
+
+/*
+ *  _hash_wrtnorelbuf() -- write a hash page to disk, but do not release
+ *                      our reference or lock.
+ *
+ *     It is an error to call _hash_wrtnorelbuf() without a write lock
+ *     or a reference to the buffer.
+ */
+void
+_hash_wrtnorelbuf(Relation rel, Buffer buf)
+{
+    BlockNumber blkno;
+    
+    blkno = BufferGetBlockNumber(buf);
+    WriteNoReleaseBuffer(buf);
+}
+
+Page
+_hash_chgbufaccess(Relation rel,
+                  Buffer *bufp,
+                  int from_access,
+                  int to_access)
+{
+    BlockNumber blkno;
+    
+    blkno = BufferGetBlockNumber(*bufp);
+    
+    switch (from_access) {
+    case HASH_WRITE:
+       _hash_wrtbuf(rel, *bufp);
+       break;
+    case HASH_READ:
+       _hash_relbuf(rel, *bufp, from_access);
+       break;
+    default:
+       elog(WARN, "_hash_chgbufaccess: invalid access (%d) on blk %x: %.*s",
+            from_access, blkno, NAMEDATALEN, RelationGetRelationName(rel));
+       break;
+    }
+    *bufp = _hash_getbuf(rel, blkno, to_access);
+    return (BufferGetPage(*bufp));
+}
+
+/*
+ *  _hash_pageinit() -- Initialize a new page.
+ */
+void
+_hash_pageinit(Page page, Size size)
+{
+    Assert(((PageHeader) page)->pd_lower == 0);
+    Assert(((PageHeader) page)->pd_upper == 0);
+    Assert(((PageHeader) page)->pd_special == 0);
+
+    /*
+     *  Cargo-cult programming -- don't really need this to be zero, but
+     *  creating new pages is an infrequent occurrence and it makes me feel
+     *  good when I know they're empty.
+     */
+    memset(page, 0, size);
+    
+    PageInit(page, size, sizeof(HashPageOpaqueData));
+}
+
+static void
+_hash_setpagelock(Relation rel,
+                 BlockNumber blkno,
+                 int access)
+{
+    ItemPointerData iptr;
+    
+    if (USELOCKING) {
+       ItemPointerSet(&iptr, blkno, 1);
+       
+       switch (access) {
+       case HASH_WRITE:
+           RelationSetSingleWLockPage(rel, &iptr);
+           break;
+       case HASH_READ:
+           RelationSetSingleRLockPage(rel, &iptr);
+           break;
+       default:
+           elog(WARN, "_hash_setpagelock: invalid access (%d) on blk %x: %.*s",
+                access, blkno, NAMEDATALEN, RelationGetRelationName(rel));
+           break;
+       }
+    }
+}
+
+static void
+_hash_unsetpagelock(Relation rel,
+                   BlockNumber blkno,
+                   int access)
+{
+    ItemPointerData iptr;
+    
+    if (USELOCKING) {
+       ItemPointerSet(&iptr, blkno, 1);
+       
+       switch (access) {
+       case HASH_WRITE:
+           RelationUnsetSingleWLockPage(rel, &iptr);
+           break;
+       case HASH_READ:
+           RelationUnsetSingleRLockPage(rel, &iptr);
+           break;
+       default:
+           elog(WARN, "_hash_unsetpagelock: invalid access (%d) on blk %x: %.*s",
+                access, blkno, NAMEDATALEN, RelationGetRelationName(rel));
+           break;
+       }
+    }
+}
+
+void
+_hash_pagedel(Relation rel, ItemPointer tid)
+{
+    Buffer buf;
+    Buffer metabuf;
+    Page page;
+    BlockNumber blkno;
+    OffsetNumber offno;
+    HashMetaPage metap;
+    HashPageOpaque opaque;
+    
+    blkno = ItemPointerGetBlockNumber(tid);
+    offno = ItemPointerGetOffsetNumber(tid);
+    
+    buf = _hash_getbuf(rel, blkno, HASH_WRITE);
+    page = BufferGetPage(buf);
+    _hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
+    opaque = (HashPageOpaque) PageGetSpecialPointer(page);
+    
+    PageIndexTupleDelete(page, offno);
+    _hash_wrtnorelbuf(rel, buf);
+    
+    if (PageIsEmpty(page) && (opaque->hasho_flag & LH_OVERFLOW_PAGE)) {
+       buf = _hash_freeovflpage(rel, buf);
+       if (BufferIsValid(buf)) {
+           _hash_relbuf(rel, buf, HASH_WRITE);
+       }
+    } else {
+       _hash_relbuf(rel, buf, HASH_WRITE);
+    }
+    
+    metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_WRITE);
+    metap = (HashMetaPage) BufferGetPage(metabuf);
+    _hash_checkpage((Page) metap, LH_META_PAGE);
+    ++metap->hashm_nkeys;
+    _hash_wrtbuf(rel, metabuf);
+}
+
+void
+_hash_expandtable(Relation rel, Buffer metabuf)
+{
+    HashMetaPage metap;
+    Bucket old_bucket;
+    Bucket new_bucket;
+    uint32 spare_ndx;
+    
+/*    elog(DEBUG, "_hash_expandtable: expanding..."); */
+
+    metap = (HashMetaPage) BufferGetPage(metabuf);
+    _hash_checkpage((Page) metap, LH_META_PAGE);
+    
+    metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_READ, HASH_WRITE);   
+    new_bucket = ++metap->MAX_BUCKET;
+    metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_WRITE, HASH_READ);   
+    old_bucket = (metap->MAX_BUCKET & metap->LOW_MASK);
+    
+    /*
+     * If the split point is increasing (MAX_BUCKET's log base 2
+     * * increases), we need to copy the current contents of the spare
+     * split bucket to the next bucket.
+     */
+    spare_ndx = _hash_log2(metap->MAX_BUCKET + 1);
+    if (spare_ndx > metap->OVFL_POINT) {
+       
+       metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_READ, HASH_WRITE);        
+       metap->SPARES[spare_ndx] = metap->SPARES[metap->OVFL_POINT];
+       metap->OVFL_POINT = spare_ndx;
+       metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_WRITE, HASH_READ);        
+    }
+    
+    if (new_bucket > metap->HIGH_MASK) {
+       
+       /* Starting a new doubling */
+       metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_READ, HASH_WRITE);        
+       metap->LOW_MASK = metap->HIGH_MASK;
+       metap->HIGH_MASK = new_bucket | metap->LOW_MASK;
+       metap = (HashMetaPage) _hash_chgbufaccess(rel, &metabuf, HASH_WRITE, HASH_READ);        
+       
+    }
+    /* Relocate records to the new bucket */
+    _hash_splitpage(rel, metabuf, old_bucket, new_bucket);
+}
+
+
+/*
+ * _hash_splitpage -- split 'obucket' into 'obucket' and 'nbucket'
+ *
+ * this routine is actually misnamed -- we are splitting a bucket that
+ * consists of a base bucket page and zero or more overflow (bucket
+ * chain) pages.
+ */
+static void
+_hash_splitpage(Relation rel,
+               Buffer metabuf,
+               Bucket obucket,
+               Bucket nbucket)
+{
+    Bucket bucket;
+    Buffer obuf;
+    Buffer nbuf;
+    Buffer ovflbuf;
+    BlockNumber oblkno;
+    BlockNumber nblkno;
+    bool null;
+    Datum datum;
+    HashItem hitem;
+    HashPageOpaque oopaque;
+    HashPageOpaque nopaque;
+    HashMetaPage metap;
+    IndexTuple itup;
+    int itemsz;
+    OffsetNumber ooffnum;
+    OffsetNumber noffnum;
+    OffsetNumber omaxoffnum;
+    Page opage;
+    Page npage;
+    TupleDesc itupdesc;
+    
+/*    elog(DEBUG, "_hash_splitpage: splitting %d into %d,%d",
+        obucket, obucket, nbucket);
+*/
+    metap = (HashMetaPage) BufferGetPage(metabuf);
+    _hash_checkpage((Page) metap, LH_META_PAGE);
+    
+    /* get the buffers & pages */
+    oblkno = BUCKET_TO_BLKNO(obucket);
+    nblkno = BUCKET_TO_BLKNO(nbucket);
+    obuf = _hash_getbuf(rel, oblkno, HASH_WRITE);
+    nbuf = _hash_getbuf(rel, nblkno, HASH_WRITE);
+    opage = BufferGetPage(obuf);
+    npage = BufferGetPage(nbuf);
+
+    /* initialize the new bucket */
+    _hash_pageinit(npage, BufferGetPageSize(nbuf));
+    nopaque = (HashPageOpaque) PageGetSpecialPointer(npage);
+    nopaque->hasho_prevblkno = InvalidBlockNumber;
+    nopaque->hasho_nextblkno = InvalidBlockNumber;
+    nopaque->hasho_flag = LH_BUCKET_PAGE;
+    nopaque->hasho_oaddr = InvalidOvflAddress;
+    nopaque->hasho_bucket = nbucket;
+    _hash_wrtnorelbuf(rel, nbuf);
+    
+    /*
+     * make sure the old bucket isn't empty.  advance 'opage' and
+     * friends through the overflow bucket chain until we find a
+     * non-empty page.
+     *
+     * XXX we should only need this once, if we are careful to
+     * preserve the invariant that overflow pages are never empty.
+     */
+    _hash_checkpage(opage, LH_BUCKET_PAGE);
+    oopaque = (HashPageOpaque) PageGetSpecialPointer(opage);
+    if (PageIsEmpty(opage)) {
+       oblkno = oopaque->hasho_nextblkno;
+       _hash_relbuf(rel, obuf, HASH_WRITE);
+       if (!BlockNumberIsValid(oblkno)) {
+           /*
+            * the old bucket is completely empty; of course, the new
+            * bucket will be as well, but since it's a base bucket
+            * page we don't care.
+            */
+           _hash_relbuf(rel, nbuf, HASH_WRITE);
+           return;
+       }
+       obuf = _hash_getbuf(rel, oblkno, HASH_WRITE);
+       opage = BufferGetPage(obuf);
+       _hash_checkpage(opage, LH_OVERFLOW_PAGE);
+       if (PageIsEmpty(opage)) {
+           elog(WARN, "_hash_splitpage: empty overflow page %d", oblkno);
+       }
+       oopaque = (HashPageOpaque) PageGetSpecialPointer(opage);
+    }
+
+    /*
+     * we are now guaranteed that 'opage' is not empty.  partition the
+     * tuples in the old bucket between the old bucket and the new
+     * bucket, advancing along their respective overflow bucket chains
+     * and adding overflow pages as needed.
+     */
+    ooffnum = FirstOffsetNumber;
+    omaxoffnum = PageGetMaxOffsetNumber(opage); 
+    for (;;) {
+       /*
+        * at each iteration through this loop, each of these variables
+        * should be up-to-date: obuf opage oopaque ooffnum omaxoffnum
+        */
+
+       /* check if we're at the end of the page */
+       if (ooffnum > omaxoffnum) {
+           /* at end of page, but check for overflow page */
+           oblkno = oopaque->hasho_nextblkno;          
+           if (BlockNumberIsValid(oblkno)) {
+               /*
+                * we ran out of tuples on this particular page, but
+                * we have more overflow pages; re-init values.
+                */
+               _hash_wrtbuf(rel, obuf);
+               obuf = _hash_getbuf(rel, oblkno, HASH_WRITE);
+               opage = BufferGetPage(obuf);
+               _hash_checkpage(opage, LH_OVERFLOW_PAGE);
+               oopaque = (HashPageOpaque) PageGetSpecialPointer(opage);
+               
+               /* we're guaranteed that an ovfl page has at least 1 tuple */
+               if (PageIsEmpty(opage)) {
+                   elog(WARN, "_hash_splitpage: empty ovfl page %d!",
+                        oblkno);
+               }
+               ooffnum = FirstOffsetNumber;
+               omaxoffnum = PageGetMaxOffsetNumber(opage);
+           } else {
+               /*
+                * we're at the end of the bucket chain, so now we're
+                * really done with everything.  before quitting, call
+                * _hash_squeezebucket to ensure the tuples in the
+                * bucket (including the overflow pages) are packed as
+                * tightly as possible.
+                */
+               _hash_wrtbuf(rel, obuf);
+               _hash_wrtbuf(rel, nbuf);
+               _hash_squeezebucket(rel, metap, obucket);
+               return;
+           }
+       }
+       
+       /* hash on the tuple */
+       hitem = (HashItem) PageGetItem(opage, PageGetItemId(opage, ooffnum));
+       itup = &(hitem->hash_itup);
+       itupdesc = RelationGetTupleDescriptor(rel);
+       datum = index_getattr(itup, 1, itupdesc, &null);
+       bucket = _hash_call(rel, metap, datum);
+       
+       if (bucket == nbucket) {
+           /*
+            * insert the tuple into the new bucket.  if it doesn't
+            * fit on the current page in the new bucket, we must
+            * allocate a new overflow page and place the tuple on
+            * that page instead.
+            */
+           itemsz = IndexTupleDSize(hitem->hash_itup) 
+               + (sizeof(HashItemData) - sizeof(IndexTupleData));
+
+           itemsz = DOUBLEALIGN(itemsz);
+           
+           if (PageGetFreeSpace(npage) < itemsz) {
+               ovflbuf = _hash_addovflpage(rel, &metabuf, nbuf);
+               _hash_wrtbuf(rel, nbuf);
+               nbuf = ovflbuf;
+               npage = BufferGetPage(nbuf);
+               _hash_checkpage(npage, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
+           }
+           
+           noffnum = OffsetNumberNext(PageGetMaxOffsetNumber(npage));
+           (void) PageAddItem(npage, (Item) hitem, itemsz, noffnum, LP_USED);
+           _hash_wrtnorelbuf(rel, nbuf);
+           
+           /*
+            * now delete the tuple from the old bucket.  after this
+            * section of code, 'ooffnum' will actually point to the
+            * ItemId to which we would point if we had advanced it
+            * before the deletion (PageIndexTupleDelete repacks the
+            * ItemId array).  this also means that 'omaxoffnum' is
+            * exactly one less than it used to be, so we really can
+            * just decrement it instead of calling
+            * PageGetMaxOffsetNumber.
+            */
+           PageIndexTupleDelete(opage, ooffnum);
+           _hash_wrtnorelbuf(rel, obuf);
+           omaxoffnum = OffsetNumberPrev(omaxoffnum);
+           
+           /*
+            * tidy up.  if the old page was an overflow page and it
+            * is now empty, we must free it (we want to preserve the
+            * invariant that overflow pages cannot be empty).
+            */
+           if (PageIsEmpty(opage) &&
+               (oopaque->hasho_flag & LH_OVERFLOW_PAGE)) {
+               obuf = _hash_freeovflpage(rel, obuf);
+               
+               /* check that we're not through the bucket chain */
+               if (BufferIsInvalid(obuf)) {
+                   _hash_wrtbuf(rel, nbuf);
+                   _hash_squeezebucket(rel, metap, obucket);
+                   return;
+               }
+               
+               /* 
+                * re-init. again, we're guaranteed that an ovfl page
+                * has at least one tuple.
+                */
+               opage = BufferGetPage(obuf);
+               _hash_checkpage(opage, LH_OVERFLOW_PAGE);
+               oblkno = BufferGetBlockNumber(obuf);
+               oopaque = (HashPageOpaque) PageGetSpecialPointer(opage);
+               if (PageIsEmpty(opage)) {
+                   elog(WARN, "_hash_splitpage: empty overflow page %d",
+                        oblkno);
+               }
+               ooffnum = FirstOffsetNumber;
+               omaxoffnum = PageGetMaxOffsetNumber(opage);
+           }
+       } else {
+           /*
+            * the tuple stays on this page.  we didn't move anything,
+            * so we didn't delete anything and therefore we don't
+            * have to change 'omaxoffnum'.
+            *
+            * XXX any hash value from [0, nbucket-1] will map to this
+            * bucket, which doesn't make sense to me.
+            */
+           ooffnum = OffsetNumberNext(ooffnum);
+       }
+    }
+    /*NOTREACHED*/
+}
diff --git a/src/backend/access/hash/hashscan.c b/src/backend/access/hash/hashscan.c
new file mode 100644 (file)
index 0000000..717c004
--- /dev/null
@@ -0,0 +1,172 @@
+/*-------------------------------------------------------------------------
+ *
+ * hashscan.c--
+ *    manage scans on hash tables
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    Because we can be doing an index scan on a relation while we
+ *    update it, we need to avoid missing data that moves around in
+ *    the index.  The routines and global variables in this file
+ *    guarantee that all scans in the local address space stay
+ *    correctly positioned.  This is all we need to worry about, since
+ *    write locking guarantees that no one else will be on the same
+ *    page at the same time as we are.
+ *
+ *    The scheme is to manage a list of active scans in the current
+ *    backend.  Whenever we add or remove records from an index, we
+ *    check the list of active scans to see if any has been affected.
+ *    A scan is affected only if it is on the same relation, and the
+ *    same page, as the update.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+#include "utils/excid.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/sdir.h"
+#include "access/hash.h"
+
+static void _hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno);
+static bool _hash_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno);
+
+typedef struct HashScanListData {
+    IndexScanDesc              hashsl_scan;
+    struct HashScanListData    *hashsl_next;
+} HashScanListData;
+
+typedef HashScanListData       *HashScanList;
+
+static HashScanList    HashScans = (HashScanList) NULL;
+
+/*
+ *  _Hash_regscan() -- register a new scan.
+ */
+void
+_hash_regscan(IndexScanDesc scan)
+{
+    HashScanList new_el;
+    
+    new_el = (HashScanList) palloc(sizeof(HashScanListData));
+    new_el->hashsl_scan = scan;
+    new_el->hashsl_next = HashScans;
+    HashScans = new_el;
+}
+
+/*
+ *  _hash_dropscan() -- drop a scan from the scan list
+ */
+void
+_hash_dropscan(IndexScanDesc scan)
+{
+    HashScanList chk, last;
+    
+    last = (HashScanList) NULL;
+    for (chk = HashScans;
+        chk != (HashScanList) NULL && chk->hashsl_scan != scan;
+        chk = chk->hashsl_next) {
+       last = chk;
+    }
+    
+    if (chk == (HashScanList) NULL)
+       elog(WARN, "hash scan list trashed; can't find 0x%lx", scan);
+    
+    if (last == (HashScanList) NULL)
+       HashScans = chk->hashsl_next;
+    else
+       last->hashsl_next = chk->hashsl_next;
+    
+#ifdef PERFECT_MEM
+    pfree (chk);
+#endif /* PERFECT_MEM */
+}
+
+void
+_hash_adjscans(Relation rel, ItemPointer tid)
+{
+    HashScanList l;
+    Oid relid;
+    
+    relid = rel->rd_id;
+    for (l = HashScans; l != (HashScanList) NULL; l = l->hashsl_next) {
+       if (relid == l->hashsl_scan->relation->rd_id)
+           _hash_scandel(l->hashsl_scan, ItemPointerGetBlockNumber(tid),
+                         ItemPointerGetOffsetNumber(tid));
+    }
+}
+
+static void
+_hash_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno)
+{
+    ItemPointer current;
+    Buffer buf;
+    Buffer metabuf;
+    HashScanOpaque so;
+    
+    if (!_hash_scantouched(scan, blkno, offno))
+       return;
+    
+    metabuf = _hash_getbuf(scan->relation, HASH_METAPAGE, HASH_READ);
+    
+    so = (HashScanOpaque) scan->opaque;
+    buf = so->hashso_curbuf;
+    
+    current = &(scan->currentItemData);
+    if (ItemPointerIsValid(current)
+       && ItemPointerGetBlockNumber(current) == blkno
+       && ItemPointerGetOffsetNumber(current) >= offno) {
+       _hash_step(scan, &buf, BackwardScanDirection, metabuf);
+       so->hashso_curbuf = buf;
+    }
+    
+    current = &(scan->currentMarkData);
+    if (ItemPointerIsValid(current)
+       && ItemPointerGetBlockNumber(current) == blkno
+       && ItemPointerGetOffsetNumber(current) >= offno) {
+       ItemPointerData tmp;
+       tmp = *current;
+       *current = scan->currentItemData;
+       scan->currentItemData = tmp;
+       _hash_step(scan, &buf, BackwardScanDirection, metabuf);
+       so->hashso_mrkbuf = buf;
+       tmp = *current;
+       *current = scan->currentItemData;
+       scan->currentItemData = tmp;
+    }
+}
+
+static bool
+_hash_scantouched(IndexScanDesc scan,
+                 BlockNumber blkno,
+                 OffsetNumber offno)
+{
+    ItemPointer current;
+    
+    current = &(scan->currentItemData);
+    if (ItemPointerIsValid(current)
+       && ItemPointerGetBlockNumber(current) == blkno
+       && ItemPointerGetOffsetNumber(current) >= offno)
+       return (true);
+    
+    current = &(scan->currentMarkData);
+    if (ItemPointerIsValid(current)
+       && ItemPointerGetBlockNumber(current) == blkno
+       && ItemPointerGetOffsetNumber(current) >= offno)
+       return (true);
+    
+    return (false);
+}
diff --git a/src/backend/access/hash/hashsearch.c b/src/backend/access/hash/hashsearch.c
new file mode 100644 (file)
index 0000000..2a4934c
--- /dev/null
@@ -0,0 +1,425 @@
+/*-------------------------------------------------------------------------
+ *
+ * hashsearch.c--
+ *    search code for postgres hash tables
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+#include "utils/excid.h"
+
+#include "fmgr.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/skey.h"
+#include "access/sdir.h"
+#include "access/hash.h"
+
+/*
+ *  _hash_search() -- Finds the page/bucket that the contains the
+ *  scankey and loads it into *bufP.  the buffer has a read lock.
+ */
+void
+_hash_search(Relation rel,
+            int keysz,
+            ScanKey scankey,
+            Buffer *bufP,
+            HashMetaPage metap)
+{
+    BlockNumber blkno;
+    Datum keyDatum;
+    Bucket bucket;
+
+    if (scankey == (ScanKey) NULL ||
+       (keyDatum = scankey[0].sk_argument) == (Datum) NULL) {
+       /* 
+        * If the scankey argument is NULL, all tuples will satisfy
+        * the scan so we start the scan at the first bucket (bucket
+        * 0).
+        */
+       bucket = 0;
+    } else {
+       bucket = _hash_call(rel, metap, keyDatum);
+    }
+
+    blkno = BUCKET_TO_BLKNO(bucket);
+    
+    *bufP = _hash_getbuf(rel, blkno, HASH_READ);
+}
+
+/*
+ *  _hash_next() -- Get the next item in a scan.
+ *
+ *     On entry, we have a valid currentItemData in the scan, and a
+ *     read lock on the page that contains that item.  We do not have
+ *     the page pinned.  We return the next item in the scan.  On
+ *     exit, we have the page containing the next item locked but not
+ *     pinned.
+ */
+RetrieveIndexResult
+_hash_next(IndexScanDesc scan, ScanDirection dir)
+{
+    Relation rel;
+    Buffer buf;
+    Buffer metabuf;
+    Page page;
+    OffsetNumber offnum;
+    RetrieveIndexResult res;
+    ItemPointer current;
+    ItemPointer iptr;
+    HashItem hitem;
+    IndexTuple itup;
+    HashScanOpaque so;
+
+    rel = scan->relation;
+    so = (HashScanOpaque) scan->opaque; 
+    current = &(scan->currentItemData);
+
+    metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
+
+    /*
+     *  XXX 10 may 91:  somewhere there's a bug in our management of the
+     *  cached buffer for this scan.  wei discovered it.  the following
+     *  is a workaround so he can work until i figure out what's going on.
+     */
+
+    if (!BufferIsValid(so->hashso_curbuf)) {
+       so->hashso_curbuf = _hash_getbuf(rel,
+                                        ItemPointerGetBlockNumber(current),
+                                        HASH_READ);
+    }
+
+    /* we still have the buffer pinned and locked */
+    buf = so->hashso_curbuf;
+
+    /*
+     * step to next valid tuple.  note that _hash_step releases our
+     * lock on 'metabuf'; if we switch to a new 'buf' while looking
+     * for the next tuple, we come back with a lock on that buffer.
+     */
+    if (!_hash_step(scan, &buf, dir, metabuf)) {
+       return ((RetrieveIndexResult) NULL);
+    }
+
+    /* if we're here, _hash_step found a valid tuple */
+    current = &(scan->currentItemData);
+    offnum = ItemPointerGetOffsetNumber(current);
+    page = BufferGetPage(buf);
+    _hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
+    hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
+    itup = &hitem->hash_itup;
+    iptr = (ItemPointer) palloc(sizeof(ItemPointerData));
+    memmove((char *) iptr, (char *) &(itup->t_tid),  sizeof(ItemPointerData));
+    res = FormRetrieveIndexResult(current, iptr);
+
+    return (res);
+}
+
+static void
+_hash_readnext(Relation rel,
+              Buffer *bufp, Page *pagep, HashPageOpaque *opaquep)
+{
+    BlockNumber blkno;
+
+    blkno = (*opaquep)->hasho_nextblkno;
+    _hash_relbuf(rel, *bufp, HASH_READ);
+    *bufp = InvalidBuffer;
+    if (BlockNumberIsValid(blkno)) {
+       *bufp = _hash_getbuf(rel, blkno, HASH_READ);
+       *pagep = BufferGetPage(*bufp);
+       _hash_checkpage(*pagep, LH_OVERFLOW_PAGE);
+       *opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
+       Assert(!PageIsEmpty(*pagep));
+    }
+}
+
+static void
+_hash_readprev(Relation rel,
+              Buffer *bufp, Page *pagep, HashPageOpaque *opaquep)
+{
+    BlockNumber blkno;
+
+    blkno = (*opaquep)->hasho_prevblkno;
+    _hash_relbuf(rel, *bufp, HASH_READ);
+    *bufp = InvalidBuffer;
+    if (BlockNumberIsValid(blkno)) {
+       *bufp = _hash_getbuf(rel, blkno, HASH_READ);
+       *pagep = BufferGetPage(*bufp);
+       _hash_checkpage(*pagep, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
+       *opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
+       if (PageIsEmpty(*pagep)) {
+           Assert((*opaquep)->hasho_flag & LH_BUCKET_PAGE);
+           _hash_relbuf(rel, *bufp, HASH_READ);
+           *bufp = InvalidBuffer;
+       }
+    }
+}
+
+/*
+ *  _hash_first() -- Find the first item in a scan.
+ *
+ *     Return the RetrieveIndexResult of the first item in the tree that
+ *     satisfies the qualificatin associated with the scan descriptor. On
+ *     exit, the page containing the current index tuple is read locked
+ *     and pinned, and the scan's opaque data entry is updated to 
+ *     include the buffer.  
+ */
+RetrieveIndexResult
+_hash_first(IndexScanDesc scan, ScanDirection dir)
+{
+    Relation rel;
+    Buffer buf;
+    Buffer metabuf;
+    Page page;
+    HashPageOpaque opaque;
+    HashMetaPage metap;
+    HashItem hitem;
+    IndexTuple itup;
+    ItemPointer current;
+    ItemPointer iptr;
+    OffsetNumber offnum;
+    RetrieveIndexResult res;
+    HashScanOpaque so;
+
+    rel = scan->relation;
+    so = (HashScanOpaque) scan->opaque;
+    current = &(scan->currentItemData);
+
+    metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ);
+    metap = (HashMetaPage) BufferGetPage(metabuf);
+    _hash_checkpage((Page) metap, LH_META_PAGE);
+
+    /*
+     *  XXX -- The attribute number stored in the scan key is the attno
+     *        in the heap relation.  We need to transmogrify this into
+     *         the index relation attno here.  For the moment, we have
+     *        hardwired attno == 1.
+     */
+
+    /* find the correct bucket page and load it into buf */
+    _hash_search(rel, 1, scan->keyData, &buf, metap);
+    page = BufferGetPage(buf);
+    _hash_checkpage(page, LH_BUCKET_PAGE);
+    opaque = (HashPageOpaque) PageGetSpecialPointer(page);
+
+    /*
+     * if we are scanning forward, we need to find the first non-empty
+     * page (if any) in the bucket chain.  since overflow pages are
+     * never empty, this had better be either the bucket page or the
+     * first overflow page.
+     *
+     * if we are scanning backward, we always go all the way to the
+     * end of the bucket chain.
+     */
+    if (PageIsEmpty(page)) {
+       if (BlockNumberIsValid(opaque->hasho_nextblkno)) {
+           _hash_readnext(rel, &buf, &page, &opaque);
+       } else {
+           ItemPointerSetInvalid(current);
+           so->hashso_curbuf = InvalidBuffer;
+           return ((RetrieveIndexResult) NULL);
+       }
+    }
+    if (ScanDirectionIsBackward(dir)) {
+       while (BlockNumberIsValid(opaque->hasho_nextblkno)) {
+           _hash_readnext(rel, &buf, &page, &opaque);
+       }
+    }
+
+    if (!_hash_step(scan, &buf, dir, metabuf)) {
+       return ((RetrieveIndexResult) NULL);
+    }
+
+    /* if we're here, _hash_step found a valid tuple */
+    current = &(scan->currentItemData);
+    offnum = ItemPointerGetOffsetNumber(current);
+    page = BufferGetPage(buf);
+    _hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
+    hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
+    itup = &hitem->hash_itup;
+    iptr = (ItemPointer) palloc(sizeof(ItemPointerData));
+    memmove((char *) iptr, (char *) &(itup->t_tid), sizeof(ItemPointerData));
+    res = FormRetrieveIndexResult(current, iptr);
+
+    return (res);
+}
+
+/*
+ *  _hash_step() -- step to the next valid item in a scan in the bucket.
+ *
+ *     If no valid record exists in the requested direction, return
+ *     false.  Else, return true and set the CurrentItemData for the
+ *     scan to the right thing.
+ * 
+ *     'bufP' points to the buffer which contains the current page
+ *     that we'll step through.
+ *
+ *     'metabuf' is released when this returns.
+ */
+bool
+_hash_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir, Buffer metabuf)
+{
+    Relation rel;
+    ItemPointer current;
+    HashScanOpaque so;
+    int allbuckets;
+    HashMetaPage metap;
+    Buffer buf;
+    Page page;
+    HashPageOpaque opaque;
+    OffsetNumber maxoff;
+    OffsetNumber offnum;
+    Bucket bucket;
+    BlockNumber blkno;
+    HashItem hitem;
+    IndexTuple itup;
+
+    rel = scan->relation;
+    current = &(scan->currentItemData);
+    so = (HashScanOpaque) scan->opaque;
+    allbuckets = (scan->numberOfKeys < 1);
+
+    metap = (HashMetaPage) BufferGetPage(metabuf);
+    _hash_checkpage((Page) metap, LH_META_PAGE);
+
+    buf = *bufP;
+    page = BufferGetPage(buf);
+    _hash_checkpage(page, LH_BUCKET_PAGE|LH_OVERFLOW_PAGE);
+    opaque = (HashPageOpaque) PageGetSpecialPointer(page);
+
+    /*
+     * If _hash_step is called from _hash_first, current will not be
+     * valid, so we can't dereference it.  However, in that case, we
+     * presumably want to start at the beginning/end of the page...
+     */
+    maxoff = PageGetMaxOffsetNumber(page);
+    if (ItemPointerIsValid(current)) {
+       offnum = ItemPointerGetOffsetNumber(current);
+    } else {
+       offnum = InvalidOffsetNumber;
+    }
+
+    /*
+     * 'offnum' now points to the last tuple we have seen (if any).
+     *
+     * continue to step through tuples until:
+     *           1) we get to the end of the bucket chain or
+     *           2) we find a valid tuple.
+     */
+    do {
+       bucket = opaque->hasho_bucket;
+
+       switch (dir) {
+       case ForwardScanDirection:
+           if (offnum != InvalidOffsetNumber) {
+               offnum = OffsetNumberNext(offnum);      /* move forward */
+           } else {
+               offnum = FirstOffsetNumber;             /* new page */
+           }
+           while (offnum > maxoff) {
+               /*
+                * either this page is empty (maxoff ==
+                * InvalidOffsetNumber) or we ran off the end.
+                */
+               _hash_readnext(rel, &buf, &page, &opaque);
+               if (BufferIsInvalid(buf)) {     /* end of chain */
+                   if (allbuckets && bucket < metap->hashm_maxbucket) {
+                       ++bucket;
+                       blkno = BUCKET_TO_BLKNO(bucket);
+                       buf = _hash_getbuf(rel, blkno, HASH_READ);
+                       page = BufferGetPage(buf);
+                       _hash_checkpage(page, LH_BUCKET_PAGE);
+                       opaque = (HashPageOpaque) PageGetSpecialPointer(page);
+                       Assert(opaque->hasho_bucket == bucket);
+                       while (PageIsEmpty(page) &&
+                              BlockNumberIsValid(opaque->hasho_nextblkno)) {
+                           _hash_readnext(rel, &buf, &page, &opaque);
+                       }
+                       maxoff = PageGetMaxOffsetNumber(page);
+                       offnum = FirstOffsetNumber;
+                   } else {
+                       maxoff = offnum = InvalidOffsetNumber;
+                       break;  /* while */
+                   }
+               } else {
+                   /* _hash_readnext never returns an empty page */
+                   maxoff = PageGetMaxOffsetNumber(page);
+                   offnum = FirstOffsetNumber;
+               }
+           }
+           break;
+       case BackwardScanDirection:
+           if (offnum != InvalidOffsetNumber) {
+               offnum = OffsetNumberPrev(offnum);      /* move back */
+           } else {
+               offnum = maxoff;                        /* new page */
+           }
+           while (offnum < FirstOffsetNumber) {
+               /*
+                * either this page is empty (offnum ==
+                * InvalidOffsetNumber) or we ran off the end.
+                */
+               _hash_readprev(rel, &buf, &page, &opaque);
+               if (BufferIsInvalid(buf)) {     /* end of chain */
+                   if (allbuckets && bucket > 0) {
+                       --bucket;
+                       blkno = BUCKET_TO_BLKNO(bucket);
+                       buf = _hash_getbuf(rel, blkno, HASH_READ);
+                       page = BufferGetPage(buf);
+                       _hash_checkpage(page, LH_BUCKET_PAGE);
+                       opaque = (HashPageOpaque) PageGetSpecialPointer(page);
+                       Assert(opaque->hasho_bucket == bucket);
+                       while (BlockNumberIsValid(opaque->hasho_nextblkno)) {
+                           _hash_readnext(rel, &buf, &page, &opaque);
+                       }
+                       maxoff = offnum = PageGetMaxOffsetNumber(page);
+                   } else {
+                       maxoff = offnum = InvalidOffsetNumber;
+                       break;  /* while */
+                   }
+               } else {
+                   /* _hash_readprev never returns an empty page */
+                   maxoff = offnum = PageGetMaxOffsetNumber(page);
+               }
+           }
+           break;
+       default:
+           /* NoMovementScanDirection */
+           /* this should not be reached */
+           break;
+       }
+
+       /* we ran off the end of the world without finding a match */
+       if (offnum == InvalidOffsetNumber) {
+           _hash_relbuf(rel, metabuf, HASH_READ);
+           *bufP = so->hashso_curbuf = InvalidBuffer;
+           ItemPointerSetInvalid(current);
+           return(false);
+       }
+       
+       /* get ready to check this tuple */
+       hitem = (HashItem) PageGetItem(page, PageGetItemId(page, offnum));
+       itup = &hitem->hash_itup;
+    } while (!_hash_checkqual(scan, itup));
+   
+    /* if we made it to here, we've found a valid tuple */
+    _hash_relbuf(rel, metabuf, HASH_READ);
+    blkno = BufferGetBlockNumber(buf);
+    *bufP = so->hashso_curbuf = buf;
+    ItemPointerSet(current, blkno, offnum);
+    return(true);
+}
diff --git a/src/backend/access/hash/hashstrat.c b/src/backend/access/hash/hashstrat.c
new file mode 100644 (file)
index 0000000..4e5c24f
--- /dev/null
@@ -0,0 +1,104 @@
+/*-------------------------------------------------------------------------
+ *
+ * btstrat.c--
+ *    Srategy map entries for the btree indexed access method
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/bufpage.h"
+
+#include "utils/elog.h"
+#include "utils/rel.h"
+#include "utils/excid.h"
+
+#include "access/genam.h"
+#include "access/hash.h"
+
+/* 
+ *  only one valid strategy for hash tables: equality. 
+ */
+
+static StrategyNumber  HTNegate[1] = {
+    InvalidStrategy
+};
+
+static StrategyNumber  HTCommute[1] = {
+    HTEqualStrategyNumber
+};
+
+static StrategyNumber  HTNegateCommute[1] = {
+    InvalidStrategy
+};
+
+static StrategyEvaluationData  HTEvaluationData = {
+    /* XXX static for simplicity */
+
+    HTMaxStrategyNumber,
+    (StrategyTransformMap)HTNegate,
+    (StrategyTransformMap)HTCommute,
+    (StrategyTransformMap)HTNegateCommute,
+    {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}
+};
+
+/* ----------------------------------------------------------------
+ *     RelationGetHashStrategy
+ * ----------------------------------------------------------------
+ */
+
+StrategyNumber
+_hash_getstrat(Relation rel,
+              AttrNumber attno,
+              RegProcedure proc)
+{
+    StrategyNumber     strat;
+
+    strat = RelationGetStrategy(rel, attno, &HTEvaluationData, proc);
+
+    Assert(StrategyNumberIsValid(strat));
+
+    return (strat);
+}
+
+bool
+_hash_invokestrat(Relation rel,
+                 AttrNumber attno,
+                 StrategyNumber strat,
+                 Datum left,
+                 Datum right)
+{
+    return (RelationInvokeStrategy(rel, &HTEvaluationData, attno, strat, 
+                                  left, right));
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c
new file mode 100644 (file)
index 0000000..b7832d4
--- /dev/null
@@ -0,0 +1,147 @@
+/*-------------------------------------------------------------------------
+ *
+ * btutils.c--
+ *    Utility code for Postgres btree implementation.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+
+#include "fmgr.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+#include "utils/excid.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/iqual.h"
+#include "access/hash.h"
+
+ScanKey
+_hash_mkscankey(Relation rel, IndexTuple itup, HashMetaPage metap)
+{
+    ScanKey skey;
+    TupleDesc itupdesc;
+    int natts;
+    AttrNumber i;
+    Datum arg;
+    RegProcedure proc;
+    bool null;
+    
+    natts = rel->rd_rel->relnatts;
+    itupdesc = RelationGetTupleDescriptor(rel);
+    
+    skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
+    
+    for (i = 0; i < natts; i++) {
+       arg = index_getattr(itup, i + 1, itupdesc, &null);
+       proc = metap->hashm_procid;
+       ScanKeyEntryInitialize(&skey[i],
+                              0x0, (AttrNumber) (i + 1), proc, arg);
+    }
+    
+    return (skey);
+}      
+
+void
+_hash_freeskey(ScanKey skey)
+{
+    pfree(skey);
+}
+
+
+bool
+_hash_checkqual(IndexScanDesc scan, IndexTuple itup)
+{
+    if (scan->numberOfKeys > 0)
+       return (index_keytest(itup, 
+                             RelationGetTupleDescriptor(scan->relation),
+                             scan->numberOfKeys, scan->keyData));
+    else
+       return (true);
+}
+
+HashItem
+_hash_formitem(IndexTuple itup)
+{
+    int nbytes_hitem;
+    HashItem hitem;
+    Size tuplen;
+    
+    /* disallow nulls in hash keys */
+    if (itup->t_info & INDEX_NULL_MASK)
+       elog(WARN, "hash indices cannot include null keys");
+    
+    /* make a copy of the index tuple with room for the sequence number */
+    tuplen = IndexTupleSize(itup);
+    nbytes_hitem = tuplen +
+       (sizeof(HashItemData) - sizeof(IndexTupleData));
+    
+    hitem = (HashItem) palloc(nbytes_hitem);
+    memmove((char *) &(hitem->hash_itup), (char *) itup, tuplen);
+    
+    return (hitem);
+}
+
+Bucket
+_hash_call(Relation rel, HashMetaPage metap, Datum key)
+{
+    uint32 n;
+    Bucket bucket;
+    RegProcedure proc;
+    
+    proc = metap->hashm_procid;
+    n = (uint32) fmgr(proc, key);
+    bucket = n & metap->hashm_highmask;
+    if (bucket > metap->hashm_maxbucket)
+       bucket = bucket & metap->hashm_lowmask;
+    return (bucket);
+}
+
+/*
+ * _hash_log2 -- returns ceil(lg2(num))
+ */
+uint32
+_hash_log2(uint32 num)
+{
+    uint32 i, limit;
+    
+    limit = 1;
+    for (i = 0; limit < num; limit = limit << 1, i++)
+       ;
+    return (i);
+}
+
+/*
+ * _hash_checkpage -- sanity checks on the format of all hash pages
+ */
+void
+_hash_checkpage(Page page, int flags)
+{
+    PageHeader ph = (PageHeader) page;
+    HashPageOpaque opaque;
+
+    Assert(page);
+    Assert(ph->pd_lower >= (sizeof(PageHeaderData) - sizeof(ItemIdData)));
+#if 1
+    Assert(ph->pd_upper <=
+          (BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData))));
+    Assert(ph->pd_special ==
+          (BLCKSZ - DOUBLEALIGN(sizeof(HashPageOpaqueData))));
+    Assert(ph->pd_opaque.od_pagesize == BLCKSZ);
+#endif
+    if (flags) {
+       opaque = (HashPageOpaque) PageGetSpecialPointer(page);
+       Assert(opaque->hasho_flag & flags);
+    }
+}
diff --git a/src/backend/access/heap/Makefile.inc b/src/backend/access/heap/Makefile.inc
new file mode 100644 (file)
index 0000000..077b0bf
--- /dev/null
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for access/heap
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= heapam.c hio.c stats.c
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
new file mode 100644 (file)
index 0000000..51839e9
--- /dev/null
@@ -0,0 +1,1507 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam.c--
+ *    heap access method code
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *
+ * INTERFACE ROUTINES
+ *     heapgettup      - fetch next heap tuple from a scan
+ *     heap_open       - open a heap relation by relationId
+ *     heap_openr      - open a heap relation by name
+ *     heap_close      - close a heap relation
+ *     heap_beginscan  - begin relation scan
+ *     heap_rescan     - restart a relation scan
+ *     heap_endscan    - end relation scan
+ *     heap_getnext    - retrieve next tuple in scan
+ *     heap_fetch      - retrive tuple with tid
+ *     heap_insert     - insert tuple into a relation
+ *     heap_delete     - delete a tuple from a relation
+ *     heap_replace    - replace a tuple in a relation with another tuple
+ *     heap_markpos    - mark scan position
+ *     heap_restrpos   - restore position to marked location
+ *     
+ * NOTES
+ *    This file contains the heap_ routines which implement
+ *    the POSTGRES heap access method used for all POSTGRES
+ *    relations.  
+ *
+ * OLD COMMENTS
+ *     struct relscan hints:  (struct should be made AM independent?)
+ *
+ *     rs_ctid is the tid of the last tuple returned by getnext.
+ *     rs_ptid and rs_ntid are the tids of the previous and next tuples
+ *     returned by getnext, respectively.  NULL indicates an end of
+ *     scan (either direction); NON indicates an unknow value.
+ *
+ *     possible combinations:
+ *     rs_p    rs_c    rs_n            interpretation
+ *     NULL    NULL    NULL            empty scan
+ *     NULL    NULL    NON             at begining of scan
+ *     NULL    NULL    t1              at begining of scan (with cached tid)
+ *     NON     NULL    NULL            at end of scan
+ *     t1      NULL    NULL            at end of scan (with cached tid)
+ *     NULL    t1      NULL            just returned only tuple
+ *     NULL    t1      NON             just returned first tuple
+ *     NULL    t1      t2              returned first tuple (with cached tid)
+ *     NON     t1      NULL            just returned last tuple
+ *     t2      t1      NULL            returned last tuple (with cached tid)
+ *     t1      t2      NON             in the middle of a forward scan
+ *     NON     t2      t1              in the middle of a reverse scan
+ *     ti      tj      tk              in the middle of a scan (w cached tid)
+ *
+ *     Here NULL is ...tup == NULL && ...buf == InvalidBuffer,
+ *     and NON is ...tup == NULL && ...buf == UnknownBuffer.
+ *
+ *     Currently, the NONTID values are not cached with their actual
+ *     values by getnext.  Values may be cached by markpos since it stores
+ *     all three tids.
+ *
+ *     NOTE:  the calls to elog() must stop.  Should decide on an interface
+ *     between the general and specific AM calls.
+ *
+ *     XXX probably do not need a free tuple routine for heaps.
+ *     Huh?  Free tuple is not necessary for tuples returned by scans, but
+ *     is necessary for tuples which are returned by
+ *     RelationGetTupleByItemPointer. -hirohama
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <sys/file.h>
+#include <string.h>
+
+#include "postgres.h"
+
+#include "access/attnum.h"
+#include "access/heapam.h"
+#include "access/hio.h"
+#include "access/htup.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+
+#include "utils/tqual.h"
+#include "access/valid.h"
+#include "access/xact.h"
+
+#include "catalog/catalog.h"
+#include "catalog/catname.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+#include "storage/itemid.h"
+#include "storage/itemptr.h"
+#include "storage/lmgr.h"
+
+#include "tcop/tcopdebug.h"
+#include "miscadmin.h"
+
+#include "utils/memutils.h"
+#include "utils/palloc.h"
+#include "fmgr.h"
+#include "utils/inval.h"
+#include "utils/elog.h"
+#include "utils/mcxt.h"
+#include "utils/rel.h"
+#include "utils/relcache.h"
+
+static bool    ImmediateInvalidation;
+
+/* ----------------------------------------------------------------
+ *                       heap support routines
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     initsdesc - sdesc code common to heap_beginscan and heap_rescan
+ * ----------------
+ */
+static void
+initsdesc(HeapScanDesc sdesc,
+         Relation relation,
+         int atend,
+         unsigned nkeys,
+         ScanKey key)
+{
+    if (!RelationGetNumberOfBlocks(relation)) {
+       /* ----------------
+        *  relation is empty
+        * ----------------
+        */
+       sdesc->rs_ntup = sdesc->rs_ctup = sdesc->rs_ptup = NULL;
+       sdesc->rs_nbuf = sdesc->rs_cbuf = sdesc->rs_pbuf = InvalidBuffer;
+    } else if (atend) {
+       /* ----------------
+        *  reverse scan
+        * ----------------
+        */
+       sdesc->rs_ntup = sdesc->rs_ctup = NULL;
+       sdesc->rs_nbuf = sdesc->rs_cbuf = InvalidBuffer;
+       sdesc->rs_ptup = NULL;
+       sdesc->rs_pbuf = UnknownBuffer;
+    } else {
+       /* ----------------
+        *  forward scan
+        * ----------------
+        */
+       sdesc->rs_ctup = sdesc->rs_ptup = NULL;
+       sdesc->rs_cbuf = sdesc->rs_pbuf = InvalidBuffer;
+       sdesc->rs_ntup = NULL;
+       sdesc->rs_nbuf = UnknownBuffer;
+    } /* invalid too */
+    
+    /* we don't have a marked position... */
+    ItemPointerSetInvalid(&(sdesc->rs_mptid));
+    ItemPointerSetInvalid(&(sdesc->rs_mctid));
+    ItemPointerSetInvalid(&(sdesc->rs_mntid));
+    ItemPointerSetInvalid(&(sdesc->rs_mcd));
+    
+    /* ----------------
+     * copy the scan key, if appropriate
+     * ----------------
+     */
+    if (key != NULL)
+       memmove(sdesc->rs_key, key, nkeys * sizeof(ScanKeyData));
+}
+
+/* ----------------
+ *     unpinsdesc - code common to heap_rescan and heap_endscan
+ * ----------------
+ */
+static void
+unpinsdesc(HeapScanDesc sdesc)
+{
+    if (BufferIsValid(sdesc->rs_pbuf)) {
+       ReleaseBuffer(sdesc->rs_pbuf);
+    }
+    
+    /* ------------------------------------
+     *  Scan will pin buffer one for each non-NULL tuple pointer
+     *  (ptup, ctup, ntup), so they have to be unpinned multiple
+     *  times.
+     * ------------------------------------
+     */
+    if (BufferIsValid(sdesc->rs_cbuf)) {
+       ReleaseBuffer(sdesc->rs_cbuf);
+    }
+    
+    if (BufferIsValid(sdesc->rs_nbuf)) {
+       ReleaseBuffer(sdesc->rs_nbuf);
+    }
+}
+
+/* ------------------------------------------
+ *     nextpage
+ *
+ *     figure out the next page to scan after the current page
+ *     taking into account of possible adjustment of degrees of
+ *     parallelism
+ * ------------------------------------------
+ */
+static int
+nextpage(int page, int dir)
+{
+    return((dir<0)?page-1:page+1);
+}
+
+/* ----------------
+ *     heapgettup - fetch next heap tuple
+ *
+ *     routine used by heap_getnext() which does most of the
+ *     real work in scanning tuples.
+ * ----------------
+ */
+static HeapTuple 
+heapgettup(Relation relation,
+          ItemPointer tid,
+          int dir,
+          Buffer *b,
+          TimeQual timeQual,
+          int nkeys,
+          ScanKey key)
+{
+    ItemId             lpp;
+    Page               dp;
+    int                        page;
+    int                        pages;
+    int                        lines;
+    HeapTuple          rtup;
+    OffsetNumber       lineoff;
+    int                        linesleft;
+    
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_heapgettup);
+    IncrHeapAccessStat(global_heapgettup);
+    
+    /* ----------------
+     * debugging stuff 
+     *
+     * check validity of arguments, here and for other functions too
+     * Note: no locking manipulations needed--this is a local function
+     * ----------------
+     */
+#ifdef HEAPDEBUGALL
+    if (ItemPointerIsValid(tid)) {
+       elog(DEBUG, "heapgettup(%.16s, tid=0x%x[%d,%d], dir=%d, ...)",
+            RelationGetRelationName(relation), tid, tid->ip_blkid,
+            tid->ip_posid, dir);
+    } else {
+       elog(DEBUG, "heapgettup(%.16s, tid=0x%x, dir=%d, ...)",
+            RelationGetRelationName(relation), tid, dir);
+    }
+    elog(DEBUG, "heapgettup(..., b=0x%x, timeQ=0x%x, nkeys=%d, key=0x%x",
+        b, timeQual, nkeys, key);
+    if (timeQual == SelfTimeQual) {
+       elog(DEBUG, "heapgettup: relation(%c)=`%.16s', SelfTimeQual",
+            relation->rd_rel->relkind, &relation->rd_rel->relname);
+    } else {
+       elog(DEBUG, "heapgettup: relation(%c)=`%.16s', timeQual=%d",
+            relation->rd_rel->relkind, &relation->rd_rel->relname,
+            timeQual);
+    }
+#endif /* !defined(HEAPDEBUGALL) */
+    
+    if (!ItemPointerIsValid(tid)) {
+       Assert(!PointerIsValid(tid));
+    }
+    
+    /* ----------------
+     * return null immediately if relation is empty
+     * ----------------
+     */
+    if (!(pages = relation->rd_nblocks))
+       return (NULL);
+    
+    /* ----------------
+     * calculate next starting lineoff, given scan direction
+     * ----------------
+     */
+    if (!dir) {
+       /* ----------------
+        * ``no movement'' scan direction
+        * ----------------
+        */
+       /* assume it is a valid TID XXX */
+       if (ItemPointerIsValid(tid) == false) {
+           *b = InvalidBuffer;
+           return (NULL);
+       }
+       *b = RelationGetBufferWithBuffer(relation,
+                                        ItemPointerGetBlockNumber(tid),
+                                        *b);
+       
+#ifndef NO_BUFFERISVALID
+       if (!BufferIsValid(*b)) {
+           elog(WARN, "heapgettup: failed ReadBuffer");
+       }
+#endif
+       
+       dp = (Page) BufferGetPage(*b);
+       lineoff = ItemPointerGetOffsetNumber(tid);
+       lpp = PageGetItemId(dp, lineoff);
+       
+       rtup = (HeapTuple)PageGetItem((Page) dp, lpp);
+       return (rtup);
+       
+    } else if (dir < 0) {
+       /* ----------------
+        *  reverse scan direction
+        * ----------------
+        */
+       if (ItemPointerIsValid(tid) == false) {
+           tid = NULL;
+       }
+       if (tid == NULL) {
+           page = pages - 1;                           /* final page */
+       } else {
+           page = ItemPointerGetBlockNumber(tid);      /* current page */
+       }
+       if (page < 0) { 
+           *b = InvalidBuffer;
+           return (NULL);
+       }
+       
+       *b = RelationGetBufferWithBuffer(relation, page, *b);
+#ifndef NO_BUFFERISVALID
+       if (!BufferIsValid(*b)) {
+           elog(WARN, "heapgettup: failed ReadBuffer");
+       }
+#endif
+       
+       dp = (Page) BufferGetPage(*b);
+       lines = PageGetMaxOffsetNumber(dp);
+       if (tid == NULL) {
+           lineoff = lines;                            /* final offnum */
+       } else {
+           lineoff =                                   /* previous offnum */
+               OffsetNumberPrev(ItemPointerGetOffsetNumber(tid));
+       }
+       /* page and lineoff now reference the physically previous tid */
+
+    } else {
+       /* ----------------
+        *  forward scan direction
+        * ----------------
+        */
+       if (ItemPointerIsValid(tid) == false) {
+           page = 0;                                   /* first page */
+           lineoff = FirstOffsetNumber;                /* first offnum */
+       } else {
+           page = ItemPointerGetBlockNumber(tid);      /* current page */
+           lineoff =                                   /* next offnum */
+               OffsetNumberNext(ItemPointerGetOffsetNumber(tid));
+       }
+       
+       if (page >= pages) {
+           *b = InvalidBuffer;
+           return (NULL);
+       }
+       /* page and lineoff now reference the physically next tid */
+
+       *b = RelationGetBufferWithBuffer(relation, page, *b);
+#ifndef NO_BUFFERISVALID
+       if (!BufferIsValid(*b)) {
+           elog(WARN, "heapgettup: failed ReadBuffer");
+       }
+#endif
+       
+       dp = (Page) BufferGetPage(*b);
+       lines = PageGetMaxOffsetNumber(dp);
+    }
+    
+    /* 'dir' is now non-zero */
+
+    /* ----------------
+     * calculate line pointer and number of remaining items
+     *  to check on this page.
+     * ----------------
+     */
+    lpp = PageGetItemId(dp, lineoff);
+    if (dir < 0) {
+       linesleft = lineoff - 1;
+    } else {
+       linesleft = lines - lineoff;
+    }
+
+    /* ----------------
+     * advance the scan until we find a qualifying tuple or
+     *  run out of stuff to scan
+     * ----------------
+     */
+    for (;;) {
+       while (linesleft >= 0) {
+           /* ----------------
+            *  if current tuple qualifies, return it.
+            * ----------------
+            */
+           if ((rtup = heap_tuple_satisfies(lpp, relation, (PageHeader) dp,
+                                            timeQual, nkeys, key)) != NULL) {
+               ItemPointer iptr = &(rtup->t_ctid); 
+               if (ItemPointerGetBlockNumber(iptr) != page) {
+                   /*
+                    * set block id to the correct page number
+                    * --- this is a hack to support the virtual fragment
+                    * concept
+                    */
+                   ItemPointerSetBlockNumber(iptr, page);
+               }
+               return (rtup);
+           }
+           
+           /* ----------------
+            *  otherwise move to the next item on the page
+            * ----------------
+            */
+           --linesleft;
+           if (dir < 0) {
+               --lpp;  /* move back in this page's ItemId array */
+           } else {
+               ++lpp;  /* move forward in this page's ItemId array */
+           }
+       }
+       
+       /* ----------------
+        *  if we get here, it means we've exhausted the items on
+        *  this page and it's time to move to the next..
+        * ----------------
+        */
+       page = nextpage(page, dir);
+       
+       /* ----------------
+        *  return NULL if we've exhausted all the pages..
+        * ----------------
+        */
+       if (page < 0 || page >= pages) {
+           if (BufferIsValid(*b))
+               ReleaseBuffer(*b);
+           *b = InvalidBuffer;
+           return (NULL);
+       }
+       
+       *b = ReleaseAndReadBuffer(*b, relation, page);
+       
+#ifndef NO_BUFFERISVALID
+       if (!BufferIsValid(*b)) {
+           elog(WARN, "heapgettup: failed ReadBuffer");
+       }
+#endif
+       dp = (Page) BufferGetPage(*b);
+       lines = lineoff = PageGetMaxOffsetNumber((Page) dp);
+       linesleft = lines - 1;
+       if (dir < 0) {
+           lpp = PageGetItemId(dp, lineoff);
+       } else {
+           lpp = PageGetItemId(dp, FirstOffsetNumber);
+       }
+    }
+}
+
+void
+doinsert(Relation relation, HeapTuple tup)
+{
+    RelationPutHeapTupleAtEnd(relation, tup);
+    return;
+}
+
+/* 
+ *     HeapScanIsValid is now a macro in relscan.h -cim 4/27/91
+ */
+
+/* ----------------
+ *     SetHeapAccessMethodImmediateInvalidation
+ * ----------------
+ */
+void
+SetHeapAccessMethodImmediateInvalidation(bool on)
+{
+    ImmediateInvalidation = on;
+}
+
+/* ----------------------------------------------------------------
+ *                   heap access method interface
+ * ----------------------------------------------------------------
+ */
+/* ----------------
+ *     heap_open - open a heap relation by relationId
+ *
+ *     presently the relcache routines do all the work we need
+ *     to open/close heap relations.
+ * ----------------
+ */
+Relation
+heap_open(Oid relationId)
+{
+    Relation r;
+    
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_open);
+    IncrHeapAccessStat(global_open);
+    
+    r = (Relation) RelationIdGetRelation(relationId);
+    
+    if (RelationIsValid(r) && r->rd_rel->relkind == RELKIND_INDEX) {
+       elog(WARN, "%s is an index relation", r->rd_rel->relname.data);
+    }
+    
+    return (r);
+}
+
+/* ----------------
+ *     heap_openr - open a heap relation by name
+ *
+ *     presently the relcache routines do all the work we need
+ *     to open/close heap relations.
+ * ----------------
+ */
+Relation
+heap_openr(char *relationName)
+{
+    Relation r;
+    
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_openr);
+    IncrHeapAccessStat(global_openr);
+    
+    r = RelationNameGetRelation(relationName);
+    
+    if (RelationIsValid(r) && r->rd_rel->relkind == RELKIND_INDEX) {
+       elog(WARN, "%s is an index relation", r->rd_rel->relname.data);
+    }
+    
+    return (r);
+}
+
+/* ----------------
+ *     heap_close - close a heap relation
+ *
+ *     presently the relcache routines do all the work we need
+ *     to open/close heap relations.
+ * ----------------
+ */
+void
+heap_close(Relation relation)
+{
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_close);
+    IncrHeapAccessStat(global_close);
+    
+    (void) RelationClose(relation);
+}
+
+
+/* ----------------
+ *     heap_beginscan  - begin relation scan
+ * ----------------
+ */
+HeapScanDesc
+heap_beginscan(Relation relation,
+              int atend,
+              TimeQual timeQual,
+              unsigned nkeys,
+              ScanKey key)
+{
+    HeapScanDesc       sdesc;
+    
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_beginscan);
+    IncrHeapAccessStat(global_beginscan);
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    if (RelationIsValid(relation) == false)
+       elog(WARN, "heap_beginscan: !RelationIsValid(relation)");
+    
+    /* ----------------
+     * set relation level read lock
+     * ----------------
+     */
+    RelationSetLockForRead(relation);
+    
+    /* XXX someday assert SelfTimeQual if relkind == RELKIND_UNCATALOGED */
+    if (relation->rd_rel->relkind == RELKIND_UNCATALOGED) {
+       timeQual = SelfTimeQual;
+    }
+    
+    /* ----------------
+     *  increment relation ref count while scanning relation
+     * ----------------
+     */
+    RelationIncrementReferenceCount(relation);
+    
+    /* ----------------
+     * allocate and initialize scan descriptor
+     * ----------------
+     */
+    sdesc = (HeapScanDesc) palloc(sizeof(HeapScanDescData));
+    
+    relation->rd_nblocks = smgrnblocks(relation->rd_rel->relsmgr, relation);
+    sdesc->rs_rd = relation;
+    
+    if (nkeys) {
+       /*
+        * we do this here instead of in initsdesc() because heap_rescan also
+        * calls initsdesc() and we don't want to allocate memory again
+        */
+       sdesc->rs_key = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
+    } else {
+       sdesc->rs_key = NULL;
+    }
+
+    initsdesc(sdesc, relation, atend, nkeys, key);
+    
+    sdesc->rs_atend = atend;
+    sdesc->rs_tr = timeQual;
+    sdesc->rs_nkeys = (short)nkeys;
+    
+    return (sdesc);
+}
+
+/* ----------------
+ *     heap_rescan     - restart a relation scan
+ * ----------------
+ */
+void
+heap_rescan(HeapScanDesc sdesc,
+           bool scanFromEnd,
+           ScanKey key)
+{
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_rescan);
+    IncrHeapAccessStat(global_rescan);
+    
+    /* Note: set relation level read lock is still set */
+    
+    /* ----------------
+     * unpin scan buffers
+     * ----------------
+     */
+    unpinsdesc(sdesc);
+    
+    /* ----------------
+     * reinitialize scan descriptor
+     * ----------------
+     */
+    initsdesc(sdesc, sdesc->rs_rd, scanFromEnd, sdesc->rs_nkeys, key);
+    sdesc->rs_atend = (bool) scanFromEnd;
+}
+
+/* ----------------
+ *     heap_endscan    - end relation scan
+ *
+ *     See how to integrate with index scans.
+ *     Check handling if reldesc caching.
+ * ----------------
+ */
+void
+heap_endscan(HeapScanDesc sdesc)
+{
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_endscan);    
+    IncrHeapAccessStat(global_endscan);    
+    
+    /* Note: no locking manipulations needed */
+    
+    /* ----------------
+     * unpin scan buffers
+     * ----------------
+     */
+    unpinsdesc(sdesc);
+    
+    /* ----------------
+     * decrement relation reference count and free scan descriptor storage
+     * ----------------
+     */
+    RelationDecrementReferenceCount(sdesc->rs_rd);
+    
+    /* ----------------
+     * Non 2-phase read locks on catalog relations
+     * ----------------
+     */
+    if ( IsSystemRelationName(RelationGetRelationName(sdesc->rs_rd)->data) )
+
+       RelationUnsetLockForRead(sdesc->rs_rd);
+    
+    pfree(sdesc);      /* XXX */
+}
+
+/* ----------------
+ *     heap_getnext    - retrieve next tuple in scan
+ *
+ *     Fix to work with index relations.
+ * ----------------
+ */
+
+#ifdef HEAPDEBUGALL
+#define HEAPDEBUG_1 \
+elog(DEBUG, "heap_getnext([%s,nkeys=%d],backw=%d,0x%x) called", \
+     sdesc->rs_rd->rd_rel->relname.data, sdesc->rs_nkeys, backw, b)
+     
+#define HEAPDEBUG_2 \
+     elog(DEBUG, "heap_getnext called with backw (no tracing yet)")
+     
+#define HEAPDEBUG_3 \
+     elog(DEBUG, "heap_getnext returns NULL at end")
+     
+#define HEAPDEBUG_4 \
+     elog(DEBUG, "heap_getnext valid buffer UNPIN'd")
+     
+#define HEAPDEBUG_5 \
+     elog(DEBUG, "heap_getnext next tuple was cached")
+     
+#define HEAPDEBUG_6 \
+     elog(DEBUG, "heap_getnext returning EOS")
+     
+#define HEAPDEBUG_7 \
+     elog(DEBUG, "heap_getnext returning tuple");
+#else
+#define HEAPDEBUG_1
+#define HEAPDEBUG_2
+#define HEAPDEBUG_3
+#define HEAPDEBUG_4
+#define HEAPDEBUG_5
+#define HEAPDEBUG_6
+#define HEAPDEBUG_7
+#endif /* !defined(HEAPDEBUGALL) */
+     
+     
+HeapTuple
+heap_getnext(HeapScanDesc scandesc,
+            int backw,
+            Buffer *b)
+{
+    register HeapScanDesc sdesc = scandesc;
+    Buffer               localb;
+    
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_getnext);    
+    IncrHeapAccessStat(global_getnext);    
+    
+    /* Note: no locking manipulations needed */
+    
+    /* ----------------
+     * argument checks
+     * ----------------
+     */
+    if (sdesc == NULL)
+       elog(WARN, "heap_getnext: NULL relscan");
+    
+    /* ----------------
+     * initialize return buffer to InvalidBuffer
+     * ----------------
+     */
+    if (! PointerIsValid(b)) b = &localb;
+    (*b) = InvalidBuffer;
+    
+    HEAPDEBUG_1; /* heap_getnext( info ) */
+    
+    if (backw) {
+       /* ----------------
+        *  handle reverse scan
+        * ----------------
+        */
+       HEAPDEBUG_2; /* heap_getnext called with backw */
+       
+       if (sdesc->rs_ptup == sdesc->rs_ctup &&
+           BufferIsInvalid(sdesc->rs_pbuf))
+           {
+               if (BufferIsValid(sdesc->rs_nbuf))
+                   ReleaseBuffer(sdesc->rs_nbuf);
+               return (NULL);
+           }
+       
+       /*
+        * Copy the "current" tuple/buffer
+        * to "next". Pin/unpin the buffers
+        * accordingly
+        */
+       if (sdesc->rs_nbuf != sdesc->rs_cbuf) {
+           if (BufferIsValid(sdesc->rs_nbuf))
+               ReleaseBuffer(sdesc->rs_nbuf);
+           if (BufferIsValid(sdesc->rs_cbuf))
+               IncrBufferRefCount(sdesc->rs_cbuf);
+       }
+       sdesc->rs_ntup = sdesc->rs_ctup;
+       sdesc->rs_nbuf = sdesc->rs_cbuf;
+       
+       if (sdesc->rs_ptup != NULL) {
+           if (sdesc->rs_cbuf != sdesc->rs_pbuf) {
+               if (BufferIsValid(sdesc->rs_cbuf))
+                   ReleaseBuffer(sdesc->rs_cbuf);
+               if (BufferIsValid(sdesc->rs_pbuf))
+                   IncrBufferRefCount(sdesc->rs_pbuf);
+           }
+           sdesc->rs_ctup = sdesc->rs_ptup;
+           sdesc->rs_cbuf = sdesc->rs_pbuf;
+       } else { /* NONTUP */
+           ItemPointer iptr;
+           
+           iptr = (sdesc->rs_ctup != NULL) ?
+               &(sdesc->rs_ctup->t_ctid) : (ItemPointer) NULL;
+           
+            /* Don't release sdesc->rs_cbuf at this point, because
+               heapgettup doesn't increase PrivateRefCount if it
+               is already set. On a backward scan, both rs_ctup and rs_ntup
+               usually point to the same buffer page, so
+               PrivateRefCount[rs_cbuf] should be 2 (or more, if for instance
+               ctup is stored in a TupleTableSlot).  - 01/09/94 */
+           
+           sdesc->rs_ctup = (HeapTuple)
+               heapgettup(sdesc->rs_rd,
+                          iptr,
+                          -1,
+                          &(sdesc->rs_cbuf),
+                          sdesc->rs_tr,
+                          sdesc->rs_nkeys,
+                          sdesc->rs_key);
+       }
+       
+       if (sdesc->rs_ctup == NULL && !BufferIsValid(sdesc->rs_cbuf))
+           {
+               if (BufferIsValid(sdesc->rs_pbuf))
+                   ReleaseBuffer(sdesc->rs_pbuf);
+               sdesc->rs_ptup = NULL;
+               sdesc->rs_pbuf = InvalidBuffer;
+               if (BufferIsValid(sdesc->rs_nbuf))
+                   ReleaseBuffer(sdesc->rs_nbuf);
+               sdesc->rs_ntup = NULL;
+               sdesc->rs_nbuf = InvalidBuffer;
+               return (NULL);
+           }
+       
+       if (BufferIsValid(sdesc->rs_pbuf))
+           ReleaseBuffer(sdesc->rs_pbuf);
+       sdesc->rs_ptup = NULL;
+       sdesc->rs_pbuf = UnknownBuffer;
+       
+    } else {
+       /* ----------------
+        *  handle forward scan
+        * ----------------
+        */
+       if (sdesc->rs_ctup == sdesc->rs_ntup &&
+           BufferIsInvalid(sdesc->rs_nbuf)) {
+           if (BufferIsValid(sdesc->rs_pbuf))
+               ReleaseBuffer(sdesc->rs_pbuf);
+           HEAPDEBUG_3; /* heap_getnext returns NULL at end */
+           return (NULL);
+       }
+       
+       /*
+        * Copy the "current" tuple/buffer
+        * to "previous". Pin/unpin the buffers
+        * accordingly
+        */
+       if (sdesc->rs_pbuf != sdesc->rs_cbuf) {
+           if (BufferIsValid(sdesc->rs_pbuf))
+               ReleaseBuffer(sdesc->rs_pbuf);
+           if (BufferIsValid(sdesc->rs_cbuf))
+               IncrBufferRefCount(sdesc->rs_cbuf);
+       }
+       sdesc->rs_ptup = sdesc->rs_ctup;
+       sdesc->rs_pbuf = sdesc->rs_cbuf;
+       
+       if (sdesc->rs_ntup != NULL) {
+           if (sdesc->rs_cbuf != sdesc->rs_nbuf) {
+               if (BufferIsValid(sdesc->rs_cbuf))
+                   ReleaseBuffer(sdesc->rs_cbuf);
+               if (BufferIsValid(sdesc->rs_nbuf))
+                   IncrBufferRefCount(sdesc->rs_nbuf);
+           }
+           sdesc->rs_ctup = sdesc->rs_ntup;
+           sdesc->rs_cbuf = sdesc->rs_nbuf;
+           HEAPDEBUG_5; /* heap_getnext next tuple was cached */
+       } else { /* NONTUP */
+           ItemPointer iptr;
+           
+           iptr = (sdesc->rs_ctup != NULL) ?
+               &sdesc->rs_ctup->t_ctid : (ItemPointer) NULL;
+           
+            /* Don't release sdesc->rs_cbuf at this point, because
+               heapgettup doesn't increase PrivateRefCount if it
+               is already set. On a forward scan, both rs_ctup and rs_ptup
+               usually point to the same buffer page, so
+               PrivateRefCount[rs_cbuf] should be 2 (or more, if for instance
+               ctup is stored in a TupleTableSlot).  - 01/09/93 */
+           
+           sdesc->rs_ctup = (HeapTuple)
+               heapgettup(sdesc->rs_rd,
+                          iptr,
+                          1,
+                          &sdesc->rs_cbuf,
+                          sdesc->rs_tr,
+                          sdesc->rs_nkeys,
+                          sdesc->rs_key);
+       }
+       
+       if (sdesc->rs_ctup == NULL && !BufferIsValid(sdesc->rs_cbuf)) {
+           if (BufferIsValid(sdesc->rs_nbuf))
+               ReleaseBuffer(sdesc->rs_nbuf);
+           sdesc->rs_ntup = NULL;
+           sdesc->rs_nbuf = InvalidBuffer;
+           if (BufferIsValid(sdesc->rs_pbuf))
+               ReleaseBuffer(sdesc->rs_pbuf);
+           sdesc->rs_ptup = NULL;
+           sdesc->rs_pbuf = InvalidBuffer;
+           HEAPDEBUG_6; /* heap_getnext returning EOS */
+           return (NULL);
+       }
+       
+       if (BufferIsValid(sdesc->rs_nbuf))
+           ReleaseBuffer(sdesc->rs_nbuf);
+       sdesc->rs_ntup = NULL;
+       sdesc->rs_nbuf = UnknownBuffer;
+    }
+    
+    /* ----------------
+     * if we get here it means we have a new current scan tuple, so
+     *  point to the proper return buffer and return the tuple.
+     * ----------------
+     */
+    (*b) = sdesc->rs_cbuf;
+    
+    HEAPDEBUG_7; /* heap_getnext returning tuple */
+    
+    return (sdesc->rs_ctup);
+}
+
+/* ----------------
+ *     heap_fetch      - retrive tuple with tid
+ *
+ *     Currently ignores LP_IVALID during processing!
+ * ----------------
+ */
+HeapTuple
+heap_fetch(Relation relation,
+          TimeQual timeQual,
+          ItemPointer tid,
+          Buffer *b)
+{
+    ItemId             lp;
+    Buffer             buffer;
+    PageHeader         dp;
+    HeapTuple          tuple;
+    OffsetNumber       offnum;
+    
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_fetch);    
+    IncrHeapAccessStat(global_fetch);    
+    
+    /*
+     * Note: This is collosally expensive - does two system calls per
+     * indexscan tuple fetch.  Not good, and since we should be doing
+     * page level locking by the scanner anyway, it is commented out.
+     */
+    
+    /* RelationSetLockForTupleRead(relation, tid); */
+    
+    /* ----------------
+     * get the buffer from the relation descriptor
+     *  Note that this does a buffer pin.
+     * ----------------
+     */
+    
+    buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+    
+#ifndef NO_BUFFERISVALID
+    if (!BufferIsValid(buffer)) {
+       elog(WARN, "heap_fetch: %s relation: ReadBuffer(%lx) failed",
+            &relation->rd_rel->relname, (long)tid);
+    }
+#endif
+    
+    /* ----------------
+     * get the item line pointer corresponding to the requested tid
+     * ----------------
+     */
+    dp = (PageHeader) BufferGetPage(buffer);
+    offnum = ItemPointerGetOffsetNumber(tid);
+    lp = PageGetItemId(dp, offnum);
+    
+    /* ----------------
+     * more sanity checks
+     * ----------------
+     */
+    
+    Assert(ItemIdIsUsed(lp)); 
+    
+    /* ----------------
+     * check time qualification of tid
+     * ----------------
+     */
+    
+    tuple = heap_tuple_satisfies(lp, relation, dp,
+                                timeQual, 0,(ScanKey)NULL);
+    
+    if (tuple == NULL)
+       {
+           ReleaseBuffer(buffer);
+           return (NULL);
+       }
+    
+    /* ----------------
+     * all checks passed, now either return a copy of the tuple
+     *  or pin the buffer page and return a pointer, depending on
+     *  whether caller gave us a valid b.
+     * ----------------
+     */
+    
+    if (PointerIsValid(b)) {
+       *b = buffer;
+    } else {
+       tuple = heap_copytuple(tuple);
+       ReleaseBuffer(buffer);
+    }
+    return (tuple);
+}
+
+/* ----------------
+ *     heap_insert     - insert tuple
+ *
+ *     The assignment of t_min (and thus the others) should be
+ *     removed eventually.
+ *
+ *     Currently places the tuple onto the last page.  If there is no room,
+ *     it is placed on new pages.  (Heap relations)
+ *     Note that concurrent inserts during a scan will probably have
+ *     unexpected results, though this will be fixed eventually.
+ *
+ *     Fix to work with indexes.
+ * ----------------
+ */
+Oid
+heap_insert(Relation relation, HeapTuple tup)
+{
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_insert);
+    IncrHeapAccessStat(global_insert);
+    
+    /* ----------------
+     * set relation level write lock. If this is a "local" relation (not
+     *  visible to others), we don't need to set a write lock.
+     * ----------------
+     */
+    if (!relation->rd_islocal)
+       RelationSetLockForWrite(relation);
+
+    /* ----------------
+     *  If the object id of this tuple has already been assigned, trust
+     *  the caller.  There are a couple of ways this can happen.  At initial
+     *  db creation, the backend program sets oids for tuples.  When we
+     *  define an index, we set the oid.  Finally, in the future, we may
+     *  allow users to set their own object ids in order to support a
+     *  persistent object store (objects need to contain pointers to one
+     *  another).
+     * ----------------
+     */
+    if (!OidIsValid(tup->t_oid)) {
+       tup->t_oid = newoid();
+       LastOidProcessed = tup->t_oid;
+    }
+    
+    TransactionIdStore(GetCurrentTransactionId(), &(tup->t_xmin));
+    tup->t_cmin = GetCurrentCommandId();
+    StoreInvalidTransactionId(&(tup->t_xmax));
+    tup->t_tmin = INVALID_ABSTIME;
+    tup->t_tmax = CURRENT_ABSTIME;
+    
+    doinsert(relation, tup);
+    
+    if ( IsSystemRelationName(RelationGetRelationName(relation)->data)) {
+       RelationUnsetLockForWrite(relation);
+    
+       /* ----------------
+        *      invalidate caches (only works for system relations)
+        * ----------------
+        */
+       SetRefreshWhenInvalidate(ImmediateInvalidation);
+       RelationInvalidateHeapTuple(relation, tup);
+       SetRefreshWhenInvalidate((bool)!ImmediateInvalidation);
+    }
+    
+    return(tup->t_oid);
+}
+
+/* ----------------
+ *     heap_delete     - delete a tuple
+ *
+ *     Must decide how to handle errors.
+ * ----------------
+ */
+void
+heap_delete(Relation relation, ItemPointer tid)
+{
+    ItemId             lp;
+    HeapTuple          tp;
+    PageHeader         dp;
+    Buffer             b;
+    
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_delete);
+    IncrHeapAccessStat(global_delete);
+    
+    /* ----------------
+     * sanity check
+     * ----------------
+     */
+    Assert(ItemPointerIsValid(tid));
+    
+    /* ----------------
+     * set relation level write lock
+     * ----------------
+     */
+    RelationSetLockForWrite(relation);
+    
+    b = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+    
+#ifndef NO_BUFFERISVALID
+    if (!BufferIsValid(b)) { /* XXX L_SH better ??? */
+       elog(WARN, "heap_delete: failed ReadBuffer");
+    }
+#endif /* NO_BUFFERISVALID */
+    
+    dp = (PageHeader) BufferGetPage(b);
+    lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid));
+    
+    /* ----------------
+     * check that we're deleteing a valid item
+     * ----------------
+     */
+    if (!(tp = heap_tuple_satisfies(lp, relation, dp,
+                                   NowTimeQual, 0, (ScanKey) NULL))) {
+       
+       /* XXX call something else */
+       ReleaseBuffer(b);
+       
+       elog(WARN, "heap_delete: (am)invalid tid");
+    }
+    
+    /* ----------------
+     * get the tuple and lock tell the buffer manager we want
+     *  exclusive access to the page
+     * ----------------
+     */
+    
+    /* ----------------
+     * store transaction information of xact deleting the tuple
+     * ----------------
+     */
+    TransactionIdStore(GetCurrentTransactionId(), &(tp->t_xmax));
+    tp->t_cmax = GetCurrentCommandId();
+    ItemPointerSetInvalid(&tp->t_chain);
+    
+    /* ----------------
+     * invalidate caches
+     * ----------------
+     */
+    SetRefreshWhenInvalidate(ImmediateInvalidation);
+    RelationInvalidateHeapTuple(relation, tp);
+    SetRefreshWhenInvalidate((bool)!ImmediateInvalidation);
+    
+    WriteBuffer(b);
+    if ( IsSystemRelationName(RelationGetRelationName(relation)->data) )
+       RelationUnsetLockForWrite(relation);
+}
+
+/* ----------------
+ *     heap_replace    - replace a tuple
+ *
+ *     Must decide how to handle errors.
+ *
+ *     Fix arguments, work with indexes.
+ * 
+ *      12/30/93 - modified the return value to be 1 when
+ *                a non-functional update is detected. This
+ *                prevents the calling routine from updating
+ *                indices unnecessarily. -kw
+ *
+ * ----------------
+ */
+int
+heap_replace(Relation relation, ItemPointer otid, HeapTuple tup)
+{
+    ItemId             lp;
+    HeapTuple          tp;
+    Page               dp;
+    Buffer             buffer;
+    
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_replace);
+    IncrHeapAccessStat(global_replace);
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(ItemPointerIsValid(otid));
+    
+    /* ----------------
+     * set relation level write lock
+     * ----------------
+     */
+    if (!relation->rd_islocal)
+       RelationSetLockForWrite(relation);
+    
+    buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(otid));
+#ifndef NO_BUFFERISVALID
+    if (!BufferIsValid(buffer)) {
+       /* XXX L_SH better ??? */
+       elog(WARN, "amreplace: failed ReadBuffer");
+    }  
+#endif /* NO_BUFFERISVALID */
+    
+    dp = (Page) BufferGetPage(buffer);
+    lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(otid));
+    
+    /* ----------------
+     * logically delete old item
+     * ----------------
+     */
+    
+    tp = (HeapTuple) PageGetItem(dp, lp);
+    Assert(HeapTupleIsValid(tp));
+    
+    /* -----------------
+     *  the following test should be able to catch all non-functional
+     *  update attempts and shut out all ghost tuples.
+     *  XXX In the future, Spyros may need to update the rule lock on a tuple
+     *  more than once within the same command and same transaction.
+     *  He will have to introduce a new flag to override the following check.
+     *  -- Wei
+     *
+     * -----------------
+     */
+    
+    if (TupleUpdatedByCurXactAndCmd(tp)) {
+       elog(NOTICE, "Non-functional update, only first update is performed");
+       if ( IsSystemRelationName(RelationGetRelationName(relation)->data) )
+           RelationUnsetLockForWrite(relation);
+       ReleaseBuffer(buffer);
+       return(1);
+    }
+    
+    /* ----------------
+     * check that we're replacing a valid item -
+     *
+     *  NOTE that this check must follow the non-functional update test
+     *       above as it can happen that we try to 'replace' the same tuple
+     *       twice in a single transaction.  The second time around the
+     *       tuple will fail the NowTimeQual.  We don't want to abort the
+     *       xact, we only want to flag the 'non-functional' NOTICE. -mer
+     * ----------------
+     */
+    if (!heap_tuple_satisfies(lp,
+                             relation,
+                             (PageHeader)dp,
+                             NowTimeQual,
+                             0,
+                             (ScanKey)NULL))
+       {
+           ReleaseBuffer(buffer);
+           elog(WARN, "heap_replace: (am)invalid otid");
+       }
+    
+    /* XXX order problems if not atomic assignment ??? */
+    tup->t_oid = tp->t_oid;
+    TransactionIdStore(GetCurrentTransactionId(), &(tup->t_xmin));
+    tup->t_cmin = GetCurrentCommandId();
+    StoreInvalidTransactionId(&(tup->t_xmax));
+    tup->t_tmin = INVALID_ABSTIME;
+    tup->t_tmax = CURRENT_ABSTIME;
+    ItemPointerSetInvalid(&tup->t_chain);
+    
+    /* ----------------
+     * insert new item
+     * ----------------
+     */
+    if ((unsigned)DOUBLEALIGN(tup->t_len) <= PageGetFreeSpace((Page) dp)) {
+       RelationPutHeapTuple(relation, BufferGetBlockNumber(buffer), tup);
+    } else {
+       /* ----------------
+        *  new item won't fit on same page as old item, have to look
+        *  for a new place to put it.
+        * ----------------
+        */
+       doinsert(relation, tup);
+    }
+
+    /* ----------------
+     * new item in place, now record transaction information
+     * ----------------
+     */
+    TransactionIdStore(GetCurrentTransactionId(), &(tp->t_xmax));
+    tp->t_cmax = GetCurrentCommandId();
+    tp->t_chain = tup->t_ctid;
+    
+    /* ----------------
+     * invalidate caches
+     * ----------------
+     */
+    SetRefreshWhenInvalidate(ImmediateInvalidation);
+    RelationInvalidateHeapTuple(relation, tp);
+    SetRefreshWhenInvalidate((bool)!ImmediateInvalidation);
+    
+    WriteBuffer(buffer);
+    
+    if ( IsSystemRelationName(RelationGetRelationName(relation)->data) )
+       RelationUnsetLockForWrite(relation);
+    
+    return(0);
+}
+
+/* ----------------
+ *     heap_markpos    - mark scan position
+ *
+ *     Note:
+ *             Should only one mark be maintained per scan at one time.
+ *     Check if this can be done generally--say calls to get the
+ *     next/previous tuple and NEVER pass struct scandesc to the
+ *     user AM's.  Now, the mark is sent to the executor for safekeeping.
+ *     Probably can store this info into a GENERAL scan structure.
+ *
+ *     May be best to change this call to store the marked position
+ *     (up to 2?) in the scan structure itself.
+ *     Fix to use the proper caching structure.
+ * ----------------
+ */
+void
+heap_markpos(HeapScanDesc sdesc)
+{
+    
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_markpos);
+    IncrHeapAccessStat(global_markpos);
+    
+    /* Note: no locking manipulations needed */
+    
+    if (sdesc->rs_ptup == NULL &&
+       BufferIsUnknown(sdesc->rs_pbuf)) { /* == NONTUP */
+       sdesc->rs_ptup = (HeapTuple)
+           heapgettup(sdesc->rs_rd,
+                      (sdesc->rs_ctup == NULL) ?
+                      (ItemPointer)NULL : &sdesc->rs_ctup->t_ctid,
+                      -1,
+                      &sdesc->rs_pbuf,
+                      sdesc->rs_tr,
+                      sdesc->rs_nkeys,
+                      sdesc->rs_key);
+       
+    } else if (sdesc->rs_ntup == NULL &&
+              BufferIsUnknown(sdesc->rs_nbuf)) { /* == NONTUP */
+       sdesc->rs_ntup = (HeapTuple)
+           heapgettup(sdesc->rs_rd,
+                      (sdesc->rs_ctup == NULL) ?
+                      (ItemPointer)NULL : &sdesc->rs_ctup->t_ctid,
+                      1,
+                      &sdesc->rs_nbuf,
+                      sdesc->rs_tr,
+                      sdesc->rs_nkeys,
+                      sdesc->rs_key);
+    }
+    
+    /* ----------------
+     * Should not unpin the buffer pages.  They may still be in use.
+     * ----------------
+     */
+    if (sdesc->rs_ptup != NULL) {
+       sdesc->rs_mptid = sdesc->rs_ptup->t_ctid;
+    } else {
+       ItemPointerSetInvalid(&sdesc->rs_mptid);
+    }
+    if (sdesc->rs_ctup != NULL) {
+       sdesc->rs_mctid = sdesc->rs_ctup->t_ctid;
+    } else {
+       ItemPointerSetInvalid(&sdesc->rs_mctid);
+    }
+    if (sdesc->rs_ntup != NULL) {
+       sdesc->rs_mntid = sdesc->rs_ntup->t_ctid;
+    } else {
+       ItemPointerSetInvalid(&sdesc->rs_mntid);
+    }
+}
+
+/* ----------------
+ *     heap_restrpos   - restore position to marked location
+ *
+ *     Note:  there are bad side effects here.  If we were past the end
+ *     of a relation when heapmarkpos is called, then if the relation is
+ *     extended via insert, then the next call to heaprestrpos will set
+ *     cause the added tuples to be visible when the scan continues.
+ *     Problems also arise if the TID's are rearranged!!!
+ *
+ *     Now pins buffer once for each valid tuple pointer (rs_ptup,
+ *     rs_ctup, rs_ntup) referencing it.
+ *      - 01/13/94
+ *
+ * XXX might be better to do direct access instead of
+ *     using the generality of heapgettup().
+ *
+ * XXX It is very possible that when a scan is restored, that a tuple
+ * XXX which previously qualified may fail for time range purposes, unless
+ * XXX some form of locking exists (ie., portals currently can act funny.
+ * ----------------
+ */
+void
+heap_restrpos(HeapScanDesc sdesc)
+{
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_restrpos);
+    IncrHeapAccessStat(global_restrpos);
+    
+    /* XXX no amrestrpos checking that ammarkpos called */
+    
+    /* Note: no locking manipulations needed */
+    
+    unpinsdesc(sdesc);
+    
+    /* force heapgettup to pin buffer for each loaded tuple */
+    sdesc->rs_pbuf = InvalidBuffer;
+    sdesc->rs_cbuf = InvalidBuffer;
+    sdesc->rs_nbuf = InvalidBuffer;
+    
+    if (!ItemPointerIsValid(&sdesc->rs_mptid)) {
+       sdesc->rs_ptup = NULL;
+    } else {
+       sdesc->rs_ptup = (HeapTuple)
+           heapgettup(sdesc->rs_rd,
+                      &sdesc->rs_mptid,
+                      0,
+                      &sdesc->rs_pbuf,
+                      NowTimeQual,
+                      0,
+                      (ScanKey) NULL);
+    }
+    
+    if (!ItemPointerIsValid(&sdesc->rs_mctid)) {
+       sdesc->rs_ctup = NULL;
+    } else {
+       sdesc->rs_ctup = (HeapTuple)
+           heapgettup(sdesc->rs_rd,
+                      &sdesc->rs_mctid,
+                      0,
+                      &sdesc->rs_cbuf,
+                      NowTimeQual,
+                      0,
+                      (ScanKey) NULL);
+    }
+    
+    if (!ItemPointerIsValid(&sdesc->rs_mntid)) {
+       sdesc->rs_ntup = NULL;
+    } else {
+       sdesc->rs_ntup = (HeapTuple)
+           heapgettup(sdesc->rs_rd,
+                      &sdesc->rs_mntid,
+                      0,
+                      &sdesc->rs_nbuf,
+                      NowTimeQual,
+                      0,
+                      (ScanKey) NULL);
+    }
+}
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c
new file mode 100644 (file)
index 0000000..b5ed7b9
--- /dev/null
@@ -0,0 +1,195 @@
+/*-------------------------------------------------------------------------
+ *
+ * hio.c--
+ *    POSTGRES heap access method input/output code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+
+#include "c.h"
+
+#include "access/heapam.h"
+#include "access/hio.h"
+#include "access/htup.h"
+
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+#include "storage/itemid.h"
+#include "storage/itemptr.h"
+#include "storage/off.h"
+
+#include "utils/memutils.h"
+#include "utils/elog.h"
+#include "utils/rel.h"
+
+/*
+ * amputunique - place tuple at tid
+ *   Currently on errors, calls elog.  Perhaps should return -1?
+ *   Possible errors include the addition of a tuple to the page
+ *   between the time the linep is chosen and the page is L_UP'd.
+ *
+ *   This should be coordinated with the B-tree code.
+ *   Probably needs to have an amdelunique to allow for
+ *   internal index records to be deleted and reordered as needed.
+ *   For the heap AM, this should never be needed.
+ */
+void
+RelationPutHeapTuple(Relation relation,
+                    BlockNumber blockIndex,
+                    HeapTuple tuple)
+{
+    Buffer             buffer;
+    Page               pageHeader;
+    BlockNumber                numberOfBlocks;
+    OffsetNumber       offnum;
+    unsigned int       len;
+    ItemId             itemId;
+    Item               item;
+    
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_RelationPutHeapTuple);
+    IncrHeapAccessStat(global_RelationPutHeapTuple);
+    
+    Assert(RelationIsValid(relation));
+    Assert(HeapTupleIsValid(tuple));
+    
+    numberOfBlocks = RelationGetNumberOfBlocks(relation);
+    Assert(blockIndex < numberOfBlocks);
+    
+    buffer = ReadBuffer(relation, blockIndex);
+#ifndef NO_BUFFERISVALID
+    if (!BufferIsValid(buffer)) {
+       elog(WARN, "RelationPutHeapTuple: no buffer for %ld in %s",
+            blockIndex, &relation->rd_rel->relname);
+    }
+#endif
+    
+    pageHeader = (Page)BufferGetPage(buffer);
+    len = (unsigned)DOUBLEALIGN(tuple->t_len); /* be conservative */
+    Assert((int)len <= PageGetFreeSpace(pageHeader));
+    
+    offnum = PageAddItem((Page)pageHeader, (Item)tuple,
+                        tuple->t_len, InvalidOffsetNumber, LP_USED);
+    
+    itemId = PageGetItemId((Page)pageHeader, offnum);
+    item = PageGetItem((Page)pageHeader, itemId);
+    
+    ItemPointerSet(&((HeapTuple)item)->t_ctid, blockIndex, offnum);
+    
+    WriteBuffer(buffer);
+    /* return an accurate tuple */
+    ItemPointerSet(&tuple->t_ctid, blockIndex, offnum);
+}
+
+/*
+ * The heap_insert routines "know" that a buffer page is initialized to
+ * zero when a BlockExtend operation is performed. 
+ */
+
+#define PageIsNew(page) ((page)->pd_upper == 0)
+
+/*
+ * This routine is another in the series of attempts to reduce the number
+ * of I/O's and system calls executed in the various benchmarks.  In
+ * particular, this routine is used to append data to the end of a relation
+ * file without excessive lseeks.  This code should do no more than 2 semops
+ * in the ideal case.
+ *
+ * Eventually, we should cache the number of blocks in a relation somewhere.
+ * Until that time, this code will have to do an lseek to determine the number
+ * of blocks in a relation.
+ * 
+ * This code should ideally do at most 4 semops, 1 lseek, and possibly 1 write
+ * to do an append; it's possible to eliminate 2 of the semops if we do direct
+ * buffer stuff (!); the lseek and the write can go if we get
+ * RelationGetNumberOfBlocks to be useful.
+ *
+ * NOTE: This code presumes that we have a write lock on the relation.
+ *
+ * Also note that this routine probably shouldn't have to exist, and does
+ * screw up the call graph rather badly, but we are wasting so much time and
+ * system resources being massively general that we are losing badly in our
+ * performance benchmarks.
+ */
+void
+RelationPutHeapTupleAtEnd(Relation relation, HeapTuple tuple)
+{
+    Buffer             buffer;
+    Page               pageHeader;
+    BlockNumber                lastblock;
+    OffsetNumber       offnum;
+    unsigned int       len;
+    ItemId             itemId;
+    Item               item;
+    
+    Assert(RelationIsValid(relation));
+    Assert(HeapTupleIsValid(tuple));
+    
+    /*
+     * XXX This does an lseek - VERY expensive - but at the moment it
+     * is the only way to accurately determine how many blocks are in
+     * a relation.  A good optimization would be to get this to actually
+     * work properly.
+     */
+    
+    lastblock = RelationGetNumberOfBlocks(relation);
+    
+    if (lastblock == 0)
+       {
+           buffer = ReadBuffer(relation, lastblock);
+           pageHeader = (Page)BufferGetPage(buffer);
+           if (PageIsNew((PageHeader) pageHeader))
+               {
+                   buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW);
+                   pageHeader = (Page)BufferGetPage(buffer);
+                   PageInit(pageHeader, BufferGetPageSize(buffer), 0);
+               }
+       }
+    else
+       buffer = ReadBuffer(relation, lastblock - 1);
+    
+    pageHeader = (Page)BufferGetPage(buffer);
+    len = (unsigned)DOUBLEALIGN(tuple->t_len); /* be conservative */
+    
+    /*
+     * Note that this is true if the above returned a bogus page, which
+     * it will do for a completely empty relation.
+     */
+    
+    if (len > PageGetFreeSpace(pageHeader))
+       {
+           buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW);
+           pageHeader = (Page)BufferGetPage(buffer);
+           PageInit(pageHeader, BufferGetPageSize(buffer), 0);
+           
+           if (len > PageGetFreeSpace(pageHeader))
+               elog(WARN, "Tuple is too big: size %d", len);
+       }
+    
+    offnum = PageAddItem((Page)pageHeader, (Item)tuple,
+                        tuple->t_len, InvalidOffsetNumber, LP_USED);
+    
+    itemId = PageGetItemId((Page)pageHeader, offnum);
+    item = PageGetItem((Page)pageHeader, itemId);
+    
+    lastblock = BufferGetBlockNumber(buffer);
+    
+    ItemPointerSet(&((HeapTuple)item)->t_ctid, lastblock, offnum);
+    
+    /* return an accurate tuple */
+    ItemPointerSet(&tuple->t_ctid, lastblock, offnum);
+    
+    WriteBuffer(buffer);
+}
diff --git a/src/backend/access/heap/stats.c b/src/backend/access/heap/stats.c
new file mode 100644 (file)
index 0000000..3e1bef6
--- /dev/null
@@ -0,0 +1,329 @@
+/*-------------------------------------------------------------------------
+ *
+ * stats.c--
+ *    heap access method debugging statistic collection routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    initam should be moved someplace else.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+
+#include "utils/memutils.h"
+#include "utils/palloc.h"
+#include "utils/elog.h"
+#include "utils/mcxt.h"
+
+/* ----------------
+ *      InitHeapAccessStatistics
+ * ----------------
+ */
+HeapAccessStatistics heap_access_stats = (HeapAccessStatistics) NULL;
+     
+void
+InitHeapAccessStatistics()    
+{
+    MemoryContext    oldContext;
+    HeapAccessStatistics stats;
+    
+    /* ----------------
+     *  make sure we don't initialize things twice
+     * ----------------
+     */
+    if (heap_access_stats != NULL)
+        return;
+    
+    /* ----------------
+     *  allocate statistics structure from the top memory context
+     * ----------------
+     */
+    oldContext = MemoryContextSwitchTo(TopMemoryContext);
+    
+    stats = (HeapAccessStatistics)
+        palloc(sizeof(HeapAccessStatisticsData));
+    
+    /* ----------------
+     *  initialize fields to default values
+     * ----------------
+     */
+    stats->global_open = 0;                    
+    stats->global_openr = 0;
+    stats->global_close = 0;
+    stats->global_beginscan = 0;
+    stats->global_rescan = 0;
+    stats->global_endscan = 0;
+    stats->global_getnext = 0;
+    stats->global_fetch = 0;
+    stats->global_insert = 0;
+    stats->global_delete = 0;
+    stats->global_replace = 0;
+    stats->global_markpos = 0;
+    stats->global_restrpos = 0;
+    stats->global_BufferGetRelation = 0;
+    stats->global_RelationIdGetRelation = 0;
+    stats->global_RelationIdGetRelation_Buf = 0;
+    stats->global_getreldesc = 0;
+    stats->global_heapgettup = 0;
+    stats->global_RelationPutHeapTuple = 0;
+    stats->global_RelationPutLongHeapTuple = 0;
+    
+    stats->local_open = 0;
+    stats->local_openr = 0;
+    stats->local_close = 0;
+    stats->local_beginscan = 0;
+    stats->local_rescan = 0;
+    stats->local_endscan = 0;
+    stats->local_getnext = 0;
+    stats->local_fetch = 0;
+    stats->local_insert = 0;
+    stats->local_delete = 0;
+    stats->local_replace = 0;
+    stats->local_markpos = 0;
+    stats->local_restrpos = 0;
+    stats->local_BufferGetRelation = 0;
+    stats->local_RelationIdGetRelation = 0;
+    stats->local_RelationIdGetRelation_Buf = 0;
+    stats->local_getreldesc = 0;
+    stats->local_heapgettup = 0;
+    stats->local_RelationPutHeapTuple = 0;
+    stats->local_RelationPutLongHeapTuple = 0;
+    stats->local_RelationNameGetRelation = 0;
+    stats->global_RelationNameGetRelation = 0;
+    
+    /* ----------------
+     *  record init times
+     * ----------------
+     */
+    time(&stats->init_global_timestamp);
+    time(&stats->local_reset_timestamp);
+    time(&stats->last_request_timestamp);
+    
+    /* ----------------
+     *  return to old memory context
+     * ----------------
+     */
+    (void) MemoryContextSwitchTo(oldContext);
+    
+    heap_access_stats = stats;
+}
+
+/* ----------------
+ *      ResetHeapAccessStatistics
+ * ----------------
+ */
+void
+ResetHeapAccessStatistics()    
+{
+    HeapAccessStatistics stats;
+    
+    /* ----------------
+     *  do nothing if stats aren't initialized
+     * ----------------
+     */
+    if (heap_access_stats == NULL)
+        return;
+    
+    stats = heap_access_stats;
+    
+    /* ----------------
+     *  reset local counts
+     * ----------------
+     */
+    stats->local_open = 0;
+    stats->local_openr = 0;
+    stats->local_close = 0;
+    stats->local_beginscan = 0;
+    stats->local_rescan = 0;
+    stats->local_endscan = 0;
+    stats->local_getnext = 0;
+    stats->local_fetch = 0;
+    stats->local_insert = 0;
+    stats->local_delete = 0;
+    stats->local_replace = 0;
+    stats->local_markpos = 0;
+    stats->local_restrpos = 0;
+    stats->local_BufferGetRelation = 0;
+    stats->local_RelationIdGetRelation = 0;
+    stats->local_RelationIdGetRelation_Buf = 0;
+    stats->local_getreldesc = 0;
+    stats->local_heapgettup = 0;
+    stats->local_RelationPutHeapTuple = 0;
+    stats->local_RelationPutLongHeapTuple = 0;
+    
+    /* ----------------
+     *  reset local timestamps
+     * ----------------
+     */
+    time(&stats->local_reset_timestamp);
+    time(&stats->last_request_timestamp);
+}
+
+/* ----------------
+ *      GetHeapAccessStatistics
+ * ----------------
+ */
+HeapAccessStatistics GetHeapAccessStatistics()    
+{
+    HeapAccessStatistics stats;
+    
+    /* ----------------
+     *  return nothing if stats aren't initialized
+     * ----------------
+     */
+    if (heap_access_stats == NULL)
+        return NULL;
+    
+    /* ----------------
+     *  record the current request time
+     * ----------------
+     */
+    time(&heap_access_stats->last_request_timestamp);
+    
+    /* ----------------
+     *  allocate a copy of the stats and return it to the caller.
+     * ----------------
+     */
+    stats = (HeapAccessStatistics)
+        palloc(sizeof(HeapAccessStatisticsData));
+    
+     memmove(stats,
+            heap_access_stats,
+            sizeof(HeapAccessStatisticsData)); 
+    
+    return stats;
+}
+
+/* ----------------
+ *      PrintHeapAccessStatistics
+ * ----------------
+ */
+void
+PrintHeapAccessStatistics(HeapAccessStatistics stats)
+{
+    /* ----------------
+     *  return nothing if stats aren't valid
+     * ----------------
+     */
+    if (stats == NULL)
+        return;
+    
+    printf("======== heap am statistics ========\n");
+    printf("init_global_timestamp:      %s",
+           ctime(&(stats->init_global_timestamp)));
+    
+    printf("local_reset_timestamp:      %s",
+           ctime(&(stats->local_reset_timestamp)));
+    
+    printf("last_request_timestamp:     %s",
+           ctime(&(stats->last_request_timestamp)));
+    
+    printf("local/global_open:                        %6d/%6d\n",
+           stats->local_open, stats->global_open);
+    
+    printf("local/global_openr:                       %6d/%6d\n",
+           stats->local_openr, stats->global_openr);
+    
+    printf("local/global_close:                       %6d/%6d\n",
+           stats->local_close, stats->global_close);
+    
+    printf("local/global_beginscan:                   %6d/%6d\n",
+           stats->local_beginscan, stats->global_beginscan);
+    
+    printf("local/global_rescan:                      %6d/%6d\n",
+           stats->local_rescan, stats->global_rescan);
+    
+    printf("local/global_endscan:                     %6d/%6d\n",
+           stats->local_endscan, stats->global_endscan);
+    
+    printf("local/global_getnext:                     %6d/%6d\n",
+           stats->local_getnext, stats->global_getnext);
+    
+    printf("local/global_fetch:                       %6d/%6d\n",
+           stats->local_fetch, stats->global_fetch);
+    
+    printf("local/global_insert:                      %6d/%6d\n",
+           stats->local_insert, stats->global_insert);
+    
+    printf("local/global_delete:                      %6d/%6d\n",
+           stats->local_delete, stats->global_delete);
+    
+    printf("local/global_replace:                     %6d/%6d\n",
+           stats->local_replace, stats->global_replace);
+    
+    printf("local/global_markpos:                     %6d/%6d\n",
+           stats->local_markpos, stats->global_markpos);
+    
+    printf("local/global_restrpos:                    %6d/%6d\n",
+           stats->local_restrpos, stats->global_restrpos);
+    
+    printf("================\n");
+    
+    printf("local/global_BufferGetRelation:             %6d/%6d\n",
+           stats->local_BufferGetRelation,
+           stats->global_BufferGetRelation);
+    
+    printf("local/global_RelationIdGetRelation:         %6d/%6d\n",
+           stats->local_RelationIdGetRelation,
+           stats->global_RelationIdGetRelation);
+    
+    printf("local/global_RelationIdGetRelation_Buf:     %6d/%6d\n",
+           stats->local_RelationIdGetRelation_Buf,
+           stats->global_RelationIdGetRelation_Buf);
+    
+    printf("local/global_getreldesc:                    %6d/%6d\n",
+           stats->local_getreldesc, stats->global_getreldesc);
+    
+    printf("local/global_heapgettup:                    %6d/%6d\n",
+           stats->local_heapgettup, stats->global_heapgettup);
+    
+    printf("local/global_RelationPutHeapTuple:          %6d/%6d\n",
+           stats->local_RelationPutHeapTuple,
+           stats->global_RelationPutHeapTuple);
+    
+    printf("local/global_RelationPutLongHeapTuple:      %6d/%6d\n",
+           stats->local_RelationPutLongHeapTuple,
+           stats->global_RelationPutLongHeapTuple);
+    
+    printf("===================================\n");
+    
+    printf("\n");
+}
+
+/* ----------------
+ *      PrintAndFreeHeapAccessStatistics
+ * ----------------
+ */
+void
+PrintAndFreeHeapAccessStatistics(HeapAccessStatistics stats)
+{
+    PrintHeapAccessStatistics(stats);
+    if (stats != NULL)
+       pfree(stats);
+}
+
+/* ----------------------------------------------------------------
+ *                 access method initialization
+ * ----------------------------------------------------------------
+ */
+/* ----------------
+ *     initam should someday be moved someplace else.
+ * ----------------
+ */
+void
+initam()
+{
+    /* ----------------
+     * initialize heap statistics.
+     * ----------------
+     */
+    InitHeapAccessStatistics();
+}
diff --git a/src/backend/access/heapam.h b/src/backend/access/heapam.h
new file mode 100644 (file)
index 0000000..69f2f23
--- /dev/null
@@ -0,0 +1,149 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam.h--
+ *    POSTGRES heap access method definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        HEAPAM_H
+#define HEAPAM_H
+
+#include <sys/types.h>
+
+#include "postgres.h"
+
+#include "access/attnum.h"
+#include "access/htup.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "utils/tqual.h"
+#include "access/tupdesc.h"
+#include "storage/smgr.h"
+#include "utils/rel.h"
+
+/* ----------------------------------------------------------------
+ *             heap access method statistics
+ * ----------------------------------------------------------------
+ */
+
+typedef struct HeapAccessStatisticsData {
+    time_t  init_global_timestamp;     /* time global statistics started */
+    time_t  local_reset_timestamp;     /* last time local reset was done */
+    time_t  last_request_timestamp;    /* last time stats were requested */
+
+    int global_open;                   
+    int global_openr;
+    int global_close;
+    int global_beginscan;
+    int global_rescan;
+    int global_endscan;
+    int global_getnext;
+    int global_fetch;
+    int global_insert;
+    int global_delete;
+    int global_replace; 
+    int global_markpos; 
+    int global_restrpos;
+    int global_BufferGetRelation;
+    int global_RelationIdGetRelation;
+    int global_RelationIdGetRelation_Buf;
+    int global_RelationNameGetRelation;
+    int global_getreldesc;
+    int global_heapgettup;
+    int global_RelationPutHeapTuple;
+    int global_RelationPutLongHeapTuple;
+
+    int local_open;                    
+    int local_openr;
+    int local_close;
+    int local_beginscan;
+    int local_rescan;
+    int local_endscan;
+    int local_getnext;
+    int local_fetch;
+    int local_insert;
+    int local_delete;
+    int local_replace; 
+    int local_markpos; 
+    int local_restrpos;
+    int local_BufferGetRelation;
+    int local_RelationIdGetRelation;
+    int local_RelationIdGetRelation_Buf;
+    int local_RelationNameGetRelation;
+    int local_getreldesc;
+    int local_heapgettup;
+    int local_RelationPutHeapTuple;
+    int local_RelationPutLongHeapTuple;
+} HeapAccessStatisticsData;
+
+typedef HeapAccessStatisticsData *HeapAccessStatistics;
+
+#define IncrHeapAccessStat(x) \
+    (heap_access_stats == NULL ? 0 : (heap_access_stats->x)++)
+
+extern HeapAccessStatistics heap_access_stats; /* in stats.c */
+
+/* ----------------
+ *     function prototypes for heap access method
+ * ----------------
+ */
+/* heap_create, heap_creatr, and heap_destroy are declared in catalog/heap.h */
+#include "catalog/heap.h"
+
+/* heapam.c */
+extern void doinsert(Relation relation, HeapTuple tup);
+extern void SetHeapAccessMethodImmediateInvalidation(bool on);
+
+extern Relation heap_open(Oid relationId);
+extern Relation heap_openr(char *relationName);
+extern void heap_close(Relation relation);
+extern HeapScanDesc heap_beginscan(Relation relation, int atend,
+                           TimeQual timeQual, unsigned nkeys, ScanKey key);
+extern void heap_rescan(HeapScanDesc sdesc, bool scanFromEnd, ScanKey key);
+extern void heap_endscan(HeapScanDesc sdesc);
+extern HeapTuple heap_getnext(HeapScanDesc scandesc, int backw, Buffer *b);
+extern HeapTuple heap_fetch(Relation relation, TimeQual timeQual,
+                           ItemPointer tid, Buffer *b);
+extern Oid heap_insert(Relation relation, HeapTuple tup);
+extern void heap_delete(Relation relation, ItemPointer tid);
+extern int heap_replace(Relation relation, ItemPointer otid,
+                       HeapTuple tup);
+extern void heap_markpos(HeapScanDesc sdesc);
+extern void heap_restrpos(HeapScanDesc sdesc);
+
+/* in common/heaptuple.c */
+extern Size ComputeDataSize(TupleDesc tupleDesc, Datum value[], char nulls[]);
+extern void DataFill(char *data, TupleDesc tupleDesc,
+                    Datum value[], char nulls[], char *infomask,
+                    bits8 bit[]);
+extern int heap_attisnull(HeapTuple tup, int attnum);
+extern int heap_sysattrlen(AttrNumber attno);
+extern bool heap_sysattrbyval(AttrNumber attno);
+extern char *heap_getsysattr(HeapTuple tup, Buffer b, int attnum);
+extern char *fastgetattr(HeapTuple tup, unsigned attnum,
+                        TupleDesc att, bool *isnull);
+extern char *heap_getattr(HeapTuple tup, Buffer b, int attnum,
+                         TupleDesc att, bool *isnull);
+extern HeapTuple heap_copytuple(HeapTuple tuple);
+extern void heap_deformtuple(HeapTuple tuple, TupleDesc tdesc,
+                            Datum values[], char nulls[]);
+extern HeapTuple heap_formtuple(TupleDesc tupleDescriptor, 
+                               Datum value[], char nulls[]);
+extern HeapTuple heap_modifytuple(HeapTuple tuple, Buffer buffer,
+       Relation relation, Datum replValue[], char replNull[], char repl[]);
+HeapTuple heap_addheader(uint32        natts, int structlen, char *structure);
+
+/* in common/heap/stats.c */
+extern void InitHeapAccessStatistics(void);
+extern void ResetHeapAccessStatistics(void);
+extern HeapAccessStatistics GetHeapAccessStatistics(void);
+extern void PrintHeapAccessStatistics(HeapAccessStatistics stats);
+extern void PrintAndFreeHeapAccessStatistics(HeapAccessStatistics stats);
+extern void initam(void);
+
+#endif /* HEAPAM_H */
diff --git a/src/backend/access/hio.h b/src/backend/access/hio.h
new file mode 100644 (file)
index 0000000..6bb1139
--- /dev/null
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * hio.h--
+ *    POSTGRES heap access method input/output definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        HIO_H
+#define HIO_H
+
+#include "c.h"
+
+#include "storage/block.h"
+#include "access/htup.h"
+#include "utils/rel.h"
+
+extern void RelationPutHeapTuple(Relation relation, BlockNumber blockIndex,
+                                HeapTuple tuple);
+extern void RelationPutHeapTupleAtEnd(Relation relation, HeapTuple tuple);
+
+#endif /* HIO_H */
diff --git a/src/backend/access/htup.h b/src/backend/access/htup.h
new file mode 100644 (file)
index 0000000..f1eb535
--- /dev/null
@@ -0,0 +1,115 @@
+/*-------------------------------------------------------------------------
+ *
+ * htup.h--
+ *    POSTGRES heap tuple definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        HTUP_H
+#define HTUP_H
+
+#include "access/attnum.h"
+#include "storage/bufpage.h"           /* just to reduce levels of #include */
+#include "storage/itemptr.h"
+#include "utils/nabstime.h"
+
+#define MinHeapTupleBitmapSize 32              /* 8 * 4 */
+
+/* check these, they are likely to be more severely limited by t_hoff */
+
+#define MaxHeapAttributeNumber 1600            /* 8 * 200 */
+
+/*
+ * to avoid wasting space, the attributes should be layed out in such a
+ * way to reduce structure padding.
+ */
+typedef struct HeapTupleData {
+
+    unsigned int       t_len;          /* length of entire tuple */
+
+    ItemPointerData    t_ctid;         /* current TID of this tuple */
+
+    ItemPointerData    t_chain;        /* replaced tuple TID */
+
+    Oid                        t_oid;          /* OID of this tuple -- 4 bytes */
+
+    CommandId          t_cmin;         /* insert CID stamp -- 2 bytes each */
+    CommandId          t_cmax;         /* delete CommandId stamp */
+
+    TransactionId      t_xmin;         /* insert XID stamp -- 4 bytes each */
+    TransactionId      t_xmax;         /* delete XID stamp */
+
+    AbsoluteTime       t_tmin;         /* time stamps -- 4 bytes each */
+    AbsoluteTime       t_tmax; 
+
+    int16              t_natts;        /* number of attributes */
+    char               t_vtype;        /* not used - padding */
+
+    char               t_infomask;     /* whether tuple as null or variable
+                                        * length attributes
+                                        */
+
+    uint8              t_hoff;         /* sizeof tuple header */
+
+    bits8              t_bits[MinHeapTupleBitmapSize / 8];
+                                       /* bit map of domains */
+
+    /* MORE DATA FOLLOWS AT END OF STRUCT */
+} HeapTupleData;       
+
+typedef HeapTupleData  *HeapTuple;
+
+
+#define SelfItemPointerAttributeNumber         (-1)
+#define ObjectIdAttributeNumber                        (-2)
+#define MinTransactionIdAttributeNumber                (-3)
+#define MinCommandIdAttributeNumber            (-4)
+#define MaxTransactionIdAttributeNumber                (-5)
+#define MaxCommandIdAttributeNumber            (-6)
+#define ChainItemPointerAttributeNumber                (-7)
+#define AnchorItemPointerAttributeNumber       (-8)
+#define MinAbsoluteTimeAttributeNumber         (-9)
+#define MaxAbsoluteTimeAttributeNumber         (-10)
+#define VersionTypeAttributeNumber             (-11)
+#define FirstLowInvalidHeapAttributeNumber     (-12)
+
+
+/* ----------------
+ *     support macros
+ * ----------------
+ */
+#define GETSTRUCT(TUP) (((char *)(TUP)) + ((HeapTuple)(TUP))->t_hoff)
+
+
+/*
+ * BITMAPLEN(NATTS) - 
+ *     Computes minimum size of bitmap given number of domains.
+ */
+#define BITMAPLEN(NATTS) \
+       ((((((int)(NATTS) - 1) >> 3) + 4 - (MinHeapTupleBitmapSize >> 3)) \
+         & ~03) + (MinHeapTupleBitmapSize >> 3))
+
+/*
+ * HeapTupleIsValid
+ *     True iff the heap tuple is valid.
+ */
+#define        HeapTupleIsValid(tuple) PointerIsValid(tuple)
+
+/*
+ * information stored in t_infomask:
+ */
+#define HEAP_HASNULL           0x01    /* has null attribute(s) */
+#define        HEAP_HASVARLENA         0x02    /* has variable length attribute(s) */
+
+#define HeapTupleNoNulls(tuple) \
+       (!(((HeapTuple) (tuple))->t_infomask & HEAP_HASNULL))
+
+#define HeapTupleAllFixed(tuple) \
+       (!(((HeapTuple) (tuple))->t_infomask & HEAP_HASVARLENA))
+
+#endif /* HTUP_H */
diff --git a/src/backend/access/ibit.h b/src/backend/access/ibit.h
new file mode 100644 (file)
index 0000000..f25c04c
--- /dev/null
@@ -0,0 +1,34 @@
+/*-------------------------------------------------------------------------
+ *
+ * ibit.h--
+ *    POSTGRES index valid attribute bit map definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        IBIT_H
+#define IBIT_H
+
+#include "c.h"
+#include "utils/memutils.h"
+
+typedef struct IndexAttributeBitMapData {
+       char    bits[(MaxIndexAttributeNumber + MaxBitsPerByte - 1)
+               / MaxBitsPerByte];
+} IndexAttributeBitMapData;
+
+typedef IndexAttributeBitMapData       *IndexAttributeBitMap;
+
+#define IndexAttributeBitMapSize       sizeof(IndexAttributeBitMapData)
+
+/*
+ * IndexAttributeBitMapIsValid --
+ *     True iff attribute bit map is valid.
+ */
+#define        IndexAttributeBitMapIsValid(bits) PointerIsValid(bits)
+
+#endif /* IBIT_H */
diff --git a/src/backend/access/index/Makefile.inc b/src/backend/access/index/Makefile.inc
new file mode 100644 (file)
index 0000000..4ce0254
--- /dev/null
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for access/index
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= genam.c indexam.c istrat.c
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
new file mode 100644 (file)
index 0000000..e676280
--- /dev/null
@@ -0,0 +1,275 @@
+/*-------------------------------------------------------------------------
+ *
+ * genam.c--
+ *    general index access method routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    many of the old access method routines have been turned into
+ *    macros and moved to genam.h -cim 4/30/91
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * OLD COMMENTS
+ * Scans are implemented as follows:
+ *
+ * `0' represents an invalid item pointer.
+ * `-' represents an unknown item pointer.
+ * `X' represents a known item pointers.
+ * `+' represents known or invalid item pointers.
+ * `*' represents any item pointers.
+ *
+ * State is represented by a triple of these symbols in the order of
+ * previous, current, next.  Note that the case of reverse scans works
+ * identically.
+ *
+ *     State   Result
+ * (1) + + -   + 0 0           (if the next item pointer is invalid)
+ * (2)         + X -           (otherwise)
+ * (3) * 0 0   * 0 0           (no change)
+ * (4) + X 0   X 0 0           (shift)
+ * (5) * + X   + X -           (shift, add unknown)
+ *
+ * All other states cannot occur.
+ *
+ * Note:
+ *It would be possible to cache the status of the previous and
+ *     next item pointer using the flags.
+ * ----------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/attnum.h"
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/itup.h"
+#include "access/relscan.h"
+#include "access/sdir.h"
+#include "access/skey.h"
+
+#include "storage/bufmgr.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+
+#include "catalog/catname.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_index.h"
+#include "catalog/pg_proc.h"
+
+#include "catalog/index.h"
+
+/* ----------------------------------------------------------------
+ *     general access method routines
+ *
+ *     All indexed access methods use an identical scan structure.
+ *     We don't know how the various AMs do locking, however, so we don't
+ *     do anything about that here.
+ *
+ *     The intent is that an AM implementor will define a front-end routine
+ *     that calls this one, to fill in the scan, and then does whatever kind
+ *     of locking he wants.
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *  RelationGetIndexScan -- Create and fill an IndexScanDesc.
+ *
+ *     This routine creates an index scan structure and sets its contents
+ *     up correctly. This routine calls AMrescan to set up the scan with
+ *     the passed key.
+ *
+ *     Parameters:
+ *             relation -- index relation for scan.
+ *             scanFromEnd -- if true, begin scan at one of the index's
+ *                            endpoints.
+ *             numberOfKeys -- count of scan keys (more than one won't
+ *                             necessarily do anything useful, yet).
+ *             key -- the ScanKey for the starting position of the scan.
+ *
+ *     Returns:
+ *             An initialized IndexScanDesc.
+ *
+ *     Side Effects:
+ *             Bumps the ref count on the relation to keep it in the cache.
+ *     
+ * ----------------
+ */
+IndexScanDesc
+RelationGetIndexScan(Relation relation,
+                    bool scanFromEnd,
+                    uint16 numberOfKeys,
+                    ScanKey key)
+{
+    IndexScanDesc      scan;
+    
+    if (! RelationIsValid(relation))
+       elog(WARN, "RelationGetIndexScan: relation invalid");
+    
+    scan = (IndexScanDesc) palloc(sizeof(IndexScanDescData));
+    
+    scan->relation = relation;
+    scan->opaque = NULL;
+    scan->numberOfKeys = numberOfKeys;
+    
+    ItemPointerSetInvalid(&scan->previousItemData);
+    ItemPointerSetInvalid(&scan->currentItemData);
+    ItemPointerSetInvalid(&scan->nextItemData);
+    ItemPointerSetInvalid(&scan->previousMarkData);
+    ItemPointerSetInvalid(&scan->currentMarkData);
+    ItemPointerSetInvalid(&scan->nextMarkData);
+
+    if (numberOfKeys > 0) {
+       scan->keyData = (ScanKey) palloc(sizeof(ScanKeyData) * numberOfKeys);
+    } else {
+       scan->keyData = NULL;
+    }
+
+    index_rescan(scan, scanFromEnd, key);
+    
+    return (scan);
+}
+
+/* ----------------
+ *  IndexScanRestart -- Restart an index scan.
+ *
+ *     This routine isn't used by any existing access method.  It's
+ *     appropriate if relation level locks are what you want.
+ *
+ *  Returns:
+ *     None.
+ *
+ *  Side Effects:
+ *     None.
+ * ----------------
+ */
+void
+IndexScanRestart(IndexScanDesc scan,
+                bool scanFromEnd,
+                ScanKey key)
+{
+    if (! IndexScanIsValid(scan))
+       elog(WARN, "IndexScanRestart: invalid scan");
+    
+    ItemPointerSetInvalid(&scan->previousItemData);
+    ItemPointerSetInvalid(&scan->currentItemData);
+    ItemPointerSetInvalid(&scan->nextItemData);
+    
+    if (RelationGetNumberOfBlocks(scan->relation) == 0) 
+       scan->flags = ScanUnmarked;
+    else if (scanFromEnd)
+       scan->flags = ScanUnmarked | ScanUncheckedPrevious;
+    else
+       scan->flags = ScanUnmarked | ScanUncheckedNext;
+    
+    scan->scanFromEnd = (bool) scanFromEnd;
+    
+    if (scan->numberOfKeys > 0)
+       memmove(scan->keyData,
+               key,
+               scan->numberOfKeys * sizeof(ScanKeyData));
+}
+
+/* ----------------
+ *  IndexScanEnd -- End and index scan.
+ *
+ *     This routine is not used by any existing access method, but is
+ *     suitable for use if you don't want to do sophisticated locking.
+ *
+ *  Returns:
+ *     None.
+ *
+ *  Side Effects:
+ *     None.
+ * ----------------
+ */
+void
+IndexScanEnd(IndexScanDesc scan)
+{
+    if (! IndexScanIsValid(scan))
+       elog(WARN, "IndexScanEnd: invalid scan");
+    
+    pfree(scan);
+}
+
+/* ----------------
+ *  IndexScanMarkPosition -- Mark current position in a scan.
+ *
+ *     This routine isn't used by any existing access method, but is the
+ *     one that AM implementors should use, if they don't want to do any
+ *     special locking.  If relation-level locking is sufficient, this is
+ *     the routine for you.
+ *
+ *  Returns:
+ *     None.
+ *
+ *  Side Effects:
+ *     None.
+ * ----------------
+ */
+void
+IndexScanMarkPosition(IndexScanDesc scan)
+{
+    RetrieveIndexResult        result;
+    
+    if (scan->flags & ScanUncheckedPrevious) {
+       result = 
+           index_getnext(scan, BackwardScanDirection);
+       
+       if (result != NULL) {
+           scan->previousItemData = result->index_iptr;
+       } else {
+           ItemPointerSetInvalid(&scan->previousItemData);
+       }
+       
+    } else if (scan->flags & ScanUncheckedNext) {
+       result = (RetrieveIndexResult)
+           index_getnext(scan, ForwardScanDirection);
+       
+       if (result != NULL) {
+           scan->nextItemData = result->index_iptr;
+       } else {
+           ItemPointerSetInvalid(&scan->nextItemData);
+       }
+    }
+    
+    scan->previousMarkData = scan->previousItemData;
+    scan->currentMarkData = scan->currentItemData;
+    scan->nextMarkData = scan->nextItemData;
+    
+    scan->flags = 0x0; /* XXX should have a symbolic name */
+}
+
+/* ----------------
+ *  IndexScanRestorePosition -- Restore position on a marked scan.
+ *
+ *     This routine isn't used by any existing access method, but is the
+ *     one that AM implementors should use if they don't want to do any
+ *     special locking.  If relation-level locking is sufficient, then
+ *     this is the one you want.
+ *
+ *  Returns:
+ *     None.
+ *
+ *  Side Effects:
+ *     None.
+ * ----------------
+ */
+void
+IndexScanRestorePosition(IndexScanDesc scan)
+{      
+    if (scan->flags & ScanUnmarked) 
+       elog(WARN, "IndexScanRestorePosition: no mark to restore");
+    
+    scan->previousItemData = scan->previousMarkData;
+    scan->currentItemData = scan->currentMarkData;
+    scan->nextItemData = scan->nextMarkData;
+    
+    scan->flags = 0x0; /* XXX should have a symbolic name */
+}
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
new file mode 100644 (file)
index 0000000..b1cd582
--- /dev/null
@@ -0,0 +1,411 @@
+/*-------------------------------------------------------------------------
+ *
+ * indexam.c--
+ *    general index access method routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * INTERFACE ROUTINES
+ *     index_open      - open an index relation by relationId
+ *     index_openr     - open a index relation by name
+ *     index_close     - close a index relation
+ *     index_beginscan - start a scan of an index
+ *     index_rescan    - restart a scan of an index
+ *     index_endscan   - end a scan
+ *     index_insert    - insert an index tuple into a relation
+ *     index_delete    - delete an item from an index relation
+ *     index_markpos   - mark a scan position
+ *     index_restrpos  - restore a scan position
+ *     index_getnext   - get the next tuple from a scan
+ * **  index_fetch     - retrieve tuple with tid
+ * **  index_replace   - replace a tuple
+ * **  index_getattr   - get an attribute from an index tuple
+ *     index_getprocid - get a support procedure id from the rel tuple
+ *     
+ *     IndexScanIsValid - check index scan
+ *
+ * NOTES
+ *     This file contains the index_ routines which used
+ *     to be a scattered collection of stuff in access/genam.
+ *
+ *     The ** routines: index_fetch, index_replace, and index_getattr
+ *     have not yet been implemented.  They may not be needed.
+ *
+ * old comments
+ *     Scans are implemented as follows:
+ *
+ *     `0' represents an invalid item pointer.
+ *     `-' represents an unknown item pointer.
+ *     `X' represents a known item pointers.
+ *     `+' represents known or invalid item pointers.
+ *     `*' represents any item pointers.
+ *
+ *     State is represented by a triple of these symbols in the order of
+ *     previous, current, next.  Note that the case of reverse scans works
+ *     identically.
+ *
+ *             State   Result
+ *     (1)     + + -   + 0 0           (if the next item pointer is invalid)
+ *     (2)             + X -           (otherwise)
+ *     (3)     * 0 0   * 0 0           (no change)
+ *     (4)     + X 0   X 0 0           (shift)
+ *     (5)     * + X   + X -           (shift, add unknown)
+ *
+ *     All other states cannot occur.
+ *
+ *     Note: It would be possible to cache the status of the previous and
+ *           next item pointer using the flags.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/attnum.h"
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/itup.h"
+#include "access/relscan.h"
+#include "access/sdir.h"
+#include "access/skey.h"
+#include "access/funcindex.h"
+
+#include "storage/lmgr.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+#include "utils/relcache.h"
+
+#include "catalog/catname.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_index.h"
+#include "catalog/pg_proc.h"
+
+#include "catalog/index.h"
+
+#include "fmgr.h"
+
+/* ----------------
+ *   undefine macros we aren't going to use that would otherwise
+ *   get in our way..  delete is defined in c.h and the am's are
+ *   defined in heapam.h
+ * ----------------
+ */
+#undef delete
+#undef aminsert
+#undef amdelete
+#undef ambeginscan
+#undef amrescan
+#undef amendscan
+#undef ammarkpos
+#undef amrestrpos
+#undef amgettuple
+
+/* ----------------------------------------------------------------
+ *                 macros used in index_ routines
+ * ----------------------------------------------------------------
+ */
+#define RELATION_CHECKS \
+Assert(RelationIsValid(relation)); \
+        Assert(PointerIsValid(relation->rd_am))
+     
+#define SCAN_CHECKS \
+     Assert(IndexScanIsValid(scan)); \
+        Assert(RelationIsValid(scan->relation)); \
+        Assert(PointerIsValid(scan->relation->rd_am))
+     
+#define GET_REL_PROCEDURE(x,y) \
+        CppConcat(procedure = relation->rd_am->,y); \
+        if (! RegProcedureIsValid(procedure)) \
+        elog(WARN, "index_%s: invalid %s regproc", \
+             CppAsString(x), CppAsString(y))
+     
+#define GET_SCAN_PROCEDURE(x,y) \
+        CppConcat(procedure = scan->relation->rd_am->,y); \
+        if (! RegProcedureIsValid(procedure)) \
+        elog(WARN, "index_%s: invalid %s regproc", \
+             CppAsString(x), CppAsString(y))
+     
+     
+/* ----------------------------------------------------------------
+ *                index_ interface functions
+ * ----------------------------------------------------------------
+ */
+/* ----------------
+ *     index_open - open an index relation by relationId
+ *
+ *     presently the relcache routines do all the work we need
+ *     to open/close index relations.
+ * ----------------
+ */
+Relation
+index_open(Oid relationId)
+{
+    return RelationIdGetRelation(relationId);
+}
+
+/* ----------------
+ *     index_openr - open a index relation by name
+ *
+ *     presently the relcache routines do all the work we need
+ *     to open/close index relations.
+ * ----------------
+ */
+Relation
+index_openr(char *relationName)
+{
+    return RelationNameGetRelation(relationName);
+}
+
+/* ----------------
+ *     index_close - close a index relation
+ *
+ *     presently the relcache routines do all the work we need
+ *     to open/close index relations.
+ * ----------------
+ */
+void
+index_close(Relation relation)
+{
+    (void) RelationClose(relation);
+}
+
+/* ----------------
+ *     index_insert - insert an index tuple into a relation
+ * ----------------
+ */
+InsertIndexResult
+index_insert(Relation relation,
+            IndexTuple indexTuple)
+{
+    RegProcedure               procedure;
+    InsertIndexResult          specificResult;
+    
+    RELATION_CHECKS;
+    GET_REL_PROCEDURE(insert,aminsert);
+    
+    /* ----------------
+     * have the am's insert proc do all the work.  
+     * ----------------
+     */
+    specificResult = (InsertIndexResult)
+       fmgr(procedure, relation, indexTuple, NULL);
+    
+    /* ----------------
+     * the insert proc is supposed to return a "specific result" and
+     *  this routine has to return a "general result" so after we get
+     *  something back from the insert proc, we allocate a
+     *  "general result" and copy some crap between the two.
+     *
+     *  As far as I'm concerned all this result shit is needlessly c
+     *  omplicated and should be eliminated.  -cim 1/19/91
+     *
+     *  mao concurs.  regardless of how we feel here, however, it is
+     *  important to free memory we don't intend to return to anyone.
+     *  2/28/91
+     *
+     *  this "general result" crap is now gone. -ay 3/6/95
+     * ----------------
+     */
+    
+    return (specificResult);
+}
+
+/* ----------------
+ *     index_delete - delete an item from an index relation
+ * ----------------
+ */
+void
+index_delete(Relation relation, ItemPointer indexItem)
+{
+    RegProcedure       procedure;
+    
+    RELATION_CHECKS;
+    GET_REL_PROCEDURE(delete,amdelete);
+    
+    (void) fmgr(procedure, relation, indexItem);    
+}
+
+/* ----------------
+ *     index_beginscan - start a scan of an index
+ * ----------------
+ */
+IndexScanDesc
+index_beginscan(Relation relation,
+               bool scanFromEnd,
+               uint16 numberOfKeys,
+               ScanKey key)
+{
+    IndexScanDesc      scandesc;
+    RegProcedure       procedure;
+    
+    RELATION_CHECKS;
+    GET_REL_PROCEDURE(beginscan,ambeginscan);
+    
+    RelationSetRIntentLock(relation);
+    
+    scandesc = (IndexScanDesc)
+       fmgr(procedure, relation, scanFromEnd, numberOfKeys, key);
+    
+    return scandesc;
+}
+
+/* ----------------
+ *     index_rescan  - restart a scan of an index
+ * ----------------
+ */
+void
+index_rescan(IndexScanDesc scan, bool scanFromEnd, ScanKey key)
+{
+    RegProcedure       procedure;
+    
+    SCAN_CHECKS;
+    GET_SCAN_PROCEDURE(rescan,amrescan);
+    
+    (void) fmgr(procedure, scan, scanFromEnd, key);
+}
+
+/* ----------------
+ *     index_endscan - end a scan
+ * ----------------
+ */
+void
+index_endscan(IndexScanDesc scan)
+{
+    RegProcedure       procedure;
+    
+    SCAN_CHECKS;
+    GET_SCAN_PROCEDURE(endscan,amendscan);
+    
+    (void) fmgr(procedure, scan);
+    
+    RelationUnsetRIntentLock(scan->relation);
+}
+
+/* ----------------
+ *     index_markpos  - mark a scan position
+ * ----------------
+ */
+void
+index_markpos(IndexScanDesc scan)
+{
+    RegProcedure       procedure;
+    
+    SCAN_CHECKS;
+    GET_SCAN_PROCEDURE(markpos,ammarkpos);
+    
+    (void) fmgr(procedure, scan);
+}
+
+/* ----------------
+ *     index_restrpos  - restore a scan position
+ * ----------------
+ */
+void
+index_restrpos(IndexScanDesc scan)
+{
+    RegProcedure       procedure;
+    
+    SCAN_CHECKS;
+    GET_SCAN_PROCEDURE(restrpos,amrestrpos);
+    
+    (void) fmgr(procedure, scan);
+}
+
+/* ----------------
+ *     index_getnext - get the next tuple from a scan
+ *
+ *     A RetrieveIndexResult is a index tuple/heap tuple pair
+ * ----------------
+ */
+RetrieveIndexResult
+index_getnext(IndexScanDesc scan,
+             ScanDirection direction)
+{
+    RegProcedure               procedure;
+    RetrieveIndexResult                result;
+    
+    SCAN_CHECKS;
+    GET_SCAN_PROCEDURE(getnext,amgettuple);
+    
+    /* ----------------
+     * have the am's gettuple proc do all the work.  
+     * ----------------
+     */
+    result = (RetrieveIndexResult)
+       fmgr(procedure, scan, direction);
+    
+    return result;
+}
+
+/* ----------------
+ *     index_getprocid
+ *
+ *     Some indexed access methods may require support routines that are
+ *     not in the operator class/operator model imposed by pg_am.  These
+ *     access methods may store the OIDs of registered procedures they
+ *     need in pg_amproc.  These registered procedure OIDs are ordered in
+ *     a way that makes sense to the access method, and used only by the
+ *     access method.  The general index code doesn't know anything about
+ *     the routines involved; it just builds an ordered list of them for
+ *     each attribute on which an index is defined.
+ *
+ *     This routine returns the requested procedure OID for a particular
+ *     indexed attribute.
+ * ----------------
+ */
+RegProcedure
+index_getprocid(Relation irel,
+               AttrNumber attnum,
+               uint16 procnum)
+{
+    RegProcedure *loc;
+    int natts;
+    
+    natts = irel->rd_rel->relnatts;
+    
+    loc = irel->rd_support;
+
+    Assert(loc != NULL);
+    
+    return (loc[(natts * (procnum - 1)) + (attnum - 1)]);
+}
+
+Datum
+GetIndexValue(HeapTuple tuple,
+             TupleDesc hTupDesc,
+             int attOff,
+             AttrNumber attrNums[],
+             FuncIndexInfo *fInfo,
+             bool *attNull,
+             Buffer buffer)
+{
+    Datum returnVal;
+    bool       isNull;
+    
+    if (PointerIsValid(fInfo) && FIgetProcOid(fInfo) != InvalidOid) {
+       int i;
+       Datum *attData = (Datum *)palloc(FIgetnArgs(fInfo)*sizeof(Datum));
+       
+       for (i = 0; i < FIgetnArgs(fInfo); i++) {
+           attData[i] = (Datum) heap_getattr(tuple, 
+                                             buffer, 
+                                             attrNums[i], 
+                                             hTupDesc,
+                                             attNull);
+       }
+       returnVal = (Datum)fmgr_array_args(FIgetProcOid(fInfo),
+                                          FIgetnArgs(fInfo),
+                                          (char **) attData,
+                                          &isNull);
+       pfree(attData);
+       *attNull = FALSE;
+    }else {
+       returnVal = (Datum) heap_getattr(tuple, buffer, attrNums[attOff], 
+                                        hTupDesc, attNull);
+    }
+    return returnVal;
+}
diff --git a/src/backend/access/index/istrat.c b/src/backend/access/index/istrat.c
new file mode 100644 (file)
index 0000000..5f8c738
--- /dev/null
@@ -0,0 +1,679 @@
+/*-------------------------------------------------------------------------
+ *
+ * istrat.c--
+ *    index scan strategy manipulation code and index strategy manipulation
+ *    operator code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/attnum.h"
+#include "access/heapam.h"
+#include "access/istrat.h"
+#include "access/itup.h"       /* for MaxIndexAttributeNumber */
+#include "access/skey.h"
+#include "utils/tqual.h"       /* for NowTimeQual */
+
+#include "fmgr.h"
+#include "utils/elog.h"
+#include "utils/rel.h"
+
+#include "catalog/catname.h"
+#include "catalog/pg_amop.h"
+#include "catalog/pg_amproc.h"
+#include "catalog/pg_index.h"
+#include "catalog/pg_proc.h"
+
+/* ----------------------------------------------------------------
+ *                misc strategy support routines
+ * ----------------------------------------------------------------
+ */
+     
+/* 
+ *     StrategyNumberIsValid
+ *     StrategyNumberIsInBounds
+ *     StrategyMapIsValid
+ *     StrategyTransformMapIsValid
+ *     IndexStrategyIsValid
+ *
+ *             ... are now macros in istrat.h -cim 4/27/91
+ */
+     
+/*
+ * StrategyMapGetScanKeyEntry --
+ *     Returns a scan key entry of a index strategy mapping member.
+ *
+ * Note:
+ *     Assumes that the index strategy mapping is valid.
+ *     Assumes that the index strategy number is valid.
+ *     Bounds checking should be done outside this routine.
+ */
+ScanKey
+StrategyMapGetScanKeyEntry(StrategyMap map,
+                          StrategyNumber strategyNumber)
+{
+    Assert(StrategyMapIsValid(map));
+    Assert(StrategyNumberIsValid(strategyNumber));
+    return (&map->entry[strategyNumber - 1]);
+}
+
+/*
+ * IndexStrategyGetStrategyMap --
+ *     Returns an index strategy mapping of an index strategy.
+ *
+ * Note:
+ *     Assumes that the index strategy is valid.
+ *     Assumes that the number of index strategies is valid.
+ *     Bounds checking should be done outside this routine.
+ */
+StrategyMap
+IndexStrategyGetStrategyMap(IndexStrategy indexStrategy,
+                           StrategyNumber maxStrategyNum,
+                           AttrNumber attrNum)
+{
+    Assert(IndexStrategyIsValid(indexStrategy));
+    Assert(StrategyNumberIsValid(maxStrategyNum));
+    Assert(AttributeNumberIsValid(attrNum));
+    
+    maxStrategyNum = AMStrategies(maxStrategyNum);     /* XXX */
+    return
+       &indexStrategy->strategyMapData[maxStrategyNum * (attrNum - 1)];
+}
+
+/*
+ * AttributeNumberGetIndexStrategySize --
+ *     Computes the size of an index strategy.
+ */
+Size
+AttributeNumberGetIndexStrategySize(AttrNumber maxAttributeNumber,
+                                   StrategyNumber maxStrategyNumber)
+{
+    maxStrategyNumber = AMStrategies(maxStrategyNumber);       /* XXX */
+    return
+       maxAttributeNumber * maxStrategyNumber * sizeof (ScanKeyData);
+}
+
+/* 
+ * StrategyTransformMapIsValid is now a macro in istrat.h -cim 4/27/91
+ */
+
+/* ----------------
+ *     StrategyOperatorIsValid
+ * ----------------
+ */
+bool
+StrategyOperatorIsValid(StrategyOperator operator,
+                       StrategyNumber maxStrategy)
+{
+    return (bool)
+       (PointerIsValid(operator) &&
+        StrategyNumberIsInBounds(operator->strategy, maxStrategy) &&
+        !(operator->flags & ~(SK_NEGATE | SK_COMMUTE)));
+}
+
+/* ----------------
+ *     StrategyTermIsValid
+ * ----------------
+ */
+bool
+StrategyTermIsValid(StrategyTerm term,
+                   StrategyNumber maxStrategy)
+{
+    Index      index;
+    
+    if (! PointerIsValid(term) || term->degree == 0)
+       return false;
+    
+    for (index = 0; index < term->degree; index += 1) {
+       if (! StrategyOperatorIsValid(&term->operatorData[index],
+                                     maxStrategy)) {
+           
+           return false;
+       }
+    }
+    
+    return true;
+}
+
+/* ----------------
+ *     StrategyExpressionIsValid
+ * ----------------
+ */
+bool
+StrategyExpressionIsValid(StrategyExpression expression,
+                         StrategyNumber maxStrategy)
+{
+    StrategyTerm       *termP;
+    
+    if (!PointerIsValid(expression))
+       return true;
+    
+    if (!StrategyTermIsValid(expression->term[0], maxStrategy))
+       return false;
+    
+    termP = &expression->term[1];
+    while (StrategyTermIsValid(*termP, maxStrategy))
+       termP += 1;
+    
+    return (bool)
+       (! PointerIsValid(*termP));
+}
+
+/* ----------------
+ *     StrategyEvaluationIsValid
+ * ----------------
+ */
+bool
+StrategyEvaluationIsValid(StrategyEvaluation evaluation)
+{
+    Index      index;
+    
+    if (! PointerIsValid(evaluation) ||
+       ! StrategyNumberIsValid(evaluation->maxStrategy) ||
+       ! StrategyTransformMapIsValid(evaluation->negateTransform) ||
+       ! StrategyTransformMapIsValid(evaluation->commuteTransform) ||
+       ! StrategyTransformMapIsValid(evaluation->negateCommuteTransform)) {
+       
+       return false;
+    }
+    
+    for (index = 0; index < evaluation->maxStrategy; index += 1) {
+       if (! StrategyExpressionIsValid(evaluation->expression[index],
+                                       evaluation->maxStrategy)) {
+           
+           return false;
+       }
+    }
+    return true;
+}
+
+/* ----------------
+ *     StrategyTermEvaluate
+ * ----------------
+ */
+static bool
+StrategyTermEvaluate(StrategyTerm term,
+                    StrategyMap map,
+                    Datum left,
+                    Datum right)
+{
+    Index              index;
+    long               tmpres;
+    bool               result;
+    StrategyOperator   operator;
+    ScanKey            entry;
+    
+    for (index = 0, operator = &term->operatorData[0];
+        index < term->degree; index += 1, operator += 1) {
+       
+       entry = &map->entry[operator->strategy - 1];
+       
+       Assert(RegProcedureIsValid(entry->sk_procedure));
+       
+       switch (operator->flags ^ entry->sk_flags) {
+       case 0x0:
+           tmpres = (long) FMGR_PTR2(entry->sk_func, entry->sk_procedure,
+                                     left, right);
+           break;
+           
+       case SK_NEGATE:
+           tmpres = (long) !FMGR_PTR2(entry->sk_func, entry->sk_procedure,
+                                      left, right);
+           break;
+           
+       case SK_COMMUTE:
+           tmpres = (long) FMGR_PTR2(entry->sk_func, entry->sk_procedure,
+                                     right, left);
+           break;
+           
+       case SK_NEGATE | SK_COMMUTE:
+           tmpres = (long) !FMGR_PTR2(entry->sk_func, entry->sk_procedure,
+                                      right, left);
+           break;
+           
+       default:
+           elog(FATAL, "StrategyTermEvaluate: impossible case %d",
+                operator->flags ^ entry->sk_flags);
+       }
+       
+       result = (bool) tmpres;
+       if (!result)
+           return result;
+    }
+    
+    return result;
+}
+
+
+/* ----------------
+ *     RelationGetStrategy
+ * ----------------
+ */
+StrategyNumber
+RelationGetStrategy(Relation relation,
+                   AttrNumber attributeNumber,
+                   StrategyEvaluation evaluation,
+                   RegProcedure procedure)
+{
+    StrategyNumber     strategy;
+    StrategyMap                strategyMap;
+    ScanKey            entry;
+    Index              index;
+    int                numattrs;
+    
+    Assert(RelationIsValid(relation));
+    numattrs = RelationGetNumberOfAttributes(relation);
+    
+    Assert(relation->rd_rel->relkind == RELKIND_INDEX);        /* XXX use accessor */
+    Assert(AttributeNumberIsValid(attributeNumber));
+    Assert( (attributeNumber >= 1) && (attributeNumber < 1 + numattrs));
+    
+    Assert(StrategyEvaluationIsValid(evaluation));
+    Assert(RegProcedureIsValid(procedure));
+    
+    strategyMap =
+       IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation),
+                                   evaluation->maxStrategy,
+                                   attributeNumber);
+    
+    /* get a strategy number for the procedure ignoring flags for now */
+    for (index = 0; index < evaluation->maxStrategy; index += 1) {
+       if (strategyMap->entry[index].sk_procedure == procedure) {
+           break;
+       }
+    }
+    
+    if (index == evaluation->maxStrategy)
+       return InvalidStrategy;
+    
+    strategy = 1 + index;
+    entry = StrategyMapGetScanKeyEntry(strategyMap, strategy);
+    
+    Assert(!(entry->sk_flags & ~(SK_NEGATE | SK_COMMUTE)));
+    
+    switch (entry->sk_flags & (SK_NEGATE | SK_COMMUTE)) {
+    case 0x0:
+       return strategy;
+       
+    case SK_NEGATE:
+       strategy = evaluation->negateTransform->strategy[strategy - 1];
+       break;
+       
+    case SK_COMMUTE:
+       strategy = evaluation->commuteTransform->strategy[strategy - 1];
+       break;
+       
+    case SK_NEGATE | SK_COMMUTE:
+       strategy = evaluation->negateCommuteTransform->strategy[strategy - 1];
+       break;
+       
+    default:
+       elog(FATAL, "RelationGetStrategy: impossible case %d", entry->sk_flags);
+    }
+    
+    
+    if (! StrategyNumberIsInBounds(strategy, evaluation->maxStrategy)) {
+       if (! StrategyNumberIsValid(strategy)) {
+           elog(WARN, "RelationGetStrategy: corrupted evaluation");
+       }
+    }
+    
+    return strategy;
+}
+
+/* ----------------
+ *     RelationInvokeStrategy
+ * ----------------
+ */
+bool           /* XXX someday, this may return Datum */
+RelationInvokeStrategy(Relation relation,
+                      StrategyEvaluation evaluation,
+                      AttrNumber attributeNumber,
+                      StrategyNumber strategy,
+                      Datum left,
+                      Datum right)
+{
+    StrategyNumber     newStrategy;
+    StrategyMap                strategyMap;
+    ScanKey            entry;
+    StrategyTermData   termData;
+    int                numattrs;
+    
+    Assert(RelationIsValid(relation));
+    Assert(relation->rd_rel->relkind == RELKIND_INDEX);        /* XXX use accessor */
+    numattrs = RelationGetNumberOfAttributes(relation);
+    
+    Assert(StrategyEvaluationIsValid(evaluation));
+    Assert(AttributeNumberIsValid(attributeNumber));
+    Assert( (attributeNumber >= 1) && (attributeNumber < 1 + numattrs));
+
+    Assert(StrategyNumberIsInBounds(strategy, evaluation->maxStrategy));
+    
+    termData.degree = 1;
+    
+    strategyMap =
+       IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation),
+                                   evaluation->maxStrategy,
+                                   attributeNumber);
+    
+    entry = StrategyMapGetScanKeyEntry(strategyMap, strategy);
+    
+    if (RegProcedureIsValid(entry->sk_procedure)) {
+       termData.operatorData[0].strategy = strategy;
+       termData.operatorData[0].flags = 0x0;
+       
+       return
+           StrategyTermEvaluate(&termData, strategyMap, left, right);
+    }
+    
+    
+    newStrategy = evaluation->negateTransform->strategy[strategy - 1];
+    if (newStrategy != strategy && StrategyNumberIsValid(newStrategy)) {
+       
+       entry = StrategyMapGetScanKeyEntry(strategyMap, newStrategy);
+       
+       if (RegProcedureIsValid(entry->sk_procedure)) {
+           termData.operatorData[0].strategy = newStrategy;
+           termData.operatorData[0].flags = SK_NEGATE;
+           
+           return
+               StrategyTermEvaluate(&termData, strategyMap, left, right);
+       }
+    }
+    
+    newStrategy = evaluation->commuteTransform->strategy[strategy - 1];
+    if (newStrategy != strategy && StrategyNumberIsValid(newStrategy)) {
+       
+       entry = StrategyMapGetScanKeyEntry(strategyMap, newStrategy);
+       
+       if (RegProcedureIsValid(entry->sk_procedure)) {
+           termData.operatorData[0].strategy = newStrategy;
+           termData.operatorData[0].flags = SK_COMMUTE;
+           
+           return
+               StrategyTermEvaluate(&termData, strategyMap, left, right);
+       }
+    }
+    
+    newStrategy = evaluation->negateCommuteTransform->strategy[strategy - 1];
+    if (newStrategy != strategy && StrategyNumberIsValid(newStrategy)) {
+       
+       entry = StrategyMapGetScanKeyEntry(strategyMap, newStrategy);
+       
+       if (RegProcedureIsValid(entry->sk_procedure)) {
+           termData.operatorData[0].strategy = newStrategy;
+           termData.operatorData[0].flags = SK_NEGATE | SK_COMMUTE;
+           
+           return
+               StrategyTermEvaluate(&termData, strategyMap, left, right);
+       }
+    }
+    
+    if (PointerIsValid(evaluation->expression[strategy - 1])) {
+       StrategyTerm            *termP;
+       
+       termP = &evaluation->expression[strategy - 1]->term[0];
+       while (PointerIsValid(*termP)) {
+           Index       index;
+           
+           for (index = 0; index < (*termP)->degree; index += 1) {
+               entry = StrategyMapGetScanKeyEntry(strategyMap,
+                                                  (*termP)->operatorData[index].strategy);
+               
+               if (! RegProcedureIsValid(entry->sk_procedure)) {
+                   break;
+               }
+           }
+           
+           if (index == (*termP)->degree) {
+               return
+                   StrategyTermEvaluate(*termP, strategyMap, left, right);
+           }
+           
+           termP += 1;
+       }
+    }
+    
+    elog(WARN, "RelationInvokeStrategy: cannot evaluate strategy %d",
+        strategy);
+
+     /* not reached, just to make compiler happy */
+     return FALSE; 
+
+
+}
+
+/* ----------------
+ *     OperatorRelationFillScanKeyEntry
+ * ----------------
+ */
+static void
+OperatorRelationFillScanKeyEntry(Relation operatorRelation,
+                                Oid operatorObjectId,
+                                ScanKey entry)
+{
+    HeapScanDesc       scan;
+    ScanKeyData                scanKeyData;
+    HeapTuple          tuple;
+    
+    ScanKeyEntryInitialize(&scanKeyData, 0, 
+                          ObjectIdAttributeNumber,
+                          ObjectIdEqualRegProcedure,
+                          ObjectIdGetDatum(operatorObjectId));
+    
+    scan = heap_beginscan(operatorRelation, false, NowTimeQual,
+                         1, &scanKeyData);
+    
+    tuple = heap_getnext(scan, false, (Buffer *)NULL);
+    if (! HeapTupleIsValid(tuple)) {
+       elog(WARN, "OperatorObjectIdFillScanKeyEntry: unknown operator %lu",
+            (uint32) operatorObjectId);
+    }
+    
+    entry->sk_flags = 0;
+    entry->sk_procedure =
+       ((OperatorTupleForm) GETSTRUCT(tuple))->oprcode;
+    fmgr_info(entry->sk_procedure, &entry->sk_func, &entry->sk_nargs);
+    
+    if (! RegProcedureIsValid(entry->sk_procedure)) {
+       elog(WARN,
+            "OperatorObjectIdFillScanKeyEntry: no procedure for operator %lu",
+            (uint32) operatorObjectId);
+    }
+    
+    heap_endscan(scan);
+}
+
+
+/*
+ * IndexSupportInitialize --
+ *     Initializes an index strategy and associated support procedures.
+ */
+void
+IndexSupportInitialize(IndexStrategy indexStrategy,
+                      RegProcedure *indexSupport,
+                      Oid indexObjectId,
+                      Oid accessMethodObjectId,
+                      StrategyNumber maxStrategyNumber,
+                      StrategyNumber maxSupportNumber,
+                      AttrNumber maxAttributeNumber)
+{
+    Relation           relation;
+    Relation           operatorRelation;
+    HeapScanDesc       scan;
+    HeapTuple          tuple;
+    ScanKeyData        entry[2];
+    StrategyMap                map;
+    AttrNumber         attributeNumber;
+    int                        attributeIndex;
+    Oid                        operatorClassObjectId[ MaxIndexAttributeNumber ];
+    
+    maxStrategyNumber = AMStrategies(maxStrategyNumber);
+    
+    ScanKeyEntryInitialize(&entry[0], 0, Anum_pg_index_indexrelid,
+                          ObjectIdEqualRegProcedure, 
+                          ObjectIdGetDatum(indexObjectId));
+    
+    relation = heap_openr(IndexRelationName);
+    scan = heap_beginscan(relation, false, NowTimeQual, 1, entry);
+    tuple = heap_getnext(scan, 0, (Buffer *)NULL);
+    if (! HeapTupleIsValid(tuple))
+       elog(WARN, "IndexSupportInitialize: corrupted catalogs");
+    
+    /*
+     * XXX note that the following assumes the INDEX tuple is well formed and
+     * that the key[] and class[] are 0 terminated.
+     */
+    for (attributeIndex=0; attributeIndex<maxAttributeNumber; attributeIndex++)
+       {
+           IndexTupleForm      iform;
+           
+           iform = (IndexTupleForm) GETSTRUCT(tuple);
+           
+           if (!OidIsValid(iform->indkey[attributeIndex])) {
+               if (attributeIndex == 0) {
+                   elog(WARN, "IndexSupportInitialize: no pg_index tuple");
+               }
+               break;
+           }
+           
+           operatorClassObjectId[attributeIndex]
+               = iform->indclass[attributeIndex];
+       }
+    
+    heap_endscan(scan);
+    heap_close(relation);
+    
+    /* if support routines exist for this access method, load them */
+    if (maxSupportNumber > 0) {
+       
+       ScanKeyEntryInitialize(&entry[0], 0, Anum_pg_amproc_amid,
+                              ObjectIdEqualRegProcedure,
+                              ObjectIdGetDatum(accessMethodObjectId));
+       
+       ScanKeyEntryInitialize(&entry[1], 0, Anum_pg_amproc_amopclaid,
+                              ObjectIdEqualRegProcedure, 0);
+       
+/*     relation = heap_openr(Name_pg_amproc); */
+       relation = heap_openr(AccessMethodProcedureRelationName);
+
+       
+       for (attributeNumber = maxAttributeNumber; attributeNumber > 0;
+            attributeNumber--) {
+           
+           int16               support;
+           Form_pg_amproc      form;
+           RegProcedure        *loc;
+           
+           loc = &indexSupport[((attributeNumber - 1) * maxSupportNumber)];
+           
+           for (support = maxSupportNumber; --support >= 0; ) {
+               loc[support] = InvalidOid;
+           }
+           
+           entry[1].sk_argument =
+               ObjectIdGetDatum(operatorClassObjectId[attributeNumber - 1]);
+           
+           scan = heap_beginscan(relation, false, NowTimeQual, 2, entry);
+           
+           while (tuple = heap_getnext(scan, 0, (Buffer *)NULL),
+                  HeapTupleIsValid(tuple)) {
+               
+               form = (Form_pg_amproc) GETSTRUCT(tuple);
+               loc[(form->amprocnum - 1)] = form->amproc;
+           }
+           
+           heap_endscan(scan);
+       }
+       heap_close(relation);
+    }
+    
+    ScanKeyEntryInitialize(&entry[0], 0, 
+                          Anum_pg_amop_amopid,
+                           ObjectIdEqualRegProcedure,
+                           ObjectIdGetDatum(accessMethodObjectId));
+    
+    ScanKeyEntryInitialize(&entry[1], 0, 
+                          Anum_pg_amop_amopclaid,
+                           ObjectIdEqualRegProcedure, 0);
+    
+    relation = heap_openr(AccessMethodOperatorRelationName);
+    operatorRelation = heap_openr(OperatorRelationName);
+    
+    for (attributeNumber = maxAttributeNumber; attributeNumber > 0;
+        attributeNumber--) {
+       
+       StrategyNumber  strategy;
+       
+       entry[1].sk_argument =
+           ObjectIdGetDatum(operatorClassObjectId[attributeNumber - 1]);
+       
+       map = IndexStrategyGetStrategyMap(indexStrategy,
+                                         maxStrategyNumber,
+                                         attributeNumber);
+       
+       for (strategy = 1; strategy <= maxStrategyNumber; strategy++)
+           ScanKeyEntrySetIllegal(StrategyMapGetScanKeyEntry(map, strategy));
+       
+       scan = heap_beginscan(relation, false, NowTimeQual, 2, entry);
+       
+       while (tuple = heap_getnext(scan, 0, (Buffer *)NULL),
+              HeapTupleIsValid(tuple)) {
+           Form_pg_amop form;
+           
+           form = (Form_pg_amop) GETSTRUCT(tuple);
+           
+           OperatorRelationFillScanKeyEntry(operatorRelation,
+                                            form->amopopr,
+                                            StrategyMapGetScanKeyEntry(map, form->amopstrategy));
+       }
+       
+       heap_endscan(scan);
+    }
+    
+    heap_close(operatorRelation);
+    heap_close(relation);
+}
+
+/* ----------------
+ *     IndexStrategyDisplay
+ * ----------------
+ */
+#ifdef ISTRATDEBUG
+int
+IndexStrategyDisplay(IndexStrategy indexStrategy,
+                    StrategyNumber numberOfStrategies,
+                    int numberOfAttributes)
+{
+    StrategyMap        strategyMap;
+    AttrNumber attributeNumber;
+    StrategyNumber     strategyNumber;
+    
+    for (attributeNumber = 1; attributeNumber <= numberOfAttributes;
+        attributeNumber += 1) {
+       
+       strategyMap = IndexStrategyGetStrategyMap(indexStrategy,
+                                                 numberOfStrategies,
+                                                 attributeNumber);
+       
+       for (strategyNumber = 1;
+            strategyNumber <= AMStrategies(numberOfStrategies);
+            strategyNumber += 1) {
+           
+           printf(":att %d\t:str %d\t:opr 0x%x(%d)\n",
+                  attributeNumber, strategyNumber,
+                  strategyMap->entry[strategyNumber - 1].sk_procedure,
+                  strategyMap->entry[strategyNumber - 1].sk_procedure);
+       }
+    }
+}
+#endif /* defined(ISTRATDEBUG) */
+
+
diff --git a/src/backend/access/iqual.h b/src/backend/access/iqual.h
new file mode 100644 (file)
index 0000000..62b3b0d
--- /dev/null
@@ -0,0 +1,32 @@
+/*-------------------------------------------------------------------------
+ *
+ * iqual.h--
+ *    Index scan key qualification definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        IQUAL_H
+#define IQUAL_H
+
+#include "c.h"
+
+#include "storage/itemid.h"
+#include "utils/rel.h"
+#include "access/skey.h"
+
+/* ----------------
+ *     index tuple qualification support
+ * ----------------
+ */
+
+extern int NIndexTupleProcessed;
+
+extern bool index_keytest(IndexTuple tuple, TupleDesc tupdesc,
+                         int scanKeySize, ScanKey key);
+
+#endif /* IQUAL_H */
diff --git a/src/backend/access/istrat.h b/src/backend/access/istrat.h
new file mode 100644 (file)
index 0000000..b26b5ff
--- /dev/null
@@ -0,0 +1,80 @@
+/*-------------------------------------------------------------------------
+ *
+ * istrat.h--
+ *    POSTGRES index strategy definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        ISTRAT_H
+#define ISTRAT_H
+
+#include "postgres.h"
+#include "access/attnum.h"
+#include "access/skey.h"
+#include "access/strat.h"
+#include "utils/rel.h"         /* for Relation */
+
+/*
+ * StrategyNumberIsValid --
+ *     True iff the strategy number is valid.
+ */
+#define StrategyNumberIsValid(strategyNumber) \
+    ((bool) ((strategyNumber) != InvalidStrategy))
+
+/*
+ * StrategyNumberIsInBounds --
+ *     True iff strategy number is within given bounds.
+ *
+ * Note:
+ *     Assumes StrategyNumber is an unsigned type.
+ *     Assumes the bounded interval to be (0,max].
+ */
+#define StrategyNumberIsInBounds(strategyNumber, maxStrategyNumber) \
+    ((bool)(InvalidStrategy < (strategyNumber) && \
+           (strategyNumber) <= (maxStrategyNumber)))
+
+/*
+ * StrategyMapIsValid --
+ *     True iff the index strategy mapping is valid.
+ */
+#define        StrategyMapIsValid(map) PointerIsValid(map)
+
+/*
+ * IndexStrategyIsValid --
+ *     True iff the index strategy is valid.
+ */
+#define        IndexStrategyIsValid(s) PointerIsValid(s)
+
+extern ScanKey StrategyMapGetScanKeyEntry(StrategyMap map,
+                                         StrategyNumber strategyNumber);
+extern StrategyMap IndexStrategyGetStrategyMap(IndexStrategy indexStrategy,
+       StrategyNumber maxStrategyNum, AttrNumber attrNum);
+
+extern Size
+AttributeNumberGetIndexStrategySize(AttrNumber maxAttributeNumber,
+                                   StrategyNumber maxStrategyNumber);
+extern bool StrategyOperatorIsValid(StrategyOperator operator,
+                                   StrategyNumber maxStrategy);
+extern bool StrategyTermIsValid(StrategyTerm term,
+                               StrategyNumber maxStrategy);
+extern bool StrategyExpressionIsValid(StrategyExpression expression,
+                                     StrategyNumber maxStrategy);
+extern bool StrategyEvaluationIsValid(StrategyEvaluation evaluation);
+extern StrategyNumber RelationGetStrategy(Relation relation,
+       AttrNumber attributeNumber, StrategyEvaluation evaluation,
+       RegProcedure procedure);
+extern bool RelationInvokeStrategy(Relation relation,
+       StrategyEvaluation evaluation, AttrNumber attributeNumber,
+       StrategyNumber strategy, Datum left, Datum right);
+extern void IndexSupportInitialize(IndexStrategy indexStrategy,
+       RegProcedure *indexSupport, Oid indexObjectId,
+       Oid accessMethodObjectId, StrategyNumber maxStrategyNumber,
+       StrategyNumber maxSupportNumber, AttrNumber maxAttributeNumber);
+
+
+#endif /* ISTRAT_H */
diff --git a/src/backend/access/itup.h b/src/backend/access/itup.h
new file mode 100644 (file)
index 0000000..01fee16
--- /dev/null
@@ -0,0 +1,104 @@
+/*-------------------------------------------------------------------------
+ *
+ * itup.h--
+ *    POSTGRES index tuple definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef ITUP_H
+#define ITUP_H
+
+#include "c.h"
+#include "access/ibit.h"
+#include "access/tupdesc.h"    /* for TupleDesc */
+#include "storage/itemptr.h"
+
+#define MaxIndexAttributeNumber        7
+
+typedef struct IndexTupleData {
+    ItemPointerData            t_tid; /* reference TID to base tuple */
+
+    /*
+     * t_info is layed out in the following fashion:
+     *
+     * 15th (leftmost) bit: "has nulls" bit
+     * 14th bit: "has varlenas" bit
+     * 13th bit: "has rules" bit - (removed ay 11/94)
+     * bits 12-0 bit: size of tuple.
+     */
+
+    unsigned short             t_info; /* various info about tuple */
+
+    /*
+     * please make sure sizeof(IndexTupleData) is MAXALIGN'ed.
+     * See IndexInfoFindDataOffset() for the reason.
+     */
+    
+} IndexTupleData;              /* MORE DATA FOLLOWS AT END OF STRUCT */
+
+typedef IndexTupleData *IndexTuple;
+
+
+typedef struct InsertIndexResultData {
+    ItemPointerData    pointerData;
+} InsertIndexResultData;
+
+typedef InsertIndexResultData *InsertIndexResult;
+
+
+typedef struct RetrieveIndexResultData {
+    ItemPointerData    index_iptr;
+    ItemPointerData    heap_iptr;
+} RetrieveIndexResultData;
+
+typedef RetrieveIndexResultData        *RetrieveIndexResult;
+
+
+/*-----------------
+ * PredInfo -
+ *    used for partial indices
+ *-----------------
+ */
+typedef struct PredInfo {
+    Node               *pred;
+    Node               *oldPred;
+} PredInfo;
+
+
+/* ----------------
+ *     externs 
+ * ----------------
+ */
+
+#define INDEX_SIZE_MASK 0x1FFF
+#define INDEX_NULL_MASK 0x8000
+#define INDEX_VAR_MASK  0x4000
+
+#define IndexTupleSize(itup)       (((IndexTuple) (itup))->t_info & 0x1FFF)
+#define IndexTupleDSize(itup)                      ((itup).t_info & 0x1FFF)
+#define IndexTupleNoNulls(itup)  (!(((IndexTuple) (itup))->t_info & 0x8000))
+#define IndexTupleAllFixed(itup) (!(((IndexTuple) (itup))->t_info & 0x4000))
+
+#define IndexTupleHasMinHeader(itup) (IndexTupleNoNulls(itup))
+
+
+/* indextuple.h */
+extern IndexTuple index_formtuple(TupleDesc tupleDescriptor,
+                                 Datum value[], char null[]);
+extern char *fastgetiattr(IndexTuple tup, int attnum,
+       TupleDesc att, bool *isnull);
+extern Datum index_getattr(IndexTuple tuple, AttrNumber attNum,
+       TupleDesc tupDesc, bool *isNullOutP);
+extern RetrieveIndexResult
+FormRetrieveIndexResult(ItemPointer indexItemPointer,
+                       ItemPointer heapItemPointer);
+extern void CopyIndexTuple(IndexTuple source, IndexTuple *target);
+
+
+#endif /* ITUP_H */
+
diff --git a/src/backend/access/nbtree.h b/src/backend/access/nbtree.h
new file mode 100644 (file)
index 0000000..0810bd7
--- /dev/null
@@ -0,0 +1,264 @@
+/*-------------------------------------------------------------------------
+ *
+ * nbtree.h--
+ *    header file for postgres btree access method implementation.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        NBTREE_H
+#define        NBTREE_H
+
+#include "access/attnum.h"
+#include "access/itup.h"
+#include "access/htup.h"
+#include "access/tupdesc.h"
+
+#include "access/istrat.h"
+#include "access/funcindex.h"
+#include "access/relscan.h"
+#include "access/sdir.h"
+#include "nodes/pg_list.h"
+
+/*
+ *  BTPageOpaqueData -- At the end of every page, we store a pointer
+ *  to both siblings in the tree.  See Lehman and Yao's paper for more
+ *  info.  In addition, we need to know what sort of page this is
+ *  (leaf or internal), and whether the page is available for reuse.
+ *
+ *  Lehman and Yao's algorithm requires a ``high key'' on every page.
+ *  The high key on a page is guaranteed to be greater than or equal
+ *  to any key that appears on this page.  Our insertion algorithm
+ *  guarantees that we can use the initial least key on our right
+ *  sibling as the high key.  We allocate space for the line pointer
+ *  to the high key in the opaque data at the end of the page.
+ *
+ *  Rightmost pages in the tree have no high key.
+ */
+
+typedef struct BTPageOpaqueData {
+    BlockNumber        btpo_prev;
+    BlockNumber        btpo_next;
+    uint16     btpo_flags;
+
+#define BTP_LEAF       (1 << 0)
+#define BTP_ROOT       (1 << 1)
+#define BTP_FREE       (1 << 2)
+#define BTP_META       (1 << 3)
+
+} BTPageOpaqueData;
+
+typedef BTPageOpaqueData       *BTPageOpaque;
+
+/*
+ *  ScanOpaqueData is used to remember which buffers we're currently
+ *  examining in the scan.  We keep these buffers locked and pinned
+ *  and recorded in the opaque entry of the scan in order to avoid
+ *  doing a ReadBuffer() for every tuple in the index.  This avoids
+ *  semop() calls, which are expensive.
+ */
+
+typedef struct BTScanOpaqueData {
+    Buffer     btso_curbuf;
+    Buffer     btso_mrkbuf;
+} BTScanOpaqueData;
+
+typedef BTScanOpaqueData       *BTScanOpaque;
+
+/*
+ *  BTItems are what we store in the btree.  Each item has an index
+ *  tuple, including key and pointer values.  In addition, we must
+ *  guarantee that all tuples in the index are unique, in order to
+ *  satisfy some assumptions in Lehman and Yao.  The way that we do
+ *  this is by generating a new OID for every insertion that we do in
+ *  the tree.  This adds eight bytes to the size of btree index
+ *  tuples.  Note that we do not use the OID as part of a composite
+ *  key; the OID only serves as a unique identifier for a given index
+ *  tuple (logical position within a page).
+ */
+
+typedef struct BTItemData {
+    Oid                                bti_oid;
+    int32                      bti_dummy;      /* padding to make bti_itup
+                                                * align at 8-byte boundary
+                                                */
+    IndexTupleData             bti_itup;
+} BTItemData;
+
+typedef BTItemData     *BTItem;
+
+/*
+ *  BTStackData -- As we descend a tree, we push the (key, pointer)
+ *  pairs from internal nodes onto a private stack.  If we split a
+ *  leaf, we use this stack to walk back up the tree and insert data
+ *  into parent nodes (and possibly to split them, too).  Lehman and
+ *  Yao's update algorithm guarantees that under no circumstances can
+ *  our private stack give us an irredeemably bad picture up the tree.
+ *  Again, see the paper for details.
+ */
+
+typedef struct BTStackData {
+    BlockNumber                bts_blkno;
+    OffsetNumber       bts_offset;
+    BTItem             bts_btitem;
+    struct BTStackData *bts_parent;
+} BTStackData;
+
+typedef BTStackData    *BTStack;
+
+/*
+ *  We need to be able to tell the difference between read and write
+ *  requests for pages, in order to do locking correctly.
+ */
+
+#define        BT_READ         0
+#define        BT_WRITE        1
+
+/*
+ *  Similarly, the difference between insertion and non-insertion binary
+ *  searches on a given page makes a difference when we're descending the
+ *  tree.
+ */
+
+#define BT_INSERTION   0
+#define BT_DESCENT     1
+
+/*
+ *  In general, the btree code tries to localize its knowledge about
+ *  page layout to a couple of routines.  However, we need a special
+ *  value to indicate "no page number" in those places where we expect
+ *  page numbers.
+ */
+
+#define P_NONE         0
+#define        P_LEFTMOST(opaque)      ((opaque)->btpo_prev == P_NONE)
+#define        P_RIGHTMOST(opaque)     ((opaque)->btpo_next == P_NONE)
+
+#define        P_HIKEY         ((OffsetNumber) 1)
+#define        P_FIRSTKEY      ((OffsetNumber) 2)
+
+/*
+ *  Strategy numbers -- ordering of these is <, <=, =, >=, > 
+ */
+
+#define BTLessStrategyNumber           1
+#define BTLessEqualStrategyNumber      2
+#define BTEqualStrategyNumber          3
+#define BTGreaterEqualStrategyNumber   4
+#define BTGreaterStrategyNumber                5
+#define BTMaxStrategyNumber            5
+
+/*
+ *  When a new operator class is declared, we require that the user
+ *  supply us with an amproc procedure for determining whether, for
+ *  two keys a and b, a < b, a = b, or a > b.  This routine must
+ *  return < 0, 0, > 0, respectively, in these three cases.  Since we
+ *  only have one such proc in amproc, it's number 1.
+ */
+
+#define BTORDER_PROC   1
+
+
+/*
+ * prototypes for functions in nbtinsert.c
+ */
+extern InsertIndexResult _bt_doinsert(Relation rel, BTItem btitem);
+extern bool _bt_itemcmp(Relation rel, Size keysz, BTItem item1, BTItem item2,
+                       StrategyNumber strat);
+
+/*
+ * prototypes for functions in nbtpage.c
+ */
+extern void _bt_metapinit(Relation rel);
+extern void _bt_checkmeta(Relation rel);
+extern Buffer _bt_getroot(Relation rel, int access);
+extern Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access);
+extern void _bt_relbuf(Relation rel, Buffer buf, int access);
+extern void _bt_wrtbuf(Relation rel, Buffer buf);
+extern void _bt_wrtnorelbuf(Relation rel, Buffer buf);
+extern void _bt_pageinit(Page page, Size size);
+extern void _bt_metaproot(Relation rel, BlockNumber rootbknum);
+extern Buffer _bt_getstackbuf(Relation rel, BTStack stack, int access);
+extern void _bt_setpagelock(Relation rel, BlockNumber blkno, int access);
+extern void _bt_unsetpagelock(Relation rel, BlockNumber blkno, int access);
+extern void _bt_pagedel(Relation rel, ItemPointer tid);
+
+/*
+ * prototypes for functions in nbtree.c
+ */
+extern bool BuildingBtree;     /* in nbtree.c */
+
+extern void btbuild(Relation heap, Relation index, int natts,
+       AttrNumber *attnum, IndexStrategy istrat, uint16 pcount,
+       Datum *params, FuncIndexInfo *finfo, PredInfo *predInfo);
+extern InsertIndexResult btinsert(Relation rel, IndexTuple itup);
+extern char *btgettuple(IndexScanDesc scan, ScanDirection dir);
+extern char *btbeginscan(Relation rel, bool fromEnd, uint16 keysz,
+                        ScanKey scankey);
+
+extern void btrescan(IndexScanDesc scan, bool fromEnd, ScanKey scankey);
+extern void btmovescan(IndexScanDesc scan, Datum v);
+extern void btendscan(IndexScanDesc scan);
+extern void btmarkpos(IndexScanDesc scan);
+extern void btrestrpos(IndexScanDesc scan);
+extern void btdelete(Relation rel, ItemPointer tid);
+
+/*
+ * prototypes for functions in nbtscan.c
+ */
+extern void _bt_regscan(IndexScanDesc scan);
+extern void _bt_dropscan(IndexScanDesc scan);
+extern void _bt_adjscans(Relation rel, ItemPointer tid);
+extern void _bt_scandel(IndexScanDesc scan, BlockNumber blkno,
+                       OffsetNumber offno);
+extern bool _bt_scantouched(IndexScanDesc scan, BlockNumber blkno,
+                           OffsetNumber offno);
+
+/*
+ * prototypes for functions in nbtsearch.c
+ */
+extern BTStack _bt_search(Relation rel, int keysz, ScanKey scankey,
+                         Buffer *bufP);
+extern Buffer _bt_moveright(Relation rel, Buffer buf, int keysz,
+                           ScanKey scankey, int access);
+extern bool _bt_skeycmp(Relation rel, Size keysz, ScanKey scankey,
+                       Page page, ItemId itemid, StrategyNumber strat);
+extern OffsetNumber _bt_binsrch(Relation rel, Buffer buf, int keysz,
+                               ScanKey scankey, int srchtype);
+extern RetrieveIndexResult _bt_next(IndexScanDesc scan, ScanDirection dir);
+extern RetrieveIndexResult _bt_first(IndexScanDesc scan, ScanDirection dir);
+extern bool _bt_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir);
+
+/*
+ * prototypes for functions in nbtstrat.c
+ */
+extern StrategyNumber _bt_getstrat(Relation rel, AttrNumber attno,
+                                  RegProcedure proc);
+extern bool _bt_invokestrat(Relation rel, AttrNumber attno,
+                           StrategyNumber strat, Datum left, Datum right);
+
+/*
+ * prototypes for functions in nbtutils.c
+ */
+extern ScanKey  _bt_mkscankey(Relation rel, IndexTuple itup);
+extern void _bt_freeskey(ScanKey skey);
+extern void _bt_freestack(BTStack stack);
+extern void _bt_orderkeys(Relation relation, uint16 *numberOfKeys,
+                         ScanKey key);
+extern bool _bt_checkqual(IndexScanDesc scan, IndexTuple itup);
+extern BTItem _bt_formitem(IndexTuple itup);
+
+/*
+ * prototypes for functions in nbtsort.c
+ */
+extern void *_bt_spoolinit(Relation index, int ntapes);
+extern void _bt_spooldestroy(void *spool);
+extern void _bt_spool(Relation index, BTItem btitem, void *spool);
+extern void _bt_upperbuild(Relation index, BlockNumber blk, int level);
+extern void _bt_leafbuild(Relation index, void *spool);
+
+#endif /* NBTREE_H */
diff --git a/src/backend/access/nbtree/Makefile.inc b/src/backend/access/nbtree/Makefile.inc
new file mode 100644 (file)
index 0000000..94f352a
--- /dev/null
@@ -0,0 +1,15 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for access/nbtree (btree acess methods)
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= nbtcompare.c nbtinsert.c nbtpage.c nbtree.c nbtscan.c nbtsearch.c \
+       nbtstrat.c nbtutils.c nbtsort.c
diff --git a/src/backend/access/nbtree/README b/src/backend/access/nbtree/README
new file mode 100644 (file)
index 0000000..8d932fd
--- /dev/null
@@ -0,0 +1,68 @@
+$Header$
+
+This directory contains a correct implementation of Lehman and Yao's
+btree management algorithm that supports concurrent access for Postgres.
+We have made the following changes in order to incorporate their algorithm
+into Postgres:
+
+       +  The requirement that all btree keys be unique is too onerous,
+          but the algorithm won't work correctly without it.  As a result,
+          this implementation adds an OID (guaranteed to be unique) to
+          every key in the index.  This guarantees uniqueness within a set
+          of duplicates.  Space overhead is four bytes.
+
+          For this reason, when we're passed an index tuple to store by the
+          common access method code, we allocate a larger one and copy the
+          supplied tuple into it.  No Postgres code outside of the btree
+          access method knows about this xid or sequence number.
+
+       +  Lehman and Yao don't require read locks, but assume that in-
+          memory copies of tree nodes are unshared.  Postgres shares
+          in-memory buffers among backends.  As a result, we do page-
+          level read locking on btree nodes in order to guarantee that
+          no record is modified while we are examining it.  This reduces
+          concurrency but guaranteees correct behavior.
+
+       +  Read locks on a page are held for as long as a scan has a pointer
+          to the page.  However, locks are always surrendered before the
+          sibling page lock is acquired (for readers), so we remain deadlock-
+          free.  I will do a formal proof if I get bored anytime soon.
+
+In addition, the following things are handy to know:
+
+       +  Page zero of every btree is a meta-data page.  This page stores
+          the location of the root page, a pointer to a list of free
+          pages, and other stuff that's handy to know.
+
+       +  This algorithm doesn't really work, since it requires ordered
+          writes, and UNIX doesn't support ordered writes.
+
+       +  There's one other case where we may screw up in this
+          implementation.  When we start a scan, we descend the tree
+          to the key nearest the one in the qual, and once we get there,
+          position ourselves correctly for the qual type (eg, <, >=, etc).
+          If we happen to step off a page, decide we want to get back to
+          it, and fetch the page again, and if some bad person has split
+          the page and moved the last tuple we saw off of it, then the
+          code complains about botched concurrency in an elog(WARN, ...)
+          and gives up the ghost.  This is the ONLY violation of Lehman
+          and Yao's guarantee of correct behavior that I am aware of in
+          this code.
+
+Notes to operator class implementors:
+
+       With this implementation, we require the user to supply us with
+       a procedure for pg_amproc.  This procedure should take two keys
+       A and B and return < 0, 0, or > 0 if A < B, A = B, or A > B,
+       respectively.  See the contents of that relation for the btree
+       access method for some samples.
+
+Notes to mao for implementation document:
+
+       On deletions, we need to adjust the position of active scans on
+       the index.  The code in nbtscan.c handles this.  We don't need to
+       do this for splits because of the way splits are handled; if they
+       happen behind us, we'll automatically go to the next page, and if
+       they happen in front of us, we're not affected by them.  For
+       insertions, if we inserted a tuple behind the current scan location
+       on the current scan page, we move one space ahead.
diff --git a/src/backend/access/nbtree/nbtcompare.c b/src/backend/access/nbtree/nbtcompare.c
new file mode 100644 (file)
index 0000000..e84de1c
--- /dev/null
@@ -0,0 +1,173 @@
+/*-------------------------------------------------------------------------
+ *
+ * btcompare.c--
+ *    Comparison functions for btree access method.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *  NOTES
+ *     These functions are stored in pg_amproc.  For each operator class
+ *     defined on btrees, they compute
+ *
+ *             compare(a, b):
+ *                     < 0 if a < b,
+ *                     = 0 if a == b,
+ *                     > 0 if a > b.
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+#include "utils/nabstime.h"
+
+int32
+btint2cmp(int16 a, int16 b)
+{
+    return ((int32) (a - b));
+}
+
+int32
+btint4cmp(int32 a, int32 b)
+{
+    return (a - b);
+}
+
+int32
+btint24cmp(int16 a, int32 b)
+{
+    return (((int32) a) - b);
+}
+
+int32
+btint42cmp(int32 a, int16 b)
+{
+    return (a - ((int32) b));
+}
+
+int32
+btfloat4cmp(float32 a, float32 b)
+{
+    if (*a > *b)
+       return (1);
+    else if (*a == *b)
+       return (0);
+    else
+       return (-1);
+}
+
+int32
+btfloat8cmp(float64 a, float64 b)
+{
+    if (*a > *b)
+       return (1);
+    else if (*a == *b)
+       return (0);
+    else
+       return (-1);
+}
+
+int32
+btoidcmp(Oid a, Oid b)
+{
+    if (a > b)
+       return (1);
+    else if (a == b)
+       return (0);
+    else
+       return (-1);
+}
+
+int32
+btabstimecmp(AbsoluteTime a, AbsoluteTime b)
+{
+    if (AbsoluteTimeIsBefore(a, b))
+       return (1);
+    else if (AbsoluteTimeIsBefore(b, a))
+       return (-1);
+    else
+       return (0);
+}
+
+int32
+btcharcmp(char a, char b)
+{
+    return ((int32) (a - b));
+}
+
+int32
+btchar2cmp(uint16 a, uint16 b)
+{
+    return (strncmp((char *) &a, (char *) &b, 2));
+}
+
+int32
+btchar4cmp(uint32 a, uint32 b)
+{
+    return (strncmp((char *) &a, (char *) &b, 4));
+}
+
+int32
+btchar8cmp(char *a, char *b)
+{
+    return (strncmp(a, b, 8));
+}
+
+int32
+btchar16cmp(char *a, char *b)
+{
+    return (strncmp(a, b, 16));
+}
+
+int32
+btnamecmp(NameData *a, NameData *b)
+{
+     return (strncmp(a->data, b->data, NAMEDATALEN));
+}
+
+int32
+bttextcmp(struct varlena *a, struct varlena *b)
+{
+    char *ap, *bp;
+    int len;
+    int res;
+    
+    ap = VARDATA(a);
+    bp = VARDATA(b);
+    
+    /* len is the length of the shorter of the two strings */
+    if ((len = VARSIZE(a)) > VARSIZE(b))
+       len = VARSIZE(b);
+    
+    /* len includes the four bytes in which string length is stored */
+    len -= sizeof(VARSIZE(a));
+    
+    /*
+     *  If the two strings differ in the first len bytes, or if they're
+     *  the same in the first len bytes and they're both len bytes long,
+     *  we're done.
+     */
+    
+    res = 0;
+    if (len > 0) {
+       do {
+           res = (int) (*ap++ - *bp++);
+           len--;
+       } while (res == 0 && len != 0);
+    }
+    
+    if (res != 0 || VARSIZE(a) == VARSIZE(b))
+       return (res);
+    
+    /*
+     *  The two strings are the same in the first len bytes, and they
+     *  are of different lengths.
+     */
+    
+    if (VARSIZE(a) < VARSIZE(b))
+       return (-1);
+    else
+       return (1);
+}
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
new file mode 100644 (file)
index 0000000..6187fd7
--- /dev/null
@@ -0,0 +1,831 @@
+/*-------------------------------------------------------------------------
+ *
+ * btinsert.c--
+ *    Item insertion in Lehman and Yao btrees for Postgres.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+#include "utils/excid.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/nbtree.h"
+
+static InsertIndexResult _bt_insertonpg(Relation rel, Buffer buf, BTStack stack, int keysz, ScanKey scankey, BTItem btitem, BTItem afteritem);
+static Buffer _bt_split(Relation rel, Buffer buf);
+static OffsetNumber _bt_findsplitloc(Relation rel, Page page, OffsetNumber start, OffsetNumber maxoff, Size llimit);
+static void _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf);
+static OffsetNumber _bt_pgaddtup(Relation rel, Buffer buf, int keysz, ScanKey itup_scankey, Size itemsize, BTItem btitem, BTItem afteritem);
+static bool _bt_goesonpg(Relation rel, Buffer buf, Size keysz, ScanKey scankey, BTItem afteritem);
+static void _bt_updateitem(Relation rel, Size keysz, Buffer buf, Oid bti_oid, BTItem newItem);
+
+/*
+ *  _bt_doinsert() -- Handle insertion of a single btitem in the tree.
+ *
+ *     This routine is called by the public interface routines, btbuild
+ *     and btinsert.  By here, btitem is filled in, and has a unique
+ *     (xid, seqno) pair.
+ */
+InsertIndexResult
+_bt_doinsert(Relation rel, BTItem btitem)
+{
+    ScanKey itup_scankey;
+    IndexTuple itup;
+    BTStack stack;
+    Buffer buf;
+    BlockNumber blkno;
+    int natts;
+    InsertIndexResult res;
+    
+    itup = &(btitem->bti_itup);
+    
+    /* we need a scan key to do our search, so build one */
+    itup_scankey = _bt_mkscankey(rel, itup);
+    natts = rel->rd_rel->relnatts;
+    
+    /* find the page containing this key */
+    stack = _bt_search(rel, natts, itup_scankey, &buf);
+    blkno = BufferGetBlockNumber(buf);
+    
+    /* trade in our read lock for a write lock */
+    _bt_relbuf(rel, buf, BT_READ);
+    buf = _bt_getbuf(rel, blkno, BT_WRITE);
+    
+    /*
+     *  If the page was split between the time that we surrendered our
+     *  read lock and acquired our write lock, then this page may no
+     *  longer be the right place for the key we want to insert.  In this
+     *  case, we need to move right in the tree.  See Lehman and Yao for
+     *  an excruciatingly precise description.
+     */
+    
+    buf = _bt_moveright(rel, buf, natts, itup_scankey, BT_WRITE);
+    
+    /* do the insertion */
+    res = _bt_insertonpg(rel, buf, stack, natts, itup_scankey,
+                        btitem, (BTItem) NULL);
+    
+    /* be tidy */
+    _bt_freestack(stack);
+    _bt_freeskey(itup_scankey);
+    
+    return (res);
+}
+
+/*
+ *  _bt_insertonpg() -- Insert a tuple on a particular page in the index.
+ *
+ *     This recursive procedure does the following things:
+ *
+ *         +  if necessary, splits the target page.
+ *         +  finds the right place to insert the tuple (taking into
+ *            account any changes induced by a split).
+ *         +  inserts the tuple.
+ *         +  if the page was split, pops the parent stack, and finds the
+ *            right place to insert the new child pointer (by walking
+ *            right using information stored in the parent stack).
+ *         +  invoking itself with the appropriate tuple for the right
+ *            child page on the parent.
+ *
+ *     On entry, we must have the right buffer on which to do the
+ *     insertion, and the buffer must be pinned and locked.  On return,
+ *     we will have dropped both the pin and the write lock on the buffer.
+ *
+ *     The locking interactions in this code are critical.  You should
+ *     grok Lehman and Yao's paper before making any changes.  In addition,
+ *     you need to understand how we disambiguate duplicate keys in this
+ *     implementation, in order to be able to find our location using
+ *     L&Y "move right" operations.  Since we may insert duplicate user
+ *     keys, and since these dups may propogate up the tree, we use the
+ *     'afteritem' parameter to position ourselves correctly for the
+ *     insertion on internal pages.
+ */
+static InsertIndexResult
+_bt_insertonpg(Relation rel,
+              Buffer buf,
+              BTStack stack,
+              int keysz,
+              ScanKey scankey,
+              BTItem btitem,
+              BTItem afteritem)
+{
+    InsertIndexResult res;
+    Page page;
+    Buffer rbuf;
+    Buffer pbuf;
+    Page rpage;
+    ScanKey newskey;
+    BTItem ritem;
+    BTPageOpaque rpageop;
+    BlockNumber rbknum, itup_blkno;
+    OffsetNumber itup_off;
+    int itemsz;
+    InsertIndexResult newres;
+    BTItem new_item = (BTItem) NULL;
+    BTItem lowLeftItem;
+    
+    page = BufferGetPage(buf);
+    itemsz = IndexTupleDSize(btitem->bti_itup)
+       + (sizeof(BTItemData) - sizeof(IndexTupleData));
+
+    itemsz = DOUBLEALIGN(itemsz);      /* be safe, PageAddItem will do this
+                                          but we need to be consistent */
+    
+    if (PageGetFreeSpace(page) < itemsz) {
+       
+       /* split the buffer into left and right halves */
+       rbuf = _bt_split(rel, buf);
+       
+       /* which new page (left half or right half) gets the tuple? */
+       if (_bt_goesonpg(rel, buf, keysz, scankey, afteritem)) {
+           /* left page */
+           itup_off = _bt_pgaddtup(rel, buf, keysz, scankey,
+                                   itemsz, btitem, afteritem);
+           itup_blkno = BufferGetBlockNumber(buf);
+       } else {
+           /* right page */
+           itup_off = _bt_pgaddtup(rel, rbuf, keysz, scankey,
+                                   itemsz, btitem, afteritem);
+           itup_blkno = BufferGetBlockNumber(rbuf);
+       }
+       
+       /*
+        *  By here,
+        *
+        *      +  our target page has been split;
+        *      +  the original tuple has been inserted;
+        *      +  we have write locks on both the old (left half) and new
+        *         (right half) buffers, after the split; and
+        *      +  we have the key we want to insert into the parent.
+        *
+        *  Do the parent insertion.  We need to hold onto the locks for
+        *  the child pages until we locate the parent, but we can release
+        *  them before doing the actual insertion (see Lehman and Yao for
+        *  the reasoning).
+        */
+       
+       if (stack == (BTStack) NULL) {
+           
+           /* create a new root node and release the split buffers */
+           _bt_newroot(rel, buf, rbuf);
+           _bt_relbuf(rel, buf, BT_WRITE);
+           _bt_relbuf(rel, rbuf, BT_WRITE);
+           
+       } else {
+
+           /* form a index tuple that points at the new right page */
+           rbknum = BufferGetBlockNumber(rbuf);
+           rpage = BufferGetPage(rbuf);
+           rpageop = (BTPageOpaque) PageGetSpecialPointer(rpage);
+           
+           /*
+            *  By convention, the first entry (0) on every
+            *  non-rightmost page is the high key for that page.  In
+            *  order to get the lowest key on the new right page, we
+            *  actually look at its second (1) entry.
+            */
+           
+           if (! P_RIGHTMOST(rpageop)) {
+               ritem = (BTItem) PageGetItem(rpage,
+                                            PageGetItemId(rpage, P_FIRSTKEY));
+           } else {
+               ritem = (BTItem) PageGetItem(rpage,
+                                            PageGetItemId(rpage, P_HIKEY));
+           }
+           
+           /* get a unique btitem for this key */
+           new_item = _bt_formitem(&(ritem->bti_itup));
+           
+           ItemPointerSet(&(new_item->bti_itup.t_tid), rbknum, P_HIKEY);
+           
+           /* find the parent buffer */
+           pbuf = _bt_getstackbuf(rel, stack, BT_WRITE);
+           
+           /*
+            *  If the key of new_item is < than the key of the item
+            *  in the parent page pointing to the left page
+            *  (stack->bts_btitem), we have to update the latter key;
+            *  otherwise the keys on the parent page wouldn't be
+            *  monotonically increasing after we inserted the new
+            *  pointer to the right page (new_item). This only
+            *  happens if our left page is the leftmost page and a
+            *  new minimum key had been inserted before, which is not
+            *  reflected in the parent page but didn't matter so
+            *  far. If there are duplicate keys and this new minimum
+            *  key spills over to our new right page, we get an
+            *  inconsistency if we don't update the left key in the
+            *  parent page.
+            */
+           
+           if (_bt_itemcmp(rel, keysz, stack->bts_btitem, new_item,
+                           BTGreaterStrategyNumber)) {
+               lowLeftItem =
+                   (BTItem) PageGetItem(page,
+                                        PageGetItemId(page, P_FIRSTKEY));
+               /* page must have right pointer after split */
+               _bt_updateitem(rel, keysz, pbuf, stack->bts_btitem->bti_oid,
+                              lowLeftItem);
+           }
+           
+           /* don't need the children anymore */
+           _bt_relbuf(rel, buf, BT_WRITE);
+           _bt_relbuf(rel, rbuf, BT_WRITE);
+           
+           newskey = _bt_mkscankey(rel, &(new_item->bti_itup));
+           newres = _bt_insertonpg(rel, pbuf, stack->bts_parent,
+                                   keysz, newskey, new_item,
+                                   stack->bts_btitem);
+           
+           /* be tidy */
+           pfree(newres);
+           pfree(newskey);
+           pfree(new_item);
+       }
+    } else {
+       itup_off = _bt_pgaddtup(rel, buf, keysz, scankey,
+                               itemsz, btitem, afteritem);
+       itup_blkno = BufferGetBlockNumber(buf);
+       
+       _bt_relbuf(rel, buf, BT_WRITE);
+    }
+    
+    /* by here, the new tuple is inserted */
+    res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
+    ItemPointerSet(&(res->pointerData), itup_blkno, itup_off);
+    
+    return (res);
+}
+
+/*
+ *  _bt_split() -- split a page in the btree.
+ *
+ *     On entry, buf is the page to split, and is write-locked and pinned.
+ *     Returns the new right sibling of buf, pinned and write-locked.  The
+ *     pin and lock on buf are maintained.
+ */
+static Buffer
+_bt_split(Relation rel, Buffer buf)
+{
+    Buffer rbuf;
+    Page origpage;
+    Page leftpage, rightpage;
+    BTPageOpaque ropaque, lopaque, oopaque;
+    Buffer sbuf;
+    Page spage;
+    BTPageOpaque sopaque;
+    Size itemsz;
+    ItemId itemid;
+    BTItem item;
+    OffsetNumber leftoff, rightoff;
+    OffsetNumber start;
+    OffsetNumber maxoff;
+    OffsetNumber firstright;
+    OffsetNumber i;
+    Size llimit;
+    
+    rbuf = _bt_getbuf(rel, P_NEW, BT_WRITE);
+    origpage = BufferGetPage(buf);
+    leftpage = PageGetTempPage(origpage, sizeof(BTPageOpaqueData));
+    rightpage = BufferGetPage(rbuf);
+    
+    _bt_pageinit(rightpage, BufferGetPageSize(rbuf));
+    _bt_pageinit(leftpage, BufferGetPageSize(buf));
+    
+    /* init btree private data */
+    oopaque = (BTPageOpaque) PageGetSpecialPointer(origpage);
+    lopaque = (BTPageOpaque) PageGetSpecialPointer(leftpage);
+    ropaque = (BTPageOpaque) PageGetSpecialPointer(rightpage);
+    
+    /* if we're splitting this page, it won't be the root when we're done */
+    oopaque->btpo_flags &= ~BTP_ROOT;
+    lopaque->btpo_flags = ropaque->btpo_flags = oopaque->btpo_flags;
+    lopaque->btpo_prev = oopaque->btpo_prev;
+    ropaque->btpo_prev = BufferGetBlockNumber(buf);
+    lopaque->btpo_next = BufferGetBlockNumber(rbuf);
+    ropaque->btpo_next = oopaque->btpo_next;
+    
+    /*
+     *  If the page we're splitting is not the rightmost page at its
+     *  level in the tree, then the first (0) entry on the page is the
+     *  high key for the page.  We need to copy that to the right
+     *  half.  Otherwise (meaning the rightmost page case), we should
+     *  treat the line pointers beginning at zero as user data.
+     *
+     *  We leave a blank space at the start of the line table for the
+     *  left page.  We'll come back later and fill it in with the high
+     *  key item we get from the right key.
+     */
+    
+    leftoff = P_FIRSTKEY;
+    ropaque->btpo_next = oopaque->btpo_next;
+    if (! P_RIGHTMOST(oopaque)) {
+       /* splitting a non-rightmost page, start at the first data item */
+       start = P_FIRSTKEY;
+
+       /* copy the original high key to the new page */
+       itemid = PageGetItemId(origpage, P_HIKEY);
+       itemsz = ItemIdGetLength(itemid);
+       item = (BTItem) PageGetItem(origpage, itemid);
+       (void) PageAddItem(rightpage, (Item) item, itemsz, P_HIKEY, LP_USED);
+       rightoff = P_FIRSTKEY;
+    } else {
+       /* splitting a rightmost page, "high key" is the first data item */
+       start = P_HIKEY;
+
+       /* the new rightmost page will not have a high key */
+       rightoff = P_HIKEY;
+    }
+    maxoff = PageGetMaxOffsetNumber(origpage);
+    llimit = PageGetFreeSpace(leftpage) / 2;
+    firstright = _bt_findsplitloc(rel, origpage, start, maxoff, llimit);
+    
+    for (i = start; i <= maxoff; i = OffsetNumberNext(i)) {
+       itemid = PageGetItemId(origpage, i);
+       itemsz = ItemIdGetLength(itemid);
+       item = (BTItem) PageGetItem(origpage, itemid);
+       
+       /* decide which page to put it on */
+       if (i < firstright) {
+           (void) PageAddItem(leftpage, (Item) item, itemsz, leftoff,
+                              LP_USED);
+           leftoff = OffsetNumberNext(leftoff);
+       } else {
+           (void) PageAddItem(rightpage, (Item) item, itemsz, rightoff,
+                              LP_USED);
+           rightoff = OffsetNumberNext(rightoff);
+       }
+    }
+    
+    /*
+     *  Okay, page has been split, high key on right page is correct.  Now
+     *  set the high key on the left page to be the min key on the right
+     *  page.
+     */
+    
+    if (P_RIGHTMOST(ropaque)) {
+       itemid = PageGetItemId(rightpage, P_HIKEY);
+    } else {
+       itemid = PageGetItemId(rightpage, P_FIRSTKEY);
+    }
+    itemsz = ItemIdGetLength(itemid);
+    item = (BTItem) PageGetItem(rightpage, itemid);
+    
+    /*
+     *  We left a hole for the high key on the left page; fill it.  The
+     *  modal crap is to tell the page manager to put the new item on the
+     *  page and not screw around with anything else.  Whoever designed
+     *  this interface has presumably crawled back into the dung heap they
+     *  came from.  No one here will admit to it.
+     */
+    
+    PageManagerModeSet(OverwritePageManagerMode);
+    (void) PageAddItem(leftpage, (Item) item, itemsz, P_HIKEY, LP_USED);
+    PageManagerModeSet(ShufflePageManagerMode);
+    
+    /*
+     *  By here, the original data page has been split into two new halves,
+     *  and these are correct.  The algorithm requires that the left page
+     *  never move during a split, so we copy the new left page back on top
+     *  of the original.  Note that this is not a waste of time, since we
+     *  also require (in the page management code) that the center of a
+     *  page always be clean, and the most efficient way to guarantee this
+     *  is just to compact the data by reinserting it into a new left page.
+     */
+    
+    PageRestoreTempPage(leftpage, origpage);
+    
+    /* write these guys out */
+    _bt_wrtnorelbuf(rel, rbuf);
+    _bt_wrtnorelbuf(rel, buf);
+    
+    /*
+     *  Finally, we need to grab the right sibling (if any) and fix the
+     *  prev pointer there.  We are guaranteed that this is deadlock-free
+     *  since no other writer will be moving holding a lock on that page
+     *  and trying to move left, and all readers release locks on a page
+     *  before trying to fetch its neighbors.
+     */
+    
+    if (! P_RIGHTMOST(ropaque)) {
+       sbuf = _bt_getbuf(rel, ropaque->btpo_next, BT_WRITE);
+       spage = BufferGetPage(sbuf);
+       sopaque = (BTPageOpaque) PageGetSpecialPointer(spage);
+       sopaque->btpo_prev = BufferGetBlockNumber(rbuf);
+       
+       /* write and release the old right sibling */
+       _bt_wrtbuf(rel, sbuf);
+    }
+    
+    /* split's done */
+    return (rbuf);
+}
+
+/*
+ *  _bt_findsplitloc() -- find a safe place to split a page.
+ *
+ *     In order to guarantee the proper handling of searches for duplicate
+ *     keys, the first duplicate in the chain must either be the first
+ *     item on the page after the split, or the entire chain must be on
+ *     one of the two pages.  That is,
+ *             [1 2 2 2 3 4 5]
+ *     must become
+ *             [1] [2 2 2 3 4 5]
+ *     or
+ *             [1 2 2 2] [3 4 5]
+ *     but not
+ *             [1 2 2] [2 3 4 5].
+ *     However,
+ *             [2 2 2 2 2 3 4]
+ *     may be split as
+ *             [2 2 2 2] [2 3 4].
+ */
+static OffsetNumber
+_bt_findsplitloc(Relation rel,
+                Page page,
+                OffsetNumber start,
+                OffsetNumber maxoff,
+                Size llimit)
+{
+    OffsetNumber i;
+    OffsetNumber saferight;
+    ItemId nxtitemid, safeitemid;
+    BTItem safeitem, nxtitem;
+    IndexTuple safetup, nxttup;
+    Size nbytes;
+    TupleDesc itupdesc;
+    int natts;
+    int attno;
+    Datum attsafe;
+    Datum attnext;
+    bool null;
+    
+    itupdesc = RelationGetTupleDescriptor(rel);
+    natts = rel->rd_rel->relnatts;
+    
+    saferight = start;
+    safeitemid = PageGetItemId(page, saferight);
+    nbytes = ItemIdGetLength(safeitemid) + sizeof(ItemIdData);
+    safeitem = (BTItem) PageGetItem(page, safeitemid);
+    safetup = &(safeitem->bti_itup);
+    
+    i = OffsetNumberNext(start);
+    
+    while (nbytes < llimit) {
+       
+       /* check the next item on the page */
+       nxtitemid = PageGetItemId(page, i);
+       nbytes += (ItemIdGetLength(nxtitemid) + sizeof(ItemIdData));
+       nxtitem = (BTItem) PageGetItem(page, nxtitemid);
+       nxttup = &(nxtitem->bti_itup);
+       
+       /* test against last known safe item */
+       for (attno = 1; attno <= natts; attno++) {
+           attsafe = index_getattr(safetup, attno, itupdesc, &null);
+           attnext = index_getattr(nxttup, attno, itupdesc, &null);
+
+           /*
+            *  If the tuple we're looking at isn't equal to the last safe one
+            *  we saw, then it's our new safe tuple.
+            */
+           
+           if (!_bt_invokestrat(rel, attno, BTEqualStrategyNumber,
+                                attsafe, attnext)) {
+               safetup = nxttup;
+               saferight = i;
+               
+               /* break is for the attno for loop */
+               break;
+           }
+       }
+       i = OffsetNumberNext(i);
+    }
+    
+    /*
+     *  If the chain of dups starts at the beginning of the page and extends
+     *  past the halfway mark, we can split it in the middle.
+     */
+    
+    if (saferight == start)
+       saferight = i;
+    
+    return (saferight);
+}
+
+/*
+ *  _bt_newroot() -- Create a new root page for the index.
+ *
+ *     We've just split the old root page and need to create a new one.
+ *     In order to do this, we add a new root page to the file, then lock
+ *     the metadata page and update it.  This is guaranteed to be deadlock-
+ *     free, because all readers release their locks on the metadata page
+ *     before trying to lock the root, and all writers lock the root before
+ *     trying to lock the metadata page.  We have a write lock on the old
+ *     root page, so we have not introduced any cycles into the waits-for
+ *     graph.
+ *
+ *     On entry, lbuf (the old root) and rbuf (its new peer) are write-
+ *     locked.  We don't drop the locks in this routine; that's done by
+ *     the caller.  On exit, a new root page exists with entries for the
+ *     two new children.  The new root page is neither pinned nor locked.
+ */
+static void
+_bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf)
+{
+    Buffer rootbuf;
+    Page lpage, rpage, rootpage;
+    BlockNumber lbkno, rbkno;
+    BlockNumber rootbknum;
+    BTPageOpaque rootopaque;
+    ItemId itemid;
+    BTItem item;
+    Size itemsz;
+    BTItem new_item;
+    
+    /* get a new root page */
+    rootbuf = _bt_getbuf(rel, P_NEW, BT_WRITE);
+    rootpage = BufferGetPage(rootbuf);
+    _bt_pageinit(rootpage, BufferGetPageSize(rootbuf));
+    
+    /* set btree special data */
+    rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage);
+    rootopaque->btpo_prev = rootopaque->btpo_next = P_NONE;
+    rootopaque->btpo_flags |= BTP_ROOT;
+    
+    /*
+     *  Insert the internal tuple pointers.
+     */
+    
+    lbkno = BufferGetBlockNumber(lbuf);
+    rbkno = BufferGetBlockNumber(rbuf);
+    lpage = BufferGetPage(lbuf);
+    rpage = BufferGetPage(rbuf);
+    
+    /*
+     * step over the high key on the left page while building the 
+     * left page pointer.
+     */
+    itemid = PageGetItemId(lpage, P_FIRSTKEY);
+    itemsz = ItemIdGetLength(itemid);
+    item = (BTItem) PageGetItem(lpage, itemid);
+    new_item = _bt_formitem(&(item->bti_itup));
+    ItemPointerSet(&(new_item->bti_itup.t_tid), lbkno, P_FIRSTKEY);
+    
+    /*
+     * insert the left page pointer into the new root page.  the root
+     * page is the rightmost page on its level so the "high key" item
+     * is the first data item.
+     */
+    (void) PageAddItem(rootpage, (Item) new_item, itemsz, P_HIKEY, LP_USED);
+    pfree(new_item);
+    
+    /*
+     * the right page is the rightmost page on the second level, so 
+     * the "high key" item is the first data item on that page as well.
+     */
+    itemid = PageGetItemId(rpage, P_HIKEY);
+    itemsz = ItemIdGetLength(itemid);
+    item = (BTItem) PageGetItem(rpage, itemid);
+    new_item = _bt_formitem(&(item->bti_itup));
+    ItemPointerSet(&(new_item->bti_itup.t_tid), rbkno, P_HIKEY);
+    
+    /*
+     * insert the right page pointer into the new root page.
+     */
+    (void) PageAddItem(rootpage, (Item) new_item, itemsz, P_FIRSTKEY, LP_USED);
+    pfree(new_item);
+    
+    /* write and let go of the root buffer */
+    rootbknum = BufferGetBlockNumber(rootbuf);
+    _bt_wrtbuf(rel, rootbuf);
+    
+    /* update metadata page with new root block number */
+    _bt_metaproot(rel, rootbknum);
+}
+
+/*
+ *  _bt_pgaddtup() -- add a tuple to a particular page in the index.
+ *
+ *     This routine adds the tuple to the page as requested, and keeps the
+ *     write lock and reference associated with the page's buffer.  It is
+ *     an error to call pgaddtup() without a write lock and reference.  If
+ *     afteritem is non-null, it's the item that we expect our new item
+ *     to follow.  Otherwise, we do a binary search for the correct place
+ *     and insert the new item there.
+ */
+static OffsetNumber
+_bt_pgaddtup(Relation rel,
+            Buffer buf,
+            int keysz,
+            ScanKey itup_scankey,
+            Size itemsize,
+            BTItem btitem,
+            BTItem afteritem)
+{
+    OffsetNumber itup_off;
+    OffsetNumber first;
+    Page page;
+    BTPageOpaque opaque;
+    BTItem chkitem;
+    Oid afteroid;
+    
+    page = BufferGetPage(buf);
+    opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+    first = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY;
+    
+    if (afteritem == (BTItem) NULL) {
+       itup_off = _bt_binsrch(rel, buf, keysz, itup_scankey, BT_INSERTION);
+    } else {
+       afteroid = afteritem->bti_oid;
+       itup_off = first;
+       
+       do {
+           chkitem =
+               (BTItem) PageGetItem(page, PageGetItemId(page, itup_off));
+           itup_off = OffsetNumberNext(itup_off);
+       } while (chkitem->bti_oid != afteroid);
+    }
+
+    (void) PageAddItem(page, (Item) btitem, itemsize, itup_off, LP_USED);
+    
+    /* write the buffer, but hold our lock */
+    _bt_wrtnorelbuf(rel, buf);
+    
+    return (itup_off);
+}
+
+/*
+ *  _bt_goesonpg() -- Does a new tuple belong on this page?
+ *
+ *     This is part of the complexity introduced by allowing duplicate
+ *     keys into the index.  The tuple belongs on this page if:
+ *
+ *             + there is no page to the right of this one; or
+ *             + it is less than the high key on the page; or
+ *             + the item it is to follow ("afteritem") appears on this
+ *               page.
+ */
+static bool
+_bt_goesonpg(Relation rel,
+            Buffer buf,
+            Size keysz,
+            ScanKey scankey,
+            BTItem afteritem)
+{
+    Page page;
+    ItemId hikey;
+    BTPageOpaque opaque;
+    BTItem chkitem;
+    OffsetNumber offnum, maxoff;
+    Oid afteroid;
+    bool found;
+    
+    page = BufferGetPage(buf);
+    
+    /* no right neighbor? */
+    opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+    if (P_RIGHTMOST(opaque))
+       return (true);
+    
+    /*
+     *  this is a non-rightmost page, so it must have a high key item.
+     *
+     *  If the scan key is < the high key (the min key on the next page),
+     *  then it for sure belongs here.
+     */
+    hikey = PageGetItemId(page, P_HIKEY);
+    if (_bt_skeycmp(rel, keysz, scankey, page, hikey, BTLessStrategyNumber))
+       return (true);
+    
+    /*
+     *  If the scan key is > the high key, then it for sure doesn't belong
+     *  here.
+     */
+    
+    if (_bt_skeycmp(rel, keysz, scankey, page, hikey, BTGreaterStrategyNumber))
+       return (false);
+    
+    /*
+     *  If we have no adjacency information, and the item is equal to the
+     *  high key on the page (by here it is), then the item does not belong
+     *  on this page.
+     */
+    
+    if (afteritem == (BTItem) NULL)
+       return (false);
+    
+    /* damn, have to work for it.  i hate that. */
+    afteroid = afteritem->bti_oid;
+    maxoff = PageGetMaxOffsetNumber(page);
+    
+    /*
+     *  Search the entire page for the afteroid.  We need to do this, rather
+     *  than doing a binary search and starting from there, because if the
+     *  key we're searching for is the leftmost key in the tree at this
+     *  level, then a binary search will do the wrong thing.  Splits are
+     *  pretty infrequent, so the cost isn't as bad as it could be.
+     */
+    
+    found = false;
+    for (offnum = P_FIRSTKEY;
+        offnum <= maxoff;
+        offnum = OffsetNumberNext(offnum)) {
+       chkitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum));
+       if (chkitem->bti_oid == afteroid) {
+           found = true;
+           break;
+       }
+    }
+    
+    return (found);
+}
+
+/*
+ *     _bt_itemcmp() -- compare item1 to item2 using a requested
+ *                      strategy (<, <=, =, >=, >)
+ *
+ */
+bool
+_bt_itemcmp(Relation rel,
+           Size keysz,
+           BTItem item1,
+           BTItem item2,
+           StrategyNumber strat)
+{
+    TupleDesc tupDes;
+    IndexTuple indexTuple1, indexTuple2;
+    Datum attrDatum1, attrDatum2;
+    int i;
+    bool isNull;
+    bool compare;
+    
+    tupDes = RelationGetTupleDescriptor(rel);
+    indexTuple1 = &(item1->bti_itup);
+    indexTuple2 = &(item2->bti_itup);
+    
+    for (i = 1; i <= keysz; i++) {
+       attrDatum1 = index_getattr(indexTuple1, i, tupDes, &isNull);
+       attrDatum2 = index_getattr(indexTuple2, i, tupDes, &isNull);
+       compare = _bt_invokestrat(rel, i, strat, attrDatum1, attrDatum2);
+       if (!compare) {
+           return (false);
+       }
+    }
+    return (true);
+}
+
+/*
+ *     _bt_updateitem() -- updates the key of the item identified by the
+ *                         oid with the key of newItem (done in place)
+ *
+ */
+static void
+_bt_updateitem(Relation rel,
+              Size keysz,
+              Buffer buf,
+              Oid bti_oid,
+              BTItem newItem)
+{
+    Page page;
+    OffsetNumber maxoff;
+    OffsetNumber i;
+    ItemPointerData itemPtrData;
+    BTItem item;
+    IndexTuple oldIndexTuple, newIndexTuple;
+    
+    page = BufferGetPage(buf);
+    maxoff = PageGetMaxOffsetNumber(page);
+    
+    /* locate item on the page */
+    i = P_HIKEY;
+    do {
+       item = (BTItem) PageGetItem(page, PageGetItemId(page, i));
+       i = OffsetNumberNext(i);
+    } while (i <= maxoff && item->bti_oid != bti_oid);
+    
+    /* this should never happen (in theory) */
+    if (item->bti_oid != bti_oid) {
+       elog(FATAL, "_bt_getstackbuf was lying!!");
+    }
+    
+    oldIndexTuple = &(item->bti_itup);
+    newIndexTuple = &(newItem->bti_itup);
+    
+    /* keep the original item pointer */
+    ItemPointerCopy(&(oldIndexTuple->t_tid), &itemPtrData);
+    CopyIndexTuple(newIndexTuple, &oldIndexTuple);
+    ItemPointerCopy(&itemPtrData, &(oldIndexTuple->t_tid));
+}
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
new file mode 100644 (file)
index 0000000..b7c5e04
--- /dev/null
@@ -0,0 +1,523 @@
+/*-------------------------------------------------------------------------
+ *
+ * btpage.c--
+ *    BTree-specific page management code for the Postgres btree access
+ *    method.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *  NOTES
+ *     Postgres btree pages look like ordinary relation pages.  The opaque
+ *     data at high addresses includes pointers to left and right siblings
+ *     and flag data describing page state.  The first page in a btree, page
+ *     zero, is special -- it stores meta-information describing the tree.
+ *     Pages one and higher store the actual tree data.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+
+#include "utils/elog.h"
+#include "utils/rel.h"
+#include "utils/excid.h"
+
+#include "access/genam.h"
+#include "access/nbtree.h"
+
+#define BTREE_METAPAGE 0
+#define BTREE_MAGIC    0x053162
+#define BTREE_VERSION  0
+
+typedef struct BTMetaPageData {
+    uint32     btm_magic;
+    uint32     btm_version;
+    BlockNumber        btm_root;
+} BTMetaPageData;
+
+#define        BTPageGetMeta(p) \
+    ((BTMetaPageData *) &((PageHeader) p)->pd_linp[0])
+
+extern bool    BuildingBtree;
+
+/*
+ *  We use high-concurrency locking on btrees.  There are two cases in
+ *  which we don't do locking.  One is when we're building the btree.
+ *  Since the creating transaction has not committed, no one can see
+ *  the index, and there's no reason to share locks.  The second case
+ *  is when we're just starting up the database system.  We use some
+ *  special-purpose initialization code in the relation cache manager
+ *  (see utils/cache/relcache.c) to allow us to do indexed scans on
+ *  the system catalogs before we'd normally be able to.  This happens
+ *  before the lock table is fully initialized, so we can't use it.
+ *  Strictly speaking, this violates 2pl, but we don't do 2pl on the
+ *  system catalogs anyway, so I declare this to be okay.
+ */
+
+#define USELOCKING     (!BuildingBtree && !IsInitProcessingMode())
+
+/*
+ *  _bt_metapinit() -- Initialize the metadata page of a btree.
+ */
+void
+_bt_metapinit(Relation rel)
+{
+    Buffer buf;
+    Page pg;
+    int nblocks;
+    BTMetaPageData metad;
+    BTPageOpaque op;
+    
+    /* can't be sharing this with anyone, now... */
+    if (USELOCKING)
+       RelationSetLockForWrite(rel);
+    
+    if ((nblocks = RelationGetNumberOfBlocks(rel)) != 0) {
+       elog(WARN, "Cannot initialize non-empty btree %s",
+            RelationGetRelationName(rel));
+    }
+    
+    buf = ReadBuffer(rel, P_NEW);
+    pg = BufferGetPage(buf);
+    _bt_pageinit(pg, BufferGetPageSize(buf));
+    
+    metad.btm_magic = BTREE_MAGIC;
+    metad.btm_version = BTREE_VERSION;
+    metad.btm_root = P_NONE;
+    memmove((char *) BTPageGetMeta(pg), (char *) &metad, sizeof(metad));
+    
+    op = (BTPageOpaque) PageGetSpecialPointer(pg);
+    op->btpo_flags = BTP_META;
+
+    WriteBuffer(buf);
+    
+    /* all done */
+    if (USELOCKING)
+       RelationUnsetLockForWrite(rel);
+}
+
+/*
+ *  _bt_checkmeta() -- Verify that the metadata stored in a btree are
+ *                    reasonable.
+ */
+void
+_bt_checkmeta(Relation rel)
+{
+    Buffer metabuf;
+    Page metap;
+    BTMetaPageData *metad;
+    BTPageOpaque op;
+    int nblocks;
+    
+    /* if the relation is empty, this is init time; don't complain */
+    if ((nblocks = RelationGetNumberOfBlocks(rel)) == 0)
+       return;
+    
+    metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
+    metap = BufferGetPage(metabuf);
+    op = (BTPageOpaque) PageGetSpecialPointer(metap);
+    if (!(op->btpo_flags & BTP_META)) {
+       elog(WARN, "Invalid metapage for index %s",
+            RelationGetRelationName(rel));
+    }
+    metad = BTPageGetMeta(metap);
+
+    if (metad->btm_magic != BTREE_MAGIC) {
+       elog(WARN, "Index %s is not a btree",
+            RelationGetRelationName(rel));
+    }
+    
+    if (metad->btm_version != BTREE_VERSION) {
+       elog(WARN, "Version mismatch on %s:  version %d file, version %d code",
+            RelationGetRelationName(rel),
+            metad->btm_version, BTREE_VERSION);
+    }
+    
+    _bt_relbuf(rel, metabuf, BT_READ);
+}
+
+/*
+ *  _bt_getroot() -- Get the root page of the btree.
+ *
+ *     Since the root page can move around the btree file, we have to read
+ *     its location from the metadata page, and then read the root page
+ *     itself.  If no root page exists yet, we have to create one.  The
+ *     standard class of race conditions exists here; I think I covered
+ *     them all in the Hopi Indian rain dance of lock requests below.
+ *
+ *     We pass in the access type (BT_READ or BT_WRITE), and return the
+ *     root page's buffer with the appropriate lock type set.  Reference
+ *     count on the root page gets bumped by ReadBuffer.  The metadata
+ *     page is unlocked and unreferenced by this process when this routine
+ *     returns.
+ */
+Buffer
+_bt_getroot(Relation rel, int access)
+{
+    Buffer metabuf;
+    Page metapg;
+    BTPageOpaque metaopaque;
+    Buffer rootbuf;
+    Page rootpg;
+    BTPageOpaque rootopaque;
+    BlockNumber rootblkno;
+    BTMetaPageData *metad;
+    
+    metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
+    metapg = BufferGetPage(metabuf);
+    metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg);
+    Assert(metaopaque->btpo_flags & BTP_META);
+    metad = BTPageGetMeta(metapg);
+    
+    /* if no root page initialized yet, do it */
+    if (metad->btm_root == P_NONE) {
+       
+       /* turn our read lock in for a write lock */
+       _bt_relbuf(rel, metabuf, BT_READ);
+       metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE);
+       metapg = BufferGetPage(metabuf);
+       metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg);
+       Assert(metaopaque->btpo_flags & BTP_META);
+       metad = BTPageGetMeta(metapg);
+       
+       /*
+        *  Race condition:  if someone else initialized the metadata between
+        *  the time we released the read lock and acquired the write lock,
+        *  above, we want to avoid doing it again.
+        */
+       
+       if (metad->btm_root == P_NONE) {
+           
+           /*
+            *  Get, initialize, write, and leave a lock of the appropriate
+            *  type on the new root page.  Since this is the first page in
+            *  the tree, it's a leaf.
+            */
+           
+           rootbuf = _bt_getbuf(rel, P_NEW, BT_WRITE);
+           rootblkno = BufferGetBlockNumber(rootbuf);
+           rootpg = BufferGetPage(rootbuf);
+           metad->btm_root = rootblkno;
+           _bt_pageinit(rootpg, BufferGetPageSize(rootbuf));
+           rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpg);
+           rootopaque->btpo_flags |= (BTP_LEAF | BTP_ROOT);
+           _bt_wrtnorelbuf(rel, rootbuf);
+           
+           /* swap write lock for read lock, if appropriate */
+           if (access != BT_WRITE) {
+               _bt_setpagelock(rel, rootblkno, BT_READ);
+               _bt_unsetpagelock(rel, rootblkno, BT_WRITE);
+           }
+           
+           /* okay, metadata is correct */
+           _bt_wrtbuf(rel, metabuf);
+       } else {
+           
+           /*
+            *  Metadata initialized by someone else.  In order to guarantee
+            *  no deadlocks, we have to release the metadata page and start
+            *  all over again.
+            */
+           
+           _bt_relbuf(rel, metabuf, BT_WRITE);
+           return (_bt_getroot(rel, access));
+       }
+    } else {
+       rootbuf = _bt_getbuf(rel, metad->btm_root, access);
+       
+       /* done with the meta page */
+       _bt_relbuf(rel, metabuf, BT_READ);
+    }
+    
+    /*
+     *  Race condition:  If the root page split between the time we looked
+     *  at the metadata page and got the root buffer, then we got the wrong
+     *  buffer.
+     */
+    
+    rootpg = BufferGetPage(rootbuf);
+    rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpg);
+    if (!(rootopaque->btpo_flags & BTP_ROOT)) {
+       
+       /* it happened, try again */
+       _bt_relbuf(rel, rootbuf, access);
+       return (_bt_getroot(rel, access));
+    }
+    
+    /*
+     *  By here, we have a correct lock on the root block, its reference
+     *  count is correct, and we have no lock set on the metadata page.
+     *  Return the root block.
+     */
+    
+    return (rootbuf);
+}
+
+/*
+ *  _bt_getbuf() -- Get a buffer by block number for read or write.
+ *
+ *     When this routine returns, the appropriate lock is set on the
+ *     requested buffer its reference count is correct.
+ */
+Buffer
+_bt_getbuf(Relation rel, BlockNumber blkno, int access)
+{
+    Buffer buf;
+    Page page;
+    
+    /*
+     *  If we want a new block, we can't set a lock of the appropriate type
+     *  until we've instantiated the buffer.
+     */
+    
+    if (blkno != P_NEW) {
+       if (access == BT_WRITE)
+           _bt_setpagelock(rel, blkno, BT_WRITE);
+       else
+           _bt_setpagelock(rel, blkno, BT_READ);
+       
+       buf = ReadBuffer(rel, blkno);
+    } else {
+       buf = ReadBuffer(rel, blkno);
+       blkno = BufferGetBlockNumber(buf);
+       page = BufferGetPage(buf);
+       _bt_pageinit(page, BufferGetPageSize(buf));
+       
+       if (access == BT_WRITE)
+           _bt_setpagelock(rel, blkno, BT_WRITE);
+       else
+           _bt_setpagelock(rel, blkno, BT_READ);
+    }
+    
+    /* ref count and lock type are correct */
+    return (buf);
+}
+
+/*
+ *  _bt_relbuf() -- release a locked buffer.
+ */
+void
+_bt_relbuf(Relation rel, Buffer buf, int access)
+{
+    BlockNumber blkno;
+    
+    blkno = BufferGetBlockNumber(buf);
+    
+    /* access had better be one of read or write */
+    if (access == BT_WRITE)
+       _bt_unsetpagelock(rel, blkno, BT_WRITE);
+    else
+       _bt_unsetpagelock(rel, blkno, BT_READ);
+    
+    ReleaseBuffer(buf);
+}
+
+/*
+ *  _bt_wrtbuf() -- write a btree page to disk.
+ *
+ *     This routine releases the lock held on the buffer and our reference
+ *     to it.  It is an error to call _bt_wrtbuf() without a write lock
+ *     or a reference to the buffer.
+ */
+void
+_bt_wrtbuf(Relation rel, Buffer buf)
+{
+    BlockNumber blkno;
+    
+    blkno = BufferGetBlockNumber(buf);
+    WriteBuffer(buf);
+    _bt_unsetpagelock(rel, blkno, BT_WRITE);
+}
+
+/*
+ *  _bt_wrtnorelbuf() -- write a btree page to disk, but do not release
+ *                      our reference or lock.
+ *
+ *     It is an error to call _bt_wrtnorelbuf() without a write lock
+ *     or a reference to the buffer.
+ */
+void
+_bt_wrtnorelbuf(Relation rel, Buffer buf)
+{
+    BlockNumber blkno;
+    
+    blkno = BufferGetBlockNumber(buf);
+    WriteNoReleaseBuffer(buf);
+}
+
+/*
+ *  _bt_pageinit() -- Initialize a new page.
+ */
+void
+_bt_pageinit(Page page, Size size)
+{
+    /*
+     *  Cargo-cult programming -- don't really need this to be zero, but
+     *  creating new pages is an infrequent occurrence and it makes me feel
+     *  good when I know they're empty.
+     */
+    
+    memset(page, 0, size);
+    
+    PageInit(page, size, sizeof(BTPageOpaqueData));
+}
+
+/*
+ *  _bt_metaproot() -- Change the root page of the btree.
+ *
+ *     Lehman and Yao require that the root page move around in order to
+ *     guarantee deadlock-free short-term, fine-granularity locking.  When
+ *     we split the root page, we record the new parent in the metadata page
+ *     for the relation.  This routine does the work.
+ *
+ *     No direct preconditions, but if you don't have the a write lock on
+ *     at least the old root page when you call this, you're making a big
+ *     mistake.  On exit, metapage data is correct and we no longer have
+ *     a reference to or lock on the metapage.
+ */
+void
+_bt_metaproot(Relation rel, BlockNumber rootbknum)
+{
+    Buffer metabuf;
+    Page metap;
+    BTPageOpaque metaopaque;
+    BTMetaPageData *metad;
+    
+    metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE);
+    metap = BufferGetPage(metabuf);
+    metaopaque = (BTPageOpaque) PageGetSpecialPointer(metap);
+    Assert(metaopaque->btpo_flags & BTP_META);
+    metad = BTPageGetMeta(metap);
+    metad->btm_root = rootbknum;
+    _bt_wrtbuf(rel, metabuf);
+}
+
+/*
+ *  _bt_getstackbuf() -- Walk back up the tree one step, and find the item
+ *                      we last looked at in the parent.
+ *
+ *     This is possible because we save a bit image of the last item
+ *     we looked at in the parent, and the update algorithm guarantees
+ *     that if items above us in the tree move, they only move right.
+ */
+Buffer
+_bt_getstackbuf(Relation rel, BTStack stack, int access)
+{
+    Buffer buf;
+    BlockNumber blkno;
+    OffsetNumber start, offnum, maxoff;
+    OffsetNumber i;
+    Page page;
+    ItemId itemid;
+    BTItem item;
+    BTPageOpaque opaque;
+    
+    blkno = stack->bts_blkno;
+    buf = _bt_getbuf(rel, blkno, access);
+    page = BufferGetPage(buf);
+    opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+    maxoff = PageGetMaxOffsetNumber(page);
+    
+    if (maxoff >= stack->bts_offset) {
+       itemid = PageGetItemId(page, stack->bts_offset);
+       item = (BTItem) PageGetItem(page, itemid);
+       
+       /* if the item is where we left it, we're done */
+       if (item->bti_oid == stack->bts_btitem->bti_oid)
+           return (buf);
+       
+       /* if the item has just moved right on this page, we're done */
+       for (i = OffsetNumberNext(stack->bts_offset);
+            i <= maxoff;
+            i = OffsetNumberNext(i)) {
+           itemid = PageGetItemId(page, i);
+           item = (BTItem) PageGetItem(page, itemid);
+           
+           /* if the item is where we left it, we're done */
+           if (item->bti_oid == stack->bts_btitem->bti_oid)
+               return (buf);
+       }
+    }
+    
+    /* by here, the item we're looking for moved right at least one page */
+    for (;;) {
+       blkno = opaque->btpo_next;
+       if (P_RIGHTMOST(opaque))
+           elog(FATAL, "my bits moved right off the end of the world!");
+       
+       _bt_relbuf(rel, buf, access);
+       buf = _bt_getbuf(rel, blkno, access);
+       page = BufferGetPage(buf);
+       maxoff = PageGetMaxOffsetNumber(page);
+       opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+       
+       /* if we have a right sibling, step over the high key */
+       start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY;
+       
+       /* see if it's on this page */
+       for (offnum = start;
+            offnum <= maxoff;
+            offnum = OffsetNumberNext(offnum)) {
+           itemid = PageGetItemId(page, offnum);
+           item = (BTItem) PageGetItem(page, itemid);
+           if (item->bti_oid == stack->bts_btitem->bti_oid)
+               return (buf);
+       }
+    }
+}
+
+void
+_bt_setpagelock(Relation rel, BlockNumber blkno, int access)
+{
+    ItemPointerData iptr;
+    
+    if (USELOCKING) {
+       ItemPointerSet(&iptr, blkno, P_HIKEY);
+       
+       if (access == BT_WRITE)
+           RelationSetSingleWLockPage(rel, &iptr);
+       else
+           RelationSetSingleRLockPage(rel, &iptr);
+    }
+}
+
+void
+_bt_unsetpagelock(Relation rel, BlockNumber blkno, int access)
+{
+    ItemPointerData iptr;
+    
+    if (USELOCKING) {
+       ItemPointerSet(&iptr, blkno, P_HIKEY);
+       
+       if (access == BT_WRITE)
+           RelationUnsetSingleWLockPage(rel, &iptr);
+       else
+           RelationUnsetSingleRLockPage(rel, &iptr);
+    }
+}
+
+void
+_bt_pagedel(Relation rel, ItemPointer tid)
+{
+    Buffer buf;
+    Page page;
+    BlockNumber blkno;
+    OffsetNumber offno;
+    
+    blkno = ItemPointerGetBlockNumber(tid);
+    offno = ItemPointerGetOffsetNumber(tid);
+    
+    buf = _bt_getbuf(rel, blkno, BT_WRITE);
+    page = BufferGetPage(buf);
+    
+    PageIndexTupleDelete(page, offno);
+    
+    /* write the buffer and release the lock */
+    _bt_wrtbuf(rel, buf);
+}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
new file mode 100644 (file)
index 0000000..53b1563
--- /dev/null
@@ -0,0 +1,516 @@
+/*-------------------------------------------------------------------------
+ *
+ * btree.c--
+ *    Implementation of Lehman and Yao's btree management algorithm for
+ *    Postgres.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    This file contains only the public interface routines.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+#include "utils/excid.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/sdir.h"
+#include "access/nbtree.h"
+#include "access/funcindex.h"
+
+#include "nodes/execnodes.h"
+#include "nodes/plannodes.h"
+
+#include "executor/executor.h"
+#include "executor/tuptable.h"
+
+#include "catalog/index.h"
+
+bool   BuildingBtree = false;
+bool   FastBuild = false; /* turn this on to make bulk builds work*/
+
+/*
+ *  btbuild() -- build a new btree index.
+ *
+ *     We use a global variable to record the fact that we're creating
+ *     a new index.  This is used to avoid high-concurrency locking,
+ *     since the index won't be visible until this transaction commits
+ *     and since building is guaranteed to be single-threaded.
+ */
+void
+btbuild(Relation heap,
+       Relation index,
+       int natts,
+       AttrNumber *attnum,
+       IndexStrategy istrat,
+       uint16 pcount,
+       Datum *params,
+       FuncIndexInfo *finfo,
+       PredInfo *predInfo)
+{
+    HeapScanDesc hscan;
+    Buffer buffer;
+    HeapTuple htup;
+    IndexTuple itup;
+    TupleDesc htupdesc, itupdesc;
+    Datum *attdata;
+    bool *nulls;
+    InsertIndexResult res;
+    int nhtups, nitups;
+    int i;
+    BTItem btitem;
+    ExprContext *econtext;
+    TupleTable tupleTable;
+    TupleTableSlot *slot;
+    Oid hrelid, irelid;
+    Node *pred, *oldPred;
+    void *spool;
+    
+    /* note that this is a new btree */
+    BuildingBtree = true;
+    
+    pred = predInfo->pred;
+    oldPred = predInfo->oldPred;
+
+    /* initialize the btree index metadata page (if this is a new index) */
+    if (oldPred == NULL)
+       _bt_metapinit(index);
+    
+    /* get tuple descriptors for heap and index relations */
+    htupdesc = RelationGetTupleDescriptor(heap);
+    itupdesc = RelationGetTupleDescriptor(index);
+    
+    /* get space for data items that'll appear in the index tuple */
+    attdata = (Datum *) palloc(natts * sizeof(Datum));
+    nulls = (bool *) palloc(natts * sizeof(bool));
+    
+    /*
+     * If this is a predicate (partial) index, we will need to evaluate the
+     * predicate using ExecQual, which requires the current tuple to be in a
+     * slot of a TupleTable.  In addition, ExecQual must have an ExprContext
+     * referring to that slot.  Here, we initialize dummy TupleTable and
+     * ExprContext objects for this purpose. --Nels, Feb '92
+     */
+#ifndef OMIT_PARTIAL_INDEX
+    if (pred != NULL || oldPred != NULL) {
+       tupleTable = ExecCreateTupleTable(1);
+       slot = ExecAllocTableSlot(tupleTable);
+       econtext = makeNode(ExprContext);
+       FillDummyExprContext(econtext, slot, htupdesc, InvalidBuffer);
+    }
+#endif /* OMIT_PARTIAL_INDEX */
+    
+    /* start a heap scan */
+    hscan = heap_beginscan(heap, 0, NowTimeQual, 0, (ScanKey) NULL);
+    htup = heap_getnext(hscan, 0, &buffer);
+    
+    /* build the index */
+    nhtups = nitups = 0;
+    
+    if (FastBuild) {
+       spool = _bt_spoolinit(index, 7);
+       res = (InsertIndexResult) NULL;
+    }
+
+    for (; HeapTupleIsValid(htup); htup = heap_getnext(hscan, 0, &buffer)) {
+       
+       nhtups++;
+       
+       /*
+        * If oldPred != NULL, this is an EXTEND INDEX command, so skip
+        * this tuple if it was already in the existing partial index
+        */
+       if (oldPred != NULL) {
+#ifndef OMIT_PARTIAL_INDEX
+
+           /*SetSlotContents(slot, htup);*/
+           slot->val = htup;
+           if (ExecQual((List*)oldPred, econtext) == true) {
+               nitups++;
+               continue;
+           }
+#endif /* OMIT_PARTIAL_INDEX */        
+       }
+       
+       /* Skip this tuple if it doesn't satisfy the partial-index predicate */
+       if (pred != NULL) {
+#ifndef OMIT_PARTIAL_INDEX
+           /* SetSlotContents(slot, htup); */
+           slot->val = htup;
+           if (ExecQual((List*)pred, econtext) == false)
+               continue;
+#endif /* OMIT_PARTIAL_INDEX */        
+       }
+       
+       nitups++;
+       
+       /*
+        *  For the current heap tuple, extract all the attributes
+        *  we use in this index, and note which are null.
+        */
+       
+       for (i = 1; i <= natts; i++) {
+           int  attoff;
+           bool attnull;
+           
+           /*
+            *  Offsets are from the start of the tuple, and are
+            *  zero-based; indices are one-based.  The next call
+            *  returns i - 1.  That's data hiding for you.
+            */
+           
+           attoff = AttrNumberGetAttrOffset(i);
+           attdata[attoff] = GetIndexValue(htup, 
+                                           htupdesc,
+                                           attoff, 
+                                           attnum, 
+                                           finfo, 
+                                           &attnull,
+                                           buffer);
+           nulls[attoff] = (attnull ? 'n' : ' ');
+       }
+       
+       /* form an index tuple and point it at the heap tuple */
+       itup = index_formtuple(itupdesc, attdata, nulls);
+       
+       /*
+        *  If the single index key is null, we don't insert it into
+        *  the index.  Btrees support scans on <, <=, =, >=, and >.
+        *  Relational algebra says that A op B (where op is one of the
+        *  operators above) returns null if either A or B is null.  This
+        *  means that no qualification used in an index scan could ever
+        *  return true on a null attribute.  It also means that indices
+        *  can't be used by ISNULL or NOTNULL scans, but that's an
+        *  artifact of the strategy map architecture chosen in 1986, not
+        *  of the way nulls are handled here.
+        */
+       
+       if (itup->t_info & INDEX_NULL_MASK) {
+           pfree(itup);
+           continue;
+       }
+       
+       itup->t_tid = htup->t_ctid;
+       btitem = _bt_formitem(itup);
+
+       /*
+        * if we are doing bottom-up btree build, we insert the index
+        * into a spool page for subsequent processing.  otherwise, we
+        * insert into the btree.
+        */
+       if (FastBuild) {
+           _bt_spool(index, btitem, spool);
+       } else {
+           res = _bt_doinsert(index, btitem);
+       }
+
+       pfree(btitem);
+       pfree(itup);
+       if (res) {
+           pfree(res);
+       }
+    }
+    
+    /* okay, all heap tuples are indexed */
+    heap_endscan(hscan);
+    
+    if (pred != NULL || oldPred != NULL) {
+#ifndef OMIT_PARTIAL_INDEX
+       ExecDestroyTupleTable(tupleTable, true);
+       pfree(econtext);
+#endif /* OMIT_PARTIAL_INDEX */        
+    }
+    
+    /*
+     * if we are doing bottom-up btree build, we now have a bunch of
+     * sorted runs in the spool pages.  finish the build by (1)
+     * merging the runs, (2) inserting the sorted tuples into btree
+     * pages and (3) building the upper levels.
+     */
+    if (FastBuild) {
+       _bt_spool(index, (BTItem) NULL, spool); /* flush spool */
+       _bt_leafbuild(index, spool);
+       _bt_spooldestroy(spool);
+    }
+
+    /*
+     *  Since we just counted the tuples in the heap, we update its
+     *  stats in pg_class to guarantee that the planner takes advantage
+     *  of the index we just created. Finally, only update statistics
+     *  during normal index definitions, not for indices on system catalogs
+     *  created during bootstrap processing.  We must close the relations
+     *  before updatings statistics to guarantee that the relcache entries
+     *  are flushed when we increment the command counter in UpdateStats().
+     */
+    if (IsNormalProcessingMode())
+       {
+           hrelid = heap->rd_id;
+           irelid = index->rd_id;
+           heap_close(heap);
+           index_close(index);
+           UpdateStats(hrelid, nhtups, true);
+           UpdateStats(irelid, nitups, false);
+           if (oldPred != NULL) {
+               if (nitups == nhtups) pred = NULL;
+               UpdateIndexPredicate(irelid, oldPred, pred);
+           }  
+       }
+    
+    /* be tidy */
+    pfree(nulls);
+    pfree(attdata);
+    
+    /* all done */
+    BuildingBtree = false;
+}
+
+/*
+ *  btinsert() -- insert an index tuple into a btree.
+ *
+ *     Descend the tree recursively, find the appropriate location for our
+ *     new tuple, put it there, set its unique OID as appropriate, and
+ *     return an InsertIndexResult to the caller.
+ */
+InsertIndexResult
+btinsert(Relation rel, IndexTuple itup)
+{
+    BTItem btitem;
+    InsertIndexResult res;
+    
+    if (itup->t_info & INDEX_NULL_MASK)
+       return ((InsertIndexResult) NULL);
+    
+    btitem = _bt_formitem(itup);
+    
+    res = _bt_doinsert(rel, btitem);
+    pfree(btitem);
+    
+    return (res);
+}
+
+/*
+ *  btgettuple() -- Get the next tuple in the scan.
+ */
+char *
+btgettuple(IndexScanDesc scan, ScanDirection dir)
+{
+    RetrieveIndexResult res;
+    
+    /*
+     *  If we've already initialized this scan, we can just advance it
+     *  in the appropriate direction.  If we haven't done so yet, we
+     *  call a routine to get the first item in the scan.
+     */
+    
+    if (ItemPointerIsValid(&(scan->currentItemData)))
+       res = _bt_next(scan, dir);
+    else
+       res = _bt_first(scan, dir);
+    
+    return ((char *) res);
+}
+
+/*
+ *  btbeginscan() -- start a scan on a btree index
+ */
+char *
+btbeginscan(Relation rel, bool fromEnd, uint16 keysz, ScanKey scankey)
+{
+    IndexScanDesc scan;
+    StrategyNumber strat;
+    BTScanOpaque so;
+    
+    /* first order the keys in the qualification */
+    if (keysz > 1)
+       _bt_orderkeys(rel, &keysz, scankey);
+    
+    /* now get the scan */
+    scan = RelationGetIndexScan(rel, fromEnd, keysz, scankey);
+    so = (BTScanOpaque) palloc(sizeof(BTScanOpaqueData));
+    so->btso_curbuf = so->btso_mrkbuf = InvalidBuffer;
+    scan->opaque = so;
+    
+    /* finally, be sure that the scan exploits the tree order */
+    scan->scanFromEnd = false;
+    scan->flags = 0x0;
+    if (keysz > 0) {
+       strat = _bt_getstrat(scan->relation, 1 /* XXX */,
+                            scankey[0].sk_procedure);
+       
+       if (strat == BTLessStrategyNumber
+           || strat == BTLessEqualStrategyNumber)
+           scan->scanFromEnd = true;
+    } else {
+       scan->scanFromEnd = true;
+    }
+    
+    /* register scan in case we change pages it's using */
+    _bt_regscan(scan);
+    
+    return ((char *) scan);
+}
+
+/*
+ *  btrescan() -- rescan an index relation
+ */
+void
+btrescan(IndexScanDesc scan, bool fromEnd, ScanKey scankey)
+{
+    ItemPointer iptr;
+    BTScanOpaque so;
+    
+    so = (BTScanOpaque) scan->opaque;
+    
+    /* we hold a read lock on the current page in the scan */
+    if (ItemPointerIsValid(iptr = &(scan->currentItemData))) {
+       _bt_relbuf(scan->relation, so->btso_curbuf, BT_READ);
+       so->btso_curbuf = InvalidBuffer;
+       ItemPointerSetInvalid(iptr);
+    }
+    
+    /* and we hold a read lock on the last marked item in the scan */
+    if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) {
+       _bt_relbuf(scan->relation, so->btso_mrkbuf, BT_READ);
+       so->btso_mrkbuf = InvalidBuffer;
+       ItemPointerSetInvalid(iptr);
+    }
+    
+    /* reset the scan key */
+    if (scan->numberOfKeys > 0) {
+       memmove(scan->keyData,
+               scankey,
+               scan->numberOfKeys * sizeof(ScanKeyData));
+    }
+}
+
+void
+btmovescan(IndexScanDesc scan, Datum v)
+{
+    ItemPointer iptr;
+    BTScanOpaque so;
+    
+    so = (BTScanOpaque) scan->opaque;
+    
+    /* release any locks we still hold */
+    if (ItemPointerIsValid(iptr = &(scan->currentItemData))) {
+       _bt_relbuf(scan->relation, so->btso_curbuf, BT_READ);
+       so->btso_curbuf = InvalidBuffer;
+       ItemPointerSetInvalid(iptr);
+    }
+    
+    scan->keyData[0].sk_argument = v;
+}
+
+/*
+ *  btendscan() -- close down a scan
+ */
+void
+btendscan(IndexScanDesc scan)
+{
+    ItemPointer iptr;
+    BTScanOpaque so;
+    
+    so = (BTScanOpaque) scan->opaque;
+    
+    /* release any locks we still hold */
+    if (ItemPointerIsValid(iptr = &(scan->currentItemData))) {
+       if (BufferIsValid(so->btso_curbuf))
+           _bt_relbuf(scan->relation, so->btso_curbuf, BT_READ);
+       so->btso_curbuf = InvalidBuffer;
+       ItemPointerSetInvalid(iptr);
+    }
+    
+    if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) {
+       if (BufferIsValid(so->btso_mrkbuf))
+           _bt_relbuf(scan->relation, so->btso_mrkbuf, BT_READ);
+       so->btso_mrkbuf = InvalidBuffer;
+       ItemPointerSetInvalid(iptr);
+    }
+    
+    /* don't need scan registered anymore */
+    _bt_dropscan(scan);
+    
+    /* be tidy */
+#ifdef PERFECT_MMGR
+    pfree (scan->opaque);
+#endif /* PERFECT_MMGR */
+}
+
+/*
+ *  btmarkpos() -- save current scan position
+ */
+void
+btmarkpos(IndexScanDesc scan)
+{
+    ItemPointer iptr;
+    BTScanOpaque so;
+    
+    so = (BTScanOpaque) scan->opaque;
+    
+    /* release lock on old marked data, if any */
+    if (ItemPointerIsValid(iptr = &(scan->currentMarkData))) {
+       _bt_relbuf(scan->relation, so->btso_mrkbuf, BT_READ);
+       so->btso_mrkbuf = InvalidBuffer;
+       ItemPointerSetInvalid(iptr);
+    }
+    
+    /* bump lock on currentItemData and copy to currentMarkData */
+    if (ItemPointerIsValid(&(scan->currentItemData))) {
+       so->btso_mrkbuf = _bt_getbuf(scan->relation,
+                                    BufferGetBlockNumber(so->btso_curbuf),
+                                    BT_READ);
+       scan->currentMarkData = scan->currentItemData;
+    }
+}
+
+/*
+ *  btrestrpos() -- restore scan to last saved position
+ */
+void
+btrestrpos(IndexScanDesc scan)
+{
+    ItemPointer iptr;
+    BTScanOpaque so;
+    
+    so = (BTScanOpaque) scan->opaque;
+    
+    /* release lock on current data, if any */
+    if (ItemPointerIsValid(iptr = &(scan->currentItemData))) {
+       _bt_relbuf(scan->relation, so->btso_curbuf, BT_READ);
+       so->btso_curbuf = InvalidBuffer;
+       ItemPointerSetInvalid(iptr);
+    }
+    
+    /* bump lock on currentMarkData and copy to currentItemData */
+    if (ItemPointerIsValid(&(scan->currentMarkData))) {
+       so->btso_curbuf = _bt_getbuf(scan->relation,
+                                    BufferGetBlockNumber(so->btso_mrkbuf),
+                                    BT_READ);
+       
+       scan->currentItemData = scan->currentMarkData;
+    }
+}
+
+/* stubs */
+void
+btdelete(Relation rel, ItemPointer tid)
+{
+    /* adjust any active scans that will be affected by this deletion */
+    _bt_adjscans(rel, tid);
+    
+    /* delete the data from the page */
+    _bt_pagedel(rel, tid);
+}
diff --git a/src/backend/access/nbtree/nbtscan.c b/src/backend/access/nbtree/nbtscan.c
new file mode 100644 (file)
index 0000000..3863ec6
--- /dev/null
@@ -0,0 +1,164 @@
+/*-------------------------------------------------------------------------
+ *
+ * btscan.c--
+ *    manage scans on btrees.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *
+ * NOTES
+ *   Because we can be doing an index scan on a relation while we update
+ *   it, we need to avoid missing data that moves around in the index.
+ *   The routines and global variables in this file guarantee that all
+ *   scans in the local address space stay correctly positioned.  This
+ *   is all we need to worry about, since write locking guarantees that
+ *   no one else will be on the same page at the same time as we are.
+ *
+ *   The scheme is to manage a list of active scans in the current backend.
+ *   Whenever we add or remove records from an index, or whenever we
+ *   split a leaf page, we check the list of active scans to see if any
+ *   has been affected.  A scan is affected only if it is on the same
+ *   relation, and the same page, as the update.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+#include "utils/excid.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/sdir.h"
+#include "access/nbtree.h"
+
+typedef struct BTScanListData {
+    IndexScanDesc              btsl_scan;
+    struct BTScanListData      *btsl_next;
+} BTScanListData;
+
+typedef BTScanListData *BTScanList;
+
+static BTScanList      BTScans = (BTScanList) NULL;
+     
+/*
+ *  _bt_regscan() -- register a new scan.
+ */
+void
+_bt_regscan(IndexScanDesc scan)
+{
+    BTScanList new_el;
+    
+    new_el = (BTScanList) palloc(sizeof(BTScanListData));
+    new_el->btsl_scan = scan;
+    new_el->btsl_next = BTScans;
+    BTScans = new_el;
+}
+
+/*
+ *  _bt_dropscan() -- drop a scan from the scan list
+ */
+void
+_bt_dropscan(IndexScanDesc scan)
+{
+    BTScanList chk, last;
+    
+    last = (BTScanList) NULL;
+    for (chk = BTScans;
+        chk != (BTScanList) NULL && chk->btsl_scan != scan;
+        chk = chk->btsl_next) {
+       last = chk;
+    }
+    
+    if (chk == (BTScanList) NULL)
+       elog(WARN, "btree scan list trashed; can't find 0x%lx", scan);
+    
+    if (last == (BTScanList) NULL)
+       BTScans = chk->btsl_next;
+    else
+       last->btsl_next = chk->btsl_next;
+    
+#ifdef PERFECT_MEM
+    pfree (chk);
+#endif /* PERFECT_MEM */
+}
+
+void
+_bt_adjscans(Relation rel, ItemPointer tid)
+{
+    BTScanList l;
+    Oid relid;
+    
+    relid = rel->rd_id;
+    for (l = BTScans; l != (BTScanList) NULL; l = l->btsl_next) {
+       if (relid == l->btsl_scan->relation->rd_id)
+           _bt_scandel(l->btsl_scan, ItemPointerGetBlockNumber(tid),
+                       ItemPointerGetOffsetNumber(tid));
+    }
+}
+
+void
+_bt_scandel(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno)
+{
+    ItemPointer current;
+    Buffer buf;
+    BTScanOpaque so;
+    
+    if (!_bt_scantouched(scan, blkno, offno))
+       return;
+    
+    so = (BTScanOpaque) scan->opaque;
+    buf = so->btso_curbuf;
+    
+    current = &(scan->currentItemData);
+    if (ItemPointerIsValid(current)
+       && ItemPointerGetBlockNumber(current) == blkno
+       && ItemPointerGetOffsetNumber(current) >= offno) {
+       _bt_step(scan, &buf, BackwardScanDirection);
+       so->btso_curbuf = buf;
+    }
+    
+    current = &(scan->currentMarkData);
+    if (ItemPointerIsValid(current)
+       && ItemPointerGetBlockNumber(current) == blkno
+       && ItemPointerGetOffsetNumber(current) >= offno) {
+       ItemPointerData tmp;
+       tmp = *current;
+       *current = scan->currentItemData;
+       scan->currentItemData = tmp;
+       _bt_step(scan, &buf, BackwardScanDirection);
+       so->btso_mrkbuf = buf;
+       tmp = *current;
+       *current = scan->currentItemData;
+       scan->currentItemData = tmp;
+    }
+}
+
+bool
+_bt_scantouched(IndexScanDesc scan, BlockNumber blkno, OffsetNumber offno)
+{
+    ItemPointer current;
+    
+    current = &(scan->currentItemData);
+    if (ItemPointerIsValid(current)
+       && ItemPointerGetBlockNumber(current) == blkno
+       && ItemPointerGetOffsetNumber(current) >= offno)
+       return (true);
+    
+    current = &(scan->currentMarkData);
+    if (ItemPointerIsValid(current)
+       && ItemPointerGetBlockNumber(current) == blkno
+       && ItemPointerGetOffsetNumber(current) >= offno)
+       return (true);
+    
+    return (false);
+}
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
new file mode 100644 (file)
index 0000000..5e57b4f
--- /dev/null
@@ -0,0 +1,1133 @@
+/*-------------------------------------------------------------------------
+ *
+ * btsearch.c--
+ *    search code for postgres btrees.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+#include "utils/excid.h"
+
+#include "fmgr.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/skey.h"
+#include "access/sdir.h"
+#include "access/nbtree.h"
+
+static BTStack _bt_searchr(Relation rel, int keysz, ScanKey scankey, Buffer *bufP, BTStack stack_in);
+static OffsetNumber _bt_firsteq(Relation rel, TupleDesc itupdesc, Page page, Size keysz, ScanKey scankey, OffsetNumber offnum);
+static int _bt_compare(Relation rel, TupleDesc itupdesc, Page page, int keysz, ScanKey scankey, OffsetNumber offnum);
+static bool _bt_twostep(IndexScanDesc scan, Buffer *bufP, ScanDirection dir);
+static RetrieveIndexResult _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
+
+/*
+ *  _bt_search() -- Search for a scan key in the index.
+ *
+ *     This routine is actually just a helper that sets things up and
+ *     calls a recursive-descent search routine on the tree.
+ */
+BTStack
+_bt_search(Relation rel, int keysz, ScanKey scankey, Buffer *bufP)
+{
+    *bufP = _bt_getroot(rel, BT_READ);
+    return (_bt_searchr(rel, keysz, scankey, bufP, (BTStack) NULL));
+}
+
+/*
+ *  _bt_searchr() -- Search the tree recursively for a particular scankey.
+ */
+static BTStack
+_bt_searchr(Relation rel,
+           int keysz,
+           ScanKey scankey,
+           Buffer *bufP,
+           BTStack stack_in)
+{
+    BTStack stack;
+    OffsetNumber offnum;
+    Page page;
+    BTPageOpaque opaque;
+    BlockNumber par_blkno;
+    BlockNumber blkno;
+    ItemId itemid;
+    BTItem btitem;
+    BTItem item_save;
+    int item_nbytes;
+    IndexTuple itup;
+    
+    /* if this is a leaf page, we're done */
+    page = BufferGetPage(*bufP);
+    opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+    if (opaque->btpo_flags & BTP_LEAF)
+       return (stack_in);
+    
+    /*
+     *  Find the appropriate item on the internal page, and get the child
+     *  page that it points to.
+     */
+    
+    par_blkno = BufferGetBlockNumber(*bufP);
+    offnum = _bt_binsrch(rel, *bufP, keysz, scankey, BT_DESCENT);
+    itemid = PageGetItemId(page, offnum);
+    btitem = (BTItem) PageGetItem(page, itemid);
+    itup = &(btitem->bti_itup);
+    blkno = ItemPointerGetBlockNumber(&(itup->t_tid));
+    
+    /*
+     *  We need to save the bit image of the index entry we chose in the
+     *  parent page on a stack.  In case we split the tree, we'll use this
+     *  bit image to figure out what our real parent page is, in case the
+     *  parent splits while we're working lower in the tree.  See the paper
+     *  by Lehman and Yao for how this is detected and handled.  (We use
+     *  unique OIDs to disambiguate duplicate keys in the index -- Lehman
+     *  and Yao disallow duplicate keys).
+     */
+    
+    item_nbytes = ItemIdGetLength(itemid);
+    item_save = (BTItem) palloc(item_nbytes);
+    memmove((char *) item_save, (char *) btitem, item_nbytes);
+    stack = (BTStack) palloc(sizeof(BTStackData));
+    stack->bts_blkno = par_blkno;
+    stack->bts_offset = offnum;
+    stack->bts_btitem = item_save;
+    stack->bts_parent = stack_in;
+    
+    /* drop the read lock on the parent page and acquire one on the child */
+    _bt_relbuf(rel, *bufP, BT_READ);
+    *bufP = _bt_getbuf(rel, blkno, BT_READ);
+    
+    /*
+     *  Race -- the page we just grabbed may have split since we read its
+     *  pointer in the parent.  If it has, we may need to move right to its
+     *  new sibling.  Do that.
+     */
+    
+    *bufP = _bt_moveright(rel, *bufP, keysz, scankey, BT_READ);
+    
+    /* okay, all set to move down a level */
+    return (_bt_searchr(rel, keysz, scankey, bufP, stack));
+}
+
+/*
+ *  _bt_moveright() -- move right in the btree if necessary.
+ *
+ *     When we drop and reacquire a pointer to a page, it is possible that
+ *     the page has changed in the meanwhile.  If this happens, we're
+ *     guaranteed that the page has "split right" -- that is, that any
+ *     data that appeared on the page originally is either on the page
+ *     or strictly to the right of it.
+ *
+ *     This routine decides whether or not we need to move right in the
+ *     tree by examining the high key entry on the page.  If that entry
+ *     is strictly less than one we expect to be on the page, then our
+ *     picture of the page is incorrect and we need to move right.
+ *
+ *     On entry, we have the buffer pinned and a lock of the proper type.
+ *     If we move right, we release the buffer and lock and acquire the
+ *     same on the right sibling.
+ */
+Buffer
+_bt_moveright(Relation rel,
+             Buffer buf,
+             int keysz,
+             ScanKey scankey,
+             int access)
+{
+    Page page;
+    BTPageOpaque opaque;
+    ItemId hikey;
+    ItemId itemid;
+    BlockNumber rblkno;
+    
+    page = BufferGetPage(buf);
+    opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+    
+    /* if we're on a rightmost page, we don't need to move right */
+    if (P_RIGHTMOST(opaque))
+       return (buf);
+    
+    /* by convention, item 0 on non-rightmost pages is the high key */
+    hikey = PageGetItemId(page, P_HIKEY);
+    
+    /*
+     *  If the scan key that brought us to this page is >= the high key
+     *  stored on the page, then the page has split and we need to move
+     *  right.
+     */
+    
+    if (_bt_skeycmp(rel, keysz, scankey, page, hikey,
+                   BTGreaterEqualStrategyNumber)) {
+       
+       /* move right as long as we need to */
+       do {
+           /*
+            *  If this page consists of all duplicate keys (hikey and first
+            *  key on the page have the same value), then we don't need to
+            *  step right.
+            */
+           if (PageGetMaxOffsetNumber(page) > P_HIKEY) {
+               itemid = PageGetItemId(page, P_FIRSTKEY);
+               if (_bt_skeycmp(rel, keysz, scankey, page, itemid,
+                               BTEqualStrategyNumber)) {
+                   /* break is for the "move right" while loop */
+                   break;
+               }
+           }
+           
+           /* step right one page */
+           rblkno = opaque->btpo_next;
+           _bt_relbuf(rel, buf, access);
+           buf = _bt_getbuf(rel, rblkno, access);
+           page = BufferGetPage(buf);
+           opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+           hikey = PageGetItemId(page, P_HIKEY);
+           
+       } while (! P_RIGHTMOST(opaque)
+                && _bt_skeycmp(rel, keysz, scankey, page, hikey,
+                               BTGreaterEqualStrategyNumber));
+    }
+    return (buf);
+}
+
+/*
+ *  _bt_skeycmp() -- compare a scan key to a particular item on a page using
+ *                  a requested strategy (<, <=, =, >=, >).
+ *
+ *     We ignore the unique OIDs stored in the btree item here.  Those
+ *     numbers are intended for use internally only, in repositioning a
+ *     scan after a page split.  They do not impose any meaningful ordering.
+ *
+ *     The comparison is A <op> B, where A is the scan key and B is the
+ *     tuple pointed at by itemid on page.
+ */
+bool
+_bt_skeycmp(Relation rel,
+           Size keysz,
+           ScanKey scankey,
+           Page page,
+           ItemId itemid,
+           StrategyNumber strat)
+{
+    BTItem item;
+    IndexTuple indexTuple;
+    TupleDesc tupDes;
+    ScanKey entry;
+    int i;
+    Datum attrDatum;
+    Datum keyDatum;
+    bool compare;
+    bool isNull;
+    
+    item = (BTItem) PageGetItem(page, itemid);
+    indexTuple = &(item->bti_itup);
+    
+    tupDes = RelationGetTupleDescriptor(rel);
+    
+    /* see if the comparison is true for all of the key attributes */
+    for (i=1; i <= keysz; i++) {
+       
+       entry = &scankey[i-1];
+       attrDatum = index_getattr(indexTuple,
+                                 entry->sk_attno,
+                                 tupDes,
+                                 &isNull);
+       keyDatum  = entry->sk_argument;
+       
+       compare = _bt_invokestrat(rel, i, strat, keyDatum, attrDatum);
+       if (!compare)
+           return (false);
+    }
+    
+    return (true);
+}
+
+/*
+ *  _bt_binsrch() -- Do a binary search for a key on a particular page.
+ *
+ *     The scankey we get has the compare function stored in the procedure
+ *     entry of each data struct.  We invoke this regproc to do the
+ *     comparison for every key in the scankey.  _bt_binsrch() returns
+ *     the OffsetNumber of the first matching key on the page, or the
+ *     OffsetNumber at which the matching key would appear if it were
+ *     on this page.
+ *
+ *     By the time this procedure is called, we're sure we're looking
+ *     at the right page -- don't need to walk right.  _bt_binsrch() has
+ *     no lock or refcount side effects on the buffer.
+ */
+OffsetNumber
+_bt_binsrch(Relation rel,
+           Buffer buf,
+           int keysz,
+           ScanKey scankey,
+           int srchtype)
+{
+    TupleDesc itupdesc;
+    Page page;
+    BTPageOpaque opaque;
+    OffsetNumber low, mid, high;
+    bool match;
+    int result;
+    
+    page = BufferGetPage(buf);
+    opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+    
+    /* by convention, item 0 on any non-rightmost page is the high key */
+    low = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY;
+    
+    high = PageGetMaxOffsetNumber(page);
+    
+    /*
+     *  Since for non-rightmost pages, the zeroeth item on the page is the
+     *  high key, there are two notions of emptiness.  One is if nothing
+     *  appears on the page.  The other is if nothing but the high key does.
+     *  The reason we test high <= low, rather than high == low, is that
+     *  after vacuuming there may be nothing *but* the high key on a page.
+     *  In that case, given the scheme above, low = 1 and high = 0.
+     */
+    
+    if (PageIsEmpty(page) || (! P_RIGHTMOST(opaque) && high <= low))
+       return (low);
+    
+    itupdesc = RelationGetTupleDescriptor(rel);
+    match = false;
+    
+    while ((high - low) > 1) {
+       mid = low + ((high - low) / 2);
+       result = _bt_compare(rel, itupdesc, page, keysz, scankey, mid);
+       
+       if (result > 0)
+           low = mid;
+       else if (result < 0)
+           high = mid - 1;
+       else {
+           match = true;
+           break;
+       }
+    }
+    
+    /* if we found a match, we want to find the first one on the page */
+    if (match) {
+       return (_bt_firsteq(rel, itupdesc, page, keysz, scankey, mid));
+    } else {
+       
+       /*
+        *  We terminated because the endpoints got too close together.  There
+        *  are two cases to take care of.
+        *
+        *  For non-insertion searches on internal pages, we want to point at
+        *  the last key <, or first key =, the scankey on the page.  This
+        *  guarantees that we'll descend the tree correctly.
+        *
+        *  For all other cases, we want to point at the first key >=
+        *  the scankey on the page.  This guarantees that scans and
+        *  insertions will happen correctly.
+        */
+       
+       opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+       if (!(opaque->btpo_flags & BTP_LEAF) && srchtype == BT_DESCENT) {
+           
+           /*
+            *  We want the last key <, or first key ==, the scan key.
+            */
+           
+           result = _bt_compare(rel, itupdesc, page, keysz, scankey, high);
+           
+           if (result == 0) {
+               return (_bt_firsteq(rel, itupdesc, page, keysz, scankey, high));
+           } else if (result > 0) {
+               return (high);
+           } else {
+               return (low);
+           }
+       } else {
+           
+           /* we want the first key >= the scan key */
+           result = _bt_compare(rel, itupdesc, page, keysz, scankey, low);
+           if (result <= 0) {
+               return (low);
+           } else {
+               if (low == high)
+                   return (OffsetNumberNext(low));
+               
+               result = _bt_compare(rel, itupdesc, page, keysz, scankey, high);
+               if (result <= 0)
+                   return (high);
+               else
+                   return (OffsetNumberNext(high));
+           }
+       }
+    }
+}
+
+static OffsetNumber
+_bt_firsteq(Relation rel,
+           TupleDesc itupdesc,
+           Page page,
+           Size keysz,
+           ScanKey scankey,
+           OffsetNumber offnum)
+{
+    BTPageOpaque opaque;
+    OffsetNumber limit;
+    
+    opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+    
+    /* skip the high key, if any */
+    limit = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY;
+    
+    /* walk backwards looking for the first key in the chain of duplicates */
+    while (offnum > limit
+          && _bt_compare(rel, itupdesc, page,
+                         keysz, scankey, OffsetNumberPrev(offnum)) == 0) {
+       offnum = OffsetNumberPrev(offnum);
+    }
+    
+    return (offnum);
+}
+
+/*
+ *  _bt_compare() -- Compare scankey to a particular tuple on the page.
+ *
+ *     This routine returns:
+ *         -1 if scankey < tuple at offnum;
+ *          0 if scankey == tuple at offnum;
+ *         +1 if scankey > tuple at offnum.
+ *
+ *     In order to avoid having to propagate changes up the tree any time
+ *     a new minimal key is inserted, the leftmost entry on the leftmost
+ *     page is less than all possible keys, by definition.
+ */
+static int
+_bt_compare(Relation rel,
+           TupleDesc itupdesc,
+           Page page,
+           int keysz,
+           ScanKey scankey,
+           OffsetNumber offnum)
+{
+    Datum datum;
+    BTItem btitem;
+    ItemId itemid;
+    IndexTuple itup;
+    BTPageOpaque opaque;
+    ScanKey entry;
+    AttrNumber attno;
+    int result;
+    int i;
+    bool null;
+    
+    /*
+     *  If this is a leftmost internal page, and if our comparison is
+     *  with the first key on the page, then the item at that position is
+     *  by definition less than the scan key.
+     */
+    
+    opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+    if (!(opaque->btpo_flags & BTP_LEAF)
+       && P_LEFTMOST(opaque)
+       && offnum == P_HIKEY) {
+       itemid = PageGetItemId(page, offnum);
+       
+       /*
+        *  we just have to believe that this will only be called with
+        *  offnum == P_HIKEY when P_HIKEY is the OffsetNumber of the
+        *  first actual data key (i.e., this is also a rightmost
+        *  page).  there doesn't seem to be any code that implies
+        *  that the leftmost page is normally missing a high key as
+        *  well as the rightmost page.  but that implies that this
+        *  code path only applies to the root -- which seems
+        *  unlikely..
+        */
+       if (! P_RIGHTMOST(opaque)) {
+           elog(WARN, "_bt_compare: invalid comparison to high key");
+       }
+
+       /*
+        *  If the item on the page is equal to the scankey, that's
+        *  okay to admit.  We just can't claim that the first key on
+        *  the page is greater than anything.
+        */
+       
+       if (_bt_skeycmp(rel, keysz, scankey, page, itemid,
+                       BTEqualStrategyNumber)) {
+           return (0);
+       }
+       return (1);
+    }
+    
+    btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum));
+    itup = &(btitem->bti_itup);
+    
+    /*
+     *  The scan key is set up with the attribute number associated with each
+     *  term in the key.  It is important that, if the index is multi-key,
+     *  the scan contain the first k key attributes, and that they be in
+     *  order.  If you think about how multi-key ordering works, you'll
+     *  understand why this is.
+     *
+     *  We don't test for violation of this condition here.
+     */
+    
+    for (i = 1; i <= keysz; i++) {
+       long tmpres;
+       
+       entry = &scankey[i - 1];
+       attno = entry->sk_attno;
+       datum = index_getattr(itup, attno, itupdesc, &null);
+       tmpres = (long) FMGR_PTR2(entry->sk_func, entry->sk_procedure,
+                                 entry->sk_argument, datum);
+       result = tmpres;
+       
+       /* if the keys are unequal, return the difference */
+       if (result != 0)
+           return (result);
+    }
+    
+    /* by here, the keys are equal */
+    return (0);
+}
+
+/*
+ *  _bt_next() -- Get the next item in a scan.
+ *
+ *     On entry, we have a valid currentItemData in the scan, and a
+ *     read lock on the page that contains that item.  We do not have
+ *     the page pinned.  We return the next item in the scan.  On
+ *     exit, we have the page containing the next item locked but not
+ *     pinned.
+ */
+RetrieveIndexResult
+_bt_next(IndexScanDesc scan, ScanDirection dir)
+{
+    Relation rel;
+    Buffer buf;
+    Page page;
+    OffsetNumber offnum;
+    RetrieveIndexResult res;
+    BlockNumber blkno;
+    ItemPointer current;
+    ItemPointer iptr;
+    BTItem btitem;
+    IndexTuple itup;
+    BTScanOpaque so;
+    
+    rel = scan->relation;
+    so = (BTScanOpaque) scan->opaque;
+    current = &(scan->currentItemData);
+    
+    /*
+     *  XXX 10 may 91:  somewhere there's a bug in our management of the
+     *  cached buffer for this scan.  wei discovered it.  the following
+     *  is a workaround so he can work until i figure out what's going on.
+     */
+    
+    if (!BufferIsValid(so->btso_curbuf))
+       so->btso_curbuf = _bt_getbuf(rel, ItemPointerGetBlockNumber(current),
+                                    BT_READ);
+    
+    /* we still have the buffer pinned and locked */
+    buf = so->btso_curbuf;
+    blkno = BufferGetBlockNumber(buf);
+    
+    /* step one tuple in the appropriate direction */
+    if (!_bt_step(scan, &buf, dir))
+       return ((RetrieveIndexResult) NULL);
+    
+    /* by here, current is the tuple we want to return */
+    offnum = ItemPointerGetOffsetNumber(current);
+    page = BufferGetPage(buf);
+    btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum));
+    itup = &btitem->bti_itup;
+    
+    if (_bt_checkqual(scan, itup)) {
+       iptr = (ItemPointer) palloc(sizeof(ItemPointerData));
+       memmove((char *) iptr, (char *) &(itup->t_tid),
+               sizeof(ItemPointerData));
+       res = FormRetrieveIndexResult(current, iptr);
+       
+       /* remember which buffer we have pinned and locked */
+       so->btso_curbuf = buf;
+    } else {
+       ItemPointerSetInvalid(current);
+       so->btso_curbuf = InvalidBuffer;
+       _bt_relbuf(rel, buf, BT_READ);
+       res = (RetrieveIndexResult) NULL;
+    }
+    
+    return (res);
+}
+
+/*
+ *  _bt_first() -- Find the first item in a scan.
+ *
+ *     We need to be clever about the type of scan, the operation it's
+ *     performing, and the tree ordering.  We return the RetrieveIndexResult
+ *     of the first item in the tree that satisfies the qualification
+ *     associated with the scan descriptor.  On exit, the page containing
+ *     the current index tuple is read locked and pinned, and the scan's
+ *     opaque data entry is updated to include the buffer.
+ */
+RetrieveIndexResult
+_bt_first(IndexScanDesc scan, ScanDirection dir)
+{
+    Relation rel;
+    TupleDesc itupdesc;
+    Buffer buf;
+    Page page;
+    BTStack stack;
+    OffsetNumber offnum, maxoff;
+    BTItem btitem;
+    IndexTuple itup;
+    ItemPointer current;
+    ItemPointer iptr;
+    BlockNumber blkno;
+    StrategyNumber strat;
+    RetrieveIndexResult res;
+    RegProcedure proc;
+    int result;
+    BTScanOpaque so;
+    ScanKeyData skdata;
+    
+    /* if we just need to walk down one edge of the tree, do that */
+    if (scan->scanFromEnd)
+       return (_bt_endpoint(scan, dir));
+    
+    rel = scan->relation;
+    itupdesc = RelationGetTupleDescriptor(scan->relation);
+    current = &(scan->currentItemData);
+    so = (BTScanOpaque) scan->opaque;
+    
+    /*
+     *  Okay, we want something more complicated.  What we'll do is use
+     *  the first item in the scan key passed in (which has been correctly
+     *  ordered to take advantage of index ordering) to position ourselves
+     *  at the right place in the scan.
+     */
+    
+    /*
+     *  XXX -- The attribute number stored in the scan key is the attno
+     *        in the heap relation.  We need to transmogrify this into
+     *         the index relation attno here.  For the moment, we have
+     *        hardwired attno == 1.
+     */
+    proc = index_getprocid(rel, 1, BTORDER_PROC);
+    ScanKeyEntryInitialize(&skdata, 0x0, 1, proc,
+                          scan->keyData[0].sk_argument);
+    
+    stack = _bt_search(rel, 1, &skdata, &buf);
+    _bt_freestack(stack);
+    
+    /* find the nearest match to the manufactured scan key on the page */
+    offnum = _bt_binsrch(rel, buf, 1, &skdata, BT_DESCENT);
+    page = BufferGetPage(buf);
+    
+    /*
+     *  This will happen if the tree we're searching is entirely empty,
+     *  or if we're doing a search for a key that would appear on an
+     *  entirely empty internal page.  In either case, there are no
+     *  matching tuples in the index.
+     */
+    
+    if (PageIsEmpty(page)) {
+       ItemPointerSetInvalid(current);
+       so->btso_curbuf = InvalidBuffer;
+       _bt_relbuf(rel, buf, BT_READ);
+       return ((RetrieveIndexResult) NULL);
+    }
+    
+    maxoff = PageGetMaxOffsetNumber(page);
+    
+    if (offnum > maxoff)
+       offnum = maxoff;
+    
+    blkno = BufferGetBlockNumber(buf);
+    ItemPointerSet(current, blkno, offnum);
+    
+    /*
+     *  Now find the right place to start the scan.  Result is the
+     *  value we're looking for minus the value we're looking at
+     *  in the index.
+     */
+    
+    result = _bt_compare(rel, itupdesc, page, 1, &skdata, offnum);
+    strat = _bt_getstrat(rel, 1, scan->keyData[0].sk_procedure);
+    
+    switch (strat) {
+    case BTLessStrategyNumber:
+       if (result <= 0) {
+           do {
+               if (!_bt_twostep(scan, &buf, BackwardScanDirection))
+                   break;
+               
+               offnum = ItemPointerGetOffsetNumber(current);
+               page = BufferGetPage(buf);
+               result = _bt_compare(rel, itupdesc, page, 1, &skdata, offnum);
+           } while (result <= 0);
+           
+           /* if this is true, the key we just looked at is gone */
+           if (result > 0)
+               (void) _bt_twostep(scan, &buf, ForwardScanDirection);
+       }
+       break;
+       
+    case BTLessEqualStrategyNumber:
+       if (result >= 0) {
+           do {
+               if (!_bt_twostep(scan, &buf, ForwardScanDirection))
+                   break;
+               
+               offnum = ItemPointerGetOffsetNumber(current);
+               page = BufferGetPage(buf);
+               result = _bt_compare(rel, itupdesc, page, 1, &skdata, offnum);
+           } while (result >= 0);
+           
+           if (result < 0)
+               (void) _bt_twostep(scan, &buf, BackwardScanDirection);
+       }
+       break;
+       
+    case BTEqualStrategyNumber:
+       if (result != 0) {
+           _bt_relbuf(scan->relation, buf, BT_READ);
+           so->btso_curbuf = InvalidBuffer;
+           ItemPointerSetInvalid(&(scan->currentItemData));
+           return ((RetrieveIndexResult) NULL);
+       }
+       break;
+       
+    case BTGreaterEqualStrategyNumber:
+       if (result < 0) {
+           do {
+               if (!_bt_twostep(scan, &buf, BackwardScanDirection))
+                   break;
+               
+               page = BufferGetPage(buf);
+               offnum = ItemPointerGetOffsetNumber(current);
+               result = _bt_compare(rel, itupdesc, page, 1, &skdata, offnum);
+           } while (result < 0);
+           
+           if (result > 0)
+               (void) _bt_twostep(scan, &buf, ForwardScanDirection);
+       }
+       break;
+       
+    case BTGreaterStrategyNumber:
+       if (result >= 0) {
+           do {
+               if (!_bt_twostep(scan, &buf, ForwardScanDirection))
+                   break;
+               
+               offnum = ItemPointerGetOffsetNumber(current);
+               page = BufferGetPage(buf);
+               result = _bt_compare(rel, itupdesc, page, 1, &skdata, offnum);
+           } while (result >= 0);
+       }
+       break;
+    }
+    
+    /* okay, current item pointer for the scan is right */
+    offnum = ItemPointerGetOffsetNumber(current);
+    page = BufferGetPage(buf);
+    btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum));
+    itup = &btitem->bti_itup;
+    
+    if (_bt_checkqual(scan, itup)) {
+       iptr = (ItemPointer) palloc(sizeof(ItemPointerData));
+       memmove((char *) iptr, (char *) &(itup->t_tid),
+               sizeof(ItemPointerData));
+       res = FormRetrieveIndexResult(current, iptr);
+       pfree(iptr);
+       
+       /* remember which buffer we have pinned */
+       so->btso_curbuf = buf;
+    } else {
+       ItemPointerSetInvalid(current);
+       so->btso_curbuf = InvalidBuffer;
+       _bt_relbuf(rel, buf, BT_READ);
+       res = (RetrieveIndexResult) NULL;
+    }
+    
+    return (res);
+}
+
+/*
+ *  _bt_step() -- Step one item in the requested direction in a scan on
+ *               the tree.
+ *
+ *     If no adjacent record exists in the requested direction, return
+ *     false.  Else, return true and set the currentItemData for the
+ *     scan to the right thing.
+ */
+bool
+_bt_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir)
+{
+    Page page;
+    BTPageOpaque opaque;
+    OffsetNumber offnum, maxoff;
+    OffsetNumber start;
+    BlockNumber blkno;
+    BlockNumber obknum;
+    BTScanOpaque so;
+    ItemPointer current;
+    Relation rel;
+    
+    rel = scan->relation;
+    current = &(scan->currentItemData);
+    offnum = ItemPointerGetOffsetNumber(current);
+    page = BufferGetPage(*bufP);
+    opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+    so = (BTScanOpaque) scan->opaque;
+    maxoff = PageGetMaxOffsetNumber(page);
+    
+    /* get the next tuple */
+    if (ScanDirectionIsForward(dir)) {
+       if (!PageIsEmpty(page) && offnum < maxoff) {
+           offnum = OffsetNumberNext(offnum);
+       } else {
+           
+           /* if we're at end of scan, release the buffer and return */
+           blkno = opaque->btpo_next;
+           if (P_RIGHTMOST(opaque)) {
+               _bt_relbuf(rel, *bufP, BT_READ);
+               ItemPointerSetInvalid(current);
+               *bufP = so->btso_curbuf = InvalidBuffer;
+               return (false);
+           } else {
+               
+               /* walk right to the next page with data */
+               _bt_relbuf(rel, *bufP, BT_READ);
+               for (;;) {
+                   *bufP = _bt_getbuf(rel, blkno, BT_READ);
+                   page = BufferGetPage(*bufP);
+                   opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+                   maxoff = PageGetMaxOffsetNumber(page);
+                   start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY;
+                   
+                   if (!PageIsEmpty(page) && start <= maxoff) {
+                       break;
+                   } else {
+                       blkno = opaque->btpo_next;
+                       _bt_relbuf(rel, *bufP, BT_READ);
+                       if (blkno == P_NONE) {
+                           *bufP = so->btso_curbuf = InvalidBuffer;
+                           ItemPointerSetInvalid(current);
+                           return (false);
+                       }
+                   }
+               }
+               offnum = start;
+           }
+       }
+    } else if (ScanDirectionIsBackward(dir)) {
+       
+       /* remember that high key is item zero on non-rightmost pages */
+       start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY;
+
+       if (offnum > start) {
+           offnum = OffsetNumberPrev(offnum);
+       } else {
+           
+           /* if we're at end of scan, release the buffer and return */
+           blkno = opaque->btpo_prev;
+           if (P_LEFTMOST(opaque)) {
+               _bt_relbuf(rel, *bufP, BT_READ);
+               *bufP = so->btso_curbuf = InvalidBuffer;
+               ItemPointerSetInvalid(current);
+               return (false);
+           } else {
+               
+               obknum = BufferGetBlockNumber(*bufP);
+               
+               /* walk right to the next page with data */
+               _bt_relbuf(rel, *bufP, BT_READ);
+               for (;;) {
+                   *bufP = _bt_getbuf(rel, blkno, BT_READ);
+                   page = BufferGetPage(*bufP);
+                   opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+                   maxoff = PageGetMaxOffsetNumber(page);
+                   
+                   /*
+                    *  If the adjacent page just split, then we may have the
+                    *  wrong block.  Handle this case.  Because pages only
+                    *  split right, we don't have to worry about this failing
+                    *  to terminate.
+                    */
+                   
+                   while (opaque->btpo_next != obknum) {
+                       blkno = opaque->btpo_next;
+                       _bt_relbuf(rel, *bufP, BT_READ);
+                       *bufP = _bt_getbuf(rel, blkno, BT_READ);
+                       page = BufferGetPage(*bufP);
+                       opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+                       maxoff = PageGetMaxOffsetNumber(page);
+                   }
+                   
+                   /* don't consider the high key */
+                   start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY;
+                   
+                   /* anything to look at here? */
+                   if (!PageIsEmpty(page) && maxoff >= start) {
+                       break;
+                   } else {
+                       blkno = opaque->btpo_prev;
+                       obknum = BufferGetBlockNumber(*bufP);
+                       _bt_relbuf(rel, *bufP, BT_READ);
+                       if (blkno == P_NONE) {
+                           *bufP = so->btso_curbuf = InvalidBuffer;
+                           ItemPointerSetInvalid(current);
+                           return (false);
+                       }
+                   }
+               }
+               offnum = maxoff;        /* XXX PageIsEmpty? */
+           }
+       }
+    }
+    blkno = BufferGetBlockNumber(*bufP);
+    so->btso_curbuf = *bufP;
+    ItemPointerSet(current, blkno, offnum);
+    
+    return (true);
+}
+
+/*
+ *  _bt_twostep() -- Move to an adjacent record in a scan on the tree,
+ *                  if an adjacent record exists.
+ *
+ *     This is like _bt_step, except that if no adjacent record exists
+ *     it restores us to where we were before trying the step.  This is
+ *     only hairy when you cross page boundaries, since the page you cross
+ *     from could have records inserted or deleted, or could even split.
+ *     This is unlikely, but we try to handle it correctly here anyway.
+ *
+ *     This routine contains the only case in which our changes to Lehman
+ *     and Yao's algorithm.
+ *
+ *     Like step, this routine leaves the scan's currentItemData in the
+ *     proper state and acquires a lock and pin on *bufP.  If the twostep
+ *     succeeded, we return true; otherwise, we return false.
+ */
+static bool
+_bt_twostep(IndexScanDesc scan, Buffer *bufP, ScanDirection dir)
+{
+    Page page;
+    BTPageOpaque opaque;
+    OffsetNumber offnum, maxoff;
+    OffsetNumber start;
+    ItemPointer current;
+    ItemId itemid;
+    int itemsz;
+    BTItem btitem;
+    BTItem svitem;
+    BlockNumber blkno;
+    
+    blkno = BufferGetBlockNumber(*bufP);
+    page = BufferGetPage(*bufP);
+    opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+    maxoff = PageGetMaxOffsetNumber(page);
+    current = &(scan->currentItemData);
+    offnum = ItemPointerGetOffsetNumber(current);
+    
+    start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY;
+    
+    /* if we're safe, just do it */
+    if (ScanDirectionIsForward(dir) && offnum < maxoff) { /* XXX PageIsEmpty? */
+       ItemPointerSet(current, blkno, OffsetNumberNext(offnum));
+       return (true);
+    } else if (ScanDirectionIsBackward(dir) && offnum > start) {
+       ItemPointerSet(current, blkno, OffsetNumberPrev(offnum));
+       return (true);
+    }
+    
+    /* if we've hit end of scan we don't have to do any work */
+    if (ScanDirectionIsForward(dir) && P_RIGHTMOST(opaque)) {
+       return (false);
+    } else if (ScanDirectionIsBackward(dir) && P_LEFTMOST(opaque)) {
+       return (false);
+    }
+    
+    /*
+     *  Okay, it's off the page; let _bt_step() do the hard work, and we'll
+     *  try to remember where we were.  This is not guaranteed to work; this
+     *  is the only place in the code where concurrency can screw us up,
+     *  and it's because we want to be able to move in two directions in
+     *  the scan.
+     */
+    
+    itemid = PageGetItemId(page, offnum);
+    itemsz = ItemIdGetLength(itemid);
+    btitem = (BTItem) PageGetItem(page, itemid);
+    svitem = (BTItem) palloc(itemsz);
+    memmove((char *) svitem, (char *) btitem, itemsz);
+    
+    if (_bt_step(scan, bufP, dir)) {
+       pfree(svitem);
+       return (true);
+    }
+    
+    /* try to find our place again */
+    *bufP = _bt_getbuf(scan->relation, blkno, BT_READ);
+    page = BufferGetPage(*bufP);
+    maxoff = PageGetMaxOffsetNumber(page);
+    
+    while (offnum <= maxoff) {
+       itemid = PageGetItemId(page, offnum);
+       btitem = (BTItem) PageGetItem(page, itemid);
+       if (btitem->bti_oid == svitem->bti_oid) {
+           pfree(svitem);
+           ItemPointerSet(current, blkno, offnum);
+           return (false);
+       }
+    }
+    
+    /*
+     *  XXX crash and burn -- can't find our place.  We can be a little
+     *  smarter -- walk to the next page to the right, for example, since
+     *  that's the only direction that splits happen in.  Deletions screw
+     *  us up less often since they're only done by the vacuum daemon.
+     */
+    
+    elog(WARN, "btree synchronization error:  concurrent update botched scan");
+    
+    return (false);
+}
+
+/*
+ *  _bt_endpoint() -- Find the first or last key in the index.
+ */
+static RetrieveIndexResult
+_bt_endpoint(IndexScanDesc scan, ScanDirection dir)
+{
+    Relation rel;
+    Buffer buf;
+    Page page;
+    BTPageOpaque opaque;
+    ItemPointer current;
+    ItemPointer iptr;
+    OffsetNumber offnum, maxoff;
+    OffsetNumber start;
+    BlockNumber blkno;
+    BTItem btitem;
+    IndexTuple itup;
+    BTScanOpaque so;
+    RetrieveIndexResult res;
+    
+    rel = scan->relation;
+    current = &(scan->currentItemData);
+    
+    buf = _bt_getroot(rel, BT_READ);
+    blkno = BufferGetBlockNumber(buf);
+    page = BufferGetPage(buf);
+    opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+    
+    for (;;) {
+       if (opaque->btpo_flags & BTP_LEAF)
+           break;
+       
+       if (ScanDirectionIsForward(dir)) {
+           offnum = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY;
+       } else {
+           offnum = PageGetMaxOffsetNumber(page);
+       }
+       
+       btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum));
+       itup = &(btitem->bti_itup);
+       
+       blkno = ItemPointerGetBlockNumber(&(itup->t_tid));
+       
+       _bt_relbuf(rel, buf, BT_READ);
+       buf = _bt_getbuf(rel, blkno, BT_READ);
+       page = BufferGetPage(buf);
+       opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+       
+       /*
+        *  Race condition: If the child page we just stepped onto is
+        *  in the process of being split, we need to make sure we're
+        *  all the way at the right edge of the tree.  See the paper
+        *  by Lehman and Yao.
+        */
+       
+       if (ScanDirectionIsBackward(dir) && ! P_RIGHTMOST(opaque)) {
+           do {
+               blkno = opaque->btpo_next;
+               _bt_relbuf(rel, buf, BT_READ);
+               buf = _bt_getbuf(rel, blkno, BT_READ);
+               page = BufferGetPage(buf);
+               opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+           } while (! P_RIGHTMOST(opaque));
+       }
+    }
+    
+    /* okay, we've got the {left,right}-most page in the tree */
+    maxoff = PageGetMaxOffsetNumber(page);
+    
+    if (ScanDirectionIsForward(dir)) {
+       if (PageIsEmpty(page)) {
+           maxoff = FirstOffsetNumber;
+       } else {
+           maxoff = PageGetMaxOffsetNumber(page);
+       }
+       start = P_RIGHTMOST(opaque) ? P_HIKEY : P_FIRSTKEY;
+       
+       if (PageIsEmpty(page) || start > maxoff) {
+           ItemPointerSet(current, blkno, maxoff);
+           if (!_bt_step(scan, &buf, BackwardScanDirection))
+               return ((RetrieveIndexResult) NULL);
+           
+           start = ItemPointerGetOffsetNumber(current);
+           page = BufferGetPage(buf);
+       } else {
+           ItemPointerSet(current, blkno, start);
+       }
+    } else if (ScanDirectionIsBackward(dir)) {
+       if (PageIsEmpty(page)) {
+           ItemPointerSet(current, blkno, FirstOffsetNumber);
+           if (!_bt_step(scan, &buf, ForwardScanDirection))
+               return ((RetrieveIndexResult) NULL);
+           
+           start = ItemPointerGetOffsetNumber(current);
+           page = BufferGetPage(buf);
+       } else {
+           start = PageGetMaxOffsetNumber(page);
+           ItemPointerSet(current, blkno, start);
+       }
+    } else {
+       elog(WARN, "Illegal scan direction %d", dir);
+    }
+    
+    btitem = (BTItem) PageGetItem(page, PageGetItemId(page, start));
+    itup = &(btitem->bti_itup);
+    
+    /* see if we picked a winner */
+    if (_bt_checkqual(scan, itup)) {
+       iptr = (ItemPointer) palloc(sizeof(ItemPointerData));
+       memmove((char *) iptr, (char *) &(itup->t_tid),
+               sizeof(ItemPointerData));
+       res = FormRetrieveIndexResult(current, iptr);
+       
+       /* remember which buffer we have pinned */
+       so = (BTScanOpaque) scan->opaque;
+       so->btso_curbuf = buf;
+    } else {
+       _bt_relbuf(rel, buf, BT_READ);
+       res = (RetrieveIndexResult) NULL;
+    }
+    
+    return (res);
+}
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
new file mode 100644 (file)
index 0000000..9cc11a8
--- /dev/null
@@ -0,0 +1,1196 @@
+/*-------------------------------------------------------------------------
+ * btsort.c--
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Id$
+ *
+ * NOTES
+ *
+ * what we do is:
+ * - generate a set of initial one-block runs, distributed round-robin
+ *   between the output tapes.
+ * - for each pass,
+ *   - swap input and output tape sets, rewinding both and truncating
+ *     the output tapes.
+ *   - merge the current run in each input tape to the current output
+ *     tape.
+ *     - when each input run has been exhausted, switch to another output
+ *       tape and start processing another run.
+ * - when we have fewer runs than tapes, we know we are ready to start
+ *   merging into the btree leaf pages. 
+ * - every time we complete a level of the btree, we can construct the
+ *   next level up.  when we have only one page on a level, it can be
+ *   attached to the btree metapage and we are done.
+ *
+ * conventions:
+ * - external interface routines take in and return "void *" for their
+ *   opaque handles.  this is for modularity reasons (i prefer not to
+ *   export these structures without good reason).
+ *
+ * this code is moderately slow (~10% slower) compared to the regular
+ * btree (insertion) build code on sorted or well-clustered data.  on
+ * random data, however, the insertion build code is unusable -- the
+ * difference on a 60MB heap is a factor of 15 because the random
+ * probes into the btree thrash the buffer pool.
+ *
+ * this code currently packs the pages to 100% of capacity.  this is
+ * not wise, since *any* insertion will cause splitting.  filling to
+ * something like the standard 70% steady-state load factor for btrees
+ * would probably be better.
+ *
+ * somebody desperately needs to figure out how to do a better job of
+ * balancing the merge passes -- the fan-in on the final merges can be
+ * pretty poor, which is bad for performance.
+ *-------------------------------------------------------------------------
+ */
+
+#include <stdio.h>
+
+#include "c.h"
+
+#include "access/nbtree.h"
+
+#include "storage/bufmgr.h"
+#include "storage/fd.h"
+#include "utils/rel.h"
+#include "utils/palloc.h"
+#include "utils/elog.h"
+
+/*#define FASTBUILD_DEBUG*/ /* turn on debugging output */
+
+#define FASTBUILD
+
+#ifdef FASTBUILD
+
+#define        MAXTAPES        (7)
+#define        TAPEBLCKSZ      (BLCKSZ << 2)
+#define        TAPETEMP        "pg_btsortXXXXXX"
+
+
+/*-------------------------------------------------------------------------
+ * sorting comparison routine - returns {-1,0,1} depending on whether
+ * the key in the left BTItem is {<,=,>} the key in the right BTItem.
+ *
+ * we want to use _bt_isortcmp as a comparison function for qsort(3),
+ * but it needs extra arguments, so we "pass them in" as global
+ * variables.  ick.  fortunately, they are the same throughout the
+ * build, so we need do this only once.  this is why you must call
+ * _bt_isortcmpinit before the call to qsort(3).
+ *
+ * a NULL BTItem is always assumed to be greater than any actual
+ * value; our heap routines (see below) assume that the smallest
+ * element in the heap is returned.  that way, NULL values from the
+ * exhausted tapes can sift down to the bottom of the heap.  in point
+ * of fact we just don't replace the elements of exhausted tapes, but
+ * what the heck.
+ * *-------------------------------------------------------------------------
+ */
+static Relation _bt_sortrel;
+
+static void
+_bt_isortcmpinit(Relation index)
+{
+    _bt_sortrel = index;
+}
+
+static int
+_bt_isortcmp(BTItem *bti1p, BTItem *bti2p)
+{
+    BTItem bti1 = *bti1p;
+    BTItem bti2 = *bti2p;
+
+    if (bti1 == (BTItem) NULL) {
+       if (bti2 == (BTItem) NULL) {
+           return(0);  /* 1 = 2 */
+       }
+       return(1);      /* 1 > 2 */
+    } else if (bti2 == (BTItem) NULL) {
+       return(-1);     /* 1 < 2 */
+    } else if (_bt_itemcmp(_bt_sortrel, 1, bti1, bti2,
+                          BTGreaterStrategyNumber)) {
+       return(1);      /* 1 > 2 */
+    } else if (_bt_itemcmp(_bt_sortrel, 1, bti2, bti1,
+                          BTGreaterStrategyNumber)) {
+       return(-1);     /* 1 < 2 */
+    }
+    return(0);         /* 1 = 2 */
+}
+
+/*-------------------------------------------------------------------------
+ * priority queue methods
+ *
+ * these were more-or-less lifted from the heap section of the 1984
+ * edition of gonnet's book on algorithms and data structures.  they
+ * are coded so that the smallest element in the heap is returned (we
+ * use them for merging sorted runs).
+ *
+ * XXX these probably ought to be generic library functions.
+ *-------------------------------------------------------------------------
+ */
+
+typedef struct {
+    int                btpqe_tape;     /* tape identifier */
+    BTItem     btpqe_item;     /* pointer to BTItem in tape buffer */
+} BTPriQueueElem;
+
+#define        MAXELEM MAXTAPES
+typedef struct {
+    int                        btpq_nelem;
+    BTPriQueueElem     btpq_queue[MAXELEM];
+    Relation           btpq_rel;
+} BTPriQueue;
+
+/* be sure to call _bt_isortcmpinit first */
+#define GREATER(a, b) \
+    (_bt_isortcmp(&((a)->btpqe_item), &((b)->btpqe_item)) > 0)
+
+static void
+_bt_pqsift(BTPriQueue *q, int parent)
+{
+    int child;
+    BTPriQueueElem e;
+
+    for (child = parent * 2 + 1;
+        child < q->btpq_nelem;
+        child = parent * 2 + 1) {
+       if (child < q->btpq_nelem - 1) {
+           if (GREATER(&(q->btpq_queue[child]), &(q->btpq_queue[child+1]))) {
+               ++child;
+           }
+       }
+       if (GREATER(&(q->btpq_queue[parent]), &(q->btpq_queue[child]))) {
+           e = q->btpq_queue[child];                           /* struct = */
+           q->btpq_queue[child] = q->btpq_queue[parent];       /* struct = */
+           q->btpq_queue[parent] = e;                          /* struct = */
+           parent = child;
+       } else {
+           parent = child + 1;
+       }
+    }
+}
+
+static int
+_bt_pqnext(BTPriQueue *q, BTPriQueueElem *e)
+{
+    if (q->btpq_nelem < 1) {   /* already empty */
+       return(-1);
+    }
+    *e = q->btpq_queue[0];                                     /* struct = */
+
+    if (--q->btpq_nelem < 1) { /* now empty, don't sift */
+       return(0);
+    }
+    q->btpq_queue[0] = q->btpq_queue[q->btpq_nelem];           /* struct = */
+    _bt_pqsift(q, 0);
+    return(0);
+}
+
+static void
+_bt_pqadd(BTPriQueue *q, BTPriQueueElem *e)
+{
+    int child, parent;
+
+    if (q->btpq_nelem >= MAXELEM) {
+       elog(WARN, "_bt_pqadd: queue overflow");
+    }
+
+    child = q->btpq_nelem++;
+    while (child > 0) {
+       parent = child / 2;
+       if (GREATER(e, &(q->btpq_queue[parent]))) {
+           break;
+       } else {
+           q->btpq_queue[child] = q->btpq_queue[parent];       /* struct = */
+           child = parent;
+       }
+    }
+
+    q->btpq_queue[child] = *e;                                 /* struct = */
+}
+
+/*-------------------------------------------------------------------------
+ * tape methods
+ *-------------------------------------------------------------------------
+ */
+
+#define        BTITEMSZ(btitem) \
+    ((btitem) ? \
+     (IndexTupleDSize((btitem)->bti_itup) + \
+      (sizeof(BTItemData) - sizeof(IndexTupleData))) : \
+     0)
+#define        SPCLEFT(tape) \
+    (sizeof((tape)->bttb_data) - (tape)->bttb_top)
+#define        EMPTYTAPE(tape) \
+    ((tape)->bttb_ntup <= 0)
+#define        BTTAPEMAGIC     0x19660226
+
+/*
+ * this is what we use to shovel BTItems in and out of memory.  it's
+ * bigger than a standard block because we are doing a lot of strictly
+ * sequential i/o.  this is obviously something of a tradeoff since we
+ * are potentially reading a bunch of zeroes off of disk in many
+ * cases.
+ *
+ * BTItems are packed in and DOUBLEALIGN'd.
+ *
+ * the fd should not be going out to disk, strictly speaking, but it's
+ * the only thing like that so i'm not going to worry about wasting a
+ * few bytes.
+ */
+typedef struct {
+    int                bttb_magic;     /* magic number */
+    int                bttb_fd;        /* file descriptor */
+    int                bttb_top;       /* top of free space within bttb_data */
+    short      bttb_ntup;      /* number of tuples in this block */
+    short      bttb_eor;       /* End-Of-Run marker */
+    char       bttb_data[TAPEBLCKSZ - 2 * sizeof(double)];
+} BTTapeBlock;
+
+
+/*
+ * reset the tape header for its next use without doing anything to
+ * the physical tape file.  (setting bttb_top to 0 makes the block
+ * empty.)
+ */
+static void
+_bt_tapereset(BTTapeBlock *tape)
+{
+    tape->bttb_eor = 0;
+    tape->bttb_top = 0;
+    tape->bttb_ntup = 0;
+}
+
+/*
+ * rewind the physical tape file.
+ */
+static void
+_bt_taperewind(BTTapeBlock *tape)
+{
+    (void) FileSeek(tape->bttb_fd, 0, SEEK_SET);
+}
+
+/*
+ * destroy the contents of the physical tape file without destroying
+ * the tape data structure or removing the physical tape file.
+ *
+ * we use the VFD version of ftruncate(2) to do this rather than
+ * unlinking and recreating the file.  you still have to wait while
+ * the OS frees up all of the file system blocks and stuff, but at
+ * least you don't have to delete and reinsert the directory entries.
+ */
+static void
+_bt_tapeclear(BTTapeBlock *tape)
+{
+    /* blow away the contents of the old file */
+    _bt_taperewind(tape);
+#if 0
+    FileSync(tape->bttb_fd);
+#endif
+    FileTruncate(tape->bttb_fd, 0);
+
+    /* reset the buffer */
+    _bt_tapereset(tape);
+}
+
+/*
+ * create a new BTTapeBlock, allocating memory for the data structure
+ * as well as opening a physical tape file.
+ */
+static BTTapeBlock *
+_bt_tapecreate(char *fname)
+{
+    BTTapeBlock *tape = (BTTapeBlock *) palloc(sizeof(BTTapeBlock));
+
+    if (tape == (BTTapeBlock *) NULL) {
+       elog(WARN, "_bt_tapecreate: out of memory");
+    }
+
+    tape->bttb_magic = BTTAPEMAGIC;
+
+    tape->bttb_fd = FileNameOpenFile(fname, O_RDWR|O_CREAT|O_TRUNC, 0600);
+    Assert(tape->bttb_fd >= 0);
+
+    /* initialize the buffer */
+    _bt_tapereset(tape);
+
+    return(tape);
+}
+
+/*
+ * destroy the BTTapeBlock structure and its physical tape file.
+ */
+static void
+_bt_tapedestroy(BTTapeBlock *tape)
+{
+    FileUnlink(tape->bttb_fd);
+    pfree((void *) tape);
+}
+
+/*
+ * flush the tape block to the file, marking End-Of-Run if requested.
+ */
+static void
+_bt_tapewrite(BTTapeBlock *tape, int eor)
+{
+    tape->bttb_eor = eor;
+    FileWrite(tape->bttb_fd, (char*)tape, TAPEBLCKSZ);
+    _bt_tapereset(tape);
+}
+
+/*
+ * read a tape block from the file, overwriting the current contents
+ * of the buffer.
+ *
+ * returns:
+ * - 0 if there are no more blocks in the tape or in this run (call
+ *   _bt_tapereset to clear the End-Of-Run marker)
+ * - 1 if a valid block was read
+ */
+static int
+_bt_taperead(BTTapeBlock *tape)
+{
+    int fd;
+    int nread;
+
+    if (tape->bttb_eor) {
+       return(0);              /* we are at End-Of-Run */
+    }
+
+    /*
+     * we're clobbering the old tape block, but we do need to save the
+     * VFD (the one in the block we're reading is bogus).
+     */
+    fd = tape->bttb_fd;
+    nread = FileRead(fd, (char*) tape, TAPEBLCKSZ);
+    tape->bttb_fd = fd;
+
+    if (nread != TAPEBLCKSZ) {
+       Assert(nread == 0);     /* we are at EOF */
+       return(0);
+    }
+    Assert(tape->bttb_magic == BTTAPEMAGIC);
+    return(1);
+}
+
+/*
+ * get the next BTItem from a tape block.
+ *
+ * returns:
+ * - NULL if we have run out of BTItems
+ * - a pointer to the BTItemData in the block otherwise
+ *
+ * side effects:
+ * - sets 'pos' to the current position within the block.
+ */
+static BTItem
+_bt_tapenext(BTTapeBlock *tape, char **pos)
+{
+    Size itemsz;
+    BTItem bti;
+
+    if (*pos >= tape->bttb_data + tape->bttb_top) {
+       return((BTItem) NULL);
+    }
+    bti = (BTItem) *pos;
+    itemsz = BTITEMSZ(bti);
+    *pos += DOUBLEALIGN(itemsz);
+    return(bti);
+}
+
+/*
+ * copy a BTItem into a tape block.
+ *
+ * assumes that we have already checked to see if the block has enough
+ * space for the item.
+ *
+ * side effects:
+ *
+ * - advances the 'top' pointer in the tape block header to point to
+ * the beginning of free space.
+ */
+static void
+_bt_tapeadd(BTTapeBlock *tape, BTItem item, int itemsz)
+{
+    (void) memcpy(tape->bttb_data + tape->bttb_top, item, itemsz);
+    ++tape->bttb_ntup;
+    tape->bttb_top += DOUBLEALIGN(itemsz);
+}
+
+/*-------------------------------------------------------------------------
+ * spool methods
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * this structure holds the bookkeeping for a simple balanced multiway
+ * merge.  (polyphase merging is hairier than i want to get into right
+ * now, and i don't see why i have to care how many "tapes" i use
+ * right now.  though if psort was in a condition that i could hack it
+ * to do this, you bet i would.)
+ */
+typedef struct {
+    int                bts_ntapes;
+    int                bts_tape;
+    BTTapeBlock        **bts_itape;    /* input tape blocks */
+    BTTapeBlock        **bts_otape;    /* output tape blocks */
+} BTSpool;
+
+/*
+ * create and initialize a spool structure, including the underlying
+ * files.
+ */
+void *
+_bt_spoolinit(Relation index, int ntapes)
+{
+    char *mktemp();
+
+    BTSpool *btspool = (BTSpool *) palloc(sizeof(BTSpool));
+    int i;
+    char *fname = (char *) palloc(sizeof(TAPETEMP) + 1);
+
+    if (btspool == (BTSpool *) NULL || fname == (char *) NULL) {
+       elog(WARN, "_bt_spoolinit: out of memory");
+    }
+    (void) memset((char *) btspool, 0, sizeof(BTSpool));
+    btspool->bts_ntapes = ntapes;
+    btspool->bts_tape = 0;
+
+    btspool->bts_itape =
+       (BTTapeBlock **) palloc(sizeof(BTTapeBlock *) * ntapes);
+    btspool->bts_otape =
+       (BTTapeBlock **) palloc(sizeof(BTTapeBlock *) * ntapes);
+    if (btspool->bts_itape == (BTTapeBlock **) NULL ||
+       btspool->bts_otape == (BTTapeBlock **) NULL) {
+       elog(WARN, "_bt_spoolinit: out of memory");
+    }
+
+    for (i = 0; i < ntapes; ++i) {
+       btspool->bts_itape[i] =
+           _bt_tapecreate(mktemp(strcpy(fname, TAPETEMP)));
+       btspool->bts_otape[i] =
+           _bt_tapecreate(mktemp(strcpy(fname, TAPETEMP)));
+    }
+    pfree((void *) fname);
+
+    _bt_isortcmpinit(index);
+
+    return((void *) btspool);
+}
+
+/*
+ * clean up a spool structure and its substructures.
+ */
+void
+_bt_spooldestroy(void *spool)
+{
+    BTSpool *btspool = (BTSpool *) spool;
+    int i;
+
+    for (i = 0; i < btspool->bts_ntapes; ++i) {
+       _bt_tapedestroy(btspool->bts_otape[i]);
+       _bt_tapedestroy(btspool->bts_itape[i]);
+    }
+    pfree((void *) btspool);
+}
+
+/*
+ * flush out any dirty output tape blocks
+ */
+static void
+_bt_spoolflush(BTSpool *btspool)
+{
+    int i;
+
+    for (i = 0; i < btspool->bts_ntapes; ++i) {
+       if (!EMPTYTAPE(btspool->bts_otape[i])) {
+           _bt_tapewrite(btspool->bts_otape[i], 1);
+       }
+    }
+}
+
+/*
+ * swap input tapes and output tapes by swapping their file
+ * descriptors.  additional preparation for the next merge pass
+ * includes rewinding the new input tapes and clearing out the new
+ * output tapes.
+ */
+static void
+_bt_spoolswap(BTSpool *btspool)
+{
+    File tmpfd;
+    BTTapeBlock *itape;
+    BTTapeBlock *otape;
+    int i;
+
+    for (i = 0; i < btspool->bts_ntapes; ++i) {
+       itape = btspool->bts_itape[i];
+       otape = btspool->bts_otape[i];
+
+       /*
+        * swap the input and output VFDs.
+        */
+       tmpfd = itape->bttb_fd;
+       itape->bttb_fd = otape->bttb_fd;
+       otape->bttb_fd = tmpfd;
+
+       /*
+        * rewind the new input tape.
+        */
+       _bt_taperewind(itape);
+       _bt_tapereset(itape);
+
+       /*
+        * clear the new output tape -- it's ok to throw away the old
+        * inputs.
+        */
+       _bt_tapeclear(otape);
+    }    
+}
+
+/*-------------------------------------------------------------------------
+ * sorting routines
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * spool 'btitem' into an initial run.  as tape blocks are filled, the
+ * block BTItems are qsorted and written into some output tape (it
+ * doesn't matter which; we go round-robin for simplicity).  the
+ * initial runs are therefore always just one block.
+ */
+void
+_bt_spool(Relation index, BTItem btitem, void *spool)
+{
+    BTSpool *btspool = (BTSpool *) spool;
+    BTTapeBlock *itape;
+    Size itemsz;
+
+    itape = btspool->bts_itape[btspool->bts_tape];
+    itemsz = BTITEMSZ(btitem);
+    itemsz = DOUBLEALIGN(itemsz);
+
+    /*
+     * if this buffer is too full for this BTItemData, or if we have
+     * run out of BTItems, we need to sort the buffer and write it
+     * out.  in this case, the BTItemData will go into the next tape's
+     * buffer.
+     */
+    if (btitem == (BTItem) NULL || SPCLEFT(itape) < itemsz) {
+       BTItem *parray;
+       BTTapeBlock *otape;
+       BTItem bti;
+       char *pos;
+       int btisz;
+       int i;
+
+       /*
+        * build an array of pointers to the BTItemDatas on the input
+        * block.
+        */
+       parray = (BTItem *) palloc(itape->bttb_ntup * sizeof(BTItem));
+       if (parray == (BTItem *) NULL) {
+           elog(WARN, "_bt_spool: out of memory");
+       }
+       pos = itape->bttb_data;
+       for (i = 0; i < itape->bttb_ntup; ++i) {
+           parray[i] = _bt_tapenext(itape, &pos);
+       }
+
+       /*
+        * qsort the pointer array.
+        */
+       _bt_isortcmpinit(index);
+       qsort((void *) parray, itape->bttb_ntup, sizeof(BTItem), _bt_isortcmp);
+
+       /*
+        * write the spooled run into the output tape.  we copy the
+        * BTItemDatas in the order dictated by the sorted array of
+        * BTItems, not the original order.
+        *
+        * (since everything was DOUBLEALIGN'd and is all on a single
+        * page, everything had *better* still fit on one page..)
+        */
+       otape = btspool->bts_otape[btspool->bts_tape];
+       for (i = 0; i < itape->bttb_ntup; ++i) {
+           bti = parray[i];
+           btisz = BTITEMSZ(bti);
+           btisz = DOUBLEALIGN(btisz);
+           _bt_tapeadd(otape, bti, btisz);
+#ifdef FASTBUILD_DEBUG
+           {
+               bool isnull;
+               Datum d = index_getattr(&(bti->bti_itup), 1,
+                                       RelationGetTupleDescriptor(index),
+                                       &isnull);
+               printf("_bt_spool: inserted <%x> into output tape %d\n",
+                      d, btspool->bts_tape);
+           }
+#endif /* FASTBUILD_DEBUG */
+       }
+
+       /*
+        * the initial runs are always single tape blocks.  flush the
+        * output block, marking End-Of-Run.
+        */
+       _bt_tapewrite(otape, 1);
+
+       /*
+        * reset the input buffer for the next run.  we don't have to
+        * write it out or anything -- we only use it to hold the
+        * unsorted BTItemDatas, the output tape contains all the
+        * sorted stuff.
+        *
+        * changing bts_tape changes the output tape and input tape;
+        * we change itape for the code below.
+        */
+       _bt_tapereset(itape);
+       btspool->bts_tape = (btspool->bts_tape + 1) % btspool->bts_ntapes;
+       itape = btspool->bts_itape[btspool->bts_tape];
+
+       /*
+        * destroy the pointer array.
+        */
+       pfree((void *) parray);
+    }
+
+    /* insert this item into the current buffer */
+    if (btitem != (BTItem) NULL) {
+       _bt_tapeadd(itape, btitem, itemsz);
+    }
+}
+
+/*
+ * allocate a new, clean btree page, not linked to any siblings.
+ */
+static void
+_bt_blnewpage(Relation index, Buffer *buf, Page *page, int flags)
+{
+    BTPageOpaque opaque;
+
+    *buf = _bt_getbuf(index, P_NEW, BT_WRITE);
+    *page = BufferGetPage(*buf);
+    _bt_pageinit(*page, BufferGetPageSize(*buf));
+    opaque = (BTPageOpaque) PageGetSpecialPointer(*page);
+    opaque->btpo_prev = opaque->btpo_next = P_NONE;
+    opaque->btpo_flags = flags;
+}
+
+/*
+ * slide an array of ItemIds back one slot (from P_FIRSTKEY to
+ * P_HIKEY).  we need to do this when we discover that we have built
+ * an ItemId array in what has turned out to be a P_RIGHTMOST page.
+ */
+static void
+_bt_slideleft(Relation index, Buffer buf, Page page)
+{
+    OffsetNumber off;
+    OffsetNumber maxoff;
+    ItemId previi;
+    ItemId thisii;
+
+    maxoff = PageGetMaxOffsetNumber(page);
+    previi = PageGetItemId(page, P_HIKEY);
+    for (off = P_FIRSTKEY; off <= maxoff; off = OffsetNumberNext(off)) {
+       thisii = PageGetItemId(page, off);
+       *previi = *thisii;
+       previi = thisii;
+    }
+    ((PageHeader) page)->pd_lower -= sizeof(ItemIdData);
+}
+
+typedef struct {
+    Buffer             btps_buf;
+    Page               btps_page;
+    BTItem             btps_lastbti;
+    OffsetNumber       btps_lastoff;
+    OffsetNumber       btps_firstoff;
+} BTPageState;
+
+/*
+ * add an item to a disk page from a merge tape block.
+ *
+ * we must be careful to observe the following restrictions, placed
+ * upon us by the conventions in nbtsearch.c:
+ * - rightmost pages start data items at P_HIKEY instead of at
+ *   P_FIRSTKEY.
+ * - duplicates cannot be split among pages unless the chain of
+ *   duplicates starts at the first data item.
+ *
+ * a leaf page being built looks like:
+ *
+ * +----------------+---------------------------------+
+ * | PageHeaderData | linp0 linp1 linp2 ...           |
+ * +-----------+----+---------------------------------+
+ * | ... linpN |                  ^ first             |
+ * +-----------+--------------------------------------+
+ * |     ^ last                                       |
+ * |                                                  |
+ * |               v last                             |
+ * +-------------+------------------------------------+
+ * |             | itemN ...                          |
+ * +-------------+------------------+-----------------+
+ * |          ... item3 item2 item1 | "special space" |
+ * +--------------------------------+-----------------+
+ *                      ^ first
+ *
+ * contrast this with the diagram in bufpage.h; note the mismatch
+ * between linps and items.  this is because we reserve linp0 as a
+ * placeholder for the pointer to the "high key" item; when we have
+ * filled up the page, we will set linp0 to point to itemN and clear
+ * linpN.
+ *
+ * 'last' pointers indicate the last offset/item added to the page.
+ * 'first' pointers indicate the first offset/item that is part of a
+ * chain of duplicates extending from 'first' to 'last'.
+ *
+ * if all keys are unique, 'first' will always be the same as 'last'.
+ */
+static void
+_bt_buildadd(Relation index, BTPageState *state, BTItem bti, int flags)
+{
+    Buffer nbuf;
+    Page npage;
+    BTItem last_bti;
+    OffsetNumber first_off;
+    OffsetNumber last_off;
+    OffsetNumber off;
+    Size pgspc;
+    Size btisz;
+
+    nbuf = state->btps_buf;
+    npage = state->btps_page;
+    first_off = state->btps_firstoff;
+    last_off = state->btps_lastoff;
+    last_bti = state->btps_lastbti;
+
+    pgspc = PageGetFreeSpace(npage);
+    btisz = BTITEMSZ(bti);
+    btisz = DOUBLEALIGN(btisz);
+    if (pgspc < btisz) {
+       Buffer obuf = nbuf;
+       Page opage = npage;
+       OffsetNumber o, n;
+       ItemId ii;
+       ItemId hii;
+
+       _bt_blnewpage(index, &nbuf, &npage, flags);
+
+       /*
+        * if 'last' is part of a chain of duplicates that does not
+        * start at the beginning of the old page, the entire chain is
+        * copied to the new page; we delete all of the duplicates
+        * from the old page except the first, which becomes the high
+        * key item of the old page.
+        *
+        * if the chain starts at the beginning of the page or there
+        * is no chain ('first' == 'last'), we need only copy 'last'
+        * to the new page.  again, 'first' (== 'last') becomes the
+        * high key of the old page.
+        *
+        * note that in either case, we copy at least one item to the
+        * new page, so 'last_bti' will always be valid.  'bti' will
+        * never be the first data item on the new page.
+        */
+       if (first_off == P_FIRSTKEY) {
+           Assert(last_off != P_FIRSTKEY);
+           first_off = last_off;
+       }
+       for (o = first_off, n = P_FIRSTKEY;
+            o <= last_off;
+            o = OffsetNumberNext(o), n = OffsetNumberNext(n)) {
+           ii = PageGetItemId(opage, o);
+           (void) PageAddItem(npage, PageGetItem(opage, ii),
+                              ii->lp_len, n, LP_USED);
+#ifdef FASTBUILD_DEBUG
+           {
+               bool isnull;
+               BTItem tmpbti =
+                   (BTItem) PageGetItem(npage, PageGetItemId(npage, n));
+               Datum d = index_getattr(&(tmpbti->bti_itup), 1,
+                                       RelationGetTupleDescriptor(index),
+                                       &isnull);
+               printf("_bt_buildadd: moved <%x> to offset %d\n",
+                      d, n);
+           }
+#endif /* FASTBUILD_DEBUG */
+       }
+       for (o = last_off; o > first_off; o = OffsetNumberPrev(o)) {
+           PageIndexTupleDelete(opage, o);
+       }
+       hii = PageGetItemId(opage, P_HIKEY);
+       ii = PageGetItemId(opage, first_off);
+       *hii = *ii;
+       ii->lp_flags &= ~LP_USED;
+       ((PageHeader) opage)->pd_lower -= sizeof(ItemIdData);
+
+       first_off = P_FIRSTKEY;
+       last_off = PageGetMaxOffsetNumber(npage);
+       last_bti = (BTItem) PageGetItem(npage, PageGetItemId(npage, last_off));
+
+       /*
+        * set the page (side link) pointers.
+        */
+       {
+           BTPageOpaque oopaque = (BTPageOpaque) PageGetSpecialPointer(opage);
+           BTPageOpaque nopaque = (BTPageOpaque) PageGetSpecialPointer(npage);
+
+           oopaque->btpo_next = BufferGetBlockNumber(nbuf);
+           nopaque->btpo_prev = BufferGetBlockNumber(obuf);
+           nopaque->btpo_next = P_NONE;
+       }
+
+       /*
+        * write out the old stuff.  we never want to see it again, so
+        * we can give up our lock (if we had one; BuildingBtree is
+        * set, so we aren't locking).
+        */
+       _bt_wrtbuf(index, obuf);
+    }
+    
+    /*
+     * if this item is different from the last item added, we start a
+     * new chain of duplicates.
+     */
+    off = OffsetNumberNext(last_off);
+    (void) PageAddItem(npage, (Item) bti, btisz, off, LP_USED);
+#ifdef FASTBUILD_DEBUG
+    {
+       bool isnull;
+       Datum d = index_getattr(&(bti->bti_itup), 1, 
+                               RelationGetTupleDescriptor(index),
+                               &isnull);
+       printf("_bt_buildadd: inserted <%x> at offset %d\n",
+              d, off);
+    }
+#endif /* FASTBUILD_DEBUG */
+    if (last_bti == (BTItem) NULL) {
+       first_off = P_FIRSTKEY;
+    } else if (!_bt_itemcmp(index, 1, bti, last_bti, BTEqualStrategyNumber)) {
+       first_off = off;
+    }
+    last_off = off;
+    last_bti = (BTItem) PageGetItem(npage, PageGetItemId(npage, off));
+
+    state->btps_buf = nbuf;
+    state->btps_page = npage;
+    state->btps_lastbti = last_bti;
+    state->btps_lastoff = last_off;
+    state->btps_firstoff = first_off;
+}
+
+/*
+ * take the input tapes stored by 'btspool' and perform successive
+ * merging passes until at most one run is left in each tape.  at that
+ * point, merge the final tape runs into a set of btree leaves.
+ *
+ * XXX three nested loops?  gross.  cut me up into smaller routines.
+ */
+static BlockNumber
+_bt_merge(Relation index, BTSpool *btspool)
+{
+    BTPageState state;
+    BlockNumber firstblk;
+    BTPriQueue q;
+    BTPriQueueElem e;
+    BTItem bti;
+    BTTapeBlock *itape;
+    BTTapeBlock *otape;
+    char *tapepos[MAXTAPES];
+    int tapedone[MAXTAPES];
+    int t;
+    int goodtapes;
+    int nruns;
+    Size btisz;
+    bool doleaf = false;
+
+    /*
+     * initialize state needed for the merge into the btree leaf pages.
+     */
+    (void) memset((char *) &state, 0, sizeof(BTPageState));
+    _bt_blnewpage(index, &(state.btps_buf), &(state.btps_page), BTP_LEAF);
+    state.btps_lastoff = P_HIKEY;
+    state.btps_lastbti = (BTItem) NULL;
+    firstblk = BufferGetBlockNumber(state.btps_buf);
+
+    do {                                                       /* pass */
+       /*
+        * each pass starts by flushing the previous outputs and
+        * swapping inputs and outputs.  this process also clears the
+        * new output tapes and rewinds the new input tapes.
+        */
+       btspool->bts_tape = btspool->bts_ntapes - 1;
+       _bt_spoolflush(btspool);
+       _bt_spoolswap(btspool);
+
+       nruns = 0;
+
+       for (;;) {                                              /* run */
+           /*
+            * each run starts by selecting a new output tape.  the
+            * merged results of a given run are always sent to this
+            * one tape.
+            */
+           btspool->bts_tape = (btspool->bts_tape + 1) % btspool->bts_ntapes;
+           otape = btspool->bts_otape[btspool->bts_tape];
+
+           /*
+            * initialize the priority queue by loading it with the
+            * first element of the given run in each tape.  since we
+            * are starting a new run, we reset the tape (clearing the
+            * End-Of-Run marker) before reading it.  this means that
+            * _bt_taperead will return 0 only if the tape is actually
+            * at EOF.
+            */
+           (void) memset((char *) &q, 0, sizeof(BTPriQueue));
+           goodtapes = 0;
+           for (t = 0; t < btspool->bts_ntapes; ++t) {
+               itape = btspool->bts_itape[t];
+               tapepos[t] = itape->bttb_data;
+               _bt_tapereset(itape);
+               if (_bt_taperead(itape) == 0) {
+                   tapedone[t] = 1;
+               } else {
+                   ++goodtapes;
+                   tapedone[t] = 0;
+                   e.btpqe_tape = t;
+                   e.btpqe_item = _bt_tapenext(itape, &tapepos[t]);
+                   if (e.btpqe_item != (BTItem) NULL) {
+                       _bt_pqadd(&q, &e);
+                   }
+               }
+           }
+           /*
+            * if we don't have any tapes with any input (i.e., they
+            * are all at EOF), we must be done with this pass.
+            */
+           if (goodtapes == 0) {
+               break;  /* for */
+           }
+           ++nruns;
+       
+           /*
+            * output the smallest element from the queue until there are no
+            * more.
+            */
+           while (_bt_pqnext(&q, &e) >= 0) {                   /* item */
+               /*
+                * replace the element taken from priority queue,
+                * fetching a new block if needed.  a tape can run out
+                * if it hits either End-Of-Run or EOF.
+                */
+               t = e.btpqe_tape;
+               bti = e.btpqe_item;
+               if (bti != (BTItem) NULL) {
+                   btisz = BTITEMSZ(bti);
+                   btisz = DOUBLEALIGN(btisz);
+                   if (doleaf) {
+                       _bt_buildadd(index, &state, bti, BTP_LEAF);
+#ifdef FASTBUILD_DEBUG
+                       {
+                           bool isnull;
+                           Datum d = index_getattr(&(bti->bti_itup), 1,
+                                   RelationGetTupleDescriptor(index),
+                                                   &isnull);
+                           printf("_bt_merge: inserted <%x> into block %d\n",
+                                  d, BufferGetBlockNumber(state.btps_buf));
+                       }
+#endif /* FASTBUILD_DEBUG */
+                   } else {
+                       if (SPCLEFT(otape) < btisz) {
+                           /*
+                            * if it's full, write it out and add the
+                            * item to the next block.  (since we know
+                            * there will be at least one more block,
+                            * we know we do *not* want to set
+                            * End-Of-Run here!)
+                            */
+                           _bt_tapewrite(otape, 0);
+                       }
+                       _bt_tapeadd(otape, bti, btisz);
+#ifdef FASTBUILD_DEBUG
+                       {
+                           bool isnull;
+                           Datum d = index_getattr(&(bti->bti_itup), 1,
+                                 RelationGetTupleDescriptor(index), &isnull);
+                           printf("_bt_merge: inserted <%x> into tape %d\n",
+                                  d, btspool->bts_tape);
+                       }
+#endif /* FASTBUILD_DEBUG */
+                   }
+               }
+#ifdef FASTBUILD_DEBUG
+               {
+                   bool isnull;
+                   Datum d = index_getattr(&(bti->bti_itup), 1,
+                                          RelationGetTupleDescriptor(index),
+                                           &isnull);
+                   printf("_bt_merge: got <%x> from tape %d\n", d, t);
+               }
+#endif /* FASTBUILD_DEBUG */
+
+               itape = btspool->bts_itape[t];
+               if (!tapedone[t]) {
+                   BTItem newbti = _bt_tapenext(itape, &tapepos[t]);
+
+                   if (newbti == (BTItem) NULL) {
+                       if (_bt_taperead(itape) == 0) {
+                           tapedone[t] = 1;
+                       } else {
+                           tapepos[t] = itape->bttb_data;
+                           newbti = _bt_tapenext(itape, &tapepos[t]);
+                       }
+                   }
+                   if (newbti != (BTItem) NULL) {
+                       BTPriQueueElem nexte;
+                       
+                       nexte.btpqe_tape = t;
+                       nexte.btpqe_item = newbti;
+                       _bt_pqadd(&q, &nexte);
+                   }
+               }
+           }                                                   /* item */
+       }                                                       /* run */
+       
+       /*
+        * we are here because we ran out of input on all of the input
+        * tapes.
+        *
+        * if this pass did not generate more actual output runs than
+        * we have tapes, we know we have at most one run in each
+        * tape.  this means that we are ready to merge into the final
+        * btree leaf pages instead of merging into a tape file.
+        */
+       if (nruns <= btspool->bts_ntapes) {
+           doleaf = true;
+       }
+    } while (nruns > 0);                                       /* pass */
+
+    /*
+     * this is the rightmost page, so the ItemId array needs to be
+     * slid back one slot.
+     */
+    _bt_slideleft(index, state.btps_buf, state.btps_page);
+    _bt_wrtbuf(index, state.btps_buf);
+
+    return(firstblk);
+}
+
+
+/*
+ * given the block number 'blk' of the first page of a set of linked
+ * siblings (i.e., the start of an entire level of the btree),
+ * construct the corresponding next level of the btree.  we do this by
+ * placing minimum keys from each page into this page.  the format of
+ * the internal pages is otherwise the same as for leaf pages.
+ */
+void
+_bt_upperbuild(Relation index, BlockNumber blk, int level)
+{
+    Buffer rbuf;
+    Page rpage;
+    BTPageOpaque ropaque;
+    BTPageState state;
+    BlockNumber firstblk;
+    BTItem bti;
+    BTItem nbti;
+    OffsetNumber off;
+
+    rbuf = _bt_getbuf(index, blk, BT_WRITE);
+    rpage = BufferGetPage(rbuf);
+    ropaque = (BTPageOpaque) PageGetSpecialPointer(rpage);
+
+    /*
+     * if we only have one page on a level, we can just make it the
+     * root.
+     */
+    if (P_RIGHTMOST(ropaque)) {
+       ropaque->btpo_flags |= BTP_ROOT;
+       _bt_wrtbuf(index, rbuf);
+       _bt_metaproot(index, blk);
+       return;
+    }
+    _bt_relbuf(index, rbuf, BT_WRITE);
+       
+    (void) memset((char *) &state, 0, sizeof(BTPageState));
+    _bt_blnewpage(index, &(state.btps_buf), &(state.btps_page), 0);
+    state.btps_lastoff = P_HIKEY;
+    state.btps_lastbti = (BTItem) NULL;
+    firstblk = BufferGetBlockNumber(state.btps_buf);
+    
+    /* for each page... */
+    do {
+       rbuf = _bt_getbuf(index, blk, BT_READ);
+       rpage = BufferGetPage(rbuf);
+       ropaque = (BTPageOpaque) PageGetSpecialPointer(rpage);
+       
+       /* for each item... */
+       if (!PageIsEmpty(rpage)) {
+           /*
+            * form a new index tuple corresponding to the minimum key
+            * of the lower page and insert it into a page at this
+            * level.
+            */
+           off = P_RIGHTMOST(ropaque) ? P_HIKEY : P_FIRSTKEY;
+           bti = (BTItem) PageGetItem(rpage, PageGetItemId(rpage, off));
+           nbti = _bt_formitem(&(bti->bti_itup));
+           ItemPointerSet(&(nbti->bti_itup.t_tid), blk, P_HIKEY);
+#ifdef FASTBUILD_DEBUG
+           {
+               bool isnull;
+               Datum d = index_getattr(&(nbti->bti_itup), 1, 
+                                       RelationGetTupleDescriptor(index),
+                                       &isnull);
+               printf("_bt_upperbuild: inserting <%x> at %d\n",
+                      d, level);
+           }
+#endif /* FASTBUILD_DEBUG */
+           _bt_buildadd(index, &state, nbti, 0);
+           pfree((void *) nbti);
+       }
+       blk = ropaque->btpo_next;
+       _bt_relbuf(index, rbuf, BT_READ);
+    } while (blk != P_NONE);
+       
+    /*
+     * this is the rightmost page, so the ItemId array needs to be
+     * slid back one slot.
+     */
+    _bt_slideleft(index, state.btps_buf, state.btps_page);
+    _bt_wrtbuf(index, state.btps_buf);
+    
+    _bt_upperbuild(index, firstblk, level + 1);
+}
+
+/*
+ * given a spool loading by successive calls to _bt_spool, create an
+ * entire btree.
+ */
+void
+_bt_leafbuild(Relation index, void *spool)
+{
+    BTSpool *btspool = (BTSpool *) spool;
+    BlockNumber firstblk;
+
+    /*
+     * merge the runs into btree leaf pages.
+     */
+    firstblk = _bt_merge(index, btspool);
+
+    /*
+     * build the upper levels of the btree.
+     */
+    _bt_upperbuild(index, firstblk, 0);
+}
+
+#else /* !FASTBUILD */
+
+void *_bt_spoolinit(Relation index, int ntapes) { return((void *) NULL); }
+void _bt_spooldestroy(void *spool) { }
+void _bt_spool(Relation index, BTItem btitem, void *spool) { }
+void _bt_upperbuild(Relation index, BlockNumber blk, int level) { }
+void _bt_leafbuild(Relation index, void *spool) { }
+
+#endif /* !FASTBUILD */
diff --git a/src/backend/access/nbtree/nbtstrat.c b/src/backend/access/nbtree/nbtstrat.c
new file mode 100644 (file)
index 0000000..8daf7bd
--- /dev/null
@@ -0,0 +1,134 @@
+/*-------------------------------------------------------------------------
+ *
+ * btstrat.c--
+ *    Srategy map entries for the btree indexed access method
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/bufpage.h"
+
+#include "utils/elog.h"
+#include "utils/rel.h"
+#include "utils/excid.h"
+
+#include "access/genam.h"
+#include "access/nbtree.h"
+
+/*
+ * Note:
+ *     StrategyNegate, StrategyCommute, and StrategyNegateCommute
+ *     assume <, <=, ==, >=, > ordering.
+ */
+static StrategyNumber  BTNegate[5] = {
+    BTGreaterEqualStrategyNumber,
+    BTGreaterStrategyNumber,
+    InvalidStrategy,
+    BTLessStrategyNumber,
+    BTLessEqualStrategyNumber
+};
+
+static StrategyNumber  BTCommute[5] = {
+    BTGreaterStrategyNumber,
+    BTGreaterEqualStrategyNumber,
+    InvalidStrategy,
+    BTLessEqualStrategyNumber,
+    BTLessStrategyNumber
+};
+
+static StrategyNumber  BTNegateCommute[5] = {
+    BTLessEqualStrategyNumber,
+    BTLessStrategyNumber,
+    InvalidStrategy,
+    BTGreaterStrategyNumber,
+    BTGreaterEqualStrategyNumber
+};
+
+static uint16  BTLessTermData[] = {            /* XXX type clash */
+    2,
+    BTLessStrategyNumber,
+    SK_NEGATE,
+    BTLessStrategyNumber,
+    SK_NEGATE | SK_COMMUTE
+};
+
+static uint16  BTLessEqualTermData[] = {       /* XXX type clash */
+    2,
+    BTLessEqualStrategyNumber,
+    0x0,
+    BTLessEqualStrategyNumber,
+    SK_COMMUTE
+};
+
+static uint16  BTGreaterEqualTermData[] = {    /* XXX type clash */
+    2,
+    BTGreaterEqualStrategyNumber,
+    0x0,
+    BTGreaterEqualStrategyNumber,
+    SK_COMMUTE
+    };
+
+static uint16  BTGreaterTermData[] = {         /* XXX type clash */
+    2,
+    BTGreaterStrategyNumber,
+    SK_NEGATE,
+    BTGreaterStrategyNumber,
+    SK_NEGATE | SK_COMMUTE
+};
+
+static StrategyTerm    BTEqualExpressionData[] = {
+    (StrategyTerm)BTLessTermData,              /* XXX */
+    (StrategyTerm)BTLessEqualTermData,         /* XXX */
+    (StrategyTerm)BTGreaterEqualTermData,      /* XXX */
+    (StrategyTerm)BTGreaterTermData,           /* XXX */
+    NULL
+};
+
+static StrategyEvaluationData  BTEvaluationData = {
+    /* XXX static for simplicity */
+    
+    BTMaxStrategyNumber,
+    (StrategyTransformMap)BTNegate,    /* XXX */
+    (StrategyTransformMap)BTCommute,   /* XXX */
+    (StrategyTransformMap)BTNegateCommute,     /* XXX */
+
+    { NULL, NULL, (StrategyExpression)BTEqualExpressionData, NULL, NULL,
+      NULL,NULL,NULL,NULL,NULL,NULL,NULL}
+};
+
+/* ----------------------------------------------------------------
+ *     RelationGetBTStrategy
+ * ----------------------------------------------------------------
+ */
+
+StrategyNumber
+_bt_getstrat(Relation rel,
+            AttrNumber attno,
+            RegProcedure proc)
+{
+    StrategyNumber     strat;
+    
+    strat = RelationGetStrategy(rel, attno, &BTEvaluationData, proc);
+    
+    Assert(StrategyNumberIsValid(strat));
+    
+    return (strat);
+}
+
+bool
+_bt_invokestrat(Relation rel,
+               AttrNumber attno,
+               StrategyNumber strat,
+               Datum left,
+               Datum right)
+{
+    return (RelationInvokeStrategy(rel, &BTEvaluationData, attno, strat, 
+                                  left, right));
+}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
new file mode 100644 (file)
index 0000000..d589004
--- /dev/null
@@ -0,0 +1,239 @@
+/*-------------------------------------------------------------------------
+ *
+ * btutils.c--
+ *    Utility code for Postgres btree implementation.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include "postgres.h"
+
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+
+#include "fmgr.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+#include "utils/excid.h"
+#include "utils/datum.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/iqual.h"
+#include "access/nbtree.h"
+
+ScanKey 
+_bt_mkscankey(Relation rel, IndexTuple itup)
+{     
+    ScanKey skey;
+    TupleDesc itupdesc;
+    int natts;
+    int i;
+    Datum arg;
+    RegProcedure proc;
+    bool null;
+    
+    natts = rel->rd_rel->relnatts;
+    itupdesc = RelationGetTupleDescriptor(rel);
+    
+    skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
+    
+    for (i = 0; i < natts; i++) {
+       arg = index_getattr(itup, i + 1, itupdesc, &null);
+       proc = index_getprocid(rel, i + 1, BTORDER_PROC);
+       ScanKeyEntryInitialize(&skey[i],
+                              0x0, (AttrNumber) (i + 1), proc, arg);
+    }
+    
+    return (skey);
+}
+
+void
+_bt_freeskey(ScanKey skey)
+{
+    pfree(skey);
+}
+
+void
+_bt_freestack(BTStack stack)
+{
+    BTStack ostack;
+    
+    while (stack != (BTStack) NULL) {
+       ostack = stack;
+       stack = stack->bts_parent;
+       pfree(ostack->bts_btitem);
+       pfree(ostack);
+    }
+}
+
+/*
+ *  _bt_orderkeys() -- Put keys in a sensible order for conjunctive quals.
+ *
+ *     The order of the keys in the qual match the ordering imposed by
+ *     the index.  This routine only needs to be called if there are
+ *     more than one qual clauses using this index.
+ */
+void
+_bt_orderkeys(Relation relation, uint16 *numberOfKeys, ScanKey key)
+{
+    ScanKey xform;
+    ScanKeyData *cur;
+    StrategyMap map;
+    int nbytes;
+    long test;
+    int i, j;
+    int init[BTMaxStrategyNumber+1];
+    
+    /* haven't looked at any strategies yet */
+    for (i = 0; i <= BTMaxStrategyNumber; i++)
+       init[i] = 0;
+    
+    /* get space for the modified array of keys */
+    nbytes = BTMaxStrategyNumber * sizeof(ScanKeyData);
+    xform = (ScanKey) palloc(nbytes);
+    memset(xform, 0, nbytes); 
+    
+    
+    /* get the strategy map for this index/attribute pair */
+    /*
+     *  XXX
+     *  When we support multiple keys in a single index, this is what
+     *  we'll want to do.  At present, the planner is hosed, so we
+     *  hard-wire the attribute number below.  Postgres only does single-
+     *  key indices...
+     * map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation),
+     *                                     BTMaxStrategyNumber,
+     *                                     key->data[0].attributeNumber);
+     */
+    map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation),
+                                     BTMaxStrategyNumber,
+                                     1 /* XXX */ );
+    
+    /* check each key passed in */
+    for (i = *numberOfKeys; --i >= 0; ) {
+       cur = &key[i];
+       for (j = BTMaxStrategyNumber; --j >= 0; ) {
+           if (cur->sk_procedure == map->entry[j].sk_procedure)
+               break;
+       }
+       
+       /* have we seen one of these before? */
+       if (init[j]) {
+           /* yup, use the appropriate value */
+           test =
+               (long) FMGR_PTR2(cur->sk_func, cur->sk_procedure,
+                                cur->sk_argument, xform[j].sk_argument);
+           if (test)
+               xform[j].sk_argument = cur->sk_argument;
+       } else {
+           /* nope, use this value */
+           memmove(&xform[j], cur, sizeof(*cur));
+          
+           init[j] = 1;
+       }
+    }
+    
+    /* if = has been specified, no other key will be used */
+    if (init[BTEqualStrategyNumber - 1]) {
+       init[BTLessStrategyNumber - 1] = 0;
+       init[BTLessEqualStrategyNumber - 1] = 0;
+       init[BTGreaterEqualStrategyNumber - 1] = 0;
+       init[BTGreaterStrategyNumber - 1] = 0;
+    }
+    
+    /* only one of <, <= */
+    if (init[BTLessStrategyNumber - 1]
+       && init[BTLessEqualStrategyNumber - 1]) {
+       
+       ScanKeyData *lt, *le;
+       
+       lt = &xform[BTLessStrategyNumber - 1];
+       le = &xform[BTLessEqualStrategyNumber - 1];
+       
+       /*
+        *  DO NOT use the cached function stuff here -- this is key
+        *  ordering, happens only when the user expresses a hokey
+        *  qualification, and gets executed only once, anyway.  The
+        *  transform maps are hard-coded, and can't be initialized
+        *  in the correct way.
+        */
+       
+       test = (long) fmgr(le->sk_procedure, le->sk_argument, lt->sk_argument);
+       
+       if (test)
+           init[BTLessEqualStrategyNumber - 1] = 0;
+       else
+           init[BTLessStrategyNumber - 1] = 0;
+    }
+    
+    /* only one of >, >= */
+    if (init[BTGreaterStrategyNumber - 1]
+       && init[BTGreaterEqualStrategyNumber - 1]) {
+       
+       ScanKeyData *gt, *ge;
+       
+       gt = &xform[BTGreaterStrategyNumber - 1];
+       ge = &xform[BTGreaterEqualStrategyNumber - 1];
+       
+       /* see note above on function cache */
+       test = (long) fmgr(ge->sk_procedure, gt->sk_argument, gt->sk_argument);
+       
+       if (test)
+           init[BTGreaterStrategyNumber - 1] = 0;
+       else
+           init[BTGreaterEqualStrategyNumber - 1] = 0;
+    }
+    
+    /* okay, reorder and count */
+    j = 0;
+    
+    for (i = BTMaxStrategyNumber; --i >= 0; )
+       if (init[i])
+           key[j++] = xform[i];
+    
+    *numberOfKeys = j;
+    
+    pfree(xform);
+}
+
+bool
+_bt_checkqual(IndexScanDesc scan, IndexTuple itup)
+{
+    if (scan->numberOfKeys > 0)
+       return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation),
+                             scan->numberOfKeys, scan->keyData));
+    else
+       return (true);
+}
+
+BTItem
+_bt_formitem(IndexTuple itup)
+{
+    int nbytes_btitem;
+    BTItem btitem;
+    Size tuplen;
+    extern Oid newoid();
+    
+    /* disallow nulls in btree keys */
+    if (itup->t_info & INDEX_NULL_MASK)
+       elog(WARN, "btree indices cannot include null keys");
+    
+    /* make a copy of the index tuple with room for the sequence number */
+    tuplen = IndexTupleSize(itup);
+    nbytes_btitem = tuplen +
+       (sizeof(BTItemData) - sizeof(IndexTupleData));
+    
+    btitem = (BTItem) palloc(nbytes_btitem);
+    memmove((char *) &(btitem->bti_itup), (char *) itup, tuplen);
+    
+    btitem->bti_oid = newoid();
+    return (btitem);
+}
diff --git a/src/backend/access/printtup.h b/src/backend/access/printtup.h
new file mode 100644 (file)
index 0000000..5c4e79c
--- /dev/null
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * printtup.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        PRINTTUP_H
+#define PRINTTUP_H
+
+#include "access/htup.h"
+#include "access/tupdesc.h"
+
+extern Oid typtoout(Oid type);
+extern void printtup(HeapTuple tuple, TupleDesc typeinfo);
+extern void showatts(char *name, TupleDesc attinfo);
+extern void debugtup(HeapTuple tuple, TupleDesc typeinfo);
+extern void printtup_internal(HeapTuple tuple, TupleDesc typeinfo);
+extern Oid gettypelem(Oid type);
+
+#endif /* PRINTTUP_H */
diff --git a/src/backend/access/relscan.h b/src/backend/access/relscan.h
new file mode 100644 (file)
index 0000000..239d3a3
--- /dev/null
@@ -0,0 +1,87 @@
+/*-------------------------------------------------------------------------
+ *
+ * relscan.h--
+ *    POSTGRES internal relation scan descriptor definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        RELSCAN_H
+#define RELSCAN_H
+
+#include "c.h"
+
+#include "access/skey.h"
+#include "storage/buf.h"
+#include "access/htup.h"
+#include "storage/itemptr.h"
+
+#include "utils/tqual.h"
+#include "utils/rel.h"
+
+
+typedef ItemPointerData        MarkData;
+
+typedef struct HeapScanDescData {
+       Relation        rs_rd;          /* pointer to relation descriptor */
+       HeapTuple       rs_ptup;        /* previous tuple in scan */
+       HeapTuple       rs_ctup;        /* current tuple in scan */
+       HeapTuple       rs_ntup;        /* next tuple in scan */
+       Buffer          rs_pbuf;        /* previous buffer in scan */
+       Buffer          rs_cbuf;        /* current buffer in scan */
+       Buffer          rs_nbuf;        /* next buffer in scan */
+       ItemPointerData rs_mptid;       /* marked previous tid */
+       ItemPointerData rs_mctid;       /* marked current tid */
+       ItemPointerData rs_mntid;       /* marked next tid */
+       ItemPointerData rs_mcd;         /* marked current delta XXX ??? */
+       bool            rs_atend;       /* restart scan at end? */
+       TimeQual        rs_tr;          /* time qualification */
+       uint16          rs_cdelta;      /* current delta in chain */
+       uint16          rs_nkeys;       /* number of attributes in keys */
+       ScanKey         rs_key;         /* key descriptors */
+} HeapScanDescData;
+
+typedef HeapScanDescData *HeapScanDesc;
+
+typedef struct IndexScanDescData {
+       Relation        relation;               /* relation descriptor */
+       void            *opaque;                /* am-specific slot */
+       ItemPointerData previousItemData;       /* previous index pointer */
+       ItemPointerData currentItemData;        /* current index pointer */
+       ItemPointerData nextItemData;           /* next index pointer */
+       MarkData        previousMarkData;       /* marked previous pointer */
+       MarkData        currentMarkData;        /* marked current  pointer */
+       MarkData        nextMarkData;           /* marked next pointer */
+       uint8           flags;                  /* scan position flags */
+       bool            scanFromEnd;            /* restart scan at end? */
+       uint16          numberOfKeys;           /* number of key attributes */
+       ScanKey         keyData;                /* key descriptor */
+} IndexScanDescData;
+
+typedef IndexScanDescData      *IndexScanDesc;
+
+/* ----------------
+ *     IndexScanDescPtr is used in the executor where we have to
+ *     keep track of several index scans when using several indices
+ *     - cim 9/10/89
+ * ----------------
+ */
+typedef IndexScanDesc          *IndexScanDescPtr;
+
+/*
+ * HeapScanIsValid --
+ *     True iff the heap scan is valid.
+ */
+#define        HeapScanIsValid(scan) PointerIsValid(scan)
+
+/*
+ * IndexScanIsValid --
+ *     True iff the index scan is valid.
+ */
+#define IndexScanIsValid(scan) PointerIsValid(scan)
+
+#endif /* RELSCAN_H */
diff --git a/src/backend/access/rtree.h b/src/backend/access/rtree.h
new file mode 100644 (file)
index 0000000..8e0c3f5
--- /dev/null
@@ -0,0 +1,98 @@
+/*-------------------------------------------------------------------------
+ *
+ * rtree.h--
+ *    common declarations for the rtree access method code.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef RTREE_H
+#define RTREE_H
+
+/* see rtstrat.c for what all this is about */
+#define RTNStrategies                  8
+#define RTLeftStrategyNumber           1
+#define RTOverLeftStrategyNumber       2
+#define RTOverlapStrategyNumber                3
+#define RTOverRightStrategyNumber      4
+#define RTRightStrategyNumber          5
+#define RTSameStrategyNumber           6
+#define RTContainsStrategyNumber       7
+#define RTContainedByStrategyNumber    8
+
+#define RTNProcs                       3
+#define RT_UNION_PROC                  1
+#define RT_INTER_PROC                  2
+#define RT_SIZE_PROC                   3
+
+#define F_LEAF         (1 << 0)
+
+typedef struct RTreePageOpaqueData {
+       uint32          flags;
+} RTreePageOpaqueData;
+
+typedef RTreePageOpaqueData    *RTreePageOpaque;
+
+/*
+ *  When we descend a tree, we keep a stack of parent pointers.
+ */
+
+typedef struct RTSTACK {
+       struct RTSTACK  *rts_parent;
+       OffsetNumber    rts_child;
+       BlockNumber     rts_blk;
+} RTSTACK;
+
+/*
+ *  When we're doing a scan, we need to keep track of the parent stack
+ *  for the marked and current items.  Also, rtrees have the following
+ *  property:  if you're looking for the box (1,1,2,2), on the internal
+ *  nodes you have to search for all boxes that *contain* (1,1,2,2), and
+ *  not the ones that match it.  We have a private scan key for internal
+ *  nodes in the opaque structure for rtrees for this reason.  See
+ *  access/index-rtree/rtscan.c and rtstrat.c for how it gets initialized.
+ */
+
+typedef struct RTreeScanOpaqueData {
+       struct RTSTACK  *s_stack;
+       struct RTSTACK  *s_markstk;
+       uint16          s_flags;
+       uint16          s_internalNKey;
+       ScanKey         s_internalKey;
+} RTreeScanOpaqueData;
+
+typedef RTreeScanOpaqueData    *RTreeScanOpaque;
+
+/*
+ *  When we're doing a scan and updating a tree at the same time, the
+ *  updates may affect the scan.  We use the flags entry of the scan's
+ *  opaque space to record our actual position in response to updates
+ *  that we can't handle simply by adjusting pointers.
+ */
+
+#define RTS_CURBEFORE  ((uint16) (1 << 0))
+#define RTS_MRKBEFORE  ((uint16) (1 << 1))
+
+/* root page of an rtree */
+#define P_ROOT         0
+
+/*
+ *  When we update a relation on which we're doing a scan, we need to
+ *  check the scan and fix it if the update affected any of the pages it
+ *  touches.  Otherwise, we can miss records that we should see.  The only
+ *  times we need to do this are for deletions and splits.  See the code in
+ *  rtscan.c for how the scan is fixed.  These two contants tell us what sort
+ *  of operation changed the index.
+ */
+
+#define        RTOP_DEL        0
+#define        RTOP_SPLIT      1
+
+/* defined in rtree.c */
+extern void freestack(RTSTACK *s);
+
+#endif /* RTREE_H */
diff --git a/src/backend/access/rtree/Makefile.inc b/src/backend/access/rtree/Makefile.inc
new file mode 100644 (file)
index 0000000..b720fc3
--- /dev/null
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for access/rtree (R-Tree access method)
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= rtget.c rtproc.c rtree.c rtscan.c rtstrat.c
diff --git a/src/backend/access/rtree/rtget.c b/src/backend/access/rtree/rtget.c
new file mode 100644 (file)
index 0000000..8f0e10b
--- /dev/null
@@ -0,0 +1,320 @@
+/*-------------------------------------------------------------------------
+ *
+ * rtget.c--
+ *    fetch tuples from an rtree scan.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/iqual.h"
+#include "access/rtree.h"
+#include "access/sdir.h"
+
+static OffsetNumber findnext(IndexScanDesc s, Page p, OffsetNumber n,
+                            ScanDirection dir);
+static RetrieveIndexResult rtscancache(IndexScanDesc s, ScanDirection dir);
+static RetrieveIndexResult rtfirst(IndexScanDesc s, ScanDirection dir);
+static RetrieveIndexResult rtnext(IndexScanDesc s, ScanDirection dir);
+static ItemPointer rtheapptr(Relation r, ItemPointer itemp);
+
+
+RetrieveIndexResult
+rtgettuple(IndexScanDesc s, ScanDirection dir)
+{
+    RetrieveIndexResult res;
+    
+    /* if we have it cached in the scan desc, just return the value */
+    if ((res = rtscancache(s, dir)) != (RetrieveIndexResult) NULL)
+       return (res);
+    
+    /* not cached, so we'll have to do some work */
+    if (ItemPointerIsValid(&(s->currentItemData))) {
+       res = rtnext(s, dir);
+    } else {
+       res = rtfirst(s, dir);
+    }
+    return (res);
+}
+
+static RetrieveIndexResult
+rtfirst(IndexScanDesc s, ScanDirection dir)
+{
+    Buffer b;
+    Page p;
+    OffsetNumber n;
+    OffsetNumber maxoff;
+    RetrieveIndexResult res;
+    RTreePageOpaque po;
+    RTreeScanOpaque so;
+    RTSTACK *stk;
+    BlockNumber blk;
+    IndexTuple it;
+    ItemPointer ip;
+    
+    b = ReadBuffer(s->relation, P_ROOT);
+    p = BufferGetPage(b);
+    po = (RTreePageOpaque) PageGetSpecialPointer(p);
+    so = (RTreeScanOpaque) s->opaque;
+    
+    for (;;) {
+       maxoff = PageGetMaxOffsetNumber(p);
+       if (ScanDirectionIsBackward(dir))
+           n = findnext(s, p, maxoff, dir);
+       else
+           n = findnext(s, p, FirstOffsetNumber, dir);
+       
+       while (n < FirstOffsetNumber || n > maxoff) {
+           
+           ReleaseBuffer(b);
+           if (so->s_stack == (RTSTACK *) NULL)
+               return ((RetrieveIndexResult) NULL);
+           
+           stk = so->s_stack;
+           b = ReadBuffer(s->relation, stk->rts_blk);
+           p = BufferGetPage(b);
+           po = (RTreePageOpaque) PageGetSpecialPointer(p);
+           maxoff = PageGetMaxOffsetNumber(p);
+           
+           if (ScanDirectionIsBackward(dir)) {
+               n = OffsetNumberPrev(stk->rts_child);
+           } else {
+               n = OffsetNumberNext(stk->rts_child);
+           }
+           so->s_stack = stk->rts_parent;
+           pfree(stk);
+           
+           n = findnext(s, p, n, dir);
+       }
+       if (po->flags & F_LEAF) {
+           ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
+           
+           it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
+           ip = (ItemPointer) palloc(sizeof(ItemPointerData));
+           memmove((char *) ip, (char *) &(it->t_tid),
+                   sizeof(ItemPointerData));
+           ReleaseBuffer(b);
+           
+           res = FormRetrieveIndexResult(&(s->currentItemData), ip);
+           
+           return (res);
+       } else {
+           stk = (RTSTACK *) palloc(sizeof(RTSTACK));
+           stk->rts_child = n;
+           stk->rts_blk = BufferGetBlockNumber(b);
+           stk->rts_parent = so->s_stack;
+           so->s_stack = stk;
+           
+           it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
+           blk = ItemPointerGetBlockNumber(&(it->t_tid));
+           
+           ReleaseBuffer(b);
+           b = ReadBuffer(s->relation, blk);
+           p = BufferGetPage(b);
+           po = (RTreePageOpaque) PageGetSpecialPointer(p);
+       }
+    }
+}
+
+static RetrieveIndexResult
+rtnext(IndexScanDesc s, ScanDirection dir)
+{
+    Buffer b;
+    Page p;
+    OffsetNumber n;
+    OffsetNumber maxoff;
+    RetrieveIndexResult res;
+    RTreePageOpaque po;
+    RTreeScanOpaque so;
+    RTSTACK *stk;
+    BlockNumber blk;
+    IndexTuple it;
+    ItemPointer ip;
+    
+    blk = ItemPointerGetBlockNumber(&(s->currentItemData));
+    n = ItemPointerGetOffsetNumber(&(s->currentItemData));
+    
+    if (ScanDirectionIsForward(dir)) {
+       n = OffsetNumberNext(n);
+    } else {
+       n = OffsetNumberPrev(n);
+    }
+
+    b = ReadBuffer(s->relation, blk);
+    p = BufferGetPage(b);
+    po = (RTreePageOpaque) PageGetSpecialPointer(p);
+    so = (RTreeScanOpaque) s->opaque;
+    
+    for (;;) {
+       maxoff = PageGetMaxOffsetNumber(p);
+       n = findnext(s, p, n, dir);
+       
+       while (n < FirstOffsetNumber || n > maxoff) {
+           
+           ReleaseBuffer(b);
+           if (so->s_stack == (RTSTACK *) NULL)
+               return ((RetrieveIndexResult) NULL);
+           
+           stk = so->s_stack;
+           b = ReadBuffer(s->relation, stk->rts_blk);
+           p = BufferGetPage(b);
+           maxoff = PageGetMaxOffsetNumber(p);
+           po = (RTreePageOpaque) PageGetSpecialPointer(p);
+           
+           if (ScanDirectionIsBackward(dir)) {
+               n = OffsetNumberPrev(stk->rts_child);
+           } else {
+               n = OffsetNumberNext(stk->rts_child);
+           }
+           so->s_stack = stk->rts_parent;
+           pfree(stk);
+           
+           n = findnext(s, p, n, dir);
+       }
+       if (po->flags & F_LEAF) {
+           ItemPointerSet(&(s->currentItemData), BufferGetBlockNumber(b), n);
+           
+           it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
+           ip = (ItemPointer) palloc(sizeof(ItemPointerData));
+           memmove((char *) ip, (char *) &(it->t_tid),
+                   sizeof(ItemPointerData));
+           ReleaseBuffer(b);
+           
+           res = FormRetrieveIndexResult(&(s->currentItemData), ip);
+           
+           return (res);
+       } else {
+           stk = (RTSTACK *) palloc(sizeof(RTSTACK));
+           stk->rts_child = n;
+           stk->rts_blk = BufferGetBlockNumber(b);
+           stk->rts_parent = so->s_stack;
+           so->s_stack = stk;
+           
+           it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
+           blk = ItemPointerGetBlockNumber(&(it->t_tid));
+           
+           ReleaseBuffer(b);
+           b = ReadBuffer(s->relation, blk);
+           p = BufferGetPage(b);
+           po = (RTreePageOpaque) PageGetSpecialPointer(p);
+           
+           if (ScanDirectionIsBackward(dir)) {
+               n = PageGetMaxOffsetNumber(p);
+           } else {
+               n = FirstOffsetNumber;
+           }
+       }
+    }
+}
+
+static OffsetNumber
+findnext(IndexScanDesc s, Page p, OffsetNumber n, ScanDirection dir)
+{
+    OffsetNumber maxoff;
+    IndexTuple it;
+    RTreePageOpaque po;
+    RTreeScanOpaque so;
+    
+    maxoff = PageGetMaxOffsetNumber(p);
+    po = (RTreePageOpaque) PageGetSpecialPointer(p);
+    so = (RTreeScanOpaque) s->opaque;
+    
+    /*
+     *  If we modified the index during the scan, we may have a pointer to
+     *  a ghost tuple, before the scan.  If this is the case, back up one.
+     */
+    
+    if (so->s_flags & RTS_CURBEFORE) {
+       so->s_flags &= ~RTS_CURBEFORE;
+       n = OffsetNumberPrev(n);
+    }
+    
+    while (n >= FirstOffsetNumber && n <= maxoff) {
+       it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
+       if (po->flags & F_LEAF) {
+           if (index_keytest(it, 
+                             RelationGetTupleDescriptor(s->relation),
+                             s->numberOfKeys, s->keyData))
+               break;
+       } else {
+           if (index_keytest(it, 
+                             RelationGetTupleDescriptor(s->relation),
+                             so->s_internalNKey, so->s_internalKey))
+               break;
+       }
+       
+       if (ScanDirectionIsBackward(dir)) {
+           n = OffsetNumberPrev(n);
+       } else {
+           n = OffsetNumberNext(n);
+       }
+    }
+    
+    return (n);
+}
+
+static RetrieveIndexResult
+rtscancache(IndexScanDesc s, ScanDirection dir)
+{
+    RetrieveIndexResult res;
+    ItemPointer ip;
+    
+    if (!(ScanDirectionIsNoMovement(dir)
+         && ItemPointerIsValid(&(s->currentItemData)))) {
+       
+       return ((RetrieveIndexResult) NULL);
+    } 
+    
+    ip = rtheapptr(s->relation, &(s->currentItemData));
+    
+    if (ItemPointerIsValid(ip))
+       res = FormRetrieveIndexResult(&(s->currentItemData), ip);
+    else
+       res = (RetrieveIndexResult) NULL;
+    
+    return (res);
+}
+
+/*
+ *  rtheapptr returns the item pointer to the tuple in the heap relation
+ *  for which itemp is the index relation item pointer.
+ */
+static ItemPointer
+rtheapptr(Relation r, ItemPointer itemp)
+{
+    Buffer b;
+    Page p;
+    IndexTuple it;
+    ItemPointer ip;
+    OffsetNumber n;
+    
+    ip = (ItemPointer) palloc(sizeof(ItemPointerData));
+    if (ItemPointerIsValid(itemp)) {
+       b = ReadBuffer(r, ItemPointerGetBlockNumber(itemp));
+       p = BufferGetPage(b);
+       n = ItemPointerGetOffsetNumber(itemp);
+       it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
+       memmove((char *) ip, (char *) &(it->t_tid),
+               sizeof(ItemPointerData));
+       ReleaseBuffer(b);
+    } else {
+       ItemPointerSetInvalid(ip);
+    }
+    
+    return (ip);
+}
diff --git a/src/backend/access/rtree/rtproc.c b/src/backend/access/rtree/rtproc.c
new file mode 100644 (file)
index 0000000..639afff
--- /dev/null
@@ -0,0 +1,150 @@
+/*-------------------------------------------------------------------------
+ *
+ * rtproc.c--
+ *    pg_amproc entries for rtrees.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <math.h>
+#include <string.h>
+
+#include "postgres.h"
+
+#include "utils/elog.h"
+#include "utils/geo-decls.h"
+#include "utils/palloc.h"
+
+BOX
+*rt_box_union(BOX *a, BOX *b)
+{
+    BOX *n;
+    
+    if ((n = (BOX *) palloc(sizeof (*n))) == (BOX *) NULL)
+       elog(WARN, "Cannot allocate box for union");
+    
+    n->xh = Max(a->xh, b->xh);
+    n->yh = Max(a->yh, b->yh);
+    n->xl = Min(a->xl, b->xl);
+    n->yl = Min(a->yl, b->yl);
+    
+    return (n);
+}
+
+BOX *
+rt_box_inter(BOX *a, BOX *b)
+{
+    BOX *n;
+    
+    if ((n = (BOX *) palloc(sizeof (*n))) == (BOX *) NULL)
+       elog(WARN, "Cannot allocate box for union");
+    
+    n->xh = Min(a->xh, b->xh);
+    n->yh = Min(a->yh, b->yh);
+    n->xl = Max(a->xl, b->xl);
+    n->yl = Max(a->yl, b->yl);
+    
+    if (n->xh < n->xl || n->yh < n->yl) {
+       pfree(n);
+       return ((BOX *) NULL);
+    }
+    
+    return (n);
+}
+
+void
+rt_box_size(BOX *a, float *size)
+{
+    if (a == (BOX *) NULL || a->xh <= a->xl || a->yh <= a->yl)
+       *size = 0.0;
+    else
+       *size = (float) ((a->xh - a->xl) * (a->yh - a->yl));
+    
+    return;
+}
+
+/*
+ *  rt_bigbox_size() -- Compute a size for big boxes.
+ *
+ *     In an earlier release of the system, this routine did something
+ *     different from rt_box_size.  We now use floats, rather than ints,
+ *     as the return type for the size routine, so we no longer need to
+ *     have a special return type for big boxes.
+ */
+void
+rt_bigbox_size(BOX *a, float *size)
+{
+    rt_box_size(a, size);
+}
+
+POLYGON *
+rt_poly_union(POLYGON *a, POLYGON *b)
+{
+    POLYGON *p;
+    
+    p = (POLYGON *)PALLOCTYPE(POLYGON);
+    
+    if (!PointerIsValid(p))
+       elog(WARN, "Cannot allocate polygon for union");
+    
+    memset((char *) p, 0, sizeof(POLYGON));    /* zero any holes */
+    p->size = sizeof(POLYGON);
+    p->npts = 0;
+    p->boundbox.xh = Max(a->boundbox.xh, b->boundbox.xh);
+    p->boundbox.yh = Max(a->boundbox.yh, b->boundbox.yh);
+    p->boundbox.xl = Min(a->boundbox.xl, b->boundbox.xl);
+    p->boundbox.yl = Min(a->boundbox.yl, b->boundbox.yl);
+    return p;
+}
+
+void
+rt_poly_size(POLYGON *a, float *size)
+{
+    double xdim, ydim;
+    
+    size = (float *) palloc(sizeof(float));
+    if (a == (POLYGON *) NULL || 
+       a->boundbox.xh <= a->boundbox.xl || 
+       a->boundbox.yh <= a->boundbox.yl)
+       *size = 0.0;
+    else {
+       xdim = (a->boundbox.xh - a->boundbox.xl);
+       ydim = (a->boundbox.yh - a->boundbox.yl);
+       
+       *size = (float) (xdim * ydim);
+    }
+    
+    return;
+}
+
+POLYGON *
+rt_poly_inter(POLYGON *a, POLYGON *b)
+{
+    POLYGON *p;
+    
+    p = (POLYGON *) PALLOCTYPE(POLYGON);
+    
+    if (!PointerIsValid(p))
+       elog(WARN, "Cannot allocate polygon for intersection");
+    
+    memset((char *) p, 0, sizeof(POLYGON));    /* zero any holes */
+    p->size = sizeof(POLYGON);
+    p->npts = 0;
+    p->boundbox.xh = Min(a->boundbox.xh, b->boundbox.xh);
+    p->boundbox.yh = Min(a->boundbox.yh, b->boundbox.yh);
+    p->boundbox.xl = Max(a->boundbox.xl, b->boundbox.xl);
+    p->boundbox.yl = Max(a->boundbox.yl, b->boundbox.yl);
+    
+    if (p->boundbox.xh < p->boundbox.xl || p->boundbox.yh < p->boundbox.yl)
+       {
+           pfree(p);
+           return ((POLYGON *) NULL);
+       }
+    
+    return (p);
+}
diff --git a/src/backend/access/rtree/rtree.c b/src/backend/access/rtree/rtree.c
new file mode 100644 (file)
index 0000000..865d956
--- /dev/null
@@ -0,0 +1,955 @@
+/*-------------------------------------------------------------------------
+ *
+ * rtree.c--
+ *    interface routines for the postgres rtree indexed access method.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+#include "utils/excid.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/rtree.h"
+#include "access/rtscan.h"
+#include "access/funcindex.h"
+#include "access/tupdesc.h"
+
+#include "nodes/execnodes.h"
+#include "nodes/plannodes.h"
+
+#include "executor/executor.h"
+#include "executor/tuptable.h"
+
+#include "catalog/index.h"
+
+typedef struct SPLITVEC {
+    OffsetNumber       *spl_left;
+    int                        spl_nleft;
+    char               *spl_ldatum;
+    OffsetNumber       *spl_right;
+    int                        spl_nright;
+    char               *spl_rdatum;
+} SPLITVEC;
+
+typedef struct RTSTATE {
+    func_ptr unionFn;          /* union function */
+    func_ptr sizeFn;           /* size function */
+    func_ptr interFn;          /* intersection function */
+} RTSTATE;
+
+/* non-export function prototypes */
+static InsertIndexResult rtdoinsert(Relation r, IndexTuple itup,
+                                   RTSTATE *rtstate);
+static void rttighten(Relation r, RTSTACK *stk, char *datum, int att_size,
+                     RTSTATE *rtstate);
+static InsertIndexResult dosplit(Relation r, Buffer buffer, RTSTACK *stack,
+                                IndexTuple itup, RTSTATE *rtstate);
+static void rtintinsert(Relation r, RTSTACK *stk, IndexTuple ltup,
+                       IndexTuple rtup, RTSTATE *rtstate);
+static void rtnewroot(Relation r, IndexTuple lt, IndexTuple rt);
+static void picksplit(Relation r, Page page, SPLITVEC *v, IndexTuple itup,
+                     RTSTATE *rtstate);
+static void RTInitBuffer(Buffer b, uint32 f);
+static OffsetNumber choose(Relation r, Page p, IndexTuple it,
+                          RTSTATE *rtstate);
+static int nospace(Page p, IndexTuple it);
+static void initRtstate(RTSTATE *rtstate, Relation index);
+
+
+void
+rtbuild(Relation heap,
+       Relation index,
+       int natts,
+       AttrNumber *attnum,
+       IndexStrategy istrat,
+       uint16 pcount,
+       Datum *params,
+       FuncIndexInfo *finfo,
+       PredInfo *predInfo)
+{
+    HeapScanDesc scan;
+    Buffer buffer;
+    AttrNumber i;
+    HeapTuple htup;
+    IndexTuple itup;
+    TupleDesc hd, id;
+    InsertIndexResult res;
+    Datum *d;
+    bool *nulls;
+    int nb, nh, ni;
+    ExprContext *econtext;
+    TupleTable tupleTable;
+    TupleTableSlot *slot;
+    Oid hrelid, irelid;
+    Node *pred, *oldPred;
+    RTSTATE rtState;
+
+    initRtstate(&rtState, index);
+               
+    /* rtrees only know how to do stupid locking now */
+    RelationSetLockForWrite(index);
+
+    pred = predInfo->pred;
+    oldPred = predInfo->oldPred;
+
+    /*
+     *  We expect to be called exactly once for any index relation.
+     *  If that's not the case, big trouble's what we have.
+     */
+    
+    if (oldPred == NULL && (nb = RelationGetNumberOfBlocks(index)) != 0)
+       elog(WARN, "%s already contains data", index->rd_rel->relname.data);
+    
+    /* initialize the root page (if this is a new index) */
+    if (oldPred == NULL) {
+       buffer = ReadBuffer(index, P_NEW);
+       RTInitBuffer(buffer, F_LEAF);
+       WriteBuffer(buffer);
+    }
+    
+    /* init the tuple descriptors and get set for a heap scan */
+    hd = RelationGetTupleDescriptor(heap);
+    id = RelationGetTupleDescriptor(index);
+    d = (Datum *)palloc(natts * sizeof (*d));
+    nulls = (bool *)palloc(natts * sizeof (*nulls));
+    
+    /*
+     * If this is a predicate (partial) index, we will need to evaluate the
+     * predicate using ExecQual, which requires the current tuple to be in a
+     * slot of a TupleTable.  In addition, ExecQual must have an ExprContext
+     * referring to that slot.  Here, we initialize dummy TupleTable and
+     * ExprContext objects for this purpose. --Nels, Feb '92
+     */
+#ifndef OMIT_PARTIAL_INDEX
+    if (pred != NULL || oldPred != NULL) {
+       tupleTable = ExecCreateTupleTable(1);
+       slot =  ExecAllocTableSlot(tupleTable);
+       econtext = makeNode(ExprContext);
+       FillDummyExprContext(econtext, slot, hd, buffer);
+    }
+#endif /* OMIT_PARTIAL_INDEX */    
+    scan = heap_beginscan(heap, 0, NowTimeQual, 0, (ScanKey) NULL);
+    htup = heap_getnext(scan, 0, &buffer);
+    
+    /* count the tuples as we insert them */
+    nh = ni = 0;
+    
+    for (; HeapTupleIsValid(htup); htup = heap_getnext(scan, 0, &buffer)) {
+       
+       nh++;
+       
+       /*
+        * If oldPred != NULL, this is an EXTEND INDEX command, so skip
+        * this tuple if it was already in the existing partial index
+        */
+       if (oldPred != NULL) {
+#ifndef OMIT_PARTIAL_INDEX
+           /*SetSlotContents(slot, htup); */
+           slot->val = htup;
+           if (ExecQual((List*)oldPred, econtext) == true) {
+               ni++;
+               continue;
+           }
+#endif /* OMIT_PARTIAL_INDEX */        
+       }
+       
+       /* Skip this tuple if it doesn't satisfy the partial-index predicate */
+       if (pred != NULL) {
+#ifndef OMIT_PARTIAL_INDEX
+           /*SetSlotContents(slot, htup); */
+           slot->val = htup;
+           if (ExecQual((List*)pred, econtext) == false)
+               continue;
+#endif /* OMIT_PARTIAL_INDEX */        
+       }
+       
+       ni++;
+       
+       /*
+        *  For the current heap tuple, extract all the attributes
+        *  we use in this index, and note which are null.
+        */
+       
+       for (i = 1; i <= natts; i++) {
+           int  attoff;
+           bool attnull;
+           
+           /*
+            *  Offsets are from the start of the tuple, and are
+            *  zero-based; indices are one-based.  The next call
+            *  returns i - 1.  That's data hiding for you.
+            */
+           
+           attoff = AttrNumberGetAttrOffset(i);
+           /*
+             d[attoff] = HeapTupleGetAttributeValue(htup, buffer,
+             */
+           d[attoff] = GetIndexValue(htup, 
+                                     hd,
+                                     attoff, 
+                                     attnum, 
+                                     finfo, 
+                                     &attnull,
+                                     buffer);
+           nulls[attoff] = (attnull ? 'n' : ' ');
+       }
+       
+       /* form an index tuple and point it at the heap tuple */
+       itup = index_formtuple(id, &d[0], nulls);
+       itup->t_tid = htup->t_ctid;
+       
+       /*
+        *  Since we already have the index relation locked, we
+        *  call rtdoinsert directly.  Normal access method calls
+        *  dispatch through rtinsert, which locks the relation
+        *  for write.  This is the right thing to do if you're
+        *  inserting single tups, but not when you're initializing
+        *  the whole index at once.
+        */
+       
+       res = rtdoinsert(index, itup, &rtState);
+       pfree(itup);
+       pfree(res);
+    }
+    
+    /* okay, all heap tuples are indexed */
+    heap_endscan(scan);
+    RelationUnsetLockForWrite(index);
+    
+    if (pred != NULL || oldPred != NULL) {
+#ifndef OMIT_PARTIAL_INDEX
+       ExecDestroyTupleTable(tupleTable, true);
+       pfree(econtext);
+#endif /* OMIT_PARTIAL_INDEX */        
+    }
+    
+    /*
+     *  Since we just counted the tuples in the heap, we update its
+     *  stats in pg_relation to guarantee that the planner takes
+     *  advantage of the index we just created.  UpdateStats() does a
+     *  CommandCounterIncrement(), which flushes changed entries from
+     *  the system relcache.  The act of constructing an index changes
+     *  these heap and index tuples in the system catalogs, so they
+     *  need to be flushed.  We close them to guarantee that they
+     *  will be.
+     */
+    
+    hrelid = heap->rd_id;
+    irelid = index->rd_id;
+    heap_close(heap);
+    index_close(index);
+    
+    UpdateStats(hrelid, nh, true);
+    UpdateStats(irelid, ni, false);
+    
+    if (oldPred != NULL) {
+       if (ni == nh) pred = NULL;
+       UpdateIndexPredicate(irelid, oldPred, pred);
+    }
+    
+    /* be tidy */
+    pfree(nulls);
+    pfree(d);
+}
+
+/*
+ *  rtinsert -- wrapper for rtree tuple insertion.
+ *
+ *    This is the public interface routine for tuple insertion in rtrees.
+ *    It doesn't do any work; just locks the relation and passes the buck.
+ */
+InsertIndexResult
+rtinsert(Relation r, IndexTuple itup)
+{
+    InsertIndexResult res;
+    RTSTATE rtState;
+
+    initRtstate(&rtState, r);
+    
+    RelationSetLockForWrite(r);
+    res = rtdoinsert(r, itup, &rtState);
+    
+    /* XXX two-phase locking -- don't unlock the relation until EOT */
+    return (res);
+}
+
+static InsertIndexResult
+rtdoinsert(Relation r, IndexTuple itup, RTSTATE *rtstate)
+{
+    Page page;
+    Buffer buffer;
+    BlockNumber blk;
+    IndexTuple which;
+    OffsetNumber l;
+    RTSTACK *stack;
+    InsertIndexResult res;
+    RTreePageOpaque opaque;
+    char *datum;
+    
+    blk = P_ROOT;
+    buffer = InvalidBuffer;
+    stack = (RTSTACK *) NULL;
+    
+    do {
+       /* let go of current buffer before getting next */
+       if (buffer != InvalidBuffer)
+           ReleaseBuffer(buffer);
+       
+       /* get next buffer */
+       buffer = ReadBuffer(r, blk);
+       page = (Page) BufferGetPage(buffer);
+       
+       opaque = (RTreePageOpaque) PageGetSpecialPointer(page);
+       if (!(opaque->flags & F_LEAF)) {
+           RTSTACK *n;
+           ItemId iid;
+           
+           n = (RTSTACK *) palloc(sizeof(RTSTACK));
+           n->rts_parent = stack;
+           n->rts_blk = blk;
+           n->rts_child = choose(r, page, itup, rtstate);
+           stack = n;
+           
+           iid = PageGetItemId(page, n->rts_child);
+           which = (IndexTuple) PageGetItem(page, iid);
+           blk = ItemPointerGetBlockNumber(&(which->t_tid));
+       }
+    } while (!(opaque->flags & F_LEAF));
+    
+    if (nospace(page, itup)) {
+       /* need to do a split */
+       res = dosplit(r, buffer, stack, itup, rtstate);
+       freestack(stack);
+       WriteBuffer(buffer);  /* don't forget to release buffer! */
+       return (res);
+    }
+    
+    /* add the item and write the buffer */
+    if (PageIsEmpty(page)) {
+       l = PageAddItem(page, (Item) itup, IndexTupleSize(itup),
+                       FirstOffsetNumber,
+                       LP_USED);
+    } else {
+       l = PageAddItem(page, (Item) itup, IndexTupleSize(itup),
+                       OffsetNumberNext(PageGetMaxOffsetNumber(page)),
+                       LP_USED);
+    }
+    
+    WriteBuffer(buffer);
+    
+    datum = (((char *) itup) + sizeof(IndexTupleData));
+    
+    /* now expand the page boundary in the parent to include the new child */
+    rttighten(r, stack, datum,
+             (IndexTupleSize(itup) - sizeof(IndexTupleData)), rtstate);
+    freestack(stack);
+    
+    /* build and return an InsertIndexResult for this insertion */
+    res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
+    ItemPointerSet(&(res->pointerData), blk, l);
+    
+    return (res);
+}
+
+static void
+rttighten(Relation r,
+         RTSTACK *stk,
+         char *datum,
+         int att_size,
+         RTSTATE *rtstate)
+{
+    char *oldud;
+    char *tdatum;
+    Page p;
+    float old_size, newd_size;
+    Buffer b;
+    
+    if (stk == (RTSTACK *) NULL)
+       return;
+    
+    b = ReadBuffer(r, stk->rts_blk);
+    p = BufferGetPage(b);
+    
+    oldud = (char *) PageGetItem(p, PageGetItemId(p, stk->rts_child));
+    oldud += sizeof(IndexTupleData);
+    
+    (*rtstate->sizeFn)(oldud, &old_size);
+    datum = (char *) (*rtstate->unionFn)(oldud, datum);
+    
+    (*rtstate->sizeFn)(datum, &newd_size);
+    
+    if (newd_size != old_size) {
+       TupleDesc td = RelationGetTupleDescriptor(r);
+       
+       if (td->attrs[0]->attlen < 0) {
+           /*
+            * This is an internal page, so 'oldud' had better be a
+            * union (constant-length) key, too.  (See comment below.)
+            */
+           Assert(VARSIZE(datum) == VARSIZE(oldud));
+           memmove(oldud, datum, VARSIZE(datum));
+       } else {
+           memmove(oldud, datum, att_size);
+       }
+       WriteBuffer(b);
+       
+       /*
+        *  The user may be defining an index on variable-sized data (like
+        *  polygons).  If so, we need to get a constant-sized datum for
+        *  insertion on the internal page.  We do this by calling the union
+        *  proc, which is guaranteed to return a rectangle.
+        */
+       
+       tdatum = (char *) (*rtstate->unionFn)(datum, datum);
+       rttighten(r, stk->rts_parent, tdatum, att_size, rtstate);
+       pfree(tdatum);
+    } else {
+       ReleaseBuffer(b);
+    }
+    pfree(datum);
+}
+
+/*
+ *  dosplit -- split a page in the tree.
+ *
+ *    This is the quadratic-cost split algorithm Guttman describes in
+ *    his paper.  The reason we chose it is that you can implement this
+ *    with less information about the data types on which you're operating.
+ */
+static InsertIndexResult
+dosplit(Relation r,
+       Buffer buffer,
+       RTSTACK *stack,
+       IndexTuple itup,
+       RTSTATE *rtstate)
+{
+    Page p;
+    Buffer leftbuf, rightbuf;
+    Page left, right;
+    ItemId itemid;
+    IndexTuple item;
+    IndexTuple ltup, rtup;
+    OffsetNumber maxoff;
+    OffsetNumber i;
+    OffsetNumber leftoff, rightoff;
+    BlockNumber lbknum, rbknum;
+    BlockNumber bufblock;
+    RTreePageOpaque opaque;
+    int blank;
+    InsertIndexResult res;
+    char *isnull;
+    SPLITVEC v;
+    TupleDesc tupDesc;
+    
+    isnull = (char *) palloc(r->rd_rel->relnatts);
+    for (blank = 0; blank < r->rd_rel->relnatts; blank++)
+       isnull[blank] = ' ';
+    p = (Page) BufferGetPage(buffer);
+    opaque = (RTreePageOpaque) PageGetSpecialPointer(p);
+    
+    /*
+     *  The root of the tree is the first block in the relation.  If
+     *  we're about to split the root, we need to do some hocus-pocus
+     *  to enforce this guarantee.
+     */
+    
+    if (BufferGetBlockNumber(buffer) == P_ROOT) {
+       leftbuf = ReadBuffer(r, P_NEW);
+       RTInitBuffer(leftbuf, opaque->flags);
+       lbknum = BufferGetBlockNumber(leftbuf);
+       left = (Page) BufferGetPage(leftbuf);
+    } else {
+       leftbuf = buffer;
+       IncrBufferRefCount(buffer);
+       lbknum = BufferGetBlockNumber(buffer);
+       left = (Page) PageGetTempPage(p, sizeof(RTreePageOpaqueData));
+    }
+    
+    rightbuf = ReadBuffer(r, P_NEW);
+    RTInitBuffer(rightbuf, opaque->flags);
+    rbknum = BufferGetBlockNumber(rightbuf);
+    right = (Page) BufferGetPage(rightbuf);
+    
+    picksplit(r, p, &v, itup, rtstate);
+    
+    leftoff = rightoff = FirstOffsetNumber;
+    maxoff = PageGetMaxOffsetNumber(p);
+    for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
+       itemid = PageGetItemId(p, i);
+       item = (IndexTuple) PageGetItem(p, itemid);
+       
+       if (i == *(v.spl_left)) {
+           (void) PageAddItem(left, (Item) item, IndexTupleSize(item),
+                              leftoff, LP_USED);
+           leftoff = OffsetNumberNext(leftoff);
+           v.spl_left++;       /* advance in left split vector */
+       } else {
+           (void) PageAddItem(right, (Item) item, IndexTupleSize(item),
+                              rightoff, LP_USED);
+           rightoff = OffsetNumberNext(rightoff);
+           v.spl_right++;      /* advance in right split vector */
+       }
+    }
+    
+    /* build an InsertIndexResult for this insertion */
+    res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
+    
+    /* now insert the new index tuple */
+    if (*(v.spl_left) != FirstOffsetNumber) {
+       (void) PageAddItem(left, (Item) itup, IndexTupleSize(itup),
+                          leftoff, LP_USED);
+       leftoff = OffsetNumberNext(leftoff);
+       ItemPointerSet(&(res->pointerData), lbknum, leftoff);
+    } else {
+       (void) PageAddItem(right, (Item) itup, IndexTupleSize(itup),
+                          rightoff, LP_USED);
+       rightoff = OffsetNumberNext(rightoff);
+       ItemPointerSet(&(res->pointerData), rbknum, rightoff);
+    }
+    
+    if ((bufblock = BufferGetBlockNumber(buffer)) != P_ROOT) {
+       PageRestoreTempPage(left, p);
+    }
+    WriteBuffer(leftbuf);
+    WriteBuffer(rightbuf);
+    
+    /*
+     *  Okay, the page is split.  We have three things left to do:
+     *
+     *    1)  Adjust any active scans on this index to cope with changes
+     *        we introduced in its structure by splitting this page.
+     *
+     *    2)  "Tighten" the bounding box of the pointer to the left
+     *       page in the parent node in the tree, if any.  Since we
+     *       moved a bunch of stuff off the left page, we expect it
+     *       to get smaller.  This happens in the internal insertion
+     *        routine.
+     *
+     *    3)  Insert a pointer to the right page in the parent.  This
+     *       may cause the parent to split.  If it does, we need to
+     *       repeat steps one and two for each split node in the tree.
+     */
+    
+    /* adjust active scans */
+    rtadjscans(r, RTOP_SPLIT, bufblock, FirstOffsetNumber);
+    
+    tupDesc = r->rd_att;
+    ltup = (IndexTuple) index_formtuple(tupDesc,
+                                       (Datum *) &(v.spl_ldatum), isnull);
+    rtup = (IndexTuple) index_formtuple(tupDesc,
+                                       (Datum *) &(v.spl_rdatum), isnull);
+    pfree(isnull);
+    
+    /* set pointers to new child pages in the internal index tuples */
+    ItemPointerSet(&(ltup->t_tid), lbknum, 1);
+    ItemPointerSet(&(rtup->t_tid), rbknum, 1);
+    
+    rtintinsert(r, stack, ltup, rtup, rtstate);
+    
+    pfree(ltup);
+    pfree(rtup);
+    
+    return (res);
+}
+
+static void
+rtintinsert(Relation r,
+           RTSTACK *stk,
+           IndexTuple ltup,
+           IndexTuple rtup,
+           RTSTATE *rtstate)
+{
+    IndexTuple old;
+    Buffer b;
+    Page p;
+    char *ldatum, *rdatum, *newdatum;
+    InsertIndexResult res;
+    
+    if (stk == (RTSTACK *) NULL) {
+       rtnewroot(r, ltup, rtup);
+       return;
+    }
+    
+    b = ReadBuffer(r, stk->rts_blk);
+    p = BufferGetPage(b);
+    old = (IndexTuple) PageGetItem(p, PageGetItemId(p, stk->rts_child));
+    
+    /*
+     *  This is a hack.  Right now, we force rtree keys to be constant size.
+     *  To fix this, need delete the old key and add both left and right
+     *  for the two new pages.  The insertion of left may force a split if
+     *  the new left key is bigger than the old key.
+     */
+    
+    if (IndexTupleSize(old) != IndexTupleSize(ltup))
+       elog(WARN, "Variable-length rtree keys are not supported.");
+    
+    /* install pointer to left child */
+    memmove(old, ltup,IndexTupleSize(ltup));
+    
+    if (nospace(p, rtup)) {
+       newdatum = (((char *) ltup) + sizeof(IndexTupleData));
+       rttighten(r, stk->rts_parent, newdatum,
+                 (IndexTupleSize(ltup) - sizeof(IndexTupleData)), rtstate);
+       res = dosplit(r, b, stk->rts_parent, rtup, rtstate);
+       WriteBuffer(b);  /* don't forget to release buffer!  - 01/31/94 */
+       pfree(res);
+    } else {
+       (void) PageAddItem(p, (Item) rtup, IndexTupleSize(rtup),
+                          PageGetMaxOffsetNumber(p), LP_USED);
+       WriteBuffer(b);
+       ldatum = (((char *) ltup) + sizeof(IndexTupleData));
+       rdatum = (((char *) rtup) + sizeof(IndexTupleData));
+       newdatum = (char *) (*rtstate->unionFn)(ldatum, rdatum);
+       
+       rttighten(r, stk->rts_parent, newdatum,
+                 (IndexTupleSize(rtup) - sizeof(IndexTupleData)), rtstate);
+       
+       pfree(newdatum);
+    }
+}
+
+static void
+rtnewroot(Relation r, IndexTuple lt, IndexTuple rt)
+{
+    Buffer b;
+    Page p;
+    
+    b = ReadBuffer(r, P_ROOT);
+    RTInitBuffer(b, 0);
+    p = BufferGetPage(b);
+    (void) PageAddItem(p, (Item) lt, IndexTupleSize(lt),
+                      FirstOffsetNumber, LP_USED);
+    (void) PageAddItem(p, (Item) rt, IndexTupleSize(rt),
+                      OffsetNumberNext(FirstOffsetNumber), LP_USED);
+    WriteBuffer(b);
+}
+
+static void
+picksplit(Relation r,
+         Page page,
+         SPLITVEC *v,
+         IndexTuple itup,
+         RTSTATE *rtstate)
+{
+    OffsetNumber maxoff;
+    OffsetNumber i, j;
+    IndexTuple item_1, item_2;
+    char *datum_alpha, *datum_beta;
+    char *datum_l, *datum_r;
+    char *union_d, *union_dl, *union_dr;
+    char *inter_d;
+    bool firsttime;
+    float size_alpha, size_beta, size_union, size_inter;
+    float size_waste, waste;
+    float size_l, size_r;
+    int nbytes;
+    OffsetNumber seed_1 = 0, seed_2 = 0;
+    OffsetNumber *left, *right;
+    
+    maxoff = PageGetMaxOffsetNumber(page);
+    
+    nbytes = (maxoff + 2) * sizeof(OffsetNumber);
+    v->spl_left = (OffsetNumber *) palloc(nbytes);
+    v->spl_right = (OffsetNumber *) palloc(nbytes);
+    
+    firsttime = true;
+    waste = 0.0;
+    
+    for (i = FirstOffsetNumber; i < maxoff; i = OffsetNumberNext(i)) {
+       item_1 = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
+       datum_alpha = ((char *) item_1) + sizeof(IndexTupleData);
+       for (j = OffsetNumberNext(i); j <= maxoff; j = OffsetNumberNext(j)) {
+           item_2 = (IndexTuple) PageGetItem(page, PageGetItemId(page, j));
+           datum_beta = ((char *) item_2) + sizeof(IndexTupleData);
+           
+           /* compute the wasted space by unioning these guys */
+           union_d = (char *)(rtstate->unionFn)(datum_alpha, datum_beta);
+           (rtstate->sizeFn)(union_d, &size_union);
+           inter_d = (char *)(rtstate->interFn)(datum_alpha, datum_beta);
+           (rtstate->sizeFn)(inter_d, &size_inter);
+           size_waste = size_union - size_inter;
+           
+           pfree(union_d);
+           
+           if (inter_d != (char *) NULL)
+               pfree(inter_d);
+           
+           /*
+            *  are these a more promising split that what we've
+            *  already seen?
+            */
+           
+           if (size_waste > waste || firsttime) {
+               waste = size_waste;
+               seed_1 = i;
+               seed_2 = j;
+               firsttime = false;
+           }
+       }
+    }
+    
+    left = v->spl_left;
+    v->spl_nleft = 0;
+    right = v->spl_right;
+    v->spl_nright = 0;
+    
+    item_1 = (IndexTuple) PageGetItem(page, PageGetItemId(page, seed_1));
+    datum_alpha = ((char *) item_1) + sizeof(IndexTupleData);
+    datum_l = (char *)(*rtstate->unionFn)(datum_alpha, datum_alpha);
+    (*rtstate->sizeFn)(datum_l, &size_l);
+    item_2 = (IndexTuple) PageGetItem(page, PageGetItemId(page, seed_2));
+    datum_beta = ((char *) item_2) + sizeof(IndexTupleData);
+    datum_r = (char *)(*rtstate->unionFn)(datum_beta, datum_beta);
+    (*rtstate->sizeFn)(datum_r, &size_r);
+    
+    /*
+     *  Now split up the regions between the two seeds.  An important
+     *  property of this split algorithm is that the split vector v
+     *  has the indices of items to be split in order in its left and
+     *  right vectors.  We exploit this property by doing a merge in
+     *  the code that actually splits the page.
+     *
+     *  For efficiency, we also place the new index tuple in this loop.
+     *  This is handled at the very end, when we have placed all the
+     *  existing tuples and i == maxoff + 1.
+     */
+    
+    maxoff = OffsetNumberNext(maxoff);
+    for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
+       
+       /*
+        *  If we've already decided where to place this item, just
+        *  put it on the right list.  Otherwise, we need to figure
+        *  out which page needs the least enlargement in order to
+        *  store the item.
+        */
+       
+       if (i == seed_1) {
+           *left++ = i;
+           v->spl_nleft++;
+           continue;
+       } else if (i == seed_2) {
+           *right++ = i;
+           v->spl_nright++;
+           continue;
+       }
+       
+       /* okay, which page needs least enlargement? */ 
+       if (i == maxoff) {
+           item_1 = itup;
+       } else {
+           item_1 = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
+       }
+       
+       datum_alpha = ((char *) item_1) + sizeof(IndexTupleData);
+       union_dl = (char *)(*rtstate->unionFn)(datum_l, datum_alpha);
+       union_dr = (char *)(*rtstate->unionFn)(datum_r, datum_alpha);
+       (*rtstate->sizeFn)(union_dl, &size_alpha);
+       (*rtstate->sizeFn)(union_dr, &size_beta);
+       
+       /* pick which page to add it to */
+       if (size_alpha - size_l < size_beta - size_r) {
+           pfree(datum_l);
+           pfree(union_dr);
+           datum_l = union_dl;
+           size_l = size_alpha;
+           *left++ = i;
+           v->spl_nleft++;
+       } else {
+           pfree(datum_r);
+           pfree(union_dl);
+           datum_r = union_dr;
+           size_r = size_alpha;
+           *right++ = i;
+           v->spl_nright++;
+       }
+    }
+    *left = *right = FirstOffsetNumber;        /* sentinel value, see dosplit() */
+    
+    v->spl_ldatum = datum_l;
+    v->spl_rdatum = datum_r;
+}
+
+static void
+RTInitBuffer(Buffer b, uint32 f)
+{
+    RTreePageOpaque opaque;
+    Page page;
+    Size pageSize;
+    
+    pageSize = BufferGetPageSize(b);
+    
+    page = BufferGetPage(b);
+    memset(page, 0, (int) pageSize);
+    PageInit(page, pageSize, sizeof(RTreePageOpaqueData));
+    
+    opaque = (RTreePageOpaque) PageGetSpecialPointer(page);
+    opaque->flags = f;
+}
+
+static OffsetNumber
+choose(Relation r, Page p, IndexTuple it, RTSTATE *rtstate)
+{
+    OffsetNumber maxoff;
+    OffsetNumber i;
+    char *ud, *id;
+    char *datum;
+    float usize, dsize;
+    OffsetNumber which;
+    float which_grow;
+    
+    id = ((char *) it) + sizeof(IndexTupleData);
+    maxoff = PageGetMaxOffsetNumber(p);
+    which_grow = -1.0;
+    which = -1;
+    
+    for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) {
+       datum = (char *) PageGetItem(p, PageGetItemId(p, i));
+       datum += sizeof(IndexTupleData);
+       (*rtstate->sizeFn)(datum, &dsize);
+       ud = (char *) (*rtstate->unionFn)(datum, id);
+       (*rtstate->sizeFn)(ud, &usize);
+       pfree(ud);
+       if (which_grow < 0 || usize - dsize < which_grow) {
+           which = i;
+           which_grow = usize - dsize;
+           if (which_grow == 0)
+               break;
+       }
+    }
+    
+    return (which);
+}
+
+static int
+nospace(Page p, IndexTuple it)
+{
+    return (PageGetFreeSpace(p) < IndexTupleSize(it));
+}
+
+void
+freestack(RTSTACK *s)
+{
+    RTSTACK *p;
+    
+    while (s != (RTSTACK *) NULL) {
+       p = s->rts_parent;
+       pfree(s);
+       s = p;
+    }
+}
+
+char *
+rtdelete(Relation r, ItemPointer tid)
+{
+    BlockNumber blkno;
+    OffsetNumber offnum;
+    Buffer buf;
+    Page page;
+    
+    /* must write-lock on delete */
+    RelationSetLockForWrite(r);
+    
+    blkno = ItemPointerGetBlockNumber(tid);
+    offnum = ItemPointerGetOffsetNumber(tid);
+    
+    /* adjust any scans that will be affected by this deletion */
+    rtadjscans(r, RTOP_DEL, blkno, offnum);
+    
+    /* delete the index tuple */
+    buf = ReadBuffer(r, blkno);
+    page = BufferGetPage(buf);
+    
+    PageIndexTupleDelete(page, offnum);
+    
+    WriteBuffer(buf);
+    
+    /* XXX -- two-phase locking, don't release the write lock */
+    return ((char *) NULL);
+}
+
+static void initRtstate(RTSTATE *rtstate, Relation index)
+{
+    RegProcedure union_proc, size_proc, inter_proc;
+    func_ptr user_fn;
+    int pronargs;
+
+    union_proc = index_getprocid(index, 1, RT_UNION_PROC);
+    size_proc = index_getprocid(index, 1, RT_SIZE_PROC);
+    inter_proc = index_getprocid(index, 1, RT_INTER_PROC);
+    fmgr_info(union_proc, &user_fn, &pronargs);
+    rtstate->unionFn = user_fn;
+    fmgr_info(size_proc, &user_fn, &pronargs);
+    rtstate->sizeFn = user_fn;
+    fmgr_info(inter_proc, &user_fn, &pronargs);
+    rtstate->interFn = user_fn;
+    return;
+}
+
+#define RTDEBUG
+#ifdef RTDEBUG
+#include "utils/geo-decls.h"
+
+void
+_rtdump(Relation r)
+{
+    Buffer buf;
+    Page page;
+    OffsetNumber offnum, maxoff;
+    BlockNumber blkno;
+    BlockNumber nblocks;
+    RTreePageOpaque po;
+    IndexTuple itup;
+    BlockNumber itblkno;
+    OffsetNumber itoffno;
+    char *datum;
+    char *itkey;
+    
+    nblocks = RelationGetNumberOfBlocks(r);
+    for (blkno = 0; blkno < nblocks; blkno++) {
+       buf = ReadBuffer(r, blkno);
+       page = BufferGetPage(buf);
+       po = (RTreePageOpaque) PageGetSpecialPointer(page);
+       maxoff = PageGetMaxOffsetNumber(page);
+       printf("Page %d maxoff %d <%s>\n", blkno, maxoff,
+              (po->flags & F_LEAF ? "LEAF" : "INTERNAL"));
+       
+       if (PageIsEmpty(page)) {
+           ReleaseBuffer(buf);
+           continue;
+       }
+       
+       for (offnum = FirstOffsetNumber;
+            offnum <= maxoff;
+            offnum = OffsetNumberNext(offnum)) {
+           itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
+           itblkno = ItemPointerGetBlockNumber(&(itup->t_tid));
+           itoffno = ItemPointerGetOffsetNumber(&(itup->t_tid));
+           datum = ((char *) itup);
+           datum += sizeof(IndexTupleData);
+           itkey = (char *) box_out((BOX *) datum);
+           printf("\t[%d] size %d heap <%d,%d> key:%s\n",
+                  offnum, IndexTupleSize(itup), itblkno, itoffno, itkey);
+           pfree(itkey);
+       }
+       
+       ReleaseBuffer(buf);
+    }
+}
+#endif /* defined RTDEBUG */
+
diff --git a/src/backend/access/rtree/rtscan.c b/src/backend/access/rtree/rtscan.c
new file mode 100644 (file)
index 0000000..05a3c82
--- /dev/null
@@ -0,0 +1,392 @@
+/*-------------------------------------------------------------------------
+ *
+ * rtscan.c--
+ *    routines to manage scans on index relations
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+#include "postgres.h"
+
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/rtree.h"
+#include "access/rtstrat.h"
+
+/* routines defined and used here */
+static void rtregscan(IndexScanDesc s);
+static void rtdropscan(IndexScanDesc s);
+static void rtadjone(IndexScanDesc s, int op, BlockNumber blkno,
+                    OffsetNumber offnum);
+static void adjuststack(RTSTACK *stk, BlockNumber blkno,
+                       OffsetNumber offnum);
+static void adjustiptr(IndexScanDesc s, ItemPointer iptr,
+                      int op, BlockNumber blkno, OffsetNumber offnum);
+
+/*
+ *  Whenever we start an rtree scan in a backend, we register it in private
+ *  space.  Then if the rtree index gets updated, we check all registered
+ *  scans and adjust them if the tuple they point at got moved by the
+ *  update.  We only need to do this in private space, because when we update
+ *  an rtree we have a write lock on the tree, so no other process can have
+ *  any locks at all on it.  A single transaction can have write and read
+ *  locks on the same object, so that's why we need to handle this case.
+ */
+
+typedef struct RTScanListData {
+    IndexScanDesc              rtsl_scan;
+    struct RTScanListData      *rtsl_next;
+} RTScanListData;
+
+typedef RTScanListData *RTScanList;
+
+/* pointer to list of local scans on rtrees */
+static RTScanList RTScans = (RTScanList) NULL;
+     
+IndexScanDesc
+rtbeginscan(Relation r,
+           bool fromEnd,
+           uint16 nkeys,
+           ScanKey key)
+{
+    IndexScanDesc s;
+    
+    RelationSetLockForRead(r);
+    s = RelationGetIndexScan(r, fromEnd, nkeys, key);
+    rtregscan(s);
+    
+    return (s);
+}
+
+void
+rtrescan(IndexScanDesc s, bool fromEnd, ScanKey key)
+{
+    RTreeScanOpaque p;
+    RegProcedure internal_proc;
+    int i;
+    
+    if (!IndexScanIsValid(s)) {
+       elog(WARN, "rtrescan: invalid scan.");
+       return;
+    }
+    
+    /*
+     *  Clear all the pointers.
+     */
+    
+    ItemPointerSetInvalid(&s->previousItemData);
+    ItemPointerSetInvalid(&s->currentItemData);
+    ItemPointerSetInvalid(&s->nextItemData);
+    ItemPointerSetInvalid(&s->previousMarkData);
+    ItemPointerSetInvalid(&s->currentMarkData);
+    ItemPointerSetInvalid(&s->nextMarkData);
+    
+    /*
+     *  Set flags.
+     */
+    if (RelationGetNumberOfBlocks(s->relation) == 0) {
+       s->flags = ScanUnmarked;
+    } else if (fromEnd) {
+       s->flags = ScanUnmarked | ScanUncheckedPrevious;
+    } else {
+       s->flags = ScanUnmarked | ScanUncheckedNext;
+    }
+    
+    s->scanFromEnd = fromEnd;
+    
+    if (s->numberOfKeys > 0) {
+       memmove(s->keyData,
+               key,
+               s->numberOfKeys * sizeof(ScanKeyData));
+    }
+    
+    p = (RTreeScanOpaque) s->opaque;
+    if (p != (RTreeScanOpaque) NULL) {
+       freestack(p->s_stack);
+       freestack(p->s_markstk);
+       p->s_stack = p->s_markstk = (RTSTACK *) NULL;
+       p->s_flags = 0x0;
+    } else {
+       /* initialize opaque data */
+       p = (RTreeScanOpaque) palloc(sizeof(RTreeScanOpaqueData));
+       p->s_internalKey =
+           (ScanKey) palloc(sizeof(ScanKeyData) * s->numberOfKeys);
+       p->s_stack = p->s_markstk = (RTSTACK *) NULL;
+       p->s_internalNKey = s->numberOfKeys;
+       p->s_flags = 0x0;
+       for (i = 0; i < s->numberOfKeys; i++)
+           p->s_internalKey[i].sk_argument = s->keyData[i].sk_argument;
+       s->opaque = p;
+       if (s->numberOfKeys > 0) {
+           
+           /*
+            *  Scans on internal pages use different operators than they
+            *  do on leaf pages.  For example, if the user wants all boxes
+            *  that exactly match (x1,y1,x2,y2), then on internal pages
+            *  we need to find all boxes that contain (x1,y1,x2,y2).
+            */
+           
+           for (i = 0; i < s->numberOfKeys; i++) {
+               internal_proc = RTMapOperator(s->relation,
+                                             s->keyData[i].sk_attno,
+                                             s->keyData[i].sk_procedure);
+               ScanKeyEntryInitialize(&(p->s_internalKey[i]),
+                                      s->keyData[i].sk_flags,
+                                      s->keyData[i].sk_attno,
+                                      internal_proc,
+                                      s->keyData[i].sk_argument);
+           }
+       }
+    }
+}
+
+void
+rtmarkpos(IndexScanDesc s)
+{
+    RTreeScanOpaque p;
+    RTSTACK *o, *n, *tmp;
+    
+    s->currentMarkData = s->currentItemData;
+    p = (RTreeScanOpaque) s->opaque;
+    if (p->s_flags & RTS_CURBEFORE)
+       p->s_flags |= RTS_MRKBEFORE;
+    else
+       p->s_flags &= ~RTS_MRKBEFORE;
+    
+    o = (RTSTACK *) NULL;
+    n = p->s_stack;
+    
+    /* copy the parent stack from the current item data */
+    while (n != (RTSTACK *) NULL) {
+       tmp = (RTSTACK *) palloc(sizeof(RTSTACK));
+       tmp->rts_child = n->rts_child;
+       tmp->rts_blk = n->rts_blk;
+       tmp->rts_parent = o;
+       o = tmp;
+       n = n->rts_parent;
+    }
+    
+    freestack(p->s_markstk);
+    p->s_markstk = o;
+}
+
+void
+rtrestrpos(IndexScanDesc s)
+{
+    RTreeScanOpaque p;
+    RTSTACK *o, *n, *tmp;
+    
+    s->currentItemData = s->currentMarkData;
+    p = (RTreeScanOpaque) s->opaque;
+    if (p->s_flags & RTS_MRKBEFORE)
+       p->s_flags |= RTS_CURBEFORE;
+    else
+       p->s_flags &= ~RTS_CURBEFORE;
+    
+    o = (RTSTACK *) NULL;
+    n = p->s_markstk;
+    
+    /* copy the parent stack from the current item data */
+    while (n != (RTSTACK *) NULL) {
+       tmp = (RTSTACK *) palloc(sizeof(RTSTACK));
+       tmp->rts_child = n->rts_child;
+       tmp->rts_blk = n->rts_blk;
+       tmp->rts_parent = o;
+       o = tmp;
+       n = n->rts_parent;
+    }
+    
+    freestack(p->s_stack);
+    p->s_stack = o;
+}
+
+void
+rtendscan(IndexScanDesc s)
+{
+    RTreeScanOpaque p;
+    
+    p = (RTreeScanOpaque) s->opaque;
+    
+    if (p != (RTreeScanOpaque) NULL) {
+       freestack(p->s_stack);
+       freestack(p->s_markstk);
+    }
+    
+    rtdropscan(s);
+    /* XXX don't unset read lock -- two-phase locking */
+}
+
+static void
+rtregscan(IndexScanDesc s)
+{
+    RTScanList l;
+    
+    l = (RTScanList) palloc(sizeof(RTScanListData));
+    l->rtsl_scan = s;
+    l->rtsl_next = RTScans;
+    RTScans = l;
+}
+
+static void
+rtdropscan(IndexScanDesc s)
+{
+    RTScanList l;
+    RTScanList prev;
+    
+    prev = (RTScanList) NULL;
+    
+    for (l = RTScans;
+        l != (RTScanList) NULL && l->rtsl_scan != s;
+        l = l->rtsl_next) {
+       prev = l;
+    }
+    
+    if (l == (RTScanList) NULL)
+       elog(WARN, "rtree scan list corrupted -- cannot find 0x%lx", s);
+    
+    if (prev == (RTScanList) NULL)
+       RTScans = l->rtsl_next;
+    else
+       prev->rtsl_next = l->rtsl_next;
+    
+    pfree(l);
+}
+
+void
+rtadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum)
+{
+    RTScanList l;
+    Oid relid;
+    
+    relid = r->rd_id;
+    for (l = RTScans; l != (RTScanList) NULL; l = l->rtsl_next) {
+       if (l->rtsl_scan->relation->rd_id == relid)
+           rtadjone(l->rtsl_scan, op, blkno, offnum);
+    }
+}
+
+/*
+ *  rtadjone() -- adjust one scan for update.
+ *
+ *     By here, the scan passed in is on a modified relation.  Op tells
+ *     us what the modification is, and blkno and offind tell us what
+ *     block and offset index were affected.  This routine checks the
+ *     current and marked positions, and the current and marked stacks,
+ *     to see if any stored location needs to be changed because of the
+ *     update.  If so, we make the change here.
+ */
+static void
+rtadjone(IndexScanDesc s,
+        int op,
+        BlockNumber blkno,
+        OffsetNumber offnum)
+{
+    RTreeScanOpaque so;
+    
+    adjustiptr(s, &(s->currentItemData), op, blkno, offnum);
+    adjustiptr(s, &(s->currentMarkData), op, blkno, offnum);
+    
+    so = (RTreeScanOpaque) s->opaque;
+    
+    if (op == RTOP_SPLIT) {
+       adjuststack(so->s_stack, blkno, offnum);
+       adjuststack(so->s_markstk, blkno, offnum);
+    }
+}
+
+/*
+ *  adjustiptr() -- adjust current and marked item pointers in the scan
+ *
+ *     Depending on the type of update and the place it happened, we
+ *     need to do nothing, to back up one record, or to start over on
+ *     the same page.
+ */
+static void
+adjustiptr(IndexScanDesc s,
+          ItemPointer iptr,
+          int op,
+          BlockNumber blkno,
+          OffsetNumber offnum)
+{
+    OffsetNumber curoff;
+    RTreeScanOpaque so;
+    
+    if (ItemPointerIsValid(iptr)) {
+       if (ItemPointerGetBlockNumber(iptr) == blkno) {
+           curoff = ItemPointerGetOffsetNumber(iptr);
+           so = (RTreeScanOpaque) s->opaque;
+           
+           switch (op) {
+           case RTOP_DEL:
+               /* back up one if we need to */
+               if (curoff >= offnum) {
+                   
+                   if (curoff > FirstOffsetNumber) {
+                       /* just adjust the item pointer */
+                       ItemPointerSet(iptr, blkno, OffsetNumberPrev(curoff));
+                   } else {
+                       /* remember that we're before the current tuple */
+                       ItemPointerSet(iptr, blkno, FirstOffsetNumber);
+                       if (iptr == &(s->currentItemData))
+                           so->s_flags |= RTS_CURBEFORE;
+                       else
+                           so->s_flags |= RTS_MRKBEFORE;
+                   }
+               }
+               break;
+               
+           case RTOP_SPLIT:
+               /* back to start of page on split */
+               ItemPointerSet(iptr, blkno, FirstOffsetNumber);
+               if (iptr == &(s->currentItemData))
+                   so->s_flags &= ~RTS_CURBEFORE;
+               else
+                   so->s_flags &= ~RTS_MRKBEFORE;
+               break;
+               
+           default:
+               elog(WARN, "Bad operation in rtree scan adjust: %d", op);
+           }
+       }
+    }
+}
+
+/*
+ *  adjuststack() -- adjust the supplied stack for a split on a page in
+ *                  the index we're scanning.
+ *
+ *     If a page on our parent stack has split, we need to back up to the
+ *     beginning of the page and rescan it.  The reason for this is that
+ *     the split algorithm for rtrees doesn't order tuples in any useful
+ *     way on a single page.  This means on that a split, we may wind up
+ *     looking at some heap tuples more than once.  This is handled in the
+ *     access method update code for heaps; if we've modified the tuple we
+ *     are looking at already in this transaction, we ignore the update
+ *     request.
+ */
+/*ARGSUSED*/
+static void
+adjuststack(RTSTACK *stk,
+           BlockNumber blkno,
+           OffsetNumber offnum)
+{
+    while (stk != (RTSTACK *) NULL) {
+       if (stk->rts_blk == blkno)
+           stk->rts_child = FirstOffsetNumber;
+       
+       stk = stk->rts_parent;
+    }
+}
diff --git a/src/backend/access/rtree/rtstrat.c b/src/backend/access/rtree/rtstrat.c
new file mode 100644 (file)
index 0000000..97196ce
--- /dev/null
@@ -0,0 +1,239 @@
+/*-------------------------------------------------------------------------
+ *
+ * rtstrat.c--
+ *    strategy map data for rtrees.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+
+#include "utils/rel.h"
+
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+
+#include "access/istrat.h"
+#include "access/rtree.h"
+
+/*
+ *  Note:  negate, commute, and negatecommute all assume that operators are
+ *        ordered as follows in the strategy map:
+ *
+ *     left, left-or-overlap, overlap, right-or-overlap, right, same,
+ *     contains, contained-by
+ *
+ *  The negate, commute, and negatecommute arrays are used by the planner
+ *  to plan indexed scans over data that appears in the qualificiation in
+ *  a boolean negation, or whose operands appear in the wrong order.  For
+ *  example, if the operator "<%" means "contains", and the user says
+ *
+ *     where not rel.box <% "(10,10,20,20)"::box
+ *
+ *  the planner can plan an index scan by noting that rtree indices have
+ *  an operator in their operator class for negating <%.
+ *
+ *  Similarly, if the user says something like
+ *
+ *     where "(10,10,20,20)"::box <% rel.box
+ *
+ *  the planner can see that the rtree index on rel.box has an operator in
+ *  its opclass for commuting <%, and plan the scan using that operator.
+ *  This added complexity in the access methods makes the planner a lot easier
+ *  to write.
+ */
+
+/* if a op b, what operator tells us if (not a op b)? */
+static StrategyNumber  RTNegate[RTNStrategies] = {
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy
+    };
+
+/* if a op_1 b, what is the operator op_2 such that b op_2 a? */
+static StrategyNumber  RTCommute[RTNStrategies] = {
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy
+    };
+
+/* if a op_1 b, what is the operator op_2 such that (b !op_2 a)? */
+static StrategyNumber  RTNegateCommute[RTNStrategies] = {
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy,
+    InvalidStrategy
+    };
+
+/*
+ *  Now do the TermData arrays.  These exist in case the user doesn't give
+ *  us a full set of operators for a particular operator class.  The idea
+ *  is that by making multiple comparisons using any one of the supplied
+ *  operators, we can decide whether two n-dimensional polygons are equal.
+ *  For example, if a contains b and b contains a, we may conclude that
+ *  a and b are equal.
+ *
+ *  The presence of the TermData arrays in all this is a historical accident.
+ *  Early in the development of the POSTGRES access methods, it was believed
+ *  that writing functions was harder than writing arrays.  This is wrong;
+ *  TermData is hard to understand and hard to get right.  In general, when
+ *  someone populates a new operator class, the populate it completely.  If
+ *  Mike Hirohama had forced Cimarron Taylor to populate the strategy map
+ *  for btree int2_ops completely in 1988, you wouldn't have to deal with
+ *  all this now.  Too bad for you.
+ *
+ *  Since you can't necessarily do this in all cases (for example, you can't
+ *  do it given only "intersects" or "disjoint"), TermData arrays for some
+ *  operators don't appear below.
+ *
+ *  Note that if you DO supply all the operators required in a given opclass
+ *  by inserting them into the pg_opclass system catalog, you can get away
+ *  without doing all this TermData stuff.  Since the rtree code is intended
+ *  to be a reference for access method implementors, I'm doing TermData
+ *  correctly here.
+ *
+ *  Note on style:  these are all actually of type StrategyTermData, but
+ *  since those have variable-length data at the end of the struct we can't
+ *  properly initialize them if we declare them to be what they are.
+ */
+
+/* if you only have "contained-by", how do you determine equality? */
+static uint16 RTContainedByTermData[] = {
+    2,                                 /* make two comparisons */
+    RTContainedByStrategyNumber,               /* use "a contained-by b" */
+    0x0,                                       /* without any magic */
+    RTContainedByStrategyNumber,               /* then use contained-by, */
+    SK_COMMUTE                         /* swapping a and b */
+    };
+
+/* if you only have "contains", how do you determine equality? */
+static uint16 RTContainsTermData[] = {
+    2,                                 /* make two comparisons */
+    RTContainsStrategyNumber,          /* use "a contains b" */
+    0x0,                                       /* without any magic */
+    RTContainsStrategyNumber,          /* then use contains again, */
+    SK_COMMUTE                         /* swapping a and b */
+    };
+
+/* now put all that together in one place for the planner */
+static StrategyTerm RTEqualExpressionData[] = {
+    (StrategyTerm) RTContainedByTermData,
+    (StrategyTerm) RTContainsTermData,
+    NULL
+    };
+
+/*
+ *  If you were sufficiently attentive to detail, you would go through
+ *  the ExpressionData pain above for every one of the seven strategies
+ *  we defined.  I am not.  Now we declare the StrategyEvaluationData
+ *  structure that gets shipped around to help the planner and the access
+ *  method decide what sort of scan it should do, based on (a) what the
+ *  user asked for, (b) what operators are defined for a particular opclass,
+ *  and (c) the reams of information we supplied above.
+ *
+ *  The idea of all of this initialized data is to make life easier on the
+ *  user when he defines a new operator class to use this access method.
+ *  By filling in all the data, we let him get away with leaving holes in his
+ *  operator class, and still let him use the index.  The added complexity
+ *  in the access methods just isn't worth the trouble, though.
+ */
+
+static StrategyEvaluationData RTEvaluationData = {
+    RTNStrategies,                             /* # of strategies */
+    (StrategyTransformMap) RTNegate,   /* how to do (not qual) */
+    (StrategyTransformMap) RTCommute,  /* how to swap operands */
+    (StrategyTransformMap) RTNegateCommute,    /* how to do both */
+    {
+       NULL,                                   /* express left */
+       NULL,                                   /* express overleft */
+       NULL,                                   /* express over */
+       NULL,                                   /* express overright */
+       NULL,                                   /* express right */
+       (StrategyExpression) RTEqualExpressionData,     /* express same */
+       NULL,                                   /* express contains */
+       NULL,                                   /* express contained-by */
+       NULL,
+       NULL,
+       NULL
+    }
+};
+
+/*
+ *  Okay, now something peculiar to rtrees that doesn't apply to most other
+ *  indexing structures:  When we're searching a tree for a given value, we
+ *  can't do the same sorts of comparisons on internal node entries as we
+ *  do at leaves.  The reason is that if we're looking for (say) all boxes
+ *  that are the same as (0,0,10,10), then we need to find all leaf pages
+ *  that overlap that region.  So internally we search for overlap, and at
+ *  the leaf we search for equality.
+ *
+ *  This array maps leaf search operators to the internal search operators.
+ *  We assume the normal ordering on operators:
+ *
+ *     left, left-or-overlap, overlap, right-or-overlap, right, same,
+ *     contains, contained-by
+ */
+static StrategyNumber RTOperMap[RTNStrategies] = {
+    RTOverLeftStrategyNumber,
+    RTOverLeftStrategyNumber,
+    RTOverlapStrategyNumber,
+    RTOverRightStrategyNumber,
+    RTOverRightStrategyNumber,
+    RTContainsStrategyNumber,
+    RTContainsStrategyNumber,
+    RTOverlapStrategyNumber
+    };
+
+StrategyNumber
+RelationGetRTStrategy(Relation r,
+                     AttrNumber attnum,
+                     RegProcedure proc)
+{
+    return (RelationGetStrategy(r, attnum, &RTEvaluationData, proc));
+}
+
+bool
+RelationInvokeRTStrategy(Relation r,
+                        AttrNumber attnum,
+                        StrategyNumber s,
+                        Datum left,
+                        Datum right)
+{
+    return (RelationInvokeStrategy(r, &RTEvaluationData, attnum, s,
+                                  left, right));
+}
+
+RegProcedure
+RTMapOperator(Relation r,
+             AttrNumber attnum,
+             RegProcedure proc)
+{
+    StrategyNumber procstrat;
+    StrategyMap strategyMap;
+    
+    procstrat = RelationGetRTStrategy(r, attnum, proc);
+    strategyMap = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(r),
+                                             RTNStrategies,
+                                             attnum);
+    
+    return (strategyMap->entry[RTOperMap[procstrat - 1] - 1].sk_procedure);
+}
diff --git a/src/backend/access/rtscan.h b/src/backend/access/rtscan.h
new file mode 100644 (file)
index 0000000..c290577
--- /dev/null
@@ -0,0 +1,17 @@
+/*-------------------------------------------------------------------------
+ *
+ * rtscan.h--
+ *    routines defined in access/rtree/rtscan.c
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef RTSCAN_H
+
+void rtadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum);
+
+#endif /* RTSCAN_H */
diff --git a/src/backend/access/rtstrat.h b/src/backend/access/rtstrat.h
new file mode 100644 (file)
index 0000000..c084917
--- /dev/null
@@ -0,0 +1,18 @@
+/*-------------------------------------------------------------------------
+ *
+ * rtstrat.h--
+ *    routines defined in access/rtree/rtstrat.c
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef RTSTRAT_H
+
+extern RegProcedure RTMapOperator(Relation r,  AttrNumber attnum,
+                                 RegProcedure proc);
+
+#endif /* RTSTRAT_H */
diff --git a/src/backend/access/sdir.h b/src/backend/access/sdir.h
new file mode 100644 (file)
index 0000000..1e672a2
--- /dev/null
@@ -0,0 +1,57 @@
+/*-------------------------------------------------------------------------
+ *
+ * sdir.h--
+ *    POSTGRES scan direction definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        SDIR_H
+#define SDIR_H
+
+#include "c.h"
+
+/*
+ * ScanDirection was an int8 for no apparent reason. I kept the original
+ * values because I'm not sure if I'll break anything otherwise.  -ay 2/95
+ */
+typedef enum ScanDirection {
+    BackwardScanDirection = -1,
+    NoMovementScanDirection = 0,
+    ForwardScanDirection = 1
+} ScanDirection;
+
+/*
+ * ScanDirectionIsValid --
+ *     True iff scan direciton is valid.
+ */
+#define ScanDirectionIsValid(direction) \
+    ((bool) (BackwardScanDirection <= direction && \
+            direction <= ForwardScanDirection))
+
+/*
+ * ScanDirectionIsBackward --
+ *     True iff scan direciton is backward.
+ */
+#define ScanDirectionIsBackward(direction) \
+    ((bool) (direction == BackwardScanDirection))
+
+/*
+ * ScanDirectionIsNoMovement --
+ *     True iff scan direciton indicates no movement.
+ */
+#define ScanDirectionIsNoMovement(direction) \
+    ((bool) (direction == NoMovementScanDirection))
+
+/*
+ * ScanDirectionIsForward --
+ *     True iff scan direciton is forward.
+ */
+#define ScanDirectionIsForward(direction) \
+    ((bool) (direction == ForwardScanDirection))
+
+#endif /* SDIR_H */
diff --git a/src/backend/access/skey.h b/src/backend/access/skey.h
new file mode 100644 (file)
index 0000000..d6464cf
--- /dev/null
@@ -0,0 +1,52 @@
+/*-------------------------------------------------------------------------
+ *
+ * skey.h--
+ *    POSTGRES scan key definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *
+ * Note:
+ *     Needs more accessor/assignment routines.
+ *-------------------------------------------------------------------------
+ */
+#ifndef        SKEY_H
+#define SKEY_H
+
+#include "postgres.h"
+#include "access/attnum.h"
+
+
+typedef struct ScanKeyData {
+    bits16             sk_flags;       /* flags */
+    AttrNumber         sk_attno;       /* domain number */
+    RegProcedure       sk_procedure;   /* procedure OID */
+    func_ptr            sk_func;
+    int32              sk_nargs;
+    Datum              sk_argument;    /* data to compare */
+} ScanKeyData;
+
+typedef ScanKeyData    *ScanKey;
+
+
+#define        SK_ISNULL       0x1
+#define        SK_UNARY        0x2
+#define        SK_NEGATE       0x4
+#define        SK_COMMUTE      0x8
+
+#define ScanUnmarked           0x01
+#define ScanUncheckedPrevious  0x02
+#define ScanUncheckedNext      0x04
+
+
+/*
+ * prototypes for functions in access/common/scankey.c
+ */
+extern void ScanKeyEntrySetIllegal(ScanKey entry);
+extern void ScanKeyEntryInitialize(ScanKey entry, bits16 flags,
+     AttrNumber attributeNumber, RegProcedure procedure, Datum argument);
+
+#endif /* SKEY_H */
diff --git a/src/backend/access/strat.h b/src/backend/access/strat.h
new file mode 100644 (file)
index 0000000..301acf1
--- /dev/null
@@ -0,0 +1,86 @@
+/*-------------------------------------------------------------------------
+ *
+ * strat.h--
+ *    index strategy type definitions
+ *    (separated out from original istrat.h to avoid circular refs)
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STRAT_H
+#define STRAT_H
+
+#include "postgres.h"
+#include "access/attnum.h"
+#include "access/skey.h"
+
+typedef uint16 StrategyNumber;
+
+#define InvalidStrategy        0
+
+typedef struct StrategyTransformMapData {
+    StrategyNumber     strategy[1];    /* VARIABLE LENGTH ARRAY */
+} StrategyTransformMapData;    /* VARIABLE LENGTH STRUCTURE */
+
+typedef StrategyTransformMapData       *StrategyTransformMap;
+
+typedef struct StrategyOperatorData {
+    StrategyNumber     strategy;
+    bits16             flags;          /* scan qualification flags h/skey.h */
+} StrategyOperatorData;
+
+typedef StrategyOperatorData   *StrategyOperator;
+
+typedef struct StrategyTermData {      /* conjunctive term */
+    uint16                     degree;
+    StrategyOperatorData       operatorData[1];        /* VARIABLE LENGTH */
+} StrategyTermData;    /* VARIABLE LENGTH STRUCTURE */
+
+typedef StrategyTermData       *StrategyTerm;
+
+typedef struct StrategyExpressionData {        /* disjunctive normal form */
+    StrategyTerm       term[1];        /* VARIABLE LENGTH ARRAY */
+} StrategyExpressionData;      /* VARIABLE LENGTH STRUCTURE */
+
+typedef StrategyExpressionData *StrategyExpression;
+
+typedef struct StrategyEvaluationData {
+    StrategyNumber             maxStrategy;
+    StrategyTransformMap       negateTransform;
+    StrategyTransformMap       commuteTransform;
+    StrategyTransformMap       negateCommuteTransform;
+    StrategyExpression expression[12]; /* XXX VARIABLE LENGTH */
+} StrategyEvaluationData;      /* VARIABLE LENGTH STRUCTURE */
+
+typedef StrategyEvaluationData *StrategyEvaluation;
+
+/*
+ * StrategyTransformMapIsValid --
+ *     Returns true iff strategy transformation map is valid.
+ */
+#define        StrategyTransformMapIsValid(transform) PointerIsValid(transform)
+
+
+#ifndef        CorrectStrategies               /* XXX this should be removable */
+#define AMStrategies(foo)      12
+#else  /* !defined(CorrectStrategies) */
+#define AMStrategies(foo)      (foo)
+#endif /* !defined(CorrectStrategies) */
+
+typedef struct StrategyMapData {
+       ScanKeyData             entry[1];       /* VARIABLE LENGTH ARRAY */
+} StrategyMapData;     /* VARIABLE LENGTH STRUCTURE */
+
+typedef StrategyMapData        *StrategyMap;
+
+typedef struct IndexStrategyData {
+       StrategyMapData strategyMapData[1];     /* VARIABLE LENGTH ARRAY */
+} IndexStrategyData;   /* VARIABLE LENGTH STRUCTURE */
+
+typedef IndexStrategyData      *IndexStrategy;
+
+#endif /*STRAT_H */
diff --git a/src/backend/access/transam.h b/src/backend/access/transam.h
new file mode 100644 (file)
index 0000000..b778ccd
--- /dev/null
@@ -0,0 +1,213 @@
+/*-------------------------------------------------------------------------
+ *
+ * transam.h--
+ *    postgres transaction access method support code header
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *   NOTES
+ *     Transaction System Version 101 now support proper oid
+ *     generation and recording in the variable relation.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef TRANSAM_H
+#define TRANSAM_H
+
+/* ----------------
+ *     transaction system version id
+ *
+ *     this is stored on the first page of the log, time and variable
+ *     relations on the first 4 bytes.  This is so that if we improve
+ *     the format of the transaction log after postgres version 2, then
+ *     people won't have to rebuild their databases.
+ *
+ *     TRANS_SYSTEM_VERSION 100 means major version 1 minor version 0.
+ *     Two databases with the same major version should be compatible,
+ *     even if their minor versions differ.
+ * ----------------
+ */
+#define TRANS_SYSTEM_VERSION   101
+
+/* ----------------
+ *     transaction id status values
+ *
+ *     someday we will use "11" = 3 = XID_INVALID to mean the
+ *     starting of run-length encoded log data.
+ * ----------------
+ */
+#define XID_COMMIT      2                      /* transaction commited */
+#define XID_ABORT       1                      /* transaction aborted */
+#define XID_INPROGRESS  0                      /* transaction in progress */
+#define XID_INVALID     3                      /* other */
+
+typedef unsigned char XidStatus;               /* (2 bits) */
+
+/* ----------------
+ *     BitIndexOf computes the index of the Nth xid on a given block
+ * ----------------
+ */
+#define BitIndexOf(N)   ((N) * 2)
+
+/* ----------------
+ *     transaction page definitions
+ * ----------------
+ */
+#define TP_DataSize            BLCKSZ
+#define TP_NumXidStatusPerBlock        (TP_DataSize * 4)
+#define TP_NumTimePerBlock     (TP_DataSize / 4)
+
+/* ----------------
+ *     LogRelationContents structure
+ *
+ *     This structure describes the storage of the data in the
+ *     first 128 bytes of the log relation.  This storage is never
+ *     used for transaction status because transaction id's begin
+ *     their numbering at 512.
+ *
+ *     The first 4 bytes of this relation store the version
+ *     number of the transction system.
+ * ----------------
+ */
+typedef struct LogRelationContentsData {
+    int                        TransSystemVersion;
+} LogRelationContentsData;
+
+typedef LogRelationContentsData *LogRelationContents;
+
+/* ----------------
+ *     TimeRelationContents structure
+ *
+ *     This structure describes the storage of the data in the
+ *     first 2048 bytes of the time relation.  This storage is never
+ *     used for transaction commit times because transaction id's begin
+ *     their numbering at 512.
+ *
+ *     The first 4 bytes of this relation store the version
+ *     number of the transction system.
+ * ----------------
+ */
+typedef struct TimeRelationContentsData {
+    int                        TransSystemVersion;
+} TimeRelationContentsData;
+
+typedef TimeRelationContentsData *TimeRelationContents;
+
+/* ----------------
+ *     VariableRelationContents structure
+ *
+ *     The variable relation is a special "relation" which
+ *     is used to store various system "variables" persistantly.
+ *     Unlike other relations in the system, this relation
+ *     is updated in place whenever the variables change.
+ *
+ *     The first 4 bytes of this relation store the version
+ *     number of the transction system.
+ *
+ *     Currently, the relation has only one page and the next
+ *     available xid, the last committed xid and the next
+ *     available oid are stored there.
+ * ----------------
+ */
+typedef struct VariableRelationContentsData {
+    int                        TransSystemVersion;
+    TransactionId      nextXidData;
+    TransactionId      lastXidData;
+    Oid                        nextOid;
+} VariableRelationContentsData;
+
+typedef VariableRelationContentsData *VariableRelationContents;
+
+/* ----------------
+ *     extern declarations
+ * ----------------
+ */
+
+/*
+ * prototypes for functions in transam/transam.c
+ */
+extern int RecoveryCheckingEnabled();
+extern void SetRecoveryCheckingEnabled(bool state);
+extern bool TransactionLogTest(TransactionId transactionId, XidStatus status);
+extern void TransactionLogUpdate(TransactionId transactionId,
+                                XidStatus status);
+extern AbsoluteTime TransactionIdGetCommitTime(TransactionId transactionId);
+extern void TransRecover(Relation logRelation);
+extern void InitializeTransactionLog();
+extern bool TransactionIdDidCommit(TransactionId transactionId);
+extern bool TransactionIdDidAbort(TransactionId transactionId);
+extern bool TransactionIdIsInProgress(TransactionId transactionId);
+extern void TransactionIdCommit(TransactionId transactionId);
+extern void TransactionIdAbort(TransactionId transactionId);
+extern void TransactionIdSetInProgress(TransactionId transactionId);
+
+/* in transam/transsup.c */
+extern void AmiTransactionOverride(bool flag);
+extern void TransComputeBlockNumber(Relation relation,
+       TransactionId transactionId, BlockNumber *blockNumberOutP);
+extern XidStatus TransBlockGetLastTransactionIdStatus(Block tblock,
+       TransactionId baseXid, TransactionId *returnXidP);
+extern XidStatus TransBlockGetXidStatus(Block tblock,
+                                       TransactionId transactionId);
+extern void TransBlockSetXidStatus(Block tblock,
+       TransactionId transactionId, XidStatus xstatus);
+extern AbsoluteTime TransBlockGetCommitTime(Block tblock,
+       TransactionId transactionId);
+extern void TransBlockSetCommitTime(Block tblock,
+       TransactionId transactionId, AbsoluteTime commitTime);
+extern XidStatus TransBlockNumberGetXidStatus(Relation relation,
+       BlockNumber blockNumber, TransactionId xid, bool *failP);
+extern void TransBlockNumberSetXidStatus(Relation relation,
+       BlockNumber blockNumber, TransactionId xid, XidStatus xstatus,
+       bool *failP);
+extern AbsoluteTime TransBlockNumberGetCommitTime(Relation relation,
+       BlockNumber blockNumber, TransactionId xid, bool *failP);
+extern void TransBlockNumberSetCommitTime(Relation relation,
+       BlockNumber blockNumber, TransactionId xid, AbsoluteTime xtime,
+       bool *failP);
+extern void TransGetLastRecordedTransaction(Relation relation,
+       TransactionId xid, bool *failP);
+
+/* in transam/varsup.c */
+extern void VariableRelationGetNextXid(TransactionId *xidP);
+extern void VariableRelationGetLastXid(TransactionId *xidP);
+extern void VariableRelationPutNextXid(TransactionId xid);
+extern void VariableRelationPutLastXid(TransactionId xid);
+extern void VariableRelationGetNextOid(Oid *oid_return);
+extern void VariableRelationPutNextOid(Oid *oidP);
+extern void GetNewTransactionId(TransactionId *xid);
+extern void UpdateLastCommittedXid(TransactionId xid);
+extern void GetNewObjectIdBlock(Oid *oid_return, int oid_block_size);
+extern void GetNewObjectId(Oid *oid_return);
+
+/* ----------------
+ *     global variable extern declarations
+ * ----------------
+ */
+
+/* in transam.c */
+extern Relation        LogRelation;
+extern Relation        TimeRelation;
+extern Relation        VariableRelation;
+
+extern TransactionId   cachedGetCommitTimeXid;
+extern AbsoluteTime    cachedGetCommitTime;
+extern TransactionId   cachedTestXid;
+extern XidStatus       cachedTestXidStatus;
+
+extern TransactionId NullTransactionId;
+extern TransactionId AmiTransactionId;
+extern TransactionId FirstTransactionId;
+
+extern int RecoveryCheckingEnableState;
+
+/* in transsup.c */
+extern bool AMI_OVERRIDE;      
+
+/* in varsup.c */
+extern int OidGenLockId;
+
+#endif /* TRAMSAM_H */
diff --git a/src/backend/access/transam/Makefile.inc b/src/backend/access/transam/Makefile.inc
new file mode 100644 (file)
index 0000000..1ed897f
--- /dev/null
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for access/transam
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= transam.c transsup.c varsup.c xact.c xid.c 
diff --git a/src/backend/access/transam/transam.c b/src/backend/access/transam/transam.c
new file mode 100644 (file)
index 0000000..9c62b85
--- /dev/null
@@ -0,0 +1,675 @@
+/*-------------------------------------------------------------------------
+ *
+ * transam.c--
+ *    postgres transaction log/time interface routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    This file contains the high level access-method interface to the
+ *    transaction system.
+ *     
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "machine.h"           /* in port/ directory (needed for BLCKSZ) */
+
+#include "access/heapam.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+
+#include "utils/memutils.h"
+#include "utils/mcxt.h"
+#include "utils/rel.h"
+#include "utils/elog.h"
+
+#include "utils/nabstime.h"
+#include "catalog/catname.h"
+
+#include "access/transam.h"
+#include "access/xact.h"
+#include "commands/vacuum.h"   /* for VacuumRunning */
+
+/* ----------------
+ *    global variables holding pointers to relations used
+ *    by the transaction system.  These are initialized by
+ *    InitializeTransactionLog().
+ * ----------------
+ */
+
+Relation LogRelation     = (Relation) NULL;
+Relation TimeRelation    = (Relation) NULL;
+Relation VariableRelation = (Relation) NULL;
+
+/* ----------------
+ *     global variables holding cached transaction id's and statuses.
+ * ----------------
+ */
+TransactionId  cachedGetCommitTimeXid;
+AbsoluteTime   cachedGetCommitTime;
+TransactionId  cachedTestXid;
+XidStatus      cachedTestXidStatus;
+
+/* ----------------
+ *     transaction system constants
+ * ----------------
+ */
+/* ----------------------------------------------------------------
+ *     transaction system constants
+ *
+ *     read the comments for GetNewTransactionId in order to
+ *      understand the initial values for AmiTransactionId and
+ *      FirstTransactionId. -cim 3/23/90
+ * ----------------------------------------------------------------
+ */
+TransactionId NullTransactionId = (TransactionId) 0;
+
+TransactionId AmiTransactionId = (TransactionId) 512;
+
+TransactionId FirstTransactionId = (TransactionId) 514;
+
+/* ----------------
+ *     transaction recovery state variables
+ *
+ *     When the transaction system is initialized, we may
+ *     need to do recovery checking.  This decision is decided
+ *     by the postmaster or the user by supplying the backend
+ *     with a special flag.  In general, we want to do recovery
+ *     checking whenever we are running without a postmaster
+ *     or when the number of backends running under the postmaster
+ *     goes from zero to one. -cim 3/21/90
+ * ----------------
+ */
+int RecoveryCheckingEnableState = 0;
+
+/* ------------------
+ *     spinlock for oid generation
+ * -----------------
+ */
+extern int OidGenLockId;
+
+/* ----------------
+ *     globals that must be reset at abort
+ * ----------------
+ */
+extern bool    BuildingBtree;
+
+
+/* ----------------
+ *     recovery checking accessors
+ * ----------------
+ */
+int
+RecoveryCheckingEnabled()
+{    
+    return RecoveryCheckingEnableState;
+}
+
+void
+SetRecoveryCheckingEnabled(bool state)
+{    
+    RecoveryCheckingEnableState = (state == true);
+}
+
+/* ----------------------------------------------------------------
+ *     postgres log/time access method interface
+ *
+ *     TransactionLogTest
+ *     TransactionLogUpdate
+ *     ========
+ *        these functions do work for the interface
+ *        functions - they search/retrieve and append/update
+ *        information in the log and time relations.
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     TransactionLogTest
+ * --------------------------------
+ */
+
+bool   /* true/false: does transaction id have specified status? */
+TransactionLogTest(TransactionId transactionId,        /* transaction id to test */
+                  XidStatus status)            /* transaction status */
+{
+    BlockNumber                blockNumber;
+    XidStatus          xidstatus;      /* recorded status of xid */
+    bool               fail = false;           /* success/failure */
+    
+    /* ----------------
+     *         during initialization consider all transactions
+     *  as having been committed
+     * ----------------
+     */
+    if (! RelationIsValid(LogRelation))
+       return (bool) (status == XID_COMMIT);
+    
+    /* ----------------
+     *  before going to the buffer manager, check our single
+     *   item cache to see if we didn't just check the transaction
+     *   status a moment ago.
+     * ----------------
+     */
+    if (TransactionIdEquals(transactionId, cachedTestXid))
+       return (bool)
+           (status == cachedTestXidStatus);
+    
+    /* ----------------
+     * compute the item pointer corresponding to the
+     *  page containing our transaction id.  We save the item in
+     *  our cache to speed up things if we happen to ask for the
+     *  same xid's status more than once.
+     * ----------------
+     */
+    TransComputeBlockNumber(LogRelation, transactionId, &blockNumber);
+    xidstatus = TransBlockNumberGetXidStatus(LogRelation,
+                                            blockNumber,
+                                            transactionId,
+                                            &fail);
+    
+    if (! fail) {
+       TransactionIdStore(transactionId, &cachedTestXid);
+       cachedTestXidStatus = xidstatus;
+       return (bool)
+           (status == xidstatus);
+    }
+    
+    /* ----------------
+     *   here the block didn't contain the information we wanted
+     * ----------------
+     */
+    elog(WARN, "TransactionLogTest: failed to get xidstatus");
+    
+    /*
+     * so lint is happy...
+     */
+    return(false);
+}
+
+/* --------------------------------
+ *     TransactionLogUpdate
+ * --------------------------------
+ */
+void
+TransactionLogUpdate(TransactionId transactionId, /* trans id to update */
+                    XidStatus status) /* new trans status */
+{
+    BlockNumber                blockNumber;
+    bool               fail = false;           /* success/failure */
+    AbsoluteTime       currentTime;    /* time of this transaction */
+    
+    /* ----------------
+     *         during initialization we don't record any updates.
+     * ----------------
+     */
+    if (! RelationIsValid(LogRelation))
+       return;
+    
+    /* ----------------
+     *  get the transaction commit time
+     * ----------------
+     */
+    currentTime = getSystemTime();
+    
+    /* ----------------
+     *  update the log relation
+     * ----------------
+     */
+    TransComputeBlockNumber(LogRelation, transactionId, &blockNumber);
+    TransBlockNumberSetXidStatus(LogRelation,
+                                blockNumber,
+                                transactionId,
+                                status,
+                                &fail);
+    
+    /* ----------------
+     *  update (invalidate) our single item TransactionLogTest cache.
+     * ----------------
+     */
+    TransactionIdStore(transactionId, &cachedTestXid);
+    cachedTestXidStatus = status;
+    
+    /* ----------------
+     * now we update the time relation, if necessary
+     *  (we only record commit times)
+     * ----------------
+     */
+    if (RelationIsValid(TimeRelation) && status == XID_COMMIT) {
+       TransComputeBlockNumber(TimeRelation, transactionId, &blockNumber);
+       TransBlockNumberSetCommitTime(TimeRelation,
+                                     blockNumber,
+                                     transactionId,
+                                     currentTime,
+                                     &fail);
+       /* ----------------
+        *   update (invalidate) our single item GetCommitTime cache.
+        * ----------------
+        */
+       TransactionIdStore(transactionId, &cachedGetCommitTimeXid);
+       cachedGetCommitTime = currentTime;
+    }
+    
+    /* ----------------
+     * now we update the "last committed transaction" field
+     *  in the variable relation if we are recording a commit.
+     * ----------------
+     */
+    if (RelationIsValid(VariableRelation) && status == XID_COMMIT)
+       UpdateLastCommittedXid(transactionId);
+}
+
+/* --------------------------------
+ *     TransactionIdGetCommitTime
+ * --------------------------------
+ */
+
+AbsoluteTime  /* commit time of transaction id */
+TransactionIdGetCommitTime(TransactionId transactionId) /* transaction id to test */
+{
+    BlockNumber                blockNumber;
+    AbsoluteTime       commitTime;     /* commit time */
+    bool               fail = false;           /* success/failure */
+    
+    /* ----------------
+     *   return invalid if we aren't running yet...
+     * ----------------
+     */
+    if (! RelationIsValid(TimeRelation))
+       return INVALID_ABSTIME;
+    
+    /* ----------------
+     *  before going to the buffer manager, check our single
+     *   item cache to see if we didn't just get the commit time
+     *   a moment ago.
+     * ----------------
+     */
+    if (TransactionIdEquals(transactionId, cachedGetCommitTimeXid))
+       return cachedGetCommitTime;
+    
+    /* ----------------
+     * compute the item pointer corresponding to the
+     *  page containing our transaction commit time
+     * ----------------
+     */
+    TransComputeBlockNumber(TimeRelation, transactionId, &blockNumber);
+    commitTime = TransBlockNumberGetCommitTime(TimeRelation,
+                                              blockNumber,
+                                              transactionId,
+                                              &fail);
+    
+    /* ----------------
+     * update our cache and return the transaction commit time
+     * ----------------
+     */
+    if (! fail) {
+       TransactionIdStore(transactionId, &cachedGetCommitTimeXid);
+       cachedGetCommitTime = commitTime;
+       return commitTime;
+    } else
+       return INVALID_ABSTIME;
+}
+
+/* ----------------------------------------------------------------
+ *                  transaction recovery code
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     TransRecover
+ *
+ *     preform transaction recovery checking.
+ *
+ *     Note: this should only be preformed if no other backends
+ *           are running.  This is known by the postmaster and
+ *           conveyed by the postmaster passing a "do recovery checking"
+ *           flag to the backend.
+ *
+ *     here we get the last recorded transaction from the log,
+ *     get the "last" and "next" transactions from the variable relation
+ *     and then preform some integrity tests:
+ *
+ *     1) No transaction may exist higher then the "next" available
+ *         transaction recorded in the variable relation.  If this is the
+ *         case then it means either the log or the variable relation
+ *         has become corrupted.
+ *
+ *      2) The last committed transaction may not be higher then the
+ *         next available transaction for the same reason.
+ *
+ *      3) The last recorded transaction may not be lower then the
+ *         last committed transaction.  (the reverse is ok - it means
+ *         that some transactions have aborted since the last commit)
+ *
+ *     Here is what the proper situation looks like.  The line
+ *     represents the data stored in the log.  'c' indicates the
+ *      transaction was recorded as committed, 'a' indicates an
+ *      abortted transaction and '.' represents information not
+ *      recorded.  These may correspond to in progress transactions.
+ *
+ *          c  c  a  c  .  .  a  .  .  .  .  .  .  .  .  .  .
+ *                   |                 |
+ *                  last              next
+ *
+ *     Since "next" is only incremented by GetNewTransactionId() which
+ *      is called when transactions are started.  Hence if there
+ *      are commits or aborts after "next", then it means we committed
+ *      or aborted BEFORE we started the transaction.  This is the
+ *     rational behind constraint (1).
+ *
+ *      Likewise, "last" should never greater then "next" for essentially
+ *      the same reason - it would imply we committed before we started.
+ *      This is the reasoning for (2).
+ *
+ *     (3) implies we may never have a situation such as:
+ *
+ *          c  c  a  c  .  .  a  c  .  .  .  .  .  .  .  .  .
+ *                   |                 |
+ *                  last              next
+ *
+ *      where there is a 'c' greater then "last".
+ *
+ *      Recovery checking is more difficult in the case where
+ *      several backends are executing concurrently because the
+ *     transactions may be executing in the other backends.
+ *      So, we only do recovery stuff when the backend is explicitly
+ *      passed a flag on the command line.
+ * --------------------------------
+ */
+void
+TransRecover(Relation logRelation)
+{
+#if 0    
+    /* ----------------
+     *    first get the last recorded transaction in the log.
+     * ----------------
+     */
+    TransGetLastRecordedTransaction(logRelation, logLastXid, &fail);
+    if (fail == true)
+       elog(WARN, "TransRecover: failed TransGetLastRecordedTransaction");
+    
+    /* ----------------
+     *    next get the "last" and "next" variables
+     * ----------------
+     */
+    VariableRelationGetLastXid(&varLastXid);
+    VariableRelationGetNextXid(&varNextXid);
+    
+    /* ----------------
+     *    intregity test (1)
+     * ----------------
+     */
+    if (TransactionIdIsLessThan(varNextXid, logLastXid))
+       elog(WARN, "TransRecover: varNextXid < logLastXid");
+    
+    /* ----------------
+     *    intregity test (2)
+     * ----------------
+     */
+    
+    /* ----------------
+     *    intregity test (3)
+     * ----------------
+     */
+    
+    /* ----------------
+     *  here we have a valid "
+     *
+     *         **** RESUME HERE ****
+     * ----------------
+     */
+    varNextXid = TransactionIdDup(varLastXid);
+    TransactionIdIncrement(&varNextXid);
+    
+    VarPut(var, VAR_PUT_LASTXID, varLastXid);
+    VarPut(var, VAR_PUT_NEXTXID, varNextXid);
+#endif
+}
+
+/* ----------------------------------------------------------------
+ *                     Interface functions
+ *
+ *     InitializeTransactionLog
+ *     ========
+ *        this function (called near cinit) initializes
+ *        the transaction log, time and variable relations.
+ *
+ *     TransactionId DidCommit
+ *     TransactionId DidAbort
+ *     TransactionId IsInProgress
+ *     ========
+ *        these functions test the transaction status of
+ *        a specified transaction id.
+ *
+ *     TransactionId Commit
+ *     TransactionId Abort
+ *     TransactionId SetInProgress
+ *     ========
+ *        these functions set the transaction status
+ *        of the specified xid. TransactionIdCommit() also
+ *        records the current time in the time relation
+ *        and updates the variable relation counter.
+ *
+ * ----------------------------------------------------------------
+ */
+
+/*
+ * InitializeTransactionLog --
+ *     Initializes transaction logging.
+ */
+void
+InitializeTransactionLog()
+{
+    Relation     logRelation;
+    Relation     timeRelation;
+    MemoryContext oldContext;
+    
+    /* ----------------
+     *    don't do anything during bootstrapping
+     * ----------------
+     */
+    if (AMI_OVERRIDE)
+       return;
+    
+    /* ----------------
+     *  disable the transaction system so the access methods
+     *   don't interfere during initialization.
+     * ----------------
+     */
+    OverrideTransactionSystem(true);
+    
+    /* ----------------
+     * make sure allocations occur within the top memory context
+     *  so that our log management structures are protected from
+     *  garbage collection at the end of every transaction.
+     * ----------------
+     */
+    oldContext = MemoryContextSwitchTo(TopMemoryContext); 
+    
+    /* ----------------
+     *   first open the log and time relations
+     *   (these are created by amiint so they are guaranteed to exist)
+     * ----------------
+     */
+    logRelation =      heap_openr(LogRelationName);
+    timeRelation =     heap_openr(TimeRelationName);
+    VariableRelation =         heap_openr(VariableRelationName);
+    /* ----------------
+     *   XXX TransactionLogUpdate requires that LogRelation
+     *  and TimeRelation are valid so we temporarily set
+     *  them so we can initialize things properly.
+     *  This could be done cleaner.
+     * ----------------
+     */
+    LogRelation =  logRelation;
+    TimeRelation = timeRelation;
+    
+    /* ----------------
+     *   if we have a virgin database, we initialize the log and time
+     *  relation by committing the AmiTransactionId (id 512) and we
+     *   initialize the variable relation by setting the next available
+     *   transaction id to FirstTransactionId (id 514).  OID initialization
+     *   happens as a side effect of bootstrapping in varsup.c.
+     * ----------------
+     */
+    SpinAcquire(OidGenLockId);
+    if (!TransactionIdDidCommit(AmiTransactionId)) {
+       
+       /* ----------------
+        *  SOMEDAY initialize the information stored in
+        *          the headers of the log/time/variable relations.
+        * ----------------
+        */
+       TransactionLogUpdate(AmiTransactionId, XID_COMMIT);
+       VariableRelationPutNextXid(FirstTransactionId);
+       
+    } else if (RecoveryCheckingEnabled()) {
+       /* ----------------
+        *      if we have a pre-initialized database and if the
+        *      perform recovery checking flag was passed then we
+        *      do our database integrity checking.
+        * ----------------
+        */
+       TransRecover(logRelation);
+    }
+    LogRelation =  (Relation) NULL;
+    TimeRelation = (Relation) NULL;
+    SpinRelease(OidGenLockId);
+    
+    /* ----------------
+     * now re-enable the transaction system
+     * ----------------
+     */
+    OverrideTransactionSystem(false);
+    
+    /* ----------------
+     * instantiate the global variables
+     * ----------------
+     */
+    LogRelation =      logRelation;
+    TimeRelation =     timeRelation;
+    
+    /* ----------------
+     * restore the memory context to the previous context
+     *  before we return from initialization.
+     * ----------------
+     */
+    MemoryContextSwitchTo(oldContext);
+}
+
+/* --------------------------------
+ *     TransactionId DidCommit
+ *     TransactionId DidAbort
+ *     TransactionId IsInProgress
+ * --------------------------------
+ */
+
+/*
+ * TransactionIdDidCommit --
+ *     True iff transaction associated with the identifier did commit.
+ *
+ * Note:
+ *     Assumes transaction identifier is valid.
+ */
+bool   /* true if given transaction committed */
+TransactionIdDidCommit(TransactionId transactionId)
+{
+    if (AMI_OVERRIDE)
+       return true;
+    
+    return
+       TransactionLogTest(transactionId, XID_COMMIT);
+}
+
+/*
+ * TransactionIdDidAborted --
+ *     True iff transaction associated with the identifier did abort.
+ *
+ * Note:
+ *     Assumes transaction identifier is valid.
+ *     XXX Is this unneeded?
+ */
+bool   /* true if given transaction aborted */
+TransactionIdDidAbort(TransactionId transactionId)
+{
+    if (AMI_OVERRIDE)
+       return false;
+    
+    return
+       TransactionLogTest(transactionId, XID_ABORT);
+}
+
+bool   /* true if given transaction neither committed nor aborted */
+TransactionIdIsInProgress(TransactionId transactionId)
+{
+    if (AMI_OVERRIDE)
+       return false;
+    
+    return
+       TransactionLogTest(transactionId, XID_INPROGRESS);
+}
+
+/* --------------------------------
+ *     TransactionId Commit
+ *     TransactionId Abort
+ *     TransactionId SetInProgress
+ * --------------------------------
+ */
+
+/*
+ * TransactionIdCommit --
+ *     Commits the transaction associated with the identifier.
+ *
+ * Note:
+ *     Assumes transaction identifier is valid.
+ */
+void
+TransactionIdCommit(TransactionId transactionId)
+{
+    if (AMI_OVERRIDE)
+       return;
+    
+    /*
+     * Within TransactionLogUpdate we call UpdateLastCommited()
+     * which assumes we have exclusive access to pg_variable.
+     * Therefore we need to get exclusive access before calling
+     * TransactionLogUpdate. -mer 18 Aug 1992
+     */
+    SpinAcquire(OidGenLockId);
+    TransactionLogUpdate(transactionId, XID_COMMIT);
+    SpinRelease(OidGenLockId);
+}
+
+/*
+ * TransactionIdAbort --
+ *     Aborts the transaction associated with the identifier.
+ *
+ * Note:
+ *     Assumes transaction identifier is valid.
+ */
+void
+TransactionIdAbort(TransactionId transactionId)
+{
+    BuildingBtree = false;
+    
+    if (VacuumRunning)
+       vc_abort();
+    
+    if (AMI_OVERRIDE)
+       return;
+    
+    TransactionLogUpdate(transactionId, XID_ABORT);
+}
+
+void
+TransactionIdSetInProgress(TransactionId transactionId)
+{
+    if (AMI_OVERRIDE)
+       return;
+    
+    TransactionLogUpdate(transactionId, XID_INPROGRESS);
+}
diff --git a/src/backend/access/transam/transsup.c b/src/backend/access/transam/transsup.c
new file mode 100644 (file)
index 0000000..7c3b9a2
--- /dev/null
@@ -0,0 +1,663 @@
+/*-------------------------------------------------------------------------
+ *
+ * transsup.c--
+ *    postgres transaction access method support code
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    This file contains support functions for the high
+ *    level access method interface routines found in transam.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "machine.h"           /* in port/ directory (needed for BLCKSZ) */
+
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+
+#include "utils/rel.h"
+#include "utils/elog.h"
+#include "utils/memutils.h"
+#include "utils/nabstime.h"
+
+#include "catalog/heap.h"
+#include "access/transam.h"    /* where the declarations go */
+#include "access/xact.h"       /* where the declarations go */
+
+#include "storage/smgr.h"
+
+/* ----------------------------------------------------------------
+ *                   general support routines
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     AmiTransactionOverride
+ *
+ *     This function is used to manipulate the bootstrap flag.
+ * --------------------------------
+ */
+void
+AmiTransactionOverride(bool flag)
+{
+    AMI_OVERRIDE = flag;
+}
+
+/* --------------------------------
+ *     TransComputeBlockNumber
+ * --------------------------------
+ */
+void
+TransComputeBlockNumber(Relation relation, /* relation to test */
+                       TransactionId transactionId, /* transaction id to test */
+                       BlockNumber *blockNumberOutP)
+{
+    long       itemsPerBlock;
+    
+    /* ----------------
+     *  we calculate the block number of our transaction
+     *  by dividing the transaction id by the number of
+     *  transaction things per block.  
+     * ----------------
+     */
+    if (relation == LogRelation)
+       itemsPerBlock = TP_NumXidStatusPerBlock;
+    else if (relation == TimeRelation)
+       itemsPerBlock = TP_NumTimePerBlock;
+    else
+       elog(WARN, "TransComputeBlockNumber: unknown relation");
+    
+    /* ----------------
+     * warning! if the transaction id's get too large
+     *  then a BlockNumber may not be large enough to hold the results
+     *  of our division.
+     *
+     * XXX  this will all vanish soon when we implement an improved
+     *       transaction id schema -cim 3/23/90
+     *
+     *  This has vanished now that xid's are 4 bytes (no longer 5).
+     *  -mer 5/24/92
+     * ----------------
+     */
+    (*blockNumberOutP) = transactionId / itemsPerBlock;
+}
+
+
+/* ----------------------------------------------------------------
+ *                  trans block support routines
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     TransBlockGetLastTransactionIdStatus
+ *
+ *     This returns the status and transaction id of the last
+ *     transaction information recorded on the given TransBlock.
+ * --------------------------------
+ */
+
+XidStatus
+TransBlockGetLastTransactionIdStatus(Block tblock,
+                                    TransactionId baseXid,
+                                    TransactionId *returnXidP)
+{
+    Index         index;
+    Index         maxIndex;
+    bits8         bit1;
+    bits8        bit2;
+    BitIndex      offset;
+    XidStatus     xstatus;
+    
+    /* ----------------
+     * sanity check
+     * ----------------
+     */
+    Assert((tblock != NULL));
+    
+    /* ----------------
+     * search downward from the top of the block data, looking
+     *  for the first Non-in progress transaction status.  Since we
+     *  are scanning backward, this will be last recorded transaction
+     *  status on the block.
+     * ----------------
+     */
+    maxIndex = TP_NumXidStatusPerBlock;
+    for (index = maxIndex-1; index>=0; index--) {
+       offset =  BitIndexOf(index);
+       bit1 =    ((bits8) BitArrayBitIsSet((BitArray) tblock, offset++)) << 1;
+       bit2 =    (bits8)  BitArrayBitIsSet((BitArray) tblock, offset);
+       
+       xstatus =  (bit1 | bit2) ;
+       
+       /* ----------------
+        *  here we have the status of some transaction, so test
+        *  if the status is recorded as "in progress".  If so, then
+        *  we save the transaction id in the place specified by the caller.
+        * ----------------
+        */
+       if (xstatus != XID_INPROGRESS) {
+           if (returnXidP != NULL) {
+               TransactionIdStore(baseXid, returnXidP);
+               TransactionIdAdd(returnXidP, index);
+           }
+           break;
+       }
+    }
+    
+    /* ----------------
+     * if we get here and index is 0 it means we couldn't find
+     *  a non-inprogress transaction on the block.  For now we just
+     *  return this info to the user.  They can check if the return
+     *  status is "in progress" to know this condition has arisen.
+     * ----------------
+     */
+    if (index == 0) {
+       if (returnXidP != NULL)
+           TransactionIdStore(baseXid, returnXidP);
+    }
+    
+    /* ----------------
+     * return the status to the user
+     * ----------------
+     */
+    return xstatus;
+}
+
+/* --------------------------------
+ *     TransBlockGetXidStatus
+ *
+ *     This returns the status of the desired transaction
+ * --------------------------------
+ */
+
+XidStatus
+TransBlockGetXidStatus(Block tblock,
+                      TransactionId transactionId)
+{
+    Index                      index;
+    bits8                      bit1;
+    bits8                      bit2;
+    BitIndex                   offset;
+    
+    /* ----------------
+     * sanity check
+     * ----------------
+     */
+    if (tblock == NULL) {
+       return XID_INVALID;
+    }
+    
+    /* ----------------
+     * calculate the index into the transaction data where
+     *  our transaction status is located
+     *
+     *  XXX this will be replaced soon when we move to the
+     *      new transaction id scheme -cim 3/23/90
+     *
+     *  The old system has now been replaced. -mer 5/24/92
+     * ----------------
+     */
+    index = transactionId % TP_NumXidStatusPerBlock;
+    
+    /* ----------------
+     * get the data at the specified index
+     * ----------------
+     */
+    offset =    BitIndexOf(index);
+    bit1 =      ((bits8)   BitArrayBitIsSet((BitArray) tblock, offset++)) << 1;
+    bit2 =      (bits8)    BitArrayBitIsSet((BitArray) tblock, offset);
+    
+    /* ----------------
+     * return the transaction status to the caller
+     * ----------------
+     */
+    return (XidStatus)
+       (bit1 | bit2);
+}
+
+/* --------------------------------
+ *     TransBlockSetXidStatus
+ *
+ *     This sets the status of the desired transaction
+ * --------------------------------
+ */
+void
+TransBlockSetXidStatus(Block tblock,
+                      TransactionId transactionId, 
+                      XidStatus xstatus)
+{
+    Index                      index;
+    BitIndex                   offset;
+    
+    /* ----------------
+     * sanity check
+     * ----------------
+     */
+    if (tblock == NULL)
+       return;
+    
+    /* ----------------
+     * calculate the index into the transaction data where
+     *  we sould store our transaction status.
+     *
+     *  XXX this will be replaced soon when we move to the
+     *      new transaction id scheme -cim 3/23/90
+     *
+     *  The new scheme is here -mer 5/24/92
+     * ----------------
+     */
+    index = transactionId % TP_NumXidStatusPerBlock;
+    
+    offset =    BitIndexOf(index);
+    
+    /* ----------------
+     * store the transaction value at the specified offset
+     * ----------------
+     */
+    switch(xstatus) {
+    case XID_COMMIT:             /* set 10 */
+       BitArraySetBit((BitArray) tblock, offset);
+       BitArrayClearBit((BitArray) tblock, offset + 1);
+       break;
+    case XID_ABORT:             /* set 01 */
+       BitArrayClearBit((BitArray) tblock, offset);
+       BitArraySetBit((BitArray) tblock, offset + 1);
+       break;
+    case XID_INPROGRESS:        /* set 00 */
+       BitArrayClearBit((BitArray) tblock, offset);
+       BitArrayClearBit((BitArray) tblock, offset + 1);
+       break;
+    default:
+       elog(NOTICE,
+            "TransBlockSetXidStatus: invalid status: %d (ignored)",
+            xstatus);
+       break;
+    }
+}
+
+/* --------------------------------
+ *     TransBlockGetCommitTime
+ *
+ *     This returns the transaction commit time for the
+ *     specified transaction id in the trans block.
+ * --------------------------------
+ */
+AbsoluteTime
+TransBlockGetCommitTime(Block tblock,
+                       TransactionId transactionId)
+{
+    Index                      index;
+    AbsoluteTime               *timeArray;
+    
+    /* ----------------
+     * sanity check
+     * ----------------
+     */
+    if (tblock == NULL)
+       return INVALID_ABSTIME;
+    
+    /* ----------------
+     * calculate the index into the transaction data where
+     *  our transaction commit time is located
+     *
+     *  XXX this will be replaced soon when we move to the
+     *      new transaction id scheme -cim 3/23/90
+     *
+     *  The new scheme is here. -mer 5/24/92
+     * ----------------
+     */
+    index = transactionId % TP_NumTimePerBlock;
+    
+    /* ----------------
+     * return the commit time to the caller
+     * ----------------
+     */
+    timeArray =  (AbsoluteTime *) tblock;
+    return (AbsoluteTime)
+       timeArray[ index ];
+}
+
+/* --------------------------------
+ *     TransBlockSetCommitTime
+ *
+ *     This sets the commit time of the specified transaction
+ * --------------------------------
+ */
+void
+TransBlockSetCommitTime(Block tblock,
+                       TransactionId transactionId,
+                       AbsoluteTime commitTime)
+{
+    Index              index;
+    AbsoluteTime       *timeArray;
+    
+    /* ----------------
+     * sanity check
+     * ----------------
+     */
+    if (tblock == NULL)
+       return;
+    
+    
+    /* ----------------
+     * calculate the index into the transaction data where
+     *  we sould store our transaction status.  
+     *
+     *  XXX this will be replaced soon when we move to the
+     *      new transaction id scheme -cim 3/23/90
+     *
+     *  The new scheme is here.  -mer 5/24/92
+     * ----------------
+     */
+    index = transactionId % TP_NumTimePerBlock;
+    
+    /* ----------------
+     * store the transaction commit time at the specified index
+     * ----------------
+     */
+    timeArray =  (AbsoluteTime *) tblock;
+    timeArray[ index ] = commitTime;
+}
+
+/* ----------------------------------------------------------------
+ *                transam i/o support routines
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     TransBlockNumberGetXidStatus
+ * --------------------------------
+ */
+XidStatus
+TransBlockNumberGetXidStatus(Relation relation,
+                            BlockNumber blockNumber,
+                            TransactionId xid,
+                            bool *failP)
+{
+    Buffer             buffer;         /* buffer associated with block */
+    Block              block;          /* block containing xstatus */
+    XidStatus          xstatus;        /* recorded status of xid */
+    bool               localfail;      /* bool used if failP = NULL */
+    
+    /* ----------------
+     * SOMEDAY place a read lock on the log relation
+     *  That someday is today 5 Aug 1991 -mer
+     * ----------------
+     */
+    RelationSetLockForRead(relation);
+    
+    /* ----------------
+     * get the page containing the transaction information
+     * ----------------
+     */
+    buffer =      ReadBuffer(relation, blockNumber);
+    block =       BufferGetBlock(buffer);
+    
+    /* ----------------
+     * get the status from the block.  note, for now we always
+     *  return false in failP.
+     * ----------------
+     */
+    if (failP == NULL)
+       failP = &localfail;
+    (*failP) = false;
+    
+    xstatus = TransBlockGetXidStatus(block, xid);
+    
+    /* ----------------
+     * release the buffer and return the status
+     * ----------------
+     */
+    ReleaseBuffer(buffer);
+    
+    /* ----------------
+     * SOMEDAY release our lock on the log relation
+     * ----------------
+     */
+    RelationUnsetLockForRead(relation);
+    
+    return
+       xstatus;
+}
+
+/* --------------------------------
+ *     TransBlockNumberSetXidStatus
+ * --------------------------------
+ */
+void
+TransBlockNumberSetXidStatus(Relation relation,
+                            BlockNumber blockNumber,
+                            TransactionId xid,
+                            XidStatus xstatus,
+                            bool *failP)
+{
+    Buffer             buffer;         /* buffer associated with block */
+    Block              block;          /* block containing xstatus */
+    bool               localfail;      /* bool used if failP = NULL */
+    
+    /* ----------------
+     * SOMEDAY gain exclusive access to the log relation
+     *
+     *  That someday is today 5 Aug 1991 -mer
+     * ----------------
+     */
+    RelationSetLockForWrite(relation);
+    
+    /* ----------------
+     * get the block containing the transaction status
+     * ----------------
+     */
+    buffer =   ReadBuffer(relation, blockNumber);
+    block =    BufferGetBlock(buffer);
+    
+    /* ----------------
+     * attempt to update the status of the transaction on the block.
+     *  if we are successful, write the block. otherwise release the buffer.
+     *  note, for now we always return false in failP.
+     * ----------------
+     */
+    if (failP == NULL)
+       failP = &localfail;
+    (*failP) = false;
+    
+    TransBlockSetXidStatus(block, xid, xstatus);
+    
+    if ((*failP) == false)
+       WriteBuffer(buffer);
+    else
+       ReleaseBuffer(buffer);
+    
+    /* ----------------
+     * SOMEDAY release our lock on the log relation
+     * ----------------
+     */    
+    RelationUnsetLockForWrite(relation);
+}
+
+/* --------------------------------
+ *     TransBlockNumberGetCommitTime
+ * --------------------------------
+ */
+AbsoluteTime
+TransBlockNumberGetCommitTime(Relation relation,
+                             BlockNumber blockNumber,
+                             TransactionId xid,
+                             bool *failP)
+{
+    Buffer             buffer;         /* buffer associated with block */
+    Block              block;          /* block containing commit time */
+    bool               localfail;      /* bool used if failP = NULL */
+    AbsoluteTime       xtime;          /* commit time */
+    
+    /* ----------------
+     * SOMEDAY place a read lock on the time relation
+     *
+     *  That someday is today 5 Aug. 1991 -mer
+     * ----------------
+     */
+    RelationSetLockForRead(relation);
+    
+    /* ----------------
+     * get the block containing the transaction information
+     * ----------------
+     */
+    buffer =           ReadBuffer(relation, blockNumber);
+    block =            BufferGetBlock(buffer);
+    
+    /* ----------------
+     * get the commit time from the block
+     *  note, for now we always return false in failP.
+     * ----------------
+     */
+    if (failP == NULL)
+       failP = &localfail;
+    (*failP) = false;
+    
+    xtime = TransBlockGetCommitTime(block, xid);
+    
+    /* ----------------
+     * release the buffer and return the commit time
+     * ----------------
+     */
+    ReleaseBuffer(buffer);
+    
+    /* ----------------
+     * SOMEDAY release our lock on the time relation
+     * ----------------
+     */
+    RelationUnsetLockForRead(relation);
+    
+    if ((*failP) == false)
+       return xtime;
+    else
+       return INVALID_ABSTIME;
+    
+}
+
+/* --------------------------------
+ *     TransBlockNumberSetCommitTime
+ * --------------------------------
+ */
+void
+TransBlockNumberSetCommitTime(Relation relation,
+                             BlockNumber blockNumber,
+                             TransactionId xid,
+                             AbsoluteTime xtime,
+                             bool *failP)
+{
+    Buffer             buffer;         /* buffer associated with block */
+    Block              block;          /* block containing commit time */
+    bool               localfail;      /* bool used if failP = NULL */
+    
+    /* ----------------
+     * SOMEDAY gain exclusive access to the time relation
+     *
+     *  That someday is today 5 Aug. 1991 -mer
+     * ----------------
+     */
+    RelationSetLockForWrite(relation);
+    
+    /* ----------------
+     * get the block containing our commit time
+     * ----------------
+     */
+    buffer =      ReadBuffer(relation, blockNumber);
+    block =       BufferGetBlock(buffer);
+    
+    /* ----------------
+     * attempt to update the commit time of the transaction on the block.
+     *  if we are successful, write the block. otherwise release the buffer.
+     *  note, for now we always return false in failP.
+     * ----------------
+     */
+    if (failP == NULL)
+       failP = &localfail;
+    (*failP) = false;
+    
+    TransBlockSetCommitTime(block, xid, xtime);
+    
+    if ((*failP) == false)
+       WriteBuffer(buffer);
+    else
+       ReleaseBuffer(buffer);
+    
+    /* ----------------
+     * SOMEDAY release our lock on the time relation
+     * ----------------
+     */
+    RelationUnsetLockForWrite(relation);
+    
+}
+
+/* --------------------------------
+ *     TransGetLastRecordedTransaction
+ * --------------------------------
+ */
+void
+TransGetLastRecordedTransaction(Relation relation,
+                               TransactionId xid, /* return: transaction id */
+                               bool *failP)
+{
+    BlockNumber                blockNumber;    /* block number */
+    Buffer             buffer;         /* buffer associated with block */
+    Block              block;          /* block containing xid status */
+    BlockNumber                n;              /* number of blocks in the relation */
+    TransactionId      baseXid;
+    
+    (*failP) = false;
+    
+    /* ----------------
+     * SOMEDAY gain exclusive access to the log relation
+     *
+     *  That someday is today 5 Aug. 1991 -mer
+     *  It looks to me like we only need to set a read lock here, despite
+     *  the above comment about exclusive access.  The block is never 
+     *  actually written into, we only check status bits.
+     * ----------------
+     */
+    RelationSetLockForRead(relation);
+    
+    /* ----------------
+     * we assume the last block of the log contains the last
+     *  recorded transaction.  If the relation is empty we return
+     *  failure to the user.
+     * ----------------
+     */
+    n = RelationGetNumberOfBlocks(relation);
+    if (n == 0) {
+       (*failP) = true;
+       return;
+    }
+    
+    /* ----------------
+     * get the block containing the transaction information
+     * ----------------
+     */
+    blockNumber =  n-1;
+    buffer =   ReadBuffer(relation, blockNumber);
+    block =    BufferGetBlock(buffer);
+    
+    /* ----------------
+     * get the last xid on the block
+     * ----------------
+     */
+    baseXid = blockNumber * TP_NumXidStatusPerBlock;
+
+/* XXX ???? xid won't get returned! - AY '94 */
+    (void) TransBlockGetLastTransactionIdStatus(block, baseXid, &xid);
+    
+    ReleaseBuffer(buffer);
+    
+    /* ----------------
+     * SOMEDAY release our lock on the log relation
+     * ----------------
+     */
+    RelationUnsetLockForRead(relation);
+}
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
new file mode 100644 (file)
index 0000000..1473304
--- /dev/null
@@ -0,0 +1,606 @@
+/*-------------------------------------------------------------------------
+ *
+ * varsup.c--
+ *    postgres variable relation support routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <math.h>
+#include "postgres.h"
+
+#include "machine.h"           /* in port/ directory (needed for BLCKSZ) */
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/ipc.h"       /* for OIDGENLOCKID */
+
+#include "utils/rel.h"
+#include "utils/elog.h"
+
+#include "access/heapam.h"
+#include "access/transam.h"    /* where the declarations go */
+#include "access/xact.h"       /* where the declarations go */
+
+#include "catalog/catname.h"
+
+/* ----------
+ *      note: we reserve the first 16384 object ids for internal use.
+ *      oid's less than this appear in the .bki files.  the choice of
+ *      16384 is completely arbitrary.
+ * ----------
+ */
+#define BootstrapObjectIdData 16384
+
+/* ---------------------
+ *     spin lock for oid generation
+ * ---------------------
+ */
+int OidGenLockId;
+
+/* ----------------------------------------------------------------
+ *           variable relation query/update routines
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     VariableRelationGetNextXid
+ * --------------------------------
+ */
+void
+VariableRelationGetNextXid(TransactionId *xidP)
+{
+    Buffer buf;
+    VariableRelationContents var;
+    
+    /* ----------------
+     * We assume that a spinlock has been acquire to guarantee
+     * exclusive access to the variable relation.
+     * ----------------
+     */
+    
+    /* ----------------
+     * do nothing before things are initialized
+     * ----------------
+     */
+    if (! RelationIsValid(VariableRelation))
+       return;
+    
+    /* ----------------
+     * read the variable page, get the the nextXid field and
+     *  release the buffer
+     * ----------------
+     */
+    buf = ReadBuffer(VariableRelation, 0);
+    
+    if (! BufferIsValid(buf))
+       {
+           SpinRelease(OidGenLockId);
+           elog(WARN, "VariableRelationGetNextXid: ReadBuffer failed");
+       }
+    
+    var = (VariableRelationContents) BufferGetBlock(buf);
+    
+    TransactionIdStore(var->nextXidData, xidP);
+    ReleaseBuffer(buf);
+}
+
+/* --------------------------------
+ *     VariableRelationGetLastXid
+ * --------------------------------
+ */
+void
+VariableRelationGetLastXid(TransactionId *xidP)
+{
+    Buffer buf;
+    VariableRelationContents var;
+    
+    /* ----------------
+     * We assume that a spinlock has been acquire to guarantee
+     * exclusive access to the variable relation.
+     * ----------------
+     */
+    
+    /* ----------------
+     * do nothing before things are initialized
+     * ----------------
+     */
+    if (! RelationIsValid(VariableRelation))
+       return;
+    
+    /* ----------------
+     * read the variable page, get the the lastXid field and
+     *  release the buffer
+     * ----------------
+     */
+    buf = ReadBuffer(VariableRelation, 0);
+    
+    if (! BufferIsValid(buf))
+       {
+           SpinRelease(OidGenLockId);
+           elog(WARN, "VariableRelationGetNextXid: ReadBuffer failed");
+       }
+    
+    var = (VariableRelationContents) BufferGetBlock(buf);
+    
+    TransactionIdStore(var->lastXidData, xidP);
+    
+    ReleaseBuffer(buf);
+}
+
+/* --------------------------------
+ *     VariableRelationPutNextXid
+ * --------------------------------
+ */
+void
+VariableRelationPutNextXid(TransactionId xid)
+{
+    Buffer buf;
+    VariableRelationContents var;
+    
+    /* ----------------
+     * We assume that a spinlock has been acquire to guarantee
+     * exclusive access to the variable relation.
+     * ----------------
+     */
+    
+    /* ----------------
+     * do nothing before things are initialized
+     * ----------------
+     */
+    if (! RelationIsValid(VariableRelation))
+       return;
+    
+    /* ----------------
+     * read the variable page, update the nextXid field and
+     *  write the page back out to disk.
+     * ----------------
+     */
+    buf = ReadBuffer(VariableRelation, 0);
+    
+    if (! BufferIsValid(buf))
+       {
+           SpinRelease(OidGenLockId);
+           elog(WARN, "VariableRelationPutNextXid: ReadBuffer failed");
+       }
+    
+    var = (VariableRelationContents) BufferGetBlock(buf);
+    
+    TransactionIdStore(xid, &(var->nextXidData));
+    
+    WriteBuffer(buf);
+}
+
+/* --------------------------------
+ *     VariableRelationPutLastXid
+ * --------------------------------
+ */
+void
+VariableRelationPutLastXid(TransactionId xid)
+{
+    Buffer buf;
+    VariableRelationContents var;
+    
+    /* ----------------
+     * We assume that a spinlock has been acquire to guarantee
+     * exclusive access to the variable relation.
+     * ----------------
+     */
+    
+    /* ----------------
+     * do nothing before things are initialized
+     * ----------------
+     */
+    if (! RelationIsValid(VariableRelation))
+       return;
+    
+    /* ----------------
+     * read the variable page, update the lastXid field and
+     *  force the page back out to disk.
+     * ----------------
+     */
+    buf = ReadBuffer(VariableRelation, 0);
+    
+    if (! BufferIsValid(buf))
+       {
+           SpinRelease(OidGenLockId);
+           elog(WARN, "VariableRelationPutLastXid: ReadBuffer failed");
+       }
+    
+    var = (VariableRelationContents) BufferGetBlock(buf);
+    
+    TransactionIdStore(xid, &(var->lastXidData));
+    
+    WriteBuffer(buf);
+}
+
+/* --------------------------------
+ *     VariableRelationGetNextOid
+ * --------------------------------
+ */
+void
+VariableRelationGetNextOid(Oid *oid_return)
+{
+    Buffer buf;
+    VariableRelationContents var;
+    
+    /* ----------------
+     * We assume that a spinlock has been acquire to guarantee
+     * exclusive access to the variable relation.
+     * ----------------
+     */
+    
+    /* ----------------
+     * if the variable relation is not initialized, then we
+     *  assume we are running at bootstrap time and so we return
+     *  an invalid object id -- during this time GetNextBootstrapObjectId
+     *  should be called instead..
+     * ----------------
+     */
+    if (! RelationIsValid(VariableRelation)) {
+       if (PointerIsValid(oid_return))
+           (*oid_return) = InvalidOid;
+       return;
+    }
+    
+    /* ----------------
+     * read the variable page, get the the nextOid field and
+     *  release the buffer
+     * ----------------
+     */
+    buf = ReadBuffer(VariableRelation, 0);
+    
+    if (! BufferIsValid(buf))
+       {
+           SpinRelease(OidGenLockId);
+           elog(WARN, "VariableRelationGetNextXid: ReadBuffer failed");
+       }
+    
+    var = (VariableRelationContents) BufferGetBlock(buf);
+    
+    if (PointerIsValid(oid_return)) {
+       
+        /* ----------------
+         * nothing up my sleeve...  what's going on here is that this code
+        * is guaranteed never to be called until all files in data/base/
+        * are created, and the template database exists.  at that point,
+        * we want to append a pg_database tuple.  the first time we do
+        * this, the oid stored in pg_variable will be bogus, so we use
+        * a bootstrap value defined at the top of this file.
+        *
+        * this comment no longer holds true.  This code is called before
+        * all of the files in data/base are created and you can't rely
+        * on system oid's to be less than BootstrapObjectIdData. mer 9/18/91
+         * ----------------
+         */
+       if (OidIsValid(var->nextOid))
+           (*oid_return) = var->nextOid;
+       else
+           (*oid_return) = BootstrapObjectIdData;
+    }
+    
+    ReleaseBuffer(buf);
+}
+
+/* --------------------------------
+ *     VariableRelationPutNextOid
+ * --------------------------------
+ */
+void
+VariableRelationPutNextOid(Oid *oidP)
+{
+    Buffer buf;
+    VariableRelationContents var;
+    
+    /* ----------------
+     * We assume that a spinlock has been acquire to guarantee
+     * exclusive access to the variable relation.
+     * ----------------
+     */
+    
+    /* ----------------
+     * do nothing before things are initialized
+     * ----------------
+     */
+    if (! RelationIsValid(VariableRelation))
+       return;
+    
+    /* ----------------
+     * sanity check
+     * ----------------
+     */
+    if (! PointerIsValid(oidP))
+       {
+           SpinRelease(OidGenLockId);
+           elog(WARN, "VariableRelationPutNextOid: invalid oid pointer");
+       }
+    
+    /* ----------------
+     * read the variable page, update the nextXid field and
+     *  write the page back out to disk.
+     * ----------------
+     */
+    buf = ReadBuffer(VariableRelation, 0);
+    
+    if (! BufferIsValid(buf))
+       {
+           SpinRelease(OidGenLockId);
+           elog(WARN, "VariableRelationPutNextXid: ReadBuffer failed");
+       }
+    
+    var = (VariableRelationContents) BufferGetBlock(buf);
+    
+    var->nextOid = (*oidP);
+    
+    WriteBuffer(buf);
+}
+
+/* ----------------------------------------------------------------
+ *             transaction id generation support
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     GetNewTransactionId
+ *
+ *     In the version 2 transaction system, transaction id's are
+ *     restricted in several ways.
+ *
+ *     First, all transaction id's are even numbers (4, 88, 121342, etc).
+ *     This means the binary representation of the number will never
+ *     have the least significent bit set.  This bit is reserved to
+ *     indicate that the transaction id does not in fact hold an XID,
+ *     but rather a commit time.  This makes it possible for the
+ *     vaccuum daemon to disgard information from the log and time
+ *     relations for committed tuples.  This is important when archiving
+ *     tuples to an optical disk because tuples with commit times
+ *     stored in their xid fields will not need to consult the log
+ *     and time relations.
+ *
+ *     Second, since we may someday preform compression of the data
+ *     in the log and time relations, we cause the numbering of the
+ *     transaction ids to begin at 512.  This means that some space
+ *     on the page of the log and time relations corresponding to
+ *     transaction id's 0 - 510 will never be used.  This space is
+ *     in fact used to store the version number of the postgres
+ *     transaction log and will someday store compression information
+ *     about the log.
+ *
+ *     Lastly, rather then access the variable relation each time
+ *     a backend requests a new transction id, we "prefetch" 32
+ *     transaction id's by incrementing the nextXid stored in the
+ *     var relation by 64 (remember only even xid's are legal) and then
+ *     returning these id's one at a time until they are exhausted.
+ *     This means we reduce the number of accesses to the variable
+ *     relation by 32 for each backend.
+ *
+ *     Note:  32 has no special significance.  We don't want the
+ *            number to be too large because if when the backend
+ *            terminates, we lose the xid's we cached.
+ *
+ * ----------------
+ */
+
+#define VAR_XID_PREFETCH       32
+
+static int prefetched_xid_count = 0;
+static TransactionId next_prefetched_xid;
+
+void
+GetNewTransactionId(TransactionId *xid)
+{
+    TransactionId nextid;
+    
+    /* ----------------
+     * during bootstrap initialization, we return the special
+     *  bootstrap transaction id.
+     * ----------------
+     */
+    if (AMI_OVERRIDE) {        
+       TransactionIdStore(AmiTransactionId, xid);
+       return;
+    }
+    
+    /* ----------------
+     *  if we run out of prefetched xids, then we get some
+     *  more before handing them out to the caller.
+     * ----------------
+     */
+    
+    if (prefetched_xid_count == 0) {
+       /* ----------------
+        *      obtain exclusive access to the variable relation page
+        *
+        *      get the "next" xid from the variable relation
+        *      and save it in the prefetched id.
+        * ----------------
+        */
+       SpinAcquire(OidGenLockId);
+       VariableRelationGetNextXid(&nextid);
+       TransactionIdStore(nextid, &next_prefetched_xid);
+       
+       /* ----------------
+        *      now increment the variable relation's next xid
+        *      and reset the prefetched_xid_count.  We multiply
+        *      the id by two because our xid's are always even.
+        * ----------------
+        */
+       prefetched_xid_count = VAR_XID_PREFETCH;
+       TransactionIdAdd(&nextid, prefetched_xid_count);
+       VariableRelationPutNextXid(nextid);
+       SpinRelease(OidGenLockId);
+    }
+    
+    /* ----------------
+     * return the next prefetched xid in the pointer passed by
+     *  the user and decrement the prefetch count.  We add two
+     *  to id we return the next time this is called because our
+     * transaction ids are always even.
+     *
+     *  XXX Transaction Ids used to be even as the low order bit was
+     *      used to determine commit status.  This is no long true so
+     *      we now use even and odd transaction ids. -mer 5/26/92
+     * ----------------
+     */
+    TransactionIdStore(next_prefetched_xid, xid);
+    TransactionIdAdd(&next_prefetched_xid, 1);
+    prefetched_xid_count--;
+}
+
+/* ----------------
+ *     UpdateLastCommittedXid
+ * ----------------
+ */
+
+void
+UpdateLastCommittedXid(TransactionId xid)
+{
+    TransactionId lastid;
+    
+    
+    /* we assume that spinlock OidGenLockId has been acquired
+     * prior to entering this function
+     */
+    
+    /* ----------------
+     * get the "last committed" transaction id from
+     *  the variable relation page.
+     * ----------------
+     */
+    VariableRelationGetLastXid(&lastid);
+    
+    /* ----------------
+     * if the transaction id is greater than the last committed
+     *  transaction then we update the last committed transaction
+     *  in the variable relation.
+     * ----------------
+     */
+    if (TransactionIdIsLessThan(lastid, xid))
+       VariableRelationPutLastXid(xid);
+    
+}
+
+/* ----------------------------------------------------------------
+ *                 object id generation support
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     GetNewObjectIdBlock
+ *
+ *     This support function is used to allocate a block of object ids
+ *     of the given size.  applications wishing to do their own object
+ *     id assignments should use this 
+ * ----------------
+ */
+void
+GetNewObjectIdBlock(Oid *oid_return, /* place to return the new object id */
+                   int oid_block_size) /* number of oids desired */
+{
+    Oid nextoid;               
+    
+    /* ----------------
+     * SOMEDAY obtain exclusive access to the variable relation page
+     *  That someday is today -mer 6 Aug 1992
+     * ----------------
+     */
+    SpinAcquire(OidGenLockId);
+    
+    /* ----------------
+     * get the "next" oid from the variable relation
+     * and give it to the caller.
+     * ----------------
+     */
+    VariableRelationGetNextOid(&nextoid);
+    if (PointerIsValid(oid_return))
+       (*oid_return) = nextoid;
+    
+    /* ----------------
+     * now increment the variable relation's next oid
+     * field by the size of the oid block requested.
+     * ----------------
+     */
+    nextoid += oid_block_size;
+    VariableRelationPutNextOid(&nextoid);
+    
+    /* ----------------
+     * SOMEDAY relinquish our lock on the variable relation page
+     *  That someday is today -mer 6 Apr 1992
+     * ----------------
+     */
+    SpinRelease(OidGenLockId);
+}
+
+/* ----------------
+ *     GetNewObjectId
+ *
+ *     This function allocates and parses out object ids.  Like
+ *     GetNewTransactionId(), it "prefetches" 32 object ids by
+ *     incrementing the nextOid stored in the var relation by 32 and then
+ *     returning these id's one at a time until they are exhausted.
+ *     This means we reduce the number of accesses to the variable
+ *     relation by 32 for each backend.
+ *
+ *     Note:  32 has no special significance.  We don't want the
+ *            number to be too large because if when the backend
+ *            terminates, we lose the oids we cached.
+ *
+ * ----------------
+ */
+
+#define VAR_OID_PREFETCH       32
+
+static int prefetched_oid_count = 0;
+static Oid next_prefetched_oid;
+
+void
+GetNewObjectId(Oid *oid_return)        /* place to return the new object id */
+{
+    /* ----------------
+     *  if we run out of prefetched oids, then we get some
+     *  more before handing them out to the caller.
+     * ----------------
+     */
+    
+    if (prefetched_oid_count == 0) {
+       int oid_block_size = VAR_OID_PREFETCH;
+       
+       /* ----------------
+        *      during bootstrap time, we want to allocate oids
+        *      one at a time.  Otherwise there might be some
+        *      bootstrap oid's left in the block we prefetch which
+        *      would be passed out after the variable relation was
+        *      initialized.  This would be bad.
+        * ----------------
+        */
+       if (! RelationIsValid(VariableRelation))
+           VariableRelation = heap_openr(VariableRelationName);
+       
+       /* ----------------
+        *      get a new block of prefetched object ids.
+        * ----------------
+        */
+       GetNewObjectIdBlock(&next_prefetched_oid, oid_block_size);
+       
+       /* ----------------
+        *      now reset the prefetched_oid_count.
+        * ----------------
+        */
+       prefetched_oid_count = oid_block_size;
+    }
+    
+    /* ----------------
+     * return the next prefetched oid in the pointer passed by
+     *  the user and decrement the prefetch count.
+     * ----------------
+     */
+    if (PointerIsValid(oid_return))
+       (*oid_return) = next_prefetched_oid;
+    
+    next_prefetched_oid++;
+    prefetched_oid_count--;
+}
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
new file mode 100644 (file)
index 0000000..2cc3704
--- /dev/null
@@ -0,0 +1,1314 @@
+/*-------------------------------------------------------------------------
+ *
+ * xact.c--
+ *    top level transaction system support routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *     
+ * NOTES
+ *     Transaction aborts can now occur two ways:
+ *
+ *     1)  system dies from some internal cause  (Assert, etc..)
+ *     2)  user types abort
+ *
+ *     These two cases used to be treated identically, but now
+ *     we need to distinguish them.  Why?  consider the following
+ *     two situatuons:
+ *
+ *             case 1                          case 2
+ *             ------                          ------
+ *     1) user types BEGIN             1) user types BEGIN
+ *     2) user does something          2) user does something
+ *     3) user does not like what      3) system aborts for some reason
+ *        she shes and types ABORT        
+ *
+ *     In case 1, we want to abort the transaction and return to the
+ *     default state.  In case 2, there may be more commands coming
+ *     our way which are part of the same transaction block and we have
+ *     to ignore these commands until we see an END transaction.
+ *
+ *     Internal aborts are now handled by AbortTransactionBlock(), just as
+ *     they always have been, and user aborts are now handled by
+ *     UserAbortTransactionBlock().  Both of them rely on AbortTransaction()
+ *     to do all the real work.  The only difference is what state we
+ *     enter after AbortTransaction() does it's work:
+ *     
+ *     * AbortTransactionBlock() leaves us in TBLOCK_ABORT and
+ *     * UserAbortTransactionBlock() leaves us in TBLOCK_ENDABORT
+ *     
+ *   NOTES
+ *     This file is an attempt at a redesign of the upper layer
+ *     of the V1 transaction system which was too poorly thought
+ *     out to describe.  This new system hopes to be both simpler
+ *     in design, simpler to extend and needs to contain added
+ *     functionality to solve problems beyond the scope of the V1
+ *     system.  (In particuler, communication of transaction
+ *     information between parallel backends has to be supported)
+ *
+ *     The essential aspects of the transaction system are:
+ *
+ *             o  transaction id generation
+ *             o  transaction log updating
+ *             o  memory cleanup
+ *             o  cache invalidation
+ *             o  lock cleanup
+ *
+ *     Hence, the functional division of the transaction code is
+ *     based on what of the above things need to be done during
+ *     a start/commit/abort transaction.  For instance, the
+ *     routine AtCommit_Memory() takes care of all the memory
+ *     cleanup stuff done at commit time.
+ *
+ *     The code is layered as follows:
+ *
+ *             StartTransaction
+ *             CommitTransaction
+ *             AbortTransaction
+ *             UserAbortTransaction
+ *
+ *     are provided to do the lower level work like recording
+ *     the transaction status in the log and doing memory cleanup.
+ *     above these routines are another set of functions:
+ *
+ *             StartTransactionCommand
+ *             CommitTransactionCommand
+ *             AbortCurrentTransaction
+ *
+ *     These are the routines used in the postgres main processing
+ *     loop.  They are sensitive to the current transaction block state
+ *     and make calls to the lower level routines appropriately.
+ *
+ *     Support for transaction blocks is provided via the functions:
+ *
+ *             StartTransactionBlock
+ *             CommitTransactionBlock
+ *             AbortTransactionBlock
+ *
+ *     These are invoked only in responce to a user "BEGIN", "END",
+ *     or "ABORT" command.  The tricky part about these functions
+ *     is that they are called within the postgres main loop, in between
+ *     the StartTransactionCommand() and CommitTransactionCommand().
+ *
+ *     For example, consider the following sequence of user commands:
+ *
+ *     1)      begin
+ *     2)      retrieve (foo.all)
+ *     3)      append foo (bar = baz)
+ *     4)      end
+ *
+ *     in the main processing loop, this results in the following
+ *     transaction sequence:
+ *
+ *         /   StartTransactionCommand();
+ *     1) /    ProcessUtility();               << begin
+ *        \        StartTransactionBlock();
+ *         \   CommitTransactionCommand();
+ *
+ *         /   StartTransactionCommand();
+ *     2) <    ProcessQuery();                 << retrieve (foo.all)
+ *         \   CommitTransactionCommand();
+ *
+ *         /   StartTransactionCommand();
+ *     3) <    ProcessQuery();                 << append foo (bar = baz)
+ *         \   CommitTransactionCommand();
+ *
+ *         /   StartTransactionCommand();
+ *     4) /    ProcessUtility();               << end
+ *        \        CommitTransactionBlock();
+ *         \   CommitTransactionCommand();
+ *
+ *     The point of this example is to demonstrate the need for
+ *     StartTransactionCommand() and CommitTransactionCommand() to
+ *     be state smart -- they should do nothing in between the calls
+ *     to StartTransactionBlock() and EndTransactionBlock() and
+ *      outside these calls they need to do normal start/commit
+ *     processing.
+ *
+ *     Furthermore, suppose the "retrieve (foo.all)" caused an abort
+ *     condition.  We would then want to abort the transaction and
+ *     ignore all subsequent commands up to the "end".
+ *     -cim 3/23/90
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "access/xact.h"
+#include "commands/async.h"
+#include "storage/bufmgr.h"
+#include "storage/block.h"
+#include "storage/proc.h"
+#include "utils/inval.h"
+#include "utils/relcache.h"
+#include "access/transam.h"
+#include "catalog/heap.h"
+
+/* ----------------
+ *     global variables holding the current transaction state.
+ *
+ *      Note: when we are running several slave processes, the
+ *            current transaction state data is copied into shared memory
+ *           and the CurrentTransactionState pointer changed to
+ *           point to the shared copy.  All this occurrs in slaves.c
+ * ----------------
+ */
+TransactionStateData CurrentTransactionStateData = {
+    0,                         /* transaction id */
+    FirstCommandId,            /* command id */
+    0x0,                       /* start time */
+    TRANS_DEFAULT,             /* transaction state */
+    TBLOCK_DEFAULT             /* transaction block state */
+    };
+
+TransactionState CurrentTransactionState =
+    &CurrentTransactionStateData;
+
+/* ----------------
+ *     info returned when the system is desabled
+ *
+ *     Note:  I have no idea what the significance of the
+ *            1073741823 in DisabledStartTime.. I just carried
+ *            this over when converting things from the old
+ *            V1 transaction system.  -cim 3/18/90
+ * ----------------
+ */
+TransactionId DisabledTransactionId = (TransactionId)-1;
+     
+CommandId DisabledCommandId = (CommandId) -1;
+     
+AbsoluteTime DisabledStartTime = (AbsoluteTime) 1073741823;
+     
+/* ----------------
+ *     overflow flag
+ * ----------------
+ */
+bool CommandIdCounterOverflowFlag;
+     
+/* ----------------
+ *     catalog creation transaction bootstrapping flag.
+ *     This should be eliminated and added to the transaction
+ *     state stuff.  -cim 3/19/90
+ * ----------------
+ */
+bool AMI_OVERRIDE = false;
+     
+/* ----------------------------------------------------------------
+ *                  transaction state accessors
+ * ----------------------------------------------------------------
+ */
+     
+/* --------------------------------
+ *     TranactionFlushEnabled()
+ *     SetTranactionFlushEnabled()
+ *
+ *     These are used to test and set the "TransactionFlushState"
+ *     varable.  If this variable is true (the default), then
+ *     the system will flush all dirty buffers to disk at the end
+ *     of each transaction.   If false then we are assuming the
+ *     buffer pool resides in stable main memory, in which case we
+ *     only do writes as necessary.
+ * --------------------------------
+ */
+static int TransactionFlushState = 1;
+
+int
+TransactionFlushEnabled()
+{    
+    return TransactionFlushState;
+}
+
+void
+SetTransactionFlushEnabled(bool state)
+{    
+    TransactionFlushState = (state == true);
+}
+
+/* --------------------------------
+ *     IsTransactionState
+ *
+ *     This returns true if we are currently running a query
+ *     within an executing transaction.
+ * --------------------------------
+ */
+bool
+IsTransactionState()
+{
+    TransactionState s = CurrentTransactionState;
+    
+    switch (s->state) {
+    case TRANS_DEFAULT:                return false;
+    case TRANS_START:          return true;
+    case TRANS_INPROGRESS:     return true;
+    case TRANS_COMMIT:         return true;
+    case TRANS_ABORT:          return true;
+    case TRANS_DISABLED:       return false;
+    }
+    /*
+     * Shouldn't get here, but lint is not happy with this...
+     */
+    return(false);
+}
+
+/* --------------------------------
+ *     IsAbortedTransactionBlockState
+ *
+ *     This returns true if we are currently running a query
+ *     within an aborted transaction block.
+ * --------------------------------
+ */
+bool
+IsAbortedTransactionBlockState()
+{
+    TransactionState s = CurrentTransactionState;
+    
+    if (s->blockState == TBLOCK_ABORT)
+       return true;
+    
+    return false;
+}
+
+/* --------------------------------
+ *     OverrideTransactionSystem
+ *
+ *     This is used to temporarily disable the transaction
+ *     processing system in order to do initialization of
+ *     the transaction system data structures and relations
+ *     themselves.
+ * --------------------------------
+ */
+int SavedTransactionState;
+
+void
+OverrideTransactionSystem(bool flag)
+{
+    TransactionState s = CurrentTransactionState;
+    
+    if (flag == true) {
+       if (s->state == TRANS_DISABLED)
+           return;
+       
+       SavedTransactionState = s->state;
+       s->state = TRANS_DISABLED;
+    } else {
+       if (s->state != TRANS_DISABLED)
+           return;
+       
+       s->state = SavedTransactionState;
+    }
+}
+
+/* --------------------------------
+ *     GetCurrentTransactionId
+ *
+ *     This returns the id of the current transaction, or
+ *     the id of the "disabled" transaction.
+ * --------------------------------
+ */
+TransactionId
+GetCurrentTransactionId()
+{
+    TransactionState s = CurrentTransactionState;
+    
+    /* ----------------
+     * if the transaction system is disabled, we return
+     *  the special "disabled" transaction id.
+     * ----------------
+     */
+    if (s->state == TRANS_DISABLED)
+       return (TransactionId) DisabledTransactionId;
+    
+    /* ----------------
+     * otherwise return the current transaction id.
+     * ----------------
+     */
+    return (TransactionId) s->transactionIdData;
+}
+
+
+/* --------------------------------
+ *     GetCurrentCommandId
+ * --------------------------------
+ */
+CommandId
+GetCurrentCommandId()
+{
+    TransactionState s = CurrentTransactionState;
+    
+    /* ----------------
+     * if the transaction system is disabled, we return
+     *  the special "disabled" command id.
+     * ----------------
+     */
+    if (s->state == TRANS_DISABLED)
+       return (CommandId) DisabledCommandId;
+    
+    return s->commandId;
+}
+
+
+/* --------------------------------
+ *     GetCurrentTransactionStartTime
+ * --------------------------------
+ */
+AbsoluteTime
+GetCurrentTransactionStartTime()
+{
+    TransactionState s = CurrentTransactionState;
+    
+    /* ----------------
+     * if the transaction system is disabled, we return
+     *  the special "disabled" starting time.
+     * ----------------
+     */
+    if (s->state == TRANS_DISABLED)
+       return (AbsoluteTime) DisabledStartTime;
+    
+    return s->startTime;
+}
+
+
+/* --------------------------------
+ *     TransactionIdIsCurrentTransactionId
+ * --------------------------------
+ */
+bool
+TransactionIdIsCurrentTransactionId(TransactionId xid)
+{
+    TransactionState s = CurrentTransactionState;
+    
+    if (AMI_OVERRIDE)
+       return false;
+    
+    return (bool)
+       TransactionIdEquals(xid, s->transactionIdData);
+}
+
+
+/* --------------------------------
+ *     CommandIdIsCurrentCommandId
+ * --------------------------------
+ */
+bool
+CommandIdIsCurrentCommandId(CommandId cid)
+{
+    TransactionState s = CurrentTransactionState;
+    
+    if (AMI_OVERRIDE)
+       return false;
+    
+    return     
+       (cid == s->commandId) ? true : false;
+}
+
+
+/* --------------------------------
+ *     ClearCommandIdCounterOverflowFlag
+ * --------------------------------
+ */
+void
+ClearCommandIdCounterOverflowFlag()
+{
+    CommandIdCounterOverflowFlag = false;
+}
+
+
+/* --------------------------------
+ *     CommandCounterIncrement
+ * --------------------------------
+ */
+void
+CommandCounterIncrement()
+{
+    CurrentTransactionStateData.commandId += 1;
+    if (CurrentTransactionStateData.commandId == FirstCommandId) {
+       CommandIdCounterOverflowFlag = true;
+       elog(WARN, "You may only have 65535 commands per transaction");
+    }
+    
+    /* make cache changes visible to me */
+    AtCommit_Cache();
+    AtStart_Cache();
+}
+
+/* ----------------------------------------------------------------
+ *                     initialization stuff
+ * ----------------------------------------------------------------
+ */
+void
+InitializeTransactionSystem()
+{
+    InitializeTransactionLog();
+}
+
+/* ----------------------------------------------------------------
+ *                     StartTransaction stuff
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     AtStart_Cache
+ * --------------------------------
+ */
+void
+AtStart_Cache()    
+{
+    DiscardInvalid();
+}
+
+/* --------------------------------
+ *     AtStart_Locks
+ * --------------------------------
+ */
+void
+AtStart_Locks()    
+{
+    /*
+     * at present, it is unknown to me what belongs here -cim 3/18/90
+     *
+     * There isn't anything to do at the start of a xact for locks.
+     *  -mer 5/24/92
+     */
+}
+
+/* --------------------------------
+ *     AtStart_Memory
+ * --------------------------------
+ */
+void
+AtStart_Memory()    
+{
+    Portal          portal;
+    MemoryContext    portalContext;
+    
+    /* ----------------
+     * get the blank portal and its memory context
+     * ----------------
+     */
+    portal = GetPortalByName(NULL);
+    portalContext = (MemoryContext) PortalGetHeapMemory(portal);
+    
+    /* ----------------
+     * tell system to allocate in the blank portal context
+     * ----------------
+     */
+    (void) MemoryContextSwitchTo(portalContext);
+    StartPortalAllocMode(DefaultAllocMode, 0);
+}
+
+
+/* ----------------------------------------------------------------
+ *                     CommitTransaction stuff
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     RecordTransactionCommit
+ *
+ *     Note: the two calls to BufferManagerFlush() exist to ensure
+ *           that data pages are written before log pages.  These
+ *           explicit calls should be replaced by a more efficient
+ *           ordered page write scheme in the buffer manager
+ *           -cim 3/18/90
+ * --------------------------------
+ */
+void
+RecordTransactionCommit()    
+{
+    TransactionId xid;
+    int leak;
+    
+    /* ----------------
+     * get the current transaction id
+     * ----------------
+     */
+    xid = GetCurrentTransactionId();
+    
+    /* ----------------
+     * flush the buffer manager pages.  Note: if we have stable
+     *  main memory, dirty shared buffers are not flushed
+     *  plai 8/7/90
+     * ----------------
+     */
+    leak = BufferPoolCheckLeak();
+    FlushBufferPool(!TransactionFlushEnabled());
+    if (leak) ResetBufferPool();
+    
+    /* ----------------
+     * have the transaction access methods record the status
+     *  of this transaction id in the pg_log / pg_time relations.
+     * ----------------
+     */
+    TransactionIdCommit(xid);
+    
+    /* ----------------
+     * Now write the log/time info to the disk too.
+     * ----------------
+     */
+    leak = BufferPoolCheckLeak();
+    FlushBufferPool(!TransactionFlushEnabled());
+    if (leak) ResetBufferPool();
+}
+
+
+/* --------------------------------
+ *     AtCommit_Cache
+ * --------------------------------
+ */
+void
+AtCommit_Cache()
+{
+    /* ----------------
+     * Make catalog changes visible to me for the next command.
+     * Other backends will not process my invalidation messages until
+     * after I commit and free my locks--though they will do
+     * unnecessary work if I abort.
+     * ----------------
+     */
+    RegisterInvalid(true);
+}
+
+/* --------------------------------
+ *     AtCommit_Locks
+ * --------------------------------
+ */
+void
+AtCommit_Locks()  
+{
+    /* ----------------
+     * XXX What if ProcReleaseLocks fails?  (race condition?) 
+     *
+     *  Then you're up a creek! -mer 5/24/92
+     * ----------------
+     */
+    ProcReleaseLocks();
+}
+
+/* --------------------------------
+ *     AtCommit_Memory
+ * --------------------------------
+ */
+void
+AtCommit_Memory()  
+{
+    /* ----------------
+     * now that we're "out" of a transaction, have the
+     *  system allocate things in the top memory context instead
+     *  of the blank portal memory context.
+     * ----------------
+     */
+    EndPortalAllocMode();
+    (void) MemoryContextSwitchTo(TopMemoryContext);
+}
+
+/* ----------------------------------------------------------------
+ *                     AbortTransaction stuff
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     RecordTransactionAbort
+ * --------------------------------
+ */
+void
+RecordTransactionAbort()    
+{
+    TransactionId xid;
+    
+    /* ----------------
+     * get the current transaction id
+     * ----------------
+     */
+    xid = GetCurrentTransactionId();
+    
+    /* ----------------
+     * have the transaction access methods record the status
+     *  of this transaction id in the pg_log / pg_time relations.
+     * ----------------
+     */
+    TransactionIdAbort(xid);
+    
+    /* ----------------
+     * flush the buffer manager pages.  Note: if we have stable
+     *  main memory, dirty shared buffers are not flushed
+     *  plai 8/7/90
+     * ----------------
+     */
+    ResetBufferPool();
+}
+
+/* --------------------------------
+ *     AtAbort_Cache
+ * --------------------------------
+ */
+void
+AtAbort_Cache()    
+{
+    RegisterInvalid(false);
+}
+
+/* --------------------------------
+ *     AtAbort_Locks
+ * --------------------------------
+ */
+void
+AtAbort_Locks()    
+{
+    /* ----------------
+     * XXX What if ProcReleaseLocks() fails?  (race condition?)
+     *
+     *  Then you're up a creek without a paddle! -mer
+     * ----------------
+     */
+    ProcReleaseLocks();
+}
+
+
+/* --------------------------------
+ *     AtAbort_Memory
+ * --------------------------------
+ */
+void
+AtAbort_Memory()    
+{
+    /* ----------------
+     * after doing an abort transaction, make certain the
+     *  system uses the top memory context rather then the
+     *  portal memory context (until the next transaction).
+     * ----------------
+     */
+    (void) MemoryContextSwitchTo(TopMemoryContext);
+}
+
+/* ----------------------------------------------------------------
+ *                     interface routines
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     StartTransaction
+ *
+ * --------------------------------
+ */
+void
+StartTransaction()
+{
+    TransactionState s = CurrentTransactionState;
+    
+    /* ----------------
+     * Check the current transaction state.  If the transaction system
+     *  is switched off, or if we're already in a transaction, do nothing.
+     *  We're already in a transaction when the monitor sends a null
+     *  command to the backend to flush the comm channel.  This is a
+     *  hacky fix to a communications problem, and we keep having to
+     *  deal with it here.  We should fix the comm channel code.  mao 080891
+     * ----------------
+     */
+    if (s->state == TRANS_DISABLED || s->state == TRANS_INPROGRESS)
+       return;
+    
+    /* ----------------
+     * set the current transaction state information
+     *  appropriately during start processing
+     * ----------------
+     */
+    s->state = TRANS_START;
+    
+    /* ----------------
+     * generate a new transaction id
+     * ----------------
+     */
+    GetNewTransactionId(&(s->transactionIdData));
+    
+    /* ----------------
+     * initialize current transaction state fields
+     * ----------------
+     */
+    s->commandId =             FirstCommandId;
+    s->startTime =             GetCurrentAbsoluteTime();
+    
+    /* ----------------
+     * initialize the various transaction subsystems
+     * ----------------
+     */
+    AtStart_Cache();
+    AtStart_Locks();
+    AtStart_Memory();
+    
+     /* --------------
+        initialize temporary relations list
+        the tempRelList is a list of temporary relations that
+        are created in the course of the transactions
+        they need to be destroyed properly at the end of the transactions
+      */
+     InitTempRelList();
+    /* ----------------
+     * done with start processing, set current transaction
+     *  state to "in progress"
+     * ----------------
+     */
+    s->state = TRANS_INPROGRESS;      
+}
+
+/* ---------------
+ * Tell me if we are currently in progress
+ * ---------------
+ */
+bool
+CurrentXactInProgress()
+{
+    return (CurrentTransactionState->state == TRANS_INPROGRESS);
+}
+
+/* --------------------------------
+ *     CommitTransaction
+ *
+ * --------------------------------
+ */
+void
+CommitTransaction()
+{
+    TransactionState s = CurrentTransactionState;
+    
+    /* ----------------
+     * check the current transaction state
+     * ----------------
+     */
+    if (s->state == TRANS_DISABLED)
+       return;
+    
+    if (s->state != TRANS_INPROGRESS)
+       elog(NOTICE, "CommitTransaction and not in in-progress state ");
+    
+    /* ----------------
+     * set the current transaction state information
+     *  appropriately during the abort processing
+     * ----------------
+     */
+    s->state = TRANS_COMMIT;
+    
+    /* ----------------
+     * do commit processing
+     * ----------------
+     */
+     DestroyTempRels();
+    AtEOXact_portals();
+    RecordTransactionCommit();
+    RelationPurgeLocalRelation(true);
+    AtCommit_Cache();
+    AtCommit_Locks();
+    AtCommit_Memory();
+    
+    /* ----------------
+     * done with commit processing, set current transaction
+     *  state back to default
+     * ----------------
+     */
+    s->state = TRANS_DEFAULT;    
+    {                          /* want this after commit */
+       if (IsNormalProcessingMode())
+           Async_NotifyAtCommit();
+    }
+}
+
+/* --------------------------------
+ *     AbortTransaction
+ *
+ * --------------------------------
+ */
+void
+AbortTransaction()
+{
+    TransactionState s = CurrentTransactionState;
+    
+    /* ----------------
+     * check the current transaction state
+     * ----------------
+     */
+    if (s->state == TRANS_DISABLED)
+       return;
+    
+    if (s->state != TRANS_INPROGRESS)
+       elog(NOTICE, "AbortTransaction and not in in-progress state ");
+    
+    /* ----------------
+     * set the current transaction state information
+     *  appropriately during the abort processing
+     * ----------------
+     */
+    s->state = TRANS_ABORT;
+    
+    /* ----------------
+     * do abort processing
+     * ----------------
+     */
+    AtEOXact_portals();
+    RecordTransactionAbort();
+    RelationPurgeLocalRelation(false);
+    DestroyTempRels();
+    AtAbort_Cache();
+    AtAbort_Locks();
+    AtAbort_Memory();
+    
+    /* ----------------
+     * done with abort processing, set current transaction
+     *  state back to default
+     * ----------------
+     */
+    s->state = TRANS_DEFAULT;
+    {
+       /* We need to do this in case another process notified us while
+          we are in the middle of an aborted transaction.  We need to
+          notify our frontend after we finish the current transaction.
+          -- jw, 1/3/94
+          */
+       if (IsNormalProcessingMode())
+           Async_NotifyAtAbort();
+    }    
+}
+
+/* --------------------------------
+ *     StartTransactionCommand
+ * --------------------------------
+ */
+void
+StartTransactionCommand()
+{
+    TransactionState s = CurrentTransactionState;
+    
+    switch(s->blockState) {
+       /* ----------------
+        *      if we aren't in a transaction block, we
+        *      just do our usual start transaction.
+        * ----------------
+        */
+    case TBLOCK_DEFAULT:
+       StartTransaction();
+       break;
+       
+       /* ----------------
+        *      We should never experience this -- if we do it
+        *      means the BEGIN state was not changed in the previous
+        *      CommitTransactionCommand().  If we get it, we print
+        *      a warning and change to the in-progress state.
+        * ----------------
+        */
+    case TBLOCK_BEGIN:
+       elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_BEGIN");
+       s->blockState = TBLOCK_INPROGRESS;
+       break;
+       
+       /* ----------------
+        *      This is the case when are somewhere in a transaction
+        *      block and about to start a new command.  For now we
+        *      do nothing but someday we may do command-local resource
+        *      initialization.
+        * ----------------
+        */
+    case TBLOCK_INPROGRESS:
+       break;
+       
+       /* ----------------
+        *      As with BEGIN, we should never experience this --
+        *      if we do it means the END state was not changed in the
+        *      previous CommitTransactionCommand().  If we get it, we
+        *      print a warning, commit the transaction, start a new
+        *      transaction and change to the default state.
+        * ----------------
+        */
+    case TBLOCK_END:
+       elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_END");
+       s->blockState = TBLOCK_DEFAULT;
+       CommitTransaction();
+       StartTransaction();
+       break;
+       
+       /* ----------------
+        *      Here we are in the middle of a transaction block but
+        *      one of the commands caused an abort so we do nothing
+        *      but remain in the abort state.  Eventually we will get
+        *      to the "END TRANSACTION" which will set things straight.
+        * ----------------
+        */
+    case TBLOCK_ABORT:
+       break;  
+       
+       /* ----------------
+        *      This means we somehow aborted and the last call to
+        *      CommitTransactionCommand() didn't clear the state so
+        *      we remain in the ENDABORT state and mabey next time
+        *      we get to CommitTransactionCommand() the state will
+        *      get reset to default.
+        * ----------------
+        */
+    case TBLOCK_ENDABORT:
+       elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_ENDABORT");
+       break;  
+    }  
+}
+/* --------------------------------
+ *     CommitTransactionCommand
+ * --------------------------------
+ */
+void
+CommitTransactionCommand()
+{
+    TransactionState s = CurrentTransactionState;
+    
+    switch(s->blockState) {
+       /* ----------------
+        *      if we aren't in a transaction block, we
+        *      just do our usual transaction commit
+        * ----------------
+        */
+    case TBLOCK_DEFAULT:
+       CommitTransaction();
+       break;
+       
+       /* ----------------
+        *      This is the case right after we get a "BEGIN TRANSACTION"
+        *      command, but the user hasn't done anything else yet, so
+        *      we change to the "transaction block in progress" state
+        *      and return.   
+        * ----------------
+        */
+    case TBLOCK_BEGIN:
+       s->blockState = TBLOCK_INPROGRESS;
+       break;
+       
+       /* ----------------
+        *      This is the case when we have finished executing a command
+        *      someplace within a transaction block.  We increment the
+        *      command counter and return.  Someday we may free resources
+        *      local to the command.
+        * ----------------
+        */
+    case TBLOCK_INPROGRESS:
+       CommandCounterIncrement();
+       break;
+       
+       /* ----------------
+        *      This is the case when we just got the "END TRANSACTION"
+        *      statement, so we go back to the default state and
+        *      commit the transaction.
+        * ----------------
+        */
+    case TBLOCK_END:
+       s->blockState = TBLOCK_DEFAULT;
+       CommitTransaction();
+       break;
+       
+       /* ----------------
+        *      Here we are in the middle of a transaction block but
+        *      one of the commands caused an abort so we do nothing
+        *      but remain in the abort state.  Eventually we will get
+        *      to the "END TRANSACTION" which will set things straight.
+        * ----------------
+        */
+    case TBLOCK_ABORT:
+       break;
+       
+       /* ----------------
+        *      Here we were in an aborted transaction block which
+        *      just processed the "END TRANSACTION" command from the
+        *      user, so now we return the to default state.
+        * ----------------
+        */
+    case TBLOCK_ENDABORT:
+       s->blockState = TBLOCK_DEFAULT;  
+       break;
+    }    
+}
+
+/* --------------------------------
+ *     AbortCurrentTransaction
+ * --------------------------------
+ */
+void
+AbortCurrentTransaction()
+{
+    TransactionState s = CurrentTransactionState;
+    
+    switch(s->blockState) {
+       /* ----------------
+        *      if we aren't in a transaction block, we
+        *      just do our usual abort transaction.
+        * ----------------
+        */
+    case TBLOCK_DEFAULT:
+       AbortTransaction();
+       break;
+       
+       /* ----------------
+        *      If we are in the TBLOCK_BEGIN it means something
+        *      screwed up right after reading "BEGIN TRANSACTION"
+        *      so we enter the abort state.  Eventually an "END
+        *      TRANSACTION" will fix things.
+        * ----------------
+        */
+    case TBLOCK_BEGIN:
+       s->blockState = TBLOCK_ABORT;
+       AbortTransaction();
+       break;
+       
+       /* ----------------
+        *      This is the case when are somewhere in a transaction
+        *      block which aborted so we abort the transaction and
+        *      set the ABORT state.  Eventually an "END TRANSACTION"
+        *      will fix things and restore us to a normal state.
+        * ----------------
+        */
+    case TBLOCK_INPROGRESS:
+       s->blockState = TBLOCK_ABORT;
+       AbortTransaction();
+       break;
+       
+       /* ----------------
+        *      Here, the system was fouled up just after the
+        *      user wanted to end the transaction block so we
+        *      abort the transaction and put us back into the
+        *      default state.
+        * ----------------
+        */
+    case TBLOCK_END:
+       s->blockState = TBLOCK_DEFAULT;
+       AbortTransaction();
+       break;
+       
+       /* ----------------
+        *      Here, we are already in an aborted transaction
+        *      state and are waiting for an "END TRANSACTION" to
+        *      come along and lo and behold, we abort again!
+        *      So we just remain in the abort state.
+        * ----------------
+        */
+    case TBLOCK_ABORT:
+       break;
+       
+       /* ----------------
+        *      Here we were in an aborted transaction block which
+        *      just processed the "END TRANSACTION" command but somehow
+        *      aborted again.. since we must have done the abort
+        *      processing, we return to the default state.
+        * ----------------
+        */
+    case TBLOCK_ENDABORT:
+       s->blockState = TBLOCK_DEFAULT;  
+       break;
+    }
+}
+
+/* ----------------------------------------------------------------
+ *                    transaction block support
+ * ----------------------------------------------------------------
+ */
+/* --------------------------------
+ *     BeginTransactionBlock
+ * --------------------------------
+ */
+void
+BeginTransactionBlock()
+{
+    TransactionState s = CurrentTransactionState;
+    
+    /* ----------------
+     * check the current transaction state
+     * ----------------
+     */
+    if (s->state == TRANS_DISABLED)
+       return;
+    
+    if (s->blockState != TBLOCK_DEFAULT)
+       elog(NOTICE, "BeginTransactionBlock and not in default state ");
+    
+    /* ----------------
+     * set the current transaction block state information
+     *  appropriately during begin processing
+     * ----------------
+     */
+    s->blockState = TBLOCK_BEGIN;
+    
+    /* ----------------
+     * do begin processing
+     * ----------------
+     */
+    
+    /* ----------------
+     * done with begin processing, set block state to inprogress
+     * ----------------
+     */
+    s->blockState = TBLOCK_INPROGRESS;    
+}
+
+/* --------------------------------
+ *     EndTransactionBlock
+ * --------------------------------
+ */
+void
+EndTransactionBlock()
+{
+    TransactionState s = CurrentTransactionState;
+    
+    /* ----------------
+     * check the current transaction state
+     * ----------------
+     */
+    if (s->state == TRANS_DISABLED)
+       return;
+    
+    if (s->blockState == TBLOCK_INPROGRESS) {
+       /* ----------------
+        *  here we are in a transaction block which should commit
+        *  when we get to the upcoming CommitTransactionCommand()
+        *  so we set the state to "END".  CommitTransactionCommand()
+        *  will recognize this and commit the transaction and return
+        *  us to the default state
+        * ----------------
+        */
+       s->blockState = TBLOCK_END;
+       return;
+    }
+    
+    if (s->blockState == TBLOCK_ABORT) {
+       /* ----------------
+        *  here, we are in a transaction block which aborted
+        *  and since the AbortTransaction() was already done,
+        *  we do whatever is needed and change to the special
+        *  "END ABORT" state.  The upcoming CommitTransactionCommand()
+        *  will recognise this and then put us back in the default
+        *  state.
+        * ----------------
+        */
+       s->blockState = TBLOCK_ENDABORT;
+       return;
+    }
+    
+    /* ----------------
+     * We should not get here, but if we do, we go to the ENDABORT
+     *  state after printing a warning.  The upcoming call to
+     *  CommitTransactionCommand() will then put us back into the
+     *  default state.
+     * ----------------
+     */
+    elog(NOTICE, "EndTransactionBlock and not inprogress/abort state ");
+    s->blockState = TBLOCK_ENDABORT;
+}
+
+/* --------------------------------
+ *     AbortTransactionBlock
+ * --------------------------------
+ */
+void
+AbortTransactionBlock()
+{
+    TransactionState s = CurrentTransactionState;
+    
+    /* ----------------
+     * check the current transaction state
+     * ----------------
+     */
+    if (s->state == TRANS_DISABLED)
+       return;
+    
+    if (s->blockState == TBLOCK_INPROGRESS) {
+       /* ----------------
+        *  here we were inside a transaction block something
+        *  screwed up inside the system so we enter the abort state,
+        *  do the abort processing and then return.
+        *  We remain in the abort state until we see the upcoming
+        *  END TRANSACTION command.
+        * ----------------
+        */
+       s->blockState = TBLOCK_ABORT;
+       
+       /* ----------------
+        *  do abort processing and return
+        * ----------------
+        */
+       AbortTransaction();
+       return;
+    }
+    
+    /* ----------------
+     * this case should not be possible, because it would mean
+     *  the user entered an "abort" from outside a transaction block.
+     *  So we print an error message, abort the transaction and
+     *  enter the "ENDABORT" state so we will end up in the default
+     *  state after the upcoming CommitTransactionCommand().
+     * ----------------
+     */
+    elog(NOTICE, "AbortTransactionBlock and not inprogress state");
+    AbortTransaction();
+    s->blockState = TBLOCK_ENDABORT;
+}
+
+/* --------------------------------
+ *     UserAbortTransactionBlock
+ * --------------------------------
+ */
+void
+UserAbortTransactionBlock()
+{
+    TransactionState s = CurrentTransactionState;
+    
+    /* ----------------
+     * check the current transaction state
+     * ----------------
+     */
+    if (s->state == TRANS_DISABLED)
+       return;
+    
+    if (s->blockState == TBLOCK_INPROGRESS) {
+       /* ----------------
+        *  here we were inside a transaction block and we
+        *  got an abort command from the user, so we move to
+        *  the abort state, do the abort processing and
+        *  then change to the ENDABORT state so we will end up
+        *  in the default state after the upcoming
+        *  CommitTransactionCommand().
+        * ----------------
+        */
+       s->blockState = TBLOCK_ABORT;
+       
+       /* ----------------
+        *  do abort processing
+        * ----------------
+        */
+       AbortTransaction();
+       
+       /* ----------------
+        *  change to the end abort state and return
+        * ----------------
+        */
+       s->blockState = TBLOCK_ENDABORT;
+       return;
+    }
+    
+    /* ----------------
+     * this case should not be possible, because it would mean
+     *  the user entered an "abort" from outside a transaction block.
+     *  So we print an error message, abort the transaction and
+     *  enter the "ENDABORT" state so we will end up in the default
+     *  state after the upcoming CommitTransactionCommand().
+     * ----------------
+     */
+    elog(NOTICE, "UserAbortTransactionBlock and not inprogress state");
+    AbortTransaction();
+    s->blockState = TBLOCK_ENDABORT;
+}
+
+bool
+IsTransactionBlock()
+{
+    TransactionState s = CurrentTransactionState;
+    
+    if (s->blockState == TBLOCK_INPROGRESS
+       || s->blockState == TBLOCK_ENDABORT) {
+       return (true);
+    }
+    
+    return (false);
+}
diff --git a/src/backend/access/transam/xid.c b/src/backend/access/transam/xid.c
new file mode 100644 (file)
index 0000000..587228c
--- /dev/null
@@ -0,0 +1,156 @@
+/*-------------------------------------------------------------------------
+ *
+ * xid.c--
+ *    POSTGRES transaction identifier code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * OLD COMMENTS
+ * XXX WARNING
+ *     Much of this file will change when we change our representation
+ *     of transaction ids -cim 3/23/90
+ *
+ * It is time to make the switch from 5 byte to 4 byte transaction ids
+ * This file was totally reworked. -mer 5/22/92
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include "postgres.h"
+#include "utils/palloc.h"
+#include "utils/elog.h"
+#include "utils/memutils.h"
+#include "utils/nabstime.h"
+
+extern TransactionId NullTransactionId;
+extern TransactionId DisabledTransactionId;
+extern TransactionId AmiTransactionId;
+extern TransactionId FirstTransactionId;
+
+/* ----------------------------------------------------------------
+ *     TransactionIdIsValid
+ *
+ *     Macro-ize me.
+ * ----------------------------------------------------------------
+ */
+bool
+TransactionIdIsValid(TransactionId transactionId)
+{
+    return ((bool) (transactionId != NullTransactionId) );
+}
+
+/* XXX char16 name for catalogs */
+TransactionId
+xidin(char *representation)
+{
+    return (atol(representation));
+}
+
+/* XXX char16 name for catalogs */
+char*
+xidout(TransactionId transactionId)
+{
+/*    return(TransactionIdFormString(transactionId)); */
+    char                       *representation;
+    
+    /* maximum 32 bit unsigned integer representation takes 10 chars */
+    representation = palloc(11);
+    
+    (void)sprintf(representation, "%u", transactionId);
+    
+    return (representation);
+
+}
+
+/* ----------------------------------------------------------------
+ *     StoreInvalidTransactionId
+ *
+ *     Maybe do away with Pointer types in these routines.
+ *      Macro-ize this one.
+ * ----------------------------------------------------------------
+ */
+void
+StoreInvalidTransactionId(TransactionId *destination)
+{
+    *destination = NullTransactionId;
+}
+
+/* ----------------------------------------------------------------
+ *     TransactionIdStore
+ *
+ *      Macro-ize this one.
+ * ----------------------------------------------------------------
+ */
+void
+TransactionIdStore(TransactionId transactionId,
+                  TransactionId *destination)
+{
+    *destination = transactionId;
+}
+
+/* ----------------------------------------------------------------
+ *     TransactionIdEquals
+ * ----------------------------------------------------------------
+ */
+bool
+TransactionIdEquals(TransactionId id1, TransactionId id2)
+{
+    return ((bool) (id1 == id2));
+}
+
+/* ----------------------------------------------------------------
+ *     TransactionIdIsLessThan
+ * ----------------------------------------------------------------
+ */
+bool
+TransactionIdIsLessThan(TransactionId id1, TransactionId id2)
+{
+    return ((bool)(id1 < id2));
+}
+
+/* ----------------------------------------------------------------
+ *     xideq
+ * ----------------------------------------------------------------
+ */
+
+/*
+ *     xideq           - returns 1, iff xid1 == xid2
+ *                               0  else;
+ */
+bool
+xideq(TransactionId xid1, TransactionId xid2)
+{
+    return( (bool) (xid1 == xid2) );
+}
+
+
+
+/* ----------------------------------------------------------------
+ *     TransactionIdIncrement
+ * ----------------------------------------------------------------
+ */
+void
+TransactionIdIncrement(TransactionId *transactionId)
+{
+    
+    (*transactionId)++;
+    if (*transactionId == DisabledTransactionId)
+       elog(FATAL, "TransactionIdIncrement: exhausted XID's");
+    return;
+}
+
+/* ----------------------------------------------------------------
+ *     TransactionIdAdd
+ * ----------------------------------------------------------------
+ */
+void
+TransactionIdAdd(TransactionId *xid, int value)
+{
+    *xid += value;
+    return;
+}
+
diff --git a/src/backend/access/tupdesc.h b/src/backend/access/tupdesc.h
new file mode 100644 (file)
index 0000000..a3f93dd
--- /dev/null
@@ -0,0 +1,53 @@
+/*-------------------------------------------------------------------------
+ *
+ * tupdesc.h--
+ *    POSTGRES tuple descriptor definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        TUPDESC_H
+#define TUPDESC_H
+
+#include "postgres.h"
+#include "access/attnum.h"
+#include "nodes/pg_list.h"     /* for List */
+#include "catalog/pg_attribute.h"
+
+/*
+ * a TupleDesc is an array of AttributeTupleForms, each of which is a
+ * pointer to a AttributeTupleForm
+ */
+/* typedef AttributeTupleForm      *TupleDesc; */
+
+/* a TupleDesc is a pointer to a structure which includes an array of */
+/* AttributeTupleForms, i.e. pg_attribute information, and the size of */
+/* the array, i.e. the number of attributes */
+/* in short, a TupleDesc completely captures the attribute information */
+/* for a tuple */
+
+typedef struct tupleDesc {
+    int  natts;
+    AttributeTupleForm *attrs;
+} *TupleDesc;
+
+extern TupleDesc CreateTemplateTupleDesc(int natts);
+
+extern TupleDesc CreateTupleDesc(int natts, AttributeTupleForm *attrs);
+
+extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc);
+
+extern bool TupleDescInitEntry(TupleDesc desc,
+                              AttrNumber attributeNumber,
+                              char *attributeName, 
+                              char *typeName, 
+                              int attdim, 
+                              bool attisset);
+
+extern TupleDesc BuildDescForRelation(List *schema, char *relname);
+
+#endif /* TUPDESC_H */
diff --git a/src/backend/access/tupmacs.h b/src/backend/access/tupmacs.h
new file mode 100644 (file)
index 0000000..bc1c6ec
--- /dev/null
@@ -0,0 +1,43 @@
+/*-------------------------------------------------------------------------
+ *
+ * tupmacs.h--
+ *    Tuple macros used by both index tuples and heap tuples.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef TUPMACS_H
+#define TUPMACS_H
+
+/*
+ * check to see if the ATT'th bit of an array of 8-bit bytes is set.
+ */
+#define att_isnull(ATT, BITS) (!((BITS)[(ATT) >> 3] & (1 << ((ATT) & 0x07))))
+
+/*
+ * given a AttributeTupleForm and a pointer into a tuple's data
+ * area, return the correct value or pointer.
+ *
+ * note that T must already be properly LONGALIGN/SHORTALIGN'd for
+ * this to work correctly.
+ *
+ * the double-cast is to stop gcc from (correctly) complaining about 
+ * casting integer types with size < sizeof(char *) to (char *).
+ * sign-extension may get weird if you use an integer type that
+ * isn't the same size as (char *) for the first cast.  (on the other
+ * hand, it's safe to use another type for the (foo *)(T).)
+ */
+#define fetchatt(A, T) \
+ ((*(A))->attbyval \
+  ? ((*(A))->attlen > sizeof(int16) \
+     ? (char *) (long) *((int32 *)(T)) \
+     : ((*(A))->attlen < sizeof(int16) \
+        ? (char *) (long) *((char *)(T)) \
+        : (char *) (long) *((int16 *)(T)))) \
+  : (char *) (T))
+       
+#endif
diff --git a/src/backend/access/valid.h b/src/backend/access/valid.h
new file mode 100644 (file)
index 0000000..d79bfd5
--- /dev/null
@@ -0,0 +1,37 @@
+/*-------------------------------------------------------------------------
+ *
+ * valid.h--
+ *    POSTGRES tuple qualification validity definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        VALID_H
+#define VALID_H
+
+#include "c.h"
+#include "access/skey.h"
+#include "storage/buf.h"
+#include "utils/tqual.h"
+#include "access/tupdesc.h"
+#include "utils/rel.h"
+#include "storage/bufpage.h"
+
+/* ----------------
+ *     extern decl's
+ * ----------------
+ */
+
+extern bool heap_keytest(HeapTuple t, TupleDesc tupdesc,
+                        int nkeys, ScanKey keys);
+
+extern HeapTuple heap_tuple_satisfies(ItemId itemId, Relation relation,
+       PageHeader disk_page, TimeQual qual, int nKeys, ScanKey key);
+
+extern bool TupleUpdatedByCurXactAndCmd(HeapTuple t);
+
+#endif /* VALID_H */
diff --git a/src/backend/access/xact.h b/src/backend/access/xact.h
new file mode 100644 (file)
index 0000000..aa7b1e2
--- /dev/null
@@ -0,0 +1,115 @@
+/*-------------------------------------------------------------------------
+ *
+ * xact.h--
+ *    postgres transaction system header
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef XACT_H
+#define XACT_H
+
+#include <signal.h>
+
+#include "storage/ipc.h"
+#include "miscadmin.h"
+#include "utils/portal.h"
+#include "utils/elog.h"
+#include "utils/mcxt.h"
+#include "utils/nabstime.h"
+
+/* ----------------
+ *     transaction state structure
+ * ----------------
+ */
+typedef struct TransactionStateData {
+    TransactionId      transactionIdData;
+    CommandId          commandId;
+    AbsoluteTime               startTime;
+    int                        state;
+    int                        blockState;
+} TransactionStateData;
+
+/* ----------------
+ *     transaction states
+ * ----------------
+ */
+#define TRANS_DEFAULT          0
+#define TRANS_START            1
+#define TRANS_INPROGRESS       2
+#define TRANS_COMMIT           3
+#define TRANS_ABORT            4
+#define TRANS_DISABLED         5
+
+/* ----------------
+ *     transaction block states
+ * ----------------
+ */
+#define TBLOCK_DEFAULT         0
+#define TBLOCK_BEGIN           1
+#define TBLOCK_INPROGRESS      2
+#define TBLOCK_END             3
+#define TBLOCK_ABORT           4
+#define TBLOCK_ENDABORT                5
+
+typedef TransactionStateData *TransactionState;
+
+/* ----------------
+ *     extern definitions
+ * ----------------
+ */
+extern int TransactionFlushEnabled();
+extern void SetTransactionFlushEnabled(bool state);
+
+extern bool IsTransactionState(void);
+extern bool IsAbortedTransactionBlockState(void);
+extern void OverrideTransactionSystem(bool flag);
+extern TransactionId GetCurrentTransactionId(void);
+extern CommandId GetCurrentCommandId(void);
+extern AbsoluteTime GetCurrentTransactionStartTime(void);
+extern bool TransactionIdIsCurrentTransactionId(TransactionId xid);
+extern bool CommandIdIsCurrentCommandId(CommandId cid);
+extern void ClearCommandIdCounterOverflowFlag(void);
+extern void CommandCounterIncrement(void);
+extern void InitializeTransactionSystem(void);
+extern void AtStart_Cache(void);
+extern void AtStart_Locks(void);
+extern void AtStart_Memory(void);
+extern void RecordTransactionCommit(void);
+extern void AtCommit_Cache(void);
+extern void AtCommit_Locks(void);
+extern void AtCommit_Memory(void);
+extern void RecordTransactionAbort(void);
+extern void AtAbort_Cache(void);
+extern void AtAbort_Locks(void);
+extern void AtAbort_Memory(void);
+extern void StartTransaction(void);
+extern bool CurrentXactInProgress(void);
+extern void CommitTransaction(void);
+extern void AbortTransaction(void);
+extern void StartTransactionCommand(void);
+extern void CommitTransactionCommand(void);
+extern void AbortCurrentTransaction(void);
+extern void BeginTransactionBlock(void);
+extern void EndTransactionBlock(void);
+extern void AbortTransactionBlock(void);
+extern bool IsTransactionBlock();
+extern void UserAbortTransactionBlock();
+
+extern TransactionId DisabledTransactionId;
+
+/* defined in xid.c */
+extern bool TransactionIdIsValid(TransactionId transactionId);
+extern void StoreInvalidTransactionId(TransactionId *destination);
+extern void TransactionIdStore(TransactionId transactionId,
+                              TransactionId *destination);
+extern bool TransactionIdEquals(TransactionId id1, TransactionId id2);
+extern bool TransactionIdIsLessThan(TransactionId id1, TransactionId id2);
+extern void TransactionIdIncrement(TransactionId *transactionId);
+extern void TransactionIdAdd(TransactionId *xid, int value);
+
+#endif /* XACT_H */
diff --git a/src/backend/bootstrap/Makefile.inc b/src/backend/bootstrap/Makefile.inc
new file mode 100644 (file)
index 0000000..09d74aa
--- /dev/null
@@ -0,0 +1,63 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the bootstrap module
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#
+# Another kinda weird Makefile.inc cause we need two
+#  scanner/parsers in the backend and most yaccs and lexs
+#  don't have the prefix option.
+#
+#      sed files are HACK CITY! - redo...
+#
+#-------------------------------------------------------------------------
+
+bootdir= $(CURDIR)/bootstrap
+VPATH:= $(VPATH):$(bootdir)
+
+#BOOTYACCS= bootstrap_tokens.h bootparse.c
+BOOTYACCS= bootparse.c
+
+SRCS_BOOTSTRAP= bootparse.c bootscanner.c bootstrap.c 
+
+$(BOOTYACCS): bootparse.y
+       cd $(objdir); \
+       $(YACC) $(YFLAGS) $<; \
+       sed -f $(bootdir)/boot.sed < y.tab.c > bootparse.c; \
+       mv y.tab.h bootstrap_tokens.h; \
+       rm -f y.tab.c
+
+$(objdir)/bootparse.o: bootparse.c
+       $(cc_inobjdir)
+
+
+bootscanner.c: bootscanner.l
+       cd $(objdir); \
+       $(LEX) $<; \
+       sed -f $(bootdir)/boot.sed < lex.yy.c > bootscanner.c; \
+       rm -f lex.yy.c
+
+$(objdir)/bootscanner.o: bootscanner.c
+       $(cc_inobjdir)
+
+
+
+#
+# The following insures that y.tab.h gets made as bootstrap.c
+# includes it
+#
+bootstrap.o: $(BOOTYACCS)
+
+POSTGRES_DEPEND+= $(BOOTYACCS) bootscanner.c
+
+
+CLEANFILES+= bootscanner.c $(BOOTYACCS) y.tab.h y.output
+
+HEADERS+= bootstrap.h
+
diff --git a/src/backend/bootstrap/boot.sed b/src/backend/bootstrap/boot.sed
new file mode 100644 (file)
index 0000000..329497f
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# lex.sed - sed rules to remove conflicts between the 
+#               bootstrap backend interface LEX scanner and the
+#               normal backend SQL LEX scanner
+#
+# $Header$
+#
+s/^yy/Int_yy/g
+s/\([^a-zA-Z0-9_]\)yy/\1Int_yy/g
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
new file mode 100644 (file)
index 0000000..8bb503b
--- /dev/null
@@ -0,0 +1,293 @@
+%{
+/*-------------------------------------------------------------------------
+ *
+ * backendparse.y--
+ *    yacc parser grammer for the "backend" initialization program.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "access/heapam.h"
+#include "access/tupdesc.h"
+#include "bootstrap/bootstrap.h"
+#include "utils/portal.h" 
+#include "storage/smgr.h" 
+#include "nodes/pg_list.h"
+#include "catalog/catalog.h"
+#include "catalog/catname.h"
+#include "catalog/heap.h"
+#include "catalog/index.h"
+#include "commands/rename.h"
+#include "commands/defrem.h"
+#include "access/transam.h"
+#include "access/xact.h"
+
+#define DO_START { StartTransactionCommand();\
+                }
+
+#define DO_END   { CommitTransactionCommand();\
+                  if (!Quiet) { EMITPROMPT; }\
+                  fflush(stdout); \
+                }
+
+int num_tuples_read = 0;
+static Oid objectid;
+
+%}
+
+%union {
+    List     *list;
+    IndexElem  *ielem;
+    char     *str;
+    int                ival;
+}
+
+%type <list>  arg_list
+%type <ielem> index_params index_on
+%type <ival> const ident
+%type <ival> optbootstrap optoideq tuple tuplelist
+
+%token <ival> CONST ID
+%token OPEN XCLOSE XCREATE INSERT_TUPLE
+%token STRING XDEFINE 
+%token XDECLARE INDEX ON USING XBUILD INDICES
+%token COMMA EQUALS LPAREN RPAREN
+%token OBJ_ID XBOOTSTRAP NULLVAL
+%start TopLevel
+
+%nonassoc low
+%nonassoc high
+
+%%
+
+TopLevel:
+         Queries
+       |
+       ;
+
+Queries:
+         Query
+       | Queries Query
+       ;
+
+Query :
+         OpenStmt
+       | CloseStmt 
+       | CreateStmt
+       | InsertStmt 
+       | DeclareIndexStmt
+       | BuildIndsStmt
+       ;
+
+OpenStmt: 
+         OPEN ident
+               { 
+                   DO_START;
+                   boot_openrel(LexIDStr($2));
+                   DO_END; 
+               }       
+       ;
+
+CloseStmt:
+         XCLOSE ident %prec low
+               {
+                   DO_START;
+                   closerel(LexIDStr($2));
+                   DO_END;
+               }
+       | XCLOSE %prec high
+               {
+                   DO_START;
+                   closerel(NULL);
+                   DO_END;
+               }
+       ;
+
+CreateStmt:
+         XCREATE optbootstrap ident LPAREN 
+               { 
+                   DO_START; 
+                   numattr=(int)0;
+               }
+         typelist 
+               { 
+                   if (!Quiet) putchar('\n');
+                   DO_END;
+               }
+         RPAREN 
+               { 
+                   DO_START; 
+
+                   if ($2) {
+                       extern Relation reldesc;
+                       TupleDesc tupdesc;
+
+                       if (reldesc) {
+                           puts("create bootstrap: Warning, open relation");
+                           puts("exists, closing first");
+                           closerel(NULL);
+                       }
+                       if (DebugMode)
+                           puts("creating bootstrap relation");
+                       tupdesc = CreateTupleDesc(numattr,attrtypes);
+                       reldesc = heap_creatr(LexIDStr($3),
+                                             DEFAULT_SMGR,
+                                             tupdesc);
+                       if (DebugMode)
+                           puts("bootstrap relation created ok");
+                   } else {
+                       Oid id;
+                       TupleDesc tupdesc;
+                       /* extern Oid heap_create();*/
+
+                       tupdesc = CreateTupleDesc(numattr,attrtypes);
+                       id = heap_create(LexIDStr($3),
+                                        NULL,
+                                        'n',
+                                        DEFAULT_SMGR,
+                                        tupdesc);
+                       if (!Quiet)
+                           printf("CREATED relation %s with OID %d\n",
+                                  LexIDStr($3), id);
+                   }
+                   DO_END;
+                   if (DebugMode)
+                       puts("Commit End");
+               }
+       ;
+
+InsertStmt:
+         INSERT_TUPLE optoideq         
+               { 
+                   DO_START;
+                   if (DebugMode)
+                       printf("tuple %d<", $2);
+                   num_tuples_read = 0;
+               }
+         LPAREN  tuplelist RPAREN      
+               {
+                   if (num_tuples_read != numattr)
+                       elog(WARN,"incorrect number of values for tuple");
+                   if (reldesc == (Relation)NULL) {
+                       elog(WARN,"must OPEN RELATION before INSERT\n");
+                       err();
+                   }
+                   if (DebugMode)
+                       puts("Insert Begin");
+                   objectid = $2;
+                   InsertOneTuple(objectid);
+                   if (DebugMode)
+                       puts("Insert End");
+                   if (!Quiet) { putchar('\n'); }
+                   DO_END;
+                   if (DebugMode)
+                       puts("Transaction End");
+               } 
+       ;
+
+DeclareIndexStmt:
+         XDECLARE INDEX ident ON ident USING ident LPAREN index_params RPAREN
+               {
+                 List *params;
+
+                 DO_START;
+
+                 params = lappend(NIL, (List*)$9);
+                 DefineIndex(LexIDStr($5), 
+                             LexIDStr($3), 
+                             LexIDStr($7),
+                             params, NIL, 0, NIL);
+                 DO_END;
+               }
+       ;
+
+BuildIndsStmt:
+         XBUILD INDICES        { build_indices(); }
+
+index_params:
+       index_on ident
+               {
+                   IndexElem *n = (IndexElem*)$1;
+                   n->class = LexIDStr($2);
+                   $$ = n;
+               }
+
+index_on:
+         ident
+               {
+                   IndexElem *n = makeNode(IndexElem);
+                   n->name = LexIDStr($1);
+                   $$ = n;
+               }
+       | ident LPAREN arg_list RPAREN
+               {
+                   IndexElem *n = makeNode(IndexElem);
+                   n->name = LexIDStr($1);
+                   n->args = (List*)$3;
+                   $$ = n;
+               }
+
+arg_list:
+         ident
+               {
+                 $$ = lappend(NIL, makeString(LexIDStr($1)));
+               }
+       | arg_list COMMA ident
+               {
+                 $$ = lappend((List*)$1, makeString(LexIDStr($3)));
+               }
+    
+optbootstrap:
+           XBOOTSTRAP  { $$ = 1; }
+       |               { $$ = 0; }
+       ;
+
+typelist:
+         typething
+       | typelist COMMA typething
+       ;
+
+typething:
+         ident EQUALS ident
+               { 
+                  if(++numattr > MAXATTR)
+                       elog(FATAL,"Too many attributes\n");
+                  DefineAttr(LexIDStr($1),LexIDStr($3),numattr-1);
+                  if (DebugMode)
+                      printf("\n");
+               }
+       ;
+
+optoideq:
+           OBJ_ID EQUALS ident { $$ = atol(LexIDStr($3));              }
+       |                       { extern Oid newoid(); $$ = newoid();   }
+       ;
+
+tuplelist:
+          tuple
+       |  tuplelist tuple
+       |  tuplelist COMMA tuple
+       ;
+
+tuple:
+         ident {InsertOneValue(objectid, LexIDStr($1), num_tuples_read++); }
+        | const {InsertOneValue(objectid, LexIDStr($1), num_tuples_read++); }
+       | NULLVAL
+           { InsertOneNull(num_tuples_read++); }
+       ;
+  
+const :
+         CONST { $$=yylval.ival; }
+       ;
+
+ident :
+         ID    { $$=yylval.ival; }
+       ;
+%%
+
+
diff --git a/src/backend/bootstrap/bootscanner.l b/src/backend/bootstrap/bootscanner.l
new file mode 100644 (file)
index 0000000..e7f10e7
--- /dev/null
@@ -0,0 +1,108 @@
+%{
+/*-------------------------------------------------------------------------
+ *
+ * bootscanner.lex--
+ *    a lexical scanner for the bootstrap parser
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "bootstrap/bootstrap.h"
+#include "utils/portal.h" 
+#include "access/xact.h"
+#include "parser/scansup.h"
+
+#include "bootstrap_tokens.h"
+
+/* some versions of lex define this as a macro */
+#if defined(yywrap)
+#undef yywrap
+#endif /* yywrap */
+
+YYSTYPE        yylval;
+int    yyline;  /* keep track of the line number for error reporting */
+
+%}
+
+D      [0-9]
+oct     \\{D}{D}{D}
+Exp    [Ee][-+]?{D}+
+id      ([A-Za-z0-9_]|{oct}|\-)+
+sid     \"([^\"])*\"
+arrayid        [A-Za-z0-9_]+\[{D}*\]
+
+%%
+
+open           { return(OPEN); }
+
+close          { return(XCLOSE); }
+
+create         { return(XCREATE); }
+
+OID             { return(OBJ_ID); }
+bootstrap      { return(XBOOTSTRAP); }
+_null_         { return(NULLVAL); }
+
+insert         { return(INSERT_TUPLE); }
+
+","            { return(COMMA); }
+"="            { return(EQUALS); }
+"("            { return(LPAREN); }
+")"            { return(RPAREN); }
+
+[\n]           { yyline++; }
+[\t]           ;
+" "            ; 
+
+^\#[^\n]* ; /* drop everything after "#" for comments */
+
+
+"declare"      { return(XDECLARE); }
+"build"                { return(XBUILD); }
+"indices"      { return(INDICES); }
+"index"                { return(INDEX); }
+"on"           { return(ON); }
+"using"                { return(USING); }
+{arrayid}      {
+                   yylval.ival = EnterString(MapArrayTypeName((char*)yytext));
+                   return(ID);
+               }
+{id}           { 
+                   yylval.ival = EnterString(scanstr((char*)yytext));
+                   return(ID);
+               }
+{sid}          {
+                   yylval.ival = EnterString(scanstr((char*)yytext));
+                   return(ID);
+               }
+
+(-)?{D}+"."{D}*({Exp})?        |
+(-)?{D}*"."{D}+({Exp})?        |
+(-)?{D}+{Exp}          {
+                           yylval.ival = EnterString((char*)yytext);
+                           return(CONST);
+                       }
+
+.              {
+                   printf("syntax error %d : -> %s\n", yyline, yytext);
+               }
+
+
+
+%%
+
+yywrap()
+{
+    return 1;
+}
+
+yyerror(str)
+    char *str;
+{
+    fprintf(stderr,"\tsyntax error %d : %s",yyline, str);
+}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
new file mode 100644 (file)
index 0000000..deddc2b
--- /dev/null
@@ -0,0 +1,1049 @@
+/*-------------------------------------------------------------------------
+ *
+ * bootstrap.c--
+ *    routines to support running postgres in 'bootstrap' mode
+ *  bootstrap mode is used to create the initial template database
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include <unistd.h>
+#include "libpq/pqsignal.h"    /* substitute for <signal.h> */
+#if defined(PORTNAME_linux)
+#ifndef __USE_POSIX
+#define __USE_POSIX
+#endif
+#endif /* defined(PORTNAME_linux) */
+#include <setjmp.h>
+
+#define BOOTSTRAP_INCLUDE      /* mask out stuff in tcop/tcopprot.h */
+
+#include "bootstrap/bootstrap.h"
+#include "postgres.h"
+#include "miscadmin.h"
+#include "tcop/tcopprot.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/tupdesc.h"
+#include "utils/builtins.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
+#include "utils/lsyscache.h"
+#include "access/xact.h"
+#include "utils/exc.h" /* for ExcAbort and <setjmp.h> */
+#include "fmgr.h"
+#include "utils/palloc.h"
+#include "utils/mcxt.h"
+#include "storage/smgr.h"
+#include "commands/defrem.h"
+
+#include "catalog/pg_type.h"
+#include "catalog/catname.h"
+#include "catalog/indexing.h"
+#include "catalog/index.h"
+
+#define ALLOC(t, c)    (t *)calloc((unsigned)(c), sizeof(t))
+#define FIRST_TYPE_OID 16      /* OID of the first type */
+
+/* ----------------
+ *     global variables
+ * ----------------
+ */
+/*
+ * In the lexical analyzer, we need to get the reference number quickly from
+ * the string, and the string from the reference number.  Thus we have
+ * as our data structure a hash table, where the hashing key taken from
+ * the particular string.  The hash table is chained.  One of the fields
+ * of the hash table node is an index into the array of character pointers.
+ * The unique index number that every string is assigned is simply the
+ * position of its string pointer in the array of string pointers.
+ */
+
+#define STRTABLESIZE   10000
+#define HASHTABLESIZE  503
+
+/* Hash function numbers */
+#define NUM    23
+#define        NUMSQR  529
+#define        NUMCUBE 12167
+
+char            *strtable [STRTABLESIZE]; 
+hashnode       *hashtable [HASHTABLESIZE];
+
+static int     strtable_end = -1;    /* Tells us last occupied string space */
+
+/*-
+ * Basic information associated with each type.  This is used before
+ * pg_type is created.
+ *
+ *     XXX several of these input/output functions do catalog scans
+ *         (e.g., F_REGPROCIN scans pg_proc).  this obviously creates some 
+ *         order dependencies in the catalog creation process.
+ */
+struct typinfo {
+    char       name[NAMEDATALEN];
+    Oid                oid;
+    Oid                elem;
+    int16      len;
+    Oid                inproc;
+    Oid                outproc;
+};
+
+static struct typinfo Procid[] = {
+    { "bool",       16,    0,  1, F_BOOLIN,     F_BOOLOUT },
+    { "bytea",      17,    0, -1, F_BYTEAIN,    F_BYTEAOUT },
+    { "char",       18,    0,  1, F_CHARIN,     F_CHAROUT },
+    { "name",       19,    0, NAMEDATALEN, F_NAMEIN,   F_NAMEOUT },
+    { "char16",     20,    0,  16, F_CHAR16IN, F_CHAR16OUT}, 
+/*    { "dt",         20,    0,  4, F_DTIN,        F_DTOUT}, */
+    { "int2",       21,    0,  2, F_INT2IN,     F_INT2OUT },
+    { "int28",      22,    0, 16, F_INT28IN,    F_INT28OUT },
+    { "int4",       23,    0,  4, F_INT4IN,     F_INT4OUT },
+    { "regproc",    24,    0,  4, F_REGPROCIN,  F_REGPROCOUT },
+    { "text",       25,    0, -1, F_TEXTIN,     F_TEXTOUT },
+    { "oid",        26,    0,  4, F_INT4IN,     F_INT4OUT },
+    { "tid",        27,    0,  6, F_TIDIN,      F_TIDOUT },
+    { "xid",        28,    0,  5, F_XIDIN,      F_XIDOUT },
+    { "iid",        29,    0,  1, F_CIDIN,      F_CIDOUT },
+    { "oid8",       30,    0, 32, F_OID8IN,     F_OID8OUT },
+    { "smgr",      210,    0,  2, F_SMGRIN,     F_SMGROUT },
+    { "_int4",    1007,   23, -1, F_ARRAY_IN,   F_ARRAY_OUT },
+    { "_aclitem", 1034, 1033, -1, F_ARRAY_IN,   F_ARRAY_OUT }
+};
+
+static int n_types = sizeof(Procid) / sizeof(struct typinfo);
+
+struct typmap {                        /* a hack */
+    Oid        am_oid;
+    TypeTupleFormData  am_typ;
+};
+
+static struct  typmap  **Typ = (struct typmap **)NULL;
+static struct  typmap  *Ap = (struct typmap *)NULL;
+     
+static int             Warnings = 0;
+static char            Blanks[MAXATTR];
+     
+Relation       reldesc;                /* current relation descriptor */
+static char *relname;                   /* current relation name */
+
+AttributeTupleForm attrtypes[MAXATTR];  /* points to attribute info */
+static char    *values[MAXATTR];       /* cooresponding attribute values */
+int            numattr;                /* number of attributes for cur. rel */
+
+#if defined(WIN32) || defined(PORTNAME_next)
+static jmp_buf    Warn_restart;
+#define sigsetjmp(x,y)  setjmp(x)
+#define siglongjmp longjmp
+#else
+static sigjmp_buf Warn_restart;
+#endif
+
+int            DebugMode;
+static GlobalMemory nogc = (GlobalMemory) NULL;        /* special no-gc mem context */
+
+extern int     optind;
+extern char    *optarg;
+     
+/*
+ *  At bootstrap time, we first declare all the indices to be built, and
+ *  then build them.  The IndexList structure stores enough information
+ *  to allow us to build the indices after they've been declared.
+ */
+
+typedef struct _IndexList {
+    char*              il_heap;
+    char*              il_ind;
+    int                        il_natts;
+    AttrNumber         *il_attnos;
+    uint16             il_nparams;
+    Datum *            il_params;
+    FuncIndexInfo      *il_finfo;
+    PredInfo           *il_predInfo;
+    struct _IndexList  *il_next;
+} IndexList;
+
+static IndexList *ILHead = (IndexList *) NULL;
+     
+typedef void (*sig_func)();
+
+
+\f
+/* ----------------------------------------------------------------
+ *                     misc functions
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     error handling / abort routines
+ * ----------------
+ */
+#if !defined(PORTNAME_bsdi)
+void err()
+{
+    Warnings++;
+    cleanup();
+}
+#endif
+
+/* usage:
+   usage help for the bootstrap backen
+*/
+static void
+usage()
+{
+    fprintf(stderr,"Usage: postgres -boot [-d] [-C] [-O] [-Q] [-P portno] [dbName]\n");
+    fprintf(stderr,"     d: debug mode\n");
+    fprintf(stderr,"     C: disable version checking\n");
+    fprintf(stderr,"     O: set BootstrapProcessing mode\n");
+    fprintf(stderr,"     P portno: specify port number\n");
+
+    exitpg(1);
+}
+
+/* ----------------------------------------------------------------
+ *     BootstrapMain
+ *         the main loop for handling the backend in bootstrap mode
+ *   the bootstrap mode is used to initialize the template database
+ *   the bootstrap backend doesn't speak SQL, but instead expects
+ *   commands in a special bootstrap language.
+ *   they are a special bootstrap language.
+ *
+ *  the arguments passed in to BootstrapMain are the run-time arguments
+ * without the argument '-boot', the caller is required to have
+ * removed -boot from the run-time args
+ * ----------------------------------------------------------------
+ */
+int
+BootstrapMain(int argc, char *argv[])
+{
+    int          i;
+    int          portFd = -1;
+    char  *dbName;
+    int   flag;
+    int   override = 1;  /* use BootstrapProcessing or InitProcessing mode */
+    
+    extern int   optind;
+    extern char          *optarg;
+
+    /* ----------------
+     * initialize signal handlers
+     * ----------------
+     */
+    signal(SIGINT, (sig_func) die);
+#ifndef WIN32
+    signal(SIGHUP, (sig_func) die); 
+    signal(SIGTERM, (sig_func) die);
+#endif /* WIN32 */    
+
+    /* --------------------
+     * initialize globals 
+     * -------------------
+     */
+    
+    InitGlobals();
+
+    /* ----------------
+     * process command arguments
+     * ----------------
+     */
+    Quiet = 0;
+    Noversion = 0;
+    dbName = NULL;
+    
+    while ((flag = getopt(argc, argv, "dCOQP")) != EOF) {
+       switch (flag) {
+       case 'd':
+           DebugMode = 1; /* print out debuggin info while parsing */
+           break;
+       case 'C':
+           Noversion = 1; 
+           break;
+       case 'O':
+           override = true;
+           break;
+       case 'Q':
+           Quiet = 1;
+           break;
+       case 'P':/* specify port */
+           portFd = atoi(optarg);
+           break; 
+       default:
+           usage();
+           break;
+       }
+    } /* while */
+
+    if (argc - optind > 1) {
+       usage();
+    } else
+    if (argc - optind == 1) {
+       dbName = argv[optind];
+    } 
+
+    if (dbName == NULL) {
+       dbName = getenv("USER");
+       if (dbName == NULL) {
+           fputs("bootstrap backend: failed, no db name specified\n", stderr);
+           fputs("          and no USER enviroment variable\n", stderr);
+           exitpg(1);
+       }
+    }
+
+    /* ----------------
+     * initialize input fd
+     * ----------------
+     */
+    if (IsUnderPostmaster == true && portFd < 0) {
+       fputs("backend: failed, no -P option with -postmaster opt.\n", stderr);
+       exitpg(1);
+    }
+    
+#ifdef WIN32
+    _nt_init();
+    _nt_attach();
+#endif /* WIN32 */
+
+
+    /* ----------------
+     * backend initialization
+     * ----------------
+     */
+    SetProcessingMode((override) ? BootstrapProcessing : InitProcessing);
+    InitPostgres(dbName);
+    LockDisable(true);
+    
+    for (i = 0 ; i < MAXATTR; i++) {
+       attrtypes[i]=(AttributeTupleForm )NULL;
+       Blanks[i] = ' ';
+    }
+    for(i = 0; i < STRTABLESIZE; ++i)
+       strtable[i] = NULL;                    
+    for(i = 0; i < HASHTABLESIZE; ++i)
+       hashtable[i] = NULL;                   
+    
+    /* ----------------
+     * abort processing resumes here  - What to do in WIN32?
+     * ----------------
+     */
+#ifndef WIN32    
+    signal(SIGHUP, handle_warn);
+
+    if (sigsetjmp(Warn_restart, 1) != 0) {
+#else
+    if (setjmp(Warn_restart) != 0) {
+#endif /* WIN32 */
+       Warnings++;
+       AbortCurrentTransaction();
+    }
+    
+    /* ----------------
+     * process input.
+     * ----------------
+     */
+
+    /* the sed script boot.sed renamed yyparse to Int_yyparse
+       for the bootstrap parser to avoid conflicts with the normal SQL
+       parser */
+    Int_yyparse();
+
+    /* clean up processing */
+    StartTransactionCommand();
+    cleanup();
+    /* not reached, here to make compiler happy */
+    return 0;
+
+}
+
+/* ----------------------------------------------------------------
+ *             MANUAL BACKEND INTERACTIVE INTERFACE COMMANDS
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     boot_openrel
+ * ----------------
+ */
+void
+boot_openrel(char *relname)
+{
+    int                i;
+    struct     typmap  **app;
+    Relation   rdesc;
+    HeapScanDesc       sdesc;
+    HeapTuple  tup;
+    
+    if (strlen(relname) > 15) 
+       relname[15] ='\000';
+    
+    if (Typ == (struct typmap **)NULL) {
+       StartPortalAllocMode(DefaultAllocMode, 0);
+       rdesc = heap_openr(TypeRelationName);
+       sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0, (ScanKey)NULL);
+       for (i=0; PointerIsValid(tup=heap_getnext(sdesc,0,(Buffer *)NULL)); ++i);
+       heap_endscan(sdesc);
+       app = Typ = ALLOC(struct typmap *, i + 1);
+       while (i-- > 0)
+           *app++ = ALLOC(struct typmap, 1);
+       *app = (struct typmap *)NULL;
+       sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0, (ScanKey)NULL);
+       app = Typ;
+       while (PointerIsValid(tup = heap_getnext(sdesc, 0, (Buffer *)NULL))) {
+           (*app)->am_oid = tup->t_oid;
+           memmove((char *)&(*app++)->am_typ, 
+                   (char *)GETSTRUCT(tup), 
+                   sizeof ((*app)->am_typ));
+       }
+       heap_endscan(sdesc);
+       heap_close(rdesc);
+       EndPortalAllocMode();
+    }
+    
+    if (reldesc != NULL) {
+       closerel(NULL);
+    }
+    
+    if (!Quiet)
+       printf("Amopen: relation %s. attrsize %d\n", relname,
+              ATTRIBUTE_TUPLE_SIZE);
+    
+    reldesc = heap_openr(relname);
+    Assert(reldesc);
+    numattr = reldesc->rd_rel->relnatts;
+    for (i = 0; i < numattr; i++) {
+       if (attrtypes[i] == NULL) {
+           attrtypes[i] = AllocateAttribute();
+       }
+       memmove((char *)attrtypes[i],
+               (char *)reldesc->rd_att->attrs[i], 
+               ATTRIBUTE_TUPLE_SIZE);
+       
+       /* Some old pg_attribute tuples might not have attisset. */
+       /* If the attname is attisset, don't look for it - it may
+          not be defined yet.
+          */
+       if (namestrcmp(&attrtypes[i]->attname, "attisset") == 0)
+           attrtypes[i]->attisset = get_attisset(reldesc->rd_id,
+                                                 attrtypes[i]->attname.data);
+       else
+           attrtypes[i]->attisset = false;
+       
+       if (DebugMode) {
+           AttributeTupleForm at = attrtypes[i];
+           printf("create attribute %d name %.*s len %d num %d type %d\n",
+                  i, NAMEDATALEN, at->attname.data, at->attlen, at->attnum, 
+                  at->atttypid
+                  );
+           fflush(stdout);
+       }
+    }
+}
+
+/* ----------------
+ *     closerel
+ * ----------------
+ */
+void
+closerel(char *name)
+{
+    if (name) {
+       if (reldesc) {
+           if (namestrcmp(RelationGetRelationName(reldesc), name) != 0)
+               elog(WARN,"closerel: close of '%s' when '%s' was expected",
+                    name, relname);
+       } else
+           elog(WARN,"closerel: close of '%s' before any relation was opened",
+                name);
+       
+    }
+    
+    if (reldesc == NULL) {
+       elog(WARN,"Warning: no opened relation to close.\n");
+    } else {
+       if (!Quiet) printf("Amclose: relation %s.\n", relname);
+       heap_close(reldesc);
+       reldesc = (Relation)NULL;
+    }
+}
+
+\f
+/* ----------------
+ * DEFINEATTR()
+ *
+ * define a <field,type> pair
+ * if there are n fields in a relation to be created, this routine
+ * will be called n times
+ * ----------------
+ */
+void
+DefineAttr(char *name, char *type, int attnum)
+{
+    int     attlen;
+    int     t;
+    
+    if (reldesc != NULL) {
+       fputs("Warning: no open relations allowed with 't' command.\n",stderr);
+       closerel(relname);
+    }
+    
+    t = gettype(type);
+    if (attrtypes[attnum] == (AttributeTupleForm )NULL) 
+       attrtypes[attnum] = AllocateAttribute();
+    if (Typ != (struct typmap **)NULL) {
+       attrtypes[attnum]->atttypid = Ap->am_oid;
+       namestrcpy(&attrtypes[attnum]->attname, name);
+       if (!Quiet) printf("<%.*s %s> ", NAMEDATALEN, 
+                          attrtypes[attnum]->attname.data, type);
+       attrtypes[attnum]->attnum = 1 + attnum; /* fillatt */
+       attlen = attrtypes[attnum]->attlen = Ap->am_typ.typlen;
+       attrtypes[attnum]->attbyval = Ap->am_typ.typbyval;
+    } else {
+       attrtypes[attnum]->atttypid = Procid[t].oid;
+       namestrcpy(&attrtypes[attnum]->attname,name);
+       if (!Quiet) printf("<%.*s %s> ", NAMEDATALEN,
+                          attrtypes[attnum]->attname.data, type);
+       attrtypes[attnum]->attnum = 1 + attnum; /* fillatt */
+       attlen = attrtypes[attnum]->attlen = Procid[t].len;
+       attrtypes[attnum]->attbyval = (attlen==1) || (attlen==2)||(attlen==4);
+    }
+}
+
+
+/* ----------------
+ *     InsertOneTuple
+ *     assumes that 'oid' will not be zero.
+ * ----------------
+ */
+void
+InsertOneTuple(Oid objectid)
+{
+    HeapTuple tuple;
+    TupleDesc tupDesc;
+
+    int i;
+    
+    if (DebugMode) {
+       printf("InsertOneTuple oid %d, %d attrs\n", objectid, numattr);
+       fflush(stdout);
+    }
+    
+    tupDesc = CreateTupleDesc(numattr,attrtypes); 
+    tuple = heap_formtuple(tupDesc,(Datum*)values,Blanks);     
+    pfree(tupDesc); /* just free's tupDesc, not the attrtypes */
+
+    if(objectid !=(Oid)0) {
+       tuple->t_oid=objectid;
+    }
+    heap_insert(reldesc, tuple);
+    pfree(tuple);
+    if (DebugMode) {
+       printf("End InsertOneTuple, objectid=%d\n", objectid);
+       fflush(stdout);
+    }
+    /*
+     * Reset blanks for next tuple
+     */
+    for (i = 0; i<numattr; i++)
+       Blanks[i] = ' ';
+}
+
+/* ----------------
+ *     InsertOneValue
+ * ----------------
+ */
+void
+InsertOneValue(Oid objectid, char *value, int i)
+{
+    int                typeindex;
+    char       *prt;
+    struct typmap **app;
+    
+    if (DebugMode)
+       printf("Inserting value: '%s'\n", value);
+    if (i < 0 || i >= MAXATTR) {
+       printf("i out of range: %d\n", i);
+       Assert(0);
+    }
+    
+    if (Typ != (struct typmap **)NULL) {
+       struct typmap *ap;
+       if (DebugMode)
+           puts("Typ != NULL");
+       app = Typ;
+       while (*app && (*app)->am_oid != reldesc->rd_att->attrs[i]->atttypid)
+           ++app;
+       ap = *app;
+       if (ap == NULL) {
+           printf("Unable to find atttypid in Typ list! %d\n",
+                  reldesc->rd_att->attrs[i]->atttypid
+                  );
+           Assert(0);
+       }
+       values[i] = fmgr(ap->am_typ.typinput,
+                        value,
+                        ap->am_typ.typelem,
+                        -1); /* shouldn't have char() or varchar() types
+                                during boostrapping but just to be safe */
+       prt = fmgr(ap->am_typ.typoutput, values[i],
+                  ap->am_typ.typelem);
+       if (!Quiet) printf("%s ", prt);
+       pfree(prt);
+    } else {
+       typeindex = attrtypes[i]->atttypid - FIRST_TYPE_OID;
+       if (DebugMode)
+           printf("Typ == NULL, typeindex = %d idx = %d\n", typeindex, i);
+       values[i] = fmgr(Procid[typeindex].inproc, value,
+                        Procid[typeindex].elem, -1);
+       prt = fmgr(Procid[typeindex].outproc, values[i],
+                  Procid[typeindex].elem);
+       if (!Quiet) printf("%s ", prt);
+       pfree(prt);
+    }
+    if (DebugMode) {
+       puts("End InsertValue");
+       fflush(stdout);
+    }
+}
+
+/* ----------------
+ *     InsertOneNull
+ * ----------------
+ */
+void
+InsertOneNull(int i)
+{
+    if (DebugMode)
+       printf("Inserting null\n");
+    if (i < 0 || i >= MAXATTR) {
+       elog(FATAL, "i out of range (too many attrs): %d\n", i);
+    }
+    values[i] = (char *)NULL;
+    Blanks[i] = 'n';
+}
+
+#define MORE_THAN_THE_NUMBER_OF_CATALOGS 256
+
+bool
+BootstrapAlreadySeen(Oid id)
+{
+    static Oid seenArray[MORE_THAN_THE_NUMBER_OF_CATALOGS];
+    static int nseen = 0;
+    bool seenthis;
+    int i;
+    
+    seenthis = false;
+     
+    for (i=0; i < nseen; i++) {
+       if (seenArray[i] == id) {
+           seenthis = true;
+           break;
+       }
+    }
+    if (!seenthis) {
+       seenArray[nseen] = id;
+       nseen++;
+    }
+    return (seenthis);
+}
+
+/* ----------------
+ *     cleanup
+ * ----------------
+ */
+void
+cleanup()
+{
+    static     int     beenhere = 0;
+    
+    if (!beenhere)
+       beenhere = 1;
+    else {
+       elog(FATAL,"Memory manager fault: cleanup called twice.\n", stderr);
+       exitpg(1);
+    }
+    if (reldesc != (Relation)NULL) {
+       heap_close(reldesc);
+    }
+    CommitTransactionCommand();
+    exitpg(Warnings);
+}
+
+/* ----------------
+ *     gettype
+ * ----------------
+ */
+int
+gettype(char *type)
+{
+    int                i;
+    Relation   rdesc;
+    HeapScanDesc       sdesc;
+    HeapTuple  tup;
+    struct     typmap  **app;
+    
+    if (Typ != (struct typmap **)NULL) {
+       for (app = Typ; *app != (struct typmap *)NULL; app++) {
+           if (strncmp((*app)->am_typ.typname.data, type, NAMEDATALEN) == 0) {
+               Ap = *app;
+               return((*app)->am_oid);
+           }
+       }
+    } else {
+       for (i = 0; i <= n_types; i++) {
+           if (strncmp(type, Procid[i].name, NAMEDATALEN) == 0) {
+               return(i);
+           }
+       }
+       if (DebugMode)
+           printf("bootstrap.c: External Type: %.*s\n", NAMEDATALEN, type);
+        rdesc = heap_openr(TypeRelationName);
+        sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0, (ScanKey)NULL);
+       i = 0;
+       while (PointerIsValid(tup = heap_getnext(sdesc, 0, (Buffer *)NULL)))
+           ++i;
+       heap_endscan(sdesc);
+       app = Typ = ALLOC(struct typmap *, i + 1);
+       while (i-- > 0)
+           *app++ = ALLOC(struct typmap, 1);
+       *app = (struct typmap *)NULL;
+       sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0, (ScanKey)NULL);
+       app = Typ;
+       while (PointerIsValid(tup = heap_getnext(sdesc, 0, (Buffer *)NULL))) {
+           (*app)->am_oid = tup->t_oid;
+           memmove((char *)&(*app++)->am_typ,
+                   (char *)GETSTRUCT(tup), 
+                   sizeof ((*app)->am_typ));
+        }
+        heap_endscan(sdesc);
+        heap_close(rdesc);
+        return(gettype(type));
+    }
+    elog(WARN, "Error: unknown type '%s'.\n", type);
+    err();
+    /* not reached, here to make compiler happy */
+    return 0;
+}
+
+/* ----------------
+ *     AllocateAttribute
+ * ----------------
+ */
+AttributeTupleForm  /* XXX */
+AllocateAttribute()
+{
+    AttributeTupleForm attribute =
+       (AttributeTupleForm)malloc(ATTRIBUTE_TUPLE_SIZE);
+    
+    if (!PointerIsValid(attribute)) {
+       elog(FATAL, "AllocateAttribute: malloc failed");
+    }
+    memset(attribute, 0, ATTRIBUTE_TUPLE_SIZE);
+    
+    return (attribute);
+}
+
+/* ----------------
+ *     MapArrayTypeName
+ * XXX arrays of "basetype" are always "_basetype".
+ *     this is an evil hack inherited from rel. 3.1.
+ * XXX array dimension is thrown away because we
+ *     don't support fixed-dimension arrays.  again,
+ *     sickness from 3.1.
+ * 
+ * the string passed in must have a '[' character in it 
+ *
+ * the string returned is a pointer to static storage and should NOT
+ * be freed by the CALLER.
+ * ----------------
+ */
+char* 
+MapArrayTypeName(char *s)
+{
+    int i, j;
+    static char newStr[NAMEDATALEN]; /* array type names < NAMEDATALEN long */
+
+    if (s == NULL || s[0] == '\0')
+       return s;
+
+    j = 1;
+    newStr[0] = '_';
+    for (i=0; i<NAMEDATALEN-1 && s[i] != '['; i++, j++)
+       newStr[j] = s[i];
+    
+    newStr[j] = '\0';
+
+    return newStr;
+}
+
+/* ----------------
+ *     EnterString
+ *     returns the string table position of the identifier
+ *     passed to it.  We add it to the table if we can't find it.
+ * ----------------
+ */
+int
+EnterString (char *str)
+{
+    hashnode   *node;
+    int        len;
+    
+    len= strlen(str);
+
+    node = FindStr(str, len, 0);
+    if (node) {
+       return (node->strnum);
+    } else {
+       node = AddStr(str, len, 0);
+       return (node->strnum);
+    }
+}
+
+/* ----------------
+ *     LexIDStr
+ *     when given an idnum into the 'string-table' return the string
+ *     associated with the idnum
+ * ----------------
+ */
+char *
+LexIDStr(int ident_num) 
+{
+    return(strtable[ident_num]);
+}    
+
+
+/* ----------------
+ *     CompHash
+ *
+ *     Compute a hash function for a given string.  We look at the first,
+ *     the last, and the middle character of a string to try to get spread
+ *     the strings out.  The function is rather arbitrary, except that we
+ *     are mod'ing by a prime number.
+ * ----------------
+ */
+int
+CompHash(char *str, int len)
+{
+    register int result;
+    
+    result =(NUM * str[0] + NUMSQR * str[len-1] + NUMCUBE * str[(len-1)/2]);
+    
+    return (result % HASHTABLESIZE);
+    
+}
+
+/* ----------------
+ *     FindStr
+ *
+ *     This routine looks for the specified string in the hash
+ *     table.  It returns a pointer to the hash node found,
+ *     or NULL if the string is not in the table.
+ * ----------------
+ */
+hashnode *
+FindStr(char *str, int length, hashnode *mderef)
+{
+    hashnode   *node;
+    node = hashtable [CompHash (str, length)];
+    while (node != NULL) {
+       /*
+        * We must differentiate between string constants that
+        * might have the same value as a identifier
+        * and the identifier itself.
+        */
+       if (!strcmp(str, strtable[node->strnum])) {
+           return(node);  /* no need to check */
+       } else {
+           node = node->next;
+       }
+    }
+    /* Couldn't find it in the list */
+    return (NULL);
+}
+
+/* ----------------
+ *     AddStr
+ *
+ *     This function adds the specified string, along with its associated
+ *     data, to the hash table and the string table.  We return the node
+ *     so that the calling routine can find out the unique id that AddStr
+ *     has assigned to this string.
+ * ----------------
+ */
+hashnode *
+AddStr(char *str, int strlength, int mderef)
+{
+    hashnode   *temp, *trail, *newnode;
+    int                hashresult;
+    int                len;
+    
+    if (++strtable_end == STRTABLESIZE) {
+       /* Error, string table overflow, so we Punt */
+       elog(FATAL, 
+            "There are too many string constants and identifiers for the compiler to handle.");
+
+
+    }
+    
+    /*
+     *  Some of the utilites (eg, define type, create relation) assume
+     *  that the string they're passed is a NAMEDATALEN.  We get array bound
+     *  read violations from purify if we don't allocate at least NAMEDATALEN
+     *  bytes for strings of this sort.  Because we're lazy, we allocate
+     *  at least NAMEDATALEN bytes all the time.
+     */
+    
+    if ((len = strlength + 1) < NAMEDATALEN)
+       len = NAMEDATALEN;
+    
+    strtable [strtable_end] = malloc((unsigned) len);
+    strcpy (strtable[strtable_end], str);
+    
+    /* Now put a node in the hash table */
+    
+    newnode = (hashnode*)malloc(sizeof(hashnode)*1);
+    newnode->strnum = strtable_end;
+    newnode->next = NULL;
+    
+    /* Find out where it goes */
+    
+    hashresult = CompHash (str, strlength);
+    if (hashtable [hashresult] == NULL) {
+       hashtable [hashresult] = newnode;
+    } else {                   /* There is something in the list */
+       trail = hashtable [hashresult];
+       temp = trail->next;
+       while (temp != NULL) {
+           trail = temp;
+           temp = temp->next;
+       }
+       trail->next = newnode;
+    }
+    return (newnode);
+}
+
+
+
+/*
+ *  index_register() -- record an index that has been set up for building
+ *                     later.
+ *
+ *     At bootstrap time, we define a bunch of indices on system catalogs.
+ *     We postpone actually building the indices until just before we're
+ *     finished with initialization, however.  This is because more classes
+ *     and indices may be defined, and we want to be sure that all of them
+ *     are present in the index.
+ */
+void
+index_register(char *heap,
+              char *ind,
+              int natts,
+              AttrNumber *attnos,
+              uint16 nparams,
+              Datum *params,
+              FuncIndexInfo *finfo,
+              PredInfo *predInfo)
+{
+    Datum *v;
+    IndexList *newind;
+    int len;
+    MemoryContext oldcxt;
+    
+    /*
+     *  XXX mao 10/31/92 -- don't gc index reldescs, associated info
+     *  at bootstrap time.  we'll declare the indices now, but want to
+     *  create them later.
+     */
+    
+    if (nogc == (GlobalMemory) NULL)
+       nogc = CreateGlobalMemory("BootstrapNoGC");
+    
+    oldcxt = MemoryContextSwitchTo((MemoryContext) nogc);
+    
+    newind = (IndexList *) palloc(sizeof(IndexList));
+    newind->il_heap = pstrdup(heap);
+    newind->il_ind = pstrdup(ind);
+    newind->il_natts = natts;
+    
+    if (PointerIsValid(finfo))
+       len = FIgetnArgs(finfo) * sizeof(AttrNumber);
+    else
+       len = natts * sizeof(AttrNumber);
+    
+    newind->il_attnos = (AttrNumber *) palloc(len);
+    memmove(newind->il_attnos, attnos, len); 
+    
+    if ((newind->il_nparams = nparams) > 0) {
+       v = newind->il_params = (Datum *) palloc(2 * nparams * sizeof(Datum));
+       nparams *= 2;
+       while (nparams-- > 0) {
+           *v = (Datum) palloc(strlen((char *)(*params)) + 1);
+           strcpy((char *) *v++, (char *) *params++);
+       }
+    } else {
+       newind->il_params = (Datum *) NULL;
+    }
+    
+    if (finfo != (FuncIndexInfo *) NULL) {
+       newind->il_finfo = (FuncIndexInfo *) palloc(sizeof(FuncIndexInfo));
+       memmove(newind->il_finfo, finfo, sizeof(FuncIndexInfo)); 
+    } else {
+       newind->il_finfo = (FuncIndexInfo *) NULL;
+    }
+    
+    if (predInfo != NULL) {
+       newind->il_predInfo = (PredInfo*)palloc(sizeof(PredInfo));
+       newind->il_predInfo->pred = predInfo->pred;
+       newind->il_predInfo->oldPred = predInfo->oldPred;
+    } else {
+       newind->il_predInfo = NULL;
+    }
+    
+    newind->il_next = ILHead;
+    
+    ILHead = newind;
+    
+    (void) MemoryContextSwitchTo(oldcxt);
+}
+
+void
+build_indices()
+{
+    Relation heap;
+    Relation ind;
+    
+    for ( ; ILHead != (IndexList *) NULL; ILHead = ILHead->il_next) {
+       heap = heap_openr(ILHead->il_heap);
+       ind = index_openr(ILHead->il_ind);
+       index_build(heap, ind, ILHead->il_natts, ILHead->il_attnos,
+                   ILHead->il_nparams, ILHead->il_params, ILHead->il_finfo,
+                   ILHead->il_predInfo);
+       
+       /*
+        * All of the rest of this routine is needed only because in bootstrap
+        * processing we don't increment xact id's.  The normal DefineIndex
+        * code replaces a pg_class tuple with updated info including the
+        * relhasindex flag (which we need to have updated).  Unfortunately, 
+        * there are always two indices defined on each catalog causing us to 
+        * update the same pg_class tuple twice for each catalog getting an 
+        * index during bootstrap resulting in the ghost tuple problem (see 
+        * heap_replace).  To get around this we change the relhasindex 
+        * field ourselves in this routine keeping track of what catalogs we 
+        * already changed so that we don't modify those tuples twice.  The 
+        * normal mechanism for updating pg_class is disabled during bootstrap.
+        *
+        *              -mer 
+        */
+       heap = heap_openr(ILHead->il_heap);
+       
+       if (!BootstrapAlreadySeen(heap->rd_id))
+           UpdateStats(heap->rd_id, 0, true);
+    }
+}
+
diff --git a/src/backend/bootstrap/bootstrap.h b/src/backend/bootstrap/bootstrap.h
new file mode 100644 (file)
index 0000000..aa0810c
--- /dev/null
@@ -0,0 +1,78 @@
+/*-------------------------------------------------------------------------
+ *
+ * bootstrap.h--
+ *    include file for the bootstrapping code
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef BOOTSTRAP_H
+#define BOOTSTRAP_H
+
+#include <sys/file.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <ctype.h>
+
+#include "access/htup.h"
+#include "access/itup.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "utils/tqual.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"    /* for BufferManagerFlush */
+#include "utils/portal.h"
+#include "utils/elog.h"
+#include "utils/rel.h"
+
+#define        MAXATTR 40              /* max. number of attributes in a relation */
+
+typedef struct hashnode {
+    int                strnum;         /* Index into string table */
+    struct hashnode    *next;
+} hashnode;
+
+#define EMITPROMPT printf("> ")
+
+extern Relation reldesc;
+extern AttributeTupleForm attrtypes[MAXATTR];
+extern int numattr;
+extern int DebugMode;
+
+extern int BootstrapMain(int ac, char *av[]);
+extern void index_register(char *heap,
+                          char *ind,
+                          int natts,
+                          AttrNumber *attnos,
+                          uint16 nparams,
+                          Datum *params,
+                          FuncIndexInfo *finfo,
+                          PredInfo *predInfo);
+
+extern void err(void);
+extern void InsertOneTuple(Oid objectid);
+extern void closerel(char *name);
+extern void boot_openrel(char *name);
+extern char *LexIDStr(int ident_num);
+
+extern void DefineAttr(char *name, char *type, int attnum);
+extern void InsertOneValue(Oid objectid, char *value, int i);
+extern void InsertOneNull(int i);
+extern bool BootstrapAlreadySeen(Oid id);
+extern void cleanup(void);
+extern int gettype(char *type);
+extern AttributeTupleForm AllocateAttribute(void);
+extern char* MapArrayTypeName(char *s);
+extern char* CleanUpStr(char *s);
+extern int EnterString (char *str);
+extern int CompHash (char *str, int len);
+extern hashnode *FindStr (char *str, int length, hashnode *mderef);
+extern hashnode *AddStr(char *str, int strlength, int mderef);
+extern void build_indices(void);
+
+#endif /* BOOTSTRAP_H */
diff --git a/src/backend/catalog/Makefile.inc b/src/backend/catalog/Makefile.inc
new file mode 100644 (file)
index 0000000..7690b11
--- /dev/null
@@ -0,0 +1,69 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the system catalogs module
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+catdir=$(CURDIR)/catalog
+VPATH:=$(VPATH):$(catdir)
+
+
+SRCS_CATALOG= catalog.c heap.c index.c indexing.c \
+       pg_aggregate.c pg_operator.c pg_proc.c pg_type.c
+
+HEADERS+= catalog.h catname.h heap.h index.h indexing.h pg_aggregate.h \
+       pg_am.h pg_amop.h pg_amproc.h pg_attribute.h pg_database.h \
+       pg_defaults.h pg_demon.h pg_group.h pg_index.h pg_inheritproc.h \
+       pg_inherits.h pg_ipl.h pg_language.h pg_listener.h \
+       pg_log.h pg_magic.h pg_opclass.h pg_operator.h pg_parg.h \
+       pg_proc.h pg_class.h \
+       pg_rewrite.h pg_server.h pg_statistic.h pg_time.h pg_type.h \
+       pg_user.h pg_variable.h pg_version.h
+
+#
+# The following is to create the .bki files.
+# TODO: sort headers, (figure some automatic way of of determining
+#  the bki sources?)
+#
+# XXX - more grot.  includes names and uid's in the header file.  FIX THIS
+#  (not sure if i got this right - which do i need - or should i 
+#   burn the whole damned thing)
+#
+ifdef ALLOW_PG_GROUP
+BKIOPTS= -DALLOW_PG_GROUP
+endif
+
+GENBKI= $(catdir)/genbki.sh
+BKIFILES= global1.bki local1_template1.bki 
+
+GLOBALBKI_SRCS= pg_database.h pg_demon.h pg_magic.h pg_defaults.h \
+       pg_variable.h pg_server.h pg_user.h pg_hosts.h \
+       pg_group.h pg_log.h pg_time.h
+
+LOCALBKI_SRCS= pg_proc.h pg_type.h pg_attribute.h pg_class.h \
+       pg_inherits.h pg_index.h pg_version.h pg_statistic.h pg_operator.h \
+       pg_opclass.h pg_am.h pg_amop.h pg_amproc.h pg_language.h pg_parg.h \
+       pg_aggregate.h pg_ipl.h pg_inheritproc.h \
+       pg_rewrite.h pg_listener.h indexing.h
+
+global1.bki: $(GENBKI) $(GLOBALBKI_SRCS)
+       sh $(SHOPTS) $(GENBKI) $(BKIOPTS) \
+               $(patsubst $(GENBKI),,$^) > $(objdir)/$(@F)
+
+
+local1_template1.bki: $(GENBKI) $(LOCALBKI_SRCS)
+       sh $(SHOPTS) $(GENBKI) $(BKIOPTS) \
+               $(patsubst $(GENBKI),,$^) > $(objdir)/$(@F)
+
+
+#${PROG}: ${BKIFILES}
+#
+
+CLEANFILES+= ${BKIFILES}
diff --git a/src/backend/catalog/README b/src/backend/catalog/README
new file mode 100644 (file)
index 0000000..d5ff480
--- /dev/null
@@ -0,0 +1,66 @@
+$Header$
+
+This directory contains .c files that manipulate the system catalogs
+as well as .h files that define the structure of the system catalogs.
+
+When the compile-time scripts (such as Gen_fmgrtab.sh and genbki.sh)
+execute, they grep the DATA statements out of the .h files and munge
+these in order to generate the .bki files.  The .bki files are then
+used as input to initdb (which is just a wrapper around postgres
+running single-user in bootstrapping mode) in order to generate the
+initial (template) system catalog relation files.
+
+-----------------------------------------------------------------
+
+People who are going to hose around with the .h files should be aware
+of the following facts:
+
+- It is very important that the DATA statements be properly formatted
+(e.g., no broken lines, proper use of white-space and _null_).  The
+scripts are line-oriented and break easily.  In addition, the only
+documentation on the proper format for them is the code in the
+bootstrap/ directory.  Just be careful when adding new DATA
+statements.
+
+- Some catalogs require that OIDs be preallocated to tuples because
+certain catalogs contain circular references.  For example, pg_type
+contains pointers into pg_proc (pg_type.typinput), and pg_proc
+contains back-pointers into pg_type (pg_proc.proargtypes).  In these
+cases, the references may be explicitly set by use of the "OID ="
+clause of the .bki insert statement.  If no such pointers are required
+to a given tuple, then the OID may be set to the wildcard value 0
+(i.e., the system generates a random OID in the usual way).
+
+If you need to find a valid OID for a set of tuples that refer to each
+other, use the unused_oids script.  It generates inclusive ranges of
+*unused* OIDs (i.e., the line "45-900" means OIDs 45 through 900 have
+not been allocated yet).  However, you should not rely 100% on this
+script, since it only looks at the .h files in the catalog/ directory.
+Do a pg_grepsrc (recursive grep) of the source tree to insure that
+there aren't any hidden crocks (i.e., explicit use of a numeric OID)
+anywhere in the code.
+
+-----------------------------------------------------------------
+
+When munging the .c files, you should be aware of certain conventions:
+
+- The system catalog cache code (and most catalog-munging code in
+general) assumes that the fixed-length portion of all system catalog
+tuples are in fact present.  That is, only the variable-length
+portions of a catalog tuple are assumed to be permitted to be
+non-NULL.  For example, if you set pg_type.typdelim to be NULL, a
+piece of code will likely perform "typetup->typdelim" (or, worse,
+"typetyp->typelem", which follows typdelim).  This will result in
+random errors or even segmentation violations.  Hence, do NOT insert
+catalog tuples that contain NULL attributes except in their
+variable-length portions!
+
+- Modification of the catalogs must be performed with the proper
+updating of catalog indexes!  That is, several catalogs have indexes
+on them; when you munge them using the executor, the executor will
+take care of doing the index updates, but if you make direct access
+method calls to insert new or modified tuples into a heap, you must
+also make the calls to insert the tuple into ALL of its indexes!  If
+not, the new tuple will generally be "invisible" to the system because
+most of the accesses to the catalogs in question will be through the
+associated indexes.
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
new file mode 100644 (file)
index 0000000..eb1fe55
--- /dev/null
@@ -0,0 +1,205 @@
+/*-------------------------------------------------------------------------
+ *
+ * catalog.c--
+ *    
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>    /* XXX */
+#include "postgres.h"
+#include "miscadmin.h"  /* for DataDir */
+#include "access/htup.h"
+#include "storage/buf.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+#include "utils/syscache.h"
+#include "catalog/catname.h"   /* NameIs{,Shared}SystemRelationName */
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_type.h"
+#include "catalog/catalog.h"
+#include "storage/bufmgr.h"
+#include "access/transam.h"
+
+
+#ifndef        MAXPATHLEN
+#define        MAXPATHLEN      80
+#endif
+
+/*
+ * relpath             - path to the relation
+ *     Perhaps this should be in-line code in relopen().
+ */
+char *
+relpath(char relname[])
+{
+    char    *path;
+    
+    if (IsSharedSystemRelationName(relname)) {
+       path = (char *) palloc(strlen(DataDir) + sizeof(NameData) + 2);
+       sprintf(path, "%s/%.*s", DataDir, NAMEDATALEN, relname);
+       return (path);
+    }
+    return(relname);
+}
+
+/*
+ * issystem    - returns non-zero iff relname is a system catalog
+ *
+ *     We now make a new requirement where system catalog relns must begin
+ *     with pg_ while user relns are forbidden to do so.  Make the test
+ *     trivial and instantaneous.
+ *
+ *     XXX this is way bogus. -- pma
+ */
+bool
+issystem(char relname[])
+{
+    if (relname[0] && relname[1] && relname[2])
+       return (relname[0] == 'p' && 
+               relname[1] == 'g' && 
+               relname[2] == '_');
+    else
+       return FALSE;
+}
+
+/*
+ * IsSystemRelationName --
+ *     True iff name is the name of a system catalog relation.
+ *
+ *     We now make a new requirement where system catalog relns must begin
+ *     with pg_ while user relns are forbidden to do so.  Make the test
+ *     trivial and instantaneous.
+ *
+ *     XXX this is way bogus. -- pma
+ */
+bool
+IsSystemRelationName(char *relname)
+{
+    if (relname[0] && relname[1] && relname[2])
+       return (relname[0] == 'p' && 
+               relname[1] == 'g' && 
+               relname[2] == '_');
+    else
+       return FALSE;
+}
+
+/*
+ * IsSharedSystemRelationName --
+ *     True iff name is the name of a shared system catalog relation.
+ */
+bool
+IsSharedSystemRelationName(char *relname)
+{
+    int i;
+    
+    /*
+     * Quick out: if it's not a system relation, it can't be a shared
+     * system relation.
+     */
+    if (!IsSystemRelationName(relname))
+       return FALSE;
+    
+    i = 0;
+    while ( SharedSystemRelationNames[i] != NULL) {
+         if (strcmp(SharedSystemRelationNames[i],relname) == 0)
+            return TRUE;
+        i++;
+    }
+    return FALSE;
+}
+
+/*
+ *     newoid          - returns a unique identifier across all catalogs.
+ *
+ *     Object Id allocation is now done by GetNewObjectID in
+ *     access/transam/varsup.c.  oids are now allocated correctly.
+ *
+ * old comments:
+ *     This needs to change soon, it fails if there are too many more
+ *     than one call per second when postgres restarts after it dies.
+ *
+ *     The distribution of OID's should be done by the POSTMASTER.
+ *     Also there needs to be a facility to preallocate OID's.  Ie.,
+ *     for a block of OID's to be declared as invalid ones to allow
+ *     user programs to use them for temporary object identifiers.
+ */
+Oid newoid()
+{
+    Oid         lastoid;
+    
+    GetNewObjectId(&lastoid);
+    if (! OidIsValid(lastoid))
+       elog(WARN, "newoid: GetNewObjectId returns invalid oid");
+    return lastoid;
+}
+
+/*
+ *     fillatt         - fills the ATTRIBUTE relation fields from the TYP
+ *
+ *     Expects that the atttypid domain is set for each att[].
+ *     Returns with the attnum, and attlen domains set.
+ *     attnum, attproc, atttyparg, ... should be set by the user.
+ *
+ *     In the future, attnum may not be set?!? or may be passed as an arg?!?
+ *
+ *     Current implementation is very inefficient--should cashe the
+ *     information if this is at all possible.
+ *
+ *     Check to see if this is really needed, and especially in the case
+ *     of index tuples.
+ */
+void
+fillatt(TupleDesc tupleDesc)
+{
+    AttributeTupleForm *attributeP;
+    register TypeTupleForm     typp;
+    HeapTuple          tuple;
+    int                        i;
+    int natts = tupleDesc->natts;
+    AttributeTupleForm *att = tupleDesc->attrs;
+
+    if (natts < 0 || natts > MaxHeapAttributeNumber)
+       elog(WARN, "fillatt: %d attributes is too large", natts);
+    if (natts == 0) {
+       elog(DEBUG, "fillatt: called with natts == 0");
+       return;
+    }
+    
+    attributeP = &att[0];
+    
+    for (i = 0; i < natts;) {
+       tuple = SearchSysCacheTuple(TYPOID,
+                                   Int32GetDatum((*attributeP)->atttypid),
+                                   0,0,0);
+       if (!HeapTupleIsValid(tuple)) {
+           elog(WARN, "fillatt: unknown atttypid %ld",
+                (*attributeP)->atttypid);
+       } else {
+           (*attributeP)->attnum = (int16) ++i;
+           /* Check if the attr is a set before messing with the length
+              and byval, since those were already set in 
+              TupleDescInitEntry.  In fact, this seems redundant 
+              here, but who knows what I'll break if I take it out...
+
+              same for char() and varchar() stuff. I share the same
+              sentiments. This function is poorly written anyway. -ay 6/95
+              */
+           if (!(*attributeP)->attisset &&
+               (*attributeP)->atttypid!=BPCHAROID &&
+               (*attributeP)->atttypid!=VARCHAROID) {
+               
+               typp = (TypeTupleForm) GETSTRUCT(tuple);  /* XXX */
+               (*attributeP)->attlen = typp->typlen;
+               (*attributeP)->attbyval = typp->typbyval;
+           }
+       }
+       attributeP += 1;
+    }
+}
diff --git a/src/backend/catalog/catalog.h b/src/backend/catalog/catalog.h
new file mode 100644 (file)
index 0000000..c686cc4
--- /dev/null
@@ -0,0 +1,24 @@
+/*-------------------------------------------------------------------------
+ *
+ * catalog.h--
+ *    prototypes for functions in lib/catalog/catalog.c
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CATALOG_H
+#define CATALOG_H
+
+#include "access/tupdesc.h"
+
+extern char *relpath(char relname[]);
+extern bool IsSystemRelationName(char *relname);
+extern bool IsSharedSystemRelationName(char *relname);
+extern Oid newoid(void);
+extern void fillatt(TupleDesc att);
+
+#endif /* CATALOG_H */
diff --git a/src/backend/catalog/catname.h b/src/backend/catalog/catname.h
new file mode 100644 (file)
index 0000000..051c004
--- /dev/null
@@ -0,0 +1,52 @@
+/*-------------------------------------------------------------------------
+ *
+ * catname.h--
+ *    POSTGRES system catalog relation name definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        CATNAME_H
+#define CATNAME_H
+
+#include "postgres.h"
+
+
+#define  AggregateRelationName "pg_aggregate"
+#define  AccessMethodRelationName "pg_am"
+#define  AccessMethodOperatorRelationName "pg_amop"
+#define  AccessMethodProcedureRelationName "pg_amproc"
+#define  AttributeRelationName "pg_attribute"
+#define  DatabaseRelationName "pg_database"
+#define  DefaultsRelationName "pg_defaults"
+#define  DemonRelationName "pg_demon"
+#define  GroupRelationName "pg_group"
+#define  HostsRelationName "pg_hosts"
+#define  IndexRelationName "pg_index"
+#define  InheritProcedureRelationName "pg_inheritproc"
+#define  InheritsRelationName "pg_inherits"
+#define  InheritancePrecidenceListRelationName "pg_ipl"
+#define  LanguageRelationName "pg_language"
+#define  ListenerRelationName "pg_listener"
+#define  LogRelationName "pg_log"
+#define  MagicRelationName "pg_magic"
+#define  OperatorClassRelationName "pg_opclass"
+#define  OperatorRelationName "pg_operator"
+#define  ProcedureRelationName "pg_proc"
+#define  RelationRelationName "pg_class"
+#define  RewriteRelationName "pg_rewrite"
+#define  ServerRelationName "pg_server"
+#define  StatisticRelationName "pg_statistic"
+#define  TimeRelationName "pg_time"
+#define  TypeRelationName "pg_type"
+#define  UserRelationName "pg_user"
+#define  VariableRelationName "pg_variable"
+#define  VersionRelationName "pg_version"
+
+extern char *SharedSystemRelationNames[];
+
+#endif /* CATNAME_H */
diff --git a/src/backend/catalog/genbki.sh b/src/backend/catalog/genbki.sh
new file mode 100644 (file)
index 0000000..5453136
--- /dev/null
@@ -0,0 +1,218 @@
+#!/bin/sh
+#-------------------------------------------------------------------------
+#
+# genbki.sh--
+#    shell script which generates .bki files from specially formatted .h
+#    files.  These .bki files are used to initialize the postgres template
+#    database.
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+# NOTES
+#    non-essential whitespace is removed from the generated file.
+#    if this is ever a problem, then the sed script at the very
+#    end can be changed into another awk script or something smarter..
+#
+#-------------------------------------------------------------------------
+
+PATH=$PATH:/lib:/usr/ccs/lib           # to find cpp
+BKIOPTS=''
+if [ $? != 0 ]
+then
+    echo `basename $0`: Bad option
+    exit 1
+fi
+
+for opt in $*
+do
+    case $opt in
+    -D) BKIOPTS="$BKIOPTS -D$2"; shift; shift;;
+    -D*) BKIOPTS="$BKIOPTS $1";shift;;
+    --) shift; break;;
+    esac
+done
+
+# ----------------
+#      collect nodefiles
+# ----------------
+SYSFILES=''
+x=1
+numargs=$#
+while test $x -le $numargs ; do
+    SYSFILES="$SYSFILES $1"
+    x=`expr $x + 1`
+    shift
+done
+
+# ----------------
+#      strip comments and trash from .h before we generate
+#      the .bki file...
+# ----------------
+#      also, change Oid to oid. -- AY 8/94.
+#      also, change NameData to name. -- jolly 8/21/95.
+#
+cat $SYSFILES | \
+sed -e 's/\/\*.*\*\///g' \
+    -e 's/;[   ]*$//g'  \
+    -e 's/\ Oid/\ oid/g' \
+    -e 's/\ NameData/\ name/g' \
+    -e 's/(NameData/(name/g' \
+    -e 's/(Oid/(oid/g' | \
+awk '
+# ----------------
+#      now use awk to process remaining .h file..
+#
+#      nc is the number of catalogs
+#      inside is a variable set to 1 when we are scanning the
+#         contents of a catalog definition.
+#      inserting_data is a flag indicating when we are processing DATA lines.
+#              (i.e. have a relation open and need to close it)
+# ----------------
+BEGIN {
+       inside = 0;
+       raw = 0;
+       bootstrap = 0;
+       nc = 0;
+       reln_open = 0;
+}
+
+# ----------------
+#      anything in a BKI_BEGIN .. BKI_END block should be passed
+#      along without interpretation.
+# ----------------
+/^BKI_BEGIN/   { raw = 1; next; }
+/^BKI_END/     { raw = 0; next; }
+raw == 1       { print; next; }
+
+# ----------------
+#      DATA() statements should get passed right through after
+#      stripping off the DATA( and the ) on the end.
+# ----------------
+/^DATA\(/ {
+       data = substr($0, 6, length($0) - 6);
+       print data;
+       next;
+}
+
+/^DECLARE_INDEX\(/ {
+# ----
+#  end any prior catalog data insertions before starting a define index
+# ----
+       if (reln_open == 1) {
+#              print "show";
+               print "close " catalog;
+               reln_open = 0;
+       }
+
+       data = substr($0, 15, length($0) - 15);
+       print "declare index " data
+}
+
+/^BUILD_INDICES/       { print "build indices"; }
+       
+# ----------------
+#      CATALOG() definitions take some more work.
+# ----------------
+/^CATALOG\(/ { 
+# ----
+#  end any prior catalog data insertions before starting a new one..
+# ----
+       if (reln_open == 1) {
+#              print "show";
+               print "close " catalog;
+               reln_open = 0;
+       }
+
+# ----
+#  get the name of the new catalog
+# ----
+       pos = index($1,")");
+       catalog = substr($1,9,pos-9); 
+
+       if ($0 ~ /BOOTSTRAP/) {
+               bootstrap = 1;
+       }
+
+        i = 1;
+       inside = 1;
+        nc++;
+       next;
+}
+
+# ----------------
+#      process the contents of the catalog definition
+#
+#      attname[ x ] contains the attribute name for attribute x
+#      atttype[ x ] contains the attribute type fot attribute x
+# ----------------
+inside == 1 {
+# ----
+#  ignore a leading brace line..
+# ----
+        if ($1 ~ /\{/)
+               next;
+
+# ----
+#  if this is the last line, then output the bki catalog stuff.
+# ----
+       if ($1 ~ /}/) {
+               if (bootstrap) {
+                       print "create bootstrap " catalog;
+               } else {
+                       print "create " catalog;
+               }
+               print "\t(";
+
+               for (j=1; j<i-1; j++) {
+                       print "\t " attname[ j ] " = " atttype[ j ] " ,";
+               }
+               print "\t " attname[ j ] " = " atttype[ j ] ;
+               print "\t)";
+
+               if (! bootstrap) {
+                       print "open " catalog;
+               }
+
+               i = 1;
+               reln_open = 1;
+               inside = 0;
+               bootstrap = 0;
+               next;
+       }
+
+# ----
+#  if we are inside the catalog definition, then keep sucking up
+#  attibute names and types
+# ----
+       if ($2 ~ /\[.*\]/) {                    # array attribute
+               idlen = index($2,"[") - 1;
+               atttype[ i ] = $1 "[]";         # variable-length only..
+               attname[ i ] = substr($2,1,idlen);
+       } else {
+               atttype[ i ] = $1;
+               attname[ i ] = $2;
+       }
+       i++;
+       next;
+}
+
+END {
+       if (reln_open == 1) {
+#              print "show";
+               print "close " catalog;
+               reln_open = 0;
+       }
+}
+' | \
+cpp $BKIOPTS | \
+sed -e '/^[    ]*$/d' \
+    -e 's/[    ][      ]*/ /g'
+
+# ----------------
+#      all done
+# ----------------
+exit 0
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
new file mode 100644 (file)
index 0000000..269adcc
--- /dev/null
@@ -0,0 +1,1428 @@
+/*-------------------------------------------------------------------------
+ *
+ * heap.c--
+ *    code to create and destroy POSTGRES heap relations
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * INTERFACE ROUTINES
+ *     heap_creatr()           - Create an uncataloged heap relation
+ *     heap_create()           - Create a cataloged relation
+ *     heap_destroy()          - Removes named relation from catalogs
+ *
+ * NOTES
+ *    this code taken from access/heap/create.c, which contains
+ *    the old heap_creater, amcreate, and amdestroy.  those routines
+ *    will soon call these routines using the function manager,
+ *    just like the poorly named "NewXXX" routines do.  The
+ *    "New" routines are all going to die soon, once and for all!
+ *     -cim 1/13/91
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>     /* for sprintf() */
+#include <sys/file.h>
+#include <string.h>
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/htup.h"
+#include "access/istrat.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "utils/tqual.h"       /* for NowTimeQual */
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/itemptr.h"
+#include "lib/hasht.h"
+#include "miscadmin.h"
+#include "fmgr.h"
+#include "utils/builtins.h"
+#include "utils/elog.h"                        /* XXX */
+#include "utils/mcxt.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+#include "utils/relcache.h"
+
+#include "catalog/catname.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_index.h"
+#include "catalog/pg_inherits.h"
+#include "catalog/pg_ipl.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "catalog/index.h"
+#include "catalog/indexing.h"
+
+#include "catalog/catalog.h"
+#include "parser/catalog_utils.h"
+
+#include "storage/lmgr.h"
+
+#include "rewrite/rewriteRemove.h"
+
+static void AddNewAttributeTuples(Oid new_rel_oid, TupleDesc tupdesc);
+static void CheckAttributeNames(TupleDesc tupdesc);
+
+/* ----------------------------------------------------------------
+ *             XXX UGLY HARD CODED BADNESS FOLLOWS XXX
+ *
+ *     these should all be moved to someplace in the lib/catalog
+ *     module, if not obliterated first.
+ * ----------------------------------------------------------------
+ */
+
+
+/*
+ * Note:
+ *     Should the executor special case these attributes in the future?
+ *     Advantage:  consume 1/2 the space in the ATTRIBUTE relation.
+ *     Disadvantage:  having rules to compute values in these tuples may
+ *             be more difficult if not impossible.
+ */
+
+static FormData_pg_attribute a1 = {
+    0xffffffff, {"ctid"}, 27l, 0l, 0l, 0l, sizeof (ItemPointerData),
+    SelfItemPointerAttributeNumber, 0, '\0', '\001', 0l, 'i'
+};
+
+static FormData_pg_attribute a2 = {
+    0xffffffff, {"oid"}, 26l, 0l, 0l, 0l, sizeof(Oid),
+    ObjectIdAttributeNumber, 0, '\001', '\001', 0l, 'i'
+};
+
+static FormData_pg_attribute a3 = {
+    0xffffffff, {"xmin"}, 28l, 0l, 0l, 0l, sizeof (TransactionId),
+    MinTransactionIdAttributeNumber, 0, '\0', '\001', 0l, 'i',
+};
+
+static FormData_pg_attribute a4 = {
+    0xffffffff, {"cmin"}, 29l, 0l, 0l, 0l, sizeof (CommandId),
+    MinCommandIdAttributeNumber, 0, '\001', '\001', 0l, 's'
+};
+
+static FormData_pg_attribute a5 = {
+    0xffffffff, {"xmax"}, 28l, 0l, 0l, 0l, sizeof (TransactionId),
+    MaxTransactionIdAttributeNumber, 0, '\0', '\001', 0l, 'i'
+};
+
+static FormData_pg_attribute a6 = {
+    0xffffffff, {"cmax"}, 29l, 0l, 0l, 0l, sizeof (CommandId),
+    MaxCommandIdAttributeNumber, 0, '\001', '\001', 0l, 's'
+};
+
+static FormData_pg_attribute a7 = {
+    0xffffffff, {"chain"}, 27l, 0l, 0l, 0l, sizeof (ItemPointerData),
+    ChainItemPointerAttributeNumber, 0, '\0', '\001', 0l, 'i',
+};
+
+static FormData_pg_attribute a8 = {
+    0xffffffff, {"anchor"}, 27l, 0l, 0l, 0l, sizeof (ItemPointerData),
+    AnchorItemPointerAttributeNumber, 0, '\0', '\001', 0l, 'i'
+};
+
+static FormData_pg_attribute a9 = {
+    0xffffffff, {"tmin"}, 20l, 0l, 0l, 0l, sizeof (AbsoluteTime),
+    MinAbsoluteTimeAttributeNumber, 0, '\001', '\001', 0l, 'i'
+};
+
+static FormData_pg_attribute a10 = {
+    0xffffffff, {"tmax"}, 20l, 0l, 0l, 0l, sizeof (AbsoluteTime),
+    MaxAbsoluteTimeAttributeNumber, 0, '\001', '\001', 0l, 'i'
+};
+
+static FormData_pg_attribute a11 = {
+    0xffffffff, {"vtype"}, 18l, 0l, 0l, 0l, sizeof (char),
+    VersionTypeAttributeNumber, 0, '\001', '\001', 0l, 'c'
+};
+
+static AttributeTupleForm HeapAtt[] =
+{ &a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11 };
+
+/* ----------------------------------------------------------------
+ *             XXX END OF UGLY HARD CODED BADNESS XXX
+ * ----------------------------------------------------------------
+ */
+
+/* the tempRelList holds
+   the list of temporary uncatalogued relations that are created.
+   these relations should be destroyed at the end of transactions
+*/
+typedef struct tempRelList {
+    Relation *rels; /* array of relation descriptors */
+    int  num; /* number of temporary relations */
+    int size; /* size of space allocated for the rels array */
+} TempRelList;
+
+#define TEMP_REL_LIST_SIZE  32
+
+static TempRelList *tempRels = NULL;   
+
+
+/* ----------------------------------------------------------------
+ *     heap_creatr     - Create an uncataloged heap relation
+ *
+ *     Fields relpages, reltuples, reltuples, relkeys, relhistory,
+ *     relisindexed, and relkind of rdesc->rd_rel are initialized
+ *     to all zeros, as are rd_last and rd_hook.  Rd_refcnt is set to 1.
+ *
+ *     Remove the system relation specific code to elsewhere eventually.
+ *
+ *     Eventually, must place information about this temporary relation
+ *     into the transaction context block.
+ *
+ *  
+ * if heap_creatr is called with "" as the name, then heap_creatr will create a
+ * temporary name   "temp_$RELOID" for the relation
+ * ----------------------------------------------------------------
+ */
+Relation
+heap_creatr(char *name, 
+           unsigned smgr,
+           TupleDesc tupDesc) 
+{
+    register unsigned  i;
+    Oid                relid;
+    Relation           rdesc;
+    int                        len;
+    bool               nailme = false;
+    char*               relname = name;
+    char                tempname[40];
+    int isTemp = 0;
+    int natts = tupDesc->natts;
+/*    AttributeTupleForm *att = tupDesc->attrs; */
+    
+    extern GlobalMemory        CacheCxt;
+    MemoryContext      oldcxt;
+
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    AssertArg(natts > 0);
+
+    if (IsSystemRelationName(relname) && IsNormalProcessingMode())
+       {
+           elog(WARN, 
+                "Illegal class name: %s -- pg_ is reserved for system catalogs",
+                relname);
+       }
+    
+    /* ----------------
+     * switch to the cache context so that we don't lose
+     *  allocations at the end of this transaction, I guess.
+     *  -cim 6/14/90
+     * ----------------
+     */
+    if (!CacheCxt)
+       CacheCxt = CreateGlobalMemory("Cache");
+    
+    oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+    
+    /* ----------------
+     * real ugly stuff to assign the proper relid in the relation
+     *  descriptor follows.
+     * ----------------
+     */
+    if (! strcmp(RelationRelationName,relname))
+       {
+           relid = RelOid_pg_class;
+           nailme = true;
+       }
+    else if (! strcmp(AttributeRelationName,relname))
+       {
+           relid = RelOid_pg_attribute;
+           nailme = true;
+       }
+    else if (! strcmp(ProcedureRelationName, relname))
+       {
+           relid = RelOid_pg_proc;
+           nailme = true;
+       }
+    else if (! strcmp(TypeRelationName,relname))
+       {
+           relid = RelOid_pg_type;
+           nailme = true;
+       }
+    else
+      {
+       relid = newoid();
+    
+       if (name[0] == '\0')
+         {
+           sprintf(tempname, "temp_%d", relid);
+           relname = tempname;
+           isTemp = 1;
+         };
+      }
+
+    /* ----------------
+     * allocate a new relation descriptor.
+     *
+     *         XXX the length computation may be incorrect, handle elsewhere
+     * ----------------
+     */
+    len = sizeof(RelationData);
+  
+    rdesc = (Relation) palloc(len);
+    memset((char *)rdesc, 0,len);
+    
+    /* ----------
+       create a new tuple descriptor from the one passed in
+    */
+    rdesc->rd_att = CreateTupleDescCopy(tupDesc);
+    
+    /* ----------------
+     * initialize the fields of our new relation descriptor
+     * ----------------
+     */
+    
+    /* ----------------
+     *  nail the reldesc if this is a bootstrap create reln and
+     *  we may need it in the cache later on in the bootstrap
+     *  process so we don't ever want it kicked out.  e.g. pg_attribute!!!
+     * ----------------
+     */
+    if (nailme)
+       rdesc->rd_isnailed = true;
+    
+    RelationSetReferenceCount(rdesc, 1);
+    
+    rdesc->rd_rel = (Form_pg_class)palloc(sizeof *rdesc->rd_rel);
+    
+    memset((char *)rdesc->rd_rel, 0,
+          sizeof *rdesc->rd_rel);
+    namestrcpy(&(rdesc->rd_rel->relname), relname); 
+    rdesc->rd_rel->relkind = RELKIND_UNCATALOGED;
+    rdesc->rd_rel->relnatts = natts;
+    rdesc->rd_rel->relsmgr = smgr;
+    
+    for (i = 0; i < natts; i++) {
+       rdesc->rd_att->attrs[i]->attrelid = relid;
+    }
+    
+    rdesc->rd_id = relid;
+    
+     if (nailme) {
+       /* for system relations, set the reltype field here */
+       rdesc->rd_rel->reltype = relid;
+     }
+
+    /* ----------------
+     * have the storage manager create the relation.
+     * ----------------
+     */
+    
+    rdesc->rd_fd = (File)smgrcreate(smgr, rdesc);
+    
+    RelationRegisterRelation(rdesc);
+    
+    MemoryContextSwitchTo(oldcxt);
+    
+    /* add all temporary relations to the tempRels list
+       so they can be properly disposed of at the end of transaction
+    */
+    if (isTemp)
+       AddToTempRelList(rdesc);
+
+    return (rdesc);
+}
+
+
+/* ----------------------------------------------------------------
+ *     heap_create     - Create a cataloged relation
+ *
+ *     this is done in 6 steps:
+ *
+ *     1) CheckAttributeNames() is used to make certain the tuple
+ *        descriptor contains a valid set of attribute names
+ *
+ *     2) pg_class is opened and RelationAlreadyExists()
+ *        preforms a scan to ensure that no relation with the
+ *         same name already exists.
+ *
+ *     3) heap_creater() is called to create the new relation on
+ *        disk.
+ *
+ *     4) TypeDefine() is called to define a new type corresponding
+ *        to the new relation.
+ *
+ *     5) AddNewAttributeTuples() is called to register the
+ *        new relation's schema in pg_attribute.
+ *
+ *     6) AddPgRelationTuple() is called to register the
+ *        relation itself in the catalogs.
+ *
+ *     7) the relations are closed and the new relation's oid
+ *        is returned.
+ *
+ * old comments:
+ *     A new relation is inserted into the RELATION relation
+ *     with the specified attribute(s) (newly inserted into
+ *     the ATTRIBUTE relation).  How does concurrency control
+ *     work?  Is it automatic now?  Expects the caller to have
+ *     attname, atttypid, atttyparg, attproc, and attlen domains filled.
+ *     Create fills the attnum domains sequentually from zero,
+ *     fills the attnvals domains with zeros, and fills the
+ *     attrelid fields with the relid.
+ *
+ *     scan relation catalog for name conflict
+ *     scan type catalog for typids (if not arg)
+ *     create and insert attribute(s) into attribute catalog
+ *     create new relation
+ *     insert new relation into attribute catalog
+ *
+ *     Should coordinate with heap_creater().  Either it should
+ *     not be called or there should be a way to prevent
+ *     the relation from being removed at the end of the
+ *     transaction if it is successful ('u'/'r' may be enough).
+ *     Also, if the transaction does not commit, then the
+ *     relation should be removed.
+ *
+ *     XXX amcreate ignores "off" when inserting (for now).
+ *     XXX amcreate (like the other utilities) needs to understand indexes.
+ *     
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     CheckAttributeNames
+ *
+ *     this is used to make certain the tuple descriptor contains a
+ *     valid set of attribute names.  a problem simply generates
+ *     elog(WARN) which aborts the current transaction.
+ * --------------------------------
+ */
+static void
+CheckAttributeNames(TupleDesc tupdesc)
+{
+    unsigned   i;
+    unsigned   j;
+    int natts = tupdesc->natts;
+
+    /* ----------------
+     * first check for collision with system attribute names
+     * ----------------
+     *
+     *   also, warn user if attribute to be created has
+     *   an unknown typid  (usually as a result of a 'retrieve into'
+     *    - jolly
+     */
+    for (i = 0; i < natts; i += 1) {
+       for (j = 0; j < sizeof HeapAtt / sizeof HeapAtt[0]; j += 1) {
+           if (nameeq(&(HeapAtt[j]->attname),
+                           &(tupdesc->attrs[i]->attname))) {
+               elog(WARN,
+                    "create: system attribute named \"%s\"",
+                    HeapAtt[j]->attname.data);
+           }
+       }
+       if (tupdesc->attrs[i]->atttypid == UNKNOWNOID)
+           {
+               elog(NOTICE,
+                    "create: attribute named \"%s\" has an unknown type",
+                     tupdesc->attrs[i]->attname.data);
+           }
+    }
+    
+    /* ----------------
+     * next check for repeated attribute names
+     * ----------------
+     */
+    for (i = 1; i < natts; i += 1) {
+       for (j = 0; j < i; j += 1) {
+           if (nameeq(&(tupdesc->attrs[j]->attname),
+                           &(tupdesc->attrs[i]->attname))) {
+               elog(WARN,
+                    "create: repeated attribute \"%s\"",
+                    tupdesc->attrs[j]->attname.data);
+           }
+       }
+    }
+}
+
+/* --------------------------------
+ *     RelationAlreadyExists
+ *
+ *     this preforms a scan of pg_class to ensure that
+ *     no relation with the same name already exists.  The caller
+ *     has to open pg_class and pass an open descriptor.
+ * --------------------------------
+ */
+int
+RelationAlreadyExists(Relation pg_class_desc, char relname[])
+{
+    ScanKeyData                key;
+    HeapScanDesc       pg_class_scan;
+    HeapTuple          tup;
+    
+    /*
+     *  If this is not bootstrap (initdb) time, use the catalog index
+     *  on pg_class.
+     */
+    
+    if (!IsBootstrapProcessingMode()) {
+       tup = ClassNameIndexScan(pg_class_desc, relname);
+       if (HeapTupleIsValid(tup)) {
+           pfree(tup);
+           return ((int) true);
+       } else
+           return ((int) false);
+    }
+    
+    /* ----------------
+     *  At bootstrap time, we have to do this the hard way.  Form the
+     * scan key.
+     * ----------------
+     */
+    ScanKeyEntryInitialize(&key,
+                          0,
+                          (AttrNumber)Anum_pg_class_relname,
+                          (RegProcedure)NameEqualRegProcedure,
+                          (Datum) relname);
+    
+    /* ----------------
+     * begin the scan
+     * ----------------
+     */
+    pg_class_scan = heap_beginscan(pg_class_desc,
+                                     0,
+                                     NowTimeQual,
+                                     1,
+                                     &key);
+    
+    /* ----------------
+     * get a tuple.  if the tuple is NULL then it means we
+     *  didn't find an existing relation.
+     * ----------------
+     */
+    tup = heap_getnext(pg_class_scan, 0, (Buffer *)NULL);
+    
+    /* ----------------
+     * end the scan and return existance of relation.
+     * ----------------
+     */
+    heap_endscan(pg_class_scan);
+    
+    return
+       (PointerIsValid(tup) == true);
+}
+
+/* --------------------------------
+ *     AddNewAttributeTuples
+ *
+ *     this registers the new relation's schema by adding
+ *     tuples to pg_attribute.
+ * --------------------------------
+ */
+static void
+AddNewAttributeTuples(Oid new_rel_oid,
+                     TupleDesc tupdesc)
+{
+    AttributeTupleForm *dpp;           
+    unsigned   i;
+    HeapTuple  tup;
+    Relation   rdesc;
+    bool       hasindex;
+    Relation   idescs[Num_pg_attr_indices];
+    int natts = tupdesc->natts;
+    
+    /* ----------------
+     * open pg_attribute
+     * ----------------
+     */
+    rdesc = heap_openr(AttributeRelationName);
+    
+    /* -----------------
+     * Check if we have any indices defined on pg_attribute.
+     * -----------------
+     */
+    Assert(rdesc);
+    Assert(rdesc->rd_rel);
+    hasindex = RelationGetRelationTupleForm(rdesc)->relhasindex;
+    if (hasindex)
+       CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
+    
+    /* ----------------
+     * initialize tuple descriptor.  Note we use setheapoverride()
+     *  so that we can see the effects of our TypeDefine() done
+     *  previously.
+     * ----------------
+     */
+    setheapoverride(true);
+    fillatt(tupdesc);
+    setheapoverride(false);
+    
+    /* ----------------
+     *  first we add the user attributes..
+     * ----------------
+     */
+    dpp = tupdesc->attrs;
+    for (i = 0; i < natts; i++) {
+       (*dpp)->attrelid = new_rel_oid;
+       (*dpp)->attnvals = 0l;
+       
+       tup = heap_addheader(Natts_pg_attribute,
+                            ATTRIBUTE_TUPLE_SIZE,
+                            (char *) *dpp);
+       
+       heap_insert(rdesc, tup);
+       
+       if (hasindex)
+           CatalogIndexInsert(idescs, Num_pg_attr_indices, rdesc, tup);
+       
+       pfree(tup);
+       dpp++;
+    }
+    
+    /* ----------------
+     * next we add the system attributes..
+     * ----------------
+     */
+    dpp = HeapAtt;
+    for (i = 0; i < -1 - FirstLowInvalidHeapAttributeNumber; i++) {
+       (*dpp)->attrelid = new_rel_oid;
+       /*      (*dpp)->attnvals = 0l;  unneeded */
+       
+       tup = heap_addheader(Natts_pg_attribute,
+                            ATTRIBUTE_TUPLE_SIZE,
+                            (char *)*dpp);
+       
+       heap_insert(rdesc, tup);
+       
+       if (hasindex)
+           CatalogIndexInsert(idescs, Num_pg_attr_indices, rdesc, tup);
+       
+       pfree(tup);
+       dpp++;
+    }
+    
+    heap_close(rdesc);
+
+    /*
+     * close pg_attribute indices
+     */
+    if (hasindex)
+       CatalogCloseIndices(Num_pg_attr_indices, idescs);
+}
+
+/* --------------------------------
+ *     AddPgRelationTuple
+ *
+ *     this registers the new relation in the catalogs by
+ *     adding a tuple to pg_class.
+ * --------------------------------
+ */
+void
+AddPgRelationTuple(Relation pg_class_desc,
+                  Relation new_rel_desc,
+                  Oid new_rel_oid,
+                  int arch,
+                  unsigned natts)
+{
+    Form_pg_class      new_rel_reltup;
+    HeapTuple          tup;
+    Relation           idescs[Num_pg_class_indices];
+    bool               isBootstrap;
+    
+    /* ----------------
+     * first we munge some of the information in our
+     *  uncataloged relation's relation descriptor.
+     * ----------------
+     */
+    new_rel_reltup = new_rel_desc->rd_rel;
+    
+    /* CHECK should get new_rel_oid first via an insert then use XXX */
+    /*   new_rel_reltup->reltuples = 1; */ /* XXX */
+    
+    new_rel_reltup->relowner = GetUserId();
+    new_rel_reltup->relkind = RELKIND_RELATION;
+    new_rel_reltup->relarch = arch;
+    new_rel_reltup->relnatts = natts;
+    
+    /* ----------------
+     * now form a tuple to add to pg_class
+     *  XXX Natts_pg_class_fixed is a hack - see pg_class.h
+     * ----------------
+     */
+    tup = heap_addheader(Natts_pg_class_fixed,
+                        CLASS_TUPLE_SIZE,
+                        (char *) new_rel_reltup);
+    tup->t_oid = new_rel_oid;
+    
+    /* ----------------
+     *  finally insert the new tuple and free it.
+     *
+     *  Note: I have no idea why we do a
+     *         SetProcessingMode(BootstrapProcessing);
+     *        here -cim 6/14/90
+     * ----------------
+     */
+    isBootstrap = IsBootstrapProcessingMode() ? true : false;
+    
+    SetProcessingMode(BootstrapProcessing);
+    
+    heap_insert(pg_class_desc, tup);
+    
+    if (! isBootstrap) {
+       /*
+        *  First, open the catalog indices and insert index tuples for
+        *  the new relation.
+        */
+       
+       CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
+       CatalogIndexInsert(idescs, Num_pg_class_indices, pg_class_desc, tup);
+       CatalogCloseIndices(Num_pg_class_indices, idescs);
+       
+       /* now restore processing mode */
+       SetProcessingMode(NormalProcessing);
+    }
+    
+    pfree(tup);
+}
+
+
+/* --------------------------------
+ *     addNewRelationType -
+ *
+ *     define a complex type corresponding to the new relation
+ * --------------------------------
+ */
+void
+addNewRelationType(char *typeName, Oid new_rel_oid)
+{
+    Oid                new_type_oid;
+
+    /* The sizes are set to oid size because it makes implementing sets MUCH
+     * easier, and no one (we hope) uses these fields to figure out
+     * how much space to allocate for the type. 
+     * An oid is the type used for a set definition.  When a user
+     * requests a set, what they actually get is the oid of a tuple in
+     * the pg_proc catalog, so the size of the "set" is the size
+     * of an oid.
+     * Similarly, byval being true makes sets much easier, and 
+     * it isn't used by anything else.
+     * Note the assumption that OIDs are the same size as int4s.
+     */
+    new_type_oid = TypeCreate(typeName,                        /* type name */
+                             new_rel_oid,              /* relation oid */
+                             tlen(type("oid")),        /* internal size */
+                             tlen(type("oid")),        /* external size */
+                             'c',              /* type-type (catalog) */
+                             ',',              /* default array delimiter */
+                             "int4in", /* input procedure */
+                             "int4out",        /* output procedure */
+                             "int4in",  /* send procedure */
+                             "int4out",        /* receive procedure */
+                             NULL,     /* array element type - irrelevent */
+                             "-",              /* default type value */
+                             (bool) 1, /* passed by value */
+                             'i');     /* default alignment */
+}
+
+/* --------------------------------
+ *     heap_create     
+ *
+ *     creates a new cataloged relation.  see comments above.
+ * --------------------------------
+ */
+Oid
+heap_create(char relname[],
+           char *typename, /* not used currently */
+           int arch,
+           unsigned smgr,
+           TupleDesc tupdesc)
+{
+    Relation           pg_class_desc;
+    Relation           new_rel_desc;
+    Oid                        new_rel_oid;
+/*    NameData            typeNameData; */
+    int natts = tupdesc->natts;
+
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    AssertState(IsNormalProcessingMode() || IsBootstrapProcessingMode());
+    if (natts == 0 || natts > MaxHeapAttributeNumber)
+       elog(WARN, "amcreate: from 1 to %d attributes must be specified",
+            MaxHeapAttributeNumber);
+    
+    CheckAttributeNames(tupdesc);
+    
+    /* ----------------
+     * open pg_class and see that the relation doesn't
+     *  already exist.
+     * ----------------
+     */
+    pg_class_desc = heap_openr(RelationRelationName);
+    
+    if (RelationAlreadyExists(pg_class_desc, relname)) {
+       heap_close(pg_class_desc);
+       elog(WARN, "amcreate: %s relation already exists", relname);
+    }
+    
+    /* ----------------
+     *  ok, relation does not already exist so now we
+     * create an uncataloged relation and pull its relation oid
+     *  from the newly formed relation descriptor.
+     *
+     *  Note: The call to heap_creatr() does all the "real" work
+     *  of creating the disk file for the relation.
+     * ----------------
+     */
+    new_rel_desc = heap_creatr(relname, smgr, tupdesc);
+    new_rel_oid  = new_rel_desc->rd_att->attrs[0]->attrelid;
+    
+    /* ----------------
+     *  since defining a relation also defines a complex type,
+     * we add a new system type corresponding to the new relation.
+     * ----------------
+     */
+/*    namestrcpy(&typeNameData, relname);*/
+/*    addNewRelationType(&typeNameData, new_rel_oid);*/
+    addNewRelationType(relname, new_rel_oid);
+
+    /* ----------------
+     * now add tuples to pg_attribute for the attributes in
+     *  our new relation.
+     * ----------------
+     */
+    AddNewAttributeTuples(new_rel_oid, tupdesc);
+    
+    /* ----------------
+     * now update the information in pg_class.
+     * ----------------
+     */
+    AddPgRelationTuple(pg_class_desc,
+                      new_rel_desc,
+                      new_rel_oid,
+                      arch,
+                      natts);
+    
+    /* ----------------
+     * ok, the relation has been cataloged, so close our relations
+     *  and return the oid of the newly created relation.
+     *
+     * SOMEDAY: fill the STATISTIC relation properly.
+     * ----------------
+     */
+    heap_close(new_rel_desc);
+    heap_close(pg_class_desc);
+    
+    return new_rel_oid;
+}
+
+
+/* ----------------------------------------------------------------
+ *     heap_destroy    - removes all record of named relation from catalogs
+ *
+ *     1)  open relation, check for existence, etc.
+ *     2)  remove inheritance information
+ *     3)  remove indexes
+ *     4)  remove pg_class tuple
+ *     5)  remove pg_attribute tuples
+ *     6)  remove pg_type tuples
+ *     7)  unlink relation
+ *
+ * old comments
+ *     Except for vital relations, removes relation from
+ *     relation catalog, and related attributes from
+ *     attribute catalog (needed?).  (Anything else???)
+ *
+ *     get proper relation from relation catalog (if not arg)
+ *     check if relation is vital (strcmp()/reltype???)
+ *     scan attribute catalog deleting attributes of reldesc
+ *             (necessary?)
+ *     delete relation from relation catalog
+ *     (How are the tuples of the relation discarded???)
+ *
+ *     XXX Must fix to work with indexes.
+ *     There may be a better order for doing things.
+ *     Problems with destroying a deleted database--cannot create
+ *     a struct reldesc without having an open file descriptor.
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     RelationRemoveInheritance
+ *
+ *     Note: for now, we cause an exception if relation is a
+ *     superclass.  Someday, we may want to allow this and merge
+ *     the type info into subclass procedures....  this seems like
+ *     lots of work.
+ * --------------------------------
+ */
+void
+RelationRemoveInheritance(Relation relation)
+{
+    Relation           catalogRelation;
+    HeapTuple          tuple;
+    HeapScanDesc       scan;
+    ScanKeyData                entry;
+    
+    /* ----------------
+     * open pg_inherits
+     * ----------------
+     */
+    catalogRelation = heap_openr(InheritsRelationName);
+    
+    /* ----------------
+     * form a scan key for the subclasses of this class
+     *  and begin scanning
+     * ----------------
+     */
+    ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_inherits_inhparent,
+                          ObjectIdEqualRegProcedure,
+                          ObjectIdGetDatum(RelationGetRelationId(relation)));
+    
+    scan = heap_beginscan(catalogRelation,
+                         false,
+                         NowTimeQual,
+                         1,
+                         &entry);
+    
+    /* ----------------
+     * if any subclasses exist, then we disallow the deletion.
+     * ----------------
+     */
+    tuple = heap_getnext(scan, 0, (Buffer *)NULL);
+    if (HeapTupleIsValid(tuple)) {
+       heap_endscan(scan);
+       heap_close(catalogRelation);
+       
+       elog(WARN, "relation <%d> inherits \"%s\"",
+            ((InheritsTupleForm) GETSTRUCT(tuple))->inhrel,
+            RelationGetRelationName(relation));
+    }
+    
+    /* ----------------
+     * If we get here, it means the relation has no subclasses
+     *  so we can trash it.  First we remove dead INHERITS tuples.
+     * ----------------
+     */
+    entry.sk_attno = Anum_pg_inherits_inhrel;
+    
+    scan = heap_beginscan(catalogRelation,
+                         false,
+                         NowTimeQual,
+                         1,
+                         &entry);
+    
+    for (;;) {
+       tuple = heap_getnext(scan, 0, (Buffer *)NULL);
+       if (!HeapTupleIsValid(tuple)) {
+           break;
+       }
+       heap_delete(catalogRelation, &tuple->t_ctid);
+    }
+    
+    heap_endscan(scan);
+    heap_close(catalogRelation);
+    
+    /* ----------------
+     * now remove dead IPL tuples
+     * ----------------
+     */
+    catalogRelation =
+       heap_openr(InheritancePrecidenceListRelationName);
+    
+    entry.sk_attno = Anum_pg_ipl_iplrel;
+    
+    scan = heap_beginscan(catalogRelation,
+                         false,
+                         NowTimeQual,
+                         1,
+                         &entry);
+    
+    for (;;) {
+       tuple = heap_getnext(scan, 0, (Buffer *)NULL);
+       if (!HeapTupleIsValid(tuple)) {
+           break;
+       }
+       heap_delete(catalogRelation, &tuple->t_ctid);
+    }
+    
+    heap_endscan(scan);
+    heap_close(catalogRelation);
+}
+
+/* --------------------------------
+ *     RelationRemoveIndexes
+ *     
+ * --------------------------------
+ */
+void
+RelationRemoveIndexes(Relation relation)
+{
+    Relation           indexRelation;
+    HeapTuple          tuple;
+    HeapScanDesc       scan;
+    ScanKeyData        entry;
+    
+    indexRelation = heap_openr(IndexRelationName);
+    
+    ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_index_indrelid,
+                          ObjectIdEqualRegProcedure,
+                          ObjectIdGetDatum(RelationGetRelationId(relation)));
+    
+    scan = heap_beginscan(indexRelation,
+                         false,
+                         NowTimeQual,
+                         1,
+                         &entry);
+    
+    for (;;) {
+       tuple = heap_getnext(scan, 0, (Buffer *)NULL);
+       if (!HeapTupleIsValid(tuple)) {
+           break;
+       }
+       
+       index_destroy(((IndexTupleForm)GETSTRUCT(tuple))->indexrelid);
+    }
+    
+    heap_endscan(scan);
+    heap_close(indexRelation);
+}
+
+/* --------------------------------
+ *     DeletePgRelationTuple
+ *
+ * --------------------------------
+ */
+void
+DeletePgRelationTuple(Relation rdesc)
+{
+    Relation           pg_class_desc;
+    HeapScanDesc       pg_class_scan;
+    ScanKeyData        key;
+    HeapTuple          tup;
+    
+    /* ----------------
+     * open pg_class
+     * ----------------
+     */
+    pg_class_desc = heap_openr(RelationRelationName);
+    
+    /* ----------------
+     * create a scan key to locate the relation oid of the
+     *  relation to delete
+     * ----------------
+     */
+    ScanKeyEntryInitialize(&key, 0, ObjectIdAttributeNumber,
+                          F_INT4EQ, rdesc->rd_att->attrs[0]->attrelid);
+    
+    pg_class_scan =  heap_beginscan(pg_class_desc,
+                                   0,
+                                   NowTimeQual,
+                                   1,
+                                   &key);
+    
+    /* ----------------
+     * use heap_getnext() to fetch the pg_class tuple.  If this
+     *  tuple is not valid then something's wrong.
+     * ----------------
+     */
+    tup = heap_getnext(pg_class_scan, 0, (Buffer *) NULL);
+    
+    if (! PointerIsValid(tup)) {
+       heap_endscan(pg_class_scan);
+       heap_close(pg_class_desc);
+       elog(WARN, "DeletePgRelationTuple: %s relation nonexistent",
+            &rdesc->rd_rel->relname);
+    }
+    
+    /* ----------------
+     * delete the relation tuple from pg_class, and finish up.
+     * ----------------
+     */
+    heap_endscan(pg_class_scan);
+    heap_delete(pg_class_desc, &tup->t_ctid);
+    
+    heap_close(pg_class_desc);
+}
+
+/* --------------------------------
+ *     DeletePgAttributeTuples
+ *
+ * --------------------------------
+ */
+void
+DeletePgAttributeTuples(Relation rdesc)
+{
+    Relation           pg_attribute_desc;
+    HeapScanDesc       pg_attribute_scan;
+    ScanKeyData                key;
+    HeapTuple          tup;
+    
+    /* ----------------
+     * open pg_attribute
+     * ----------------
+     */
+    pg_attribute_desc = heap_openr(AttributeRelationName);
+    
+    /* ----------------
+     * create a scan key to locate the attribute tuples to delete
+     *  and begin the scan.
+     * ----------------
+     */
+    ScanKeyEntryInitialize(&key, 0, Anum_pg_attribute_attrelid,
+                          F_INT4EQ, rdesc->rd_att->attrs[0]->attrelid);
+    
+    /* -----------------
+     * Get a write lock _before_ getting the read lock in the scan
+     * ----------------
+     */
+    RelationSetLockForWrite(pg_attribute_desc);
+    
+    pg_attribute_scan = heap_beginscan(pg_attribute_desc,
+                                      0,
+                                      NowTimeQual,
+                                      1,
+                                      &key);
+    
+    /* ----------------
+     * use heap_getnext() / amdelete() until all attribute tuples
+     *  have been deleted.
+     * ----------------
+     */
+    while (tup = heap_getnext(pg_attribute_scan, 0, (Buffer *)NULL),
+          PointerIsValid(tup)) {
+       
+       heap_delete(pg_attribute_desc, &tup->t_ctid);
+    }
+    
+    /* ----------------
+     * finish up.
+     * ----------------
+     */
+    heap_endscan(pg_attribute_scan);
+    
+    /* ----------------
+     * Release the write lock 
+     * ----------------
+     */
+    RelationUnsetLockForWrite(pg_attribute_desc);
+    heap_close(pg_attribute_desc);
+}
+
+
+/* --------------------------------
+ *     DeletePgTypeTuple
+ *
+ *     If the user attempts to destroy a relation and there
+ *     exists attributes in other relations of type
+ *     "relation we are deleting", then we have to do something
+ *     special.  presently we disallow the destroy.
+ * --------------------------------
+ */
+void
+DeletePgTypeTuple(Relation rdesc)
+{
+    Relation           pg_type_desc;
+    HeapScanDesc       pg_type_scan;
+    Relation           pg_attribute_desc;
+    HeapScanDesc       pg_attribute_scan;
+    ScanKeyData                key;
+    ScanKeyData                attkey;
+    HeapTuple          tup;
+    HeapTuple          atttup;
+    Oid                typoid;
+    
+    /* ----------------
+     * open pg_type
+     * ----------------
+     */
+    pg_type_desc = heap_openr(TypeRelationName);
+    
+    /* ----------------
+     * create a scan key to locate the type tuple corresponding
+     *  to this relation.
+     * ----------------
+     */
+    ScanKeyEntryInitialize(&key, 0, Anum_pg_type_typrelid, F_INT4EQ,
+                          rdesc->rd_att->attrs[0]->attrelid);
+    
+    pg_type_scan =  heap_beginscan(pg_type_desc,
+                                  0,
+                                  NowTimeQual,
+                                  1,
+                                  &key);
+    
+    /* ----------------
+     * use heap_getnext() to fetch the pg_type tuple.  If this
+     *  tuple is not valid then something's wrong.
+     * ----------------
+     */
+    tup = heap_getnext(pg_type_scan, 0, (Buffer *)NULL);
+    
+    if (! PointerIsValid(tup)) {
+       heap_endscan(pg_type_scan);
+       heap_close(pg_type_desc);
+       elog(WARN, "DeletePgTypeTuple: %s type nonexistent",
+            &rdesc->rd_rel->relname);
+    }
+    
+    /* ----------------
+     * now scan pg_attribute.  if any other relations have
+     *  attributes of the type of the relation we are deleteing
+     *  then we have to disallow the deletion.  should talk to
+     *  stonebraker about this.  -cim 6/19/90
+     * ----------------
+     */
+    typoid = tup->t_oid;
+    
+    pg_attribute_desc = heap_openr(AttributeRelationName);
+    
+    ScanKeyEntryInitialize(&attkey,
+                          0, Anum_pg_attribute_atttypid, F_INT4EQ,
+                          typoid);
+    
+    pg_attribute_scan = heap_beginscan(pg_attribute_desc,
+                                      0,
+                                      NowTimeQual,
+                                      1,
+                                      &attkey);
+    
+    /* ----------------
+     * try and get a pg_attribute tuple.  if we succeed it means
+     *  we cant delete the relation because something depends on
+     *  the schema.
+     * ----------------
+     */
+    atttup = heap_getnext(pg_attribute_scan, 0, (Buffer *)NULL);
+    
+    if (PointerIsValid(atttup)) {
+       Oid relid = ((AttributeTupleForm) GETSTRUCT(atttup))->attrelid;
+       
+       heap_endscan(pg_type_scan);
+       heap_close(pg_type_desc);
+       heap_endscan(pg_attribute_scan);
+       heap_close(pg_attribute_desc);
+       
+       elog(WARN, "DeletePgTypeTuple: att of type %s exists in relation %d",
+            &rdesc->rd_rel->relname, relid);   
+    }
+    heap_endscan(pg_attribute_scan);
+    heap_close(pg_attribute_desc);
+    
+    /* ----------------
+     *  Ok, it's safe so we delete the relation tuple
+     *  from pg_type and finish up.  But first end the scan so that
+     *  we release the read lock on pg_type.  -mer 13 Aug 1991
+     * ----------------
+     */
+    heap_endscan(pg_type_scan);
+    heap_delete(pg_type_desc, &tup->t_ctid);
+    
+    heap_close(pg_type_desc);
+}
+
+/* --------------------------------
+ *     heap_destroy
+ *
+ * --------------------------------
+ */
+void
+heap_destroy(char *relname)
+{
+    Relation   rdesc;
+    
+    /* ----------------
+     * first open the relation.  if the relation does exist,
+     *  heap_openr() returns NULL.
+     * ----------------
+     */
+    rdesc = heap_openr(relname);
+    if (rdesc == NULL)
+       elog(WARN,"Relation %s Does Not Exist!", relname);
+    
+    /* ----------------
+     * prevent deletion of system relations
+     * ----------------
+     */
+    if (IsSystemRelationName(RelationGetRelationName(rdesc)->data))
+       elog(WARN, "amdestroy: cannot destroy %s relation",
+            &rdesc->rd_rel->relname);
+    
+    /* ----------------
+     * remove inheritance information
+     * ----------------
+     */
+    RelationRemoveInheritance(rdesc);
+    
+    /* ----------------
+     * remove indexes if necessary
+     * ----------------
+     */
+    if (rdesc->rd_rel->relhasindex) {
+       RelationRemoveIndexes(rdesc);
+    }
+
+    /* ----------------
+     * remove rules if necessary
+     * ----------------
+     */
+    if (rdesc->rd_rules != NULL) {
+       RelationRemoveRules(rdesc->rd_id);
+    }
+    
+    /* ----------------
+     * delete attribute tuples
+     * ----------------
+     */
+    DeletePgAttributeTuples(rdesc);
+    
+    /* ----------------
+     * delete type tuple.  here we want to see the effects
+     *  of the deletions we just did, so we use setheapoverride().
+     * ----------------
+     */
+    setheapoverride(true);
+    DeletePgTypeTuple(rdesc);
+    setheapoverride(false);
+    
+    /* ----------------
+     * delete relation tuple
+     * ----------------
+     */
+    DeletePgRelationTuple(rdesc);
+    
+    /* ----------------
+     * flush the relation from the relcache
+     * ----------------
+     */
+    RelationIdInvalidateRelationCacheByRelationId(rdesc->rd_id);
+
+    /* ----------------
+     * unlink the relation and finish up.
+     * ----------------
+     */
+    (void) smgrunlink(rdesc->rd_rel->relsmgr, rdesc);
+    heap_close(rdesc);
+}
+
+/*
+ * heap_destroyr
+ *    destroy and close temporary relations
+ *
+ */
+
+void 
+heap_destroyr(Relation rdesc)
+{
+    ReleaseTmpRelBuffers(rdesc);
+    (void) smgrunlink(rdesc->rd_rel->relsmgr, rdesc);
+    heap_close(rdesc);
+    RemoveFromTempRelList(rdesc);
+}
+
+
+/**************************************************************
+  functions to deal with the list of temporary relations 
+**************************************************************/
+
+/* --------------
+   InitTempRellist():
+
+   initialize temporary relations list
+   the tempRelList is a list of temporary relations that
+   are created in the course of the transactions
+   they need to be destroyed properly at the end of the transactions
+
+   MODIFIES the global variable tempRels
+
+ >> NOTE <<
+
+   malloc is used instead of palloc because we KNOW when we are
+   going to free these things.  Keeps us away from the memory context
+   hairyness
+
+*/
+void
+InitTempRelList()
+{
+    if (tempRels) {
+       free(tempRels->rels);
+       free(tempRels);
+    };
+
+    tempRels = (TempRelList*)malloc(sizeof(TempRelList));
+    tempRels->size = TEMP_REL_LIST_SIZE;
+    tempRels->rels = (Relation*)malloc(sizeof(Relation) * tempRels->size);
+    memset(tempRels->rels, sizeof(Relation) * tempRels->size , 0);
+    tempRels->num = 0;
+}
+
+/*
+   removes a relation from the TempRelList
+
+   MODIFIES the global variable tempRels
+      we don't really remove it, just mark it as NULL
+      and DestroyTempRels will look for NULLs
+*/
+void
+RemoveFromTempRelList(Relation r)
+{
+    int i;
+
+    if (!tempRels)
+       return;
+
+    for (i=0; i<tempRels->num; i++) {
+       if (tempRels->rels[i] == r) {
+           tempRels->rels[i] = NULL;
+           break;
+       }
+    }
+}
+
+/*
+   add a temporary relation to the TempRelList
+
+   MODIFIES the global variable tempRels
+*/
+void
+AddToTempRelList(Relation r)
+{
+    if (!tempRels)
+       return;
+
+    if (tempRels->num == tempRels->size) {
+       tempRels->size += TEMP_REL_LIST_SIZE;
+       tempRels->rels = realloc(tempRels->rels, tempRels->size);
+    }
+    tempRels->rels[tempRels->num] = r;
+    tempRels->num++;
+}
+
+/*
+   go through the tempRels list and destroy each of the relations
+*/
+void
+DestroyTempRels()
+{
+    int i;
+    Relation rdesc;
+
+    if (!tempRels)
+       return;
+
+    for (i=0;i<tempRels->num;i++) {
+       rdesc = tempRels->rels[i];
+       /* rdesc may be NULL if it has been removed from the list already */
+       if (rdesc) 
+           heap_destroyr(rdesc);
+    }
+    free(tempRels->rels);
+    free(tempRels);
+    tempRels = NULL;
+}
+
diff --git a/src/backend/catalog/heap.h b/src/backend/catalog/heap.h
new file mode 100644 (file)
index 0000000..18091fd
--- /dev/null
@@ -0,0 +1,42 @@
+/*-------------------------------------------------------------------------
+ *
+ * heap.h--
+ *    prototypes for functions in lib/catalog/heap.c
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef HEAP_H
+#define HEAP_H
+
+extern Relation heap_creatr(char *relname, unsigned smgr, TupleDesc att);
+
+extern int RelationAlreadyExists(Relation pg_class_desc, char relname[]);
+extern void addNewRelationType(char *typeName, Oid new_rel_oid);
+
+extern void AddPgRelationTuple(Relation pg_class_desc,
+       Relation new_rel_desc, Oid new_rel_oid, int arch, unsigned natts);
+
+extern Oid heap_create(char relname[], 
+                      char *typename,
+                      int arch, 
+                      unsigned smgr, TupleDesc tupdesc);
+
+extern void RelationRemoveInheritance(Relation relation);
+extern void RelationRemoveIndexes(Relation relation);
+extern void DeletePgRelationTuple(Relation rdesc);
+extern void DeletePgAttributeTuples(Relation rdesc);
+extern void DeletePgTypeTuple(Relation rdesc);
+extern void heap_destroy(char relname[]);
+extern void heap_destroyr(Relation r);
+extern void InitTempRelList();
+extern void AddToTempRelList(Relation r);
+extern void RemoveFromTempRelList(Relation r);
+extern void DestroyTempRels();
+
+#endif /* HEAP_H */
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
new file mode 100644 (file)
index 0000000..6024008
--- /dev/null
@@ -0,0 +1,1655 @@
+/*-------------------------------------------------------------------------
+ *
+ * index.c--
+ *    code to create and destroy POSTGRES index relations
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *
+ * INTERFACE ROUTINES
+ *     index_create()          - Create a cataloged index relation
+ *     index_destroy()         - Removes index relation from catalogs
+ *
+ * NOTES
+ *    Much of this code uses hardcoded sequential heap relation scans
+ *    to fetch information from the catalogs.  These should all be
+ *    rewritten to use the system caches lookup routines like
+ *    SearchSysCacheTuple, which can do efficient lookup and
+ *    caching.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/attnum.h"
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/itup.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "utils/builtins.h"
+#include "utils/tqual.h"
+#include "access/tupdesc.h"
+#include "access/funcindex.h"
+#include "access/xact.h"
+
+#include "storage/smgr.h"
+#include "miscadmin.h"
+#include "utils/mcxt.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+#include "utils/relcache.h"
+#include "utils/elog.h"
+
+#include "bootstrap/bootstrap.h"
+
+#include "catalog/catname.h"
+#include "catalog/catalog.h"
+#include "utils/syscache.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_index.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_type.h"
+#include "catalog/indexing.h"
+
+#include "catalog/heap.h"
+
+#include "nodes/execnodes.h"
+#include "nodes/plannodes.h"
+
+#include "catalog/index.h"
+
+#include "executor/executor.h"
+#include "executor/tuptable.h"
+
+#include "optimizer/clauses.h"
+#include "optimizer/prep.h"
+
+#include "parser/catalog_utils.h"
+
+#include "machine.h"
+
+/*
+ * macros used in guessing how many tuples are on a page.
+ */
+#define AVG_TUPLE_SIZE 8
+#define NTUPLES_PER_PAGE(natts) (BLCKSZ/((natts)*AVG_TUPLE_SIZE))
+
+/* non-export function prototypes */
+static Oid RelationNameGetObjectId(char *relationName, Relation pg_class,
+                                  bool setHasIndexAttribute);
+static Oid GetHeapRelationOid(char *heapRelationName, char *indexRelationName);
+static TupleDesc BuildFuncTupleDesc(FuncIndexInfo *funcInfo);
+static TupleDesc ConstructTupleDescriptor(Oid heapoid, Relation heapRelation,
+                                         int numatts, AttrNumber attNums[]);
+
+static void ConstructIndexReldesc(Relation indexRelation, Oid amoid);
+static Oid UpdateRelationRelation(Relation indexRelation);
+static void InitializeAttributeOids(Relation indexRelation,
+                                   int numatts,
+                                   Oid indexoid);
+static void
+AppendAttributeTuples(Relation indexRelation, int numatts);
+static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
+       FuncIndexInfo *funcInfo, int natts,
+       AttrNumber attNums[], Oid classOids[], Node *predicate);
+static void DefaultBuild(Relation heapRelation, Relation indexRelation,
+       int numberOfAttributes, AttrNumber attributeNumber[],
+       IndexStrategy indexStrategy, uint16 parameterCount,
+       Datum parameter[], FuncIndexInfoPtr funcInfo, PredInfo *predInfo);
+
+/* ----------------------------------------------------------------
+ *    sysatts is a structure containing attribute tuple forms
+ *    for system attributes (numbered -1, -2, ...).  This really
+ *    should be generated or eliminated or moved elsewhere. -cim 1/19/91
+ *
+ * typedef struct FormData_pg_attribute {
+ *     Oid     attrelid;
+ *     NameData        attname;
+ *     Oid     atttypid;
+ *     Oid     attdefrel;
+ *     uint32          attnvals;
+ *     Oid     atttyparg;      type arg for arrays/spquel/procs
+ *     int16           attlen;
+ *     AttrNumber      attnum;
+ *     uint16          attbound;
+ *     bool            attbyval;
+ *     bool            attcanindex;
+ *     Oid             attproc;        spquel?
+ * } FormData_pg_attribute;
+ *
+ *    The data in this table was taken from local1_template.ami
+ *    but tmin and tmax were switched because local1 was incorrect.
+ * ----------------------------------------------------------------
+ */
+static FormData_pg_attribute   sysatts[] = {
+   { 0l, {"ctid"},   27l,  0l, 0l, 0l,  6,  -1, 0,   '\0', '\001', 0l, 'i' },
+   { 0l, {"oid"},    26l,  0l, 0l, 0l,  4,  -2, 0, '\001', '\001', 0l, 'i' },
+   { 0l, {"xmin"},   28l,  0l, 0l, 0l,  5,  -3, 0,   '\0', '\001', 0l, 'i' },
+   { 0l, {"cmin"},   29l,  0l, 0l, 0l,  1,  -4, 0, '\001', '\001', 0l, 's' },
+   { 0l, {"xmax"},   28l,  0l, 0l, 0l,  5,  -5, 0,   '\0', '\001', 0l, 'i' },
+   { 0l, {"cmax"},   29l,  0l, 0l, 0l,  1,  -6, 0, '\001', '\001', 0l, 's' },
+   { 0l, {"chain"},  27l,  0l, 0l, 0l,  6,  -7, 0,   '\0', '\001', 0l, 'i' },
+   { 0l, {"anchor"}, 27l,  0l, 0l, 0l,  6,  -8, 0,   '\0', '\001', 0l, 'i' },
+   { 0l, {"tmin"},   20l,  0l, 0l, 0l,  4,  -9, 0, '\001', '\001', 0l, 'i' },
+   { 0l, {"tmax"},   20l,  0l, 0l, 0l,  4, -10, 0, '\001', '\001', 0l, 'i' },
+   { 0l, {"vtype"},  18l,  0l, 0l, 0l,  1, -11, 0, '\001', '\001', 0l, 'c' },
+};
+
+/* ----------------------------------------------------------------
+ * RelationNameGetObjectId --
+ *     Returns the object identifier for a relation given its name.
+ *
+ * >   The HASINDEX attribute for the relation with this name will
+ * >   be set if it exists and if it is indicated by the call argument.
+ * What a load of bull.  This setHasIndexAttribute is totally ignored.
+ * This is yet another silly routine to scan the catalogs which should
+ * probably be replaced by SearchSysCacheTuple. -cim 1/19/91
+ *
+ * Note:
+ *     Assumes relation name is valid.
+ *     Assumes relation descriptor is valid.
+ * ----------------------------------------------------------------
+ */
+static Oid
+RelationNameGetObjectId(char *relationName,
+                       Relation pg_class,
+                       bool setHasIndexAttribute)
+{      
+    HeapScanDesc       pg_class_scan;
+    HeapTuple          pg_class_tuple;
+    Oid                        relationObjectId;
+    Buffer             buffer;
+    ScanKeyData                key;
+    
+    /*
+     *  If this isn't bootstrap time, we can use the system catalogs to
+     *  speed this up.
+     */
+    
+    if (!IsBootstrapProcessingMode()) {
+       pg_class_tuple = ClassNameIndexScan(pg_class, relationName);
+       if (HeapTupleIsValid(pg_class_tuple)) {
+           relationObjectId = pg_class_tuple->t_oid;
+           pfree(pg_class_tuple);
+       } else
+           relationObjectId = InvalidOid;
+       
+       return (relationObjectId);
+    }
+    
+    /* ----------------
+     *  Bootstrap time, do this the hard way.
+     * begin a scan of pg_class for the named relation
+     * ----------------
+     */
+    ScanKeyEntryInitialize(&key, 0, Anum_pg_class_relname,
+                          NameEqualRegProcedure,
+                          PointerGetDatum(relationName));
+    
+    pg_class_scan = heap_beginscan(pg_class, 0, NowTimeQual, 1, &key);
+    
+    /* ----------------
+     * if we find the named relation, fetch its relation id
+     *  (the oid of the tuple we found). 
+     * ----------------
+     */
+    pg_class_tuple = heap_getnext(pg_class_scan, 0, &buffer);
+    
+    if (! HeapTupleIsValid(pg_class_tuple)) {
+       relationObjectId = InvalidOid;
+    } else {
+       relationObjectId = pg_class_tuple->t_oid;
+       ReleaseBuffer(buffer);
+    }
+    
+    /* ----------------
+     * cleanup and return results
+     * ----------------
+     */
+    heap_endscan(pg_class_scan);
+    
+    return
+       relationObjectId;
+}
+
+
+/* ----------------------------------------------------------------
+ *     GetHeapRelationOid
+ * ----------------------------------------------------------------
+ */
+static Oid
+GetHeapRelationOid(char *heapRelationName, char *indexRelationName)
+{
+    Relation   pg_class;
+    Oid        indoid;
+    Oid        heapoid;
+    
+    /* ----------------
+     * XXX ADD INDEXING HERE
+     * ----------------
+     */
+    /* ----------------
+     * open pg_class and get the oid of the relation
+     *  corresponding to the name of the index relation.
+     * ----------------
+     */
+    pg_class = heap_openr(RelationRelationName);
+    
+    indoid = RelationNameGetObjectId(indexRelationName,
+                                    pg_class,
+                                    false);
+    
+    if (OidIsValid(indoid))
+       elog(WARN, "Cannot create index: '%s' already exists",
+            indexRelationName);
+    
+    /* ----------------
+     * get the object id of the heap relation
+     * ----------------
+     */
+    heapoid = RelationNameGetObjectId(heapRelationName,
+                                     pg_class,
+                                     true);
+    
+    /* ----------------
+     *    check that the heap relation exists..
+     * ----------------
+     */
+    if (! OidIsValid(heapoid))
+       elog(WARN, "Cannot create index on '%s': relation does not exist",
+            heapRelationName);
+    
+    /* ----------------
+     *    close pg_class and return the heap relation oid
+     * ----------------
+     */
+    heap_close(pg_class);
+    
+    return heapoid;
+}
+
+static TupleDesc
+BuildFuncTupleDesc(FuncIndexInfo *funcInfo)
+{
+    HeapTuple          tuple;
+    TupleDesc  funcTupDesc;
+    Oid                retType;
+    char               *funcname;
+    int4               nargs;
+    Oid                *argtypes;
+    
+    /*
+     * Allocate and zero a tuple descriptor.
+     */
+    funcTupDesc = CreateTemplateTupleDesc(1);
+    funcTupDesc->attrs[0] = (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
+    memset(funcTupDesc->attrs[0], 0, ATTRIBUTE_TUPLE_SIZE);
+    
+    /*
+     * Lookup the function for the return type.
+     */
+    funcname = FIgetname(funcInfo);
+    nargs = FIgetnArgs(funcInfo);
+    argtypes = FIgetArglist(funcInfo);
+    tuple = SearchSysCacheTuple(PRONAME, 
+                               PointerGetDatum(funcname),
+                               Int32GetDatum(nargs), 
+                               PointerGetDatum(argtypes),
+                               0);
+    
+    if (!HeapTupleIsValid(tuple))
+       func_error("BuildFuncTupleDesc", funcname, nargs, (int*)argtypes);
+    
+    retType = ((Form_pg_proc)GETSTRUCT(tuple))->prorettype;
+    
+    /*
+     * Look up the return type in pg_type for the type length.
+     */
+    tuple = SearchSysCacheTuple(TYPOID,
+                               ObjectIdGetDatum(retType),
+                               0,0,0);
+    if (!HeapTupleIsValid(tuple))
+       elog(WARN,"Function %s return type does not exist",FIgetname(funcInfo));
+    
+    /*
+     * Assign some of the attributes values. Leave the rest as 0.
+     */
+    funcTupDesc->attrs[0]->attlen = ((TypeTupleForm)GETSTRUCT(tuple))->typlen;
+    funcTupDesc->attrs[0]->atttypid = retType;
+    funcTupDesc->attrs[0]->attnum = 1;
+    funcTupDesc->attrs[0]->attbyval = ((TypeTupleForm)GETSTRUCT(tuple))->typbyval;
+    funcTupDesc->attrs[0]->attcanindex = 0;
+    
+    /*
+     * make the attributes name the same as the functions
+     */
+    namestrcpy(&funcTupDesc->attrs[0]->attname, funcname);
+    
+    return (funcTupDesc);
+}
+
+/* ----------------------------------------------------------------
+ *     ConstructTupleDescriptor
+ * ----------------------------------------------------------------
+ */
+static TupleDesc
+ConstructTupleDescriptor(Oid heapoid,
+                        Relation heapRelation,
+                        int numatts,
+                        AttrNumber attNums[])
+{
+    TupleDesc  heapTupDesc;
+    TupleDesc  indexTupDesc;
+    AttrNumber         atnum;          /* attributeNumber[attributeOffset] */
+    AttrNumber         atind;
+    int        natts;          /* RelationTupleForm->relnatts */
+    char       *from;          /* used to simplify memcpy below */
+    char       *to;            /* used to simplify memcpy below */
+    int                i;
+    
+    /* ----------------
+     * allocate the new tuple descriptor
+     * ----------------
+     */
+    natts = RelationGetRelationTupleForm(heapRelation)->relnatts;
+    
+    indexTupDesc = CreateTemplateTupleDesc(numatts);
+    
+    /* ----------------
+     * 
+     * ----------------
+     */
+    
+    /* ----------------
+     *    for each attribute we are indexing, obtain its attribute
+     *    tuple form from either the static table of system attribute
+     *    tuple forms or the relation tuple descriptor
+     * ----------------
+     */
+    for (i = 0; i < numatts; i += 1) {
+       
+       /* ----------------
+        *   get the attribute number and make sure it's valid
+        * ----------------
+        */
+       atnum = attNums[i];
+       if (atnum > natts)
+           elog(WARN, "Cannot create index: attribute %d does not exist",
+                atnum);
+       
+       indexTupDesc->attrs[i] = (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
+       
+       /* ----------------
+        *   determine which tuple descriptor to copy
+        * ----------------
+        */
+       if (!AttrNumberIsForUserDefinedAttr(atnum)) {
+           
+           /* ----------------
+            *    here we are indexing on a system attribute (-1...-12)
+            *    so we convert atnum into a usable index 0...11 so we can
+            *    use it to dereference the array sysatts[] which stores
+            *    tuple descriptor information for system attributes.
+            * ----------------
+            */
+           if (atnum <= FirstLowInvalidHeapAttributeNumber || atnum >= 0 )
+               elog(WARN, "Cannot create index on system attribute: attribute number out of range (%d)", atnum);
+           atind = (-atnum) - 1;
+           
+           from = (char *) (& sysatts[atind]);
+           
+       } else {
+           /* ----------------
+            *    here we are indexing on a normal attribute (1...n)
+            * ----------------
+            */
+           
+           heapTupDesc = RelationGetTupleDescriptor(heapRelation);
+           atind =       AttrNumberGetAttrOffset(atnum);
+           
+           from = (char *) (heapTupDesc->attrs[ atind ]);
+       }
+       
+       /* ----------------
+        *   now that we've determined the "from", let's copy
+        *   the tuple desc data...
+        * ----------------
+        */
+       
+       to =   (char *) (indexTupDesc->attrs[ i ]);
+       memcpy(to, from, ATTRIBUTE_TUPLE_SIZE);
+       
+       /* ----------------
+        *    now we have to drop in the proper relation descriptor
+        *    into the copied tuple form's attrelid and we should be
+        *    all set.
+        * ----------------
+        */
+       ((AttributeTupleForm) to)->attrelid = heapoid;
+    }
+    
+    return indexTupDesc;
+}
+
+/* ----------------------------------------------------------------
+ * AccessMethodObjectIdGetAccessMethodTupleForm --
+ *     Returns the formated access method tuple given its object identifier.
+ *
+ * XXX ADD INDEXING
+ *
+ * Note:
+ *     Assumes object identifier is valid.
+ * ----------------------------------------------------------------
+ */
+Form_pg_am
+AccessMethodObjectIdGetAccessMethodTupleForm(Oid accessMethodObjectId)
+{
+    Relation           pg_am_desc;
+    HeapScanDesc       pg_am_scan;
+    HeapTuple          pg_am_tuple;
+    ScanKeyData                key;
+    Form_pg_am                 form;
+    
+    /* ----------------
+     * form a scan key for the pg_am relation
+     * ----------------
+     */
+    ScanKeyEntryInitialize(&key, 0, ObjectIdAttributeNumber,
+                          ObjectIdEqualRegProcedure,
+                          ObjectIdGetDatum(accessMethodObjectId));
+    
+    /* ----------------
+     * fetch the desired access method tuple
+     * ----------------
+     */
+    pg_am_desc = heap_openr(AccessMethodRelationName);
+    pg_am_scan = heap_beginscan(pg_am_desc, 0, NowTimeQual, 1, &key);
+    
+    pg_am_tuple = heap_getnext(pg_am_scan, 0, (Buffer *)NULL);
+    
+    /* ----------------
+     * return NULL if not found
+     * ----------------
+     */
+    if (! HeapTupleIsValid(pg_am_tuple)) {
+       heap_endscan(pg_am_scan);
+       heap_close(pg_am_desc);
+       return (NULL);
+    }
+    
+    /* ----------------
+     * if found am tuple, then copy the form and return the copy
+     * ----------------
+     */
+    form = (Form_pg_am)palloc(sizeof *form);
+    memcpy(form, GETSTRUCT(pg_am_tuple), sizeof *form);
+    
+    heap_endscan(pg_am_scan);
+    heap_close(pg_am_desc);
+    
+    return (form);
+}
+
+/* ----------------------------------------------------------------
+ *     ConstructIndexReldesc
+ * ----------------------------------------------------------------
+ */
+static void
+ConstructIndexReldesc(Relation indexRelation, Oid amoid)
+{
+    extern GlobalMemory  CacheCxt;
+    MemoryContext       oldcxt;
+    
+    /* ----------------
+     *    here we make certain to allocate the access method
+     *    tuple within the cache context lest it vanish when the
+     *    context changes
+     * ----------------
+     */
+    if (!CacheCxt)
+       CacheCxt = CreateGlobalMemory("Cache");
+    
+    oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+    
+    indexRelation->rd_am =
+       AccessMethodObjectIdGetAccessMethodTupleForm(amoid);
+    
+    MemoryContextSwitchTo(oldcxt);
+    
+    /* ----------------
+     *   XXX missing the initialization of some other fields 
+     * ----------------
+     */
+    
+    indexRelation->rd_rel->relowner =  GetUserId();
+    
+    indexRelation->rd_rel->relam =             amoid;
+    indexRelation->rd_rel->reltuples =                 1;              /* XXX */
+    indexRelation->rd_rel->relexpires =        0;              /* XXX */
+    indexRelation->rd_rel->relpreserved =      0;              /* XXX */
+    indexRelation->rd_rel->relkind =           RELKIND_INDEX;
+    indexRelation->rd_rel->relarch =           'n';            /* XXX */
+}
+
+/* ----------------------------------------------------------------
+ *     UpdateRelationRelation
+ * ----------------------------------------------------------------
+ */
+static Oid
+UpdateRelationRelation(Relation indexRelation)
+{
+    Relation   pg_class;
+    HeapTuple  tuple;
+    Oid        tupleOid;
+    Relation   idescs[Num_pg_class_indices];
+    
+    pg_class = heap_openr(RelationRelationName);
+    
+    /* XXX Natts_pg_class_fixed is a hack - see pg_class.h */
+    tuple = heap_addheader(Natts_pg_class_fixed,
+                          sizeof(*indexRelation->rd_rel),
+                          (char *) indexRelation->rd_rel);
+    
+    /* ----------------
+     *  the new tuple must have the same oid as the relcache entry for the
+     *  index.  sure would be embarassing to do this sort of thing in polite
+     *  company.
+     * ----------------
+     */
+    tuple->t_oid = indexRelation->rd_id;
+    heap_insert(pg_class, tuple);
+    
+    /*
+     *  During normal processing, we need to make sure that the system
+     *  catalog indices are correct.  Bootstrap (initdb) time doesn't
+     *  require this, because we make sure that the indices are correct
+     *  just before exiting.
+     */
+    
+    if (!IsBootstrapProcessingMode()) {
+       CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
+       CatalogIndexInsert(idescs, Num_pg_class_indices, pg_class, tuple);
+       CatalogCloseIndices(Num_pg_class_indices, idescs);
+    }
+    
+    tupleOid = tuple->t_oid;
+    pfree(tuple);
+    heap_close(pg_class);
+    
+    return(tupleOid);
+}
+
+/* ----------------------------------------------------------------
+ *     InitializeAttributeOids
+ * ----------------------------------------------------------------
+ */
+static void
+InitializeAttributeOids(Relation indexRelation,
+                       int numatts,
+                       Oid indexoid)
+{
+    TupleDesc  tupleDescriptor;
+    int                        i;
+    
+    tupleDescriptor = RelationGetTupleDescriptor(indexRelation);
+    
+    for (i = 0; i < numatts; i += 1)
+       tupleDescriptor->attrs[i]->attrelid = indexoid;
+}
+
+/* ----------------------------------------------------------------
+ *     AppendAttributeTuples
+ *
+ *     XXX For now, only change the ATTNUM attribute value
+ * ----------------------------------------------------------------
+ */
+static void
+AppendAttributeTuples(Relation indexRelation, int numatts)
+{
+    Relation   pg_attribute;
+    HeapTuple  tuple;
+    HeapTuple  newtuple;
+    bool       hasind;
+    Relation   idescs[Num_pg_attr_indices];
+    
+    Datum      value[ Natts_pg_attribute ];
+    char       nullv[ Natts_pg_attribute ];
+    char       replace[ Natts_pg_attribute ];
+    
+    TupleDesc  indexTupDesc;
+    int                i;
+    
+    /* ----------------
+     * open the attribute relation
+     *  XXX ADD INDEXING
+     * ----------------
+     */
+    pg_attribute = heap_openr(AttributeRelationName);
+    
+    /* ----------------
+     * initialize null[], replace[] and value[]
+     * ----------------
+     */
+    (void) memset(nullv, ' ', Natts_pg_attribute);
+    (void) memset(replace, ' ', Natts_pg_attribute);
+    
+    /* ----------------
+     *  create the first attribute tuple.
+     * XXX For now, only change the ATTNUM attribute value
+     * ----------------
+     */
+    replace[ Anum_pg_attribute_attnum - 1 ] = 'r';
+    
+    value[ Anum_pg_attribute_attnum - 1 ] = Int16GetDatum(1);
+    
+    tuple = heap_addheader(Natts_pg_attribute,
+                          sizeof *(indexRelation->rd_att->attrs[0]),
+                          (char *)(indexRelation->rd_att->attrs[0]));
+    
+    hasind = false;
+    if (!IsBootstrapProcessingMode() && pg_attribute->rd_rel->relhasindex) {
+       hasind = true;
+       CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
+    }
+    
+    /* ----------------
+     *  insert the first attribute tuple.
+     * ----------------
+     */
+    tuple = heap_modifytuple(tuple,
+                            InvalidBuffer,
+                            pg_attribute,
+                            value,
+                            nullv,
+                            replace);
+    
+    heap_insert(pg_attribute, tuple);
+    if (hasind)
+       CatalogIndexInsert(idescs, Num_pg_attr_indices, pg_attribute, tuple);
+    
+    /* ----------------
+     * now we use the information in the index tuple
+     *  descriptor to form the remaining attribute tuples.
+     * ----------------
+     */
+    indexTupDesc = RelationGetTupleDescriptor(indexRelation);
+    
+    for (i = 1; i < numatts; i += 1) {
+       /* ----------------
+        *  process the remaining attributes...
+        * ----------------
+        */
+       memmove(GETSTRUCT(tuple),
+              (char *)indexTupDesc->attrs[i],
+              sizeof (AttributeTupleForm));
+       
+       value[ Anum_pg_attribute_attnum - 1 ] = Int16GetDatum(i + 1);
+       
+       newtuple = heap_modifytuple(tuple,
+                                   InvalidBuffer,
+                                   pg_attribute,
+                                   value,
+                                   nullv,
+                                   replace);
+       
+       heap_insert(pg_attribute, newtuple);
+       if (hasind)
+           CatalogIndexInsert(idescs, Num_pg_attr_indices, pg_attribute, newtuple);
+       
+       /* ----------------
+        *  ModifyHeapTuple returns a new copy of a tuple
+        *  so we free the original and use the copy..
+        * ----------------
+        */
+       pfree(tuple);
+       tuple = newtuple;
+    }
+    
+    /* ----------------
+     * close the attribute relation and free the tuple
+     * ----------------
+     */
+    heap_close(pg_attribute);
+    
+    if (hasind)
+       CatalogCloseIndices(Num_pg_attr_indices, idescs);
+    
+    pfree(tuple);
+}
+
+/* ----------------------------------------------------------------
+ *     UpdateIndexRelation
+ * ----------------------------------------------------------------
+ */
+static void
+UpdateIndexRelation(Oid indexoid,
+                   Oid heapoid,
+                   FuncIndexInfo *funcInfo,
+                   int natts,
+                   AttrNumber attNums[],
+                   Oid classOids[],
+                   Node *predicate)
+{
+    IndexTupleForm     indexForm;
+    char               *predString;
+    text               *predText;
+    int                        predLen, itupLen;
+    Relation           pg_index;
+    HeapTuple          tuple;
+    int                        i;
+    
+    /* ----------------
+     * allocate an IndexTupleForm big enough to hold the
+     *  index-predicate (if any) in string form
+     * ----------------
+     */
+    if (predicate != NULL) {
+       predString = nodeToString(predicate);
+       predText = (text *)fmgr(F_TEXTIN, predString);
+       pfree(predString);
+    } else {
+       predText = (text *)fmgr(F_TEXTIN, "");
+    }
+    predLen = VARSIZE(predText);
+    itupLen = predLen + sizeof(FormData_pg_index);
+    indexForm = (IndexTupleForm) palloc(itupLen);
+    
+    memmove((char *)& indexForm->indpred, (char *)predText, predLen);
+    
+    /* ----------------
+     * store the oid information into the index tuple form
+     * ----------------
+     */
+    indexForm->indrelid =   heapoid;
+    indexForm->indexrelid = indexoid;
+    indexForm->indproc = (PointerIsValid(funcInfo)) ?
+       FIgetProcOid(funcInfo) : InvalidOid;
+    
+    memset((char *)& indexForm->indkey[0], 0, sizeof indexForm->indkey);
+    memset((char *)& indexForm->indclass[0], 0, sizeof indexForm->indclass);
+    
+    /* ----------------
+     * copy index key and op class information
+     * ----------------
+     */
+    for (i = 0; i < natts; i += 1) {
+       indexForm->indkey[i] =   attNums[i];
+       indexForm->indclass[i] = classOids[i];
+    }
+    /*
+     * If we have a functional index, add all attribute arguments
+     */
+    if (PointerIsValid(funcInfo))
+       {
+           for (i=1; i < FIgetnArgs(funcInfo); i++)
+               indexForm->indkey[i] =   attNums[i];
+       }
+    
+    indexForm->indisclustered = '\0';          /* XXX constant */
+    indexForm->indisarchived = '\0';           /* XXX constant */
+    
+    /* ----------------
+     * open the system catalog index relation
+     * ----------------
+     */
+    pg_index = heap_openr(IndexRelationName);
+    
+    /* ----------------
+     * form a tuple to insert into pg_index
+     * ----------------
+     */
+    tuple = heap_addheader(Natts_pg_index,
+                          itupLen,
+                          (char *)indexForm);
+    
+    /* ----------------
+     * insert the tuple into the pg_index
+     *  XXX ADD INDEX TUPLES TOO
+     * ----------------
+     */
+    heap_insert(pg_index, tuple);
+    
+    /* ----------------
+     * close the relation and free the tuple
+     * ----------------
+     */
+    heap_close(pg_index);
+    pfree(predText);
+    pfree(indexForm);
+    pfree(tuple);
+}
+
+/* ----------------------------------------------------------------
+ *     UpdateIndexPredicate
+ * ----------------------------------------------------------------
+ */
+void
+UpdateIndexPredicate(Oid indexoid, Node *oldPred, Node *predicate)
+{
+    Node               *newPred;
+    char               *predString;
+    text               *predText;
+    Relation           pg_index;
+    HeapTuple          tuple;
+    HeapTuple          newtup;
+    ScanKeyData        entry;
+    HeapScanDesc       scan;
+    Buffer             buffer;
+    int                        i;
+    Datum              values[Natts_pg_index];
+    char               nulls[Natts_pg_index];
+    char               replace[Natts_pg_index];
+    
+    /*
+     * Construct newPred as a CNF expression equivalent to the OR of the
+     * original partial-index predicate ("oldPred") and the extension
+     * predicate ("predicate").
+     *
+     * This should really try to process the result to change things like
+     * "a>2 OR a>1" to simply "a>1", but for now all it does is make sure
+     * that if the extension predicate is NULL (i.e., it is being extended
+     * to be a complete index), then newPred will be NULL - in effect,
+     * changing "a>2 OR TRUE" to "TRUE". --Nels, Jan '93
+     */
+    newPred = NULL;
+    if (predicate != NULL) {
+       newPred =
+           (Node*)make_orclause(lcons(make_andclause((List*)predicate),
+                                     lcons(make_andclause((List*)oldPred),
+                                          NIL)));
+       newPred = (Node*)cnfify((Expr*)newPred, true);
+    }
+    
+    /* translate the index-predicate to string form */
+    if (newPred != NULL) {
+       predString = nodeToString(newPred);
+       predText = (text *)fmgr(F_TEXTIN, predString);
+       pfree(predString);
+    } else {
+       predText = (text *)fmgr(F_TEXTIN, "");
+    }
+    
+    /* open the index system catalog relation */
+    pg_index = heap_openr(IndexRelationName);
+    
+    ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_index_indexrelid, 
+                          ObjectIdEqualRegProcedure, 
+                          ObjectIdGetDatum(indexoid));
+    
+    scan = heap_beginscan(pg_index, 0, NowTimeQual, 1, &entry);
+    tuple = heap_getnext(scan, 0, &buffer);
+    heap_endscan(scan);
+    
+    for (i = 0; i < Natts_pg_index; i++) {
+       nulls[i] = heap_attisnull(tuple, i+1) ? 'n' : ' ';
+       replace[i] = ' ';
+       values[i] = (Datum) NULL;
+    }
+    
+    replace[Anum_pg_index_indpred - 1] = 'r';
+    values[Anum_pg_index_indpred - 1] = (Datum) predText;
+    
+    newtup = heap_modifytuple(tuple, buffer, pg_index, values, nulls, replace);
+    
+    (void) heap_replace(pg_index, &(newtup->t_ctid), newtup);
+    
+    heap_close(pg_index);
+    pfree(predText);
+}
+
+/* ----------------------------------------------------------------
+ *     InitIndexStrategy
+ * ----------------------------------------------------------------
+ */
+void
+InitIndexStrategy(int numatts,
+                 Relation indexRelation,
+                 Oid accessMethodObjectId)
+{
+    IndexStrategy      strategy;
+    RegProcedure       *support;
+    uint16             amstrategies;
+    uint16             amsupport;
+    Oid                attrelid;
+    Size               strsize;
+    extern GlobalMemory CacheCxt;
+    
+    /* ----------------
+     * get information from the index relation descriptor
+     * ----------------
+     */
+    attrelid =            indexRelation->rd_att->attrs[0]->attrelid;
+    amstrategies = indexRelation->rd_am->amstrategies;
+    amsupport =    indexRelation->rd_am->amsupport;
+    
+    /* ----------------
+     * get the size of the strategy
+     * ----------------
+     */
+    strsize =  AttributeNumberGetIndexStrategySize(numatts, amstrategies);
+    
+    /* ----------------
+     *  allocate the new index strategy structure
+     *
+     * the index strategy has to be allocated in the same
+     * context as the relation descriptor cache or else
+     * it will be lost at the end of the transaction.
+     * ----------------
+     */
+    if (!CacheCxt)
+       CacheCxt = CreateGlobalMemory("Cache");
+    
+    strategy = (IndexStrategy)
+       MemoryContextAlloc((MemoryContext)CacheCxt, strsize);
+    
+    if (amsupport > 0) {
+        strsize = numatts * (amsupport * sizeof(RegProcedure));
+        support = (RegProcedure *) MemoryContextAlloc((MemoryContext)CacheCxt,
+                                                     strsize);
+    } else {
+       support = (RegProcedure *) NULL;
+    }
+    
+    /* ----------------
+     * fill in the index strategy structure with information
+     *  from the catalogs.  Note: we use heap override mode
+     *  in order to be allowed to see the correct information in the
+     *  catalogs, even though our transaction has not yet committed.
+     * ----------------
+     */
+    setheapoverride(1);
+    
+    IndexSupportInitialize(strategy, support,
+                          attrelid, accessMethodObjectId,
+                          amstrategies, amsupport, numatts);
+    
+    setheapoverride(0);
+    
+    /* ----------------
+     * store the strategy information in the index reldesc
+     * ----------------
+     */
+    RelationSetIndexSupport(indexRelation, strategy, support);
+}
+
+
+/* ----------------------------------------------------------------
+ *     index_create
+ * ----------------------------------------------------------------
+ */
+void
+index_create(char *heapRelationName,
+            char *indexRelationName,
+            FuncIndexInfo *funcInfo,
+            Oid accessMethodObjectId,
+            int numatts,
+            AttrNumber attNums[],
+            Oid classObjectId[],
+            uint16 parameterCount,
+            Datum parameter[],
+            Node *predicate)
+{
+    Relation           heapRelation;
+    Relation           indexRelation;
+    TupleDesc          indexTupDesc;
+    Oid                        heapoid;
+    Oid                        indexoid;
+    PredInfo           *predInfo;
+    
+    /* ----------------
+     * check parameters
+     * ----------------
+     */
+    if (numatts < 1)
+       elog(WARN, "must index at least one attribute");
+    
+    /* ----------------
+     *    get heap relation oid and open the heap relation
+     *   XXX ADD INDEXING
+     * ----------------
+     */
+    heapoid = GetHeapRelationOid(heapRelationName, indexRelationName);
+    
+    heapRelation = heap_open(heapoid);
+    
+    /* ----------------
+     * write lock heap to guarantee exclusive access
+     * ---------------- 
+     */
+    
+    RelationSetLockForWrite(heapRelation);
+    
+    /* ----------------
+     *    construct new tuple descriptor
+     * ----------------
+     */
+    if (PointerIsValid(funcInfo))
+       indexTupDesc = BuildFuncTupleDesc(funcInfo);
+    else
+       indexTupDesc = ConstructTupleDescriptor(heapoid,
+                                               heapRelation,
+                                               numatts,
+                                               attNums);
+    
+    /* ----------------
+     * create the index relation
+     * ----------------
+     */
+    indexRelation = heap_creatr(indexRelationName,
+                               DEFAULT_SMGR,
+                               indexTupDesc);
+    
+    /* ----------------
+     *    construct the index relation descriptor
+     *
+     *    XXX should have a proper way to create cataloged relations
+     * ----------------
+     */
+    ConstructIndexReldesc(indexRelation, accessMethodObjectId);
+    
+    /* ----------------
+     *    add index to catalogs
+     *    (append RELATION tuple)
+     * ----------------
+     */
+    indexoid = UpdateRelationRelation(indexRelation);
+    
+    /* ----------------
+     * Now get the index procedure (only relevant for functional indices).
+     * ----------------
+     */
+    
+    if (PointerIsValid(funcInfo))
+       {
+           HeapTuple proc_tup;
+           
+           proc_tup = SearchSysCacheTuple(PRONAME,
+                                  PointerGetDatum(FIgetname(funcInfo)),
+                                  Int32GetDatum(FIgetnArgs(funcInfo)),
+                                  PointerGetDatum(FIgetArglist(funcInfo)),
+                                  0);
+           
+           if (!HeapTupleIsValid(proc_tup)) {
+               func_error("index_create", FIgetname(funcInfo),
+                          FIgetnArgs(funcInfo), 
+                          (int*) FIgetArglist(funcInfo));
+           }
+           FIgetProcOid(funcInfo) = proc_tup->t_oid;
+       }
+    
+    /* ----------------
+     * now update the object id's of all the attribute
+     *  tuple forms in the index relation's tuple descriptor
+     * ----------------
+     */
+    InitializeAttributeOids(indexRelation, numatts, indexoid);
+    
+    /* ----------------
+     *    append ATTRIBUTE tuples
+     * ----------------
+     */
+    AppendAttributeTuples(indexRelation, numatts);
+    
+    /* ----------------
+     *    update pg_index
+     *    (append INDEX tuple)
+     *
+     *    Note that this stows away a representation of "predicate".
+     *    (Or, could define a rule to maintain the predicate) --Nels, Feb '92
+     * ----------------
+     */
+    UpdateIndexRelation(indexoid, heapoid, funcInfo,
+                       numatts, attNums, classObjectId, predicate);
+    
+    predInfo = (PredInfo*)palloc(sizeof(PredInfo));
+    predInfo->pred = predicate;
+    predInfo->oldPred = NULL;
+    
+    /* ----------------
+     *    initialize the index strategy
+     * ----------------
+     */
+    InitIndexStrategy(numatts, indexRelation, accessMethodObjectId);
+    
+    /*
+     *  If this is bootstrap (initdb) time, then we don't actually
+     *  fill in the index yet.  We'll be creating more indices and classes
+     *  later, so we delay filling them in until just before we're done
+     *  with bootstrapping.  Otherwise, we call the routine that constructs
+     *  the index.  The heap and index relations are closed by index_build().
+     */
+    if (IsBootstrapProcessingMode()) {
+       index_register(heapRelationName, indexRelationName, numatts, attNums,
+                      parameterCount, parameter, funcInfo, predInfo);
+    } else {
+       heapRelation = heap_openr(heapRelationName);
+       index_build(heapRelation, indexRelation, numatts, attNums,
+                   parameterCount, parameter, funcInfo, predInfo);
+    }
+}
+
+/* ----------------------------------------------------------------
+ *     index_destroy
+ *
+ *     XXX break into modules like index_create
+ * ----------------------------------------------------------------
+ */
+void
+index_destroy(Oid indexId)
+{
+    Relation           indexRelation;
+    Relation           catalogRelation;
+    HeapTuple          tuple;
+    HeapScanDesc       scan;
+    ScanKeyData                entry;
+    
+    Assert(OidIsValid(indexId));
+    
+    indexRelation = index_open(indexId);
+    
+    /* ----------------
+     * fix RELATION relation
+     * ----------------
+     */
+    catalogRelation = heap_openr(RelationRelationName);
+    
+    ScanKeyEntryInitialize(&entry, 0x0, ObjectIdAttributeNumber, 
+                          ObjectIdEqualRegProcedure, 
+                          ObjectIdGetDatum(indexId));;
+    
+    scan = heap_beginscan(catalogRelation, 0, NowTimeQual, 1, &entry);
+    tuple = heap_getnext(scan, 0, (Buffer *)NULL);
+    
+    AssertState(HeapTupleIsValid(tuple));
+    
+    heap_delete(catalogRelation, &tuple->t_ctid);
+    heap_endscan(scan);
+    heap_close(catalogRelation);
+    
+    /* ----------------
+     * fix ATTRIBUTE relation
+     * ----------------
+     */
+    catalogRelation = heap_openr(AttributeRelationName);
+    
+    entry.sk_attno = Anum_pg_attribute_attrelid;
+    
+    scan = heap_beginscan(catalogRelation, 0, NowTimeQual, 1, &entry);
+    
+    while (tuple = heap_getnext(scan, 0, (Buffer *)NULL),
+          HeapTupleIsValid(tuple)) {
+       
+       heap_delete(catalogRelation, &tuple->t_ctid);
+    }
+    heap_endscan(scan);
+    heap_close(catalogRelation);
+    
+    /* ----------------
+     * fix INDEX relation
+     * ----------------
+     */
+    catalogRelation = heap_openr(IndexRelationName);
+    
+    entry.sk_attno = Anum_pg_index_indexrelid;
+    
+    scan = heap_beginscan(catalogRelation, 0, NowTimeQual, 1,  &entry);
+    tuple = heap_getnext(scan, 0, (Buffer *)NULL);
+    if (! HeapTupleIsValid(tuple)) {
+       elog(NOTICE, "IndexRelationDestroy: %s's INDEX tuple missing",
+            RelationGetRelationName(indexRelation));
+    }
+    heap_delete(catalogRelation, &tuple->t_ctid);
+    heap_endscan(scan);
+    heap_close(catalogRelation);
+    
+    /*
+     * physically remove the file
+     */
+    if (FileNameUnlink(relpath(indexRelation->rd_rel->relname.data)) < 0)
+       elog(WARN, "amdestroyr: unlink: %m");
+    
+    index_close(indexRelation);
+}
+
+/* ----------------------------------------------------------------
+ *                     index_build support
+ * ----------------------------------------------------------------
+ */
+/* ----------------
+ *     FormIndexDatum
+ * ----------------
+ */
+void
+FormIndexDatum(int numberOfAttributes,
+              AttrNumber attributeNumber[],
+              HeapTuple heapTuple,
+              TupleDesc heapDescriptor,
+              Buffer buffer,
+              Datum *datum,
+              char *nullv,
+              FuncIndexInfoPtr fInfo)
+{
+    AttrNumber i;
+    int                offset;
+    bool       isNull;
+    
+    /* ----------------
+     * for each attribute we need from the heap tuple,
+     *  get the attribute and stick it into the datum and
+     *  null arrays.
+     * ----------------
+     */
+    
+    for (i = 1; i <= numberOfAttributes; i += 1) {
+       offset = AttrNumberGetAttrOffset(i);
+       
+       datum[ offset ] =
+           PointerGetDatum( GetIndexValue(heapTuple,
+                                          heapDescriptor,
+                                          offset,
+                                          attributeNumber,
+                                          fInfo,
+                                          &isNull,
+                                          buffer) );
+       
+       nullv[ offset ] = (isNull) ? 'n' : ' ';
+    }
+}
+
+
+/* ----------------
+ *     UpdateStats
+ * ----------------
+ */
+void
+UpdateStats(Oid relid, long reltuples, bool hasindex)
+{
+    Relation   whichRel;
+    Relation   pg_class;
+    HeapScanDesc pg_class_scan;
+    HeapTuple  htup;
+    HeapTuple  newtup;
+    long       relpages;
+    Buffer     buffer;
+    int        i;
+    Form_pg_class rd_rel;
+    Relation   idescs[Num_pg_class_indices];
+    
+    static ScanKeyData key[1] = {
+       { 0, ObjectIdAttributeNumber, ObjectIdEqualRegProcedure }
+    };
+    Datum      values[Natts_pg_class];
+    char       nulls[Natts_pg_class];
+    char       replace[Natts_pg_class];
+    
+    fmgr_info(ObjectIdEqualRegProcedure, (func_ptr *) &key[0].sk_func,
+             &key[0].sk_nargs);
+    
+    /* ----------------
+     * This routine handles updates for both the heap and index relation
+     * statistics.  In order to guarantee that we're able to *see* the index
+     * relation tuple, we bump the command counter id here.  The index
+     * relation tuple was created in the current transaction.
+     * ----------------
+     */
+    CommandCounterIncrement();
+    
+    /* ----------------
+     * CommandCounterIncrement() flushes invalid cache entries, including
+     * those for the heap and index relations for which we're updating
+     * statistics.  Now that the cache is flushed, it's safe to open the
+     * relation again.  We need the relation open in order to figure out
+     * how many blocks it contains.
+     * ----------------
+     */
+    
+    whichRel = RelationIdGetRelation(relid);
+    
+    if (!RelationIsValid(whichRel))
+       elog(WARN, "UpdateStats: cannot open relation id %d", relid);
+    
+    /* ----------------
+     * Find the RELATION relation tuple for the given relation.
+     * ----------------
+     */
+    pg_class = heap_openr(RelationRelationName);
+    if (! RelationIsValid(pg_class)) {
+       elog(WARN, "UpdateStats: could not open RELATION relation");
+    }
+    key[0].sk_argument = ObjectIdGetDatum(relid);
+    
+    pg_class_scan =
+       heap_beginscan(pg_class, 0, NowTimeQual, 1, key);
+    
+    if (! HeapScanIsValid(pg_class_scan)) {
+       heap_close(pg_class);
+       elog(WARN, "UpdateStats: cannot scan RELATION relation");
+    }
+    
+    /* if the heap_open above succeeded, then so will this heap_getnext() */
+    htup = heap_getnext(pg_class_scan, 0, &buffer);
+    heap_endscan(pg_class_scan);
+    
+    /* ----------------
+     * update statistics
+     * ----------------
+     */
+    relpages = RelationGetNumberOfBlocks(whichRel);
+    
+    /*
+     *  We shouldn't have to do this, but we do...  Modify the reldesc
+     *  in place with the new values so that the cache contains the
+     *  latest copy.
+     */
+    
+    whichRel->rd_rel->relhasindex = hasindex;
+    whichRel->rd_rel->relpages = relpages;
+    whichRel->rd_rel->reltuples = reltuples;
+    
+    for (i = 0; i < Natts_pg_class; i++) {
+       nulls[i] = heap_attisnull(htup, i+1) ? 'n' : ' ';
+       replace[i] = ' ';
+       values[i] = (Datum) NULL;
+    }
+    
+    /*
+     * If reltuples wasn't supplied take an educated guess.
+     */
+    if (reltuples == 0)
+       reltuples = relpages*NTUPLES_PER_PAGE(whichRel->rd_rel->relnatts);
+    
+    if (IsBootstrapProcessingMode()) {
+       
+       /*
+        *  At bootstrap time, we don't need to worry about concurrency
+        *  or visibility of changes, so we cheat.
+        */
+       
+       rd_rel = (Form_pg_class) GETSTRUCT(htup);
+       rd_rel->relpages = relpages;
+       rd_rel->reltuples = reltuples;
+       rd_rel->relhasindex = hasindex;
+    } else {
+       /* during normal processing, work harder */
+       replace[Anum_pg_class_relpages - 1] = 'r';
+       values[Anum_pg_class_relpages - 1] = (Datum)relpages;
+       replace[Anum_pg_class_reltuples - 1] = 'r';
+       values[Anum_pg_class_reltuples - 1] = (Datum)reltuples;
+       replace[Anum_pg_class_relhasindex - 1] = 'r';
+       values[Anum_pg_class_relhasindex - 1] = CharGetDatum(hasindex);
+       
+       newtup = heap_modifytuple(htup, buffer, pg_class, values,
+                                 nulls, replace);
+       (void) heap_replace(pg_class, &(newtup->t_ctid), newtup);
+       CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
+       CatalogIndexInsert(idescs, Num_pg_class_indices, pg_class, newtup);
+       CatalogCloseIndices(Num_pg_class_indices, idescs);
+    }
+    
+    heap_close(pg_class);
+    heap_close(whichRel);
+}
+
+
+/* -------------------------
+ *     FillDummyExprContext
+ *         Sets up dummy ExprContext and TupleTableSlot objects for use
+ *         with ExecQual.
+ * -------------------------
+ */
+void
+FillDummyExprContext(ExprContext *econtext,
+                    TupleTableSlot *slot,
+                    TupleDesc tupdesc,
+                    Buffer buffer)
+{
+    econtext->ecxt_scantuple = slot;
+    econtext->ecxt_innertuple = NULL;
+    econtext->ecxt_outertuple = NULL;
+    econtext->ecxt_param_list_info = NULL;
+    econtext->ecxt_range_table = NULL;
+    
+    slot->ttc_tupleDescriptor = tupdesc;
+    slot->ttc_buffer = buffer;
+    slot->ttc_shouldFree = false;
+
+}
+
+
+/* ----------------
+ *     DefaultBuild
+ * ----------------
+ */
+static void
+DefaultBuild(Relation heapRelation,
+            Relation indexRelation,
+            int numberOfAttributes,
+            AttrNumber attributeNumber[],
+            IndexStrategy indexStrategy, /* not used */
+            uint16 parameterCount, /* not used */
+            Datum      parameter[], /* not used */
+            FuncIndexInfoPtr funcInfo,
+            PredInfo *predInfo)
+{
+    HeapScanDesc       scan;
+    HeapTuple          heapTuple;
+    Buffer             buffer;
+    
+    IndexTuple         indexTuple;
+    TupleDesc          heapDescriptor;
+    TupleDesc          indexDescriptor;
+    Datum              *datum;
+    char               *nullv;
+    long               reltuples, indtuples;
+    ExprContext                *econtext;
+    TupleTable         tupleTable;
+    TupleTableSlot     *slot;
+    Node               *predicate;
+    Node               *oldPred;
+    
+    InsertIndexResult  insertResult;
+    
+    /* ----------------
+     * more & better checking is needed
+     * ----------------
+     */
+    Assert(OidIsValid(indexRelation->rd_rel->relam));  /* XXX */
+    
+    /* ----------------
+     * get the tuple descriptors from the relations so we know
+     *  how to form the index tuples..
+     * ----------------
+     */
+    heapDescriptor =  RelationGetTupleDescriptor(heapRelation);
+    indexDescriptor = RelationGetTupleDescriptor(indexRelation);
+    
+    /* ----------------
+     * datum and null are arrays in which we collect the index attributes
+     *  when forming a new index tuple.
+     * ----------------
+     */
+    datum = (Datum *) palloc(numberOfAttributes * sizeof *datum);
+    nullv =  (char *)  palloc(numberOfAttributes * sizeof *nullv);
+    
+    /*
+     * If this is a predicate (partial) index, we will need to evaluate the
+     * predicate using ExecQual, which requires the current tuple to be in a
+     * slot of a TupleTable.  In addition, ExecQual must have an ExprContext
+     * referring to that slot.  Here, we initialize dummy TupleTable and
+     * ExprContext objects for this purpose. --Nels, Feb '92
+     */
+
+    predicate = predInfo->pred;
+    oldPred = predInfo->oldPred;
+
+#ifndef OMIT_PARTIAL_INDEX
+    if (predicate != NULL || oldPred != NULL) {
+       tupleTable = ExecCreateTupleTable(1);
+       slot = ExecAllocTableSlot(tupleTable);
+       econtext = makeNode(ExprContext);
+       FillDummyExprContext(econtext, slot, heapDescriptor, buffer);
+    }
+#endif /* OMIT_PARTIAL_INDEX */        
+
+    /* ----------------
+     * Ok, begin our scan of the base relation.
+     * ----------------
+     */
+    scan = heap_beginscan(heapRelation,        /* relation */
+                         0,                    /* start at end */
+                         NowTimeQual,          /* time range */
+                         0,                    /* number of keys */
+                         (ScanKey) NULL);      /* scan key */
+    
+    reltuples = indtuples = 0;
+    
+    /* ----------------
+     * for each tuple in the base relation, we create an index
+     *  tuple and add it to the index relation.  We keep a running
+     *  count of the number of tuples so that we can update pg_class
+     *  with correct statistics when we're done building the index.
+     * ----------------
+     */
+    while (heapTuple = heap_getnext(scan, 0, &buffer),
+          HeapTupleIsValid(heapTuple)) {
+       
+       reltuples++;
+       
+       /*
+        * If oldPred != NULL, this is an EXTEND INDEX command, so skip
+        * this tuple if it was already in the existing partial index
+        */
+       if (oldPred != NULL) {
+#ifndef OMIT_PARTIAL_INDEX
+           /*SetSlotContents(slot, heapTuple); */
+           slot->val = heapTuple;
+           if (ExecQual((List*)oldPred, econtext) == true) {
+               indtuples++;
+               continue;
+           }
+#endif /* OMIT_PARTIAL_INDEX */        
+       }
+       
+       /* Skip this tuple if it doesn't satisfy the partial-index predicate */
+       if (predicate != NULL) {
+#ifndef OMIT_PARTIAL_INDEX
+           /*SetSlotContents(slot, heapTuple); */
+           slot->val = heapTuple;
+           if (ExecQual((List*)predicate, econtext) == false)
+               continue;
+#endif /* OMIT_PARTIAL_INDEX */        
+       }
+       
+       indtuples++;
+       
+       /* ----------------
+        *  FormIndexDatum fills in its datum and null parameters
+        *  with attribute information taken from the given heap tuple.
+        * ----------------
+        */
+       FormIndexDatum(numberOfAttributes,  /* num attributes */
+                      attributeNumber,     /* array of att nums to extract */
+                      heapTuple,           /* tuple from base relation */
+                      heapDescriptor,      /* heap tuple's descriptor */
+                      buffer,              /* buffer used in the scan */
+                      datum,           /* return: array of attributes */
+                      nullv,           /* return: array of char's */
+                      funcInfo);
+       
+       indexTuple = index_formtuple(indexDescriptor,
+                                    datum,
+                                    nullv);
+       
+       indexTuple->t_tid = heapTuple->t_ctid;
+       
+       insertResult = index_insert(indexRelation, indexTuple);
+
+       if (insertResult) pfree(insertResult);
+       pfree(indexTuple);
+    }
+    
+    heap_endscan(scan);
+    
+    if (predicate != NULL || oldPred != NULL) {
+#ifndef OMIT_PARTIAL_INDEX
+       ExecDestroyTupleTable(tupleTable, false);
+#endif /* OMIT_PARTIAL_INDEX */        
+    }
+    
+    pfree(nullv);
+    pfree(datum);
+    
+    /*
+     *  Okay, now update the reltuples and relpages statistics for both
+     *  the heap relation and the index.  These statistics are used by
+     *  the planner to choose a scan type.  They are maintained generally
+     *  by the vacuum daemon, but we update them here to make the index
+     *  useful as soon as possible.
+     */
+    UpdateStats(heapRelation->rd_id, reltuples, true);
+    UpdateStats(indexRelation->rd_id, indtuples, false);
+    if (oldPred != NULL) {
+       if (indtuples == reltuples) predicate = NULL;
+       UpdateIndexPredicate(indexRelation->rd_id, oldPred, predicate);
+    }
+}
+
+/* ----------------
+ *     index_build
+ * ----------------
+ */
+void
+index_build(Relation heapRelation,
+           Relation indexRelation,
+           int numberOfAttributes,
+           AttrNumber attributeNumber[],
+           uint16      parameterCount,
+           Datum       parameter[],
+           FuncIndexInfo *funcInfo,
+           PredInfo *predInfo)
+{
+    RegProcedure       procedure;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(RelationIsValid(indexRelation));
+    Assert(PointerIsValid(indexRelation->rd_am));
+    
+    procedure = indexRelation->rd_am->ambuild;
+    
+    /* ----------------
+     * use the access method build procedure if supplied..
+     * ----------------
+     */
+    if (RegProcedureIsValid(procedure))
+       (void) fmgr(procedure,
+                   heapRelation,
+                   indexRelation,
+                   numberOfAttributes,
+                   attributeNumber,
+                   RelationGetIndexStrategy(indexRelation),
+                   parameterCount,
+                   parameter,
+                   funcInfo,
+                   predInfo);
+    else
+       DefaultBuild(heapRelation,
+                    indexRelation,
+                    numberOfAttributes,
+                    attributeNumber,
+                    RelationGetIndexStrategy(indexRelation),
+                    parameterCount,
+                    parameter,
+                    funcInfo,
+                    predInfo);
+}
+
+
diff --git a/src/backend/catalog/index.h b/src/backend/catalog/index.h
new file mode 100644 (file)
index 0000000..2d2258f
--- /dev/null
@@ -0,0 +1,59 @@
+/*-------------------------------------------------------------------------
+ *
+ * index.h--
+ *    prototypes for index.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        INDEX_H 
+#define INDEX_H
+
+#include "access/funcindex.h"
+#include "access/itup.h"
+#include "nodes/execnodes.h"
+
+
+extern Form_pg_am
+AccessMethodObjectIdGetAccessMethodTupleForm(Oid accessMethodObjectId);
+
+extern void
+UpdateIndexPredicate(Oid indexoid, Node *oldPred, Node *predicate);
+
+extern void InitIndexStrategy(int numatts,
+                             Relation indexRelation,
+                             Oid accessMethodObjectId);
+
+extern void index_create(char *heapRelationName, 
+                        char* indexRelationName,
+                        FuncIndexInfo *funcInfo, 
+                        Oid accessMethodObjectId,
+                        int numatts, 
+                        AttrNumber attNums[],
+                        Oid classObjectId[], 
+                        uint16 parameterCount,
+                        Datum parameter[], 
+                        Node *predicate);
+
+extern void index_destroy(Oid indexId);
+
+extern void FormIndexDatum(int numberOfAttributes,
+       AttrNumber attributeNumber[], HeapTuple heapTuple,
+       TupleDesc heapDescriptor, Buffer buffer, Datum *datum,
+       char *nullv, FuncIndexInfoPtr fInfo);
+
+extern void UpdateStats(Oid relid, long reltuples, bool hasindex);
+
+extern void FillDummyExprContext(ExprContext *econtext, TupleTableSlot *slot,
+                         TupleDesc tupdesc, Buffer buffer);
+
+extern void index_build(Relation heapRelation, Relation indexRelation,
+       int numberOfAttributes, AttrNumber attributeNumber[],
+       uint16 parameterCount, Datum parameter[], FuncIndexInfo *funcInfo,
+       PredInfo *predInfo);
+
+#endif /* INDEX_H */
diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c
new file mode 100644 (file)
index 0000000..74b7c89
--- /dev/null
@@ -0,0 +1,561 @@
+/*-------------------------------------------------------------------------
+ *
+ * indexing.c--
+ *    This file contains routines to support indices defined on system
+ *    catalogs.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "utils/builtins.h"
+#include "utils/rel.h"
+#include "utils/elog.h"
+#include "utils/oidcompos.h"
+#include "utils/palloc.h"
+#include "access/htup.h"
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/attnum.h"
+#include "access/funcindex.h"
+#include "access/skey.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "nodes/execnodes.h"
+#include "catalog/catalog.h"
+#include "catalog/catname.h"
+#include "catalog/pg_index.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_attribute.h"
+#include "utils/syscache.h"
+#include "catalog/indexing.h"
+#include "catalog/index.h"
+
+/*
+ * Names of indices on the following system catalogs:
+ *
+ *     pg_attribute
+ *     pg_proc
+ *     pg_type
+ *     pg_naming
+ *     pg_class
+ */
+/*
+static NameData        AttributeNameIndexData = { "pg_attnameind" };
+static NameData        AttributeNumIndexData  = { "pg_attnumind" };
+static NameData AttributeRelidIndexData= { "pg_attrelidind" };
+static NameData        ProcedureNameIndexData = { "pg_procnameind" };
+static NameData        ProcedureOidIndexData  = { "pg_procidind" };
+static NameData        ProcedureSrcIndexData  = { "pg_procsrcind" };
+static NameData        TypeNameIndexData      = { "pg_typenameind" };
+static NameData        TypeOidIndexData       = { "pg_typeidind" };
+static NameData ClassNameIndexData     = { "pg_classnameind" };
+static NameData ClassOidIndexData      = { "pg_classoidind" };
+
+Name   AttributeNameIndex = &AttributeNameIndexData;
+Name   AttributeNumIndex  = &AttributeNumIndexData;
+Name   AttributeRelidIndex= &AttributeRelidIndexData;
+Name   ProcedureNameIndex = &ProcedureNameIndexData;
+Name   ProcedureOidIndex  = &ProcedureOidIndexData;
+Name   ProcedureSrcIndex  = &ProcedureSrcIndexData;
+Name   TypeNameIndex      = &TypeNameIndexData;
+Name   TypeOidIndex       = &TypeOidIndexData;
+Name   ClassNameIndex     = &ClassNameIndexData;
+Name   ClassOidIndex      = &ClassOidIndexData;
+char *Name_pg_attr_indices[Num_pg_attr_indices] = {AttributeNameIndexData.data,
+                                                  AttributeNumIndexData.data,
+                                                  AttributeRelidIndexData.data};
+char *Name_pg_proc_indices[Num_pg_proc_indices] = {ProcedureNameIndexData.data,
+                                                  ProcedureOidIndexData.data,
+                                                  ProcedureSrcIndexData.data};char *Name_pg_type_indices[Num_pg_type_indices] = {TypeNameIndexData.data,
+                                                  TypeOidIndexData.data};
+char *Name_pg_class_indices[Num_pg_class_indices]= {ClassNameIndexData.data,
+                                                  ClassOidIndexData.data};
+*/
+
+char *Name_pg_attr_indices[Num_pg_attr_indices] = {AttributeNameIndex,
+                                                  AttributeNumIndex,
+                                                  AttributeRelidIndex};
+char *Name_pg_proc_indices[Num_pg_proc_indices] = { ProcedureNameIndex,
+                                                   ProcedureOidIndex,
+                                                   ProcedureSrcIndex};
+char *Name_pg_type_indices[Num_pg_type_indices] = { TypeNameIndex,
+                                                   TypeOidIndex};
+char *Name_pg_class_indices[Num_pg_class_indices]= { ClassNameIndex,
+                                                    ClassOidIndex};
+
+
+static HeapTuple CatalogIndexFetchTuple(Relation heapRelation,
+                                       Relation idesc,
+                                       ScanKey skey);
+
+
+/*
+ * Changes (appends) to catalogs can (and does) happen at various places
+ * throughout the code.  We need a generic routine that will open all of
+ * the indices defined on a given catalog a return the relation descriptors
+ * associated with them.
+ */
+void
+CatalogOpenIndices(int nIndices, char *names[], Relation idescs[])
+{
+    int i;
+    
+    for (i=0; i<nIndices; i++)
+       {
+           idescs[i] = index_openr(names[i]);
+       }
+}
+
+/*
+ * This is the inverse routine to CatalogOpenIndices()
+ */
+void
+CatalogCloseIndices(int nIndices, Relation *idescs)
+{
+    int i;
+    
+    for (i=0; i<nIndices; i++)
+       index_close(idescs[i]);
+}
+
+
+/*
+ * For the same reasons outlined above CatalogOpenIndices() we need a routine
+ * that takes a new catalog tuple and inserts an associated index tuple into 
+ * each catalog index.
+ */
+void
+CatalogIndexInsert(Relation *idescs,
+                  int nIndices,
+                  Relation heapRelation,
+                  HeapTuple heapTuple)
+{
+    HeapTuple pgIndexTup;
+    TupleDesc heapDescriptor;
+    IndexTupleForm pgIndexP;
+    IndexTuple newIndxTup;
+    Datum datum;
+    int natts;
+    AttrNumber *attnumP;
+    FuncIndexInfo finfo, *finfoP;
+    char nulls[INDEX_MAX_KEYS];
+    int i;
+    
+    heapDescriptor =  RelationGetTupleDescriptor(heapRelation);
+    
+    for (i=0; i<nIndices; i++) 
+       {
+           TupleDesc     indexDescriptor;
+           InsertIndexResult indexRes;
+           
+           indexDescriptor = RelationGetTupleDescriptor(idescs[i]);
+           pgIndexTup = SearchSysCacheTuple(INDEXRELID,
+                                            Int32GetDatum(idescs[i]->rd_id),
+                                            0,0,0);
+           Assert(pgIndexTup);
+           pgIndexP = (IndexTupleForm)GETSTRUCT(pgIndexTup);
+           
+           /*
+            * Compute the number of attributes we are indexing upon.
+            * very important - can't assume one if this is a functional
+            * index.
+            */
+           for (attnumP=(&pgIndexP->indkey[0]), natts=0;
+                *attnumP != InvalidAttrNumber;
+                attnumP++, natts++)
+               ;
+           
+           if (pgIndexP->indproc != InvalidOid)
+               {
+                   FIgetnArgs(&finfo) = natts;
+                   natts = 1;
+                   FIgetProcOid(&finfo) = pgIndexP->indproc;
+                   *(FIgetname(&finfo)) = '\0';
+                   finfoP = &finfo;
+               }
+           else
+               finfoP = (FuncIndexInfo *)NULL;
+           
+           FormIndexDatum(natts,
+                          (AttrNumber *)&pgIndexP->indkey[0],
+                          heapTuple,
+                          heapDescriptor,
+                          InvalidBuffer,
+                          &datum,
+                          nulls,
+                          finfoP);
+           
+           newIndxTup = (IndexTuple)index_formtuple(indexDescriptor,
+                                                    &datum,nulls);
+           Assert(newIndxTup);
+           /*
+            * Doing this structure assignment makes me quake in my boots when I 
+            * think about portability.
+            */
+           newIndxTup->t_tid = heapTuple->t_ctid;
+           
+           indexRes = index_insert(idescs[i], newIndxTup);
+           if (indexRes) pfree(indexRes);
+       }
+}
+
+/*
+ * This is needed at initialization when reldescs for some of the crucial
+ * system catalogs are created and nailed into the cache.
+ */
+bool
+CatalogHasIndex(char *catName, Oid catId)
+{
+    Relation    pg_class;
+    HeapTuple   htup;
+    Form_pg_class pgRelP;
+    int i;
+    
+    Assert(IsSystemRelationName(catName));
+    
+    /*
+     * If we're bootstraping we don't have pg_class (or any indices).
+     */
+    if (IsBootstrapProcessingMode())
+       return false;
+    
+    if (IsInitProcessingMode()) {
+       for (i = 0; IndexedCatalogNames[i] != NULL; i++) {
+           if ( strcmp(IndexedCatalogNames[i], catName) == 0)
+               return (true);
+       }
+       return (false);
+    }
+    
+    pg_class = heap_openr(RelationRelationName);
+    htup = ClassOidIndexScan(pg_class, catId);
+    heap_close(pg_class);
+    
+    if (! HeapTupleIsValid(htup)) {
+       elog(NOTICE, "CatalogHasIndex: no relation with oid %d", catId);
+       return false;
+    }
+    
+    pgRelP = (Form_pg_class)GETSTRUCT(htup);
+    return (pgRelP->relhasindex);
+}
+
+/*
+ *  CatalogIndexFetchTuple() -- Get a tuple that satisfies a scan key
+ *                             from a catalog relation.
+ *
+ *     Since the index may contain pointers to dead tuples, we need to
+ *     iterate until we find a tuple that's valid and satisfies the scan
+ *     key.
+ */
+static HeapTuple
+CatalogIndexFetchTuple(Relation heapRelation,
+                      Relation idesc,
+                      ScanKey skey)
+{
+    IndexScanDesc sd;
+    RetrieveIndexResult indexRes;
+    HeapTuple tuple;
+    Buffer buffer;
+    
+    sd = index_beginscan(idesc, false, 1, skey);
+    tuple = (HeapTuple)NULL;
+    
+    do {
+       indexRes = index_getnext(sd, ForwardScanDirection);
+       if (indexRes) {
+           ItemPointer iptr;
+           
+           iptr = &indexRes->heap_iptr;
+           tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer);
+           pfree(indexRes);
+       } else
+           break;
+    } while (!HeapTupleIsValid(tuple));
+    
+    if (HeapTupleIsValid(tuple)) {
+       tuple = heap_copytuple(tuple);
+       ReleaseBuffer(buffer);
+    }
+    
+    index_endscan(sd);
+    if (sd->opaque)
+       pfree(sd->opaque);
+    pfree(sd);
+    return (tuple);
+}
+
+/*
+ * The remainder of the file is for individual index scan routines.  Each
+ * index should be scanned according to how it was defined during bootstrap
+ * (that is, functional or normal) and what arguments the cache lookup
+ * requires.  Each routine returns the heap tuple that qualifies.
+ */
+HeapTuple
+AttributeNameIndexScan(Relation heapRelation,
+                      Oid relid,
+                      char *attname)
+{
+    Relation idesc;
+    ScanKeyData skey;
+    OidName keyarg;
+    HeapTuple tuple;
+    
+    keyarg = mkoidname(relid, attname);
+    ScanKeyEntryInitialize(&skey,
+                          (bits16)0x0,
+                          (AttrNumber)1,
+                          (RegProcedure)OidNameEqRegProcedure,
+                          (Datum)keyarg);
+    
+    idesc = index_openr(AttributeNameIndex);
+    tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
+    
+    index_close(idesc);
+    pfree(keyarg);
+    
+    return tuple;
+}
+
+HeapTuple
+AttributeNumIndexScan(Relation heapRelation,
+                     Oid relid,
+                     AttrNumber attnum)
+{
+    Relation idesc;
+    ScanKeyData skey;
+    OidInt2 keyarg;
+    HeapTuple tuple;
+    
+    keyarg = mkoidint2(relid, (uint16)attnum);
+    ScanKeyEntryInitialize(&skey,
+                          (bits16)0x0,
+                          (AttrNumber)1,
+                          (RegProcedure)OidInt2EqRegProcedure,
+                          (Datum)keyarg);
+    
+    idesc = index_openr(AttributeNumIndex);
+    tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
+    
+    index_close(idesc);
+    pfree(keyarg);
+    
+    return tuple;
+}
+
+HeapTuple
+ProcedureOidIndexScan(Relation heapRelation, Oid procId)
+{
+    Relation idesc;
+    ScanKeyData skey;
+    HeapTuple tuple;
+    
+    ScanKeyEntryInitialize(&skey,
+                          (bits16)0x0,
+                          (AttrNumber)1,
+                          (RegProcedure)ObjectIdEqualRegProcedure,
+                          (Datum)procId);
+    
+    idesc = index_openr(ProcedureOidIndex);
+    tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
+    
+    index_close(idesc);
+    
+    return tuple;
+}
+
+HeapTuple
+ProcedureNameIndexScan(Relation heapRelation,
+                      char *procName,
+                      int nargs,
+                      Oid *argTypes)
+{
+    Relation idesc;
+    ScanKeyData skey;
+    HeapTuple tuple;
+    IndexScanDesc sd;
+    RetrieveIndexResult indexRes;
+    Buffer buffer;
+    Form_pg_proc pgProcP;
+    bool bufferUsed = FALSE;
+    
+    ScanKeyEntryInitialize(&skey,
+                          (bits16)0x0,
+                          (AttrNumber)1,
+                          (RegProcedure)NameEqualRegProcedure,
+                          (Datum)procName);
+    
+    idesc = index_openr(ProcedureNameIndex);
+    
+    sd = index_beginscan(idesc, false, 1, &skey);
+    
+    /*
+     * for now, we do the work usually done by CatalogIndexFetchTuple
+     * by hand, so that we can check that the other keys match.  when
+     * multi-key indices are added, they will be used here.
+     */
+    do {  
+       tuple = (HeapTuple)NULL;
+       if (bufferUsed) {
+           ReleaseBuffer(buffer);
+           bufferUsed = FALSE;
+        }
+       
+       indexRes = index_getnext(sd, ForwardScanDirection);
+       if (indexRes) {
+           ItemPointer iptr;
+           
+           iptr = &indexRes->heap_iptr;
+           tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer);
+           pfree(indexRes);
+           if (HeapTupleIsValid(tuple)) {
+               pgProcP = (Form_pg_proc)GETSTRUCT(tuple);
+               bufferUsed = TRUE;
+           }
+       } else
+           break;
+    } while (!HeapTupleIsValid(tuple) ||
+            pgProcP->pronargs != nargs ||
+            !oid8eq(&(pgProcP->proargtypes[0]), argTypes));
+    
+    if (HeapTupleIsValid(tuple)) {
+       tuple = heap_copytuple(tuple);
+       ReleaseBuffer(buffer);
+    }
+    
+    index_endscan(sd);
+    index_close(idesc);
+    
+    return tuple;
+}
+
+HeapTuple
+ProcedureSrcIndexScan(Relation heapRelation, text *procSrc)
+{
+    Relation idesc;
+    IndexScanDesc sd;
+    ScanKeyData skey;
+    RetrieveIndexResult indexRes;
+    HeapTuple tuple;
+    Buffer buffer;
+    
+    ScanKeyEntryInitialize(&skey,
+                          (bits16)0x0,
+                          (AttrNumber)Anum_pg_proc_prosrc,
+                          (RegProcedure)TextEqualRegProcedure,
+                          (Datum)procSrc);
+    
+    idesc = index_openr(ProcedureSrcIndex);
+    sd = index_beginscan(idesc, false, 1, &skey);
+    
+    indexRes = index_getnext(sd, ForwardScanDirection);
+    if (indexRes) {
+       ItemPointer iptr;
+           
+       iptr = &indexRes->heap_iptr;
+       tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer);
+       pfree(indexRes);
+    } else
+       tuple = (HeapTuple)NULL;
+    
+    if (HeapTupleIsValid(tuple)) {
+       tuple = heap_copytuple(tuple);
+       ReleaseBuffer(buffer);
+    }
+    
+    index_endscan(sd);
+    
+    return tuple;
+}
+
+HeapTuple
+TypeOidIndexScan(Relation heapRelation, Oid typeId)
+{
+    Relation idesc;
+    ScanKeyData skey;
+    HeapTuple tuple;
+    
+    ScanKeyEntryInitialize(&skey,
+                          (bits16)0x0,
+                          (AttrNumber)1,
+                          (RegProcedure)ObjectIdEqualRegProcedure,
+                          (Datum)typeId);
+    
+    idesc = index_openr(TypeOidIndex);
+    tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
+    
+    index_close(idesc);
+    
+    return tuple;
+}
+
+HeapTuple
+TypeNameIndexScan(Relation heapRelation, char *typeName)
+{
+    Relation idesc;
+    ScanKeyData skey;
+    HeapTuple tuple;
+    
+    ScanKeyEntryInitialize(&skey,
+                          (bits16)0x0,
+                          (AttrNumber)1,
+                          (RegProcedure)NameEqualRegProcedure,
+                          (Datum)typeName);
+    
+    idesc = index_openr(TypeNameIndex);
+    tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
+    
+    index_close(idesc);
+    
+    return tuple;
+}
+
+HeapTuple
+ClassNameIndexScan(Relation heapRelation, char *relName)
+{
+    Relation idesc;
+    ScanKeyData skey;
+    HeapTuple tuple;
+    
+    ScanKeyEntryInitialize(&skey,
+                          (bits16)0x0,
+                          (AttrNumber)1,
+                          (RegProcedure)NameEqualRegProcedure,
+                          (Datum)relName);
+    
+    idesc = index_openr(ClassNameIndex);
+    
+    tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
+    
+    index_close(idesc);
+    return tuple;
+}
+
+HeapTuple
+ClassOidIndexScan(Relation heapRelation, Oid relId)
+{
+    Relation idesc;
+    ScanKeyData skey;
+    HeapTuple tuple;
+    
+    ScanKeyEntryInitialize(&skey,
+                          (bits16)0x0,
+                          (AttrNumber)1,
+                          (RegProcedure)ObjectIdEqualRegProcedure,
+                          (Datum)relId);
+    
+    idesc = index_openr(ClassOidIndex);
+    tuple = CatalogIndexFetchTuple(heapRelation, idesc, &skey);
+    
+    index_close(idesc);
+    
+    return tuple;
+}
diff --git a/src/backend/catalog/indexing.h b/src/backend/catalog/indexing.h
new file mode 100644 (file)
index 0000000..8e17313
--- /dev/null
@@ -0,0 +1,103 @@
+/*-------------------------------------------------------------------------
+ *
+ * indexing.h--
+ *    This include provides some definitions to support indexing 
+ *    on system catalogs
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef INDEXING_H
+#define INDEXING_H
+
+#include "utils/rel.h"
+
+/*
+ * Some definitions for indices on pg_attribute
+ */
+#define Num_pg_attr_indices    3
+#define Num_pg_proc_indices    3
+#define Num_pg_type_indices    2
+#define Num_pg_class_indices   2
+
+
+/*
+ * Names of indices on system catalogs
+ */
+#define AttributeNameIndex "pg_attnameind"
+#define AttributeNumIndex  "pg_attnumind"
+#define AttributeRelidIndex "pg_attrelidind"
+#define ProcedureNameIndex "pg_procnameind"
+#define ProcedureOidIndex  "pg_procidind"
+#define ProcedureSrcIndex  "pg_procsrcind"
+#define TypeNameIndex      "pg_typenameind"
+#define TypeOidIndex       "pg_typeidind"
+#define ClassNameIndex     "pg_classnameind"
+#define ClassOidIndex      "pg_classoidind"
+
+extern char *Name_pg_attr_indices[];
+extern char *Name_pg_proc_indices[];
+extern char *Name_pg_type_indices[];
+extern char *Name_pg_class_indices[];
+
+extern char *IndexedCatalogNames[];
+
+/*
+ * indexing.c prototypes 
+ *
+ * Functions for each index to perform the necessary scan on a cache miss.
+ */
+extern void CatalogOpenIndices(int nIndices, char *names[], Relation idescs[]);
+extern void CatalogCloseIndices(int nIndices, Relation *idescs);
+extern void CatalogIndexInsert(Relation *idescs,
+                              int nIndices,
+                              Relation heapRelation,
+                              HeapTuple heapTuple);
+extern bool CatalogHasIndex(char *catName, Oid catId);
+
+extern HeapTuple AttributeNameIndexScan(Relation heapRelation,
+                                       Oid relid,
+                                       char *attname);
+
+extern HeapTuple AttributeNumIndexScan(Relation heapRelation,
+                                      Oid relid,
+                                      AttrNumber attnum);
+extern HeapTuple ProcedureOidIndexScan(Relation heapRelation, Oid procId);
+extern HeapTuple ProcedureNameIndexScan(Relation heapRelation,
+       char *procName, int nargs, Oid *argTypes);
+extern HeapTuple ProcedureSrcIndexScan(Relation heapRelation, text *procSrc);
+extern HeapTuple TypeOidIndexScan(Relation heapRelation, Oid typeId);
+extern HeapTuple TypeNameIndexScan(Relation heapRelation, char *typeName);
+extern HeapTuple ClassNameIndexScan(Relation heapRelation, char *relName);
+extern HeapTuple ClassOidIndexScan(Relation heapRelation, Oid relId);
+
+
+/*
+ * What follows are lines processed by genbki.sh to create the statements
+ * the bootstrap parser will turn into DefineIndex commands.
+ *
+ * The keyword is DECLARE_INDEX every thing after that is just like in a
+ * normal specification of the 'define index' POSTQUEL command.
+ */
+DECLARE_INDEX(pg_attnameind on pg_attribute using btree (mkoidname(attrelid, attname) oidname_ops));
+DECLARE_INDEX(pg_attnumind  on pg_attribute using btree (mkoidint2(attrelid, attnum) oidint2_ops));
+DECLARE_INDEX(pg_attrelidind on pg_attribute using btree (attrelid oid_ops));
+
+DECLARE_INDEX(pg_procidind on pg_proc using btree (Oid oid_ops));
+DECLARE_INDEX(pg_procnameind on pg_proc using btree (proname name_ops));
+DECLARE_INDEX(pg_procsrcind on pg_proc using btree (prosrc text_ops));
+
+DECLARE_INDEX(pg_typeidind on pg_type using btree (Oid oid_ops));
+DECLARE_INDEX(pg_typenameind on pg_type using btree (typname name_ops));
+
+DECLARE_INDEX(pg_classnameind on pg_class using btree (relname name_ops));
+DECLARE_INDEX(pg_classoidind on pg_class using btree (Oid oid_ops));
+
+/* now build indices in the initialization scripts */
+BUILD_INDICES
+
+#endif /* INDEXING_H */
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
new file mode 100644 (file)
index 0000000..a0d7ffc
--- /dev/null
@@ -0,0 +1,325 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_aggregate.c--
+ *    routines to support manipulation of the pg_aggregate relation
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "access/htup.h"
+#include "access/tupdesc.h"
+#include "utils/rel.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/builtins.h"
+#include "fmgr.h"
+
+#include "catalog/catname.h"
+#include "utils/syscache.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_aggregate.h"
+
+/* ----------------
+ * AggregateCreate
+ *
+ * aggregates overloading has been added.  Instead of the full
+ * overload support we have for functions, aggregate overloading only
+ * applies to exact basetype matches.  That is, we don't check the 
+ * the inheritance hierarchy
+ *
+ * OLD COMMENTS:
+ *     Currently, redefining aggregates using the same name is not
+ *     supported.  In such a case, a warning is printed that the 
+ *     aggregate already exists.  If such is not the case, a new tuple
+ *     is created and inserted in the aggregate relation.  The fields
+ *     of this tuple are aggregate name, owner id, 2 transition functions
+ *     (called aggtransfn1 and aggtransfn2), final function (aggfinalfn),
+ *     type of data on which aggtransfn1 operates (aggbasetype), return
+ *     types of the two transition functions (aggtranstype1 and 
+ *     aggtranstype2), final return type (aggfinaltype), and initial values
+ *     for the two state transition functions (agginitval1 and agginitval2).
+ *     All types and functions must have been defined
+ *     prior to defining the aggregate.
+ * 
+ * ---------------
+ */
+void
+AggregateCreate(char *aggName,
+               char *aggtransfn1Name,
+               char *aggtransfn2Name,
+               char *aggfinalfnName, 
+               char *aggbasetypeName,
+               char *aggtransfn1typeName,
+               char *aggtransfn2typeName,
+               char *agginitval1,
+               char *agginitval2)
+{
+    register           i;
+    Relation           aggdesc;
+    HeapTuple          tup;
+    char               nulls[Natts_pg_aggregate];
+    Datum               values[Natts_pg_aggregate];
+    Form_pg_proc       proc;
+    Oid                xfn1 = InvalidOid;
+    Oid                xfn2 = InvalidOid;
+    Oid                ffn = InvalidOid;
+    Oid                xbase = InvalidOid;
+    Oid                xret1 = InvalidOid;
+    Oid                xret2 = InvalidOid;
+    Oid                fret = InvalidOid;
+    Oid                fnArgs[8];
+    TupleDesc    tupDesc;
+    
+    memset(fnArgs, 0, 8 * sizeof(Oid)); 
+    
+    /* sanity checks */
+    if (!aggName)
+       elog(WARN, "AggregateCreate: no aggregate name supplied");
+    
+    if (!aggtransfn1Name && !aggtransfn2Name)
+       elog(WARN, "AggregateCreate: aggregate must have at least one transition function");
+    
+    tup = SearchSysCacheTuple(TYPNAME, 
+                             PointerGetDatum(aggbasetypeName),
+                             0,0,0);
+    if(!HeapTupleIsValid(tup))
+       elog(WARN, "AggregateCreate: Type '%s' undefined",aggbasetypeName);
+    xbase = tup->t_oid;
+
+    if (aggtransfn1Name) {
+       tup = SearchSysCacheTuple(TYPNAME, 
+                                 PointerGetDatum(aggtransfn1typeName),
+                                 0,0,0);
+       if(!HeapTupleIsValid(tup))
+           elog(WARN, "AggregateCreate: Type '%s' undefined",
+                aggtransfn1typeName);
+       xret1 = tup->t_oid;
+       
+       fnArgs[0] = xret1;
+       fnArgs[1] = xbase;
+       tup = SearchSysCacheTuple(PRONAME,
+                                 PointerGetDatum(aggtransfn1Name),
+                                 Int32GetDatum(2),
+                                 PointerGetDatum(fnArgs),
+                                 0);
+       if(!HeapTupleIsValid(tup))
+           elog(WARN, "AggregateCreate: '%s('%s', '%s') does not exist",
+                aggtransfn1Name,  aggtransfn1typeName, aggbasetypeName);
+       if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret1)
+           elog(WARN, "AggregateCreate: return type of '%s' is not '%s'",
+                aggtransfn1Name,
+                aggtransfn1typeName);
+       xfn1 = tup->t_oid;
+       if (!OidIsValid(xfn1) || !OidIsValid(xret1) ||
+           !OidIsValid(xbase))
+           elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName);
+    }
+    
+    if (aggtransfn2Name) {
+       tup = SearchSysCacheTuple(TYPNAME, 
+                                 PointerGetDatum(aggtransfn2typeName),
+                                 0,0,0);
+       if(!HeapTupleIsValid(tup))
+           elog(WARN, "AggregateCreate: Type '%s' undefined",
+                aggtransfn2typeName);
+       xret2 = tup->t_oid;
+       
+       fnArgs[0] = xret2;
+       fnArgs[1] = 0;
+       tup = SearchSysCacheTuple(PRONAME, 
+                                 PointerGetDatum(aggtransfn2Name),
+                                 Int32GetDatum(1),
+                                 PointerGetDatum(fnArgs),
+                                 0);
+       if(!HeapTupleIsValid(tup))
+           elog(WARN, "AggregateCreate: '%s'('%s') does not exist",
+                aggtransfn2Name, aggtransfn2typeName);
+       if (((Form_pg_proc) GETSTRUCT(tup))->prorettype != xret2)
+           elog(WARN, "AggregateCreate: return type of '%s' is not '%s'",
+                aggtransfn2Name, aggtransfn2typeName);
+       xfn2 = tup->t_oid;
+       if (!OidIsValid(xfn2) || !OidIsValid(xret2))
+           elog(WARN, "AggregateCreate: bogus function '%s'",aggfinalfnName);
+    }
+    
+    tup = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggName),
+                             ObjectIdGetDatum(xbase),  
+                             0,0);
+    if (HeapTupleIsValid(tup))
+       elog(WARN, 
+            "AggregateCreate: aggregate '%s' with base type '%s' already exists",
+            aggName, aggbasetypeName);
+
+    /* more sanity checks */
+    if (aggtransfn1Name && aggtransfn2Name && !aggfinalfnName)
+       elog(WARN, "AggregateCreate: Aggregate must have final function with both transition functions");
+    
+    if ((!aggtransfn1Name || !aggtransfn2Name) && aggfinalfnName)
+       elog(WARN, "AggregateCreate: Aggregate cannot have final function without both transition functions");
+    
+    if (aggfinalfnName) {
+        fnArgs[0] = xret1;
+       fnArgs[1] = xret2;
+       tup = SearchSysCacheTuple(PRONAME,
+                                 PointerGetDatum(aggfinalfnName),
+                                 Int32GetDatum(2),
+                                 PointerGetDatum(fnArgs),
+                                 0);
+       if(!HeapTupleIsValid(tup))
+           elog(WARN, "AggregateCreate: '%s'('%s','%s') does not exist",
+                aggfinalfnName, aggtransfn1typeName, aggtransfn2typeName);
+       ffn = tup->t_oid;
+       proc = (Form_pg_proc) GETSTRUCT(tup);
+       fret = proc->prorettype;
+       if (!OidIsValid(ffn) || !OidIsValid(fret))
+           elog(WARN, "AggregateCreate: bogus function '%s'", aggfinalfnName);
+    }
+    
+    /*
+     * If transition function 2 is defined, it must have an initial value,
+     * whereas transition function 1 does not, which allows man and min
+     * aggregates to return NULL if they are evaluated on empty sets.
+     */
+    if (OidIsValid(xfn2) && !agginitval2)
+       elog(WARN, "AggregateCreate: transition function 2 MUST have an initial value");
+    
+    /* initialize nulls and values */
+    for(i=0; i < Natts_pg_aggregate; i++) {
+       nulls[i] = ' ';
+       values[i] = (Datum)NULL;
+    }
+    values[Anum_pg_aggregate_aggname-1] = PointerGetDatum(aggName);
+    values[Anum_pg_aggregate_aggowner-1] =
+       Int32GetDatum(GetUserId());
+    values[Anum_pg_aggregate_aggtransfn1-1] =
+       ObjectIdGetDatum(xfn1);
+    values[Anum_pg_aggregate_aggtransfn2-1] =
+       ObjectIdGetDatum(xfn2);
+    values[Anum_pg_aggregate_aggfinalfn-1] =
+       ObjectIdGetDatum(ffn);
+    
+    values[Anum_pg_aggregate_aggbasetype-1] =
+       ObjectIdGetDatum(xbase);
+    if (!OidIsValid(xfn1)) {
+       values[Anum_pg_aggregate_aggtranstype1-1] =
+           ObjectIdGetDatum(InvalidOid);
+       values[Anum_pg_aggregate_aggtranstype2-1] =
+           ObjectIdGetDatum(xret2);
+       values[Anum_pg_aggregate_aggfinaltype-1] =
+           ObjectIdGetDatum(xret2);
+    }
+    else if (!OidIsValid(xfn2)) {
+       values[Anum_pg_aggregate_aggtranstype1-1] =
+           ObjectIdGetDatum(xret1);
+       values[Anum_pg_aggregate_aggtranstype2-1] =
+           ObjectIdGetDatum(InvalidOid);
+       values[Anum_pg_aggregate_aggfinaltype-1] =
+           ObjectIdGetDatum(xret1);
+    }
+    else {
+       values[Anum_pg_aggregate_aggtranstype1-1] =
+           ObjectIdGetDatum(xret1);
+       values[Anum_pg_aggregate_aggtranstype2-1] =
+           ObjectIdGetDatum(xret2);
+       values[Anum_pg_aggregate_aggfinaltype-1] =
+           ObjectIdGetDatum(fret);
+    }
+    
+    if (agginitval1)
+       values[Anum_pg_aggregate_agginitval1-1] = PointerGetDatum(textin(agginitval1));
+    else
+       nulls[Anum_pg_aggregate_agginitval1-1] = 'n';
+    
+    if (agginitval2)
+       values[Anum_pg_aggregate_agginitval2-1] = PointerGetDatum(textin(agginitval2));
+    else
+       nulls[Anum_pg_aggregate_agginitval2-1] = 'n';
+    
+    if (!RelationIsValid(aggdesc = heap_openr(AggregateRelationName)))
+       elog(WARN, "AggregateCreate: could not open '%s'",
+            AggregateRelationName);
+
+    tupDesc = aggdesc->rd_att;
+    if (!HeapTupleIsValid(tup = heap_formtuple(tupDesc,
+                                              values,
+                                              nulls)))
+       elog(WARN, "AggregateCreate: heap_formtuple failed");
+    if (!OidIsValid(heap_insert(aggdesc, tup)))
+       elog(WARN, "AggregateCreate: heap_insert failed");
+    heap_close(aggdesc);
+
+}
+
+char *
+AggNameGetInitVal(char *aggName, Oid basetype, int xfuncno, bool *isNull)
+{
+    HeapTuple  tup;
+    Relation   aggRel;
+    int                initValAttno;
+    Oid        transtype;
+    text       *textInitVal;
+    char       *strInitVal, *initVal;
+    extern char        *textout();
+    
+    Assert(PointerIsValid(aggName));
+    Assert(PointerIsValid(isNull));
+    Assert(xfuncno == 1 || xfuncno == 2);
+
+    tup = SearchSysCacheTuple(AGGNAME, 
+                             PointerGetDatum(aggName),
+                             PointerGetDatum(basetype),
+                             0,0);
+    if (!HeapTupleIsValid(tup))
+       elog(WARN, "AggNameGetInitVal: cache lookup failed for aggregate '%s'",
+            aggName);
+    if (xfuncno == 1) {
+       transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype1;
+       initValAttno = Anum_pg_aggregate_agginitval1;
+    }
+    else if (xfuncno == 2) {
+       transtype = ((Form_pg_aggregate) GETSTRUCT(tup))->aggtranstype2;
+       initValAttno = Anum_pg_aggregate_agginitval2;
+    }
+    
+    aggRel = heap_openr(AggregateRelationName);
+    if (!RelationIsValid(aggRel))
+       elog(WARN, "AggNameGetInitVal: could not open \"%-.*s\"",
+            AggregateRelationName);
+    /* 
+     * must use fastgetattr in case one or other of the init values is NULL
+     */
+    textInitVal = (text *) fastgetattr(tup, initValAttno, 
+                                      RelationGetTupleDescriptor(aggRel),
+                                      isNull);
+    if (!PointerIsValid(textInitVal))
+       *isNull = true;
+    if (*isNull) {
+       heap_close(aggRel);
+       return((char *) NULL);
+    }
+    strInitVal = textout(textInitVal);
+    heap_close(aggRel);
+    
+    tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(transtype),
+                             0,0,0);
+    if (!HeapTupleIsValid(tup)) {
+       pfree(strInitVal);
+       elog(WARN, "AggNameGetInitVal: cache lookup failed on aggregate transition function return type");
+    }
+    initVal = fmgr(((TypeTupleForm) GETSTRUCT(tup))->typinput, strInitVal, -1);
+    pfree(strInitVal);
+    return(initVal);
+}
diff --git a/src/backend/catalog/pg_aggregate.h b/src/backend/catalog/pg_aggregate.h
new file mode 100644 (file)
index 0000000..e55af40
--- /dev/null
@@ -0,0 +1,132 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_aggregate.h--
+ *    definition of the system "aggregate" relation (pg_aggregate)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_AGGREGATE_H
+#define PG_AGGREGATE_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------------------------------------------------------
+ *     pg_aggregate definition.
+ *
+ *     cpp turns this into typedef struct FormData_pg_aggregate
+ *
+ *  aggname            name of the aggregate
+ *  aggtransfn1                transition function 1
+ *  aggtransfn2                transition function 2
+ *  aggfinalfn         final function
+ *  aggbasetype                type of data on which aggregate operates
+ *  aggtranstype1      output types for xition func 1
+ *  aggtranstype2      output types for xition func 2
+ *  aggfinaltype       output type for final func
+ *  agginitval1                initial aggregate value
+ *  agginitval2                initial value for transition state 2
+ * ----------------------------------------------------------------
+ */ 
+CATALOG(pg_aggregate) {
+    NameData           aggname;
+    Oid                        aggowner;
+    regproc            aggtransfn1;
+    regproc            aggtransfn2;
+    regproc            aggfinalfn;
+    Oid                        aggbasetype;
+    Oid                        aggtranstype1;
+    Oid                        aggtranstype2;
+    Oid                        aggfinaltype;
+    text               agginitval1;    /* VARIABLE LENGTH FIELD */
+    text               agginitval2;    /* VARIABLE LENGTH FIELD */
+} FormData_pg_aggregate;
+
+/* ----------------
+ *     Form_pg_aggregate corresponds to a pointer to a tuple with
+ *     the format of pg_aggregate relation.
+ * ----------------
+ */
+typedef FormData_pg_aggregate  *Form_pg_aggregate;
+
+/* ----------------
+ *     compiler constants for pg_aggregate
+ * ----------------
+ */
+
+#define Natts_pg_aggregate             11
+#define Anum_pg_aggregate_aggname      1
+#define Anum_pg_aggregate_aggowner     2
+#define Anum_pg_aggregate_aggtransfn1  3
+#define Anum_pg_aggregate_aggtransfn2  4
+#define Anum_pg_aggregate_aggfinalfn   5
+#define Anum_pg_aggregate_aggbasetype  6
+#define Anum_pg_aggregate_aggtranstype1        7
+#define Anum_pg_aggregate_aggtranstype2        8
+#define Anum_pg_aggregate_aggfinaltype 9
+#define Anum_pg_aggregate_agginitval1  10
+#define Anum_pg_aggregate_agginitval2  11
+
+
+/* ----------------
+ * initial contents of pg_aggregate
+ * ---------------
+ */
+
+DATA(insert OID = 0 ( avg   PGUID int4pl  int4inc  int4div  23  23  23  23 0  0 ));
+DATA(insert OID = 0 ( avg   PGUID int2pl  int2inc  int2div  21  21  21  21  0  0 ));
+DATA(insert OID = 0 ( avg PGUID float4pl float4inc float4div  700  700  700  700 0.0 0.0 ));
+DATA(insert OID = 0 ( avg PGUID float8pl float8inc float8div  701  701  701  701 0.0 0.0 ));
+
+DATA(insert OID = 0 ( sum   PGUID int4pl   - -  23  23  0  23  0   _null_ ));
+DATA(insert OID = 0 ( sum   PGUID int2pl   - -  21  21  0  21  0   _null_ ));
+DATA(insert OID = 0 ( sum PGUID float4pl - - 700  700 0  700  0.0 _null_ ));
+DATA(insert OID = 0 ( sum PGUID float8pl - - 701  701 0  701  0.0 _null_ ));
+
+DATA(insert OID = 0 ( max   PGUID int4larger   - -  23  23  0  23  _null_ _null_ ));
+DATA(insert OID = 0 ( max   PGUID int2larger   - -  21  21  0  21  _null_ _null_ ));
+DATA(insert OID = 0 ( max PGUID float4larger - - 700  700 0  700  _null_ _null_ ));
+DATA(insert OID = 0 ( max PGUID float8larger - - 701  701 0  701  _null_ _null_ ));
+
+DATA(insert OID = 0 ( min   PGUID int4smaller   - -  23  23  0  23  _null_ _null_ ));
+DATA(insert OID = 0 ( min   PGUID int2smaller   - -  21  21  0  21    _null_ _null_ ));
+DATA(insert OID = 0 ( min PGUID float4smaller - - 700  700 0  700   _null_ _null_ ));
+DATA(insert OID = 0 ( min PGUID float8smaller - - 701  701 0  701  _null_ _null_ ));
+
+DATA(insert OID = 0 ( count     PGUID - int4inc - 0 0 23 23  _null_ 0 ));
+
+/*
+ * prototypes for fucnctions in pg_aggregate.c
+ */
+extern void AggregateCreate(char *aggName, 
+                           char *aggtransfn1Name,
+                           char *aggtransfn2Name,
+                           char *aggfinalfnName,
+                           char *aggbasetypeName,
+                           char *aggtransfn1typeName,
+                           char *aggtransfn2typeName,
+                           char *agginitval1,
+                           char *agginitval2);
+extern char *AggNameGetInitVal(char *aggName, Oid basetype, 
+                              int xfuncno, bool *isNull);
+
+#endif /* PG_AGGREGATE_H */
+
+
+
+
diff --git a/src/backend/catalog/pg_am.h b/src/backend/catalog/pg_am.h
new file mode 100644 (file)
index 0000000..a1c3125
--- /dev/null
@@ -0,0 +1,115 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_am.h--
+ *    definition of the system "am" relation (pg_am)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *     the genbki.sh script reads this file and generates .bki
+ *     information from the DATA() statements.
+ *
+ *     XXX do NOT break up DATA() statements into multiple lines!
+ *         the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_AM_H
+#define PG_AM_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_am definition.  cpp turns this into
+ *     typedef struct FormData_pg_am
+ * ----------------
+ */ 
+CATALOG(pg_am) {
+    NameData   amname;
+    Oid        amowner;
+    char       amkind;
+    int2       amstrategies;
+    int2       amsupport;
+    regproc    amgettuple;
+    regproc    aminsert;
+    regproc    amdelete;
+    regproc    amgetattr;
+    regproc    amsetlock;
+    regproc    amsettid;
+    regproc    amfreetuple;
+    regproc    ambeginscan;
+    regproc    amrescan;
+    regproc    amendscan;
+    regproc    ammarkpos;
+    regproc    amrestrpos;
+    regproc    amopen;
+    regproc    amclose;
+    regproc    ambuild;
+    regproc    amcreate; 
+    regproc    amdestroy;
+} FormData_pg_am;
+
+/* ----------------
+ *     Form_pg_am corresponds to a pointer to a tuple with
+ *     the format of pg_am relation.
+ * ----------------
+ */
+typedef FormData_pg_am *Form_pg_am;
+
+/* ----------------
+ *     compiler constants for pg_am
+ * ----------------
+ */
+#define Natts_pg_am                    22
+#define Anum_pg_am_amname              1
+#define Anum_pg_am_amowner             2
+#define Anum_pg_am_amkind              3
+#define Anum_pg_am_amstrategies                4
+#define Anum_pg_am_amsupport           5
+#define Anum_pg_am_amgettuple          6
+#define Anum_pg_am_aminsert            7
+#define Anum_pg_am_amdelete            8
+#define Anum_pg_am_amgetattr           9
+#define Anum_pg_am_amsetlock           10
+#define Anum_pg_am_amsettid            11
+#define Anum_pg_am_amfreetuple         12
+#define Anum_pg_am_ambeginscan         13
+#define Anum_pg_am_amrescan            14
+#define Anum_pg_am_amendscan           15
+#define Anum_pg_am_ammarkpos           16
+#define Anum_pg_am_amrestrpos          17
+#define Anum_pg_am_amopen              18
+#define Anum_pg_am_amclose             19
+#define Anum_pg_am_ambuild             20
+#define Anum_pg_am_amcreate            21
+#define Anum_pg_am_amdestroy           22
+
+/* ----------------
+ *     initial contents of pg_am
+ * ----------------
+ */
+
+DATA(insert OID = 405 (  hash PGUID "o"  1 1 hashgettuple hashinsert hashdelete - - - - hashbeginscan hashrescan hashendscan hashmarkpos hashrestrpos - - hashbuild - - ));
+DATA(insert OID = 402 (  rtree PGUID "o" 8 3 rtgettuple rtinsert rtdelete - - - - rtbeginscan rtrescan rtendscan rtmarkpos rtrestrpos - - rtbuild - - ));
+DATA(insert OID = 403 (  btree PGUID "o" 5 1 btgettuple btinsert btdelete - - - - btbeginscan btrescan btendscan btmarkpos btrestrpos - - btbuild - - ));
+#define BTREE_AM_OID 403
+
+BKI_BEGIN
+#ifdef NOBTREE
+BKI_END
+DATA(insert OID = 404 (  nobtree PGUID "o" 5 1 nobtgettuple nobtinsert nobtdelete - - - - nobtbeginscan nobtrescan nobtendscan nobtmarkpos nobtrestrpos - - nobtbuild - - ));
+BKI_BEGIN
+#endif /* NOBTREE */
+BKI_END
+
+#endif /* PG_AM_H */
diff --git a/src/backend/catalog/pg_amop.h b/src/backend/catalog/pg_amop.h
new file mode 100644 (file)
index 0000000..01a5571
--- /dev/null
@@ -0,0 +1,546 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_amop.h--
+ *    definition of the system "amop" relation (pg_amop)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *   the genbki.sh script reads this file and generates .bki
+ *   information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_AMOP_H
+#define PG_AMOP_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+#include "access/istrat.h"
+
+/* ----------------
+ *     pg_amop definition.  cpp turns this into
+ *     typedef struct FormData_pg_amop
+ * ----------------
+ */ 
+CATALOG(pg_amop) {
+    Oid        amopid;
+    Oid        amopclaid;
+    Oid        amopopr;
+    int2       amopstrategy;
+    regproc    amopselect;
+    regproc    amopnpages;  
+} FormData_pg_amop;
+
+/* ----------------
+ *     Form_pg_amop corresponds to a pointer to a tuple with
+ *     the format of pg_amop relation.
+ * ----------------
+ */
+typedef FormData_pg_amop       *Form_pg_amop;
+
+/* ----------------
+ *     compiler constants for pg_amop
+ * ----------------
+ */
+/* #define Name_pg_amop                        "pg_amop" */
+#define Natts_pg_amop                  6
+#define Anum_pg_amop_amopid            1
+#define Anum_pg_amop_amopclaid                 2
+#define Anum_pg_amop_amopopr           3
+#define Anum_pg_amop_amopstrategy      4
+#define Anum_pg_amop_amopselect                5
+#define Anum_pg_amop_amopnpages                6
+
+/* ----------------
+ *     initial contents of pg_amop
+ * ----------------
+ */
+
+/*
+ *  rtree box_ops
+ */
+
+DATA(insert OID = 0 (  402 422 493 1 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 422 494 2 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 422 500 3 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 422 495 4 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 422 496 5 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 422 499 6 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 422 498 7 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 422 497 8 rtsel rtnpage ));
+
+/*
+ *  rtree bigbox_ops
+ */
+
+DATA(insert OID = 0 (  402 433 493 1 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 433 494 2 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 433 500 3 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 433 495 4 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 433 496 5 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 433 499 6 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 433 498 7 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 433 497 8 rtsel rtnpage ));
+
+/*
+ *  rtree poly_ops (supports polygons)
+ */
+
+DATA(insert OID = 0 (  402 434 485 1 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 434 486 2 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 434 487 3 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 434 488 4 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 434 489 5 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 434 490 6 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 434 491 7 rtsel rtnpage ));
+DATA(insert OID = 0 (  402 434 492 8 rtsel rtnpage ));
+
+/*
+ *  nbtree int2_ops
+ */
+
+DATA(insert OID = 0 (  403 421  95 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 421 522 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 421  94 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 421 524 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 421 520 5 btreesel btreenpage ));
+
+/*
+ *  nbtree float8_ops
+ */
+
+DATA(insert OID = 0 (  403 423 672 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 423 673 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 423 670 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 423 675 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 423 674 5 btreesel btreenpage ));
+
+/*
+ *  nbtree int24_ops
+ */
+
+DATA(insert OID = 0 (  403 424 534 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 424 540 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 424 532 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 424 542 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 424 536 5 btreesel btreenpage ));
+
+/*
+ *  nbtree int42_ops
+ */
+
+DATA(insert OID = 0 (  403 425 535 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 425 541 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 425 533 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 425 543 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 425 537 5 btreesel btreenpage ));
+
+/*
+ *  nbtree int4_ops
+ */
+
+DATA(insert OID = 0 (  403 426  97 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 426 523 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 426  96 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 426 525 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 426 521 5 btreesel btreenpage ));
+
+/*
+ *  nbtree oid_ops
+ */
+
+DATA(insert OID = 0 (  403 427 609 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 427 611 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 427 607 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 427 612 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 427 610 5 btreesel btreenpage ));
+
+/*
+ *  nbtree float4_ops
+ */
+
+DATA(insert OID = 0 (  403 428 622 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 428 624 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 428 620 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 428 625 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 428 623 5 btreesel btreenpage ));
+
+/*
+ *  nbtree char_ops
+ */
+
+DATA(insert OID = 0 (  403 429 631 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 429 632 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 429 92 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 429 634 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 429 633 5 btreesel btreenpage ));
+
+/*
+ *  nbtree char2_ops
+ */
+
+DATA(insert OID = 0 (  403 406 418 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 406 457 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 406 412 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 406 463 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 406 460 5 btreesel btreenpage ));
+
+/*
+ *  nbtree char4_ops
+ */
+
+DATA(insert OID = 0 (  403 407 419 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 407 458 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 407 413 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 407 464 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 407 461 5 btreesel btreenpage ));
+
+/*
+ *  nbtree char8_ops
+ */
+
+DATA(insert OID = 0 (  403 408 420 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 408 459 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 408 414 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 408 465 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 408 462 5 btreesel btreenpage ));
+
+/*
+ *  nbtree name_ops
+ */
+
+DATA(insert OID = 0 (  403 409 660 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 409 661 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 409 93 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 409 663 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 409 662 5 btreesel btreenpage ));
+
+/*
+ *  nbtree char16_ops
+ */
+
+DATA(insert OID = 0 (  403 430 645 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 430 646 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 430 99 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 430 648 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 430 647 5 btreesel btreenpage ));
+
+/*
+ *  nbtree text_ops
+ */
+
+DATA(insert OID = 0 (  403 431 664 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 431 665 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 431 98 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 431 667 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 431 666 5 btreesel btreenpage ));
+
+/*
+ *  nbtree abstime_ops
+ */
+
+DATA(insert OID = 0 (  403 432 562 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 432 564 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 432 560 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 432 565 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 432 563 5 btreesel btreenpage ));
+
+/*
+ *  nbtree oidint4_ops
+ */
+
+DATA(insert OID = 0 (  403 435 930 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 435 931 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 435 932 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 435 933 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 435 934 5 btreesel btreenpage ));
+
+/*
+ *  nbtree oidint2_ops
+ */
+
+DATA(insert OID = 0 (  403 437 830 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 437 831 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 437 832 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 437 833 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 437 834 5 btreesel btreenpage ));
+
+/*
+ *  nbtree oidname_ops
+ */
+
+DATA(insert OID = 0 (  403 436 676 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 436 677 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 436 678 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 436 679 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 436 680 5 btreesel btreenpage ));
+
+/*
+ *  nbtree bpchar_ops
+ */
+
+DATA(insert OID = 0 (  403 1076 1058 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 1076 1059 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 1076 1054 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 1076 1061 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 1076 1060 5 btreesel btreenpage ));
+
+/*
+ *  nbtree varchar_ops
+ */
+
+DATA(insert OID = 0 (  403 1077 1066 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 1077 1067 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 1077 1062 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 1077 1069 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 1077 1068 5 btreesel btreenpage ));
+
+/*
+ *  nbtree date_ops
+ */
+
+DATA(insert OID = 0 (  403 1114 1095 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 1114 1096 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 1114 1093 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 1114 1098 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 1114 1097 5 btreesel btreenpage ));
+
+
+/*
+ *  nbtree time_ops
+ */
+
+DATA(insert OID = 0 (  403 1115 1110 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 1115 1111 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 1115 1108 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 1115 1113 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  403 1115 1112 5 btreesel btreenpage ));
+
+BKI_BEGIN
+#ifdef NOBTREE
+BKI_END
+/*
+ *  nobtree int2_ops
+ */
+
+DATA(insert OID = 0 (  404 421  95 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 421 522 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 421  94 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 421 524 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 421 520 5 btreesel btreenpage ));
+
+/*
+ *  nobtree float8_ops
+ */
+
+DATA(insert OID = 0 (  404 423 672 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 423 673 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 423 670 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 423 675 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 423 674 5 btreesel btreenpage ));
+
+/*
+ *  nobtree int24_ops
+ */
+
+DATA(insert OID = 0 (  404 424 534 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 424 540 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 424 532 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 424 542 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 424 536 5 btreesel btreenpage ));
+
+/*
+ *  nobtree int42_ops
+ */
+
+DATA(insert OID = 0 (  404 425 535 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 425 541 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 425 533 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 425 543 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 425 537 5 btreesel btreenpage ));
+
+/*
+ *  nobtree int4_ops
+ */
+
+DATA(insert OID = 0 (  404 426  97 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 426 523 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 426  96 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 426 525 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 426 521 5 btreesel btreenpage ));
+
+/*
+ *  nobtree oid_ops
+ */
+
+DATA(insert OID = 0 (  404 427 609 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 427 611 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 427 607 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 427 612 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 427 610 5 btreesel btreenpage ));
+
+/*
+ *  nobtree float4_ops
+ */
+
+DATA(insert OID = 0 (  404 428 622 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 428 624 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 428 620 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 428 625 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 428 623 5 btreesel btreenpage ));
+
+/*
+ *  nobtree char_ops
+ */
+
+DATA(insert OID = 0 (  404 429 631 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 429 632 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 429 92 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 429 634 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 429 633 5 btreesel btreenpage ));
+
+/*
+ *  nobtree char2_ops
+ */
+
+DATA(insert OID = 0 (  404 406 418 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 406 457 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 406 412 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 406 463 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 406 460 5 btreesel btreenpage ));
+
+/*
+ *  nobtree char4_ops
+ */
+
+DATA(insert OID = 0 (  404 407 419 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 407 458 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 407 413 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 407 464 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 407 461 5 btreesel btreenpage ));
+
+/*
+ *  nobtree char8_ops
+ */
+
+DATA(insert OID = 0 (  404 408 420 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 408 459 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 408 414 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 408 465 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 408 462 5 btreesel btreenpage ));
+
+/*
+ *  nobtree char16_ops
+ */
+
+DATA(insert OID = 0 (  404 430 645 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 430 646 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 430 99 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 430 648 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 430 647 5 btreesel btreenpage ));
+
+/*
+ *  nobtree name_ops
+ */
+
+DATA(insert OID = 0 (  404 409 660 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 409 661 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 409 93 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 409 663 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 409 662 5 btreesel btreenpage ));
+
+/*
+ *  nobtree text_ops
+ */
+
+DATA(insert OID = 0 (  404 431 664 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 431 665 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 431 98 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 431 667 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 431 666 5 btreesel btreenpage ));
+
+/*
+ *  nobtree abstime_ops
+ */
+
+DATA(insert OID = 0 (  404 432 562 1 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 432 564 2 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 432 560 3 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 432 565 4 btreesel btreenpage ));
+DATA(insert OID = 0 (  404 432 563 5 btreesel btreenpage ));
+
+BKI_BEGIN
+#endif /* NOBTREE */
+BKI_END
+
+/*
+ *  hash table int2_ops
+ */
+DATA(insert OID = 0 (  405 421  94 1 btreesel btreenpage ));
+/*
+ *  hash table float8_ops
+ */
+DATA(insert OID = 0 (  405 423 670 1 btreesel btreenpage ));
+/*
+ *  hash table int4_ops
+ */
+DATA(insert OID = 0 (  405 426  96 1 hashsel hashnpage ));
+/*
+ *  hash table oid_ops
+ */
+DATA(insert OID = 0 (  405 427 607 1 hashsel hashnpage ));
+/*
+ *  hash table float4_ops
+ */
+DATA(insert OID = 0 (  405 428 620 1 hashsel hashnpage ));
+/*
+ *  hash table char_ops
+ */
+DATA(insert OID = 0 (  405 429 92 1 hashsel hashnpage ));
+/*
+ *  hash table char2_ops
+ */
+DATA(insert OID = 0 (  405 406 412 1 hashsel hashnpage ));
+/*
+ *  hash table char4_ops
+ */
+DATA(insert OID = 0 (  405 407 413 1 hashsel hashnpage ));
+/*
+ *  hash table char8_ops
+ */
+DATA(insert OID = 0 (  405 408 414 1 hashsel hashnpage ));
+/*
+ *  hash table char16_ops
+ */
+DATA(insert OID = 0 (  405 430 99 1 hashsel hashnpage ));
+/*
+ *  hash table name_ops
+ */
+DATA(insert OID = 0 (  405 409 93 1 hashsel hashnpage ));
+/*
+ *  hash table text_ops
+ */
+DATA(insert OID = 0 (  405 431 98 1 hashsel hashnpage ));
+
+/*
+ *  hash table bpchar_ops
+ */
+DATA(insert OID = 0 (  405 1076 1054 1 hashsel hashnpage ));
+
+/*
+ *  hash table varchar_ops
+ */
+DATA(insert OID = 0 (  405 1077 1062 1 hashsel hashnpage ));
+
+
+#endif /* PG_AMOP_H */
diff --git a/src/backend/catalog/pg_amproc.h b/src/backend/catalog/pg_amproc.h
new file mode 100644 (file)
index 0000000..dfa2fd7
--- /dev/null
@@ -0,0 +1,134 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_amproc.h--
+ *    definition of the system "amproc" relation (pg_amproce)
+ *    along with the relation's initial contents.  The amproc
+ *    catalog is used to store procedures used by indexed access
+ *    methods that aren't associated with operators.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_AMPROC_H
+#define PG_AMPROC_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_amproc definition.  cpp turns this into
+ *     typedef struct FormData_pg_amproc
+ * ----------------
+ */ 
+CATALOG(pg_amproc) {
+    Oid        amid;
+    Oid        amopclaid;
+    Oid        amproc;
+    int2       amprocnum;
+} FormData_pg_amproc;
+
+/* ----------------
+ *     Form_pg_amproc corresponds to a pointer to a tuple with
+ *     the format of pg_amproc relation.
+ * ----------------
+ */
+typedef FormData_pg_amproc     *Form_pg_amproc;
+
+/* ----------------
+ *     compiler constants for pg_amproc
+ * ----------------
+ */
+#define Natts_pg_amproc                        4
+#define Anum_pg_amproc_amid            1
+#define Anum_pg_amproc_amopclaid       2
+#define Anum_pg_amproc_amproc          3
+#define Anum_pg_amproc_amprocnum       4
+
+/* ----------------
+ *     initial contents of pg_amproc
+ * ----------------
+ */
+
+DATA(insert OID = 0 (402 422 193 1));
+DATA(insert OID = 0 (402 422 194 2));
+DATA(insert OID = 0 (402 422 195 3));
+DATA(insert OID = 0 (402 433 193 1));
+DATA(insert OID = 0 (402 433 194 2));
+DATA(insert OID = 0 (402 433 196 3));
+DATA(insert OID = 0 (402 434 197 1));
+DATA(insert OID = 0 (402 434 198 2));
+DATA(insert OID = 0 (402 434 199 3));
+DATA(insert OID = 0 (403 421 350 1));
+DATA(insert OID = 0 (403 423 355 1));
+DATA(insert OID = 0 (403 424 353 1));
+DATA(insert OID = 0 (403 425 352 1));
+DATA(insert OID = 0 (403 426 351 1));
+DATA(insert OID = 0 (403 427 356 1));
+DATA(insert OID = 0 (403 428 354 1));
+DATA(insert OID = 0 (403 429 358 1));
+DATA(insert OID = 0 (403 406 689 1));
+DATA(insert OID = 0 (403 407 690 1));
+DATA(insert OID = 0 (403 408 691 1));
+DATA(insert OID = 0 (403 409 359 1));
+DATA(insert OID = 0 (403 430 374 1));
+DATA(insert OID = 0 (403 431 360 1));
+DATA(insert OID = 0 (403 432 357 1));
+DATA(insert OID = 0 (403 435 928 1));
+DATA(insert OID = 0 (403 436 948 1));
+DATA(insert OID = 0 (403 437 828 1));
+DATA(insert OID = 0 (403 1076 1078 1));
+DATA(insert OID = 0 (403 1077 1079 1));
+DATA(insert OID = 0 (403 1114 1092 1));
+DATA(insert OID = 0 (403 1115 1107 1));
+
+BKI_BEGIN
+#ifdef NOBTREE
+BKI_END
+DATA(insert OID = 0 (404 421 350 1));
+DATA(insert OID = 0 (404 423 355 1));
+DATA(insert OID = 0 (404 424 353 1));
+DATA(insert OID = 0 (404 425 352 1));
+DATA(insert OID = 0 (404 426 351 1));
+DATA(insert OID = 0 (404 427 356 1));
+DATA(insert OID = 0 (404 428 354 1));
+DATA(insert OID = 0 (404 429 358 1));
+DATA(insert OID = 0 (404 406 689 1));
+DATA(insert OID = 0 (404 407 690 1));
+DATA(insert OID = 0 (404 408 691 1));
+DATA(insert OID = 0 (404 409 359 1));
+DATA(insert OID = 0 (404 430 374 1));
+DATA(insert OID = 0 (404 431 360 1));
+DATA(insert OID = 0 (404 432 357 1));
+BKI_BEGIN
+#endif /* NOBTREE */
+BKI_END
+
+DATA(insert OID = 0 (405 421 449 1));
+DATA(insert OID = 0 (405 423 452 1));
+DATA(insert OID = 0 (405 426 450 1));
+DATA(insert OID = 0 (405 427 453 1));
+DATA(insert OID = 0 (405 428 451 1));
+DATA(insert OID = 0 (405 429 454 1));
+DATA(insert OID = 0 (405 406 692 1));
+DATA(insert OID = 0 (405 407 693 1));
+DATA(insert OID = 0 (405 408 694 1));
+DATA(insert OID = 0 (405 409 455 1));
+DATA(insert OID = 0 (405 430 499 1));
+DATA(insert OID = 0 (405 431 456 1));
+DATA(insert OID = 0 (405 1076 1080 1));
+DATA(insert OID = 0 (405 1077 1081 1));
+
+#endif /* PG_AMPROC_H */
diff --git a/src/backend/catalog/pg_attribute.h b/src/backend/catalog/pg_attribute.h
new file mode 100644 (file)
index 0000000..25b1652
--- /dev/null
@@ -0,0 +1,512 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_attribute.h--
+ *    definition of the system "attribute" relation (pg_attribute)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *    utils/cache/relcache.c requires some hard-coded tuple descriptors
+ *    for some of the system catalogs so if the schema for any of
+ *    these changes, be sure and change the appropriate Schema_xxx
+ *    macros!  -cim 2/5/91
+ *
+ *    fastgetattr() now uses attcacheoff to cache byte offsets of
+ *    attributes in heap tuples.  The data actually stored in 
+ *    pg_attribute (-1) indicates no cached value.  But when we copy
+ *    these tuples into a tuple descriptor, we may then update attcacheoff
+ *    in the copies.  This speeds up the attribute walking process.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_ATTRIBUTE_H
+#define PG_ATTRIBUTE_H
+   
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+#include "access/attnum.h"
+
+/* ----------------
+ *     pg_attribute definition.  cpp turns this into
+ *     typedef struct FormData_pg_attribute
+ *
+ *      If you change the following, make sure you change the structs for
+ *      system attributes in heap.c and index.c also.
+ * ----------------
+ */
+CATALOG(pg_attribute) BOOTSTRAP {
+    Oid        attrelid;      
+    NameData   attname;
+    Oid        atttypid;
+    Oid        attdefrel;
+    int4       attnvals;
+    Oid        atttyparg;      /* type arg for arrays/spquel/procs */
+    int2       attlen;
+    int2       attnum;
+    int2       attbound;
+    bool       attbyval;
+    bool       attcanindex;
+    Oid        attproc;        /* spquel? */
+    int4       attnelems;
+    int4       attcacheoff;
+    bool        attisset;
+    char       attalign;       /* alignment (c=char, s=short, i=int, d=double) */
+} FormData_pg_attribute;
+
+/*
+ * someone should figure out how to do this properly. (The problem is
+ * the size of the C struct is not the same as the size of the tuple.)
+ */
+#define ATTRIBUTE_TUPLE_SIZE \
+    (offsetof(FormData_pg_attribute,attalign) + sizeof(char))
+
+/* ----------------
+ *     Form_pg_attribute corresponds to a pointer to a tuple with
+ *     the format of pg_attribute relation.
+ * ----------------
+ */
+typedef FormData_pg_attribute  *AttributeTupleForm;
+
+/* ----------------
+ *     compiler constants for pg_attribute
+ * ----------------
+ */
+
+#define Natts_pg_attribute             16
+#define Anum_pg_attribute_attrelid     1
+#define Anum_pg_attribute_attname      2
+#define Anum_pg_attribute_atttypid     3
+#define Anum_pg_attribute_attdefrel    4
+#define Anum_pg_attribute_attnvals     5
+#define Anum_pg_attribute_atttyparg    6
+#define Anum_pg_attribute_attlen       7
+#define Anum_pg_attribute_attnum       8
+#define Anum_pg_attribute_attbound     9
+#define Anum_pg_attribute_attbyval     10
+#define Anum_pg_attribute_attcanindex  11
+#define Anum_pg_attribute_attproc      12
+#define Anum_pg_attribute_attnelems    13
+#define Anum_pg_attribute_attcacheoff  14
+#define Anum_pg_attribute_attisset      15
+#define Anum_pg_attribute_attalign      16
+
+
+/* ----------------
+ *     SCHEMA_ macros for declaring hardcoded tuple descriptors.
+ *     these are used in utils/cache/relcache.c
+ * ----------------
+#define SCHEMA_NAME(x) CppConcat(Name_,x)
+#define SCHEMA_DESC(x) CppConcat(Desc_,x)
+#define SCHEMA_NATTS(x) CppConcat(Natts_,x)
+#define SCHEMA_DEF(x) \
+    FormData_pg_attribute \
+    SCHEMA_DESC(x) [ SCHEMA_NATTS(x) ] = \
+    { \
+       CppConcat(Schema_,x) \
+    }
+ */
+
+/* ----------------
+ *     initial contents of pg_attribute
+ * ----------------
+ */
+
+/* ----------------
+ *     pg_type schema
+ * ----------------
+ */
+#define Schema_pg_type \
+{ 71l, {"typname"},      19l, 71l, 0l, 0l, NAMEDATALEN,  1, 0,   '\0', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 71l, {"typowner"},     26l, 71l, 0l, 0l,  4,  2, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 71l, {"typlen"},       21l, 71l, 0l, 0l,  2,  3, 0, '\001', '\001', 0l, 0l, -1l, '\0', 's' }, \
+{ 71l, {"typprtlen"},    21l, 71l, 0l, 0l,  2,  4, 0, '\001', '\001', 0l, 0l, -1l, '\0', 's' }, \
+{ 71l, {"typbyval"},     16l, 71l, 0l, 0l,  1,  5, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }, \
+{ 71l, {"typtype"},      18l, 71l, 0l, 0l,  1,  6, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }, \
+{ 71l, {"typisdefined"}, 16l, 71l, 0l, 0l,  1,  7, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }, \
+{ 71l, {"typdelim"},     18l, 71l, 0l, 0l,  1,  8, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }, \
+{ 71l, {"typrelid"},     26l, 71l, 0l, 0l,  4,  9, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 71l, {"typelem"},      26l, 71l, 0l, 0l,  4, 10, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 71l, {"typinput"},     24l, 71l, 0l, 0l,  4, 11, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 71l, {"typoutput"},    24l, 71l, 0l, 0l,  4, 12, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 71l, {"typreceive"},   24l, 71l, 0l, 0l,  4, 13, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 71l, {"typsend"},      24l, 71l, 0l, 0l,  4, 14, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 71l, {"typalign"},     18l, 71l, 0l, 0l,  1, 15, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }, \
+{ 71l, {"typdefault"},   25l, 71l, 0l, 0l, -1, 16, 0,   '\0', '\001', 0l, 0l, -1l, '\0', 'i' }
+
+DATA(insert OID = 0 (  71 typname          19 0 0 0 NAMEDATALEN   1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  71 typowner         26 0 0 0  4   2 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  71 typlen           21 0 0 0  2   3 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  71 typprtlen        21 0 0 0  2   4 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  71 typbyval         16 0 0 0  1   5 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  71 typtype          18 0 0 0  1   6 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  71 typisdefined     16 0 0 0  1   7 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  71 typdelim         18 0 0 0  1   8 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  71 typrelid         26 0 0 0  4   9 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  71 typelem          26 0 0 0  4  10 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  71 typinput         26 0 0 0  4  11 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  71 typoutput        26 0 0 0  4  12 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  71 typreceive       26 0 0 0  4  13 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  71 typsend          26 0 0 0  4  14 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  71 typalign         18 0 0 0  1  15 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  71 typdefault       25 0 0 0 -1  16 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  71 ctid             27 0 0 0  6  -1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  71 oid              26 0 0 0  4  -2 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  71 xmin             28 0 0 0  4  -3 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  71 cmin             29 0 0 0  2  -4 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  71 xmax             28 0 0 0  4  -5 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  71 cmax             29 0 0 0  2  -6 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  71 chain            27 0 0 0  6  -7 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  71 anchor           27 0 0 0  6  -8 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  71 tmax            702 0 0 0  4  -9 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  71 tmin            702 0 0 0  4 -10 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  71 vtype            18 0 0 0  1 -11 0 t t 0 0 -1 f c));
+
+/* ----------------
+ *     pg_database
+ * ----------------
+ */
+DATA(insert OID = 0 (  88 datname          19 0 0 0 NAMEDATALEN   1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  88 datdba           26 0 0 0  4   2 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  88 datpath          25 0 0 0 -1   3 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  88 ctid             27 0 0 0  6  -1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  88 oid              26 0 0 0  4  -2 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  88 xmin             28 0 0 0  4  -3 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  88 cmin             29 0 0 0  2  -4 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  88 xmax             28 0 0 0  4  -5 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  88 cmax             29 0 0 0  2  -6 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  88 chain            27 0 0 0  6  -7 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  88 anchor           27 0 0 0  6  -8 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  88 tmax            702 0 0 0  4  -9 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  88 tmin            702 0 0 0  4 -10 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  88 vtype            18 0 0 0  1 -11 0 t t 0 0 -1 f c));
+    
+/* ----------------
+ *     pg_demon
+ * ----------------
+ */
+DATA(insert OID = 0 (  76 demserid         26 0 0 0  4   1 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  76 demname          19 0 0 0 NAMEDATALEN   2 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  76 demowner         26 0 0 0  4   3 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  76 demcode          24 0 0 0  4   4 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  76 ctid             27 0 0 0  6  -1 0 f t 0 0 -1 f i));
+
+DATA(insert OID = 0 (  76 oid              26 0 0 0  4  -2 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  76 xmin             28 0 0 0  4  -3 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  76 cmin             29 0 0 0  2  -4 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  76 xmax             28 0 0 0  4  -5 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  76 cmax             29 0 0 0  2  -6 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  76 chain            27 0 0 0  6  -7 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  76 anchor           27 0 0 0  6  -8 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  76 tmax            702 0 0 0  4  -9 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  76 tmin            702 0 0 0  4 -10 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  76 vtype            18 0 0 0  1 -11 0 t t 0 0 -1 f c));
+    
+/* ----------------
+ *     pg_proc
+ * ----------------
+ */
+#define Schema_pg_proc \
+{ 81l, {"proname"},       19l, 81l, 0l, 0l, NAMEDATALEN,  1, 0,   '\0', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 81l, {"proowner"},      26l, 81l, 0l, 0l,  4,  2, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 81l, {"prolang"},       26l, 81l, 0l, 0l,  4,  3, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 81l, {"proisinh"},      16l, 81l, 0l, 0l,  1,  4, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }, \
+{ 81l, {"proistrusted"},  16l, 81l, 0l, 0l,  1,  5, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }, \
+{ 81l, {"proiscachable"}, 16l, 81l, 0l, 0l,  1,  6, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }, \
+{ 81l, {"pronargs"},      21l, 81l, 0l, 0l,  2,  7, 0, '\001', '\001', 0l, 0l, -1l, '\0', 's' }, \
+{ 81l, {"proretset"},     16l, 81l, 0l, 0l,  1,  8, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }, \
+{ 81l, {"prorettype"},    26l, 81l, 0l, 0l,  4,  9, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 81l, {"proargtypes"},   30l, 81l, 0l, 0l, 32, 10, 0,   '\0', '\001', 0l, 0l, \
+      -1l, '\0', 'i' }, \
+{ 81l, {"probyte_pct"},   23l, 81l, 0l, 0l,  4, 11, 0, '\001', '\001', 0l, 0l, \
+      -1l, '\0', 'i' }, \
+{ 81l, {"properbyte_cpu"},   23l, 81l, 0l, 0l,  4, 12, 0, '\001', '\001', 0l, 0l,      -1l, '\0', 'i' }, \
+{ 81l, {"propercall_cpu"},   23l, 81l, 0l, 0l,  4, 13, 0, '\001', '\001', 0l, 0l,      -1l, '\0', 'i' }, \
+{ 81l, {"prooutin_ratio"},   23l, 81l, 0l, 0l,  4, 14, 0, '\001', '\001', 0l, 0l,      -1l, '\0', 'i' }, \
+{ 81l, {"prosrc"},        25l, 81l, 0l, 0l, -1,  15, 0,   '\0', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 81l, {"probin"},        17l, 81l, 0l, 0l, -1,  16, 0,   '\0', '\001', 0l, 0l, -1l, '\0', 'i' }
+
+DATA(insert OID = 0 (  81 proname          19 0 0 0 NAMEDATALEN   1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 proowner         26 0 0 0  4   2 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 prolang          26 0 0 0  4   3 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 proisinh         16 0 0 0  1   4 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  81 proistrusted     16 0 0 0  1   5 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  81 proiscachable    16 0 0 0  1   6 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  81 pronargs         21 0 0 0  2   7 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  81 proretset        16 0 0 0  1   8 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  81 prorettype       26 0 0 0  4   9 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 proargtypes      30 0 0 0 32  10 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 probyte_pct      23 0 0 0  4  11 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 properbyte_cpu   23 0 0 0  4  12 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 propercall_cpu   23 0 0 0  4  13 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 prooutin_ratio   23 0 0 0  4  14 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 prosrc           25 0 0 0 -1  15 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 probin           17 0 0 0 -1  16 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 ctid             27 0 0 0  6  -1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 oid              26 0 0 0  4  -2 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 xmin             28 0 0 0  4  -3 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 cmin             29 0 0 0  2  -4 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  81 xmax             28 0 0 0  4  -5 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 cmax             29 0 0 0  2 -6 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  81 chain            27 0 0 0  6  -7 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 anchor           27 0 0 0  6  -8 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 tmax            702 0 0 0  4  -9 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 tmin            702 0 0 0  4 -10 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  81 vtype            18 0 0 0  1 -11 0 t t 0 0 -1 f c));
+    
+/* ----------------
+ *     pg_server
+ * ----------------
+ */
+DATA(insert OID = 0 (  82 sername          19 0 0 0 NAMEDATALEN   1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  82 serpid           21 0 0 0  2   2 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  82 serport          21 0 0 0  2   3 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  82 ctid             27 0 0 0  6  -1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  82 oid              26 0 0 0  4  -2 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  82 xmin             28 0 0 0  4  -3 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  82 cmin             29 0 0 0  2  -4 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  82 xmax             28 0 0 0  4  -5 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  82 cmax             29 0 0 0  2  -6 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  82 chain            27 0 0 0  6  -7 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  82 anchor           27 0 0 0  6  -8 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  82 tmax            702 0 0 0  4  -9 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  82 tmin            702 0 0 0  4 -10 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  82 vtype            18 0 0 0  1 -11 0 t t 0 0 -1 f c));
+    
+/* ----------------
+ *     pg_user
+ * ----------------
+ */
+DATA(insert OID = 0 (  86 usename          19 0 0 0 NAMEDATALEN   1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  86 usesysid         23 0 0 0  4   2 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  86 usecreatedb      16 0 0 0  1   3 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  86 usetrace         16 0 0 0  1   4 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  86 usesuper         16 0 0 0  1   5 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  86 usecatupd        16 0 0 0  1   6 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  86 ctid             27 0 0 0  6  -1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  86 oid              26 0 0 0  4  -2 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  86 xmin             28 0 0 0  4  -3 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  86 cmin             29 0 0 0  2  -4 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  86 xmax             28 0 0 0  4  -5 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  86 cmax             29 0 0 0  2  -6 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  86 chain            27 0 0 0  6  -7 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  86 anchor           27 0 0 0  6  -8 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  86 tmax            702 0 0 0  4  -9 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  86 tmin            702 0 0 0  4 -10 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  86 vtype            18 0 0 0  1 -11 0 t t 0 0 -1 f c));
+
+/* ----------------
+ *     pg_group
+ * ----------------
+ */
+DATA(insert OID = 0 (  87 groname          19 0 0 0 NAMEDATALEN   1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  87 grosysid         23 0 0 0  4   2 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  87 grolist        1007 0 0 0 -1   3 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  87 ctid             27 0 0 0  6  -1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  87 oid              26 0 0 0  4  -2 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  87 xmin             28 0 0 0  4  -3 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  87 cmin             29 0 0 0  2  -4 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  87 xmax             28 0 0 0  4  -5 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  87 cmax             29 0 0 0  2  -6 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  87 chain            27 0 0 0  6  -7 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  87 anchor           27 0 0 0  6  -8 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  87 tmax            702 0 0 0  4  -9 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  87 tmin            702 0 0 0  4 -10 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  87 vtype            18 0 0 0  1 -11 0 t t 0 0 -1 f c));
+    
+/* ----------------
+ *     pg_attribute
+ * ----------------
+ */
+#define Schema_pg_attribute \
+{ 75l, {"attrelid"},    26l, 75l, 0l, 0l,  4,  1, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 75l, {"attname"},     19l, 75l, 0l, 0l, NAMEDATALEN,  2, 0,   '\0', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 75l, {"atttypid"},    26l, 75l, 0l, 0l,  4,  3, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 75l, {"attdefrel"},   26l, 75l, 0l, 0l,  4,  4, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 75l, {"attnvals"},    23l, 75l, 0l, 0l,  4,  5, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 75l, {"atttyparg"},   26l, 75l, 0l, 0l,  4,  6, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 75l, {"attlen"},      21l, 75l, 0l, 0l,  2,  7, 0, '\001', '\001', 0l, 0l, -1l, '\0', 's' }, \
+{ 75l, {"attnum"},      21l, 75l, 0l, 0l,  2,  8, 0, '\001', '\001', 0l, 0l, -1l, '\0', 's' }, \
+{ 75l, {"attbound"},    21l, 75l, 0l, 0l,  2,  9, 0, '\001', '\001', 0l, 0l, -1l, '\0', 's' }, \
+{ 75l, {"attbyval"},    16l, 75l, 0l, 0l,  1, 10, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }, \
+{ 75l, {"attcanindex"}, 16l, 75l, 0l, 0l,  1, 11, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }, \
+{ 75l, {"attproc"},     26l, 75l, 0l, 0l,  4, 12, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 75l, {"attnelems"},   23l, 75l, 0l, 0l,  4, 13, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 75l, {"attcacheoff"}, 23l, 75l, 0l, 0l,  4, 14, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 75l, {"attisset"},    16l, 75l, 0l, 0l,  1, 15, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }, \
+{ 75l, {"attalign"},    18l, 75l, 0l, 0l,  1, 16, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }
+
+DATA(insert OID = 0 (  75 attrelid         26 0 0 0  4   1 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  75 attname          19 0 0 0 NAMEDATALEN   2 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  75 atttypid         26 0 0 0  4   3 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  75 attdefrel        26 0 0 0  4   4 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  75 attnvals         23 0 0 0  4   5 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  75 atttyparg        26 0 0 0  4   6 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  75 attlen           21 0 0 0  2   7 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  75 attnum           21 0 0 0  2   8 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  75 attbound         21 0 0 0  2   9 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  75 attbyval         16 0 0 0  1  10 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  75 attcanindex      16 0 0 0  1  11 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  75 attproc          26 0 0 0  4  12 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  75 attnelems        23 0 0 0  4  13 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  75 attcacheoff      23 0 0 0  4  14 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  75 attisset         16 0 0 0  1  15 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  75 attalign         18 0 0 0  1  16 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  75 ctid             27 0 0 0  6  -1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  75 oid              26 0 0 0  4  -2 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  75 xmin             28 0 0 0  4  -3 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  75 cmin             29 0 0 0  2  -4 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  75 xmax             28 0 0 0  4  -5 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  75 cmax             29 0 0 0  2  -6 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  75 chain            27 0 0 0  6  -7 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  75 anchor           27 0 0 0  6  -8 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  75 tmax            702 0 0 0  4  -9 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  75 tmin            702 0 0 0  4 -10 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  75 vtype            18 0 0 0  1 -11 0 t t 0 0 -1 f c));
+    
+/* ----------------
+ *     pg_class
+ * ----------------
+ */
+#define Schema_pg_class \
+{ 83l, {"relname"},      19l, 83l, 0l, 0l, NAMEDATALEN,  1, 0,   '\0', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 83l, {"reltype"},     26l, 83l, 0l, 0l,  4,  2, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 83l, {"relowner"},     26l, 83l, 0l, 0l,  4,  2, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 83l, {"relam"},        26l, 83l, 0l, 0l,  4,  3, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 83l, {"relpages"},     23,  83l, 0l, 0l,  4,  4, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 83l, {"reltuples"},    23,  83l, 0l, 0l,  4,  5, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 83l, {"relexpires"},   702,  83l, 0l, 0l,  4,  6, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 83l, {"relpreserved"}, 703,  83l, 0l, 0l,  4,  7, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 83l, {"relhasindex"},  16,  83l, 0l, 0l,  1,  8, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }, \
+{ 83l, {"relisshared"},  16,  83l, 0l, 0l,  1,  9, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }, \
+{ 83l, {"relkind"},      18,  83l, 0l, 0l,  1, 10, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }, \
+{ 83l, {"relarch"},      18,  83l, 0l, 0l,  1, 11, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }, \
+{ 83l, {"relnatts"},     21,  83l, 0l, 0l,  2, 12, 0, '\001', '\001', 0l, 0l, -1l, '\0', 's' }, \
+{ 83l, {"relsmgr"},      210l,  83l, 0l, 0l,  2, 13, 0, '\001', '\001', 0l, 0l, -1l, '\0', 's' }, \
+{ 83l, {"relkey"},       22,  83l, 0l, 0l, 16, 14, 0,   '\0', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 83l, {"relkeyop"},     30,  83l, 0l, 0l, 32, 15, 0,   '\0', '\001', 0l, 0l, -1l, '\0', 'i' }, \
+{ 83l, {"relhasrules"},  16,  83l, 0l, 0l,  1, 16, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'c' }, \
+{ 83l, {"relacl"},     1034l, 83l, 0l, 0l, -1, 17, 0,   '\0', '\001', 0l, 0l, -1l, '\0', 'i' }
+
+DATA(insert OID = 0 (  83 relname          19 0 0 0 NAMEDATALEN   1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 reltype          26 0 0 0  4   2 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 relowner         26 0 0 0  4   2 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 relam            26 0 0 0  4   3 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 relpages         23 0 0 0  4   4 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 reltuples        23 0 0 0  4   5 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 relexpires      702 0 0 0  4   6 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 relpreserved    702 0 0 0  4   7 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 relhasindex      16 0 0 0  1   8 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  83 relisshared      16 0 0 0  1   9 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  83 relkind          18 0 0 0  1  10 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  83 relarch          18 0 0 0  1  11 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  83 relnatts         21 0 0 0  2  12 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  83 relsmgr         210 0 0 0  2  13 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  83 relkey           22 0 0 0 16  14 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 relkeyop         30 0 0 0 32  15 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 relhasrules      16 0 0 0  1  16 0 t t 0 0 -1 f c));
+DATA(insert OID = 0 (  83 relacl         1034 0 0 0 -1  17 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 ctid             27 0 0 0  6  -1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 oid              26 0 0 0  4  -2 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 xmin             28 0 0 0  4  -3 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 cmin             29 0 0 0  2  -4 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  83 xmax             28 0 0 0  4  -5 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 cmax             29 0 0 0  2  -6 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  83 chain            27 0 0 0  6  -7 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 anchor           27 0 0 0  6  -8 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 tmax            702 0 0 0  4  -9 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 tmin            702 0 0 0  4 -10 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  83 vtype            18 0 0 0  1 -11 0 t t 0 0 -1 f c));
+    
+/* ----------------
+ *     pg_magic
+ * ----------------
+ */
+DATA(insert OID = 0 (  80 magname          19 0 0 0 NAMEDATALEN   1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  80 magvalue         19 0 0 0 NAMEDATALEN   2 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  80 ctid             27 0 0 0  6  -1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  80 oid              26 0 0 0  4  -2 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  80 xmin             28 0 0 0  4  -3 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  80 cmin             29 0 0 0  2  -4 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  80 xmax             28 0 0 0  4  -5 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  80 cmax             29 0 0 0  2  -6 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  80 chain            27 0 0 0  6  -7 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  80 anchor           27 0 0 0  6  -8 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  80 tmax            702 0 0 0  4  -9 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  80 tmin            702 0 0 0  4 -10 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  80 vtype            18 0 0 0  1 -11 0 t t 0 0 -1 f c));
+    
+/* ----------------
+ *     pg_defaults
+ * ----------------
+ */
+DATA(insert OID = 0 (  89 defname          19 0 0 0 NAMEDATALEN   1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  89 defvalue         19 0 0 0 NAMEDATALEN   2 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  89 ctid             27 0 0 0  6  -1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  89 oid              26 0 0 0  4  -2 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  89 xmin             28 0 0 0  4  -3 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  89 cmin             29 0 0 0  2  -4 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  89 xmax             28 0 0 0  4  -5 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  89 cmax             29 0 0 0  2  -6 0 t t 0 0 -1 f s));
+DATA(insert OID = 0 (  89 chain            27 0 0 0  6  -7 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  89 anchor           27 0 0 0  6  -8 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  89 tmax            702 0 0 0  4  -9 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  89 tmin            702 0 0 0  4 -10 0 t t 0 0 -1 f i));
+DATA(insert OID = 0 (  89 vtype            18 0 0 0  1 -11 0 t t 0 0 -1 f c));
+    
+
+/* ----------------
+ *     pg_hosts - this relation is used to store host based authentication
+ *                info
+ *               
+ * ----------------
+ */
+DATA(insert OID = 0 (  101 dbName           19 0 0 0  NAMEDATALEN   1 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  101 address           25 0 0 0  -1   2 0 f t 0 0 -1 f i));
+DATA(insert OID = 0 (  101 mask           25 0 0 0  -1   3 0 f t 0 0 -1 f i));
+
+/* ----------------
+ *     pg_variable - this relation is modified by special purpose access
+ *               method code.  The following is garbage but is needed
+ *               so that the reldesc code works properly.
+ * ----------------
+ */
+#define Schema_pg_variable \
+{ 90l, {"varfoo"},  26l, 90l, 0l, 0l, 4, 1, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }
+    
+DATA(insert OID = 0 (  90 varfoo           26 0 0 0  4   1 0 t t 0 0 -1 f i));
+    
+/* ----------------
+ *     pg_log - this relation is modified by special purpose access
+ *               method code.  The following is garbage but is needed
+ *               so that the reldesc code works properly.
+ * ----------------
+ */
+#define Schema_pg_log \
+{ 99l, {"logfoo"},  26l, 99l, 0l, 0l, 4, 1, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }
+
+DATA(insert OID = 0 (  99 logfoo           26 0 0 0  4   1 0 t t 0 0 -1 f i));
+    
+/* ----------------
+ *     pg_time - this relation is modified by special purpose access
+ *               method code.  The following is garbage but is needed
+ *               so that the reldesc code works properly.
+ * ----------------
+ */
+#define Schema_pg_time \
+{ 100l, {"timefoo"},  26l, 100l, 0l, 0l, 4, 1, 0, '\001', '\001', 0l, 0l, -1l, '\0', 'i' }
+
+DATA(insert OID = 0 (  100 timefoo         26 0 0 0  4   1 0 t t 0 0 -1 f i));
+    
+#endif /* PG_ATTRIBUTE_H */
diff --git a/src/backend/catalog/pg_class.h b/src/backend/catalog/pg_class.h
new file mode 100644 (file)
index 0000000..edfb87f
--- /dev/null
@@ -0,0 +1,162 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_class.h--
+ *    definition of the system "relation" relation (pg_class)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    ``pg_relation'' is being replaced by ``pg_class''.  currently
+ *    we are only changing the name in the catalogs but someday the
+ *    code will be changed too. -cim 2/26/90
+ *    [it finally happens.  -ay 11/5/94]
+ *
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_RELATION_H
+#define PG_RELATION_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+#include "utils/nabstime.h"
+
+/* ----------------
+ *     pg_class definition.  cpp turns this into
+ *     typedef struct FormData_pg_class
+ *
+ *     Note: the #if 0, #endif around the BKI_BEGIN.. END block
+ *           below keeps cpp from seeing what is meant for the
+ *           genbki script: pg_relation is now called pg_class, but
+ *           only in the catalogs -cim 2/26/90
+ * ----------------
+ */
+
+/* ----------------
+ *     This structure is actually variable-length (the last attribute is
+ *     a POSTGRES array).  Hence, sizeof(FormData_pg_class) does not
+ *     describe the fixed-length or actual size of the structure.
+ *     FormData_pg_class.relacl may not be correctly aligned, either,
+ *     if aclitem and struct varlena don't align together.  Hence,
+ *     you MUST use heap_getattr() to get the relacl field.
+ * ----------------
+ */
+CATALOG(pg_class) BOOTSTRAP {
+     NameData  relname;
+     Oid        reltype;          
+     Oid       relowner;
+     Oid       relam;
+     int4      relpages;
+     int4      reltuples;
+     int4      relexpires; /* really used as a abstime, but fudge it for now*/
+     int4      relpreserved;/*really used as a reltime, but fudge it for now*/
+     bool      relhasindex;
+     bool      relisshared;
+     char      relkind;
+     char      relarch; /* 'h' = heavy, 'l' = light, 'n' = no archival*/
+     int2      relnatts;
+     int2      relsmgr;
+     int28     relkey;                 /* not used */
+     oid8      relkeyop;               /* not used */
+     bool      relhasrules;
+     aclitem   relacl[1];              /* this is here for the catalog */
+} FormData_pg_class;
+
+#define CLASS_TUPLE_SIZE \
+     (offsetof(FormData_pg_class,relhasrules) + sizeof(bool))
+
+/* ----------------
+ *     Form_pg_class corresponds to a pointer to a tuple with
+ *     the format of pg_class relation.
+ * ----------------
+ */
+typedef FormData_pg_class      *Form_pg_class;
+
+/* ----------------
+ *     compiler constants for pg_class
+ * ----------------
+ */
+
+/* ----------------
+ *     Natts_pg_class_fixed is used to tell routines that insert new
+ *     pg_class tuples (as opposed to replacing old ones) that there's no
+ *     relacl field.
+ * ----------------
+ */
+#define Natts_pg_class_fixed           17
+#define Natts_pg_class                 18
+#define Anum_pg_class_relname          1
+#define Anum_pg_class_reltype           2
+#define Anum_pg_class_relowner         3
+#define Anum_pg_class_relam            4
+#define Anum_pg_class_relpages         5
+#define Anum_pg_class_reltuples                6
+#define Anum_pg_class_relexpires       7
+#define Anum_pg_class_relpreserved     8
+#define Anum_pg_class_relhasindex      9
+#define Anum_pg_class_relisshared      10
+#define Anum_pg_class_relkind          11
+#define Anum_pg_class_relarch          12
+#define Anum_pg_class_relnatts         13
+#define Anum_pg_class_relsmgr          14
+#define Anum_pg_class_relkey           15
+#define Anum_pg_class_relkeyop         16
+#define Anum_pg_class_relhasrules      17
+#define Anum_pg_class_relacl           18
+
+/* ----------------
+ *     initial contents of pg_class
+ * ----------------
+ */
+
+DATA(insert OID =  71 (  pg_type 71          PGUID 0 0 0 0 0 f f r n 16 0 - - f _null_ ));
+DATA(insert OID =  75 (  pg_attribute 75      PGUID 0 0 0 0 0 f f r n 16 0 - - f _null_ ));
+DATA(insert OID =  76 (  pg_demon 76          PGUID 0 0 0 0 0 f t r n 4 0 - - f _null_ ));
+DATA(insert OID =  80 (  pg_magic 80         PGUID 0 0 0 0 0 f t r n 2 0 - - f _null_ ));
+DATA(insert OID =  81 (  pg_proc 81          PGUID 0 0 0 0 0 f f r n 16 0 - - f _null_ ));
+DATA(insert OID =  82 (  pg_server 82         PGUID 0 0 0 0 0 f t r n 3 0 - - f _null_ ));
+DATA(insert OID =  83 (  pg_class 83         PGUID 0 0 0 0 0 f f r n 17 0 - - f _null_ ));    
+DATA(insert OID =  86 (  pg_user 86          PGUID 0 0 0 0 0 f t r n 6 0 - - f _null_ ));
+DATA(insert OID =  87 (  pg_group 87          PGUID 0 0 0 0 0 f t s n 3 0 - - f _null_ ));
+DATA(insert OID =  88 (  pg_database 88      PGUID 0 0 0 0 0 f t r n 3 0 - - f _null_ ));
+DATA(insert OID =  89 (  pg_defaults 89       PGUID 0 0 0 0 0 f t r n 2 0 - - f _null_ ));
+DATA(insert OID =  90 (  pg_variable 90        PGUID 0 0 0 0 0 f t s n 2 0 - - f _null_ ));
+DATA(insert OID =  99 (  pg_log  99           PGUID 0 0 0 0 0 f t s n 1 0 - - f _null_ ));
+DATA(insert OID = 100 (  pg_time 100           PGUID 0 0 0 0 0 f t s n 1 0 - - f _null_ ));
+DATA(insert OID = 101 (  pg_hosts 101           PGUID 0 0 0 0 0 f t s n 3 0 - - f _null_ ));
+
+#define RelOid_pg_type         71
+#define RelOid_pg_demon        76   
+#define RelOid_pg_attribute    75   
+#define RelOid_pg_magic        80      
+#define RelOid_pg_proc         81   
+#define RelOid_pg_server       82   
+#define RelOid_pg_class        83   
+#define RelOid_pg_user         86   
+#define RelOid_pg_group        87
+#define RelOid_pg_database     88   
+#define RelOid_pg_defaults     89    
+#define RelOid_pg_variable     90   
+#define RelOid_pg_log          99       
+#define RelOid_pg_time         100      
+#define RelOid_pg_hosts        101      
+    
+#define MAX_SYSTEM_RELOID       101
+
+#define       RELKIND_INDEX           'i'     /* secondary index */
+#define       RELKIND_RELATION        'r'     /* cataloged heap */
+#define       RELKIND_SPECIAL         's'     /* special (non-heap) */
+#define       RELKIND_UNCATALOGED     'u'     /* temporary heap */
+
+#endif /* PG_RELATION_H */
diff --git a/src/backend/catalog/pg_database.h b/src/backend/catalog/pg_database.h
new file mode 100644 (file)
index 0000000..2550c54
--- /dev/null
@@ -0,0 +1,57 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_database.h--
+ *    definition of the system "database" relation (pg_database)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_DATABASE_H
+#define PG_DATABASE_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_database definition.  cpp turns this into
+ *     typedef struct FormData_pg_database
+ * ----------------
+ */ 
+CATALOG(pg_database) BOOTSTRAP {
+    NameData   datname;
+    Oid        datdba;
+    text       datpath;        /* VARIABLE LENGTH FIELD */
+} FormData_pg_database;
+
+/* ----------------
+ *     Form_pg_database corresponds to a pointer to a tuple with
+ *     the format of pg_database relation.
+ * ----------------
+ */
+typedef FormData_pg_database   *Form_pg_database;
+
+/* ----------------
+ *     compiler constants for pg_database
+ * ----------------
+ */
+#define Natts_pg_database              3
+#define Anum_pg_database_datname       1
+#define Anum_pg_database_datdba                2
+#define Anum_pg_database_datpath       3
+
+
+#endif /* PG_DATABASE_H */
diff --git a/src/backend/catalog/pg_defaults.h b/src/backend/catalog/pg_defaults.h
new file mode 100644 (file)
index 0000000..c1d42cb
--- /dev/null
@@ -0,0 +1,55 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_defaults.h--
+ *   definition of the system "defaults" relation (pg_defaults)
+ *   along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_DEFAULTS_H
+#define PG_DEFAULTS_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_defaults definition.  cpp turns this into
+ *     typedef struct FormData_pg_defaults
+ * ----------------
+ */ 
+CATALOG(pg_defaults) BOOTSTRAP {
+    NameData   defname;
+    NameData   defvalue;
+} FormData_pg_defaults;
+
+/* ----------------
+ *     Form_pg_defaults corresponds to a pointer to a tuple with
+ *     the format of pg_defaults relation.
+ * ----------------
+ */
+typedef FormData_pg_defaults   *Form_pg_defaults;
+
+/* ----------------
+ *     compiler constants for pg_defaults
+ * ----------------
+ */
+#define Natts_pg_defaults              2
+#define Anum_pg_defaults_defname       1
+#define Anum_pg_defaults_defvalue      2
+
+
+#endif /* PG_DEFAULTS_H */
diff --git a/src/backend/catalog/pg_demon.h b/src/backend/catalog/pg_demon.h
new file mode 100644 (file)
index 0000000..437031c
--- /dev/null
@@ -0,0 +1,58 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_demon.h--
+ *   definition of the system "demon" relation (pg_demon)
+ *   along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_DEMON_H     
+#define PG_DEMON_H     
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_demon definition.  cpp turns this into
+ *     typedef struct FormData_pg_demon
+ * ----------------
+ */ 
+CATALOG(pg_demon) BOOTSTRAP {
+    Oid        demserid;
+    NameData   demname;
+    Oid        demowner;
+    regproc    demcode;
+} FormData_pg_demon;
+
+/* ----------------
+ *     Form_pg_demon corresponds to a pointer to a tuple with
+ *     the format of pg_demon relation.
+ * ----------------
+ */
+typedef FormData_pg_demon      *Form_pg_demon;
+
+/* ----------------
+ *     compiler constants for pg_demon
+ * ----------------
+ */
+#define Natts_pg_demon                 4
+#define Anum_pg_demon_demserid         1
+#define Anum_pg_demon_demname          2
+#define Anum_pg_demon_demowner         3
+#define Anum_pg_demon_demcode          4
+
+#endif /* PG_DEMON_H */
diff --git a/src/backend/catalog/pg_group.h b/src/backend/catalog/pg_group.h
new file mode 100644 (file)
index 0000000..64ff9e3
--- /dev/null
@@ -0,0 +1,42 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_group.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_GROUP_H
+#define PG_GROUP_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+CATALOG(pg_group) BOOTSTRAP {
+        NameData groname;
+        int4    grosysid;
+       int4    grolist[1];
+} FormData_pg_group;
+/* VARIABLE LENGTH STRUCTURE */
+
+typedef FormData_pg_group      *Form_pg_group;
+
+#define Natts_pg_group         1
+#define Anum_pg_group_groname  1
+#define Anum_pg_group_grosysid 2
+#define Anum_pg_group_grolist  3
+
+#endif /* PG_GROUP_H */
diff --git a/src/backend/catalog/pg_hosts.h b/src/backend/catalog/pg_hosts.h
new file mode 100644 (file)
index 0000000..fd79f16
--- /dev/null
@@ -0,0 +1,44 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_hosts.h--
+ *
+ *     the pg_hosts system catalog provides host-based access to the 
+ * backend.  Only those hosts that are in the pg_hosts 
+ *
+ *  currently, this table is not used, instead file-based host authentication
+ * is used
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef PG_HOSTS_H
+#define PG_HOSTS_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+CATALOG(pg_hosts) BOOTSTRAP {
+        NameData dbName;   
+       text    address; 
+       text    mask;
+} FormData_pg_hosts;
+
+typedef FormData_pg_hosts      *Form_pg_hosts;
+#define Natts_pg_hosts         3
+#define Anum_pg_hosts_dbName   1
+#define Anum_pg_hosts_address  2
+#define Anum_pg_hosts_mask      3
+
+#endif /* PG_HOSTS_H */
diff --git a/src/backend/catalog/pg_index.h b/src/backend/catalog/pg_index.h
new file mode 100644 (file)
index 0000000..5e133da
--- /dev/null
@@ -0,0 +1,71 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_index.h--
+ *    definition of the system "index" relation (pg_index)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_INDEX_H
+#define PG_INDEX_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_index definition.  cpp turns this into
+ *     typedef struct FormData_pg_index.  The oid of the index relation
+ *     is stored in indexrelid; the oid of the indexed relation is stored
+ *     in indrelid.
+ * ----------------
+ */ 
+CATALOG(pg_index) {
+    Oid        indexrelid;
+    Oid        indrelid;
+    Oid                indproc; /* registered procedure for functional index */
+    int28      indkey;
+    oid8       indclass;
+    bool       indisclustered;
+    bool       indisarchived;
+    text       indpred;        /* query plan for partial index predicate */
+} FormData_pg_index;
+
+#define INDEX_MAX_KEYS 8  /* maximum number of keys in an index definition */
+
+/* ----------------
+ *     Form_pg_index corresponds to a pointer to a tuple with
+ *     the format of pg_index relation.
+ * ----------------
+ */
+typedef FormData_pg_index      *IndexTupleForm;
+
+/* ----------------
+ *     compiler constants for pg_index
+ * ----------------
+ */
+#define Natts_pg_index                 8
+#define Anum_pg_index_indexrelid       1
+#define Anum_pg_index_indrelid         2
+#define Anum_pg_index_indproc          3
+#define Anum_pg_index_indkey           4
+#define Anum_pg_index_indclass         5
+#define Anum_pg_index_indisclustered   6
+#define Anum_pg_index_indisarchived    7
+#define Anum_pg_index_indpred          8
+
+
+#endif /* PG_INDEX_H */
diff --git a/src/backend/catalog/pg_inheritproc.h b/src/backend/catalog/pg_inheritproc.h
new file mode 100644 (file)
index 0000000..8e5d345
--- /dev/null
@@ -0,0 +1,59 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_inheritproc.h--
+ *    definition of the system "inheritproc" relation (pg_inheritproc)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_INHERITPROC_H
+#define PG_INHERITPROC_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_inheritproc definition.  cpp turns this into
+ *     typedef struct FormData_pg_inheritproc
+ * ----------------
+ */ 
+CATALOG(pg_inheritproc) {
+     NameData  inhproname;
+     Oid       inhargrel;
+     Oid       inhdefrel;
+     Oid       inhproc;
+} FormData_pg_inheritproc;
+
+/* ----------------
+ *     Form_pg_inheritproc corresponds to a pointer to a tuple with
+ *     the format of pg_inheritproc relation.
+ * ----------------
+ */
+typedef FormData_pg_inheritproc        *Form_pg_inheritproc;
+
+/* ----------------
+ *     compiler constants for pg_inheritproc
+ * ----------------
+ */
+#define Natts_pg_inheritproc           4
+#define Anum_pg_inheritproc_inhproname 1
+#define Anum_pg_inheritproc_inhargrel  2
+#define Anum_pg_inheritproc_inhdefrel  3
+#define Anum_pg_inheritproc_inhproc    4
+
+
+#endif /* PG_INHERITPROC_H */
diff --git a/src/backend/catalog/pg_inherits.h b/src/backend/catalog/pg_inherits.h
new file mode 100644 (file)
index 0000000..4c151f6
--- /dev/null
@@ -0,0 +1,57 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_inherits.h--
+ *    definition of the system "inherits" relation (pg_inherits)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_INHERITS_H
+#define PG_INHERITS_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_inherits definition.  cpp turns this into
+ *     typedef struct FormData_pg_inherits
+ * ----------------
+ */ 
+CATALOG(pg_inherits) {
+    Oid        inhrel;
+    Oid        inhparent;
+    int4       inhseqno;
+} FormData_pg_inherits;
+
+/* ----------------
+ *     Form_pg_inherits corresponds to a pointer to a tuple with
+ *     the format of pg_inherits relation.
+ * ----------------
+ */
+typedef FormData_pg_inherits   *InheritsTupleForm;
+
+/* ----------------
+ *     compiler constants for pg_inherits
+ * ----------------
+ */
+#define Natts_pg_inherits              3
+#define Anum_pg_inherits_inhrel                1
+#define Anum_pg_inherits_inhparent     2
+#define Anum_pg_inherits_inhseqno      3
+
+
+#endif /* PG_INHERITS_H */
diff --git a/src/backend/catalog/pg_ipl.h b/src/backend/catalog/pg_ipl.h
new file mode 100644 (file)
index 0000000..4e68298
--- /dev/null
@@ -0,0 +1,57 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_ipl.h--
+ *    definition of the system "ipl" relation (pg_ipl)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_IPL_H
+#define PG_IPL_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_ipl definition.  cpp turns this into
+ *     typedef struct FormData_pg_ipl
+ * ----------------
+ */ 
+CATALOG(pg_ipl) {
+     Oid       iplrel;
+     Oid       iplipl;
+     int4      iplseqno;
+} FormData_pg_ipl;
+
+/* ----------------
+ *     Form_pg_ipl corresponds to a pointer to a tuple with
+ *     the format of pg_ipl relation.
+ * ----------------
+ */
+typedef FormData_pg_ipl        *Form_pg_ipl;
+
+/* ----------------
+ *     compiler constants for pg_ipl
+ * ----------------
+ */
+#define Natts_pg_ipl           3
+#define Anum_pg_ipl_iplrel     1
+#define Anum_pg_ipl_iplipl     2
+#define Anum_pg_ipl_iplseqno   3
+
+
+#endif /* PG_IPL_H */
diff --git a/src/backend/catalog/pg_language.h b/src/backend/catalog/pg_language.h
new file mode 100644 (file)
index 0000000..945ec66
--- /dev/null
@@ -0,0 +1,75 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_language.h--
+ *    definition of the system "language" relation (pg_language)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_LANGUAGE_H
+#define PG_LANGUAGE_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_language definition.  cpp turns this into
+ *     typedef struct FormData_pg_language
+ * ----------------
+ */ 
+CATALOG(pg_language) {
+    NameData   lanname;
+    text       lancompiler;    /* VARIABLE LENGTH FIELD */
+} FormData_pg_language;
+
+/* ----------------
+ *     Form_pg_language corresponds to a pointer to a tuple with
+ *     the format of pg_language relation.
+ * ----------------
+ */
+typedef FormData_pg_language   *Form_pg_language;
+
+/* ----------------
+ *     compiler constants for pg_language
+ * ----------------
+ */
+#define Natts_pg_language              2
+#define Anum_pg_language_lanname       1
+#define Anum_pg_language_lancompiler   2
+
+/* ----------------
+ *     initial contents of pg_language
+ * ----------------
+ */
+
+DATA(insert OID = 11 ( internal "n/a" ));
+#define INTERNALlanguageId 11
+DATA(insert OID = 12 ( lisp "/usr/ucb/liszt" ));
+DATA(insert OID = 13 ( "C" "/bin/cc" ));
+#define ClanguageId 13
+DATA(insert OID = 14 ( "sql" "postgres"));
+#define SQLlanguageId 14
+
+    
+#endif /* PG_LANGUAGE_H */
+
+
+
+
+
+
+
diff --git a/src/backend/catalog/pg_listener.h b/src/backend/catalog/pg_listener.h
new file mode 100644 (file)
index 0000000..704716a
--- /dev/null
@@ -0,0 +1,56 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_listener.h--
+ *    Asynchronous notification
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_LISTENER_H
+#define PG_LISTENER_H
+
+/* ----------------
+ *      postgres.h contains the system type definintions and the
+ *      CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *      can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------------------------------------------------------
+ *      pg_listener definition.
+ *
+ *      cpp turns this into typedef struct FormData_pg_listener
+ * ----------------------------------------------------------------
+ */
+
+CATALOG(pg_listener) {
+    NameData       relname;
+    int4         listenerpid;
+    int4         notification;
+} FormData_pg_listener;
+
+/* ----------------
+ *      compiler constants for pg_listener
+ * ----------------
+ */
+#define Natts_pg_listener                       3
+#define Anum_pg_listener_relname                1
+#define Anum_pg_listener_pid                    2
+#define Anum_pg_listener_notify                 3
+
+/* ----------------
+ *      initial contents of pg_listener are NOTHING.
+ * ----------------
+ */
+
+
+#endif /* PG_LISTENER_H */
diff --git a/src/backend/catalog/pg_log.h b/src/backend/catalog/pg_log.h
new file mode 100644 (file)
index 0000000..2ba091c
--- /dev/null
@@ -0,0 +1,40 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_log.h--
+ *    the system log relation "pg_log" is not a "heap" relation.
+ *    it is automatically created by the transam/ code and the
+ *    information here is all bogus and is just here to make the
+ *    relcache code happy.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    The structures and macros used by the transam/ code
+ *    to access pg_log should some day go here -cim 6/18/90
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_LOG_H
+#define PG_LOG_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+CATALOG(pg_log) BOOTSTRAP {
+    Oid        logfoo;
+} FormData_pg_log;
+
+typedef FormData_pg_log        *Form_pg_log;
+
+#define Natts_pg_log           1
+#define Anum_pg_log_logfoo     1
+
+#endif /* PG_LOG_H */
diff --git a/src/backend/catalog/pg_magic.h b/src/backend/catalog/pg_magic.h
new file mode 100644 (file)
index 0000000..4429237
--- /dev/null
@@ -0,0 +1,54 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_magic.h--
+ *    definition of the system "magic" relation (pg_magic)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_MAGIC_H
+#define PG_MAGIC_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_magic definition.  cpp turns this into
+ *     typedef struct FormData_pg_magic
+ * ----------------
+ */ 
+CATALOG(pg_magic) BOOTSTRAP {
+    NameData   magname;
+    NameData   magvalue;
+} FormData_pg_magic;
+
+/* ----------------
+ *     Form_pg_magic corresponds to a pointer to a tuple with
+ *     the format of pg_magic relation.
+ * ----------------
+ */
+typedef FormData_pg_magic      *Form_pg_magic;
+
+/* ----------------
+ *     compiler constants for pg_magic
+ * ----------------
+ */
+#define Natts_pg_magic                 2
+#define Anum_pg_magic_magname          1
+#define Anum_pg_magic_magvalue         2
+
+#endif /* PG_MAGIC_H */
diff --git a/src/backend/catalog/pg_opclass.h b/src/backend/catalog/pg_opclass.h
new file mode 100644 (file)
index 0000000..0523003
--- /dev/null
@@ -0,0 +1,85 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_opclass.h--
+ *    definition of the system "opclass" relation (pg_opclass)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_OPCLASS_H
+#define PG_OPCLASS_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_opclass definition.  cpp turns this into
+ *     typedef struct FormData_pg_opclass
+ * ----------------
+ */ 
+
+CATALOG(pg_opclass) {
+    NameData opcname;
+} FormData_pg_opclass;
+
+/* ----------------
+ *     Form_pg_opclass corresponds to a pointer to a tuple with
+ *     the format of pg_opclass relation.
+ * ----------------
+ */
+typedef FormData_pg_opclass    *Form_pg_opclass;
+
+/* ----------------
+ *     compiler constants for pg_opclass
+ * ----------------
+ */
+#define Natts_pg_opclass               1
+#define Anum_pg_opclass_opcname                1
+
+/* ----------------
+ *     initial contents of pg_opclass
+ * ----------------
+ */
+
+DATA(insert OID = 406 (    char2_ops ));
+DATA(insert OID = 407 (    char4_ops ));
+DATA(insert OID = 408 (    char8_ops ));
+DATA(insert OID = 409 (    name_ops ));
+DATA(insert OID = 421 (    int2_ops ));
+DATA(insert OID = 422 (    box_ops ));
+DATA(insert OID = 423 (    float8_ops ));
+DATA(insert OID = 424 (    int24_ops ));
+DATA(insert OID = 425 (    int42_ops ));
+DATA(insert OID = 426 (    int4_ops ));
+#define INT4_OPS_OID 426
+DATA(insert OID = 427 (    oid_ops ));
+DATA(insert OID = 428 (    float4_ops ));
+DATA(insert OID = 429 (    char_ops ));
+DATA(insert OID = 430 (    char16_ops ));
+DATA(insert OID = 431 (    text_ops ));
+DATA(insert OID = 432 (    abstime_ops ));
+DATA(insert OID = 433 (    bigbox_ops));
+DATA(insert OID = 434 (    poly_ops));
+DATA(insert OID = 435 (    oidint4_ops));
+DATA(insert OID = 436 (    oidname_ops));
+DATA(insert OID = 437 (    oidint2_ops));
+DATA(insert OID = 1076 (   bpchar_ops));
+DATA(insert OID = 1077 (   varchar_ops));
+DATA(insert OID = 1114 (   date_ops));
+DATA(insert OID = 1115 (   time_ops));
+
+#endif /* PG_OPCLASS_H */
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
new file mode 100644 (file)
index 0000000..23e2e99
--- /dev/null
@@ -0,0 +1,1077 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_operator.c--
+ *    routines to support manipulation of the pg_operator relation
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    these routines moved here from commands/define.c and somewhat cleaned up.
+ *     
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "access/htup.h"
+#include "utils/rel.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "parser/catalog_utils.h"
+
+#include "catalog/catname.h"
+#include "utils/syscache.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_proc.h"
+#include "storage/bufmgr.h"
+
+#include "fmgr.h"
+
+static Oid OperatorGetWithOpenRelation(Relation pg_operator_desc,
+                                      char *operatorName,
+                                      Oid leftObjectId,
+                                      Oid rightObjectId );
+static Oid OperatorGet(char *operatorName,
+                      char *leftTypeName,
+                      char *rightTypeName );
+
+static Oid OperatorShellMakeWithOpenRelation(Relation pg_operator_desc,
+                                            char *operatorName, 
+                                            Oid leftObjectId, 
+                                            Oid rightObjectId );
+static Oid OperatorShellMake(char *operatorName,
+                            char *leftTypeName,
+                            char *rightTypeName );
+
+static void OperatorDef(char *operatorName,
+                       int definedOK,
+                       char *leftTypeName,
+                       char *rightTypeName,
+                       char *procedureName,
+                       uint16 precedence,
+                       bool isLeftAssociative,
+                       char *commutatorName,
+                       char *negatorName,
+                       char *restrictionName,
+                       char *oinName,
+                       bool canHash,
+                       char *leftSortName,
+                       char *rightSortName );
+static void OperatorUpd(Oid baseId , Oid commId , Oid negId );
+     
+/* ----------------------------------------------------------------
+ *     OperatorGetWithOpenRelation
+ *
+ *     preforms a scan on pg_operator for an operator tuple
+ *     with given name and left/right type oids.
+ * ----------------------------------------------------------------
+ *    pg_operator_desc -- reldesc for pg_operator
+ *    operatorName     -- name of operator to fetch
+ *    leftObjectId     -- left oid of operator to fetch
+ *    rightObjectId    -- right oid of operator to fetch
+ */
+static Oid
+OperatorGetWithOpenRelation(Relation pg_operator_desc,
+                           char *operatorName,
+                           Oid leftObjectId,
+                           Oid rightObjectId)
+{
+    HeapScanDesc       pg_operator_scan;
+    Oid                operatorObjectId;
+    HeapTuple          tup;
+    
+    static ScanKeyData opKey[3] = {
+       { 0, Anum_pg_operator_oprname,  NameEqualRegProcedure },
+       { 0, Anum_pg_operator_oprleft,  ObjectIdEqualRegProcedure },
+       { 0, Anum_pg_operator_oprright, ObjectIdEqualRegProcedure },
+    };
+    
+    fmgr_info(NameEqualRegProcedure,
+             &opKey[0].sk_func, &opKey[0].sk_nargs);
+    fmgr_info(ObjectIdEqualRegProcedure,
+             &opKey[1].sk_func, &opKey[1].sk_nargs);
+    fmgr_info(ObjectIdEqualRegProcedure,
+             &opKey[2].sk_func, &opKey[2].sk_nargs);
+    
+    /* ----------------
+     * form scan key
+     * ----------------
+     */
+    opKey[0].sk_argument = PointerGetDatum(operatorName);
+    opKey[1].sk_argument = ObjectIdGetDatum(leftObjectId);
+    opKey[2].sk_argument = ObjectIdGetDatum(rightObjectId);
+    
+    /* ----------------
+     * begin the scan
+     * ----------------
+     */
+    pg_operator_scan = heap_beginscan(pg_operator_desc,
+                                     0,
+                                     SelfTimeQual,
+                                     3,
+                                     opKey);
+    
+    /* ----------------
+     * fetch the operator tuple, if it exists, and determine
+     *  the proper return oid value.
+     * ----------------
+     */
+    tup = heap_getnext(pg_operator_scan, 0, (Buffer *) 0);
+    operatorObjectId = HeapTupleIsValid(tup) ? tup->t_oid : InvalidOid;
+    
+    /* ----------------
+     * close the scan and return the oid.
+     * ----------------
+     */
+    heap_endscan(pg_operator_scan);
+    
+    return
+       operatorObjectId;
+}
+
+/* ----------------------------------------------------------------
+ *     OperatorGet
+ *
+ *     finds the operator associated with the specified name
+ *     and left and right type names.
+ * ----------------------------------------------------------------
+ */
+static Oid
+OperatorGet(char *operatorName,
+           char *leftTypeName,
+           char *rightTypeName)
+{
+    Relation   pg_operator_desc;
+    
+    Oid                operatorObjectId;
+    Oid        leftObjectId = InvalidOid;
+    Oid                rightObjectId = InvalidOid;
+    bool       leftDefined = false;
+    bool       rightDefined = false;
+    
+    /* ----------------
+     * look up the operator types.
+     *
+     *  Note: types must be defined before operators
+     * ----------------
+     */
+    if (leftTypeName) {
+       leftObjectId = TypeGet(leftTypeName, &leftDefined);
+       
+       if (!OidIsValid(leftObjectId) || !leftDefined)
+           elog(WARN, "OperatorGet: left type '%s' nonexistent",leftTypeName);
+    }
+    
+    if (rightTypeName) {
+       rightObjectId = TypeGet(rightTypeName, &rightDefined);
+       
+       if (!OidIsValid(rightObjectId) || !rightDefined)
+           elog(WARN, "OperatorGet: right type '%s' nonexistent",
+                rightTypeName);
+    }
+    
+    if (!((OidIsValid(leftObjectId) && leftDefined) ||
+         (OidIsValid(rightObjectId) && rightDefined)))
+       elog(WARN, "OperatorGet: no argument types??");
+    
+    /* ----------------
+     * open the pg_operator relation
+     * ----------------
+     */
+    pg_operator_desc = heap_openr(OperatorRelationName);
+    
+    /* ----------------
+     * get the oid for the operator with the appropriate name
+     *  and left/right types.
+     * ----------------
+     */
+    operatorObjectId = OperatorGetWithOpenRelation(pg_operator_desc,
+                                                  operatorName,
+                                                  leftObjectId,
+                                                  rightObjectId);
+    
+    /* ----------------
+     * close the relation and return the operator oid.
+     * ----------------
+     */
+    heap_close(pg_operator_desc);
+    
+    return
+       operatorObjectId;
+}
+
+/* ----------------------------------------------------------------
+ *     OperatorShellMakeWithOpenRelation
+ *
+ * ----------------------------------------------------------------
+ */
+static Oid
+OperatorShellMakeWithOpenRelation(Relation pg_operator_desc,
+                                 char *operatorName,
+                                 Oid leftObjectId,
+                                 Oid rightObjectId)
+{
+    register int       i;
+    HeapTuple          tup;
+    Datum               values[ Natts_pg_operator ];
+    char               nulls[ Natts_pg_operator ];
+    Oid                operatorObjectId;
+    TupleDesc    tupDesc;
+    
+    /* ----------------
+     * initialize our nulls[] and values[] arrays
+     * ----------------
+     */
+    for (i = 0; i < Natts_pg_operator; ++i) {
+       nulls[i] = ' ';
+       values[i] = (Datum)NULL;        /* redundant, but safe */
+    }
+    
+    /* ----------------
+     * initialize values[] with the type name and 
+     * ----------------
+     */
+    i = 0;
+    values[i++] =  PointerGetDatum(operatorName);
+    values[i++] =  ObjectIdGetDatum(InvalidOid);
+    values[i++] =  (Datum) (uint16) 0;
+    
+    values[i++] = (Datum)'b';  /* fill oprkind with a bogus value */
+    
+    values[i++] = (Datum) (bool) 0;
+    values[i++] = (Datum) (bool) 0;
+    values[i++] =  ObjectIdGetDatum(leftObjectId);  /* <-- left oid */
+    values[i++] =  ObjectIdGetDatum(rightObjectId); /* <-- right oid */
+    values[i++] =  ObjectIdGetDatum(InvalidOid);
+    values[i++] =  ObjectIdGetDatum(InvalidOid);
+    values[i++] =  ObjectIdGetDatum(InvalidOid);
+    values[i++] =  ObjectIdGetDatum(InvalidOid);
+    values[i++] =  ObjectIdGetDatum(InvalidOid);
+    values[i++] =  ObjectIdGetDatum(InvalidOid);
+    values[i++] =  ObjectIdGetDatum(InvalidOid);
+    values[i++] =  ObjectIdGetDatum(InvalidOid);
+    
+    /* ----------------
+     * create a new operator tuple
+     * ----------------
+     */
+    tupDesc = pg_operator_desc->rd_att;
+
+    tup = heap_formtuple(tupDesc,
+                        values,
+                        nulls);
+    
+    /* ----------------
+     * insert our "shell" operator tuple and
+     *  close the relation
+     * ----------------
+     */
+    heap_insert(pg_operator_desc, tup);
+    operatorObjectId = tup->t_oid;
+    
+    /* ----------------
+     * free the tuple and return the operator oid
+     * ----------------
+     */
+    pfree(tup);
+    
+    return
+       operatorObjectId;   
+}
+
+/* ----------------------------------------------------------------
+ *     OperatorShellMake
+ *
+ *     Specify operator name and left and right type names,
+ *     fill an operator struct with this info and NULL's,
+ *     call heap_insert and return the Oid
+ *     to the caller.
+ * ----------------------------------------------------------------
+ */
+static Oid
+OperatorShellMake(char *operatorName,
+                 char *leftTypeName,
+                 char *rightTypeName)
+{    
+    Relation   pg_operator_desc;
+    Oid                operatorObjectId;
+    
+    Oid        leftObjectId = InvalidOid;
+    Oid                rightObjectId = InvalidOid;
+    bool       leftDefined = false;
+    bool       rightDefined = false;
+    
+    /* ----------------
+     * get the left and right type oid's for this operator
+     * ----------------
+     */
+    if (leftTypeName)
+       leftObjectId = TypeGet(leftTypeName, &leftDefined);
+    
+    if (rightTypeName)
+       rightObjectId = TypeGet(rightTypeName, &rightDefined);
+    
+    if (!((OidIsValid(leftObjectId) && leftDefined) ||
+         (OidIsValid(rightObjectId) && rightDefined)))
+       elog(WARN, "OperatorShellMake: no valid argument types??");
+    
+    /* ----------------
+     * open pg_operator
+     * ----------------
+     */
+    pg_operator_desc = heap_openr(OperatorRelationName);
+    
+    /* ----------------
+     * add a "shell" operator tuple to the operator relation
+     *  and recover the shell tuple's oid.
+     * ----------------
+     */
+    operatorObjectId =
+       OperatorShellMakeWithOpenRelation(pg_operator_desc,
+                                         operatorName,
+                                         leftObjectId,
+                                         rightObjectId);
+    /* ----------------
+     * close the operator relation and return the oid.
+     * ----------------
+     */
+    heap_close(pg_operator_desc);
+    
+    return
+       operatorObjectId;
+}
+
+/* --------------------------------
+ * OperatorDef
+ *
+ * This routine gets complicated because it allows the user to
+ * specify operators that do not exist.  For example, if operator
+ * "op" is being defined, the negator operator "negop" and the
+ * commutator "commop" can also be defined without specifying
+ * any information other than their names.  Since in order to
+ * add "op" to the PG_OPERATOR catalog, all the Oid's for these
+ * operators must be placed in the fields of "op", a forward
+ * declaration is done on the commutator and negator operators.
+ * This is called creating a shell, and its main effect is to
+ * create a tuple in the PG_OPERATOR catalog with minimal
+ * information about the operator (just its name and types).
+ * Forward declaration is used only for this purpose, it is
+ * not available to the user as it is for type definition.
+ *
+ * Algorithm:
+ * 
+ * check if operator already defined 
+ *    if so issue error if not definedOk, this is a duplicate
+ *    but if definedOk, save the Oid -- filling in a shell
+ * get the attribute types from relation descriptor for pg_operator
+ * assign values to the fields of the operator:
+ *   operatorName
+ *   owner id (simply the user id of the caller)
+ *   precedence
+ *   operator "kind" either "b" for binary or "l" for left unary
+ *   isLeftAssociative boolean
+ *   canHash boolean
+ *   leftTypeObjectId -- type must already be defined
+ *   rightTypeObjectId -- this is optional, enter ObjectId=0 if none specified
+ *   resultType -- defer this, since it must be determined from
+ *                 the pg_procedure catalog
+ *   commutatorObjectId -- if this is NULL, enter ObjectId=0
+ *                    else if this already exists, enter it's ObjectId
+ *                    else if this does not yet exist, and is not
+ *                      the same as the main operatorName, then create
+ *                      a shell and enter the new ObjectId
+ *                    else if this does not exist but IS the same
+ *                      name as the main operator, set the ObjectId=0.
+ *                      Later OperatorCreate will make another call
+ *                      to OperatorDef which will cause this field
+ *                      to be filled in (because even though the names
+ *                      will be switched, they are the same name and
+ *                      at this point this ObjectId will then be defined)
+ *   negatorObjectId   -- same as for commutatorObjectId
+ *   leftSortObjectId  -- same as for commutatorObjectId
+ *   rightSortObjectId -- same as for commutatorObjectId
+ *   operatorProcedure -- must access the pg_procedure catalog to get the
+ *                ObjectId of the procedure that actually does the operator
+ *                actions this is required.  Do an amgetattr to find out the
+ *                 return type of the procedure 
+ *   restrictionProcedure -- must access the pg_procedure catalog to get
+ *                 the ObjectId but this is optional
+ *   joinProcedure -- same as restrictionProcedure
+ * now either insert or replace the operator into the pg_operator catalog
+ * if the operator shell is being filled in
+ *   access the catalog in order to get a valid buffer
+ *   create a tuple using ModifyHeapTuple
+ *   get the t_ctid from the modified tuple and call RelationReplaceHeapTuple
+ * else if a new operator is being created
+ *   create a tuple using heap_formtuple
+ *   call heap_insert
+ * --------------------------------
+ *     "X" indicates an optional argument (i.e. one that can be NULL)
+ *     operatorName;           -- operator name
+ *     definedOK;              -- operator can already have an oid?
+ *     leftTypeName;           -- X left type name
+ *     rightTypeName;          -- X right type name
+ *     procedureName;          -- procedure oid for operator code 
+ *     precedence;             -- operator precedence 
+ *     isLeftAssociative;      -- operator is left associative?
+ *     commutatorName;         -- X commutator operator name
+ *     negatorName;            -- X negator operator name
+ *     restrictionName;        -- X restriction sel. procedure name
+ *     joinName;               -- X join sel. procedure name
+ *     canHash;                -- possible hash operator?
+ *     leftSortName;           -- X left sort operator
+ *     rightSortName;          -- X right sort operator
+ */
+static void
+OperatorDef(char *operatorName,
+           int definedOK,
+           char *leftTypeName,
+           char *rightTypeName,
+           char *procedureName,
+           uint16 precedence,
+           bool isLeftAssociative,
+           char *commutatorName,
+           char *negatorName,
+           char *restrictionName,
+           char *joinName,
+           bool canHash,
+           char *leftSortName,
+           char *rightSortName)
+{
+    register   i, j;
+    Relation   pg_operator_desc;
+    
+    HeapScanDesc       pg_operator_scan;
+    HeapTuple  tup;
+    Buffer     buffer;
+    ItemPointerData    itemPointerData;
+    char       nulls[ Natts_pg_operator ];
+    char       replaces[ Natts_pg_operator ];
+    Datum       values[ Natts_pg_operator ];
+    Oid        other_oid;
+    Oid                operatorObjectId;
+    Oid                leftTypeId = InvalidOid;
+    Oid                rightTypeId = InvalidOid;
+    Oid                commutatorId = InvalidOid;
+    Oid                negatorId = InvalidOid;
+    bool       leftDefined = false;
+    bool       rightDefined = false;
+    char        *name[4];
+    Oid                typeId[8];
+    int                nargs;
+    TupleDesc   tupDesc;
+    
+    static ScanKeyData opKey[3] = {
+       { 0, Anum_pg_operator_oprname, NameEqualRegProcedure },
+       { 0, Anum_pg_operator_oprleft, ObjectIdEqualRegProcedure },
+       { 0, Anum_pg_operator_oprright, ObjectIdEqualRegProcedure },
+    };
+    
+    fmgr_info(NameEqualRegProcedure,
+             &opKey[0].sk_func, &opKey[0].sk_nargs);
+    fmgr_info(ObjectIdEqualRegProcedure,
+             &opKey[1].sk_func, &opKey[1].sk_nargs);
+    fmgr_info(ObjectIdEqualRegProcedure,
+             &opKey[2].sk_func, &opKey[2].sk_nargs);
+    
+    operatorObjectId =         OperatorGet(operatorName,
+                                   leftTypeName,
+                                   rightTypeName);
+    
+    if (OidIsValid(operatorObjectId) && !definedOK)
+       elog(WARN, "OperatorDef: operator \"%-.*s\" already defined",
+            NAMEDATALEN, operatorName); 
+    
+    if (leftTypeName)
+       leftTypeId = TypeGet(leftTypeName, &leftDefined);
+    
+    if (rightTypeName)
+       rightTypeId = TypeGet(rightTypeName, &rightDefined);
+    
+    if (!((OidIsValid(leftTypeId && leftDefined)) ||
+         (OidIsValid(rightTypeId && rightDefined))))
+       elog(WARN, "OperatorGet: no argument types??");
+    
+    for (i = 0; i < Natts_pg_operator; ++i) {
+       values[i] = (Datum)NULL;
+       replaces[i] = 'r';
+       nulls[i] = ' ';
+    }
+    
+    /* ----------------
+     * Look up registered procedures -- find the return type
+     * of procedureName to place in "result" field.
+     * Do this before shells are created so we don't
+     * have to worry about deleting them later.
+     * ----------------
+     */
+    memset(typeId, 0, 8 * sizeof(Oid));
+    if (!leftTypeName) {
+       typeId[0] = rightTypeId;
+       nargs = 1;
+    }
+    else if (!rightTypeName) {
+       typeId[0] = leftTypeId;
+       nargs = 1;
+    }
+    else {
+       typeId[0] = leftTypeId;
+       typeId[1] = rightTypeId;
+       nargs = 2;
+    }
+    tup = SearchSysCacheTuple(PRONAME,
+                            PointerGetDatum(procedureName),
+                            Int32GetDatum(nargs),
+                            PointerGetDatum(typeId),
+                             0);
+    
+    if (!PointerIsValid(tup))
+       func_error("OperatorDef", procedureName, nargs, (int*)typeId);
+    
+    values[ Anum_pg_operator_oprcode-1 ] =  ObjectIdGetDatum(tup->t_oid);
+    values[ Anum_pg_operator_oprresult-1 ] =
+        ObjectIdGetDatum(((Form_pg_proc)
+                          GETSTRUCT(tup))->prorettype);
+    
+    /* ----------------
+     * find restriction
+     * ----------------
+     */
+    if (restrictionName) {     /* optional */
+        memset(typeId, 0, 8 * sizeof(Oid)); 
+       typeId[0] = OIDOID;             /* operator OID */
+       typeId[1] = OIDOID;             /* relation OID */
+       typeId[2] = INT2OID;            /* attribute number */
+       typeId[3] = 0;                  /* value - can be any type  */
+       typeId[4] = INT4OID;            /* flags - left or right selectivity */
+       tup = SearchSysCacheTuple(PRONAME,
+                                 PointerGetDatum(restrictionName),
+                                 Int32GetDatum(5),
+                                 ObjectIdGetDatum(typeId),
+                                 0);
+       if (!HeapTupleIsValid(tup))
+           func_error("OperatorDef", restrictionName, 5, (int*)typeId);
+       
+       values[ Anum_pg_operator_oprrest-1 ] = ObjectIdGetDatum(tup->t_oid);
+    } else
+       values[ Anum_pg_operator_oprrest-1 ] = ObjectIdGetDatum(InvalidOid);
+    
+    /* ----------------
+     * find join - only valid for binary operators
+     * ----------------
+     */
+    if (joinName) {            /* optional */
+       memset(typeId, 0, 8 * sizeof(Oid));
+       typeId[0] = OIDOID;             /* operator OID */
+       typeId[1] = OIDOID;             /* relation OID 1 */
+       typeId[2] = INT2OID;            /* attribute number 1 */
+       typeId[3] = OIDOID;             /* relation OID 2 */
+       typeId[4] = INT2OID;            /* attribute number 2 */
+       
+       tup = SearchSysCacheTuple(PRONAME,
+                                 PointerGetDatum(joinName),
+                                 Int32GetDatum(5),
+                                 Int32GetDatum(typeId),
+                                 0);
+       if (!HeapTupleIsValid(tup))
+           func_error("OperatorDef", joinName, 5, (int*)typeId);
+       
+       values[Anum_pg_operator_oprjoin-1] = ObjectIdGetDatum(tup->t_oid);
+    } else
+       values[Anum_pg_operator_oprjoin-1] = ObjectIdGetDatum(InvalidOid);
+    
+    /* ----------------
+     * set up values in the operator tuple
+     * ----------------
+     */
+    i = 0;
+    values[i++] = PointerGetDatum(operatorName);
+    values[i++] = Int32GetDatum(GetUserId());
+    values[i++] = UInt16GetDatum(precedence);
+    values[i++] = leftTypeName ?  (rightTypeName ? 'b' : 'r') : 'l';
+    values[i++] = Int8GetDatum(isLeftAssociative);
+    values[i++] = Int8GetDatum(canHash);
+    values[i++] = ObjectIdGetDatum(leftTypeId);
+    values[i++] = ObjectIdGetDatum(rightTypeId);
+    
+    ++i;       /* Skip "prorettype", this was done above */
+    
+    /*
+     * Set up the other operators.  If they do not currently exist,
+     * set up shells in order to get ObjectId's and call OperatorDef
+     * again later to fill in the shells.
+     */
+    name[0] = commutatorName;
+    name[1] = negatorName;
+    name[2] = leftSortName;
+    name[3] = rightSortName;
+    
+    for (j = 0; j < 4; ++j) {
+       if (name[j]) {
+           
+           /* for the commutator, switch order of arguments */
+           if (j == 0) {
+               other_oid = OperatorGet(name[j], rightTypeName,leftTypeName);
+               commutatorId = other_oid;
+           } else {
+               other_oid = OperatorGet(name[j], leftTypeName,rightTypeName);
+               if (j == 1)
+                   negatorId = other_oid;
+           }
+           
+           if (OidIsValid(other_oid)) /* already in catalogs */
+               values[i++] = ObjectIdGetDatum(other_oid);
+           else if (strcmp(operatorName, name[j]) != 0) {
+               /* not in catalogs, different from operator */
+               
+               /* for the commutator, switch order of arguments */
+               if (j == 0) {
+                   other_oid = OperatorShellMake(name[j],
+                                                 rightTypeName,
+                                                 leftTypeName);
+               } else {
+                   other_oid = OperatorShellMake(name[j],
+                                                 leftTypeName,
+                                                 rightTypeName);
+               }
+               
+               if (!OidIsValid(other_oid))
+                   elog(WARN,
+                        "OperatorDef: can't create operator '%s'",
+                        name[j]);     
+               values[i++] =  ObjectIdGetDatum(other_oid);
+               
+           } else /* not in catalogs, same as operator ??? */
+               values[i++] = ObjectIdGetDatum(InvalidOid);
+           
+       } else  /* new operator is optional */
+           values[i++] = ObjectIdGetDatum(InvalidOid);
+    }
+    
+    /* last three fields were filled in first */
+    
+    /*
+     * If we are adding to an operator shell, get its t_ctid and a
+     * buffer.
+     */
+    pg_operator_desc = heap_openr(OperatorRelationName);
+    
+    if (operatorObjectId) {
+       opKey[0].sk_argument = PointerGetDatum(operatorName);
+       opKey[1].sk_argument = ObjectIdGetDatum(leftTypeId);
+       opKey[2].sk_argument = ObjectIdGetDatum(rightTypeId);
+       
+       pg_operator_scan = heap_beginscan(pg_operator_desc,
+                                         0,
+                                         SelfTimeQual,
+                                         3,
+                                         opKey);
+       
+       tup = heap_getnext(pg_operator_scan, 0, &buffer);
+       if (HeapTupleIsValid(tup)) {
+           tup = heap_modifytuple(tup,
+                                  buffer,
+                                  pg_operator_desc,
+                                  values,
+                                  nulls,
+                                  replaces);
+           
+           ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+           setheapoverride(true);
+           (void) heap_replace(pg_operator_desc, &itemPointerData, tup);
+           setheapoverride(false);
+       } else
+           elog(WARN, "OperatorDef: no operator %d", other_oid);
+       
+       heap_endscan(pg_operator_scan);
+       
+    } else {
+       tupDesc = pg_operator_desc->rd_att;
+       tup = heap_formtuple(tupDesc, values, nulls);
+       
+       heap_insert(pg_operator_desc, tup);
+       operatorObjectId = tup->t_oid;
+    }
+    
+    heap_close(pg_operator_desc);
+    
+    /*
+     *  It's possible that we're creating a skeleton operator here for
+     *  the commute or negate attributes of a real operator.  If we are,
+     *  then we're done.  If not, we may need to update the negator and
+     *  commutator for this attribute.  The reason for this is that the
+     *  user may want to create two operators (say < and >=).  When he
+     *  defines <, if he uses >= as the negator or commutator, he won't
+     *  be able to insert it later, since (for some reason) define operator
+     *  defines it for him.  So what he does is to define > without a
+     *  negator or commutator.  Then he defines >= with < as the negator
+     *  and commutator.  As a side effect, this will update the > tuple
+     *  if it has no commutator or negator defined.
+     *
+     *  Alstublieft, Tom Vijlbrief.
+     */
+    if (!definedOK)
+       OperatorUpd(operatorObjectId, commutatorId, negatorId);
+}
+
+/* ----------------------------------------------------------------
+ * OperatorUpd
+ *
+ *  For a given operator, look up its negator and commutator operators.
+ *  If they are defined, but their negator and commutator operators
+ *  (respectively) are not, then use the new operator for neg and comm.
+ *  This solves a problem for users who need to insert two new operators
+ *  which are the negator or commutator of each other.
+ * ---------------------------------------------------------------- 
+ */
+static void
+OperatorUpd(Oid baseId, Oid commId, Oid negId)
+{
+    register           i;
+    Relation           pg_operator_desc;
+    HeapScanDesc       pg_operator_scan;
+    HeapTuple          tup;
+    Buffer             buffer;
+    ItemPointerData    itemPointerData;
+    char               nulls[ Natts_pg_operator ];
+    char               replaces[ Natts_pg_operator ];
+    Datum               values[ Natts_pg_operator ];
+    
+    static ScanKeyData opKey[1] = {
+       { 0, ObjectIdAttributeNumber, ObjectIdEqualRegProcedure },
+    };
+    
+    fmgr_info(ObjectIdEqualRegProcedure,
+             &opKey[0].sk_func, &opKey[0].sk_nargs);
+    
+    for (i = 0; i < Natts_pg_operator; ++i) {
+       values[i] =  (Datum)NULL;
+       replaces[i] = ' ';
+       nulls[i] = ' ';
+    }
+    
+    pg_operator_desc = heap_openr(OperatorRelationName);
+    
+    /* check and update the commutator, if necessary */
+    opKey[0].sk_argument = ObjectIdGetDatum(commId);
+    
+    pg_operator_scan = heap_beginscan(pg_operator_desc,
+                                     0,
+                                     SelfTimeQual,
+                                     1,
+                                     opKey);
+    
+    tup = heap_getnext(pg_operator_scan, 0, &buffer);
+    
+    /* if the commutator and negator are the same operator, do one update */
+    if (commId == negId) {
+       if (HeapTupleIsValid(tup)) {
+           OperatorTupleForm t;
+           
+           t = (OperatorTupleForm) GETSTRUCT(tup);
+           if (!OidIsValid(t->oprcom)
+               || !OidIsValid(t->oprnegate)) {
+               
+               if (!OidIsValid(t->oprnegate)) {
+                   values[Anum_pg_operator_oprnegate - 1] =
+                       ObjectIdGetDatum(baseId);
+                   replaces[ Anum_pg_operator_oprnegate - 1 ] = 'r';
+               }
+               
+               if (!OidIsValid(t->oprcom)) {
+                   values[Anum_pg_operator_oprcom - 1] =
+                       ObjectIdGetDatum(baseId);
+                   replaces[ Anum_pg_operator_oprcom - 1 ] = 'r';
+               }
+               
+               tup = heap_modifytuple(tup,
+                                      buffer,
+                                      pg_operator_desc,
+                                      values,
+                                      nulls,
+                                      replaces);
+               
+               ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+               
+               setheapoverride(true);
+               (void) heap_replace(pg_operator_desc, &itemPointerData, tup);
+               setheapoverride(false);
+
+           }
+       }
+       heap_endscan(pg_operator_scan);
+       
+       heap_close(pg_operator_desc);
+       
+       /* release the buffer properly */
+       if (BufferIsValid(buffer))
+           ReleaseBuffer(buffer);
+
+       return;
+    }
+    
+    /* if commutator and negator are different, do two updates */
+    if (HeapTupleIsValid(tup) &&
+       !(OidIsValid(((OperatorTupleForm) GETSTRUCT(tup))->oprcom))) {
+       values[ Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId);
+       replaces[ Anum_pg_operator_oprcom - 1] = 'r';
+       tup = heap_modifytuple(tup,
+                              buffer,
+                              pg_operator_desc,
+                              values,
+                              nulls,
+                              replaces);
+       
+       ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+       setheapoverride(true);
+       (void) heap_replace(pg_operator_desc, &itemPointerData, tup);
+       setheapoverride(false);
+       
+       values[ Anum_pg_operator_oprcom - 1 ] = (Datum)NULL;
+       replaces[ Anum_pg_operator_oprcom - 1 ] = ' ';
+
+       /* release the buffer properly */
+       if (BufferIsValid(buffer))
+           ReleaseBuffer(buffer);
+
+    }
+    
+    /* check and update the negator, if necessary */
+    opKey[0].sk_argument = ObjectIdGetDatum(negId);
+    
+    pg_operator_scan = heap_beginscan(pg_operator_desc,
+                                     0,
+                                     SelfTimeQual,
+                                     1,
+                                     opKey);
+    
+    tup = heap_getnext(pg_operator_scan, 0, &buffer);
+    if (HeapTupleIsValid(tup) &&
+       !(OidIsValid(((OperatorTupleForm) GETSTRUCT(tup))->oprnegate))) {
+       values[Anum_pg_operator_oprnegate-1] = ObjectIdGetDatum(baseId);
+       replaces[ Anum_pg_operator_oprnegate - 1 ] = 'r';
+       tup = heap_modifytuple(tup,
+                              buffer,
+                              pg_operator_desc,
+                              values,
+                              nulls,
+                              replaces);
+       
+       ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+       
+       setheapoverride(true);
+       (void) heap_replace(pg_operator_desc, &itemPointerData, tup);
+       setheapoverride(false);
+    }
+
+    /* release the buffer properly */
+    if (BufferIsValid(buffer))
+       ReleaseBuffer(buffer);
+
+    heap_endscan(pg_operator_scan);
+    
+    heap_close(pg_operator_desc);
+}
+
+
+/* ----------------------------------------------------------------
+ * OperatorCreate
+ *
+ * Algorithm:
+ *
+ *  Since the commutator, negator, leftsortoperator, and rightsortoperator
+ *  can be defined implicitly through OperatorCreate, must check before
+ *  the main operator is added to see if they already exist.  If they
+ *  do not already exist, OperatorDef makes a "shell" for each undefined
+ *  one, and then OperatorCreate must call OperatorDef again to fill in
+ *  each shell.  All this is necessary in order to get the right ObjectId's 
+ *  filled into the right fields.
+ *
+ *  The "definedOk" flag indicates that OperatorDef can be called on
+ *  the operator even though it already has an entry in the PG_OPERATOR
+ *  relation.  This allows shells to be filled in.  The user cannot
+ *  forward declare operators, this is strictly an internal capability.
+ *
+ *  When the shells are filled in by subsequent calls to OperatorDef,
+ *  all the fields are the same as the definition of the original operator
+ *  except that the target operator name and the original operatorName
+ *  are switched.  In the case of commutator and negator, special flags
+ *  are set to indicate their status, telling the executor(?) that
+ *  the operands are to be switched, or the outcome of the procedure
+ *  negated.
+ * 
+ * ************************* NOTE NOTE NOTE ******************************
+ *  
+ *  If the execution of this utility is interrupted, the pg_operator
+ *  catalog may be left in an inconsistent state.  Similarly, if
+ *  something is removed from the pg_operator, pg_type, or pg_procedure
+ *  catalog while this is executing, the results may be inconsistent.
+ * ----------------------------------------------------------------
+ *
+ * "X" indicates an optional argument (i.e. one that can be NULL) 
+ *     operatorName;           -- operator name 
+ *     leftTypeName;           -- X left type name 
+ *     rightTypeName;          -- X right type name 
+ *     procedureName;          -- procedure for operator 
+ *     precedence;             -- operator precedence 
+ *     isLeftAssociative;      -- operator is left associative 
+ *     commutatorName;         -- X commutator operator name 
+ *     negatorName;            -- X negator operator name 
+ *     restrictionName;        -- X restriction sel. procedure 
+ *     joinName;               -- X join sel. procedure name 
+ *     canHash;                -- operator hashes 
+ *     leftSortName;           -- X left sort operator 
+ *     rightSortName;          -- X right sort operator 
+ * 
+ */
+void
+OperatorCreate(char *operatorName,
+              char *leftTypeName,
+              char *rightTypeName,
+              char *procedureName,
+              uint16 precedence,
+              bool isLeftAssociative,
+              char *commutatorName,
+              char *negatorName,
+              char *restrictionName,
+              char *joinName,
+              bool canHash,
+              char *leftSortName,
+              char *rightSortName)
+{
+    Oid        commObjectId, negObjectId;
+    Oid        leftSortObjectId, rightSortObjectId;
+    int                definedOK;
+    
+    if (!leftTypeName && !rightTypeName)
+       elog(WARN, "OperatorCreate : at least one of leftarg or rightarg must be defined");
+    
+    /* ----------------
+     * get the oid's of the operator's associated operators, if possible.
+     * ----------------
+     */
+    if (commutatorName)
+       commObjectId = OperatorGet(commutatorName,  /* commute type order */
+                                  rightTypeName,
+                                  leftTypeName);
+    
+    if (negatorName)
+       negObjectId  = OperatorGet(negatorName,
+                                  leftTypeName,
+                                  rightTypeName);
+    
+    if (leftSortName)
+       leftSortObjectId = OperatorGet(leftSortName,
+                                      leftTypeName,
+                                      rightTypeName);
+    
+    if (rightSortName)
+       rightSortObjectId = OperatorGet(rightSortName,
+                                       rightTypeName,
+                                       leftTypeName);
+    
+    /* ----------------
+     *  Use OperatorDef() to define the specified operator and
+     *  also create shells for the operator's associated operators
+     *  if they don't already exist.
+     *
+     * This operator should not be defined yet.
+     * ----------------
+     */
+    definedOK = 0;
+    
+    OperatorDef(operatorName,
+               definedOK,
+               leftTypeName,
+               rightTypeName,
+               procedureName,
+               precedence,
+               isLeftAssociative,
+               commutatorName,
+               negatorName,
+               restrictionName,
+               joinName,
+               canHash,
+               leftSortName,
+               rightSortName);
+    
+    /* ----------------
+     * Now fill in information in the operator's associated
+     *  operators.
+     *
+     *  These operators should be defined or have shells defined.
+     * ----------------
+     */
+    definedOK = 1; 
+    
+    if (!OidIsValid(commObjectId) && commutatorName)
+       OperatorDef(commutatorName,
+                   definedOK,   
+                   leftTypeName,       /* should eventually */
+                   rightTypeName,      /* commute order */  
+                   procedureName,
+                   precedence,
+                   isLeftAssociative,
+                   operatorName,       /* commutator */
+                   negatorName,
+                   restrictionName,
+                   joinName,
+                   canHash,
+                   rightSortName,
+                   leftSortName);
+    
+    if (negatorName && !OidIsValid(negObjectId))
+       OperatorDef(negatorName,
+                   definedOK,
+                   leftTypeName,
+                   rightTypeName,
+                   procedureName,
+                   precedence,
+                   isLeftAssociative,
+                   commutatorName,
+                   operatorName,       /* negator */
+                   restrictionName,
+                   joinName,
+                   canHash,
+                   leftSortName,
+                   rightSortName);
+    
+    if (leftSortName && !OidIsValid(leftSortObjectId))
+       OperatorDef(leftSortName,
+                   definedOK,
+                   leftTypeName,
+                   rightTypeName,
+                   procedureName,
+                   precedence,
+                   isLeftAssociative,
+                   commutatorName,
+                   negatorName,
+                   restrictionName,
+                   joinName,
+                   canHash,
+                   operatorName,       /* left sort */
+                   rightSortName);
+    
+    if (rightSortName && !OidIsValid(rightSortObjectId))
+       OperatorDef(rightSortName,
+                   definedOK,
+                   leftTypeName,
+                   rightTypeName,
+                   procedureName,
+                   precedence,
+                   isLeftAssociative,
+                   commutatorName,
+                   negatorName,
+                   restrictionName,
+                   joinName,
+                   canHash,
+                   leftSortName,
+                   operatorName);      /* right sort */
+}
diff --git a/src/backend/catalog/pg_operator.h b/src/backend/catalog/pg_operator.h
new file mode 100644 (file)
index 0000000..89cfe12
--- /dev/null
@@ -0,0 +1,480 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_operator.h--
+ *    definition of the system "operator" relation (pg_operator)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *    XXX do NOT break up DATA() statements into multiple lines!
+ *        the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_OPERATOR_H
+#define PG_OPERATOR_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_operator definition.  cpp turns this into
+ *     typedef struct FormData_pg_operator
+ * ----------------
+ */
+CATALOG(pg_operator) {
+    NameData   oprname;
+    Oid        oprowner;
+    int2       oprprec;
+    char       oprkind;
+    bool       oprisleft;
+    bool       oprcanhash;
+    Oid        oprleft;
+    Oid        oprright;
+    Oid                oprresult;
+    Oid        oprcom;
+    Oid        oprnegate;
+    Oid        oprlsortop;
+    Oid        oprrsortop;
+    regproc    oprcode;
+    regproc    oprrest;
+    regproc    oprjoin;
+} FormData_pg_operator;
+
+/* ----------------
+ *     Form_pg_operator corresponds to a pointer to a tuple with
+ *     the format of pg_operator relation.
+ * ----------------
+ */
+typedef FormData_pg_operator   *OperatorTupleForm;
+
+/* ----------------
+ *     compiler constants for pg_operator
+ * ----------------
+ */
+
+#define Natts_pg_operator              16
+#define Anum_pg_operator_oprname       1
+#define Anum_pg_operator_oprowner      2
+#define Anum_pg_operator_oprprec       3
+#define Anum_pg_operator_oprkind       4
+#define Anum_pg_operator_oprisleft     5
+#define Anum_pg_operator_oprcanhash    6
+#define Anum_pg_operator_oprleft       7
+#define Anum_pg_operator_oprright      8
+#define Anum_pg_operator_oprresult     9
+#define Anum_pg_operator_oprcom                10
+#define Anum_pg_operator_oprnegate     11
+#define Anum_pg_operator_oprlsortop    12
+#define Anum_pg_operator_oprrsortop    13
+#define Anum_pg_operator_oprcode       14
+#define Anum_pg_operator_oprrest       15
+#define Anum_pg_operator_oprjoin       16
+
+/* ----------------
+ *     initial contents of pg_operator
+ * ----------------
+ */
+
+DATA(insert OID = 85 (  "<>"       PGUID 0 b t f  16  16  16  85   91  0  0 boolne neqsel neqjoinsel ));
+DATA(insert OID = 91 (  "="        PGUID 0 b t t  16  16  16  91   85  0  0 booleq eqsel eqjoinsel ));
+#define BooleanEqualOperator   91
+
+DATA(insert OID = 92 (  "="        PGUID 0 b t t  18  18  16  92 630  631 631 chareq eqsel eqjoinsel ));
+DATA(insert OID = 93 (  "="        PGUID 0 b t t  19  19  16  93  643 660 660 nameeq eqsel eqjoinsel ));
+DATA(insert OID = 94 (  "="        PGUID 0 b t t  21  21  16  94 519 95 95 int2eq eqsel eqjoinsel ));
+DATA(insert OID = 95 (  "<"        PGUID 0 b t f  21  21  16 520 524 0 0 int2lt intltsel intltjoinsel ));
+DATA(insert OID = 96 (  "="        PGUID 0 b t t  23  23  16  96 518 97 97 int4eq eqsel eqjoinsel ));
+DATA(insert OID = 97 (  "<"        PGUID 0 b t f  23  23  16 521 525 0 0 int4lt intltsel intltjoinsel ));
+DATA(insert OID = 98 (  "="        PGUID 0 b t t  25  25  16  98 531 664 664 texteq eqsel eqjoinsel ));
+DATA(insert OID = 99 (  "="        PGUID 0 b t t  20  20  16  99 644 645 645 char16eq eqsel eqjoinsel ));
+DATA(insert OID = 329 (  "="       PGUID 0 b t t  1000  1000  16  329 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 349 (  "="       PGUID 0 b t t  1001  1001  16  349 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 374 (  "="       PGUID 0 b t t  1002  1002  16  374 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 375 (  "="       PGUID 0 b t t  1003  1003  16  375 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 376 (  "="       PGUID 0 b t t  1004  1004  16  376 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 377 (  "="       PGUID 0 b t t  1005  1005  16  377 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 378 (  "="       PGUID 0 b t t  1006  1006  16  378 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 379 (  "="       PGUID 0 b t t  1007  1007  16  379 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 380 (  "="       PGUID 0 b t t  1008  1008  16  380 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 381 (  "="       PGUID 0 b t t  1009  1009  16  381 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 382 (  "="       PGUID 0 b t t  1028  1028  16  382 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 383 (  "="       PGUID 0 b t t  1010  1010  16  383 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 384 (  "="       PGUID 0 b t t  1011  1011  16  384 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 385 (  "="       PGUID 0 b t t  1012  1012  16  385 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 386 (  "="       PGUID 0 b t t  1013  1013  16  386 0  0  0 array_eq eqsel eqjoinsel ));
+/*
+DATA(insert OID = 387 (  "="       PGUID 0 b t t  1014  1014  16  387 0  0  0 array_eq eqsel eqjoinsel ));
+*/
+DATA(insert OID = 388 (  "="       PGUID 0 b t t  1015  1015  16  388 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 389 (  "="       PGUID 0 b t t  1016  1016  16  389 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 390 (  "="       PGUID 0 b t t  1017  1017  16  390 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 391 (  "="       PGUID 0 b t t  1018  1018  16  391 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 392 (  "="       PGUID 0 b t t  1019  1019  16  392 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 393 (  "="       PGUID 0 b t t  1020  1020  16  393 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 394 (  "="       PGUID 0 b t t  1021  1021  16  394 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 395 (  "="       PGUID 0 b t t  1022  1022  16  395 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 396 (  "="       PGUID 0 b t t  1023  1023  16  396 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 397 (  "="       PGUID 0 b t t  1024  1024  16  397 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 398 (  "="       PGUID 0 b t t  1025  1025  16  398 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 399 (  "="       PGUID 0 b t t  1026  1026  16  399 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 400 (  "="       PGUID 0 b t t  1027  1027  16  400 0  0  0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 401 (  "="       PGUID 0 b t t  1034  1034  16  401 0  0  0 array_eq eqsel eqjoinsel ));
+
+DATA(insert OID = 412 (  "="       PGUID 0 b t t  409  409  16  412 415 418 418 char2eq eqsel eqjoinsel ));
+DATA(insert OID = 413 (  "="       PGUID 0 b t t  410  410  16  413 416 419 419 char4eq eqsel eqjoinsel ));
+DATA(insert OID = 414 (  "="       PGUID 0 b t t  411  411  16  414 417 420 420 char8eq eqsel eqjoinsel ));
+
+DATA(insert OID = 415 (  "<>"      PGUID 0 b t f  409  409  16 415 412  0 0 char2ne neqsel neqjoinsel ));
+DATA(insert OID = 416 (  "<>"      PGUID 0 b t f  410  410  16 416 413  0 0 char4ne neqsel neqjoinsel ));
+DATA(insert OID = 417 (  "<>"      PGUID 0 b t f  411  411  16 417 414  0 0 char8ne neqsel neqjoinsel ));
+DATA(insert OID = 418 (  "<"       PGUID 0 b t f  409  409  16 460 463  0 0 char2lt intltsel intltjoinsel ));
+DATA(insert OID = 419 (  "<"       PGUID 0 b t f  410  410  16 461 464  0 0 char4lt intltsel intltjoinsel ));
+DATA(insert OID = 420 (  "<"       PGUID 0 b t f  411  411  16 462 465  0 0 char8lt intltsel intltjoinsel ));
+
+DATA(insert OID = 457 (  "<="      PGUID 0 b t f  409  409  16 463 460  0 0 char2le intltsel intltjoinsel ));
+DATA(insert OID = 458 (  "<="      PGUID 0 b t f  410  410  16 464 461  0 0 char4le intltsel intltjoinsel ));
+DATA(insert OID = 459 (  "<="      PGUID 0 b t f  411  411  16 465 462  0 0 char8le intltsel intltjoinsel ));
+DATA(insert OID = 460 (  ">"       PGUID 0 b t f  409  409  16 418 457  0 0 char2gt intltsel intltjoinsel ));
+DATA(insert OID = 461 (  ">"       PGUID 0 b t f  410  410  16 419 458  0 0 char4gt intltsel intltjoinsel ));
+DATA(insert OID = 462 (  ">"       PGUID 0 b t f  411  411  16 420 459  0 0 char8gt intltsel intltjoinsel ));
+DATA(insert OID = 463 (  ">="      PGUID 0 b t f  409  409  16 457 418  0 0 char2ge intltsel intltjoinsel ));
+DATA(insert OID = 464 (  ">="      PGUID 0 b t f  410  410  16 458 418  0 0 char4ge intltsel intltjoinsel ));
+DATA(insert OID = 465 (  ">="      PGUID 0 b t f  411  411  16 459 420  0 0 char8ge intltsel intltjoinsel ));
+
+DATA(insert OID = 485 (  "<<"      PGUID 0 b t f 604 604  16   0   0   0   0 poly_left intltsel intltjoinsel ));
+DATA(insert OID = 486 (  "&<"      PGUID 0 b t f 604 604  16   0   0   0   0 poly_overleft intltsel intltjoinsel ));
+DATA(insert OID = 487 (  "&>"      PGUID 0 b t f 604 604  16   0   0   0   0 poly_overright intltsel intltjoinsel ));
+DATA(insert OID = 488 (  ">>"      PGUID 0 b t f 604 604  16   0   0   0   0 poly_right intltsel intltjoinsel ));
+DATA(insert OID = 489 (  "@"       PGUID 0 b t f 604 604  16   0   0   0   0 poly_contained intltsel intltjoinsel ));
+DATA(insert OID = 490 (  "~"       PGUID 0 b t f 604 604  16   0   0   0   0 poly_contain intltsel intltjoinsel ));
+DATA(insert OID = 491 (  "~="      PGUID 0 b t f 604 604  16   0   0   0   0 poly_same intltsel intltjoinsel ));
+DATA(insert OID = 492 (  "&&"      PGUID 0 b t f 604 604  16   0   0   0   0 poly_overlap intltsel intltjoinsel ));
+DATA(insert OID = 493 (  "<<"      PGUID 0 b t f 603 603  16   0   0   0   0 box_left intltsel intltjoinsel ));
+DATA(insert OID = 494 (  "&<"      PGUID 0 b t f 603 603  16   0   0   0   0 box_overleft intltsel intltjoinsel ));
+DATA(insert OID = 495 (  "&>"      PGUID 0 b t f 603 603  16   0   0   0   0 box_overright intltsel intltjoinsel ));
+DATA(insert OID = 496 (  ">>"      PGUID 0 b t f 603 603  16   0   0   0   0 box_right intltsel intltjoinsel ));
+DATA(insert OID = 497 (  "@"       PGUID 0 b t f 603 603  16   0   0   0   0 box_contained intltsel intltjoinsel ));
+DATA(insert OID = 498 (  "~"       PGUID 0 b t f 603 603  16   0   0   0   0 box_contain intltsel intltjoinsel ));
+DATA(insert OID = 499 (  "~="      PGUID 0 b t f 603 603  16   0   0   0   0 box_same intltsel intltjoinsel ));
+DATA(insert OID = 500 (  "&&"      PGUID 0 b t f 603 603  16   0   0   0   0 box_overlap intltsel intltjoinsel ));
+DATA(insert OID = 501 (  ">="      PGUID 0 b t f 603 603  16   0   0   0   0 box_ge areasel areajoinsel ));
+DATA(insert OID = 502 (  ">"       PGUID 0 b t f 603 603  16   0   0   0   0 box_gt areasel areajoinsel ));
+DATA(insert OID = 503 (  "="       PGUID 0 b t t 603 603  16   0   0   0   0 box_eq areasel areajoinsel ));
+DATA(insert OID = 504 (  "<"       PGUID 0 b t f 603 603  16   0   0   0   0 box_lt areasel areajoinsel ));
+DATA(insert OID = 505 (  "<="      PGUID 0 b t f 603 603  16   0   0   0   0 box_le areasel areajoinsel ));
+DATA(insert OID = 506 (  "!^"      PGUID 0 b t f 600 600  16   0   0   0   0 point_above intltsel intltjoinsel ));
+DATA(insert OID = 507 (  "!<"      PGUID 0 b t f 600 600  16   0   0   0   0 point_left intltsel intltjoinsel ));
+DATA(insert OID = 508 (  "!>"      PGUID 0 b t f 600 600  16   0   0   0   0 point_right intltsel intltjoinsel ));
+DATA(insert OID = 509 (  "!|"      PGUID 0 b t f 600 600  16   0   0   0   0 point_below intltsel intltjoinsel ));
+DATA(insert OID = 510 (  "=|="     PGUID 0 b t f 600 600  16   0   0   0   0 point_eq intltsel intltjoinsel ));
+DATA(insert OID = 511 (  "===>"    PGUID 0 b t f 600 603  16   0   0   0   0 on_pb intltsel intltjoinsel ));
+DATA(insert OID = 512 (  "===`"    PGUID 0 b t f 600 602  16   0   0   0   0 on_ppath intltsel intltjoinsel ));
+DATA(insert OID = 513 (  "@@"      PGUID 0 l t f   0 603 600   0   0   0   0 box_center intltsel intltjoinsel ));
+DATA(insert OID = 514 (  "*"       PGUID 0 b t f  23  23  23 514   0   0   0 int4mul intltsel intltjoinsel ));
+DATA(insert OID = 515 (  "!"       PGUID 0 r t f  23   0  23   0   0   0   0 int4fac intltsel intltjoinsel ));
+DATA(insert OID = 516 (  "!!"      PGUID 0 l t f   0  23  23   0   0   0   0 int4fac intltsel intltjoinsel ));
+DATA(insert OID = 517 (  "<===>"   PGUID 0 b t f 600 600  23   0   0   0   0 pointdist intltsel intltjoinsel ));
+DATA(insert OID = 518 (  "<>"      PGUID 0 b t f  23  23  16 518  96  0  0 int4ne neqsel neqjoinsel ));
+DATA(insert OID = 519 (  "<>"      PGUID 0 b t f  21  21  16 519  94  0  0 int2ne neqsel neqjoinsel ));
+DATA(insert OID = 520 (  ">"       PGUID 0 b t f  21  21  16  95   0  0  0 int2gt intgtsel intgtjoinsel ));
+DATA(insert OID = 521 (  ">"       PGUID 0 b t f  23  23  16  97   0  0  0 int4gt intgtsel intgtjoinsel ));
+DATA(insert OID = 522 (  "<="      PGUID 0 b t f  21  21  16 524 520  0  0 int2le intltsel intltjoinsel ));
+DATA(insert OID = 523 (  "<="      PGUID 0 b t f  23  23  16 525 521  0  0 int4le intltsel intltjoinsel ));
+DATA(insert OID = 524 (  ">="      PGUID 0 b t f  21  21  16 522  95  0  0 int2ge intgtsel intgtjoinsel ));
+DATA(insert OID = 525 (  ">="      PGUID 0 b t f  23  23  16 523  97  0  0 int4ge intgtsel intgtjoinsel ));
+DATA(insert OID = 526 (  "*"       PGUID 0 b t f  21  21  21 526   0  0  0 int2mul intltsel intltjoinsel ));
+DATA(insert OID = 527 (  "/"       PGUID 0 b t f  21  21  21   0   0  0  0 int2div intltsel intltjoinsel ));
+DATA(insert OID = 528 (  "/"       PGUID 0 b t f  23  23  23   0   0  0  0 int4div intltsel intltjoinsel ));
+DATA(insert OID = 529 (  "%"       PGUID 0 b t f  21  21  21   6   0  0  0 int2mod intltsel intltjoinsel ));
+DATA(insert OID = 530 (  "%"       PGUID 0 b t f  23  23  23   6   0  0  0 int4mod intltsel intltjoinsel ));
+DATA(insert OID = 531 (  "<>"      PGUID 0 b t f  25  25  16 531  98   0   0 textne neqsel neqjoinsel ));
+DATA(insert OID = 532 (  "="       PGUID 0 b t t  21  23  16 533 538  95  97 int24eq eqsel eqjoinsel ));
+DATA(insert OID = 533 (  "="       PGUID 0 b t t  23  21  16 532 539  97  95 int42eq eqsel eqjoinsel ));
+DATA(insert OID = 534 (  "<"       PGUID 0 b t f  21  23  16 537 542  0  0 int24lt intltsel intltjoinsel ));
+DATA(insert OID = 535 (  "<"       PGUID 0 b t f  23  21  16 536 543  0  0 int42lt intltsel intltjoinsel ));
+DATA(insert OID = 536 (  ">"       PGUID 0 b t f  21  23  16 535 540  0  0 int24gt intgtsel intgtjoinsel ));
+DATA(insert OID = 537 (  ">"       PGUID 0 b t f  23  21  16 534 541  0  0 int42gt intgtsel intgtjoinsel ));
+DATA(insert OID = 538 (  "<>"      PGUID 0 b t f  21  23  16 539 532  0  0 int24ne neqsel neqjoinsel ));
+DATA(insert OID = 539 (  "<>"      PGUID 0 b t f  23  21  16 538 533  0  0 int42ne neqsel neqjoinsel ));
+DATA(insert OID = 540 (  "<="      PGUID 0 b t f  21  23  16 543 536  0  0 int24le intltsel intltjoinsel ));
+DATA(insert OID = 541 (  "<="      PGUID 0 b t f  23  21  16 542 537  0  0 int42le intltsel intltjoinsel ));
+DATA(insert OID = 542 (  ">="      PGUID 0 b t f  21  23  16 541 534  0  0 int24ge intgtsel intgtjoinsel ));
+DATA(insert OID = 543 (  ">="      PGUID 0 b t f  23  21  16 540 535  0  0 int42ge intgtsel intgtjoinsel ));
+DATA(insert OID = 544 (  "*"       PGUID 0 b t f  21  23  23 545   0  0  0 int24mul intltsel intltjoinsel ));
+DATA(insert OID = 545 (  "*"       PGUID 0 b t f  23  21  23 544   0  0  0 int42mul intltsel intltjoinsel ));
+DATA(insert OID = 546 (  "/"       PGUID 0 b t f  21  23  23   0   0  0  0 int24div intltsel intltjoinsel ));
+DATA(insert OID = 547 (  "/"       PGUID 0 b t f  23  21  23   0   0  0  0 int42div intltsel intltjoinsel ));
+DATA(insert OID = 548 (  "%"       PGUID 0 b t f  21  23  23   6   0  0  0 int24mod intltsel intltjoinsel ));
+DATA(insert OID = 549 (  "%"       PGUID 0 b t f  23  21  23   6   0  0  0 int42mod intltsel intltjoinsel ));
+DATA(insert OID = 550 (  "+"       PGUID 0 b t f  21  21  21 550   0   0   0 int2pl intltsel intltjoinsel ));
+DATA(insert OID = 551 (  "+"       PGUID 0 b t f  23  23  23 551   0   0   0 int4pl intltsel intltjoinsel ));
+DATA(insert OID = 552 (  "+"       PGUID 0 b t f  21  23  23 553   0   0   0 int24pl intltsel intltjoinsel ));
+DATA(insert OID = 553 (  "+"       PGUID 0 b t f  23  21  23 552   0   0   0 int42pl intltsel intltjoinsel ));
+DATA(insert OID = 554 (  "-"       PGUID 0 b t f  21  21  21   0   0   0   0 int2mi intltsel intltjoinsel ));
+DATA(insert OID = 555 (  "-"       PGUID 0 b t f  23  23  23   0   0   0   0 int4mi intltsel intltjoinsel ));
+DATA(insert OID = 556 (  "-"       PGUID 0 b t f  21  23  23   0   0   0   0 int24mi intltsel intltjoinsel ));
+DATA(insert OID = 557 (  "-"       PGUID 0 b t f  23  21  23   0   0   0   0 int42mi intltsel intltjoinsel ));
+DATA(insert OID = 558   (  "-"       PGUID 0 l t f   0  23  23  0   0   0   0 int4um intltsel intltjoinsel ));
+DATA(insert OID = 559   (  "-"       PGUID 0 l t f   0  21  21  0   0   0   0 int2um intltsel intltjoinsel ));
+DATA(insert OID = 560 (  "="       PGUID 0 b t t 702 702  16 560 561 562 562 abstimeeq eqsel eqjoinsel ));
+DATA(insert OID = 561 (  "<>"      PGUID 0 b t f 702 702  16 561 560 0 0 abstimene neqsel neqjoinsel ));
+DATA(insert OID = 562 (  "<"       PGUID 0 b t f 702 702  16 563 565 0 0 abstimelt intltsel intltjoinsel ));
+DATA(insert OID = 563 (  ">"       PGUID 0 b t f 702 702  16 562 564 0 0 abstimegt intltsel intltjoinsel ));
+DATA(insert OID = 564 (  "<="      PGUID 0 b t f 702 702  16 565 563 0 0 abstimele intltsel intltjoinsel ));
+DATA(insert OID = 565 (  ">="      PGUID 0 b t f 702 702  16 564 562 0 0 abstimege intltsel intltjoinsel ));
+DATA(insert OID = 566 (  "="       PGUID 0 b t t 703 703  16 566 567 568 568 reltimeeq - - ));
+DATA(insert OID = 567 (  "<>"      PGUID 0 b t f 703 703  16 567 566 0 0 reltimene - - ));
+DATA(insert OID = 568 (  "<"       PGUID 0 b t f 703 703  16 569 571 0 0 reltimelt - - ));
+DATA(insert OID = 569 (  ">"       PGUID 0 b t f 703 703  16 568 570 0 0 reltimegt - - ));
+DATA(insert OID = 570 (  "<="      PGUID 0 b t f 703 703  16 571 569 0 0 reltimele - - ));
+DATA(insert OID = 571 (  ">="      PGUID 0 b t f 703 703  16 570 568 0 0 reltimege - - ));
+DATA(insert OID = 572 (  "="       PGUID 0 b t t 704 704  16 572   0   0   0 intervaleq - - ));
+DATA(insert OID = 573 (  "<<"      PGUID 0 b t f 704 704  16   0   0   0   0 intervalct - - ));
+DATA(insert OID = 574 (  "&&"      PGUID 0 b t f 704 704  16   0   0   0   0 intervalov - - ));
+DATA(insert OID = 575 (  "#="      PGUID 0 b t f 704 703  16   0 576   0 568 intervalleneq - - ));
+DATA(insert OID = 576 (  "#<>"     PGUID 0 b t f 704 703  16   0 575   0 568 intervallenne - - ));
+DATA(insert OID = 577 (  "#<"      PGUID 0 b t f 704 703  16   0 580   0 568 intervallenlt - - ));
+DATA(insert OID = 578 (  "#>"      PGUID 0 b t f 704 703  16   0 579   0 568 intervallengt - - ));
+DATA(insert OID = 579 (  "#<="     PGUID 0 b t f 704 703  16   0 578   0 568 intervallenle - - ));
+DATA(insert OID = 580 (  "#>="     PGUID 0 b t f 704 703  16   0 577   0 568 intervallenge - - ));
+DATA(insert OID = 581 (  "+"       PGUID 0 b t f 702 703 702 581   0 0 0 timepl - - ));
+DATA(insert OID = 582 (  "-"       PGUID 0 b t f 702 703 702   0   0 0 0 timemi - - ));
+DATA(insert OID = 583 (  "<?>"     PGUID 0 b t f 702 704  16   0   0 562   0 ininterval - - ));
+DATA(insert OID = 584 (  "-"       PGUID 0 l t f   0 700 700   0   0   0   0 float4um - - ));
+DATA(insert OID = 585 (  "-"       PGUID 0 l t f   0 701 701   0   0   0   0 float8um - - ));
+DATA(insert OID = 586 (  "+"       PGUID 0 b t f 700 700 700 586   0   0   0 float4pl - - ));
+DATA(insert OID = 587 (  "-"       PGUID 0 b t f 700 700 700   0   0   0   0 float4mi - - ));
+DATA(insert OID = 588 (  "/"       PGUID 0 b t f 700 700 700   0   0   0   0 float4div - - ));
+DATA(insert OID = 589 (  "*"       PGUID 0 b t f 700 700 700 589   0   0   0 float4mul - - ));
+DATA(insert OID = 590 (  "@"       PGUID 0 l t f   0 700 700   0   0   0   0 float4abs - - ));
+DATA(insert OID = 591 (  "+"       PGUID 0 b t f 701 701 701 591   0   0   0 float8pl - - ));
+DATA(insert OID = 592 (  "-"       PGUID 0 b t f 701 701 701   0   0   0   0 float8mi - - ));
+DATA(insert OID = 593 (  "/"       PGUID 0 b t f 701 701 701   0   0   0   0 float8div - - ));
+DATA(insert OID = 594 (  "*"       PGUID 0 b t f 701 701 701 594   0   0   0 float8mul - - ));
+DATA(insert OID = 595 (  "@"       PGUID 0 l t f   0 701 701   0   0   0   0 float8abs - - ));
+DATA(insert OID = 596 (  "|/"      PGUID 0 l t f   0 701 701   0   0   0   0 dsqrt - - ));
+DATA(insert OID = 597 (  "||/"     PGUID 0 l t f   0 701 701   0   0   0   0 dcbrt - - ));
+DATA(insert OID = 598 (  "%"       PGUID 0 l t f   0 701 701   0   0   0   0 dtrunc - - ));
+DATA(insert OID = 599 (  "%"       PGUID 0 r t f 701   0 701   0   0   0   0 dround - - ));
+DATA(insert OID = 601 (  ":"       PGUID 0 l t f   0 701 701   0   0   0   0 dexp - - ));
+DATA(insert OID = 602 (  ";"       PGUID 0 l t f   0 701 701   0   0   0   0 dlog1 - - ));
+DATA(insert OID = 603 (  "|"       PGUID 0 l t f   0 704 702   0   0   0   0 intervalstart - - ));
+DATA(insert OID = 606 (  "<#>"      PGUID 0 b t f 702 702 704   0   0   0   0 mktinterval - - ));
+DATA(insert OID = 607 (  "="       PGUID 0 b t t  26  26  16 607 608 97 97 oideq eqsel eqjoinsel ));
+#define        OIDEqualOperator 607    /* XXX planner/prep/semanopt.c crock */
+DATA(insert OID = 608 (  "<>"      PGUID 0 b t f  26  26  16 608 607  0  0 oidne neqsel neqjoinsel ));
+DATA(insert OID = 609 (  "<"       PGUID 0 b t f  26  26  16 610 612  0  0 int4lt intltsel intltjoinsel ));
+DATA(insert OID = 610 (  ">"       PGUID 0 b t f  26  26  16 609 611  0  0 int4gt intgtsel intgtjoinsel ));
+DATA(insert OID = 611 (  "<="      PGUID 0 b t f  26  26  16 612 610  0  0 int4le intltsel intltjoinsel ));
+DATA(insert OID = 612 (  ">="      PGUID 0 b t f  26  26  16 611 609  0  0 int4ge intgtsel intgtjoinsel ));
+DATA(insert OID = 620 (  "="       PGUID 0 b t t  700  700  16 620 621  622 622 float4eq eqsel eqjoinsel ));
+DATA(insert OID = 621 (  "<>"      PGUID 0 b t f  700  700  16 621 620  0 0 float4ne neqsel neqjoinsel ));
+DATA(insert OID = 622 (  "<"       PGUID 0 b t f  700  700  16 623 625  0 0 float4lt intltsel intltjoinsel ));
+DATA(insert OID = 623 (  ">"       PGUID 0 b t f  700  700  16 622 624  0 0 float4gt intgtsel intgtjoinsel ));
+DATA(insert OID = 624 (  "<="      PGUID 0 b t f  700  700  16 625 623  0 0 float4le intltsel intltjoinsel ));
+DATA(insert OID = 625 (  ">="      PGUID 0 b t f  700  700  16 624 622  0 0 float4ge intgtsel intgtjoinsel ));
+DATA(insert OID = 626 (  "!!="     PGUID 0 b t f  23   19   16 0   0    0   0   int4notin "-"     "-"));
+DATA(insert OID = 627 (  "!!="     PGUID 0 b t f  26   19   16 0   0    0   0   oidnotin "-"     "-"));
+#define OIDNotInOperator 627   /* XXX planner/prep/semanopt.c crock */
+DATA(insert OID = 630 (  "<>"      PGUID 0 b t f  18  18  16 630  92  0 0 charne neqsel neqjoinsel ));
+    
+DATA(insert OID = 631 (  "<"       PGUID 0 b t f  18  18  16 633 634  0 0 charlt intltsel intltjoinsel ));
+DATA(insert OID = 632 (  "<="      PGUID 0 b t f  18  18  16 634 633  0 0 charle intltsel intltjoinsel ));
+DATA(insert OID = 633 (  ">"       PGUID 0 b t f  18  18  16 631 632  0 0 chargt intltsel intltjoinsel ));
+DATA(insert OID = 634 (  ">="      PGUID 0 b t f  18  18  16 632 631  0 0 charge intltsel intltjoinsel ));
+    
+DATA(insert OID = 635 (  "+"       PGUID 0 b t f  18  18  18 0 0  0 0 charpl eqsel eqjoinsel ));
+DATA(insert OID = 636 (  "-"       PGUID 0 b t f  18  18  18 0 0  0 0 charmi eqsel eqjoinsel ));
+DATA(insert OID = 637 (  "*"       PGUID 0 b t f  18  18  18 0 0  0 0 charmul eqsel eqjoinsel ));
+DATA(insert OID = 638 (  "/"       PGUID 0 b t f  18  18  18 0 0  0 0 chardiv eqsel eqjoinsel ));
+
+DATA(insert OID = 639 (  "~"       PGUID 0 b t f  19  25  16 0 640  0 0 nameregexeq eqsel eqjoinsel ));
+DATA(insert OID = 640 (  "!~"      PGUID 0 b t f  19  25  16 0 639  0 0 nameregexne neqsel neqjoinsel ));
+DATA(insert OID = 641 (  "~"       PGUID 0 b t f  25  25  16 0 642  0 0 textregexeq eqsel eqjoinsel ));
+DATA(insert OID = 642 (  "!~"      PGUID 0 b t f  25  25  16 0 641  0 0 textregexne eqsel eqjoinsel ));
+DATA(insert OID = 643 (  "<>"      PGUID 0 b t f  19  19  16 643 93 0 0 namene neqsel neqjoinsel ));
+DATA(insert OID = 644 (  "<>"      PGUID 0 b t f  20  20  16 644 99 0 0 char16ne neqsel neqjoinsel ));
+DATA(insert OID = 645 (  "<"       PGUID 0 b t f  20  20  16 647 648  0 0 char16lt intltsel intltjoinsel ));
+DATA(insert OID = 646 (  "<="       PGUID 0 b t f  20  20  16 648 647  0 0 char16le intltsel intltjoinsel ));
+DATA(insert OID = 647 (  ">"       PGUID 0 b t f  20  20  16 645 646  0 0 char16gt intltsel intltjoinsel ));
+DATA(insert OID = 648 (  ">="       PGUID 0 b t f  20  20  16 646 645  0 0 char16ge intltsel intltjoinsel ));
+DATA(insert OID = 649 (  "~"       PGUID 0 b t f  20  25  16 0 650  0 0 char16regexeq intltsel intltjoinsel ));
+DATA(insert OID = 650 (  "!~"       PGUID 0 b t f  20  25  16 650 0  0 0 char16regexne intltsel intltjoinsel ));
+DATA(insert OID = 651 (  "~~"       PGUID 0 b t f  20  25  16 0 651  0 0 char16like eqsel eqjoinsel ));
+DATA(insert OID = 652 (  "!~~"       PGUID 0 b t f  20  25  16 651 0  0 0 char16nlike neqsel neqjoinsel ));
+
+DATA(insert OID = 660 (  "<"       PGUID 0 b t f  19  19  16 662 663  0 0 namelt intltsel intltjoinsel ));
+DATA(insert OID = 661 (  "<="      PGUID 0 b t f  19  19  16 663 662  0 0 namele intltsel intltjoinsel ));
+DATA(insert OID = 662 (  ">"       PGUID 0 b t f  19  19  16 660 661  0 0 namegt intltsel intltjoinsel ));
+DATA(insert OID = 663 (  ">="      PGUID 0 b t f  19  19  16 661 660  0 0 namege intltsel intltjoinsel ));
+DATA(insert OID = 664 (  "<"       PGUID 0 b t f  25  25  16 666 667  0 0 text_lt intltsel intltjoinsel ));
+DATA(insert OID = 665 (  "<="      PGUID 0 b t f  25  25  16 667 666  0 0 text_le intltsel intltjoinsel ));
+DATA(insert OID = 666 (  ">"       PGUID 0 b t f  25  25  16 664 665  0 0 text_gt intltsel intltjoinsel ));
+DATA(insert OID = 667 (  ">="      PGUID 0 b t f  25  25  16 665 664  0 0 text_ge intltsel intltjoinsel ));
+
+DATA(insert OID = 670 (  "="       PGUID 0 b t f  701  701  16 670 671  0 0 float8eq eqsel eqjoinsel ));
+DATA(insert OID = 671 (  "<>"      PGUID 0 b t f  701  701  16 671 670  0 0 float8ne neqsel neqjoinsel ));
+DATA(insert OID = 672 (  "<"       PGUID 0 b t f  701  701  16 674 675  0 0 float8lt intltsel intltjoinsel ));
+DATA(insert OID = 673 (  "<="      PGUID 0 b t f  701  701  16 675 674  0 0 float8le intltsel intltjoinsel ));
+DATA(insert OID = 674 (  ">"       PGUID 0 b t f  701  701  16 672 673  0 0 float8gt intltsel intltjoinsel ));
+DATA(insert OID = 675 (  ">="      PGUID 0 b t f  701  701  16 673 672  0 0 float8ge intltsel intltjoinsel ));
+
+DATA(insert OID = 676 (  "<"       PGUID 0 b t f  911  911  16 680 679  0 0 oidnamelt intltsel intltjoinsel ));
+DATA(insert OID = 677 (  "<="      PGUID 0 b t f  911  911  16 679 680  0 0 oidnamele intltsel intltjoinsel ));
+DATA(insert OID = 678 (  "="       PGUID 0 b t f  911  911  16 678 681  0 0 oidnameeq intltsel intltjoinsel ));
+DATA(insert OID = 679 (  ">="      PGUID 0 b t f  911  911  16 677 676  0 0 oidnamege intltsel intltjoinsel ));
+DATA(insert OID = 680 (  ">"       PGUID 0 b t f  911  911  16 676 677  0 0 oidnamegt intltsel intltjoinsel ));
+DATA(insert OID = 681 (  "<>"      PGUID 0 b t f  911  911  16 681 678  0 0 oidnamene intltsel intltjoinsel ));
+
+DATA(insert OID = 697 (  "~"       PGUID 0 b t f  411  25  16 0 698  0 0 char8regexeq eqsel eqjoinsel ));
+DATA(insert OID = 698 (  "!~"      PGUID 0 b t f  411  25  16 0 697  0 0 char8regexne neqsel neqjoinsel ));
+
+DATA(insert OID = 830 (  "<"       PGUID 0 b t f  810  810  16 834 833  0 0 oidint2lt intltsel intltjoinsel ));
+DATA(insert OID = 831 (  "<="      PGUID 0 b t f  810  810  16 833 834  0 0 oidint2le intltsel intltjoinsel ));
+DATA(insert OID = 832 (  "="       PGUID 0 b t f  810  810  16 832 835  0 0 oidint2eq intltsel intltjoinsel ));
+DATA(insert OID = 833 (  ">="      PGUID 0 b t f  810  810  16 831 830  0 0 oidint2ge intltsel intltjoinsel ));
+DATA(insert OID = 834 (  ">"       PGUID 0 b t f  810  810  16 830 831  0 0 oidint2gt intltsel intltjoinsel ));
+DATA(insert OID = 835 (  "<>"      PGUID 0 b t f  810  810  16 835 832  0 0 oidint2ne intltsel intltjoinsel ));
+
+DATA(insert OID = 839 (  "~"       PGUID 0 b t f  409  25  16 0 841  0 0 char2regexeq eqsel eqjoinsel ));
+DATA(insert OID = 841 (  "!~"      PGUID 0 b t f  409  25  16 0 839  0 0 char2regexne neqsel neqjoinsel ));
+DATA(insert OID = 840 (  "~"       PGUID 0 b t f  410  25  16 0 842  0 0 char4regexeq eqsel eqjoinsel ));
+DATA(insert OID = 842 (  "!~"      PGUID 0 b t f  410  25  16 0 840  0 0 char4regexne neqsel neqjoinsel ));
+
+DATA(insert OID = 930 (  "<"       PGUID 0 b t f  910  910  16 934 933  0 0 oidint4lt intltsel intltjoinsel ));
+DATA(insert OID = 931 (  "<="      PGUID 0 b t f  910  910  16 933 934  0 0 oidint4le intltsel intltjoinsel ));
+DATA(insert OID = 932 (  "="       PGUID 0 b t f  910  910  16 932 935  0 0 oidint4eq intltsel intltjoinsel ));
+DATA(insert OID = 933 (  ">="      PGUID 0 b t f  910  910  16 931 930  0 0 oidint4ge intltsel intltjoinsel ));
+DATA(insert OID = 934 (  ">"       PGUID 0 b t f  910  910  16 930 931  0 0 oidint4gt intltsel intltjoinsel ));
+DATA(insert OID = 935 (  "<>"      PGUID 0 b t f  910  910  16 935 932  0 0 oidint4ne intltsel intltjoinsel ));
+
+DATA(insert OID = 965 (  "^"       PGUID 0 b t f 701 701 701   0   0   0   0 dpow - - ));
+DATA(insert OID = 966 (  "+"       PGUID 0 b t f 1034 1033 1034 0 0 0 0 aclinsert   intltsel intltjoinsel ));
+DATA(insert OID =  967 (  "-"       PGUID 0 b t f 1034 1033 1034 0 0 0 0 aclremove   intltsel intltjoinsel ));
+DATA(insert OID =   968 (  "~"       PGUID 0 b t f 1034 1033   16 0 0 0 0 aclcontains intltsel intltjoinsel ));
+
+DATA(insert OID = 1054 ( "="       PGUID 0 b t t  1042  1042  16  1054 1057 1058 1058 bpchareq eqsel eqjoinsel ));
+DATA(insert OID = 1055 (  "~"      PGUID 0 b t f  1042  25  16 0 1056  0 0 textregexeq eqsel eqjoinsel ));
+DATA(insert OID = 1056 ( "!~"      PGUID 0 b t f  1042  25  16 0 1055  0 0 textregexne neqsel neqjoinsel ));
+DATA(insert OID = 1057 ( "<>"      PGUID 0 b t f  1042  1042  16 1057 1054  0 0 bpcharne neqsel neqjoinsel ));
+DATA(insert OID = 1058 ( "<"       PGUID 0 b t f  1042  1042  16 1060 1061  0 0 bpcharlt intltsel intltjoinsel ));
+DATA(insert OID = 1059 ( "<="      PGUID 0 b t f  1042  1042  16 1061 1060  0 0 bpcharle intltsel intltjoinsel ));
+DATA(insert OID = 1060 ( ">"       PGUID 0 b t f  1042  1042  16 1058 1059  0 0 bpchargt intltsel intltjoinsel ));
+DATA(insert OID = 1061 ( ">="      PGUID 0 b t f  1042  1042  16 1059 1058  0 0 bpcharge intltsel intltjoinsel ));
+
+DATA(insert OID = 1062 ( "="       PGUID 0 b t t  1043  1043  16  1062 1065 1066 1066 varchareq eqsel eqjoinsel ));
+DATA(insert OID = 1063 (  "~"      PGUID 0 b t f  1043  25  16 0 1064  0 0 textregexeq eqsel eqjoinsel ));
+DATA(insert OID = 1064 ( "!~"      PGUID 0 b t f  1043  25  16 0 1063  0 0 textregexne neqsel neqjoinsel ));
+DATA(insert OID = 1065 ( "<>"      PGUID 0 b t f  1043  1043  16 1065 1062  0 0 varcharne neqsel neqjoinsel ));
+DATA(insert OID = 1066 ( "<"       PGUID 0 b t f  1043  1043  16 1068 1069  0 0 varcharlt intltsel intltjoinsel ));
+DATA(insert OID = 1067 ( "<="      PGUID 0 b t f  1043  1043  16 1069 1068  0 0 varcharle intltsel intltjoinsel ));
+DATA(insert OID = 1068 ( ">"       PGUID 0 b t f  1043  1043  16 1066 1067  0 0 varchargt intltsel intltjoinsel ));
+DATA(insert OID = 1069 ( ">="      PGUID 0 b t f  1043  1043  16 1067 1066  0 0 varcharge intltsel intltjoinsel ));
+
+DATA(insert OID = 1093 ( "="       PGUID 0 b t t  1082  1082  16 1093 1094 1095 1095 date_eq eqsel eqjoinsel ));
+DATA(insert OID = 1094 ( "<>"      PGUID 0 b t f  1082  1082  16 1094 1093  0 0 date_ne neqsel neqjoinsel ));
+DATA(insert OID = 1095 ( "<"       PGUID 0 b t f  1082  1082  16 1097 1098  0 0 date_lt intltsel intltjoinsel ));
+DATA(insert OID = 1096 ( "<="      PGUID 0 b t f  1082  1082  16 1098 1097  0 0 date_le intltsel intltjoinsel ));
+DATA(insert OID = 1097 ( ">"       PGUID 0 b t f  1082  1082  16 1095 1096  0 0 date_gt intltsel intltjoinsel ));
+DATA(insert OID = 1098 ( ">="      PGUID 0 b t f  1082  1082  16 1096 1065  0 0 date_ge intltsel intltjoinsel ));
+
+DATA(insert OID = 1108 ( "="       PGUID 0 b t t  1083  1083  16 1108 1109 1110 1110 time_eq eqsel eqjoinsel ));
+DATA(insert OID = 1109 ( "<>"      PGUID 0 b t f  1083  1083  16 1109 1108  0 0 time_ne neqsel neqjoinsel ));
+DATA(insert OID = 1110 ( "<"       PGUID 0 b t f  1083  1083  16 1112 1113  0 0 time_lt intltsel intltjoinsel ));
+DATA(insert OID = 1111 ( "<="      PGUID 0 b t f  1083  1083  16 1113 1112  0 0 time_le intltsel intltjoinsel ));
+DATA(insert OID = 1112 ( ">"       PGUID 0 b t f  1083  1083  16 1110 1111  0 0 time_gt intltsel intltjoinsel ));
+DATA(insert OID = 1113 ( ">="      PGUID 0 b t f  1083  1083  16 1111 1065  0 0 time_ge intltsel intltjoinsel ));
+
+/* float48 operators */
+DATA(insert OID = 1116 (  "+"       PGUID 0 b t f 700 701 701 1116   0   0   0 float48pl - - ));
+DATA(insert OID = 1117 (  "-"       PGUID 0 b t f 700 701 701   0   0   0   0 float48mi - - ));
+DATA(insert OID = 1118 (  "/"       PGUID 0 b t f 700 701 701   0   0   0   0 float48div - - ));
+DATA(insert OID = 1119 (  "*"       PGUID 0 b t f 700 701 701 1119   0   0   0 float48mul - - ));
+DATA(insert OID = 1120 (  "="       PGUID 0 b t t  700  701  16 1120 1121  1122 1122 float48eq eqsel eqjoinsel ));
+DATA(insert OID = 1121 (  "<>"      PGUID 0 b t f  700  701  16 1121 1120  0 0 float48ne neqsel neqjoinsel ));
+DATA(insert OID = 1122 (  "<"       PGUID 0 b t f  700  701  16 1123 1125  0 0 float48lt intltsel intltjoinsel ));
+DATA(insert OID = 1123 (  ">"       PGUID 0 b t f  700  701  16 1122 1124  0 0 float48gt intgtsel intgtjoinsel ));
+DATA(insert OID = 1124 (  "<="      PGUID 0 b t f  700  701  16 1125 1123  0 0 float48le intltsel intltjoinsel ));
+DATA(insert OID = 1125 (  ">="      PGUID 0 b t f  700  701  16 1124 1122  0 0 float48ge intgtsel intgtjoinsel ));
+
+/* float84 operators */
+DATA(insert OID = 1126 (  "+"       PGUID 0 b t f 701 700 701 1126   0   0   0 float84pl - - ));
+DATA(insert OID = 1127 (  "-"       PGUID 0 b t f 701 700 701   0   0   0   0 float84mi - - ));
+DATA(insert OID = 1128 (  "/"       PGUID 0 b t f 701 700 701   0   0   0   0 float84div - - ));
+DATA(insert OID = 1129 (  "*"       PGUID 0 b t f 701 700 701 1129   0   0   0 float84mul - - ));
+DATA(insert OID = 1130 (  "="       PGUID 0 b t t  701  700  16 1130 1131  1132 1132 float84eq eqsel eqjoinsel ));
+DATA(insert OID = 1131 (  "<>"      PGUID 0 b t f  701  700  16 1131 1130  0 0 float84ne neqsel neqjoinsel ));
+DATA(insert OID = 1132 (  "<"       PGUID 0 b t f  701  700  16 1133 1135  0 0 float84lt intltsel intltjoinsel ));
+DATA(insert OID = 1133 (  ">"       PGUID 0 b t f  701  700  16 1132 1134  0 0 float84gt intgtsel intgtjoinsel ));
+DATA(insert OID = 1134 (  "<="      PGUID 0 b t f  701  700  16 1135 1133  0 0 float84le intltsel intltjoinsel ));
+DATA(insert OID = 1135 (  ">="      PGUID 0 b t f  701  700  16 1134 1132  0 0 float84ge intgtsel intgtjoinsel ));
+
+/* int4 and oid equality */
+DATA(insert OID = 1136 (  "="       PGUID 0 b t t 23 26 16 1137 0 0 0 int4eqoid eqsel eqjoinsel ));
+DATA(insert OID = 1137 (  "="       PGUID 0 b t t 26 23 16 1136 0 0 0 oideqint4 eqsel eqjoinsel ));
+
+/* LIKE hacks by Keith Parks. */
+DATA(insert OID = 1201 (  "~~"    PGUID 0 b t f  409  25  16 0 1202 0 0 char2like eqsel eqjoinsel ));
+DATA(insert OID = 1202 (  "!~~"   PGUID 0 b t f  409  25  16 0 1201 0 0 char2nlike neqsel neqjoinsel ));
+DATA(insert OID = 1203 (  "~~"    PGUID 0 b t f  410  25  16 0 1204 0 0 char4like eqsel eqjoinsel ));
+DATA(insert OID = 1204 (  "!~~"   PGUID 0 b t f  410  25  16 0 1203 0 0 char4nlike neqsel neqjoinsel ));
+DATA(insert OID = 1205 (  "~~"    PGUID 0 b t f  411  25  16 0 1206 0 0 char8like eqsel eqjoinsel ));
+DATA(insert OID = 1206 (  "!~~"   PGUID 0 b t f  411  25  16 0 1205 0 0 char8nlike neqsel neqjoinsel ));
+DATA(insert OID = 1207 (  "~~"    PGUID 0 b t f  19   25  16 0 1208 0 0 namelike eqsel eqjoinsel ));
+DATA(insert OID = 1208 (  "!~~"   PGUID 0 b t f  19   25  16 0 1207 0 0 namenlike neqsel neqjoinsel ));
+DATA(insert OID = 1209 (  "~~"    PGUID 0 b t f  25   25  16 0 1210 0 0 textlike eqsel eqjoinsel ));
+DATA(insert OID = 1210 (  "!~~"   PGUID 0 b t f  25   25  16 0 1209 0 0 textnlike neqsel neqjoinsel ));
+DATA(insert OID = 1211 (  "~~"    PGUID 0 b t f  1042 25  16 0 1212 0 0 textlike eqsel eqjoinsel ));
+DATA(insert OID = 1212 (  "!~~"   PGUID 0 b t f  1042 25  16 0 1211 0 0 textnlike neqsel neqjoinsel ));
+DATA(insert OID = 1213 (  "~~"    PGUID 0 b t f  1043 25  16 0 1214 0 0 textlike eqsel eqjoinsel ));
+DATA(insert OID = 1214 (  "!~~"   PGUID 0 b t f  1043 25  16 0 1213 0 0 textnlike neqsel neqjoinsel ));
+DATA(insert OID = 1215 (  "~~"    PGUID 0 b t f  20   25  16 0 1216 0 0 char16like eqsel eqjoinsel ));
+DATA(insert OID = 1216 (  "!~~"   PGUID 0 b t f  20   25  16 0 1215 0 0 char16nlike neqsel neqjoinsel ));
+
+/* case-insensitive LIKE hacks */
+DATA(insert OID = 1220 (  "~*"       PGUID 0 b t f  409  25  16 0 1221  0 0 char2icregexeq eqsel eqjoinsel ));
+DATA(insert OID = 1221 (  "!~*"      PGUID 0 b t f  409  25  16 0 1220  0 0 char2icregexne neqsel neqjoinsel ));
+DATA(insert OID = 1222 (  "~*"       PGUID 0 b t f  410  25  16 0 1223  0 0 char4icregexeq eqsel eqjoinsel ));
+DATA(insert OID = 1223 (  "!~*"      PGUID 0 b t f  410  25  16 0 1222  0 0 char4icregexne neqsel neqjoinsel ));
+DATA(insert OID = 1224 (  "~*"       PGUID 0 b t f  411  25  16 0 1225  0 0 char8icregexeq eqsel eqjoinsel ));
+DATA(insert OID = 1225 (  "!~*"      PGUID 0 b t f  411  25  16 0 1224  0 0 char8icregexne neqsel neqjoinsel ));
+DATA(insert OID = 1226 (  "~*"       PGUID 0 b t f  19  25  16 0 1227  0 0 nameicregexeq eqsel eqjoinsel ));
+DATA(insert OID = 1227 (  "!~*"      PGUID 0 b t f  19  25  16 0 1226  0 0 nameicregexne neqsel neqjoinsel ));
+DATA(insert OID = 1228 (  "~*"       PGUID 0 b t f  25  25  16 0 1229  0 0 texticregexeq eqsel eqjoinsel ));
+DATA(insert OID = 1229 (  "!~*"      PGUID 0 b t f  25  25  16 0 1228  0 0 texticregexne eqsel eqjoinsel ));
+DATA(insert OID = 1230 (  "~*"       PGUID 0 b t f  20  25  16 0 1231  0 0 char16icregexeq eqsel eqjoinsel ));
+DATA(insert OID = 1231 (  "!~*"      PGUID 0 b t f  20  25  16 0 1230  0 0 char16icregexne neqsel neqjoinsel ));
+
+
+
+/*
+ * function prototypes
+ */
+extern void OperatorCreate(char *operatorName, 
+                          char *leftTypeName,
+                          char *rightTypeName,
+                          char *procedureName,
+                          uint16 precedence, 
+                          bool isLeftAssociative,
+                          char *commutatorName,
+                          char *negatorName,
+                          char *restrictionName,
+                          char *joinName,
+                          bool canHash,
+                          char *leftSortName,
+                          char *rightSortName);
+
+#endif /* PG_OPERATOR_H */
diff --git a/src/backend/catalog/pg_parg.h b/src/backend/catalog/pg_parg.h
new file mode 100644 (file)
index 0000000..c25edd0
--- /dev/null
@@ -0,0 +1,116 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_parg.h--
+ *    definition of the system "parg" relation (pg_parg)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PARG_H
+#define PG_PARG_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_parg definition.  cpp turns this into
+ *     typedef struct FormData_pg_parg
+ * ----------------
+ */ 
+CATALOG(pg_parg) {
+    Oid        parproid;
+    int2       parnum;
+    char       parbound;
+    Oid        partype;
+} FormData_pg_parg;
+
+/* ----------------
+ *     Form_pg_parg corresponds to a pointer to a tuple with
+ *     the format of pg_parg relation.
+ * ----------------
+ */
+typedef FormData_pg_parg       *Form_pg_parg;
+
+/* ----------------
+ *     compiler constants for pg_parg
+ * ----------------
+ */
+#define Natts_pg_parg                  4
+#define Anum_pg_parg_parproid          1
+#define Anum_pg_parg_parnum            2
+#define Anum_pg_parg_parbound          3
+#define Anum_pg_parg_partype           4
+
+/* ----------------
+ *     initial contents of pg_parg
+ * ----------------
+ */
+
+DATA(insert OID = 0 (  28 1 - 23 ));
+DATA(insert OID = 0 (  29 1 - 16 ));
+DATA(insert OID = 0 (  30 1 - 23 ));
+DATA(insert OID = 0 (  31 1 - 17 ));
+DATA(insert OID = 0 (  32 1 - 23 ));
+DATA(insert OID = 0 (  33 1 - 18 ));
+DATA(insert OID = 0 (  34 1 - 23 ));
+DATA(insert OID = 0 (  35 1 - 19 ));
+DATA(insert OID = 0 (  36 1 - 23 ));
+DATA(insert OID = 0 (  37 1 - 20 ));
+DATA(insert OID = 0 (  38 1 - 23 ));
+DATA(insert OID = 0 (  39 1 - 21 ));
+DATA(insert OID = 0 (  40 1 - 23 ));
+DATA(insert OID = 0 (  41 1 - 22 ));
+DATA(insert OID = 0 (  42 1 - 23 ));
+DATA(insert OID = 0 (  43 1 - 23 ));
+DATA(insert OID = 0 (  44 1 - 23 ));
+DATA(insert OID = 0 (  45 1 - 24 ));
+DATA(insert OID = 0 (  46 1 - 23 ));
+DATA(insert OID = 0 (  47 1 - 25 ));
+DATA(insert OID = 0 (  50 1 - 23 ));
+DATA(insert OID = 0 (  50 2 - 23 ));
+DATA(insert OID = 0 (  50 3 - 23 ));
+DATA(insert OID = 0 (  51 1 - 23 ));
+DATA(insert OID = 0 (  52 1 - 23 ));
+DATA(insert OID = 0 (  52 2 - 23 ));
+DATA(insert OID = 0 (  52 3 - 23 ));
+DATA(insert OID = 0 (  52 4 - 23 ));
+DATA(insert OID = 0 (  53 1 - 23 ));
+DATA(insert OID = 0 (  54 1 - 23 ));
+DATA(insert OID = 0 (  54 2 - 23 ));
+DATA(insert OID = 0 (  55 1 - 23 ));
+DATA(insert OID = 0 (  55 2 - 23 ));
+DATA(insert OID = 0 (  56 1 - 23 ));
+DATA(insert OID = 0 (  56 2 - 23 ));
+DATA(insert OID = 0 (  57 1 - 23 ));
+DATA(insert OID = 0 (  57 2 - 23 ));
+DATA(insert OID = 0 (  57 3 - 23 ));
+DATA(insert OID = 0 (  60 1 - 16 ));
+DATA(insert OID = 0 (  60 2 - 16 ));
+DATA(insert OID = 0 (  61 1 - 18 ));
+DATA(insert OID = 0 (  61 2 - 18 ));
+DATA(insert OID = 0 (  63 1 - 21 ));
+DATA(insert OID = 0 (  63 2 - 21 ));
+DATA(insert OID = 0 (  64 1 - 21 ));
+DATA(insert OID = 0 (  64 2 - 21 ));
+DATA(insert OID = 0 (  65 1 - 23 ));
+DATA(insert OID = 0 (  65 2 - 23 ));
+DATA(insert OID = 0 (  66 1 - 23 ));
+DATA(insert OID = 0 (  66 2 - 23 ));
+DATA(insert OID = 0 (  67 1 - 25 ));
+DATA(insert OID = 0 (  67 2 - 25 ));
+
+#endif /* PG_PARG_H */
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
new file mode 100644 (file)
index 0000000..7b37240
--- /dev/null
@@ -0,0 +1,265 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_proc.c--
+ *    routines to support manipulation of the pg_proc relation
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "utils/rel.h"
+#include "fmgr.h"
+#include "utils/elog.h"
+#include "utils/builtins.h"
+#include "utils/sets.h"
+
+#include "nodes/pg_list.h"
+
+#include "catalog/catname.h"
+#include "utils/syscache.h"
+#include "catalog/pg_proc.h"
+#include "catalog/indexing.h"
+#include "tcop/dest.h"
+#include "parser/parse_query.h"
+#include "tcop/tcopprot.h"
+#include "catalog/pg_type.h"
+#include "parser/catalog_utils.h"
+#include "utils/lsyscache.h"
+#include "optimizer/internal.h"
+#include "optimizer/planner.h"
+
+/* ----------------------------------------------------------------
+ *     ProcedureDefine
+ * ----------------------------------------------------------------
+ */
+Oid
+ProcedureCreate(char *procedureName,
+               bool returnsSet,
+               char *returnTypeName,   
+               char *languageName,
+               char *prosrc,
+               char *probin,
+               bool canCache,
+               bool trusted,
+               int32 byte_pct,
+               int32 perbyte_cpu,
+               int32 percall_cpu,
+               int32 outin_ratio,
+               List *argList,
+               CommandDest dest)
+{
+    register   i;
+    Relation   rdesc;
+    HeapTuple  tup;
+    bool        defined;
+    uint16     parameterCount;
+    char       nulls[ Natts_pg_proc ];
+    Datum      values[ Natts_pg_proc ];
+    Oid        languageObjectId;
+    Oid                typeObjectId;
+    List       *x;
+    QueryTreeList *querytree_list;
+    List       *plan_list;
+    Oid                typev[8];
+    Oid        relid;
+    Oid        toid;
+    text       *prosrctext;
+    TupleDesc   tupDesc;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(PointerIsValid(prosrc));
+    Assert(PointerIsValid(probin));
+    
+    parameterCount = 0;
+    memset(typev, 0, 8 * sizeof(Oid));
+    foreach (x, argList) {
+       Value *t = lfirst(x);
+       
+       if (parameterCount == 8)
+           elog(WARN, "Procedures cannot take more than 8 arguments");
+       
+       if (strcmp(strVal(t), "opaque") == 0) {
+           if (strcmp(languageName, "sql") == 0) {
+               elog(WARN, "ProcedureDefine: sql functions cannot take type \"opaque\"");
+           }
+           else
+               toid = 0;
+       } else {
+           toid = TypeGet(strVal(t), &defined);
+           
+           if (!OidIsValid(toid)) {
+               elog(WARN, "ProcedureCreate: arg type '%s' is not defined",
+                    strVal(t));
+           }
+           
+           if (!defined) {
+               elog(NOTICE, "ProcedureCreate: arg type '%s' is only a shell",
+                    strVal(t));
+           }
+       }
+       
+       typev[parameterCount++] = toid;
+    }
+    
+    tup = SearchSysCacheTuple(PRONAME,
+                             PointerGetDatum(procedureName),
+                             UInt16GetDatum(parameterCount),
+                             PointerGetDatum(typev),
+                             0);
+    
+    if (HeapTupleIsValid(tup))
+       elog(WARN, "ProcedureCreate: procedure %s already exists with same arguments",
+            procedureName);
+    
+    if (!strcmp(languageName, "sql"))  {
+       /* If this call is defining a set, check if the set is already
+        * defined by looking to see whether this call's function text
+        * matches a function already in pg_proc.  If so just return the 
+        * OID of the existing set.
+        */
+       if (!strcmp(procedureName, GENERICSETNAME)) {
+           prosrctext = textin(prosrc);
+           tup = SearchSysCacheTuple(PROSRC,
+                                     PointerGetDatum(prosrctext),
+                                     0,0,0);
+           if (HeapTupleIsValid(tup))
+               return tup->t_oid;
+       }
+    }
+    
+    tup = SearchSysCacheTuple(LANNAME,
+                             PointerGetDatum(languageName),
+                             0,0,0);
+    
+    if (!HeapTupleIsValid(tup))
+       elog(WARN, "ProcedureCreate: no such language %s",
+            languageName);
+    
+    languageObjectId = tup->t_oid;
+    
+    if (strcmp(returnTypeName, "opaque") == 0) {
+       if (strcmp(languageName, "sql") == 0) {
+           elog(WARN, "ProcedureCreate: sql functions cannot return type \"opaque\"");
+       }
+       else
+           typeObjectId = 0;
+    }
+    
+    else {
+       typeObjectId = TypeGet(returnTypeName, &defined);
+       
+       if (!OidIsValid(typeObjectId)) {
+           elog(NOTICE, "ProcedureCreate: type '%s' is not yet defined",
+                returnTypeName);
+#if 0
+           elog(NOTICE, "ProcedureCreate: creating a shell for type '%s'",
+                returnTypeName);
+#endif     
+           typeObjectId = TypeShellMake(returnTypeName);
+           if (!OidIsValid(typeObjectId)) {
+               elog(WARN, "ProcedureCreate: could not create type '%s'",
+                    returnTypeName);
+           }
+       }
+       
+       else if (!defined) {
+           elog(NOTICE, "ProcedureCreate: return type '%s' is only a shell",
+                returnTypeName);
+       }
+    }
+    
+    /* don't allow functions of complex types that have the same name as
+       existing attributes of the type */
+    if (parameterCount == 1 && 
+       (toid = TypeGet(strVal(lfirst(argList)), &defined)) &&
+       defined &&
+       (relid = typeid_get_relid(toid)) != 0 &&
+       get_attnum(relid, procedureName) != InvalidAttrNumber)
+       elog(WARN, "method %s already an attribute of type %s",
+            procedureName, strVal(lfirst(argList)));
+    
+    
+    /*
+     *  If this is a postquel procedure, we parse it here in order to
+     *  be sure that it contains no syntax errors.  We should store
+     *  the plan in an Inversion file for use later, but for now, we
+     *  just store the procedure's text in the prosrc attribute.
+     */
+    
+    if (strcmp(languageName, "sql") == 0) {
+       plan_list = pg_plan(prosrc, typev, parameterCount,
+                           &querytree_list, dest);
+       
+       /* typecheck return value */
+       pg_checkretval(typeObjectId, querytree_list);
+    }
+    
+    for (i = 0; i < Natts_pg_proc; ++i) {
+       nulls[i] = ' ';
+       values[i] = (Datum)NULL;
+    }
+    
+    i = 0;
+    values[i++] = PointerGetDatum(procedureName);
+    values[i++] =  Int32GetDatum(GetUserId());
+    values[i++] =  ObjectIdGetDatum(languageObjectId);
+    
+    /* XXX isinherited is always false for now */
+    
+    values[i++] = Int8GetDatum((bool) 0);
+    
+    /* XXX istrusted is always false for now */
+    
+    values[i++] =  Int8GetDatum(trusted);
+    values[i++] =  Int8GetDatum(canCache);
+    values[i++] =  UInt16GetDatum(parameterCount);
+    values[i++] =  Int8GetDatum(returnsSet);
+    values[i++] =  ObjectIdGetDatum(typeObjectId);
+    
+    values[i++] = (Datum) typev;
+    /*
+     * The following assignments of constants are made.  The real values
+     * will have to be extracted from the arglist someday soon.
+     */
+    values[i++] =  Int32GetDatum(byte_pct); /* probyte_pct */
+    values[i++] =  Int32GetDatum(perbyte_cpu); /* properbyte_cpu */
+    values[i++] =  Int32GetDatum(percall_cpu); /* propercall_cpu */
+    values[i++] =  Int32GetDatum(outin_ratio); /* prooutin_ratio */
+    
+    values[i++] = (Datum)fmgr(TextInRegProcedure, prosrc);     /* prosrc */
+    values[i++] = (Datum)fmgr(TextInRegProcedure, probin);   /* probin */
+    
+    rdesc = heap_openr(ProcedureRelationName);
+    
+    tupDesc = rdesc->rd_att;
+    tup = heap_formtuple(tupDesc,
+                        values,
+                        nulls);
+    
+    heap_insert(rdesc, tup);
+    
+    if (RelationGetRelationTupleForm(rdesc)->relhasindex)
+       {
+           Relation idescs[Num_pg_proc_indices];
+           
+           CatalogOpenIndices(Num_pg_proc_indices, Name_pg_proc_indices, idescs);
+           CatalogIndexInsert(idescs, Num_pg_proc_indices, rdesc, tup);
+           CatalogCloseIndices(Num_pg_proc_indices, idescs);
+       }
+    heap_close(rdesc);
+    return tup->t_oid;
+}
+
diff --git a/src/backend/catalog/pg_proc.h b/src/backend/catalog/pg_proc.h
new file mode 100644 (file)
index 0000000..5655940
--- /dev/null
@@ -0,0 +1,769 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_proc.h--
+ *    definition of the system "procedure" relation (pg_proc)
+ *    along with the relation's initial contents.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    The script catalog/genbki.sh reads this file and generates .bki
+ *    information from the DATA() statements.  utils/Gen_fmgrtab.sh 
+ *    generates fmgr.h and fmgrtab.c the same way.
+ *
+ *    XXX do NOT break up DATA() statements into multiple lines!
+ *        the scripts are not as smart as you might think...
+ *    XXX (eg. #if 0 #endif won't do what you think)
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PROC_H
+#define PG_PROC_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+#include "nodes/pg_list.h"
+#include "tcop/dest.h"
+
+/* ----------------
+ *     pg_proc definition.  cpp turns this into
+ *     typedef struct FormData_pg_proc
+ * ----------------
+ */
+CATALOG(pg_proc) BOOTSTRAP {
+    NameData   proname;
+    Oid        proowner;
+    Oid        prolang;
+    bool       proisinh;
+    bool       proistrusted;
+    bool       proiscachable;
+    int2       pronargs;
+    bool       proretset;
+    Oid        prorettype;
+    oid8        proargtypes;
+    int4        probyte_pct;
+    int4        properbyte_cpu;
+    int4        propercall_cpu;
+    int4        prooutin_ratio;
+    text       prosrc;         /* VARIABLE LENGTH FIELD */
+    bytea      probin;         /* VARIABLE LENGTH FIELD */
+} FormData_pg_proc;
+
+/* ----------------
+ *     Form_pg_proc corresponds to a pointer to a tuple with
+ *     the format of pg_proc relation.
+ * ----------------
+ */
+typedef FormData_pg_proc       *Form_pg_proc;
+
+/* ----------------
+ *     compiler constants for pg_proc
+ * ----------------
+ */
+#define Natts_pg_proc                  16
+#define Anum_pg_proc_proname           1
+#define Anum_pg_proc_proowner          2
+#define Anum_pg_proc_prolang           3
+#define Anum_pg_proc_proisinh          4
+#define Anum_pg_proc_proistrusted      5
+#define Anum_pg_proc_proiscachable     6
+#define Anum_pg_proc_pronargs          7
+#define Anum_pg_proc_proretset         8
+#define Anum_pg_proc_prorettype                9
+#define Anum_pg_proc_proargtypes        10
+#define Anum_pg_proc_probyte_pct        11
+#define Anum_pg_proc_properbyte_cpu     12
+#define Anum_pg_proc_propercall_cpu     13
+#define Anum_pg_proc_prooutin_ratio     14 
+#define Anum_pg_proc_prosrc            15
+#define Anum_pg_proc_probin            16
+
+/* ----------------
+ *     initial contents of pg_proc
+ * ----------------
+ */
+
+/* keep the following ordered by OID so that later changes can be made easier*/
+
+/* OIDS 1 - 99 */
+DATA(insert OID =  28 (  boolin            PGUID 11 f t f 1 f 16 "0" 100 0 0  100  foo bar ));
+DATA(insert OID =  29 (  boolout           PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  30 (  byteain           PGUID 11 f t f 1 f 17 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  31 (  byteaout          PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  32 (  charin            PGUID 11 f t f 1 f 18 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  33 (  charout           PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  34 (  namein          PGUID 11 f t f 1 f 19 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  35 (  nameout         PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  36 (  char16in          PGUID 11 f t f 1 f 19 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  37 (  char16out         PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  38 (  int2in            PGUID 11 f t f 1 f 21 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  39 (  int2out           PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  40 (  int28in           PGUID 11 f t f 1 f 22 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  41 (  int28out          PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  42 (  int4in            PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  43 (  int4out           PGUID 11 f t f 1 f 19 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  44 (  regprocin         PGUID 11 f t f 1 f 24 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  45 (  regprocout        PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  46 (  textin            PGUID 11 f t f 1 f 25 "0" 100 0 0 100  foo bar ));
+#define TextInRegProcedure 46
+
+DATA(insert OID =  47 (  textout           PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  48 (  tidin             PGUID 11 f t f 1 f 27 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  49 (  tidout            PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  50 (  xidin             PGUID 11 f t f 1 f 28 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  51 (  xidout            PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  52 (  cidin             PGUID 11 f t f 1 f 29 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  53 (  cidout            PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  54 (  oid8in            PGUID 11 f t f 1 f 30 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  55 (  oid8out           PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID =  60 (  booleq            PGUID 11 f t f 2 f 16 "16 16" 100 0 0 100  foo bar ));
+DATA(insert OID =  61 (  chareq            PGUID 11 f t f 2 f 16 "18 18" 100 0 0 100  foo bar ));
+#define       CharacterEqualRegProcedure      61
+
+DATA(insert OID =  62 (  nameeq          PGUID 11 f t f 2 f 16 "19 19" 100 0 0 100  foo bar ));
+#define NameEqualRegProcedure          62
+    
+DATA(insert OID =  63 (  int2eq            PGUID 11 f t f 2 f 16 "21 21" 100 0 0 100  foo bar ));
+#define Integer16EqualRegProcedure     63
+    
+DATA(insert OID =  64 (  int2lt            PGUID 11 f t f 2 f 16 "21 21" 100 0 0 100  foo bar ));
+DATA(insert OID =  65 (  int4eq            PGUID 11 f t f 2 f 16 "23 23" 100 0 0 100  foo bar ));
+#define Integer32EqualRegProcedure     65
+    
+DATA(insert OID =  66 (  int4lt            PGUID 11 f t f 2 f 16 "23 23" 100 0 0 100  foo bar ));
+DATA(insert OID =  67 (  texteq            PGUID 11 f t f 2 f 16 "25 25" 100 0 0 0  foo bar ));
+#define TextEqualRegProcedure           67
+
+DATA(insert OID =  68 (  xideq             PGUID 11 f t f 2 f 16 "28 28" 100 0 0 100  foo bar ));
+DATA(insert OID =  69 (  cideq             PGUID 11 f t f 2 f 16 "29 29" 100 0 0 100  foo bar ));
+DATA(insert OID =  70 (  charne            PGUID 11 f t f 2 f 16 "18 18" 100 0 0 100  foo bar ));
+DATA(insert OID =  71 (  charlt            PGUID 11 f t f 2 f 16 "18 18" 100 0 0 100  foo bar ));
+DATA(insert OID =  72 (  charle            PGUID 11 f t f 2 f 16 "18 18" 100 0 0 100  foo bar ));
+DATA(insert OID =  73 (  chargt            PGUID 11 f t f 2 f 16 "18 18" 100 0 0 100  foo bar ));
+DATA(insert OID =  74 (  charge            PGUID 11 f t f 2 f 16 "18 18" 100 0 0 100  foo bar ));
+DATA(insert OID =  75 (  charpl            PGUID 11 f t f 2 f 18 "18 18" 100 0 0 100  foo bar ));
+DATA(insert OID =  76 (  charmi            PGUID 11 f t f 2 f 18 "18 18" 100 0 0 100  foo bar ));
+DATA(insert OID =  77 (  charmul           PGUID 11 f t f 2 f 18 "18 18" 100 0 0 100  foo bar ));
+DATA(insert OID =  78 (  chardiv           PGUID 11 f t f 2 f 18 "18 18" 100 0 0 100  foo bar ));
+
+DATA(insert OID =  79 (  nameregexeq     PGUID 11 f t f 2 f 16 "19 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  80 (  nameregexne     PGUID 11 f t f 2 f 16 "19 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  81 (  textregexeq       PGUID 11 f t f 2 f 16 "25 25" 100 0 1 0  foo bar ));
+DATA(insert OID =  82 (  textregexne       PGUID 11 f t f 2 f 16 "25 25" 100 0 1 0  foo bar ));
+DATA(insert OID =  83 (  textcat           PGUID 11 f t f 2 f 25 "25 25" 100 0 1 0  foo bar ));
+DATA(insert OID =  84 (  boolne            PGUID 11 f t f 2 f 16 "16 16" 100 0 0 100  foo bar ));
+
+DATA(insert OID =  97 (  rtsel             PGUID 11 f t f 7 f 701 "26 26 21 0 23 23 26" 100 0 0 100  foo bar ));
+DATA(insert OID =  98 (  rtnpage           PGUID 11 f t f 7 f 701 "26 26 21 0 23 23 26" 100 0 0 100  foo bar ));
+DATA(insert OID =  99 (  btreesel          PGUID 11 f t f 7 f 701 "26 26 21 0 23 23 26" 100 0 0 100  foo bar ));
+
+/* OIDS 100 - 199 */
+
+DATA(insert OID = 100 (  btreenpage        PGUID 11 f t f 7 f 701 "26 26 21 0 23 23 26" 100 0 0 100  foo bar ));
+DATA(insert OID = 101 (  eqsel             PGUID 11 f t f 5 f 701 "26 26 21 0 23" 100 0 0 100  foo bar ));
+#define EqualSelectivityProcedure 101
+
+DATA(insert OID = 102 (  neqsel            PGUID 11 f t f 5 f 701 "26 26 21 0 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 103 (  intltsel          PGUID 11 f t f 5 f 701 "26 26 21 0 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 104 (  intgtsel          PGUID 11 f t f 5 f 701 "26 26 21 0 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 105 (  eqjoinsel         PGUID 11 f t f 5 f 701 "26 26 21 26 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 106 (  neqjoinsel        PGUID 11 f t f 5 f 701 "26 26 21 26 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 107 (  intltjoinsel      PGUID 11 f t f 5 f 701 "26 26 21 26 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 108 (  intgtjoinsel      PGUID 11 f t f 5 f 701 "26 26 21 26 21" 100 0 0 100  foo bar ));
+
+
+
+DATA(insert OID = 117 (  point_in          PGUID 11 f t f 1 f 600 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 118 (  point_out         PGUID 11 f t f 1 f 23  "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 119 (  lseg_in           PGUID 11 f t f 1 f 601 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 120 (  lseg_out          PGUID 11 f t f 1 f 23  "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 121 (  path_in           PGUID 11 f t f 1 f 602 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 122 (  path_out          PGUID 11 f t f 1 f 23  "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 123 (  box_in            PGUID 11 f t f 1 f 603 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 124 (  box_out           PGUID 11 f t f 1 f 23  "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 125 (  box_overlap       PGUID 11 f t f 2 f 16 "603 603" 100 1 0 100  foo bar ));
+DATA(insert OID = 126 (  box_ge            PGUID 11 f t f 2 f 16 "603 603" 100 1 0 100  foo bar ));
+DATA(insert OID = 127 (  box_gt            PGUID 11 f t f 2 f 16 "603 603" 100 1 0 100  foo bar ));
+DATA(insert OID = 128 (  box_eq            PGUID 11 f t f 2 f 16 "603 603" 100 1 0 100  foo bar ));
+DATA(insert OID = 129 (  box_lt            PGUID 11 f t f 2 f 16 "603 603" 100 1 0 100  foo bar ));
+DATA(insert OID = 130 (  box_le            PGUID 11 f t f 2 f 16 "603 603" 100 1 0 100  foo bar ));
+DATA(insert OID = 131 (  point_above       PGUID 11 f t f 2 f 16 "600 600" 100 0 0 100  foo bar ));
+DATA(insert OID = 132 (  point_left        PGUID 11 f t f 2 f 16 "600 600" 100 0 0 100  foo bar ));
+DATA(insert OID = 133 (  point_right       PGUID 11 f t f 2 f 16 "600 600" 100 0 0 100  foo bar ));
+DATA(insert OID = 134 (  point_below       PGUID 11 f t f 2 f 16 "600 600" 100 0 0 100  foo bar ));
+DATA(insert OID = 135 (  point_eq          PGUID 11 f t f 2 f 16 "600 600" 100 0 0 100  foo bar ));
+DATA(insert OID = 136 (  on_pb             PGUID 11 f t f 2 f 16 "600 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 137 (  on_ppath          PGUID 11 f t f 2 f 16 "600 602" 100 0 1 0  foo bar ));
+DATA(insert OID = 138 (  box_center        PGUID 11 f t f 1 f 600 "603" 100 1 0 100  foo bar ));
+DATA(insert OID = 139 (  areasel           PGUID 11 f t f 5 f 701 "26 26 21 0 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 140 (  areajoinsel       PGUID 11 f t f 5 f 701 "26 26 21 0 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 141 (  int4mul           PGUID 11 f t f 2 f 23 "23 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 142 (  int4fac           PGUID 11 f t f 1 f 23 "23" 100 0 0 100  foo bar ));
+DATA(insert OID = 143 (  pointdist         PGUID 11 f t f 2 f 23 "600 600" 100 0 0 100  foo bar ));
+DATA(insert OID = 144 (  int4ne            PGUID 11 f t f 2 f 16 "23 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 145 (  int2ne            PGUID 11 f t f 2 f 16 "21 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 146 (  int2gt            PGUID 11 f t f 2 f 16 "21 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 147 (  int4gt            PGUID 11 f t f 2 f 16 "23 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 148 (  int2le            PGUID 11 f t f 2 f 16 "21 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 149 (  int4le            PGUID 11 f t f 2 f 16 "23 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 150 (  int4ge            PGUID 11 f t f 2 f 16 "23 23" 100 0 0 100  foo bar ));
+#define INT4GE_PROC_OID 150
+DATA(insert OID = 151 (  int2ge            PGUID 11 f t f 2 f 16 "21 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 152 (  int2mul           PGUID 11 f t f 2 f 21 "21 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 153 (  int2div           PGUID 11 f t f 2 f 21 "21 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 154 (  int4div           PGUID 11 f t f 2 f 23 "23 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 155 (  int2mod           PGUID 11 f t f 2 f 21 "21 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 156 (  int4mod           PGUID 11 f t f 2 f 23 "23 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 157 (  textne            PGUID 11 f t f 2 f 16 "25 25" 100 0 0 0  foo bar ));
+DATA(insert OID = 158 (  int24eq           PGUID 11 f t f 2 f 23 "21 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 159 (  int42eq           PGUID 11 f t f 2 f 23 "23 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 160 (  int24lt           PGUID 11 f t f 2 f 23 "21 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 161 (  int42lt           PGUID 11 f t f 2 f 23 "23 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 162 (  int24gt           PGUID 11 f t f 2 f 23 "21 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 163 (  int42gt           PGUID 11 f t f 2 f 23 "23 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 164 (  int24ne           PGUID 11 f t f 2 f 23 "21 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 165 (  int42ne           PGUID 11 f t f 2 f 23 "23 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 166 (  int24le           PGUID 11 f t f 2 f 23 "21 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 167 (  int42le           PGUID 11 f t f 2 f 23 "23 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 168 (  int24ge           PGUID 11 f t f 2 f 23 "21 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 169 (  int42ge           PGUID 11 f t f 2 f 23 "23 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 170 (  int24mul          PGUID 11 f t f 2 f 23 "21 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 171 (  int42mul          PGUID 11 f t f 2 f 23 "23 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 172 (  int24div          PGUID 11 f t f 2 f 23 "21 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 173 (  int42div          PGUID 11 f t f 2 f 23 "23 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 174 (  int24mod          PGUID 11 f t f 2 f 23 "21 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 175 (  int42mod          PGUID 11 f t f 2 f 23 "23 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 176 (  int2pl            PGUID 11 f t f 2 f 21 "21 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 177 (  int4pl            PGUID 11 f t f 2 f 23 "23 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 178 (  int24pl           PGUID 11 f t f 2 f 23 "21 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 179 (  int42pl           PGUID 11 f t f 2 f 23 "23 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 180 (  int2mi            PGUID 11 f t f 2 f 21 "21 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 181 (  int4mi            PGUID 11 f t f 2 f 23 "23 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 182 (  int24mi           PGUID 11 f t f 2 f 23 "21 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 183 (  int42mi           PGUID 11 f t f 2 f 23 "23 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 184 (  oideq             PGUID 11 f t f 2 f 16 "26 26" 100 0 0 100  foo bar ));
+#define ObjectIdEqualRegProcedure      184
+    
+DATA(insert OID = 185 (  oidne             PGUID 11 f t f 2 f 16 "26 26" 100 0 0 100  foo bar ));
+DATA(insert OID = 186 (  box_same          PGUID 11 f t f 2 f 16 "603 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 187 (  box_contain       PGUID 11 f t f 2 f 16 "603 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 188 (  box_left          PGUID 11 f t f 2 f 16 "603 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 189 (  box_overleft      PGUID 11 f t f 2 f 16 "603 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 190 (  box_overright     PGUID 11 f t f 2 f 16 "603 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 191 (  box_right         PGUID 11 f t f 2 f 16 "603 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 192 (  box_contained     PGUID 11 f t f 2 f 16 "603 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 193 (  rt_box_union      PGUID 11 f t f 2 f 603 "603 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 194 (  rt_box_inter      PGUID 11 f t f 2 f 603 "603 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 195 (  rt_box_size       PGUID 11 f t f 2 f 700 "603 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 196 (  rt_bigbox_size    PGUID 11 f t f 2 f 700 "603 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 197 (  rt_poly_union     PGUID 11 f t f 2 f 604 "604 604" 100 0 0 100  foo bar ));
+DATA(insert OID = 198 (  rt_poly_inter     PGUID 11 f t f 2 f 604 "604 604" 100 0 0 100  foo bar ));
+DATA(insert OID = 199 (  rt_poly_size      PGUID 11 f t f 2 f 23 "604 23" 100 0 0 100  foo bar ));
+
+/* OIDS 200 - 299 */
+
+DATA(insert OID = 200 (  float4in          PGUID 11 f t f 1 f 700 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 201 (  float4out         PGUID 11 f t f 1 f 23  "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 202 (  float4mul         PGUID 11 f t f 2 f 700 "700 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 203 (  float4div         PGUID 11 f t f 2 f 700 "700 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 204 (  float4pl          PGUID 11 f t f 2 f 700 "700 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 205 (  float4mi          PGUID 11 f t f 2 f 700 "700 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 206 (  float4um          PGUID 11 f t f 1 f 700 "700" 100 0 0 100  foo bar ));
+DATA(insert OID = 207 (  float4abs         PGUID 11 f t f 1 f 700 "700 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 208 (  float4inc         PGUID 11 f t f 1 f 700 "700" 100 0 0 100  foo bar ));
+DATA(insert OID = 209 (  float4larger      PGUID 11 f t f 2 f 700 "700 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 211 (  float4smaller     PGUID 11 f t f 2 f 700 "700 700" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 212 (  int4um            PGUID 11 f t f 1 f 23 "23" 100 0 0 100  foo bar ));
+DATA(insert OID = 213 (  int2um            PGUID 11 f t f 1 f 21 "21" 100 0 0 100  foo bar ));
+    
+DATA(insert OID = 214 (  float8in          PGUID 11 f t f 1 f 701 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 215 (  float8out         PGUID 11 f t f 1 f 23  "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 216 (  float8mul         PGUID 11 f t f 2 f 701 "701 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 217 (  float8div         PGUID 11 f t f 2 f 701 "701 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 218 (  float8pl          PGUID 11 f t f 2 f 701 "701 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 219 (  float8mi          PGUID 11 f t f 2 f 701 "701 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 220 (  float8um          PGUID 11 f t f 1 f 701 "701" 100 0 0 100  foo bar ));
+DATA(insert OID = 221 (  float8abs         PGUID 11 f t f 1 f 701 "701" 100 0 0 100  foo bar ));
+DATA(insert OID = 222 (  float8inc         PGUID 11 f t f 1 f 701 "701" 100 0 0 100  foo bar ));
+DATA(insert OID = 223 (  float8larger      PGUID 11 f t f 2 f 701 "701 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 224 (  float8smaller     PGUID 11 f t f 2 f 701 "701 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 228 (  dround            PGUID 11 f t f 1 f 701 "701" 100 0 0 100  foo bar ));
+DATA(insert OID = 229 (  dtrunc            PGUID 11 f t f 1 f 701 "701" 100 0 0 100  foo bar ));
+DATA(insert OID = 230 (  dsqrt             PGUID 11 f t f 1 f 701 "701" 100 0 0 100  foo bar ));
+DATA(insert OID = 231 (  dcbrt             PGUID 11 f t f 1 f 701 "701" 100 0 0 100  foo bar ));
+DATA(insert OID = 232 (  dpow              PGUID 11 f t f 2 f 701 "701" 100 0 0 100  foo bar ));
+DATA(insert OID = 233 (  dexp              PGUID 11 f t f 1 f 701 "701" 100 0 0 100  foo bar ));
+DATA(insert OID = 234 (  dlog1             PGUID 11 f t f 1 f 701 "701" 100 0 0 100  foo bar ));
+    
+DATA(insert OID = 240 (  nabstimein        PGUID 11 f t f 1 f 702 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 241 (  nabstimeout       PGUID 11 f t f 1 f 23  "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 242 (  reltimein         PGUID 11 f t f 1 f 703 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 243 (  reltimeout        PGUID 11 f t f 1 f 23  "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 244 (  timepl            PGUID 11 f t f 2 f 702 "702 703" 100 0 0 100  foo bar ));
+DATA(insert OID = 245 (  timemi            PGUID 11 f t f 2 f 702 "702 703" 100 0 0 100  foo bar ));
+DATA(insert OID = 246 (  tintervalin       PGUID 11 f t f 1 f 704 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 247 (  tintervalout      PGUID 11 f t f 1 f 23  "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 248 (  ininterval        PGUID 11 f t f 2 f 16 "702 704" 100 0 0 100  foo bar ));
+DATA(insert OID = 249 (  intervalrel       PGUID 11 f t f 1 f 703 "704" 100 0 0 100  foo bar ));
+DATA(insert OID = 250 (  timenow           PGUID 11 f t f 0 f 702 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 251 (  abstimeeq         PGUID 11 f t f 2 f 16 "702 702" 100 0 0 100  foo bar ));
+DATA(insert OID = 252 (  abstimene         PGUID 11 f t f 2 f 16 "702 702" 100 0 0 100  foo bar ));
+DATA(insert OID = 253 (  abstimelt         PGUID 11 f t f 2 f 16 "702 702" 100 0 0 100  foo bar ));
+DATA(insert OID = 254 (  abstimegt         PGUID 11 f t f 2 f 16 "702 702" 100 0 0 100  foo bar ));
+DATA(insert OID = 255 (  abstimele         PGUID 11 f t f 2 f 16 "702 702" 100 0 0 100  foo bar ));
+DATA(insert OID = 256 (  abstimege         PGUID 11 f t f 2 f 16 "702 702" 100 0 0 100  foo bar ));
+DATA(insert OID = 257 (  reltimeeq         PGUID 11 f t f 2 f 16 "703 703" 100 0 0 100  foo bar ));
+DATA(insert OID = 258 (  reltimene         PGUID 11 f t f 2 f 16 "703 703" 100 0 0 100  foo bar ));
+DATA(insert OID = 259 (  reltimelt         PGUID 11 f t f 2 f 16 "703 703" 100 0 0 100  foo bar ));
+DATA(insert OID = 260 (  reltimegt         PGUID 11 f t f 2 f 16 "703 703" 100 0 0 100  foo bar ));
+DATA(insert OID = 261 (  reltimele         PGUID 11 f t f 2 f 16 "703 703" 100 0 0 100  foo bar ));
+DATA(insert OID = 262 (  reltimege         PGUID 11 f t f 2 f 16 "703 703" 100 0 0 100  foo bar ));
+DATA(insert OID = 263 (  intervaleq        PGUID 11 f t f 2 f 16 "704 704" 100 0 0 100  foo bar ));
+DATA(insert OID = 264 (  intervalct        PGUID 11 f t f 2 f 16 "704 704" 100 0 0 100  foo bar ));
+DATA(insert OID = 265 (  intervalov        PGUID 11 f t f 2 f 16 "704 704" 100 0 0 100  foo bar ));
+DATA(insert OID = 266 (  intervalleneq     PGUID 11 f t f 2 f 16 "704 703" 100 0 0 100  foo bar ));
+DATA(insert OID = 267 (  intervallenne     PGUID 11 f t f 2 f 16 "704 703" 100 0 0 100  foo bar ));
+DATA(insert OID = 268 (  intervallenlt     PGUID 11 f t f 2 f 16 "704 703" 100 0 0 100  foo bar ));
+DATA(insert OID = 269 (  intervallengt     PGUID 11 f t f 2 f 16 "704 703" 100 0 0 100  foo bar ));
+DATA(insert OID = 270 (  intervallenle     PGUID 11 f t f 2 f 16 "704 703" 100 0 0 100  foo bar ));
+DATA(insert OID = 271 (  intervallenge     PGUID 11 f t f 2 f 16 "704 703" 100 0 0 100  foo bar ));
+DATA(insert OID = 272 (  intervalstart     PGUID 11 f t f 1 f 702 "704" 100 0 0 100  foo bar ));
+DATA(insert OID = 273 (  intervalend       PGUID 11 f t f 1 f 702 "704" 100 0 0 100  foo bar ));
+DATA(insert OID = 274 (  timeofday         PGUID 11 f t f 0 f 25 "0" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 276 (  int2fac           PGUID 11 f t f 1 f 21 "21" 100 0 0 100  foo bar ));
+DATA(insert OID = 279 (  float48mul        PGUID 11 f t f 2 f 701 "700 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 280 (  float48div        PGUID 11 f t f 2 f 701 "700 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 281 (  float48pl         PGUID 11 f t f 2 f 701 "700 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 282 (  float48mi         PGUID 11 f t f 2 f 701 "700 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 283 (  float84mul        PGUID 11 f t f 2 f 701 "701 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 284 (  float84div        PGUID 11 f t f 2 f 701 "701 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 285 (  float84pl         PGUID 11 f t f 2 f 701 "701 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 286 (  float84mi         PGUID 11 f t f 2 f 701 "701 700" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 287 (  float4eq          PGUID 11 f t f 2 f 16 "700 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 288 (  float4ne          PGUID 11 f t f 2 f 16 "700 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 289 (  float4lt          PGUID 11 f t f 2 f 16 "700 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 290 (  float4le          PGUID 11 f t f 2 f 16 "700 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 291 (  float4gt          PGUID 11 f t f 2 f 16 "700 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 292 (  float4ge          PGUID 11 f t f 2 f 16 "700 700" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 293 (  float8eq          PGUID 11 f t f 2 f 16 "701 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 294 (  float8ne          PGUID 11 f t f 2 f 16 "701 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 295 (  float8lt          PGUID 11 f t f 2 f 16 "701 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 296 (  float8le          PGUID 11 f t f 2 f 16 "701 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 297 (  float8gt          PGUID 11 f t f 2 f 16 "701 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 298 (  float8ge          PGUID 11 f t f 2 f 16 "701 701" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 299 (  float48eq         PGUID 11 f t f 2 f 16 "700 701" 100 0 0 100  foo bar ));
+
+/* OIDS 300 - 399 */
+
+DATA(insert OID = 300 (  float48ne         PGUID 11 f t f 2 f 16 "700 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 301 (  float48lt         PGUID 11 f t f 2 f 16 "700 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 302 (  float48le         PGUID 11 f t f 2 f 16 "700 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 303 (  float48gt         PGUID 11 f t f 2 f 16 "700 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 304 (  float48ge         PGUID 11 f t f 2 f 16 "700 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 305 (  float84eq         PGUID 11 f t f 2 f 16 "701 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 306 (  float84ne         PGUID 11 f t f 2 f 16 "701 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 307 (  float84lt         PGUID 11 f t f 2 f 16 "701 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 308 (  float84le         PGUID 11 f t f 2 f 16 "701 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 309 (  float84gt         PGUID 11 f t f 2 f 16 "701 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 310 (  float84ge         PGUID 11 f t f 2 f 16 "701 700" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 311 (  ftod              PGUID 11 f t f 2 f 701 "700" 100 0 0 100  foo bar ));
+DATA(insert OID = 312 (  dtof              PGUID 11 f t f 2 f 700 "701" 100 0 0 100  foo bar ));
+DATA(insert OID = 313 (  i2toi4            PGUID 11 f t f 2 f 23 "21" 100 0 0 100  foo bar ));
+DATA(insert OID = 314 (  i4toi2            PGUID 11 f t f 2 f 21 "23" 100 0 0 100  foo bar ));
+DATA(insert OID = 315 (  keyfirsteq        PGUID 11 f t f 2 f 16 "0 21" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 320 (  rtinsert          PGUID 11 f t f 2 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 321 (  rtdelete          PGUID 11 f t f 2 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 322 (  rtgettuple        PGUID 11 f t f 2 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 323 (  rtbuild           PGUID 11 f t f 9 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 324 (  rtbeginscan       PGUID 11 f t f 4 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 325 (  rtendscan         PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 326 (  rtmarkpos         PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 327 (  rtrestrpos        PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 328 (  rtrescan          PGUID 11 f t f 3 f 23 "0" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 330 (  btgettuple        PGUID 11 f t f 2 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 331 (  btinsert          PGUID 11 f t f 2 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 332 (  btdelete          PGUID 11 f t f 2 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 333 (  btbeginscan       PGUID 11 f t f 4 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 334 (  btrescan          PGUID 11 f t f 3 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 335 (  btendscan         PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 336 (  btmarkpos         PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 337 (  btrestrpos        PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 338 (  btbuild           PGUID 11 f t f 9 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 339 (  poly_same         PGUID 11 f t f 2 f 16 "604 604" 100 0 1 0  foo bar ));
+DATA(insert OID = 340 (  poly_contain      PGUID 11 f t f 2 f 16 "604 604" 100 0 1 0  foo bar ));
+DATA(insert OID = 341 (  poly_left         PGUID 11 f t f 2 f 16 "604 604" 100 0 1 0  foo bar ));
+DATA(insert OID = 342 (  poly_overleft     PGUID 11 f t f 2 f 16 "604 604" 100 0 1 0  foo bar ));
+DATA(insert OID = 343 (  poly_overright    PGUID 11 f t f 2 f 16 "604 604" 100 0 1 0  foo bar ));
+DATA(insert OID = 344 (  poly_right        PGUID 11 f t f 2 f 16 "604 604" 100 0 1 0  foo bar ));
+DATA(insert OID = 345 (  poly_contained    PGUID 11 f t f 2 f 16 "604 604" 100 0 1 0  foo bar ));
+DATA(insert OID = 346 (  poly_overlap      PGUID 11 f t f 2 f 16 "604 604" 100 0 1 0  foo bar ));
+DATA(insert OID = 347 (  poly_in           PGUID 11 f t f 1 f 604 "0" 100 0 1 0  foo bar ));
+DATA(insert OID = 348 (  poly_out          PGUID 11 f t f 1 f 23  "0" 100 0 1 0  foo bar ));
+
+DATA(insert OID = 350 (  btint2cmp         PGUID 11 f t f 2 f 23 "21 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 351 (  btint4cmp         PGUID 11 f t f 2 f 23 "23 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 352 (  btint42cmp        PGUID 11 f t f 2 f 23 "23 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 353 (  btint24cmp        PGUID 11 f t f 2 f 23 "21 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 354 (  btfloat4cmp       PGUID 11 f t f 2 f 23 "700 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 355 (  btfloat8cmp       PGUID 11 f t f 2 f 23 "701 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 356 (  btoidcmp          PGUID 11 f t f 2 f 23 "26 26" 100 0 0 100  foo bar ));
+DATA(insert OID = 357 (  btabstimecmp      PGUID 11 f t f 2 f 23 "702 702" 100 0 0 100  foo bar ));
+DATA(insert OID = 358 (  btcharcmp         PGUID 11 f t f 2 f 23 "18 18" 100 0 0 100  foo bar ));
+DATA(insert OID = 359 (  btnamecmp       PGUID 11 f t f 2 f 23 "19 19" 100 0 0 100  foo bar ));
+DATA(insert OID = 360 (  bttextcmp         PGUID 11 f t f 2 f 23 "25 25" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 361 (  lseg_distance     PGUID 11 f t f 2 f 701 "601 601" 100 0 0 100  foo bar ));
+DATA(insert OID = 362 (  lseg_interpt      PGUID 11 f t f 2 f 600 "601 601" 100 0 0 100  foo bar ));
+DATA(insert OID = 363 (  dist_ps           PGUID 11 f t f 2 f 701 "600 601" 100 0 0 100  foo bar ));
+DATA(insert OID = 364 (  dist_pb           PGUID 11 f t f 2 f 701 "600 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 365 (  dist_sb           PGUID 11 f t f 2 f 701 "601 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 366 (  close_ps          PGUID 11 f t f 2 f 600 "600 601" 100 0 0 100  foo bar ));
+DATA(insert OID = 367 (  close_pb          PGUID 11 f t f 2 f 600 "600 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 368 (  close_sb          PGUID 11 f t f 2 f 600 "601 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 369 (  on_ps             PGUID 11 f t f 2 f 16 "600 601" 100 0 0 100  foo bar ));
+DATA(insert OID = 370 (  path_distance     PGUID 11 f t f 2 f 701 "602 602" 100 0 1 0 foo bar ));
+DATA(insert OID = 371 (  dist_ppth         PGUID 11 f t f 2 f 701 "600 602" 100 0 1 0 foo bar ));
+DATA(insert OID = 372 (  on_sb             PGUID 11 f t f 2 f 16 "601 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 373 (  inter_sb          PGUID 11 f t f 2 f 16 "601 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 374 (  btchar16cmp       PGUID 11 f t f 2 f 23 "19 19" 100 0 0 100  foo bar ));
+
+/* OIDS 400 - 499 */
+
+DATA(insert OID =  438 (  hashsel          PGUID 11 f t t 7 f 701 "26 26 21 0 23 23 26" 100 0 0 100  foo bar ));
+DATA(insert OID =  439 (  hashnpage        PGUID 11 f t t 7 f 701 "26 26 21 0 23 23 26" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 440 (  hashgettuple     PGUID 11 f t f 2 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 441 (  hashinsert       PGUID 11 f t f 2 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 442 (  hashdelete       PGUID 11 f t f 2 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 443 (  hashbeginscan    PGUID 11 f t f 4 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 444 (  hashrescan       PGUID 11 f t f 3 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 445 (  hashendscan      PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 446 (  hashmarkpos      PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 447 (  hashrestrpos     PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 448 (  hashbuild        PGUID 11 f t f 9 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 449 (  hashint2         PGUID 11 f t f 2 f 23 "21 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 450 (  hashint4         PGUID 11 f t f 2 f 23 "23 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 451 (  hashfloat4       PGUID 11 f t f 2 f 23 "700 700" 100 0 0 100  foo bar ));
+DATA(insert OID = 452 (  hashfloat8       PGUID 11 f t f 2 f 23 "701 701" 100 0 0 100  foo bar ));
+DATA(insert OID = 453 (  hashoid          PGUID 11 f t f 2 f 23 "26 26" 100 0 0 100  foo bar ));
+DATA(insert OID = 454 (  hashchar         PGUID 11 f t f 2 f 23 "18 18" 100 0 0 100  foo bar ));
+DATA(insert OID = 455 (  hashname       PGUID 11 f t f 2 f 23 "19 19" 100 0 0 100  foo bar ));
+DATA(insert OID = 456 (  hashtext         PGUID 11 f t f 2 f 23 "25 25" 100 0 0 100  foo bar ));
+DATA(insert OID = 466 (  char2in          PGUID 11 f t f 1 f 409 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 467 (  char4in          PGUID 11 f t f 1 f 410 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 468 (  char8in          PGUID 11 f t f 1 f 411 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 469 (  char2out         PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 470 (  char4out         PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 471 (  char8out         PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 472 (  char2eq          PGUID 11 f t f 2 f 16 "409 409" 100 0 0 100  foo bar ));
+DATA(insert OID = 473 (  char4eq          PGUID 11 f t f 2 f 16 "410 410" 100 0 0 100  foo bar ));
+DATA(insert OID = 474 (  char8eq          PGUID 11 f t f 2 f 16 "411 411" 100 0 0 100  foo bar ));
+DATA(insert OID = 475 (  char2lt          PGUID 11 f t f 2 f 16 "409 409" 100 0 0 100  foo bar ));
+DATA(insert OID = 476 (  char4lt          PGUID 11 f t f 2 f 16 "410 410" 100 0 0 100  foo bar ));
+DATA(insert OID = 477 (  char8lt          PGUID 11 f t f 2 f 16 "411 411" 100 0 0 100  foo bar ));
+DATA(insert OID = 478 (  char2le          PGUID 11 f t f 2 f 16 "409 409" 100 0 0 100  foo bar ));
+DATA(insert OID = 479 (  char4le          PGUID 11 f t f 2 f 16 "410 410" 100 0 0 100  foo bar ));
+DATA(insert OID = 480 (  char8le          PGUID 11 f t f 2 f 16 "411 411" 100 0 0 100  foo bar ));
+DATA(insert OID = 481 (  char2gt          PGUID 11 f t f 2 f 16 "409 409" 100 0 0 100  foo bar ));
+DATA(insert OID = 482 (  char4gt          PGUID 11 f t f 2 f 16 "410 410" 100 0 0 100  foo bar ));
+DATA(insert OID = 483 (  char8gt          PGUID 11 f t f 2 f 16 "411 411" 100 0 0 100  foo bar ));
+DATA(insert OID = 484 (  char2ge          PGUID 11 f t f 2 f 16 "409 409" 100 0 0 100  foo bar ));
+DATA(insert OID =  490 (  char16eq          PGUID 11 f t f 2 f 16 "19 19" 100 0 0 100  foo bar ));
+#define Character16EqualRegProcedure   490
+DATA(insert OID = 492 (  char16lt          PGUID 11 f t f 2 f 16 "19 19" 100 0 0 100  foo bar ));
+DATA(insert OID = 493 (  char16le          PGUID 11 f t f 2 f 16 "19 19" 100 0 0 100  foo bar ));
+DATA(insert OID = 494 (  char16gt          PGUID 11 f t f 2 f 16 "19 19" 100 0 0 100  foo bar ));
+DATA(insert OID = 495 (  char16ge          PGUID 11 f t f 2 f 16 "19 19" 100 0 0 100  foo bar ));
+DATA(insert OID = 496 (  char16ne          PGUID 11 f t f 2 f 16 "19 19" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 499 (  hashchar16       PGUID 11 f t f 2 f 23 "19 19" 100 0 0 100  foo bar ));
+
+/* OIDS 500 - 599 */
+
+/* OIDS 600 - 699 */
+
+DATA(insert OID = 650 (  int4notin         PGUID 11 f t f 2 f 16 "21 0" 100 0 0 100  foo bar ));
+DATA(insert OID = 651 (  oidnotin          PGUID 11 f t f 2 f 16 "26 0" 100 0 0 100  foo bar ));
+DATA(insert OID = 652 (  int44in           PGUID 11 f t f 1 f 22 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 653 (  int44out          PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 655 (  namelt          PGUID 11 f t f 2 f 16 "19 19" 100 0 0 100  foo bar ));
+DATA(insert OID = 656 (  namele          PGUID 11 f t f 2 f 16 "19 19" 100 0 0 100  foo bar ));
+DATA(insert OID = 657 (  namegt          PGUID 11 f t f 2 f 16 "19 19" 100 0 0 100  foo bar ));
+DATA(insert OID = 658 (  namege          PGUID 11 f t f 2 f 16 "19 19" 100 0 0 100  foo bar ));
+DATA(insert OID = 659 (  namene          PGUID 11 f t f 2 f 16 "19 19" 100 0 0 100  foo bar ));
+DATA(insert OID = 682 (  mktinterval       PGUID 11 f t f 2 f 704 "702 702" 100 0 0 100 foo bar ));
+DATA(insert OID = 683 (  oid8eq                   PGUID 11 f t f 2 f 16 "30 30" 100 0 0 100  foo bar ));
+DATA(insert OID = 684 (  char4ge          PGUID 11 f t f 2 f 16 "410 410" 100 0 0 100  foo bar ));
+DATA(insert OID = 685 (  char8ge          PGUID 11 f t f 2 f 16 "411 411" 100 0 0 100  foo bar ));
+DATA(insert OID = 686 (  char2ne          PGUID 11 f t f 2 f 16 "409 409" 100 0 0 100  foo bar ));
+DATA(insert OID = 687 (  char4ne          PGUID 11 f t f 2 f 16 "410 410" 100 0 0 100  foo bar ));
+DATA(insert OID = 688 (  char8ne          PGUID 11 f t f 2 f 16 "411 411" 100 0 0 100  foo bar ));
+DATA(insert OID = 689 (  btchar2cmp       PGUID 11 f t f 2 f 23 "409 409" 100 0 0 100  foo bar ));
+DATA(insert OID = 690 (  btchar4cmp       PGUID 11 f t f 2 f 23 "410 410" 100 0 0 100  foo bar ));
+DATA(insert OID = 691 (  btchar8cmp       PGUID 11 f t f 2 f 23 "411 411" 100 0 0 100  foo bar ));
+DATA(insert OID = 692 (  hashchar2       PGUID 11 f t f 2 f 23 "409 409" 100 0 0 100  foo bar ));
+DATA(insert OID = 693 (  hashchar4       PGUID 11 f t f 2 f 23 "410 410" 100 0 0 100  foo bar ));
+DATA(insert OID = 694 (  hashchar8       PGUID 11 f t f 2 f 23 "411 411" 100 0 0 100  foo bar ));
+DATA(insert OID =  695 (  char8regexeq     PGUID 11 f t f 2 f 16 "411 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  696 (  char8regexne     PGUID 11 f t f 2 f 16 "411 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  699 (  char2regexeq     PGUID 11 f t f 2 f 16 "409 25" 100 0 0 100  foo bar ));
+
+/* OIDS 700 - 799 */
+DATA(insert OID =  700 (  char16regexeq     PGUID 11 f t f 2 f 16 "19 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  701 (  char16regexne     PGUID 11 f t f 2 f 16 "19 25" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 710 (  GetPgUserName       PGUID 11 f t f 0 f 19 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 711 (  userfntest        PGUID 11 f t f 1 f 23 "23" 100 0 0 100  foo bar ));
+DATA(insert OID = 713 (  oidrand          PGUID 11 f t f 2 f 16 "26 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 715 (  oidsrand         PGUID 11 f t f 1 f 16 "23" 100 0 0 100  foo bar ));
+DATA(insert OID = 716 (  oideqint4        PGUID 11 f t f 2 f 16 "26 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 717 (  int4eqoid        PGUID 11 f t f 2 f 16 "23 26" 100 0 0 100  foo bar ));
+
+
+DATA(insert OID = 720 (  byteaGetSize     PGUID 11 f t f 1 f 23 "17" 100 0 0 100  foo bar ));
+DATA(insert OID = 721 (  byteaGetByte     PGUID 11 f t f 2 f 23 "17 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 722 (  byteaSetByte     PGUID 11 f t f 3 f 17 "17 23 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 723 (  byteaGetBit      PGUID 11 f t f 2 f 23 "17 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 724 (  byteaSetBit      PGUID 11 f t f 3 f 17 "17 23 23" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 730 (  pqtest            PGUID 11 f t f 1 f 23 "25" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 740 (  text_lt           PGUID 11 f t f 2 f 16 "25 25" 100 0 0 0  foo bar ));
+DATA(insert OID = 741 (  text_le           PGUID 11 f t f 2 f 16 "25 25" 100 0 0 0  foo bar ));
+DATA(insert OID = 742 (  text_gt           PGUID 11 f t f 2 f 16 "25 25" 100 0 0 0  foo bar ));
+DATA(insert OID = 743 (  text_ge           PGUID 11 f t f 2 f 16 "25 25" 100 0 0 0  foo bar ));
+
+DATA(insert OID = 744 (  array_eq         PGUID 11 f t f 2 f 16 "0 0" 100 0 0 100 foo bar));
+DATA(insert OID = 745 (  array_assgn      PGUID 11 f t f 8 f 23 "0 23 0 0 0 23 23 0" 100 0 0 100 foo bar));
+DATA(insert OID = 746 (  array_clip        PGUID 11 f t f 7 f 23 "0 23 0 0 23 23 0" 100 0 0 100 foo bar));
+DATA(insert OID = 747 (  array_dims        PGUID 11 f t f 1 f 25 "0" 100 0 0 100 foo bar));
+DATA(insert OID = 748 (  array_set         PGUID 11 f t f 8 f 23 "0 23 0 0 23 23 23 0" 100 0 0 100 foo bar));
+DATA(insert OID = 749 (  array_ref         PGUID 11 f t f 7 f 23 "0 23 0 23 23 23 0" 100 0 0 100 foo bar));
+DATA(insert OID = 750 (  array_in          PGUID 11 f t f 2 f 23 "0 0" 100 0 0 100  foo bar ));
+DATA(insert OID = 751 (  array_out         PGUID 11 f t f 2 f 23 "0 0" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 752 (  filename_in       PGUID 11 f t f 2 f 605 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 753 (  filename_out      PGUID 11 f t f 2 f 19  "0" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 760 (  smgrin                   PGUID 11 f t f 1 f 210 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 761 (  smgrout          PGUID 11 f t f 1 f 23  "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 762 (  smgreq                   PGUID 11 f t f 2 f 16 "210 210" 100 0 0 100  foo bar ));
+DATA(insert OID = 763 (  smgrne                   PGUID 11 f t f 2 f 16 "210 210" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 764 (  lo_import         PGUID 11 f t f 1 f 26 "25" 100 0 0 100  foo bar ));
+DATA(insert OID = 765 (  lo_export         PGUID 11 f t f 2 f 23 "26 25" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 766 (  int4inc           PGUID 11 f t f 1 f 23 "23" 100 0 0 100  foo bar ));
+DATA(insert OID = 767 (  int2inc           PGUID 11 f t f 1 f 21 "21" 100 0 0 100  foo bar ));
+DATA(insert OID = 768 (  int4larger        PGUID 11 f t f 2 f 23 "23 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 769 (  int4smaller       PGUID 11 f t f 2 f 23 "23 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 770 (  int2larger        PGUID 11 f t f 2 f 23 "21 21" 100 0 0 100  foo bar ));
+DATA(insert OID = 771 (  int2smaller       PGUID 11 f t f 2 f 23 "21 21" 100 0 0 100  foo bar ));
+
+/* OIDS 800 - 899 */
+DATA(insert OID = 820 (  oidint2in        PGUID 11 f t f 1 f 810 "0" 100 0 0 100  foo bar));
+DATA(insert OID = 821 (  oidint2out       PGUID 11 f t f 1 f 19 "0" 100 0 0 100  foo bar));
+DATA(insert OID = 822 (  oidint2lt        PGUID 11 f t f 2 f 16 "810 810" 100 0 0 100  foo bar));
+DATA(insert OID = 823 (  oidint2le        PGUID 11 f t f 2 f 16 "810 810" 100 0 0 100  foo bar));
+DATA(insert OID = 824 (  oidint2eq        PGUID 11 f t f 2 f 16 "810 810" 100 0 0 100  foo bar));
+
+#define OidInt2EqRegProcedure 824
+
+DATA(insert OID = 825 (  oidint2ge        PGUID 11 f t f 2 f 16 "810 810" 100 0 0 100  foo bar));
+DATA(insert OID = 826 (  oidint2gt        PGUID 11 f t f 2 f 16 "810 810" 100 0 0 100  foo bar));
+DATA(insert OID = 827 (  oidint2ne        PGUID 11 f t f 2 f 16 "810 810" 100 0 0 100  foo bar));
+DATA(insert OID = 828 (  oidint2cmp       PGUID 11 f t f 2 f 21 "810 810" 100 0 0 100  foo bar));
+DATA(insert OID = 829 (  mkoidint2        PGUID 11 f t f 2 f 810 "26 21" 100 0 0 100  foo bar));
+
+DATA(insert OID =  837 (  char2regexne     PGUID 11 f t f 2 f 16 "409 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  836 (  char4regexeq     PGUID 11 f t f 2 f 16 "410 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  838 (  char4regexne     PGUID 11 f t f 2 f 16 "410 25" 100 0 0 100  foo bar ));
+
+DATA(insert OID =  850 (  textlike     PGUID 11 f t f 2 f 16 "25 25" 100 0 1 0 foo bar ));
+DATA(insert OID =  851 (  textnlike    PGUID 11 f t f 2 f 16 "25 25" 100 0 1 0 foo bar ));
+DATA(insert OID =  852 (  char2like    PGUID 11 f t f 2 f 16 "409 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  853 (  char2nlike   PGUID 11 f t f 2 f 16 "409 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  854 (  char4like    PGUID 11 f t f 2 f 16 "410 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  855 (  char4nlike   PGUID 11 f t f 2 f 16 "410 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  856 (  char8like    PGUID 11 f t f 2 f 16 "411 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  857 (  char8nlike   PGUID 11 f t f 2 f 16 "411 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  858 (  namelike   PGUID 11 f t f 2 f 16 "19 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  859 (  namenlike  PGUID 11 f t f 2 f 16 "19 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  860 (  char16like   PGUID 11 f t f 2 f 16 "20 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  861 (  char16nlike  PGUID 11 f t f 2 f 16 "20 25" 100 0 0 100  foo bar ));
+/* OIDS 900 - 999 */
+
+DATA(insert OID = 920 (  oidint4in        PGUID 11 f t f 1 f 910 "0" 100 0 0 100  foo bar));
+DATA(insert OID = 921 (  oidint4out       PGUID 11 f t f 1 f 19 "0" 100 0 0 100  foo bar));
+DATA(insert OID = 922 (  oidint4lt        PGUID 11 f t f 2 f 16 "910 910" 100 0 0 100  foo bar));
+DATA(insert OID = 923 (  oidint4le        PGUID 11 f t f 2 f 16 "910 910" 100 0 0 100  foo bar));
+DATA(insert OID = 924 (  oidint4eq        PGUID 11 f t f 2 f 16 "910 910" 100 0 0 100  foo bar));
+
+#define OidInt4EqRegProcedure 924
+
+DATA(insert OID = 925 (  oidint4ge        PGUID 11 f t f 2 f 16 "910 910" 100 0 0 100  foo bar));
+DATA(insert OID = 926 (  oidint4gt        PGUID 11 f t f 2 f 16 "910 910" 100 0 0 100  foo bar));
+DATA(insert OID = 927 (  oidint4ne        PGUID 11 f t f 2 f 16 "910 910" 100 0 0 100  foo bar));
+DATA(insert OID = 928 (  oidint4cmp       PGUID 11 f t f 2 f 23 "910 910" 100 0 0 100  foo bar));
+DATA(insert OID = 929 (  mkoidint4        PGUID 11 f t f 2 f 910 "26 23" 100 0 0 100  foo bar));
+
+DATA(insert OID = 940 (  oidnamein        PGUID 11 f t f 1 f 911 "0" 100 0 0 100  foo bar));
+DATA(insert OID = 941 (  oidnameout       PGUID 11 f t f 1 f 19 "0" 100 0 0 100  foo bar));
+DATA(insert OID = 942 (  oidnamelt        PGUID 11 f t f 2 f 16 "911 911" 100 0 0 100  foo bar));
+DATA(insert OID = 943 (  oidnamele        PGUID 11 f t f 2 f 16 "911 911" 100 0 0 100  foo bar));
+DATA(insert OID = 944 (  oidnameeq        PGUID 11 f t f 2 f 16 "911 911" 100 0 0 100  foo bar));
+
+#define OidNameEqRegProcedure 944
+
+DATA(insert OID = 945 (  oidnamege        PGUID 11 f t f 2 f 16 "911 911" 100 0 0 100  foo bar));
+DATA(insert OID = 946 (  oidnamegt        PGUID 11 f t f 2 f 16 "911 911" 100 0 0 100  foo bar));
+DATA(insert OID = 947 (  oidnamene        PGUID 11 f t f 2 f 16 "911 911" 100 0 0 100  foo bar));
+DATA(insert OID = 948 (  oidnamecmp       PGUID 11 f t f 2 f 23 "911 911" 100 0 0 100  foo bar));
+DATA(insert OID = 949 (  mkoidname        PGUID 11 f t f 2 f 911 "26 19" 100 0 0 100  foo bar));
+
+DATA(insert OID = 952 (  lo_open           PGUID 11 f t f 2 f 23 "26 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 953 (  lo_close          PGUID 11 f t f 1 f 23 "23" 100 0 0 100  foo bar ));
+DATA(insert OID = 954 (  LOread            PGUID 11 f t f 2 f 17 "23 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 955 (  LOwrite           PGUID 11 f t f 2 f 23 "23 17" 100 0 0 100  foo bar ));
+DATA(insert OID = 956 (  lo_lseek          PGUID 11 f t f 3 f 23 "23 23 23" 100 0 0 100  foo bar ));
+DATA(insert OID = 957 (  lo_creat          PGUID 11 f t f 1 f 26 "23" 100 0 0 100  foo bar ));
+DATA(insert OID = 958 (  lo_tell           PGUID 11 f t f 1 f 23 "23" 100 0 0 100  foo bar ));
+DATA(insert OID = 964 (  lo_unlink         PGUID 11 f t f 1 f 23 "23" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 972 (  RegprocToOid      PGUID 11 f t f 1 f 26 "24" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 973 (  path_inter        PGUID 11 f t f 2 f 16 "602 602" 100 0 10 100  foo bar ));
+DATA(insert OID = 974 (  box_copy          PGUID 11 f t f 1 f 603 "603" 100 0 0 100  foo bar ));
+DATA(insert OID = 975 (  box_area          PGUID 11 f t f 1 f 701 "603" 100 0 0 100  foo bar ));
+DATA(insert OID = 976 (  box_length        PGUID 11 f t f 1 f 701 "603" 100 0 0 100  foo bar ));
+DATA(insert OID = 977 (  box_height        PGUID 11 f t f 1 f 701 "603" 100 0 0 100  foo bar ));
+DATA(insert OID = 978 (  box_distance      PGUID 11 f t f 2 f 701 "603 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 980 (  box_intersect     PGUID 11 f t f 2 f 603 "603 603" 100 0 0 100  foo bar ));
+DATA(insert OID = 981 (  box_diagonal      PGUID 11 f t f 1 f 601 "603" 100 0 0 100  foo bar ));
+DATA(insert OID = 982 (  path_n_lt         PGUID 11 f t f 2 f 16 "602 602" 100 0 0 100  foo bar ));
+DATA(insert OID = 983 (  path_n_gt         PGUID 11 f t f 2 f 16 "602 602" 100 0 0 100  foo bar ));
+DATA(insert OID = 984 (  path_n_eq         PGUID 11 f t f 2 f 16 "602 602" 100 0 0 100  foo bar ));
+DATA(insert OID = 985 (  path_n_le         PGUID 11 f t f 2 f 16 "602 602" 100 0 0 100  foo bar ));
+DATA(insert OID = 986 (  path_n_ge         PGUID 11 f t f 2 f 16 "602 602" 100 0 0 100  foo bar ));
+DATA(insert OID = 987 (  path_length       PGUID 11 f t f 1 f 701 "602" 100 0 1 0  foo bar ));
+DATA(insert OID = 988 (  point_copy        PGUID 11 f t f 1 f 600 "600" 100 0 0 100  foo bar ));
+DATA(insert OID = 989 (  point_vert        PGUID 11 f t f 2 f 16 "600 600" 100 0 0 100  foo bar ));
+DATA(insert OID = 990 (  point_horiz       PGUID 11 f t f 2 f 16 "600 600" 100 0 0 100  foo bar ));
+DATA(insert OID = 991 (  point_distance    PGUID 11 f t f 2 f 701 "600 600" 100 0 0 100  foo bar ));
+DATA(insert OID = 992 (  point_slope       PGUID 11 f t f 2 f 701 "600 600" 100 0 0 100  foo bar ));
+DATA(insert OID = 993 (  lseg_construct    PGUID 11 f t f 2 f 601 "600 600" 100 0 0 100  foo bar ));
+DATA(insert OID = 994 (  lseg_intersect    PGUID 11 f t f 2 f 16 "601 601" 100 0 0 100  foo bar ));
+DATA(insert OID = 995 (  lseg_parallel     PGUID 11 f t f 2 f 16 "601 601" 100 0 0 100  foo bar ));
+DATA(insert OID = 996 (  lseg_perp         PGUID 11 f t f 2 f 16 "601 601" 100 0 0 100  foo bar ));
+DATA(insert OID = 997 (  lseg_vertical     PGUID 11 f t f 1 f 16 "601" 100 0 0 100  foo bar ));
+DATA(insert OID = 998 (  lseg_horizontal   PGUID 11 f t f 1 f 16 "601" 100 0 0 100  foo bar ));
+DATA(insert OID = 999 (  lseg_eq           PGUID 11 f t f 2 f 16 "601 601" 100 0 0 100  foo bar ));
+
+/* OIDS 1000 - 1999 */
+
+DATA(insert OID = 1029 (  NullValue        PGUID 11 f t f 1 f 16 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 1030 (  NonNullValue     PGUID 11 f t f 1 f 16 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 1031 (  aclitemin        PGUID 11 f t f 1 f 1033 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 1032 (  aclitemout       PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 1035 (  aclinsert        PGUID 11 f t f 2 f 1034 "1034 1033" 100 0 0 100  foo bar ));
+DATA(insert OID = 1036 (  aclremove        PGUID 11 f t f 2 f 1034 "1034 1033" 100 0 0 100  foo bar ));
+DATA(insert OID = 1037 (  aclcontains      PGUID 11 f t f 2 f 16 "1034 1033" 100 0 0 100  foo bar ));
+DATA(insert OID = 1038 (  seteval          PGUID 11 f t f 1 f 23 "26" 100 0 0 100  foo bar ));
+#define SetEvalRegProcedure 1038
+
+DATA(insert OID = 1044 (  bpcharin         PGUID 11 f t f 3 f 1042 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 1045 (  bpcharout        PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 1046 (  varcharin        PGUID 11 f t f 3 f 1043 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 1047 (  varcharout       PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 1048 (  bpchareq         PGUID 11 f t f 2 f 16 "1042 1042" 100 0 0 100  foo bar ));
+DATA(insert OID = 1049 (  bpcharlt         PGUID 11 f t f 2 f 16 "1042 1042" 100 0 0 100  foo bar ));
+DATA(insert OID = 1050 (  bpcharle         PGUID 11 f t f 2 f 16 "1042 1042" 100 0 0 100  foo bar ));
+DATA(insert OID = 1051 (  bpchargt         PGUID 11 f t f 2 f 16 "1042 1042" 100 0 0 100  foo bar ));
+DATA(insert OID = 1052 (  bpcharge         PGUID 11 f t f 2 f 16 "1042 1042" 100 0 0 100  foo bar ));
+DATA(insert OID = 1053 (  bpcharne         PGUID 11 f t f 2 f 16 "1042 1042" 100 0 0 100  foo bar ));
+DATA(insert OID = 1070 (  varchareq        PGUID 11 f t f 2 f 16 "1043 1043" 100 0 0 100  foo bar ));
+DATA(insert OID = 1071 (  varcharlt        PGUID 11 f t f 2 f 16 "1043 1043" 100 0 0 100  foo bar ));
+DATA(insert OID = 1072 (  varcharle        PGUID 11 f t f 2 f 16 "1043 1043" 100 0 0 100  foo bar ));
+DATA(insert OID = 1073 (  varchargt        PGUID 11 f t f 2 f 16 "1043 1043" 100 0 0 100  foo bar ));
+DATA(insert OID = 1074 (  varcharge        PGUID 11 f t f 2 f 16 "1043 1043" 100 0 0 100  foo bar ));
+DATA(insert OID = 1075 (  varcharne        PGUID 11 f t f 2 f 16 "1043 1043" 100 0 0 100  foo bar ));
+DATA(insert OID = 1078 (  bpcharcmp        PGUID 11 f t f 2 f 23 "1042 1042" 100 0 0 100  foo bar ));
+DATA(insert OID = 1079 (  varcharcmp       PGUID 11 f t f 2 f 23 "1043 1043" 100 0 0 100  foo bar ));
+DATA(insert OID = 1080 (  hashbpchar       PGUID 11 f t f 1 f 23 "1042" 100 0 0 100  foo bar ));
+DATA(insert OID = 1081 (  hashvarchar      PGUID 11 f t f 1 f 23 "1043" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 1084 (  date_in          PGUID 11 f t f 1 f 1082 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 1085 (  date_out         PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 1086 (  date_eq          PGUID 11 f t f 2 f 16 "1082 1082" 100 0 0 100  foo bar ));
+DATA(insert OID = 1087 (  date_lt          PGUID 11 f t f 2 f 16 "1082 1082" 100 0 0 100  foo bar ));
+DATA(insert OID = 1088 (  date_le          PGUID 11 f t f 2 f 16 "1082 1082" 100 0 0 100  foo bar ));
+DATA(insert OID = 1089 (  date_gt          PGUID 11 f t f 2 f 16 "1082 1082" 100 0 0 100  foo bar ));
+DATA(insert OID = 1090 (  date_ge          PGUID 11 f t f 2 f 16 "1082 1082" 100 0 0 100  foo bar ));
+DATA(insert OID = 1091 (  date_ne          PGUID 11 f t f 2 f 16 "1082 1082" 100 0 0 100  foo bar ));
+DATA(insert OID = 1092 (  date_cmp         PGUID 11 f t f 2 f 23 "1082 1082" 100 0 0 100  foo bar ));
+
+DATA(insert OID = 1099 (  time_in          PGUID 11 f t f 1 f 1083 "0" 100 0 0 100  foo bar ));
+
+/* OIDS 1100 - 1199 */
+DATA(insert OID = 1100 (  time_out         PGUID 11 f t f 1 f 23 "0" 100 0 0 100  foo bar ));
+DATA(insert OID = 1101 (  time_eq          PGUID 11 f t f 2 f 16 "1083 1083" 100 0 0 100  foo bar ));
+DATA(insert OID = 1102 (  time_lt          PGUID 11 f t f 2 f 16 "1083 1083" 100 0 0 100  foo bar ));
+DATA(insert OID = 1103 (  time_le          PGUID 11 f t f 2 f 16 "1083 1083" 100 0 0 100  foo bar ));
+DATA(insert OID = 1104 (  time_gt          PGUID 11 f t f 2 f 16 "1083 1083" 100 0 0 100  foo bar ));
+DATA(insert OID = 1105 (  time_ge          PGUID 11 f t f 2 f 16 "1083 1083" 100 0 0 100  foo bar ));
+DATA(insert OID = 1106 (  time_ne          PGUID 11 f t f 2 f 16 "1083 1083" 100 0 0 100  foo bar ));
+DATA(insert OID = 1107 (  time_cmp         PGUID 11 f t f 2 f 23 "1083 1083" 100 0 0 100  foo bar ));
+DATA(insert OID = 1200 (  int42reltime      PGUID 11 f t f 1 f 703 "21" 100 0 0 100  foo bar ));
+
+DATA(insert OID =  1230 (  char2icregexeq     PGUID 11 f t f 2 f 16 "409 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  1231 (  char2icregexne     PGUID 11 f t f 2 f 16 "409 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  1232 (  char4icregexeq     PGUID 11 f t f 2 f 16 "410 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  1233 (  char4icregexne     PGUID 11 f t f 2 f 16 "410 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  1234 (  char8icregexeq     PGUID 11 f t f 2 f 16 "411 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  1235 (  char8icregexne     PGUID 11 f t f 2 f 16 "411 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  1236 (  char16icregexeq     PGUID 11 f t f 2 f 16 "20 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  1237 (  char16icregexne     PGUID 11 f t f 2 f 16 "20 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  1238 (  texticregexeq       PGUID 11 f t f 2 f 16 "25 25" 100 0 1 0  foo bar ));
+DATA(insert OID =  1239 (  texticregexne       PGUID 11 f t f 2 f 16 "25 25" 100 0 1 0  foo bar ));
+DATA(insert OID =  1240 (  nameicregexeq     PGUID 11 f t f 2 f 16 "19 25" 100 0 0 100  foo bar ));
+DATA(insert OID =  1241 (  nameicregexne     PGUID 11 f t f 2 f 16 "19 25" 100 0 0 100  foo bar ));
+
+
+#include "nodes/pg_list.h"
+
+/* 
+ * prototypes for functions pg_proc.c 
+ */
+extern Oid ProcedureCreate(char* procedureName, 
+                          bool returnsSet,
+                          char *returnTypeName,
+                          char *languageName,
+                          char *prosrc,
+                          char *probin,
+                          bool canCache,
+                          bool trusted,
+                          int32 byte_pct,
+                          int32 perbyte_cpu, 
+                          int32 percall_cpu,
+                          int32 outin_ratio, 
+                          List *argList, 
+                          CommandDest dest);
+
+
+#endif /* PG_PROC_H */
diff --git a/src/backend/catalog/pg_rewrite.h b/src/backend/catalog/pg_rewrite.h
new file mode 100644 (file)
index 0000000..62c2c31
--- /dev/null
@@ -0,0 +1,64 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_rewrite.h--
+ *    definition of the system "rewrite-rule" relation (pg_rewrite)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_REWRITE_H
+#define PG_REWRITE_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_rewrite definition.  cpp turns this into
+ *     typedef struct FormData_pg_rewrite
+ * ----------------
+ */ 
+CATALOG(pg_rewrite) {
+    NameData   rulename;
+    char       ev_type;
+    Oid        ev_class;
+    int2       ev_attr;
+    bool       is_instead;
+    text       ev_qual;        /* VARLENA */
+    text       action;         /* VARLENA */
+} FormData_pg_rewrite;
+
+/* ----------------
+ *     Form_pg_rewrite corresponds to a pointer to a tuple with
+ *     the format of pg_rewrite relation.
+ * ----------------
+ */
+typedef FormData_pg_rewrite *Form_pg_rewrite;
+
+/* ----------------
+ *     compiler constants for pg_rewrite
+ * ----------------
+ */
+#define Natts_pg_rewrite               7
+#define Anum_pg_rewrite_rulename       1
+#define Anum_pg_rewrite_ev_type        2
+#define Anum_pg_rewrite_ev_class       3
+#define Anum_pg_rewrite_ev_attr        4
+#define Anum_pg_rewrite_is_instead      5
+#define Anum_pg_rewrite_ev_qual                6
+#define Anum_pg_rewrite_action         7
+
+#endif /* PG_REWRITE_H */
diff --git a/src/backend/catalog/pg_server.h b/src/backend/catalog/pg_server.h
new file mode 100644 (file)
index 0000000..24775ce
--- /dev/null
@@ -0,0 +1,56 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_server.h--
+ *    definition of the system "server" relation (pg_server)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_SERVER_H
+#define PG_SERVER_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_server definition.  cpp turns this into
+ *     typedef struct FormData_pg_server
+ * ----------------
+ */ 
+CATALOG(pg_server) BOOTSTRAP {
+    NameData   sername;
+    int2       serpid;
+    int2       serport;
+} FormData_pg_server;
+
+/* ----------------
+ *     Form_pg_server corresponds to a pointer to a tuple with
+ *     the format of pg_server relation.
+ * ----------------
+ */
+typedef FormData_pg_server     *Form_pg_server;
+
+/* ----------------
+ *     compiler constants for pg_server
+ * ----------------
+ */
+#define Natts_pg_server                        3
+#define Anum_pg_server_sername         1
+#define Anum_pg_server_serpid          2
+#define Anum_pg_server_serport         3
+
+#endif /* PG_SERVER_H */
diff --git a/src/backend/catalog/pg_statistic.h b/src/backend/catalog/pg_statistic.h
new file mode 100644 (file)
index 0000000..7ecc625
--- /dev/null
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_statistic.h--
+ *    definition of the system "statistic" relation (pg_statistic)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_STATISTIC_H
+#define PG_STATISTIC_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_statistic definition.  cpp turns this into
+ *     typedef struct FormData_pg_statistic
+ * ----------------
+ */ 
+CATALOG(pg_statistic) {
+    Oid        starelid;
+    int2       staattnum;
+    Oid        staop;
+    text       stalokey;       /* VARIABLE LENGTH FIELD */
+    text       stahikey;       /* VARIABLE LENGTH FIELD */
+} FormData_pg_statistic;
+
+/* ----------------
+ *     Form_pg_statistic corresponds to a pointer to a tuple with
+ *     the format of pg_statistic relation.
+ * ----------------
+ */
+typedef FormData_pg_statistic  *Form_pg_statistic;
+
+/* ----------------
+ *     compiler constants for pg_statistic
+ * ----------------
+ */
+#define Natts_pg_statistic             5
+#define Anum_pg_statistic_starelid     1
+#define Anum_pg_statistic_staattnum    2
+#define Anum_pg_statistic_staop                3
+#define Anum_pg_statistic_stalokey     4
+#define Anum_pg_statistic_stahikey     5
+
+#endif /* PG_STATISTIC_H */
diff --git a/src/backend/catalog/pg_time.h b/src/backend/catalog/pg_time.h
new file mode 100644 (file)
index 0000000..829f172
--- /dev/null
@@ -0,0 +1,41 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_time.h--
+ *    the system commit-time relation "pg_time" is not a "heap" relation.
+ *    it is automatically created by the transam/ code and the
+ *    information here is all bogus and is just here to make the
+ *    relcache code happy.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    The structures and macros used by the transam/ code
+ *    to access pg_time should some day go here -cim 6/18/90
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_TIME_H
+#define PG_TIME_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+CATALOG(pg_time) BOOTSTRAP {
+    Oid        timefoo;
+} FormData_pg_time;
+
+typedef FormData_pg_time       *Form_pg_time;
+
+#define Natts_pg_time          1
+#define Anum_pg_time_timefoo   1
+
+
+#endif /* PG_TIME_H */
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
new file mode 100644 (file)
index 0000000..b51ba82
--- /dev/null
@@ -0,0 +1,595 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_type.c--
+ *    routines to support manipulation of the pg_type relation
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "access/tupdesc.h"
+#include "utils/builtins.h"
+#include "utils/rel.h"
+#include "utils/palloc.h"
+#include "fmgr.h"
+#include "utils/elog.h"
+#include "parser/catalog_utils.h"
+
+#include "catalog/catname.h"
+#include "utils/syscache.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "catalog/indexing.h"
+#include "storage/lmgr.h"
+
+/* ----------------------------------------------------------------
+ *     TypeGetWithOpenRelation
+ *
+ *     preforms a scan on pg_type for a type tuple with the
+ *     given type name.
+ * ----------------------------------------------------------------
+ *     pg_type_desc             -- reldesc for pg_type
+ *     typeName                 -- name of type to be fetched 
+ *     defined                  -- has the type been defined? 
+ */
+static Oid
+TypeGetWithOpenRelation(Relation pg_type_desc,
+                       char* typeName, 
+                       bool *defined)
+{
+    HeapScanDesc       scan;
+    HeapTuple          tup;
+    
+    static ScanKeyData typeKey[1] = {
+       { 0, Anum_pg_type_typname, NameEqualRegProcedure }
+    };
+    
+    /* ----------------
+     * initialize the scan key and begin a scan of pg_type
+     * ----------------
+     */
+    fmgr_info(NameEqualRegProcedure,
+             &typeKey[0].sk_func, &typeKey[0].sk_nargs);
+    typeKey[0].sk_argument = PointerGetDatum(typeName);
+    
+    scan = heap_beginscan(pg_type_desc,
+                         0,
+                         SelfTimeQual,
+                         1,
+                         typeKey);
+    
+    /* ----------------
+     * get the type tuple, if it exists.
+     * ----------------
+     */
+    tup = heap_getnext(scan, 0, (Buffer *) 0);
+    
+    /* ----------------
+     * if no type tuple exists for the given type name, then
+     *  end the scan and return appropriate information.
+     * ----------------
+     */
+    if (! HeapTupleIsValid(tup)) {
+       heap_endscan(scan);
+       *defined = false;
+       return InvalidOid;
+    }
+    
+    /* ----------------
+     * here, the type tuple does exist so we pull information from
+     *  the typisdefined field of the tuple and return the tuple's
+     *  oid, which is the oid of the type.
+     * ----------------
+     */
+    heap_endscan(scan);
+    *defined = (bool) ((TypeTupleForm) GETSTRUCT(tup))->typisdefined;
+    
+    return
+       tup->t_oid;
+}
+
+/* ----------------------------------------------------------------
+ *     TypeGet
+ *
+ *     Finds the ObjectId of a type, even if uncommitted; "defined"
+ *     is only set if the type has actually been defined, i.e., if
+ *     the type tuple is not a shell.
+ *
+ *     Note: the meat of this function is now in the function
+ *           TypeGetWithOpenRelation().  -cim 6/15/90
+ *
+ *     Also called from util/remove.c
+ * ----------------------------------------------------------------
+ */
+Oid
+TypeGet(char* typeName,                /* name of type to be fetched */
+       bool *defined)          /* has the type been defined? */
+{
+    Relation   pg_type_desc;
+    Oid                typeoid;
+    
+    /* ----------------
+     * open the pg_type relation
+     * ----------------
+     */
+    pg_type_desc = heap_openr(TypeRelationName);
+    
+    /* ----------------
+     * scan the type relation for the information we want
+     * ----------------
+     */
+    typeoid = TypeGetWithOpenRelation(pg_type_desc,
+                                     typeName,
+                                     defined);
+    
+    /* ----------------
+     * close the type relation and return the type oid.
+     * ----------------
+     */
+    heap_close(pg_type_desc);
+    
+    return
+       typeoid;
+}
+
+/* ----------------------------------------------------------------
+ *     TypeShellMakeWithOpenRelation
+ *
+ * ----------------------------------------------------------------
+ */
+Oid
+TypeShellMakeWithOpenRelation(Relation pg_type_desc, char *typeName)
+{
+    register int       i;
+    HeapTuple          tup;
+    Datum               values[ Natts_pg_type ];
+    char               nulls[ Natts_pg_type ];
+    Oid                        typoid;
+    TupleDesc           tupDesc;
+    
+    /* ----------------
+     * initialize our nulls[] and values[] arrays
+     * ----------------
+     */
+    for (i = 0; i < Natts_pg_type; ++i) {
+       nulls[i] = ' ';
+       values[i] = (Datum)NULL;        /* redundant, but safe */
+    }
+    
+    /* ----------------
+     * initialize values[] with the type name and 
+     * ----------------
+     */
+    i = 0;
+    values[i++] = (Datum) typeName;    /* 1 */
+    values[i++] = (Datum) InvalidOid;  /* 2 */
+    values[i++] = (Datum) (int16) 0;   /* 3 */
+    values[i++] = (Datum) (int16) 0;   /* 4 */
+    values[i++] = (Datum) (bool) 0;    /* 5 */
+    values[i++] = (Datum) (bool) 0;    /* 6 */
+    values[i++] = (Datum) (bool) 0;    /* 7 */
+    values[i++] = (Datum) (bool) 0;    /* 8 */
+    values[i++] = (Datum) InvalidOid;  /* 9 */
+    values[i++] = (Datum) InvalidOid;  /* 10 */
+    values[i++] = (Datum) InvalidOid;  /* 11 */
+    values[i++] = (Datum) InvalidOid;  /* 12 */
+    values[i++] = (Datum) InvalidOid;  /* 13 */
+    values[i++] = (Datum) InvalidOid;  /* 14 */
+    values[i++] = (Datum) 'i';         /* 15 */
+
+    /*
+     * ... and fill typdefault with a bogus value
+     */
+    values[i++] =
+       (Datum)fmgr(TextInRegProcedure, typeName);      /* 15 */
+    
+    /* ----------------
+     * create a new type tuple with FormHeapTuple
+     * ----------------
+     */
+    tupDesc = pg_type_desc->rd_att;
+
+    tup = heap_formtuple(tupDesc, values, nulls);
+    
+    /* ----------------
+     * insert the tuple in the relation and get the tuple's oid.
+     * ----------------
+     */
+    heap_insert(pg_type_desc, tup);
+    typoid = tup->t_oid;
+    
+    if (RelationGetRelationTupleForm(pg_type_desc)->relhasindex)
+       {
+           Relation idescs[Num_pg_type_indices];
+           
+           CatalogOpenIndices(Num_pg_type_indices, Name_pg_type_indices, idescs);
+           CatalogIndexInsert(idescs, Num_pg_type_indices, pg_type_desc, tup);
+           CatalogCloseIndices(Num_pg_type_indices, idescs);
+       }
+    /* ----------------
+     * free the tuple and return the type-oid
+     * ----------------
+     */
+    pfree(tup);
+    
+    return
+       typoid;   
+}
+
+/* ----------------------------------------------------------------
+ *     TypeShellMake
+ *
+ *     This procedure inserts a "shell" tuple into the type
+ *     relation.  The type tuple inserted has invalid values
+ *     and in particular, the "typisdefined" field is false.
+ *
+ *     This is used so that a tuple exists in the catalogs.
+ *     The invalid fields should be fixed up sometime after
+ *     this routine is called, and then the "typeisdefined"
+ *     field is set to true. -cim 6/15/90
+ * ----------------------------------------------------------------
+ */
+Oid
+TypeShellMake(char *typeName)
+{
+    Relation   pg_type_desc;
+    Oid        typoid;
+    
+    Assert(PointerIsValid(typeName));
+    
+    /* ----------------
+     * open pg_type
+     * ----------------
+     */
+    pg_type_desc = heap_openr(TypeRelationName);
+    
+    /* ----------------
+     * insert the shell tuple
+     * ----------------
+     */
+    typoid = TypeShellMakeWithOpenRelation(pg_type_desc, typeName);
+    
+    /* ----------------
+     * close pg_type and return the tuple's oid.
+     * ----------------
+     */
+    heap_close(pg_type_desc);
+    
+    return
+       typoid;
+}
+
+/* ----------------------------------------------------------------
+ *     TypeCreate
+ *
+ *     This does all the necessary work needed to define a new type.
+ * ----------------------------------------------------------------
+ */
+Oid
+TypeCreate(char *typeName,
+          Oid relationOid,     /* only for 'c'atalog typeTypes */
+          int16 internalSize,
+          int16 externalSize,
+          char typeType,
+          char typDelim,
+          char *inputProcedure,
+          char *outputProcedure,
+          char *sendProcedure,
+          char *receiveProcedure,
+          char *elementTypeName,
+          char *defaultTypeValue, /* internal rep */
+          bool passedByValue,
+          char alignment)
+{
+    register           i, j;
+    Relation           pg_type_desc;
+    HeapScanDesc       pg_type_scan;
+    
+    Oid            typeObjectId;
+    Oid            elementObjectId = InvalidOid;
+    
+    HeapTuple          tup;
+    char               nulls[Natts_pg_type];
+    char               replaces[Natts_pg_type];
+    Datum               values[Natts_pg_type];
+    
+    Buffer             buffer;
+    char               *procname;
+    char               *procs[4];
+    bool               defined;
+    ItemPointerData    itemPointerData;
+    TupleDesc           tupDesc;
+
+    Oid argList[8];
+
+    
+    static ScanKeyData typeKey[1] = {
+       { 0, Anum_pg_type_typname, NameEqualRegProcedure }
+    };
+    
+    fmgr_info(NameEqualRegProcedure,
+             &typeKey[0].sk_func, &typeKey[0].sk_nargs);
+    
+    /* ----------------
+     * check that the type is not already defined.
+     * ----------------
+     */
+    typeObjectId = TypeGet(typeName, &defined);
+    if (OidIsValid(typeObjectId) && defined) {
+       elog(WARN, "TypeCreate: type %s already defined", typeName);
+    }
+    
+    /* ----------------
+     * if this type has an associated elementType, then we check that
+     *  it is defined.
+     * ----------------
+     */
+    if (elementTypeName) {
+       elementObjectId = TypeGet(elementTypeName, &defined);
+       if (!defined) {
+           elog(WARN, "TypeCreate: type %s is not defined", elementTypeName);
+       }
+    }
+    
+    /* ----------------
+     * XXX comment me
+     * ----------------
+     */
+    if (externalSize == 0) {
+       externalSize = -1;              /* variable length */
+    }
+    
+    /* ----------------
+     * initialize arrays needed by FormHeapTuple
+     * ----------------
+     */
+    for (i = 0; i < Natts_pg_type; ++i) {
+       nulls[i] = ' ';
+       replaces[i] = 'r';
+       values[i] = (Datum)NULL;        /* redundant, but nice */
+    }
+    
+    /*
+     * XXX
+     *
+     * Do this so that user-defined types have size -1 instead of zero if
+     * they are variable-length - this is so that everything else in the
+     * backend works.
+     */
+    
+    if (internalSize == 0)
+       internalSize = -1; 
+    
+    /* ----------------
+     * initialize the values[] information
+     * ----------------
+     */
+    i = 0;
+    values[i++] = PointerGetDatum(typeName);   /* 1 */
+    values[i++] = (Datum) GetUserId(); /* 2 */
+    values[i++] = (Datum) internalSize;        /* 3 */
+    values[i++] = (Datum) externalSize;        /* 4 */
+    values[i++] = (Datum) passedByValue; /* 5 */
+    values[i++] = (Datum) typeType;    /* 6 */
+    values[i++] = (Datum) (bool) 1;    /* 7 */
+    values[i++] = (Datum) typDelim;    /* 8 */
+    values[i++] = (Datum) (typeType == 'c' ? relationOid : InvalidOid); /* 9 */
+    values[i++] = (Datum) elementObjectId; /* 10 */
+    
+    /*
+     * arguments to type input and output functions must be 0
+     */
+    memset(argList, 0, 8 * sizeof(Oid)); 
+    
+    procs[0] = inputProcedure;
+    procs[1] = outputProcedure;
+    procs[2] = (receiveProcedure) ? receiveProcedure : inputProcedure;
+    procs[3] = (sendProcedure) ? sendProcedure : outputProcedure;
+    
+    for (j = 0; j < 4; ++j) {
+       procname = procs[j];
+       
+       tup = SearchSysCacheTuple(PRONAME,
+                                 PointerGetDatum(procname),
+                                 Int32GetDatum(1),
+                                 PointerGetDatum(argList),
+                                 0);
+       
+       if (!HeapTupleIsValid(tup)) {
+           /*
+            * it is possible for the input/output procedure
+            * to take two arguments, where the second argument
+            * is the element type (eg array_in/array_out)
+            */
+           if (OidIsValid(elementObjectId)) {
+               tup = SearchSysCacheTuple(PRONAME,
+                                         PointerGetDatum(procname),
+                                         Int32GetDatum(2),
+                                         PointerGetDatum(argList), 
+                                         0);
+           }
+           if (!HeapTupleIsValid(tup)) {
+               func_error("TypeCreate", procname, 1, (int*)argList);
+           }
+       }
+       
+       values[i++] = (Datum)tup->t_oid;        /* 11 - 14 */
+    }
+
+    /* ----------------
+     * set default alignment
+     * ----------------
+     */
+    values[i++] = (Datum)alignment;            /* 15 */
+
+    /* ----------------
+     * initialize the default value for this type.
+     * ----------------
+     */
+    values[i] = (Datum)fmgr(TextInRegProcedure,                /* 16 */
+                    PointerIsValid(defaultTypeValue)
+                    ? defaultTypeValue : "-"); /* XXX default typdefault */
+    
+    /* ----------------
+     * open pg_type and begin a scan for the type name.
+     * ----------------
+     */
+    pg_type_desc = heap_openr(TypeRelationName);
+    
+    /* -----------------
+     * Set a write lock initially so as not upgrade a read to a write
+     * when the heap_insert() or heap_replace() is called.
+     * -----------------
+     */
+    RelationSetLockForWrite(pg_type_desc);
+    
+    typeKey[0].sk_argument = PointerGetDatum(typeName);
+    pg_type_scan = heap_beginscan(pg_type_desc,
+                                 0,
+                                 SelfTimeQual,
+                                 1,
+                                 typeKey);
+    
+    /* ----------------
+     *  define the type either by adding a tuple to the type
+     *  relation, or by updating the fields of the "shell" tuple
+     *  already there.
+     * ----------------
+     */
+    tup = heap_getnext(pg_type_scan, 0, &buffer);
+    if (HeapTupleIsValid(tup)) {
+       tup = heap_modifytuple(tup,
+                              buffer,
+                              pg_type_desc,
+                              values,
+                              nulls,
+                              replaces);
+       
+       /* XXX may not be necessary */
+       ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+       
+       setheapoverride(true);
+       (void) heap_replace(pg_type_desc, &itemPointerData, tup);
+       setheapoverride(false);
+       
+       typeObjectId = tup->t_oid;
+    } else {
+       tupDesc = pg_type_desc->rd_att;
+
+       tup = heap_formtuple(tupDesc,
+                            values,
+                            nulls);
+       
+       heap_insert(pg_type_desc, tup);
+       
+       typeObjectId = tup->t_oid;
+    }
+    
+    /* ----------------
+     * finish up
+     * ----------------
+     */
+    heap_endscan(pg_type_scan);
+    
+    if (RelationGetRelationTupleForm(pg_type_desc)->relhasindex)
+       {
+           Relation idescs[Num_pg_type_indices];
+           
+           CatalogOpenIndices(Num_pg_type_indices, Name_pg_type_indices, idescs);
+           CatalogIndexInsert(idescs, Num_pg_type_indices, pg_type_desc, tup);
+           CatalogCloseIndices(Num_pg_type_indices, idescs);
+       }
+    RelationUnsetLockForWrite(pg_type_desc);
+    heap_close(pg_type_desc);
+    
+    
+    return
+       typeObjectId;
+}
+
+/* ----------------------------------------------------------------
+ *     TypeRename
+ *
+ *     This renames a type 
+ * ----------------------------------------------------------------
+ */
+void
+TypeRename(char *oldTypeName, char *newTypeName)
+{
+    Relation           pg_type_desc;
+    Relation           idescs[Num_pg_type_indices];
+    Oid            type_oid;
+    HeapTuple          tup;
+    bool               defined;
+    ItemPointerData    itemPointerData;
+    
+    /* check that that the new type is not already defined */
+    type_oid = TypeGet(newTypeName, &defined);
+    if (OidIsValid(type_oid) && defined) {
+       elog(WARN, "TypeRename: type %s already defined", newTypeName); 
+    }
+    
+    /* get the type tuple from the catalog index scan manager */
+    pg_type_desc = heap_openr(TypeRelationName);
+    tup = TypeNameIndexScan(pg_type_desc, oldTypeName);
+    
+    /* ----------------
+     *  change the name of the type
+     * ----------------
+     */
+    if (HeapTupleIsValid(tup)) {
+       
+       namestrcpy(& (((TypeTupleForm) GETSTRUCT(tup))->typname),newTypeName);
+       
+       ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+       
+       setheapoverride(true);
+       heap_replace(pg_type_desc, &itemPointerData, tup);
+       setheapoverride(false);
+       
+       /* update the system catalog indices */
+       CatalogOpenIndices(Num_pg_type_indices, Name_pg_type_indices, idescs);
+       CatalogIndexInsert(idescs, Num_pg_type_indices, pg_type_desc, tup);
+       CatalogCloseIndices(Num_pg_type_indices, idescs);
+       
+       /* all done */
+       pfree(tup);
+       
+    } else {
+       elog(WARN, "TypeRename: type %s not defined", oldTypeName);     
+    }
+    
+    /* finish up */
+    heap_close(pg_type_desc);
+}
+
+/*
+ * makeArrayTypeName(typeName);
+ *    - given a base type name, make an array of type name out of it
+ *
+ * the CALLER is responsible for pfreeing the 
+ */
+
+char*
+makeArrayTypeName(char* typeName)
+{
+    char *arr;
+
+    if (!typeName) return NULL;
+    arr = palloc (strlen(typeName) + 2);
+    arr[0] = '_';
+    strcpy(arr+1, typeName);
+
+    return arr;
+       
+}
diff --git a/src/backend/catalog/pg_type.h b/src/backend/catalog/pg_type.h
new file mode 100644 (file)
index 0000000..a88c62b
--- /dev/null
@@ -0,0 +1,267 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_type.h--
+ *    definition of the system "type" relation (pg_type)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_TYPE_H
+#define PG_TYPE_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+#include "utils/rel.h"         /* for Relation */
+
+/* ----------------
+ *     pg_type definition.  cpp turns this into
+ *     typedef struct FormData_pg_type
+ * ----------------
+ */
+CATALOG(pg_type) BOOTSTRAP {
+    NameData   typname;
+    Oid        typowner;
+    int2       typlen;
+    int2       typprtlen;
+    bool       typbyval;
+    char       typtype;
+    bool       typisdefined;
+    char       typdelim;
+    Oid        typrelid;
+    Oid        typelem;
+    regproc    typinput;
+    regproc    typoutput;
+    regproc    typreceive;
+    regproc    typsend;
+    char       typalign;       /* alignment (c=char, s=short, i=int, d=double) */
+    text       typdefault;     /* VARIABLE LENGTH FIELD */
+} TypeTupleFormData;
+
+/* ----------------
+ *     Form_pg_type corresponds to a pointer to a tuple with
+ *     the format of pg_type relation.
+ * ----------------
+ */
+typedef TypeTupleFormData      *TypeTupleForm;
+
+/* ----------------
+ *     compiler constants for pg_type
+ * ----------------
+ */
+#define Natts_pg_type                  16
+#define Anum_pg_type_typname           1
+#define Anum_pg_type_typowner          2
+#define Anum_pg_type_typlen            3
+#define Anum_pg_type_typprtlen         4
+#define Anum_pg_type_typbyval          5
+#define Anum_pg_type_typtype           6
+#define Anum_pg_type_typisdefined      7
+#define Anum_pg_type_typdelim          8
+#define Anum_pg_type_typrelid          9
+#define Anum_pg_type_typelem           10
+#define Anum_pg_type_typinput          11
+#define Anum_pg_type_typoutput         12
+#define Anum_pg_type_typreceive                13
+#define Anum_pg_type_typsend           14
+#define Anum_pg_type_typalign          15
+#define Anum_pg_type_typdefault                16
+
+/* ----------------
+ *     initial contents of pg_type
+ * ----------------
+ */
+
+/* keep the following ordered by OID so that later changes can be made easier*/
+
+/* OIDS 1 - 99 */
+DATA(insert OID = 16 (  bool       PGUID  1   1 t b t \054 0   0 boolin boolout boolin boolout c _null_ ));
+
+#define BOOLOID                16
+
+DATA(insert OID = 17 (  bytea      PGUID -1  -1 f b t \054 0  18 byteain byteaout byteain byteaout i _null_ ));
+DATA(insert OID = 18 (  char       PGUID  1   1 t b t \054 0   0 charin charout charin charout c _null_ ));
+
+DATA(insert OID = 19 (  name      PGUID NAMEDATALEN NAMEDATALEN  f b t \054 0  18 namein nameout namein nameout d _null_ ));
+DATA(insert OID = 20 (  char16     PGUID 16  16 f b t \054 0  18 char16in char16out char16in char16out i _null_ ));
+/*DATA(insert OID = 20 (  dt         PGUID  4  10 t b t \054 0   0 dtin dtout dtin dtout i _null_ )); */
+DATA(insert OID = 21 (  int2       PGUID  2   5 t b t \054 0   0 int2in int2out int2in int2out s _null_ ));
+
+#define INT2OID                21
+
+DATA(insert OID = 22 (  int28      PGUID 16  50 f b t \054 0  21 int28in int28out int28in int28out i _null_ ));
+
+/*
+ * XXX -- the implementation of int28's in postgres is a hack, and will
+ *       go away someday.  until that happens, there is a case (in the
+ *       catalog cache management code) where we need to step gingerly
+ *       over piles of int28's on the sidewalk.  in order to do so, we
+ *       need the OID of the int28 tuple from pg_type.
+ */
+
+#define INT28OID       22
+
+
+DATA(insert OID = 23 (  int4       PGUID  4  10 t b t \054 0   0 int4in int4out int4in int4out i _null_ ));
+
+#define INT4OID                23
+
+DATA(insert OID = 24 (  regproc    PGUID  4  16 t b t \054 0   0 regprocin regprocout regprocin regprocout i _null_ ));
+DATA(insert OID = 25 (  text       PGUID -1  -1 f b t \054 0  18 textin textout textin textout i _null_ ));
+DATA(insert OID = 26 (  oid        PGUID  4  10 t b t \054 0   0 int4in int4out int4in int4out i _null_ ));
+
+#define OIDOID         26
+
+DATA(insert OID = 27 (  tid        PGUID  6  19 f b t \054 0   0 tidin tidout tidin tidout i _null_ ));
+DATA(insert OID = 28 (  xid        PGUID  4  12 t b t \054 0   0 xidin xidout xidin xidout i _null_ ));
+DATA(insert OID = 29 (  cid        PGUID  2   3 t b t \054 0   0 cidin cidout cidin cidout s _null_ ));
+DATA(insert OID = 30 (  oid8       PGUID 32  89 f b t \054 0  26 oid8in oid8out oid8in oid8out i _null_ ));
+DATA(insert OID = 32 (  SET        PGUID -1  -1 f r t \054 0  -1 textin textout textin textout i _null_ ));
+
+DATA(insert OID = 71 ( pg_type PGUID 1 1 t b t \054 71 0 foo bar foo bar c _null_));
+DATA(insert OID = 75 ( pg_attribute PGUID 1 1 t b t \054 75 0 foo bar foo bar c _null_));
+DATA(insert OID = 76 ( pg_demon PGUID 1 1 t b t \054 76 0 foo bar foo bar c _null_));
+DATA(insert OID = 80 ( pg_magic PGUID 1 1 t b t \054 80 0 foo bar foo bar c _null_));
+DATA(insert OID = 81 ( pg_proc PGUID 1 1 t b t \054 81 0 foo bar foo bar c _null_));
+DATA(insert OID = 82 ( pg_server PGUID 1 1 t b t \054 82 0 foo bar foo bar c _null_));
+DATA(insert OID = 83 ( pg_class PGUID 1 1 t b t \054 83 0 foo bar foo bar c _null_));
+DATA(insert OID = 86 ( pg_user PGUID 1 1 t b t \054 86 0 foo bar foo bar c _null_));
+DATA(insert OID = 87 ( pg_group PGUID 1 1 t b t \054 87 0 foo bar foo bar c _null_));
+DATA(insert OID = 88 ( pg_database PGUID 1 1 t b t \054 88 0 foo bar foo bar c _null_));
+DATA(insert OID = 89 ( pg_defaults PGUID 1 1 t b t \054 89 0 foo bar foo bar c _null_));
+DATA(insert OID = 90 ( pg_variable PGUID 1 1 t b t \054 90 0 foo bar foo bar c _null_));
+DATA(insert OID = 99 ( pg_log PGUID 1 1 t b t \054 99 0 foo bar foo bar c _null_));
+
+/* OIDS 100 - 199 */
+
+DATA(insert OID = 100 ( pg_time PGUID 1 1 t b t \054 100 0 foo bar foo bar c _null_));
+DATA(insert OID = 101 ( pg_time PGUID 1 1 t b t \054 101 0 foo bar foo bar c _null_));
+
+/* OIDS 200 - 299 */
+
+DATA(insert OID = 210 (  smgr       PGUID 2  12 t b t \054 0  -1 smgrin smgrout smgrin smgrout s _null_ ));
+
+/* OIDS 300 - 399 */
+
+/* OIDS 400 - 499 */
+DATA(insert OID = 409 (  char2     PGUID 2  2 t b t \054 0  18 char2in char2out char2in char2out s _null_ ));
+DATA(insert OID = 410 (  char4     PGUID 4  4 t b t \054 0  18 char4in char4out char4in char4out i _null_ ));
+DATA(insert OID = 411 (  char8     PGUID 8  8 f b t \054 0  18 char8in char8out char8in char8out i _null_ ));
+
+/* OIDS 500 - 599 */
+
+/* OIDS 600 - 699 */
+DATA(insert OID = 600 (  point     PGUID 16  24 f b t \054 0 701 point_in point_out point_in point_out d _null_ ));
+DATA(insert OID = 601 (  lseg      PGUID 32  48 f b t \054 0 600 lseg_in lseg_out lseg_in lseg_out d _null_ ));
+DATA(insert OID = 602 (  path      PGUID -1  -1 f b t \054 0 600 path_in path_out path_in path_out d _null_ ));
+DATA(insert OID = 603 (  box       PGUID 32 100 f b t \073 0 600 box_in box_out box_in box_out d _null_ ));
+DATA(insert OID = 604 (  polygon   PGUID -1  -1 f b t \054 0  -1 poly_in poly_out poly_in poly_out d _null_ ));
+DATA(insert OID = 605 (  filename  PGUID 256 -1 f b t \054 0 18 filename_in filename_out filename_in filename_out i _null_ ));
+
+/* OIDS 700 - 799 */
+
+#define FLOAT4OID 700
+
+DATA(insert OID = 700 (  float4    PGUID  4  12 f b t \054 0   0 float4in float4out float4in float4out i _null_ ));
+
+
+#define FLOAT8OID 701
+
+DATA(insert OID = 701 (  float8    PGUID  8  24 f b t \054 0   0 float8in float8out float8in float8out d _null_ ));
+DATA(insert OID = 702 (  abstime   PGUID  4  20 t b t \054 0   0 nabstimein nabstimeout nabstimein nabstimeout i _null_ ));
+DATA(insert OID = 703 (  reltime   PGUID  4  20 t b t \054 0   0 reltimein reltimeout reltimein reltimeout i _null_ ));
+DATA(insert OID = 704 (  tinterval PGUID 12  47 f b t \054 0   0 tintervalin tintervalout tintervalin tintervalout i _null_ ));
+DATA(insert OID = 705 (  unknown PGUID -1  -1 f b t \054 0   18 textin textout textin textout i _null_ ));
+
+#define UNKNOWNOID     705
+
+/* OIDS 800 - 899 */
+DATA(insert OID = 810 (  oidint2    PGUID  6  20 f b t \054 0   0 oidint2in oidint2out oidint2in oidint2out i _null_ ));
+
+/* OIDS 900 - 999 */
+DATA(insert OID = 910 (  oidint4    PGUID  8  20 f b t \054 0   0 oidint4in oidint4out oidint4in oidint4out i _null_ ));
+DATA(insert OID = 911 (  oidname  PGUID  OIDNAMELEN OIDNAMELEN f b t \054 0   0 oidnamein oidnameout oidnamein oidnameout i _null_ ));
+
+/* OIDS 1000 - 1099 */
+DATA(insert OID = 1000 (  _bool      PGUID -1  -1 f b t \054 0  16 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1001 (  _bytea     PGUID -1  -1 f b t \054 0  17 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1002 (  _char      PGUID -1  -1 f b t \054 0  18 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1003 (  _name    PGUID -1  -1 f b t \054 0  19 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1004 (  _char16    PGUID -1  -1 f b t \054 0  19 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1005 (  _int2      PGUID -1  -1 f b t \054 0  21 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1006 (  _int28     PGUID -1  -1 f b t \054 0  22 array_in array_out array_in array_out i _null_ )); 
+DATA(insert OID = 1007 (  _int4      PGUID -1  -1 f b t \054 0  23 array_in array_out array_in array_out i _null_ ));  
+DATA(insert OID = 1008 (  _regproc   PGUID -1  -1 f b t \054 0  24 array_in array_out array_in array_out i _null_ ));  
+DATA(insert OID = 1009 (  _text      PGUID -1  -1 f b t \054 0  25 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1028 (  _oid       PGUID -1  -1 f b t \054 0  26 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1010 (  _tid       PGUID -1  -1 f b t \054 0  27 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1011 (  _xid       PGUID -1  -1 f b t \054 0  28 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1012 (  _cid       PGUID -1  -1 f b t \054 0  29 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1013 (  _oid8      PGUID -1  -1 f b t \054 0  30 array_in array_out array_in array_out i _null_ ));
+/*DATA(insert OID = 1014 (  _lock      PGUID -1  -1 f b t \054 0  31 array_in array_out array_in array_out i _null_ ));*/
+DATA(insert OID = 1015 (  _stub      PGUID -1  -1 f b t \054 0  33 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1016 (  _ref              PGUID -1  -1 f b t \054 0 591 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1017 (  _point     PGUID -1  -1 f b t \054 0 600 array_in array_out array_in array_out d _null_ ));
+DATA(insert OID = 1018 (  _lseg      PGUID -1  -1 f b t \054 0 601 array_in array_out array_in array_out d _null_ ));
+DATA(insert OID = 1019 (  _path      PGUID -1  -1 f b t \054 0 602 array_in array_out array_in array_out d _null_ ));
+DATA(insert OID = 1020 (  _box       PGUID -1  -1 f b t \073 0 603 array_in array_out array_in array_out d _null_ ));
+DATA(insert OID = 1021 (  _float4    PGUID -1  -1 f b t \054 0 700 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1022 (  _float8    PGUID -1  -1 f b t \054 0 701 array_in array_out array_in array_out d _null_ ));
+DATA(insert OID = 1023 (  _abstime   PGUID -1  -1 f b t \054 0 702 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1024 (  _reltime   PGUID -1  -1 f b t \054 0 703 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1025 (  _tinterval PGUID -1  -1 f b t \054 0 704 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1026 (  _filename PGUID -1  -1 f b t \054 0 605 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1027 (  _polygon PGUID -1  -1 f b t \054 0 604 array_in array_out array_in array_out d _null_ ));
+/* Note: the size of an aclitem needs to match sizeof(AclItem) in acl.h */
+DATA(insert OID = 1033 (  aclitem PGUID 8  -1 f b t \054 0 0 aclitemin aclitemout aclitemin aclitemout i _null_ ));
+DATA(insert OID = 1034 (  _aclitem PGUID -1  -1 f b t \054 0 1033 array_in array_out array_in array_out i _null_ ));
+
+DATA(insert OID = 1039 (  _char2    PGUID -1  -1 f b t \054 0  409 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1040 (  _char4    PGUID -1  -1 f b t \054 0  410 array_in array_out array_in array_out i _null_ ));
+DATA(insert OID = 1041 (  _char8    PGUID -1  -1 f b t \054 0  411 array_in array_out array_in array_out i _null_ ));
+
+#define        BPCHAROID       1042
+DATA(insert OID = 1042 ( bpchar  PGUID -1  -1 f b t \054 0  18 bpcharin bpcharout bpcharin bpcharout i _null_ ));
+#define        VARCHAROID      1043
+DATA(insert OID = 1043 ( varchar PGUID -1  -1 f b t \054 0  18 varcharin varcharout varcharin varcharout i _null_ ));
+
+DATA(insert OID = 1082 ( date      PGUID  4  10 t b t \054 0  0 date_in date_out date_in date_out i _null_ ));
+DATA(insert OID = 1083 ( time      PGUID  8  16 f b t \054 0  0 time_in time_out time_in time_out i _null_ ));
+/*
+ * prototypes for functions in pg_type.c 
+ */
+extern Oid TypeGet(char *typeName, bool *defined);
+extern Oid TypeShellMakeWithOpenRelation(Relation pg_type_desc,
+                                        char *typeName);
+extern Oid TypeShellMake(char *typeName);
+extern Oid TypeCreate(char *typeName, 
+                     Oid relationOid,
+                     int16 internalSize, 
+                     int16 externalSize, 
+                     char typeType,
+                     char typDelim, 
+                     char *inputProcedure, 
+                     char *outputProcedure,
+                     char *sendProcedure, 
+                     char *receiveProcedure,
+                     char *elementTypeName,
+                     char *defaultTypeValue,
+                     bool passedByValue, char alignment);
+extern void TypeRename(char *oldTypeName, char *newTypeName);
+extern char *makeArrayTypeName(char *typeName);
+
+
+#endif /* PG_TYPE_H */
diff --git a/src/backend/catalog/pg_user.h b/src/backend/catalog/pg_user.h
new file mode 100644 (file)
index 0000000..9e6335f
--- /dev/null
@@ -0,0 +1,99 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_user.h--
+ *    definition of the system "user" relation (pg_user)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_USER_H
+#define PG_USER_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+/* ----------------
+ *     pg_user definition.  cpp turns this into
+ *     typedef struct FormData_pg_user
+ * ----------------
+ */ 
+CATALOG(pg_user) BOOTSTRAP {
+    NameData   usename;
+    int4       usesysid;
+    bool       usecreatedb;
+    bool       usetrace;
+    bool       usesuper;
+    bool       usecatupd;  
+} FormData_pg_user;
+
+/* ----------------
+ *     Form_pg_user corresponds to a pointer to a tuple with
+ *     the format of pg_user relation.
+ * ----------------
+ */
+typedef FormData_pg_user       *Form_pg_user;
+
+/* ----------------
+ *     compiler constants for pg_user
+ * ----------------
+ */
+#define Natts_pg_user                  6
+#define Anum_pg_user_usename           1
+#define Anum_pg_user_usesysid          2
+#define Anum_pg_user_usecreatedb       3
+#define Anum_pg_user_usetrace          4
+#define Anum_pg_user_usesuper          5
+#define Anum_pg_user_usecatupd         6
+
+/* ----------------
+ *     initial contents of pg_user
+ * ----------------
+ */
+DATA(insert OID = 0 ( postgres PGUID t t t t ));
+
+BKI_BEGIN
+#ifdef ALLOW_PG_GROUP
+BKI_END
+
+DATA(insert OID = 0 ( mike 799 t t t t ));
+DATA(insert OID = 0 ( mao 1806 t t t t ));
+DATA(insert OID = 0 ( hellers 1089 t t t t ));
+DATA(insert OID = 0 ( joey 5209 t t t t ));
+DATA(insert OID = 0 ( jolly 5443 t t t t ));
+DATA(insert OID = 0 ( sunita 6559 t t t t ));
+DATA(insert OID = 0 ( paxson 3029 t t t t ));
+DATA(insert OID = 0 ( marc 2435 t t t t ));
+DATA(insert OID = 0 ( jiangwu 6124 t t t t ));
+DATA(insert OID = 0 ( aoki 2360 t t t t ));
+DATA(insert OID = 0 ( avi 31080 t t t t ));
+DATA(insert OID = 0 ( kristin 1123 t t t t ));
+DATA(insert OID = 0 ( andrew 5229 t t t t ));
+DATA(insert OID = 0 ( nobuko 5493 t t t t ));
+DATA(insert OID = 0 ( hartzell 6676 t t t t ));
+DATA(insert OID = 0 ( devine 6724 t t t t ));
+DATA(insert OID = 0 ( boris 6396 t t t t ));
+DATA(insert OID = 0 ( sklower 354 t t t t ));
+DATA(insert OID = 0 ( marcel 31113 t t t t ));
+DATA(insert OID = 0 ( ginger 3692 t t t t ));
+DATA(insert OID = 0 ( woodruff 31026 t t t t ));
+DATA(insert OID = 0 ( searcher 8261 t t t t ));
+     
+BKI_BEGIN
+#endif /* ALLOW_PG_GROUP */
+BKI_END
+
+#endif /* PG_USER_H */
diff --git a/src/backend/catalog/pg_variable.h b/src/backend/catalog/pg_variable.h
new file mode 100644 (file)
index 0000000..9c58831
--- /dev/null
@@ -0,0 +1,40 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_variable.h--
+ *    the system variable relation "pg_variable" is not a "heap" relation.
+ *    it is automatically created by the transam/ code and the
+ *    information here is all bogus and is just here to make the
+ *    relcache code happy.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    The structures and macros used by the transam/ code
+ *    to access pg_variable should someday go here -cim 6/18/90
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_VARIABLE_H
+#define PG_VARIABLE_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+
+CATALOG(pg_variable) BOOTSTRAP {
+    Oid        varfoo;
+} FormData_pg_variable;
+
+typedef FormData_pg_variable   *Form_pg_variable;
+
+#define Natts_pg_variable      1
+#define Anum_pg_variable_varfoo        1
+
+#endif /* PG_VARIABLE_H */
diff --git a/src/backend/catalog/pg_version.h b/src/backend/catalog/pg_version.h
new file mode 100644 (file)
index 0000000..0ac4439
--- /dev/null
@@ -0,0 +1,58 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_version.h--
+ *    definition of the system "version" relation (pg_version)
+ *    along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    the genbki.sh script reads this file and generates .bki
+ *    information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_VERSION_H
+#define PG_VERSION_H
+
+/* ----------------
+ *     postgres.h contains the system type definintions and the
+ *     CATALOG(), BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+#include "postgres.h"
+#include "utils/nabstime.h"
+
+/* ----------------
+ *     pg_version definition.  cpp turns this into
+ *     typedef struct FormData_pg_version
+ * ----------------
+ */ 
+CATALOG(pg_version) {
+    Oid        verrelid;
+    Oid        verbaseid;
+    int4       vertime;  /* really should be some abstime */
+} FormData_pg_version;
+
+/* ----------------
+ *     Form_pg_version corresponds to a pointer to a tuple with
+ *     the format of pg_version relation.
+ * ----------------
+ */
+typedef FormData_pg_version    *VersionTupleForm;
+
+/* ----------------
+ *     compiler constants for pg_version
+ * ----------------
+ */
+#define Natts_pg_version               3
+#define Anum_pg_version_verrelid       1
+#define Anum_pg_version_verbaseid      2
+#define Anum_pg_version_vertime                3
+
+
+#endif /* PG_VERSION_H */
diff --git a/src/backend/catalog/unused_oids b/src/backend/catalog/unused_oids
new file mode 100644 (file)
index 0000000..1bbf958
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+# unused_oids
+#
+# $Header$
+#
+#      finds blocks of oids that have not already been claimed by 
+#      post_hackers for internal purposes.  primarily useful for
+#      finding valid oids for new internal function oids.  the numbers
+#      printed are inclusive ranges of valid (unused) oids.
+#
+#      before using a large empty block, make sure you aren't about
+#      to take over what was intended as expansion space for something
+#      else.  also, before using a number, do a "grepsrc" to make sure 
+#      that someone isn't using a literal numeric constant somewhere..
+#
+#      non-berkeley post_hackers should probably not try to use oids 
+#      less than the highest one that comes with the distributed source.
+#
+#      run this script in src/backend/catalog.
+#
+egrep '^DATA' pg_*.h | \
+       sed -e 's/^.*OID[^=]*=[^0-9]*//' -e 's/[^0-9].*$//' | \
+       sort -n | \
+       uniq | \
+       awk '
+BEGIN {
+       last = 0;
+}
+/^[0-9]/ {
+       if ($1 > last + 1) {
+               if ($1 > last + 2) {
+                       print last + 1, "-", $1 - 1;
+               } else {
+                       print last + 1;
+               }
+       }
+       last = $1;
+}
+END {
+       print last + 1, "-";
+}'
diff --git a/src/backend/commands/Makefile.inc b/src/backend/commands/Makefile.inc
new file mode 100644 (file)
index 0000000..de33cb8
--- /dev/null
@@ -0,0 +1,25 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the commands module
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+VPATH:=$(VPATH):$(CURDIR)/commands
+
+
+SRCS_COMMANDS= async.c creatinh.c command.c copy.c defind.c define.c \
+       purge.c remove.c rename.c vacuum.c version.c view.c cluster.c \
+       recipe.c explain.c
+
+HEADERS+= async.h command.h copy.h creatinh.h defrem.h purge.h \
+       rename.h vacuum.h version.h view.h cluster.h \
+       recipe.h
+
+
diff --git a/src/backend/commands/_deadcode/version.c b/src/backend/commands/_deadcode/version.c
new file mode 100644 (file)
index 0000000..53421d1
--- /dev/null
@@ -0,0 +1,336 @@
+/*-------------------------------------------------------------------------
+ *
+ * version.c--
+ *    This file contains all the rules that govern all version semantics.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *  The version stuff has not been tested under postgres95 and probably doesn't
+ *  work! - jolly 8/19/95
+ *  
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    At the point the version is defined, 2 physical relations are created
+ *    <vname>_added and <vname>_deleted.
+ *
+ *    In addition, 4 rules are defined which govern the semantics of versions 
+ *    w.r.t retrieves, appends, replaces and deletes.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+
+#include "postgres.h"
+
+#include "utils/rel.h"
+#include "access/heapam.h"
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "nodes/pg_list.h"
+#include "commands/version.h"
+#include "access/xact.h"               /* for GetCurrentXactStartTime */
+#include "tcop/tcopprot.h"
+
+#define MAX_QUERY_LEN 1024
+
+char rule_buf[MAX_QUERY_LEN];
+static char attr_list[MAX_QUERY_LEN];
+
+static void setAttrList(char *bname);
+
+/*
+ * problem: the version system assumes that the rules it declares will
+ *          be fired in the order of declaration, it also assumes
+ *          goh's silly instead semantics.  Unfortunately, it is a pain
+ *          to make the version system work with the new semantics.
+ *          However the whole problem can be solved, and some nice
+ *          functionality can be achieved if we get multiple action rules
+ *          to work.  So thats what I did                       -- glass
+ *
+ * Well, at least they've been working for about 20 minutes.
+ * 
+ * So any comments in this code about 1 rule per transction are false...:)
+ *
+ */
+
+/*
+ *  This is needed because the rule system only allows 
+ *  *1* rule to be defined per transaction.
+ *
+ * NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+ * OOOOOOOOOOOOOOOOOOO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ *
+ * DONT DO THAT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ *
+ * If you commit the current Xact all the palloced memory GOES AWAY
+ * and could be re-palloced in the new Xact and the whole hell breaks
+ * loose and poor people like me spend 2 hours of their live chassing
+ * a strange memory bug instead of watching the "Get Smart" marathon
+ * in NICK !
+ * DO NOT COMMIT THE XACT, just increase the Cid counter!
+ *                                                     _sp.
+ */
+static void
+eval_as_new_xact(char *query)
+{
+    /* WARNING! do not uncomment the following lines WARNING!
+     *  CommitTransactionCommand();
+     * StartTransactionCommand();
+     */
+    CommandCounterIncrement();
+    pg_eval(query, (char **) NULL, (Oid *) NULL, 0);
+}
+
+/*
+ *  Define a version.
+ */
+void
+DefineVersion(char *name, char *fromRelname, char *date)
+{
+    char *bname;
+    static char saved_basename[512];
+    static char saved_snapshot[512];
+
+    if (date == NULL) {
+       /* no time ranges */
+       bname = fromRelname;
+       (void) strcpy(saved_basename, (char *) bname);
+       *saved_snapshot = (char)NULL;
+    } else {
+       /* version is a snapshot */
+       bname = fromRelname;
+       (void) strcpy(saved_basename, (char *) bname);
+       sprintf(saved_snapshot, "['%s']", date);
+    }
+    
+    
+    /*
+     * Calls the routine ``GetAttrList'' get the list of attributes
+     * from the base relation. 
+     * Code is put here so that we only need to look up the attribute once for
+     * both appends and replaces.
+     */
+    setAttrList(bname);
+
+    VersionCreate (name, saved_basename);
+    VersionAppend (name, saved_basename);
+    VersionDelete (name, saved_basename,saved_snapshot);
+    VersionReplace (name, saved_basename,saved_snapshot);
+    VersionRetrieve (name, saved_basename, saved_snapshot);
+}
+
+
+/*
+ *  Creates the deltas.
+ */
+void
+VersionCreate(char *vname, char *bname)
+{
+    static char query_buf [MAX_QUERY_LEN];
+    
+    /*
+     *  Creating the dummy version relation for triggering rules.
+     */
+    sprintf(query_buf, "SELECT * INTO TABLE %s from %s where 1 =2",
+           vname, bname);
+    
+    pg_eval (query_buf, (char **) NULL, (Oid *) NULL, 0);  
+    
+    /* 
+     * Creating the ``v_added'' relation 
+     */
+    sprintf (query_buf, "SELECT * INTO TABLE %s_added from %s where 1 = 2", 
+            vname, bname);
+    eval_as_new_xact (query_buf); 
+    
+    /* 
+     * Creating the ``v_deleted'' relation. 
+     */
+    sprintf (query_buf, "CREATE TABLE %s_del (DOID oid)", vname);
+    eval_as_new_xact (query_buf); 
+}
+
+
+/*
+ * Given the relation name, does a catalog lookup for that relation and
+ * sets the global variable 'attr_list' with the list of attributes (names)
+ * for that relation. 
+ */
+static void
+setAttrList(char *bname)
+{
+    Relation rdesc;
+    int i = 0;
+    int maxattrs = 0;
+    char *attrname;
+    char temp_buf[512];
+    int notfirst = 0;    
+
+    rdesc = heap_openr(bname);
+    if (rdesc == NULL ) {
+       elog(WARN,"Unable to expand all -- amopenr failed ");
+       return;
+    }
+    maxattrs = RelationGetNumberOfAttributes(rdesc);
+
+    attr_list[0] = '\0';
+    
+    for ( i = maxattrs-1 ; i > -1 ; --i ) {
+       attrname = (rdesc->rd_att->attrs[i]->attname).data;
+
+       if (notfirst == 1) {
+           sprintf(temp_buf, ", %s = new.%s", attrname, attrname);
+       } else {
+           sprintf(temp_buf, "%s = new.%s", attrname, attrname);
+           notfirst = 1;
+       }
+       strcat(attr_list, temp_buf);  
+    }
+    
+    heap_close(rdesc);
+    
+    return;
+}
+
+/*
+ * This routine defines the rule governing the append semantics of
+ * versions.  All tuples appended to a version gets appended to the 
+ * <vname>_added relation.
+ */
+void
+VersionAppend(char *vname, char *bname)
+{
+    sprintf(rule_buf,
+           "define rewrite rule %s_append is on INSERT to %s do instead append %s_added(%s)",
+           vname, vname, vname, attr_list);
+    
+    eval_as_new_xact(rule_buf); 
+}
+
+
+/*
+ * This routine defines the rule governing the retrieval semantics of
+ * versions.  To retrieve tuples from a version , we need to:
+ *
+ *      1. Retrieve all tuples in the <vname>_added relation.
+ *      2. Retrieve all tuples in the base relation which are not in 
+ *         the <vname>_del relation.
+ */
+void
+VersionRetrieve(char *vname, char *bname, char *snapshot)
+{
+    
+    sprintf(rule_buf, 
+           "define rewrite rule %s_retrieve is on SELECT to %s do instead\n\
+SELECT %s_1.oid, %s_1.* from _%s in %s%s, %s_1 in (%s_added | _%s) \
+where _%s.oid !!= '%s_del.DOID'",
+           vname, vname, vname, vname, bname,
+           bname, snapshot,
+           vname, vname, bname, bname, vname);
+    
+    eval_as_new_xact(rule_buf); 
+    
+    /*  printf("%s\n",rule_buf); */
+    
+}
+
+/*
+ * This routine defines the rules that govern the delete semantics of 
+ * versions. Two things happens when we delete a tuple from a version:
+ *
+ *     1. If the tuple to be deleted was added to the version *after*
+ *        the version was created, then we simply delete the tuple 
+ *        from the <vname>_added relation.
+ *     2. If the tuple to be deleted is actually in the base relation,
+ *        then we have to mark that tuple as being deleted by adding
+ *        it to the <vname>_del relation.
+ */
+void
+VersionDelete(char *vname, char *bname, char *snapshot)
+{
+    
+    sprintf(rule_buf,
+           "define rewrite rule %s_delete1 is on delete to %s do instead\n \
+[delete %s_added where current.oid = %s_added.oid\n \
+ append %s_del(DOID = current.oid) from _%s in %s%s \
+ where current.oid = _%s.oid] \n",
+         vname,vname,vname,vname,vname,
+bname,bname,snapshot,bname); 
+
+  eval_as_new_xact(rule_buf); 
+#ifdef OLD_REWRITE
+   sprintf(rule_buf,
+         "define rewrite rule %s_delete2 is on delete to %s do instead \n \
+    append %s_del(DOID = current.oid) from _%s in %s%s \
+    where current.oid = _%s.oid \n",
+         vname,vname,vname,bname,bname,snapshot,bname);
+
+   eval_as_new_xact(rule_buf);
+#endif /*  OLD_REWRITE */
+}
+
+/*
+ *  This routine defines the rules that govern the update semantics
+ *  of versions. To update a tuple in a version:
+ *
+ *      1. If the tuple is in <vname>_added, we simply ``replace''
+ *         the tuple (as per postgres style).
+ *      2. if the tuple is in the base relation, then two things have to
+ *         happen:
+ *         2.1  The tuple is marked ``deleted'' from the base relation by 
+ *              adding the tuple to the <vname>_del relation. 
+ *         2.2  A copy of the tuple is appended to the <vname>_added relation
+ */
+void
+VersionReplace(char *vname, char *bname, char *snapshot)
+{
+    sprintf(rule_buf,
+           "define rewrite rule %s_replace1 is on replace to %s do instead \n\
+[replace %s_added(%s) where current.oid = %s_added.oid \n\
+ append %s_del(DOID = current.oid) from _%s in %s%s \
+ where current.oid = _%s.oid\n\
+ append %s_added(%s) from _%s in %s%s \
+ where current.oid !!= '%s_added.oid' and current.oid = _%s.oid]\n",
+         vname,vname,vname,attr_list,vname,
+          vname,bname,bname,snapshot,bname,
+vname,attr_list,bname,bname,snapshot,vname,bname);
+
+  eval_as_new_xact(rule_buf); 
+
+/*  printf("%s\n",rule_buf); */
+#ifdef OLD_REWRITE
+  sprintf(rule_buf,
+         "define rewrite rule %s_replace2 is on replace to %s do \n\
+    append %s_del(DOID = current.oid) from _%s in %s%s \
+    where current.oid = _%s.oid\n",
+         vname,vname,vname,bname,bname,snapshot,bname);
+
+  eval_as_new_xact(rule_buf); 
+
+  sprintf(rule_buf,
+         "define rewrite rule %s_replace3 is on replace to %s do instead\n\
+    append %s_added(%s) from _%s in %s%s \
+    where current.oid !!= '%s_added.oid' and current.oid = \
+    _%s.oid\n",
+         vname,vname, vname,attr_list,bname,bname,snapshot,vname,bname);
+
+  eval_as_new_xact(rule_buf); 
+#endif /* OLD_REWRITE */
+/*  printf("%s\n",rule_buf); */
+
+}
+
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
new file mode 100644 (file)
index 0000000..f3dde05
--- /dev/null
@@ -0,0 +1,605 @@
+/*-------------------------------------------------------------------------
+ *
+ * async.c--
+ *    Asynchronous notification
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/* New Async Notification Model:
+ * 1. Multiple backends on same machine.  Multiple backends listening on
+ *    one relation.
+ *
+ * 2. One of the backend does a 'notify <relname>'.  For all backends that
+ *    are listening to this relation (all notifications take place at the
+ *    end of commit),
+ *    2.a  If the process is the same as the backend process that issued
+ *         notification (we are notifying something that we are listening),
+ *         signal the corresponding frontend over the comm channel using the
+ *         out-of-band channel.
+ *    2.b  For all other listening processes, we send kill(2) to wake up
+ *         the listening backend.
+ * 3. Upon receiving a kill(2) signal from another backend process notifying
+ *    that one of the relation that we are listening is being notified,
+ *    we can be in either of two following states:
+ *    3.a  We are sleeping, wake up and signal our frontend.
+ *    3.b  We are in middle of another transaction, wait until the end of
+ *         of the current transaction and signal our frontend.
+ * 4. Each frontend receives this notification and prcesses accordingly.
+ *
+ * -- jw, 12/28/93
+ *
+ */
+/*
+ * The following is the old model which does not work.
+ */
+/*
+ * Model is:
+ * 1. Multiple backends on same machine.
+ *
+ * 2. Query on one backend sends stuff over an asynchronous portal by 
+ *    appending to a relation, and then doing an async. notification
+ *    (which takes place after commit) to all listeners on this relation.
+ *
+ * 3. Async. notification results in all backends listening on relation 
+ *    to be woken up, by a process signal kill(2), with name of relation
+ *    passed in shared memory.
+ *
+ * 4. Each backend notifies its respective frontend over the comm
+ *    channel using the out-of-band channel.
+ *
+ * 5. Each frontend receives this notification and processes accordingly.
+ *
+ * #4,#5 are changing soon with pending rewrite of portal/protocol.
+ *
+ */
+
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include "postgres.h"
+
+#include "access/attnum.h"
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "utils/builtins.h"
+#include "utils/tqual.h"
+#include "access/xact.h"
+
+#include "commands/async.h"
+#include "commands/copy.h"
+#include "storage/buf.h"
+#include "storage/itemptr.h"
+#include "miscadmin.h"
+#include "utils/portal.h"
+#include "utils/excid.h"
+#include "utils/elog.h"
+#include "utils/mcxt.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+
+#include "nodes/pg_list.h"
+#include "tcop/dest.h"
+#include "commands/command.h"
+
+#include "catalog/catname.h"
+#include "utils/syscache.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_listener.h"
+
+#include "executor/execdefs.h"
+/* #include "executor/execdesc.h"*/
+
+#include "storage/bufmgr.h"
+#include "lib/dllist.h"
+#include "libpq/libpq.h"
+
+
+static int notifyFrontEndPending = 0;
+static int notifyIssued = 0;
+static Dllist *pendingNotifies = NULL;
+
+
+static int AsyncExistsPendingNotify(char *);
+static void ClearPendingNotify(void);
+     
+/*
+ *--------------------------------------------------------------
+ * Async_NotifyHandler --
+ *
+ *      This is the signal handler for SIGUSR2.  When the backend
+ *      is signaled, the backend can be in two states.
+ *      1. If the backend is in the middle of another transaction,
+ *         we set the flag, notifyFrontEndPending, and wait until
+ *         the end of the transaction to notify the front end.
+ *      2. If the backend is not in the middle of another transaction,
+ *         we notify the front end immediately.
+ *
+ *      -- jw, 12/28/93
+ * Results:
+ *      none
+ *
+ * Side effects:
+ *      none
+ */
+void
+#if defined(PORTNAME_linux)
+Async_NotifyHandler(int i)
+#else
+Async_NotifyHandler()
+#endif
+{
+    extern TransactionState CurrentTransactionState;
+    
+    if ((CurrentTransactionState->state == TRANS_DEFAULT) &&
+       (CurrentTransactionState->blockState == TRANS_DEFAULT)) {
+
+       elog(DEBUG, "Waking up sleeping backend process");
+       Async_NotifyFrontEnd();
+
+    }else {
+       elog(DEBUG, "Process is in the middle of another transaction, state = %d, block state = %d",
+            CurrentTransactionState->state,
+            CurrentTransactionState->blockState);
+       notifyFrontEndPending = 1;
+    }
+}
+
+/*
+ *--------------------------------------------------------------
+ * Async_Notify --
+ *
+ *      Adds the relation to the list of pending notifies.
+ *      All notification happens at end of commit.
+ *      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ *
+ *      All notification of backend processes happens here,
+ *      then each backend notifies its corresponding front end at
+ *      the end of commit.
+ *
+ *      This correspond to 'notify <relname>' command
+ *      -- jw, 12/28/93
+ *
+ * Results:
+ *      XXX
+ *
+ * Side effects:
+ *      All tuples for relname in pg_listener are updated.
+ *
+ *--------------------------------------------------------------
+ */
+void
+Async_Notify(char *relname)
+{
+    
+    HeapTuple lTuple, rTuple;
+    Relation lRel;
+    HeapScanDesc sRel;
+    TupleDesc tdesc;
+    ScanKeyData key;
+    Buffer b;
+    Datum d, value[3];
+    bool isnull;
+    char repl[3], nulls[3];
+    
+    char *notifyName;
+    
+    elog(DEBUG,"Async_Notify: %s",relname);
+    
+    if (!pendingNotifies) 
+      pendingNotifies = DLNewList();
+
+    notifyName = pstrdup(relname);
+    DLAddHead(pendingNotifies, DLNewElem(notifyName));
+    
+    ScanKeyEntryInitialize(&key, 0,
+                          Anum_pg_listener_relname,
+                          NameEqualRegProcedure,
+                          PointerGetDatum(notifyName));
+    
+    lRel = heap_openr(ListenerRelationName);
+    tdesc = RelationGetTupleDescriptor(lRel);
+    sRel = heap_beginscan(lRel, 0, NowTimeQual, 1, &key);
+    
+    nulls[0] = nulls[1] = nulls[2] = ' ';
+    repl[0] = repl[1] = repl[2] = ' ';
+    repl[Anum_pg_listener_notify - 1] = 'r';
+    value[0] = value[1] = value[2] = (Datum) 0;
+    value[Anum_pg_listener_notify - 1] = Int32GetDatum(1);
+    
+    while (HeapTupleIsValid(lTuple = heap_getnext(sRel, 0, &b))) {
+       d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_notify,
+                                tdesc, &isnull);
+       if (!DatumGetInt32(d)) {
+           rTuple = heap_modifytuple(lTuple, b, lRel, value, nulls, repl);
+           (void) heap_replace(lRel, &lTuple->t_ctid, rTuple);
+       }
+       ReleaseBuffer(b);
+    }
+    heap_endscan(sRel);
+    heap_close(lRel);
+    notifyIssued = 1;
+}
+
+/*
+ *--------------------------------------------------------------
+ * Async_NotifyAtCommit --
+ *
+ *      Signal our corresponding frontend process on relations that
+ *      were notified.  Signal all other backend process that
+ *      are listening also.
+ *
+ *      -- jw, 12/28/93
+ *
+ * Results:
+ *      XXX
+ *
+ * Side effects:
+ *      Tuples in pg_listener that has our listenerpid are updated so
+ *      that the notification is 0.  We do not want to notify frontend
+ *      more than once.
+ *
+ *      -- jw, 12/28/93
+ *
+ *--------------------------------------------------------------
+ */
+void
+Async_NotifyAtCommit()
+{
+    HeapTuple lTuple;
+    Relation lRel;
+    HeapScanDesc sRel;
+    TupleDesc tdesc;
+    ScanKeyData key;
+    Datum d;
+    int ourpid;
+    bool isnull;
+    Buffer b;
+    extern TransactionState CurrentTransactionState;
+    
+    if (!pendingNotifies)
+      pendingNotifies = DLNewList();
+    
+    if ((CurrentTransactionState->state == TRANS_DEFAULT) &&
+       (CurrentTransactionState->blockState == TRANS_DEFAULT)) {
+       
+       if (notifyIssued) {             /* 'notify <relname>' issued by us */
+           notifyIssued = 0;
+           StartTransactionCommand();
+           elog(DEBUG, "Async_NotifyAtCommit.");
+           ScanKeyEntryInitialize(&key, 0,
+                                  Anum_pg_listener_notify,
+                                  Integer32EqualRegProcedure,
+                                  Int32GetDatum(1));
+           lRel = heap_openr(ListenerRelationName);
+           sRel = heap_beginscan(lRel, 0, NowTimeQual, 1, &key);
+           tdesc = RelationGetTupleDescriptor(lRel);
+           ourpid = getpid();
+           
+           while (HeapTupleIsValid(lTuple = heap_getnext(sRel,0, &b))) {
+               d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_relname,
+                                        tdesc, &isnull);
+               
+               if (AsyncExistsPendingNotify((char *) DatumGetPointer(d))) {
+                   d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_pid,
+                                            tdesc, &isnull);
+                   
+                   if (ourpid == DatumGetInt32(d)) {
+                       elog(DEBUG, "Notifying self, setting notifyFronEndPending to 1");
+                       notifyFrontEndPending = 1;
+                   } else {
+                       elog(DEBUG, "Notifying others");
+#ifndef WIN32
+                       if (kill(DatumGetInt32(d), SIGUSR2) < 0) {
+                           if (errno == ESRCH) {
+                               heap_delete(lRel, &lTuple->t_ctid);
+                           }
+                       }
+#endif /* WIN32 */
+                   }
+               }
+               ReleaseBuffer(b);
+           }
+           CommitTransactionCommand();
+           ClearPendingNotify();
+       }
+       
+       if (notifyFrontEndPending) {    /* we need to notify the frontend of
+                                          all pending notifies. */
+           notifyFrontEndPending = 1;
+           Async_NotifyFrontEnd();
+       }
+    }
+}
+
+/*
+ *--------------------------------------------------------------
+ * Async_NotifyAtAbort --
+ *
+ *      Gets rid of pending notifies.  List elements are automatically
+ *      freed through memory context.
+ *      
+ *
+ * Results:
+ *      XXX
+ *
+ * Side effects:
+ *      XXX
+ *
+ *--------------------------------------------------------------
+ */
+void
+Async_NotifyAtAbort()
+{
+    extern TransactionState CurrentTransactionState;
+    
+    if (notifyIssued) {
+       ClearPendingNotify();
+    }
+    notifyIssued = 0;
+    if (pendingNotifies)
+       DLFreeList(pendingNotifies);
+    pendingNotifies = DLNewList();
+
+    if ((CurrentTransactionState->state == TRANS_DEFAULT) &&
+       (CurrentTransactionState->blockState == TRANS_DEFAULT)) {
+       if (notifyFrontEndPending) { /* don't forget to notify front end */
+           Async_NotifyFrontEnd();
+       }
+    }
+}
+
+/*
+ *--------------------------------------------------------------
+ * Async_Listen --
+ *
+ *      Register a backend (identified by its Unix PID) as listening
+ *      on the specified relation.  
+ *
+ *      This corresponds to the 'listen <relation>' command in SQL
+ *
+ *      One listener per relation, pg_listener relation is keyed
+ *      on (relname,pid) to provide multiple listeners in future.
+ *
+ * Results:
+ *      pg_listeners is updated.
+ *
+ * Side effects:
+ *      XXX
+ *
+ *--------------------------------------------------------------
+ */
+void
+Async_Listen(char *relname, int pid)
+{
+    Datum values[Natts_pg_listener];
+    char nulls[Natts_pg_listener];
+    TupleDesc tdesc;
+    HeapScanDesc s;
+    HeapTuple htup,tup;
+    Relation lDesc;
+    Buffer b;
+    Datum d;
+    int i;
+    bool isnull;
+    int alreadyListener = 0;
+    int ourPid = getpid();
+    char *relnamei;
+    TupleDesc tupDesc;
+    
+    elog(DEBUG,"Async_Listen: %s",relname);
+    for (i = 0 ; i < Natts_pg_listener; i++) {
+        nulls[i] = ' ';
+        values[i] = PointerGetDatum(NULL);
+    }
+    
+    i = 0;
+    values[i++] = (Datum) relname;
+    values[i++] = (Datum) pid;
+    values[i++] = (Datum) 0;   /* no notifies pending */
+    
+    lDesc = heap_openr(ListenerRelationName);
+    
+    /* is someone already listening.  One listener per relation */
+    tdesc = RelationGetTupleDescriptor(lDesc);
+    s = heap_beginscan(lDesc,0,NowTimeQual,0,(ScanKey)NULL);
+    while (HeapTupleIsValid(htup = heap_getnext(s,0,&b))) {
+       d = (Datum) heap_getattr(htup,b,Anum_pg_listener_relname,tdesc,
+                                &isnull);
+       relnamei = DatumGetPointer(d);
+       if (!strncmp(relnamei,relname, NAMEDATALEN)) {
+           d = (Datum) heap_getattr(htup,b,Anum_pg_listener_pid,tdesc,&isnull);
+           pid = DatumGetInt32(d);
+           if (pid == ourPid) {
+               alreadyListener = 1;
+           }
+       }
+       ReleaseBuffer(b);
+    }
+    heap_endscan(s);
+    
+    if (alreadyListener) {
+       elog(NOTICE, "Async_Listen: We are already listening on %s",
+            relname);
+       return;
+    }
+    
+    tupDesc = lDesc->rd_att;
+    tup = heap_formtuple(tupDesc,
+                        values,
+                        nulls);
+    heap_insert(lDesc, tup);
+    
+    pfree(tup);
+    /*    if (alreadyListener) {
+         elog(NOTICE,"Async_Listen: already one listener on %s (possibly dead)",relname);
+         }*/
+    heap_close(lDesc);
+    
+    /*
+     * now that we are listening, we should make a note to ourselves 
+     * to unlisten prior to dying.
+     */
+    relnamei = malloc(NAMEDATALEN); /* persists to process exit */
+    memset (relnamei, 0, NAMEDATALEN);
+    strncpy(relnamei, relname, NAMEDATALEN);
+    on_exitpg(Async_UnlistenOnExit, (caddr_t) relnamei);
+}
+
+/*
+ *--------------------------------------------------------------
+ * Async_Unlisten --
+ *
+ *      Remove the backend from the list of listening backends
+ *      for the specified relation.
+ *    
+ *      This would correspond to the 'unlisten <relation>' 
+ *      command, but there isn't one yet.
+ *
+ * Results:
+ *      pg_listeners is updated.
+ *
+ * Side effects:
+ *      XXX
+ *
+ *--------------------------------------------------------------
+ */
+void
+Async_Unlisten(char *relname, int pid)
+{
+    Relation lDesc;
+    HeapTuple lTuple;
+    
+    lTuple = SearchSysCacheTuple(LISTENREL, PointerGetDatum(relname),
+                                Int32GetDatum(pid),
+                                0,0);
+    lDesc = heap_openr(ListenerRelationName);
+    if (lTuple != NULL) {
+       heap_delete(lDesc,&lTuple->t_ctid);
+    }
+    heap_close(lDesc);
+}
+
+void
+Async_UnlistenOnExit(int code, /* from exitpg */
+                    char *relname)
+{
+    Async_Unlisten((char *) relname, getpid());
+}
+
+/*
+ * --------------------------------------------------------------
+ * Async_NotifyFrontEnd --
+ *
+ *      Perform an asynchronous notification to front end over
+ *      portal comm channel.  The name of the relation which contains the
+ *      data is sent to the front end.
+ *
+ *      We remove the notification flag from the pg_listener tuple
+ *      associated with our process.
+ *
+ * Results:
+ *      XXX
+ *
+ * Side effects:
+ *
+ *      We make use of the out-of-band channel to transmit the
+ *      notification to the front end.  The actual data transfer takes
+ *      place at the front end's request.
+ *
+ * --------------------------------------------------------------
+ */
+GlobalMemory notifyContext = NULL;
+
+void
+Async_NotifyFrontEnd()
+{
+    extern CommandDest whereToSendOutput;
+    HeapTuple lTuple, rTuple;
+    Relation lRel;
+    HeapScanDesc sRel;
+    TupleDesc tdesc;
+    ScanKeyData key[2];
+    Datum d, value[3];
+    char repl[3], nulls[3];
+    Buffer b;
+    int ourpid;
+    bool isnull;
+    
+    notifyFrontEndPending = 0;
+    
+    elog(DEBUG, "Async_NotifyFrontEnd: notifying front end.");
+    
+    StartTransactionCommand();
+    ourpid = getpid();
+    ScanKeyEntryInitialize(&key[0], 0,
+                          Anum_pg_listener_notify,
+                          Integer32EqualRegProcedure,
+                          Int32GetDatum(1));
+    ScanKeyEntryInitialize(&key[1], 0,
+                          Anum_pg_listener_pid,
+                          Integer32EqualRegProcedure,
+                          Int32GetDatum(ourpid));
+    lRel = heap_openr(ListenerRelationName);
+    tdesc = RelationGetTupleDescriptor(lRel);
+    sRel = heap_beginscan(lRel, 0, NowTimeQual, 2, key);
+    
+    nulls[0] = nulls[1] = nulls[2] = ' ';
+    repl[0] = repl[1] = repl[2] = ' ';
+    repl[Anum_pg_listener_notify - 1] = 'r';
+    value[0] = value[1] = value[2] = (Datum) 0;
+    value[Anum_pg_listener_notify - 1] = Int32GetDatum(0);
+    
+    while (HeapTupleIsValid(lTuple = heap_getnext(sRel, 0,&b))) {
+       d = (Datum) heap_getattr(lTuple, b, Anum_pg_listener_relname,
+                                tdesc, &isnull);
+       rTuple = heap_modifytuple(lTuple, b, lRel, value, nulls, repl);
+       (void) heap_replace(lRel, &lTuple->t_ctid, rTuple);
+       
+       /* notifying the front end */
+       
+       if (whereToSendOutput == Remote) {
+           pq_putnchar("A", 1);
+           pq_putint(ourpid, 4);
+           pq_putstr(DatumGetName(d)->data);
+           pq_flush();
+       } else {
+           elog(NOTICE, "Async_NotifyFrontEnd: no asynchronous notification to frontend on interactive sessions");
+       }
+       ReleaseBuffer(b);
+    }
+    CommitTransactionCommand();
+}
+
+static int
+AsyncExistsPendingNotify(char *relname)
+{
+    Dlelem* p;
+    for (p = DLGetHead(pendingNotifies); 
+        p != NULL;
+        p = DLGetSucc(p)) {
+      if (!strcmp(DLE_VAL(p), relname))
+       return 1;
+    }
+
+    return 0;
+}
+
+static void
+ClearPendingNotify()
+{
+    Dlelem* p;
+    while ( (p = DLRemHead(pendingNotifies)) != NULL)
+       free(DLE_VAL(p));
+}
+
diff --git a/src/backend/commands/async.h b/src/backend/commands/async.h
new file mode 100644 (file)
index 0000000..971af19
--- /dev/null
@@ -0,0 +1,33 @@
+/*-------------------------------------------------------------------------
+ *
+ * async.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        ASYNC_H
+#define        ASYNC_H
+
+#include "nodes/memnodes.h"
+
+#if defined(PORTNAME_linux)
+extern void Async_NotifyHandler(int);
+#else
+extern void Async_NotifyHandler(void);
+#endif
+extern void Async_Notify(char *relname);
+extern void Async_NotifyAtCommit(void);
+extern void Async_NotifyAtAbort(void);
+extern void Async_Listen(char *relname, int pid);
+extern void Async_Unlisten(char *relname, int pid);
+extern void Async_UnlistenOnExit(int code, char *relname);
+
+extern GlobalMemory notifyContext;
+extern void Async_NotifyFrontEnd(void);
+
+#endif /* ASYNC_H */
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
new file mode 100644 (file)
index 0000000..c8f9118
--- /dev/null
@@ -0,0 +1,370 @@
+/*-------------------------------------------------------------------------
+ *
+ * cluster.c--
+ *    Paul Brown's implementation of cluster index. 
+ *
+ *    I am going to use the rename function as a model for this in the
+ *    parser and executor, and the vacuum code as an example in this
+ *    file. As I go - in contrast to the rest of postgres - there will
+ *    be BUCKETS of comments. This is to allow reviewers to understand
+ *    my (probably bogus) assumptions about the way this works.
+ *                                                     [pbrown '94]
+ *
+ * Copyright (c) 1994-5, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include <stdio.h>
+
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+
+#include "access/attnum.h"
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/htup.h"
+#include "access/itup.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "access/xact.h"
+#include "utils/tqual.h"
+
+#include "catalog/catname.h"
+#include "utils/syscache.h"
+#include "catalog/index.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_type.h"
+
+#include "commands/copy.h"
+#include "commands/cluster.h"
+#include "commands/rename.h"
+
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/itemptr.h"
+
+#include "miscadmin.h"
+#include "tcop/dest.h"
+#include "commands/command.h"
+
+#include "utils/builtins.h"
+#include "utils/excid.h"
+#include "utils/elog.h"
+#include "utils/mcxt.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_class.h"
+
+#include "optimizer/internal.h"
+
+#ifndef NO_SECURITY
+#include "utils/acl.h"
+#include "utils/syscache.h"
+#endif /* !NO_SECURITY */
+
+/*
+ * cluster
+ *
+ *   Check that the relation is a relation in the appropriate user
+ *   ACL. I will use the same security that limits users on the
+ *   renamerel() function.
+ *
+ *   Check that the index specified is appropriate for the task
+ *   ( ie it's an index over this relation ). This is trickier.
+ *
+ *   Create a list of all the other indicies on this relation. Because
+ *   the cluster will wreck all the tids, I'll need to destroy bogus
+ *   indicies. The user will have to re-create them. Not nice, but
+ *   I'm not a nice guy. The alternative is to try some kind of post
+ *   destroy re-build. This may be possible. I'll check out what the
+ *   index create functiond want in the way of paramaters. On the other
+ *   hand, re-creating n indicies may blow out the space. 
+ *
+ *   Create new (temporary) relations for the base heap and the new 
+ *   index. 
+ *  
+ *   Exclusively lock the relations.
+ * 
+ *   Create new clustered index and base heap relation.
+ *
+ */
+void
+cluster(char oldrelname[], char oldindexname[])
+{
+    Oid OIDOldHeap, OIDOldIndex, OIDNewHeap;
+    
+    Relation OldHeap, OldIndex;
+    Relation NewHeap;
+    
+    char *NewIndexName;
+    char *szNewHeapName;
+    
+    /*
+     *
+     * I'm going to force all checking back into the commands.c function.
+     *
+     * Get the list if indicies for this relation. If the index we want
+     * is among them, do not add it to the 'kill' list, as it will be
+     * handled by the 'clean up' code which commits this transaction.
+     *
+     * I'm not using the SysCache, because this will happen but
+     * once, and the slow way is the sure way in this case.
+     *
+     */
+    /*
+     * Like vacuum, cluster spans transactions, so I'm going to handle it in
+     * the same way.
+     */
+    
+    /* matches the StartTransaction in PostgresMain() */
+    
+    OldHeap = heap_openr(oldrelname);
+    if (!RelationIsValid(OldHeap)) {
+       elog(WARN, "cluster: unknown relation: \"%-.*s\"",
+            NAMEDATALEN, oldrelname);
+    }
+    OIDOldHeap = OldHeap->rd_id; /* Get OID for the index scan   */
+    
+    OldIndex=index_openr(oldindexname);/* Open old index relation  */
+    if (!RelationIsValid(OldIndex)) {
+       elog(WARN, "cluster: unknown index: \"%-.*s\"",
+            NAMEDATALEN, oldindexname);
+    }
+    OIDOldIndex = OldIndex->rd_id;     /* OID for the index scan         */
+    
+    heap_close(OldHeap);
+    index_close(OldIndex);
+    
+    /*
+     * I need to build the copies of the heap and the index. The Commit()
+     * between here is *very* bogus. If someone is appending stuff, they will
+     * get the lock after being blocked and add rows which won't be present in
+     * the new table. Bleagh! I'd be best to try and ensure that no-one's
+     * in the tables for the entire duration of this process with a pg_vlock.
+     */
+    NewHeap    = copy_heap(OIDOldHeap);
+    OIDNewHeap = NewHeap->rd_id;
+    szNewHeapName = pstrdup(NewHeap->rd_rel->relname.data);
+
+     /* Need to do this to make the new heap visible. */
+    CommandCounterIncrement();
+    
+    rebuildheap(OIDNewHeap, OIDOldHeap, OIDOldIndex);
+    
+    /* Need to do this to make the new heap visible. */
+    CommandCounterIncrement();
+
+    /* can't be found in the SysCache. */
+    copy_index(OIDOldIndex, OIDNewHeap); /* No contention with the old */
+    
+    /* 
+     * make this really happen. Flush all the buffers.
+     */
+    CommitTransactionCommand();
+    StartTransactionCommand();
+    
+    /*
+     * Questionable bit here. Because the renamerel destroys all trace of the
+     * pre-existing relation, I'm going to Destroy old, and then rename new
+     * to old. If this fails, it fails, and you lose your old. Tough - say
+     * I. Have good backups!
+     */
+
+    /*
+       Here lies the bogosity. The RelationNameGetRelation returns a bad
+       list of TupleDescriptors. Damn. Can't work out why this is.
+       */
+    
+    heap_destroy(oldrelname); /* AAAAAAAAGH!! */
+    
+    CommandCounterIncrement();
+    
+    /*
+     * The Commit flushes all palloced memory, so I have to grab the 
+     * New stuff again. This is annoying, but oh heck!
+     */
+/*
+    renamerel(szNewHeapName.data, oldrelname);
+    TypeRename(&szNewHeapName, &szOldRelName);
+    
+    sprintf(NewIndexName.data, "temp_%x", OIDOldIndex);
+    renamerel(NewIndexName.data, szOldIndexName.data);
+*/
+    NewIndexName = palloc(NAMEDATALEN+1); /* XXX */
+    sprintf(NewIndexName, "temp_%x", OIDOldIndex);
+    renamerel(NewIndexName, oldindexname);
+}
+
+Relation
+copy_heap(Oid OIDOldHeap)
+{
+    char NewName[NAMEDATALEN];
+    TupleDesc OldHeapDesc, tupdesc;
+    Oid OIDNewHeap;
+    Relation NewHeap, OldHeap;
+
+    /*
+     *  Create a new heap relation with a temporary name, which has the
+     *  same tuple description as the old one.
+     */
+    sprintf(NewName,"temp_%x", OIDOldHeap);
+
+    OldHeap= heap_open(OIDOldHeap);
+    OldHeapDesc= RelationGetTupleDescriptor(OldHeap);
+
+    /*
+     * Need to make a copy of the tuple descriptor, heap_create modifies
+     * it.
+     */
+
+    tupdesc = CreateTupleDescCopy(OldHeapDesc);
+    
+    OIDNewHeap=heap_create(NewName,
+                          NULL,
+                          OldHeap->rd_rel->relarch,
+                          OldHeap->rd_rel->relsmgr,
+                          tupdesc);
+
+    if (!OidIsValid(OIDNewHeap))
+       elog(WARN,"clusterheap: cannot create temporary heap relation\n");
+
+    NewHeap=heap_open(OIDNewHeap);
+
+    heap_close(NewHeap);
+    heap_close(OldHeap);
+
+    return NewHeap;
+}
+
+void
+copy_index(Oid OIDOldIndex, Oid OIDNewHeap)
+{
+    Relation OldIndex, NewHeap;
+    HeapTuple Old_pg_index_Tuple, Old_pg_index_relation_Tuple, pg_proc_Tuple;
+    IndexTupleForm        Old_pg_index_Form;
+    Form_pg_class        Old_pg_index_relation_Form;
+    Form_pg_proc          pg_proc_Form;
+    char                 *NewIndexName;
+    AttrNumber                  *attnumP;
+    int                   natts;
+    FuncIndexInfo *       finfo;
+
+    NewHeap  = heap_open(OIDNewHeap);
+    OldIndex = index_open(OIDOldIndex);
+
+    /*
+     * OK. Create a new (temporary) index for the one that's already
+     * here. To do this I get the info from pg_index, re-build the
+     * FunctInfo if I have to, and add a new index with a temporary
+     * name.
+     */
+    Old_pg_index_Tuple =
+       SearchSysCacheTuple(INDEXRELID,
+                           ObjectIdGetDatum(OldIndex->rd_id),
+                           0,0,0);
+
+    Assert(Old_pg_index_Tuple);
+    Old_pg_index_Form = (IndexTupleForm)GETSTRUCT(Old_pg_index_Tuple);
+
+    Old_pg_index_relation_Tuple =
+       SearchSysCacheTuple(RELOID,
+                           ObjectIdGetDatum(OldIndex->rd_id),
+                           0,0,0);
+
+    Assert(Old_pg_index_relation_Tuple);
+    Old_pg_index_relation_Form =
+       (Form_pg_class)GETSTRUCT(Old_pg_index_relation_Tuple);
+
+     NewIndexName = palloc(NAMEDATALEN+1);  /* XXX */
+     sprintf(NewIndexName, "temp_%x", OIDOldIndex); /* Set the name. */
+
+    /*
+     * Ugly as it is, the only way I have of working out the number of
+     * attribues is to count them. Mostly there'll be just one but 
+     * I've got to be sure.
+     */
+    for (attnumP = &(Old_pg_index_Form->indkey[0]), natts = 0;
+        *attnumP != InvalidAttrNumber;
+        attnumP++, natts++);
+
+    /*
+     * If this is a functional index, I need to rebuild the functional
+     * component to pass it to the defining procedure.
+     */
+    if (Old_pg_index_Form->indproc != InvalidOid) {
+       FIgetnArgs(finfo) = natts;
+       FIgetProcOid(finfo) = Old_pg_index_Form->indproc;
+
+       pg_proc_Tuple =
+           SearchSysCacheTuple(PROOID,
+                               ObjectIdGetDatum(Old_pg_index_Form->indproc),
+                               0,0,0);
+
+       Assert(pg_proc_Tuple);
+       pg_proc_Form = (Form_pg_proc)GETSTRUCT(pg_proc_Tuple);
+       namecpy(&(finfo->funcName), &(pg_proc_Form->proname));
+    } else {
+       finfo = (FuncIndexInfo *) NULL;
+       natts = 1;
+    }
+
+    index_create((NewHeap->rd_rel->relname).data,
+                NewIndexName, 
+                finfo,
+                Old_pg_index_relation_Form->relam,
+                natts, 
+                Old_pg_index_Form->indkey,
+                Old_pg_index_Form->indclass,
+                (uint16)0, (Datum) NULL, NULL);
+
+    heap_close(OldIndex);
+    heap_close(NewHeap);
+}
+
+
+void
+rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
+{
+    Relation              LocalNewHeap, LocalOldHeap, LocalOldIndex;
+    IndexScanDesc         ScanDesc;
+    RetrieveIndexResult   ScanResult;
+    ItemPointer           HeapTid;
+    HeapTuple             LocalHeapTuple;
+    Buffer                LocalBuffer;
+    Oid                  OIDNewHeapInsert;
+
+    /*
+     * Open the relations I need. Scan through the OldHeap on the OldIndex and
+     * insert each tuple into the NewHeap.
+     */
+    LocalNewHeap=(Relation)heap_open(OIDNewHeap);
+    LocalOldHeap=(Relation)heap_open(OIDOldHeap);
+    LocalOldIndex=(Relation)index_open(OIDOldIndex);
+
+    ScanDesc=index_beginscan(LocalOldIndex, false, 0, (ScanKey) NULL);
+
+    while ((ScanResult =
+           index_getnext(ScanDesc, ForwardScanDirection)) != NULL) {
+
+       HeapTid = &ScanResult->heap_iptr;
+       LocalHeapTuple = heap_fetch(LocalOldHeap, 0, HeapTid, &LocalBuffer);
+       OIDNewHeapInsert =
+           heap_insert(LocalNewHeap, LocalHeapTuple);
+       pfree(ScanResult);
+       ReleaseBuffer(LocalBuffer);
+    }
+
+    index_close(LocalOldIndex);
+    heap_close(LocalOldHeap);
+    heap_close(LocalNewHeap);
+}
+
diff --git a/src/backend/commands/cluster.h b/src/backend/commands/cluster.h
new file mode 100644 (file)
index 0000000..9f5ba50
--- /dev/null
@@ -0,0 +1,30 @@
+/*-------------------------------------------------------------------------
+ *
+ * cluster.h--
+ *    header file for postgres cluster command stuff 
+ *
+ * Copyright (c) 1994-5, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        CLUSTER_H
+#define        CLUSTER_H
+
+/*
+ * defines for contant stuff
+ */
+#define _TEMP_RELATION_KEY_            "clXXXXXXXX"
+#define _SIZE_OF_TEMP_RELATION_KEY_    11
+
+
+/*
+ * functions
+ */
+extern void cluster(char oldrelname[], char oldindexname[]);
+extern Relation copy_heap(Oid OIDOldHeap);
+extern void copy_index(Oid OIDOldIndex, Oid OIDNewHeap);
+extern void rebuildheap(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
+
+#endif /* CLUSTER_H */
diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c
new file mode 100644 (file)
index 0000000..3972fdc
--- /dev/null
@@ -0,0 +1,511 @@
+/*-------------------------------------------------------------------------
+ *
+ * command.c--
+ *    random postgres portal and utility support code
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    The PortalExecutorHeapMemory crap needs to be eliminated
+ *    by designing a better executor / portal processing memory
+ *    interface.
+ *     
+ *    The PerformAddAttribute() code, like most of the relation
+ *    manipulating code in the commands/ directory, should go
+ *    someplace closer to the lib/catalog code.
+ *     
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+
+#include "access/attnum.h"
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "utils/builtins.h"
+#include "utils/tqual.h"
+
+#include "commands/copy.h"
+
+#include "storage/buf.h"
+#include "storage/itemptr.h"
+
+#include "miscadmin.h"
+
+#include "utils/portal.h"
+#include "utils/excid.h"
+#include "utils/elog.h"
+#include "utils/mcxt.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
+#include "tcop/dest.h"
+#include "commands/command.h"
+
+#include "catalog/catalog.h"
+#include "catalog/catname.h"
+#include "utils/syscache.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_type.h"
+#include "catalog/indexing.h"
+
+#include "executor/executor.h"
+#include "executor/execdefs.h"
+#include "executor/execdesc.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/prep.h"    /* for find_all_inheritors */
+
+
+#ifndef NO_SECURITY
+#include "miscadmin.h"
+#include "utils/acl.h"
+#include "utils/syscache.h"
+#endif /* !NO_SECURITY */
+
+/* ----------------
+ *     PortalExecutorHeapMemory stuff
+ *
+ *     This is where the XXXSuperDuperHacky code was. -cim 3/15/90
+ * ----------------
+ */
+MemoryContext PortalExecutorHeapMemory  = NULL;
+
+/* --------------------------------
+ *     PortalCleanup
+ * --------------------------------
+ */
+void
+PortalCleanup(Portal portal)
+{
+    MemoryContext      context;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    AssertArg(PortalIsValid(portal));
+    AssertArg(portal->cleanup == PortalCleanup);
+    
+    /* ----------------
+     * set proper portal-executor context before calling ExecMain.
+     * ----------------
+     */
+    context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal));
+    PortalExecutorHeapMemory = (MemoryContext)
+       PortalGetHeapMemory(portal);
+    
+    /* ----------------
+     * tell the executor to shutdown the query
+     * ----------------
+     */
+    ExecutorEnd(PortalGetQueryDesc(portal), PortalGetState(portal));
+    
+    /* ----------------
+     * switch back to previous context
+     * ----------------
+     */
+    (void) MemoryContextSwitchTo(context);
+    PortalExecutorHeapMemory = (MemoryContext) NULL;
+}
+
+/* --------------------------------
+ *     PerformPortalFetch
+ * --------------------------------
+ */
+void
+PerformPortalFetch(char *name,
+                  bool forward,
+                  int count,
+                  char *tag,
+                  CommandDest dest)
+{
+    Portal             portal;
+    int                        feature;
+    QueryDesc          *queryDesc;
+    MemoryContext      context;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    if (name == NULL) {
+       elog(NOTICE, "PerformPortalFetch: blank portal unsupported");
+       return;
+    }
+    
+    /* ----------------
+     * get the portal from the portal name
+     * ----------------
+     */
+    portal = GetPortalByName(name);
+    if (! PortalIsValid(portal)) {
+       elog(NOTICE, "PerformPortalFetch: portal \"%-.*s\" not found",
+            NAMEDATALEN, name);
+       return;
+    }
+    
+    /* ----------------
+     *         switch into the portal context
+     * ----------------
+     */
+    context= MemoryContextSwitchTo((MemoryContext)PortalGetHeapMemory(portal));
+    
+    AssertState(context ==
+               (MemoryContext)PortalGetHeapMemory(GetPortalByName(NULL)));
+    
+    /* ----------------
+     *  setup "feature" to tell the executor what direction and
+     *  how many tuples to fetch.
+     * ----------------
+     */
+    if (forward) 
+       feature = EXEC_FOR;
+    else
+       feature = EXEC_BACK;
+    
+    /* ----------------
+     * tell the destination to prepare to recieve some tuples
+     * ----------------
+     */
+    queryDesc = PortalGetQueryDesc(portal);
+    BeginCommand(name,
+                queryDesc->operation,
+                portal->attinfo,/* QueryDescGetTypeInfo(queryDesc), */
+                false, /* portal fetches don't end up in relations */
+                false, /* this is a portal fetch, not a "retrieve portal" */
+                tag,
+                dest);
+    
+    /* ----------------
+     * execute the portal fetch operation
+     * ----------------
+     */
+    PortalExecutorHeapMemory = (MemoryContext)
+       PortalGetHeapMemory(portal);
+    
+    ExecutorRun(queryDesc, PortalGetState(portal), feature, count);
+    
+    /* ----------------
+     * Note: the "end-of-command" tag is returned by higher-level
+     *      utility code
+     *
+     * Return blank portal for now.
+     * Otherwise, this named portal will be cleaned.
+     * Note: portals will only be supported within a BEGIN...END
+     * block in the near future.  Later, someone will fix it to
+     * do what is possible across transaction boundries.
+     * ----------------
+     */
+    (void) MemoryContextSwitchTo(
+       (MemoryContext)PortalGetHeapMemory(GetPortalByName(NULL)));
+}
+
+/* --------------------------------
+ *     PerformPortalClose
+ * --------------------------------
+ */
+void
+PerformPortalClose(char *name, CommandDest dest)
+{
+    Portal     portal;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    if (name == NULL) {
+       elog(NOTICE, "PerformPortalClose: blank portal unsupported");
+       return;
+    }
+    
+    /* ----------------
+     * get the portal from the portal name
+     * ----------------
+     */
+    portal = GetPortalByName(name);
+    if (! PortalIsValid(portal)) {
+       elog(NOTICE, "PerformPortalClose: portal \"%-.*s\" not found",
+            NAMEDATALEN, name);
+       return;
+    }
+    
+    /* ----------------
+     * Note: PortalCleanup is called as a side-effect
+     * ----------------
+     */
+    PortalDestroy(&portal);
+}
+
+/* ----------------
+ *     PerformAddAttribute
+ *
+ *     adds an additional attribute to a relation
+ *
+ *     Adds attribute field(s) to a relation.  Each new attribute
+ *     is given attnums in sequential order and is added to the
+ *     ATTRIBUTE relation.  If the AMI fails, defunct tuples will
+ *     remain in the ATTRIBUTE relation for later vacuuming.
+ *     Later, there may be some reserved attribute names???
+ *
+ *     (If needed, can instead use elog to handle exceptions.)
+ *
+ *     Note:
+ *             Initial idea of ordering the tuple attributes so that all
+ *     the variable length domains occured last was scratched.  Doing
+ *     so would not speed access too much (in general) and would create
+ *     many complications in formtuple, amgetattr, and addattribute.
+ *
+ *     scan attribute catalog for name conflict (within rel)
+ *     scan type catalog for absence of data type (if not arg)
+ *     create attnum magically???
+ *     create attribute tuple
+ *     insert attribute in attribute catalog
+ *     modify reldesc
+ *     create new relation tuple
+ *     insert new relation in relation catalog
+ *     delete original relation from relation catalog
+ * ----------------
+ */
+void
+PerformAddAttribute(char *relationName,
+                   char *userName,
+                   bool inherits,
+                   ColumnDef *colDef)
+{      
+    Relation           relrdesc, attrdesc;
+    HeapScanDesc       attsdesc;
+    HeapTuple          reltup;
+    HeapTuple          attributeTuple;
+    AttributeTupleForm attribute;
+    FormData_pg_attribute attributeD;
+    int                        i;
+    int                        minattnum, maxatts;
+    HeapTuple          tup;
+    ScanKeyData                key[2];
+    ItemPointerData    oldTID;
+    Relation           idescs[Num_pg_attr_indices];
+    Relation           ridescs[Num_pg_class_indices];
+    bool               hasindex;
+    
+    /*
+     * permissions checking.  this would normally be done in utility.c,
+     * but this particular routine is recursive.
+     *
+     * normally, only the owner of a class can change its schema.
+     */
+    if (IsSystemRelationName(relationName))
+       elog(WARN, "PerformAddAttribute: class \"%-.*s\" is a system catalog",
+            NAMEDATALEN, relationName);
+#ifndef NO_SECURITY
+    if (!pg_ownercheck(userName, relationName, RELNAME))
+       elog(WARN, "PerformAddAttribute: you do not own class \"%s\"",
+            relationName);
+#endif
+    
+    /*
+     * if the first element in the 'schema' list is a "*" then we are
+     * supposed to add this attribute to all classes that inherit from
+     * 'relationName' (as well as to 'relationName').
+     *
+     * any permissions or problems with duplicate attributes will cause
+     * the whole transaction to abort, which is what we want -- all or
+     * nothing.
+     */
+    if (colDef != NULL) {
+       if (inherits) {
+           Oid myrelid, childrelid;
+           List *child, *children;
+           
+           relrdesc = heap_openr(relationName);
+           if (!RelationIsValid(relrdesc)) {
+               elog(WARN, "PerformAddAttribute: unknown relation: \"%-.*s\"",
+                    NAMEDATALEN, relationName);
+           }
+           myrelid = relrdesc->rd_id;
+           heap_close(relrdesc);
+           
+           /* this routine is actually in the planner */
+           children = find_all_inheritors(lconsi(myrelid,NIL), NIL);
+
+           /*
+            * find_all_inheritors does the recursive search of the
+            * inheritance hierarchy, so all we have to do is process
+            * all of the relids in the list that it returns.
+            */
+           foreach (child, children) {
+               childrelid = lfirsti(child);
+               if (childrelid == myrelid)
+                   continue;
+               relrdesc = heap_open(childrelid);
+               if (!RelationIsValid(relrdesc)) {
+                   elog(WARN, "PerformAddAttribute: can't find catalog entry for inheriting class with oid %d",
+                        childrelid);
+               }
+               PerformAddAttribute((relrdesc->rd_rel->relname).data,
+                                   userName, false, colDef);
+               heap_close(relrdesc);
+           }
+       }
+    }
+    
+    relrdesc = heap_openr(RelationRelationName);
+    reltup = ClassNameIndexScan(relrdesc, relationName);
+    
+    if (!PointerIsValid(reltup)) {
+       heap_close(relrdesc);
+       elog(WARN, "PerformAddAttribute: relation \"%s\" not found",
+            relationName);
+    }
+    /*
+     * XXX is the following check sufficient?
+     */
+    if (((Form_pg_class) GETSTRUCT(reltup))->relkind == RELKIND_INDEX) {
+       elog(WARN, "PerformAddAttribute: index relation \"%s\" not changed",
+            relationName);
+       return;
+    }
+    
+    minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts;
+    maxatts = minattnum + 1;
+    if (maxatts > MaxHeapAttributeNumber) {
+       pfree(reltup);                          /* XXX temp */
+       heap_close(relrdesc);                   /* XXX temp */
+       elog(WARN, "PerformAddAttribute: relations limited to %d attributes",
+            MaxHeapAttributeNumber);
+       return;
+    }
+    
+    attrdesc = heap_openr(AttributeRelationName);
+    
+    Assert(attrdesc);
+    Assert(RelationGetRelationTupleForm(attrdesc));
+    
+    /*
+     * Open all (if any) pg_attribute indices
+     */
+    hasindex = RelationGetRelationTupleForm(attrdesc)->relhasindex;
+    if (hasindex)
+       CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
+    
+    ScanKeyEntryInitialize(&key[0], 
+                          (bits16) NULL,
+                          (AttrNumber) Anum_pg_attribute_attrelid,
+                          (RegProcedure)ObjectIdEqualRegProcedure,
+                          (Datum) reltup->t_oid);
+    
+    ScanKeyEntryInitialize(&key[1],
+                          (bits16) NULL,
+                          (AttrNumber) Anum_pg_attribute_attname,
+                           (RegProcedure)NameEqualRegProcedure,
+                          (Datum) NULL);
+    
+    attributeD.attrelid = reltup->t_oid;
+    attributeD.attdefrel = InvalidOid; /* XXX temporary */
+    attributeD.attnvals = 0;           /* XXX temporary */
+    attributeD.atttyparg = InvalidOid; /* XXX temporary */
+    attributeD.attbound = 0;           /* XXX temporary */
+    attributeD.attcanindex = 0;                /* XXX need this info */
+    attributeD.attproc = InvalidOid;   /* XXX tempoirary */
+    attributeD.attcacheoff = -1;
+    
+    attributeTuple = heap_addheader(Natts_pg_attribute,
+                                   sizeof attributeD,
+                                   (char *)&attributeD);
+    
+    attribute = (AttributeTupleForm)GETSTRUCT(attributeTuple);
+    
+    i = 1 + minattnum;
+
+    {
+       HeapTuple       typeTuple;
+       TypeTupleForm   form;
+       char *p;
+       int attnelems;
+       
+       /*
+        * XXX use syscache here as an optimization
+        */
+       key[1].sk_argument = (Datum)colDef->colname;
+       attsdesc = heap_beginscan(attrdesc, 0, NowTimeQual, 2, key); 
+
+
+       tup = heap_getnext(attsdesc, 0, (Buffer *) NULL);
+       if (HeapTupleIsValid(tup)) {
+           pfree(reltup);                      /* XXX temp */
+           heap_endscan(attsdesc);             /* XXX temp */
+           heap_close(attrdesc);               /* XXX temp */
+           heap_close(relrdesc);               /* XXX temp */
+           elog(WARN, "PerformAddAttribute: attribute \"%s\" already exists in class \"%s\"",
+                key[1].sk_argument,
+                relationName);
+           return;
+       }
+       heap_endscan(attsdesc);
+       
+       /*
+        * check to see if it is an array attribute.
+        */
+       
+       p = colDef->typename->name;
+       
+       if (colDef->typename->arrayBounds)
+           {
+               attnelems = length(colDef->typename->arrayBounds);
+               p = makeArrayTypeName(colDef->typename->name);
+           }
+       else
+           attnelems = 0;
+       
+       typeTuple = SearchSysCacheTuple(TYPNAME, 
+                                       PointerGetDatum(p),
+                                       0,0,0);
+       form = (TypeTupleForm)GETSTRUCT(typeTuple);
+       
+       if (!HeapTupleIsValid(typeTuple)) {
+           elog(WARN, "Add: type \"%s\" nonexistant", p);
+       }
+       namestrcpy(&(attribute->attname), (char*) key[1].sk_argument);
+       attribute->atttypid = typeTuple->t_oid;
+       attribute->attlen = form->typlen;
+       attribute->attnum = i;
+       attribute->attbyval = form->typbyval;
+       attribute->attnelems = attnelems;
+       attribute->attcacheoff = -1;
+       attribute->attisset = (bool) (form->typtype == 'c');
+       attribute->attalign = form->typalign;
+       
+       heap_insert(attrdesc, attributeTuple);
+       if (hasindex)
+           CatalogIndexInsert(idescs,
+                              Num_pg_attr_indices,
+                              attrdesc,
+                              attributeTuple);
+    }
+
+    if (hasindex)
+       CatalogCloseIndices(Num_pg_attr_indices, idescs);
+    heap_close(attrdesc);
+    
+    ((Form_pg_class) GETSTRUCT(reltup))->relnatts = maxatts;
+    oldTID = reltup->t_ctid;
+    (void) heap_replace(relrdesc, &oldTID, reltup);
+    
+    /* keep catalog indices current */
+    CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
+    CatalogIndexInsert(ridescs, Num_pg_class_indices, relrdesc, reltup);
+    CatalogCloseIndices(Num_pg_class_indices, ridescs);
+    
+    pfree(reltup);
+    heap_close(relrdesc);
+}
diff --git a/src/backend/commands/command.h b/src/backend/commands/command.h
new file mode 100644 (file)
index 0000000..a57cbdf
--- /dev/null
@@ -0,0 +1,56 @@
+/*-------------------------------------------------------------------------
+ *
+ * command.h--
+ *    prototypes for command.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef COMMAND_H
+#define COMMAND_H
+
+#include "utils/portal.h"
+#include "tcop/dest.h"
+
+extern MemoryContext PortalExecutorHeapMemory;
+
+/*
+ * PortalCleanup --
+ *     Cleans up the query state of the portal.
+ *
+ * Exceptions:
+ *     BadArg if portal invalid.
+ */
+extern void PortalCleanup(Portal portal);
+
+
+/*
+ * PerformPortalFetch --
+ *     Performs the POSTQUEL function FETCH.  Fetches count (or all if 0)
+ * tuples in portal with name in the forward direction iff goForward.
+ *
+ * Exceptions:
+ *     BadArg if forward invalid.
+ *     "WARN" if portal not found.
+ */
+extern void PerformPortalFetch(char *name, bool forward, int count,
+                              char *tag, CommandDest dest);
+
+/*
+ * PerformPortalClose --
+ *     Performs the POSTQUEL function CLOSE.
+ */
+extern void PerformPortalClose(char *name, CommandDest dest);
+
+/*
+ * PerformAddAttribute --
+ *     Performs the POSTQUEL function ADD.
+ */
+extern void PerformAddAttribute(char *relationName, char *userName,
+                               bool inh, ColumnDef *colDef);
+
+#endif /* COMMAND_H */
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
new file mode 100644 (file)
index 0000000..88bd85e
--- /dev/null
@@ -0,0 +1,782 @@
+/*-------------------------------------------------------------------------
+ *
+ * copy.c--
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <sys/types.h> /* for mode_t */
+#include <sys/stat.h>  /* for umask(2) prototype */
+
+#include "postgres.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/syscache.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_index.h"
+#include "catalog/index.h"
+
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/itup.h"
+#include "access/relscan.h"
+#include "access/funcindex.h"
+#include "access/tupdesc.h"
+#include "nodes/execnodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/pg_list.h"
+#include "executor/tuptable.h"
+#include "executor/executor.h"
+#include "utils/rel.h"
+#include "utils/elog.h"
+#include "utils/memutils.h"
+#include "utils/palloc.h"
+#include "fmgr.h"
+#include "machine.h"
+
+/*
+ * New copy code.
+ * 
+ * This code "knows" the following about tuples:
+ * 
+ */
+
+static bool reading_from_input = false;
+
+/* non-export function prototypes */
+static void CopyTo(Relation rel, bool binary, FILE *fp, char *delim);
+static void CopyFrom(Relation rel, bool binary, FILE *fp, char *delim);
+static Oid GetOutputFunction(Oid type);
+static Oid GetTypeElement(Oid type);
+static Oid GetInputFunction(Oid type);
+static Oid IsTypeByVal(Oid type);
+static void GetIndexRelations(Oid main_relation_oid,
+                             int *n_indices,
+                             Relation **index_rels);
+static char *CopyReadAttribute(int attno, FILE *fp, bool *isnull, char *delim);
+static void CopyAttributeOut(FILE *fp, char *string, char *delim);
+static int CountTuples(Relation relation);
+
+extern FILE *Pfout, *Pfin;
+
+void
+DoCopy(char *relname, bool binary, bool from, bool pipe, char *filename, 
+       char *delim)
+{
+    FILE *fp;
+    Relation rel;
+    reading_from_input = pipe;
+    
+    rel = heap_openr(relname);
+    if (rel == NULL) elog(WARN, "Copy: class %s does not exist.", relname);
+    
+    if (from) {
+       if (pipe && IsUnderPostmaster) ReceiveCopyBegin();
+       if (IsUnderPostmaster) {
+           fp = pipe ? Pfin : fopen(filename, "r");
+       }else {
+           fp = pipe ? stdin : fopen(filename, "r");
+       }
+       if (fp == NULL) {
+           elog(WARN, "COPY: file %s could not be open for reading", filename);
+       }
+       CopyFrom(rel, binary, fp, delim);
+    }else {
+       
+       mode_t oumask = umask((mode_t) 0);
+       
+       if (pipe && IsUnderPostmaster) SendCopyBegin();
+       if (IsUnderPostmaster) {
+           fp = pipe ? Pfout : fopen(filename, "w");
+           
+       }else {
+           fp = pipe ? stdout : fopen(filename, "w");
+       }
+       (void) umask(oumask);
+       if (fp == NULL)  {
+           elog(WARN, "COPY: file %s could not be open for writing", filename);
+       }
+       CopyTo(rel, binary, fp, delim);
+    }
+    if (!pipe) {
+       fclose(fp);
+    }else if (!from && !binary) {
+       fputs(".\n", fp);
+       if (IsUnderPostmaster) fflush(Pfout);
+    }
+}
+
+static void
+CopyTo(Relation rel, bool binary, FILE *fp, char *delim)
+{
+    HeapTuple tuple;
+    HeapScanDesc scandesc;
+    
+    int32 attr_count, i;
+    AttributeTupleForm *attr;
+    func_ptr *out_functions;
+    int dummy;
+    Oid out_func_oid;
+    Oid *elements;
+    Datum value;
+    bool isnull = (bool) true;
+    char *nulls;
+    char *string;
+    int32 ntuples;
+    TupleDesc tupDesc;
+    
+    scandesc = heap_beginscan(rel, 0, NULL, 0, NULL);
+
+    attr_count = rel->rd_att->natts;
+    attr = rel->rd_att->attrs;
+    tupDesc = rel->rd_att;
+
+    if (!binary) {
+       out_functions = (func_ptr *)
+           palloc(attr_count * sizeof(func_ptr));
+       elements = (Oid *) palloc(attr_count * sizeof(Oid));
+       for (i = 0; i < attr_count; i++) {
+           out_func_oid = (Oid) GetOutputFunction(attr[i]->atttypid);
+           fmgr_info(out_func_oid, &out_functions[i], &dummy);
+           elements[i] = GetTypeElement(attr[i]->atttypid);
+       }
+    }else {
+       nulls = (char *) palloc(attr_count);
+       for (i = 0; i < attr_count; i++) nulls[i] = ' ';
+       
+       /* XXX expensive */
+       
+       ntuples = CountTuples(rel);
+       fwrite(&ntuples, sizeof(int32), 1, fp);
+    }
+    
+    for (tuple = heap_getnext(scandesc, 0, NULL);
+         tuple != NULL; 
+         tuple = heap_getnext(scandesc, 0, NULL)) {
+       
+       for (i = 0; i < attr_count; i++) {
+           value = (Datum) 
+               heap_getattr(tuple, InvalidBuffer, i+1, tupDesc, &isnull);
+           if (!binary) {
+               if (!isnull) {
+                   string = (char *) (out_functions[i]) (value, elements[i]);
+                   CopyAttributeOut(fp, string, delim);
+                   pfree(string);
+               }
+               if (i == attr_count - 1) {
+                   fputc('\n', fp);
+               }else {
+                   /* when copying out, only use the first char of the delim
+                      string */
+                   fputc(delim[0], fp);
+               }
+           }else {
+               /*
+                * only interesting thing heap_getattr tells us in this case
+                * is if we have a null attribute or not.
+                */
+               if (isnull) nulls[i] = 'n';
+           }
+       }
+       
+       if (binary) {
+           int32 null_ct = 0, length;
+           
+           for (i = 0; i < attr_count; i++) {
+               if (nulls[i] == 'n') null_ct++;
+           }
+           
+           length = tuple->t_len - tuple->t_hoff;
+           fwrite(&length, sizeof(int32), 1, fp);
+           fwrite(&null_ct, sizeof(int32), 1, fp);
+           if (null_ct > 0) {
+               for (i = 0; i < attr_count; i++) {
+                   if (nulls[i] == 'n') {
+                       fwrite(&i, sizeof(int32), 1, fp);
+                       nulls[i] = ' ';
+                   }
+               }
+           }
+           fwrite((char *) tuple + tuple->t_hoff, length, 1, fp);
+       }
+    }
+    
+    heap_endscan(scandesc);
+    if (binary) {
+       pfree(nulls);
+    }else {
+       pfree(out_functions);
+       pfree(elements);
+    }
+    
+    heap_close(rel);
+}
+
+static void
+CopyFrom(Relation rel, bool binary, FILE *fp, char *delim)
+{
+    HeapTuple tuple;
+    IndexTuple ituple;
+    AttrNumber attr_count;
+    AttributeTupleForm *attr;
+    func_ptr *in_functions;
+    int i, dummy;
+    Oid in_func_oid;
+    Datum *values;
+    char *nulls, *index_nulls;
+    bool *byval;
+    bool isnull;
+    bool has_index;
+    int done = 0;
+    char *string, *ptr;
+    Relation *index_rels;
+    int32 len, null_ct, null_id;
+    int32 ntuples, tuples_read = 0;
+    bool reading_to_eof = true;
+    Oid *elements;
+    FuncIndexInfo *finfo, **finfoP;
+    TupleDesc *itupdescArr;
+    HeapTuple pgIndexTup;
+    IndexTupleForm *pgIndexP;
+    int *indexNatts;
+    char *predString;
+    Node **indexPred;
+    TupleDesc rtupdesc;
+    ExprContext *econtext;
+    TupleTable tupleTable;
+    TupleTableSlot *slot;
+    int natts;
+    AttrNumber *attnumP;
+    Datum idatum;
+    int n_indices;
+    InsertIndexResult indexRes;
+    TupleDesc tupDesc;
+
+    tupDesc = RelationGetTupleDescriptor(rel);
+    attr = tupDesc->attrs;
+    attr_count = tupDesc->natts;
+
+    has_index = false;
+    
+    /*
+     *  This may be a scalar or a functional index.  We initialize all
+     *  kinds of arrays here to avoid doing extra work at every tuple
+     *  copy.
+     */
+    
+    if (rel->rd_rel->relhasindex) {
+        GetIndexRelations(rel->rd_id, &n_indices, &index_rels);
+       if (n_indices > 0) {
+           has_index = true;
+           itupdescArr = 
+               (TupleDesc *)palloc(n_indices * sizeof(TupleDesc));
+           pgIndexP =
+               (IndexTupleForm *)palloc(n_indices * sizeof(IndexTupleForm));
+           indexNatts = (int *) palloc(n_indices * sizeof(int));
+           finfo = (FuncIndexInfo *) palloc(n_indices * sizeof(FuncIndexInfo));
+           finfoP = (FuncIndexInfo **) palloc(n_indices * sizeof(FuncIndexInfo *));
+           indexPred = (Node **) palloc(n_indices * sizeof(Node*));
+           econtext = NULL;
+           for (i = 0; i < n_indices; i++) {
+               itupdescArr[i] = RelationGetTupleDescriptor(index_rels[i]);
+               pgIndexTup =
+                   SearchSysCacheTuple(INDEXRELID,
+                                       ObjectIdGetDatum(index_rels[i]->rd_id),
+                                       0,0,0);
+               Assert(pgIndexTup);
+               pgIndexP[i] = (IndexTupleForm)GETSTRUCT(pgIndexTup);
+               for (attnumP = &(pgIndexP[i]->indkey[0]), natts = 0;
+                    *attnumP != InvalidAttrNumber;
+                    attnumP++, natts++);
+               if (pgIndexP[i]->indproc != InvalidOid) {
+                   FIgetnArgs(&finfo[i]) = natts;
+                   natts = 1;
+                   FIgetProcOid(&finfo[i]) = pgIndexP[i]->indproc;
+                   *(FIgetname(&finfo[i])) = '\0';
+                   finfoP[i] = &finfo[i];
+               } else
+                   finfoP[i] = (FuncIndexInfo *) NULL;
+               indexNatts[i] = natts;
+               if (VARSIZE(&pgIndexP[i]->indpred) != 0) {
+                   predString = fmgr(F_TEXTOUT, &pgIndexP[i]->indpred);
+                   indexPred[i] = stringToNode(predString);
+                   pfree(predString);
+                   /* make dummy ExprContext for use by ExecQual */
+                   if (econtext == NULL) {
+#ifndef OMIT_PARTIAL_INDEX
+                       tupleTable = ExecCreateTupleTable(1); 
+                       slot =   ExecAllocTableSlot(tupleTable);
+                       econtext = makeNode(ExprContext);
+                       econtext->ecxt_scantuple = slot;
+                       rtupdesc = RelationGetTupleDescriptor(rel);
+                       slot->ttc_tupleDescriptor = rtupdesc;
+                       /*
+                        * There's no buffer associated with heap tuples here,
+                        * so I set the slot's buffer to NULL.  Currently, it
+                        * appears that the only way a buffer could be needed
+                        * would be if the partial index predicate referred to
+                        * the "lock" system attribute.  If it did, then
+                        * heap_getattr would call HeapTupleGetRuleLock, which
+                        * uses the buffer's descriptor to get the relation id.
+                        * Rather than try to fix this, I'll just disallow
+                        * partial indexes on "lock", which wouldn't be useful
+                        * anyway. --Nels, Nov '92
+                        */
+                       /* SetSlotBuffer(slot, (Buffer) NULL); */
+                       /* SetSlotShouldFree(slot, false); */
+                       slot->ttc_buffer = (Buffer)NULL;
+                       slot->ttc_shouldFree = false;
+#endif /* OMIT_PARTIAL_INDEX */    
+                   }
+               } else {
+                   indexPred[i] = NULL;
+               }
+           }
+       }
+    }
+    
+    if (!binary)
+       {
+           in_functions = (func_ptr *) palloc(attr_count * sizeof(func_ptr));
+           elements = (Oid *) palloc(attr_count * sizeof(Oid));
+           for (i = 0; i < attr_count; i++)
+               {
+                   in_func_oid = (Oid) GetInputFunction(attr[i]->atttypid);
+                   fmgr_info(in_func_oid, &in_functions[i], &dummy);
+                   elements[i] = GetTypeElement(attr[i]->atttypid);
+               }
+       }
+    else
+       {
+           fread(&ntuples, sizeof(int32), 1, fp);
+           if (ntuples != 0) reading_to_eof = false;
+       }
+    
+    values       = (Datum *) palloc(sizeof(Datum) * attr_count);
+    nulls        = (char *) palloc(attr_count);
+    index_nulls  = (char *) palloc(attr_count);
+    byval        = (bool *) palloc(attr_count * sizeof(bool));
+    
+    for (i = 0; i < attr_count; i++) {
+       nulls[i] = ' ';
+       index_nulls[i] = ' ';
+       byval[i] = (bool) IsTypeByVal(attr[i]->atttypid);
+    }
+    
+    while (!done) {
+       if (!binary) {
+           for (i = 0; i < attr_count && !done; i++) {
+               string = CopyReadAttribute(i, fp, &isnull, delim);
+               if (isnull) {
+                   values[i] = PointerGetDatum(NULL);
+                   nulls[i] = 'n';
+               }else if (string == NULL) {
+                   done = 1;
+               }else {
+                   values[i] =
+                       (Datum)(in_functions[i])(string,
+                                                elements[i],
+                                                attr[i]->attlen);
+                   /*
+                    * Sanity check - by reference attributes cannot return
+                    * NULL
+                    */
+                   if (!PointerIsValid(values[i]) &&
+                       !(rel->rd_att->attrs[i]->attbyval)) {
+                       elog(WARN, "copy from: Bad file format");
+                   }
+               }
+           }
+       }else { /* binary */
+           fread(&len, sizeof(int32), 1, fp);
+           if (feof(fp)) {
+               done = 1;
+           }else {
+               fread(&null_ct, sizeof(int32), 1, fp);
+               if (null_ct > 0) {
+                   for (i = 0; i < null_ct; i++) {
+                       fread(&null_id, sizeof(int32), 1, fp);
+                       nulls[null_id] = 'n';
+                   }
+               }
+               
+               string = (char *) palloc(len);
+               fread(string, len, 1, fp);
+               
+               ptr = string;
+               
+               for (i = 0; i < attr_count; i++) {
+                   if (byval[i] && nulls[i] != 'n') {
+                       
+                       switch(attr[i]->attlen) {
+                       case sizeof(char):
+                           values[i] = (Datum) *(unsigned char *) ptr;
+                           ptr += sizeof(char);
+                           break;
+                       case sizeof(short):
+                           ptr = (char *) SHORTALIGN(ptr);
+                           values[i] = (Datum) *(unsigned short *) ptr; 
+                           ptr += sizeof(short);
+                           break;
+                       case sizeof(int32):
+                           ptr = (char *) INTALIGN(ptr);
+                           values[i] = (Datum) *(uint32 *) ptr;
+                           ptr += sizeof(int32);
+                           break;
+                       default:
+                           elog(WARN, "COPY BINARY: impossible size!");
+                           break;
+                       }
+                   }else if (nulls[i] != 'n') {
+                       switch (attr[i]->attlen) {
+                       case -1:
+                           if (attr[i]->attalign == 'd') 
+                               ptr = (char *)DOUBLEALIGN(ptr);
+                           else
+                               ptr = (char *)INTALIGN(ptr);
+                           values[i] = (Datum) ptr;
+                           ptr += * (uint32 *) ptr;
+                           break;
+                       case sizeof(char):
+                           values[i] = (Datum)ptr;
+                           ptr += attr[i]->attlen;
+                           break;
+                       case sizeof(short):
+                           ptr = (char*)SHORTALIGN(ptr);
+                           values[i] = (Datum)ptr;
+                           ptr += attr[i]->attlen;
+                           break;
+                       case sizeof(int32):
+                           ptr = (char*)INTALIGN(ptr);
+                           values[i] = (Datum)ptr;
+                           ptr += attr[i]->attlen;
+                           break;
+                       default:
+                           if (attr[i]->attalign == 'd')
+                               ptr = (char *)DOUBLEALIGN(ptr);
+                           else 
+                               ptr = (char *)LONGALIGN(ptr);
+                           values[i] = (Datum) ptr;
+                           ptr += attr[i]->attlen;
+                       }
+                   }
+               }
+           }
+       }
+       if (done) continue;
+           
+       tupDesc = CreateTupleDesc(attr_count, attr);
+       tuple = heap_formtuple(tupDesc, values, nulls);
+       heap_insert(rel, tuple);
+           
+       if (has_index) {
+           for (i = 0; i < n_indices; i++) {
+               if (indexPred[i] != NULL) {
+#ifndef OMIT_PARTIAL_INDEX
+                   /* if tuple doesn't satisfy predicate,
+                    * don't update index
+                    */
+                   slot->val = tuple;
+                   /*SetSlotContents(slot, tuple); */
+                   if (ExecQual((List*)indexPred[i], econtext) == false)
+                       continue;
+#endif /* OMIT_PARTIAL_INDEX */        
+               }
+               FormIndexDatum(indexNatts[i],
+                              (AttrNumber *)&(pgIndexP[i]->indkey[0]),
+                              tuple,
+                              tupDesc,
+                              InvalidBuffer,
+                              &idatum,
+                              index_nulls,
+                              finfoP[i]);
+               ituple = index_formtuple(itupdescArr[i], &idatum, index_nulls);
+               ituple->t_tid = tuple->t_ctid;
+               indexRes = index_insert(index_rels[i], ituple);
+               if (indexRes) pfree(indexRes);
+               pfree(ituple);
+           }
+       }
+           
+       if (binary) pfree(string);
+           
+       for (i = 0; i < attr_count; i++) {
+           if (!byval[i] && nulls[i] != 'n') {
+               if (!binary) pfree((void*)values[i]);
+           }else if (nulls[i] == 'n') {
+               nulls[i] = ' ';
+           }
+       }
+           
+       pfree(tuple);
+       tuples_read++;
+           
+       if (!reading_to_eof && ntuples == tuples_read) done = true;
+    }
+    pfree(values);
+    if (!binary) pfree(in_functions);
+    pfree(nulls);
+    pfree(byval);
+    heap_close(rel);
+}
+
+static Oid
+GetOutputFunction(Oid type)
+{
+    HeapTuple    typeTuple;
+    
+    typeTuple = SearchSysCacheTuple(TYPOID,
+                                   ObjectIdGetDatum(type),
+                                   0,0,0);
+    
+    if (HeapTupleIsValid(typeTuple))
+       return((int) ((TypeTupleForm) GETSTRUCT(typeTuple))->typoutput);
+    
+    elog(WARN, "GetOutputFunction: Cache lookup of type %d failed", type);
+    return(InvalidOid);
+}
+
+static Oid
+GetTypeElement(Oid type)
+{
+    HeapTuple    typeTuple;
+    
+    typeTuple = SearchSysCacheTuple(TYPOID,
+                                   ObjectIdGetDatum(type),
+                                   0,0,0);
+
+    
+    if (HeapTupleIsValid(typeTuple))
+       return((int) ((TypeTupleForm) GETSTRUCT(typeTuple))->typelem);
+    
+    elog(WARN, "GetOutputFunction: Cache lookup of type %d failed", type);
+    return(InvalidOid);
+}
+
+static Oid
+GetInputFunction(Oid type)
+{
+    HeapTuple    typeTuple;
+    
+    typeTuple = SearchSysCacheTuple(TYPOID,
+                                   ObjectIdGetDatum(type),
+                                   0,0,0);
+    
+    if (HeapTupleIsValid(typeTuple))
+       return((int) ((TypeTupleForm) GETSTRUCT(typeTuple))->typinput);
+    
+    elog(WARN, "GetInputFunction: Cache lookup of type %d failed", type);
+    return(InvalidOid);
+}
+
+static Oid
+IsTypeByVal(Oid type)
+{
+    HeapTuple    typeTuple;
+    
+    typeTuple = SearchSysCacheTuple(TYPOID,
+                                   ObjectIdGetDatum(type),
+                                   0,0,0);
+    
+    if (HeapTupleIsValid(typeTuple))
+        return((int) ((TypeTupleForm) GETSTRUCT(typeTuple))->typbyval);
+    
+    elog(WARN, "GetInputFunction: Cache lookup of type %d failed", type);
+    
+    return(InvalidOid);
+}
+
+/* 
+ * Given the OID of a relation, return an array of index relation descriptors
+ * and the number of index relations.  These relation descriptors are open
+ * using heap_open().
+ *
+ * Space for the array itself is palloc'ed.
+ */
+
+typedef struct rel_list {
+    Oid index_rel_oid;
+    struct rel_list *next;
+} RelationList;
+
+static void
+GetIndexRelations(Oid main_relation_oid,
+                 int *n_indices,
+                 Relation **index_rels)
+{
+    RelationList *head, *scan;
+    Relation pg_index_rel;
+    HeapScanDesc scandesc;
+    Oid index_relation_oid;
+    HeapTuple tuple;
+    TupleDesc tupDesc;
+    int i;
+    bool isnull;
+    
+    pg_index_rel = heap_openr(IndexRelationName);
+    scandesc = heap_beginscan(pg_index_rel, 0, NULL, 0, NULL);
+    tupDesc = RelationGetTupleDescriptor(pg_index_rel);
+    
+    *n_indices = 0;
+    
+    head = (RelationList *) palloc(sizeof(RelationList));
+    scan = head;
+    head->next = NULL;
+    
+    for (tuple = heap_getnext(scandesc, 0, NULL);
+         tuple != NULL; 
+         tuple = heap_getnext(scandesc, 0, NULL)) {
+       
+       index_relation_oid =
+           (Oid) DatumGetInt32(heap_getattr(tuple, InvalidBuffer, 2,
+                                            tupDesc, &isnull));
+       if (index_relation_oid == main_relation_oid) {
+           scan->index_rel_oid =
+               (Oid) DatumGetInt32(heap_getattr(tuple, InvalidBuffer,
+                                                Anum_pg_index_indexrelid,
+                                                tupDesc, &isnull));
+           (*n_indices)++;
+           scan->next = (RelationList *) palloc(sizeof(RelationList));
+           scan = scan->next;
+       }
+    }
+    
+    heap_endscan(scandesc);
+    heap_close(pg_index_rel);
+    
+    *index_rels = (Relation *) palloc(*n_indices * sizeof(Relation));
+    
+    for (i = 0, scan = head; i < *n_indices; i++, scan = scan->next) {
+       (*index_rels)[i] = index_open(scan->index_rel_oid);
+    }
+    
+    for (i = 0, scan = head; i < *n_indices + 1; i++) {
+       scan = head->next;
+       pfree(head);
+       head = scan;
+    }
+}
+
+#define EXT_ATTLEN 5*8192
+
+/*
+   returns 1 is c is in s
+*/
+static bool
+inString(char c, char* s)
+{
+    int i;
+
+    if (s) {
+       i = 0;
+       while (s[i] != '\0') {
+           if (s[i] == c)
+               return 1;
+           i++;
+       }
+    }
+    return 0;
+}
+
+/*
+ * Reads input from fp until eof is seen.  If we are reading from standard
+ * input, AND we see a dot on a line by itself (a dot followed immediately
+ * by a newline), we exit as if we saw eof.  This is so that copy pipelines
+ * can be used as standard input.
+ */
+
+static char *
+CopyReadAttribute(int attno, FILE *fp, bool *isnull, char *delim)
+{
+    static char attribute[EXT_ATTLEN];
+    char c;
+    int done = 0;
+    int i = 0;
+    
+    if (feof(fp)) {
+       *isnull = (bool) false;
+       return(NULL);
+    }
+    
+    while (!done) {
+       c = getc(fp);
+           
+       if (feof(fp)) {
+           *isnull = (bool) false;
+           return(NULL);
+       }else if (reading_from_input && attno == 0 && i == 0 && c == '.') {
+           attribute[0] = c;
+           c = getc(fp);
+           if (c == '\n') {
+               *isnull = (bool) false;
+               return(NULL);
+           }else if (inString(c,delim)) {
+               attribute[1] = 0;
+               *isnull = (bool) false;
+               return(&attribute[0]);
+           }else {
+               attribute[1] = c;
+               i = 2;
+           }
+       }else if (c == '\\') {
+           c = getc(fp);
+       }else if (inString(c,delim) || c == '\n') {
+           done = 1;
+       }
+       if (!done) attribute[i++] = c;
+       if (i == EXT_ATTLEN - 1)
+           elog(WARN, "CopyReadAttribute - attribute length too long");
+    }
+    attribute[i] = '\0';
+    if (i == 0) {
+       *isnull = (bool) true;
+       return(NULL);
+    }else {
+       *isnull = (bool) false;
+       return(&attribute[0]);
+    }
+}
+
+static void
+CopyAttributeOut(FILE *fp, char *string, char *delim)
+{
+    int i;
+    int len = strlen(string);
+    
+    for (i = 0; i < len; i++) {
+       if (string[i] == delim[0] || string[i] == '\n' || string[i] == '\\') {
+           fputc('\\', fp);
+       }
+       fputc(string[i], fp);
+    }
+}
+
+/*
+ * Returns the number of tuples in a relation.  Unfortunately, currently
+ * must do a scan of the entire relation to determine this.
+ *
+ * relation is expected to be an open relation descriptor.
+ */
+static int
+CountTuples(Relation relation)
+{
+    HeapScanDesc scandesc;
+    HeapTuple tuple;
+    
+    int i;
+    
+    scandesc = heap_beginscan(relation, 0, NULL, 0, NULL);
+    
+    for (tuple = heap_getnext(scandesc, 0, NULL), i = 0;
+         tuple != NULL; 
+         tuple = heap_getnext(scandesc, 0, NULL), i++)
+       ;
+    heap_endscan(scandesc);
+    return(i);
+}
diff --git a/src/backend/commands/copy.h b/src/backend/commands/copy.h
new file mode 100644 (file)
index 0000000..0c694f9
--- /dev/null
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * copy.h--
+ *    Definitions for using the POSTGRES copy command.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef COPY_H
+#define        COPY_H
+
+#include "postgres.h"
+
+void DoCopy(char *relname, bool binary, bool from, bool pipe, char *filename,
+           char *delim);
+
+#endif /* COPY_H */
diff --git a/src/backend/commands/creatinh.c b/src/backend/commands/creatinh.c
new file mode 100644 (file)
index 0000000..7dbfd98
--- /dev/null
@@ -0,0 +1,564 @@
+/*-------------------------------------------------------------------------
+ *
+ * creatinh.c--
+ *    POSTGRES create/destroy relation with inheritance utility code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>     /* for sprintf() */
+#include <string.h>
+#include "postgres.h"
+
+#include "tcop/tcopdebug.h"
+
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/parsenodes.h"
+#include "nodes/execnodes.h"
+
+#include "utils/syscache.h"
+#include "utils/relcache.h"
+#include "catalog/catname.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_inherits.h"
+#include "catalog/pg_ipl.h"
+#include "parser/catalog_utils.h"
+
+#include "commands/creatinh.h"
+
+#include "access/tupdesc.h"
+#include "access/heapam.h"
+#include "access/xact.h"
+
+/* ----------------
+ *     local stuff
+ * ----------------
+ */
+
+static int checkAttrExists(char *attributeName, 
+                          char *attributeType, List *schema);
+static List *MergeAttributes(List *schema, List *supers);
+static void StoreCatalogInheritance(Oid relationId, List *supers);
+
+/* ----------------------------------------------------------------
+ *     DefineRelation --
+ *             Creates a new relation.
+ * ----------------------------------------------------------------
+ */
+void
+DefineRelation(CreateStmt *stmt)
+{
+    char *relname = stmt->relname;
+    List *schema = stmt->tableElts;
+    int                        numberOfAttributes;
+    Oid                        relationId;
+    char               archChar;
+    List               *inheritList    = NULL;
+    char *archiveName  = NULL;
+    TupleDesc  descriptor;
+    int                        heaploc, archloc;
+    
+    char*   typename = NULL;  /* the typename of this relation. not useod for now */
+
+    if ( strlen(relname) > NAMEDATALEN)
+       elog(WARN, "the relation name %s is > %d characters long", relname,
+            NAMEDATALEN);
+    
+    /* ----------------
+     *         Handle parameters
+     *         XXX parameter handling missing below.
+     * ----------------
+     */
+    inheritList = stmt->inhRelnames;
+    
+    /* ----------------
+     * determine archive mode
+     *         XXX use symbolic constants...
+     * ----------------
+     */
+    archChar = 'n';
+    
+    switch (stmt->archiveType) {
+    case ARCH_NONE:
+       archChar = 'n';
+       break;
+    case ARCH_LIGHT:
+       archChar = 'l';
+       break;
+    case ARCH_HEAVY:
+       archChar = 'h';
+       break;
+    default:
+       elog(WARN, "Botched archive mode %d, ignoring",
+            stmt->archiveType);
+       break;
+    }
+    
+    if (stmt->location == -1)
+       heaploc = 0;
+    else
+       heaploc = stmt->location;
+    
+    /*
+     *  For now, any user-defined relation defaults to the magnetic
+     *  disk storgage manager.  --mao 2 july 91
+     */
+    if (stmt->archiveLoc == -1) {
+       archloc = 0;
+    } else {
+       if (archChar == 'n') {
+           elog(WARN, "Set archive location, but not mode, for %s",
+                relname);
+       }
+       archloc = stmt->archiveLoc;
+    }
+    
+    /* ----------------
+     * generate relation schema, including inherited attributes.
+     * ----------------
+     */
+    schema = MergeAttributes(schema, inheritList);
+    
+    numberOfAttributes = length(schema);
+    if (numberOfAttributes <= 0) {
+       elog(WARN, "DefineRelation: %s",
+            "please inherit from a relation or define an attribute");
+    }
+    
+    /* ----------------
+     * create a relation descriptor from the relation schema
+     *  and create the relation.  
+     * ----------------
+     */
+    descriptor = BuildDescForRelation(schema, relname);
+    relationId = heap_create(relname,
+                            typename,
+                            archChar,
+                            heaploc,
+                            descriptor);
+    
+    StoreCatalogInheritance(relationId, inheritList);
+    
+    /* ----------------
+     * create an archive relation if necessary
+     * ----------------
+     */
+    if (archChar != 'n') {
+       /*
+        *  Need to create an archive relation for this heap relation.
+        *  We cobble up the command by hand, and increment the command
+        *  counter ourselves.
+        */
+       
+       CommandCounterIncrement();
+       archiveName = MakeArchiveName(relationId);
+       
+       relationId = heap_create(archiveName,
+                                typename,
+                                'n',           /* archive isn't archived */
+                                archloc,
+                                descriptor);
+       
+       pfree(archiveName);
+    }
+}
+
+/*
+ * RemoveRelation --
+ *     Deletes a new relation.
+ *
+ * Exceptions:
+ *     BadArg if name is invalid.
+ *
+ * Note:
+ *     If the relation has indices defined on it, then the index relations
+ * themselves will be destroyed, too.
+ */
+void
+RemoveRelation(char *name)
+{
+    AssertArg(name);
+    heap_destroy(name);
+}
+
+
+/*
+ * MergeAttributes --
+ *     Returns new schema given initial schema and supers.
+ *
+ *
+ * 'schema' is the column/attribute definition for the table. (It's a list
+ *     of ColumnDef's.) It is destructively changed.
+ * 'inheritList' is the list of inherited relations (a list of Value(str)'s).
+ *
+ * Notes:
+ *    The order in which the attributes are inherited is very important.
+ *    Intuitively, the inherited attributes should come first. If a table
+ *    inherits from multiple parents, the order of those attributes are
+ *    according to the order of the parents specified in CREATE TABLE.
+ *
+ *    Here's an example:
+ *
+ *     create table person (name text, age int4, location point);
+ *     create table emp (salary int4, manager char16) inherits(person);
+ *     create table student (gpa float8) inherits (person);
+ *     create table stud_emp (percent int4) inherits (emp, student);
+ *
+ *    the order of the attributes of stud_emp is as follow:
+ *
+ *
+ *                          person {1:name, 2:age, 3:location}
+ *                          /    \
+ *             {6:gpa}  student   emp {4:salary, 5:manager}
+ *                          \    /
+ *                         stud_emp {7:percent}
+ */
+static List *
+MergeAttributes(List *schema, List *supers)
+{
+    List *entry;
+    List *inhSchema = NIL;
+    
+    /*
+     * Validates that there are no duplications.
+     * Validity checking of types occurs later.
+     */
+    foreach (entry, schema) {
+       List    *rest;
+       ColumnDef *coldef = lfirst(entry);
+       
+       foreach (rest, lnext(entry)) {
+           /*
+            * check for duplicated relation names
+            */
+           ColumnDef *restdef = lfirst(rest);
+           
+           if (!strcmp(coldef->colname, restdef->colname)) {
+               elog(WARN, "attribute \"%s\" duplicated",
+                    coldef->colname);
+           }
+       }
+    }
+    foreach (entry, supers) {
+       List    *rest;
+       
+       foreach (rest, lnext(entry)) {
+           if (!strcmp(strVal(lfirst(entry)), strVal(lfirst(rest)))) {
+               elog(WARN, "relation \"%s\" duplicated",
+                    strVal(lfirst(entry)));
+           }
+       }
+    }
+    
+    /*
+     * merge the inherited attributes into the schema
+     */
+    foreach (entry, supers) {
+       char            *name = strVal(lfirst(entry));
+       Relation        relation;
+       List            *partialResult = NIL;
+       AttrNumber      attrno;
+       TupleDesc       tupleDesc;
+       
+       relation =  heap_openr(name);
+       if (relation==NULL) {
+           elog(WARN,
+                "MergeAttr: Can't inherit from non-existent superclass '%s'",
+                name);
+       }
+       tupleDesc = RelationGetTupleDescriptor(relation);
+       
+       for (attrno = relation->rd_rel->relnatts - 1; attrno >= 0; attrno--) {
+           AttributeTupleForm  attribute = tupleDesc->attrs[attrno];
+           char *attributeName;
+           char *attributeType;
+           HeapTuple   tuple;
+           ColumnDef   *def;
+           TypeName    *typename;
+           
+           /*
+            * form name and type
+            */
+           attributeName = (attribute->attname).data;
+           tuple =
+               SearchSysCacheTuple(TYPOID,
+                                   ObjectIdGetDatum(attribute->atttypid),
+                                   0,0,0);
+           AssertState(HeapTupleIsValid(tuple));
+           attributeType =
+               (((TypeTupleForm)GETSTRUCT(tuple))->typname).data;
+           /*
+            * check validity
+            *
+            */
+           if (checkAttrExists(attributeName, attributeType, inhSchema) ||
+               checkAttrExists(attributeName, attributeType, schema)) {
+               /*
+                * this entry already exists
+                */
+               continue;
+           }
+
+           /*
+            * add an entry to the schema
+            */
+           def = makeNode(ColumnDef);
+           typename = makeNode(TypeName);
+           def->colname = pstrdup(attributeName);
+           typename->name = pstrdup(attributeType); 
+           def->typename = typename;
+           partialResult = lcons(def, partialResult);
+       }
+       
+       /*
+        * iteration cleanup and result collection
+        */
+       heap_close(relation);
+
+       /*
+        * wants the inherited schema to appear in the order they are
+        * specified in CREATE TABLE
+        */
+       inhSchema = nconc(inhSchema, partialResult);
+    }
+
+    /*
+     * put the inherited schema before our the schema for this table
+     */
+    schema = nconc(inhSchema, schema);
+    
+    return (schema);
+}
+
+/*
+ * StoreCatalogInheritance --
+ *     Updates the system catalogs with proper inheritance information.
+ */
+static void
+StoreCatalogInheritance(Oid relationId, List *supers)
+{
+    Relation   relation;
+    TupleDesc  desc;
+    int16      seqNumber;
+    List       *entry;
+    List       *idList;
+    HeapTuple  tuple;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    AssertArg(OidIsValid(relationId));
+    
+    if (supers==NIL)
+       return;
+    
+    /* ----------------
+     * Catalog INHERITS information.
+     * ----------------
+     */
+    relation = heap_openr( InheritsRelationName );
+    desc = RelationGetTupleDescriptor(relation);
+
+    seqNumber = 1;
+    idList = NIL;
+    foreach (entry, supers) {
+       Datum           datum[ Natts_pg_inherits ];
+       char            nullarr[ Natts_pg_inherits ];
+       
+       tuple = SearchSysCacheTuple(RELNAME, 
+                                   PointerGetDatum(strVal(lfirst(entry))),
+                                   0,0,0);
+       AssertArg(HeapTupleIsValid(tuple));
+       
+       /*
+        * build idList for use below
+        */
+       idList = lappendi(idList, tuple->t_oid);
+       
+       datum[0] = ObjectIdGetDatum(relationId);        /* inhrel */
+       datum[1] = ObjectIdGetDatum(tuple->t_oid);      /* inhparent */
+       datum[2] = Int16GetDatum(seqNumber);            /* inhseqno */
+       
+       nullarr[0] = ' ';
+       nullarr[1] = ' ';
+       nullarr[2] = ' ';
+       
+       tuple = heap_formtuple(desc,datum, nullarr);
+       
+       (void) heap_insert(relation, tuple);
+       pfree(tuple);
+       
+       seqNumber += 1;
+    }
+    
+    heap_close(relation);
+    
+    /* ----------------
+     * Catalog IPL information.
+     *
+     * Algorithm:
+     * 0. list superclasses (by Oid) in order given (see idList).
+     * 1. append after each relationId, its superclasses, recursively.
+     * 3. remove all but last of duplicates.
+     * 4. store result.
+     * ----------------
+     */
+    
+    /* ----------------
+     * 1.
+     * ----------------
+     */
+    foreach (entry, idList) {
+       HeapTuple               tuple;
+       Oid                     id;
+       int16                   number;
+       List                    *next;
+       List                    *current;
+       
+       id = (Oid)lfirsti(entry);
+       current = entry;
+       next = lnext(entry);
+       
+       for (number = 1; ; number += 1) {
+           tuple = SearchSysCacheTuple(INHRELID,
+                                       ObjectIdGetDatum(id),
+                                       Int16GetDatum(number),
+                                       0,0);
+           
+           if (! HeapTupleIsValid(tuple))
+               break;
+           
+           lnext(current) =
+               lconsi(((InheritsTupleForm)
+                        GETSTRUCT(tuple))->inhparent,
+                       NIL);
+           
+           current = lnext(current);
+       }
+       lnext(current) = next;
+    }
+    
+    /* ----------------
+     * 2.
+     * ----------------
+     */
+    foreach (entry, idList) {
+       Oid             name;
+       List            *rest;
+       bool            found = false;
+       
+    again:
+       name = lfirsti(entry);
+       foreach (rest, lnext(entry)) {
+           if (name == lfirsti(rest)) {
+               found = true;
+               break;
+           }
+       }
+       if (found) {
+           /*
+            * entry list must be of length >= 2 or else no match
+            *
+            * so, remove this entry.
+            */
+           lfirst(entry) = lfirst(lnext(entry));
+           lnext(entry) = lnext(lnext(entry));
+           
+           found = false;
+           goto again;
+       }
+    }
+    
+    /* ----------------
+     * 3.
+     * ----------------
+     */
+    relation = heap_openr( InheritancePrecidenceListRelationName );
+    desc = RelationGetTupleDescriptor(relation);
+    
+    seqNumber = 1;
+    
+    foreach (entry, idList) {
+       Datum   datum[ Natts_pg_ipl ];
+       char    nullarr[ Natts_pg_ipl ];
+       
+       datum[0] = ObjectIdGetDatum(relationId);        /* iplrel */
+       datum[1] = ObjectIdGetDatum(lfirsti(entry));
+       /*iplinherits*/
+       datum[2] = Int16GetDatum(seqNumber);            /* iplseqno */
+       
+       nullarr[0] = ' ';
+       nullarr[1] = ' ';
+       nullarr[2] = ' ';
+       
+       tuple = heap_formtuple( desc, datum, nullarr);
+       
+       (void) heap_insert(relation, tuple);
+       pfree(tuple);
+       
+       seqNumber += 1;
+    }
+
+    heap_close(relation);
+}
+
+/*
+ * returns 1 if attribute already exists in schema, 0 otherwise.
+ */
+static int
+checkAttrExists(char *attributeName, char *attributeType, List *schema)
+{
+    List *s;
+
+    foreach (s, schema) {
+       ColumnDef *def = lfirst(s);
+
+       if (!strcmp(attributeName, def->colname)) {
+           /*
+            * attribute exists. Make sure the types are the same.
+            */
+           if (strcmp(attributeType, def->typename->name) != 0) {
+               elog(WARN, "%s and %s conflict for %s",
+                    attributeType, def->typename->name, attributeName);
+           }
+           return 1;
+       }
+    }
+    return 0;
+}
+
+/*
+ * MakeArchiveName
+ *    make an archive rel name out of a regular rel name
+ *
+* the CALLER is responsible for freeing the memory allocated
+ */
+
+char*
+MakeArchiveName(Oid relationId)
+{
+    char *arch;
+
+    /*
+     *  Archive relations are named a,XXXXX where XXXXX == the OID
+     *  of the relation they archive.  Create a string containing
+     *  this name and find the reldesc for the archive relation.
+     */
+    arch = palloc(NAMEDATALEN); 
+    sprintf(arch, "a,%d",relationId);
+
+    return arch;
+}
+
diff --git a/src/backend/commands/creatinh.h b/src/backend/commands/creatinh.h
new file mode 100644 (file)
index 0000000..cc2ab3a
--- /dev/null
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * creatinh.h--
+ *    prototypes for creatinh.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CREATINH_H
+#define CREATINH_H
+
+extern void DefineRelation(CreateStmt *stmt);
+extern void RemoveRelation(char *name);
+extern char* MakeArchiveName(Oid relid);
+
+#endif /* CREATINH_H */
diff --git a/src/backend/commands/defind.c b/src/backend/commands/defind.c
new file mode 100644 (file)
index 0000000..3bb0915
--- /dev/null
@@ -0,0 +1,505 @@
+/*-------------------------------------------------------------------------
+ *
+ * defind.c--
+ *    POSTGRES define, extend and remove index code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/attnum.h"
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/funcindex.h"
+#include "utils/builtins.h"
+#include "utils/syscache.h"
+#include "catalog/index.h"
+#include "catalog/pg_index.h"
+#include "catalog/pg_proc.h"
+#include "nodes/pg_list.h"
+#include "nodes/plannodes.h"
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/relcache.h"
+#include "utils/lsyscache.h"
+
+#include "commands/defrem.h"
+#include "parser/parsetree.h"          /* for getrelid() */
+
+#include "optimizer/prep.h"
+#include "optimizer/clauses.h"
+#include "storage/lmgr.h"
+
+#define IsFuncIndex(ATTR_LIST) (((IndexElem*)lfirst(ATTR_LIST))->args!=NULL)
+
+/* non-export function prototypes */
+static void CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid);
+static void CheckPredExpr(Node *predicate, List *rangeTable,
+                         Oid baseRelOid);
+static void
+CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid);
+static void FuncIndexArgs(IndexElem *funcIndex, AttrNumber *attNumP,
+                         Oid *argTypes, Oid *opOidP, Oid relId);
+static void NormIndexAttrs(List *attList, AttrNumber *attNumP,
+                          Oid *opOidP, Oid relId);
+
+/*
+ * DefineIndex --
+ *     Creates a new index.
+ *
+ * 'attributeList' is a list of IndexElem specifying either a functional
+ *     index or a list of attributes to index on.
+ * 'parameterList' is a list of ParamString specified in the with clause.
+ * 'predicate' is the qual specified in the where clause.
+ * 'rangetable' is for the predicate
+ *
+ * Exceptions:
+ *     XXX
+ */
+void
+DefineIndex(char *heapRelationName,
+           char *indexRelationName,
+           char *accessMethodName,
+           List *attributeList,
+           List *parameterList,
+           Expr *predicate,
+           List *rangetable)
+{
+    Oid        *classObjectId;
+    Oid        accessMethodId;
+    Oid        relationId;
+    int                numberOfAttributes;
+    AttrNumber *attributeNumberA;
+    HeapTuple  tuple;
+    uint16     parameterCount = 0;
+    Datum      *parameterA = NULL;
+    FuncIndexInfo fInfo;
+    List       *cnfPred = NULL;
+    
+    
+    /*
+     * Handle attributes
+     */
+    numberOfAttributes = length(attributeList);
+    if (numberOfAttributes <= 0) {
+       elog(WARN, "DefineIndex: must specify at least one attribute");
+    }
+    
+    /*
+     * compute heap relation id
+     */
+    tuple = SearchSysCacheTuple(RELNAME, 
+                               PointerGetDatum(heapRelationName),
+                               0,0,0);
+    if (!HeapTupleIsValid(tuple)) {
+       elog(WARN, "DefineIndex: %s relation not found",
+            heapRelationName);
+    }
+    relationId = tuple->t_oid;
+    
+    /*
+     * compute access method id
+     */
+    tuple = SearchSysCacheTuple(AMNAME, PointerGetDatum(accessMethodName),
+                               0,0,0);
+    if (!HeapTupleIsValid(tuple)) {
+       elog(WARN, "DefineIndex: %s access method not found",
+            accessMethodName);
+    }
+    accessMethodId = tuple->t_oid;
+    
+    
+    /*
+     * Handle parameters
+     * [param list is now different (NOT USED, really) - ay 10/94]
+     */
+
+    
+    /*
+     * Convert the partial-index predicate from parsetree form to plan
+     * form, so it can be readily evaluated during index creation.
+     * Note: "predicate" comes in as a list containing (1) the predicate
+     * itself (a where_clause), and (2) a corresponding range table.
+     *
+     * [(1) is 'predicate' and (2) is 'rangetable' now. - ay 10/94]
+     */
+    if (predicate != NULL && rangetable != NIL) {
+       cnfPred = cnfify((Expr*)copyObject(predicate), true);
+       fix_opids(cnfPred);
+       CheckPredicate(cnfPred, rangetable, relationId);
+    }
+    
+    if (IsFuncIndex(attributeList)) {
+       IndexElem *funcIndex= lfirst(attributeList);
+       int nargs;
+       
+       nargs = length(funcIndex->args);
+       if (nargs > INDEX_MAX_KEYS) {
+           elog(WARN, 
+                "Too many args to function, limit of %d",
+                INDEX_MAX_KEYS);
+       }
+       
+       FIsetnArgs(&fInfo,nargs);
+
+       strcpy(FIgetname(&fInfo), funcIndex->name);
+
+       attributeNumberA =
+           (AttrNumber *)palloc(nargs * sizeof attributeNumberA[0]);
+                    
+       classObjectId = (Oid *)palloc(sizeof classObjectId[0]);
+                                
+       
+       FuncIndexArgs(funcIndex, attributeNumberA, 
+                     &(FIgetArg(&fInfo, 0)),
+                     classObjectId, relationId);
+       
+       index_create(heapRelationName, 
+                    indexRelationName,
+                    &fInfo, accessMethodId, 
+                    numberOfAttributes, attributeNumberA,
+                    classObjectId, parameterCount, parameterA, (Node*)cnfPred);
+    }else {
+       attributeNumberA =
+           (AttrNumber *)palloc(numberOfAttributes *
+                                     sizeof attributeNumberA[0]);
+                    
+       classObjectId =
+           (Oid *)palloc(numberOfAttributes * sizeof classObjectId[0]);
+       
+       NormIndexAttrs(attributeList, attributeNumberA, 
+                      classObjectId, relationId);
+       
+       index_create(heapRelationName, indexRelationName, NULL,
+                    accessMethodId, numberOfAttributes, attributeNumberA,
+                    classObjectId, parameterCount, parameterA, (Node*)cnfPred);
+    }
+}
+
+
+/*
+ * ExtendIndex --
+ *     Extends a partial index.
+ *
+ * Exceptions:
+ *     XXX
+ */
+void
+ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable)
+{
+    Oid        *classObjectId;
+    Oid        accessMethodId;
+    Oid        indexId, relationId;
+    Oid        indproc;
+    int                numberOfAttributes;
+    AttrNumber *attributeNumberA;
+    HeapTuple  tuple;
+    FuncIndexInfo      fInfo;
+    FuncIndexInfo      *funcInfo = NULL;
+    IndexTupleForm     index;
+    Node       *oldPred = NULL;
+    List       *cnfPred = NULL;
+    PredInfo   *predInfo;
+    Relation   heapRelation;
+    Relation   indexRelation;
+    int                i;
+    
+    /*
+     * compute index relation id and access method id
+     */
+    tuple = SearchSysCacheTuple(RELNAME, PointerGetDatum(indexRelationName),
+                               0,0,0);
+    if (!HeapTupleIsValid(tuple)) {
+       elog(WARN, "ExtendIndex: %s index not found",
+            indexRelationName);
+    }
+    indexId = tuple->t_oid;
+    accessMethodId = ((Form_pg_class) GETSTRUCT(tuple))->relam;
+    
+    /*
+     * find pg_index tuple
+     */
+    tuple = SearchSysCacheTuple(INDEXRELID, 
+                               ObjectIdGetDatum(indexId),
+                               0,0,0);
+    if (!HeapTupleIsValid(tuple)) {
+       elog(WARN, "ExtendIndex: %s is not an index",
+            indexRelationName);
+    }
+    
+    /*
+     * Extract info from the pg_index tuple
+     */
+    index = (IndexTupleForm)GETSTRUCT(tuple);
+    Assert(index->indexrelid == indexId);
+    relationId = index->indrelid;
+    indproc = index->indproc;
+    
+    for (i=0; i<INDEX_MAX_KEYS; i++)
+       if (index->indkey[i] == 0) break;
+    numberOfAttributes = i;
+    
+    if (VARSIZE(&index->indpred) != 0) {
+       char *predString;
+
+       predString = fmgr(F_TEXTOUT, &index->indpred);
+       oldPred = stringToNode(predString);
+       pfree(predString);
+    }
+    if (oldPred == NULL)
+       elog(WARN, "ExtendIndex: %s is not a partial index",
+            indexRelationName);
+    
+    /*
+     * Convert the extension predicate from parsetree form to plan
+     * form, so it can be readily evaluated during index creation.
+     * Note: "predicate" comes in as a list containing (1) the predicate
+     * itself (a where_clause), and (2) a corresponding range table.
+     */
+    if (rangetable != NIL) {
+       cnfPred = cnfify((Expr*)copyObject(predicate), true);
+       fix_opids(cnfPred);
+       CheckPredicate(cnfPred, rangetable, relationId);
+    }
+    
+    /* make predInfo list to pass to index_build */
+    predInfo = (PredInfo*)palloc(sizeof(PredInfo));
+    predInfo->pred = (Node*)cnfPred;
+    predInfo->oldPred = oldPred;
+    
+    attributeNumberA =
+       (AttrNumber *)palloc(numberOfAttributes*
+                                 sizeof attributeNumberA[0]);
+    classObjectId =
+       (Oid *)palloc(numberOfAttributes * sizeof classObjectId[0]);
+                
+    
+    for (i=0; i<numberOfAttributes; i++) {
+       attributeNumberA[i] = index->indkey[i];
+       classObjectId[i] = index->indclass[i];
+    }
+    
+    if (indproc != InvalidOid) {
+       funcInfo = &fInfo;
+/*     FIgetnArgs(funcInfo) = numberOfAttributes; */
+       FIsetnArgs(funcInfo,numberOfAttributes);
+       
+       tuple = SearchSysCacheTuple(PROOID,
+                                   ObjectIdGetDatum(indproc),
+                                   0,0,0);
+       if (!HeapTupleIsValid(tuple))
+           elog(WARN, "ExtendIndex: index procedure not found");
+
+       namecpy(&(funcInfo->funcName),
+               &(((Form_pg_proc) GETSTRUCT(tuple))->proname));
+
+       FIsetProcOid(funcInfo,tuple->t_oid);
+    }
+    
+    heapRelation = heap_open(relationId);
+    indexRelation = index_open(indexId);
+    
+    RelationSetLockForWrite(heapRelation);
+    
+    InitIndexStrategy(numberOfAttributes, indexRelation, accessMethodId);
+    
+    index_build(heapRelation, indexRelation, numberOfAttributes,
+               attributeNumberA, 0, NULL, funcInfo, predInfo);
+}
+
+
+/*
+ * CheckPredicate
+ *     Checks that the given list of partial-index predicates refer
+ *     (via the given range table) only to the given base relation oid,
+ *     and that they're in a form the planner can handle, i.e.,
+ *     boolean combinations of "ATTR OP CONST" (yes, for now, the ATTR
+ *     has to be on the left).
+ */
+
+static void
+CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid)
+{
+    List *item;
+    
+    foreach (item, predList) {
+       CheckPredExpr(lfirst(item), rangeTable, baseRelOid);
+    }
+}
+
+static void
+CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid)
+{
+    List *clauses = NIL, *clause;
+
+    if (is_opclause(predicate)) {
+       CheckPredClause((Expr*)predicate, rangeTable, baseRelOid);
+       return;
+    } else if (or_clause(predicate))
+       clauses = ((Expr*)predicate)->args;
+    else if (and_clause(predicate))
+       clauses = ((Expr*)predicate)->args;
+    else
+       elog(WARN, "Unsupported partial-index predicate expression type");
+    
+    foreach (clause, clauses) {
+       CheckPredExpr(lfirst(clause), rangeTable, baseRelOid);
+    }
+}
+
+static void
+CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid)
+{
+    Var                *pred_var;
+    Const      *pred_const;
+    
+    pred_var = (Var *)get_leftop(predicate);
+    pred_const = (Const *)get_rightop(predicate);
+    
+    if (!IsA(predicate->oper,Oper) ||
+       !IsA(pred_var,Var) ||
+       !IsA(pred_const,Const)) {
+       elog(WARN, "Unsupported partial-index predicate clause type");
+    }
+
+    if (getrelid(pred_var->varno, rangeTable) != baseRelOid)
+       elog(WARN,
+            "Partial-index predicates may refer only to the base relation");
+}
+
+
+static void 
+FuncIndexArgs(IndexElem *funcIndex,
+             AttrNumber *attNumP,
+             Oid *argTypes,
+             Oid *opOidP,
+             Oid relId)
+{
+    List       *rest;
+    HeapTuple  tuple;
+    AttributeTupleForm att;
+    
+    tuple = SearchSysCacheTuple(CLANAME,
+                               PointerGetDatum(funcIndex->class),
+                               0,0,0);
+    
+    if (!HeapTupleIsValid(tuple)) 
+       {
+           elog(WARN, "DefineIndex: %s class not found",
+                funcIndex->class);
+       }
+    *opOidP = tuple->t_oid;
+    
+    memset(argTypes, 0, 8 * sizeof(Oid)); 
+    
+    /*
+     * process the function arguments 
+     */
+    for (rest=funcIndex->args; rest != NIL; rest = lnext(rest)) {
+       char *arg;
+       
+       arg = strVal(lfirst(rest));
+       
+       tuple = SearchSysCacheTuple(ATTNAME,
+                                   ObjectIdGetDatum(relId),
+                                   PointerGetDatum(arg),0,0);
+       
+       if (!HeapTupleIsValid(tuple)) {
+           elog(WARN, 
+                "DefineIndex: attribute \"%s\" not found",
+                arg);
+       }
+       att = (AttributeTupleForm)GETSTRUCT(tuple);
+       *attNumP++ = att->attnum;
+       *argTypes++ = att->atttypid;
+    }
+}
+
+static void    
+NormIndexAttrs(List *attList,          /* list of IndexElem's */
+              AttrNumber *attNumP,
+              Oid *opOidP,
+              Oid relId)
+{
+    List       *rest;
+    HeapTuple  tuple;
+    
+    /*
+     * process attributeList
+     */
+    
+    for (rest=attList; rest != NIL; rest = lnext(rest)) {
+       IndexElem *attribute;
+       
+       attribute = lfirst(rest);
+       
+       if (attribute->class == NULL) {
+           elog(WARN,
+                "DefineIndex: default index class unsupported");
+       }
+       
+       if (attribute->name == NULL)
+           elog(WARN, "missing attribute for define index");
+       
+       tuple = SearchSysCacheTuple(ATTNAME,
+                                   ObjectIdGetDatum(relId),
+                                   PointerGetDatum(attribute->name),
+                                   0,0);
+       if (!HeapTupleIsValid(tuple)) {
+           elog(WARN, 
+                "DefineIndex: attribute \"%s\" not found",
+                attribute->name);
+       }
+       *attNumP++ = ((AttributeTupleForm)GETSTRUCT(tuple))->attnum;
+       
+       tuple = SearchSysCacheTuple(CLANAME,
+                                   PointerGetDatum(attribute->class),
+                                   0,0,0);
+       
+       if (!HeapTupleIsValid(tuple)) {
+           elog(WARN, "DefineIndex: %s class not found",
+                attribute->class);
+       }
+       *opOidP++ = tuple->t_oid;
+    }
+}
+
+/*
+ * RemoveIndex --
+ *     Deletes an index.
+ *
+ * Exceptions:
+ *     BadArg if name is invalid.
+ *     "WARN" if index nonexistant.
+ *     ...
+ */
+void
+RemoveIndex(char *name)
+{
+    HeapTuple  tuple;
+    
+    tuple = SearchSysCacheTuple(RELNAME, 
+                               PointerGetDatum(name),
+                               0,0,0);
+    
+    if (!HeapTupleIsValid(tuple)) {
+       elog(WARN, "index \"%s\" nonexistant", name);
+    }
+    
+    if (((Form_pg_class)GETSTRUCT(tuple))->relkind != RELKIND_INDEX) {
+       elog(WARN, "relation \"%s\" is of type \"%c\"",
+            name,
+            ((Form_pg_class)GETSTRUCT(tuple))->relkind);
+    }
+    
+    index_destroy(tuple->t_oid);
+}
diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c
new file mode 100644 (file)
index 0000000..2094240
--- /dev/null
@@ -0,0 +1,564 @@
+/*-------------------------------------------------------------------------
+ *
+ * define.c--
+ *    POSTGRES "define" utility code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * DESCRIPTION
+ *    The "DefineFoo" routines take the parse tree and pick out the
+ *    appropriate arguments/flags, passing the results to the
+ *    corresponding "FooDefine" routines (in src/catalog) that do
+ *    the actual catalog-munging.
+ *
+ * NOTES
+ *    These things must be defined and committed in the following order:
+ *     "define function":
+ *             input/output, recv/send procedures
+ *     "define type":
+ *             type
+ *     "define operator":
+ *             operators
+ *
+ *     Most of the parse-tree manipulation routines are defined in
+ *     commands/manip.c.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "utils/tqual.h"
+#include "catalog/catname.h"
+#include "catalog/pg_aggregate.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "utils/syscache.h"
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+#include "fmgr.h"              /* for fmgr */
+
+#include "utils/builtins.h"    /* prototype for textin() */
+
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "commands/defrem.h"
+#include "optimizer/xfunc.h"
+#include "tcop/dest.h"
+
+static char *defGetString(DefElem *def);
+static int  defGetTypeLength(DefElem *def);
+
+#define        DEFAULT_TYPDELIM        ','
+
+/*
+ * DefineFunction --
+ *     Registers a new function.
+ *
+ */
+void
+DefineFunction(ProcedureStmt *stmt, CommandDest dest)
+{
+    List       *parameters = stmt->withClause;
+    char        *proname = stmt->funcname;
+    char*      probin_str;
+    char*      prosrc_str;
+    char *prorettype;
+    char *languageName;
+    bool       canCache;
+    bool        trusted = TRUE;
+    List       *argList;
+    int32       byte_pct, perbyte_cpu, percall_cpu, outin_ratio;
+    bool       returnsSet;
+    int                i;
+    
+    /* ----------------
+     * figure out the language and convert it to lowercase.
+     * ----------------
+     */
+    languageName = stmt->language;
+    for (i = 0; i < NAMEDATALEN && languageName[i]; ++i) {
+       languageName[i] = tolower(languageName[i]);
+    }
+    
+    /* ----------------
+     * handle "returntype = X".  The function could return a singleton
+     * value or a set of values.  Figure out which.
+     * ----------------
+     */
+    if (nodeTag(stmt->returnType)==T_TypeName) {
+       TypeName *setType = (TypeName *)stmt->returnType;
+       /* a set of values */
+       prorettype = setType->name,
+       returnsSet = true;
+    }else {
+       /* singleton */
+       prorettype = strVal(stmt->returnType);
+       returnsSet = false;
+    }
+    
+    /* Next attributes are only defined for C functions */
+    if ( strcmp(languageName, "c") == 0 ||
+        strcmp(languageName, "internal") == 0 )  {
+       List *pl;
+
+       /* the defaults */
+       canCache = FALSE;
+       byte_pct = BYTE_PCT;
+       perbyte_cpu = PERBYTE_CPU;
+       percall_cpu = PERCALL_CPU;
+       outin_ratio = OUTIN_RATIO;
+
+       foreach(pl, parameters) {
+           int count;
+           char *ptr;
+           ParamString *param = (ParamString*)lfirst(pl);
+
+           if (!strcasecmp(param->name, "isacachable")) {
+               /* ----------------
+                * handle "[ iscachable ]": figure out if Postquel functions 
+                * are cacheable automagically?
+                * ----------------
+                */
+               canCache = TRUE;
+           }else if (!strcasecmp(param->name, "trusted")) {
+               /*
+                * we don't have untrusted functions any more. The 4.2
+                * implementation is lousy anyway so I took it out.
+                *                                         -ay 10/94
+                */
+               elog(WARN, "untrusted function has been decommissioned.");
+           }else if (!strcasecmp(param->name, "byte_pct")) {
+               /*
+                ** handle expensive function parameters
+                */
+               byte_pct = atoi(param->val);
+           }else if (!strcasecmp(param->name, "perbyte_cpu")) {
+               if (!sscanf(param->val, "%d", &perbyte_cpu)) {
+                   for (count = 0, ptr = param->val; *ptr != '\0'; ptr++) {
+                       if (*ptr == '!') {
+                           count++;
+                       }
+                   }
+                   perbyte_cpu = (int) pow(10.0, (double) count);
+               }
+           }else if (!strcasecmp(param->name, "percall_cpu")) {
+               if (!sscanf(param->val, "%d", &percall_cpu)) {
+                   for (count = 0, ptr = param->val; *ptr != '\0'; ptr++) {
+                       if (*ptr == '!') {
+                           count++;
+                       }
+                   }
+                   percall_cpu = (int) pow(10.0, (double) count);
+               }
+           }else if (!strcasecmp(param->name, "outin_ratio")) {
+               outin_ratio = atoi(param->val);
+           }
+       }
+    } else if (!strcmp(languageName, "sql")) {
+       canCache = false;
+       trusted = true;
+       
+       /* query optimizer groks sql, these are meaningless */
+       perbyte_cpu = percall_cpu = 0;
+       byte_pct = outin_ratio = 100;
+    } else {
+       elog(WARN, "DefineFunction: language '%s' is not supported",
+            languageName);
+    }
+    
+    /* ----------------
+     * handle "[ arg is (...) ]"
+     * XXX fix optional arg handling below
+     * ----------------
+     */
+    argList = stmt->defArgs;
+    
+    if ( strcmp(languageName, "c") == 0 ||
+        strcmp(languageName, "internal") == 0 ) {
+       prosrc_str = "-";
+       probin_str = stmt->as;
+    } else {
+       prosrc_str = stmt->as;
+       probin_str = "-";
+    }
+    
+    /* C is stored uppercase in pg_language */
+    if (!strcmp(languageName, "c")) {
+       languageName[0] = 'C';
+    }
+    
+    /* ----------------
+     * now have ProcedureDefine do all the work..
+     * ----------------
+     */
+    ProcedureCreate(proname,
+                   returnsSet,
+                   prorettype,
+                   languageName,
+                   prosrc_str,         /* converted to text later */
+                   probin_str,         /* converted to text later */
+                   canCache,
+                   trusted,
+                   byte_pct,
+                   perbyte_cpu,
+                   percall_cpu, 
+                   outin_ratio,
+                   argList,
+                   dest);
+    
+}
+
+/* --------------------------------
+ * DefineOperator--
+ *
+ *     this function extracts all the information from the
+ *     parameter list generated by the parser and then has
+ *     OperatorCreate() do all the actual work.
+ *
+ * 'parameters' is a list of DefElem
+ * --------------------------------
+ */
+void
+DefineOperator(char *oprName,  
+              List *parameters)
+{
+    uint16     precedence=0;           /* operator precedence */
+    bool       canHash=false;          /* operator hashes */
+    bool       isLeftAssociative=true; /* operator is left associative */
+    char *functionName=NULL;   /* function for operator */
+    char *typeName1=NULL;              /* first type name */
+    char *typeName2=NULL;              /* second type name */
+    char *commutatorName=NULL;         /* optional commutator operator name */
+    char *negatorName=NULL;    /* optional negator operator name */
+    char *restrictionName=NULL;        /* optional restrict. sel. procedure */
+    char *joinName=NULL;               /* optional join sel. procedure name */
+    char *sortName1=NULL;              /* optional first sort operator */
+    char *sortName2=NULL;              /* optional second sort operator */
+    List       *pl;
+
+    /*
+     * loop over the definition list and extract the information we need.
+     */
+    foreach (pl, parameters) {
+       DefElem *defel = (DefElem *)lfirst(pl);
+
+       if (!strcasecmp(defel->defname, "leftarg")) {
+           /* see gram.y, must be setof */
+           if (nodeTag(defel->arg)==T_TypeName) 
+               elog(WARN, "setof type not implemented for leftarg");
+
+           if (nodeTag(defel->arg)==T_String) {
+               typeName1 = defGetString(defel);
+           }else {
+               elog(WARN, "type for leftarg is malformed.");
+           }
+       } else if (!strcasecmp(defel->defname, "rightarg")) {
+           /* see gram.y, must be setof */
+           if (nodeTag(defel->arg)==T_TypeName) 
+               elog(WARN, "setof type not implemented for rightarg");
+
+           if (nodeTag(defel->arg)==T_String) {
+               typeName2 = defGetString(defel);
+           }else {
+               elog(WARN, "type for rightarg is malformed.");
+           }
+       } else if (!strcasecmp(defel->defname, "procedure")) {
+           functionName = defGetString(defel);
+       } else if (!strcasecmp(defel->defname, "precedence")) {
+           /* NOT IMPLEMENTED (never worked in v4.2) */
+           elog(NOTICE, "CREATE OPERATOR: precedence not implemented");
+       } else if (!strcasecmp(defel->defname, "associativity")) {
+           /* NOT IMPLEMENTED (never worked in v4.2) */
+           elog(NOTICE, "CREATE OPERATOR: associativity not implemented");
+       } else if (!strcasecmp(defel->defname, "commutator")) {
+           commutatorName = defGetString(defel);
+       } else if (!strcasecmp(defel->defname, "negator")) {
+           negatorName = defGetString(defel);
+       } else if (!strcasecmp(defel->defname, "restrict")) {
+           restrictionName = defGetString(defel);
+       } else if (!strcasecmp(defel->defname, "join")) {
+           joinName = defGetString(defel);
+       } else if (!strcasecmp(defel->defname, "hashes")) {
+           canHash = TRUE;
+       } else if (!strcasecmp(defel->defname, "sort1")) {
+           /* ----------------
+            * XXX ( ... [ , sort1 = oprname ] [ , sort2 = oprname ] ... )
+            * XXX is undocumented in the reference manual source as of
+            * 89/8/22.
+            * ----------------
+            */
+           sortName1 = defGetString(defel);
+       } else if (!strcasecmp(defel->defname, "sort2")) {
+           sortName2 = defGetString(defel);
+       } else {
+           elog(NOTICE, "DefineOperator: attribute \"%s\" not recognized",
+                defel->defname);
+       }
+    }
+
+    /*
+     * make sure we have our required definitions
+     */
+    if (functionName==NULL) {
+       elog(WARN, "Define: \"procedure\" unspecified");
+    }
+    
+    /* ----------------
+     * now have OperatorCreate do all the work..
+     * ----------------
+     */
+    OperatorCreate(oprName,    /* operator name */
+                  typeName1,           /* first type name */
+                  typeName2,           /* second type name */
+                  functionName,        /* function for operator */
+                  precedence,          /* operator precedence */
+                  isLeftAssociative,   /* operator is left associative */
+                  commutatorName,      /* optional commutator operator name */
+                  negatorName,         /* optional negator operator name */
+                  restrictionName,     /* optional restrict. sel. procedure */
+                  joinName,            /* optional join sel. procedure name */
+                  canHash,             /* operator hashes */
+                  sortName1,           /* optional first sort operator */
+                  sortName2);          /* optional second sort operator */
+    
+}
+
+/* -------------------
+ *  DefineAggregate
+ * ------------------
+ */
+void
+DefineAggregate(char *aggName, List *parameters)
+
+{
+    char *stepfunc1Name = NULL;
+    char *stepfunc2Name = NULL;
+    char *finalfuncName = NULL;
+    char *baseType = NULL;
+    char *stepfunc1Type = NULL;
+    char *stepfunc2Type = NULL;
+    char *init1 = NULL;
+    char *init2 = NULL;
+    List *pl;
+    
+    foreach (pl, parameters) {
+       DefElem *defel = (DefElem *)lfirst(pl);
+
+       /*
+        * sfunc1
+        */
+       if (!strcasecmp(defel->defname, "sfunc1")) {
+           stepfunc1Name = defGetString(defel);
+       } else if (!strcasecmp(defel->defname, "basetype")) {
+           baseType = defGetString(defel);
+       } else if (!strcasecmp(defel->defname, "stype1")) {
+           stepfunc1Type = defGetString(defel);
+
+       /*
+        * sfunc2
+        */
+       } else if (!strcasecmp(defel->defname, "sfunc2")) {
+           stepfunc2Name = defGetString(defel);
+       } else if (!strcasecmp(defel->defname, "stype2")) {
+           stepfunc2Type = defGetString(defel);
+       /*
+        * final
+        */
+       } else if (!strcasecmp(defel->defname, "finalfunc")) {
+           finalfuncName = defGetString(defel);
+       /*
+        * initial conditions
+        */
+       } else if (!strcasecmp(defel->defname, "initcond1")) {
+           init1 = defGetString(defel);
+       } else if (!strcasecmp(defel->defname, "initcond2")) {
+           init2 = defGetString(defel);
+       } else {
+           elog(NOTICE, "DefineAggregate: attribute \"%s\" not recognized",
+                defel->defname);
+       }
+    }
+
+    /*
+     * make sure we have our required definitions
+     */
+    if (baseType==NULL) 
+       elog(WARN, "Define: \"basetype\" unspecified");
+    if (stepfunc1Name!=NULL) {
+       if (stepfunc1Type==NULL) 
+           elog(WARN, "Define: \"stype1\" unspecified");
+    }
+    if (stepfunc2Name!=NULL) {
+       if (stepfunc2Type==NULL)
+           elog(WARN, "Define: \"stype2\" unspecified");
+    }
+
+    /*
+     * Most of the argument-checking is done inside of AggregateCreate
+     */
+    AggregateCreate(aggName,           /* aggregate name */
+                   stepfunc1Name,      /* first step function name */
+                   stepfunc2Name,      /* second step function name */
+                   finalfuncName,      /* final function name */
+                   baseType,           /* type of object being aggregated */
+                   stepfunc1Type,      /* return type of first function */
+                   stepfunc2Type,      /* return type of second function */
+                   init1,      /* first initial condition */
+                   init2);     /* second initial condition */
+    
+    /* XXX free palloc'd memory */
+}
+
+/*
+ * DefineType --
+ *     Registers a new type.
+ *
+ */
+void
+DefineType(char *typeName, List *parameters)
+{
+    int16              internalLength= 0;      /* int2 */
+    int16              externalLength= 0;      /* int2 */
+    char *elemName = NULL;
+    char *inputName = NULL;
+    char *outputName = NULL;
+    char *sendName = NULL;
+    char *receiveName = NULL;
+    char               *defaultValue = NULL;   /* Datum */
+    bool               byValue = false;
+    char               delimiter = DEFAULT_TYPDELIM;
+    char *shadow_type;
+    List               *pl;
+    char               alignment = 'i';        /* default alignment */
+
+    /*
+     * Type names can only be 15 characters long, so that the shadow type
+     * can be created using the 16th character as necessary.
+     */
+    if (strlen(typeName) >= (NAMEDATALEN - 1)) {
+       elog(WARN, "DefineType: type names must be %d characters or less",
+            NAMEDATALEN - 1);
+    }
+
+    foreach(pl, parameters) {
+       DefElem *defel = (DefElem*)lfirst(pl);
+
+       if (!strcasecmp(defel->defname, "internallength")) {
+           internalLength = defGetTypeLength(defel);
+       }else if (!strcasecmp(defel->defname, "externallength")) {
+           externalLength = defGetTypeLength(defel);
+       }else if (!strcasecmp(defel->defname, "input")) {
+           inputName = defGetString(defel);
+       }else if (!strcasecmp(defel->defname, "output")) {
+           outputName = defGetString(defel);
+       }else if (!strcasecmp(defel->defname, "send")) {
+           sendName = defGetString(defel);
+       }else if (!strcasecmp(defel->defname, "delimiter")) {
+           char *p = defGetString(defel);
+           delimiter = p[0];
+       }else if (!strcasecmp(defel->defname, "receive")) {
+           receiveName = defGetString(defel);
+       }else if (!strcasecmp(defel->defname, "element")) {
+           elemName = defGetString(defel);
+       }else if (!strcasecmp(defel->defname, "default")) {
+           defaultValue = defGetString(defel);
+       }else if (!strcasecmp(defel->defname, "passedbyvalue")) {
+           byValue = true;
+       }else if (!strcasecmp(defel->defname, "alignment")) {
+           char *a = defGetString(defel);
+           if (!strcasecmp(a, "double")) {
+               alignment = 'd';
+           } else if (!strcasecmp(a, "int")) {
+               alignment = 'i';
+           } else {
+               elog(WARN, "DefineType: \"%s\" alignment  not recognized",
+                    a);
+           }
+       }else {
+           elog(NOTICE, "DefineType: attribute \"%s\" not recognized",
+                defel->defname);
+       }
+    }
+
+    /*
+     * make sure we have our required definitions
+     */
+    if (inputName==NULL)
+       elog(WARN, "Define: \"input\" unspecified");
+    if (outputName==NULL)
+       elog(WARN, "Define: \"output\" unspecified");
+    
+    /* ----------------
+     * now have TypeCreate do all the real work.
+     * ----------------
+     */
+    (void) TypeCreate(typeName,        /* type name */
+                     InvalidOid,  /* relation oid (n/a here) */
+                     internalLength,   /* internal size */
+                     externalLength,   /* external size */
+                     'b',              /* type-type (base type) */
+                     delimiter,        /* array element delimiter */
+                     inputName,        /* input procedure */
+                     outputName,       /* output procedure */
+                     sendName,         /* send procedure */
+                     receiveName,      /* receive procedure */
+                     elemName,         /* element type name */
+                     defaultValue,     /* default type value */
+                     byValue,          /* passed by value */
+                     alignment);
+    
+    /* ----------------
+     *  When we create a true type (as opposed to a complex type)
+     *  we need to have an shadow array entry for it in pg_type as well.
+     * ----------------
+     */
+    shadow_type = makeArrayTypeName(typeName);
+
+    (void) TypeCreate(shadow_type,     /* type name */
+                     InvalidOid,  /* relation oid (n/a here) */
+                     -1,               /* internal size */
+                     -1,               /* external size */
+                     'b',              /* type-type (base type) */
+                     DEFAULT_TYPDELIM, /* array element delimiter */
+                     "array_in",               /* input procedure */
+                     "array_out",              /* output procedure */
+                     "array_out",              /* send procedure */
+                     "array_in",               /* receive procedure */
+                     typeName, /* element type name */
+                     defaultValue,     /* default type value */
+                     false,            /* never passed by value */
+                     alignment);
+    
+    pfree(shadow_type);
+}
+
+static char *
+defGetString(DefElem *def)
+{
+    if (nodeTag(def->arg)!=T_String)
+       elog(WARN, "Define: \"%s\" = what?", def->defname);
+    return (strVal(def->arg));
+}
+
+static int 
+defGetTypeLength(DefElem *def)
+{
+    if (nodeTag(def->arg)==T_Integer)
+       return (intVal(def->arg));
+    else if (nodeTag(def->arg)==T_String &&
+            !strcasecmp(strVal(def->arg),"variable"))
+       return -1;      /* variable length */
+
+    elog(WARN, "Define: \"%s\" = what?", def->defname);
+    return -1;
+}
diff --git a/src/backend/commands/defrem.h b/src/backend/commands/defrem.h
new file mode 100644 (file)
index 0000000..35a4ab4
--- /dev/null
@@ -0,0 +1,53 @@
+/*-------------------------------------------------------------------------
+ *
+ * defrem.h--
+ *    POSTGRES define and remove utility definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        DEFREM_H
+#define DEFREM_H
+
+#include "postgres.h"
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
+#include "nodes/parsenodes.h"
+#include "tcop/dest.h"
+
+/*
+ * prototypes in defind.c
+ */
+extern void DefineIndex(char *heapRelationName, 
+                       char *indexRelationName,
+                       char *accessMethodName,
+                       List *attributeList,
+                       List *parameterList, Expr *predicate,
+                       List *rangetable);
+extern void ExtendIndex(char *indexRelationName,
+                       Expr *predicate,
+                       List *rangetable);
+extern void RemoveIndex(char *name);
+
+/*
+ * prototypes in define.c
+ */
+extern void DefineFunction(ProcedureStmt *nameargsexe, CommandDest dest);
+extern void DefineOperator(char *name, List *parameters);
+extern void DefineAggregate(char *name, List *parameters);
+extern void DefineType(char *name, List *parameters);
+
+/*
+ * prototypes in remove.c
+ */
+extern void RemoveFunction(char *functionName, int nargs, List *argNameList);
+extern void RemoveOperator(char *operatorName, 
+                          char *typeName1, char *typeName2);
+extern void RemoveType(char *typeName);
+extern void RemoveAggregate(char *aggName);
+
+#endif /* DEFREM_H */
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
new file mode 100644 (file)
index 0000000..e6199ff
--- /dev/null
@@ -0,0 +1,219 @@
+/*-------------------------------------------------------------------------
+ *
+ * explain.c--
+ *    Explain the query execution plan
+ *
+ * Copyright (c) 1994-5, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "parser/catalog_utils.h"
+#include "parser/parse_query.h"            /* for MakeTimeRange() */
+#include "nodes/plannodes.h"
+#include "tcop/tcopprot.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "lib/stringinfo.h"
+#include "commands/explain.h"
+#include "optimizer/planner.h"
+#include "access/xact.h"
+
+typedef struct ExplainState {
+    /* options */
+    int                printCost;      /* print cost */
+    int                printNodes;     /* do nodeToString() instead */
+    /* other states */
+    List       *rtable;        /* range table */
+} ExplainState;
+
+static char *Explain_PlanToString(Plan *plan, ExplainState *es);
+
+/*
+ * ExplainQuery -
+ *    print out the execution plan for a given query
+ *
+ */
+void
+ExplainQuery(Query *query, List *options, CommandDest dest)
+{
+    char *s;
+    Plan *plan;
+    ExplainState *es;
+    int len;
+
+    if (IsAbortedTransactionBlockState()) {
+       char *tag = "*ABORT STATE*";
+       EndCommand(tag, dest);
+               
+       elog(NOTICE, "(transaction aborted): %s",
+            "queries ignored until END");
+               
+       return;
+    }
+
+    /* plan the queries (XXX we've ignored rewrite!!) */
+    plan = planner(query);
+
+    /* pg_plan could have failed */
+    if (plan == NULL)
+       return;
+
+    es = (ExplainState*)malloc(sizeof(ExplainState));
+    memset(es, 0, sizeof(ExplainState));
+
+    /* parse options */
+    while (options) {
+       char *ostr = strVal(lfirst(options));
+       if (!strcasecmp(ostr, "cost"))
+           es->printCost = 1;
+       else if (!strcasecmp(ostr, "full_plan"))
+           es->printNodes = 1;
+
+       options = lnext(options);
+    }
+    es->rtable = query->rtable;
+
+    if (es->printNodes) {
+       s = nodeToString(plan);
+    } else {
+       s = Explain_PlanToString(plan, es);
+    }
+
+    /* output the plan */
+    len = strlen(s);
+    elog(NOTICE, "QUERY PLAN:\n\n%.*s", ELOG_MAXLEN-64, s);
+    len -= ELOG_MAXLEN-64;
+    while (len > 0) {
+       s += ELOG_MAXLEN-64;
+       elog(NOTICE, "%.*s", ELOG_MAXLEN-64, s);
+       len -= ELOG_MAXLEN-64;
+    }
+    free(es);
+}
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+/*
+ * explain_outNode -
+ *    converts a Node into ascii string and append it to 'str'
+ */
+static void
+explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
+{
+    char *pname;
+    char buf[1000];
+    int i;
+    
+    if (plan==NULL) {
+       appendStringInfo(str, "\n");
+       return;
+    }
+
+    switch(nodeTag(plan)) {
+    case T_Result:
+       pname = "Result";
+       break;
+    case T_Append:
+       pname = "Append";
+       break;
+    case T_NestLoop:
+       pname = "Nested Loop";
+       break;
+    case T_MergeJoin:
+       pname = "Merge Join";
+       break;
+    case T_HashJoin:
+       pname = "Hash Join";
+       break;
+    case T_SeqScan:
+       pname = "Seq Scan";
+       break;
+    case T_IndexScan:
+       pname = "Index Scan";
+       break;
+    case T_Temp:
+       pname = "Temp Scan";
+       break;
+    case T_Sort:
+       pname = "Sort";
+       break;
+    case T_Agg:
+       pname = "Aggregate";
+       break;
+    case T_Unique:
+       pname = "Unique";
+       break;
+    case T_Hash:
+       pname = "Hash";
+       break;
+    case T_Tee:
+       pname = "Tee";
+       break;
+    default:
+       break;
+    }
+
+    for(i=0; i < indent; i++)
+       appendStringInfo(str, "  ");
+
+    appendStringInfo(str, pname);
+    switch(nodeTag(plan)) {
+    case T_SeqScan:
+    case T_IndexScan:
+       if (((Scan*)plan)->scanrelid > 0) {
+           RangeTblEntry *rte = nth(((Scan*)plan)->scanrelid-1, es->rtable);
+           sprintf(buf, " on %.*s", NAMEDATALEN, rte->refname);
+           appendStringInfo(str, buf);
+       }
+       break;
+    default:
+       break;
+    }
+    if (es->printCost) {
+       sprintf(buf, "  (cost=%.2f size=%d width=%d)",
+               plan->cost, plan->plan_size, plan->plan_width);
+       appendStringInfo(str, buf);
+    }
+    appendStringInfo(str, "\n");
+
+    /* lefttree */
+    if (outerPlan(plan)) {
+       for(i=0; i < indent; i++)
+           appendStringInfo(str, "  ");
+       appendStringInfo(str, "  -> ");
+       explain_outNode(str, outerPlan(plan), indent+1, es);
+    }
+
+    /* righttree */
+    if (innerPlan(plan)) {
+       for(i=0; i < indent; i++)
+           appendStringInfo(str, "  ");
+       appendStringInfo(str, "  -> ");
+       explain_outNode(str, innerPlan(plan), indent+1, es);
+    }
+    return;
+}
+
+static char *
+Explain_PlanToString(Plan *plan, ExplainState *es)
+{
+    StringInfo str;
+    char *s;
+    
+    if (plan==NULL)
+       return "";
+    Assert(plan!=NULL);
+    str = makeStringInfo();
+    explain_outNode(str, plan, 0, es);
+    s = str->data;
+    pfree(str);
+
+    return s;
+}
diff --git a/src/backend/commands/explain.h b/src/backend/commands/explain.h
new file mode 100644 (file)
index 0000000..57d7fcb
--- /dev/null
@@ -0,0 +1,17 @@
+/*-------------------------------------------------------------------------
+ *
+ * explain.h--
+ *    prototypes for explain.c
+ *
+ * Copyright (c) 1994-5, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        EXPLAIN_H
+#define        EXPLAIN_H
+
+extern void ExplainQuery(Query *query, List *options, CommandDest dest);
+
+#endif /* EXPLAIN_H*/
diff --git a/src/backend/commands/purge.c b/src/backend/commands/purge.c
new file mode 100644 (file)
index 0000000..ebca54f
--- /dev/null
@@ -0,0 +1,168 @@
+/*-------------------------------------------------------------------------
+ *
+ * purge.c--
+ *    the POSTGRES purge command.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * Note:
+ *     XXX There are many instances of int32 instead of ...Time.  These
+ *     should be changed once it is decided the signed'ness will be.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+
+#include "access/heapam.h"
+#include "access/xact.h"
+#include "utils/tqual.h"       /* for NowTimeQual */
+#include "catalog/catname.h"
+#include "catalog/indexing.h"
+#include "fmgr.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/nabstime.h"
+
+#include "catalog/pg_class.h"
+#include "commands/purge.h"
+#include "utils/builtins.h"    /* for isreltime() */
+
+static char    cmdname[] = "RelationPurge";
+
+#define        RELATIVE        01
+#define        ABSOLUTE        02
+
+int32
+RelationPurge(char *relationName,
+             char *absoluteTimeString,
+             char *relativeTimeString)
+{
+    register           i;
+    AbsoluteTime               absoluteTime = INVALID_ABSTIME;
+    RelativeTime               relativeTime = INVALID_RELTIME;
+    bits8                      dateTag;
+    Relation           relation;
+    HeapScanDesc               scan;
+    static ScanKeyData key[1] = {
+       { 0, Anum_pg_class_relname, F_NAMEEQ }
+    };
+    Buffer                     buffer;
+    HeapTuple          newTuple, oldTuple;
+    AbsoluteTime               currentTime;
+    char                       *values[Natts_pg_class];
+    char                       nulls[Natts_pg_class];
+    char                       replace[Natts_pg_class];
+    Relation           idescs[Num_pg_class_indices];
+    
+    /*
+     * XXX for some reason getmyrelids (in inval.c) barfs when
+     * you heap_replace tuples from these classes.  i thought
+     * setheapoverride would fix it but it didn't.  for now,
+     * just disallow purge on these classes.
+     */
+    if (strcmp(RelationRelationName, relationName) == 0 ||
+       strcmp(AttributeRelationName, relationName)  == 0 ||
+       strcmp(AccessMethodRelationName, relationName) == 0 ||
+       strcmp(AccessMethodOperatorRelationName, relationName) == 0) {
+       elog(WARN, "%s: cannot purge catalog \"%s\"",
+            cmdname, relationName);
+    }
+    
+    if (PointerIsValid(absoluteTimeString)) {
+       absoluteTime = (int32) nabstimein(absoluteTimeString);
+       absoluteTimeString[0] = '\0';
+       if (absoluteTime == INVALID_ABSTIME) {
+           elog(NOTICE, "%s: bad absolute time string \"%s\"",
+                cmdname, absoluteTimeString);
+           elog(WARN, "purge not executed");
+       }
+    }
+    
+#ifdef PURGEDEBUG
+    elog(DEBUG, "%s: absolute time `%s' is %d.",
+        cmdname, absoluteTimeString, absoluteTime);
+#endif /* defined(PURGEDEBUG) */
+    
+    if (PointerIsValid(relativeTimeString)) {
+       if (isreltime(relativeTimeString, NULL, NULL, NULL) != 1) {
+           elog(WARN, "%s: bad relative time string \"%s\"",
+                cmdname, relativeTimeString);
+       }
+       relativeTime = reltimein(relativeTimeString);
+       
+#ifdef PURGEDEBUG
+       elog(DEBUG, "%s: relative time `%s' is %d.",
+            cmdname, relativeTimeString, relativeTime);
+#endif /* defined(PURGEDEBUG) */
+    }
+    
+    /*
+     * Find the RELATION relation tuple for the given relation.
+     */
+    relation = heap_openr(RelationRelationName);
+    key[0].sk_argument = PointerGetDatum(relationName);
+    fmgr_info(key[0].sk_procedure, &key[0].sk_func, &key[0].sk_nargs);
+    
+    scan = heap_beginscan(relation, 0, NowTimeQual, 1, key);
+    oldTuple = heap_getnext(scan, 0, &buffer);
+    if (!HeapTupleIsValid(oldTuple)) {
+       heap_endscan(scan);
+       heap_close(relation);
+       elog(WARN, "%s: no such relation: %s", cmdname, relationName);
+       return(0);
+    }
+    
+    /*
+     * Dig around in the tuple.
+     */
+    currentTime = GetCurrentTransactionStartTime();
+    if (!RelativeTimeIsValid(relativeTime)) {
+       dateTag = ABSOLUTE;
+       if (!AbsoluteTimeIsValid(absoluteTime))
+           absoluteTime = currentTime;
+    } else if (!AbsoluteTimeIsValid(absoluteTime))
+       dateTag = RELATIVE;
+    else
+       dateTag = ABSOLUTE | RELATIVE;
+    
+    for (i = 0; i < Natts_pg_class; ++i) {
+       nulls[i] = heap_attisnull(oldTuple, i+1) ? 'n' : ' ';
+       values[i] = NULL;
+       replace[i] = ' ';
+    }
+    if (dateTag & ABSOLUTE) {
+       values[Anum_pg_class_relexpires-1] =
+           (char *) UInt32GetDatum(absoluteTime);
+       replace[Anum_pg_class_relexpires-1] = 'r';
+    }
+    if (dateTag & RELATIVE) {
+       values[Anum_pg_class_relpreserved-1] =
+           (char *) UInt32GetDatum(relativeTime);
+       replace[Anum_pg_class_relpreserved-1] = 'r';
+    }
+    
+    /*
+     * Change the RELATION relation tuple for the given relation.
+     */
+    newTuple = heap_modifytuple(oldTuple, buffer, relation, (Datum*)values,
+                               nulls, replace);
+    
+    /* XXX How do you detect an insertion error?? */
+    (void) heap_replace(relation, &newTuple->t_ctid, newTuple);
+    
+    /* keep the system catalog indices current */
+    CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
+    CatalogIndexInsert(idescs, Num_pg_class_indices, relation, newTuple);
+    CatalogCloseIndices(Num_pg_class_indices, idescs);
+    
+    pfree(newTuple);
+    
+    heap_endscan(scan);
+    heap_close(relation);
+    return(1);
+}
+
diff --git a/src/backend/commands/purge.h b/src/backend/commands/purge.h
new file mode 100644 (file)
index 0000000..4ae70ff
--- /dev/null
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * purge.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        PURGE_H
+#define        PURGE_H
+
+extern int32 RelationPurge(char *relationName,
+                          char *absoluteTimeString,
+                          char *relativeTimeString);
+
+#endif /* PURGE_H */
diff --git a/src/backend/commands/recipe.c b/src/backend/commands/recipe.c
new file mode 100644 (file)
index 0000000..15d08a3
--- /dev/null
@@ -0,0 +1,1181 @@
+/*-------------------------------------------------------------------------
+ *
+ * recipe.c--
+ *    routines for handling execution of Tioga recipes
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+
+#include "include/postgres.h"
+#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
+#include "nodes/pg_list.h"
+#include "nodes/makefuncs.h"
+#include "catalog/pg_type.h"
+#include "commands/recipe.h"
+#include "libpq/libpq-be.h"
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "utils/geo-decls.h"
+#include "utils/relcache.h" /* for RelationNameGetRelation*/
+#include "parser/parse_query.h"
+#include "rewrite/rewriteHandler.h"
+#include "rewrite/rewriteManip.h"
+#include "tcop/pquery.h"
+#include "tcop/dest.h"
+#include "optimizer/planner.h"
+#include "executor/executor.h"
+
+/* from tcop/postgres.c */
+extern CommandDest whereToSendOutput;
+
+#ifndef TIOGA
+
+void beginRecipe(RecipeStmt *stmt) {
+  elog(NOTICE,"You must compile with TIOGA defined in order to use recipes\n");
+}
+#else
+
+#include "tioga/tgRecipe.h"
+
+#define DEBUG_RECIPE 1
+
+/* structure to keep track of the tee node plans */
+typedef struct _teePlanInfo {
+    char* tpi_relName;
+    Query* tpi_parsetree;
+    Plan*  tpi_plan;
+} TeePlanInfo;
+
+typedef struct _teeInfo {
+    int num;
+    TeePlanInfo *val;
+} TeeInfo;
+
+QueryTreeList *appendQlist(QueryTreeList *q1, QueryTreeList *q2);
+void OffsetVarAttno(Node* node, int varno, int offset);
+
+static void appendTeeQuery(TeeInfo *teeInfo, 
+                          QueryTreeList *q, 
+                          char* teeNodeName);
+
+static Plan* replaceTeeScans(Plan* plan,
+                            Query* parsetree,
+                            TeeInfo *teeInfo);
+static void replaceSeqScan(Plan* plan, 
+                          Plan* parent,
+                          int rt_ind, 
+                          Plan* tplan);
+
+static void tg_rewriteQuery(TgRecipe* r, TgNode *n, 
+                           QueryTreeList *q,
+                           QueryTreeList *inputQlist);
+static Node *tg_replaceNumberedParam(Node* expression,
+                                    int pnum,
+                                    int rt_ind,
+                                    char *teeRelName);
+static Node *tg_rewriteParamsInExpr(Node *expression, 
+                                   QueryTreeList *inputQlist);
+static QueryTreeList *tg_parseSubQuery(TgRecipe* r, 
+                                      TgNode* n,
+                                      TeeInfo* teeInfo);
+static QueryTreeList* tg_parseTeeNode(TgRecipe *r, 
+                                     TgNode *n, 
+                                     int i, 
+                                     QueryTreeList *qList,
+                                     TeeInfo* teeInfo);
+
+
+/*
+   The Tioga recipe rewrite algorithm:
+
+   To parse a Tioga recipe, we start from an eye node and go backwards through
+   its input nodes.  To rewrite a Tioga node, we do the following:
+
+      1) parse the node we're at in the standard way (calling parser() )
+      2) rewrite its input nodes recursively using Tioga rewrite
+      3) now, with the rewritten input parse trees and the original parse tree
+         of the node,  we rewrite the the node.
+        To do the rewrite, we use the target lists, range tables, and
+        qualifications of the input parse trees
+*/
+
+/*
+ * beginRecipe:
+ *    this is the main function to recipe execution
+ *   this function is invoked for EXECUTE RECIPE ...  statements
+ *  
+ *  takes in a RecipeStmt structure from the parser
+ * and returns a list of cursor names
+ */
+
+void
+beginRecipe(RecipeStmt* stmt)
+{
+  TgRecipe* r;
+  int i;
+  QueryTreeList *qList;
+  char portalName[1024];
+
+  Plan *plan;
+  TupleDesc attinfo;
+  QueryDesc *queryDesc;
+  Query *parsetree;
+
+  int numTees;
+  TeeInfo* teeInfo;
+
+  /* retrieveRecipe() reads the recipe from the database
+     and returns a TgRecipe* structure we can work with */
+
+  r = retrieveRecipe(stmt->recipeName);
+
+  if (r == NULL) return;
+
+  /* find the number of tees in the recipe */
+  numTees = r->tees->num;
+
+  if (numTees > 0) {
+      /* allocate a teePlan structure */
+      teeInfo = (TeeInfo*)malloc(sizeof(TeeInfo));
+      teeInfo->num = numTees;
+      teeInfo->val = (TeePlanInfo*)malloc(numTees * sizeof(TeePlanInfo));
+      for (i=0;i<numTees;i++) {
+         teeInfo->val[i].tpi_relName = r->tees->val[i]->nodeName;
+         teeInfo->val[i].tpi_parsetree = NULL;
+         teeInfo->val[i].tpi_plan = NULL;
+      }
+  } else
+      teeInfo = NULL;
+
+  /* 
+   *  for each viewer in the recipe, go backwards from each viewer input
+   *  and generate a plan.  Attach the plan to cursors.
+   **/
+  for (i=0;i<r->eyes->num;i++) {
+      TgNodePtr e;
+
+      e = r->eyes->val[i];
+      if (e->inNodes->num > 1) {
+         elog(NOTICE,
+              "beginRecipe: Currently eyes cannot have more than one input");
+      }
+      if (e->inNodes->num == 0) { 
+         /* no input to this eye, skip it */
+         continue;
+      }
+
+#ifdef DEBUG_RECIPE
+   elog(NOTICE,"beginRecipe: eyes[%d] = %s\n", i, e->nodeName);
+#endif  /* DEBUG_RECIPE */
+
+      qList = tg_parseSubQuery(r,e->inNodes->val[0], teeInfo);
+
+      if (qList == NULL) {
+         /* eye is directly connected to a tee node */
+         /* XXX TODO: handle this case */
+      }
+
+      /* now, plan the queries */
+      /* should really do everything pg_plan() does, but for now,
+        we skip the rule rewrite and time qual stuff */
+
+      /* ---------------------------------------------------------- 
+       * 1) plan the main query, everything from an eye node back to
+           a Tee 
+       * ---------------------------------------------------------- */
+      parsetree = qList->qtrees[0];
+
+      /* before we plan, we want to see all the changes
+        we did, during the rewrite phase, such as
+        creating the tee tables,
+        setheapoverride() allows us to see the changes */
+      setheapoverride(true);
+      plan = planner(parsetree);
+
+      /* ---------------------------------------------------------- 
+       * 2) plan the tee queries, (subgraphs rooted from a Tee)
+           by the time the eye is processed, all tees that contribute
+          to that eye will have been included in the teeInfo list
+       * ---------------------------------------------------------- */
+      if (teeInfo) {
+         int t;
+         Plan* tplan;
+         Tee* newplan;
+
+         for (t=0; t<teeInfo->num;t++) {
+             if (teeInfo->val[t].tpi_plan == NULL) {
+                 /* plan it in the usual fashion */
+                 tplan =  planner(teeInfo->val[t].tpi_parsetree);
+
+                 /* now add a tee node to the root of the plan */
+elog(NOTICE, "adding tee plan node to the root of the %s\n",
+     teeInfo->val[t].tpi_relName);
+                 newplan = (Tee*)makeNode(Tee);
+                 newplan->plan.targetlist = tplan->targetlist;
+                 newplan->plan.qual = NULL; /* tplan->qual; */
+                 newplan->plan.lefttree = tplan;
+                 newplan->plan.righttree = NULL;
+                 newplan->leftParent = NULL;
+                 newplan->rightParent = NULL;
+                 /* the range table of the tee is the range table
+                    of the tplan */
+                 newplan->rtentries = teeInfo->val[t].tpi_parsetree->rtable;
+                 strcpy(newplan->teeTableName,
+                        teeInfo->val[t].tpi_relName);
+                 teeInfo->val[t].tpi_plan = (Plan*)newplan;
+             }
+         }
+         
+         /* ---------------------------------------------------------- 
+          * 3) replace the tee table scans in the main plan with
+               actual tee plannodes
+          * ---------------------------------------------------------- */
+
+         plan = replaceTeeScans(plan, parsetree, teeInfo);
+
+      } /* if (teeInfo) */
+
+      setheapoverride(false);
+      
+      /* define a portal for this viewer input */
+      /* for now, eyes can only have one input */
+      sprintf(portalName, "%s%d",e->nodeName,0);
+         
+      queryDesc = CreateQueryDesc(parsetree,
+                                 plan,
+                                 whereToSendOutput);
+      /* ----------------
+       *       call ExecStart to prepare the plan for execution
+       * ----------------
+       */
+      attinfo = ExecutorStart(queryDesc,NULL);
+      
+      ProcessPortal(portalName,
+                   parsetree,
+                   plan,
+                   attinfo,
+                   whereToSendOutput);
+elog(NOTICE, "beginRecipe: cursor named %s is now available", portalName);
+      }
+
+}
+
+
+
+/*
+ * tg_rewriteQuery -
+ *    r - the recipe being rewritten
+ *    n - the node that we're current at
+ *    q - a QueryTree List containing the parse tree of the node
+ *    inputQlist - the parsetrees of its input nodes,
+ *                 the size of inputQlist must be the same as the
+ *                 number of input nodes.  Some elements in the inpuQlist
+ *                 may be null if the inputs to those nodes are unconnected
+ *
+ *  this is the main routine for rewriting the recipe queries
+ *  the original query tree 'q' is modified
+ */
+
+static void 
+tg_rewriteQuery(TgRecipe* r, 
+               TgNode *n, 
+               QueryTreeList *q,
+               QueryTreeList *inputQlist)
+{
+    Query* orig;
+    Query* inputQ;
+    int i;
+    List *rtable;
+    List *input_rtable;
+    int rt_length;
+
+    /* orig is the original parse tree of the node */
+    orig = q->qtrees[0];
+
+
+    /*-------------------------------------------------------------------
+       step 1:
+       form a combined range table from all the range tables in the original
+       query as well as the input nodes 
+
+       form a combined qualification from the qual in the original plus
+       the quals of the input nodes
+      -------------------------------------------------------------------
+    */
+
+    /* start with the original range table */
+    rtable = orig->rtable;
+    rt_length = length(rtable);
+
+    for (i=0;i<n->inNodes->num;i++) {
+       if (n->inNodes->val[i] != NULL &&
+           n->inNodes->val[i]->nodeType != TG_TEE_NODE) {
+           inputQ = inputQlist->qtrees[i];
+           input_rtable = inputQ->rtable;
+
+           /* need to offset the var nodes in the qual and targetlist
+              because they are indexed off the original rtable */
+           OffsetVarNodes((Node*)inputQ->qual, rt_length);
+           OffsetVarNodes((Node*)inputQ->targetList, rt_length);
+
+           /* append the range tables from the children nodes  */
+           rtable = nconc (rtable, input_rtable);
+
+           /* append the qualifications of the child node into the
+              original qual list */
+           AddQual(orig, inputQ->qual);
+       }
+    }
+    orig->rtable = rtable;
+
+    /* step 2:
+       rewrite the target list of the original parse tree
+       if there are any references to params, replace them with 
+       the appropriate target list entry of the children node
+     */
+    if (orig->targetList != NIL) {
+       List *tl;
+       TargetEntry *tle;
+
+       foreach (tl, orig->targetList) {
+         tle = lfirst(tl);
+         if (tle->resdom != NULL) {
+             tle->expr = tg_rewriteParamsInExpr(tle->expr, inputQlist);
+         }
+        }
+    }
+    
+    /* step 3:
+       rewrite the qual of the original parse tree
+       if there are any references to params, replace them with 
+       the appropriate target list entry of the children node
+     */
+    if (orig->qual) {
+       if (nodeTag(orig->qual) == T_List) {
+           elog(WARN, "tg_rewriteQuery: Whoa! why is my qual a List???");
+       }
+       orig->qual = tg_rewriteParamsInExpr(orig->qual, inputQlist);
+    }
+
+    /* at this point, we're done with the rewrite, the querytreelist q
+       has been modified */
+
+}
+
+
+/* tg_replaceNumberedParam:
+
+   this procedure replaces the specified numbered param with a 
+   reference to a range table
+
+   this procedure recursively calls itself
+
+   it returns a (possibly modified) Node*.
+
+*/
+static Node*
+tg_replaceNumberedParam(Node *expression, 
+                       int pnum,  /* the number of the parameter */
+                       int rt_ind, /* the range table index */
+                       char *teeRelName) /* the relname of the tee table */
+{
+    TargetEntry *param_tle;
+    Param* p;
+    Var *newVar,*oldVar;
+
+    if (expression == NULL) return NULL;
+    
+    switch (nodeTag(expression)) {
+    case T_Param:
+     {
+        /* the node is a parameter, 
+          substitute the entry from the target list of the child that
+          corresponds to the parameter number*/
+        p = (Param*)expression;
+
+        /* we only deal with the case of numbered parameters */
+        if (p->paramkind == PARAM_NUM && p->paramid == pnum) {
+
+            if (p->param_tlist) {
+                /* we have a parameter with an attribute like $N.foo 
+                   so replace it with a new var node */
+
+                /* param tlist can only have one entry in them! */
+                param_tle = (TargetEntry*)(lfirst(p->param_tlist));
+                oldVar = (Var*)param_tle->expr;
+                oldVar->varno = rt_ind;
+                oldVar->varnoold = rt_ind;
+                return (Node*)oldVar;
+            } else {
+                /* we have $N without the .foo */
+                bool defined;
+                bool isRel;
+                /* TODO here, we need to check to see whether the type of the
+                   tee is a complex type (relation) or a simple type */
+                /* if it is a simple type, then we need to get the "result"
+                   attribute from the tee relation */
+                
+                isRel = (typeid_get_relid(p->paramtype) != 0);
+                if (isRel) {
+                    newVar = makeVar(rt_ind,
+                                     0, /* the whole tuple */
+                                     TypeGet(teeRelName,&defined),
+                                     rt_ind,
+                                     0);
+                    return (Node*)newVar;
+                } else
+                    newVar = makeVar(rt_ind,
+                                     1, /* just the first field, which is 'result' */
+                                     TypeGet(teeRelName,&defined),
+                                     rt_ind,
+                                     0);
+                    return (Node*)newVar;
+
+            }
+        }
+        else {
+            elog(NOTICE, "tg_replaceNumberedParam: unexpected paramkind value of %d", p->paramkind);
+        }
+     }
+     break;
+    case T_Expr: 
+     {
+        /* the node is an expression, we need to recursively
+           call ourselves until we find parameter nodes */
+        List *l;
+        Expr *expr = (Expr*)expression;
+        List *newArgs; 
+
+        /* we have to make a new args lists because Params
+           can be replaced by Var nodes in tg_replaceNumberedParam()*/
+        newArgs = NIL;
+
+        /* we only care about argument to expressions,
+           it doesn't matter when the opType is */
+        /* recursively rewrite the arguments of this expression */
+        foreach (l, expr->args) {
+            newArgs = lappend(newArgs,
+                              tg_replaceNumberedParam(lfirst(l),
+                                                      pnum,
+                                                      rt_ind,
+                                                      teeRelName));
+        }
+        /* change the arguments of the expression */
+        expr->args = newArgs;
+     }
+    break;
+ default:
+     {
+     /* ignore other expr types */
+     }
+ }
+
+    return expression;
+}
+
+
+
+
+
+/* tg_rewriteParamsInExpr:
+
+   rewrite the params in expressions by using the targetlist entries 
+   from the input parsetrees 
+
+   this procedure recursively calls itself
+
+   it returns a (possibly modified) Node*.
+
+*/
+static Node*
+tg_rewriteParamsInExpr(Node *expression, QueryTreeList *inputQlist)
+{
+    List *tl;
+    TargetEntry *param_tle, *tle;
+    Param* p;
+    int childno;
+    char *resname;
+
+    if (expression == NULL) return NULL;
+    
+    switch (nodeTag(expression)) {
+    case T_Param:
+     {
+        /* the node is a parameter, 
+          substitute the entry from the target list of the child that
+          corresponds to the parameter number*/
+        p = (Param*)expression;
+
+        /* we only deal with the case of numbered parameters */
+        if (p->paramkind == PARAM_NUM) {
+            /* paramid's start from 1*/
+            childno = p->paramid - 1;
+
+            if (p->param_tlist) {
+                /* we have a parameter with an attribute like $N.foo 
+                   so match the resname "foo" against the target list
+                   of the (N-1)th inputQlist */
+
+                /* param tlist can only have one entry in them! */
+                param_tle = (TargetEntry*)(lfirst(p->param_tlist));
+                resname = param_tle->resdom->resname;
+
+                if (inputQlist->qtrees[childno]) {
+                    foreach (tl, inputQlist->qtrees[childno]->targetList) {
+                        tle = lfirst(tl);
+                        if (strcmp(resname, tle->resdom->resname) == 0) {
+                            return tle->expr;
+                        }
+                    }
+                }
+                else {
+                    elog(WARN,"tg_rewriteParamsInExpr:can't substitute for parameter %d when that input is unconnected", p->paramid);
+                }
+            } else {
+                /* we have $N without the .foo */
+                /* use the first resdom in the targetlist of the */
+                /* appropriate child query */
+                tl = inputQlist->qtrees[childno]->targetList;
+                tle = lfirst(tl);
+                return tle->expr;
+            }
+        }
+        else {
+            elog(NOTICE, "tg_rewriteParamsInExpr: unexpected paramkind value of %d", p->paramkind);
+        }
+     }
+     break;
+    case T_Expr: 
+     {
+        /* the node is an expression, we need to recursively
+           call ourselves until we find parameter nodes */
+        List *l;
+        Expr *expr = (Expr*)expression;
+        List *newArgs; 
+
+        /* we have to make a new args lists because Params
+           can be replaced by Var nodes in tg_rewriteParamsInExpr()*/
+        newArgs = NIL;
+
+        /* we only care about argument to expressions,
+           it doesn't matter when the opType is */
+        /* recursively rewrite the arguments of this expression */
+        foreach (l, expr->args) {
+            newArgs = lappend(newArgs,
+                              tg_rewriteParamsInExpr(lfirst(l), inputQlist));
+        }
+        /* change the arguments of the expression */
+        expr->args = newArgs;
+     }
+    break;
+ default:
+     {
+     /* ignore other expr types */
+     }
+ }
+
+    return expression;
+}
+
+
+
+/*
+   getParamTypes:
+      given an element, finds its parameter types.
+      the typev array argument is set to the parameter types. 
+      the parameterCount is returned
+   
+   this code is very similar to ProcedureDefine() in pg_proc.c 
+*/
+static int
+getParamTypes (TgElement *elem, Oid typev[])
+{
+    /* this code is similar to ProcedureDefine() */
+    int16 parameterCount;
+    bool defined;
+    Oid toid;
+    char *t;
+    int i,j;
+
+    parameterCount = 0;
+    for (i=0;i<8;i++) {
+       typev[i] = 0;
+    }
+    for (j=0;j<elem->inTypes->num;j++) {
+       if (parameterCount == 8) {
+           elog(WARN, 
+                "getParamTypes: Ingredients cannot take > 8 arguments"); 
+       }
+       t = elem->inTypes->val[j];
+       if (strcmp(t,"opaque") == 0) {
+           elog(WARN, 
+                "getParamTypes: Ingredient functions cannot take type 'opaque'");
+       } else {
+           toid = TypeGet(elem->inTypes->val[j], &defined);
+           if (!OidIsValid(toid)) {
+               elog(WARN, "getParamTypes: arg type '%s' is not defined",t);
+           }
+           if (!defined) {
+               elog(NOTICE, "getParamTypes: arg type '%s' is only a shell",t);
+           }
+       }
+       typev[parameterCount++] = toid;
+    }
+
+    return parameterCount;
+}
+
+
+/*
+ * tg_parseTeeNode
+ *    
+ *   handles the parsing of the tee node 
+ *   
+ *
+ */
+
+static QueryTreeList*
+tg_parseTeeNode(TgRecipe *r, 
+               TgNode *n,  /* the tee node */
+               int i, /* which input this node is to its parent */
+               QueryTreeList *qList,
+               TeeInfo* teeInfo)
+
+{
+    QueryTreeList *q;
+    char* tt;
+    int rt_ind;
+    Query* orig;
+
+    /* the input Node is a tee node, so we need to do the following:
+     *  we need to parse the child of the tee node,
+     we add that to our query tree list
+     *  we need the name of the tee node table 
+     the tee node table is the table into which the tee node
+     may materialize results.  Call it TT
+     *  we add a range table to our existing query with TT in it
+     *  we need to replace the parameter $i with TT
+     (otherwise the optimizer won't know to use the table
+     on expression containining $i)
+     After that rewrite, the optimizer will generate
+     sequential scans of TT
+     
+     Later, in the glue phase, we replace all instances of TT
+     sequential scans with the actual Tee node
+     */
+    q = tg_parseSubQuery(r,n, teeInfo);
+
+    /* tt is the name of the tee node table */
+    tt = n->nodeName;
+
+    if (q) 
+       appendTeeQuery(teeInfo,q,tt);
+
+    orig = qList->qtrees[0];
+    rt_ind = RangeTablePosn(orig->rtable,tt);
+    /* check to see that this table is not part of 
+       the range table already.  This usually only 
+       happens if multiple inputs are connected to the
+       same Tee. */
+    if (rt_ind == 0) {
+       orig->rtable = lappend(orig->rtable,
+                              makeRangeTableEntry(tt,
+                                                  FALSE,
+                                                  NULL,
+                                                  tt));
+       rt_ind = length(orig->rtable);
+    }
+                       
+    orig->qual = tg_replaceNumberedParam(orig->qual,
+                                        i+1, /* params start at 1*/
+                                        rt_ind,
+                                        tt);
+    return qList;
+}
+
+
+/*
+ * tg_parseSubQuery:
+ *    go backwards from a node and parse the query
+ *
+ *   the result parse tree is passed back
+ * 
+ * could return NULL if trying to parse a teeNode 
+ * that's already been processed by another parent
+ * 
+ */
+
+static QueryTreeList*
+tg_parseSubQuery(TgRecipe* r, TgNode* n, TeeInfo* teeInfo)
+{
+    TgElement *elem;
+    char* funcName;
+    Oid typev[8];     /* eight arguments maximum  */
+    int i;
+    int parameterCount;
+
+    QueryTreeList *qList;      /* the parse tree of the nodeElement */
+    QueryTreeList *inputQlist; /* the list of parse trees for the
+                                 inputs to this node */
+    QueryTreeList *q;
+    Oid relid;
+    TgNode* child;
+    Relation rel;
+    unsigned int len;
+    TupleDesc tupdesc;
+
+    qList = NULL;
+
+    if (n->nodeType == TG_INGRED_NODE) {
+       /* parse each ingredient node in turn */
+
+       elem = n->nodeElem;
+       switch (elem->srcLang) {
+       case TG_SQL:
+           { 
+               /* for SQL ingredients, the SQL query is contained in the
+                  'src' field  */
+
+#ifdef DEBUG_RECIPE
+elog(NOTICE,"calling parser with %s",elem->src);
+#endif  /* DEBUG_RECIPE */
+
+               parameterCount = getParamTypes(elem,typev);
+
+               qList = parser(elem->src,typev,parameterCount);
+
+               if (qList->len > 1) {
+                   elog(NOTICE,
+                        "tg_parseSubQuery: parser produced > 1 query tree");
+               }
+           }
+           break;
+       case TG_C:
+           {
+               /* C ingredients are registered functions in postgres */
+               /* we create a new query string by using the function name
+                  (found in the 'src' field) and adding parameters to it
+                  so if the function was FOOBAR and took in two arguments,
+                  we would create a string
+                        select FOOBAR($1,$2)
+                  */
+               char newquery[1000];
+
+               funcName = elem->src;
+               parameterCount = getParamTypes(elem,typev);
+
+               if (parameterCount > 0) {
+                   int i;
+                   sprintf(newquery,"select %s($1",funcName);
+                   for (i=1;i<parameterCount;i++) {
+                       sprintf(newquery,"%s,$%d",newquery,i);
+                   }
+                   sprintf(newquery,"%s)",newquery);
+               } else
+                   sprintf(newquery,"select %s()",funcName);
+
+#ifdef DEBUG_RECIPE
+elog(NOTICE,"calling parser with %s", newquery);
+#endif  /* DEBUG_RECIPE */
+
+               qList = parser(newquery,typev,parameterCount);
+               if (qList->len > 1) {
+                   elog(NOTICE,
+                        "tg_parseSubQuery: parser produced > 1 query tree");
+               }
+           }
+           break;
+       case TG_RECIPE_GRAPH:
+            elog(NOTICE,"tg_parseSubQuery: can't parse recipe graph ingredients yet!");
+           break;
+       case TG_COMPILED:
+            elog(NOTICE,"tg_parseSubQuery: can't parse compiled ingredients yet!");
+           break;
+       default:
+           elog(NOTICE,"tg_parseSubQuery: unknown srcLang: %d",elem->srcLang);
+       }
+
+       /* parse each of the subrecipes that are input to this node*/
+
+       if (n->inNodes->num > 0) {
+           inputQlist = malloc(sizeof(QueryTreeList));
+           inputQlist->len = n->inNodes->num + 1 ;
+           inputQlist->qtrees = (Query**)malloc(inputQlist->len * sizeof(Query*));
+           for (i=0;i<n->inNodes->num;i++) {
+
+               inputQlist->qtrees[i] = NULL;
+               if (n->inNodes->val[i]) {
+                   if (n->inNodes->val[i]->nodeType == TG_TEE_NODE) {
+                       qList = tg_parseTeeNode(r,n->inNodes->val[i],
+                                               i,qList,teeInfo);
+                   }
+                   else 
+                       { /* input node is not a Tee */
+                           q = tg_parseSubQuery(r,n->inNodes->val[i],
+                                                teeInfo);
+                           Assert (q->len == 1);
+                           inputQlist->qtrees[i] = q->qtrees[0];
+                       }
+               }
+           }
+
+           /* now, we have all the query trees from our input nodes */
+           /* transform the original parse tree appropriately */
+           tg_rewriteQuery(r,n,qList,inputQlist);
+       }
+    }
+    else if (n->nodeType == TG_EYE_NODE) {
+       /* if we hit an eye, we need to stop and make what we have 
+          into a subrecipe query block*/
+       elog(NOTICE,"tg_parseSubQuery: can't handle eye nodes yet");
+    }
+    else if (n->nodeType == TG_TEE_NODE) {
+       /* if we hit a tee, check to see if the parsing has been done
+          for this tee already by the other parent */
+       
+       rel = RelationNameGetRelation(n->nodeName);
+       if (RelationIsValid(rel)) {
+           /* this tee has already been visited, 
+              no need to do any further processing */
+           return NULL;
+       } else {
+           /* we need to process the child of the tee first, */
+           child = n->inNodes->val[0];
+
+           if (child->nodeType == TG_TEE_NODE) {
+               /* nested Tee nodes */
+               qList = tg_parseTeeNode(r,child,0,qList,teeInfo);
+               return qList;
+           }
+
+           Assert (child != NULL);
+           
+           /* parse the input node */
+           q = tg_parseSubQuery(r,child, teeInfo);
+           Assert (q->len == 1);
+
+           /* add the parsed query to the main list of queries */
+           qList = appendQlist(qList,q);
+
+           /* need to create the tee table here */
+           /* the tee table created is used both for materializing the values
+              at the tee node, and for parsing and optimization.
+              The optimization needs to have a real table before it will
+              consider scans on it */
+
+           /* first, find the type of the tuples being produced by the 
+              tee.  The type is the same as the output type of 
+              the child node.  
+
+              NOTE: we are assuming that the child node only has a single
+              output here! */
+           getParamTypes(child->nodeElem,typev);
+
+           /* the output type is either a complex type, 
+              (and is thus a relation) or is a simple type */
+           
+           rel = RelationNameGetRelation(child->nodeElem->outTypes->val[0]);
+
+           if (RelationIsValid(rel)) {
+               /* for complex types, create new relation with the same
+                  tuple descriptor as the output table type*/
+                len = length(q->qtrees[0]->targetList);
+               tupdesc = rel->rd_att;
+               
+               relid = heap_create(child->nodeElem->outTypes->val[0],
+                                   NULL, /* XXX */
+                                   'n',
+                                   DEFAULT_SMGR,
+                                   tupdesc);
+           }
+           else {
+               /* we have to create a relation with one attribute of
+                  the simple base type.  That attribute will have
+                   an attr name of "result" */
+               /*NOTE: ignore array types for the time being */
+
+               len = 1;
+               tupdesc = CreateTemplateTupleDesc(len);
+
+               if ( !TupleDescInitEntry(tupdesc,1,
+                                         "result",
+                                        NULL,
+                                        0, false)) {
+                      elog(NOTICE,"tg_parseSubQuery: unexpected result from TupleDescInitEntry");
+                } else {
+                    relid =  heap_create(child->nodeElem->outTypes->val[0],
+                                         NULL, /* XXX */
+                                         'n',
+                                         DEFAULT_SMGR,
+                                         tupdesc);
+                 }
+           }
+       }
+    }
+    else if (n->nodeType == TG_RECIPE_NODE) {
+       elog(NOTICE,"tg_parseSubQuery: can't handle embedded recipes yet!");
+    } else
+       elog (NOTICE, "unknown nodeType: %d", n->nodeType);
+
+    return qList;
+}
+
+/*
+ * OffsetVarAttno -
+ *    recursively find all the var nodes with the specified varno
+ * and offset their varattno with the offset
+ * 
+ *  code is similar to OffsetVarNodes in rewriteManip.c
+ */
+
+void
+OffsetVarAttno(Node* node, int varno, int offset)
+{
+    if (node == NULL) return;
+    switch (nodeTag(node)) {
+    case T_TargetEntry:
+       {
+           TargetEntry *tle = (TargetEntry *)node;
+           OffsetVarAttno(tle->expr, varno, offset);
+       }
+       break;
+    case T_Expr:
+       {
+           Expr *expr = (Expr*)node;
+           OffsetVarAttno((Node*)expr->args, varno, offset);
+       }
+       break;
+    case T_Var:
+       {
+           Var *var = (Var*)node;
+           if (var->varno == varno)
+               var->varattno += offset;
+       }
+       break;
+    case T_List:
+       {
+           List *l;
+
+           foreach(l, (List*)node) {
+               OffsetVarAttno(lfirst(l), varno, offset);
+           }
+       }
+       break;
+    default:
+       /* ignore the others */
+       break;
+    }
+}
+
+/*
+ * appendQlist 
+ *    add the contents of a QueryTreeList q2 to the end of the QueryTreeList
+ *   q1
+ *
+ *  returns a new querytree list
+ */
+
+QueryTreeList*
+appendQlist(QueryTreeList *q1, QueryTreeList *q2)
+{
+    QueryTreeList* newq;
+    int i,j;
+    int newlen;
+    
+    if (q1 == NULL)
+       return q2;
+
+    if (q2 == NULL)
+       return q1;
+
+    newlen = q1->len + q2->len;
+    newq = (QueryTreeList*)malloc(sizeof(QueryTreeList));
+    newq->len = newlen;
+    newq->qtrees = (Query**)malloc(newlen * sizeof(Query*));
+    for (i=0;i<q1->len;i++)
+       newq->qtrees[i] = q1->qtrees[i];
+    for (j=0;j<q2->len;j++) {
+       newq->qtrees[i + j] = q2->qtrees[j];
+    }
+    return newq;
+}
+
+/*
+ * appendTeeQuery 
+ *    
+ *  modify the query field of the teeInfo list of the particular tee node
+ */
+static void
+appendTeeQuery(TeeInfo *teeInfo, QueryTreeList *q, char* teeNodeName)
+{
+    int i;
+    
+    Assert(teeInfo);
+
+    for (i=0;i<teeInfo->num;i++) {
+       if ( strcmp(teeInfo->val[i].tpi_relName, teeNodeName) == 0) {
+
+           Assert(q->len == 1);
+           teeInfo->val[i].tpi_parsetree = q->qtrees[0];
+           return;
+       }
+    }
+    elog(NOTICE, "appendTeeQuery: teeNodeName '%s' not found in teeInfo");
+}
+
+
+
+/*
+ * replaceSeqScan 
+ *    replaces sequential scans of a specified relation with the tee plan
+ *  the relation is specified by its index in the range table,   rt_ind
+ *
+ * returns the modified plan
+ * the offset_attno is the offset that needs to be added to the parent's
+ * qual or targetlist because the child plan has been replaced with a tee node
+ */
+static void
+replaceSeqScan(Plan* plan, Plan* parent,
+              int rt_ind, Plan* tplan)
+{
+    Scan* snode;
+    Tee* teePlan;
+    Result* newPlan;
+
+    if (plan == NULL) {
+       return;
+    }
+
+    if (plan->type == T_SeqScan) {
+       snode = (Scan*)plan;
+       if (snode->scanrelid == rt_ind) {
+           /* found the sequential scan that should be replaced
+              with the tplan. */
+           /* we replace the plan, but we also need to modify its parent*/
+
+           /* replace the sequential scan with a Result node
+              the reason we use a result node is so that we get the proper
+              projection behavior.  The Result node is simply (ab)used as
+              a projection node */
+
+           newPlan = makeNode(Result);
+           newPlan->plan.cost = 0.0;
+           newPlan->plan.state = (EState*)NULL;
+           newPlan->plan.targetlist = plan->targetlist;
+           newPlan->plan.lefttree = tplan;
+           newPlan->plan.righttree = NULL;
+           newPlan->resconstantqual = NULL;
+           newPlan->resstate = NULL;
+
+           /* change all the varno's to 1*/
+           ChangeVarNodes((Node*)newPlan->plan.targetlist,
+                          snode->scanrelid, 1);
+
+           if (parent) {
+               teePlan = (Tee*)tplan;
+
+               if (parent->lefttree == plan)
+                   parent->lefttree = (Plan*)newPlan;
+               else
+                   parent->righttree = (Plan*)newPlan;
+               
+
+               if (teePlan->leftParent == NULL)
+                   teePlan->leftParent = (Plan*)newPlan;
+               else
+                   teePlan->rightParent = (Plan*)newPlan;
+
+/* comment for now to test out executor-stuff
+               if (parent->state) {
+                   ExecInitNode((Plan*)newPlan, parent->state, (Plan*)newPlan);
+               }
+*/
+           }
+       }
+
+    } else {
+       if (plan->lefttree) {
+           replaceSeqScan(plan->lefttree, plan, rt_ind, tplan);
+       }
+       if (plan->righttree) {
+           replaceSeqScan(plan->righttree, plan, rt_ind, tplan);
+       }
+    }
+}
+
+/*
+ * replaceTeeScans 
+ *    places the sequential scans of the Tee table with 
+ * a connection to the actual tee plan node
+ */
+static Plan* 
+replaceTeeScans(Plan* plan, Query* parsetree, TeeInfo *teeInfo)
+{
+
+    int i;
+    List* rtable;
+    RangeTblEntry *rte;
+    char prefix[5];
+    int rt_ind;
+    Plan* tplan;
+
+    rtable = parsetree->rtable;
+    if (rtable == NULL)
+       return plan;
+
+    /* look through the range table for the tee relation entry,
+       that will give use the varno we need to detect which
+       sequential scans need to be replaced with tee nodes*/
+
+    rt_ind = 0;
+    while (rtable != NIL) {
+       rte = lfirst(rtable);
+       rtable = lnext(rtable);
+       rt_ind++; /* range table references in varno fields start w/ 1 */
+
+       /* look for the "tee_" prefix in the refname,
+          also check to see that the relname and the refname are the same
+          this should eliminate any user-specified table and leave
+          us with the tee table entries only*/
+       if  ((strlen(rte->refname) < 4) ||
+            (strcmp (rte->relname, rte->refname) != 0))
+           continue;
+       strncpy(prefix,rte->refname,4);
+       prefix[4] = '\0';
+       if (strcmp(prefix,"tee_") == 0) {
+           /* okay, we found a tee node entry in the range table */
+
+           /* find the appropriate plan in the teeInfo list */
+           tplan = NULL;
+           for (i=0;i<teeInfo->num;i++) {
+               if (strcmp(teeInfo->val[i].tpi_relName,
+                          rte->refname) == 0) {
+                   tplan = teeInfo->val[i].tpi_plan;
+               }
+           }
+           if (tplan == NULL) {
+               elog(NOTICE, "replaceTeeScans didn't find the corresponding tee plan"); }
+               
+           /* replace the sequential scan node with that var number 
+              with the tee plan node */
+           replaceSeqScan(plan, NULL, rt_ind, tplan);
+       }
+    }
+
+    return plan;
+}
+
+
+
+#endif /* TIOGA */
diff --git a/src/backend/commands/recipe.h b/src/backend/commands/recipe.h
new file mode 100644 (file)
index 0000000..a4e2f24
--- /dev/null
@@ -0,0 +1,17 @@
+/*-------------------------------------------------------------------------
+ *
+ * recipe.h--
+ *    recipe handling routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef RECIPE_H
+#define RECIPE_H
+
+extern void beginRecipe(RecipeStmt* stmt);
+
+#endif /* RECIPE_H */
diff --git a/src/backend/commands/remove.c b/src/backend/commands/remove.c
new file mode 100644 (file)
index 0000000..5c16181
--- /dev/null
@@ -0,0 +1,435 @@
+/*-------------------------------------------------------------------------
+ *
+ * remove.c--
+ *    POSTGRES remove (function | type | operator ) utilty code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "c.h"
+
+#include "access/attnum.h"
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/skey.h"
+#include "utils/builtins.h"
+#include "utils/tqual.h"       /* for NowTimeQual */
+#include "catalog/catname.h"
+#include "commands/defrem.h"
+#include "utils/elog.h"
+
+#include "miscadmin.h"
+
+#include "catalog/pg_aggregate.h"
+#include "catalog/pg_language.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "utils/syscache.h"
+#include "parser/catalog_utils.h"
+#include "storage/bufmgr.h"
+#include "fmgr.h"
+
+/*
+ * RemoveOperator --
+ *     Deletes an operator.
+ *
+ * Exceptions:
+ *     BadArg if name is invalid.
+ *     BadArg if type1 is invalid.
+ *     "WARN" if operator nonexistant.
+ *     ...
+ */
+void
+RemoveOperator(char *operatorName, /* operator name */
+              char *typeName1, /* first type name */
+              char *typeName2) /* optional second type name */
+{
+    Relation   relation;
+    HeapScanDesc scan;
+    HeapTuple  tup;
+    Oid        typeId1 = InvalidOid;
+    Oid        typeId2 = InvalidOid;
+    bool       defined;
+    ItemPointerData    itemPointerData;
+    Buffer      buffer;
+    ScanKeyData operatorKey[3];
+    char *userName;
+    
+     if (typeName1) {
+       typeId1 = TypeGet(typeName1, &defined);
+       if (!OidIsValid(typeId1)) {
+           elog(WARN, "RemoveOperator: type '%s' does not exist", typeName1);
+           return;
+       }
+    }
+    
+    if (typeName2) {
+       typeId2 = TypeGet(typeName2, &defined);
+       if (!OidIsValid(typeId2)) {
+           elog(WARN, "RemoveOperator: type '%s' does not exist", typeName2);
+           return;
+       }
+    }
+    
+    ScanKeyEntryInitialize(&operatorKey[0], 0x0,
+                          Anum_pg_operator_oprname,
+                          NameEqualRegProcedure,
+                          PointerGetDatum(operatorName));
+    
+    ScanKeyEntryInitialize(&operatorKey[1], 0x0,
+                          Anum_pg_operator_oprleft,
+                          ObjectIdEqualRegProcedure,
+                          ObjectIdGetDatum(typeId1));
+    
+    ScanKeyEntryInitialize(&operatorKey[2], 0x0,
+                          Anum_pg_operator_oprright,
+                          ObjectIdEqualRegProcedure,
+                          ObjectIdGetDatum(typeId2));
+    
+    relation = heap_openr(OperatorRelationName);
+    scan = heap_beginscan(relation, 0, NowTimeQual, 3, operatorKey);
+    tup = heap_getnext(scan, 0, &buffer);
+    if (HeapTupleIsValid(tup)) {
+#ifndef NO_SECURITY
+       userName = GetPgUserName();
+       if (!pg_ownercheck(userName,
+                          (char *) ObjectIdGetDatum(tup->t_oid),
+                          OPROID))
+           elog(WARN, "RemoveOperator: operator '%s': permission denied",
+                operatorName);
+#endif
+       ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+       heap_delete(relation, &itemPointerData);
+    } else {
+       if (OidIsValid(typeId1) && OidIsValid(typeId2)) {
+           elog(WARN, "RemoveOperator: binary operator '%s' taking '%s' and '%s' does not exist",
+                operatorName,
+                typeName1,
+                typeName2);
+       } else if (OidIsValid(typeId1)) {
+           elog(WARN, "RemoveOperator: right unary operator '%s' taking '%s' does not exist",
+                operatorName,
+                typeName1);
+       } else {
+           elog(WARN, "RemoveOperator: left unary operator '%s' taking '%s' does not exist",
+                operatorName,
+                typeName2);
+       }
+    }
+    heap_endscan(scan);
+    heap_close(relation);
+}
+
+#ifdef NOTYET
+/*
+ * this stuff is to support removing all reference to a type
+ * don't use it  - pma 2/1/94
+ */
+/*
+ *  SingleOpOperatorRemove
+ *     Removes all operators that have operands or a result of type 'typeOid'.
+ */
+static void
+SingleOpOperatorRemove(Oid typeOid)
+{
+    Relation   rdesc;
+    ScanKeyData        key[3];
+    HeapScanDesc       sdesc;
+    HeapTuple  tup;
+    ItemPointerData itemPointerData;
+    Buffer             buffer;
+    static             attnums[3] = { 7, 8, 9 }; /* left, right, return */
+    register   i;
+    
+    ScanKeyEntryInitialize(&key[0],
+                          0, 0, ObjectIdEqualRegProcedure, (Datum)typeOid);
+    rdesc = heap_openr(OperatorRelationName);
+    for (i = 0; i < 3; ++i) {
+       key[0].sk_attno = attnums[i];
+       sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, key);
+       while (PointerIsValid(tup = heap_getnext(sdesc,  0, &buffer))) {
+           ItemPointerCopy(&tup->t_ctid, &itemPointerData);   
+           /* XXX LOCK not being passed */
+           heap_delete(rdesc, &itemPointerData);
+       }
+       heap_endscan(sdesc);
+    }
+    heap_close(rdesc);
+}
+
+/*
+ *  AttributeAndRelationRemove
+ *     Removes all entries in the attribute and relation relations
+ *     that contain entries of type 'typeOid'.
+ *      Currently nothing calls this code, it is untested.
+ */
+static void
+AttributeAndRelationRemove(Oid typeOid)
+{
+    struct oidlist {
+       Oid             reloid;
+       struct oidlist  *next;
+    };
+    struct oidlist     *oidptr, *optr;
+    Relation           rdesc;
+    ScanKeyData                key[1];
+    HeapScanDesc       sdesc;
+    HeapTuple          tup;
+    ItemPointerData    itemPointerData;
+    Buffer             buffer;
+    
+    /*
+     * Get the oid's of the relations to be removed by scanning the
+     * entire attribute relation.
+     * We don't need to remove the attributes here,
+     * because amdestroy will remove all attributes of the relation.
+     * XXX should check for duplicate relations
+     */
+    
+    ScanKeyEntryInitialize(&key[0],
+                          0, 3, ObjectIdEqualRegProcedure, (Datum)typeOid);
+    
+    oidptr = (struct oidlist *) palloc(sizeof(*oidptr));
+    oidptr->next = NULL;
+    optr = oidptr; 
+    rdesc = heap_openr(AttributeRelationName);
+    sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, key);
+    while (PointerIsValid(tup = heap_getnext(sdesc, 0,  &buffer))) {
+       ItemPointerCopy(&tup->t_ctid, &itemPointerData);   
+       optr->reloid = ((AttributeTupleForm)GETSTRUCT(tup))->attrelid;
+       optr->next = (struct oidlist *) palloc(sizeof(*oidptr));     
+       optr = optr->next;
+    }
+    optr->next = NULL;
+    heap_endscan(sdesc);
+    heap_close(rdesc);
+    
+    
+    ScanKeyEntryInitialize(&key[0], 0,
+                          ObjectIdAttributeNumber,
+                          ObjectIdEqualRegProcedure, (Datum)0);
+    optr = oidptr;
+    rdesc = heap_openr(RelationRelationName);
+    while (PointerIsValid((char *) optr->next)) {
+       key[0].sk_argument = (Datum) (optr++)->reloid;
+       sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 1, key);
+       tup = heap_getnext(sdesc, 0, &buffer);
+       if (PointerIsValid(tup)) {
+           char *name;
+           
+           name = (((Form_pg_class)GETSTRUCT(tup))->relname).data;
+           heap_destroy(name);
+       }
+    }
+    heap_endscan(sdesc);
+    heap_close(rdesc);
+}
+#endif /* NOTYET */
+
+/*
+ *  TypeRemove
+ *     Removes the type 'typeName' and all attributes and relations that
+ *     use it.
+ */
+void
+RemoveType(char *typeName)    /* type name to be removed */
+{
+    Relation   relation;  
+    HeapScanDesc       scan;      
+    HeapTuple  tup;
+    Oid        typeOid;
+    ItemPointerData    itemPointerData;
+    static ScanKeyData typeKey[1] = {
+       { 0, Anum_pg_type_typname, NameEqualRegProcedure }
+    };
+    char *shadow_type;
+    char *userName;
+    
+#ifndef NO_SECURITY
+    userName = GetPgUserName();
+    if (!pg_ownercheck(userName, typeName, TYPNAME))
+       elog(WARN, "RemoveType: type '%s': permission denied",
+            typeName);
+#endif
+    
+    relation = heap_openr(TypeRelationName);
+    fmgr_info(typeKey[0].sk_procedure, &typeKey[0].sk_func,
+             &typeKey[0].sk_nargs);
+    
+    /* Delete the primary type */
+    
+    typeKey[0].sk_argument = PointerGetDatum(typeName);
+    
+    scan = heap_beginscan(relation, 0, NowTimeQual, 1, typeKey);
+    tup = heap_getnext(scan, 0, (Buffer *) 0);
+    if (!HeapTupleIsValid(tup)) {
+       heap_endscan(scan);
+       heap_close(relation);
+       elog(WARN, "RemoveType: type '%s' does not exist",
+            typeName);
+    }
+    typeOid = tup->t_oid;
+    ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+    heap_delete(relation, &itemPointerData);
+    heap_endscan(scan);
+    
+    /* Now, Delete the "array of" that type */
+    shadow_type = makeArrayTypeName(typeName);
+    typeKey[0].sk_argument = NameGetDatum(shadow_type);
+    
+    scan = heap_beginscan(relation, 0, NowTimeQual,
+                         1, (ScanKey) typeKey);
+    tup = heap_getnext(scan, 0,  (Buffer *) 0);
+    
+    if (!HeapTupleIsValid(tup))
+       {
+           elog(WARN, "RemoveType: type '%s': array stub not found",
+                typeName);
+       }
+    typeOid = tup->t_oid;
+    ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+    heap_delete(relation, &itemPointerData);
+    heap_endscan(scan);
+    
+    heap_close(relation);
+}
+
+/*
+ * RemoveFunction --
+ *     Deletes a function.
+ *
+ * Exceptions:
+ *     BadArg if name is invalid.
+ *     "WARN" if function nonexistant.
+ *     ...
+ */
+void
+RemoveFunction(char *functionName, /* function name to be removed */
+              int nargs,
+              List *argNameList /* list of TypeNames */)
+{
+    Relation         relation;
+    HeapScanDesc         scan;
+    HeapTuple        tup;
+    Buffer           buffer = InvalidBuffer;
+    bool                bufferUsed = FALSE;
+    Oid         argList[8];
+    Form_pg_proc        the_proc;
+    ItemPointerData  itemPointerData;
+    static ScanKeyData key[3] = {
+       { 0, Anum_pg_proc_proname, NameEqualRegProcedure }
+    };
+    char *userName;
+    char *typename;
+    int                i;
+    
+    memset(argList, 0, 8 * sizeof(Oid));
+    for (i=0; i<nargs; i++) {
+/*     typename = ((TypeName*)(lfirst(argNameList)))->name; */
+       typename = strVal(lfirst(argNameList));
+       argNameList = lnext(argNameList);
+       
+       if (strcmp(typename, "opaque") == 0)
+           argList[i] = 0;
+       else {
+           tup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(typename),
+                                     0,0,0);
+           
+           if (!HeapTupleIsValid(tup)) {
+               elog(WARN, "RemoveFunction: type '%s' not found",typename);
+           }
+           argList[i] = tup->t_oid;
+       }
+    }
+    
+    tup = SearchSysCacheTuple(PRONAME, PointerGetDatum(functionName),
+                             Int32GetDatum(nargs),
+                             PointerGetDatum(argList),0);
+    if (!HeapTupleIsValid(tup))
+       func_error("RemoveFunction", functionName, nargs, (int*)argList);
+    
+#ifndef NO_SECURITY
+    userName = GetPgUserName();
+    if (!pg_func_ownercheck(userName, functionName, nargs, argList)) {
+       elog(WARN, "RemoveFunction: function '%s': permission denied",
+            functionName);
+    }
+#endif
+    
+    key[0].sk_argument = PointerGetDatum(functionName);
+    
+    fmgr_info(key[0].sk_procedure, &key[0].sk_func, &key[0].sk_nargs);
+    
+    relation = heap_openr(ProcedureRelationName);
+    scan = heap_beginscan(relation, 0, NowTimeQual, 1, key);
+    
+    do { /* hope this is ok because it's indexed */
+       if (bufferUsed) {
+           ReleaseBuffer(buffer);
+           bufferUsed = FALSE;
+       }
+       tup = heap_getnext(scan,  0, (Buffer *) &buffer);
+       if (!HeapTupleIsValid(tup))
+           break;
+       bufferUsed = TRUE;
+       the_proc = (Form_pg_proc) GETSTRUCT(tup);
+    } while ( (namestrcmp(&(the_proc->proname), functionName) == 0) &&
+            (the_proc->pronargs != nargs ||
+             !oid8eq(&(the_proc->proargtypes[0]), &argList[0])));
+    
+    
+    if (!HeapTupleIsValid(tup) || namestrcmp(&(the_proc->proname), 
+                                            functionName) != 0)
+       {       
+           heap_endscan(scan);
+           heap_close(relation);
+           func_error("RemoveFunction", functionName,nargs, (int*)argList);
+       }
+    
+    /* ok, function has been found */
+    
+    if (the_proc->prolang == INTERNALlanguageId)
+       elog(WARN, "RemoveFunction: function \"%-.*s\" is built-in",
+            NAMEDATALEN, functionName);
+    
+    ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+    heap_delete(relation, &itemPointerData);
+    heap_endscan(scan);
+    heap_close(relation);
+}
+
+void
+RemoveAggregate(char *aggName)
+{
+    Relation   relation;
+    HeapScanDesc       scan;
+    HeapTuple  tup;
+    ItemPointerData      itemPointerData;
+    static ScanKeyData key[3] = {
+       { 0, Anum_pg_aggregate_aggname, NameEqualRegProcedure }
+    };
+    
+    key[0].sk_argument = PointerGetDatum(aggName);
+    
+    fmgr_info(key[0].sk_procedure, &key[0].sk_func, &key[0].sk_nargs);
+    relation = heap_openr(AggregateRelationName);
+    scan = heap_beginscan(relation, 0, NowTimeQual, 1, key);
+    tup = heap_getnext(scan, 0, (Buffer *) 0);
+    if (!HeapTupleIsValid(tup)) {
+       heap_endscan(scan);
+       heap_close(relation);
+       elog(WARN, "RemoveAggregate: aggregate '%s' does not exist",
+            aggName);
+    }
+    ItemPointerCopy(&tup->t_ctid, &itemPointerData);
+    heap_delete(relation, &itemPointerData);
+    heap_endscan(scan);
+    heap_close(relation);
+}
diff --git a/src/backend/commands/rename.c b/src/backend/commands/rename.c
new file mode 100644 (file)
index 0000000..c6a44c8
--- /dev/null
@@ -0,0 +1,275 @@
+/*-------------------------------------------------------------------------
+ *
+ * rename.c--
+ *    renameatt() and renamerel() reside here.   
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "access/attnum.h"
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "utils/builtins.h"
+#include "utils/tqual.h"
+
+#include "catalog/catname.h"
+#include "utils/syscache.h"
+#include "catalog/indexing.h"
+#include "catalog/catalog.h"
+
+#include "commands/copy.h"
+
+#include "executor/execdefs.h" /* for EXEC_{FOR,BACK,FDEBUG,BDEBUG} */
+
+#include "storage/buf.h"
+#include "storage/itemptr.h"
+
+#include "miscadmin.h"
+#include "utils/portal.h"
+#include "tcop/dest.h"
+#include "commands/command.h"
+
+#include "utils/excid.h"
+#include "utils/elog.h"
+#include "utils/mcxt.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_class.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/prep.h"    /* for find_all_inheritors */
+
+#ifndef NO_SECURITY
+#include "utils/acl.h"
+#include "utils/syscache.h"
+#endif /* !NO_SECURITY */
+
+/*
+ *     renameatt       - changes the name of a attribute in a relation
+ *
+ *     Attname attribute is changed in attribute catalog.
+ *     No record of the previous attname is kept (correct?).
+ *
+ *     get proper reldesc from relation catalog (if not arg)
+ *     scan attribute catalog
+ *             for name conflict (within rel)
+ *             for original attribute (if not arg)
+ *     modify attname in attribute tuple
+ *     insert modified attribute in attribute catalog
+ *     delete original attribute from attribute catalog
+ *
+ *     XXX Renaming an indexed attribute must (eventually) also change
+ *             the attribute name in the associated indexes.
+ */
+void
+renameatt(char *relname,
+         char *oldattname,
+         char *newattname,
+         char *userName,
+         int recurse)
+{
+    Relation   relrdesc, attrdesc;
+    HeapTuple  reltup, oldatttup, newatttup;
+    ItemPointerData    oldTID;
+    Relation   idescs[Num_pg_attr_indices];
+    
+    /*
+     * permissions checking.  this would normally be done in utility.c,
+     * but this particular routine is recursive.
+     *
+     * normally, only the owner of a class can change its schema.
+     */
+    if (IsSystemRelationName(relname))
+       elog(WARN, "renameatt: class \"%-.*s\" is a system catalog",
+            NAMEDATALEN, relname);
+#ifndef NO_SECURITY
+    if (!IsBootstrapProcessingMode() &&
+       !pg_ownercheck(userName, relname, RELNAME))
+       elog(WARN, "renameatt: you do not own class \"%-.*s\"",
+            NAMEDATALEN, relname);
+#endif
+    
+    /*
+     * if the 'recurse' flag is set then we are supposed to rename this
+     * attribute in all classes that inherit from 'relname' (as well as
+     * in 'relname').
+     *
+     * any permissions or problems with duplicate attributes will cause
+     * the whole transaction to abort, which is what we want -- all or
+     * nothing.
+     */
+    if (recurse) {
+       Oid myrelid, childrelid;
+       List *child, *children;
+       
+       relrdesc = heap_openr(relname);
+       if (!RelationIsValid(relrdesc)) {
+           elog(WARN, "renameatt: unknown relation: \"%-.*s\"",
+                NAMEDATALEN, relname);
+       }
+       myrelid = relrdesc->rd_id;
+       heap_close(relrdesc);
+       
+       /* this routine is actually in the planner */
+       children = find_all_inheritors(lconsi(myrelid, NIL), NIL);
+
+
+       /*
+        * find_all_inheritors does the recursive search of the
+        * inheritance hierarchy, so all we have to do is process
+        * all of the relids in the list that it returns.
+        */
+       foreach (child, children) {
+           char *childname;
+           
+           childrelid = lfirsti(child);
+           if (childrelid == myrelid)
+               continue;
+           relrdesc = heap_open(childrelid);
+           if (!RelationIsValid(relrdesc)) {
+               elog(WARN, "renameatt: can't find catalog entry for inheriting class with oid %d",
+                    childrelid);
+           }
+           childname = (relrdesc->rd_rel->relname).data;
+           heap_close(relrdesc);
+           renameatt(childname, oldattname, newattname,
+                     userName, 0);     /* no more recursion! */
+       }
+    }
+    
+    relrdesc = heap_openr(RelationRelationName);
+    reltup = ClassNameIndexScan(relrdesc, relname);
+    if (!PointerIsValid(reltup)) {
+       heap_close(relrdesc);
+       elog(WARN, "renameatt: relation \"%-.*s\" nonexistent",
+            NAMEDATALEN, relname);
+       return;
+    }
+    heap_close(relrdesc);
+    
+    attrdesc = heap_openr(AttributeRelationName);
+    oldatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, oldattname);
+    if (!PointerIsValid(oldatttup)) {
+       heap_close(attrdesc);
+       elog(WARN, "renameatt: attribute \"%-.*s\" nonexistent",
+            NAMEDATALEN, oldattname);
+    }
+    if (((AttributeTupleForm ) GETSTRUCT(oldatttup))->attnum < 0) {
+       elog(WARN, "renameatt: system attribute \"%-.*s\" not renamed",
+            NAMEDATALEN, oldattname);
+    }
+    
+    newatttup = AttributeNameIndexScan(attrdesc, reltup->t_oid, newattname);
+    if (PointerIsValid(newatttup)) {
+       pfree(oldatttup);
+       heap_close(attrdesc);
+       elog(WARN, "renameatt: attribute \"%-.*s\" exists", 
+            NAMEDATALEN, newattname);
+    }
+    
+    namestrcpy(&(((AttributeTupleForm)(GETSTRUCT(oldatttup)))->attname),
+              newattname);
+    oldTID = oldatttup->t_ctid;
+    
+    /* insert "fixed" tuple */
+    (void) heap_replace(attrdesc, &oldTID, oldatttup);
+    
+    /* keep system catalog indices current */
+    CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
+    CatalogIndexInsert(idescs, Num_pg_attr_indices, attrdesc, oldatttup);
+    CatalogCloseIndices(Num_pg_attr_indices, idescs);
+    
+    heap_close(attrdesc);
+    pfree(oldatttup);
+}
+
+/*
+ *     renamerel       - change the name of a relation
+ *
+ *     Relname attribute is changed in relation catalog.
+ *     No record of the previous relname is kept (correct?).
+ *
+ *     scan relation catalog
+ *             for name conflict
+ *             for original relation (if not arg)
+ *     modify relname in relation tuple
+ *     insert modified relation in relation catalog
+ *     delete original relation from relation catalog
+ *
+ *     XXX Will currently lose track of a relation if it is unable to
+ *             properly replace the new relation tuple.
+ */
+void
+renamerel(char oldrelname[], char newrelname[])
+{
+    Relation   relrdesc;               /* for RELATION relation */
+    HeapTuple  oldreltup, newreltup;
+    ItemPointerData    oldTID;
+    char               oldpath[MAXPGPATH], newpath[MAXPGPATH];
+    Relation   idescs[Num_pg_class_indices];
+    
+    if (IsSystemRelationName(oldrelname)) {
+       elog(WARN, "renamerel: system relation \"%-.*s\" not renamed",
+            NAMEDATALEN, oldrelname);
+       return;
+    }
+    if (IsSystemRelationName(newrelname)) {
+       elog(WARN, "renamerel: Illegal class name: \"%-.*s\" -- pg_ is reserved for system catalogs",
+            NAMEDATALEN, newrelname);
+       return;
+    }
+    
+    relrdesc = heap_openr(RelationRelationName);
+    oldreltup = ClassNameIndexScan(relrdesc, oldrelname);
+    
+    if (!PointerIsValid(oldreltup)) {
+       heap_close(relrdesc);
+       elog(WARN, "renamerel: relation \"%-.*s\" does not exist",
+            NAMEDATALEN, oldrelname);
+    }
+    
+    newreltup = ClassNameIndexScan(relrdesc, newrelname);
+    if (PointerIsValid(newreltup)) {
+       pfree(oldreltup);
+       heap_close(relrdesc);
+       elog(WARN, "renamerel: relation \"%-.*s\" exists", 
+            NAMEDATALEN, newrelname);
+    }
+    
+    /* rename the directory first, so if this fails the rename's not done */
+    (void) strcpy(oldpath, relpath(oldrelname));
+    (void) strcpy(newpath, relpath(newrelname));
+    if (rename(oldpath, newpath) < 0)
+       elog(WARN, "renamerel: unable to rename file: %m");
+    
+    memmove((char *) (((Form_pg_class) GETSTRUCT(oldreltup))->relname.data),
+           newrelname,
+           NAMEDATALEN);
+    oldTID = oldreltup->t_ctid;
+    
+    /* insert fixed rel tuple */
+    (void) heap_replace(relrdesc, &oldTID, oldreltup);
+    
+    /* keep the system catalog indices current */
+    CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
+    CatalogIndexInsert(idescs, Num_pg_class_indices, relrdesc, oldreltup);
+    CatalogCloseIndices(Num_pg_class_indices, idescs);
+    
+    pfree(oldreltup);
+    heap_close(relrdesc);
+}
diff --git a/src/backend/commands/rename.h b/src/backend/commands/rename.h
new file mode 100644 (file)
index 0000000..07389ea
--- /dev/null
@@ -0,0 +1,24 @@
+/*-------------------------------------------------------------------------
+ *
+ * rename.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        RENAME_H
+#define        RENAME_H
+
+extern void renameatt(char *relname, 
+                     char *oldattname,
+                     char *newattname, 
+                     char *userName, int recurse);
+
+extern void renamerel(char *oldrelname,
+                     char *newrelname);
+
+#endif /* RENAME_H */
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
new file mode 100644 (file)
index 0000000..3da15b8
--- /dev/null
@@ -0,0 +1,853 @@
+/*-------------------------------------------------------------------------
+ *
+ * vacuum.c--
+ *    the postgres vacuum cleaner
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <sys/file.h>
+
+#include "postgres.h"
+#include "utils/portal.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/xact.h"
+#include "storage/bufmgr.h"
+#include "access/transam.h"
+#include "utils/tqual.h"
+#include "access/htup.h"
+
+#include "catalog/pg_index.h"
+#include "catalog/catname.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_proc.h"
+
+#include "storage/fd.h"                /* for O_ */
+#include "storage/itemid.h"
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+#include "storage/smgr.h"
+
+#include "utils/elog.h"
+#include "utils/mcxt.h"
+#include "utils/palloc.h"
+
+#include "commands/vacuum.h"
+
+bool VacuumRunning =   false;
+
+/* non-export function prototypes */
+static void _vc_init(char *vacrel);
+static void _vc_shutdown(char *vacrel);
+static void _vc_vacuum(char *vacrel);
+static VRelList _vc_getrels(Portal p, char *vacrel);
+static void _vc_vacone(Portal p, VRelList curvrl);
+static void _vc_vacheap(Portal p, VRelList curvrl, Relation onerel);
+static void _vc_vacindices(VRelList curvrl, Relation onerel);
+static void _vc_vaconeind(VRelList curvrl, Relation indrel);
+static void _vc_updstats(Oid relid, int npages, int ntuples, bool hasindex);
+static void _vc_setpagelock(Relation rel, BlockNumber blkno);
+static bool _vc_ontidlist(ItemPointer itemptr, VTidList tidlist);
+static void _vc_reaptid(Portal p, VRelList curvrl, BlockNumber blkno,
+                       OffsetNumber offnum);
+static void _vc_free(Portal p, VRelList vrl);
+static Relation _vc_getarchrel(Relation heaprel);
+static void _vc_archive(Relation archrel, HeapTuple htup);
+static bool _vc_isarchrel(char *rname);
+
+void
+vacuum(char *vacrel)
+{
+    /* initialize vacuum cleaner */
+    _vc_init(vacrel);
+
+    /* vacuum the database */
+    _vc_vacuum(vacrel);
+
+    /* clean up */
+    _vc_shutdown(vacrel);
+}
+
+/*
+ *  _vc_init(), _vc_shutdown() -- start up and shut down the vacuum cleaner.
+ *
+ *     We run exactly one vacuum cleaner at a time.  We use the file system
+ *     to guarantee an exclusive lock on vacuuming, since a single vacuum
+ *     cleaner instantiation crosses transaction boundaries, and we'd lose
+ *     postgres-style locks at the end of every transaction.
+ *
+ *     The strangeness with committing and starting transactions in the
+ *     init and shutdown routines is due to the fact that the vacuum cleaner
+ *     is invoked via a sql command, and so is already executing inside
+ *     a transaction.  We need to leave ourselves in a predictable state
+ *     on entry and exit to the vacuum cleaner.  We commit the transaction
+ *     started in PostgresMain() inside _vc_init(), and start one in
+ *     _vc_shutdown() to match the commit waiting for us back in
+ *     PostgresMain().
+ */
+static void
+_vc_init(char *vacrel)
+{
+    int fd;
+
+    if ((fd = open("pg_vlock", O_CREAT|O_EXCL, 0600)) < 0)
+       elog(WARN, "can't create lock file -- another vacuum cleaner running?");
+
+    close(fd);
+
+    /*
+     *  By here, exclusive open on the lock file succeeded.  If we abort
+     *  for any reason during vacuuming, we need to remove the lock file.
+     *  This global variable is checked in the transaction manager on xact
+     *  abort, and the routine vc_abort() is called if necessary.
+     */
+
+    VacuumRunning = true;
+
+    /* matches the StartTransaction in PostgresMain() */
+    CommitTransactionCommand();
+}
+
+static void
+_vc_shutdown(char *vacrel)
+{
+    /* on entry, not in a transaction */
+    if (unlink("pg_vlock") < 0)
+       elog(WARN, "vacuum: can't destroy lock file!");
+
+    /* okay, we're done */
+    VacuumRunning = false;
+
+    /* matches the CommitTransaction in PostgresMain() */
+    StartTransactionCommand();
+}
+
+void
+vc_abort()
+{
+    /* on abort, remove the vacuum cleaner lock file */
+    (void) unlink("pg_vlock");
+
+    VacuumRunning = false;
+}
+
+/*
+ *  _vc_vacuum() -- vacuum the database.
+ *
+ *     This routine builds a list of relations to vacuum, and then calls
+ *     code that vacuums them one at a time.  We are careful to vacuum each
+ *     relation in a separate transaction in order to avoid holding too many
+ *     locks at one time.
+ */
+static void
+_vc_vacuum(char *vacrel)
+{
+    VRelList vrl, cur;
+    char *pname;
+    Portal p;
+
+    /*
+     *  Create a portal for safe memory across transctions.  We need to
+     *  palloc the name space for it because our hash function expects
+     * the name to be on a longword boundary.  CreatePortal copies the
+     *  name to safe storage for us.
+     */
+
+    pname = (char *) palloc(strlen(VACPNAME) + 1);
+    strcpy(pname, VACPNAME);
+    p = CreatePortal(pname);
+    pfree(pname);
+
+    /* get list of relations */
+    vrl = _vc_getrels(p, vacrel);
+
+    /* vacuum each heap relation */
+    for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next)
+       _vc_vacone(p, cur);
+
+    _vc_free(p, vrl);
+
+    PortalDestroy(&p);
+}
+
+static VRelList
+_vc_getrels(Portal p, char *vacrel)
+{
+    Relation pgclass;
+    TupleDesc pgcdesc;
+    HeapScanDesc pgcscan;
+    HeapTuple pgctup;
+    Buffer buf;
+    PortalVariableMemory portalmem;
+    MemoryContext old;
+    VRelList vrl, cur;
+    Datum d;
+    char *rname;
+    int16 smgrno;
+    bool n;
+    ScanKeyData  pgckey;
+
+    StartTransactionCommand();
+
+    if (vacrel) {
+       ScanKeyEntryInitialize(&pgckey, 0x0, Anum_pg_class_relname,
+                              NameEqualRegProcedure, 
+                              PointerGetDatum(vacrel));
+    } else {
+       ScanKeyEntryInitialize(&pgckey, 0x0, Anum_pg_class_relkind,
+                              CharacterEqualRegProcedure, CharGetDatum('r'));
+    }
+
+    portalmem = PortalGetVariableMemory(p);
+    vrl = (VRelList) NULL;
+
+    pgclass = heap_openr(RelationRelationName);
+    pgcdesc = RelationGetTupleDescriptor(pgclass);
+
+    pgcscan = heap_beginscan(pgclass, false, NowTimeQual, 1, &pgckey);
+
+    while (HeapTupleIsValid(pgctup = heap_getnext(pgcscan, 0, &buf))) {
+
+       /*
+        *  We have to be careful not to vacuum the archive (since it
+        *  already contains vacuumed tuples), and not to vacuum
+        *  relations on write-once storage managers like the Sony
+        *  jukebox at Berkeley.
+        */
+
+       d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relname,
+                                pgcdesc, &n);
+       rname = (char*)d;
+
+       /* skip archive relations */
+       if (_vc_isarchrel(rname)) {
+           ReleaseBuffer(buf);
+           continue;
+       }
+
+       d = (Datum) heap_getattr(pgctup, buf, Anum_pg_class_relsmgr,
+                                pgcdesc, &n);
+       smgrno = DatumGetInt16(d);
+
+       /* skip write-once storage managers */
+       if (smgriswo(smgrno)) {
+           ReleaseBuffer(buf);
+           continue;
+       }
+
+       /* get a relation list entry for this guy */
+       old = MemoryContextSwitchTo((MemoryContext)portalmem);
+       if (vrl == (VRelList) NULL) {
+           vrl = cur = (VRelList) palloc(sizeof(VRelListData));
+       } else {
+           cur->vrl_next = (VRelList) palloc(sizeof(VRelListData));
+           cur = cur->vrl_next;
+       }
+       (void) MemoryContextSwitchTo(old);
+
+       cur->vrl_relid = pgctup->t_oid;
+       cur->vrl_attlist = (VAttList) NULL;
+       cur->vrl_tidlist = (VTidList) NULL;
+       cur->vrl_npages = cur->vrl_ntups = 0;
+       cur->vrl_hasindex = false;
+       cur->vrl_next = (VRelList) NULL;
+
+       /* wei hates it if you forget to do this */
+       ReleaseBuffer(buf);
+    }
+
+    heap_close(pgclass);
+    heap_endscan(pgcscan);
+
+    CommitTransactionCommand();
+
+    return (vrl);
+}
+
+/*
+ *  _vc_vacone() -- vacuum one heap relation
+ *
+ *     This routine vacuums a single heap, cleans out its indices, and
+ *     updates its statistics npages and ntuples statistics.
+ *
+ *     Doing one heap at a time incurs extra overhead, since we need to
+ *     check that the heap exists again just before we vacuum it.  The
+ *     reason that we do this is so that vacuuming can be spread across
+ *     many small transactions.  Otherwise, two-phase locking would require
+ *     us to lock the entire database during one pass of the vacuum cleaner.
+ */
+static void
+_vc_vacone(Portal p, VRelList curvrl)
+{
+    Relation pgclass;
+    TupleDesc pgcdesc;
+    HeapTuple pgctup;
+    Buffer pgcbuf;
+    HeapScanDesc pgcscan;
+    Relation onerel;
+    ScanKeyData pgckey;
+
+    StartTransactionCommand();
+
+    ScanKeyEntryInitialize(&pgckey, 0x0, ObjectIdAttributeNumber,
+                          ObjectIdEqualRegProcedure,
+                          ObjectIdGetDatum(curvrl->vrl_relid));
+
+    pgclass = heap_openr(RelationRelationName);
+    pgcdesc = RelationGetTupleDescriptor(pgclass);
+    pgcscan = heap_beginscan(pgclass, false, NowTimeQual, 1, &pgckey);
+
+    /*
+     *  Race condition -- if the pg_class tuple has gone away since the
+     *  last time we saw it, we don't need to vacuum it.
+     */
+
+    if (!HeapTupleIsValid(pgctup = heap_getnext(pgcscan, 0, &pgcbuf))) {
+       heap_endscan(pgcscan);
+       heap_close(pgclass);
+       CommitTransactionCommand();
+       return;
+    }
+
+    /* now open the class and vacuum it */
+    onerel = heap_open(curvrl->vrl_relid);
+
+    /* we require the relation to be locked until the indices are cleaned */
+    RelationSetLockForWrite(onerel);
+
+    /* vacuum it */
+    _vc_vacheap(p, curvrl, onerel);
+
+    /* if we vacuumed any heap tuples, vacuum the indices too */
+    if (curvrl->vrl_tidlist != (VTidList) NULL)
+       _vc_vacindices(curvrl, onerel);
+    else
+       curvrl->vrl_hasindex = onerel->rd_rel->relhasindex;
+
+    /* all done with this class */
+    heap_close(onerel);
+    heap_endscan(pgcscan);
+    heap_close(pgclass);
+
+    /* update statistics in pg_class */
+    _vc_updstats(curvrl->vrl_relid, curvrl->vrl_npages, curvrl->vrl_ntups,
+                curvrl->vrl_hasindex);
+
+    CommitTransactionCommand();
+}
+
+/*
+ *  _vc_vacheap() -- vacuum an open heap relation
+ *
+ *     This routine sets commit times, vacuums dead tuples, cleans up
+ *     wasted space on the page, and maintains statistics on the number
+ *     of live tuples in a heap.  In addition, it records the tids of
+ *     all tuples removed from the heap for any reason.  These tids are
+ *     used in a scan of indices on the relation to get rid of dead
+ *     index tuples.
+ */
+static void
+_vc_vacheap(Portal p, VRelList curvrl, Relation onerel)
+{
+    int nblocks, blkno;
+    ItemId itemid;
+    HeapTuple htup;
+    Buffer buf;
+    Page page;
+    OffsetNumber offnum, maxoff;
+    Relation archrel;
+    bool isarchived;
+    int nvac;
+    int ntups;
+    bool pgchanged, tupgone;
+    AbsoluteTime purgetime, expiretime;
+    RelativeTime preservetime;
+
+    nvac = 0;
+    ntups = 0;
+    nblocks = RelationGetNumberOfBlocks(onerel);
+
+    {
+       char *relname;
+       relname = (RelationGetRelationName(onerel))->data;
+
+       if ( (strlen(relname) > 4) && 
+           relname[0] == 'X' &&
+           relname[1] == 'i' &&
+           relname[2] == 'n' &&
+           (relname[3] == 'v' || relname[3] == 'x'))
+           return;
+    }
+
+
+    /* if the relation has an archive, open it */
+    if (onerel->rd_rel->relarch != 'n') {
+       isarchived = true;
+       archrel = _vc_getarchrel(onerel);
+    } else
+       isarchived = false;
+
+    /* don't vacuum large objects for now.
+       something breaks when we do*/
+    {
+       char *relname;
+       relname = (RelationGetRelationName(onerel))->data;
+
+       if ( (strlen(relname) > 4) && 
+           relname[0] == 'X' &&
+           relname[1] == 'i' &&
+           relname[2] == 'n' &&
+           (relname[3] == 'v' || relname[3] == 'x'))
+           return;
+    }
+
+    /* calculate the purge time: tuples that expired before this time
+       will be archived or deleted */
+    purgetime = GetCurrentTransactionStartTime();
+    expiretime = (AbsoluteTime)onerel->rd_rel->relexpires;
+    preservetime = (RelativeTime)onerel->rd_rel->relpreserved;
+
+    if (RelativeTimeIsValid(preservetime) && (preservetime)) {
+       purgetime -= preservetime;
+       if (AbsoluteTimeIsBackwardCompatiblyValid(expiretime) &&
+           expiretime > purgetime)
+           purgetime = expiretime;
+    }
+
+    else if (AbsoluteTimeIsBackwardCompatiblyValid(expiretime))
+       purgetime = expiretime;
+           
+    for (blkno = 0; blkno < nblocks; blkno++) {
+       buf = ReadBuffer(onerel, blkno);
+       page = BufferGetPage(buf);
+
+       if (PageIsEmpty(page)) {
+           ReleaseBuffer(buf);
+           continue;
+       }
+
+       pgchanged = false;
+       maxoff = PageGetMaxOffsetNumber(page);
+       for (offnum = FirstOffsetNumber;
+            offnum <= maxoff;
+            offnum = OffsetNumberNext(offnum)) {
+           itemid = PageGetItemId(page, offnum);
+
+           if (!ItemIdIsUsed(itemid))
+               continue;
+
+           htup = (HeapTuple) PageGetItem(page, itemid);
+           tupgone = false;
+
+           if (!AbsoluteTimeIsBackwardCompatiblyValid(htup->t_tmin) && 
+               TransactionIdIsValid((TransactionId)htup->t_xmin)) {
+
+               if (TransactionIdDidAbort(htup->t_xmin)) {
+                   _vc_reaptid(p, curvrl, blkno, offnum);
+                   pgchanged = true;
+                   tupgone = true;
+               } else if (TransactionIdDidCommit(htup->t_xmin)) {
+                   htup->t_tmin = TransactionIdGetCommitTime(htup->t_xmin);
+                   pgchanged = true;
+               }
+           }
+
+           if (TransactionIdIsValid((TransactionId)htup->t_xmax)) {
+               if (TransactionIdDidAbort(htup->t_xmax)) {
+                   StoreInvalidTransactionId(&(htup->t_xmax));
+                   pgchanged = true;
+               } else if (TransactionIdDidCommit(htup->t_xmax)) {
+                   if (!AbsoluteTimeIsBackwardCompatiblyReal(htup->t_tmax)) {
+
+                       htup->t_tmax = TransactionIdGetCommitTime(htup->t_xmax);  
+                       pgchanged = true;
+                   }
+
+                   /*
+                    *  Reap the dead tuple if its expiration time is
+                    *  before purgetime.
+                    */
+
+                   if (!tupgone && htup->t_tmax < purgetime) {
+                       _vc_reaptid(p, curvrl, blkno, offnum);
+                       tupgone = true;
+                       pgchanged = true;
+                   }
+               }
+           }
+
+           if (tupgone) {
+               ItemId lpp = &(((PageHeader) page)->pd_linp[offnum - 1]);
+
+               /* write the tuple to the archive, if necessary */
+               if (isarchived)
+                   _vc_archive(archrel, htup);
+
+               /* mark it unused */
+               lpp->lp_flags &= ~LP_USED;
+
+               ++nvac;
+           } else {
+               ntups++;
+           }
+       }
+
+       if (pgchanged) {
+           PageRepairFragmentation(page);
+           WriteBuffer(buf);
+       } else {
+           ReleaseBuffer(buf);
+       }
+    }
+
+    if (isarchived)
+       heap_close(archrel);
+
+    /* save stats in the rel list for use later */
+    curvrl->vrl_ntups = ntups;
+    curvrl->vrl_npages = nblocks;
+}
+
+/*
+ *  _vc_vacindices() -- vacuum all the indices for a particular heap relation.
+ *
+ *     On entry, curvrl points at the relation currently being vacuumed.
+ *     We already have a write lock on the relation, so we don't need to
+ *     worry about anyone building an index on it while we're doing the
+ *     vacuuming.  The tid list for curvrl is sorted in reverse tid order:
+ *     that is, tids on higher page numbers are before those on lower page
+ *     numbers, and tids high on the page are before those low on the page.
+ *     We use this ordering to cut down the search cost when we look at an
+ *     index entry.
+ *
+ *     We're executing inside the transaction that vacuumed the heap.
+ */
+static void
+_vc_vacindices(VRelList curvrl, Relation onerel)
+{
+    Relation pgindex;
+    TupleDesc pgidesc;
+    HeapTuple pgitup;
+    HeapScanDesc pgiscan;
+    Buffer buf;
+    Relation indrel;
+    Oid indoid;
+    Datum d;
+    bool n;
+    int nindices;
+    ScanKeyData pgikey;
+
+    /* see if we can dodge doing any work at all */
+    if (!(onerel->rd_rel->relhasindex))
+       return;
+
+    nindices = 0;
+
+    /* prepare a heap scan on the pg_index relation */
+    pgindex = heap_openr(IndexRelationName);
+    pgidesc = RelationGetTupleDescriptor(pgindex);
+
+    ScanKeyEntryInitialize(&pgikey, 0x0, Anum_pg_index_indrelid,
+                          ObjectIdEqualRegProcedure,
+                          ObjectIdGetDatum(curvrl->vrl_relid));
+
+    pgiscan = heap_beginscan(pgindex, false, NowTimeQual, 1, &pgikey);
+
+    /* vacuum all the indices */
+    while (HeapTupleIsValid(pgitup = heap_getnext(pgiscan, 0, &buf))) {
+       d = (Datum) heap_getattr(pgitup, buf, Anum_pg_index_indexrelid,
+                                pgidesc, &n);
+       indoid = DatumGetObjectId(d);
+       indrel = index_open(indoid);
+       _vc_vaconeind(curvrl, indrel);
+       heap_close(indrel);
+       nindices++;
+    }
+
+    heap_endscan(pgiscan);
+    heap_close(pgindex);
+
+    if (nindices > 0)
+       curvrl->vrl_hasindex = true;
+    else
+       curvrl->vrl_hasindex = false;
+}
+
+/*
+ *  _vc_vaconeind() -- vacuum one index relation.
+ *
+ *     Curvrl is the VRelList entry for the heap we're currently vacuuming.
+ *     It's locked.  The vrl_tidlist entry in curvrl is the list of deleted
+ *     heap tids, sorted in reverse (page, offset) order.  Onerel is an
+ *     index relation on the vacuumed heap.  We don't set locks on the index
+ *     relation here, since the indexed access methods support locking at
+ *     different granularities.  We let them handle it.
+ *
+ *     Finally, we arrange to update the index relation's statistics in
+ *     pg_class.
+ */
+static void
+_vc_vaconeind(VRelList curvrl, Relation indrel)
+{
+    RetrieveIndexResult res;
+    IndexScanDesc iscan;
+    ItemPointer heapptr;
+    int nvac;
+    int nitups;
+    int nipages;
+
+    /* walk through the entire index */
+    iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL);
+    nvac = 0;
+    nitups = 0;
+
+    while ((res = index_getnext(iscan, ForwardScanDirection))
+          != (RetrieveIndexResult) NULL) {
+       heapptr = &res->heap_iptr;
+
+       if (_vc_ontidlist(heapptr, curvrl->vrl_tidlist)) {
+#if 0
+           elog(DEBUG, "<%x,%x> -> <%x,%x>",
+                ItemPointerGetBlockNumber(&(res->index_iptr)),
+                ItemPointerGetOffsetNumber(&(res->index_iptr)),
+                ItemPointerGetBlockNumber(&(res->heap_iptr)),
+                ItemPointerGetOffsetNumber(&(res->heap_iptr)));
+#endif
+           ++nvac;
+           index_delete(indrel, &res->index_iptr);
+       } else {
+           nitups++;
+       }
+
+       /* be tidy */
+       pfree(res);
+    }
+
+    index_endscan(iscan);
+
+    /* now update statistics in pg_class */
+    nipages = RelationGetNumberOfBlocks(indrel);
+    _vc_updstats(indrel->rd_id, nipages, nitups, false);
+}
+
+/*
+ *  _vc_updstats() -- update pg_class statistics for one relation
+ *
+ *     This routine works for both index and heap relation entries in
+ *     pg_class.  We violate no-overwrite semantics here by storing new
+ *     values for ntuples, npages, and hasindex directly in the pg_class
+ *     tuple that's already on the page.  The reason for this is that if
+ *     we updated these tuples in the usual way, then every tuple in pg_class
+ *     would be replaced every day.  This would make planning and executing
+ *     historical queries very expensive.
+ */
+static void
+_vc_updstats(Oid relid, int npages, int ntuples, bool hasindex)
+{
+    Relation rd;
+    HeapScanDesc sdesc;
+    HeapTuple tup;
+    Buffer buf;
+    Form_pg_class pgcform;
+    ScanKeyData skey;
+
+    /*
+     * update number of tuples and number of pages in pg_class
+     */
+    ScanKeyEntryInitialize(&skey, 0x0, ObjectIdAttributeNumber,
+                          ObjectIdEqualRegProcedure,
+                          ObjectIdGetDatum(relid));
+
+    rd = heap_openr(RelationRelationName);
+    sdesc = heap_beginscan(rd, false, NowTimeQual, 1, &skey);
+
+    if (!HeapTupleIsValid(tup = heap_getnext(sdesc, 0, &buf)))
+       elog(WARN, "pg_class entry for relid %d vanished during vacuuming",
+                  relid);
+
+    /* overwrite the existing statistics in the tuple */
+    _vc_setpagelock(rd, BufferGetBlockNumber(buf));
+    pgcform = (Form_pg_class) GETSTRUCT(tup);
+    pgcform->reltuples = ntuples;
+    pgcform->relpages = npages;
+    pgcform->relhasindex = hasindex;
+    /* XXX -- after write, should invalidate relcache in other backends */
+    WriteNoReleaseBuffer(buf);
+
+    /* that's all, folks */
+    heap_endscan(sdesc);
+    heap_close(rd);
+
+}
+
+static void _vc_setpagelock(Relation rel, BlockNumber blkno)
+{
+    ItemPointerData itm;
+
+    ItemPointerSet(&itm, blkno, 1);
+
+    RelationSetLockForWritePage(rel, &itm);
+}
+
+/*
+ *  _vc_ontidlist() -- is a particular tid on the supplied tid list?
+ *
+ *     Tidlist is sorted in reverse (page, offset) order.
+ */
+static bool
+_vc_ontidlist(ItemPointer itemptr, VTidList tidlist)
+{
+    BlockNumber ibkno;
+    OffsetNumber ioffno;
+    ItemPointer check;
+    BlockNumber ckbkno;
+    OffsetNumber ckoffno;
+
+    ibkno = ItemPointerGetBlockNumber(itemptr);
+    ioffno = ItemPointerGetOffsetNumber(itemptr);
+
+    while (tidlist != (VTidList) NULL) {
+       check = &(tidlist->vtl_tid);
+       ckbkno = ItemPointerGetBlockNumber(check);
+       ckoffno = ItemPointerGetOffsetNumber(check);
+
+       /* see if we've looked far enough down the list */
+       if ((ckbkno < ibkno) || (ckbkno == ibkno && ckoffno < ioffno))
+           return (false);
+
+       /* see if we have a match */
+       if (ckbkno == ibkno && ckoffno == ioffno)
+           return (true);
+
+       /* check next */
+       tidlist = tidlist->vtl_next;
+    }
+
+    /* ran off the end of the list without finding a match */
+    return (false);
+}
+
+/*
+ *  _vc_reaptid() -- save a tid on the list of reaped tids for the current
+ *                  entry on the vacuum relation list.
+ *
+ *     As a side effect of the way that the vacuuming loop for a given
+ *     relation works, the tids of vacuumed tuples wind up in reverse
+ *     order in the list -- highest tid on a page is first, and higher
+ *     pages come before lower pages.  This is important later when we
+ *     vacuum the indices, as it gives us a way of stopping the search
+ *     for a tid if we notice we've passed the page it would be on.
+ */
+static void
+_vc_reaptid(Portal p,
+           VRelList curvrl,
+           BlockNumber blkno,
+           OffsetNumber offnum)
+{
+    PortalVariableMemory pmem;
+    MemoryContext old;
+    VTidList newvtl;
+
+    /* allocate a VTidListData entry in the portal memory context */
+    pmem = PortalGetVariableMemory(p);
+    old = MemoryContextSwitchTo((MemoryContext) pmem);
+    newvtl = (VTidList) palloc(sizeof(VTidListData));
+    MemoryContextSwitchTo(old);
+
+    /* fill it in */
+    ItemPointerSet(&(newvtl->vtl_tid), blkno, offnum);
+    newvtl->vtl_next = curvrl->vrl_tidlist;
+    curvrl->vrl_tidlist = newvtl;
+}
+
+static void
+_vc_free(Portal p, VRelList vrl)
+{
+    VRelList p_vrl;
+    VAttList p_val, val;
+    VTidList p_vtl, vtl;
+    MemoryContext old;
+    PortalVariableMemory pmem;
+
+    pmem = PortalGetVariableMemory(p);
+    old = MemoryContextSwitchTo((MemoryContext)pmem);
+
+    while (vrl != (VRelList) NULL) {
+
+       /* free attribute list */
+       val = vrl->vrl_attlist;
+       while (val != (VAttList) NULL) {
+           p_val = val;
+           val = val->val_next;
+           pfree(p_val);
+       }
+
+       /* free tid list */
+       vtl = vrl->vrl_tidlist;
+       while (vtl != (VTidList) NULL) {
+           p_vtl = vtl;
+           vtl = vtl->vtl_next;
+           pfree(p_vtl);
+       }
+
+       /* free rel list entry */
+       p_vrl = vrl;
+       vrl = vrl->vrl_next;
+       pfree(p_vrl);
+    }
+
+    (void) MemoryContextSwitchTo(old);
+}
+
+/*
+ *  _vc_getarchrel() -- open the archive relation for a heap relation
+ *
+ *     The archive relation is named 'a,XXXXX' for the heap relation
+ *     whose relid is XXXXX.
+ */
+
+#define ARCHIVE_PREFIX "a,"
+
+static Relation
+_vc_getarchrel(Relation heaprel)
+{
+    Relation archrel;
+    char *archrelname;
+
+    archrelname = palloc(sizeof(ARCHIVE_PREFIX) + NAMEDATALEN); /* bogus */
+    sprintf(archrelname, "%s%d", ARCHIVE_PREFIX, heaprel->rd_id);
+
+    archrel = heap_openr(archrelname);
+
+    pfree(archrelname);
+    return (archrel);
+}
+
+/*
+ *  _vc_archive() -- write a tuple to an archive relation
+ *
+ *     In the future, this will invoke the archived accessd method.  For
+ *     now, archive relations are on mag disk.
+ */
+static void
+_vc_archive(Relation archrel, HeapTuple htup)
+{
+    doinsert(archrel, htup);
+}
+
+static bool
+_vc_isarchrel(char *rname)
+{
+    if (strncmp(ARCHIVE_PREFIX, rname,strlen(ARCHIVE_PREFIX)) == 0)
+       return (true);
+
+    return (false);
+}
diff --git a/src/backend/commands/vacuum.h b/src/backend/commands/vacuum.h
new file mode 100644 (file)
index 0000000..15fa4f2
--- /dev/null
@@ -0,0 +1,48 @@
+/*-------------------------------------------------------------------------
+ *
+ * vacuum.h--
+ *    header file for postgres vacuum cleaner
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        VACUUM_H
+#define        VACUUM_H
+
+typedef struct VAttListData {
+    int                        val_dummy;
+    struct VAttListData        *val_next;
+} VAttListData;
+
+typedef VAttListData   *VAttList;
+
+typedef struct VTidListData {
+    ItemPointerData    vtl_tid;
+    struct VTidListData        *vtl_next;
+} VTidListData;
+
+typedef VTidListData   *VTidList;
+
+typedef struct VRelListData {
+    Oid                        vrl_relid;
+    VAttList           vrl_attlist;
+    VTidList           vrl_tidlist;
+    int                        vrl_ntups;
+    int                        vrl_npages;
+    bool               vrl_hasindex;
+    struct VRelListData        *vrl_next;
+} VRelListData;
+
+typedef VRelListData   *VRelList;
+
+extern bool VacuumRunning;
+
+extern void vc_abort(void);
+extern void vacuum(char *vacrel);
+
+
+#endif /* VACUUM_H */
diff --git a/src/backend/commands/version.h b/src/backend/commands/version.h
new file mode 100644 (file)
index 0000000..52d583f
--- /dev/null
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * version.h--
+ *    Header file for versions.  
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef VERSION_H
+#define VERSION_H
+
+#include "postgres.h"
+#include "nodes/pg_list.h"
+
+extern void DefineVersion(char *name, char *fromRelname, char *date);
+extern void VersionCreate(char *vname, char *bname);
+extern void VersionAppend(char *vname, char *bname);
+extern void VersionRetrieve(char *vname, char *bname, char *snapshot);
+extern void VersionDelete(char *vname, char *bname, char *snapshot);
+extern void VersionReplace(char *vname, char *bname, char *snapshot);
+    
+#endif /* VERSION_H */
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
new file mode 100644 (file)
index 0000000..76ccb50
--- /dev/null
@@ -0,0 +1,325 @@
+/*-------------------------------------------------------------------------
+ *
+ * view.c--
+ *    use rewrite rules to construct views
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>     /* for sprintf() */
+#include "postgres.h"
+#include "access/heapam.h"
+#include "access/xact.h"
+#include "utils/builtins.h"
+#include "utils/syscache.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "nodes/relation.h"
+#include "nodes/primnodes.h"
+#include "nodes/parsenodes.h"
+#include "parser/catalog_utils.h"
+#include "parser/parse_query.h"
+#include "rewrite/rewriteDefine.h"
+#include "rewrite/rewriteHandler.h"
+#include "rewrite/rewriteManip.h"
+#include "rewrite/rewriteRemove.h"
+#include "commands/creatinh.h"
+
+/*---------------------------------------------------------------------
+ * DefineVirtualRelation
+ *
+ * Create the "view" relation.
+ * `DefineRelation' does all the work, we just provide the correct
+ * arguments!
+ *
+ * If the relation already exists, then 'DefineRelation' will abort
+ * the xact...
+ *---------------------------------------------------------------------
+ */
+static void
+DefineVirtualRelation(char *relname, List *tlist)
+{
+    CreateStmt createStmt;
+    List *attrList, *t;
+    TargetEntry *entry;
+    Resdom  *res;
+    char *resname;
+    char *restypename;
+    
+    /*
+     * create a list with one entry per attribute of this relation.
+     * Each entry is a two element list. The first element is the
+     * name of the attribute (a string) and the second the name of the type
+     * (NOTE: a string, not a type id!).
+     */
+    attrList = NIL;
+    if (tlist!=NIL) {
+       foreach (t, tlist ) {
+           ColumnDef *def = makeNode(ColumnDef);
+           TypeName *typename;
+
+           /*
+            * find the names of the attribute & its type
+            */
+           entry = lfirst(t);
+           res   = entry->resdom;
+           resname = res->resname;
+           restypename = tname(get_id_type((long)res->restype));
+
+           typename = makeNode(TypeName);
+
+           typename->name = pstrdup(restypename);
+           def->colname = pstrdup(resname);
+
+           def->typename = typename;
+
+           attrList = lappend(attrList, def);
+       }
+    } else {
+       elog ( WARN, "attempted to define virtual relation with no attrs");
+    }
+    
+    /*
+     * now create the parametesr for keys/inheritance etc.
+     * All of them are nil...
+     */
+    createStmt.relname = relname;
+    createStmt.tableElts = attrList;
+/*    createStmt.tableType = NULL;*/
+    createStmt.inhRelnames = NIL;
+    createStmt.archiveType = ARCH_NONE;
+    createStmt.location = -1;
+    createStmt.archiveLoc = -1;
+
+    /*
+     * finally create the relation...
+     */
+    DefineRelation(&createStmt);
+}    
+
+/*------------------------------------------------------------------
+ * makeViewRetrieveRuleName
+ *
+ * Given a view name, returns the name for the 'on retrieve to "view"'
+ * rule.
+ * This routine is called when defining/removing a view.
+ *
+ * NOTE: it quarantees that the name is at most 15 chars long
+ *
+ * XXX it also means viewName cannot be 16 chars long! - ay 11/94
+ *------------------------------------------------------------------
+ */
+char *
+MakeRetrieveViewRuleName(char *viewName)
+{
+/*
+    char buf[100];
+
+    memset(buf, 0, sizeof(buf));
+    sprintf(buf, "_RET%.*s", NAMEDATALEN, viewName->data);
+    buf[15] = '\0';
+    namestrcpy(rule_name, buf);
+*/
+
+    char *buf;
+    buf = palloc(strlen(viewName) + 5);
+    sprintf(buf, "_RET%s",viewName);
+    return buf;
+}
+
+static RuleStmt *
+FormViewRetrieveRule(char *viewName, Query *viewParse)
+{
+    RuleStmt *rule;
+    char *rname;
+    Attr *attr;
+    
+    /*
+     * Create a RuleStmt that corresponds to the suitable
+     * rewrite rule args for DefineQueryRewrite();
+     */
+    rule = makeNode(RuleStmt);
+    rname = MakeRetrieveViewRuleName(viewName);
+
+    attr = makeNode(Attr);
+    attr->relname = pstrdup(viewName);
+/*    attr->refname = pstrdup(viewName);*/
+    rule->rulename = pstrdup(rname);
+    rule->whereClause = NULL;
+    rule->event = CMD_SELECT;
+    rule->object = attr;
+    rule->instead = true;
+    rule->actions = lcons(viewParse, NIL);
+       
+    return rule;
+}
+
+static void
+DefineViewRules(char *viewName, Query *viewParse)
+{
+    RuleStmt *retrieve_rule    = NULL;
+#ifdef NOTYET
+    RuleStmt *replace_rule     = NULL;
+    RuleStmt *append_rule      = NULL;
+    RuleStmt *delete_rule      = NULL;
+#endif
+    
+    retrieve_rule = 
+       FormViewRetrieveRule(viewName, viewParse);
+    
+#ifdef NOTYET
+    
+    replace_rule =
+       FormViewReplaceRule(viewName, viewParse);
+    append_rule = 
+       FormViewAppendRule(viewName, viewParse);
+    delete_rule = 
+       FormViewDeleteRule(viewName, viewParse);
+    
+#endif
+    
+    DefineQueryRewrite(retrieve_rule);
+
+#ifdef NOTYET
+    DefineQueryRewrite(replace_rule);
+    DefineQueryRewrite(append_rule);
+    DefineQueryRewrite(delete_rule);
+#endif
+    
+}     
+
+/*---------------------------------------------------------------
+ * UpdateRangeTableOfViewParse
+ *
+ * Update the range table of the given parsetree.
+ * This update consists of adding two new entries IN THE BEGINNING
+ * of the range table (otherwise the rule system will die a slow,
+ * horrible and painful death, and we do not want that now, do we?)
+ * one for the CURRENT relation and one for the NEW one (both of
+ * them refer in fact to the "view" relation).
+ *
+ * Of course we must also increase the 'varnos' of all the Var nodes
+ * by 2...
+ *
+ * NOTE: these are destructive changes. It would be difficult to
+ * make a complete copy of the parse tree and make the changes
+ * in the copy.
+ *---------------------------------------------------------------
+ */
+static void
+UpdateRangeTableOfViewParse(char *viewName, Query *viewParse)
+{
+    List *old_rt;
+    List *new_rt;
+    RangeTblEntry *rt_entry1, *rt_entry2;
+    
+    /*
+     * first offset all var nodes by 2
+     */
+    OffsetVarNodes((Node*)viewParse->targetList, 2);
+    OffsetVarNodes(viewParse->qual, 2);
+    
+    /*
+     * find the old range table...
+     */
+    old_rt = viewParse->rtable;
+
+    /*
+     * create the 2 new range table entries and form the new
+     * range table...
+     * CURRENT first, then NEW....
+     */
+    rt_entry1 =
+       makeRangeTableEntry((char*)viewName, FALSE, NULL, "*CURRENT*");
+    rt_entry2 =
+       makeRangeTableEntry((char*)viewName, FALSE, NULL, "*NEW*");
+    new_rt = lcons(rt_entry2, old_rt);
+    new_rt = lcons(rt_entry1, new_rt);
+    
+    /*
+     * Now the tricky part....
+     * Update the range table in place... Be careful here, or
+     * hell breaks loooooooooooooOOOOOOOOOOOOOOOOOOSE!
+     */
+    viewParse->rtable = new_rt;
+}
+
+/*-------------------------------------------------------------------
+ * DefineView
+ *
+ *     - takes a "viewname", "parsetree" pair and then
+ *     1)      construct the "virtual" relation 
+ *     2)      commit the command but NOT the transaction,
+ *             so that the relation exists
+ *             before the rules are defined.
+ *     2)      define the "n" rules specified in the PRS2 paper
+ *             over the "virtual" relation
+ *-------------------------------------------------------------------
+ */
+void
+DefineView(char *viewName, Query *viewParse)
+{
+    List *viewTlist;
+
+    viewTlist = viewParse->targetList;
+    
+    /*
+     * Create the "view" relation
+     * NOTE: if it already exists, the xaxt will be aborted.
+     */
+    DefineVirtualRelation(viewName, viewTlist);
+    
+    /*
+     * The relation we have just created is not visible
+     * to any other commands running with the same transaction &
+     * command id.
+     * So, increment the command id counter (but do NOT pfree any
+     * memory!!!!)
+     */
+    CommandCounterIncrement();
+    
+    /*
+     * The range table of 'viewParse' does not contain entries
+     * for the "CURRENT" and "NEW" relations.
+     * So... add them!
+     * NOTE: we make the update in place! After this call 'viewParse' 
+     * will never be what it used to be...
+     */
+    UpdateRangeTableOfViewParse(viewName, viewParse);
+    DefineViewRules(viewName, viewParse);
+}
+
+/*------------------------------------------------------------------
+ * RemoveView
+ *
+ * Remove a view given its name
+ *------------------------------------------------------------------
+ */
+void
+RemoveView(char *viewName)
+{
+    char* rname;
+    
+    /*
+     * first remove all the "view" rules...
+     * Currently we only have one!
+     */
+    rname = MakeRetrieveViewRuleName(viewName);
+    RemoveRewriteRule(rname);
+
+    /*
+     * we don't really need that, but just in case...
+     */
+    CommandCounterIncrement();
+    
+    /*
+     * now remove the relation.
+     */
+    heap_destroy(viewName);
+    pfree(rname);
+}
diff --git a/src/backend/commands/view.h b/src/backend/commands/view.h
new file mode 100644 (file)
index 0000000..87e72e6
--- /dev/null
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * view.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        VIEW_H
+#define        VIEW_H
+
+extern char *MakeRetrieveViewRuleName(char *view_name);
+extern void DefineView(char *view_name, Query *view_parse);
+extern void RemoveView(char *view_name);
+
+#endif /* VIEW_H */
diff --git a/src/backend/executor/Makefile.inc b/src/backend/executor/Makefile.inc
new file mode 100644 (file)
index 0000000..acbc281
--- /dev/null
@@ -0,0 +1,29 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the executor module
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+VPATH:= $(VPATH):$(CURDIR)/executor
+
+SRCS_EXECUTOR= execAmi.c execFlatten.c execJunk.c execMain.c \
+       execProcnode.c execQual.c execScan.c execTuples.c \
+       execUtils.c functions.c nodeAppend.c nodeAgg.c nodeHash.c \
+       nodeHashjoin.c nodeIndexscan.c nodeMaterial.c nodeMergejoin.c \
+       nodeNestloop.c nodeResult.c nodeSeqscan.c nodeSort.c \
+       nodeUnique.c nodeTee.c nodeGroup.c
+
+HEADERS+= execFlatten.h execdebug.h execdefs.h execdesc.h \
+       executor.h functions.h hashjoin.h nodeAgg.h nodeAppend.h \
+       nodeHash.h nodeHashjoin.h nodeIndexscan.h nodeMaterial.h \
+       nodeMergejoin.h nodeNestloop.h nodeResult.h  \
+       nodeSeqscan.h nodeSort.h nodeUnique.h tuptable.h nodeTee.h \
+       nodeGroup.h
+
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
new file mode 100644 (file)
index 0000000..03f8cca
--- /dev/null
@@ -0,0 +1,439 @@
+/*-------------------------------------------------------------------------
+ *
+ * execAmi.c--
+ *    miscellanious executor access method routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ *   INTERFACE ROUTINES
+ *
+ *     ExecOpenScanR   \                             / amopen
+ *     ExecBeginScan    \                           /  ambeginscan
+ *     ExecCloseR        \                         /   amclose
+ *     ExecInsert         \  executor interface   /    aminsert
+ *     ExecReScanNode     /  to access methods    \    amrescan
+ *     ExecReScanR       /                         \   amrescan
+ *     ExecMarkPos      /                           \  ammarkpos
+ *     ExecRestrPos    /                             \ amrestpos
+ *
+ *     ExecCreatR      function to create temporary relations
+ *
+ */
+#include <stdio.h>     /* for sprintf() */
+#include "executor/executor.h"
+#include "storage/smgr.h"
+#include "executor/nodeSeqscan.h"
+#include "executor/nodeIndexscan.h"
+#include "executor/nodeSort.h"
+#include "executor/nodeTee.h"
+#include "optimizer/internal.h" /* for _TEMP_RELATION_ID_ */
+
+/* ----------------------------------------------------------------
+ *     ExecOpenScanR
+ *
+ * old comments:
+ *     Parameters:
+ *       relation -- relation to be opened and scanned.
+ *       nkeys    -- number of keys
+ *       skeys    -- keys to restrict scanning
+ *           isindex  -- if this is true, the relation is the relid of
+ *                       an index relation, else it is an index into the
+ *                       range table.
+ *     Returns the relation as(relDesc scanDesc)
+ *         If this structure is changed, need to modify the access macros
+ *     defined in execInt.h.
+ * ----------------------------------------------------------------
+ */
+void
+ExecOpenScanR(Oid relOid,
+             int nkeys,
+             ScanKey skeys,
+             bool isindex,
+             ScanDirection dir,
+             TimeQual timeRange,
+             Relation *returnRelation,         /* return */
+             Pointer *returnScanDesc)          /* return */
+{
+    Relation relation;
+    Pointer  scanDesc;
+    
+    /* ----------------
+     * note: scanDesc returned by ExecBeginScan can be either
+     *       a HeapScanDesc or an IndexScanDesc so for now we
+     *       make it a Pointer.  There should be a better scan
+     *       abstraction someday -cim 9/9/89
+     * ----------------
+     */
+    relation = ExecOpenR(relOid, isindex);
+    scanDesc = ExecBeginScan(relation,
+                            nkeys,
+                            skeys,
+                            isindex,
+                            dir,
+                            timeRange);
+    
+    if (returnRelation != NULL)
+       *returnRelation = relation;
+    if (scanDesc != NULL)
+       *returnScanDesc = scanDesc;
+}
+/* ----------------------------------------------------------------
+ *     ExecOpenR
+ *
+ *     returns a relation descriptor given an object id.
+ * ----------------------------------------------------------------
+ */
+Relation
+ExecOpenR(Oid relationOid, bool isindex)
+{
+    Relation relation;
+    relation = (Relation) NULL;
+
+    /* ----------------
+     * open the relation with the correct call depending
+     *  on whether this is a heap relation or an index relation.
+     * ----------------
+     */
+    if (isindex) {
+       relation = index_open(relationOid);
+    } else
+       relation = heap_open(relationOid);
+       
+    if (relation == NULL)
+       elog(DEBUG, "ExecOpenR: relation == NULL, heap_open failed.");
+
+    return relation;
+}
+/* ----------------------------------------------------------------
+ *     ExecBeginScan
+ *
+ *     beginscans a relation in current direction.
+ *
+ *     XXX fix parameters to AMbeginscan (and btbeginscan)
+ *             currently we need to pass a flag stating whether
+ *             or not the scan should begin at an endpoint of
+ *             the relation.. Right now we always pass false
+ *             -cim 9/14/89
+ * ----------------------------------------------------------------
+ */
+Pointer
+ExecBeginScan(Relation relation,
+             int nkeys,
+             ScanKey skeys,
+             bool isindex,
+             ScanDirection dir,
+             TimeQual time_range)
+{
+    Pointer  scanDesc;
+    
+    scanDesc =   NULL;
+
+    /* ----------------
+     * open the appropriate type of scan.
+     * 
+     *  Note: ambeginscan()'s second arg is a boolean indicating
+     *       that the scan should be done in reverse..  That is,
+     *       if you pass it true, then the scan is backward.
+     * ----------------
+     */
+    if (isindex) {
+       scanDesc = (Pointer) index_beginscan(relation,
+                                            false,     /* see above comment */
+                                            nkeys,
+                                            skeys);
+    } else {
+       scanDesc = (Pointer) heap_beginscan(relation,
+                                           ScanDirectionIsBackward(dir),
+                                           time_range,
+                                           nkeys,
+                                           skeys);
+    }
+   
+    if (scanDesc == NULL)
+       elog(DEBUG, "ExecBeginScan: scanDesc = NULL, heap_beginscan failed.");
+    
+    
+    return scanDesc;
+}
+/* ----------------------------------------------------------------
+ *     ExecCloseR
+ *
+ *     closes the relation and scan descriptor for a scan or sort
+ *     node.  Also closes index relations and scans for index scans.
+ *
+ * old comments
+ *     closes the relation indicated in 'relID'
+ * ----------------------------------------------------------------
+ */
+void
+ExecCloseR(Plan *node)
+{
+    CommonScanState *state;
+    Relation    relation;
+    HeapScanDesc scanDesc;
+    
+    /* ----------------
+     *  shut down the heap scan and close the heap relation
+     * ----------------
+     */
+    switch (nodeTag(node)) {
+       
+    case T_SeqScan:
+       state =  ((SeqScan *)node)->scanstate;
+       break;
+
+    case T_IndexScan:
+       state = ((IndexScan *)node)->scan.scanstate;
+       break;
+       
+    case T_Material:
+       state = &(((Material *)node)->matstate->csstate);
+       break;
+       
+    case T_Sort:
+       state =  &(((Sort *)node)->sortstate->csstate);
+       break;
+
+    case T_Agg:
+       state = &(((Agg *)node)->aggstate->csstate);
+       break;
+
+    default:
+       elog(DEBUG, "ExecCloseR: not a scan, material, or sort node!");
+       return;
+    }
+    
+    relation = state->css_currentRelation;
+    scanDesc = state->css_currentScanDesc;
+    
+    if (scanDesc != NULL)
+       heap_endscan(scanDesc);
+    
+    if (relation != NULL)
+       heap_close(relation);
+    
+    /* ----------------
+     * if this is an index scan then we have to take care
+     *  of the index relations as well..
+     * ----------------
+     */
+    if (nodeTag(node) == T_IndexScan) {
+       IndexScan        *iscan= (IndexScan *)node;
+       IndexScanState   *indexstate;
+       int              numIndices;
+       RelationPtr      indexRelationDescs;
+       IndexScanDescPtr indexScanDescs;
+       int              i;
+       
+       indexstate =         iscan->indxstate;
+       numIndices =         indexstate->iss_NumIndices;
+       indexRelationDescs = indexstate->iss_RelationDescs;
+       indexScanDescs =     indexstate->iss_ScanDescs;
+       
+       for (i = 0; i<numIndices; i++) {
+           /* ----------------
+            *  shut down each of the scans and
+            *  close each of the index relations
+            * ----------------
+            */
+           if (indexScanDescs[i] != NULL)
+               index_endscan(indexScanDescs[i]);
+           
+           if (indexRelationDescs[i] != NULL)
+               index_close(indexRelationDescs[i]);
+       }
+    }
+}
+/* ----------------------------------------------------------------
+ *     ExecReScan
+ *
+ *     XXX this should be extended to cope with all the node types..
+ *
+ *     takes the new expression context as an argument, so that 
+ *     index scans needn't have their scan keys updated separately
+ *     - marcel 09/20/94
+ * ----------------------------------------------------------------
+ */
+void
+ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
+{
+    switch(nodeTag(node)) {
+    case T_SeqScan:
+       ExecSeqReScan((SeqScan *) node, exprCtxt, parent);
+       return;
+    
+    case T_IndexScan:
+       ExecIndexReScan((IndexScan *) node, exprCtxt, parent);
+       return;
+
+    case T_Material:
+       /* the first call to ExecReScan should have no effect because
+        * everything is initialized properly already.  the following
+        * calls will be handled by ExecSeqReScan() because the nodes
+        * below the Material node have already been materialized into
+        * a temp relation.
+        */
+       return;
+
+    case T_Tee:
+       ExecTeeReScan((Tee*) node, exprCtxt, parent);
+       break;
+
+    default:
+       elog(WARN, "ExecReScan: not a seqscan or indexscan node.");
+       return;
+    }
+}
+/* ----------------------------------------------------------------
+ *     ExecReScanR
+ *
+ *     XXX this does not do the right thing with indices yet.
+ * ----------------------------------------------------------------
+ */
+HeapScanDesc
+ExecReScanR(Relation relDesc,  /* LLL relDesc unused  */
+           HeapScanDesc scanDesc,
+           ScanDirection direction,
+           int nkeys,          /* LLL nkeys unused  */
+           ScanKey skeys)
+{
+    if (scanDesc != NULL)
+       heap_rescan(scanDesc,                   /* scan desc */
+                   ScanDirectionIsBackward(direction), /* backward flag */
+                   skeys);                     /* scan keys */
+    
+    return scanDesc;
+}
+/* ----------------------------------------------------------------
+ *     ExecMarkPos
+ *
+ *     Marks the current scan position.  
+ *
+ *     XXX Needs to be extended to include all the node types.
+ * ----------------------------------------------------------------
+ */
+void
+ExecMarkPos(Plan *node)
+{
+    switch(nodeTag(node)) {
+    case T_SeqScan:
+       ExecSeqMarkPos((SeqScan *) node);
+       break;
+
+    case T_IndexScan:
+       ExecIndexMarkPos((IndexScan *) node);
+       break;
+
+    case T_Sort:
+       ExecSortMarkPos((Sort *) node);
+       break;
+
+    default:
+       /* elog(DEBUG, "ExecMarkPos: unsupported node type"); */
+       break;
+    }
+    return;
+}
+/* ----------------------------------------------------------------
+ *     ExecRestrPos
+ *
+ *     restores the scan position previously saved with ExecMarkPos()
+ * ----------------------------------------------------------------
+ */
+void
+ExecRestrPos(Plan *node)
+{
+    switch(nodeTag(node)) {
+    case T_SeqScan:
+       ExecSeqRestrPos((SeqScan *) node);
+       return;
+    
+    case T_IndexScan:
+       ExecIndexRestrPos((IndexScan *) node);
+       return;
+    
+    case T_Sort:
+       ExecSortRestrPos((Sort *) node);
+       return;
+
+    default:
+       /* elog(DEBUG, "ExecRestrPos: node type not supported"); */
+       return;
+    }
+}
+/* ----------------------------------------------------------------
+ *     ExecCreatR
+ *
+ * old comments
+ *     Creates a relation.
+ *
+ *     Parameters:
+ *       attrType  -- type information on the attributes.
+ *       accessMtd -- access methods used to access the created relation.
+ *       relation  -- optional. Either an index to the range table or
+ *                    negative number indicating a temporary relation.
+ *                    A temporary relation is assume is this field is absent.
+ * ----------------------------------------------------------------
+ */
+
+Relation
+ExecCreatR(TupleDesc tupType,
+          Oid relationOid)
+{
+    Relation   relDesc;
+
+    EU4_printf("ExecCreatR: %s numatts=%d type=%d oid=%d\n",
+              "entering: ", numberAttributes, tupType, relationOid);
+    CXT1_printf("ExecCreatR: context is %d\n", CurrentMemoryContext);
+    
+    relDesc = NULL;
+    
+    if (relationOid == _TEMP_RELATION_ID_ ) {
+       /* ----------------
+        *   create a temporary relation
+        *   (currently the planner always puts a _TEMP_RELATION_ID
+        *   in the relation argument so we expect this to be the case although
+        *   it's possible that someday we'll get the name from
+        *   from the range table.. -cim 10/12/89)
+        * ----------------
+        */
+/*
+       sprintf(tempname, "temp_%d.%d", getpid(), tmpcnt++);
+       EU1_printf("ExecCreatR: attempting to create %s\n", tempname);
+*/
+      /* heap_creatr creates a name if the argument to heap_creatr is '\0 ' */
+       relDesc = heap_creatr("",
+                             DEFAULT_SMGR,
+                             tupType);
+    } else {
+       /* ----------------
+        *      use a relation from the range table
+        * ----------------
+        */
+       elog(DEBUG, "ExecCreatR: %s",
+            "stuff using range table id's is not functional");
+    }
+    
+    if (relDesc == NULL)
+       elog(DEBUG, "ExecCreatR: failed to create relation.");
+    
+    EU1_printf("ExecCreatR: returning relDesc=%d\n", relDesc);
+    
+    return relDesc;
+}
diff --git a/src/backend/executor/execFlatten.c b/src/backend/executor/execFlatten.c
new file mode 100644 (file)
index 0000000..af94ceb
--- /dev/null
@@ -0,0 +1,236 @@
+/*-------------------------------------------------------------------------
+ *
+ * execFlatten.c--
+ *    This file handles the nodes associated with flattening sets in the
+ *    target list of queries containing functions returning sets.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * ExecEvalIter() -
+ *   Iterate through the all return tuples/base types from a function one
+ *   at time (i.e. one per ExecEvalIter call).  Not really needed for
+ *   postquel functions, but for reasons of orthogonality, these nodes
+ *   exist above pq functions as well as c functions.
+ *
+ * ExecEvalFjoin() -
+ *   Given N Iter nodes return a vector of all combinations of results
+ *   one at a time (i.e. one result vector per ExecEvalFjoin call).  This
+ *   node does the actual flattening work.
+ */
+#include "postgres.h"
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+#include "nodes/execnodes.h"
+#include "executor/executor.h"
+#include "executor/execFlatten.h"
+
+Datum
+ExecEvalIter(Iter *iterNode,
+            ExprContext *econtext,
+            bool *resultIsNull,
+            bool *iterIsDone)
+{
+    Node *expression;
+    
+    expression = iterNode->iterexpr;
+    
+    /*
+     * Really Iter nodes are only needed for C functions, postquel function
+     * by their nature return 1 result at a time.  For now we are only worrying
+     * about postquel functions, c functions will come later.
+     */
+    return ExecEvalExpr(expression, econtext, resultIsNull, iterIsDone);
+}
+
+void
+ExecEvalFjoin(TargetEntry *tlist,
+             ExprContext *econtext,
+             bool *isNullVect,
+             bool *fj_isDone)
+{
+
+#ifdef SETS_FIXED
+    bool     isDone;
+    int      curNode;
+    List     *tlistP;
+
+    Fjoin    *fjNode     = tlist->fjoin;
+    DatumPtr resVect    = fjNode->fj_results;
+    BoolPtr  alwaysDone = fjNode->fj_alwaysDone;
+    
+    if (fj_isDone) *fj_isDone = false;
+    /*
+     * For the next tuple produced by the plan, we need to re-initialize
+     * the Fjoin node.
+     */
+    if (!fjNode->fj_initialized)
+       {
+           /*
+            * Initialize all of the Outer nodes
+            */
+           curNode = 1;
+           foreach(tlistP, lnext(tlist))
+               {
+                   TargetEntry *tle = lfirst(tlistP);
+                   
+                   resVect[curNode] = ExecEvalIter((Iter*)tle->expr,
+                                                   econtext,
+                                                   &isNullVect[curNode],
+                                                   &isDone);
+                   if (isDone)
+                       isNullVect[curNode] = alwaysDone[curNode] = true;
+                   else
+                       alwaysDone[curNode] = false;
+                   
+                   curNode++;
+               }
+           
+           /*
+            * Initialize the inner node
+            */
+           resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr,
+                                     econtext,
+                                     &isNullVect[0],
+                                     &isDone);
+           if (isDone)
+               isNullVect[0] = alwaysDone[0] = true;
+           else
+               alwaysDone[0] = false;
+           
+           /*
+            * Mark the Fjoin as initialized now.
+            */
+           fjNode->fj_initialized = TRUE;
+           
+           /*
+            * If the inner node is always done, then we are done for now
+            */
+           if (isDone)
+               return;
+       }
+    else
+       {
+           /*
+            * If we're already initialized, all we need to do is get the
+            * next inner result and pair it up with the existing outer node
+            * result vector.  Watch out for the degenerate case, where the
+            * inner node never returns results.
+            */
+           
+           /*
+            * Fill in nulls for every function that is always done.
+            */
+           for (curNode=fjNode->fj_nNodes-1; curNode >= 0; curNode--)
+               isNullVect[curNode] = alwaysDone[curNode];
+           
+           if (alwaysDone[0] == true)
+               {
+                   *fj_isDone = FjoinBumpOuterNodes(tlist,
+                                                    econtext,
+                                                    resVect,
+                                                    isNullVect);
+                   return;
+               }
+           else
+               resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr,
+                                         econtext,
+                                         &isNullVect[0],
+                                         &isDone);
+       }
+    
+    /*
+     * if the inner node is done
+     */
+    if (isDone)
+       {
+           *fj_isDone = FjoinBumpOuterNodes(tlist,
+                                            econtext,
+                                            resVect,
+                                            isNullVect);
+           if (*fj_isDone)
+               return;
+           
+           resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr,
+                                     econtext,
+                                     &isNullVect[0],
+                                     &isDone);
+           
+       }
+#endif
+    return;
+}
+
+bool
+FjoinBumpOuterNodes(TargetEntry *tlist,
+                   ExprContext *econtext,
+                   DatumPtr results,
+                   char *nulls)
+{
+#ifdef SETS_FIXED
+    bool   funcIsDone = true;
+    Fjoin  *fjNode    = tlist->fjoin;
+    char *alwaysDone = fjNode->fj_alwaysDone;
+    List   *outerList  = lnext(tlist);
+    List   *trailers   = lnext(tlist);
+    int    trailNode  = 1;
+    int    curNode    = 1;
+    
+    /*
+     * Run through list of functions until we get to one that isn't yet
+     * done returning values.  Watch out for funcs that are always done.
+     */
+    while ((funcIsDone == true) && (outerList != NIL))
+       {
+           TargetEntry *tle = lfirst(outerList);
+           
+           if (alwaysDone[curNode] == true)
+               nulls[curNode] = 'n';
+           else
+               results[curNode] = ExecEvalIter((Iter)tle->expr,
+                                               econtext,
+                                               &nulls[curNode],
+                                               &funcIsDone);
+           curNode++;
+           outerList = lnext(outerList);
+       }
+    
+    /*
+     * If every function is done, then we are done flattening.
+     * Mark the Fjoin node unitialized, it is time to get the
+     * next tuple from the plan and redo all of the flattening.
+     */
+    if (funcIsDone)
+       {
+           set_fj_initialized(fjNode, false);
+           return (true);
+       }
+    
+    /*
+     * We found a function that wasn't done.  Now re-run every function
+     * before it.  As usual watch out for functions that are always done.
+     */
+    trailNode = 1;
+    while (trailNode != curNode-1)
+       {
+           TargetEntry *tle = lfirst(trailers);
+           
+           if (alwaysDone[trailNode] != true)
+               results[trailNode] = ExecEvalIter((Iter)tle->expr,
+                                                 econtext,
+                                                 &nulls[trailNode],
+                                                 &funcIsDone);
+           trailNode++;
+           trailers = lnext(trailers);
+       }
+    return false;
+#endif
+    return false;
+}
diff --git a/src/backend/executor/execFlatten.h b/src/backend/executor/execFlatten.h
new file mode 100644 (file)
index 0000000..397f133
--- /dev/null
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * execFlatten.h--
+ *    prototypes for execFlatten.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef EXECFLATTEN_H
+#define EXECFLATTEN_H
+
+extern Datum ExecEvalIter(Iter *iterNode, ExprContext *econtext, bool *resultIsNull, bool *iterIsDone);
+
+extern void ExecEvalFjoin(TargetEntry *tlist, ExprContext *econtext, bool *isNullVect, bool *fj_isDone);
+
+extern bool FjoinBumpOuterNodes(TargetEntry *tlist, ExprContext *econtext, DatumPtr results, char *nulls);
+
+
+#endif /* EXECFLATTEN_H */
+
+
+
diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c
new file mode 100644 (file)
index 0000000..afec4e7
--- /dev/null
@@ -0,0 +1,389 @@
+/*-------------------------------------------------------------------------
+ *
+ * junk.c--
+ *    Junk attribute support stuff....
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "utils/palloc.h"
+#include "executor/executor.h"
+#include "nodes/relation.h"
+#include "optimizer/tlist.h" /* for MakeTLE */
+
+/*-------------------------------------------------------------------------
+ *     XXX this stuff should be rewritten to take advantage
+ *         of ExecProject() and the ProjectionInfo node.
+ *         -cim 6/3/91
+ * 
+ * An attribute of a tuple living inside the executor, can be
+ * either a normal attribute or a "junk" attribute. "junk" attributes
+ * never make it out of the executor, i.e. they are never printed,
+ * returned or stored in disk. Their only purpose in life is to
+ * store some information useful only to the executor, mainly the values
+ * of some system attributes like "ctid" or rule locks.
+ * 
+ * The general idea is the following: A target list consists of a list of
+ * Resdom nodes & expression pairs. Each Resdom node has an attribute
+ * called 'resjunk'. If the value of this attribute is 1 then the
+ * corresponding attribute is a "junk" attribute.
+ * 
+ * When we initialize a plan  we call 'ExecInitJunkFilter' to create
+ * and store the appropriate information in the 'es_junkFilter' attribute of
+ * EState.
+ * 
+ * We then execute the plan ignoring the "resjunk" attributes.
+ * 
+ * Finally, when at the top level we get back a tuple, we can call
+ * 'ExecGetJunkAttribute' to retrieve the value of the junk attributes we
+ * are interested in, and 'ExecRemoveJunk' to remove all the junk attributes
+ * from a tuple. This new "clean" tuple is then printed, replaced, deleted
+ * or inserted.
+ * 
+ *-------------------------------------------------------------------------
+ */
+
+/*-------------------------------------------------------------------------
+ * ExecInitJunkFilter
+ *
+ * Initialize the Junk filter.
+ *-------------------------------------------------------------------------
+ */
+JunkFilter *
+ExecInitJunkFilter(List *targetList)
+{
+    JunkFilter                 *junkfilter;
+    List               *cleanTargetList;
+    int                len, cleanLength;
+    TupleDesc          tupType, cleanTupType;
+    List               *t;
+    TargetEntry                *tle;
+    Resdom             *resdom, *cleanResdom;
+    int                resjunk;
+    AttrNumber                 cleanResno;
+    AttrNumber                 *cleanMap;
+    Size               size;
+    Node               *expr; 
+
+    /* ---------------------
+     * First find the "clean" target list, i.e. all the entries
+     * in the original target list which have a zero 'resjunk'
+     * NOTE: make copy of the Resdom nodes, because we have
+     * to change the 'resno's...
+     * ---------------------
+     */
+    cleanTargetList = NIL;
+    cleanResno = 1;
+    
+    foreach (t, targetList) {
+       TargetEntry *rtarget = lfirst(t);
+       if (rtarget->resdom != NULL) {
+           resdom = rtarget->resdom;
+           expr = rtarget->expr;
+           resjunk = resdom->resjunk;
+           if (resjunk == 0) {
+               /*
+                * make a copy of the resdom node, changing its resno.
+                */
+               cleanResdom = (Resdom *) copyObject(resdom);
+               cleanResdom->resno = cleanResno;
+               cleanResno ++;
+               /*
+                * create a new target list entry
+                */
+               tle = makeNode(TargetEntry);
+               tle->resdom = cleanResdom;
+               tle->expr = expr;
+               cleanTargetList = lappend(cleanTargetList, tle);
+           }
+       }
+       else {
+#ifdef SETS_FIXED
+           List *fjListP;
+           Fjoin *cleanFjoin;
+           List *cleanFjList;
+           List *fjList = lfirst(t);
+           Fjoin *fjNode = (Fjoin *)tl_node(fjList);
+           
+           cleanFjoin = (Fjoin)copyObject((Node) fjNode);
+           cleanFjList = lcons(cleanFjoin, NIL);
+           
+           resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
+           expr =   lsecond(get_fj_innerNode(fjNode));
+           cleanResdom = (Resdom) copyObject((Node) resdom);
+           set_resno(cleanResdom, cleanResno);
+           cleanResno++;
+           tle = (List) MakeTLE(cleanResdom, (Expr) expr);
+           set_fj_innerNode(cleanFjoin, tle);
+           
+           foreach(fjListP, lnext(fjList)) {
+               TargetEntry *tle = lfirst(fjListP);
+
+               resdom = tle->resdom;
+               expr = tle->expr;
+               cleanResdom = (Resdom*) copyObject((Node) resdom);
+               cleanResno++;
+               cleanResdom->Resno = cleanResno;
+               /*
+                * create a new target list entry
+                */
+               tle = (List) MakeTLE(cleanResdom, (Expr) expr);
+               cleanFjList = lappend(cleanFjList, tle);
+           }
+           lappend(cleanTargetList, cleanFjList);
+#endif
+       }
+    }
+    
+    /* ---------------------
+     * Now calculate the tuple types for the original and the clean tuple
+     *
+     * XXX ExecTypeFromTL should be used sparingly.  Don't we already
+     *    have the tupType corresponding to the targetlist we are passed?
+     *     -cim 5/31/91
+     * ---------------------
+     */
+    tupType =     (TupleDesc) ExecTypeFromTL(targetList);
+    cleanTupType = (TupleDesc) ExecTypeFromTL(cleanTargetList);
+    
+    len =        ExecTargetListLength(targetList);
+    cleanLength = ExecTargetListLength(cleanTargetList);
+    
+    /* ---------------------
+     * Now calculate the "map" between the original tuples attributes
+     * and the "clean" tuple's attributes.
+     *
+     * The "map" is an array of "cleanLength" attribute numbers, i.e.
+     * one entry for every attribute of the "clean" tuple.
+     * The value of this entry is the attribute number of the corresponding
+     * attribute of the "original" tuple.
+     * ---------------------
+     */
+    if (cleanLength > 0) {
+       size = cleanLength * sizeof(AttrNumber);
+       cleanMap = (AttrNumber*) palloc(size);
+       cleanResno = 1;
+       foreach (t, targetList) {
+           TargetEntry *tle = lfirst(t);
+           if (tle->resdom != NULL) {
+               resdom = tle->resdom;
+               expr = tle->expr;
+               resjunk = resdom->resjunk;
+               if (resjunk == 0) {
+                   cleanMap[cleanResno-1] = resdom->resno;
+                   cleanResno ++;
+               }
+           } else {
+#ifdef SETS_FIXED
+               List fjListP;
+               List fjList = lfirst(t);
+               Fjoin fjNode = (Fjoin)lfirst(fjList);
+
+               /* what the hell is this????? */
+               resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
+#endif
+
+               cleanMap[cleanResno-1] = tle->resdom->resno;
+               cleanResno++;
+
+#ifdef SETS_FIXED
+               foreach(fjListP, lnext(fjList)) {
+                   TargetEntry *tle = lfirst(fjListP);
+
+                   resdom = tle->resdom;
+                   cleanMap[cleanResno-1] = resdom->resno;
+                   cleanResno++;
+               }
+#endif
+           }
+       }
+    } else {
+       cleanMap = NULL;
+    }
+    
+    /* ---------------------
+     * Finally create and initialize the JunkFilter.
+     * ---------------------
+     */
+    junkfilter = makeNode(JunkFilter);
+    
+    junkfilter->jf_targetList = targetList;
+    junkfilter->jf_length = len;
+    junkfilter->jf_tupType = tupType;
+    junkfilter->jf_cleanTargetList = cleanTargetList;
+    junkfilter->jf_cleanLength = cleanLength;
+    junkfilter->jf_cleanTupType = cleanTupType;
+    junkfilter->jf_cleanMap = cleanMap;
+    
+    return(junkfilter);
+    
+}
+
+/*-------------------------------------------------------------------------
+ * ExecGetJunkAttribute
+ *
+ * Given a tuple (slot), the junk filter and a junk attribute's name,
+ * extract & return the value of this attribute.
+ *
+ * It returns false iff no junk attribute with such name was found.
+ *
+ * NOTE: isNull might be NULL !
+ *-------------------------------------------------------------------------
+ */
+bool
+ExecGetJunkAttribute(JunkFilter *junkfilter,
+                    TupleTableSlot *slot,
+                    char *attrName,
+                    Datum *value,
+                    bool *isNull)
+{
+    List               *targetList;
+    List               *t;
+    Resdom             *resdom;
+    AttrNumber                 resno;
+    char                *resname;
+    int                resjunk;
+    TupleDesc          tupType;
+    HeapTuple          tuple;
+    
+    /* ---------------------
+     * first look in the junkfilter's target list for
+     * an attribute with the given name
+     * ---------------------
+     */
+    resno =     InvalidAttrNumber;
+    targetList = junkfilter->jf_targetList;
+    
+    foreach (t, targetList) {
+       TargetEntry *tle = lfirst(t);
+       resdom = tle->resdom;
+       resname = resdom->resname;
+       resjunk = resdom->resjunk;
+       if (resjunk != 0 && (strcmp(resname, attrName) == 0)) {
+           /* We found it ! */
+           resno = resdom->resno;
+           break;
+       }
+    }
+    
+    if (resno == InvalidAttrNumber) {
+       /* Ooops! We couldn't find this attribute... */
+       return(false);
+    }
+    
+    /* ---------------------
+     * Now extract the attribute value from the tuple.
+     * ---------------------
+     */
+    tuple =    slot->val;
+    tupType =  (TupleDesc) junkfilter->jf_tupType;
+    
+    *value =   (Datum)
+       heap_getattr(tuple, InvalidBuffer, resno, tupType, isNull);
+    
+    return true;
+}
+
+/*-------------------------------------------------------------------------
+ * ExecRemoveJunk
+ *
+ * Construct and return a tuple with all the junk attributes removed.
+ *-------------------------------------------------------------------------
+ */
+HeapTuple
+ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
+{
+    HeapTuple  tuple;
+    HeapTuple  cleanTuple;
+    AttrNumber         *cleanMap;
+    TupleDesc  cleanTupType;
+    TupleDesc  tupType;
+    int        cleanLength;
+    bool       isNull;
+    int        i;
+    Size       size;
+    Datum      *values;
+    char       *nulls;
+    Datum      values_array[64];
+    char       nulls_array[64];
+    
+    /* ----------------
+     * get info from the slot and the junk filter
+     * ----------------
+     */
+    tuple = slot->val;
+    
+    tupType =          (TupleDesc) junkfilter->jf_tupType;
+    cleanTupType =     (TupleDesc) junkfilter->jf_cleanTupType;
+    cleanLength =      junkfilter->jf_cleanLength;
+    cleanMap =                 junkfilter->jf_cleanMap;
+    
+    /* ---------------------
+     *  Handle the trivial case first.
+     * ---------------------
+     */
+    if (cleanLength == 0)
+       return (HeapTuple) NULL;
+    
+    /* ---------------------
+     * Create the arrays that will hold the attribute values
+     * and the null information for the new "clean" tuple.
+     *
+     * Note: we use memory on the stack to optimize things when
+     *       we are dealing with a small number of tuples.
+     *      for large tuples we just use palloc.
+     * ---------------------
+     */
+    if (cleanLength > 64) {
+       size =   cleanLength * sizeof(Datum);
+       values = (Datum *) palloc(size);
+       
+       size =   cleanLength * sizeof(char);
+       nulls =  (char *) palloc(size);
+    } else {
+       values = values_array;
+       nulls =  nulls_array;
+    }
+    
+    /* ---------------------
+     * Exctract one by one all the values of the "clean" tuple.
+     * ---------------------
+     */
+    for (i=0; i<cleanLength; i++) {
+       Datum d = (Datum)
+           heap_getattr(tuple, InvalidBuffer, cleanMap[i], tupType, &isNull);
+       
+       values[i] = d;
+       
+       if (isNull)
+           nulls[i] = 'n';
+       else
+           nulls[i] = ' ';
+    }
+    
+    /* ---------------------
+     * Now form the new tuple.
+     * ---------------------
+     */
+    cleanTuple = heap_formtuple(cleanTupType,
+                               values,
+                               nulls);
+    
+    /* ---------------------
+     * We are done.  Free any space allocated for 'values' and 'nulls'
+     * and return the new tuple.
+     * ---------------------
+     */
+    if (cleanLength > 64) {
+       pfree(values);
+       pfree(nulls);
+    }
+    
+    return(cleanTuple);
+}
+
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
new file mode 100644 (file)
index 0000000..dfcb282
--- /dev/null
@@ -0,0 +1,1023 @@
+/*-------------------------------------------------------------------------
+ *
+ * execMain.c--
+ *    top level executor interface routines
+ *
+ * INTERFACE ROUTINES
+ *  ExecutorStart()
+ *  ExecutorRun()
+ *  ExecutorEnd()
+ *
+ *  The old ExecutorMain() has been replaced by ExecutorStart(),
+ *  ExecutorRun() and ExecutorEnd()
+ *
+ *  These three procedures are the external interfaces to the executor.
+ *  In each case, the query descriptor and the execution state is required
+ *   as arguments
+ * 
+ *  ExecutorStart() must be called at the beginning of any execution of any 
+ *  query plan and ExecutorEnd() should always be called at the end of
+ *  execution of a plan.
+ *  
+ *  ExecutorRun accepts 'feature' and 'count' arguments that specify whether
+ *  the plan is to be executed forwards, backwards, and for how many tuples.
+ * 
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "executor/executor.h"
+#include "utils/builtins.h"
+#include "utils/palloc.h"
+#include "utils/acl.h"
+#include "parser/parsetree.h"          /* rt_fetch() */
+#include "storage/bufmgr.h"
+#include "commands/async.h"
+/* #include "access/localam.h" */
+#include "optimizer/var.h"
+
+
+/* decls for local routines only used within this module */
+static void ExecCheckPerms(CmdType operation, int resultRelation, List *rangeTable,
+                          Query *parseTree);
+static TupleDesc InitPlan(CmdType operation, Query *parseTree, 
+                         Plan *plan, EState *estate);
+static void EndPlan(Plan *plan, EState *estate);
+static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan,
+                                  Query *parseTree, CmdType operation,
+                                  int numberTuples, int direction,
+                                  void (*printfunc)());
+static void ExecRetrieve(TupleTableSlot *slot, void (*printfunc)(),
+                        Relation intoRelationDesc);
+static void ExecAppend(TupleTableSlot *slot,ItemPointer tupleid,
+                      EState *estate);
+static void ExecDelete(TupleTableSlot *slot, ItemPointer tupleid,
+                      EState *estate);
+static void ExecReplace(TupleTableSlot *slot, ItemPointer tupleid,
+                       EState *estate, Query *parseTree);
+
+/* end of local decls */
+
+/* ----------------------------------------------------------------
+ *     ExecutorStart
+ *   
+ *      This routine must be called at the beginning of any execution of any
+ *      query plan
+ *      
+ *      returns (AttrInfo*) which describes the attributes of the tuples to
+ *      be returned by the query.
+ *
+ * ----------------------------------------------------------------
+ */
+TupleDesc
+ExecutorStart(QueryDesc *queryDesc, EState *estate)
+{
+    TupleDesc result;
+
+    /* sanity checks */
+    Assert(queryDesc!=NULL);
+
+    result = InitPlan(queryDesc->operation,
+                     queryDesc->parsetree,
+                     queryDesc->plantree,
+                     estate);
+
+    /* reset buffer refcount.  the current refcounts
+     * are saved and will be restored when ExecutorEnd is called
+     *
+     * this makes sure that when ExecutorRun's are
+     * called recursively as for postquel functions,
+     * the buffers pinned by one ExecutorRun will not be
+     * unpinned by another ExecutorRun.
+     */
+    BufferRefCountReset(estate->es_refcount); 
+
+    return result;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecutorRun
+ *   
+ *     This is the main routine of the executor module. It accepts
+ *     the query descriptor from the traffic cop and executes the
+ *     query plan.
+ *   
+ *      ExecutorStart must have been called already.
+ *
+ *      the different features supported are:
+ *           EXEC_RUN:  retrieve all tuples in the forward direction
+ *           EXEC_FOR:  retrieve 'count' number of tuples in the forward dir
+ *           EXEC_BACK: retrieve 'count' number of tuples in the backward dir
+ *           EXEC_RETONE: return one tuple but don't 'retrieve' it
+ *                         used in postquel function processing
+ *
+ *
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot*
+ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
+{
+    CmdType    operation;
+    Query      *parseTree;
+    Plan       *plan;
+    TupleTableSlot     *result;
+    CommandDest dest;
+    void       (*destination)();
+
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(queryDesc!=NULL);
+
+    /* ----------------
+     * extract information from the query descriptor
+     *  and the query feature.
+     * ----------------
+     */
+    operation =   queryDesc->operation;
+    parseTree =   queryDesc->parsetree;
+    plan =       queryDesc->plantree;
+    dest =       queryDesc->dest;
+    destination = (void (*)()) DestToFunction(dest);
+
+    switch(feature) {
+
+    case EXEC_RUN:
+      result = ExecutePlan(estate,
+                          plan,
+                          parseTree,
+                          operation,
+                          ALL_TUPLES,
+                          EXEC_FRWD,
+                          destination);
+      break;
+    case EXEC_FOR:
+       result =  ExecutePlan(estate,
+                             plan,
+                             parseTree,
+                             operation,
+                             count,
+                             EXEC_FRWD,
+                             destination);
+       break;
+       
+       /* ----------------
+        *      retrieve next n "backward" tuples
+        * ----------------
+        */
+    case EXEC_BACK:
+       result =  ExecutePlan(estate,
+                             plan,
+                             parseTree,
+                             operation,
+                             count,
+                             EXEC_BKWD,
+                             destination);
+       break;
+       
+       /* ----------------
+        *      return one tuple but don't "retrieve" it.
+        *      (this is used by the rule manager..) -cim 9/14/89
+        * ----------------
+        */
+    case EXEC_RETONE:
+       result = ExecutePlan(estate,
+                            plan,
+                            parseTree,
+                            operation,
+                            ONE_TUPLE,
+                            EXEC_FRWD,
+                            destination);
+       break;
+    default:
+       elog(DEBUG, "ExecutorRun: Unknown feature %d", feature);
+       break;
+    }
+
+    return result;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecutorEnd
+ *   
+ *      This routine must be called at the end of any execution of any
+ *      query plan
+ *      
+ *      returns (AttrInfo*) which describes the attributes of the tuples to
+ *      be returned by the query.
+ *
+ * ----------------------------------------------------------------
+ */
+void
+ExecutorEnd(QueryDesc *queryDesc, EState *estate)
+{
+  /* sanity checks */
+  Assert(queryDesc!=NULL);
+
+  EndPlan(queryDesc->plantree, estate);
+
+  /* restore saved refcounts. */
+  BufferRefCountRestore(estate->es_refcount);  
+}
+
+/* ===============================================================
+ * ===============================================================
+                         static routines follow 
+ * ===============================================================
+ * ===============================================================
+ */
+
+static void
+ExecCheckPerms(CmdType operation,
+              int resultRelation,
+              List *rangeTable,
+              Query *parseTree)
+{
+    int i = 1;
+    Oid relid;
+    HeapTuple htp;
+    List *lp;
+    List *qvars, *tvars;
+    int32 ok = 1;
+    char *opstr;
+    NameData rname;
+    char *userName;
+
+#define CHECK(MODE)    pg_aclcheck(rname.data, userName, MODE)
+
+    userName = GetPgUserName();
+
+    foreach (lp, rangeTable) {
+       RangeTblEntry *rte = lfirst(lp);
+       
+       relid = rte->relid;
+       htp = SearchSysCacheTuple(RELOID,
+                                 ObjectIdGetDatum(relid),
+                                 0,0,0);
+       if (!HeapTupleIsValid(htp))
+           elog(WARN, "ExecCheckPerms: bogus RT relid: %d",
+                relid);
+       strncpy(rname.data,
+               ((Form_pg_class) GETSTRUCT(htp))->relname.data,
+               NAMEDATALEN);
+       if (i == resultRelation) {      /* this is the result relation */
+           qvars = pull_varnos(parseTree->qual);
+           tvars = pull_varnos((Node*)parseTree->targetList);
+           if (intMember(resultRelation, qvars) ||
+               intMember(resultRelation, tvars)) {
+               /* result relation is scanned */
+               ok = CHECK(ACL_RD);
+               opstr = "read";
+               if (!ok)
+                   break;
+           }
+           switch (operation) {
+           case CMD_INSERT:
+               ok = CHECK(ACL_AP) ||
+                   CHECK(ACL_WR);
+               opstr = "append";
+               break;
+           case CMD_NOTIFY: /* what does this mean?? -- jw, 1/6/94 */
+           case CMD_DELETE:
+           case CMD_UPDATE:
+               ok = CHECK(ACL_WR);
+               opstr = "write";
+               break;
+           default:
+               elog(WARN, "ExecCheckPerms: bogus operation %d",
+                    operation);
+           }
+       } else {
+           /* XXX NOTIFY?? */
+           ok = CHECK(ACL_RD);
+           opstr = "read";
+       }
+       if (!ok)
+           break;
+       ++i;
+    }
+    if (!ok) {
+/*
+       elog(WARN, "%s on \"%-.*s\": permission denied", opstr, 
+            NAMEDATALEN, rname.data);
+*/         
+       elog(WARN, "%s %s", rname.data, ACL_NO_PRIV_WARNING);
+    }
+}
+
+
+/* ----------------------------------------------------------------
+ *     InitPlan
+ *   
+ *     Initializes the query plan: open files, allocate storage
+ *     and start up the rule manager
+ * ----------------------------------------------------------------
+ */
+static TupleDesc
+InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
+{      
+    List       *rangeTable;
+    int                resultRelation;
+    Relation    intoRelationDesc;
+
+    TupleDesc  tupType;
+    List       *targetList;
+    int                len;
+
+    /* ----------------
+     *  get information from query descriptor
+     * ----------------
+     */
+    rangeTable =       parseTree->rtable;
+    resultRelation =   parseTree->resultRelation;
+
+    /* ----------------
+     *  initialize the node's execution state
+     * ----------------
+     */
+    estate->es_range_table = rangeTable;
+
+    /* ----------------
+     * initialize the BaseId counter so node base_id's
+     *  are assigned correctly.  Someday baseid's will have to
+     *  be stored someplace other than estate because they
+     *  should be unique per query planned.
+     * ----------------
+     */
+    estate->es_BaseId = 1;
+
+    /* ----------------
+     * initialize result relation stuff
+     * ----------------
+     */
+
+    if (resultRelation != 0 && operation != CMD_SELECT) {
+       /* ----------------
+        *    if we have a result relation, open it and
+        *    initialize the result relation info stuff.
+        * ----------------
+        */
+       RelationInfo    *resultRelationInfo;
+       Index           resultRelationIndex;
+       RangeTblEntry   *rtentry;
+       Oid             resultRelationOid;
+       Relation        resultRelationDesc;
+       
+       resultRelationIndex = resultRelation;
+       rtentry =             rt_fetch(resultRelationIndex, rangeTable);
+       resultRelationOid =   rtentry->relid;
+       resultRelationDesc =  heap_open(resultRelationOid);
+       
+       /* Write-lock the result relation right away: if the relation
+          is used in a subsequent scan, we won't have to elevate the 
+          read-lock set by heap_beginscan to a write-lock (needed by 
+          heap_insert, heap_delete and heap_replace).
+          This will hopefully prevent some deadlocks.  - 01/24/94 */
+       RelationSetLockForWrite(resultRelationDesc);
+
+       resultRelationInfo = makeNode(RelationInfo);
+       resultRelationInfo->ri_RangeTableIndex = resultRelationIndex;
+       resultRelationInfo->ri_RelationDesc = resultRelationDesc;
+       resultRelationInfo->ri_NumIndices = 0;
+       resultRelationInfo->ri_IndexRelationDescs = NULL;
+       resultRelationInfo->ri_IndexRelationInfo = NULL;
+
+       /* ----------------
+        *  open indices on result relation and save descriptors
+        *  in the result relation information..
+        * ----------------
+        */
+       ExecOpenIndices(resultRelationOid, resultRelationInfo);
+       
+       estate->es_result_relation_info = resultRelationInfo;
+    } else {
+       /* ----------------
+        *      if no result relation, then set state appropriately
+        * ----------------
+        */
+       estate->es_result_relation_info = NULL;
+    }
+
+#ifndef NO_SECURITY
+    ExecCheckPerms(operation, resultRelation, rangeTable, parseTree);
+#endif
+
+    /* ----------------
+     *    initialize the executor "tuple" table.
+     * ----------------
+     */
+    {
+       int        nSlots     = ExecCountSlotsNode(plan);
+       TupleTable tupleTable = ExecCreateTupleTable(nSlots+10); /* why add ten? - jolly */
+       
+       estate->es_tupleTable = tupleTable;
+    }
+
+    /* ----------------
+     *     initialize the private state information for
+     *    all the nodes in the query tree.  This opens
+     *    files, allocates storage and leaves us ready
+     *     to start processing tuples..
+     * ----------------
+     */
+    ExecInitNode(plan, estate, NULL);
+
+    /* ----------------
+     *     get the tuple descriptor describing the type
+     *    of tuples to return.. (this is especially important
+     *    if we are creating a relation with "retrieve into")
+     * ----------------
+     */
+    tupType =    ExecGetTupType(plan);             /* tuple descriptor */
+    targetList = plan->targetlist;
+    len =       ExecTargetListLength(targetList); /* number of attributes */
+
+    /* ----------------
+     *    now that we have the target list, initialize the junk filter
+     *    if this is a REPLACE or a DELETE query.
+     *    We also init the junk filter if this is an append query
+     *    (there might be some rule lock info there...)
+     *    NOTE: in the future we might want to initialize the junk
+     *   filter for all queries.
+     * ----------------
+     */
+    if (operation == CMD_UPDATE || operation == CMD_DELETE ||
+       operation == CMD_INSERT) {
+       
+       JunkFilter *j = (JunkFilter*) ExecInitJunkFilter(targetList);
+       estate->es_junkFilter = j;
+    } else
+       estate->es_junkFilter = NULL;
+
+    /* ----------------
+     * initialize the "into" relation
+     * ----------------
+     */
+    intoRelationDesc = (Relation) NULL;
+
+    if (operation == CMD_SELECT) {
+       char *intoName;
+       char   archiveMode;
+       Oid    intoRelationId;
+       
+       if (!parseTree->isPortal) {
+           /*
+            * a select into table
+            */
+           if (parseTree->into != NULL) {
+               /* ----------------
+                *  create the "into" relation
+                *
+                *  note: there is currently no way for the user to
+                *        specify the desired archive mode of the
+                *        "into" relation...
+                * ----------------
+                */
+               intoName = parseTree->into;
+               archiveMode = 'n';
+               
+               intoRelationId = heap_create(intoName,
+                                            intoName, /* not used */
+                                            archiveMode,
+                                            DEFAULT_SMGR,
+                                            tupType);
+               
+               /* ----------------
+                *  XXX rather than having to call setheapoverride(true)
+                *      and then back to false, we should change the
+                *      arguments to heap_open() instead..
+                * ----------------
+                */
+               setheapoverride(true);
+               
+               intoRelationDesc = heap_open(intoRelationId);
+               
+               setheapoverride(false);
+           }
+       }
+    }
+
+    estate->es_into_relation_descriptor = intoRelationDesc;
+
+    /* ----------------
+     * return the type information..
+     * ----------------
+     */
+/*
+    attinfo = (AttrInfo *)palloc(sizeof(AttrInfo));
+    attinfo->numAttr = len;
+    attinfo->attrs = tupType->attrs;
+*/
+
+    return tupType;
+}
+
+/* ----------------------------------------------------------------
+ *     EndPlan
+ *   
+ *     Cleans up the query plan -- closes files and free up storages
+ * ----------------------------------------------------------------
+ */
+static void
+EndPlan(Plan *plan, EState *estate)
+{
+    RelationInfo       *resultRelationInfo;
+    Relation           intoRelationDesc;
+
+    /* ----------------
+     * get information from state
+     * ----------------
+     */
+    resultRelationInfo =  estate->es_result_relation_info;
+    intoRelationDesc =   estate->es_into_relation_descriptor;
+
+    /* ----------------
+     *   shut down the query
+     * ----------------
+     */
+    ExecEndNode(plan, plan);
+
+    /* ----------------
+     *    destroy the executor "tuple" table.
+     * ----------------
+     */
+    {
+       TupleTable tupleTable = (TupleTable) estate->es_tupleTable;
+       ExecDestroyTupleTable(tupleTable,true); /* was missing last arg */
+       estate->es_tupleTable = NULL;
+    }
+
+    /* ----------------
+     *   close the result relations if necessary
+     * ----------------
+     */
+    if (resultRelationInfo != NULL) {
+       Relation resultRelationDesc;
+       
+       resultRelationDesc = resultRelationInfo->ri_RelationDesc;
+       heap_close(resultRelationDesc);
+       
+       /* ----------------
+        *  close indices on the result relation
+        * ----------------
+        */
+       ExecCloseIndices(resultRelationInfo);
+    }
+
+    /* ----------------
+     *   close the "into" relation if necessary
+     * ----------------
+     */
+    if (intoRelationDesc != NULL) {
+      heap_close(intoRelationDesc);
+    }
+}
+
+/* ----------------------------------------------------------------
+ *     ExecutePlan
+ *   
+ *     processes the query plan to retrieve 'tupleCount' tuples in the
+ *     direction specified.
+ *     Retrieves all tuples if tupleCount is 0
+ *
+ *     result is either a slot containing a tuple in the case
+ *      of a RETRIEVE or NULL otherwise.
+ * 
+ * ----------------------------------------------------------------
+ */
+
+/* the ctid attribute is a 'junk' attribute that is removed before the
+   user can see it*/
+
+static TupleTableSlot *
+ExecutePlan(EState *estate,
+           Plan *plan,
+           Query *parseTree,
+           CmdType operation,
+           int numberTuples,
+           int direction,
+           void (*printfunc)())
+{
+    Relation           intoRelationDesc;
+    JunkFilter         *junkfilter;
+
+    TupleTableSlot     *slot;
+    ItemPointer                tupleid = NULL;
+    ItemPointerData    tuple_ctid;
+    int                        current_tuple_count;
+    TupleTableSlot     *result;        
+
+    /* ----------------
+     *  get information
+     * ----------------
+     */
+    intoRelationDesc = estate->es_into_relation_descriptor;
+
+    /* ----------------
+     * initialize local variables
+     * ----------------
+     */
+    slot               = NULL;
+    current_tuple_count = 0;
+    result             = NULL;
+
+    /* ----------------
+     * Set the direction.
+     * ----------------
+     */
+    estate->es_direction = direction;
+
+    /* ----------------
+     * Loop until we've processed the proper number
+     *  of tuples from the plan..
+     * ----------------
+     */
+
+    for(;;) {
+       if (operation != CMD_NOTIFY) {
+           /* ----------------
+            *  Execute the plan and obtain a tuple
+            * ----------------
+            */
+            /* at the top level, the parent of a plan (2nd arg) is itself */
+           slot = ExecProcNode(plan,plan); 
+       
+           /* ----------------
+            *  if the tuple is null, then we assume
+            *  there is nothing more to process so
+            *  we just return null...
+            * ----------------
+            */
+           if (TupIsNull(slot)) {
+               result = NULL;
+               break;
+           }
+       }
+       
+       /* ----------------
+        *      if we have a junk filter, then project a new
+        *      tuple with the junk removed.
+        *
+        *      Store this new "clean" tuple in the place of the 
+        *      original tuple.
+        *
+        *      Also, extract all the junk ifnormation we need.
+        * ----------------
+        */
+       if ((junkfilter = estate->es_junkFilter) != (JunkFilter*)NULL) {
+           Datum       datum;
+/*         NameData    attrName; */
+           HeapTuple   newTuple;
+           bool        isNull;
+           
+           /* ---------------
+            * extract the 'ctid' junk attribute.
+            * ---------------
+            */
+           if (operation == CMD_UPDATE || operation == CMD_DELETE) {
+               if (! ExecGetJunkAttribute(junkfilter,
+                                          slot,
+                                          "ctid",
+                                          &datum,
+                                          &isNull))
+                   elog(WARN,"ExecutePlan: NO (junk) `ctid' was found!");
+               
+               if (isNull) 
+                   elog(WARN,"ExecutePlan: (junk) `ctid' is NULL!");
+               
+               tupleid = (ItemPointer) DatumGetPointer(datum);
+               tuple_ctid = *tupleid; /* make sure we don't free the ctid!! */
+               tupleid = &tuple_ctid;
+           }
+           
+           /* ---------------
+            * Finally create a new "clean" tuple with all junk attributes
+            * removed 
+            * ---------------
+            */
+           newTuple = ExecRemoveJunk(junkfilter, slot);
+           
+           slot = ExecStoreTuple(newTuple, /* tuple to store */
+                                 slot,     /* destination slot */
+                                 InvalidBuffer,/* this tuple has no buffer */
+                                 true); /* tuple should be pfreed */
+       } /* if (junkfilter... */
+       
+       /* ----------------
+        *      now that we have a tuple, do the appropriate thing
+        *      with it.. either return it to the user, add
+        *      it to a relation someplace, delete it from a
+        *      relation, or modify some of it's attributes.
+        * ----------------
+        */
+       
+       switch(operation) {
+       case CMD_SELECT:
+           ExecRetrieve(slot,    /* slot containing tuple */
+                        printfunc,       /* print function */
+                        intoRelationDesc); /* "into" relation */
+           result = slot;
+           break;
+           
+       case CMD_INSERT:
+           ExecAppend(slot, tupleid, estate);
+           result = NULL;
+           break;
+           
+       case CMD_DELETE:
+           ExecDelete(slot, tupleid, estate);
+           result = NULL;
+           break;
+           
+       case CMD_UPDATE:
+           ExecReplace(slot, tupleid, estate, parseTree);
+           result = NULL;
+           break;
+           
+           /* Total hack. I'm ignoring any accessor functions for
+              Relation, RelationTupleForm, NameData.
+              Assuming that NameData.data has offset 0.
+              */
+       case CMD_NOTIFY: {
+           RelationInfo *rInfo = estate->es_result_relation_info;
+           Relation rDesc = rInfo->ri_RelationDesc;
+           Async_Notify(rDesc->rd_rel->relname.data);
+           result = NULL;
+           current_tuple_count = 0;
+           numberTuples = 1;
+           elog(DEBUG, "ExecNotify %s",&rDesc->rd_rel->relname);
+       }
+           break;
+           
+       default:
+           elog(DEBUG, "ExecutePlan: unknown operation in queryDesc");
+           result = NULL;
+           break;
+       }
+       /* ----------------
+        *      check our tuple count.. if we've returned the
+        *      proper number then return, else loop again and
+        *      process more tuples..
+        * ----------------
+        */
+       current_tuple_count += 1;
+       if (numberTuples == current_tuple_count)
+           break;
+    }
+
+    /* ----------------
+     * here, result is either a slot containing a tuple in the case
+     *  of a RETRIEVE or NULL otherwise.
+     * ----------------
+     */
+    return result;         
+}
+
+/* ----------------------------------------------------------------
+ *     ExecRetrieve
+ *
+ *     RETRIEVEs are easy.. we just pass the tuple to the appropriate
+ *     print function.  The only complexity is when we do a
+ *     "retrieve into", in which case we insert the tuple into
+ *     the appropriate relation (note: this is a newly created relation
+ *     so we don't need to worry about indices or locks.)
+ * ----------------------------------------------------------------
+ */
+static void
+ExecRetrieve(TupleTableSlot *slot,
+            void (*printfunc)(),
+            Relation intoRelationDesc)
+{
+    HeapTuple  tuple;
+    TupleDesc  attrtype;
+
+    /* ----------------
+     * get the heap tuple out of the tuple table slot
+     * ----------------
+     */
+    tuple =  slot->val;
+    attrtype = slot->ttc_tupleDescriptor;
+
+    /* ----------------
+     * insert the tuple into the "into relation"
+     * ----------------
+     */
+    if (intoRelationDesc != NULL) {
+      heap_insert (intoRelationDesc, tuple);
+      IncrAppended();
+    }
+
+    /* ----------------
+     * send the tuple to the front end (or the screen)
+     * ----------------
+     */
+    (*printfunc)(tuple, attrtype);
+    IncrRetrieved();
+}
+
+/* ----------------------------------------------------------------
+ *     ExecAppend
+ *
+ *     APPENDs are trickier.. we have to insert the tuple into
+ *     the base relation and insert appropriate tuples into the
+ *     index relations. 
+ * ----------------------------------------------------------------
+ */
+
+static void
+ExecAppend(TupleTableSlot *slot,
+           ItemPointer tupleid,
+          EState *estate)
+{
+    HeapTuple   tuple;
+    RelationInfo *resultRelationInfo;
+    Relation    resultRelationDesc;
+    int                 numIndices;
+    Oid         newId;
+
+    /* ----------------
+     * get the heap tuple out of the tuple table slot
+     * ----------------
+     */
+    tuple = slot->val;
+
+    /* ----------------
+     * get information on the result relation
+     * ----------------
+     */
+    resultRelationInfo = estate->es_result_relation_info;
+    resultRelationDesc = resultRelationInfo->ri_RelationDesc;
+
+    /* ----------------
+     * have to add code to preform unique checking here.
+     *  cim -12/1/89
+     * ----------------
+     */
+
+    /* ----------------
+     * insert the tuple
+     * ----------------
+     */
+    newId = heap_insert(resultRelationDesc, /* relation desc */
+                       tuple);             /* heap tuple */
+    IncrAppended();
+    UpdateAppendOid(newId);
+
+    /* ----------------
+     * process indices
+     * 
+     * Note: heap_insert adds a new tuple to a relation.  As a side
+     *  effect, the tupleid of the new tuple is placed in the new
+     *  tuple's t_ctid field.
+     * ----------------
+     */
+    numIndices = resultRelationInfo->ri_NumIndices;
+    if (numIndices > 0) {
+       ExecInsertIndexTuples(slot, &(tuple->t_ctid), estate);
+    }
+}
+
+/* ----------------------------------------------------------------
+ *     ExecDelete
+ *
+ *     DELETE is like append, we delete the tuple and its
+ *     index tuples. 
+ * ----------------------------------------------------------------
+ */
+static void
+ExecDelete(TupleTableSlot *slot,
+          ItemPointer tupleid,
+          EState *estate)
+{
+    RelationInfo *resultRelationInfo;
+    Relation    resultRelationDesc;
+
+    /* ----------------
+     * get the result relation information
+     * ----------------
+     */
+    resultRelationInfo = estate->es_result_relation_info;
+    resultRelationDesc = resultRelationInfo->ri_RelationDesc;
+
+    /* ----------------
+     * delete the tuple
+     * ----------------
+     */
+    (void) heap_delete(resultRelationDesc, /* relation desc */
+                       tupleid);           /* item pointer to tuple */
+
+    IncrDeleted();
+
+    /* ----------------
+     * Note: Normally one would think that we have to
+     *       delete index tuples associated with the
+     *       heap tuple now..
+     *
+     *       ... but in POSTGRES, we have no need to do this
+     *        because the vacuum daemon automatically
+     *       opens an index scan and deletes index tuples
+     *       when it finds deleted heap tuples. -cim 9/27/89
+     * ----------------
+     */
+
+}
+
+/* ----------------------------------------------------------------
+ *     ExecReplace
+ *
+ *     note: we can't run replace queries with transactions
+ *     off because replaces are actually appends and our
+ *     scan will mistakenly loop forever, replacing the tuple
+ *     it just appended..  This should be fixed but until it
+ *     is, we don't want to get stuck in an infinite loop
+ *     which corrupts your database..
+ * ----------------------------------------------------------------
+ */
+static void
+ExecReplace(TupleTableSlot *slot,
+           ItemPointer tupleid,
+           EState *estate,
+           Query *parseTree)
+{
+    HeapTuple          tuple;
+    RelationInfo       *resultRelationInfo;
+    Relation           resultRelationDesc;
+    int                        numIndices;
+
+    /* ----------------
+     * abort the operation if not running transactions
+     * ----------------
+     */
+    if (IsBootstrapProcessingMode()) {
+       elog(DEBUG, "ExecReplace: replace can't run without transactions");
+       return;
+    }
+
+    /* ----------------
+     * get the heap tuple out of the tuple table slot
+     * ----------------
+     */
+    tuple = slot->val;
+
+    /* ----------------
+     * get the result relation information
+     * ----------------
+     */
+    resultRelationInfo = estate->es_result_relation_info;
+    resultRelationDesc = resultRelationInfo->ri_RelationDesc;
+
+    /* ----------------
+     * have to add code to preform unique checking here.
+     *  in the event of unique tuples, this becomes a deletion
+     *  of the original tuple affected by the replace.
+     *  cim -12/1/89
+     * ----------------
+     */
+
+    /* ----------------
+     * replace the heap tuple
+     *
+     * Don't want to continue if our heap_replace didn't actually
+     * do a replace. This would be the case if heap_replace 
+     * detected a non-functional update. -kw 12/30/93
+     * ----------------
+     */
+    if (heap_replace(resultRelationDesc, /* relation desc */
+                    tupleid,            /* item ptr of tuple to replace */
+                    tuple)) {           /* replacement heap tuple */
+       return;
+    }
+
+    IncrReplaced();
+
+    /* ----------------
+     * Note: instead of having to update the old index tuples
+     *        associated with the heap tuple, all we do is form
+     *       and insert new index tuples..  This is because
+     *        replaces are actually deletes and inserts and
+     *       index tuple deletion is done automagically by
+     *       the vaccuum deamon.. All we do is insert new
+     *       index tuples.  -cim 9/27/89
+     * ----------------
+     */
+
+    /* ----------------
+     * process indices
+     *
+     * heap_replace updates a tuple in the base relation by invalidating
+     *  it and then appending a new tuple to the relation.  As a side
+     *  effect, the tupleid of the new tuple is placed in the new
+     *  tuple's t_ctid field.  So we now insert index tuples using
+     *  the new tupleid stored there.
+     * ----------------
+     */
+    numIndices = resultRelationInfo->ri_NumIndices;
+    if (numIndices > 0) {
+       ExecInsertIndexTuples(slot, &(tuple->t_ctid), estate);
+    }
+}
diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
new file mode 100644 (file)
index 0000000..d6d92bf
--- /dev/null
@@ -0,0 +1,477 @@
+/*-------------------------------------------------------------------------
+ *
+ * execProcnode.c--
+ *   contains dispatch functions which call the appropriate "initialize",
+ *   "get a tuple", and "cleanup" routines for the given node type.
+ *   If the node has children, then it will presumably call ExecInitNode,
+ *   ExecProcNode, or ExecEndNode on it's subnodes and do the appropriate
+ *   processing..
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ *   INTERFACE ROUTINES
+ *     ExecInitNode    -       initialize a plan node and it's subplans
+ *     ExecProcNode    -       get a tuple by executing the plan node
+ *     ExecEndNode     -       shut down a plan node and it's subplans
+ *
+ *   NOTES
+ *     This used to be three files.  It is now all combined into
+ *     one file so that it is easier to keep ExecInitNode, ExecProcNode,
+ *     and ExecEndNode in sync when new nodes are added.
+ *     
+ *   EXAMPLE
+ *     suppose we want the age of the manager of the shoe department and
+ *     the number of employees in that department.  so we have the query:
+ *
+ *             retrieve (DEPT.no_emps, EMP.age)
+ *             where EMP.name = DEPT.mgr and
+ *                   DEPT.name = "shoe"
+ *     
+ *     Suppose the planner gives us the following plan:
+ *     
+ *                     Nest Loop (DEPT.mgr = EMP.name)
+ *                     /       \ 
+ *                    /         \
+ *                Seq Scan     Seq Scan
+ *                 DEPT          EMP
+ *             (name = "shoe")
+ *     
+ *     ExecStart() is called first.
+ *     It calls InitPlan() which calls ExecInitNode() on
+ *     the root of the plan -- the nest loop node.
+ *
+ *    *        ExecInitNode() notices that it is looking at a nest loop and
+ *     as the code below demonstrates, it calls ExecInitNestLoop().
+ *     Eventually this calls ExecInitNode() on the right and left subplans
+ *     and so forth until the entire plan is initialized.
+ *     
+ *    *        Then when ExecRun() is called, it calls ExecutePlan() which
+ *     calls ExecProcNode() repeatedly on the top node of the plan.
+ *     Each time this happens, ExecProcNode() will end up calling
+ *     ExecNestLoop(), which calls ExecProcNode() on its subplans.
+ *     Each of these subplans is a sequential scan so ExecSeqScan() is
+ *     called.  The slots returned by ExecSeqScan() may contain
+ *     tuples which contain the attributes ExecNestLoop() uses to
+ *     form the tuples it returns.
+ *
+ *    *        Eventually ExecSeqScan() stops returning tuples and the nest
+ *     loop join ends.  Lastly, ExecEnd() calls ExecEndNode() which
+ *     calls ExecEndNestLoop() which in turn calls ExecEndNode() on
+ *     its subplans which result in ExecEndSeqScan().
+ *
+ *     This should show how the executor works by having
+ *     ExecInitNode(), ExecProcNode() and ExecEndNode() dispatch
+ *     their work to the appopriate node support routines which may
+ *     in turn call these routines themselves on their subplans.
+ *
+ */
+#include "executor/executor.h"
+#include "executor/nodeResult.h"
+#include "executor/nodeAppend.h"
+#include "executor/nodeSeqscan.h"
+#include "executor/nodeIndexscan.h"
+#include "executor/nodeNestloop.h"
+#include "executor/nodeMergejoin.h"
+#include "executor/nodeMaterial.h"
+#include "executor/nodeSort.h"
+#include "executor/nodeUnique.h"
+#include "executor/nodeGroup.h"
+#include "executor/nodeAgg.h"
+#include "executor/nodeHash.h"
+#include "executor/nodeHashjoin.h"
+#include "executor/nodeTee.h"
+
+/* ------------------------------------------------------------------------
+ *     ExecInitNode
+ *   
+ *     Recursively initializes all the nodes in the plan rooted
+ *     at 'node'. 
+ *   
+ *     Initial States:
+ *       'node' is the plan produced by the query planner
+ * 
+ *      returns TRUE/FALSE on whether the plan was successfully initialized
+ * ------------------------------------------------------------------------
+ */
+bool
+ExecInitNode(Plan *node, EState *estate, Plan *parent)
+{
+    bool               result;
+    
+    /* ----------------
+     * do nothing when we get to the end
+     *  of a leaf on tree.
+     * ----------------
+     */   
+    if (node == NULL)
+       return FALSE;
+    
+    switch(nodeTag(node)) {
+       /* ----------------
+        *      control nodes
+        * ----------------
+        */
+    case T_Result:
+       result = ExecInitResult((Result *)node, estate, parent);        
+       break;
+       
+    case T_Append:
+       result = ExecInitAppend((Append *)node, estate, parent);
+       break;
+       
+       /* ----------------
+        *      scan nodes
+        * ----------------
+        */
+    case T_SeqScan:
+       result = ExecInitSeqScan((SeqScan *)node, estate, parent);      
+       break;
+       
+    case T_IndexScan:
+       result = ExecInitIndexScan((IndexScan *)node, estate, parent);
+       break;
+       
+       /* ----------------
+        *      join nodes
+        * ----------------
+        */
+    case T_NestLoop:
+       result = ExecInitNestLoop((NestLoop *)node, estate, parent);
+       break;
+       
+    case T_MergeJoin:
+       result = ExecInitMergeJoin((MergeJoin *)node, estate, parent);
+       break;
+       
+       /* ----------------
+        *      materialization nodes
+        * ----------------
+        */
+    case T_Material:
+       result = ExecInitMaterial((Material *)node, estate, parent);
+       break;
+       
+    case T_Sort:
+       result = ExecInitSort((Sort *)node, estate, parent);
+       break;
+       
+    case T_Unique:
+       result = ExecInitUnique((Unique *)node, estate, parent);
+       break;
+       
+    case T_Group:    
+       result = ExecInitGroup((Group *)node, estate, parent);
+       break;
+
+    case T_Agg:
+       result = ExecInitAgg((Agg *)node, estate, parent);
+       break;
+       
+    case T_Hash:
+       result = ExecInitHash((Hash *)node, estate, parent);
+       break;
+       
+    case T_HashJoin:
+       result = ExecInitHashJoin((HashJoin *)node, estate, parent);
+       break;
+       
+    case T_Tee:
+       result = ExecInitTee((Tee*)node, estate, parent);
+       break;
+
+    default:
+       elog(DEBUG, "ExecInitNode: node not yet supported: %d",
+            nodeTag(node));
+       result = FALSE;
+    }
+    
+    return result;
+}
+
+
+/* ----------------------------------------------------------------
+ *     ExecProcNode
+ *   
+ *     Initial States:
+ *       the query tree must be initialized once by calling ExecInit.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecProcNode(Plan *node, Plan *parent)
+{
+    TupleTableSlot     *result;
+    
+    /* ----------------
+     * deal with NULL nodes..
+     * ----------------
+     */
+    if (node == NULL)
+       return NULL;
+    
+    switch(nodeTag(node)) {
+       /* ----------------
+        *      control nodes
+        * ----------------
+        */
+    case T_Result:
+       result = ExecResult((Result *)node);
+       break;
+       
+    case T_Append:
+       result = ExecProcAppend((Append *)node);
+       break;
+       
+       /* ----------------
+        *      scan nodes
+        * ----------------
+        */
+    case T_SeqScan:
+       result = ExecSeqScan((SeqScan *)node);
+       break;
+
+    case T_IndexScan:
+       result = ExecIndexScan((IndexScan *)node);
+       break;
+       
+       /* ----------------
+        *      join nodes
+        * ----------------
+        */
+    case T_NestLoop:
+       result = ExecNestLoop((NestLoop *)node, parent);
+       break;
+       
+    case T_MergeJoin:
+       result = ExecMergeJoin((MergeJoin *)node);
+       break;
+       
+       /* ----------------
+        *      materialization nodes
+        * ----------------
+        */
+    case T_Material:
+       result = ExecMaterial((Material *)node);
+       break;
+       
+    case T_Sort:
+       result = ExecSort((Sort *)node);
+       break;
+       
+    case T_Unique:
+       result = ExecUnique((Unique *)node);
+       break;
+       
+    case T_Group:
+       result = ExecGroup((Group *)node);
+       break;
+
+    case T_Agg:
+       result = ExecAgg((Agg *)node);
+       break;
+       
+    case T_Hash:
+       result = ExecHash((Hash *)node);
+       break;
+       
+    case T_HashJoin:
+       result = ExecHashJoin((HashJoin *)node);
+       break;
+       
+    case T_Tee:
+       result = ExecTee((Tee*)node, parent);
+       break;
+
+    default:
+       elog(DEBUG, "ExecProcNode: node not yet supported: %d",
+            nodeTag(node));
+       result = FALSE;
+    }
+    
+    return result;
+}
+
+int
+ExecCountSlotsNode(Plan *node)
+{
+    if (node == (Plan *)NULL)
+       return 0;
+    
+    switch(nodeTag(node)) {
+       /* ----------------
+        *      control nodes
+        * ----------------
+        */
+    case T_Result:
+       return ExecCountSlotsResult((Result *)node);
+       
+    case T_Append:
+       return ExecCountSlotsAppend((Append *)node);
+       
+       /* ----------------
+        *      scan nodes
+        * ----------------
+        */
+    case T_SeqScan:
+       return ExecCountSlotsSeqScan((SeqScan *)node);
+
+    case T_IndexScan:
+       return ExecCountSlotsIndexScan((IndexScan *)node);
+       
+       /* ----------------
+        *      join nodes
+        * ----------------
+        */
+    case T_NestLoop:
+       return ExecCountSlotsNestLoop((NestLoop *)node);
+       
+    case T_MergeJoin:
+       return ExecCountSlotsMergeJoin((MergeJoin *)node);
+       
+       /* ----------------
+        *      materialization nodes
+        * ----------------
+        */
+    case T_Material:
+       return ExecCountSlotsMaterial((Material *)node);
+       
+    case T_Sort:
+       return ExecCountSlotsSort((Sort *)node);
+       
+    case T_Unique:
+       return ExecCountSlotsUnique((Unique *)node);
+       
+    case T_Group:
+       return ExecCountSlotsGroup((Group *)node);
+
+    case T_Agg:
+       return ExecCountSlotsAgg((Agg *)node);
+       
+    case T_Hash:
+       return ExecCountSlotsHash((Hash *)node);
+       
+    case T_HashJoin:
+       return ExecCountSlotsHashJoin((HashJoin *)node);
+       
+    case T_Tee:
+       return ExecCountSlotsTee((Tee*)node);
+
+    default:
+       elog(WARN, "ExecCountSlotsNode: node not yet supported: %d",
+            nodeTag(node));
+       break;
+    }
+    return 0;
+}
+
+/* ----------------------------------------------------------------  
+ *     ExecEndNode
+ *   
+ *     Recursively cleans up all the nodes in the plan rooted
+ *     at 'node'.
+ *
+ *     After this operation, the query plan will not be able to
+ *     processed any further.  This should be called only after
+ *     the query plan has been fully executed.
+ * ----------------------------------------------------------------  
+ */
+void
+ExecEndNode(Plan *node, Plan *parent)
+{
+    /* ----------------
+     * do nothing when we get to the end
+     *  of a leaf on tree.
+     * ----------------
+     */
+    if (node == NULL) 
+       return;
+    
+    switch(nodeTag(node)) {
+       /* ----------------
+        *  control nodes
+        * ----------------
+        */
+    case T_Result:
+       ExecEndResult((Result *)node);
+       break;
+       
+    case T_Append:
+       ExecEndAppend((Append *)node);
+       break;
+       
+       /* ----------------
+        *      scan nodes
+        * ----------------
+        */
+    case T_SeqScan:
+       ExecEndSeqScan((SeqScan *)node);
+       break;
+
+    case T_IndexScan:
+       ExecEndIndexScan((IndexScan *)node);
+       break;
+       
+       /* ----------------
+        *      join nodes
+        * ----------------
+        */
+    case T_NestLoop:
+       ExecEndNestLoop((NestLoop *)node);
+       break;
+       
+    case T_MergeJoin:
+       ExecEndMergeJoin((MergeJoin *)node);
+       break;
+       
+       /* ----------------
+        *      materialization nodes
+        * ----------------
+        */
+    case T_Material:
+       ExecEndMaterial((Material *)node);
+       break;
+       
+    case T_Sort:
+       ExecEndSort((Sort *)node);
+       break;
+       
+    case T_Unique:
+       ExecEndUnique((Unique *)node);
+       break;
+       
+    case T_Group:
+       ExecEndGroup((Group *)node);
+       break;
+
+    case T_Agg:
+       ExecEndAgg((Agg *)node);
+       break;
+       
+       /* ----------------
+        *      XXX add hooks to these
+        * ----------------
+        */
+    case T_Hash:
+       ExecEndHash((Hash *) node);
+       break;
+       
+    case T_HashJoin:
+       ExecEndHashJoin((HashJoin *) node);
+       break;
+       
+    case T_Tee:
+        ExecEndTee((Tee*) node, parent);
+       break;
+
+    default:
+       elog(DEBUG, "ExecEndNode: node not yet supported",
+            nodeTag(node));
+       break;
+    }
+}
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
new file mode 100644 (file)
index 0000000..4101643
--- /dev/null
@@ -0,0 +1,1504 @@
+/*-------------------------------------------------------------------------
+ *
+ * execQual.c--
+ *    Routines to evaluate qualification and targetlist expressions
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ *   INTERFACE ROUTINES
+ *     ExecEvalExpr    - evaluate an expression and return a datum
+ *     ExecQual        - return true/false if qualification is satisified
+ *     ExecTargetList  - form a new tuple by projecting the given tuple
+ *
+ *   NOTES
+ *     ExecEvalExpr() and ExecEvalVar() are hotspots.  making these faster
+ *     will speed up the entire system.  Unfortunately they are currently
+ *     implemented recursively..  Eliminating the recursion is bound to
+ *     improve the speed of the executor.
+ *
+ *     ExecTargetList() is used to make tuple projections.  Rather then
+ *     trying to speed it up, the execution plan should be pre-processed
+ *     to facilitate attribute sharing between nodes wherever possible,
+ *     instead of doing needless copying.  -cim 5/31/91
+ *
+ */
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+
+#include "optimizer/clauses.h"
+
+#include "nodes/memnodes.h"
+#include "catalog/pg_language.h"
+#include "executor/executor.h"
+#include "executor/execFlatten.h"
+#include "executor/functions.h"
+#include "access/heapam.h"
+#include "utils/memutils.h"
+#include "utils/builtins.h"
+#include "utils/palloc.h"
+#include "utils/fcache.h"
+#include "utils/fcache2.h"
+#include "utils/array.h"
+
+/* ----------------
+ *     externs and constants
+ * ----------------
+ */
+
+/*
+ * XXX Used so we can get rid of use of Const nodes in the executor.
+ * Currently only used by ExecHashGetBucket and set only by ExecMakeVarConst
+ * and by ExecEvalArrayRef.
+ */
+bool   execConstByVal;
+int    execConstLen;
+
+/* static functions decls */
+static Datum ExecEvalAggreg(Aggreg *agg, ExprContext *econtext, bool *isNull);
+static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext,
+                             bool *isNull, bool *isDone);
+
+/* --------------------------------
+ *    ExecEvalArrayRef
+ *
+ *     This function takes an ArrayRef and returns a Const Node if it
+ *     is an array reference or returns the changed Array Node if it is
+ *        an array assignment.
+ *
+ * --------------------------------
+ */
+static Datum
+ExecEvalArrayRef(ArrayRef *arrayRef,
+                ExprContext *econtext,
+                bool *isNull,
+                bool *isDone)
+{
+    bool       dummy;
+    int        i = 0, j = 0;
+    ArrayType  *array_scanner;
+    List       *upperIndexpr, *lowerIndexpr;
+    Node       *assgnexpr;
+    List       *elt;
+    IntArray   upper, lower;
+    int        *lIndex;
+    char *dataPtr;
+    
+    execConstByVal = arrayRef->refelembyval;
+    *isNull       =    false; 
+    array_scanner =    (ArrayType*)ExecEvalExpr(arrayRef->refexpr,
+                                            econtext,
+                                            isNull,
+                                            isDone);
+    if (*isNull) return (Datum)NULL;
+    
+    upperIndexpr = arrayRef->refupperindexpr;
+
+    foreach (elt, upperIndexpr) {
+       upper.indx[i++] = (int32)ExecEvalExpr((Node*)lfirst(elt),
+                                             econtext,
+                                             isNull,
+                                             &dummy);
+       if (*isNull) return (Datum)NULL;
+    }
+    
+    lowerIndexpr = arrayRef->reflowerindexpr;
+    lIndex = NULL;
+    if (lowerIndexpr != NIL) {
+       foreach (elt, lowerIndexpr) {
+           lower.indx[j++] = (int32)ExecEvalExpr((Node*)lfirst(elt),
+                                                 econtext,
+                                                 isNull,
+                                                 &dummy);
+           if (*isNull) return (Datum)NULL;
+       }
+       if (i != j)
+           elog(WARN, 
+                "ExecEvalArrayRef: upper and lower indices mismatch");
+       lIndex = lower.indx;
+    }
+
+    assgnexpr    = arrayRef->refassgnexpr;
+    if (assgnexpr != NULL) {
+       dataPtr = (char*)ExecEvalExpr((Node *)
+                                     assgnexpr, econtext,
+                                     isNull, &dummy);
+       if (*isNull) return (Datum)NULL;
+       if (lIndex == NULL)
+           return (Datum) array_set(array_scanner, i, upper.indx, dataPtr, 
+                                    arrayRef->refelembyval,
+                                    arrayRef->refelemlength,
+                                    arrayRef->refattrlength, isNull);
+       return (Datum) array_assgn(array_scanner, i, upper.indx,
+                                  lower.indx, 
+                                  (ArrayType*)dataPtr, 
+                                  arrayRef->refelembyval,
+                                  arrayRef->refelemlength, isNull);
+    }
+    if (lIndex == NULL) 
+       return (Datum) array_ref(array_scanner, i, upper.indx,
+                                arrayRef->refelembyval,
+                                arrayRef->refelemlength,
+                                arrayRef->refattrlength, isNull);
+    return (Datum) array_clip(array_scanner, i, upper.indx, lower.indx,
+                             arrayRef->refelembyval,
+                             arrayRef->refelemlength, isNull);
+}
+
+
+/* ----------------------------------------------------------------
+ *     ExecEvalAggreg
+ *    
+ *     Returns a Datum whose value is the value of the precomputed
+ *     aggregate found in the given expression context.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalAggreg(Aggreg *agg, ExprContext *econtext, bool *isNull)
+{
+    
+    *isNull = econtext->ecxt_nulls[agg->aggno];
+    return econtext->ecxt_values[agg->aggno];
+}
+
+/* ----------------------------------------------------------------
+ *     ExecEvalVar
+ *    
+ *     Returns a Datum whose value is the value of a range
+ *     variable with respect to given expression context.
+ * ----------------------------------------------------------------
+ */
+Datum
+ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
+{
+    Datum              result;
+    TupleTableSlot     *slot;
+    AttrNumber                 attnum;
+    HeapTuple          heapTuple;
+    TupleDesc          tuple_type;
+    Buffer             buffer;
+    bool               byval;
+    int16              len;
+    
+    /* ----------------
+     * get the slot we want
+     * ----------------
+     */
+    switch(variable->varno) {
+    case INNER: /* get the tuple from the inner node */
+       slot = econtext->ecxt_innertuple;
+       break;
+       
+    case OUTER: /* get the tuple from the outer node */
+       slot = econtext->ecxt_outertuple;
+       break;
+       
+    default: /* get the tuple from the relation being scanned */
+       slot = econtext->ecxt_scantuple;
+       break;
+    } 
+    
+    /* ----------------
+     *   extract tuple information from the slot
+     * ----------------
+     */
+    heapTuple =  slot->val;
+    tuple_type = slot->ttc_tupleDescriptor;
+    buffer =    slot->ttc_buffer;
+    
+    attnum =   variable->varattno;
+    
+    /*
+     * If the attribute number is invalid, then we are supposed to
+     * return the entire tuple, we give back a whole slot so that
+     * callers know what the tuple looks like.
+     */
+    if (attnum == InvalidAttrNumber)
+       {
+           TupleTableSlot  *tempSlot;
+           TupleDesc td;
+           HeapTuple tup;
+
+           tempSlot = makeNode(TupleTableSlot);
+           tempSlot->ttc_shouldFree = false;
+           tempSlot->ttc_descIsNew = true;
+           tempSlot->ttc_tupleDescriptor = (TupleDesc)NULL,
+           tempSlot->ttc_buffer = InvalidBuffer;
+           tempSlot->ttc_whichplan = -1;
+
+           tup = heap_copytuple(slot->val);
+           td = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
+           
+           ExecSetSlotDescriptor(tempSlot, td);
+           
+           ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
+           return (Datum) tempSlot;
+       }
+    
+    result =  (Datum)
+      heap_getattr(heapTuple,  /* tuple containing attribute */
+                  buffer,     /* buffer associated with tuple */
+                  attnum,     /* attribute number of desired attribute */
+                  tuple_type, /* tuple descriptor of tuple */
+                  isNull);    /* return: is attribute null? */
+    
+    /* ----------------
+     * return null if att is null
+     * ----------------
+     */
+    if (*isNull)
+       return (Datum) NULL;
+    
+    /* ----------------
+     *   get length and type information..
+     *   ??? what should we do about variable length attributes
+     *  - variable length attributes have their length stored
+     *     in the first 4 bytes of the memory pointed to by the
+     *     returned value.. If we can determine that the type
+     *     is a variable length type, we can do the right thing.
+     *    -cim 9/15/89
+     * ----------------
+     */
+    if (attnum < 0) {
+       /* ----------------
+        *  If this is a pseudo-att, we get the type and fake the length.
+        *  There ought to be a routine to return the real lengths, so
+        *  we'll mark this one ... XXX -mao
+        * ----------------
+        */
+        len =  heap_sysattrlen(attnum);        /* XXX see -mao above */
+       byval = heap_sysattrbyval(attnum);      /* XXX see -mao above */
+    } else {
+        len =   tuple_type->attrs[ attnum-1 ]->attlen;
+       byval = tuple_type->attrs[ attnum-1 ]->attbyval ? true : false ;
+    }
+    
+    execConstByVal = byval;
+    execConstLen =   len;
+    
+    return result;
+}
+
+/* ----------------------------------------------------------------
+ *      ExecEvalParam
+ *
+ *      Returns the value of a parameter.  A param node contains
+ *     something like ($.name) and the expression context contains
+ *     the current parameter bindings (name = "sam") (age = 34)...
+ *     so our job is to replace the param node with the datum
+ *     containing the appropriate information ("sam").
+ *
+ *     Q: if we have a parameter ($.foo) without a binding, i.e.
+ *        there is no (foo = xxx) in the parameter list info,
+ *        is this a fatal error or should this be a "not available"
+ *        (in which case we shoud return a Const node with the
+ *         isnull flag) ?  -cim 10/13/89
+ *
+ *      Minor modification: Param nodes now have an extra field,
+ *      `paramkind' which specifies the type of parameter 
+ *      (see params.h). So while searching the paramList for
+ *      a paramname/value pair, we have also to check for `kind'.
+ *     
+ *      NOTE: The last entry in `paramList' is always an
+ *      entry with kind == PARAM_INVALID.
+ * ----------------------------------------------------------------
+ */
+Datum
+ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
+{
+    
+    char               *thisParameterName;
+    int                        thisParameterKind;
+    AttrNumber         thisParameterId;
+    int                matchFound;
+    ParamListInfo      paramList;
+    
+    thisParameterName = expression->paramname;
+    thisParameterKind = expression->paramkind;
+    thisParameterId =   expression->paramid;
+    paramList =        econtext->ecxt_param_list_info;
+    
+    *isNull = false;
+    /*
+     * search the list with the parameter info to find a matching name.
+     * An entry with an InvalidName denotes the last element in the array.
+     */
+    matchFound = 0;
+    if (paramList != NULL) {
+       /*
+        * search for an entry in 'paramList' that matches
+        * the `expression'.
+        */
+       while(paramList->kind != PARAM_INVALID && !matchFound) {
+           switch (thisParameterKind) {
+           case PARAM_NAMED:
+               if (thisParameterKind == paramList->kind &&
+                   strcmp(paramList->name, thisParameterName) == 0){
+                   matchFound = 1;
+               } 
+               break;
+           case PARAM_NUM:
+               if (thisParameterKind == paramList->kind &&
+                   paramList->id == thisParameterId) {
+                   matchFound = 1;
+               }
+               break;
+           case PARAM_OLD:
+           case PARAM_NEW:
+               if (thisParameterKind == paramList->kind &&
+                   paramList->id == thisParameterId)
+                   {
+                       matchFound = 1;
+                       /*
+                        * sanity check
+                        */
+                       if (strcmp(paramList->name, thisParameterName) != 0){
+                           elog(WARN,
+                                "ExecEvalParam: new/old params with same id & diff names");
+                       }
+                   }
+               break;
+           default:
+               /*
+                * oops! this is not supposed to happen!
+                */
+               elog(WARN, "ExecEvalParam: invalid paramkind %d",
+                    thisParameterKind);
+           }
+           if (! matchFound) {
+               paramList++;
+           }
+       } /*while*/
+    } /*if*/
+    
+    if (!matchFound) {
+       /*
+        * ooops! we couldn't find this parameter
+        * in the parameter list. Signal an error
+        */
+       elog(WARN, "ExecEvalParam: Unknown value for parameter %s",
+            thisParameterName);
+    }
+    
+    /*
+     * return the value.
+     */
+    if (paramList->isnull)
+       {
+           *isNull = true;
+           return (Datum)NULL;
+       }
+    
+    if (expression->param_tlist != NIL)
+       {
+           HeapTuple      tup;
+           Datum          value;
+           List           *tlist = expression->param_tlist;
+           TargetEntry    *tle = (TargetEntry *)lfirst(tlist);
+           TupleTableSlot *slot = (TupleTableSlot *)paramList->value;
+           
+           tup = slot->val;
+           value = ProjectAttribute(slot->ttc_tupleDescriptor,
+                                    tle, tup, isNull);
+           return value;
+       }
+    return(paramList->value);
+}
+
+
+/* ----------------------------------------------------------------
+ *     ExecEvalOper / ExecEvalFunc support routines
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     GetAttributeByName
+ *     GetAttributeByNum
+ *
+ *     These are functions which return the value of the
+ *     named attribute out of the tuple from the arg slot.  User defined
+ *     C functions which take a tuple as an argument are expected
+ *     to use this.  Ex: overpaid(EMP) might call GetAttributeByNum().
+ * ----------------
+ */
+char *
+GetAttributeByNum(TupleTableSlot  *slot,
+                 AttrNumber attrno,
+                 bool *isNull)
+{
+    Datum retval;
+    
+    if (!AttributeNumberIsValid(attrno))
+       elog(WARN, "GetAttributeByNum: Invalid attribute number");
+    
+    if (!AttrNumberIsForUserDefinedAttr(attrno))
+       elog(WARN, "GetAttributeByNum: cannot access system attributes here");
+    
+    if (isNull == (bool *)NULL)
+       elog(WARN, "GetAttributeByNum: a NULL isNull flag was passed");
+    
+    if (TupIsNull(slot))
+       {
+           *isNull = true;
+           return (char *) NULL;
+       }
+    
+    retval = (Datum)
+       heap_getattr(slot->val,
+                    slot->ttc_buffer,
+                    attrno,
+                    slot->ttc_tupleDescriptor,
+                    isNull);
+    if (*isNull)
+       return (char *) NULL;
+    return (char *) retval;
+}
+
+/* XXX char16 name for catalogs */
+char *
+att_by_num(TupleTableSlot *slot,
+          AttrNumber attrno,
+          bool *isNull)
+{
+    return(GetAttributeByNum(slot, attrno, isNull));
+}
+
+char *
+GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
+{
+    AttrNumber attrno;
+    TupleDesc tupdesc;
+    HeapTuple tuple;
+    Datum retval;
+    int natts;
+    int i;
+    
+    if (attname == NULL)
+       elog(WARN, "GetAttributeByName: Invalid attribute name");
+    
+    if (isNull == (bool *)NULL)
+       elog(WARN, "GetAttributeByName: a NULL isNull flag was passed");
+    
+    if (TupIsNull(slot))
+       {
+           *isNull = true;
+           return (char *) NULL;
+       }
+    
+    tupdesc = slot->ttc_tupleDescriptor;
+    tuple = slot->val;
+    
+    natts = tuple->t_natts;
+    
+    attrno = InvalidAttrNumber;
+    for (i=0;i<tupdesc->natts;i++) {
+       if (namestrcmp(&(tupdesc->attrs[i]->attname), attname) == 0) {
+           attrno = tupdesc->attrs[i]->attnum;
+           break;
+       }
+    }
+
+    if (attrno == InvalidAttrNumber)
+       elog(WARN, "GetAttributeByName: attribute %s not found", attname);
+    
+    retval = (Datum)
+       heap_getattr(slot->val,
+                    slot->ttc_buffer,
+                    attrno,
+                    tupdesc,
+                    isNull);
+    if (*isNull)
+       return (char *) NULL;
+    return (char *) retval;
+}
+
+/* XXX char16 name for catalogs */
+char *
+att_by_name(TupleTableSlot *slot, char *attname, bool *isNull)
+{
+    return(GetAttributeByName(slot, attname, isNull));
+}
+
+void
+ExecEvalFuncArgs(FunctionCachePtr fcache,
+                ExprContext *econtext,
+                List *argList,
+                Datum argV[],
+                bool *argIsDone)
+{
+    int i;
+    bool argIsNull, *nullVect;
+    List *arg;
+    
+    nullVect = fcache->nullVect;
+    
+    i = 0;
+    foreach (arg, argList) {
+       /* ----------------
+        *   evaluate the expression, in general functions cannot take
+        *   sets as arguments but we make an exception in the case of
+        *   nested dot expressions.  We have to watch out for this case
+        *   here.
+        * ----------------
+        */
+       argV[i] = (Datum)
+           ExecEvalExpr((Node *) lfirst(arg),
+                        econtext,
+                        &argIsNull,
+                        argIsDone);
+       if (! (*argIsDone))
+           {
+               Assert(i == 0);
+               fcache->setArg = (char *)argV[0];
+               fcache->hasSetArg = true;
+           }
+       if (argIsNull)
+           nullVect[i] = true;
+       else
+           nullVect[i] = false;
+       i++;
+    }
+}
+
+/* ----------------
+ *     ExecMakeFunctionResult
+ * ----------------
+ */
+Datum
+ExecMakeFunctionResult(Node *node,
+                      List *arguments,
+                      ExprContext *econtext,
+                      bool *isNull,
+                      bool *isDone)
+{
+    Datum      argv[MAXFMGRARGS];
+    FunctionCachePtr fcache;
+    Func       *funcNode = NULL;
+    Oper       *operNode = NULL;
+    bool        funcisset = false;
+    
+    /*
+     * This is kind of ugly, Func nodes now have targetlists so that
+     * we know when and what to project out from postquel function results.
+     * This means we have to pass the func node all the way down instead
+     * of using only the fcache struct as before.  ExecMakeFunctionResult
+     * becomes a little bit more of a dual personality as a result.
+     */
+    if (IsA(node,Func))
+       {
+           funcNode = (Func *)node;
+           fcache = funcNode->func_fcache;
+       }
+    else
+       {
+           operNode = (Oper *)node;
+           fcache = operNode->op_fcache;
+       }
+    
+    /* ----------------
+     * arguments is a list of expressions to evaluate
+     *  before passing to the function manager.
+     *  We collect the results of evaluating the expressions
+     *  into a datum array (argv) and pass this array to arrayFmgr()
+     * ----------------
+     */
+    if (fcache->nargs != 0) {
+       bool argDone;
+       
+       if (fcache->nargs > MAXFMGRARGS) 
+           elog(WARN, "ExecMakeFunctionResult: too many arguments");
+       
+       /*
+        * If the setArg in the fcache is set we have an argument
+        * returning a set of tuples (i.e. a nested dot expression).  We
+        * don't want to evaluate the arguments again until the function
+        * is done. hasSetArg will always be false until we eval the args
+        * for the first time. We should set this in the parser.
+        */
+       if ((fcache->hasSetArg) && fcache->setArg != NULL)
+           {
+               argv[0] = (Datum)fcache->setArg;
+               argDone = false;
+           }
+       else
+           ExecEvalFuncArgs(fcache, econtext, arguments, argv, &argDone);
+       
+       if ((fcache->hasSetArg) && (argDone)) {
+           if (isDone) *isDone = true;
+           return (Datum)NULL;
+       }
+    }
+    
+    /* If this function is really a set, we have to diddle with things.
+     * If the function has already been called at least once, then the 
+     * setArg field of the fcache holds
+     * the OID of this set in pg_proc.  (This is not quite legit, since
+     * the setArg field is really for functions which take sets of tuples
+     * as input - set functions take no inputs at all.  But it's a nice
+     * place to stash this value, for now.)
+     *
+     * If this is the first call of the set's function, then
+     * the call to ExecEvalFuncArgs above just returned the OID of 
+     * the pg_proc tuple which defines this set.  So replace the existing
+     * funcid in the funcnode with the set's OID.  Also, we want a new
+     * fcache which points to the right function, so get that, now that
+     * we have the right OID.  Also zero out the argv, since the real
+     * set doesn't take any arguments.
+     */
+    if (((Func *)node)->funcid == SetEvalRegProcedure) {
+       funcisset = true;
+       if (fcache->setArg) {
+           argv[0] = 0;
+
+           ((Func *)node)->funcid = (Oid) PointerGetDatum(fcache->setArg);
+                      
+       } else {
+           ((Func *)node)->funcid = (Oid) argv[0];
+           setFcache(node, argv[0], NIL,econtext);
+           fcache = ((Func *)node)->func_fcache;
+           fcache->setArg = (char*)argv[0];
+           argv[0] = (Datum)0;
+       }
+    }
+    
+    /* ----------------
+     *   now return the value gotten by calling the function manager, 
+     *   passing the function the evaluated parameter values. 
+     * ----------------
+     */
+    if (fcache->language == SQLlanguageId) {
+       Datum result;
+       
+       Assert(funcNode);
+       result = postquel_function (funcNode, (char **) argv, isNull, isDone);
+       /*
+        * finagle the situation where we are iterating through all results
+        * in a nested dot function (whose argument function returns a set
+        * of tuples) and the current function finally finishes.  We need to
+        * get the next argument in the set and run the function all over
+        * again.  This is getting unclean.
+        */
+       if ((*isDone) && (fcache->hasSetArg)) {
+           bool argDone;
+           
+           ExecEvalFuncArgs(fcache, econtext, arguments, argv, &argDone);
+           
+           if (argDone) {
+               fcache->setArg = (char *)NULL;
+               *isDone = true;
+               result = (Datum)NULL;
+           }
+           else
+               result = postquel_function(funcNode,
+                                          (char **) argv,
+                                          isNull,
+                                          isDone);
+       }
+       if (funcisset) {
+           /* reset the funcid so that next call to this routine will
+            * still recognize this func as a set.
+            * Note that for now we assume that the set function in
+            * pg_proc must be a Postquel function - the funcid is
+            * not reset below for C functions.
+            */
+           ((Func *)node)->funcid = SetEvalRegProcedure;
+           /* If we're done with the results of this function, get rid
+            * of its func cache.
+            */
+           if (*isDone) {
+               ((Func *)node)->func_fcache = NULL;
+           }
+       }
+       return result;
+    }
+    else 
+       {
+           int i;
+           
+           if (isDone) *isDone = true;
+           for (i = 0; i < fcache->nargs; i++) 
+               if (fcache->nullVect[i] == true) *isNull = true;
+           
+           return((Datum) fmgr_c(fcache->func, fcache->foid, fcache->nargs,
+                                 (FmgrValues *) argv, isNull));
+       }
+}
+
+
+/* ----------------------------------------------------------------
+ *     ExecEvalOper
+ *     ExecEvalFunc
+ *    
+ *     Evaluate the functional result of a list of arguments by calling the
+ *     function manager.  Note that in the case of operator expressions, the
+ *     optimizer had better have already replaced the operator OID with the
+ *     appropriate function OID or we're hosed.
+ *
+ * old comments
+ *     Presumably the function manager will not take null arguments, so we
+ *     check for null arguments before sending the arguments to (fmgr).
+ *    
+ *     Returns the value of the functional expression.
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------------------------------------------------------
+ *     ExecEvalOper
+ * ----------------------------------------------------------------
+ */
+Datum
+ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull)
+{
+    Oper       *op; 
+    List       *argList;
+    FunctionCachePtr fcache;
+    bool       isDone;
+
+    /* ----------------
+     *  an opclause is a list (op args).  (I think)
+     *
+     *  we extract the oid of the function associated with
+     *  the op and then pass the work onto ExecMakeFunctionResult
+     *  which evaluates the arguments and returns the result of
+     *  calling the function on the evaluated arguments.
+     * ----------------
+     */
+    op =       (Oper *) opClause->oper;
+    argList =   opClause->args;
+    
+    /*
+     * get the fcache from the Oper node.
+     * If it is NULL, then initialize it
+     */
+    fcache = op->op_fcache;
+    if (fcache == NULL) {
+        setFcache((Node*)op, op->opid, argList, econtext);
+       fcache = op->op_fcache;
+    }
+    
+    /* -----------
+     *  call ExecMakeFunctionResult() with a dummy isDone that we ignore.
+     *  We don't have operator whose arguments are sets.
+     * -----------
+     */
+    return
+       ExecMakeFunctionResult((Node *)op, argList, econtext, isNull, &isDone);
+}
+
+/* ----------------------------------------------------------------
+ *     ExecEvalFunc
+ * ----------------------------------------------------------------
+ */
+
+Datum
+ExecEvalFunc(Expr *funcClause,
+            ExprContext *econtext,
+            bool *isNull,
+            bool *isDone)
+{
+    Func       *func;
+    List       *argList;
+    FunctionCachePtr fcache;
+    
+    /* ----------------
+     *  an funcclause is a list (func args).  (I think)
+     *
+     *  we extract the oid of the function associated with
+     *  the func node and then pass the work onto ExecMakeFunctionResult
+     *  which evaluates the arguments and returns the result of
+     *  calling the function on the evaluated arguments.
+     *
+     *  this is nearly identical to the ExecEvalOper code.
+     * ----------------
+     */
+    func =     (Func *)funcClause->oper;
+    argList =   funcClause->args;
+    
+    /*
+     * get the fcache from the Func node.
+     * If it is NULL, then initialize it
+     */
+    fcache = func->func_fcache;
+    if (fcache == NULL) {
+       setFcache((Node*)func, func->funcid, argList, econtext);
+       fcache = func->func_fcache;
+    }
+    
+    return
+       ExecMakeFunctionResult((Node*)func, argList, econtext, isNull, isDone);
+}
+
+/* ----------------------------------------------------------------
+ *     ExecEvalNot
+ *     ExecEvalOr
+ *     ExecEvalAnd
+ *    
+ *     Evaluate boolean expressions.  Evaluation of 'or' is
+ *     short-circuited when the first true (or null) value is found.
+ *
+ *     The query planner reformulates clause expressions in the
+ *     qualification to conjunctive normal form.  If we ever get
+ *     an AND to evaluate, we can be sure that it's not a top-level
+ *     clause in the qualification, but appears lower (as a function
+ *     argument, for example), or in the target list.  Not that you
+ *     need to know this, mind you...
+ * ----------------------------------------------------------------
+ */
+Datum
+ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull)
+{
+    Datum expr_value;
+    Node *clause;
+    bool  isDone;
+    
+    clause = lfirst(notclause->args);
+    
+    /* ----------------
+     *  We don't iterate over sets in the quals, so pass in an isDone
+     *  flag, but ignore it.
+     * ----------------
+     */
+    expr_value = ExecEvalExpr(clause, econtext, isNull, &isDone);
+    
+    /* ----------------
+     * if the expression evaluates to null, then we just
+     *  cascade the null back to whoever called us.
+     * ----------------
+     */
+    if (*isNull)
+       return expr_value;
+    
+    /* ----------------
+     *  evaluation of 'not' is simple.. expr is false, then
+     *  return 'true' and vice versa.
+     * ----------------
+     */
+    if (DatumGetInt32(expr_value) == 0)
+       return (Datum) true;
+    
+    return (Datum) false;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecEvalOr
+ * ----------------------------------------------------------------
+ */
+Datum
+ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull)
+{
+    List   *clauses;
+    List   *clause;
+    bool   isDone;
+    bool IsNull;
+    Datum  const_value;
+    
+    IsNull = false; 
+    clauses = orExpr->args;
+    
+    /* ----------------
+     *  we use three valued logic functions here...
+     * we evaluate each of the clauses in turn,
+     *  as soon as one is true we return that
+     *  value.  If none is true and  none of the
+     *  clauses evaluate to NULL we return
+     *  the value of the last clause evaluated (which
+     *  should be false) with *isNull set to false else 
+     *  if none is true and at least one clause evaluated
+     *  to NULL we set *isNull flag to true -  
+     * ----------------
+     */
+    foreach (clause, clauses) {
+       
+       /* ----------------
+        *  We don't iterate over sets in the quals, so pass in an isDone
+        *  flag, but ignore it.
+        * ----------------
+        */
+       const_value = ExecEvalExpr((Node *) lfirst(clause),
+                                  econtext,
+                                  isNull,
+                                  &isDone);
+       
+       /* ----------------
+        *  if the expression evaluates to null, then we 
+        *  remember it in the local IsNull flag, if none of the
+        *  clauses are true then we need to set *isNull
+        *  to true again.
+        * ----------------
+        */
+       if (*isNull)
+           IsNull = *isNull;
+       
+       /* ----------------
+        *   if we have a true result, then we return it.
+        * ----------------
+        */
+       if (DatumGetInt32(const_value) != 0)
+           return const_value;
+    }
+    
+    /* IsNull is true if at least one clause evaluated to NULL */      
+    *isNull = IsNull;
+    return const_value;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecEvalAnd
+ * ----------------------------------------------------------------
+ */
+Datum
+ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
+{
+    List   *clauses;
+    List   *clause;
+    Datum  const_value;
+    bool   isDone;
+    bool IsNull;
+    
+    IsNull = false;
+    
+    clauses = andExpr->args;
+    
+    /* ----------------
+     * we evaluate each of the clauses in turn,
+     *  as soon as one is false we return that
+     *  value.  If none are false or NULL then we return
+     *  the value of the last clause evaluated, which
+     *  should be true. 
+     * ----------------
+     */
+    foreach (clause, clauses) {
+       
+       /* ----------------
+        *  We don't iterate over sets in the quals, so pass in an isDone
+        *  flag, but ignore it.
+        * ----------------
+        */
+       const_value = ExecEvalExpr((Node *) lfirst(clause),
+                                  econtext,
+                                  isNull,
+                                  &isDone);
+       
+       /* ----------------
+        *  if the expression evaluates to null, then we 
+        *  remember it in IsNull, if none of the clauses after
+        *  this evaluates to false we will have to set *isNull
+        *  to true again.
+        * ----------------
+        */
+       if (*isNull)
+           IsNull = *isNull;
+       
+       /* ----------------
+        *   if we have a false result, then we return it, since the
+        *   conjunction must be false.
+        * ----------------
+        */
+       if (DatumGetInt32(const_value) == 0)
+           return const_value;
+    }
+    
+    *isNull = IsNull;
+    return const_value;
+}
+
+/* ----------------------------------------------------------------  
+ *     ExecEvalExpr
+ *    
+ *     Recursively evaluate a targetlist or qualification expression.
+ *
+ *     This routine is an inner loop routine and should be as fast
+ *      as possible.
+ *
+ *      Node comparison functions were replaced by macros for speed and to plug
+ *      memory leaks incurred by using the planner's Lispy stuff for
+ *      comparisons.  Order of evaluation of node comparisons IS IMPORTANT;
+ *      the macros do no checks.  Order of evaluation:
+ *      
+ *      o an isnull check, largely to avoid coredumps since greg doubts this
+ *        routine is called with a null ptr anyway in proper operation, but is
+ *        not completely sure...
+ *      o ExactNodeType checks.
+ *      o clause checks or other checks where we look at the lfirst of something.
+ * ----------------------------------------------------------------
+ */
+Datum
+ExecEvalExpr(Node *expression,
+            ExprContext *econtext,
+            bool *isNull,
+            bool *isDone)
+{
+    Datum retDatum;
+    
+    *isNull = false;
+    
+    /*
+     * Some callers don't care about is done and only want 1 result.  They
+     * indicate this by passing NULL
+     */
+    if (isDone)
+       *isDone = true;
+    
+    /* ----------------
+     * here we dispatch the work to the appropriate type
+     *  of function given the type of our expression.
+     * ----------------
+     */
+    if (expression == NULL) {
+       *isNull = true;
+       return (Datum) true;
+    }
+
+    switch(nodeTag(expression)) {
+    case T_Var:
+       retDatum = (Datum) ExecEvalVar((Var *) expression, econtext, isNull);
+       break;
+    case T_Const: {
+       Const *con = (Const *)expression;
+
+       if (con->constisnull)
+           *isNull = true;
+       retDatum = con->constvalue;
+       break;
+    }
+    case T_Param:
+       retDatum = (Datum)ExecEvalParam((Param *)expression, econtext, isNull);
+       break;
+    case T_Iter:
+       retDatum = (Datum)  ExecEvalIter((Iter *) expression,
+                                        econtext,
+                                        isNull,
+                                        isDone);
+       break;
+    case T_Aggreg:
+       retDatum = (Datum) ExecEvalAggreg((Aggreg *)expression,
+                                         econtext,
+                                         isNull);
+       break;
+    case T_ArrayRef:
+       retDatum = (Datum)  ExecEvalArrayRef((ArrayRef *) expression,
+                                            econtext,
+                                            isNull,
+                                            isDone);
+       break;
+    case T_Expr: {
+       Expr *expr = (Expr *)expression;
+       switch (expr->opType) {
+       case OP_EXPR:
+           retDatum = (Datum) ExecEvalOper(expr, econtext, isNull);
+           break;
+       case FUNC_EXPR:
+           retDatum = (Datum) ExecEvalFunc(expr, econtext, isNull, isDone);
+           break;
+       case OR_EXPR:
+           retDatum = (Datum) ExecEvalOr(expr, econtext, isNull);
+           break;
+       case AND_EXPR:
+           retDatum = (Datum)  ExecEvalAnd(expr, econtext, isNull);
+           break;
+       case NOT_EXPR:
+           retDatum = (Datum) ExecEvalNot(expr, econtext, isNull);
+           break;
+       default:
+           elog(WARN, "ExecEvalExpr: unknown expression type");
+           break;
+       }
+       break;
+    }
+    default:
+       elog(WARN, "ExecEvalExpr: unknown expression type");
+       break;
+    }
+    
+    return retDatum;
+}
+
+/* ----------------------------------------------------------------
+ *                  ExecQual / ExecTargetList 
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------------------------------------------------------
+ *     ExecQualClause
+ *
+ *     this is a workhorse for ExecQual.  ExecQual has to deal
+ *     with a list of qualifications, so it passes each qualification
+ *     in the list to this function one at a time.  ExecQualClause
+ *     returns true when the qualification *fails* and false if
+ *     the qualification succeeded (meaning we have to test the
+ *     rest of the qualification)
+ * ----------------------------------------------------------------
+ */
+bool
+ExecQualClause(Node *clause, ExprContext *econtext)
+{
+    Datum   expr_value;
+    bool isNull;
+    bool isDone;
+    
+    /* when there is a null clause, consider the qualification to be true */
+    if (clause == NULL)
+      return true;
+
+    /*
+     * pass isDone, but ignore it.  We don't iterate over multiple
+     * returns in the qualifications.
+     */
+    expr_value = (Datum)
+       ExecEvalExpr(clause, econtext, &isNull, &isDone);
+    
+    /* ----------------
+     * this is interesting behaviour here.  When a clause evaluates
+     *  to null, then we consider this as passing the qualification.
+     *  it seems kind of like, if the qual is NULL, then there's no
+     *  qual.. 
+     * ----------------
+     */
+    if (isNull)
+       return true;
+    
+    /* ----------------
+     *  remember, we return true when the qualification fails..
+     * ----------------
+     */
+    if (DatumGetInt32(expr_value) == 0)
+       return true;
+    
+    return false;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecQual
+ *    
+ *     Evaluates a conjunctive boolean expression and returns t
+ *     iff none of the subexpressions are false (or null).
+ * ----------------------------------------------------------------
+ */
+bool
+ExecQual(List *qual, ExprContext *econtext)
+{
+    List               *clause;
+    bool               result;
+    
+    /* ----------------
+     * debugging stuff
+     * ----------------
+     */
+    EV_printf("ExecQual: qual is ");
+    EV_nodeDisplay(qual);
+    EV_printf("\n");
+    
+    IncrProcessed();
+    
+    /* ----------------
+     * return true immediately if no qual
+     * ----------------
+     */
+    if (qual == NIL)
+       return true;
+    
+    /* ----------------
+     *  a "qual" is a list of clauses.  To evaluate the
+     *  qual, we evaluate each of the clauses in the list.
+     *
+     *  ExecQualClause returns true when we know the qualification
+     *  *failed* so we just pass each clause in qual to it until
+     *  we know the qual failed or there are no more clauses.
+     * ----------------
+     */
+    result = false;
+    foreach (clause, qual) {
+       result = ExecQualClause((Node *)lfirst(clause), econtext);
+       if (result == true)
+           break;
+    }
+    
+    /* ----------------
+     * if result is true, then it means a clause failed so we
+     *  return false.  if result is false then it means no clause
+     *  failed so we return true.
+     * ----------------
+     */
+    if (result == true)
+       return false;
+    
+    return true;
+}
+
+int
+ExecTargetListLength(List *targetlist)
+{
+    int len;
+    List *tl;
+    TargetEntry *curTle;
+    
+    len = 0;
+    foreach (tl, targetlist) {
+       curTle = lfirst(tl);
+       
+       if (curTle->resdom != NULL)
+           len++;
+       else
+           len += curTle->fjoin->fj_nNodes;
+    }
+    return len;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecTargetList
+ *    
+ *     Evaluates a targetlist with respect to the current
+ *     expression context and return a tuple.
+ * ----------------------------------------------------------------
+ */
+static HeapTuple
+ExecTargetList(List *targetlist,
+              int nodomains, 
+              TupleDesc targettype,
+              Datum *values,
+              ExprContext *econtext,
+              bool *isDone)
+{
+    char               nulls_array[64];
+    bool               fjNullArray[64];
+    bool               *fjIsNull;
+    char               *null_head;
+    List               *tl;
+    TargetEntry                *tle;
+    Node               *expr;
+    Resdom             *resdom;
+    AttrNumber         resind;
+    Datum              constvalue;
+    HeapTuple          newTuple;
+    bool               isNull;
+    
+    /* ----------------
+     * debugging stuff
+     * ----------------
+     */
+    EV_printf("ExecTargetList: tl is ");
+    EV_nodeDisplay(targetlist);
+    EV_printf("\n");
+    
+    /* ----------------
+     * Return a dummy tuple if the targetlist is empty .
+     * the dummy tuple is necessary to differentiate 
+     * between passing and failing the qualification.
+     * ----------------
+     */
+    if (targetlist == NIL) {
+       /* ----------------
+        *      I now think that the only time this makes
+        *      any sence is when we run a delete query.  Then
+        *      we need to return something other than nil
+        *      so we know to delete the tuple associated
+        *      with the saved tupleid.. see what ExecutePlan
+        *      does with the returned tuple.. -cim 9/21/89
+        *
+        *      It could also happen in queries like:
+        *          retrieve (foo.all) where bar.a = 3
+        *
+        *      is this a new phenomenon? it might cause bogus behavior
+        *      if we try to free this tuple later!! I put a hook in
+        *      ExecProject to watch out for this case -mer 24 Aug 1992
+        * ----------------
+        */
+       CXT1_printf("ExecTargetList: context is %d\n", CurrentMemoryContext);
+       *isDone = true;
+       return (HeapTuple) true;
+    }
+    
+    /* ----------------
+     *  allocate an array of char's to hold the "null" information
+     *  only if we have a really large targetlist.  otherwise we use
+     *  the stack.
+     * ----------------
+     */
+    if (nodomains > 64) {
+        null_head = (char *) palloc(nodomains+1);
+       fjIsNull = (bool *) palloc(nodomains+1);
+    } else {
+       null_head = &nulls_array[0];
+       fjIsNull = &fjNullArray[0];
+    }
+    
+    /* ----------------
+     * evaluate all the expressions in the target list
+     * ----------------
+     */
+    EV_printf("ExecTargetList: setting target list values\n");
+    
+    *isDone = true;
+    foreach (tl, targetlist) {
+       /* ----------------
+        *    remember, a target list is a list of lists:
+        *
+        *      ((<resdom | fjoin> expr) (<resdom | fjoin> expr) ...)
+        *
+        *    tl is a pointer to successive cdr's of the targetlist
+        *    tle is a pointer to the target list entry in tl
+        * ----------------
+        */
+       tle = lfirst(tl);
+
+       if (tle->resdom != NULL) {
+           expr       = tle->expr;
+           resdom     = tle->resdom;
+           resind     = resdom->resno - 1;
+           constvalue = (Datum) ExecEvalExpr(expr,
+                                             econtext,
+                                             &isNull,
+                                             isDone);
+           
+           if ((IsA(expr,Iter)) && (*isDone))
+               return (HeapTuple)NULL;
+           
+           values[resind] = constvalue;
+           
+           if (!isNull)
+               null_head[resind] = ' ';
+           else
+               null_head[resind] = 'n';
+       }else {
+           int      curNode;
+           Resdom   *fjRes;
+           List     *fjTlist   = (List *)tle->expr;
+           Fjoin    *fjNode    = tle->fjoin;
+           int      nNodes    = fjNode->fj_nNodes;
+           DatumPtr results   = fjNode->fj_results;
+           
+           ExecEvalFjoin(tle, econtext, fjIsNull, isDone);
+           if (*isDone)
+               return (HeapTuple)NULL;
+           
+           /*
+            * get the result from the inner node
+            */
+           fjRes = (Resdom *)fjNode->fj_innerNode;
+           resind = fjRes->resno - 1;
+           if (fjIsNull[0])
+               null_head[resind] = 'n';
+           else {
+               null_head[resind] = ' ';
+               values[resind] = results[0];
+           }
+           
+           /*
+            * Get results from all of the outer nodes
+            */
+           for (curNode = 1;
+                curNode < nNodes;
+                curNode++, fjTlist = lnext(fjTlist))
+               {
+#if 0 /* what is this?? */
+                   Node *outernode = lfirst(fjTlist);
+                   fjRes = (Resdom *)outernode->iterexpr;
+#endif             
+                   resind = fjRes->resno - 1;
+                   if (fjIsNull[curNode]) {
+                       null_head[resind] = 'n';
+                   }else {
+                       null_head[resind] = ' ';
+                       values[resind] = results[curNode];
+                   }
+               }
+       }
+    }
+    
+    /* ----------------
+     *         form the new result tuple (in the "normal" context)
+     * ----------------
+     */
+    newTuple = (HeapTuple)
+       heap_formtuple(targettype, values, null_head);
+    
+    /* ----------------
+     * free the nulls array if we allocated one..
+     * ----------------
+     */
+    if (nodomains > 64) pfree(null_head);
+    
+    return
+       newTuple;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecProject
+ *    
+ *     projects a tuple based in projection info and stores
+ *     it in the specified tuple table slot.
+ *
+ *     Note: someday soon the executor can be extended to eliminate
+ *           redundant projections by storing pointers to datums
+ *           in the tuple table and then passing these around when
+ *           possible.  this should make things much quicker.
+ *           -cim 6/3/91
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, bool *isDone)
+{
+    TupleTableSlot     *slot;
+    List               *targetlist;
+    int                len;
+    TupleDesc          tupType;
+    Datum              *tupValue;
+    ExprContext                *econtext;
+    HeapTuple          newTuple;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    if (projInfo == NULL)
+       return (TupleTableSlot *) NULL;
+
+    /* ----------------
+     *  get the projection info we want
+     * ----------------
+     */
+    slot =             projInfo->pi_slot;
+    targetlist =       projInfo->pi_targetlist;
+    len =              projInfo->pi_len;
+    tupType =          slot->ttc_tupleDescriptor;
+    
+    tupValue =                 projInfo->pi_tupValue;
+    econtext =                 projInfo->pi_exprContext;
+    
+    if (targetlist == NIL) {
+      *isDone = true;
+      return (TupleTableSlot *) NULL;
+    }
+
+    /* ----------------
+     *  form a new (result) tuple
+     * ----------------
+     */
+    newTuple = ExecTargetList(targetlist,
+                             len,
+                             tupType,
+                             tupValue,
+                             econtext,
+                             isDone);
+    
+    /* ----------------
+     * store the tuple in the projection slot and return the slot.
+     *
+     *  If there's no projection target list we don't want to pfree
+     *  the bogus tuple that ExecTargetList passes back to us.
+     *       -mer 24 Aug 1992
+     * ----------------
+     */
+    return (TupleTableSlot *)
+       ExecStoreTuple(newTuple, /* tuple to store */
+                      slot,     /* slot to store in */
+                      InvalidBuffer,      /* tuple has no buffer */
+                      true);
+}
+
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
new file mode 100644 (file)
index 0000000..18318a1
--- /dev/null
@@ -0,0 +1,136 @@
+/*-------------------------------------------------------------------------
+ *
+ * execScan.c--
+ *    This code provides support for generalized relation scans. ExecScan
+ *    is passed a node and a pointer to a function to "do the right thing"
+ *    and return a tuple from the relation. ExecScan then does the tedious
+ *    stuff - checking the qualification and projecting the tuple
+ *    appropriately.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <sys/file.h>
+#include "executor/executor.h"
+
+/* ----------------------------------------------------------------
+ *     ExecScan
+ *   
+ *     Scans the relation using the 'access method' indicated and 
+ *     returns the next qualifying tuple in the direction specified
+ *     in the global variable ExecDirection.
+ *     The access method returns the next tuple and execScan() is 
+ *     responisble for checking the tuple returned against the qual-clause.
+ *     
+ *     Conditions:
+ *       -- the "cursor" maintained by the AMI is positioned at the tuple
+ *          returned previously.
+ *   
+ *     Initial States:
+ *       -- the relation indicated is opened for scanning so that the 
+ *          "cursor" is positioned before the first qualifying tuple.
+ *
+ *     May need to put startmmgr  and endmmgr in here.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecScan(Scan *node, 
+        TupleTableSlot* (*accessMtd)())        /* function returning a tuple */
+{
+    CommonScanState    *scanstate;
+    EState             *estate;
+    List               *qual;
+    bool               isDone;
+
+    TupleTableSlot     *slot;
+    TupleTableSlot     *resultSlot;
+    HeapTuple          newTuple;
+
+    ExprContext                *econtext;
+    ProjectionInfo     *projInfo;
+
+
+    /* ----------------
+     * initialize misc variables
+     * ----------------
+     */
+    newTuple =         NULL;
+    slot =     NULL;
+
+    estate =   node->plan.state;
+    scanstate =        node->scanstate;
+
+    /* ----------------
+     * get the expression context
+     * ----------------
+     */
+    econtext =         scanstate->cstate.cs_ExprContext;
+
+    /* ----------------
+     * initialize fields in ExprContext which don't change
+     * in the course of the scan..
+     * ----------------
+     */
+    qual = node->plan.qual;
+    econtext->ecxt_relation = scanstate->css_currentRelation;
+    econtext->ecxt_relid = node->scanrelid;
+
+    if (scanstate->cstate.cs_TupFromTlist) {
+       projInfo = scanstate->cstate.cs_ProjInfo;
+       resultSlot = ExecProject(projInfo, &isDone);
+       if (!isDone)
+         return resultSlot;
+    }
+    /* 
+     * get a tuple from the access method 
+     * loop until we obtain a tuple which passes the qualification.
+     */
+    for(;;) {
+       slot = (TupleTableSlot *) (*accessMtd)(node);
+
+       /* ----------------
+        *  if the slot returned by the accessMtd contains
+        *  NULL, then it means there is nothing more to scan
+        *  so we just return the empty slot.
+        * ----------------
+        */
+       if (TupIsNull(slot)) return slot;
+       
+       /* ----------------
+        *   place the current tuple into the expr context
+        * ----------------
+        */
+       econtext->ecxt_scantuple = slot;
+       
+       /* ----------------
+        *  check that the current tuple satisfies the qual-clause
+        *  if our qualification succeeds then we
+        *  leave the loop.
+        * ----------------
+        */
+
+       /* add a check for non-nil qual here to avoid a
+          function call to ExecQual() when the qual is nil */
+       if (!qual || ExecQual(qual, econtext) == true)
+           break;
+    }
+
+    /* ----------------
+     * form a projection tuple, store it in the result tuple
+     *  slot and return it.
+     * ----------------
+     */
+    projInfo = scanstate->cstate.cs_ProjInfo;
+
+    resultSlot = ExecProject(projInfo, &isDone);
+    scanstate->cstate.cs_TupFromTlist = !isDone;
+
+    return resultSlot;
+}
+
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
new file mode 100644 (file)
index 0000000..a4678e9
--- /dev/null
@@ -0,0 +1,1013 @@
+/*-------------------------------------------------------------------------
+ *
+ * execTuples.c--
+ *    Routines dealing with the executor tuple tables.  These are used to
+ *    ensure that the executor frees copies of tuples (made by
+ *    ExecTargetList) properly.
+ *
+ *    Routines dealing with the type information for tuples. Currently,
+ *    the type information for a tuple is an array of FormData_pg_attribute.
+ *    This information is needed by routines manipulating tuples
+ *    (getattribute, formtuple, etc.).
+ *     
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *
+ *   TABLE CREATE/DELETE
+ *     ExecCreateTupleTable    - create a new tuple table
+ *     ExecDestroyTupleTable   - destroy a table
+ *
+ *   SLOT RESERVERATION
+ *     ExecAllocTableSlot      - find an available slot in the table
+ *
+ *   SLOT ACCESSORS
+ *     ExecStoreTuple          - store a tuple in the table
+ *     ExecFetchTuple          - fetch a tuple from the table
+ *     ExecClearTuple          - clear contents of a table slot
+ *     ExecSlotPolicy          - return slot's tuple pfree policy
+ *     ExecSetSlotPolicy       - diddle the slot policy
+ *     ExecSlotDescriptor      - type of tuple in a slot
+ *     ExecSetSlotDescriptor   - set a slot's tuple descriptor
+ *     ExecSetSlotDescriptorIsNew - diddle the slot-desc-is-new flag
+ *     ExecSetNewSlotDescriptor - set a desc and the is-new-flag all at once
+ *     ExecSlotBuffer          - return buffer of tuple in slot
+ *     ExecSetSlotBuffer       - set the buffer for tuple in slot
+ *     ExecIncrSlotBufferRefcnt - bump the refcnt of the slot buffer
+ *
+ *   SLOT STATUS PREDICATES
+ *     TupIsNull               - true when slot contains no tuple
+ *     ExecSlotDescriptorIsNew - true if we're now storing a different
+ *                               type of tuple in a slot
+ *
+ *   CONVENIENCE INITIALIZATION ROUTINES
+ *     ExecInitResultTupleSlot    \    convience routines to initialize
+ *     ExecInitScanTupleSlot       \   the various tuple slots for nodes
+ *     ExecInitMarkedTupleSlot     /  which store copies of tuples.    
+ *     ExecInitOuterTupleSlot     /    
+ *     ExecInitHashTupleSlot     /     
+ *
+ *   old routines:
+ *     ExecGetTupType          - get type of tuple returned by this node
+ *     ExecTypeFromTL          - form a TupleDesc from a target list
+ *
+ *   EXAMPLE OF HOW TABLE ROUTINES WORK
+ *     Suppose we have a query such as retrieve (EMP.name) and we have
+ *     a single SeqScan node in the query plan.
+ *
+ *     At ExecStart()
+ *     ----------------
+ *     - InitPlan() calls ExecCreateTupleTable() to create the tuple
+ *       table which will hold tuples processed by the executor.
+ *
+ *     - ExecInitSeqScan() calls ExecInitScanTupleSlot() and
+ *       ExecInitResultTupleSlot() to reserve places in the tuple
+ *       table for the tuples returned by the access methods and the
+ *       tuples resulting from preforming target list projections.
+ *
+ *     During ExecRun()
+ *     ----------------
+ *     - SeqNext() calls ExecStoreTuple() to place the tuple returned
+ *       by the access methods into the scan tuple slot.
+ *
+ *     - ExecSeqScan() calls ExecStoreTuple() to take the result
+ *       tuple from ExecTargetList() and place it into the result tuple
+ *       slot.
+ *
+ *     - ExecutePlan() calls ExecRetrieve() which gets the tuple out of
+ *       the slot passed to it by calling ExecFetchTuple().  this tuple
+ *       is then returned.
+ *
+ *     At ExecEnd()
+ *     ----------------
+ *     - EndPlan() calls ExecDestroyTupleTable() to clean up any remaining
+ *       tuples left over from executing the query.
+ *
+ *     The important thing to watch in the executor code is how pointers
+ *     to the slots containing tuples are passed instead of the tuples
+ *     themselves.  This facilitates the communication of related information
+ *     (such as whether or not a tuple should be pfreed, what buffer contains
+ *     this tuple, the tuple's tuple descriptor, etc).   Note that much of
+ *     this information is also kept in the ExprContext of each node.
+ *     Soon the executor will be redesigned and ExprContext's will contain
+ *     only slot pointers.  -cim 3/14/91
+ *
+ *   NOTES
+ *     The tuple table stuff is relatively new, put here to alleviate
+ *     the process growth problems in the executor.  The other routines
+ *     are old (from the original lisp system) and may someday become
+ *     obsolete.  -cim 6/23/90
+ *
+ *     In the implementation of nested-dot queries such as
+ *     "retrieve (EMP.hobbies.all)", a single scan may return tuples
+ *     of many types, so now we return pointers to tuple descriptors
+ *     along with tuples returned via the tuple table.  This means
+ *     we now have a bunch of routines to diddle the slot descriptors
+ *     too.  -cim 1/18/90
+ *
+ *     The tuple table stuff depends on the executor/tuptable.h macros,
+ *     and the TupleTableSlot node in execnodes.h.
+ *
+ */
+
+#include "executor/executor.h"
+#undef ExecStoreTuple
+
+#include "access/tupdesc.h"
+#include "utils/palloc.h"
+#include "utils/lsyscache.h"
+#include "storage/bufmgr.h"
+#include "parser/catalog_utils.h"
+
+/* ----------------------------------------------------------------
+ *               tuple table create/delete functions
+ * ----------------------------------------------------------------
+ */
+/* --------------------------------
+ *     ExecCreateTupleTable
+ *
+ *     This creates a new tuple table of the specified initial
+ *     size.  If the size is insufficient, ExecAllocTableSlot()
+ *     will grow the table as necessary.
+ *
+ *     This should be used by InitPlan() to allocate the table.
+ *     The table's address will be stored in the EState structure.
+ * --------------------------------
+ */
+TupleTable                     /* return: address of table */
+ExecCreateTupleTable(int initialSize) /* initial number of slots in table */
+{
+    TupleTable newtable;       /* newly allocated table */
+    TupleTableSlot* array;     /* newly allocated slot array */
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(initialSize >= 1);
+    
+    /* ----------------
+     * Now allocate our new table along with space for the pointers
+     * to the tuples.
+     */
+
+    newtable = (TupleTable) palloc(sizeof(TupleTableData));
+    array    = (TupleTableSlot*) palloc(initialSize * sizeof(TupleTableSlot));
+    
+    /* ----------------
+     * clean out the slots we just allocated
+     * ----------------
+     */
+    memset(array, 0, initialSize * sizeof(TupleTableSlot));
+    
+    /* ----------------
+     * initialize the new table and return it to the caller.
+     * ----------------
+     */
+    newtable->size =    initialSize;
+    newtable->next =    0;
+    newtable->array =  array;
+    
+    return newtable;
+}
+
+/* --------------------------------
+ *     ExecDestroyTupleTable
+ *
+ *     This pfrees the storage assigned to the tuple table and
+ *     optionally pfrees the contents of the table also.  
+ *     It is expected that this routine be called by EndPlan().
+ * --------------------------------
+ */
+void
+ExecDestroyTupleTable(TupleTable table,        /* tuple table */
+                     bool shouldFree) /* true if we should free slot contents */
+{
+    int                next;           /* next avaliable slot */
+    TupleTableSlot     *array; /* start of table array */
+    int                i;              /* counter */
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(table != NULL);
+    
+    /* ----------------
+     * get information from the table
+     * ----------------
+     */
+    array = table->array;
+    next =  table->next;
+    
+    /* ----------------
+     * first free all the valid pointers in the tuple array
+     *  if that's what the caller wants..
+     *
+     * Note: we do nothing about the Buffer and Tuple Descriptor's
+     *  we store in the slots.  This may have to change (ex: we should
+     *  probably worry about pfreeing tuple descs too) -cim 3/14/91
+     * ----------------
+     */
+    if (shouldFree)
+       for (i = 0; i < next; i++) {
+           TupleTableSlot slot;
+           HeapTuple   tuple;
+           
+           slot = array[i];
+           tuple = slot.val;
+
+           if (tuple != NULL) {
+              slot.val = (HeapTuple)NULL;
+               if (slot.ttc_shouldFree) {
+                   /* ----------------
+                    *  since a tuple may contain a pointer to
+                    *  lock information allocated along with the
+                    *  tuple, we have to be careful to free any
+                    *  rule locks also -cim 1/17/90
+                    * ----------------
+                    */
+                   pfree(tuple);
+               }
+           }
+       }
+    
+    /* ----------------
+     * finally free the tuple array and the table itself.
+     * ----------------
+     */
+    pfree(array);
+    pfree(table);
+    
+}
+
+
+/* ----------------------------------------------------------------
+ *               tuple table slot reservation functions
+ * ----------------------------------------------------------------
+ */
+/* --------------------------------
+ *     ExecAllocTableSlot
+ *
+ *     This routine is used to reserve slots in the table for
+ *     use by the various plan nodes.  It is expected to be
+ *     called by the node init routines (ex: ExecInitNestLoop).
+ *     once per slot needed by the node.  Not all nodes need
+ *     slots (some just pass tuples around).
+ * --------------------------------
+ */
+TupleTableSlot*                /* return: the slot allocated in the tuple table */
+ExecAllocTableSlot(TupleTable table)
+{
+    int        slotnum;                /* new slot number */
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(table != NULL);
+    
+    /* ----------------
+     * if our table is full we have to allocate a larger
+     * size table.  Since ExecAllocTableSlot() is only called
+     *  before the table is ever used to store tuples, we don't
+     *  have to worry about the contents of the old table.
+     *  If this changes, then we will have to preserve the contents.
+     *  -cim 6/23/90
+     *
+     *  Unfortunately, we *cannot* do this.  All of the nodes in
+     *  the plan that have already initialized their slots will have
+     *  pointers into _freed_ memory.  This leads to bad ends.  We
+     *  now count the number of slots we will need and create all the
+     *  slots we will need ahead of time.  The if below should never
+     *  happen now.  Give a WARN if it does.  -mer 4 Aug 1992
+     * ----------------
+     */
+    if (table->next >= table->size) {
+       /*
+        * int newsize = NewTableSize(table->size);
+        *
+        * pfree(table->array);
+        * table->array = (Pointer) palloc(newsize * TableSlotSize);
+        * bzero(table->array, newsize * TableSlotSize);
+        * table->size =  newsize;
+        */
+       elog(NOTICE, "Plan requires more slots than are available");
+       elog(WARN, "send mail to your local executor guru to fix this");
+    }
+    
+    /* ----------------
+     * at this point, space in the table is guaranteed so we
+     *  reserve the next slot, initialize and return it.
+     * ----------------
+     */
+    slotnum = table->next;
+    table->next++;
+    
+    table->array[slotnum].type = T_TupleTableSlot;
+
+    return &(table->array[slotnum]);
+}
+
+/* ----------------------------------------------------------------
+ *               tuple table slot accessor functions
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     ExecStoreTuple
+ *
+ *     This function is used to store a tuple into a specified
+ *     slot in the tuple table.  Note: the only slots which should
+ *     be called with shouldFree == false are those slots used to
+ *     store tuples not allocated with pfree().  Currently the
+ *     seqscan and indexscan nodes use this for the tuples returned
+ *     by amgetattr, which are actually pointers onto disk pages.
+ * --------------------------------
+ */
+TupleTableSlot*                        /* return: slot passed */
+ExecStoreTuple(HeapTuple tuple,        /* tuple to store */
+              TupleTableSlot* slot,    /* slot in which to store tuple */
+              Buffer buffer,   /* buffer associated with tuple */
+              bool shouldFree) /* true if we call pfree() when we gc. */
+{
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(slot != NULL);
+    
+    /* clear out the slot first */
+    ExecClearTuple(slot);
+
+    /* ----------------
+     * store the new tuple into the specified slot and
+     *  return the slot into which we stored the tuple.
+     * ----------------
+     */
+    slot->val = tuple;
+    slot->ttc_buffer = buffer;
+    slot->ttc_shouldFree = shouldFree;
+    
+    return slot;
+}
+
+/* --------------------------------
+ *     ExecClearTuple
+ *
+ *     This function is used to clear out a slot in the tuple table.
+ * --------------------------------
+ */
+TupleTableSlot*                        /* return: slot passed */
+ExecClearTuple(TupleTableSlot* slot)   /* slot in which to store tuple */
+{
+    HeapTuple  oldtuple;       /* prior contents of slot */
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(slot != NULL);
+    
+    /* ----------------
+     * get information from the tuple table
+     * ----------------
+     */
+    oldtuple =         slot->val;
+    
+    /* ----------------
+     * free the old contents of the specified slot if necessary.
+     * ----------------
+     */
+    if (slot->ttc_shouldFree && oldtuple != NULL) {
+       /* ----------------
+        *  since a tuple may contain a pointer to
+        *  lock information allocated along with the
+        *  tuple, we have to be careful to free any
+        *  rule locks also -cim 1/17/90
+        * ----------------
+        */
+       pfree(oldtuple);
+    }
+    
+    /* ----------------
+     * store NULL into the specified slot and return the slot.
+     *  - also set buffer to InvalidBuffer -cim 3/14/91
+     * ----------------
+     */
+    slot->val = (HeapTuple)NULL;
+    
+    if (BufferIsValid(slot->ttc_buffer))
+       ReleaseBuffer(slot->ttc_buffer);
+    
+    slot->ttc_buffer = InvalidBuffer;
+    slot->ttc_shouldFree = true;
+    
+    return slot;
+}
+
+
+/* --------------------------------
+ *     ExecSlotPolicy
+ *
+ *     This function is used to get the call/don't call pfree
+ *     setting of a slot.  Most executor routines don't need this.
+ *     It's only when you do tricky things like marking tuples for
+ *     merge joins that you need to diddle the slot policy.
+ * --------------------------------
+ */
+bool                           /* return: slot policy */
+ExecSlotPolicy(TupleTableSlot* slot)   /* slot to inspect */
+{
+    return slot->ttc_shouldFree;
+}
+
+/* --------------------------------
+ *     ExecSetSlotPolicy
+ *
+ *     This function is used to change the call/don't call pfree
+ *     setting of a slot.  Most executor routines don't need this.
+ *     It's only when you do tricky things like marking tuples for
+ *     merge joins that you need to diddle the slot policy.
+ * --------------------------------
+ */
+bool                           /* return: old slot policy */
+ExecSetSlotPolicy(TupleTableSlot* slot,                /* slot to change */
+                 bool shouldFree)      /* true if we call pfree() when we gc. */
+{
+    bool old_shouldFree =  slot->ttc_shouldFree;
+    slot->ttc_shouldFree = shouldFree;
+
+    return old_shouldFree;
+}
+
+/* --------------------------------
+ *     ExecSlotDescriptor
+ *
+ *     This function is used to get the tuple descriptor associated
+ *     with the slot's tuple.
+ *
+ * Now a macro in tuptable.h  -mer 5 March 1992
+ * --------------------------------
+ */
+
+/* --------------------------------
+ *     ExecSetSlotDescriptor
+ *
+ *     This function is used to set the tuple descriptor associated
+ *     with the slot's tuple.
+ * --------------------------------
+ */
+TupleDesc                      /* return: old slot tuple descriptor */
+ExecSetSlotDescriptor(TupleTableSlot *slot,    /* slot to change */
+                     TupleDesc tupdesc)        /* tuple descriptor */
+{
+    TupleDesc old_tupdesc = slot->ttc_tupleDescriptor;
+
+    slot->ttc_tupleDescriptor = tupdesc;
+    return old_tupdesc;
+}
+
+/* --------------------------------
+ *     ExecSetSlotDescriptorIsNew
+ *
+ *     This function is used to change the setting of the "isNew" flag
+ * --------------------------------
+ */
+void
+ExecSetSlotDescriptorIsNew(TupleTableSlot *slot,/* slot to change */
+                          bool isNew)          /* "isNew" setting */
+{
+    slot->ttc_descIsNew = isNew;
+}
+
+/* --------------------------------
+ *     ExecSetNewSlotDescriptor
+ *
+ *     This function is used to set the tuple descriptor associated
+ *     with the slot's tuple, and set the "isNew" flag at the same time.
+ * --------------------------------
+ */
+TupleDesc                      /* return: old slot tuple descriptor */
+ExecSetNewSlotDescriptor(TupleTableSlot *slot, /* slot to change */
+                        TupleDesc tupdesc) /* tuple descriptor */
+{
+    TupleDesc old_tupdesc = slot->ttc_tupleDescriptor;
+    slot->ttc_tupleDescriptor = tupdesc;
+    slot->ttc_descIsNew = true;
+    
+    return old_tupdesc;
+}
+
+/* --------------------------------
+ *     ExecSlotBuffer
+ *
+ *     This function is used to get the tuple descriptor associated
+ *     with the slot's tuple.  Be very careful with this as it does not
+ *     balance the reference counts.  If the buffer returned is stored
+ *     someplace else, then also use ExecIncrSlotBufferRefcnt().
+ *
+ * Now a macro in tuptable.h
+ * --------------------------------
+ */
+
+/* --------------------------------
+ *     ExecSetSlotBuffer
+ *
+ *     This function is used to set the tuple descriptor associated
+ *     with the slot's tuple.   Be very careful with this as it does not
+ *     balance the reference counts.  If we're using this then we should
+ *     also use ExecIncrSlotBufferRefcnt().
+ * --------------------------------
+ */
+Buffer                         /* return: old slot buffer */
+ExecSetSlotBuffer(TupleTableSlot *slot,        /* slot to change */
+                 Buffer  b)            /* tuple descriptor */
+{
+    Buffer oldb = slot->ttc_buffer;
+    slot->ttc_buffer = b;
+    
+    return oldb;
+}
+
+/* --------------------------------
+ *     ExecIncrSlotBufferRefcnt
+ *
+ *     When we pass around buffers in the tuple table, we have to
+ *     be careful to increment reference counts appropriately.
+ *     This is used mainly in the mergejoin code.
+ * --------------------------------
+ */
+void
+ExecIncrSlotBufferRefcnt(TupleTableSlot *slot) /* slot to bump refcnt */
+{
+/*    Buffer b = SlotBuffer((TupleTableSlot*) slot); */
+    Buffer b = slot->ttc_buffer;
+    if (BufferIsValid(b))
+       IncrBufferRefCount(b);
+}
+
+/* ----------------------------------------------------------------
+ *               tuple table slot status predicates
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     TupIsNull
+ *
+ *     This is used mainly to detect when there are no more
+ *     tuples to process. 
+ * ----------------
+ */
+bool                           /* return: true if tuple in slot is NULL */
+TupIsNull(TupleTableSlot* slot)                /* slot to check */
+{
+    HeapTuple  tuple;          /* contents of slot (returned) */
+    
+    /* ----------------
+     * if the slot itself is null then we return true
+     * ----------------
+     */
+    if (slot == NULL)
+       return true;
+    
+    /* ----------------
+     * get information from the slot and return true or
+     *  false depending on the contents of the slot.
+     * ----------------
+     */
+    tuple =    slot->val;
+    
+    return
+       (tuple == NULL ? true : false);
+}
+
+/* --------------------------------
+ *     ExecSlotDescriptorIsNew
+ *
+ *     This function is used to check if the tuple descriptor
+ *     associated with this slot has just changed.  ie: we are
+ *     now storing a new type of tuple in this slot
+ * --------------------------------
+ */
+bool                           /* return: descriptor "is new" */
+ExecSlotDescriptorIsNew(TupleTableSlot *slot)  /* slot to inspect */
+{
+/*    bool isNew = SlotTupleDescriptorIsNew((TupleTableSlot*) slot); 
+    return isNew; */
+  return slot->ttc_descIsNew;
+}
+
+/* ----------------------------------------------------------------
+ *             convenience initialization routines 
+ * ----------------------------------------------------------------
+ */
+/* --------------------------------
+ *     ExecInit{Result,Scan,Raw,Marked,Outer,Hash}TupleSlot
+ *
+ *     These are convenience routines to initialize the specfied slot
+ *     in nodes inheriting the appropriate state.
+ * --------------------------------
+ */
+#define INIT_SLOT_DEFS \
+    TupleTable     tupleTable; \
+    TupleTableSlot*   slot
+    
+#define INIT_SLOT_ALLOC \
+    tupleTable = (TupleTable) estate->es_tupleTable; \
+    slot =       ExecAllocTableSlot(tupleTable); \
+    slot->val = (HeapTuple)NULL; \
+    slot->ttc_shouldFree = true; \
+    slot->ttc_tupleDescriptor = (TupleDesc)NULL; \
+    slot->ttc_whichplan = -1;\
+    slot->ttc_descIsNew = true;
+
+/* ----------------
+ *     ExecInitResultTupleSlot
+ * ----------------
+ */
+void
+ExecInitResultTupleSlot(EState *estate, CommonState *commonstate)
+{
+    INIT_SLOT_DEFS;
+    INIT_SLOT_ALLOC;
+    commonstate->cs_ResultTupleSlot = (TupleTableSlot *) slot;
+}
+
+/* ----------------
+ *     ExecInitScanTupleSlot
+ * ----------------
+ */
+void
+ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate)
+{
+    INIT_SLOT_DEFS;
+    INIT_SLOT_ALLOC;
+    commonscanstate->css_ScanTupleSlot = (TupleTableSlot *)slot;
+}
+
+/* ----------------
+ *     ExecInitMarkedTupleSlot
+ * ----------------
+ */
+void
+ExecInitMarkedTupleSlot(EState *estate, MergeJoinState *mergestate)
+{
+    INIT_SLOT_DEFS;
+    INIT_SLOT_ALLOC;
+    mergestate->mj_MarkedTupleSlot = (TupleTableSlot *) slot;
+}
+
+/* ----------------
+ *     ExecInitOuterTupleSlot
+ * ----------------
+ */
+void
+ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate)
+{
+    INIT_SLOT_DEFS;
+    INIT_SLOT_ALLOC;
+    hashstate->hj_OuterTupleSlot =  slot;
+}
+
+/* ----------------
+ *     ExecInitHashTupleSlot
+ * ----------------
+ */
+void
+ExecInitHashTupleSlot(EState *estate, HashJoinState *hashstate)
+{
+    INIT_SLOT_DEFS;
+    INIT_SLOT_ALLOC;
+    hashstate->hj_HashTupleSlot = slot;
+}
+
+TupleTableSlot *
+NodeGetResultTupleSlot(Plan *node)
+{
+    TupleTableSlot *slot;
+    
+    switch(nodeTag(node)) {
+       
+    case T_Result:
+       {
+           ResultState *resstate = ((Result *)node)->resstate;
+           slot = resstate->cstate.cs_ResultTupleSlot;
+       }
+       break;
+       
+    case T_SeqScan:
+       {
+           CommonScanState *scanstate = ((SeqScan *)node)->scanstate;
+           slot = scanstate->cstate.cs_ResultTupleSlot;
+       }
+       break;
+       
+    case T_NestLoop:
+       {
+           NestLoopState       *nlstate = ((NestLoop *)node)->nlstate;
+           slot = nlstate->jstate.cs_ResultTupleSlot;
+       }
+       break;
+       
+    case T_Append:
+       {
+           Append              *n = (Append *)node;
+           AppendState         *unionstate;
+           List                *unionplans;
+           int                 whichplan;
+           Plan                *subplan;
+           
+           unionstate =        n->unionstate;
+           unionplans =        n->unionplans;
+           whichplan =         unionstate->as_whichplan;
+           
+           subplan = (Plan*) nth(whichplan, unionplans);
+           slot = NodeGetResultTupleSlot(subplan);
+           break;
+       }
+       
+    case T_IndexScan:
+       {
+           CommonScanState *scanstate = ((IndexScan *)node)->scan.scanstate;
+           slot = scanstate->cstate.cs_ResultTupleSlot;
+       }
+       break;
+       
+    case T_Material:
+       {
+           MaterialState *matstate = ((Material *)node)->matstate;
+           slot = matstate->csstate.css_ScanTupleSlot;
+       }
+       break;
+       
+    case T_Sort:
+       {
+           SortState *sortstate = ((Sort *)node)->sortstate;
+           slot = sortstate->csstate.css_ScanTupleSlot; 
+       }
+       break;
+        
+    case T_Agg:
+       {
+           AggState *aggstate = ((Agg *)node)->aggstate;
+           slot = aggstate->csstate.cstate.cs_ResultTupleSlot;
+       }
+       break;
+       
+    case T_Group:
+       {
+           GroupState *grpstate = ((Group *)node)->grpstate;
+           slot = grpstate->csstate.cstate.cs_ResultTupleSlot;
+       }
+       break;
+       
+    case T_Hash:
+       {
+           HashState *hashstate = ((Hash *)node)->hashstate;
+           slot = hashstate->cstate.cs_ResultTupleSlot;
+       }
+       break;
+        
+    case T_Unique:
+       {
+           UniqueState *uniquestate = ((Unique *)node)->uniquestate;
+           slot = uniquestate->cs_ResultTupleSlot;
+       }
+       break;
+       
+    case T_MergeJoin:
+       {
+           MergeJoinState *mergestate = ((MergeJoin *)node)->mergestate;
+           slot = mergestate->jstate.cs_ResultTupleSlot;
+       }
+       break;
+       
+    case T_HashJoin:
+       {
+           HashJoinState *hashjoinstate = ((HashJoin *)node)->hashjoinstate;
+           slot = hashjoinstate->jstate.cs_ResultTupleSlot;
+       }
+       break;
+       
+   case T_Tee:
+        {
+           TeeState *teestate = ((Tee*)node)->teestate;
+           slot = teestate->cstate.cs_ResultTupleSlot;
+        }
+       break;
+
+    default:
+       /* ----------------
+        *    should never get here
+        * ----------------
+        */
+       elog(WARN, "NodeGetResultTupleSlot: node not yet supported: %d ",
+            nodeTag(node));
+       
+       return NULL;
+    }
+    return slot;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecGetTupType
+ *
+ *     this gives you the tuple descriptor for tuples returned
+ *     by this node.  I really wish I could ditch this routine,
+ *     but since not all nodes store their type info in the same
+ *     place, we have to do something special for each node type.
+ *
+ *     Soon, the system will have to adapt to deal with changing
+ *     tuple descriptors as we deal with dynamic tuple types
+ *     being returned from procedure nodes.  Perhaps then this
+ *     routine can be retired.  -cim 6/3/91
+ *
+ * old comments
+ *     This routine just gets the type information out of the
+ *     node's state.  If you already have a node's state, you
+ *     can get this information directly, but this is a useful
+ *     routine if you want to get the type information from
+ *     the node's inner or outer subplan easily without having
+ *     to inspect the subplan.. -cim 10/16/89
+ *
+ *     Assume that for existential nodes, we get the targetlist out
+ *     of the right node's targetlist
+ * ----------------------------------------------------------------
+ */
+
+TupleDesc
+ExecGetTupType(Plan *node) 
+{
+    TupleTableSlot    *slot;
+    TupleDesc   tupType;
+    
+    if (node == NULL)
+       return NULL;
+    
+    slot = NodeGetResultTupleSlot(node);
+    tupType = slot->ttc_tupleDescriptor;
+    return tupType;
+}
+
+/*
+TupleDesc
+ExecCopyTupType(TupleDesc td, int natts) 
+{
+    TupleDesc newTd;
+    int             i;
+    
+    newTd = CreateTemplateTupleDesc(natts);
+    i = 0;
+    while (i < natts)
+       {
+           newTd[i] =
+               (AttributeTupleForm)palloc(sizeof(FormData_pg_attribute));
+           memmove(newTd[i], td[i], sizeof(FormData_pg_attribute));
+           i++;
+       }
+    return newTd;
+}
+*/
+
+/* ----------------------------------------------------------------
+ *     ExecTypeFromTL
+ *     
+ *     Currently there are about 4 different places where we create
+ *     TupleDescriptors.  They should all be merged, or perhaps
+ *     be rewritten to call BuildDesc().
+ *     
+ *  old comments
+ *     Forms attribute type info from the target list in the node.
+ *     It assumes all domains are individually specified in the target list.
+ *     It fails if the target list contains something like Emp.all
+ *     which represents all the attributes from EMP relation.
+ *   
+ *     Conditions:
+ *         The inner and outer subtrees should be initialized because it
+ *         might be necessary to know the type infos of the subtrees.
+ * ----------------------------------------------------------------
+ */
+TupleDesc
+ExecTypeFromTL(List *targetList)
+{
+    List       *tlcdr;
+    TupleDesc  typeInfo;
+    Resdom     *resdom;
+    Oid        restype;
+    int        len;
+    
+    /* ----------------
+     *  examine targetlist - if empty then return NULL
+     * ----------------
+     */
+    len = ExecTargetListLength(targetList);
+    
+    if (len == 0)
+       return NULL;
+    
+    /* ----------------
+     *  allocate a new typeInfo
+     * ----------------
+     */
+    typeInfo = CreateTemplateTupleDesc(len);
+    
+    /* ----------------
+     * notes: get resdom from (resdom expr)
+     *        get_typbyval comes from src/lib/l-lisp/lsyscache.c
+     * ----------------
+     */
+    tlcdr = targetList;
+    while (tlcdr != NIL) {
+       TargetEntry *tle = lfirst(tlcdr);
+       if (tle->resdom != NULL) {
+           resdom =  tle->resdom;
+           restype = resdom->restype;
+           
+           TupleDescInitEntry(typeInfo,
+                              resdom->resno,
+                              resdom->resname,
+                              get_id_typname(restype),
+                              0,
+                              false);
+
+/*
+           ExecSetTypeInfo(resdom->resno - 1,
+                           typeInfo,
+                           (Oid) restype,
+                           resdom->resno,
+                           resdom->reslen,
+                           resdom->resname->data,
+                           get_typbyval(restype),
+                           get_typalign(restype));
+*/
+       }
+       else {
+           Resdom *fjRes;
+           List  *fjTlistP;
+           List  *fjList = lfirst(tlcdr);
+#ifdef SETS_FIXED
+           TargetEntry *tle; 
+           Fjoin *fjNode = ((TargetEntry *)lfirst(fjList))->fjoin;
+
+           tle = fjNode->fj_innerNode; /* ??? */
+#endif
+           fjRes = tle->resdom;
+           restype = fjRes->restype;
+           
+           TupleDescInitEntry(typeInfo,
+                              fjRes->resno,
+                              fjRes->resname,
+                              get_id_typname(restype),
+                              0,
+                              false);
+/*
+           ExecSetTypeInfo(fjRes->resno - 1,
+                           typeInfo,    
+                           (Oid) restype,
+                           fjRes->resno,
+                           fjRes->reslen,
+                           (char *) fjRes->resname,
+                           get_typbyval(restype),
+                           get_typalign(restype));
+*/
+           
+           foreach(fjTlistP, lnext(fjList)) {
+               TargetEntry *fjTle = lfirst(fjTlistP);
+               
+               fjRes = fjTle->resdom;
+
+               TupleDescInitEntry(typeInfo,
+                                  fjRes->resno,
+                                  fjRes->resname,
+                                  get_id_typname(restype),
+                                  0,
+                                  false);
+               
+/*
+               ExecSetTypeInfo(fjRes->resno - 1,
+                               typeInfo, 
+                               (Oid) fjRes->restype,
+                               fjRes->resno,
+                               fjRes->reslen,
+                               (char *) fjRes->resname,
+                               get_typbyval(fjRes->restype),
+                               get_typalign(fjRes->restype));
+*/
+           }
+       }
+       
+       tlcdr = lnext(tlcdr);
+    }
+    
+    return typeInfo;
+}
+
+
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
new file mode 100644 (file)
index 0000000..9ddce56
--- /dev/null
@@ -0,0 +1,1092 @@
+/*-------------------------------------------------------------------------
+ *
+ * execUtils.c--
+ *    miscellanious executor utility routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *     ExecAssignNodeBaseInfo  \
+ *     ExecAssignDebugHooks     >  preforms misc work done in all the
+ *     ExecAssignExprContext   /   init node routines.
+ *
+ *     ExecGetTypeInfo           |  old execCStructs interface
+ *     ExecMakeTypeInfo          |  code from the version 1
+ *     ExecOrderTypeInfo         |  lisp system.  These should
+ *     ExecSetTypeInfo           |  go away or be updated soon.
+ *     ExecFreeTypeInfo          |  -cim 11/1/89
+ *     ExecTupleAttributes     /
+ *
+
+ *     QueryDescGetTypeInfo - moved here from main.c
+ *                             am not sure what uses it -cim 10/12/89
+ *
+ *     ExecGetIndexKeyInfo     \
+ *     ExecOpenIndices          | referenced by InitPlan, EndPlan,
+ *     ExecCloseIndices         | ExecAppend, ExecReplace
+ *     ExecFormIndexTuple       |
+ *     ExecInsertIndexTuple    /
+ *
+ *   NOTES
+ *     This file has traditionally been the place to stick misc.
+ *     executor support stuff that doesn't really go anyplace else.
+ *     
+ */
+
+#include "executor/executor.h"
+#include "access/itup.h"
+#include "optimizer/clauses.h"
+#include "utils/palloc.h"
+#include "commands/command.h"
+#include "catalog/index.h"
+
+/* ----------------------------------------------------------------
+ *      global counters for number of tuples processed, retrieved,
+ *      appended, replaced, deleted.
+ * ----------------------------------------------------------------
+ */
+int     NTupleProcessed;
+int     NTupleRetrieved;
+int     NTupleReplaced;
+int     NTupleAppended;
+int     NTupleDeleted;
+int    NIndexTupleInserted;
+extern int NIndexTupleProcessed;  /* have to be defined in the access
+                                    method level so that the cinterface.a
+                                    will link ok. */
+
+/* ----------------------------------------------------------------
+ *                     statistic functions
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------------------------------------------------------
+ *     ResetTupleCount
+ * ----------------------------------------------------------------
+ */
+void
+ResetTupleCount()
+{
+    NTupleProcessed = 0;
+    NTupleRetrieved = 0;
+    NTupleAppended = 0;
+    NTupleDeleted = 0;
+    NTupleReplaced = 0;
+    NIndexTupleProcessed = 0;
+}
+
+/* ----------------------------------------------------------------
+ *     PrintTupleCount
+ * ----------------------------------------------------------------
+ */
+void
+DisplayTupleCount(FILE *statfp)
+{
+    if (NTupleProcessed > 0)
+       fprintf(statfp, "!\t%d tuple%s processed, ", NTupleProcessed,
+               (NTupleProcessed == 1) ? "" : "s");
+    else {
+       fprintf(statfp, "!\tno tuples processed.\n");
+       return;
+    }
+    if (NIndexTupleProcessed > 0)
+       fprintf(statfp, "%d indextuple%s processed, ", NIndexTupleProcessed,
+               (NIndexTupleProcessed == 1) ? "" : "s");
+    if (NIndexTupleInserted > 0)
+       fprintf(statfp, "%d indextuple%s inserted, ", NIndexTupleInserted,
+               (NIndexTupleInserted == 1) ? "" : "s");
+    if (NTupleRetrieved > 0)
+       fprintf(statfp, "%d tuple%s retrieved. ", NTupleRetrieved,
+               (NTupleRetrieved == 1) ? "" : "s");
+    if (NTupleAppended > 0)
+       fprintf(statfp, "%d tuple%s appended. ", NTupleAppended,
+               (NTupleAppended == 1) ? "" : "s");
+    if (NTupleDeleted > 0)
+       fprintf(statfp, "%d tuple%s deleted. ", NTupleDeleted,
+               (NTupleDeleted == 1) ? "" : "s");
+    if (NTupleReplaced > 0)
+       fprintf(statfp, "%d tuple%s replaced. ", NTupleReplaced,
+               (NTupleReplaced == 1) ? "" : "s");
+    fprintf(statfp, "\n");
+}
+
+/* ----------------------------------------------------------------
+ *              miscellanious init node support functions
+ *
+ *     ExecAssignNodeBaseInfo  - assigns the baseid field of the node
+ *     ExecAssignDebugHooks    - assigns the node's debugging hooks
+ *     ExecAssignExprContext   - assigns the node's expression context
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     ExecAssignNodeBaseInfo
+ *
+ *     as it says, this assigns the baseid field of the node and
+ *     increments the counter in the estate.  In addition, it initializes
+ *     the base_parent field of the basenode.
+ * ----------------
+ */
+void
+ExecAssignNodeBaseInfo(EState *estate, CommonState *cstate, Plan *parent)
+{
+    int baseId;
+    
+    baseId = estate->es_BaseId;
+    cstate->cs_base_id = baseId;
+    estate->es_BaseId = baseId + 1;
+}
+
+/* ----------------
+ *     ExecAssignExprContext
+ *
+ *     This initializes the ExprContext field.  It is only necessary
+ *     to do this for nodes which use ExecQual or ExecTargetList
+ *     because those routines depend on econtext.  Other nodes which
+ *     dont have to evaluate expressions don't need to do this.
+ * ----------------
+ */
+void
+ExecAssignExprContext(EState *estate, CommonState *commonstate)
+{
+    ExprContext    *econtext;
+    ParamListInfo  paraminfo;
+    List           *rangeTable;
+    
+    paraminfo = estate->es_param_list_info;
+    rangeTable = estate->es_range_table;
+
+    econtext = makeNode(ExprContext);
+    econtext->ecxt_scantuple = NULL;           /* scan tuple slot */
+    econtext->ecxt_innertuple = NULL;          /* inner tuple slot */
+    econtext->ecxt_outertuple = NULL;          /* outer tuple slot */
+    econtext->ecxt_relation = NULL;            /* relation */
+    econtext->ecxt_relid = 0;                  /* relid */
+    econtext->ecxt_param_list_info = paraminfo;        /* param list info */
+    econtext->ecxt_range_table = rangeTable;   /* range table */
+    
+    commonstate->cs_ExprContext = econtext;
+}
+
+/* ----------------------------------------------------------------
+ *     Result slot tuple type and ProjectionInfo support
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     ExecAssignResultType
+ * ----------------
+ */
+void
+ExecAssignResultType(CommonState *commonstate,
+                    TupleDesc tupDesc)
+{
+    TupleTableSlot     *slot;
+    
+    slot = commonstate->cs_ResultTupleSlot;
+    slot->ttc_tupleDescriptor = tupDesc;
+}
+
+/* ----------------
+ *     ExecAssignResultTypeFromOuterPlan
+ * ----------------
+ */
+void
+ExecAssignResultTypeFromOuterPlan(Plan *node, CommonState *commonstate)
+{
+    Plan       *outerPlan;
+    TupleDesc  tupDesc;
+    
+    outerPlan =   outerPlan(node);
+    tupDesc = ExecGetTupType(outerPlan);
+    
+    ExecAssignResultType(commonstate, tupDesc);
+}
+
+/* ----------------
+ *     ExecAssignResultTypeFromTL
+ * ----------------
+ */
+void
+ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate)
+{
+    List       *targetList;
+    int                i;
+    int                len;
+    List       *tl;
+    TargetEntry        *tle;
+    List       *fjtl;
+    TupleDesc  origTupDesc;
+    
+    targetList =  node->targetlist;
+    origTupDesc = ExecTypeFromTL(targetList);
+    len = ExecTargetListLength(targetList);
+    
+    fjtl = NIL;
+    tl = targetList;
+    i = 0;
+    while (tl != NIL || fjtl != NIL) {
+       if (fjtl != NIL) {
+           tle = lfirst(fjtl);
+           fjtl = lnext(fjtl);
+       }
+       else {
+           tle = lfirst(tl);
+           tl = lnext(tl);
+       }
+#ifdef SETS_FIXED
+       if (!tl_is_resdom(tle)) {
+           Fjoin *fj = (Fjoin *)lfirst(tle);
+           /* it is a FJoin */
+           fjtl = lnext(tle);
+           tle = fj->fj_innerNode;
+       }
+#endif
+       i++;
+    }
+    if (len > 0) {
+       ExecAssignResultType(commonstate, 
+                            origTupDesc);
+    }
+    else
+       ExecAssignResultType(commonstate,
+                            (TupleDesc)NULL);
+}
+
+/* ----------------
+ *     ExecGetResultType
+ * ----------------
+ */
+TupleDesc
+ExecGetResultType(CommonState *commonstate)
+{
+    TupleTableSlot *slot = commonstate->cs_ResultTupleSlot;
+    
+    return slot->ttc_tupleDescriptor;
+}
+
+/* ----------------
+ *     ExecFreeResultType
+ * ----------------
+ */
+void
+ExecFreeResultType(CommonState *commonstate)
+{
+    TupleTableSlot *slot;
+    TupleDesc tupType;
+    
+    slot =       commonstate->cs_ResultTupleSlot;
+    tupType =    slot->ttc_tupleDescriptor;
+    
+/*    ExecFreeTypeInfo(tupType); */
+    pfree(tupType);
+}
+
+
+/* ----------------
+ *     ExecAssignProjectionInfo
+          forms the projection information from the node's targetlist
+ * ----------------
+ */
+void
+ExecAssignProjectionInfo(Plan *node, CommonState *commonstate)
+{
+    ProjectionInfo     *projInfo;
+    List               *targetList;
+    int                len;
+    
+    targetList =  node->targetlist;
+    len =        ExecTargetListLength(targetList);
+
+    projInfo = makeNode(ProjectionInfo);
+    projInfo->pi_targetlist = targetList;
+    projInfo->pi_len = len;
+    projInfo->pi_tupValue =
+       (len <= 0) ? NULL : (Datum *) palloc(sizeof(Datum) * len);
+    projInfo->pi_exprContext = commonstate->cs_ExprContext;
+    projInfo->pi_slot = commonstate->cs_ResultTupleSlot;
+
+    commonstate->cs_ProjInfo = projInfo;
+}
+
+
+/* ----------------
+ *     ExecFreeProjectionInfo
+ * ----------------
+ */
+void
+ExecFreeProjectionInfo(CommonState *commonstate)
+{
+    ProjectionInfo     *projInfo;
+    
+    /* ----------------
+     * get projection info.  if NULL then this node has
+     *  none so we just return.
+     * ----------------
+     */
+    projInfo = commonstate->cs_ProjInfo;
+    if (projInfo == NULL)
+       return;
+    
+    /* ----------------
+     * clean up memory used.
+     * ----------------
+     */
+    if (projInfo->pi_tupValue != NULL)
+       pfree(projInfo->pi_tupValue);
+    
+    pfree(projInfo);
+    commonstate->cs_ProjInfo = NULL;
+}
+
+/* ----------------------------------------------------------------
+ *     the following scan type support functions are for
+ *     those nodes which are stubborn and return tuples in
+ *     their Scan tuple slot instead of their Result tuple
+ *     slot..  luck fur us, these nodes do not do projections
+ *     so we don't have to worry about getting the ProjectionInfo
+ *     right for them...  -cim 6/3/91
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     ExecGetScanType
+ * ----------------
+ */
+TupleDesc
+ExecGetScanType(CommonScanState *csstate)
+{
+    TupleTableSlot *slot = csstate->css_ScanTupleSlot;
+    return slot->ttc_tupleDescriptor;
+}
+
+/* ----------------
+ *     ExecFreeScanType
+ * ----------------
+ */
+void
+ExecFreeScanType(CommonScanState *csstate)
+{
+    TupleTableSlot *slot;
+    TupleDesc tupType;
+    
+    slot =       csstate->css_ScanTupleSlot;
+    tupType =    slot->ttc_tupleDescriptor;
+    
+/*    ExecFreeTypeInfo(tupType); */
+    pfree(tupType);
+}
+
+/* ----------------
+ *     ExecAssignScanType
+ * ----------------
+ */
+void
+ExecAssignScanType(CommonScanState *csstate,
+                  TupleDesc tupDesc)
+{
+    TupleTableSlot     *slot;
+    
+    slot = (TupleTableSlot *) csstate->css_ScanTupleSlot;
+    slot->ttc_tupleDescriptor = tupDesc;
+}
+
+/* ----------------
+ *     ExecAssignScanTypeFromOuterPlan
+ * ----------------
+ */
+void
+ExecAssignScanTypeFromOuterPlan(Plan *node, CommonScanState *csstate)
+{
+    Plan       *outerPlan;
+    TupleDesc  tupDesc;
+    
+    outerPlan =   outerPlan(node);
+    tupDesc =    ExecGetTupType(outerPlan);
+
+    ExecAssignScanType(csstate, tupDesc);
+}
+
+
+/* ----------------------------------------------------------------
+ *     ExecTypeFromTL support routines.
+ *
+ *     these routines are used mainly from ExecTypeFromTL.
+ *     -cim 6/12/90
+ *
+ * old comments
+ *     Routines dealing with the structure 'attribute' which conatains
+ *     the type information about attributes in a tuple:
+ *
+ *     ExecMakeTypeInfo(noType) --
+ *             returns pointer to array of 'noType' structure 'attribute'.
+ *     ExecSetTypeInfo(index, typeInfo, attNum, attLen) --
+ *             sets the element indexed by 'index' in typeInfo with
+ *             the values: attNum, attLen.
+ *     ExecFreeTypeInfo(typeInfo) --
+ *             frees the structure 'typeInfo'.
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     ExecSetTypeInfo
+ *
+ *     This initializes fields of a single attribute in a
+ *     tuple descriptor from the specified parameters.
+ *
+ *     XXX this duplicates much of the functionality of TupleDescInitEntry.
+ *         the routines should be moved to the same place and be rewritten
+ *         to share common code.
+ * ----------------
+ */
+#if 0 
+void
+ExecSetTypeInfo(int index,
+               TupleDesc typeInfo,
+               Oid typeID,
+               int attNum,
+               int attLen,
+               char *attName,
+               bool attbyVal,
+               char attalign)
+{
+    AttributeTupleForm att;
+    
+    /* ----------------
+     * get attribute pointer and preform a sanity check..
+     * ----------------
+     */
+    att = typeInfo[index];
+    if (att == NULL)
+       elog(WARN, "ExecSetTypeInfo: trying to assign through NULL");
+    
+    /* ----------------
+     * assign values to the tuple descriptor, being careful not
+     *  to copy a null attName..
+     *
+     *  XXX it is unknown exactly what information is needed to
+     *      initialize the attribute struct correctly so for now
+     *     we use 0.  this should be fixed -- otherwise we run the
+     *     risk of using garbage data. -cim 5/5/91
+     * ----------------
+     */
+    att->attrelid  = 0;                                /* dummy value */
+    
+    if (attName != (char *) NULL)
+       strncpy(att->attname.data, attName, NAMEDATALEN);
+    else
+       memset(att->attname.data,0,NAMEDATALEN);
+    
+    att->atttypid =    typeID;
+    att->attdefrel =   0;                      /* dummy value */
+    att->attnvals  =   0;                      /* dummy value */
+    att->atttyparg =   0;                      /* dummy value */
+    att->attlen =      attLen;
+    att->attnum =      attNum;
+    att->attbound =    0;                      /* dummy value */
+    att->attbyval =    attbyVal;
+    att->attcanindex =         0;                      /* dummy value */
+    att->attproc =     0;                      /* dummy value */
+    att->attnelems =   0;                      /* dummy value */
+    att->attcacheoff =         -1;
+    att->attisset =     false;
+    att->attalign =     attalign;
+}
+
+/* ----------------
+ *     ExecFreeTypeInfo frees the array of attrbutes
+ *     created by ExecMakeTypeInfo and returned by ExecTypeFromTL...
+ * ----------------
+ */
+void
+ExecFreeTypeInfo(TupleDesc typeInfo)
+{
+    /* ----------------
+     * do nothing if asked to free a null pointer
+     * ----------------
+     */
+    if (typeInfo == NULL)
+       return;
+    
+    /* ----------------
+     * the entire array of typeinfo pointers created by
+     *  ExecMakeTypeInfo was allocated with a single palloc()
+     *  so we can deallocate the whole array with a single pfree().
+     *  (we should not try and free all the elements in the array)
+     *  -cim 6/12/90
+     * ----------------
+     */
+    pfree(typeInfo);
+}
+
+
+/* ----------------------------------------------------------------
+ *     QueryDescGetTypeInfo
+ *
+ *|    I don't know how this is used, all I know is that it
+ *|    appeared one day in main.c so I moved it here. -cim 11/1/89
+ * ----------------------------------------------------------------
+ */
+TupleDesc
+QueryDescGetTypeInfo(QueryDesc *queryDesc)
+{
+    Plan      *plan;
+    TupleDesc tupleType;
+    List      *targetList;
+    AttrInfo  *attinfo = (AttrInfo *)palloc(sizeof(AttrInfo));
+    
+    plan =     queryDesc->plantree;
+    tupleType = (TupleDesc) ExecGetTupType(plan);
+/*
+    targetList =  plan->targetlist;
+
+    attinfo->numAttr = ExecTargetListLength(targetList);
+    attinfo->attrs = tupleType;
+*/
+    attinfo->numAttr = tupleType->natts;
+    attinfo->attrs = tupleType->attrs;
+    return attinfo;
+}
+#endif
+
+/* ----------------------------------------------------------------
+ *               ExecInsertIndexTuples support
+ * ----------------------------------------------------------------
+ */
+/* ----------------------------------------------------------------
+ *     ExecGetIndexKeyInfo
+ *
+ *     Extracts the index key attribute numbers from
+ *     an index tuple form (i.e. a tuple from the pg_index relation)
+ *     into an array of attribute numbers.  The array and the
+ *     size of the array are returned to the caller via return
+ *     parameters.
+ * ----------------------------------------------------------------
+ */
+void
+ExecGetIndexKeyInfo(IndexTupleForm indexTuple,
+                   int *numAttsOutP,
+                   AttrNumber **attsOutP,
+                   FuncIndexInfoPtr fInfoP)
+{
+    int                i;
+    int        numKeys;
+    AttrNumber         *attKeys;
+
+    /* ----------------
+     * check parameters
+     * ----------------
+     */
+    if (numAttsOutP == NULL && attsOutP == NULL) {
+       elog(DEBUG, "ExecGetIndexKeyInfo: %s",
+            "invalid parameters: numAttsOutP and attsOutP must be non-NULL");
+    }
+
+    /* ----------------
+     * set the procid for a possible functional index.
+     * ----------------
+     */
+    FIsetProcOid(fInfoP, indexTuple->indproc);
+
+    /* ----------------
+     * count the number of keys..
+     * ----------------
+     */
+    numKeys = 0;
+    for (i=0; i<8 && indexTuple->indkey[i] != 0; i++)
+       numKeys++;
+
+    /* ----------------
+     * place number keys in callers return area
+     *  or the number of arguments for a functional index.
+     *
+     *  If we have a functional index then the number of 
+     *  attributes defined in the index must 1 (the function's 
+     *  single return value).
+     * ----------------
+     */
+    if (FIgetProcOid(fInfoP) != InvalidOid) {
+       FIsetnArgs(fInfoP, numKeys);
+       (*numAttsOutP) = 1;
+    }
+    else
+       (*numAttsOutP) = numKeys;
+
+    if (numKeys < 1) {
+       elog(DEBUG, "ExecGetIndexKeyInfo: %s",
+            "all index key attribute numbers are zero!");
+       (*attsOutP) = NULL;
+       return;
+    }
+
+    /* ----------------
+     * allocate and fill in array of key attribute numbers
+     * ----------------
+     */
+    CXT1_printf("ExecGetIndexKeyInfo: context is %d\n", CurrentMemoryContext);
+
+    attKeys = (AttrNumber*)
+       palloc(numKeys * sizeof(AttrNumber));
+
+    for (i=0; i<numKeys; i++)
+       attKeys[i] = indexTuple->indkey[i];
+
+    /* ----------------
+     * return array to caller.
+     * ----------------
+     */
+    (*attsOutP) = attKeys;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecOpenIndices
+ *
+ *     Here we scan the pg_index relation to find indices
+ *     associated with a given heap relation oid.  Since we
+ *     don't know in advance how many indices we have, we
+ *     form lists containing the information we need from
+ *     pg_index and then process these lists.
+ *
+ *     Note: much of this code duplicates effort done by
+ *     the IndexCatalogInformation function in plancat.c
+ *     because IndexCatalogInformation is poorly written.
+ *
+ *     It would be much better the functionality provided
+ *     by this function and IndexCatalogInformation was
+ *     in the form of a small set of orthogonal routines..
+ *     If you are trying to understand this, I suggest you
+ *     look at the code to IndexCatalogInformation and
+ *     FormIndexTuple.. -cim 9/27/89
+ * ----------------------------------------------------------------
+ */
+void
+ExecOpenIndices(Oid resultRelationOid,
+               RelationInfo *resultRelationInfo)
+{
+    Relation           indexRd;
+    HeapScanDesc       indexSd;
+    ScanKeyData                key;
+    HeapTuple          tuple;
+    IndexTupleForm     indexStruct;
+    Oid                indexOid;
+    List               *oidList;
+    List               *nkeyList;
+    List               *keyList;
+    List               *fiList;
+    char               *predString;
+    List               *predList;
+    List               *indexoid;
+    List               *numkeys;
+    List               *indexkeys;
+    List               *indexfuncs;
+    List               *indexpreds;
+    int                        len;
+
+    RelationPtr                relationDescs;
+    IndexInfo          **indexInfoArray;
+    FuncIndexInfoPtr   fInfoP;
+    int                        numKeyAtts;
+    AttrNumber                 *indexKeyAtts;
+    PredInfo           *predicate;
+    int                        i;
+
+    /* ----------------
+     * open pg_index
+     * ----------------
+     */
+    indexRd = heap_openr(IndexRelationName);
+
+    /* ----------------
+     * form a scan key
+     * ----------------
+     */
+    ScanKeyEntryInitialize(&key, 0, Anum_pg_index_indrelid,
+                          ObjectIdEqualRegProcedure,
+                          ObjectIdGetDatum(resultRelationOid));
+
+    /* ----------------
+     * scan the index relation, looking for indices for our
+     *  result relation..
+     * ----------------
+     */
+    indexSd = heap_beginscan(indexRd,          /* scan desc */
+                            false,             /* scan backward flag */
+                            NowTimeQual,       /* time qual */
+                            1,                 /* number scan keys */
+                            &key);             /* scan keys */
+
+    oidList =  NIL;
+    nkeyList = NIL;
+    keyList =  NIL;
+    fiList =   NIL;
+    predList = NIL;
+
+    while(tuple = heap_getnext(indexSd,                /* scan desc */
+                              false,           /* scan backward flag */
+                              NULL),                   /* return: buffer */
+         HeapTupleIsValid(tuple)) {
+       
+       /* ----------------
+        *  For each index relation we find, extract the information
+        *  we need and store it in a list..
+        * 
+        *  first get the oid of the index relation from the tuple
+        * ----------------
+        */
+       indexStruct = (IndexTupleForm) GETSTRUCT(tuple);
+       indexOid = indexStruct->indexrelid;
+       
+       /* ----------------
+        * allocate space for functional index information.
+        * ----------------
+        */
+       fInfoP = (FuncIndexInfoPtr)palloc( sizeof(*fInfoP) );
+       
+       /* ----------------
+        *  next get the index key information from the tuple
+        * ----------------
+        */
+       ExecGetIndexKeyInfo(indexStruct,
+                           &numKeyAtts,
+                           &indexKeyAtts,
+                           fInfoP);
+       
+       /* ----------------
+        *  next get the index predicate from the tuple
+        * ----------------
+        */
+       if (VARSIZE(&indexStruct->indpred) != 0) {
+           predString = fmgr(F_TEXTOUT, &indexStruct->indpred);
+           predicate = (PredInfo*)stringToNode(predString);
+           pfree(predString);
+       } else {
+           predicate = NULL;
+       }
+       
+       /* ----------------
+        *  save the index information into lists
+        * ----------------
+        */
+       oidList =  lconsi(indexOid, oidList);
+       nkeyList = lconsi(numKeyAtts, nkeyList);
+       keyList =  lcons(indexKeyAtts, keyList);
+       fiList =   lcons(fInfoP, fiList);
+       predList = lcons(predicate, predList);
+    }
+
+    /* ----------------
+     * we have the info we need so close the pg_index relation..
+     * ----------------
+     */
+    heap_endscan(indexSd);
+    heap_close(indexRd);
+
+    /* ----------------
+     * Now that we've collected the index information into three
+     *  lists, we open the index relations and store the descriptors
+     *  and the key information into arrays.
+     * ----------------
+     */
+    len = length(oidList);
+    if (len > 0) {
+       /* ----------------
+        *   allocate space for relation descs
+        * ----------------
+        */
+       CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext);
+       relationDescs = (RelationPtr)
+           palloc(len * sizeof(Relation));
+       
+       /* ----------------
+        *   initialize index info array
+        * ----------------
+        */
+       CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext);
+       indexInfoArray = (IndexInfo**)
+           palloc(len * sizeof(IndexInfo*));
+       
+       for (i=0; i<len; i++) {
+           IndexInfo *ii = makeNode(IndexInfo);
+           ii->ii_NumKeyAttributes = 0;
+           ii->ii_KeyAttributeNumbers = (AttrNumber*) NULL;
+           ii->ii_FuncIndexInfo = (FuncIndexInfoPtr) NULL;
+           ii->ii_Predicate = NULL;
+           indexInfoArray[i] = ii;
+       }
+       
+       /* ----------------
+        *   attempt to open each of the indices.  If we succeed,
+        *   then store the index relation descriptor into the
+        *   relation descriptor array.
+        * ----------------
+        */
+       i = 0;
+       foreach (indexoid, oidList) {
+           Relation  indexDesc;
+           
+           indexOid =  lfirsti(indexoid);
+           indexDesc = index_open(indexOid);
+           if (indexDesc != NULL)
+               relationDescs[i++] = indexDesc;
+       }
+       
+       /* ----------------
+        *   store the relation descriptor array and number of
+        *   descs into the result relation info.
+        * ----------------
+        */
+       resultRelationInfo->ri_NumIndices = i;
+       resultRelationInfo->ri_IndexRelationDescs = relationDescs;
+       
+       /* ----------------
+        *   store the index key information collected in our
+        *   lists into the index info array
+        * ----------------
+        */
+       i = 0;
+       foreach (numkeys, nkeyList) {
+           numKeyAtts = lfirsti(numkeys);
+           indexInfoArray[i++]->ii_NumKeyAttributes = numKeyAtts;
+       }
+       
+       i = 0;
+       foreach (indexkeys, keyList) {
+           indexKeyAtts = (AttrNumber*) lfirst(indexkeys);
+           indexInfoArray[i++]->ii_KeyAttributeNumbers = indexKeyAtts;
+       }
+       
+       i = 0;
+       foreach (indexfuncs, fiList) {
+           FuncIndexInfoPtr fiP = (FuncIndexInfoPtr)lfirst(indexfuncs);
+           indexInfoArray[i++]->ii_FuncIndexInfo = fiP;
+       }
+       
+       i = 0;
+       foreach (indexpreds, predList) {
+           indexInfoArray[i++]->ii_Predicate = lfirst(indexpreds);
+       }
+       /* ----------------
+        *   store the index info array into relation info
+        * ----------------
+        */
+       resultRelationInfo->ri_IndexRelationInfo = indexInfoArray;
+    }
+
+    /* ----------------
+     * All done,  resultRelationInfo now contains complete information
+     *  on the indices associated with the result relation.
+     * ----------------
+     */
+
+    /* should free oidList, nkeyList and keyList here */
+    /* OK - let's do it   -jolly */
+    freeList(oidList);
+    freeList(nkeyList);
+    freeList(keyList);
+    freeList(fiList);
+    freeList(predList);
+}
+
+/* ----------------------------------------------------------------
+ *     ExecCloseIndices
+ *
+ *     Close the index relations stored in resultRelationInfo
+ * ----------------------------------------------------------------
+ */
+void
+ExecCloseIndices(RelationInfo *resultRelationInfo)
+{
+    int        i;
+    int        numIndices;
+    RelationPtr        relationDescs;
+
+    numIndices = resultRelationInfo->ri_NumIndices;
+    relationDescs = resultRelationInfo->ri_IndexRelationDescs;
+
+    for (i=0; i<numIndices; i++)
+       if (relationDescs[i] != NULL)
+           index_close(relationDescs[i]);
+    /*
+     * XXX should free indexInfo array here too.
+     */
+}
+
+/* ----------------------------------------------------------------
+ *     ExecFormIndexTuple
+ *
+ *     Most of this code is cannabilized from DefaultBuild().
+ *     As said in the comments for ExecOpenIndices, most of
+ *     this functionality should be rearranged into a proper
+ *     set of routines..
+ * ----------------------------------------------------------------
+ */
+IndexTuple
+ExecFormIndexTuple(HeapTuple heapTuple,
+                  Relation heapRelation,
+                  Relation indexRelation,
+                  IndexInfo *indexInfo)
+{
+    IndexTuple indexTuple;
+    TupleDesc  heapDescriptor;
+    TupleDesc  indexDescriptor;
+    Datum      *datum;
+    char       *nulls;
+
+    int                        numberOfAttributes;
+    AttrNumber         *keyAttributeNumbers;
+    FuncIndexInfoPtr   fInfoP;
+
+    /* ----------------
+     * get information from index info structure
+     * ----------------
+     */
+    numberOfAttributes =  indexInfo->ii_NumKeyAttributes;
+    keyAttributeNumbers = indexInfo->ii_KeyAttributeNumbers;
+    fInfoP =              indexInfo->ii_FuncIndexInfo;
+
+    /* ----------------
+     * datum and null are arrays in which we collect the index attributes
+     *  when forming a new index tuple.
+     * ----------------
+     */
+    CXT1_printf("ExecFormIndexTuple: context is %d\n", CurrentMemoryContext);
+    datum = (Datum *)  palloc(numberOfAttributes * sizeof *datum);
+    nulls =  (char *)  palloc(numberOfAttributes * sizeof *nulls);
+
+    /* ----------------
+     * get the tuple descriptors from the relations so we know
+     *  how to form the index tuples..
+     * ----------------
+     */
+    heapDescriptor =  RelationGetTupleDescriptor(heapRelation);
+    indexDescriptor = RelationGetTupleDescriptor(indexRelation);
+
+    /* ----------------
+     *  FormIndexDatum fills in its datum and null parameters
+     *  with attribute information taken from the given heap tuple.
+     * ----------------
+     */
+    FormIndexDatum(numberOfAttributes,  /* num attributes */
+                  keyAttributeNumbers, /* array of att nums to extract */
+                  heapTuple,           /* tuple from base relation */
+                  heapDescriptor,      /* heap tuple's descriptor */
+                  InvalidBuffer,       /* buffer associated with heap tuple */
+                  datum,               /* return: array of attributes */
+                  nulls,               /* return: array of char's */
+                  fInfoP);             /* functional index information */
+
+    indexTuple = index_formtuple(indexDescriptor,
+                                datum,
+                                nulls);
+
+    /* ----------------
+     * free temporary arrays
+     *
+     *  XXX should store these in the IndexInfo instead of allocating
+     *     and freeing on every insertion, but efficency here is not
+     *     that important and FormIndexTuple is wasteful anyways..
+     *     -cim 9/27/89
+     * ----------------
+     */
+    pfree(nulls);
+    pfree(datum);
+
+    return indexTuple;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecInsertIndexTuples
+ *
+ *     This routine takes care of inserting index tuples
+ *     into all the relations indexing the result relation
+ *     when a heap tuple is inserted into the result relation.
+ *     Much of this code should be moved into the genam
+ *     stuff as it only exists here because the genam stuff
+ *     doesn't provide the functionality needed by the
+ *     executor.. -cim 9/27/89
+ * ----------------------------------------------------------------
+ */
+void
+ExecInsertIndexTuples(TupleTableSlot *slot,
+                     ItemPointer tupleid,
+                     EState *estate)
+{
+    HeapTuple                  heapTuple;
+    RelationInfo               *resultRelationInfo;
+    int                        i;
+    int                        numIndices;
+    RelationPtr                        relationDescs;
+    Relation                   heapRelation;
+    IndexInfo                  **indexInfoArray;
+    Node                       *predicate;
+    bool                       satisfied;
+    ExprContext                        *econtext;
+    IndexTuple                 indexTuple;
+    InsertIndexResult          result;
+
+    heapTuple = slot->val;
+
+    /* ----------------
+     * get information from the result relation info structure.
+     * ----------------
+     */
+    resultRelationInfo = estate->es_result_relation_info;
+    numIndices =         resultRelationInfo->ri_NumIndices;
+    relationDescs =      resultRelationInfo->ri_IndexRelationDescs;
+    indexInfoArray =     resultRelationInfo->ri_IndexRelationInfo;
+    heapRelation =       resultRelationInfo->ri_RelationDesc;
+
+    /* ----------------
+     * for each index, form and insert the index tuple
+     * ----------------
+     */
+    econtext = NULL;
+    for (i=0; i<numIndices; i++) {
+       if (relationDescs[i] == NULL) continue;
+       
+       predicate = indexInfoArray[i]->ii_Predicate;
+       if (predicate != NULL) {
+           if (econtext == NULL) {
+               econtext = makeNode(ExprContext);
+           }
+           econtext->ecxt_scantuple = slot;
+           
+           /* Skip this index-update if the predicate isn't satisfied */
+           satisfied = ExecQual((List*)predicate, econtext);
+           if (satisfied == false)
+               continue;
+       }
+       
+       indexTuple = ExecFormIndexTuple(heapTuple,
+                                       heapRelation,
+                                       relationDescs[i],
+                                       indexInfoArray[i]);
+       
+       indexTuple->t_tid = (*tupleid);     /* structure assignment */
+       
+       result = index_insert(relationDescs[i], /* index relation */
+                             indexTuple);      /* index tuple */
+       
+       /* ----------------
+        *      keep track of index inserts for debugging
+        * ----------------
+        */
+       IncrIndexInserted();
+       
+       /* ----------------
+        *      free index tuple after insertion
+        * ----------------
+        */
+       if (result) pfree(result);
+       pfree(indexTuple);
+    }
+    if (econtext != NULL) pfree(econtext);
+}
+
diff --git a/src/backend/executor/execdebug.h b/src/backend/executor/execdebug.h
new file mode 100644 (file)
index 0000000..c672cba
--- /dev/null
@@ -0,0 +1,377 @@
+/*-------------------------------------------------------------------------
+ *
+ * execdebug.h--
+ *    #defines governing debugging behaviour in the executor
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef EXECDEBUG_H
+#define EXECDEBUG_H
+
+/* ----------------------------------------------------------------
+ *     debugging defines.
+ *
+ *     If you want certain debugging behaviour, then #define
+ *     the variable to 1, else #undef it. -cim 10/26/89
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     EXEC_DEBUGSTORETUP is for tuple table debugging - this
+ *     will print a message every time we call ExecStoreTuple.
+ *     -cim 3/20/91
+ * ----------------
+ */
+#undef EXEC_DEBUGSTORETUP
+
+/* ----------------
+ *     EXEC_TUPLECOUNT is a #define which causes the
+ *     executor keep track of tuple counts.  This might be
+ *     causing some problems with the decstation stuff so
+ *     you might want to undefine this if you are doing work
+ *     on the decs  - cim 10/20/89
+ * ----------------
+ */
+#undef EXEC_TUPLECOUNT
+
+/* ----------------
+ *     EXEC_SHOWBUFSTATS controls whether or not buffer statistics
+ *     are shown for each query.  -cim 2/9/89
+ * ----------------
+ */
+#undef EXEC_SHOWBUFSTATS
+
+/* ----------------
+ *     EXEC_CONTEXTDEBUG turns on the printing of debugging information
+ *     by CXT_printf() calls regarding which memory context is the
+ *     CurrentMemoryContext for palloc() calls.  
+ * ----------------
+ */
+#undef EXEC_CONTEXTDEBUG
+
+/* ----------------
+ *     EXEC_RETURNSIZE is a compile flag governing the
+ *     behaviour of lispFmgr..  See ExecMakeFunctionResult().
+ *     Undefining this avoids a problem in the system cache.
+ *
+ *     Note: undefining this means that there is incorrect
+ *           information in the const nodes corresponding
+ *           to function (or operator) results.  The thing is,
+ *           99% of the time this is fine because when you do
+ *           something like x = emp.sal + 1, you already know
+ *           the type and size of x so the fact that + didn't
+ *           return the correct size doesn't matter.
+ *           With variable length stuff the size is stored in
+ *           the first few bytes of the data so again, it's
+ *           not likely to matter.
+ * ----------------
+ */
+#undef EXEC_RETURNSIZE
+
+/* ----------------
+ *     EXEC_UTILSDEBUG is a flag which turns on debugging of the
+ *     executor utilities by EU_printf() in eutils.c
+ * ----------------
+ */
+#undef EXEC_UTILSDEBUG
+
+/* ----------------
+ *     EXEC_NESTLOOPDEBUG is a flag which turns on debugging of the
+ *     nest loop node by NL_printf() and ENL_printf() in nestloop.c
+ * ----------------
+ */
+#undef EXEC_NESTLOOPDEBUG
+
+/* ----------------
+ *     EXEC_PROCDEBUG is a flag which turns on debugging of
+ *     ExecProcNode() by PN_printf() in procnode.c
+ * ----------------
+ */
+#undef EXEC_PROCDEBUG
+
+/* ----------------
+ *     EXEC_EVALDEBUG is a flag which turns on debugging of
+ *     ExecEval and ExecTargetList() stuff by EV_printf() in qual.c
+ * ----------------
+ */
+#undef EXEC_EVALDEBUG
+
+/* ----------------
+ *     EXEC_SCANDEBUG is a flag which turns on debugging of
+ *     the ExecSeqScan() stuff by S_printf() in seqscan.c
+ * ----------------
+ */
+#undef EXEC_SCANDEBUG
+
+/* ----------------
+ *     EXEC_SORTDEBUG is a flag which turns on debugging of
+ *     the ExecSort() stuff by SO_printf() in sort.c
+ * ----------------
+ */
+#undef EXEC_SORTDEBUG
+
+/* ----------------
+ *     EXEC_MERGEJOINDEBUG is a flag which turns on debugging of
+ *     the ExecMergeJoin() stuff by MJ_printf() in mergejoin.c
+ * ----------------
+ */
+#undef EXEC_MERGEJOINDEBUG
+
+/* ----------------
+ *     EXEC_MERGEJOINPFREE is a flag which causes merge joins
+ *     to pfree intermittant tuples (which is the proper thing)
+ *     Not defining this means we avoid menory management problems
+ *     at the cost of doing deallocation of stuff only at the
+ *     end of the transaction
+ * ----------------
+ */
+#undef EXEC_MERGEJOINPFREE
+
+/* ----------------
+ *     EXEC_DEBUGINTERACTIVE is a flag which enables the
+ *     user to issue "DEBUG" commands from an interactive
+ *     backend. 
+ * ----------------
+ */
+#undef EXEC_DEBUGINTERACTIVE
+
+/* ----------------
+ *     EXEC_DEBUGVARIABLEFILE is string, which if defined will
+ *     be loaded when the executor is initialized.  If this
+ *     string is not defined then nothing will be loaded..
+ *
+ *     Example:
+ *
+ * #define EXEC_DEBUGVARIABLEFILE "/a/postgres/cimarron/.pg_debugvars"
+ #
+ *     Note: since these variables are read at execution time,
+ *     they can't affect the first query.. this hack should be
+ *     replaced by something better sometime. -cim 11/2/89
+ * ----------------
+ */
+#undef EXEC_DEBUGVARIABLEFILE
+
+/* ----------------------------------------------------------------
+ *     #defines controlled by above definitions
+ *
+ *     Note: most of these are "incomplete" because I didn't
+ *           need the ones not defined.  More should be added
+ *           only as necessary -cim 10/26/89
+ * ----------------------------------------------------------------
+ */
+#define T_OR_F(b)              (b ? "true" : "false")
+#define NULL_OR_TUPLE(slot)    (TupIsNull(slot) ? "null" : "a tuple")
+
+
+/* #define EXEC_TUPLECOUNT - XXX take out for now for executor stubbing -- jolly*/
+/* ----------------
+ *     tuple count debugging defines
+ * ----------------
+ */
+#ifdef EXEC_TUPLECOUNT
+extern int     NTupleProcessed;
+extern int     NTupleRetrieved;
+extern int     NTupleReplaced;
+extern int     NTupleAppended;
+extern int     NTupleDeleted;
+extern int     NIndexTupleProcessed;
+extern int     NIndexTupleInserted;
+
+#define IncrRetrieved()                NTupleRetrieved++
+#define        IncrAppended()          NTupleAppended++
+#define        IncrDeleted()           NTupleDeleted++
+#define        IncrReplaced()          NTupleReplaced++
+#define        IncrInserted()          NTupleInserted++
+#define IncrProcessed()                NTupleProcessed++
+#define IncrIndexProcessed()   NIndexTupleProcessed++
+#define IncrIndexInserted()    NIndexTupleInserted++
+#else
+#define IncrRetrieved()
+#define        IncrAppended()
+#define        IncrDeleted()
+#define        IncrReplaced()
+#define        IncrInserted()
+#define IncrProcessed()
+#define IncrIndexProcessed()
+#define IncrIndexInserted()
+#endif /* EXEC_TUPLECOUNT */
+
+/* ----------------
+ *     memory context debugging defines
+ * ----------------
+ */
+#ifdef EXEC_CONTEXTDEBUG
+#define CXT_printf(s)                  printf(s)
+#define CXT1_printf(s, a)              printf(s, a)
+#else
+#define CXT_printf(s)          
+#define CXT1_printf(s, a)              
+#endif /* EXEC_CONTEXTDEBUG */
+
+/* ----------------
+ *     eutils debugging defines
+ * ----------------
+ */
+#ifdef EXEC_UTILSDEBUG
+#define EU_nodeDisplay(l)              nodeDisplay(l, 0)
+#define EU_printf(s)                   printf(s)
+#define EU1_printf(s, a)               printf(s, a)
+#define EU4_printf(s, a, b, c, d)      printf(s, a, b, c, d)
+#else
+#define EU_nodeDisplay(l)              
+#define EU_printf(s)                   
+#define EU1_printf(s, a)               
+#define EU4_printf(s, a, b, c, d)      
+#endif /* EXEC_UTILSDEBUG */
+
+
+/* ----------------
+ *     nest loop debugging defines
+ * ----------------
+ */
+#ifdef EXEC_NESTLOOPDEBUG
+#define NL_nodeDisplay(l)              nodeDisplay(l, 0)
+#define NL_printf(s)                   printf(s)
+#define NL1_printf(s, a)               printf(s, a)
+#define NL4_printf(s, a, b, c, d)      printf(s, a, b, c, d)
+#define ENL1_printf(message)           printf("ExecNestLoop: %s\n", message)
+#else
+#define NL_nodeDisplay(l)              
+#define NL_printf(s)                   
+#define NL1_printf(s, a)               
+#define NL4_printf(s, a, b, c, d)      
+#define ENL1_printf(message)
+#endif /* EXEC_NESTLOOPDEBUG */
+
+/* ----------------
+ *     proc node debugging defines
+ * ----------------
+ */
+#ifdef EXEC_PROCDEBUG
+#define PN_printf(s)                   printf(s)
+#define PN1_printf(s, p)               printf(s, p)
+#else
+#define PN_printf(s)           
+#define PN1_printf(s, p)       
+#endif /* EXEC_PROCDEBUG */
+
+/* ----------------
+ *     exec eval / target list debugging defines
+ * ----------------
+ */
+#ifdef EXEC_EVALDEBUG
+#define EV_nodeDisplay(l)              nodeDisplay(l, 0)
+#define EV_printf(s)                   printf(s)
+#define EV1_printf(s, a)               printf(s, a)
+#define EV5_printf(s, a, b, c, d, e)   printf(s, a, b, c, d, e)
+#else
+#define EV_nodeDisplay(l)              
+#define EV_printf(s)                   
+#define EV1_printf(s, a)               
+#define EV5_printf(s, a, b, c, d, e)   
+#endif /* EXEC_EVALDEBUG */
+
+/* ----------------
+ *     scan debugging defines
+ * ----------------
+ */
+#ifdef EXEC_SCANDEBUG
+#define S_nodeDisplay(l)               nodeDisplay(l, 0)
+#define S_printf(s)                    printf(s)
+#define S1_printf(s, p)                        printf(s, p)
+#else
+#define S_nodeDisplay(l)       
+#define S_printf(s)            
+#define S1_printf(s, p)                
+#endif /*  EXEC_SCANDEBUG */
+/* ----------------
+ *     sort node debugging defines
+ * ----------------
+ */
+#ifdef EXEC_SORTDEBUG
+#define SO_nodeDisplay(l)              nodeDisplay(l, 0)
+#define SO_printf(s)                   printf(s)
+#define SO1_printf(s, p)               printf(s, p)
+#else
+#define SO_nodeDisplay(l)      
+#define SO_printf(s)           
+#define SO1_printf(s, p)               
+#endif /* EXEC_SORTDEBUG */
+
+/* ----------------
+ *     merge join debugging defines
+ * ----------------
+ */
+#ifdef EXEC_MERGEJOINDEBUG
+#define MJ_nodeDisplay(l)              nodeDisplay(l, 0)
+#define MJ_printf(s)                   printf(s)
+#define MJ1_printf(s, p)               printf(s, p)
+#define MJ2_printf(s, p1, p2)          printf(s, p1, p2)
+#define MJ_debugtup(tuple, type)       debugtup(tuple, type)
+#define MJ_dump(context, state)                ExecMergeTupleDump(econtext, state)
+#define MJ_DEBUG_QUAL(clause, res) \
+  MJ2_printf("  ExecQual(%s, econtext) returns %s\n", \
+            CppAsString(clause), T_OR_F(res));
+           
+#define MJ_DEBUG_MERGE_COMPARE(qual, res) \
+  MJ2_printf("  MergeCompare(mergeclauses, %s, ..) returns %s\n", \
+            CppAsString(qual), T_OR_F(res));
+
+#define MJ_DEBUG_PROC_NODE(slot) \
+  MJ2_printf("  %s = ExecProcNode(innerPlan) returns %s\n", \
+            CppAsString(slot), NULL_OR_TUPLE(slot));
+#else
+#define MJ_nodeDisplay(l)
+#define MJ_printf(s)           
+#define MJ1_printf(s, p)               
+#define MJ2_printf(s, p1, p2)
+#define MJ_debugtup(tuple, type)
+#define MJ_dump(context, state)
+#define MJ_DEBUG_QUAL(clause, res)
+#define MJ_DEBUG_MERGE_COMPARE(qual, res)
+#define MJ_DEBUG_PROC_NODE(slot)
+#endif /* EXEC_MERGEJOINDEBUG */
+
+/* ----------------------------------------------------------------
+ *     DO NOT DEFINE THESE EVER OR YOU WILL BURN!
+ * ----------------------------------------------------------------
+ */
+/* ----------------
+ *     DOESNOTWORK is currently placed around memory manager
+ *     code that is known to cause problems.  Code in between
+ *     is likely not converted and probably won't work anyways.
+ * ----------------
+ */
+#undef DOESNOTWORK
+
+/* ----------------
+ *     PERHAPSNEVER is placed around the "scan attribute"
+ *     support code for the rule manager because for now we
+ *     do things inefficiently.  The correct solution to our
+ *     problem is to add code to the parser/planner to save
+ *     attribute information for the rule manager rather than
+ *     have the executor have to grope through the entire plan
+ *     for it so if we ever decide to make things better,
+ *     we should probably delete the stuff in between PERHAPSNEVER..
+ * ----------------
+ */
+#undef PERHAPSNEVER
+
+/* ----------------
+ *     NOTYET is placed around any code not yet implemented
+ *     in the executor.  Only remove these when actually implementing
+ *     said code.
+ * ----------------
+ */
+#undef NOTYET
+
+extern long NDirectFileRead;
+extern long NDirectFileWrite;
+
+#endif /*  ExecDebugIncluded */
diff --git a/src/backend/executor/execdefs.h b/src/backend/executor/execdefs.h
new file mode 100644 (file)
index 0000000..463b75c
--- /dev/null
@@ -0,0 +1,54 @@
+/*-------------------------------------------------------------------------
+ *
+ * execdefs.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef EXECDEFS_H
+#define EXECDEFS_H
+
+/* ----------------
+ *     executor scan direction definitions
+ * ----------------
+ */
+#define EXEC_FRWD              1               /* Scan forward */
+#define EXEC_BKWD              -1              /* Scan backward */
+
+/* ----------------
+ *     ExecutePlan() tuplecount definitions
+ * ----------------
+ */
+#define ALL_TUPLES             0               /* return all tuples */
+#define ONE_TUPLE              1               /* return only one tuple */
+
+/* ----------------
+ *     constants used by ExecMain
+ * ----------------
+ */
+#define EXEC_RUN                       3
+#define EXEC_FOR                       4
+#define EXEC_BACK                      5
+#define EXEC_RETONE                    6
+#define EXEC_RESULT                    7
+
+/* ----------------
+ *     Merge Join states
+ * ----------------
+ */
+#define EXEC_MJ_INITIALIZE             1
+#define EXEC_MJ_JOINMARK               2
+#define EXEC_MJ_JOINTEST               3
+#define EXEC_MJ_JOINTUPLES             4
+#define EXEC_MJ_NEXTOUTER              5
+#define EXEC_MJ_TESTOUTER              6
+#define EXEC_MJ_NEXTINNER              7
+#define EXEC_MJ_SKIPINNER              8
+#define EXEC_MJ_SKIPOUTER              9
+
+#endif /* EXECDEFS_H */
diff --git a/src/backend/executor/execdesc.h b/src/backend/executor/execdesc.h
new file mode 100644 (file)
index 0000000..c089a8f
--- /dev/null
@@ -0,0 +1,38 @@
+/*-------------------------------------------------------------------------
+ *
+ * execdesc.h--
+ *    plan and query descriptor accessor macros used by the executor
+ *    and related modules.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef EXECDESC_H
+#define EXECDESC_H
+
+#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+#include "tcop/dest.h"
+
+/* ----------------
+ *     query descriptor:
+ *  a QueryDesc encapsulates everything that the executor
+ *  needs to execute the query
+ * ---------------------
+ */
+typedef struct QueryDesc {
+    CmdType            operation; /* CMD_SELECT, CMD_UPDATE, etc. */
+    Query              *parsetree; 
+    Plan               *plantree;
+    CommandDest                dest;  /* the destination output of the execution */
+} QueryDesc;
+
+/* in pquery.c */
+extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree,
+                                 CommandDest dest);
+
+#endif /*  EXECDESC_H  */
diff --git a/src/backend/executor/executor.h b/src/backend/executor/executor.h
new file mode 100644 (file)
index 0000000..9f2b258
--- /dev/null
@@ -0,0 +1,229 @@
+/*-------------------------------------------------------------------------
+ *
+ * executor.h--
+ *    support for the POSTGRES executor module
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef EXECUTOR_H
+#define EXECUTOR_H
+
+/* ----------------------------------------------------------------
+ *     #includes
+ * ----------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <string.h>
+
+#include "postgres.h"
+#include "nodes/pg_list.h"
+
+/* ----------------
+ * executor debugging definitions are kept in a separate file
+ * so people can customize what debugging they want to see and not
+ * have this information clobbered every time a new version of
+ * executor.h is checked in -cim 10/26/89
+ * ----------------
+ */
+#include "executor/execdebug.h"
+
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/istrat.h"
+#include "access/itup.h"
+#include "access/skey.h"
+#include "utils/tqual.h"
+#include "catalog/catname.h"
+#include "utils/syscache.h"
+#include "executor/execdefs.h"
+#include "executor/tuptable.h"
+
+#include "nodes/parsenodes.h"
+
+#include "storage/buf.h"
+#include "miscadmin.h"
+#include "fmgr.h"
+#include "utils/elog.h"
+#include "utils/mcxt.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+
+#include "catalog/pg_index.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_aggregate.h"
+
+#include "access/printtup.h"
+#include "nodes/primnodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
+
+#include "tcop/dest.h"
+#include "storage/smgr.h"
+
+#include "access/genam.h"
+#include "executor/execdesc.h"
+
+/*
+ * prototypes from functions in execAmi.c
+ */
+extern void ExecOpenScanR(Oid relOid, int nkeys, ScanKey skeys, bool isindex,
+                  ScanDirection dir, TimeQual timeRange,
+                  Relation *returnRelation, Pointer *returnScanDesc);
+extern Relation ExecOpenR(Oid relationOid, bool isindex);
+extern Pointer ExecBeginScan(Relation relation, int nkeys, ScanKey skeys,
+                     bool isindex, ScanDirection dir, TimeQual time_range);
+extern void ExecCloseR(Plan *node);
+extern void ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent);
+extern HeapScanDesc ExecReScanR(Relation relDesc, HeapScanDesc scanDesc,
+                        ScanDirection direction, int nkeys, ScanKey skeys);
+extern void ExecMarkPos(Plan *node);
+extern void ExecRestrPos(Plan *node);
+extern Relation ExecCreatR(TupleDesc tupType, Oid relationOid);
+
+/*
+ * prototypes from functions in execJunk.c
+ */
+extern JunkFilter *ExecInitJunkFilter(List *targetList);
+extern bool ExecGetJunkAttribute(JunkFilter *junkfilter, TupleTableSlot *slot,
+                                char *attrName, Datum *value, bool *isNull);
+extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot);
+
+
+/*
+ * prototypes from functions in execMain.c
+ */
+extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate);
+extern TupleTableSlot* ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count);
+extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate);
+
+/*
+ * prototypes from functions in execProcnode.c
+ */
+extern bool ExecInitNode(Plan *node, EState *estate, Plan *parent);
+extern TupleTableSlot *ExecProcNode(Plan *node, Plan *parent);
+extern int ExecCountSlotsNode(Plan *node);
+extern void ExecEndNode(Plan *node, Plan *parent);
+
+/*
+ * prototypes from functions in execQual.c
+ */
+extern bool execConstByVal;
+extern int     execConstLen;
+
+extern Datum ExecExtractResult(TupleTableSlot *slot, AttrNumber attnum,
+                       bool *isNull);
+extern Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
+extern Datum ExecEvalParam(Param *expression, ExprContext *econtext,
+                          bool *isNull);
+extern char *GetAttributeByNum(TupleTableSlot *slot, AttrNumber attrno,
+                       bool *isNull);
+extern char *att_by_num(TupleTableSlot *slot, AttrNumber attrno,
+                       bool *isNull);
+/* stop here */
+extern char *GetAttributeByName(TupleTableSlot *slot, char *attname,
+                               bool *isNull);
+extern char *att_by_name(TupleTableSlot *slot, char *attname, bool *isNull);
+extern void ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext *econtext,
+                     List *argList, Datum argV[], bool *argIsDone);
+extern Datum ExecMakeFunctionResult(Node *node, List *arguments,
+               ExprContext *econtext, bool *isNull, bool *isDone);
+extern Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
+                         bool *isNull);
+extern Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext,
+                         bool *isNull, bool *isDone);
+extern Datum ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull);
+extern Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull);
+extern Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull);
+extern Datum ExecEvalExpr(Node *expression, ExprContext *econtext, bool *isNull,
+                         bool *isDone);
+extern bool ExecQualClause(Node *clause, ExprContext *econtext);
+extern bool ExecQual(List *qual, ExprContext *econtext);
+extern int ExecTargetListLength(List *targetlist);
+extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, bool *isDone);
+
+/*
+ * prototypes from functions in execScan.c
+ */
+extern TupleTableSlot *ExecScan(Scan *node, TupleTableSlot* (*accessMtd)());
+
+/*
+ * prototypes from functions in execTuples.c
+ */
+extern TupleTable ExecCreateTupleTable(int initialSize);
+extern void ExecDestroyTupleTable(TupleTable table, bool shouldFree);
+extern TupleTableSlot* ExecAllocTableSlot(TupleTable table);
+extern TupleTableSlot* ExecStoreTuple(HeapTuple tuple, 
+                                     TupleTableSlot *slot,
+                                     Buffer buffer,
+                                     bool shouldFree);
+extern TupleTableSlot* ExecClearTuple(TupleTableSlot* slot);
+extern bool ExecSlotPolicy(TupleTableSlot *slot);
+extern bool ExecSetSlotPolicy(TupleTableSlot *slot, bool shouldFree);
+extern TupleDesc ExecSetSlotDescriptor(TupleTableSlot *slot,
+                                      TupleDesc tupdesc);
+extern void ExecSetSlotDescriptorIsNew(TupleTableSlot *slot, bool isNew);
+extern TupleDesc ExecSetNewSlotDescriptor(TupleTableSlot *slot,
+                                         TupleDesc tupdesc);
+extern Buffer ExecSetSlotBuffer(TupleTableSlot *slot, Buffer b);
+extern void ExecIncrSlotBufferRefcnt(TupleTableSlot *slot);
+extern bool TupIsNull(TupleTableSlot* slot);
+extern bool ExecSlotDescriptorIsNew(TupleTableSlot *slot);
+extern void ExecInitResultTupleSlot(EState *estate, CommonState *commonstate);
+extern void ExecInitScanTupleSlot(EState *estate,
+                                 CommonScanState *commonscanstate);
+extern void ExecInitMarkedTupleSlot(EState *estate, MergeJoinState *mergestate);
+extern void ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate);
+extern void ExecInitHashTupleSlot(EState *estate, HashJoinState *hashstate);
+extern TupleTableSlot *NodeGetResultTupleSlot(Plan *node);
+
+extern TupleDesc ExecGetTupType(Plan *node);
+extern TupleDesc ExecTypeFromTL(List *targetList);
+
+/*
+ * prototypes from functions in execTuples.c
+ */
+extern void ResetTupleCount();
+extern void DisplayTupleCount(FILE *statfp);
+extern void ExecAssignNodeBaseInfo(EState *estate, CommonState *basenode,
+                                  Plan *parent);
+extern void ExecAssignExprContext(EState *estate, CommonState *commonstate);
+extern void ExecAssignResultType(CommonState *commonstate,
+                                TupleDesc tupDesc);
+extern void ExecAssignResultTypeFromOuterPlan(Plan *node,
+               CommonState *commonstate);
+extern void ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate);
+extern TupleDesc ExecGetResultType(CommonState *commonstate);
+extern void ExecFreeResultType(CommonState *commonstate);
+extern void ExecAssignProjectionInfo(Plan *node, CommonState *commonstate);
+extern void ExecFreeProjectionInfo(CommonState *commonstate);
+extern TupleDesc ExecGetScanType(CommonScanState *csstate);
+extern void ExecFreeScanType(CommonScanState *csstate);
+extern void ExecAssignScanType(CommonScanState *csstate,
+                              TupleDesc tupDesc);
+extern void ExecAssignScanTypeFromOuterPlan(Plan *node,
+                                           CommonScanState *csstate);
+extern AttributeTupleForm ExecGetTypeInfo(Relation relDesc);
+
+extern void ExecGetIndexKeyInfo(IndexTupleForm indexTuple, int *numAttsOutP,
+                AttrNumber **attsOutP, FuncIndexInfoPtr fInfoP);
+extern void ExecOpenIndices(Oid resultRelationOid,
+                           RelationInfo *resultRelationInfo);
+extern void ExecCloseIndices(RelationInfo *resultRelationInfo);
+extern IndexTuple ExecFormIndexTuple(HeapTuple heapTuple,
+       Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo);
+extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
+                                     EState *estate);
+
+
+/* ----------------------------------------------------------------
+ *     the end
+ * ----------------------------------------------------------------
+ */
+
+#endif /*  EXECUTOR_H  */
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
new file mode 100644 (file)
index 0000000..124505a
--- /dev/null
@@ -0,0 +1,388 @@
+/*-------------------------------------------------------------------------
+ *
+ * functions.c--
+ *    Routines to handle functions called from the executor
+ *    Putting this stuff in fmgr makes the postmaster a mess....
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+#include "nodes/execnodes.h"
+#include "nodes/plannodes.h"
+
+#include "catalog/pg_proc.h"
+#include "parser/parse_query.h"
+#include "tcop/pquery.h"
+#include "tcop/tcopprot.h"
+#include "nodes/params.h"
+#include "fmgr.h"
+#include "utils/fcache.h"
+#include "utils/datum.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/syscache.h"
+#include "catalog/pg_language.h"
+#include "access/heapam.h"
+#include "access/xact.h"
+#include "executor/executor.h"
+#include "executor/functions.h"
+
+#undef new
+
+typedef enum {F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE} ExecStatus;
+
+typedef struct local_es {
+    QueryDesc *qd;
+    EState *estate;
+    struct local_es *next;
+    ExecStatus status;
+} execution_state;
+
+#define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *)NULL)
+
+/* non-export function prototypes */
+static TupleDesc postquel_start(execution_state *es);
+static execution_state *init_execution_state(FunctionCachePtr fcache,
+                                            char *args[]);
+static TupleTableSlot *postquel_getnext(execution_state *es);
+static void postquel_end(execution_state *es);
+static void postquel_sub_params(execution_state *es, int nargs,
+                               char *args[], bool *nullV);
+static Datum postquel_execute(execution_state *es, FunctionCachePtr fcache,
+                             List *fTlist, char **args, bool *isNull);
+     
+
+Datum
+ProjectAttribute(TupleDesc TD,
+                TargetEntry *tlist,
+                HeapTuple tup,
+                bool *isnullP)
+{
+    Datum val,valueP;
+    Var  *attrVar = (Var *)tlist->expr;
+    AttrNumber attrno = attrVar->varattno;
+    
+    
+    val = PointerGetDatum(heap_getattr(tup,
+                                      InvalidBuffer,
+                                      attrno,
+                                      TD,
+                                      isnullP));
+    if (*isnullP)
+       return (Datum) NULL;
+    
+    valueP = datumCopy(val,
+                      TD->attrs[attrno-1]->atttypid,
+                      TD->attrs[attrno-1]->attbyval,
+                      (Size) TD->attrs[attrno-1]->attlen);
+    return valueP;
+}
+
+static execution_state *
+init_execution_state(FunctionCachePtr fcache,
+                    char *args[])
+{
+    execution_state       *newes;
+    execution_state       *nextes;
+    execution_state       *preves;
+    QueryTreeList        *queryTree_list;
+    int                   i;
+    List                 *planTree_list;
+    int nargs;
+    
+    nargs = fcache->nargs;
+    
+    newes = (execution_state *) palloc(sizeof(execution_state));
+    nextes = newes;
+    preves = (execution_state *)NULL;
+    
+    
+    planTree_list = (List *)
+       pg_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None);
+    
+    for (i=0; i < queryTree_list->len; i++) {
+       EState    *estate;
+       Query *queryTree = (Query*) (queryTree_list->qtrees[i]);
+       Plan  *planTree = lfirst(planTree_list);
+           
+       if (!nextes)
+           nextes = (execution_state *) palloc(sizeof(execution_state));
+       if (preves)
+           preves->next = nextes;
+           
+       nextes->next = NULL;
+       nextes->status = F_EXEC_START;
+       nextes->qd = CreateQueryDesc(queryTree,
+                                    planTree,
+                                    None);
+       estate = CreateExecutorState();
+           
+       if (nargs > 0) {
+           int           i;
+           ParamListInfo paramLI;
+                   
+           paramLI =
+               (ParamListInfo)palloc((nargs+1)*sizeof(ParamListInfoData));
+                   
+           memset(paramLI, 0, nargs*sizeof(ParamListInfoData)); 
+
+           estate->es_param_list_info = paramLI;
+                   
+           for (i=0; i<nargs; paramLI++, i++) {
+               paramLI->kind = PARAM_NUM;
+               paramLI->id = i+1;
+               paramLI->isnull = false;
+               paramLI->value = (Datum) NULL;
+           }
+           paramLI->kind = PARAM_INVALID;
+       }
+       else
+           estate->es_param_list_info = (ParamListInfo)NULL;
+       nextes->estate = estate;
+       preves = nextes;
+       nextes = (execution_state *)NULL;
+
+       planTree_list = lnext(planTree_list);
+    }
+    
+    return newes;
+}
+
+static TupleDesc
+postquel_start(execution_state *es)
+{
+    return ExecutorStart(es->qd, es->estate);
+}
+
+static TupleTableSlot *
+postquel_getnext(execution_state *es)
+{
+    int feature;
+    
+    feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
+    
+    return ExecutorRun(es->qd, es->estate, feature, 0);
+}
+
+static void
+postquel_end(execution_state *es)
+{
+    ExecutorEnd(es->qd, es->estate);
+}
+
+static void
+postquel_sub_params(execution_state *es,
+                   int nargs,
+                   char *args[],
+                   bool *nullV)
+{
+    ParamListInfo paramLI;
+    EState *estate;
+    
+    estate = es->estate;
+    paramLI = estate->es_param_list_info;
+    
+    while (paramLI->kind != PARAM_INVALID) {
+       if (paramLI->kind == PARAM_NUM) {
+           Assert(paramLI->id <= nargs);
+           paramLI->value = (Datum)args[(paramLI->id - 1)];
+           paramLI->isnull = nullV[(paramLI->id - 1)];
+       }
+       paramLI++;
+    }
+}
+
+static TupleTableSlot *
+copy_function_result(FunctionCachePtr fcache,
+                    TupleTableSlot *resultSlot)
+{
+    TupleTableSlot  *funcSlot;
+    TupleDesc resultTd;
+    HeapTuple newTuple;
+    HeapTuple oldTuple;
+    
+    Assert(! TupIsNull(resultSlot));
+    oldTuple = resultSlot->val;
+    
+    funcSlot = (TupleTableSlot*)fcache->funcSlot;
+    
+    if (funcSlot == (TupleTableSlot*)NULL)
+       return resultSlot;
+    
+    resultTd = resultSlot->ttc_tupleDescriptor;
+    /*
+     * When the funcSlot is NULL we have to initialize the funcSlot's
+     * tuple descriptor.
+     */
+    if (TupIsNull(funcSlot)) {
+       int i= 0;
+       TupleDesc funcTd = funcSlot->ttc_tupleDescriptor;
+       
+       while (i < oldTuple->t_natts) {
+           funcTd->attrs[i] =
+               (AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE);
+           memmove(funcTd->attrs[i], 
+                   resultTd->attrs[i], 
+                   ATTRIBUTE_TUPLE_SIZE);
+           i++;
+       }
+    }
+    
+    newTuple = heap_copytuple(oldTuple);
+    
+    return ExecStoreTuple(newTuple,funcSlot,InvalidBuffer,true);
+}
+
+static Datum
+postquel_execute(execution_state  *es,
+                FunctionCachePtr fcache,
+                List *fTlist,
+                char **args,
+                bool *isNull)
+{
+    TupleTableSlot *slot;
+    Datum          value;
+    
+    if (es->status == F_EXEC_START)
+       {
+           (void) postquel_start(es);
+           es->status = F_EXEC_RUN;
+       }
+    
+    if (fcache->nargs > 0)
+        postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
+    
+    slot = postquel_getnext(es);
+    
+    if (TupIsNull(slot)) {
+       postquel_end(es);
+       es->status = F_EXEC_DONE;
+       *isNull = true;
+       /*
+        * If this isn't the last command for the function
+        * we have to increment the command
+        * counter so that subsequent commands can see changes made
+        * by previous ones.
+        */
+       if (!LAST_POSTQUEL_COMMAND(es)) CommandCounterIncrement();
+       return (Datum)NULL;
+    }
+    
+    if (LAST_POSTQUEL_COMMAND(es)) {
+       TupleTableSlot *resSlot;
+       
+       /*
+        * Copy the result.  copy_function_result is smart enough
+        * to do nothing when no action is called for.  This helps
+        * reduce the logic and code redundancy here.
+        */
+       resSlot = copy_function_result(fcache, slot);
+       if (fTlist != NIL) {
+           HeapTuple tup;
+           TargetEntry *tle = lfirst(fTlist);
+           
+           tup = resSlot->val;
+           value = ProjectAttribute(resSlot->ttc_tupleDescriptor,
+                                    tle,
+                                    tup,
+                                    isNull);
+       }else {
+           value = (Datum)resSlot;
+           *isNull = false;
+       }
+       
+       /*
+        * If this is a single valued function we have to end the
+        * function execution now.
+        */
+       if (fcache->oneResult) {
+           postquel_end(es);
+           es->status = F_EXEC_DONE;
+       }
+       
+       return value;
+    }
+    /*
+     * If this isn't the last command for the function, we don't
+     * return any results, but we have to increment the command
+     * counter so that subsequent commands can see changes made
+     * by previous ones.
+     */
+    CommandCounterIncrement();
+    return (Datum)NULL;
+}
+
+Datum
+postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone)
+{
+    execution_state  *es;
+    Datum            result;
+    FunctionCachePtr fcache = funcNode->func_fcache;
+    
+    es = (execution_state *) fcache->func_state;
+    if (es == NULL)
+       {
+           es = init_execution_state(fcache, args);
+           fcache->func_state = (char *) es;
+       }
+    
+    while (es && es->status == F_EXEC_DONE)
+       es = es->next;
+    
+    Assert(es);
+    /*
+     * Execute each command in the function one after another until we're
+     * executing the final command and get a result or we run out of
+     * commands.
+     */
+    while (es != (execution_state *)NULL)
+       {
+           result = postquel_execute(es,
+                                     fcache,
+                                     funcNode->func_tlist,
+                                     args,
+                                     isNull);
+           if (es->status != F_EXEC_DONE)
+               break;
+           es = es->next;
+       }
+    
+    /*
+     * If we've gone through every command in this function, we are done.
+     */
+    if (es == (execution_state *)NULL)
+       {
+           /*
+            * Reset the execution states to start over again
+            */
+           es = (execution_state *)fcache->func_state;
+           while (es)
+               {
+                   es->status = F_EXEC_START;
+                   es = es->next;
+               }
+           /*
+            * Let caller know we're finished.
+            */
+           *isDone = true;
+           return (fcache->oneResult) ? result : (Datum)NULL;
+       }
+    /*
+     * If we got a result from a command within the function it has
+     * to be the final command.  All others shouldn't be returing
+     * anything.
+     */
+    Assert ( LAST_POSTQUEL_COMMAND(es) );
+    *isDone = false;
+    
+    return result;
+}
diff --git a/src/backend/executor/functions.h b/src/backend/executor/functions.h
new file mode 100644 (file)
index 0000000..4188cbb
--- /dev/null
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * functions.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        FUNCTIONS_H
+#define FUNCTIONS_H
+
+extern Datum ProjectAttribute(TupleDesc TD, TargetEntry *tlist,
+                             HeapTuple tup, bool *isnullP);
+
+extern Datum postquel_function(Func *funcNode, char **args,
+                              bool *isNull, bool *isDone);
+
+#endif /* FUNCTIONS_H */
diff --git a/src/backend/executor/hashjoin.h b/src/backend/executor/hashjoin.h
new file mode 100644 (file)
index 0000000..e588f51
--- /dev/null
@@ -0,0 +1,82 @@
+/*-------------------------------------------------------------------------
+ *
+ * hashjoin.h--
+ *    internal structures for hash table and buckets
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        HASHJOIN_H
+#define HASHJOIN_H
+
+#include "access/htup.h"
+#include "storage/ipc.h"
+
+/* -----------------
+ *  have to use relative address as pointers in the hashtable
+ *  because the hashtable may reallocate in difference processes
+ * -----------------
+ */
+typedef int    RelativeAddr;
+
+/* ------------------
+ *  the relative addresses are always relative to the head of the
+ *  hashtable, the following macro converts them to absolute address.
+ * ------------------
+ */
+#define ABSADDR(X)     ((X) < 0 ? NULL: (char*)hashtable + X)
+#define RELADDR(X)     (RelativeAddr)((char*)(X) - (char*)hashtable)
+
+typedef char    **charPP;
+typedef int     *intP;
+
+/* ----------------------------------------------------------------
+ *             hash-join hash table structures
+ * ----------------------------------------------------------------
+ */
+typedef struct HashTableData {
+    int                    nbuckets;
+    int                    totalbuckets;
+    int                    bucketsize;
+    IpcMemoryId            shmid;
+    RelativeAddr    top;               /* char* */
+    RelativeAddr    bottom;            /* char* */
+    RelativeAddr    overflownext;      /* char* */
+    RelativeAddr    batch;             /* char* */
+    RelativeAddr    readbuf;           /* char* */
+    int             nbatch;
+    RelativeAddr    outerbatchNames;   /* RelativeAddr* */
+    RelativeAddr    outerbatchPos;     /* RelativeAddr* */
+    RelativeAddr    innerbatchNames;   /* RelativeAddr* */
+    RelativeAddr    innerbatchPos;     /* RelativeAddr* */
+    RelativeAddr    innerbatchSizes;   /* int* */
+    int             curbatch;
+    int                    nprocess;
+    int                    pcount;
+} HashTableData;               /* real hash table follows here */
+
+typedef HashTableData  *HashJoinTable;
+
+typedef struct OverflowTupleData {
+    RelativeAddr tuple;                /* HeapTuple */
+    RelativeAddr next;         /* struct OverflowTupleData * */
+} OverflowTupleData;           /* real tuple follows here */
+
+typedef OverflowTupleData *OverflowTuple;
+
+typedef struct HashBucketData {
+    RelativeAddr  top;         /* HeapTuple */
+    RelativeAddr  bottom;      /* HeapTuple */
+    RelativeAddr  firstotuple;         /* OverflowTuple */
+    RelativeAddr  lastotuple;  /* OverflowTuple */
+} HashBucketData;              /* real bucket follows here */
+
+typedef HashBucketData *HashBucket;
+
+#define HASH_PERMISSION         0700
+
+#endif /* HASHJOIN_H */
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
new file mode 100644 (file)
index 0000000..ee18736
--- /dev/null
@@ -0,0 +1,558 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeAgg.c--
+ *    Routines to handle aggregate nodes. 
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * NOTE
+ *    The implementation of Agg node has been reworked to handle legal
+ *    SQL aggregates. (Do not expect POSTQUEL semantics.)    -- ay 2/95
+ *
+ * IDENTIFICATION
+ *    /usr/local/devel/pglite/cvs/src/backend/executor/nodeAgg.c,v 1.13 1995/08/01 20:19:07 jolly Exp
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "access/heapam.h"
+#include "catalog/pg_aggregate.h"
+#include "catalog/catalog.h"
+#include "executor/executor.h"
+#include "executor/nodeAgg.h"
+#include "storage/bufmgr.h"
+#include "utils/palloc.h"
+#include "parser/catalog_utils.h"
+
+/*
+ * AggFuncInfo -
+ *    keeps the transition functions information around
+ */
+typedef struct AggFuncInfo {
+    Oid                xfn1_oid;
+    Oid                xfn2_oid;
+    Oid                finalfn_oid;
+    func_ptr   xfn1;
+    func_ptr   xfn2;
+    func_ptr   finalfn;
+    int                xfn1_nargs;
+    int                xfn2_nargs;
+    int                finalfn_nargs;
+} AggFuncInfo;
+
+static Datum aggGetAttr(TupleTableSlot *tuple, Aggreg *agg, bool *isNull);
+
+
+/* ---------------------------------------
+ *
+ * ExecAgg -
+ *
+ *    ExecAgg receives tuples from its outer subplan and aggregates over
+ *    the appropriate attribute for each (unique) aggregate in the target
+ *    list. (The number of tuples to aggregate over depends on whether a
+ *    GROUP BY clause is present. It might be the number of tuples in a
+ *    group or all the tuples that satisfy the qualifications.) The value of
+ *    each aggregate is stored in the expression context for ExecProject to
+ *    evaluate the result tuple.
+ *    
+ *    ExecAgg evaluates each aggregate in the following steps: (initcond1,
+ *    initcond2 are the initial values and sfunc1, sfunc2, and finalfunc are
+ *    the transition functions.)
+ *
+ *       value1[i] = initcond1
+ *       value2[i] = initcond2
+ *       forall tuples do 
+ *         value1[i] = sfunc1(aggregate_attribute, value1[i])
+ *         value2[i] = sfunc2(value2[i])
+ *      value1[i] = finalfunc(value1[i], value2[i])
+ *
+ *    If the outer subplan is a Group node, ExecAgg returns as many tuples
+ *    as there are groups.
+ *
+ *    XXX handling of NULL doesn't work
+ *
+ *    OLD COMMENTS
+ *
+ *     XXX Aggregates should probably have another option: what to do 
+ *     with transfn2 if we hit a null value.  "count" (transfn1 = null,
+ *     transfn2 = increment) will want to have transfn2 called; "avg"
+ *     (transfn1 = add, transfn2 = increment) will not. -pma 1/3/93
+ *
+ * ------------------------------------------
+ */
+TupleTableSlot *
+ExecAgg(Agg *node)
+{
+    AggState           *aggstate;
+    EState             *estate;
+    Aggreg             **aggregates;
+    Plan               *outerPlan;
+    int                        i, nagg;
+    Datum              *value1, *value2;
+    int                        *noInitValue;
+    AggFuncInfo                *aggFuncInfo;
+    long               nTuplesAgged = 0;
+    ExprContext                *econtext;
+    ProjectionInfo     *projInfo;
+    TupleTableSlot     *resultSlot;
+    HeapTuple          oneTuple;
+    char*               nulls;
+    bool               isDone;
+    bool               isNull = FALSE, isNull1 = FALSE, isNull2 = FALSE;
+
+    /* ---------------------
+     *  get state info from node
+     * ---------------------
+     */
+    aggstate = node->aggstate;
+    if (aggstate->agg_done)
+       return NULL;
+
+    estate = node->plan.state;
+    econtext = aggstate->csstate.cstate.cs_ExprContext;
+    aggregates = node->aggs;
+    nagg = node->numAgg;
+
+    value1 = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_values;
+    nulls = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_nulls;
+
+    value2 = (Datum *)palloc(sizeof(Datum) * nagg);
+    memset(value2, 0, sizeof(Datum) * nagg);
+
+    aggFuncInfo = (AggFuncInfo *)palloc(sizeof(AggFuncInfo) * nagg);
+    memset(aggFuncInfo, 0, sizeof(AggFuncInfo) * nagg);
+
+    noInitValue = (int *)palloc(sizeof(int) * nagg);
+    memset(noInitValue, 0, sizeof(noInitValue) * nagg);
+    
+    outerPlan = outerPlan(node);
+    oneTuple = NULL;
+    
+    projInfo = aggstate->csstate.cstate.cs_ProjInfo;
+
+    for(i = 0; i < nagg; i++) {
+       Aggreg          *agg;
+       char            *aggname;
+       HeapTuple       aggTuple;
+       Form_pg_aggregate  aggp;
+       Oid             xfn1_oid, xfn2_oid, finalfn_oid;
+       func_ptr        xfn1_ptr, xfn2_ptr, finalfn_ptr;
+       int             xfn1_nargs, xfn2_nargs, finalfn_nargs;
+
+       agg = aggregates[i];
+
+       /* ---------------------
+        *  find transfer functions of all the aggregates and initialize
+        *  their initial values
+        * ---------------------
+        */
+       aggname = agg->aggname;
+       aggTuple = SearchSysCacheTuple(AGGNAME, 
+                                      PointerGetDatum(aggname),
+                                      ObjectIdGetDatum(agg->basetype),
+                                      0,0);
+       if (!HeapTupleIsValid(aggTuple))
+           elog(WARN, "ExecAgg: cache lookup failed for aggregate \"%s\"(%s)",
+                aggname,
+                tname(get_id_type(agg->basetype)));
+       aggp = (Form_pg_aggregate) GETSTRUCT(aggTuple);
+
+       xfn1_oid = aggp->aggtransfn1;
+       xfn2_oid = aggp->aggtransfn2;
+       finalfn_oid = aggp->aggfinalfn;
+    
+       if (OidIsValid(finalfn_oid)) {
+           fmgr_info(finalfn_oid, &finalfn_ptr, &finalfn_nargs);
+           aggFuncInfo[i].finalfn_oid = finalfn_oid;
+           aggFuncInfo[i].finalfn = finalfn_ptr;
+           aggFuncInfo[i].finalfn_nargs = finalfn_nargs;
+       }
+
+       if (OidIsValid(xfn2_oid)) {
+           fmgr_info(xfn2_oid, &xfn2_ptr, &xfn2_nargs);
+           aggFuncInfo[i].xfn2_oid = xfn2_oid;
+           aggFuncInfo[i].xfn2 = xfn2_ptr;
+           aggFuncInfo[i].xfn2_nargs = xfn2_nargs;
+           value2[i] = (Datum)AggNameGetInitVal((char*)aggname,
+                                                aggp->aggbasetype,
+                                                2,
+                                                &isNull2);
+           /* ------------------------------------------
+            * If there is a second transition function, its initial
+            * value must exist -- as it does not depend on data values,
+            * we have no other way of determining an initial value.
+            * ------------------------------------------
+            */
+           if (isNull2)
+               elog(WARN, "ExecAgg: agginitval2 is null");
+       }
+
+       if (OidIsValid(xfn1_oid)) {
+           fmgr_info(xfn1_oid, &xfn1_ptr, &xfn1_nargs);
+           aggFuncInfo[i].xfn1_oid = xfn1_oid;
+           aggFuncInfo[i].xfn1 = xfn1_ptr;
+           aggFuncInfo[i].xfn1_nargs = xfn1_nargs;
+           value1[i] = (Datum)AggNameGetInitVal((char*)aggname,
+                                                aggp->aggbasetype,
+                                                1,
+                                                &isNull1);
+
+           /* ------------------------------------------
+            * If the initial value for the first transition function
+            * doesn't exist in the pg_aggregate table then we let
+            * the first value returned from the outer procNode become
+            * the initial value. (This is useful for aggregates like
+            * max{} and min{}.)
+            * ------------------------------------------
+            */
+           if (isNull1) {
+               noInitValue[i] = 1;
+               nulls[i] = 1;
+           }
+       }
+    }
+
+    /* ----------------
+     *   for each tuple from the the outer plan, apply all the aggregates
+     * ----------------
+     */
+    for (;;) {
+       HeapTuple outerTuple = NULL;
+       TupleTableSlot *outerslot;
+       
+       isNull = isNull1 = isNull2 = 0;
+       outerslot = ExecProcNode(outerPlan, (Plan*)node);
+       if (outerslot) outerTuple = outerslot->val;
+       if (!HeapTupleIsValid(outerTuple)) {
+           /* when the outerplan doesn't return a single tuple,
+              create a dummy heaptuple anyway
+              because we still need to return a valid aggregate value.
+              The value returned will be the initial values of the
+              transition functions */
+           if (nTuplesAgged == 0) {
+               TupleDesc tupType;
+               Datum *tupValue;
+               char* null_array;
+
+               tupType = aggstate->csstate.css_ScanTupleSlot->ttc_tupleDescriptor;
+               tupValue =      projInfo->pi_tupValue;
+               
+               /* initially, set all the values to NULL */
+               null_array = malloc(nagg);
+               for (i=0;i<nagg;i++)
+                   null_array[i] = 'n';
+               oneTuple = heap_formtuple(tupType, tupValue, null_array);
+               free(null_array);
+           }
+           break;
+       }
+
+       for(i = 0; i < nagg; i++) {
+           AttrNumber attnum;
+           int2 attlen;
+           Datum newVal;
+           AggFuncInfo *aggfns = &aggFuncInfo[i];
+           Datum args[2];
+
+           newVal = aggGetAttr(outerslot,
+                               aggregates[i],
+                               &isNull);
+
+           if (isNull)
+               continue;       /* ignore this tuple for this agg */
+
+           if (aggfns->xfn1) {
+               if (noInitValue[i]) {
+                   /*
+                    * value1 and value2 has not been initialized. This
+                    * is the first non-NULL value. We use it as the
+                    * initial value.
+                    */
+                    /* but we can't just use it straight, we have
+                       to make a copy of it since the tuple from which
+                       it came will be freed on the next iteration 
+                       of the scan */
+                   attnum = ((Var*)aggregates[i]->target)->varattno;
+                   attlen = outerslot->ttc_tupleDescriptor->attrs[attnum-1]->attlen;
+                   if (attlen == -1)  {
+                       /* variable length */
+                       attlen = VARSIZE((struct varlena*) newVal);
+                   }
+                   value1[i] = (Datum)palloc(attlen);
+                 if (outerslot->ttc_tupleDescriptor->attrs[attnum-1]->attbyval)
+                        value1[i] = newVal;
+                    else
+                        memmove((char*) (value1[i]), (char*) (newVal), attlen);
+                   /* value1[i] = newVal; */
+                   noInitValue[i] = 0;
+                   nulls[i] = 0;
+               } else {
+                   /*
+                    * apply the transition functions.
+                    */
+                   args[0] = value1[i];
+                   args[1] = newVal;
+                   value1[i] =
+                       (Datum)fmgr_c(aggfns->xfn1, aggfns->xfn1_oid,
+                                     aggfns->xfn1_nargs, (FmgrValues *)args,
+                                     &isNull1);
+                   Assert(!isNull1);
+               }
+           }
+
+           if (aggfns->xfn2) {
+               Datum xfn2_val = value2[i];
+               
+               value2[i] =
+                   (Datum)fmgr_c(aggfns->xfn2, aggfns->xfn2_oid,
+                                 aggfns->xfn2_nargs,
+                                 (FmgrValues *)&xfn2_val, &isNull2);
+               Assert(!isNull2);
+           }
+       }
+
+       /*
+        * keep this for the projection (we only need one of these -
+        * all the tuples we aggregate over share the same group column)
+        */
+       if (!oneTuple) {
+           oneTuple = heap_copytuple(outerslot->val);
+       }
+
+       nTuplesAgged++;
+    }
+
+    /* --------------
+     * finalize the aggregate (if necessary), and get the resultant value
+     * --------------
+     */
+    for(i = 0; i < nagg; i++) {
+       char *args[2];
+       AggFuncInfo *aggfns = &aggFuncInfo[i];
+       
+       if (aggfns->finalfn && nTuplesAgged > 0) { 
+           if (aggfns->finalfn_nargs > 1) {
+               args[0] = (char*)value1[i];
+               args[1] = (char*)value2[i];
+           } else if (aggfns->xfn1) {
+               args[0] = (char*)value1[i];
+           } else if (aggfns->xfn2) {
+               args[0] = (char*)value2[i];
+           } else
+               elog(WARN, "ExecAgg: no valid transition functions??");
+           value1[i] =
+               (Datum)fmgr_c(aggfns->finalfn, aggfns->finalfn_oid,
+                             aggfns->finalfn_nargs, (FmgrValues *) args,
+                             &(nulls[i]));
+       } else if (aggfns->xfn1) {
+           /*
+            * value in the right place, ignore. (If you remove this
+            * case, fix the else part. -ay 2/95)
+            */
+       } else if (aggfns->xfn2) {
+           value1[i] = value2[i];
+       } else
+           elog(WARN, "ExecAgg: no valid transition functions??");
+    }
+
+    /*
+     * whether the aggregation is done depends on whether we are doing
+     * aggregation over groups or the entire table
+     */
+    if (nodeTag(outerPlan)==T_Group) {
+       /* aggregation over groups */
+       aggstate->agg_done = ((Group*)outerPlan)->grpstate->grp_done;
+    } else {
+       aggstate->agg_done = TRUE;
+    }
+
+    /* ----------------
+     * form a projection tuple, store it in the result tuple
+     *  slot and return it.
+     * ----------------
+     */
+    ExecStoreTuple(oneTuple,
+                  aggstate->csstate.css_ScanTupleSlot,
+                  InvalidBuffer,
+                  false);
+    econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot;
+    resultSlot = ExecProject(projInfo, &isDone);
+
+    if (oneTuple)
+       pfree(oneTuple);
+    
+    return resultSlot;
+}
+
+/* -----------------
+ * ExecInitAgg
+ *
+ *  Creates the run-time information for the agg node produced by the
+ *  planner and initializes its outer subtree
+ * -----------------
+ */
+bool
+ExecInitAgg(Agg *node, EState *estate, Plan *parent)
+{
+    AggState           *aggstate;
+    Plan               *outerPlan;
+    ExprContext                *econtext;
+    
+    /* 
+     * assign the node's execution state
+     */
+    node->plan.state = estate;
+    
+    /*
+     * create state structure
+     */
+    aggstate = makeNode(AggState);
+    node->aggstate = aggstate;
+    aggstate->agg_done = FALSE;
+
+    /*
+     * assign node's base id and create expression context
+     */
+    ExecAssignNodeBaseInfo(estate, &aggstate->csstate.cstate,
+                          (Plan*) parent);
+    ExecAssignExprContext(estate, &aggstate->csstate.cstate);
+    
+#define AGG_NSLOTS 2
+    /*
+     * tuple table initialization
+     */
+    ExecInitScanTupleSlot(estate, &aggstate->csstate);
+    ExecInitResultTupleSlot(estate, &aggstate->csstate.cstate);
+
+    econtext = aggstate->csstate.cstate.cs_ExprContext;
+    econtext->ecxt_values =
+       (Datum *)palloc(sizeof(Datum) * node->numAgg);
+    memset(econtext->ecxt_values, 0, sizeof(Datum) * node->numAgg);
+    econtext->ecxt_nulls = (char *)palloc(node->numAgg);
+    memset(econtext->ecxt_nulls, 0, node->numAgg);
+
+    /*
+     * initializes child nodes
+     */
+    outerPlan = outerPlan(node);
+    ExecInitNode(outerPlan, estate, (Plan *)node);
+    
+    /* ----------------
+     *         initialize tuple type.
+     * ----------------
+     */
+    ExecAssignScanTypeFromOuterPlan((Plan *) node, &aggstate->csstate); 
+
+    /*
+     * Initialize tuple type for both result and scan.
+     * This node does no projection
+     */
+    ExecAssignResultTypeFromTL((Plan*) node, &aggstate->csstate.cstate);
+    ExecAssignProjectionInfo((Plan*)node, &aggstate->csstate.cstate);
+
+    return TRUE;
+}
+
+int
+ExecCountSlotsAgg(Agg *node)
+{
+    return ExecCountSlotsNode(outerPlan(node)) +
+       ExecCountSlotsNode(innerPlan(node)) +
+           AGG_NSLOTS;
+}
+
+/* ------------------------
+ *     ExecEndAgg(node)
+ *
+ * -----------------------
+ */
+void
+ExecEndAgg(Agg *node)
+{
+    AggState   *aggstate;
+    Plan       *outerPlan;
+
+    aggstate = node->aggstate;
+
+    ExecFreeProjectionInfo(&aggstate->csstate.cstate);
+
+    outerPlan = outerPlan(node);
+    ExecEndNode(outerPlan, (Plan*)node);
+    
+    /* clean up tuple table */
+    ExecClearTuple(aggstate->csstate.css_ScanTupleSlot);
+}
+
+
+/*****************************************************************************
+ *  Support Routines
+ *****************************************************************************/
+
+/*
+ * aggGetAttr -
+ *    get the attribute (specified in the Var node in agg) to aggregate 
+ *    over from the tuple
+ */
+static Datum
+aggGetAttr(TupleTableSlot *slot,
+          Aggreg *agg,
+          bool *isNull)
+{
+    Datum      result;
+    AttrNumber         attnum;
+    HeapTuple  heapTuple;
+    TupleDesc  tuple_type;
+    Buffer     buffer;
+
+    /* ----------------
+     *   extract tuple information from the slot
+     * ----------------
+     */
+    heapTuple =  slot->val;
+    tuple_type = slot->ttc_tupleDescriptor;
+    buffer =    slot->ttc_buffer;
+    
+    attnum =   ((Var*)agg->target)->varattno;
+    
+    /*
+     * If the attribute number is invalid, then we are supposed to
+     * return the entire tuple, we give back a whole slot so that
+     * callers know what the tuple looks like.
+     */
+    if (attnum == InvalidAttrNumber) {
+       TupleTableSlot  *tempSlot;
+       TupleDesc td;
+       HeapTuple tup;
+       
+       tempSlot = makeNode(TupleTableSlot);
+       tempSlot->ttc_shouldFree = false;
+       tempSlot->ttc_descIsNew = true;
+       tempSlot->ttc_tupleDescriptor = (TupleDesc)NULL,
+       tempSlot->ttc_buffer = InvalidBuffer;
+       tempSlot->ttc_whichplan = -1;
+       
+       tup = heap_copytuple(slot->val);
+       td = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
+       
+       ExecSetSlotDescriptor(tempSlot, td);
+       
+       ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
+       return (Datum) tempSlot;
+    }
+    
+    result =  (Datum)
+      heap_getattr(heapTuple,  /* tuple containing attribute */
+                  buffer,     /* buffer associated with tuple */
+                  attnum,     /* attribute number of desired attribute */
+                  tuple_type, /* tuple descriptor of tuple */
+                  isNull);    /* return: is attribute null? */
+    
+    /* ----------------
+     * return null if att is null
+     * ----------------
+     */
+    if (*isNull)
+       return (Datum) NULL;
+    
+    return result;
+}
diff --git a/src/backend/executor/nodeAgg.h b/src/backend/executor/nodeAgg.h
new file mode 100644 (file)
index 0000000..8efbfb7
--- /dev/null
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeAgg.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        NODEAGG_H
+#define        NODEAGG_H
+
+extern TupleTableSlot *ExecAgg(Agg *node);
+extern bool ExecInitAgg(Agg *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsAgg(Agg *node);
+extern void ExecEndAgg(Agg *node);
+
+#endif /* NODEAGG_H */
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
new file mode 100644 (file)
index 0000000..7a79e13
--- /dev/null
@@ -0,0 +1,483 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeAppend.c--
+ *    routines to handle append nodes.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/* INTERFACE ROUTINES
+ *      ExecInitAppend  - initialize the append node
+ *      ExecProcAppend  - retrieve the next tuple from the node
+ *      ExecEndAppend   - shut down the append node
+ *
+ *   NOTES
+ *      Each append node contains a list of one or more subplans which
+ *      must be iteratively processed (forwards or backwards).
+ *      Tuples are retrieved by executing the 'whichplan'th subplan
+ *      until the subplan stops returning tuples, at which point that
+ *      plan is shut down and the next started up.
+ *
+ *      Append nodes don't make use of their left and right
+ *      subtrees, rather they maintain a list of subplans so
+ *      a typical append node looks like this in the plan tree:
+ *
+ *                 ...
+ *                 /
+ *              Append -------+------+------+--- nil
+ *              /   \         |      |      |
+ *            nil   nil      ...    ...    ...
+ *                               subplans
+ *
+ *      Append nodes are currently used to support inheritance
+ *      queries, where several relations need to be scanned.
+ *      For example, in our standard person/student/employee/student-emp
+ *      example, where student and employee inherit from person
+ *      and student-emp inherits from student and employee, the
+ *      query:
+ *
+ *              retrieve (e.name) from e in person*
+ *
+ *      generates the plan:
+ *
+ *                |
+ *              Append -------+-------+--------+--------+
+ *              /   \         |       |        |        |
+ *            nil   nil      Scan    Scan     Scan     Scan
+ *                            |       |        |        |
+ *                          person employee student student-emp
+ */
+
+#include "executor/executor.h"
+#include "executor/nodeAppend.h"
+#include "executor/nodeIndexscan.h"
+#include "utils/palloc.h"
+#include "parser/parsetree.h"          /* for rt_store() macro */
+
+/* ----------------------------------------------------------------
+ *      exec-append-initialize-next
+ *    
+ *      Sets up the append node state (i.e. the append state node)
+ *      for the "next" scan.
+ *    
+ *      Returns t iff there is a "next" scan to process.
+ * ----------------------------------------------------------------
+ */
+bool
+exec_append_initialize_next(Append *node)
+{
+    EState         *estate;
+    AppendState    *unionstate;
+    TupleTableSlot *result_slot;
+    List           *rangeTable;
+    
+    int            whichplan;
+    int            nplans;
+    List           *rtentries;
+    ResTarget      *rtentry;
+    
+    Index          unionrelid;
+    
+    /* ----------------
+     *  get information from the append node
+     * ----------------
+     */
+    estate = node->plan.state;
+    unionstate = node->unionstate;
+    result_slot = unionstate->cstate.cs_ResultTupleSlot;
+    rangeTable = estate->es_range_table;
+    
+    whichplan = unionstate->as_whichplan;
+    nplans =    unionstate->as_nplans;
+    rtentries = node->unionrtentries;
+    
+    if (whichplan < 0) {
+        /* ----------------
+         *      if scanning in reverse, we start at
+         *      the last scan in the list and then
+         *      proceed back to the first.. in any case
+         *      we inform ExecProcAppend that we are
+         *      at the end of the line by returning FALSE
+         * ----------------
+         */
+        unionstate->as_whichplan = 0;
+        return FALSE;
+       
+    } else if (whichplan >= nplans) {
+        /* ----------------
+         *      as above, end the scan if we go beyond
+         *      the last scan in our list..
+         * ----------------
+         */
+        unionstate->as_whichplan = nplans - 1;
+        return FALSE;
+        
+    } else {
+        /* ----------------
+         *      initialize the scan
+         *      (and update the range table appropriately)
+         *        (doesn't this leave the range table hosed for anybody upstream
+        *         of the Append node??? - jolly )
+         * ----------------
+         */
+       if (node->unionrelid > 0) {
+           rtentry = nth(whichplan, rtentries);
+           if (rtentry == NULL)
+               elog(DEBUG, "exec_append_initialize_next: rtentry is nil");
+           
+           unionrelid = node->unionrelid;
+
+           rt_store(unionrelid, rangeTable, rtentry);
+
+           if (unionstate->as_junkFilter_list) {
+               estate->es_junkFilter =
+                 (JunkFilter*)nth(whichplan, 
+                                  unionstate->as_junkFilter_list);
+           }
+           if (unionstate->as_result_relation_info_list) {
+               estate->es_result_relation_info =
+                 (RelationInfo*) nth(whichplan, 
+                                     unionstate->as_result_relation_info_list);
+           }
+           result_slot->ttc_whichplan = whichplan;
+       }
+       
+       return TRUE;
+    }
+}
+
+/* ----------------------------------------------------------------
+ *      ExecInitAppend
+ *    
+ *      Begins all of the subscans of the append node, storing the
+ *      scan structures in the 'initialized' vector of the append-state
+ *      structure.
+ *
+ *     (This is potentially wasteful, since the entire result of the
+ *      append node may not be scanned, but this way all of the
+ *      structures get allocated in the executor's top level memory
+ *      block instead of that of the call to ExecProcAppend.)
+ *    
+ *      Returns the scan result of the first scan.
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitAppend(Append *node, EState *estate, Plan *parent)
+{
+    AppendState *unionstate;
+    int         nplans;
+    List        *resultList;
+    List        *rtentries;
+    List        *unionplans;
+    bool        *initialized;
+    int         i;
+    Plan        *initNode;
+    List        *junkList;
+    RelationInfo *es_rri = estate->es_result_relation_info;
+    
+    /* ----------------
+     *  assign execution state to node and get information
+     *  for append state
+     * ----------------
+     */
+    node->plan.state = estate;
+    
+    unionplans = node->unionplans;
+    nplans = length(unionplans);
+    rtentries = node->unionrtentries;
+    
+    CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext);
+    initialized = (bool *)palloc(nplans * sizeof(bool));
+    
+    /* ----------------
+     *  create new AppendState for our append node
+     * ----------------
+     */   
+    unionstate = makeNode(AppendState);
+    unionstate->as_whichplan = 0;
+    unionstate->as_nplans = nplans;
+    unionstate->as_initialized = initialized;
+    unionstate->as_rtentries = rtentries;
+
+    node->unionstate = unionstate;
+    
+    /* ----------------
+     *  Miscellanious initialization
+     *
+     *      +  assign node's base_id
+     *       + assign debugging hooks
+     *
+     *  Append plans don't have expression contexts because they
+     *  never call ExecQual or ExecTargetList.
+     * ----------------
+     */
+    ExecAssignNodeBaseInfo(estate, &unionstate->cstate, parent);
+    
+#define APPEND_NSLOTS 1
+    /* ----------------
+     * append nodes still have Result slots, which hold pointers
+     *  to tuples, so we have to initialize them..
+     * ----------------
+     */
+    ExecInitResultTupleSlot(estate, &unionstate->cstate);
+    
+    /*
+     * If the inherits rtentry is the result relation, we have to make
+     * a result relation info list for all inheritors so we can update
+     * their indices and put the result tuples in the right place etc.
+     *
+     * e.g. replace p (age = p.age + 1) from p in person*
+     */
+    if ((es_rri != (RelationInfo*)NULL) &&
+       (node->unionrelid == es_rri->ri_RangeTableIndex))
+       {
+           RelationInfo *rri;
+           List         *rtentryP;
+           
+           foreach(rtentryP,rtentries)
+               {
+                   Oid reloid;
+                   RangeTblEntry *rtentry = lfirst(rtentryP);
+                   
+                   reloid = rtentry->relid;
+                   rri = makeNode(RelationInfo); 
+                   rri->ri_RangeTableIndex = es_rri->ri_RangeTableIndex;
+                   rri->ri_RelationDesc = heap_open(reloid);
+                   rri->ri_NumIndices = 0;
+                   rri->ri_IndexRelationDescs = NULL; /* index descs */
+                   rri->ri_IndexRelationInfo = NULL;  /* index key info */
+
+                   resultList = lcons(rri,resultList);
+                   ExecOpenIndices(reloid, rri);
+               }
+           unionstate->as_result_relation_info_list = resultList;
+       }
+    /* ----------------
+     *  call ExecInitNode on each of the plans in our list
+     *  and save the results into the array "initialized"
+     * ----------------
+     */       
+    junkList = NIL;
+
+    for(i = 0; i < nplans ; i++ ) {
+       JunkFilter *j;
+       List       *targetList;
+        /* ----------------
+         *  NOTE: we first modify range table in 
+         *        exec_append_initialize_next() and
+         *        then initialize the subnode,
+         *        since it may use the range table.
+         * ----------------
+         */
+        unionstate->as_whichplan = i;
+        exec_append_initialize_next(node);
+
+        initNode = (Plan *) nth(i, unionplans);
+        initialized[i] =  ExecInitNode(initNode, estate, (Plan*) node);
+       
+       /* ---------------
+        *  Each targetlist in the subplan may need its own junk filter
+        *
+        *  This is true only when the reln being replaced/deleted is
+        *  the one that we're looking at the subclasses of
+        * ---------------
+        */
+       if ((es_rri != (RelationInfo*)NULL) &&
+           (node->unionrelid == es_rri->ri_RangeTableIndex)) {
+           
+           targetList = initNode->targetlist;
+           j = (JunkFilter *) ExecInitJunkFilter(targetList);
+           junkList = lappend(junkList, j);
+       }
+       
+    }
+    unionstate->as_junkFilter_list = junkList;
+    if (junkList != NIL)
+       estate->es_junkFilter = (JunkFilter *)lfirst(junkList);
+    
+    /* ----------------
+     * initialize the return type from the appropriate subplan.
+     * ----------------
+     */
+    initNode = (Plan *) nth(0, unionplans);
+    ExecAssignResultType(&unionstate->cstate,
+/*                      ExecGetExecTupDesc(initNode), */
+                        ExecGetTupType(initNode));
+    unionstate->cstate.cs_ProjInfo = NULL;
+    
+    /* ----------------
+     *  return the result from the first subplan's initialization
+     * ----------------
+     */       
+    unionstate->as_whichplan = 0;
+    exec_append_initialize_next(node);
+#if 0
+    result = (List *) initialized[0];
+#endif    
+    return TRUE;
+}
+
+int
+ExecCountSlotsAppend(Append *node)
+{
+    List *plan;
+    List *unionplans = node->unionplans;
+    int  nSlots     = 0;
+    
+    foreach (plan,unionplans) {
+       nSlots += ExecCountSlotsNode((Plan *)lfirst(plan));
+    }
+    return nSlots + APPEND_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecProcAppend
+ *    
+ *      Handles the iteration over the multiple scans.
+ *    
+ *     NOTE: Can't call this ExecAppend, that name is used in execMain.l
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecProcAppend(Append *node)
+{
+    EState              *estate;
+    AppendState         *unionstate;
+    
+    int                 whichplan;
+    List                *unionplans;
+    Plan                *subnode;
+    TupleTableSlot      *result;
+    TupleTableSlot      *result_slot;
+    ScanDirection       direction;
+    
+    /* ----------------
+     *  get information from the node
+     * ----------------
+     */
+    unionstate =      node->unionstate;
+    estate =         node->plan.state;
+    direction =       estate->es_direction;
+    
+    unionplans =      node->unionplans;
+    whichplan =       unionstate->as_whichplan;
+    result_slot =     unionstate->cstate.cs_ResultTupleSlot;
+    
+    /* ----------------
+     *  figure out which subplan we are currently processing
+     * ----------------
+     */
+    subnode = (Plan *) nth(whichplan, unionplans);
+    
+    if (subnode == NULL)
+        elog(DEBUG, "ExecProcAppend: subnode is NULL");
+    
+    /* ----------------
+     *  get a tuple from the subplan
+     * ----------------
+     */
+    result = ExecProcNode(subnode, (Plan*)node);
+    
+    if (! TupIsNull(result)) {
+        /* ----------------
+         *  if the subplan gave us something then place a copy of
+        *  whatever we get into our result slot and return it, else..
+         * ----------------
+         */
+       return ExecStoreTuple(result->val,
+                             result_slot, result->ttc_buffer, false);
+       
+    } else {
+        /* ----------------
+         *  .. go on to the "next" subplan in the appropriate
+         *  direction and try processing again (recursively)
+         * ----------------
+         */
+        whichplan = unionstate->as_whichplan;
+        
+        if (ScanDirectionIsForward(direction))
+           {
+               unionstate->as_whichplan = whichplan + 1;
+           }
+        else
+           {
+               unionstate->as_whichplan = whichplan - 1;
+           }
+       
+       /* ----------------
+        *  return something from next node or an empty slot
+        *  all of our subplans have been exhausted.
+        * ----------------
+        */
+        if (exec_append_initialize_next(node)) {
+           ExecSetSlotDescriptorIsNew(result_slot, true);
+            return
+               ExecProcAppend(node);
+        } else
+           return ExecClearTuple(result_slot);
+    }
+}
+
+/* ----------------------------------------------------------------
+ *      ExecEndAppend
+ *    
+ *      Shuts down the subscans of the append node.
+ *    
+ *      Returns nothing of interest.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndAppend(Append *node)
+{
+    AppendState *unionstate;
+    int         nplans;
+    List        *unionplans;
+    bool       *initialized;
+    int         i;
+    List        *resultRelationInfoList;
+    RelationInfo    *resultRelationInfo;
+
+    /* ----------------
+     *  get information from the node
+     * ----------------
+     */
+    unionstate =  node->unionstate;
+    unionplans =  node->unionplans;
+    nplans =      unionstate->as_nplans;
+    initialized = unionstate->as_initialized;
+    
+    /* ----------------
+     *  shut down each of the subscans
+     * ----------------
+     */
+    for(i = 0; i < nplans; i++) {
+        if (initialized[i]==TRUE) {
+            ExecEndNode( (Plan *) nth(i, unionplans), (Plan*)node );
+        }
+    }
+    
+    /* ----------------
+     *  close out the different result relations
+     * ----------------
+     */
+    resultRelationInfoList = unionstate->as_result_relation_info_list;
+    while (resultRelationInfoList != NIL) {
+      Relation resultRelationDesc;
+       
+      resultRelationInfo = (RelationInfo*) lfirst(resultRelationInfoList);
+      resultRelationDesc = resultRelationInfo->ri_RelationDesc;
+      heap_close(resultRelationDesc);
+      pfree(resultRelationInfo);
+      resultRelationInfoList = lnext(resultRelationInfoList);
+    }
+    if (unionstate->as_result_relation_info_list)
+      pfree(unionstate->as_result_relation_info_list);
+
+  /* XXX should free unionstate->as_rtentries  and unionstate->as_junkfilter_list here  */
+}
+
diff --git a/src/backend/executor/nodeAppend.h b/src/backend/executor/nodeAppend.h
new file mode 100644 (file)
index 0000000..6c373ea
--- /dev/null
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeAppend.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        NODEAPPEND_H
+#define        NODEAPPEND_H
+
+extern bool exec_append_initialize_next(Append *node);
+extern bool ExecInitAppend(Append *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsAppend(Append *node);
+extern TupleTableSlot *ExecProcAppend(Append *node);
+extern void ExecEndAppend(Append *node);
+
+#endif /* NODEAPPEND_H */
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
new file mode 100644 (file)
index 0000000..116fd03
--- /dev/null
@@ -0,0 +1,407 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeGroup.c--
+ *    Routines to handle group nodes (used for queries with GROUP BY clause).
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * DESCRIPTION
+ *    The Group node is designed for handling queries with a GROUP BY clause.
+ *    It's outer plan must be a sort node. It assumes that the tuples it gets
+ *    back from the outer plan is sorted in the order specified by the group
+ *    columns. (ie. tuples from the same group are consecutive)
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "access/heapam.h"
+#include "catalog/catalog.h"
+#include "executor/executor.h"
+#include "executor/nodeGroup.h"
+
+static TupleTableSlot *ExecGroupEveryTuple(Group *node);
+static TupleTableSlot *ExecGroupOneTuple(Group *node);
+static bool sameGroup(TupleTableSlot *oldslot, TupleTableSlot *newslot,
+                     int numCols, AttrNumber *grpColIdx, TupleDesc tupdesc);
+
+/* ---------------------------------------
+ *   ExecGroup -
+ *
+ *     There are two modes in which tuples are returned by ExecGroup. If
+ *     tuplePerGroup is TRUE, every tuple from the same group will be
+ *     returned, followed by a NULL at the end of each group. This is
+ *     useful for Agg node which needs to aggregate over tuples of the same
+ *     group. (eg. SELECT salary, count{*} FROM emp GROUP BY salary)
+ *
+ *     If tuplePerGroup is FALSE, only one tuple per group is returned. The
+ *     tuple returned contains only the group columns. NULL is returned only
+ *     at the end when no more groups is present. This is useful when
+ *     the query does not involve aggregates. (eg. SELECT salary FROM emp
+ *     GROUP BY salary)
+ * ------------------------------------------
+ */
+TupleTableSlot *
+ExecGroup(Group *node)
+{
+    if (node->tuplePerGroup)
+       return ExecGroupEveryTuple(node);
+    else
+       return ExecGroupOneTuple(node);
+}
+
+/*
+ * ExecGroupEveryTuple -
+ *   return every tuple with a NULL between each group
+ */
+static TupleTableSlot *
+ExecGroupEveryTuple(Group *node)
+{
+    GroupState         *grpstate;
+    EState             *estate;
+    ExprContext                *econtext;
+
+    HeapTuple          outerTuple = NULL;
+    TupleTableSlot     *outerslot, *lastslot;
+    ProjectionInfo     *projInfo;
+    TupleTableSlot     *resultSlot;
+
+    bool isDone;
+
+    /* ---------------------
+     *  get state info from node
+     * ---------------------
+     */
+    grpstate = node->grpstate;
+    if (grpstate->grp_done)
+       return NULL;
+
+    estate = node->plan.state;
+
+    econtext = grpstate->csstate.cstate.cs_ExprContext;
+
+    if (grpstate->grp_useLastTuple) {
+       /*
+        * we haven't returned last tuple yet because it is not of the
+        * same group
+        */
+       grpstate->grp_useLastTuple = FALSE;
+
+       ExecStoreTuple(grpstate->grp_lastSlot->val,
+                      grpstate->csstate.css_ScanTupleSlot,
+                      grpstate->grp_lastSlot->ttc_buffer,
+                      false);
+    } else {
+       outerslot = ExecProcNode(outerPlan(node), (Plan*)node);
+       if (outerslot)
+           outerTuple = outerslot->val;
+       if (!HeapTupleIsValid(outerTuple)) {
+           grpstate->grp_done = TRUE;
+           return NULL;
+       }
+
+       /* ----------------
+        *  Compare with last tuple and see if this tuple is of
+        *  the same group.
+        * ----------------
+        */
+       lastslot = grpstate->csstate.css_ScanTupleSlot;
+
+       if (lastslot->val != NULL &&
+           (!sameGroup(lastslot, outerslot,
+                       node->numCols, node->grpColIdx,
+                       ExecGetScanType(&grpstate->csstate)))) {
+/*                     ExecGetResultType(&grpstate->csstate.cstate)))) {*/
+
+           grpstate->grp_useLastTuple = TRUE;
+
+           /* save it for next time */
+           grpstate->grp_lastSlot = outerslot;
+
+           /*
+            * signifies the end of the group 
+            */
+           return NULL;
+       }
+       
+       ExecStoreTuple(outerTuple,
+                      grpstate->csstate.css_ScanTupleSlot,
+                      outerslot->ttc_buffer,
+                      false);
+    }
+
+    /* ----------------
+     * form a projection tuple, store it in the result tuple
+     *  slot and return it.
+     * ----------------
+     */
+    projInfo = grpstate->csstate.cstate.cs_ProjInfo;
+
+    econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
+    resultSlot = ExecProject(projInfo, &isDone);
+    
+    return resultSlot;
+}
+
+/*
+ * ExecGroupOneTuple -
+ *    returns one tuple per group, a NULL at the end when there are no more
+ *    tuples.
+ */
+static TupleTableSlot *
+ExecGroupOneTuple(Group *node)
+{
+    GroupState         *grpstate;
+    EState             *estate;
+    ExprContext                *econtext;
+
+    HeapTuple          outerTuple = NULL;
+    TupleTableSlot     *outerslot, *lastslot;
+    ProjectionInfo     *projInfo;
+    TupleTableSlot     *resultSlot;
+
+    bool isDone;
+
+    /* ---------------------
+     *  get state info from node
+     * ---------------------
+     */
+    grpstate = node->grpstate;
+    if (grpstate->grp_done)
+       return NULL;
+
+    estate = node->plan.state;
+
+    econtext = node->grpstate->csstate.cstate.cs_ExprContext;
+
+    if (grpstate->grp_useLastTuple) {
+       grpstate->grp_useLastTuple = FALSE;
+       ExecStoreTuple(grpstate->grp_lastSlot->val,
+                      grpstate->csstate.css_ScanTupleSlot,
+                      grpstate->grp_lastSlot->ttc_buffer,
+                      false);
+    } else {
+       outerslot = ExecProcNode(outerPlan(node), (Plan*)node);
+       if (outerslot) outerTuple = outerslot->val;
+       if (!HeapTupleIsValid(outerTuple)) {
+           grpstate->grp_done = TRUE;
+           return NULL;
+       }
+       ExecStoreTuple(outerTuple,
+                      grpstate->csstate.css_ScanTupleSlot,
+                      outerslot->ttc_buffer,
+                      false);
+    }
+    lastslot = grpstate->csstate.css_ScanTupleSlot;
+
+    /*
+     * find all tuples that belong to a group
+     */
+    for(;;) {
+       outerslot = ExecProcNode(outerPlan(node), (Plan*)node);
+       outerTuple = (outerslot) ? outerslot->val : NULL;
+       if (!HeapTupleIsValid(outerTuple)) {
+           /*
+            * we have at least one tuple (lastslot) if we reach here
+            */
+           grpstate->grp_done = TRUE;
+
+           /* return lastslot */
+           break;
+       }
+
+        /* ----------------
+        *  Compare with last tuple and see if this tuple is of
+        *  the same group.
+        * ----------------
+        */
+       if ((!sameGroup(lastslot, outerslot,
+                       node->numCols, node->grpColIdx,
+                       ExecGetScanType(&grpstate->csstate)))) {
+/*                     ExecGetResultType(&grpstate->csstate.cstate)))) {*/
+
+           grpstate->grp_useLastTuple = TRUE;
+
+           /* save it for next time */
+           grpstate->grp_lastSlot = outerslot;
+
+           /* return lastslot */
+           break; 
+       }
+       
+       ExecStoreTuple(outerTuple,
+                      grpstate->csstate.css_ScanTupleSlot,
+                      outerslot->ttc_buffer,
+                      false);
+
+       lastslot = grpstate->csstate.css_ScanTupleSlot;
+    }
+
+    ExecStoreTuple(lastslot->val,
+                  grpstate->csstate.css_ScanTupleSlot,
+                  lastslot->ttc_buffer,
+                  false);
+
+    /* ----------------
+     * form a projection tuple, store it in the result tuple
+     *  slot and return it.
+     * ----------------
+     */
+    projInfo = grpstate->csstate.cstate.cs_ProjInfo;
+
+    econtext->ecxt_scantuple = lastslot;
+    resultSlot = ExecProject(projInfo, &isDone);
+    
+    return resultSlot;
+}
+
+/* -----------------
+ * ExecInitGroup 
+ *
+ *  Creates the run-time information for the group node produced by the
+ *  planner and initializes its outer subtree
+ * -----------------
+ */
+bool
+ExecInitGroup(Group *node, EState *estate, Plan *parent)
+{
+    GroupState         *grpstate;
+    Plan               *outerPlan;
+    
+    /* 
+     * assign the node's execution state
+     */
+    node->plan.state = estate;
+    
+    /*
+     * create state structure
+     */
+    grpstate = makeNode(GroupState);
+    node->grpstate = grpstate;
+    grpstate->grp_useLastTuple = FALSE;
+    grpstate->grp_done = FALSE;
+
+    /*
+     * assign node's base id and create expression context
+     */
+    ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate,
+                          (Plan*) parent);
+    ExecAssignExprContext(estate, &grpstate->csstate.cstate);
+    
+#define GROUP_NSLOTS 2
+    /*
+     * tuple table initialization
+     */
+    ExecInitScanTupleSlot(estate, &grpstate->csstate);
+    ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
+
+    /*
+     * initializes child nodes
+     */
+    outerPlan = outerPlan(node);
+    ExecInitNode(outerPlan, estate, (Plan *)node);
+    
+    /* ----------------
+     *         initialize tuple type.
+     * ----------------
+     */
+    ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate); 
+
+    /*
+     * Initialize tuple type for both result and scan.
+     * This node does no projection
+     */
+    ExecAssignResultTypeFromTL((Plan*) node, &grpstate->csstate.cstate);
+    ExecAssignProjectionInfo((Plan*)node, &grpstate->csstate.cstate);
+
+    return TRUE;
+}
+
+int
+ExecCountSlotsGroup(Group *node)
+{
+    return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
+}
+
+/* ------------------------
+ *     ExecEndGroup(node)
+ *
+ * -----------------------
+ */
+void
+ExecEndGroup(Group *node)
+{
+    GroupState *grpstate;
+    Plan       *outerPlan;
+
+    grpstate = node->grpstate;
+
+    ExecFreeProjectionInfo(&grpstate->csstate.cstate);
+
+    outerPlan = outerPlan(node);
+    ExecEndNode(outerPlan, (Plan*)node);
+    
+    /* clean up tuple table */
+    ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
+}
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+/*
+ * code swiped from nodeUnique.c
+ */
+static bool
+sameGroup(TupleTableSlot *oldslot,
+         TupleTableSlot *newslot,
+         int            numCols,
+         AttrNumber     *grpColIdx,
+         TupleDesc      tupdesc)
+{
+    bool isNull1,isNull2;
+    char *attr1, *attr2;
+    char *val1, *val2;
+    int i;
+    AttrNumber att;
+    Oid typoutput;
+
+    for(i = 0; i < numCols; i++) {
+       att = grpColIdx[i];
+       typoutput = typtoout((Oid)tupdesc->attrs[att-1]->atttypid);    
+
+       attr1 = heap_getattr(oldslot->val,
+                            InvalidBuffer,
+                            att,
+                            tupdesc,
+                            &isNull1);
+
+       attr2 = heap_getattr(newslot->val,
+                            InvalidBuffer, 
+                            att,
+                            tupdesc,
+                            &isNull2);
+                            
+       if (isNull1 == isNull2) {
+           if (isNull1) /* both are null, they are equal */
+             continue;
+
+           val1 = fmgr(typoutput, attr1,
+                       gettypelem(tupdesc->attrs[att-1]->atttypid));
+           val2 = fmgr(typoutput, attr2,
+                       gettypelem(tupdesc->attrs[att-1]->atttypid));
+
+           /* now, val1 and val2 are ascii representations so we can
+              use strcmp for comparison */
+           if (strcmp(val1,val2) != 0)
+               return FALSE;
+       } else {
+           /* one is null and the other isn't, they aren't equal */
+           return FALSE;
+       }
+    }
+
+    return TRUE;
+}
diff --git a/src/backend/executor/nodeGroup.h b/src/backend/executor/nodeGroup.h
new file mode 100644 (file)
index 0000000..a81faf4
--- /dev/null
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeGroup.h--
+ *    prototypes for nodeGroup.c
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        NODEGROUP_H
+#define        NODEGROUP_H
+
+extern TupleTableSlot *ExecGroup(Group *node);
+extern bool ExecInitGroup(Group *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsGroup(Group *node);
+extern void ExecEndGroup(Group *node);
+
+#endif /* NODEGROUP_H */
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
new file mode 100644 (file)
index 0000000..7087db4
--- /dev/null
@@ -0,0 +1,828 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeHash.c--
+ *    Routines to hash relations for hashjoin
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *             ExecHash        - generate an in-memory hash table of the relation 
+ *             ExecInitHash    - initialize node and subnodes..
+ *             ExecEndHash     - shutdown node and subnodes
+ *
+ */
+
+#include <stdio.h>     /* for sprintf() */
+#include <math.h>
+#include <sys/file.h>
+#include "storage/fd.h"                /* for SEEK_ */
+#include "storage/ipc.h"
+#include "storage/bufmgr.h"    /* for BLCKSZ */
+#include "executor/executor.h"
+#include "executor/nodeHash.h"
+#include "executor/nodeHashjoin.h"
+#include "utils/palloc.h"
+
+extern int NBuffers;
+static int HashTBSize;
+
+static void mk_hj_temp(char *tempname);
+static int hashFunc(char *key, int len);
+
+/* ----------------------------------------------------------------
+ *     ExecHash
+ *
+ *     build hash table for hashjoin, all do partitioning if more
+ *     than one batches are required.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecHash(Hash *node)
+{
+    EState       *estate;
+    HashState    *hashstate;
+    Plan         *outerNode;
+    Var                  *hashkey;
+    HashJoinTable hashtable;
+    TupleTableSlot *slot;
+    ExprContext          *econtext;
+    
+    int                  nbatch;
+    File         *batches;
+    RelativeAddr  *batchPos;
+    int                  *batchSizes;
+    int                  i;
+    RelativeAddr  *innerbatchNames;
+    
+    /* ----------------
+     * get state info from node
+     * ----------------
+     */
+    
+    hashstate =   node->hashstate;
+    estate =      node->plan.state;
+    outerNode =   outerPlan(node);
+
+    hashtable =        node->hashtable;
+    if (hashtable == NULL)
+       elog(WARN, "ExecHash: hash table is NULL.");
+    
+    nbatch = hashtable->nbatch;
+    
+    if (nbatch > 0) {  /* if needs hash partition */
+       innerbatchNames = (RelativeAddr *) ABSADDR(hashtable->innerbatchNames);
+       
+       /* --------------
+        *  allocate space for the file descriptors of batch files
+        *  then open the batch files in the current processes.
+        * --------------
+        */
+       batches = (File*)palloc(nbatch * sizeof(File));
+       for (i=0; i<nbatch; i++) {
+           batches[i] = FileNameOpenFile(ABSADDR(innerbatchNames[i]),
+                                         O_CREAT | O_RDWR, 0600);
+       }
+       hashstate->hashBatches = batches;
+        batchPos = (RelativeAddr*) ABSADDR(hashtable->innerbatchPos);
+        batchSizes = (int*) ABSADDR(hashtable->innerbatchSizes);
+    }
+    
+    /* ----------------
+     * set expression context
+     * ----------------
+     */
+    hashkey = node->hashkey;
+    econtext = hashstate->cstate.cs_ExprContext;
+    
+    /* ----------------
+     * get tuple and insert into the hash table
+     * ----------------
+     */
+    for (;;) {
+       slot = ExecProcNode(outerNode, (Plan*)node);
+       if (TupIsNull(slot))
+           break;
+       
+       econtext->ecxt_innertuple = slot;
+       ExecHashTableInsert(hashtable, econtext, hashkey, 
+                           hashstate->hashBatches);
+       
+       ExecClearTuple(slot);
+    }
+    
+    /*
+     * end of build phase, flush all the last pages of the batches.
+     */
+    for (i=0; i<nbatch; i++) {
+       if (FileSeek(batches[i], 0L, SEEK_END) < 0)
+           perror("FileSeek");
+       if (FileWrite(batches[i],ABSADDR(hashtable->batch)+i*BLCKSZ,BLCKSZ) < 0)
+           perror("FileWrite");
+       NDirectFileWrite++;
+    }
+    
+    /* ---------------------
+     *  Return the slot so that we have the tuple descriptor 
+     *  when we need to save/restore them.  -Jeff 11 July 1991
+     * ---------------------
+     */
+    return slot;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecInitHash
+ *
+ *     Init routine for Hash node
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitHash(Hash *node, EState *estate, Plan *parent)
+{
+    HashState          *hashstate;
+    Plan               *outerPlan;
+    
+    SO1_printf("ExecInitHash: %s\n",
+              "initializing hash node");
+    
+    /* ----------------
+     *  assign the node's execution state
+     * ----------------
+     */
+    node->plan.state = estate;
+    
+    /* ----------------
+     * create state structure
+     * ----------------
+     */
+    hashstate = makeNode(HashState);
+    node->hashstate = hashstate;
+    hashstate->hashBatches = NULL;
+    
+    /* ----------------
+     *  Miscellanious initialization
+     *
+     *      +  assign node's base_id
+     *       + assign debugging hooks and
+     *       + create expression context for node
+     * ----------------
+     */
+    ExecAssignNodeBaseInfo(estate, &hashstate->cstate, parent);
+    ExecAssignExprContext(estate, &hashstate->cstate);
+    
+#define HASH_NSLOTS 1
+    /* ----------------
+     * initialize our result slot
+     * ----------------
+     */
+    ExecInitResultTupleSlot(estate, &hashstate->cstate);
+    
+    /* ----------------
+     * initializes child nodes
+     * ----------------
+     */
+    outerPlan = outerPlan(node);
+    ExecInitNode(outerPlan, estate, (Plan *)node);
+    
+    /* ----------------
+     *         initialize tuple type. no need to initialize projection
+     *  info because this node doesn't do projections
+     * ----------------
+     */
+    ExecAssignResultTypeFromOuterPlan((Plan *) node, &hashstate->cstate);
+    hashstate->cstate.cs_ProjInfo = NULL;
+    
+    return TRUE;
+}
+
+int
+ExecCountSlotsHash(Hash *node)
+{
+    return ExecCountSlotsNode(outerPlan(node)) +
+       ExecCountSlotsNode(innerPlan(node)) +
+           HASH_NSLOTS;
+}
+
+/* ---------------------------------------------------------------
+ *     ExecEndHash
+ *
+ *     clean up routine for Hash node
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndHash(Hash *node)
+{
+    HashState          *hashstate;
+    Plan               *outerPlan;
+    File               *batches;
+    
+    /* ----------------
+     * get info from the hash state 
+     * ----------------
+     */
+    hashstate = node->hashstate;
+    batches = hashstate->hashBatches;
+    if (batches != NULL)
+       pfree(batches);
+    
+    /* ----------------
+     * free projection info.  no need to free result type info
+     *  because that came from the outer plan...
+     * ----------------
+     */
+    ExecFreeProjectionInfo(&hashstate->cstate);
+    
+    /* ----------------
+     * shut down the subplan
+     * ----------------
+     */
+    outerPlan = outerPlan(node);
+    ExecEndNode(outerPlan, (Plan*)node);
+} 
+
+RelativeAddr
+hashTableAlloc(int size, HashJoinTable hashtable)
+{
+    RelativeAddr p;
+    p = hashtable->top;
+    hashtable->top += size;
+    return p;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecHashTableCreate
+ *
+ *     create a hashtable in shared memory for hashjoin.
+ * ----------------------------------------------------------------
+ */
+#define NTUP_PER_BUCKET                10
+#define FUDGE_FAC              1.5
+
+HashJoinTable
+ExecHashTableCreate(Hash *node)
+{
+    Plan         *outerNode;
+    int                  nbatch;
+    int          ntuples;
+    int          tupsize;
+    IpcMemoryId   shmid;
+    HashJoinTable hashtable;
+    HashBucket           bucket;
+    int          nbuckets;
+    int                  totalbuckets;
+    int          bucketsize;
+    int          i;
+    RelativeAddr  *outerbatchNames;
+    RelativeAddr  *outerbatchPos;
+    RelativeAddr  *innerbatchNames;
+    RelativeAddr  *innerbatchPos;
+    int                  *innerbatchSizes;
+    RelativeAddr  tempname;
+    
+    nbatch = -1;
+    HashTBSize = NBuffers/2;
+    while (nbatch < 0) {
+       /*
+        * determine number of batches for the hashjoin
+        */
+       HashTBSize *= 2;
+       nbatch = ExecHashPartition(node);
+    }
+    /* ----------------
+     * get information about the size of the relation
+     * ----------------
+     */
+    outerNode = outerPlan(node);
+    ntuples = outerNode->plan_size;
+    if (ntuples <= 0)
+       ntuples = 1000;  /* XXX just a hack */
+    tupsize = outerNode->plan_width + sizeof(HeapTupleData);
+    
+    /*
+     * totalbuckets is the total number of hash buckets needed for
+     * the entire relation
+     */
+    totalbuckets = ceil((double)ntuples/NTUP_PER_BUCKET);
+    bucketsize = LONGALIGN (NTUP_PER_BUCKET * tupsize + sizeof(*bucket));
+    
+    /*
+     * nbuckets is the number of hash buckets for the first pass
+     * of hybrid hashjoin
+     */
+    nbuckets = (HashTBSize - nbatch) * BLCKSZ / (bucketsize * FUDGE_FAC);
+    if (totalbuckets < nbuckets)
+       totalbuckets = nbuckets;
+    if (nbatch == 0)
+       nbuckets = totalbuckets;
+#ifdef HJDEBUG
+    printf("nbatch = %d, totalbuckets = %d, nbuckets = %d\n", nbatch, totalbuckets, nbuckets);
+#endif
+    
+    /* ----------------
+     *  in non-parallel machines, we don't need to put the hash table
+     *  in the shared memory.  We just palloc it.
+     * ----------------
+     */
+    hashtable = (HashJoinTable)palloc((HashTBSize+1)*BLCKSZ);
+    shmid = 0;
+    
+    if (hashtable == NULL) {
+       elog(WARN, "not enough memory for hashjoin.");
+    }
+    /* ----------------
+     * initialize the hash table header
+     * ----------------
+     */
+    hashtable->nbuckets = nbuckets;
+    hashtable->totalbuckets = totalbuckets;
+    hashtable->bucketsize = bucketsize;
+    hashtable->shmid = shmid;
+    hashtable->top = sizeof(HashTableData);
+    hashtable->bottom = HashTBSize * BLCKSZ;
+    /*
+     *  hashtable->readbuf has to be long aligned!!!
+     */
+    hashtable->readbuf = hashtable->bottom;
+    hashtable->nbatch = nbatch;
+    hashtable->curbatch = 0;
+    hashtable->pcount = hashtable->nprocess = 0;
+    if (nbatch > 0) {
+       /* ---------------
+        *  allocate and initialize the outer batches
+        * ---------------
+        */
+       outerbatchNames = (RelativeAddr*)ABSADDR(
+                                                hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable));
+       outerbatchPos = (RelativeAddr*)ABSADDR(
+                                              hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable));
+       for (i=0; i<nbatch; i++) {
+           tempname = hashTableAlloc(12, hashtable);
+           mk_hj_temp(ABSADDR(tempname));
+           outerbatchNames[i] = tempname;
+           outerbatchPos[i] = -1;
+       }
+       hashtable->outerbatchNames = RELADDR(outerbatchNames);
+       hashtable->outerbatchPos = RELADDR(outerbatchPos);
+       /* ---------------
+        *  allocate and initialize the inner batches
+        * ---------------
+        */
+       innerbatchNames = (RelativeAddr*)ABSADDR(
+                                                hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable));
+       innerbatchPos = (RelativeAddr*)ABSADDR(
+                                              hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable));
+       innerbatchSizes = (int*)ABSADDR(
+                                       hashTableAlloc(nbatch * sizeof(int), hashtable));
+       for (i=0; i<nbatch; i++) {
+           tempname = hashTableAlloc(12, hashtable);
+           mk_hj_temp(ABSADDR(tempname));
+           innerbatchNames[i] = tempname;
+           innerbatchPos[i] = -1;
+           innerbatchSizes[i] = 0;
+       }
+       hashtable->innerbatchNames = RELADDR(innerbatchNames);
+       hashtable->innerbatchPos = RELADDR(innerbatchPos);
+       hashtable->innerbatchSizes = RELADDR(innerbatchSizes);
+    }
+    else {
+       hashtable->outerbatchNames = (RelativeAddr)NULL;
+       hashtable->outerbatchPos = (RelativeAddr)NULL;
+       hashtable->innerbatchNames = (RelativeAddr)NULL;
+       hashtable->innerbatchPos = (RelativeAddr)NULL;
+       hashtable->innerbatchSizes = (RelativeAddr)NULL;
+    }
+    
+    hashtable->batch = (RelativeAddr)LONGALIGN(hashtable->top + 
+                                              bucketsize * nbuckets);
+    hashtable->overflownext=hashtable->batch + nbatch * BLCKSZ;
+    /* ----------------
+     * initialize each hash bucket
+     * ----------------
+     */
+    bucket = (HashBucket)ABSADDR(hashtable->top);
+    for (i=0; i<nbuckets; i++) {
+       bucket->top = RELADDR((char*)bucket + sizeof(*bucket));
+       bucket->bottom = bucket->top;
+       bucket->firstotuple = bucket->lastotuple = -1;
+       bucket = (HashBucket)LONGALIGN(((char*)bucket + bucketsize));
+    }
+    return(hashtable);
+}
+
+/* ----------------------------------------------------------------
+ *     ExecHashTableInsert
+ *
+ *     insert a tuple into the hash table depending on the hash value
+ *     it may just go to a tmp file for other batches
+ * ----------------------------------------------------------------
+ */
+void
+ExecHashTableInsert(HashJoinTable hashtable,
+                   ExprContext *econtext,
+                   Var *hashkey,
+                   File *batches)
+{
+    TupleTableSlot     *slot;
+    HeapTuple          heapTuple;
+    HashBucket                 bucket;
+    int                        bucketno;
+    int                        nbatch;
+    int                        batchno;
+    char               *buffer;
+    RelativeAddr       *batchPos;
+    int                        *batchSizes;
+    char               *pos;
+    
+    nbatch =           hashtable->nbatch;
+    batchPos =                 (RelativeAddr*)ABSADDR(hashtable->innerbatchPos);
+    batchSizes =       (int*)ABSADDR(hashtable->innerbatchSizes);
+    
+    slot = econtext->ecxt_innertuple;
+    heapTuple = slot->val;
+    
+#ifdef HJDEBUG
+    printf("Inserting ");
+#endif
+    
+    bucketno = ExecHashGetBucket(hashtable, econtext, hashkey);
+    
+    /* ----------------
+     * decide whether to put the tuple in the hash table or a tmp file
+     * ----------------
+     */
+    if (bucketno < hashtable->nbuckets) {
+       /* ---------------
+        *  put the tuple in hash table
+        * ---------------
+        */
+       bucket = (HashBucket)
+           (ABSADDR(hashtable->top) + bucketno * hashtable->bucketsize);
+       if ((char*)LONGALIGN(ABSADDR(bucket->bottom))
+           -(char*)bucket+heapTuple->t_len > hashtable->bucketsize)
+           ExecHashOverflowInsert(hashtable, bucket, heapTuple);
+       else {
+           memmove((char*)LONGALIGN(ABSADDR(bucket->bottom)),
+                   heapTuple,
+                   heapTuple->t_len); 
+           bucket->bottom = 
+               ((RelativeAddr)LONGALIGN(bucket->bottom) + heapTuple->t_len);
+       }
+    }
+    else {
+       /* -----------------
+        * put the tuple into a tmp file for other batches
+        * -----------------
+        */
+       batchno = (float)(bucketno - hashtable->nbuckets)/
+           (float)(hashtable->totalbuckets - hashtable->nbuckets)
+               * nbatch;
+       buffer = ABSADDR(hashtable->batch) + batchno * BLCKSZ;
+       batchSizes[batchno]++;
+       pos= (char *)
+           ExecHashJoinSaveTuple(heapTuple,
+                                 buffer,
+                                 batches[batchno],
+                                 (char*)ABSADDR(batchPos[batchno]));
+       batchPos[batchno] = RELADDR(pos);
+    }
+}
+
+/* ----------------------------------------------------------------
+ *     ExecHashTableDestroy
+ *
+ *     destroy a hash table
+ * ----------------------------------------------------------------
+ */
+void
+ExecHashTableDestroy(HashJoinTable hashtable)
+{
+    pfree(hashtable);
+}
+
+/* ----------------------------------------------------------------
+ *     ExecHashGetBucket
+ *
+ *     Get the hash value for a tuple
+ * ----------------------------------------------------------------
+ */
+int
+ExecHashGetBucket(HashJoinTable hashtable,
+                 ExprContext *econtext,
+                 Var *hashkey)
+{
+    int        bucketno;
+    Datum      keyval;
+    bool isNull;
+    
+    
+    /* ----------------
+     * Get the join attribute value of the tuple
+     * ----------------
+     */
+    keyval = ExecEvalVar(hashkey, econtext, &isNull);
+    
+    /* ------------------
+     *  compute the hash function
+     * ------------------
+     */
+    if (execConstByVal)
+        bucketno =
+           hashFunc((char *) &keyval, execConstLen) % hashtable->totalbuckets;
+    else
+        bucketno =
+           hashFunc((char *) keyval, execConstLen) % hashtable->totalbuckets;
+#ifdef HJDEBUG
+    if (bucketno >= hashtable->nbuckets)
+       printf("hash(%d) = %d SAVED\n", keyval, bucketno);
+    else
+       printf("hash(%d) = %d\n", keyval, bucketno);
+#endif
+    
+    return(bucketno);
+}
+
+/* ----------------------------------------------------------------
+ *     ExecHashOverflowInsert
+ *
+ *     insert into the overflow area of a hash bucket
+ * ----------------------------------------------------------------
+ */
+void
+ExecHashOverflowInsert(HashJoinTable hashtable,
+                      HashBucket bucket,
+                      HeapTuple heapTuple)
+{
+    OverflowTuple otuple;
+    RelativeAddr  newend;
+    OverflowTuple firstotuple;
+    OverflowTuple lastotuple;
+    
+    firstotuple = (OverflowTuple)ABSADDR(bucket->firstotuple);
+    lastotuple = (OverflowTuple)ABSADDR(bucket->lastotuple);
+    /* ----------------
+     * see if we run out of overflow space
+     * ----------------
+     */
+    newend = (RelativeAddr)LONGALIGN(hashtable->overflownext + sizeof(*otuple)
+                                    + heapTuple->t_len);
+    if (newend > hashtable->bottom) {
+       elog(DEBUG, "hash table out of memory. expanding.");
+       /* ------------------
+        * XXX this is a temporary hack
+        * eventually, recursive hash partitioning will be 
+        * implemented
+        * ------------------
+        */
+       hashtable->readbuf = hashtable->bottom = 2 * hashtable->bottom;
+       hashtable =
+           (HashJoinTable)repalloc(hashtable, hashtable->bottom+BLCKSZ);
+       if (hashtable == NULL) {
+           perror("repalloc");
+           elog(WARN, "can't expand hashtable.");
+       }
+    }
+    
+    /* ----------------
+     * establish the overflow chain
+     * ----------------
+     */
+    otuple = (OverflowTuple)ABSADDR(hashtable->overflownext);
+    hashtable->overflownext = newend;
+    if (firstotuple == NULL)
+       bucket->firstotuple = bucket->lastotuple = RELADDR(otuple);
+    else {
+       lastotuple->next = RELADDR(otuple);
+       bucket->lastotuple = RELADDR(otuple);
+    }
+    
+    /* ----------------
+     * copy the tuple into the overflow area
+     * ----------------
+     */
+    otuple->next = -1;
+    otuple->tuple = RELADDR(LONGALIGN(((char*)otuple + sizeof(*otuple))));
+    memmove(ABSADDR(otuple->tuple),
+           heapTuple,
+           heapTuple->t_len);
+}
+
+/* ----------------------------------------------------------------
+ *     ExecScanHashBucket
+ *
+ *     scan a hash bucket of matches
+ * ----------------------------------------------------------------
+ */
+HeapTuple
+ExecScanHashBucket(HashJoinState *hjstate,
+                  HashBucket bucket,
+                  HeapTuple curtuple,
+                  List *hjclauses,
+                  ExprContext *econtext)
+{
+    HeapTuple          heapTuple;
+    bool               qualResult;
+    OverflowTuple      otuple = NULL;
+    OverflowTuple      curotuple;
+    TupleTableSlot     *inntuple;
+    OverflowTuple      firstotuple;
+    OverflowTuple      lastotuple;
+    HashJoinTable      hashtable;
+    
+    hashtable = hjstate->hj_HashTable;
+    firstotuple = (OverflowTuple)ABSADDR(bucket->firstotuple);
+    lastotuple = (OverflowTuple)ABSADDR(bucket->lastotuple);
+    
+    /* ----------------
+     * search the hash bucket
+     * ----------------
+     */
+    if (curtuple == NULL || curtuple < (HeapTuple)ABSADDR(bucket->bottom)) {
+        if (curtuple == NULL)
+           heapTuple = (HeapTuple)
+               LONGALIGN(ABSADDR(bucket->top));
+       else 
+           heapTuple = (HeapTuple)
+               LONGALIGN(((char*)curtuple+curtuple->t_len));
+       
+        while (heapTuple < (HeapTuple)ABSADDR(bucket->bottom)) {
+           
+           inntuple =  ExecStoreTuple(heapTuple,       /* tuple to store */
+                                      hjstate->hj_HashTupleSlot,   /* slot */
+                                      InvalidBuffer,/* tuple has no buffer */
+                                      false);  /* do not pfree this tuple */
+           
+           econtext->ecxt_innertuple = inntuple;
+           qualResult = ExecQual((List*)hjclauses, econtext);
+           
+           if (qualResult)
+               return heapTuple;
+           
+           heapTuple = (HeapTuple)
+               LONGALIGN(((char*)heapTuple+heapTuple->t_len));
+       }
+       
+       if (firstotuple == NULL)
+           return NULL;
+       otuple = firstotuple;
+    }
+    
+    /* ----------------
+     * search the overflow area of the hash bucket
+     * ----------------
+     */
+    if (otuple == NULL) {
+       curotuple = hjstate->hj_CurOTuple;
+       otuple = (OverflowTuple)ABSADDR(curotuple->next);
+    }
+    
+    while (otuple != NULL) {
+       heapTuple = (HeapTuple)ABSADDR(otuple->tuple);
+       
+       inntuple =  ExecStoreTuple(heapTuple,     /* tuple to store */
+                          hjstate->hj_HashTupleSlot, /* slot */
+                          InvalidBuffer, /* SP?? this tuple has no buffer */
+                          false);        /* do not pfree this tuple */
+       
+       econtext->ecxt_innertuple = inntuple;
+       qualResult = ExecQual((List*)hjclauses, econtext);
+       
+       if (qualResult) {
+           hjstate->hj_CurOTuple = otuple;
+           return heapTuple;
+       }
+       
+       otuple = (OverflowTuple)ABSADDR(otuple->next);
+    }
+    
+    /* ----------------
+     * no match
+     * ----------------
+     */
+    return NULL;
+}
+
+/* ----------------------------------------------------------------
+ *     hashFunc
+ *
+ *     the hash function, copied from Margo
+ * ----------------------------------------------------------------
+ */
+static int
+hashFunc(char *key, int len)
+{
+    register unsigned int h;
+    register int l;
+    register unsigned char *k;
+    
+    /*
+     * If this is a variable length type, then 'k' points
+     * to a "struct varlena" and len == -1.
+     * NOTE:
+     * VARSIZE returns the "real" data length plus the sizeof the
+     * "vl_len" attribute of varlena (the length information).
+     * 'k' points to the beginning of the varlena struct, so
+     * we have to use "VARDATA" to find the beginning of the "real"
+     * data.
+     */
+    if (len == -1) {
+       l = VARSIZE(key) - VARHDRSZ;
+       k = (unsigned char*) VARDATA(key);
+    } else {
+       l = len;
+       k = (unsigned char *) key;
+    }
+    
+    h = 0;
+    
+    /*
+     * Convert string to integer
+     */
+    while (l--) h = h * PRIME1 ^ (*k++);
+    h %= PRIME2;
+    
+    return (h);
+}
+
+/* ----------------------------------------------------------------
+ *     ExecHashPartition
+ *
+ *     determine the number of batches needed for a hashjoin
+ * ----------------------------------------------------------------
+ */
+int
+ExecHashPartition(Hash *node)
+{
+    Plan       *outerNode;
+    int                b;
+    int                pages;
+    int                ntuples;
+    int                tupsize;
+    
+    /*
+     * get size information for plan node
+     */
+    outerNode = outerPlan(node);
+    ntuples = outerNode->plan_size;
+    if (ntuples == 0) ntuples = 1000;
+    tupsize = outerNode->plan_width + sizeof(HeapTupleData);
+    pages = ceil((double)ntuples * tupsize * FUDGE_FAC / BLCKSZ);
+    
+    /*
+     * if amount of buffer space below hashjoin threshold,
+     * return negative
+     */
+    if (ceil(sqrt((double)pages)) > HashTBSize)
+       return -1;
+    if (pages <= HashTBSize)
+       b = 0;  /* fit in memory, no partitioning */
+    else
+       b = ceil((double)(pages - HashTBSize)/(double)(HashTBSize - 1));
+    
+    return b;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecHashTableReset
+ *
+ *     reset hash table header for new batch
+ * ----------------------------------------------------------------
+ */
+void
+ExecHashTableReset(HashJoinTable hashtable, int ntuples)
+{
+    int i;
+    HashBucket bucket;
+    
+    hashtable->nbuckets = hashtable->totalbuckets
+       = ceil((double)ntuples/NTUP_PER_BUCKET);
+    
+    hashtable->overflownext = hashtable->top + hashtable->bucketsize *
+       hashtable->nbuckets;
+    
+    bucket = (HashBucket)ABSADDR(hashtable->top);
+    for (i=0; i<hashtable->nbuckets; i++) {
+       bucket->top = RELADDR((char*)bucket + sizeof(*bucket));
+       bucket->bottom = bucket->top;
+       bucket->firstotuple = bucket->lastotuple = -1;
+       bucket = (HashBucket)((char*)bucket + hashtable->bucketsize);
+    }
+    hashtable->pcount = hashtable->nprocess;
+}
+
+static int hjtmpcnt = 0;
+
+static void
+mk_hj_temp(char *tempname)
+{
+    sprintf(tempname, "HJ%d.%d", getpid(), hjtmpcnt);
+    hjtmpcnt = (hjtmpcnt + 1) % 1000;
+}
+
+
+
diff --git a/src/backend/executor/nodeHash.h b/src/backend/executor/nodeHash.h
new file mode 100644 (file)
index 0000000..d4d2b1a
--- /dev/null
@@ -0,0 +1,35 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeHash.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        NODEHASH_H
+#define        NODEHASH_H
+
+extern TupleTableSlot *ExecHash(Hash *node);
+extern bool ExecInitHash(Hash *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsHash(Hash *node);
+extern void ExecEndHash(Hash *node);
+extern RelativeAddr hashTableAlloc(int size, HashJoinTable hashtable);
+extern HashJoinTable ExecHashTableCreate(Hash *node);
+extern void ExecHashTableInsert(HashJoinTable hashtable, ExprContext *econtext,
+                               Var *hashkey, File *batches);
+extern void ExecHashTableDestroy(HashJoinTable hashtable);
+extern int ExecHashGetBucket(HashJoinTable hashtable, ExprContext *econtext,
+                            Var *hashkey);
+extern void ExecHashOverflowInsert(HashJoinTable hashtable, HashBucket bucket,
+                                  HeapTuple heapTuple);
+extern HeapTuple ExecScanHashBucket(HashJoinState *hjstate, HashBucket bucket,
+                                   HeapTuple curtuple, List *hjclauses,
+                                   ExprContext *econtext);
+extern int ExecHashPartition(Hash *node);
+extern void ExecHashTableReset(HashJoinTable hashtable, int ntuples);
+
+#endif /* NODEHASH_H */
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
new file mode 100644 (file)
index 0000000..5d50027
--- /dev/null
@@ -0,0 +1,792 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeHashjoin.c--
+ *    Routines to handle hash join nodes
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <sys/file.h>
+
+#include "storage/bufmgr.h"    /* for BLCKSZ */
+#include "storage/fd.h"                /* for SEEK_ */
+#include "executor/executor.h"
+#include "executor/nodeHash.h"
+#include "executor/nodeHashjoin.h"
+
+#include "optimizer/clauses.h" /* for get_leftop */
+
+
+#include "utils/palloc.h"
+
+static TupleTableSlot *
+ExecHashJoinOuterGetTuple(Plan *node, Plan* parent, HashJoinState *hjstate);
+
+static TupleTableSlot *
+ExecHashJoinGetSavedTuple(HashJoinState *hjstate, char *buffer,
+       File file, TupleTableSlot *tupleSlot, int *block, char **position);
+
+/* ----------------------------------------------------------------
+ *     ExecHashJoin
+ *
+ *     This function implements the Hybrid Hashjoin algorithm.
+ *     recursive partitioning remains to be added.
+ *     Note: the relation we build hash table on is the inner
+ *           the other one is outer.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *                       /* return: a tuple or NULL */
+ExecHashJoin(HashJoin *node)
+{
+    HashJoinState      *hjstate;
+    EState             *estate;
+    Plan               *outerNode;
+    Hash               *hashNode;
+    List               *hjclauses;
+    Expr               *clause;
+    List               *qual;
+    ScanDirection      dir;
+    TupleTableSlot     *inntuple;
+    Var                        *outerVar;
+    ExprContext                *econtext;
+    
+    HashJoinTable      hashtable;
+    int                        bucketno;
+    HashBucket         bucket;
+    HeapTuple          curtuple;
+    
+    bool               qualResult;
+    
+    TupleTableSlot     *outerTupleSlot;
+    TupleTableSlot     *innerTupleSlot;
+    int                        nbatch;
+    int                        curbatch;
+    File               *outerbatches;
+    RelativeAddr       *outerbatchNames;
+    RelativeAddr       *outerbatchPos;
+    Var                        *innerhashkey;
+    int                        batch;
+    int                        batchno;
+    char               *buffer;
+    int                        i;
+    bool               hashPhaseDone;
+    char               *pos;
+    
+    /* ----------------
+     * get information from HashJoin node
+     * ----------------
+     */
+    hjstate =          node->hashjoinstate;
+    hjclauses =        node->hashclauses;
+    clause =           lfirst(hjclauses);
+    estate =           node->join.state;
+    qual =             node->join.qual;
+    hashNode =                 (Hash *)innerPlan(node);
+    outerNode =        outerPlan(node);
+    hashPhaseDone =    node->hashdone;
+    
+    dir =              estate->es_direction;
+    
+    /* -----------------
+     * get information from HashJoin state
+     * -----------------
+     */
+    hashtable =        hjstate->hj_HashTable;
+    bucket =           hjstate->hj_CurBucket;
+    curtuple =         hjstate->hj_CurTuple;
+    
+    /* --------------------
+     * initialize expression context
+     * --------------------
+     */
+    econtext =         hjstate->jstate.cs_ExprContext;
+    
+    if (hjstate->jstate.cs_TupFromTlist) {
+       TupleTableSlot  *result;
+       bool            isDone;
+       
+       result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
+       if (!isDone)
+           return result;
+    }
+    /* ----------------
+     * if this is the first call, build the hash table for inner relation
+     * ----------------
+     */
+    if (!hashPhaseDone) {  /* if the hash phase not completed */
+       hashtable = node->hashjointable;
+        if (hashtable == NULL) { /* if the hash table has not been created */
+           /* ----------------
+            * create the hash table
+            * ----------------
+            */
+           hashtable = ExecHashTableCreate(hashNode);
+           hjstate->hj_HashTable = hashtable;
+           innerhashkey = hashNode->hashkey;
+           hjstate->hj_InnerHashKey = innerhashkey;
+           
+           /* ----------------
+            * execute the Hash node, to build the hash table 
+            * ----------------
+            */
+           hashNode->hashtable = hashtable;
+           innerTupleSlot = ExecProcNode((Plan *)hashNode, (Plan*) node);
+       }
+       bucket = NULL;
+       curtuple = NULL;
+       curbatch = 0;
+       node->hashdone = true;
+    }
+    nbatch = hashtable->nbatch;
+    outerbatches = hjstate->hj_OuterBatches;
+    if (nbatch > 0 && outerbatches == NULL) {  /* if needs hash partition */
+       /* -----------------
+        *  allocate space for file descriptors of outer batch files
+        *  then open the batch files in the current process
+        * -----------------
+        */
+       innerhashkey = hashNode->hashkey;
+       hjstate->hj_InnerHashKey = innerhashkey;
+        outerbatchNames = (RelativeAddr*)
+           ABSADDR(hashtable->outerbatchNames);
+       outerbatches = (File*)
+           palloc(nbatch * sizeof(File));
+       for (i=0; i<nbatch; i++) {
+           outerbatches[i] = FileNameOpenFile(
+                                              ABSADDR(outerbatchNames[i]), 
+                                              O_CREAT | O_RDWR, 0600);
+       }
+       hjstate->hj_OuterBatches = outerbatches;
+
+       /* ------------------
+        *  get the inner batch file descriptors from the
+        *  hash node
+        * ------------------
+        */
+       hjstate->hj_InnerBatches =
+           hashNode->hashstate->hashBatches;
+    }
+    outerbatchPos = (RelativeAddr*)ABSADDR(hashtable->outerbatchPos);
+    curbatch = hashtable->curbatch;
+    outerbatchNames = (RelativeAddr*)ABSADDR(hashtable->outerbatchNames);
+    
+    /* ----------------
+     * Now get an outer tuple and probe into the hash table for matches
+     * ----------------
+     */
+    outerTupleSlot =   hjstate->jstate.cs_OuterTupleSlot;
+    outerVar =         get_leftop(clause);
+    
+    bucketno = -1;  /* if bucketno remains -1, means use old outer tuple */
+    if (TupIsNull(outerTupleSlot)) {
+       /*
+        * if the current outer tuple is nil, get a new one
+        */
+       outerTupleSlot = (TupleTableSlot*)
+           ExecHashJoinOuterGetTuple(outerNode, (Plan*)node, hjstate);
+       
+       while (curbatch <= nbatch && TupIsNull(outerTupleSlot)) {
+           /*
+            * if the current batch runs out, switch to new batch
+            */
+           curbatch = ExecHashJoinNewBatch(hjstate);
+           if (curbatch > nbatch) {
+               /*
+                * when the last batch runs out, clean up
+                */
+               ExecHashTableDestroy(hashtable);
+               hjstate->hj_HashTable = NULL;
+               return NULL;
+           }
+           else
+               outerTupleSlot = (TupleTableSlot*)
+                   ExecHashJoinOuterGetTuple(outerNode, (Plan*)node, hjstate);
+       }
+       /*
+        * now we get an outer tuple, find the corresponding bucket for
+        * this tuple from the hash table
+        */
+       econtext->ecxt_outertuple = outerTupleSlot;
+       
+#ifdef HJDEBUG
+       printf("Probing ");
+#endif
+       bucketno = ExecHashGetBucket(hashtable, econtext, outerVar);
+       bucket=(HashBucket)(ABSADDR(hashtable->top) 
+                           + bucketno * hashtable->bucketsize);
+    }
+    
+    for (;;) {
+       /* ----------------
+        *      Now we've got an outer tuple and the corresponding hash bucket,
+        *  but this tuple may not belong to the current batch.
+        * ----------------
+        */
+       if (curbatch == 0 && bucketno != -1)  /* if this is the first pass */
+           batch = ExecHashJoinGetBatch(bucketno, hashtable, nbatch);
+       else
+           batch = 0;
+       if (batch > 0) {
+           /*
+            * if the current outer tuple does not belong to
+            * the current batch, save to the tmp file for
+            * the corresponding batch.
+            */
+           buffer = ABSADDR(hashtable->batch) + (batch - 1) * BLCKSZ;
+           batchno = batch - 1;
+           pos  = ExecHashJoinSaveTuple(outerTupleSlot->val,
+                                        buffer,
+                                        outerbatches[batchno],
+                                        ABSADDR(outerbatchPos[batchno]));
+           
+           outerbatchPos[batchno] = RELADDR(pos);
+       }
+       else if (bucket != NULL) {
+           do {
+               /*
+                * scan the hash bucket for matches
+                */
+               curtuple = ExecScanHashBucket(hjstate,
+                                             bucket,
+                                             curtuple,
+                                             hjclauses,
+                                             econtext);
+               
+               if (curtuple != NULL) {
+                   /*
+                    * we've got a match, but still need to test qpqual
+                    */
+                    inntuple = ExecStoreTuple(curtuple, 
+                                             hjstate->hj_HashTupleSlot,
+                                             InvalidBuffer,
+                                             false); /* don't pfree this tuple */
+                   
+                   econtext->ecxt_innertuple = inntuple;
+                   
+                   /* ----------------
+                    * test to see if we pass the qualification
+                    * ----------------
+                    */
+                   qualResult = ExecQual((List*)qual, econtext);
+                   
+                   /* ----------------
+                    * if we pass the qual, then save state for next call and
+                    * have ExecProject form the projection, store it
+                    * in the tuple table, and return the slot.
+                    * ----------------
+                    */
+                   if (qualResult) {
+                       ProjectionInfo  *projInfo;
+                       TupleTableSlot  *result;
+                       bool            isDone;
+                       
+                       hjstate->hj_CurBucket = bucket;
+                       hjstate->hj_CurTuple = curtuple;
+                       hashtable->curbatch = curbatch;
+                       hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
+                       
+                       projInfo = hjstate->jstate.cs_ProjInfo;
+                       result = ExecProject(projInfo, &isDone);
+                       hjstate->jstate.cs_TupFromTlist = !isDone;
+                       return result;
+                   }
+               }
+           }
+           while (curtuple != NULL);
+       }
+       
+       /* ----------------
+        *   Now the current outer tuple has run out of matches,
+        *   so we free it and get a new outer tuple.
+        * ----------------
+        */
+       outerTupleSlot = (TupleTableSlot*)
+           ExecHashJoinOuterGetTuple(outerNode, (Plan*) node, hjstate);
+       
+       while (curbatch <= nbatch && TupIsNull(outerTupleSlot)) {
+           /*
+            * if the current batch runs out, switch to new batch
+            */
+           curbatch = ExecHashJoinNewBatch(hjstate);
+           if (curbatch > nbatch) {
+               /*
+                * when the last batch runs out, clean up
+                */
+               ExecHashTableDestroy(hashtable);
+               hjstate->hj_HashTable = NULL;
+               return NULL;
+           }
+           else
+               outerTupleSlot = (TupleTableSlot*)
+                   ExecHashJoinOuterGetTuple(outerNode, (Plan*)node, hjstate);
+       }
+       
+       /* ----------------
+        *   Now get the corresponding hash bucket for the new
+        *   outer tuple.
+        * ----------------
+        */
+       econtext->ecxt_outertuple = outerTupleSlot;
+#ifdef HJDEBUG
+       printf("Probing ");
+#endif
+       bucketno = ExecHashGetBucket(hashtable, econtext, outerVar);
+       bucket=(HashBucket)(ABSADDR(hashtable->top) 
+                           + bucketno * hashtable->bucketsize);
+       curtuple = NULL;
+    }
+}
+
+/* ----------------------------------------------------------------
+ *     ExecInitHashJoin
+ *
+ *     Init routine for HashJoin node.
+ * ----------------------------------------------------------------
+ */
+bool   /* return: initialization status */
+ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
+{
+    HashJoinState      *hjstate;
+    Plan               *outerNode;
+    Hash               *hashNode;
+    
+    /* ----------------
+     *  assign the node's execution state
+     * ----------------
+     */
+    node->join.state = estate;
+    
+    /* ----------------
+     * create state structure
+     * ----------------
+     */
+    hjstate = makeNode(HashJoinState);
+    
+    node->hashjoinstate = hjstate;
+    
+    /* ----------------
+     *  Miscellanious initialization
+     *
+     *      +  assign node's base_id
+     *       + assign debugging hooks and
+     *       + create expression context for node
+     * ----------------
+     */
+    ExecAssignNodeBaseInfo(estate, &hjstate->jstate, parent);
+    ExecAssignExprContext(estate, &hjstate->jstate);
+    
+#define HASHJOIN_NSLOTS 2
+    /* ----------------
+     * tuple table initialization
+     * ----------------
+     */
+    ExecInitResultTupleSlot(estate, &hjstate->jstate);
+    ExecInitOuterTupleSlot(estate,  hjstate);    
+    
+    /* ----------------
+     * initializes child nodes
+     * ----------------
+     */
+    outerNode = outerPlan((Plan *)node);
+    hashNode  = (Hash*)innerPlan((Plan *)node);
+    
+    ExecInitNode(outerNode, estate, (Plan *) node);
+    ExecInitNode((Plan*)hashNode,  estate, (Plan *) node);
+    
+    /* ----------------
+     * now for some voodoo.  our temporary tuple slot
+     *  is actually the result tuple slot of the Hash node
+     *  (which is our inner plan).  we do this because Hash
+     *  nodes don't return tuples via ExecProcNode() -- instead
+     *  the hash join node uses ExecScanHashBucket() to get
+     *  at the contents of the hash table.  -cim 6/9/91
+     * ----------------
+     */
+    {
+       HashState      *hashstate  = hashNode->hashstate;
+       TupleTableSlot *slot      =
+           hashstate->cstate.cs_ResultTupleSlot;
+       hjstate->hj_HashTupleSlot = slot;
+    }
+    hjstate->hj_OuterTupleSlot->ttc_tupleDescriptor = 
+                               ExecGetTupType(outerNode);
+    
+/*
+    hjstate->hj_OuterTupleSlot->ttc_execTupDescriptor =
+                             ExecGetExecTupDesc(outerNode);
+*/
+    
+    /* ----------------
+     *         initialize tuple type and projection info
+     * ----------------
+     */
+    ExecAssignResultTypeFromTL((Plan*) node, &hjstate->jstate);
+    ExecAssignProjectionInfo((Plan*) node, &hjstate->jstate);
+    
+    /* ----------------
+     * XXX comment me
+     * ----------------
+     */
+    
+    node->hashdone = false;
+    
+    hjstate->hj_HashTable = (HashJoinTable)NULL;
+    hjstate->hj_HashTableShmId = (IpcMemoryId)0;
+    hjstate->hj_CurBucket = (HashBucket )NULL;
+    hjstate->hj_CurTuple = (HeapTuple )NULL;
+    hjstate->hj_CurOTuple = (OverflowTuple )NULL;
+    hjstate->hj_InnerHashKey = (Var*)NULL;
+    hjstate->hj_OuterBatches = (File*)NULL;
+    hjstate->hj_InnerBatches = (File*)NULL;
+    hjstate->hj_OuterReadPos = (char*)NULL;
+    hjstate->hj_OuterReadBlk = (int)0;
+
+    hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot*) NULL;
+    hjstate->jstate.cs_TupFromTlist = (bool) false;
+    
+    return TRUE;
+}
+
+int
+ExecCountSlotsHashJoin(HashJoin *node)
+{
+    return ExecCountSlotsNode(outerPlan(node)) +
+       ExecCountSlotsNode(innerPlan(node)) +
+           HASHJOIN_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecEndHashJoin
+ *
+ *     clean up routine for HashJoin node
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndHashJoin(HashJoin *node)
+{
+    HashJoinState   *hjstate;
+    
+    /* ----------------
+     * get info from the HashJoin state 
+     * ----------------
+     */
+    hjstate = node->hashjoinstate;
+    
+    /* ----------------
+     * free hash table in case we end plan before all tuples are retrieved
+     * ---------------
+     */
+    if (hjstate->hj_HashTable) {
+       ExecHashTableDestroy(hjstate->hj_HashTable);
+       hjstate->hj_HashTable = NULL;
+    }
+    
+    /* ----------------
+     * Free the projection info and the scan attribute info
+     *
+     *  Note: we don't ExecFreeResultType(hjstate) 
+     *        because the rule manager depends on the tupType
+     *       returned by ExecMain().  So for now, this
+     *       is freed at end-transaction time.  -cim 6/2/91     
+     * ----------------
+     */    
+    ExecFreeProjectionInfo(&hjstate->jstate);
+    
+    /* ----------------
+     * clean up subtrees 
+     * ----------------
+     */
+    ExecEndNode(outerPlan((Plan *) node), (Plan*)node);
+    ExecEndNode(innerPlan((Plan *) node), (Plan*)node);
+    
+    /* ----------------
+     *  clean out the tuple table
+     * ----------------
+     */
+    ExecClearTuple(hjstate->jstate.cs_ResultTupleSlot);
+    ExecClearTuple(hjstate->hj_OuterTupleSlot);
+    ExecClearTuple(hjstate->hj_HashTupleSlot);
+    
+}
+
+/* ----------------------------------------------------------------
+ *     ExecHashJoinOuterGetTuple
+ *
+ *     get the next outer tuple for hashjoin: either by
+ *     executing a plan node as in the first pass, or from
+ *     the tmp files for the hashjoin batches.
+ * ----------------------------------------------------------------
+ */
+
+static TupleTableSlot *
+ExecHashJoinOuterGetTuple(Plan *node, Plan* parent, HashJoinState *hjstate)
+{
+    TupleTableSlot     *slot;
+    HashJoinTable      hashtable;
+    int                        curbatch;
+    File               *outerbatches;
+    char               *outerreadPos;
+    int                batchno;
+    char               *outerreadBuf;
+    int                outerreadBlk;
+    
+    hashtable = hjstate->hj_HashTable;
+    curbatch = hashtable->curbatch;
+    
+    if (curbatch == 0) {  /* if it is the first pass */
+       slot = ExecProcNode(node, parent);
+       return slot;
+    }
+    
+    /*
+     * otherwise, read from the tmp files
+     */
+    outerbatches = hjstate->hj_OuterBatches;
+    outerreadPos = hjstate->hj_OuterReadPos;
+    outerreadBlk = hjstate->hj_OuterReadBlk;
+    outerreadBuf = ABSADDR(hashtable->readbuf); 
+    batchno = curbatch - 1;
+    
+    slot = ExecHashJoinGetSavedTuple(hjstate,
+                                    outerreadBuf,
+                                    outerbatches[batchno],
+                                    hjstate->hj_OuterTupleSlot,
+                                    &outerreadBlk,
+                                    &outerreadPos);
+    
+    hjstate->hj_OuterReadPos = outerreadPos;
+    hjstate->hj_OuterReadBlk = outerreadBlk;
+    
+    return slot;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecHashJoinGetSavedTuple
+ *
+ *     read the next tuple from a tmp file using a certain buffer
+ * ----------------------------------------------------------------
+ */
+
+static TupleTableSlot *
+ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
+                         char *buffer,
+                         File file,
+                         TupleTableSlot *tupleSlot,
+                         int *block,           /* return parameter */
+                         char **position)      /* return parameter */
+{
+    char       *bufstart;
+    char       *bufend;
+    int                cc;
+    HeapTuple  heapTuple;
+    HashJoinTable hashtable;
+    
+    hashtable = hjstate->hj_HashTable;
+    bufend = buffer + *(long*)buffer;
+    bufstart = (char*)(buffer + sizeof(long));
+    if ((*position == NULL) || (*position >= bufend)) {
+       if (*position == NULL)
+           (*block) = 0;
+       else
+           (*block)++;
+       FileSeek(file, *block * BLCKSZ, SEEK_SET);
+       cc = FileRead(file, buffer, BLCKSZ);
+       NDirectFileRead++;
+       if (cc < 0)
+           perror("FileRead");
+       if (cc == 0)  /* end of file */
+           return NULL;
+       else
+           (*position) = bufstart;
+    }
+    heapTuple = (HeapTuple) (*position);
+    (*position) = (char*)LONGALIGN(*position + heapTuple->t_len);
+    
+    return ExecStoreTuple(heapTuple,tupleSlot,InvalidBuffer,false);
+}
+
+/* ----------------------------------------------------------------
+ *     ExecHashJoinNewBatch
+ *
+ *     switch to a new hashjoin batch
+ * ----------------------------------------------------------------
+ */
+int
+ExecHashJoinNewBatch(HashJoinState *hjstate)
+{
+    File               *innerBatches;
+    File               *outerBatches;
+    int                *innerBatchSizes;
+    Var                *innerhashkey;
+    HashJoinTable      hashtable;
+    int                nbatch;
+    char               *readPos;
+    int                readBlk;
+    char               *readBuf;
+    TupleTableSlot     *slot;
+    ExprContext        *econtext;
+    int                i;
+    int                cc;
+    int                        newbatch;
+    
+    hashtable = hjstate->hj_HashTable;
+    outerBatches = hjstate->hj_OuterBatches;
+    innerBatches = hjstate->hj_InnerBatches;
+    nbatch = hashtable->nbatch;
+    newbatch = hashtable->curbatch + 1;
+
+    /* ------------------
+     *  this is the last process, so it will do the cleanup and
+     *  batch-switching.
+     * ------------------
+     */
+       if (newbatch == 1) {
+           /* 
+            * if it is end of the first pass, flush all the last pages for
+            * the batches.
+            */
+           outerBatches = hjstate->hj_OuterBatches;
+           for (i=0; i<nbatch; i++) {
+               cc = FileSeek(outerBatches[i], 0L, SEEK_END);
+               if (cc < 0)
+                   perror("FileSeek");
+               cc = FileWrite(outerBatches[i],
+                              ABSADDR(hashtable->batch) + i * BLCKSZ, BLCKSZ);
+               NDirectFileWrite++;
+               if (cc < 0)
+                   perror("FileWrite");
+           }
+       }
+    if (newbatch > 1) {
+       /*
+        * remove the previous outer batch
+        */
+       FileUnlink(outerBatches[newbatch - 2]);
+    }
+    /*
+     * rebuild the hash table for the new inner batch
+     */
+       innerBatchSizes = (int*)ABSADDR(hashtable->innerbatchSizes);
+    /* --------------
+     *  skip over empty inner batches
+     * --------------
+     */
+       while (newbatch <= nbatch && innerBatchSizes[newbatch - 1] == 0) {
+           FileUnlink(outerBatches[newbatch-1]);
+           FileUnlink(innerBatches[newbatch-1]);
+           newbatch++;
+       }
+    if (newbatch > nbatch) {
+       hashtable->pcount = hashtable->nprocess;
+
+       return newbatch;
+    }
+    ExecHashTableReset(hashtable, innerBatchSizes[newbatch - 1]);
+    
+
+    econtext = hjstate->jstate.cs_ExprContext;
+    innerhashkey = hjstate->hj_InnerHashKey;
+    readPos = NULL;
+    readBlk = 0;
+    readBuf = ABSADDR(hashtable->readbuf);
+    
+    while ((slot = ExecHashJoinGetSavedTuple(hjstate,
+                                            readBuf, 
+                                            innerBatches[newbatch-1],
+                                            hjstate->hj_HashTupleSlot,
+                                            &readBlk,
+                                            &readPos))
+          && ! TupIsNull(slot)) {
+       econtext->ecxt_innertuple = slot;
+       ExecHashTableInsert(hashtable, econtext, innerhashkey,NULL);
+       /* possible bug - glass */
+    }
+    
+    
+    /* -----------------
+     *  only the last process comes to this branch
+     *  now all the processes have finished the build phase
+     * ----------------
+     */
+       
+    /*
+     * after we build the hash table, the inner batch is no longer needed
+     */
+    FileUnlink(innerBatches[newbatch - 1]);
+    hjstate->hj_OuterReadPos = NULL;
+    hashtable->pcount = hashtable->nprocess;
+
+    hashtable->curbatch = newbatch;
+    return newbatch;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecHashJoinGetBatch
+ *
+ *     determine the batch number for a bucketno
+ *      +----------------+-------+-------+ ... +-------+
+ *     0             nbuckets                       totalbuckets
+ * batch         0           1       2     ...
+ * ----------------------------------------------------------------
+ */
+int
+ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable, int nbatch)
+{
+    int b;
+    if (bucketno < hashtable->nbuckets || nbatch == 0)
+       return 0;
+    
+    b = (float)(bucketno - hashtable->nbuckets) /
+       (float)(hashtable->totalbuckets - hashtable->nbuckets) *
+           nbatch;
+    return b+1;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecHashJoinSaveTuple
+ *
+ *     save a tuple to a tmp file using a buffer.
+ *     the first few bytes in a page is an offset to the end
+ *     of the page.
+ * ----------------------------------------------------------------
+ */
+
+char *
+ExecHashJoinSaveTuple(HeapTuple heapTuple,
+                     char *buffer,
+                     File file,
+                     char *position)
+{
+    long       *pageend;
+    char       *pagestart;
+    char       *pagebound;
+    int                cc;
+    
+    pageend = (long*)buffer;
+    pagestart = (char*)(buffer + sizeof(long));
+    pagebound = buffer + BLCKSZ;
+    if (position == NULL)
+       position = pagestart;
+    
+    if (position + heapTuple->t_len >= pagebound) {
+       cc = FileSeek(file, 0L, SEEK_END);
+       if (cc < 0)
+           perror("FileSeek");
+       cc = FileWrite(file, buffer, BLCKSZ);
+       NDirectFileWrite++;
+       if (cc < 0)
+           perror("FileWrite");
+       position = pagestart;
+       *pageend = 0;
+    }
+    memmove(position, heapTuple, heapTuple->t_len);
+    position = (char*)LONGALIGN(position + heapTuple->t_len);
+    *pageend = position - buffer;
+    
+    return position;
+}
diff --git a/src/backend/executor/nodeHashjoin.h b/src/backend/executor/nodeHashjoin.h
new file mode 100644 (file)
index 0000000..d341abe
--- /dev/null
@@ -0,0 +1,33 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeHashjoin.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        NODEHASHJOIN_H
+#define        NODEHASHJOIN_H
+
+extern TupleTableSlot *ExecHashJoin(HashJoin *node);
+
+extern bool ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent);
+
+extern int ExecCountSlotsHashJoin(HashJoin *node);
+
+extern void ExecEndHashJoin(HashJoin *node);
+
+extern int ExecHashJoinNewBatch(HashJoinState *hjstate);
+
+extern char *ExecHashJoinSaveTuple(HeapTuple heapTuple, char *buffer,
+                                  File file, char *position);
+
+extern int ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable,
+                               int nbatch);
+
+
+#endif /* NODEHASHJOIN_H */
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
new file mode 100644 (file)
index 0000000..85149c1
--- /dev/null
@@ -0,0 +1,902 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeIndexscan.c--
+ *    Routines to support indexes and indexed scans of relations
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *     ExecInsertIndexTuples   inserts tuples into indices on result relation
+ *
+ *     ExecIndexScan           scans a relation using indices
+ *     ExecIndexNext           using index to retrieve next tuple
+ *     ExecInitIndexScan       creates and initializes state info. 
+ *     ExecIndexReScan         rescans the indexed relation.
+ *     ExecEndIndexScan        releases all storage.
+ *     ExecIndexMarkPos        marks scan position.
+ *     ExecIndexRestrPos       restores scan position.
+ *
+ *   NOTES
+ *     the code supporting ExecInsertIndexTuples should be
+ *     collected and merged with the genam stuff.
+ *
+ */
+#include "executor/executor.h"
+#include "executor/nodeIndexscan.h"
+
+#include "optimizer/clauses.h" /* for get_op, get_leftop, get_rightop */
+#include "parser/parsetree.h"  /* for rt_fetch() */
+
+#include "access/skey.h"
+#include "utils/palloc.h"
+#include "catalog/index.h"
+#include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+#include "nodes/nodeFuncs.h"
+
+/* ----------------
+ *     Misc stuff to move to executor.h soon -cim 6/5/90
+ * ----------------
+ */
+#define NO_OP          0
+#define LEFT_OP                1
+#define RIGHT_OP       2
+
+static TupleTableSlot *IndexNext(IndexScan *node);
+
+/* ----------------------------------------------------------------
+ *     IndexNext
+ *
+ *     Retrieve a tuple from the IndexScan node's currentRelation
+ *     using the indices in the IndexScanState information.
+ *
+ *     note: the old code mentions 'Primary indices'.  to my knowledge
+ *     we only support a single secondary index. -cim 9/11/89
+ *
+ * old comments:
+ *     retrieve a tuple from relation using the indices given.
+ *     The indices are used in the order they appear in 'indices'.
+ *     The indices may be primary or secondary indices:
+ *       * primary index --    scan the relation 'relID' using keys supplied.
+ *       * secondary index --  scan the index relation to get the 'tid' for
+ *                             a tuple in the relation 'relID'.
+ *     If the current index(pointed by 'indexPtr') fails to return a
+ *     tuple, the next index in the indices is used.
+ *   
+ *        bug fix so that it should retrieve on a null scan key.
+ * ----------------------------------------------------------------
+ */
+static TupleTableSlot *
+IndexNext(IndexScan *node)
+{
+    EState                     *estate;
+    CommonScanState            *scanstate;
+    IndexScanState             *indexstate;
+    ScanDirection              direction;
+    int                                indexPtr;
+    IndexScanDescPtr           scanDescs;
+    IndexScanDesc              scandesc;
+    Relation                   heapRelation;
+    RetrieveIndexResult        result;
+    ItemPointer                        iptr;
+    HeapTuple                  tuple;
+    TupleTableSlot             *slot;
+    Buffer                     buffer = InvalidBuffer;
+
+    /* ----------------
+     * extract necessary information from index scan node
+     * ----------------
+     */
+    estate =           node->scan.plan.state;
+    direction =        estate->es_direction;
+    scanstate =        node->scan.scanstate;
+    indexstate =       node->indxstate;
+    indexPtr =         indexstate->iss_IndexPtr;
+    scanDescs =        indexstate->iss_ScanDescs;
+    scandesc =         scanDescs[ indexPtr ];
+    heapRelation =     scanstate->css_currentRelation;
+
+    slot = scanstate->css_ScanTupleSlot;
+
+    /* ----------------
+     * ok, now that we have what we need, fetch an index tuple.
+     * ----------------
+     */
+
+    for(;;) {
+       result = index_getnext(scandesc, direction);
+       /* ----------------
+        *  if scanning this index succeeded then return the
+        *  appropriate heap tuple.. else return NULL.
+        * ----------------
+        */
+       if (result) {
+           iptr =  &result->heap_iptr;
+           tuple = heap_fetch(heapRelation,
+                              NowTimeQual,
+                              iptr,
+                              &buffer);
+           /* be tidy */
+           pfree(result);
+           
+           if (tuple == NULL) {
+               /* ----------------
+                *   we found a deleted tuple, so keep on scanning..
+                * ----------------
+                */
+               if (BufferIsValid(buffer))
+                   ReleaseBuffer(buffer);
+               continue;
+           }
+           
+           /* ----------------
+            *  store the scanned tuple in the scan tuple slot of
+            *  the scan state.  Eventually we will only do this and not
+            *  return a tuple.  Note: we pass 'false' because tuples
+            *  returned by amgetnext are pointers onto disk pages and
+            *  were not created with palloc() and so should not be pfree()'d.
+            * ----------------
+            */
+           ExecStoreTuple(tuple,         /* tuple to store */
+                          slot,          /* slot to store in */
+                          buffer,        /* buffer associated with tuple  */
+                          false);        /* don't pfree */
+           
+           return slot;
+       }
+       
+       /* ----------------
+        *  if we get here it means the index scan failed so we
+        *  are at the end of the scan..
+        * ----------------
+        */
+       return ExecClearTuple(slot);
+    }
+}
+
+/* ----------------------------------------------------------------
+ *     ExecIndexScan(node)
+ *
+ * old comments:
+ *     Scans the relation using primary or secondary indices and returns 
+ *         the next qualifying tuple in the direction specified.
+ *     It calls ExecScan() and passes it the access methods which returns
+ *     the next tuple using the indices.
+ *     
+ *     Conditions:
+ *       -- the "cursor" maintained by the AMI is positioned at the tuple
+ *          returned previously.
+ *   
+ *     Initial States:
+ *       -- the relation indicated is opened for scanning so that the 
+ *          "cursor" is positioned before the first qualifying tuple.
+ *       -- all index realtions are opened for scanning.
+ *       -- indexPtr points to the first index.
+ *       -- state variable ruleFlag = nil.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecIndexScan(IndexScan *node)
+{
+    TupleTableSlot *returnTuple;
+
+    /* ----------------
+     * use IndexNext as access method
+     * ----------------
+     */
+    returnTuple = ExecScan(&node->scan, IndexNext);
+    return returnTuple;
+}      
+
+/* ----------------------------------------------------------------
+ *     ExecIndexReScan(node)
+ *
+ *     Recalculates the value of the scan keys whose value depends on 
+ *     information known at runtime and rescans the indexed relation.
+ *     Updating the scan key was formerly done separately in 
+ *     ExecUpdateIndexScanKeys. Integrating it into ReScan
+ *     makes rescans of indices and
+ *     relations/general streams more uniform.
+ *
+ * ----------------------------------------------------------------
+ */
+void
+ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan* parent)
+{
+    EState         *estate;
+    IndexScanState  *indexstate;
+    ScanDirection   direction;
+    IndexScanDescPtr scanDescs;
+    ScanKey        *scanKeys;
+    IndexScanDesc   sdesc;
+    ScanKey        skey;
+    int                    numIndices;
+    int            i;
+
+    Pointer            *runtimeKeyInfo;
+    int                        indexPtr;
+    int                        *numScanKeys;
+    List               *indxqual;
+    List               *qual;
+    int                n_keys;
+    ScanKey            scan_keys;
+    int                        *run_keys;
+    int                        j;
+    Expr               *clause;
+    Node               *scanexpr;
+    Datum              scanvalue;
+    bool               isNull;
+    bool               isDone;
+
+    indexstate = node->indxstate;
+    estate = node->scan.plan.state;
+    direction = estate->es_direction;
+    indexstate = node->indxstate;
+    numIndices = indexstate->iss_NumIndices;
+    scanDescs = indexstate->iss_ScanDescs;
+    scanKeys = indexstate->iss_ScanKeys;
+
+    runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo;
+
+    if (runtimeKeyInfo != NULL) {
+       /* 
+        * get the index qualifications and
+        * recalculate the appropriate values
+        */
+       indexPtr = indexstate->iss_IndexPtr;
+       indxqual = node->indxqual;
+       qual = nth(indexPtr, indxqual);
+       numScanKeys = indexstate->iss_NumScanKeys;
+       n_keys = numScanKeys[indexPtr];
+       run_keys = (int *) runtimeKeyInfo[indexPtr];
+       scan_keys = (ScanKey) scanKeys[indexPtr];
+
+       for (j=0; j < n_keys; j++) {
+           /* 
+            * If we have a run-time key, then extract the run-time
+            * expression and evaluate it with respect to the current
+            * outer tuple.  We then stick the result into the scan
+            * key.
+            */
+           if (run_keys[j] != NO_OP) {
+               clause =    nth(j, qual);
+               scanexpr =  (run_keys[j] == RIGHT_OP) ?
+                   (Node*) get_rightop(clause) : (Node*) get_leftop(clause) ;
+               /* pass in isDone but ignore it.  We don't iterate in quals */
+               scanvalue = (Datum)
+                   ExecEvalExpr(scanexpr, exprCtxt, &isNull, &isDone);
+               scan_keys[j].sk_argument = scanvalue;
+           }
+       }
+    } 
+
+    /* 
+     * rescans all indices
+     *
+     * note: AMrescan assumes only one scan key.  This may have
+     *      to change if we ever decide to support multiple keys.
+     */
+    for (i = 0; i < numIndices; i++) {
+       sdesc = scanDescs[ i ];
+       skey =  scanKeys[ i ];
+       index_rescan(sdesc, direction, skey);
+    }
+
+    /* ----------------
+     * perhaps return something meaningful
+     * ----------------
+     */
+    return;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecEndIndexScan
+ *
+ * old comments
+ *     Releases any storage allocated through C routines.
+ *     Returns nothing.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndIndexScan(IndexScan *node)
+{
+    CommonScanState    *scanstate;
+    IndexScanState     *indexstate;
+    ScanKey            *scanKeys;
+    int                        numIndices;
+    int                i;
+
+    scanstate =  node->scan.scanstate;
+    indexstate = node->indxstate;
+
+    /* ----------------
+     * extract information from the node
+     * ----------------
+     */
+    numIndices = indexstate->iss_NumIndices;
+    scanKeys =   indexstate->iss_ScanKeys;
+
+    /* ----------------
+     * Free the projection info and the scan attribute info
+     *
+     *  Note: we don't ExecFreeResultType(scanstate) 
+     *        because the rule manager depends on the tupType
+     *       returned by ExecMain().  So for now, this
+     *       is freed at end-transaction time.  -cim 6/2/91     
+     * ----------------
+     */    
+    ExecFreeProjectionInfo(&scanstate->cstate);
+
+    /* ----------------
+     * close the heap and index relations
+     * ----------------
+     */
+    ExecCloseR((Plan *) node);
+
+    /* ----------------
+     * free the scan keys used in scanning the indices
+     * ----------------
+     */
+    for (i=0; i<numIndices; i++) { 
+       if (scanKeys[i]!=NULL)
+           pfree(scanKeys[i]);
+           
+    }
+
+    /* ----------------
+     * clear out tuple table slots
+     * ----------------
+     */
+    ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
+    ExecClearTuple(scanstate->css_ScanTupleSlot);
+/*    ExecClearTuple(scanstate->css_RawTupleSlot); */
+}
+
+/* ----------------------------------------------------------------
+ *     ExecIndexMarkPos
+ *
+ * old comments
+ *     Marks scan position by marking the current index.
+ *     Returns nothing.
+ * ----------------------------------------------------------------
+ */
+void
+ExecIndexMarkPos(IndexScan *node)
+{
+    IndexScanState     *indexstate;
+    IndexScanDescPtr   indexScanDescs;
+    IndexScanDesc      scanDesc;
+    int                        indexPtr;
+
+    indexstate =     node->indxstate;
+    indexPtr =       indexstate->iss_IndexPtr;
+    indexScanDescs = indexstate->iss_ScanDescs;
+    scanDesc = indexScanDescs[ indexPtr ];
+
+    /* ----------------
+     * XXX access methods don't return marked positions so
+     * ----------------
+     */
+    IndexScanMarkPosition( scanDesc );
+    return;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecIndexRestrPos
+ *
+ * old comments
+ *     Restores scan position by restoring the current index.
+ *     Returns nothing.
+ *    
+ *     XXX Assumes previously marked scan position belongs to current index
+ * ----------------------------------------------------------------
+ */
+void
+ExecIndexRestrPos(IndexScan *node)
+{
+    IndexScanState     *indexstate;
+    IndexScanDescPtr   indexScanDescs;
+    IndexScanDesc      scanDesc;
+    int                        indexPtr;
+
+    indexstate =     node->indxstate;
+    indexPtr =       indexstate->iss_IndexPtr;
+    indexScanDescs = indexstate->iss_ScanDescs;
+    scanDesc = indexScanDescs[ indexPtr ];
+
+    IndexScanRestorePosition( scanDesc );
+}
+
+/* ----------------------------------------------------------------
+ *     ExecInitIndexScan
+ *
+ *     Initializes the index scan's state information, creates
+ *     scan keys, and opens the base and index relations.
+ *
+ *     Note: index scans have 2 sets of state information because
+ *           we have to keep track of the base relation and the
+ *           index relations.
+ *           
+ * old comments
+ *     Creates the run-time state information for the node and 
+ *     sets the relation id to contain relevant decriptors.
+ *    
+ *     Parameters: 
+ *       node: IndexNode node produced by the planner.
+ *       estate: the execution state initialized in InitPlan.
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
+{
+    IndexScanState     *indexstate;
+    CommonScanState    *scanstate;
+    List               *indxqual;
+    List               *indxid;
+    int                        i;
+    int                        numIndices;
+    int                        indexPtr;
+    ScanKey            *scanKeys;
+    int                        *numScanKeys;
+    RelationPtr                relationDescs;
+    IndexScanDescPtr   scanDescs;
+    Pointer            *runtimeKeyInfo;
+    bool               have_runtime_keys;
+    List               *rangeTable;
+    RangeTblEntry      *rtentry;
+    Index              relid;
+    Oid                        reloid;
+    TimeQual           timeQual;
+
+    Relation           currentRelation;
+    HeapScanDesc       currentScanDesc;
+    ScanDirection      direction;
+    int                        baseid;
+
+    /* ----------------
+     * assign execution state to node
+     * ----------------
+     */
+    node->scan.plan.state = estate;
+
+    /* --------------------------------
+     *  Part 1)  initialize scan state
+     *
+     * create new CommonScanState for node
+     * --------------------------------
+     */
+    scanstate = makeNode(CommonScanState);
+/*
+    scanstate->ss_ProcOuterFlag = false;
+    scanstate->ss_OldRelId = 0;
+*/
+
+    node->scan.scanstate = scanstate;
+
+    /* ----------------
+     * assign node's base_id .. we don't use AssignNodeBaseid() because
+     *  the increment is done later on after we assign the index scan's
+     *  scanstate.  see below. 
+     * ----------------
+     */
+    baseid = estate->es_BaseId;
+/*    scanstate->csstate.cstate.bnode.base_id = baseid; */
+    scanstate->cstate.cs_base_id = baseid;
+
+    /* ----------------
+     *  create expression context for node
+     * ----------------
+     */
+    ExecAssignExprContext(estate, &scanstate->cstate);
+
+#define INDEXSCAN_NSLOTS 3
+    /* ----------------
+     * tuple table initialization
+     * ----------------
+     */
+    ExecInitResultTupleSlot(estate, &scanstate->cstate); 
+    ExecInitScanTupleSlot(estate, scanstate);
+/*    ExecInitRawTupleSlot(estate, scanstate); */
+
+    /* ----------------
+     *         initialize projection info.  result type comes from scan desc
+     *  below..
+     * ----------------
+     */
+    ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate);
+
+   /* --------------------------------
+     *  Part 2)  initialize index scan state
+     *
+     * create new IndexScanState for node
+     * --------------------------------
+     */
+    indexstate = makeNode(IndexScanState);
+    indexstate->iss_NumIndices = 0;
+    indexstate->iss_IndexPtr = 0;
+    indexstate->iss_ScanKeys = NULL;
+    indexstate->iss_NumScanKeys = NULL;
+    indexstate->iss_RuntimeKeyInfo = NULL;
+    indexstate->iss_RelationDescs = NULL;
+    indexstate->iss_ScanDescs = NULL;
+    
+    node->indxstate = indexstate;
+
+    /* ----------------
+     * assign base id to index scan state also
+     * ----------------
+     */
+    indexstate->cstate.cs_base_id = baseid;
+    baseid++;
+    estate->es_BaseId = baseid;
+
+    /* ----------------
+     * get the index node information
+     * ----------------
+     */
+    indxid =   node->indxid;
+    indxqual =         node->indxqual;
+    numIndices = length(indxid);
+    indexPtr =          0;
+
+    CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext);
+
+    /* ----------------
+     * scanKeys is used to keep track of the ScanKey's. This is needed
+     * because a single scan may use several indices and each index has
+     *  its own ScanKey.
+     * ----------------
+     */
+    numScanKeys = (int *) palloc(numIndices * sizeof(int));
+    scanKeys = (ScanKey *) palloc(numIndices * sizeof(ScanKey));
+    relationDescs = (RelationPtr) palloc(numIndices * sizeof(Relation));
+    scanDescs =  (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc));
+
+    /* ----------------
+     * initialize runtime key info.
+     * ----------------
+     */
+    have_runtime_keys = false;
+    runtimeKeyInfo = (Pointer *)
+       palloc(numIndices * sizeof(Pointer));
+
+    /* ----------------
+     * build the index scan keys from the index qualification
+     * ----------------
+     */
+    for (i=0; i < numIndices; i++) {
+       int             j;
+       List            *qual;
+       int             n_keys;
+       ScanKey         scan_keys;
+       int             *run_keys;
+       
+       qual =          nth(i, indxqual);
+       n_keys =        length(qual);
+       scan_keys = (n_keys <= 0) ? NULL :
+           (ScanKey)palloc(n_keys * sizeof(ScanKeyData));
+       
+       CXT1_printf("ExecInitIndexScan: context is %d\n",
+                   CurrentMemoryContext);
+       
+       if (n_keys > 0) {
+           run_keys = (int *)  palloc(n_keys * sizeof(int));
+       }
+       
+       /* ----------------
+        *  for each opclause in the given qual,
+        *  convert each qual's opclause into a single scan key
+        * ----------------
+        */
+       for (j=0; j < n_keys; j++) {
+           Expr        *clause;                /* one part of index qual */
+           Oper        *op;            /* operator used in scan.. */
+           Node        *leftop;                /* expr on lhs of operator */
+           Node        *rightop;       /* expr on rhs ... */
+           
+           int         scanvar;        /* which var identifies varattno */
+           AttrNumber  varattno;       /* att number used in scan */
+           Oid         opid;           /* operator id used in scan */
+           Datum       scanvalue;      /* value used in scan (if const) */
+           
+           /* ----------------
+            *  extract clause information from the qualification
+            * ----------------
+            */
+           clause =    nth(j, qual);
+           
+           op = (Oper*)clause->oper;
+           if (!IsA(op,Oper))
+               elog(WARN, "ExecInitIndexScan: op not an Oper!");
+           
+           opid = op->opid;
+           
+           /* ----------------
+            *  Here we figure out the contents of the index qual.
+            *  The usual case is (op var const) or (op const var)
+            *  which means we form a scan key for the attribute
+            *  listed in the var node and use the value of the const.
+            *
+            *  If we don't have a const node, then it means that
+            *  one of the var nodes refers to the "scan" tuple and
+            *  is used to determine which attribute to scan, and the
+            *  other expression is used to calculate the value used in
+            *  scanning the index.
+            *
+            *  This means our index scan's scan key is a function of
+            *  information obtained during the execution of the plan
+            *  in which case we need to recalculate the index scan key
+            *  at run time.
+            *  
+            *  Hence, we set have_runtime_keys to true and then set
+            *  the appropriate flag in run_keys to LEFT_OP or RIGHT_OP.
+            *  The corresponding scan keys are recomputed at run time.
+            * ----------------
+            */
+           
+           scanvar = NO_OP;
+           
+           /* ----------------
+            *  determine information in leftop
+            * ----------------
+            */
+           leftop =    (Node*) get_leftop(clause);
+           
+           if (IsA(leftop,Var) && var_is_rel((Var*)leftop)) {
+               /* ----------------
+                *  if the leftop is a "rel-var", then it means
+                *  that it is a var node which tells us which
+                *  attribute to use for our scan key.
+                * ----------------
+                */
+               varattno =      ((Var*) leftop)->varattno;
+               scanvar =       LEFT_OP;
+           } else if (IsA(leftop,Const)) {
+               /* ----------------
+                *  if the leftop is a const node then it means
+                *  it identifies the value to place in our scan key.
+                * ----------------
+                */
+               run_keys[ j ] = NO_OP;
+               scanvalue = ((Const*) leftop)->constvalue;
+           } else if (leftop != NULL &&
+                      is_funcclause(leftop) &&
+                      var_is_rel(lfirst(((Expr*)leftop)->args))) {
+               /* ----------------
+                *  if the leftop is a func node then it means
+                *  it identifies the value to place in our scan key.
+                *  Since functional indices have only one attribute
+                *  the attno must always be set to 1.
+                * ----------------
+                */
+               varattno =      1;
+               scanvar =       LEFT_OP;
+               
+           } else {
+               /* ----------------
+                *  otherwise, the leftop contains information usable
+                *  at runtime to figure out the value to place in our
+                *  scan key.
+                * ----------------
+                */
+               have_runtime_keys = true;
+               run_keys[ j ] = LEFT_OP;
+               scanvalue = Int32GetDatum((int32) true);
+           }
+           
+           /* ----------------
+            *  now determine information in rightop
+            * ----------------
+            */
+           rightop =   (Node*) get_rightop(clause);
+           
+           if (IsA(rightop,Var) && var_is_rel((Var*)rightop)) {
+               /* ----------------
+                *  here we make sure only one op identifies the
+                *  scan-attribute...
+                * ----------------
+                */
+               if (scanvar == LEFT_OP)
+                   elog(WARN, "ExecInitIndexScan: %s",
+                        "both left and right op's are rel-vars");
+               
+               /* ----------------
+                *  if the rightop is a "rel-var", then it means
+                *  that it is a var node which tells us which
+                *  attribute to use for our scan key.
+                * ----------------
+                */
+               varattno =      ((Var*) rightop)->varattno;
+               scanvar =       RIGHT_OP;
+               
+           } else if (IsA(rightop,Const)) {
+               /* ----------------
+                *  if the leftop is a const node then it means
+                *  it identifies the value to place in our scan key.
+                * ----------------
+                */
+               run_keys[ j ] = NO_OP;
+               scanvalue = ((Const*) rightop)->constvalue;
+               
+           } else if (rightop!=NULL &&
+                      is_funcclause(rightop) &&
+                      var_is_rel(lfirst(((Expr*)rightop)->args))) {
+               /* ----------------
+                *  if the rightop is a func node then it means
+                *  it identifies the value to place in our scan key.
+                *  Since functional indices have only one attribute
+                *  the attno must always be set to 1.
+                * ----------------
+                */
+               if (scanvar == LEFT_OP)
+                   elog(WARN, "ExecInitIndexScan: %s",
+                        "both left and right ops are rel-vars");
+               
+               varattno =      1;
+               scanvar =       RIGHT_OP;
+               
+           } else {
+               /* ----------------
+                *  otherwise, the leftop contains information usable
+                *  at runtime to figure out the value to place in our
+                *  scan key.
+                * ----------------
+                */
+               have_runtime_keys = true;
+               run_keys[ j ] = RIGHT_OP;
+               scanvalue = Int32GetDatum((int32) true);
+           }
+           
+           /* ----------------
+            *  now check that at least one op tells us the scan
+            *  attribute...
+            * ----------------
+            */
+           if (scanvar == NO_OP) 
+               elog(WARN, "ExecInitIndexScan: %s",
+                    "neither leftop nor rightop refer to scan relation");
+           
+           /* ----------------
+            *  initialize the scan key's fields appropriately
+            * ----------------
+            */
+           ScanKeyEntryInitialize(&scan_keys[j],
+                                  0,
+                                  varattno, /* attribute number to scan */
+                                  (RegProcedure) opid, /* reg proc to use */
+                                  (Datum) scanvalue);  /* constant */
+       }
+       
+       /* ----------------
+        *  store the key information into our array.
+        * ----------------
+        */
+       numScanKeys[ i ] =      n_keys;
+       scanKeys[ i ] =         scan_keys;
+       runtimeKeyInfo[ i ] =   (Pointer) run_keys;
+    }
+
+    indexstate->iss_NumIndices = numIndices;
+    indexstate->iss_IndexPtr = indexPtr;
+    indexstate->iss_ScanKeys = scanKeys;
+    indexstate->iss_NumScanKeys = numScanKeys;
+
+    /* ----------------
+     * If all of our keys have the form (op var const) , then we have no
+     *  runtime keys so we store NULL in the runtime key info.
+     *  Otherwise runtime key info contains an array of pointers
+     *  (one for each index) to arrays of flags (one for each key)
+     *  which indicate that the qual needs to be evaluated at runtime.
+     *  -cim 10/24/89
+     * ----------------
+     */
+    if (have_runtime_keys)
+       {
+           indexstate->iss_RuntimeKeyInfo = (Pointer) runtimeKeyInfo;
+       }
+    else {
+       indexstate->iss_RuntimeKeyInfo = NULL;
+       for (i=0; i < numIndices; i++) {
+           List *qual;
+           int n_keys;
+           qual = nth(i, indxqual);
+           n_keys = length(qual);
+           if (n_keys > 0)
+               pfree(runtimeKeyInfo[i]);
+       }
+       pfree(runtimeKeyInfo);
+    }
+
+    /* ----------------
+     * get the range table and direction information
+     *  from the execution state (these are needed to
+     *  open the relations).
+     * ----------------
+     */
+    rangeTable = estate->es_range_table;
+    direction =  estate->es_direction;
+
+    /* ----------------
+     * open the base relation
+     * ----------------
+     */
+    relid =   node->scan.scanrelid;
+    rtentry = rt_fetch(relid, rangeTable);
+    reloid =  rtentry->relid;
+    timeQual = rtentry->timeQual;
+
+    ExecOpenScanR(reloid,            /* relation */
+                 0,                  /* nkeys */
+                 (ScanKey) NULL,     /* scan key */
+                 0,                  /* is index */
+                 direction,          /* scan direction */
+                 timeQual,           /* time qual */
+                 &currentRelation,   /* return: rel desc */
+                 (Pointer *) &currentScanDesc);  /* return: scan desc */
+
+    scanstate->css_currentRelation = currentRelation;
+    scanstate->css_currentScanDesc = currentScanDesc;
+
+
+    /* ----------------
+     * get the scan type from the relation descriptor.
+     * ----------------
+     */
+    ExecAssignScanType(scanstate, RelationGetTupleDescriptor(currentRelation));
+    ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
+
+    /* ----------------
+     * index scans don't have subtrees..
+     * ----------------
+     */
+/*    scanstate->ss_ProcOuterFlag = false; */
+
+    /* ----------------
+     * open the index relations and initialize
+     *  relation and scan descriptors.
+     * ----------------
+     */
+    for (i=0; i < numIndices; i++) {
+       Oid     indexOid;
+       
+       indexOid =  (Oid)nth(i, indxid);
+       
+       if (indexOid != 0) {
+           ExecOpenScanR(indexOid,               /* relation */
+                         numScanKeys[ i ],       /* nkeys */
+                         scanKeys[ i ],          /* scan key */
+                         true,                   /* is index */
+                         direction,              /* scan direction */
+                         timeQual,               /* time qual */
+                         &(relationDescs[ i ]),  /* return: rel desc */
+                         (Pointer *) &(scanDescs[ i ]));
+           /* return: scan desc */
+       }
+    }
+
+    indexstate->iss_RelationDescs = relationDescs;
+    indexstate->iss_ScanDescs = scanDescs;
+
+    indexstate->cstate.cs_TupFromTlist = false;
+
+    /* ----------------
+     * all done.
+     * ----------------
+     */
+    return TRUE;
+}
+
+int
+ExecCountSlotsIndexScan(IndexScan *node)
+{
+    return ExecCountSlotsNode(outerPlan((Plan *)node)) +
+       ExecCountSlotsNode(innerPlan((Plan *)node)) +
+           INDEXSCAN_NSLOTS;
+}
diff --git a/src/backend/executor/nodeIndexscan.h b/src/backend/executor/nodeIndexscan.h
new file mode 100644 (file)
index 0000000..c71894c
--- /dev/null
@@ -0,0 +1,32 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeIndexscan.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        NODEINDEXSCAN_H
+#define        NODEINDEXSCAN_H
+
+extern TupleTableSlot *ExecIndexScan(IndexScan *node);
+
+extern void ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent);
+
+extern void ExecEndIndexScan(IndexScan *node);
+
+extern void ExecIndexMarkPos(IndexScan *node);
+
+extern void ExecIndexRestrPos(IndexScan *node);
+
+extern void ExecUpdateIndexScanKeys(IndexScan *node, ExprContext *econtext);
+
+extern bool ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent);
+
+extern int ExecCountSlotsIndexScan(IndexScan *node);
+
+#endif /* NODEINDEXSCAN_H */
diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c
new file mode 100644 (file)
index 0000000..50d7d78
--- /dev/null
@@ -0,0 +1,392 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeMaterial.c--
+ *    Routines to handle materialization nodes.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *             ExecMaterial            - generate a temporary relation
+ *             ExecInitMaterial        - initialize node and subnodes..
+ *             ExecEndMaterial         - shutdown node and subnodes
+ *
+ */
+
+#include "executor/executor.h"
+#include "executor/nodeMaterial.h"
+#include "catalog/catalog.h"
+#include "optimizer/internal.h" /* for _TEMP_RELATION_ID_ */
+
+/* ----------------------------------------------------------------
+ *     ExecMaterial
+ *
+ *     The first time this is called, ExecMaterial retrieves tuples
+ *     this node's outer subplan and inserts them into a temporary
+ *     relation.  After this is done, a flag is set indicating that
+ *     the subplan has been materialized.  Once the relation is
+ *     materialized, the first tuple is then returned.  Successive
+ *     calls to ExecMaterial return successive tuples from the temp
+ *     relation.
+ *
+ *     Initial State:
+ *
+ *     ExecMaterial assumes the temporary relation has been
+ *     created and openend by ExecInitMaterial during the prior
+ *     InitPlan() phase.
+ *
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *                       /* result tuple from subplan */
+ExecMaterial(Material *node)
+{
+    EState             *estate;
+    MaterialState      *matstate;
+    Plan               *outerNode;
+    ScanDirection      dir;
+    Relation           tempRelation;
+    Relation           currentRelation;
+    HeapScanDesc       currentScanDesc;
+    HeapTuple          heapTuple;
+    TupleTableSlot     *slot;
+    Buffer             buffer;
+    
+    /* ----------------
+     * get state info from node
+     * ----------------
+     */
+    matstate =         node->matstate;
+    estate =           node->plan.state;
+    dir =              estate->es_direction;
+    
+    /* ----------------
+     * the first time we call this, we retrieve all tuples
+     *  from the subplan into a temporary relation and then
+     *  we sort the relation.  Subsequent calls return tuples
+     *  from the temporary relation.
+     * ----------------
+     */
+    
+    if (matstate->mat_Flag == false) {
+       /* ----------------
+        *  set all relations to be scanned in the forward direction 
+        *  while creating the temporary relation.
+        * ----------------
+        */
+       estate->es_direction = EXEC_FRWD;
+       
+       /* ----------------
+        *   if we couldn't create the temp or current relations then
+        *   we print a warning and return NULL.
+        * ----------------
+        */
+       tempRelation =  matstate->mat_TempRelation;
+       if (tempRelation == NULL) {
+           elog(DEBUG, "ExecMaterial: temp relation is NULL! aborting...");
+           return NULL;
+       }
+       
+       currentRelation = matstate->csstate.css_currentRelation;
+       if (currentRelation == NULL) {
+           elog(DEBUG, "ExecMaterial: current relation is NULL! aborting...");
+           return NULL;
+       }
+       
+       /* ----------------
+        *   retrieve tuples from the subplan and
+        *   insert them in the temporary relation
+        * ----------------
+        */
+       outerNode = outerPlan((Plan *) node);
+       for (;;) {
+           slot = ExecProcNode(outerNode, (Plan*) node);
+           
+           heapTuple = slot->val;
+           if (heapTuple == NULL)
+               break;
+           
+           heap_insert(tempRelation,   /* relation desc */
+                       heapTuple);     /* heap tuple to insert */
+           
+           ExecClearTuple( slot);
+       }
+       currentRelation = tempRelation;
+       
+       /* ----------------
+        *   restore to user specified direction
+        * ----------------
+        */
+       estate->es_direction = dir;
+       
+       /* ----------------
+        *   now initialize the scan descriptor to scan the
+        *   sorted relation and update the sortstate information
+        * ----------------
+        */
+       currentScanDesc = heap_beginscan(currentRelation,    /* relation */
+                                        ScanDirectionIsBackward(dir),
+                                        /* bkwd flag */
+                                        NowTimeQual,        /* time qual */
+                                        0,               /* num scan keys */
+                                        NULL);           /* scan keys */
+       matstate->csstate.css_currentRelation = currentRelation;
+       matstate->csstate.css_currentScanDesc = currentScanDesc;
+
+       ExecAssignScanType(&matstate->csstate,
+                          RelationGetTupleDescriptor(currentRelation));
+       
+       /* ----------------
+        *  finally set the sorted flag to true
+        * ----------------
+        */
+       matstate->mat_Flag = true;
+    }
+    
+    /* ----------------
+     * at this point we know we have a sorted relation so
+     *  we preform a simple scan on it with amgetnext()..
+     * ----------------
+     */
+    currentScanDesc = matstate->csstate.css_currentScanDesc;
+    
+    heapTuple = heap_getnext(currentScanDesc,  /* scan desc */
+                            ScanDirectionIsBackward(dir),
+                            /* bkwd flag */
+                            &buffer);          /* return: buffer */
+    
+    /* ----------------
+     * put the tuple into the scan tuple slot and return the slot.
+     *  Note: since the tuple is really a pointer to a page, we don't want
+     *  to call pfree() on it..
+     * ----------------
+     */
+    slot = (TupleTableSlot *)matstate->csstate.css_ScanTupleSlot;
+    
+    return ExecStoreTuple(heapTuple,  /* tuple to store */
+                         slot,      /* slot to store in */
+                         buffer,   /* buffer for this tuple */
+                         false);   /* don't pfree this pointer */
+    
+}
+
+/* ----------------------------------------------------------------
+ *     ExecInitMaterial
+ * ----------------------------------------------------------------
+ */
+bool   /* initialization status */
+ExecInitMaterial(Material *node, EState *estate, Plan *parent)
+{
+    MaterialState      *matstate;
+    Plan               *outerPlan;
+    TupleDesc          tupType;
+    Relation           tempDesc;
+    int                        len;
+    
+    /* ----------------
+     *  assign the node's execution state
+     * ----------------
+     */
+    node->plan.state = estate;
+    
+    /* ----------------
+     * create state structure
+     * ----------------
+     */
+    matstate = makeNode(MaterialState);
+    matstate->mat_Flag = false;
+    matstate->mat_TempRelation = NULL;
+    node->matstate = matstate;
+    
+    /* ----------------
+     *  Miscellanious initialization
+     *
+     *      +  assign node's base_id
+     *       + assign debugging hooks and
+     *       +  assign result tuple slot
+     *
+     *  Materialization nodes don't need ExprContexts because
+     *  they never call ExecQual or ExecTargetList.
+     * ----------------
+     */
+    ExecAssignNodeBaseInfo(estate, &matstate->csstate.cstate, parent);
+    
+#define MATERIAL_NSLOTS 1
+    /* ----------------
+     * tuple table initialization
+     * ----------------
+     */
+    ExecInitScanTupleSlot(estate, &matstate->csstate);
+    
+    /* ----------------
+     * initializes child nodes
+     * ----------------
+     */
+    outerPlan = outerPlan((Plan *) node);
+    ExecInitNode(outerPlan, estate, (Plan *) node);
+    
+    /* ----------------
+     * initialize matstate information
+     * ----------------
+     */
+    matstate->mat_Flag = false;
+    
+    /* ----------------
+     *         initialize tuple type.  no need to initialize projection
+     *  info because this node doesn't do projections.
+     * ----------------
+     */
+    ExecAssignScanTypeFromOuterPlan((Plan *) node, &matstate->csstate);
+    matstate->csstate.cstate.cs_ProjInfo = NULL;
+    
+    /* ----------------
+     * get type information needed for ExecCreatR
+     * ----------------
+     */
+    tupType = ExecGetScanType(&matstate->csstate);
+    
+    /* ----------------
+     * ExecCreatR wants it's second argument to be an object id of
+     *  a relation in the range table or a _TEMP_RELATION_ID
+     *  indicating that the relation is not in the range table.
+     *
+     *  In the second case ExecCreatR creates a temp relation.
+     *  (currently this is the only case we support -cim 10/16/89)
+     * ----------------
+     */
+    /* ----------------
+     * create the temporary relation
+     * ----------------
+     */
+/*    len = ExecTargetListLength(node->plan.targetlist); */
+    tempDesc =         ExecCreatR(tupType, _TEMP_RELATION_ID_);
+    
+    /* ----------------
+     * save the relation descriptor in the sortstate
+     * ----------------
+     */
+    matstate->mat_TempRelation = tempDesc;
+    matstate->csstate.css_currentRelation = tempDesc;
+    
+    /* ----------------
+     *  return relation oid of temporary relation in a list
+     * (someday -- for now we return LispTrue... cim 10/12/89)
+     * ----------------
+     */
+    return TRUE;
+}
+
+int
+ExecCountSlotsMaterial(Material *node)
+{
+    return ExecCountSlotsNode(outerPlan((Plan *)node)) +
+       ExecCountSlotsNode(innerPlan((Plan *)node)) +
+           MATERIAL_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecEndMaterial
+ *
+ * old comments
+ *     destroys the temporary relation.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndMaterial(Material *node)
+{
+    MaterialState      *matstate;
+    Relation           tempRelation;
+    Plan               *outerPlan;
+    
+    /* ----------------
+     * get info from the material state 
+     * ----------------
+     */
+    matstate = node->matstate;
+    tempRelation = matstate->mat_TempRelation;
+    
+    heap_destroyr(tempRelation);
+    
+    /* ----------------
+     * close the temp relation and shut down the scan.
+     * ----------------
+     */
+    ExecCloseR((Plan *) node);
+    
+    /* ----------------
+     * shut down the subplan
+     * ----------------
+     */
+    outerPlan = outerPlan((Plan *) node);
+    ExecEndNode(outerPlan, (Plan*) node);
+    
+    /* ----------------
+     * clean out the tuple table
+     * ----------------
+     */
+    ExecClearTuple(matstate->csstate.css_ScanTupleSlot);
+} 
+
+#if 0  /* not used */
+/* ----------------------------------------------------------------
+ *     ExecMaterialMarkPos
+ * ----------------------------------------------------------------
+ */
+List /* nothing of interest */
+ExecMaterialMarkPos(Material node)
+{
+    MaterialState      matstate;
+    HeapScanDesc       sdesc;
+    
+    /* ----------------
+     * if we haven't materialized yet, just return NIL.
+     * ----------------
+     */
+    matstate =          get_matstate(node);
+    if (get_mat_Flag(matstate) == false)
+       return NIL;
+    
+    /* ----------------
+     *  XXX access methods don't return positions yet so
+     *      for now we return NIL.  It's possible that
+     *      they will never return positions for all I know -cim 10/16/89
+     * ----------------
+     */
+    sdesc = get_css_currentScanDesc((CommonScanState)matstate);
+    heap_markpos(sdesc);
+    
+    return NIL;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecMaterialRestrPos
+ * ----------------------------------------------------------------
+ */
+void
+ExecMaterialRestrPos(Material node)
+{
+    MaterialState      matstate;
+    HeapScanDesc       sdesc;
+    
+    /* ----------------
+     * if we haven't materialized yet, just return.
+     * ----------------
+     */
+    matstate =  get_matstate(node);
+    if (get_mat_Flag(matstate) == false)
+       return;
+    
+    /* ----------------
+     * restore the scan to the previously marked position
+     * ----------------
+     */
+    sdesc = get_css_currentScanDesc((CommonScanState)matstate);
+    heap_restrpos(sdesc);
+}
+#endif
+
diff --git a/src/backend/executor/nodeMaterial.h b/src/backend/executor/nodeMaterial.h
new file mode 100644 (file)
index 0000000..31af727
--- /dev/null
@@ -0,0 +1,23 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeMaterial.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        NODEMATERIAL_H
+#define        NODEMATERIAL_H
+
+extern TupleTableSlot *ExecMaterial(Material *node);
+extern bool ExecInitMaterial(Material *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsMaterial(Material *node);
+extern void ExecEndMaterial(Material *node);
+extern List ExecMaterialMarkPos(Material *node);
+extern void ExecMaterialRestrPos(Material *node);
+
+#endif /* NODEMATERIAL_H */
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
new file mode 100644 (file)
index 0000000..a19ee64
--- /dev/null
@@ -0,0 +1,1194 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeMergejoin.c--
+ *    routines supporting merge joins
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *     ExecMergeJoin           mergejoin outer and inner relations.
+ *     ExecInitMergeJoin       creates and initializes run time states
+ *     ExecEndMergeJoin        cleand up the node.
+ *
+ * NOTES
+ *     Essential operation of the merge join algorithm is as follows:
+ *     (** indicates the tuples satisify the merge clause).
+ *
+ *     Join {                                                 -
+ *         get initial outer and inner tuples              INITIALIZE
+ *         Skip Inner                                      SKIPINNER
+ *         mark inner position                             JOINMARK
+ *         do forever {                                       -
+ *             while (outer ** inner) {                    JOINTEST
+ *                 join tuples                             JOINTUPLES
+ *                 advance inner position                  NEXTINNER
+ *             }                                              -
+ *             advance outer position                      NEXTOUTER
+ *             if (outer ** mark) {                        TESTOUTER
+ *                  restore inner position to mark          TESTOUTER
+ *                 continue                                   -
+ *             } else {                                       -
+ *                 Skip Outer                              SKIPOUTER
+ *                 mark inner position                     JOINMARK
+ *             }                                              -
+ *         }                                                  -
+ *      }                                                     -
+ *
+ *      Skip Outer {                                       SKIPOUTER
+ *         if (inner ** outer) Join Tuples                 JOINTUPLES
+ *         while (outer < inner)                           SKIPOUTER
+ *             advance outer                               SKIPOUTER
+ *         if (outer > inner)                              SKIPOUTER
+ *             Skip Inner                                  SKIPINNER
+ *     }                                                      -
+ *
+ *     Skip Inner {                                        SKIPINNER
+ *         if (inner ** outer) Join Tuples                 JOINTUPLES
+ *         while (outer > inner)                           SKIPINNER
+ *             advance inner                               SKIPINNER
+ *         if (outer < inner)                              SKIPINNER
+ *             Skip Outer                                  SKIPOUTER
+ *     }                                                      -
+ *
+ *     Currently, the merge join operation is coded in the fashion
+ *     of a state machine.  At each state, we do something and then
+ *     proceed to another state.  This state is stored in the node's
+ *     execution state information and is preserved across calls to
+ *     ExecMergeJoin. -cim 10/31/89
+ *
+ *     Warning:  This code is known to fail for inequality operations
+ *               and is being redesigned.  Specifically, = and > work
+ *               but the logic is not correct for <.  Since mergejoins
+ *               are no better then nestloops for inequalitys, the planner
+ *               should not plan them anyways.  Alternatively, the
+ *               planner could just exchange the inner/outer relations
+ *               if it ever sees a <... -cim 7/1/90
+ *
+ *     Update:   The executor tuple table has long since alleviated the
+ *               problem described above -cim 4/23/91
+ *
+ */
+#include "executor/executor.h"
+#include "executor/nodeMergejoin.h"
+#include "utils/lsyscache.h"
+
+/* ----------------------------------------------------------------
+ *     MarkInnerTuple and RestoreInnerTuple macros
+ *
+ *     when we "mark" a tuple, we place a pointer to it
+ *     in the marked tuple slot.  now there are two pointers
+ *     to this tuple and we don't want it to be freed until
+ *     next time we mark a tuple, so we move the policy to
+ *     the marked tuple slot and set the inner tuple slot policy
+ *     to false.
+ *
+ *     But, when we restore the inner tuple, the marked tuple
+ *     retains the policy.  Basically once a tuple is marked, it
+ *     should only be freed when we mark another tuple.  -cim 9/27/90
+ *
+ *     Note:  now that we store buffers in the tuple table,
+ *            we have to also increment buffer reference counts
+ *            correctly whenever we propagate an additional pointer
+ *            to a buffer item.  Later, when ExecStoreTuple() is
+ *            called again on this slot, the refcnt is decremented
+ *            when the old tuple is replaced.
+ * ----------------------------------------------------------------
+ */
+#define MarkInnerTuple(innerTupleSlot, mergestate) \
+{ \
+    bool          shouldFree; \
+    shouldFree = ExecSetSlotPolicy(innerTupleSlot, false); \
+    ExecStoreTuple(innerTupleSlot->val, \
+                  mergestate->mj_MarkedTupleSlot, \
+                  innerTupleSlot->ttc_buffer, \
+                  shouldFree); \
+    ExecIncrSlotBufferRefcnt(innerTupleSlot); \
+}
+
+#define RestoreInnerTuple(innerTupleSlot, markedTupleSlot) \
+    ExecStoreTuple(markedTupleSlot->val, \
+                  innerTupleSlot, \
+                  markedTupleSlot->ttc_buffer, \
+                  false); \
+    ExecIncrSlotBufferRefcnt(innerTupleSlot)
+
+/* ----------------------------------------------------------------
+ *     MJFormOSortopI
+ *
+ *     This takes the mergeclause which is a qualification of the
+ *     form ((= expr expr) (= expr expr) ...) and forms a new
+ *     qualification like ((> expr expr) (> expr expr) ...) which
+ *     is used by ExecMergeJoin() in order to determine if we should
+ *     skip tuples.
+ *
+ * old comments
+ *     The 'qual' must be of the form:
+ *        {(= outerkey1 innerkey1)(= outerkey2 innerkey2) ...}
+ *     The "sortOp outerkey innerkey" is formed by substituting the "="
+ *     by "sortOp".
+ * ----------------------------------------------------------------
+ */
+static List *
+MJFormOSortopI(List *qualList, Oid sortOp)
+{
+    List *qualCopy;
+    List *qualcdr;
+    Expr *qual;
+    Oper *op;
+    
+    /* ----------------
+     *  qualList is a list: ((op .. ..) ...)
+     * first we make a copy of it.  copyObject() makes a deep copy 
+     *  so let's use it instead of the old fashoned lispCopy()...
+     * ----------------
+     */
+    qualCopy = (List*) copyObject((Node*) qualList);
+    
+    foreach (qualcdr, qualCopy) {
+       /* ----------------
+        *   first get the current (op .. ..) list
+        * ----------------
+        */
+       qual = lfirst(qualcdr);
+       
+       /* ----------------
+        *   now get at the op
+        * ----------------
+        */
+       op = (Oper*)qual->oper;
+       if (!IsA(op,Oper)) {
+           elog(DEBUG, "MJFormOSortopI: op not an Oper!");
+           return NIL;
+       }
+       
+       /* ----------------
+        *   change it's opid and since Op nodes now carry around a
+        *   cached pointer to the associated op function, we have
+        *   to make sure we invalidate this.  Otherwise you get bizarre
+        *   behavior when someone runs a mergejoin with _exec_repeat_ > 1
+        *   -cim 4/23/91
+        * ----------------
+        */
+       op->opid = sortOp;
+       op->op_fcache = NULL;
+    }
+    
+    return qualCopy;
+}
+/* ----------------------------------------------------------------
+ *     MJFormISortopO
+ *
+ *     This does the same thing as MJFormOSortopI() except that
+ *     it also reverses the expressions in the qualifications.
+ *     For example: ((= expr1 expr2)) produces ((> expr2 expr1))
+ *
+ * old comments
+ *     The 'qual' must be of the form:
+ *        {(= outerkey1 innerkey1) (= outerkey2 innerkey2) ...}
+ *     The 'sortOp innerkey1 outerkey" is formed by substituting the "="
+ *     by "sortOp" and reversing the positions of the keys.
+ *  ----------------------------------------------------------------
+ */
+List *
+MJFormISortopO(List *qualList, Oid sortOp)
+{
+    List       *ISortopO;
+    List       *qualcdr;
+    
+    /* ----------------
+     * first generate OSortopI, a list of the form
+     *  ((op outer inner) (op outer inner) ... )
+     * ----------------
+     */
+    ISortopO = MJFormOSortopI(qualList, sortOp);
+    
+    /* ----------------
+     * now swap the cadr and caddr of each qual to form ISortopO,
+     *  ((op inner outer) (op inner outer) ... )
+     * ----------------
+     */
+    foreach (qualcdr, ISortopO) {
+       Expr *qual;
+       List *inner;
+       List *outer;
+       qual =  lfirst(qualcdr);
+       
+       inner = lfirst(qual->args);
+       outer = lfirst(lnext(qual->args));
+       lfirst(qual->args) = outer;
+       lfirst(lnext(qual->args)) = inner;
+    }
+    
+    return ISortopO;
+}
+/* ----------------------------------------------------------------
+ *     MergeCompare
+ *   
+ *     Compare the keys according to 'compareQual' which is of the 
+ *     form: {(key1a > key2a)(key1b > key2b) ...}.
+ *
+ *     (actually, it could also be the form (key1a < key2a)..)
+ *   
+ *     This is different from calling ExecQual because ExecQual returns
+ *     true only if ALL the comparisions clauses are satisfied.
+ *     However, there is an order of significance among the keys with
+ *     the first keys being most significant. Therefore, the clauses
+ *     are evaluated in order and the 'compareQual' is satisfied
+ *     if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i.
+ * ----------------------------------------------------------------
+ */
+bool
+MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
+{
+    List   *clause;
+    List   *eqclause;
+    Datum  const_value;
+    bool isNull;
+    bool isDone;
+    
+    /* ----------------
+     * if we have no compare qualification, return nil
+     * ----------------
+     */
+    if (compareQual == NIL)
+       return false;
+    
+    /* ----------------
+     * for each pair of clauses, test them until
+     *  our compare conditions are satisified
+     * ----------------
+     */
+    eqclause = eqQual;
+    foreach (clause, compareQual) {
+       /* ----------------
+        *   first test if our compare clause is satisified.
+        *   if so then return true. ignore isDone, don't iterate in
+        *   quals.
+        * ----------------
+        */
+       const_value = (Datum)
+           ExecEvalExpr((Node*) lfirst(clause), econtext, &isNull, &isDone);
+    
+       if (DatumGetInt32(const_value) != 0)
+           return true;
+       
+       /* ----------------
+        *   ok, the compare clause failed so we test if the keys
+        *   are equal... if key1 != key2, we return false.
+        *   otherwise key1 = key2 so we move on to the next pair of keys.
+        *
+        *   ignore isDone, don't iterate in quals.
+        * ----------------
+        */
+       const_value = ExecEvalExpr((Node*) lfirst(eqclause),
+                                  econtext,
+                                  &isNull,
+                                  &isDone);
+    
+       if (DatumGetInt32(const_value) == 0)
+           return false;
+       eqclause = lnext(eqclause);
+    }
+    
+    /* ----------------
+     * if we get here then it means none of our key greater-than
+     *  conditions were satisified so we return false.
+     * ----------------
+     */
+    return false;
+}
+/* ----------------------------------------------------------------
+ *     ExecMergeTupleDump
+ *
+ *     This function is called through the MJ_dump() macro
+ *     when EXEC_MERGEJOINDEBUG is defined
+ * ----------------------------------------------------------------
+ */
+void
+ExecMergeTupleDumpInner(ExprContext *econtext)
+{
+    TupleTableSlot *innerSlot;
+
+    printf("==== inner tuple ====\n");
+    innerSlot = econtext->ecxt_innertuple;
+    if (TupIsNull(innerSlot))
+       printf("(nil)\n");
+    else
+       debugtup(innerSlot->val,
+                innerSlot->ttc_tupleDescriptor);
+}
+void
+ExecMergeTupleDumpOuter(ExprContext *econtext)
+{
+    TupleTableSlot *outerSlot;
+
+    printf("==== outer tuple ====\n");
+    outerSlot = econtext->ecxt_outertuple;
+    if (TupIsNull(outerSlot))
+       printf("(nil)\n");
+    else
+       debugtup(outerSlot->val,
+                outerSlot->ttc_tupleDescriptor);
+}
+void
+ExecMergeTupleDumpMarked(ExprContext *econtext,
+                        MergeJoinState *mergestate)
+{
+    TupleTableSlot *markedSlot;
+
+    printf("==== marked tuple ====\n");
+    markedSlot = mergestate->mj_MarkedTupleSlot;
+
+    if (TupIsNull(markedSlot))
+       printf("(nil)\n");
+    else
+       debugtup(markedSlot->val,
+                markedSlot->ttc_tupleDescriptor);
+}
+void
+ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate)
+{
+    printf("******** ExecMergeTupleDump ********\n");
+    
+    ExecMergeTupleDumpInner(econtext);
+    ExecMergeTupleDumpOuter(econtext);
+    ExecMergeTupleDumpMarked(econtext, mergestate);
+    
+    printf("******** \n");
+}
+/* ----------------------------------------------------------------
+ *     ExecMergeJoin
+ *
+ * old comments
+ *     Details of the merge-join routines:
+ *     
+ *     (1) ">" and "<" operators
+ *   
+ *     Merge-join is done by joining the inner and outer tuples satisfying 
+ *     the join clauses of the form ((= outerKey innerKey) ...).
+ *     The join clauses is provided by the query planner and may contain
+ *     more than one (= outerKey innerKey) clauses (for composite key).
+ *   
+ *     However, the query executor needs to know whether an outer
+ *     tuple is "greater/smaller" than an inner tuple so that it can
+ *     "synchronize" the two relations. For e.g., consider the following
+ *     relations:
+ *   
+ *             outer: (0 ^1 1 2 5 5 5 6 6 7)   current tuple: 1
+ *             inner: (1 ^3 5 5 5 5 6)         current tuple: 3
+ *   
+ *     To continue the merge-join, the executor needs to scan both inner
+ *     and outer relations till the matching tuples 5. It needs to know
+ *     that currently inner tuple 3 is "greater" than outer tuple 1 and
+ *     therefore it should scan the outer relation first to find a 
+ *     matching tuple and so on.
+ *     
+ *     Therefore, when initializing the merge-join node, the executor
+ *     creates the "greater/smaller" clause by substituting the "=" 
+ *     operator in the join clauses with the sort operator used to
+ *     sort the outer and inner relation forming (outerKey sortOp innerKey).
+ *     The sort operator is "<" if the relations are in ascending order  
+ *     otherwise, it is ">" if the relations are in descending order.
+ *     The opposite "smaller/greater" clause is formed by reversing the
+ *     outer and inner keys forming (innerKey sortOp outerKey).
+ *   
+ *     (2) repositioning inner "cursor"
+ *   
+ *     Consider the above relations and suppose that the executor has
+ *     just joined the first outer "5" with the last inner "5". The
+ *     next step is of course to join the second outer "5" with all
+ *     the inner "5's". This requires repositioning the inner "cursor"
+ *     to point at the first inner "5". This is done by "marking" the
+ *     first inner 5 and restore the "cursor" to it before joining
+ *     with the second outer 5. The access method interface provides
+ *     routines to mark and restore to a tuple.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecMergeJoin(MergeJoin *node)
+{
+    EState        *estate;
+    MergeJoinState *mergestate;
+    ScanDirection  direction;
+    List          *innerSkipQual;
+    List          *outerSkipQual;
+    List          *mergeclauses;
+    List          *qual;
+    bool          qualResult;    
+    bool          compareResult;
+    
+    Plan          *innerPlan;
+    TupleTableSlot *innerTupleSlot;
+
+    Plan          *outerPlan;
+    TupleTableSlot *outerTupleSlot;
+    
+    TupleTableSlot *markedTupleSlot;
+    
+    ExprContext           *econtext;
+    
+    /* ----------------
+     * get information from node
+     * ----------------
+     */
+    mergestate =   node->mergestate;
+    estate =      node->join.state;
+    direction =    estate->es_direction;
+    innerPlan =    innerPlan((Plan *)node);
+    outerPlan =    outerPlan((Plan *)node);
+    econtext =     mergestate->jstate.cs_ExprContext;
+    mergeclauses = node->mergeclauses;
+    qual =        node->join.qual;
+    
+    if (ScanDirectionIsForward(direction)) {
+       outerSkipQual = mergestate->mj_OSortopI;
+       innerSkipQual = mergestate->mj_ISortopO;
+    } else {
+       outerSkipQual = mergestate->mj_ISortopO;
+       innerSkipQual = mergestate->mj_OSortopI;
+    }
+
+    /* ----------------
+     * ok, everything is setup.. let's go to work
+     * ----------------
+     */
+    if (mergestate->jstate.cs_TupFromTlist) {
+       TupleTableSlot *result;
+       ProjectionInfo *projInfo;
+       bool           isDone;
+
+       projInfo = mergestate->jstate.cs_ProjInfo;
+       result   = ExecProject(projInfo, &isDone);
+       if (!isDone)
+           return result;
+    }
+    for (;;) {
+       /* ----------------
+        *  get the current state of the join and do things accordingly.
+        *  Note: The join states are highlighted with 32-* comments for
+        *        improved readability.
+        * ----------------
+        */
+       MJ_dump(econtext, mergestate);
+       
+       switch (mergestate->mj_JoinState) {
+       /* ********************************
+        *      EXEC_MJ_INITIALIZE means that this is the first time
+        *      ExecMergeJoin() has been called and so we have to
+        *      initialize the inner, outer and marked tuples as well
+        *      as various stuff in the expression context.
+        * ********************************
+        */
+       case EXEC_MJ_INITIALIZE:
+           MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n");
+           /* ----------------
+            *   Note: at this point, if either of our inner or outer
+            *   tuples are nil, then the join ends immediately because
+            *   we know one of the subplans is empty.
+            * ----------------
+            */
+           innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node);
+           if (TupIsNull(innerTupleSlot)) {
+               MJ_printf("ExecMergeJoin: **** inner tuple is nil ****\n");
+               return NULL;
+           }
+           
+           outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node);
+           if (TupIsNull(outerTupleSlot)) {
+               MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n");
+               return NULL;
+           }
+           
+           /* ----------------
+            *   store the inner and outer tuple in the merge state
+            * ----------------
+            */
+           econtext->ecxt_innertuple = innerTupleSlot;
+           econtext->ecxt_outertuple = outerTupleSlot;
+           
+           /* ----------------
+            *   set the marked tuple to nil
+            *   and initialize its tuple descriptor atttributes. 
+            *      -jeff 10 july 1991
+            * ----------------
+            */
+           ExecClearTuple(mergestate->mj_MarkedTupleSlot);
+           mergestate->mj_MarkedTupleSlot->ttc_tupleDescriptor =
+             innerTupleSlot->ttc_tupleDescriptor;
+/*
+           mergestate->mj_MarkedTupleSlot->ttc_execTupDescriptor =         
+             innerTupleSlot->ttc_execTupDescriptor;
+*/         
+           /* ----------------
+            *  initialize merge join state to skip inner tuples.
+            * ----------------
+            */
+           mergestate->mj_JoinState = EXEC_MJ_SKIPINNER;
+           break;
+           
+       /* ********************************
+        *      EXEC_MJ_JOINMARK means we have just found a new
+        *      outer tuple and a possible matching inner tuple.
+        *      This is the case after the INITIALIZE, SKIPOUTER
+        *      or SKIPINNER states.
+        * ********************************
+        */
+       case EXEC_MJ_JOINMARK:
+           MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n");
+           ExecMarkPos(innerPlan);
+           
+           innerTupleSlot = econtext->ecxt_innertuple;
+           MarkInnerTuple(innerTupleSlot, mergestate);
+           
+           mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
+           break;
+           
+       /* ********************************
+        *      EXEC_MJ_JOINTEST means we have two tuples which
+        *      might satisify the merge clause, so we test them.
+        *
+        *      If they do satisify, then we join them and move
+        *      on to the next inner tuple (EXEC_MJ_JOINTUPLES).
+        *
+        *      If they do not satisify then advance to next outer tuple.
+        * ********************************
+        */
+       case EXEC_MJ_JOINTEST:
+           MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
+           
+           qualResult = ExecQual((List*)mergeclauses, econtext);
+           MJ_DEBUG_QUAL(mergeclauses, qualResult);
+           
+           if (qualResult)
+           {
+               mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
+           }
+           else 
+           {
+               mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
+           }
+           break;
+           
+       /* ********************************
+        *      EXEC_MJ_JOINTUPLES means we have two tuples which
+        *      satisified the merge clause so we join them and then
+        *      proceed to get the next inner tuple (EXEC_NEXT_INNER).
+        * ********************************
+        */
+       case EXEC_MJ_JOINTUPLES:
+           MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
+           mergestate->mj_JoinState = EXEC_MJ_NEXTINNER;
+               
+           qualResult = ExecQual((List*)qual, econtext);
+           MJ_DEBUG_QUAL(qual, qualResult);
+           
+           if (qualResult) {
+               /* ----------------
+                *  qualification succeeded.  now form the desired
+                *  projection tuple and return the slot containing it.
+                * ----------------
+                */
+               ProjectionInfo *projInfo;
+               TupleTableSlot *result;
+               bool           isDone;
+               
+               MJ_printf("ExecMergeJoin: **** returning tuple ****\n");
+               
+               projInfo = mergestate->jstate.cs_ProjInfo;
+               
+               result = ExecProject(projInfo, &isDone);
+               mergestate->jstate.cs_TupFromTlist = !isDone;
+               return result;
+           }
+           break;
+           
+       /* ********************************
+        *      EXEC_MJ_NEXTINNER means advance the inner scan
+        *      to the next tuple.  If the tuple is not nil, we then
+        *      proceed to test it against the join qualification.
+        * ********************************
+        */
+       case EXEC_MJ_NEXTINNER:
+           MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n");
+           
+           /* ----------------
+            *  now we get the next inner tuple, if any
+            * ----------------
+            */
+           innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node);
+           MJ_DEBUG_PROC_NODE(innerTupleSlot);
+           econtext->ecxt_innertuple = innerTupleSlot;
+           
+           if (TupIsNull(innerTupleSlot))
+           {
+               mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
+           }
+           else
+           {
+               mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
+           }
+           break;
+           
+       /* ********************************
+        *      EXEC_MJ_NEXTOUTER means
+        *
+        *                outer inner
+        *   outer tuple -  5     5  - marked tuple    
+        *                  5     5
+        *                  6     6  - inner tuple
+        *                  7     7
+        *
+        *      we know we just bumped into
+        *      the first inner tuple > current outer tuple
+        *      so get a new outer tuple and then proceed to test
+        *      it against the marked tuple (EXEC_MJ_TESTOUTER)
+        * ********************************
+        */
+       case EXEC_MJ_NEXTOUTER:
+           MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n");
+           
+           outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node);
+           MJ_DEBUG_PROC_NODE(outerTupleSlot);
+           econtext->ecxt_outertuple = outerTupleSlot;
+           
+           /* ----------------
+            *  if the outer tuple is null then we know
+            *  we are done with the join
+            * ----------------
+            */
+            if (TupIsNull(outerTupleSlot)) {
+               MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n");
+               return NULL;
+           }
+           
+           mergestate->mj_JoinState = EXEC_MJ_TESTOUTER;
+           break;
+           
+       /* ********************************
+        *      EXEC_MJ_TESTOUTER
+        *      If the new outer tuple and the marked tuple satisify
+        *      the merge clause then we know we have duplicates in
+        *      the outer scan so we have to restore the inner scan
+        *      to the marked tuple and proceed to join the new outer
+        *      tuples with the inner tuples (EXEC_MJ_JOINTEST)
+        *
+        *      This is the case when
+        *
+        *                        outer inner
+        *                          4     5  - marked tuple    
+        *           outer tuple -  5     5  
+        *       new outer tuple -  5     5
+        *                          6     8  - inner tuple
+        *                          7    12
+        *
+        *              new outer tuple = marked tuple
+        *
+        *      If the outer tuple fails the test, then we know we have
+        *      to proceed to skip outer tuples until outer >= inner
+        *      (EXEC_MJ_SKIPOUTER).
+        *
+        *      This is the case when
+        *
+        *                        outer inner
+        *                          5     5  - marked tuple    
+        *           outer tuple -  5     5
+        *       new outer tuple -  6     8  - inner tuple
+        *                          7    12
+        *
+        *              new outer tuple > marked tuple
+        *
+        * ********************************
+        */
+       case EXEC_MJ_TESTOUTER:
+           MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n");
+           
+           /* ----------------
+            *  here we compare the outer tuple with the marked inner tuple
+            *  by using the marked tuple in place of the inner tuple.
+            * ----------------
+            */
+           innerTupleSlot =  econtext->ecxt_innertuple;
+           markedTupleSlot = mergestate->mj_MarkedTupleSlot;
+           econtext->ecxt_innertuple = markedTupleSlot;
+           
+           qualResult = ExecQual((List*)mergeclauses, econtext);
+           MJ_DEBUG_QUAL(mergeclauses, qualResult);
+           
+           if (qualResult) {
+               /* ----------------
+                *  the merge clause matched so now we juggle the slots
+                *  back the way they were and proceed to JOINTEST.
+                * ----------------
+                */
+               econtext->ecxt_innertuple = innerTupleSlot;
+
+               RestoreInnerTuple(innerTupleSlot, markedTupleSlot);
+               
+               ExecRestrPos(innerPlan);
+               mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
+               
+           } else {
+               /* ----------------
+                *  if the inner tuple was nil and the new outer
+                *  tuple didn't match the marked outer tuple then
+                *  we may have the case:
+                *
+                *              outer   inner
+                *                  4     4   - marked tuple
+                *      new outer - 5     4
+                *                  6    nil  - inner tuple
+                *                  7
+                *
+                *  which means that all subsequent outer tuples will be
+                *  larger than our inner tuples.  
+                * ----------------
+                */
+               if (TupIsNull(innerTupleSlot)) {
+                   MJ_printf("ExecMergeJoin: **** wierd case 1 ****\n");
+                   return NULL;
+               }
+               
+               /* ----------------
+                *  restore the inner tuple and continue on to
+                *  skip outer tuples.
+                * ----------------
+                */
+               econtext->ecxt_innertuple = innerTupleSlot;
+               mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER;
+           }
+           break;
+           
+       /* ********************************
+        *      EXEC_MJ_SKIPOUTER means skip over tuples in the outer plan
+        *      until we find an outer tuple > current inner tuple.
+        *
+        *      For example:
+        *
+        *                        outer inner
+        *                          5     5  
+        *                          5     5
+        *           outer tuple -  6     8  - inner tuple
+        *                          7    12
+        *                          8    14
+        *
+        *              we have to advance the outer scan
+        *              until we find the outer 8.
+        *
+        * ********************************
+        */
+       case EXEC_MJ_SKIPOUTER:
+           MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER\n");
+           /* ----------------
+            *  before we advance, make sure the current tuples
+            *  do not satisify the mergeclauses.  If they do, then
+            *  we update the marked tuple and go join them.
+            * ----------------
+            */
+           qualResult = ExecQual((List*)mergeclauses, econtext);
+           MJ_DEBUG_QUAL(mergeclauses, qualResult);
+
+           if (qualResult) {
+               ExecMarkPos(innerPlan);
+               innerTupleSlot = econtext->ecxt_innertuple;
+
+               MarkInnerTuple(innerTupleSlot, mergestate);
+               
+               mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
+               break;
+           }
+
+           /* ----------------
+            *  ok, now test the skip qualification
+            * ----------------
+            */
+           compareResult = MergeCompare(mergeclauses,
+                                        outerSkipQual,
+                                        econtext);
+           
+           MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
+
+           /* ----------------
+            *  compareResult is true as long as we should
+            *  continue skipping tuples.
+            * ----------------
+            */
+           if (compareResult) {
+               
+               outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node);
+               MJ_DEBUG_PROC_NODE(outerTupleSlot);
+               econtext->ecxt_outertuple = outerTupleSlot;
+               
+               /* ----------------
+                *  if the outer tuple is null then we know
+                *  we are done with the join
+                * ----------------
+                */
+               if (TupIsNull(outerTupleSlot)) {
+                   MJ_printf("ExecMergeJoin: **** outerTuple is nil ****\n");
+                   return NULL;
+                }
+               /* ----------------
+                *  otherwise test the new tuple against the skip qual.
+                *  (we remain in the EXEC_MJ_SKIPOUTER state)
+                * ----------------
+                */
+               break;
+           }
+           
+           /* ----------------
+            *  now check the inner skip qual to see if we
+            *  should now skip inner tuples... if we fail the
+            *  inner skip qual, then we know we have a new pair
+            *  of matching tuples.
+            * ----------------
+            */
+           compareResult = MergeCompare(mergeclauses,
+                                        innerSkipQual,
+                                        econtext);
+
+           MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
+
+           if (compareResult)
+           {
+               mergestate->mj_JoinState = EXEC_MJ_SKIPINNER;
+           }
+           else
+           {
+               mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
+           }
+           break;
+
+       /* ********************************
+        *      EXEC_MJ_SKIPINNER means skip over tuples in the inner plan
+        *      until we find an inner tuple > current outer tuple.
+        *
+        *      For example:
+        *
+        *                        outer inner
+        *                          5     5   
+        *                          5     5
+        *           outer tuple - 12     8 - inner tuple
+        *                         14    10
+        *                         17    12
+        *
+        *              we have to advance the inner scan
+        *              until we find the inner 12.
+        *
+        * ********************************
+        */
+       case EXEC_MJ_SKIPINNER:
+           MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER\n");
+           /* ----------------
+            *  before we advance, make sure the current tuples
+            *  do not satisify the mergeclauses.  If they do, then
+            *  we update the marked tuple and go join them.
+            * ----------------
+            */
+           qualResult = ExecQual((List*)mergeclauses, econtext);
+           MJ_DEBUG_QUAL(mergeclauses, qualResult);
+           
+           if (qualResult) {
+               ExecMarkPos(innerPlan);
+               innerTupleSlot = econtext->ecxt_innertuple;
+
+               MarkInnerTuple(innerTupleSlot, mergestate);
+               
+               mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
+               break;
+           }
+
+           /* ----------------
+            *  ok, now test the skip qualification
+            * ----------------
+            */
+           compareResult = MergeCompare(mergeclauses,
+                                        innerSkipQual,
+                                        econtext);
+           
+           MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
+           
+           /* ----------------
+            *  compareResult is true as long as we should
+            *  continue skipping tuples.
+            * ----------------
+            */
+           if (compareResult) {
+               /* ----------------
+                *  now try and get a new inner tuple
+                * ----------------
+                */
+               innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node);
+               MJ_DEBUG_PROC_NODE(innerTupleSlot);
+               econtext->ecxt_innertuple = innerTupleSlot;
+               
+               /* ----------------
+                *  if the inner tuple is null then we know
+                *  we have to restore the inner scan
+                *  and advance to the next outer tuple
+                * ----------------
+                */
+               if (TupIsNull(innerTupleSlot)) {
+                   /* ----------------
+                    *  this is an interesting case.. all our
+                    *  inner tuples are smaller then our outer
+                    *  tuples so we never found an inner tuple
+                    *  to mark.
+                    *
+                    *            outer inner
+                    *   outer tuple -  5     4     
+                    *                  5     4
+                    *                  6    nil  - inner tuple
+                    *                  7
+                    *
+                    *  This means the join should end.
+                    * ----------------
+                    */
+                   MJ_printf("ExecMergeJoin: **** wierd case 2 ****\n");
+                   return NULL;
+               }
+               
+               /* ----------------
+                *  otherwise test the new tuple against the skip qual.
+                *  (we remain in the EXEC_MJ_SKIPINNER state)
+                * ----------------
+                */
+               break;
+           }
+           
+           /* ----------------
+            *  compare finally failed and we have stopped skipping
+            *  inner tuples so now check the outer skip qual
+            *  to see if we should now skip outer tuples...
+            * ----------------
+            */
+           compareResult = MergeCompare(mergeclauses,
+                                        outerSkipQual,
+                                        econtext);
+           
+           MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
+           
+           if (compareResult)
+           {
+               mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER;
+           }
+           else
+           {
+               mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
+           }
+           
+           break;
+           
+       /* ********************************
+        *      if we get here it means our code is fucked up and
+        *      so we just end the join prematurely.
+        * ********************************
+        */
+       default:
+           elog(NOTICE, "ExecMergeJoin: invalid join state. aborting");
+           return NULL;
+       }
+    }
+}
+/* ----------------------------------------------------------------
+ *     ExecInitMergeJoin
+ *
+ * old comments
+ *     Creates the run-time state information for the node and
+ *     sets the relation id to contain relevant decriptors.
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
+{
+    MergeJoinState     *mergestate;
+    List               *joinclauses;
+    RegProcedure       rightsortop;
+    RegProcedure       leftsortop;
+    RegProcedure       sortop;
+    
+    List               *OSortopI;
+    List               *ISortopO;
+    
+    MJ1_printf("ExecInitMergeJoin: %s\n",
+              "initializing node");
+       
+    /* ----------------
+     *  assign the node's execution state and 
+     * get the range table and direction from it
+     * ----------------
+     */
+    node->join.state = estate;
+    
+    /* ----------------
+     * create new merge state for node
+     * ----------------
+     */
+    mergestate = makeNode(MergeJoinState);
+    mergestate->mj_OSortopI = NIL;
+    mergestate->mj_ISortopO = NIL;
+    mergestate->mj_JoinState = 0;
+    mergestate->mj_MarkedTupleSlot = NULL;
+    node->mergestate = mergestate;
+    
+    /* ----------------
+     *  Miscellanious initialization
+     *
+     *      +  assign node's base_id
+     *       + assign debugging hooks and
+     *       + create expression context for node
+     * ----------------
+     */
+    ExecAssignNodeBaseInfo(estate, &mergestate->jstate, parent);
+    ExecAssignExprContext(estate, &mergestate->jstate);
+
+#define MERGEJOIN_NSLOTS 2
+    /* ----------------
+     * tuple table initialization
+     * ----------------
+     */
+    ExecInitResultTupleSlot(estate, &mergestate->jstate);
+    ExecInitMarkedTupleSlot(estate, mergestate);
+    
+    /* ----------------
+     * get merge sort operators.
+     *
+     *  XXX for now we assume all quals in the joinclauses were
+     *      sorted with the same operator in both the inner and
+     *      outer relations. -cim 11/2/89
+     * ----------------
+     */
+    joinclauses = node->mergeclauses;
+
+    rightsortop = get_opcode(node->mergerightorder[0]);
+    leftsortop =  get_opcode(node->mergeleftorder[0]);
+    
+    if (leftsortop != rightsortop)
+       elog(NOTICE, "ExecInitMergeJoin: %s",
+            "left and right sortop's are unequal!");
+    
+    sortop = rightsortop;
+    
+    /* ----------------
+     * form merge skip qualifications
+     *
+     *  XXX MJform routines need to be extended
+     *     to take a list of sortops.. -cim 11/2/89
+     * ----------------
+     */
+    OSortopI = MJFormOSortopI(joinclauses, sortop);
+    ISortopO = MJFormISortopO(joinclauses, sortop);
+    mergestate->mj_OSortopI = OSortopI;
+    mergestate->mj_ISortopO = ISortopO;
+
+    MJ_printf("\nExecInitMergeJoin: OSortopI is ");
+    MJ_nodeDisplay(OSortopI);
+    MJ_printf("\nExecInitMergeJoin: ISortopO is ");
+    MJ_nodeDisplay(ISortopO);
+    MJ_printf("\n");
+    
+    /* ----------------
+     * initialize join state
+     * ----------------
+     */
+    mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
+    
+    /* ----------------
+     * initialize subplans
+     * ----------------
+     */
+    ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
+    ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
+        
+    /* ----------------
+     *         initialize tuple type and projection info
+     * ----------------
+     */
+    ExecAssignResultTypeFromTL((Plan *) node, &mergestate->jstate);
+    ExecAssignProjectionInfo((Plan *) node, &mergestate->jstate);
+    
+    mergestate->jstate.cs_TupFromTlist = false;
+    /* ----------------
+     * initialization successful
+     * ----------------
+     */
+    MJ1_printf("ExecInitMergeJoin: %s\n",
+              "node initialized");
+
+    return TRUE;
+}
+int
+ExecCountSlotsMergeJoin(MergeJoin *node)
+{
+    return ExecCountSlotsNode(outerPlan((Plan *)node)) +
+          ExecCountSlotsNode(innerPlan((Plan *)node)) +
+          MERGEJOIN_NSLOTS;
+}
+/* ----------------------------------------------------------------
+ *     ExecEndMergeJoin
+ *
+ * old comments
+ *     frees storage allocated through C routines.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndMergeJoin(MergeJoin *node)
+{
+    MergeJoinState     *mergestate;
+    
+    MJ1_printf("ExecEndMergeJoin: %s\n",
+              "ending node processing");
+           
+    /* ----------------
+     * get state information from the node
+     * ----------------
+     */
+    mergestate = node->mergestate;
+
+    /* ----------------
+     * Free the projection info and the scan attribute info
+     *
+     *  Note: we don't ExecFreeResultType(mergestate) 
+     *        because the rule manager depends on the tupType
+     *       returned by ExecMain().  So for now, this
+     *       is freed at end-transaction time.  -cim 6/2/91     
+     * ----------------
+     */    
+    ExecFreeProjectionInfo(&mergestate->jstate);
+    
+    /* ----------------
+     * shut down the subplans
+     * ----------------
+     */
+    ExecEndNode((Plan*) innerPlan((Plan *) node), (Plan*)node);
+    ExecEndNode((Plan*) outerPlan((Plan *) node), (Plan*)node);
+
+    /* ----------------
+     * clean out the tuple table so that we don't try and
+     *  pfree the marked tuples..  see HACK ALERT at the top of
+     *  this file.
+     * ----------------
+     */
+    ExecClearTuple(mergestate->jstate.cs_ResultTupleSlot);
+    ExecClearTuple(mergestate->mj_MarkedTupleSlot);
+    
+    MJ1_printf("ExecEndMergeJoin: %s\n",
+              "node processing ended");
+}
diff --git a/src/backend/executor/nodeMergejoin.h b/src/backend/executor/nodeMergejoin.h
new file mode 100644 (file)
index 0000000..ecba79e
--- /dev/null
@@ -0,0 +1,40 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeMergejoin.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        NODEMERGEJOIN_H
+#define        NODEMERGEJOIN_H
+
+#if 0  /* aren't these static? */
+extern List MJFormOSortopI(List qualList, Oid sortOp);
+extern List MJFormISortopO(List qualList, Oid sortOp);
+#endif
+extern bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext);
+
+extern void ExecMergeTupleDumpInner(ExprContext *econtext);
+
+extern void ExecMergeTupleDumpOuter(ExprContext *econtext);
+
+extern void ExecMergeTupleDumpMarked(ExprContext *econtext,
+                                    MergeJoinState *mergestate);
+
+extern void ExecMergeTupleDump(ExprContext *econtext,
+                              MergeJoinState *mergestate);
+
+extern TupleTableSlot *ExecMergeJoin(MergeJoin *node);
+
+extern bool ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent);
+
+extern int ExecCountSlotsMergeJoin(MergeJoin *node);
+
+extern void ExecEndMergeJoin(MergeJoin *node);
+
+#endif /* NODEMERGEJOIN_H; */
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
new file mode 100644 (file)
index 0000000..606b289
--- /dev/null
@@ -0,0 +1,370 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeNestloop.c--
+ *    routines to support nest-loop joins
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ *   INTERFACE ROUTINES
+ *     ExecNestLoop     - process a nestloop join of two plans
+ *     ExecInitNestLoop - initialize the join
+ *     ExecEndNestLoop  - shut down the join
+ */
+#include "executor/executor.h"
+#include "executor/nodeNestloop.h"
+#include "executor/nodeIndexscan.h"
+
+/* ----------------------------------------------------------------
+ *     ExecNestLoop(node)
+ *
+ * old comments
+ *     Returns the tuple joined from inner and outer tuples which 
+ *     satisfies the qualification clause.
+ *
+ *     It scans the inner relation to join with current outer tuple.
+ *
+ *     If none is found, next tuple form the outer relation is retrieved
+ *     and the inner relation is scanned from the beginning again to join
+ *     with the outer tuple.
+ *
+ *     Nil is returned if all the remaining outer tuples are tried and
+ *     all fail to join with the inner tuples.
+ *
+ *     Nil is also returned if there is no tuple from inner realtion.
+ *   
+ *     Conditions:
+ *       -- outerTuple contains current tuple from outer relation and
+ *          the right son(inner realtion) maintains "cursor" at the tuple
+ *          returned previously.
+ *              This is achieved by maintaining a scan position on the outer
+ *              relation.
+ *   
+ *     Initial States:
+ *       -- the outer child and the inner child 
+ *             are prepared to return the first tuple.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecNestLoop(NestLoop *node, Plan* parent)
+{
+    NestLoopState      *nlstate;
+    Plan               *innerPlan;
+    Plan               *outerPlan;
+    bool               needNewOuterTuple;
+
+    TupleTableSlot     *outerTupleSlot;
+    TupleTableSlot     *innerTupleSlot;
+
+    List               *qual;
+    bool               qualResult;
+    ExprContext                *econtext;
+
+    /* ----------------
+     * get information from the node
+     * ----------------
+     */
+    ENL1_printf("getting info from node");
+
+    nlstate =    node->nlstate;
+    qual =       node->join.qual;
+    outerPlan =  outerPlan(&node->join);
+    innerPlan =  innerPlan(&node->join);
+
+    /* ----------------
+     * initialize expression context
+     * ----------------
+     */
+    econtext = nlstate->jstate.cs_ExprContext;
+
+    /* ----------------     *  get the current outer tuple
+     * ----------------
+     */
+    outerTupleSlot = nlstate->jstate.cs_OuterTupleSlot;
+    econtext->ecxt_outertuple = outerTupleSlot;
+
+    /* ----------------
+     *  Ok, everything is setup for the join so now loop until
+     *  we return a qualifying join tuple..
+     * ----------------
+     */
+
+    if (nlstate->jstate.cs_TupFromTlist) {
+       TupleTableSlot  *result;
+       bool            isDone;
+
+       result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
+       if (!isDone)
+           return result;
+    }
+
+    ENL1_printf("entering main loop");
+    for(;;) {
+       /* ----------------
+        *  The essential idea now is to get the next inner tuple
+        *  and join it with the current outer tuple.
+        * ----------------
+        */
+       needNewOuterTuple = false;
+       
+       /* ----------------
+        *  If outer tuple is not null then that means
+        *  we are in the middle of a scan and we should
+        *  restore our previously saved scan position.
+        * ----------------
+        */
+       if (! TupIsNull(outerTupleSlot)) {          
+           ENL1_printf("have outer tuple, restoring outer plan");
+           ExecRestrPos(outerPlan);
+       } else {
+           ENL1_printf("outer tuple is nil, need new outer tuple");
+           needNewOuterTuple = true;
+       }
+       
+       /* ----------------
+        *  if we have an outerTuple, try to get the next inner tuple.
+        * ----------------
+        */
+       if (!needNewOuterTuple) {
+           ENL1_printf("getting new inner tuple");
+       
+           innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node);
+           econtext->ecxt_innertuple = innerTupleSlot;
+           
+           if (TupIsNull(innerTupleSlot)) {
+               ENL1_printf("no inner tuple, need new outer tuple");
+               needNewOuterTuple = true;
+           }
+       }
+       
+       /* ----------------
+        *  loop until we have a new outer tuple and a new
+        *  inner tuple.
+        * ----------------
+        */
+       while (needNewOuterTuple) {
+           /* ----------------
+            *  now try to get the next outer tuple
+            * ----------------
+            */
+           ENL1_printf("getting new outer tuple");
+           outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node);
+           econtext->ecxt_outertuple = outerTupleSlot;
+           
+           /* ----------------
+            *  if there are no more outer tuples, then the join
+            *  is complete..
+            * ----------------
+            */
+           if (TupIsNull(outerTupleSlot)) {
+               ENL1_printf("no outer tuple, ending join");
+               return NULL;
+           }
+           
+           /* ----------------
+            *  we have a new outer tuple so we mark our position
+            *  in the outer scan and save the outer tuple in the
+            *  NestLoop state
+            * ----------------
+            */
+           ENL1_printf("saving new outer tuple information");
+           ExecMarkPos(outerPlan);
+           nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
+           
+           /* ----------------
+            *  now rescan the inner plan and get a new inner tuple
+            * ----------------
+            */
+           
+           ENL1_printf("rescanning inner plan");
+           /* 
+            * The scan key of the inner plan might depend on the current
+            * outer tuple (e.g. in index scans), that's why we pass our
+            * expr context.
+            */
+           ExecReScan(innerPlan, econtext, parent);
+
+           ENL1_printf("getting new inner tuple");
+           
+           innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node);
+           econtext->ecxt_innertuple = innerTupleSlot;
+           
+           if (TupIsNull(innerTupleSlot)) {
+               ENL1_printf("couldn't get inner tuple - need new outer tuple");
+           } else {
+               ENL1_printf("got inner and outer tuples");
+               needNewOuterTuple = false;
+           }
+       } /* while (needNewOuterTuple) */
+       
+       /* ----------------
+        *   at this point we have a new pair of inner and outer
+        *   tuples so we test the inner and outer tuples to see
+        *   if they satisify the node's qualification.
+        * ----------------
+        */
+       ENL1_printf("testing qualification");
+       qualResult = ExecQual((List*)qual, econtext);
+       
+       if (qualResult) {
+           /* ----------------
+            *  qualification was satisified so we project and
+            *  return the slot containing the result tuple
+            *  using ExecProject().
+            * ----------------
+            */
+           ProjectionInfo *projInfo;
+           TupleTableSlot *result;
+           bool           isDone;
+           
+           ENL1_printf("qualification succeeded, projecting tuple");
+           
+           projInfo = nlstate->jstate.cs_ProjInfo;
+           result = ExecProject(projInfo, &isDone);
+           nlstate->jstate.cs_TupFromTlist = !isDone;
+           return result;
+       } 
+       
+       /* ----------------
+        *  qualification failed so we have to try again..
+        * ----------------
+        */
+       ENL1_printf("qualification failed, looping");
+    }
+}
+
+/* ----------------------------------------------------------------
+ *     ExecInitNestLoop
+ *   
+ *     Creates the run-time state information for the nestloop node
+ *     produced by the planner and initailizes inner and outer relations 
+ *     (child nodes).
+ * ----------------------------------------------------------------    
+ */
+bool
+ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
+{
+    NestLoopState   *nlstate;
+
+    NL1_printf("ExecInitNestLoop: %s\n",
+              "initializing node");
+       
+    /* ----------------
+     * assign execution state to node
+     * ----------------
+     */
+    node->join.state = estate;
+
+    /* ----------------
+     *    create new nest loop state
+     * ----------------
+     */
+    nlstate = makeNode(NestLoopState);
+    nlstate->nl_PortalFlag = false;
+    node->nlstate = nlstate;
+
+    /* ----------------
+     *  Miscellanious initialization
+     *
+     *      +  assign node's base_id
+     *       + assign debugging hooks and
+     *       + create expression context for node
+     * ----------------
+     */
+    ExecAssignNodeBaseInfo(estate, &nlstate->jstate, parent);
+    ExecAssignExprContext(estate, &nlstate->jstate);
+
+#define NESTLOOP_NSLOTS 1
+    /* ----------------
+     * tuple table initialization
+     * ----------------
+     */
+    ExecInitResultTupleSlot(estate, &nlstate->jstate);
+
+    /* ----------------
+     *    now initialize children
+     * ----------------
+     */
+    ExecInitNode(outerPlan((Plan*)node), estate, (Plan*)node);
+    ExecInitNode(innerPlan((Plan*)node), estate, (Plan*)node);
+
+    /* ----------------
+     *         initialize tuple type and projection info
+     * ----------------
+     */
+    ExecAssignResultTypeFromTL((Plan *) node, &nlstate->jstate);
+    ExecAssignProjectionInfo((Plan *) node, &nlstate->jstate);
+
+    /* ----------------
+     *  finally, wipe the current outer tuple clean.
+     * ----------------
+     */
+    nlstate->jstate.cs_OuterTupleSlot = NULL;
+    nlstate->jstate.cs_TupFromTlist = false;
+
+    NL1_printf("ExecInitNestLoop: %s\n",
+              "node initialized");
+    return TRUE;
+}
+
+int
+ExecCountSlotsNestLoop(NestLoop *node)
+{
+    return ExecCountSlotsNode(outerPlan(node)) +
+          ExecCountSlotsNode(innerPlan(node)) +
+          NESTLOOP_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecEndNestLoop
+ *   
+ *     closes down scans and frees allocated storage
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndNestLoop(NestLoop *node)
+{
+    NestLoopState   *nlstate;
+
+    NL1_printf("ExecEndNestLoop: %s\n",
+              "ending node processing");
+
+    /* ----------------
+     * get info from the node
+     * ----------------
+     */
+    nlstate =  node->nlstate;
+
+    /* ----------------
+     * Free the projection info
+     *
+     *  Note: we don't ExecFreeResultType(nlstate) 
+     *        because the rule manager depends on the tupType
+     *       returned by ExecMain().  So for now, this
+     *       is freed at end-transaction time.  -cim 6/2/91     
+     * ----------------
+     */    
+    ExecFreeProjectionInfo(&nlstate->jstate);
+
+    /* ----------------
+     * close down subplans
+     * ----------------
+     */
+    ExecEndNode(outerPlan((Plan *) node), (Plan*)node);
+    ExecEndNode(innerPlan((Plan *) node), (Plan*)node);
+
+    /* ----------------
+     * clean out the tuple table 
+     * ----------------
+     */
+    ExecClearTuple(nlstate->jstate.cs_ResultTupleSlot);
+
+    NL1_printf("ExecEndNestLoop: %s\n",
+              "node processing ended");
+}
diff --git a/src/backend/executor/nodeNestloop.h b/src/backend/executor/nodeNestloop.h
new file mode 100644 (file)
index 0000000..d03b27f
--- /dev/null
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeNestloop.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        NODENESTLOOP_H
+#define        NODENESTLOOP_H
+
+extern TupleTableSlot *ExecNestLoop(NestLoop *node, Plan *parent);
+extern bool ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsNestLoop(NestLoop *node);
+extern void ExecEndNestLoop(NestLoop *node);
+
+#endif /* NODENESTLOOP_H */
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
new file mode 100644 (file)
index 0000000..978e297
--- /dev/null
@@ -0,0 +1,288 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeResult.c--
+ *    support for constant nodes needing special code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * DESCRIPTION 
+ *
+ *     Example: in constant queries where no relations are scanned,
+ *     the planner generates result nodes.  Examples of such queries are:
+ *
+ *             retrieve (x = 1)
+ *     and
+ *             append emp (name = "mike", salary = 15000)
+ *
+ *     Result nodes are also used to optimise queries
+ *     with tautological qualifications like:
+ *
+ *             retrieve (emp.all) where 2 > 1
+ *
+ *     In this case, the plan generated is
+ *
+ *                     Result  (with 2 > 1 qual)
+ *                     /
+ *                SeqScan (emp.all)
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "executor/executor.h"
+#include "executor/nodeResult.h"
+
+/* ----------------------------------------------------------------
+ *     ExecResult(node)
+ *
+ *     returns the tuples from the outer plan which satisify the
+ *     qualification clause.  Since result nodes with right
+ *     subtrees are never planned, we ignore the right subtree
+ *     entirely (for now).. -cim 10/7/89
+ *
+ *     The qualification containing only constant clauses are
+ *     checked first before any processing is done. It always returns
+ *     'nil' if the constant qualification is not satisfied.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecResult(Result *node)
+{
+    ResultState                *resstate;
+    TupleTableSlot     *outerTupleSlot;
+    TupleTableSlot     *resultSlot;
+    Plan               *outerPlan;
+    ExprContext                *econtext;
+    Node               *qual;
+    bool               qualResult;
+    bool               isDone;
+    ProjectionInfo     *projInfo;
+    
+    /* ----------------
+     * initialize the result node's state
+     * ----------------
+     */
+    resstate =  node->resstate;
+    
+    /* ----------------
+     * get the expression context
+     * ----------------
+     */
+    econtext = resstate->cstate.cs_ExprContext;
+    
+    /* ----------------
+     *   check tautological qualifications like (2 > 1)
+     * ----------------
+     */
+    qual = node->resconstantqual;
+    if (qual != NULL) {
+       qualResult = ExecQual((List*)qual, econtext);
+       /* ----------------
+        *  if we failed the constant qual, then there
+        *  is no need to continue processing because regardless of
+        *  what happens, the constant qual will be false..
+        * ----------------
+        */
+       if (qualResult == false)
+           return NULL;
+       
+       /* ----------------
+        *  our constant qualification succeeded so now we
+        *  throw away the qual because we know it will always
+        *  succeed.
+        * ----------------
+        */
+       node->resconstantqual = NULL;
+    }
+    
+    if (resstate->cstate.cs_TupFromTlist) {
+       ProjectionInfo *projInfo;
+       
+       projInfo = resstate->cstate.cs_ProjInfo;
+       resultSlot = ExecProject(projInfo, &isDone);
+       if (!isDone)
+           return resultSlot;
+    }
+
+    /* ----------------
+     *  retrieve a tuple that satisfy the qual from the outer plan until
+     *  there are no more.
+     *
+     *  if rs_done is 1 then it means that we were asked to return
+     *  a constant tuple and we alread did the last time ExecResult()
+     *  was called, so now we are through.
+     * ----------------
+     */
+    outerPlan = outerPlan(node);
+
+    while (!resstate->rs_done) {
+
+       /* ----------------
+        *    get next outer tuple if necessary.
+        * ----------------
+        */
+       if (outerPlan != NULL) {
+           outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node);
+       
+           if (TupIsNull(outerTupleSlot))
+               return NULL;
+           
+           resstate->cstate.cs_OuterTupleSlot = outerTupleSlot;
+       } else {
+
+           /* ----------------
+            *  if we don't have an outer plan, then it's probably
+            *  the case that we are doing a retrieve or an append
+            *  with a constant target list, so we should only return
+            *  the constant tuple once or never if we fail the qual.
+            * ----------------
+            */
+           resstate->rs_done = 1;
+       }
+       
+       /* ----------------
+        *    get the information to place into the expr context
+        * ----------------
+        */
+       resstate =  node->resstate;
+       
+       outerTupleSlot = resstate->cstate.cs_OuterTupleSlot;
+       
+       /* ----------------
+        *   fill in the information in the expression context
+        *   XXX gross hack. use outer tuple as scan tuple
+        * ----------------
+        */
+       econtext->ecxt_outertuple = outerTupleSlot;
+       econtext->ecxt_scantuple =  outerTupleSlot;
+       
+       /* ----------------
+        *   form the result tuple and pass it back using ExecProject()
+        * ----------------
+        */
+       projInfo = resstate->cstate.cs_ProjInfo;
+       resultSlot = ExecProject(projInfo, &isDone);
+       resstate->cstate.cs_TupFromTlist = !isDone;
+       return resultSlot;
+    }
+
+    return NULL;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecInitResult
+ *   
+ *     Creates the run-time state information for the result node
+ *     produced by the planner and initailizes outer relations 
+ *     (child nodes).
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitResult(Result *node, EState *estate, Plan *parent)
+{
+    ResultState            *resstate;
+    
+    /* ----------------
+     * assign execution state to node
+     * ----------------
+     */
+    node->plan.state = estate;
+    
+    /* ----------------
+     * create new ResultState for node
+     * ----------------
+     */
+    resstate = makeNode(ResultState);
+    resstate->rs_done = 0;
+    node->resstate = resstate;
+    
+    /* ----------------
+     *  Miscellanious initialization
+     *
+     *      +  assign node's base_id
+     *       + assign debugging hooks and
+     *       + create expression context for node
+     * ----------------
+     */
+    ExecAssignNodeBaseInfo(estate, &resstate->cstate, parent);
+    ExecAssignExprContext(estate, &resstate->cstate);
+    
+#define RESULT_NSLOTS 1
+    /* ----------------
+     * tuple table initialization
+     * ----------------
+     */
+    ExecInitResultTupleSlot(estate, &resstate->cstate);
+    
+    /* ----------------
+     * then initialize children
+     * ----------------
+     */
+    ExecInitNode(outerPlan(node), estate, (Plan*)node);
+
+    /*
+     * we don't use inner plan
+     */
+    Assert(innerPlan(node)==NULL);
+    
+    /* ----------------
+     *         initialize tuple type and projection info
+     * ----------------
+     */
+    ExecAssignResultTypeFromTL((Plan*)node, &resstate->cstate);
+    ExecAssignProjectionInfo((Plan*)node, &resstate->cstate);
+    
+    /* ----------------
+     * set "are we done yet" to false
+     * ----------------
+     */
+    resstate->rs_done = 0;
+    
+    return TRUE;
+}
+
+int
+ExecCountSlotsResult(Result *node)
+{
+    return ExecCountSlotsNode(outerPlan(node)) + RESULT_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecEndResult
+ *   
+ *     fees up storage allocated through C routines
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndResult(Result *node)
+{
+    ResultState            *resstate;
+    
+    resstate = node->resstate;
+    
+    /* ----------------
+     * Free the projection info
+     *
+     *  Note: we don't ExecFreeResultType(resstate) 
+     *        because the rule manager depends on the tupType
+     *       returned by ExecMain().  So for now, this
+     *       is freed at end-transaction time.  -cim 6/2/91     
+     * ----------------
+     */    
+    ExecFreeProjectionInfo(&resstate->cstate);
+    
+    /* ----------------
+     * shut down subplans
+     * ----------------
+     */
+    ExecEndNode(outerPlan(node), (Plan*)node);
+
+    /* ----------------
+     * clean out the tuple table
+     * ----------------
+     */
+    ExecClearTuple(resstate->cstate.cs_ResultTupleSlot);
+}
diff --git a/src/backend/executor/nodeResult.h b/src/backend/executor/nodeResult.h
new file mode 100644 (file)
index 0000000..f98a612
--- /dev/null
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeResult.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        NODERESULT_H
+#define        NODERESULT_H
+
+extern TupleTableSlot *ExecResult(Result *node);
+extern bool ExecInitResult(Result *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsResult(Result *node);
+extern void ExecEndResult(Result *node);
+
+#endif /* NODERESULT_H */
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
new file mode 100644 (file)
index 0000000..af9f59d
--- /dev/null
@@ -0,0 +1,449 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeSeqscan.c--
+ *    Support routines for sequential scans of relations.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *     ExecSeqScan             sequentially scans a relation.
+ *     ExecSeqNext             retrieve next tuple in sequential order.
+ *     ExecInitSeqScan         creates and initializes a seqscan node.
+ *     ExecEndSeqScan          releases any storage allocated.
+ *     ExecSeqReScan           rescans the relation
+ *     ExecMarkPos             marks scan position
+ *     ExecRestrPos            restores scan position
+ *
+ */
+#include "executor/executor.h"
+#include "executor/nodeSeqscan.h"
+#include "parser/parsetree.h"
+
+/* ----------------------------------------------------------------
+ *                     Scan Support
+ * ----------------------------------------------------------------
+ */
+/* ----------------------------------------------------------------
+ *     SeqNext
+ *
+ *     This is a workhorse for ExecSeqScan
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+SeqNext(SeqScan *node)
+{
+    HeapTuple          tuple;
+    HeapScanDesc       scandesc;
+    CommonScanState    *scanstate;
+    EState             *estate;
+    ScanDirection      direction;
+    TupleTableSlot     *slot;
+    Buffer             buffer;
+
+    /* ----------------
+     * get information from the estate and scan state
+     * ----------------
+     */
+    estate =        node->plan.state;
+    scanstate =     node->scanstate;
+    scandesc =      scanstate->css_currentScanDesc;
+    direction =     estate->es_direction;
+
+    /* ----------------
+     * get the next tuple from the access methods
+     * ----------------
+     */
+    tuple = heap_getnext(scandesc,                /* scan desc */
+                        ScanDirectionIsBackward(direction), /*backward flag*/
+                        &buffer);                 /* return: buffer */
+
+    /* ----------------
+     * save the tuple and the buffer returned to us by the access methods
+     *  in our scan tuple slot and return the slot.  Note: we pass 'false'
+     *  because tuples returned by heap_getnext() are pointers onto
+     *  disk pages and were not created with palloc() and so should not
+     *  be pfree()'d.
+     * ----------------
+     */
+    slot = scanstate->css_ScanTupleSlot;  
+
+    slot = ExecStoreTuple(tuple,  /* tuple to store */
+                         slot,   /* slot to store in */
+                         buffer, /* buffer associated with this tuple */
+                         false); /* don't pfree this pointer */
+
+    /* ----------------
+     *  XXX -- mao says:  The sequential scan for heap relations will
+     *  automatically unpin the buffer this tuple is on when we cross
+     *  a page boundary.  The clearslot code also does this.  We bump
+     *  the pin count on the page here, since we actually have two
+     *  pointers to it -- one in the scan desc and one in the tuple
+     *  table slot.  --mar 20 91
+     * ----------------
+     */
+    ExecIncrSlotBufferRefcnt(slot);
+
+    return slot;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecSeqScan(node)
+ *
+ *     Scans the relation sequentially and returns the next qualifying 
+ *     tuple.
+ *     It calls the ExecScan() routine and passes it the access method
+ *     which retrieve tuples sequentially.
+ *   
+ */
+
+TupleTableSlot *
+ExecSeqScan(SeqScan *node)
+{
+    TupleTableSlot     *slot;
+    Plan               *outerPlan;
+
+S_printf("ExecSeqScan: scanning node: "); S_nodeDisplay(node);
+
+    /* ----------------
+     * if there is an outer subplan, get a tuple from it
+     * else, scan the relation
+     * ----------------
+     */
+    outerPlan = outerPlan((Plan *) node);
+    if (outerPlan) {
+       slot = ExecProcNode(outerPlan, (Plan*) node);
+    } else {
+       slot = ExecScan(node, SeqNext);
+    }
+
+S1_printf("ExecSeqScan: returned tuple slot: %d\n", slot);
+
+    return slot;
+}
+
+/* ----------------------------------------------------------------
+ *     InitScanRelation
+ *
+ *     This does the initialization for scan relations and
+ *     subplans of scans.
+ * ----------------------------------------------------------------
+ */
+Oid
+InitScanRelation(SeqScan *node, EState *estate,
+                CommonScanState *scanstate, Plan *outerPlan)
+{
+    Index              relid;
+    List               *rangeTable;
+    RangeTblEntry       *rtentry;
+    Oid                        reloid;
+    TimeQual           timeQual;
+    ScanDirection      direction;
+    Relation           currentRelation;
+    HeapScanDesc       currentScanDesc;
+    RelationInfo       *resultRelationInfo;
+
+    if (outerPlan == NULL) {
+       /* ----------------
+        * if the outer node is nil then we are doing a simple
+        * sequential scan of a relation...
+        *
+        * get the relation object id from the relid'th entry
+        * in the range table, open that relation and initialize
+        * the scan state...
+        * ----------------
+        */
+       relid =                 node->scanrelid;
+       rangeTable =            estate->es_range_table;
+       rtentry =               rt_fetch(relid, rangeTable);
+       reloid =                rtentry->relid;
+       timeQual =              rtentry->timeQual;
+       direction =             estate->es_direction;
+       resultRelationInfo =    estate->es_result_relation_info;
+       
+       ExecOpenScanR(reloid,             /* relation */
+                     0,                  /* nkeys */
+                     NULL,               /* scan key */
+                     0,                  /* is index */
+                     direction,          /* scan direction */
+                     timeQual,           /* time qual */
+                     &currentRelation,   /* return: rel desc */
+                     (Pointer *) &currentScanDesc);  /* return: scan desc */
+       
+       scanstate->css_currentRelation = currentRelation;
+       scanstate->css_currentScanDesc = currentScanDesc;
+       
+       ExecAssignScanType(scanstate, 
+                          RelationGetTupleDescriptor(currentRelation));
+    } else {
+       /* ----------------
+        *   otherwise we are scanning tuples from the
+        *   outer subplan so we initialize the outer plan
+        *   and nullify 
+        * ----------------
+        */
+       ExecInitNode(outerPlan, estate, (Plan*)node);
+       
+       node->scanrelid = 0;
+       scanstate->css_currentRelation = NULL;
+       scanstate->css_currentScanDesc = NULL;
+       ExecAssignScanType(scanstate, NULL);  
+       reloid = InvalidOid;
+    }
+
+    /* ----------------
+     * return the relation
+     * ----------------
+     */
+    return reloid;
+}
+
+
+/* ----------------------------------------------------------------
+ *     ExecInitSeqScan
+ *
+ * old comments
+ *     Creates the run-time state information for the seqscan node 
+ *     and sets the relation id to contain relevant descriptors.
+ *   
+ *     If there is a outer subtree (sort), the outer subtree
+ *     is initialized and the relation id is set to the descriptors
+ *     returned by the subtree.
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
+{
+    CommonScanState     *scanstate;
+    Plan               *outerPlan;
+    Oid                        reloid;
+    HeapScanDesc       scandesc;
+
+    /* ----------------
+     *  assign the node's execution state
+     * ----------------
+     */
+    node->plan.state = estate;
+
+    /* ----------------
+     *   create new CommonScanState for node
+     * ----------------
+     */
+    scanstate = makeNode(CommonScanState);
+    node->scanstate = scanstate;
+
+    /* ----------------
+     *  Miscellanious initialization
+     *
+     *      +  assign node's base_id
+     *       + create expression context for node
+     * ----------------
+     */
+    ExecAssignNodeBaseInfo(estate, &scanstate->cstate, parent);
+    ExecAssignExprContext(estate, &scanstate->cstate);
+
+#define SEQSCAN_NSLOTS 3
+    /* ----------------
+     * tuple table initialization
+     * ----------------
+     */
+    ExecInitResultTupleSlot(estate, &scanstate->cstate);
+    ExecInitScanTupleSlot(estate, scanstate); 
+
+    /* ----------------
+     * initialize scan relation or outer subplan
+     * ----------------
+     */
+    outerPlan = outerPlan((Plan *)node);
+
+    reloid = InitScanRelation(node, estate, scanstate, outerPlan);
+
+    scandesc = scanstate->css_currentScanDesc;
+    scanstate->cstate.cs_TupFromTlist = false;
+
+    /* ----------------
+     *         initialize tuple type
+     * ----------------
+     */
+    ExecAssignResultTypeFromTL((Plan*)node, &scanstate->cstate);
+    ExecAssignProjectionInfo((Plan*)node, &scanstate->cstate);
+
+    return TRUE;
+}
+
+int
+ExecCountSlotsSeqScan(SeqScan *node)
+{
+    return ExecCountSlotsNode(outerPlan(node)) +
+       ExecCountSlotsNode(innerPlan(node)) +
+           SEQSCAN_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecEndSeqScan
+ *   
+ *     frees any storage allocated through C routines.
+ *|    ...and also closes relations and/or shuts down outer subplan
+ *|    -cim 8/14/89
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndSeqScan(SeqScan *node)
+{
+    CommonScanState    *scanstate;
+    Plan               *outerPlan;
+
+    /* ----------------
+     * get information from node
+     * ----------------
+     */
+    scanstate = node->scanstate;
+
+    /* ----------------
+     * Free the projection info and the scan attribute info
+     *
+     *  Note: we don't ExecFreeResultType(scanstate) 
+     *        because the rule manager depends on the tupType
+     *       returned by ExecMain().  So for now, this
+     *       is freed at end-transaction time.  -cim 6/2/91     
+     * ----------------
+     */    
+    ExecFreeProjectionInfo(&scanstate->cstate);
+
+    /* ----------------
+     * close scan relation
+     * ----------------
+     */
+    ExecCloseR((Plan*) node);
+
+    /* ----------------
+     * clean up outer subtree (does nothing if there is no outerPlan)
+     * ----------------
+     */
+    outerPlan = outerPlan((Plan *)node);
+    ExecEndNode(outerPlan, (Plan*)node);
+
+    /* ----------------
+     * clean out the tuple table
+     * ----------------
+     */
+    ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
+    ExecClearTuple(scanstate->css_ScanTupleSlot); 
+}
+
+/* ----------------------------------------------------------------
+ *                     Join Support
+ * ----------------------------------------------------------------
+ */
+/* ----------------------------------------------------------------
+ *     ExecSeqReScan
+ *   
+ *     Rescans the relation.
+ * ----------------------------------------------------------------
+ */
+void
+ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan* parent)
+{
+    CommonScanState      *scanstate;
+    EState       *estate;
+    Plan         *outerPlan;
+    Relation      rdesc;
+    HeapScanDesc  sdesc;
+    ScanDirection direction;
+
+    scanstate = node->scanstate;
+    estate =    node->plan.state;
+
+    outerPlan = outerPlan((Plan*)node);
+    if (outerPlan) {
+      /* we are scanning a subplan */
+       outerPlan = outerPlan((Plan *)node);
+       ExecReScan(outerPlan, exprCtxt, parent);
+      } else {
+       /* otherwise, we are scanning a relation */
+       rdesc =         scanstate->css_currentRelation;
+       sdesc =         scanstate->css_currentScanDesc;
+       direction =     estate->es_direction;
+       sdesc =         ExecReScanR(rdesc, sdesc, direction, 0, NULL);
+       scanstate->css_currentScanDesc = sdesc;
+    }
+}
+
+/* ----------------------------------------------------------------
+ *     ExecSeqMarkPos(node)
+ *   
+ *     Marks scan position.
+ * ----------------------------------------------------------------
+ */
+void
+ExecSeqMarkPos(SeqScan *node)
+{
+    CommonScanState     *scanstate;
+    Plan        *outerPlan;
+    HeapScanDesc sdesc;
+
+    scanstate =     node->scanstate;
+
+    /* ----------------
+     * if we are scanning a subplan then propagate
+     *  the ExecMarkPos() request to the subplan
+     * ----------------
+     */
+    outerPlan = outerPlan((Plan*)node);
+    if (outerPlan) {
+       ExecMarkPos(outerPlan);
+       return;
+    }
+
+    /* ----------------
+     *  otherwise we are scanning a relation so mark the
+     *  position using the access methods..
+     *
+     * ----------------
+     */
+    sdesc = scanstate->css_currentScanDesc;
+    heap_markpos(sdesc);
+
+    return;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecSeqRestrPos
+ *   
+ *     Restores scan position.
+ * ----------------------------------------------------------------
+ */
+void
+ExecSeqRestrPos(SeqScan *node)
+{
+    CommonScanState     *scanstate;
+    Plan        *outerPlan;
+    HeapScanDesc sdesc;
+
+    scanstate = node->scanstate;
+
+    /* ----------------
+     * if we are scanning a subplan then propagate
+     *  the ExecRestrPos() request to the subplan
+     * ----------------
+     */
+    outerPlan = outerPlan((Plan*)node);
+    if (outerPlan) {
+       ExecRestrPos(outerPlan);
+       return;
+    }
+
+    /* ----------------
+     *  otherwise we are scanning a relation so restore the
+     *  position using the access methods..
+     * ----------------
+     */
+    sdesc = scanstate->css_currentScanDesc;
+    heap_restrpos(sdesc);
+}
diff --git a/src/backend/executor/nodeSeqscan.h b/src/backend/executor/nodeSeqscan.h
new file mode 100644 (file)
index 0000000..562d447
--- /dev/null
@@ -0,0 +1,27 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeSeqscan.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        NODESEQSCAN_H
+#define        NODESEQSCAN_H
+
+extern TupleTableSlot *SeqNext(SeqScan *node);
+extern TupleTableSlot *ExecSeqScan(SeqScan *node);
+extern Oid InitScanRelation(SeqScan *node, EState *estate,
+                           CommonScanState *scanstate, Plan *outerPlan);
+extern bool ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsSeqScan(SeqScan *node);
+extern void ExecEndSeqScan(SeqScan *node);
+extern void ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan* parent);
+extern void ExecSeqMarkPos(SeqScan *node);
+extern void ExecSeqRestrPos(SeqScan *node);
+
+#endif /* NODESEQSCAN_H */
diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c
new file mode 100644 (file)
index 0000000..c82d1e7
--- /dev/null
@@ -0,0 +1,523 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeSort.c--
+ *    Routines to handle sorting of relations into temporaries.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "executor/executor.h"
+#include "executor/nodeSort.h"
+#include "utils/palloc.h"
+#include "utils/psort.h"
+#include "catalog/catalog.h"
+#include "storage/bufmgr.h"
+#include "optimizer/internal.h" /* for _TEMP_RELATION_ID_ */
+
+/* ----------------------------------------------------------------
+ *     FormSortKeys(node)
+ *    
+ *     Forms the structure containing information used to sort the relation.
+ *    
+ *     Returns an array of ScanKeyData.
+ * ----------------------------------------------------------------
+ */
+static ScanKey
+FormSortKeys(Sort *sortnode)
+{
+    ScanKey            sortkeys;
+    List               *targetList;
+    List               *tl;
+    int                        keycount;
+    Resdom             *resdom;
+    AttrNumber                 resno;
+    Index              reskey;
+    Oid                        reskeyop;
+    
+    /* ----------------
+     * get information from the node
+     * ----------------
+     */
+    targetList = sortnode->plan.targetlist;
+    keycount =   sortnode->keycount;
+    
+    /* ----------------
+     * first allocate space for scan keys
+     * ----------------
+     */
+    if (keycount <= 0)
+       elog(WARN, "FormSortKeys: keycount <= 0");
+    sortkeys = (ScanKey) palloc(keycount * sizeof(ScanKeyData));
+
+    /* ----------------  
+     * form each scan key from the resdom info in the target list
+     * ----------------
+     */
+    foreach(tl, targetList) {
+       TargetEntry *target = (TargetEntry *)lfirst(tl);
+       resdom =  target->resdom;
+       resno =   resdom->resno;
+       reskey =  resdom->reskey;
+       reskeyop = resdom->reskeyop;
+       
+       if (reskey > 0) {
+           ScanKeyEntryInitialize(&sortkeys[reskey-1],
+                                  0,
+                                  resno,
+                                  (RegProcedure) DatumGetInt32(reskeyop),
+                                  (Datum) 0);
+       }
+    }
+    
+    return sortkeys;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecSort
+ *
+ * old comments
+ *     Retrieves tuples fron the outer subtree and insert them into a 
+ *     temporary relation. The temporary relation is then sorted and
+ *     the sorted relation is stored in the relation whose ID is indicated
+ *     in the 'tempid' field of this node.
+ *     Assumes that heap access method is used.
+ *   
+ *     Conditions:
+ *       -- none.
+ *   
+ *     Initial States:
+ *       -- the outer child is prepared to return the first tuple.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecSort(Sort *node)
+{
+    EState        *estate;
+    SortState     *sortstate;
+    Plan          *outerNode;
+    ScanDirection  dir;
+    int                   keycount;
+    ScanKey       sortkeys;
+    Relation      tempRelation;
+    Relation      currentRelation;
+    HeapScanDesc   currentScanDesc;
+    HeapTuple      heapTuple;
+    TupleTableSlot *slot;
+    Buffer        buffer;
+    int                   tupCount = 0;
+    
+    /* ----------------
+     * get state info from node
+     * ----------------
+     */
+    SO1_printf("ExecSort: %s\n",
+              "entering routine");
+    
+    sortstate =   node->sortstate;
+    estate =      node->plan.state;
+    dir =        estate->es_direction;
+    
+    /* ----------------
+     * the first time we call this, we retrieve all tuples
+     *  from the subplan into a temporary relation and then
+     *  we sort the relation.  Subsequent calls return tuples
+     *  from the temporary relation.
+     * ----------------
+     */
+    
+    if (sortstate->sort_Flag == false) {
+       SO1_printf("ExecSort: %s\n",
+                  "sortstate == false -> sorting subplan");
+       /* ----------------
+        *  set all relations to be scanned in the forward direction 
+        *  while creating the temporary relation.
+        * ----------------
+        */
+       estate->es_direction = EXEC_FRWD;
+       
+       /* ----------------
+        *   if we couldn't create the temp or current relations then
+        *   we print a warning and return NULL.
+        * ----------------
+        */
+       tempRelation =  sortstate->sort_TempRelation;
+       if (tempRelation == NULL) {
+           elog(DEBUG, "ExecSort: temp relation is NULL! aborting...");
+           return NULL;
+       }
+       
+       currentRelation = sortstate->csstate.css_currentRelation;
+       if (currentRelation == NULL) {
+           elog(DEBUG, "ExecSort: current relation is NULL! aborting...");
+           return NULL;
+       }
+       
+       /* ----------------
+        *   retrieve tuples from the subplan and
+        *   insert them in the temporary relation
+        * ----------------
+        */
+       outerNode = outerPlan((Plan *) node);
+       SO1_printf("ExecSort: %s\n",
+                  "inserting tuples into tempRelation");
+       
+       for (;;) {
+           slot = ExecProcNode(outerNode, (Plan*)node);
+           
+           if (TupIsNull(slot))
+               break;
+           
+           tupCount++;
+           
+           heapTuple = slot->val;
+           
+           heap_insert(tempRelation,   /* relation desc */
+                       heapTuple);     /* heap tuple to insert */
+           
+           ExecClearTuple(slot);
+       }
+       
+       /* ----------------
+        *   now sort the tuples in our temporary relation
+        *   into a new sorted relation using psort()
+        *
+        *   psort() seems to require that the relations
+        *   are created and opened in advance.
+        *   -cim 1/25/90
+        * ----------------
+        */
+       keycount = node->keycount;
+       sortkeys = (ScanKey)sortstate->sort_Keys;
+       SO1_printf("ExecSort: %s\n",
+                  "calling psort");
+       
+       /*
+        * If no tuples were fetched from the proc node return NULL now
+        * psort dumps it if 0 tuples are in the relation and I don't want
+        * to try to debug *that* routine!!
+        */
+       if (tupCount == 0)
+           return NULL;
+       
+       psort(tempRelation,     /* old relation */
+             currentRelation,  /* new relation */
+             keycount,         /* number keys */
+             sortkeys);        /* keys */
+       
+       if (currentRelation == NULL) {
+           elog(DEBUG, "ExecSort: sorted relation is NULL! aborting...");
+           return NULL;
+       }
+       
+       /* ----------------
+        *   restore to user specified direction
+        * ----------------
+        */
+       estate->es_direction = dir;
+       
+       /* ----------------
+        *   now initialize the scan descriptor to scan the
+        *   sorted relation and update the sortstate information
+        * ----------------
+        */
+       currentScanDesc = heap_beginscan(currentRelation,    /* relation */
+                                        ScanDirectionIsBackward(dir),
+                                        /* bkwd flag */
+                                        NowTimeQual,        /* time qual */
+                                        0,               /* num scan keys */
+                                        NULL);           /* scan keys */
+       
+       sortstate->csstate.css_currentRelation = currentRelation;
+       sortstate->csstate.css_currentScanDesc = currentScanDesc;
+       
+       /* ----------------
+        *  make sure the tuple descriptor is up to date
+        * ----------------
+        */
+       slot = sortstate->csstate.css_ScanTupleSlot;
+       
+       slot->ttc_tupleDescriptor = 
+           RelationGetTupleDescriptor(currentRelation);
+       
+       /* ----------------
+        *  finally set the sorted flag to true
+        * ----------------
+        */
+       sortstate->sort_Flag = true;
+    }
+    else {
+       slot =  sortstate->csstate.css_ScanTupleSlot; 
+    }
+    
+    SO1_printf("ExecSort: %s\n",
+              "retrieveing tuple from sorted relation");
+    
+    /* ----------------
+     * at this point we know we have a sorted relation so
+     *  we preform a simple scan on it with amgetnext()..
+     * ----------------
+     */
+    currentScanDesc = sortstate->csstate.css_currentScanDesc;
+    
+    heapTuple = heap_getnext(currentScanDesc,  /* scan desc */
+                            ScanDirectionIsBackward(dir),
+                            /* bkwd flag */
+                            &buffer);          /* return: buffer */
+    
+    /* Increase the pin count on the buffer page, because the tuple stored in 
+       the slot also points to it (as well as the scan descriptor). If we 
+       don't, ExecStoreTuple will decrease the pin count on the next iteration.
+       - 01/09/93 */
+    
+    if (buffer != InvalidBuffer) 
+        IncrBufferRefCount(buffer);
+    
+    return ExecStoreTuple(heapTuple,   /* tuple to store */
+                         slot,         /* slot to store in */
+                         buffer,       /* this tuple's buffer */
+                         false);       /* don't free stuff from amgetnext */
+}
+
+/* ----------------------------------------------------------------
+ *     ExecInitSort
+ *
+ * old comments
+ *     Creates the run-time state information for the sort node
+ *     produced by the planner and initailizes its outer subtree.
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitSort(Sort *node, EState *estate, Plan *parent)
+{
+    SortState          *sortstate;
+    Plan               *outerPlan;
+    ScanKey            sortkeys;
+    TupleDesc          tupType;
+    Oid                        tempOid;
+    Oid                        sortOid;
+    Relation           tempDesc;
+    Relation           sortedDesc;
+    
+    SO1_printf("ExecInitSort: %s\n",
+              "initializing sort node");
+    
+    /* ----------------
+     *  assign the node's execution state
+     * ----------------
+     */
+    node->plan.state = estate;
+    
+    /* ----------------
+     * create state structure
+     * ----------------
+     */
+    sortstate = makeNode(SortState);
+    sortstate->sort_Flag = 0;
+    sortstate->sort_Keys = NULL;
+    sortstate->sort_TempRelation = NULL;
+
+    node->sortstate = sortstate;
+    
+    /* ----------------
+     *  Miscellanious initialization
+     *
+     *      +  assign node's base_id
+     *       + assign debugging hooks
+     *
+     *  Sort nodes don't initialize their ExprContexts because
+     *  they never call ExecQual or ExecTargetList.
+     * ----------------
+     */
+    ExecAssignNodeBaseInfo(estate, &sortstate->csstate.cstate, parent);
+    
+#define SORT_NSLOTS 1
+    /* ----------------
+     * tuple table initialization
+     *
+     *  sort nodes only return scan tuples from their sorted
+     *  relation.
+     * ----------------
+     */
+      ExecInitScanTupleSlot(estate, &sortstate->csstate);  
+      ExecInitResultTupleSlot(estate, &sortstate->csstate.cstate); 
+    
+    /* ----------------
+     * initializes child nodes
+     * ----------------
+     */
+    outerPlan = outerPlan((Plan *) node);
+    ExecInitNode(outerPlan, estate, (Plan *) node);
+    
+    /* ----------------
+     * initialize sortstate information
+     * ----------------
+     */
+    sortkeys = FormSortKeys(node);
+    sortstate->sort_Keys = sortkeys;
+    sortstate->sort_Flag = false;
+    
+    /* ----------------
+     *         initialize tuple type.  no need to initialize projection
+     *  info because this node doesn't do projections.
+     * ----------------
+     */
+    ExecAssignScanTypeFromOuterPlan((Plan *) node, &sortstate->csstate); 
+    sortstate->csstate.cstate.cs_ProjInfo = NULL;
+    
+    /* ----------------
+     * get type information needed for ExecCreatR
+     * ----------------
+     */
+    tupType = ExecGetScanType(&sortstate->csstate); 
+    
+    /* ----------------
+     * ExecCreatR wants its second argument to be an object id of
+     *  a relation in the range table or _TEMP_RELATION_ID_
+     *  indicating that the relation is not in the range table.
+     *
+     *  In the second case ExecCreatR creates a temp relation.
+     *  (currently this is the only case we support -cim 10/16/89)
+     * ----------------
+     */
+    tempOid =  node->tempid;
+    sortOid =  _TEMP_RELATION_ID_;
+    
+    /* ----------------
+     * create the temporary relations
+     * ----------------
+     */
+/*    len =            ExecTargetListLength(node->plan.targetlist); */
+    tempDesc =                 ExecCreatR(tupType, tempOid);
+    sortedDesc =       ExecCreatR(tupType, sortOid);
+    
+    /* ----------------
+     * save the relation descriptor in the sortstate
+     * ----------------
+     */
+    sortstate->sort_TempRelation = tempDesc;
+    sortstate->csstate.css_currentRelation = sortedDesc;
+    SO1_printf("ExecInitSort: %s\n",
+              "sort node initialized");
+    
+    /* ----------------
+     *  return relation oid of temporary sort relation in a list
+     * (someday -- for now we return LispTrue... cim 10/12/89)
+     * ----------------
+     */
+    return TRUE;
+}
+
+int
+ExecCountSlotsSort(Sort *node)
+{
+    return ExecCountSlotsNode(outerPlan((Plan *)node)) +
+       ExecCountSlotsNode(innerPlan((Plan *)node)) +
+           SORT_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecEndSort(node)
+ *
+ * old comments
+ *     destroys the temporary relation.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndSort(Sort *node)
+{
+    SortState  *sortstate;
+    Relation   tempRelation;
+    Relation   sortedRelation;
+    Plan       *outerPlan;
+    
+    /* ----------------
+     * get info from the sort state 
+     * ----------------
+     */
+    SO1_printf("ExecEndSort: %s\n",
+              "shutting down sort node");
+    
+    sortstate =      node->sortstate;
+    tempRelation =   sortstate->sort_TempRelation;
+    sortedRelation = sortstate->csstate.css_currentRelation;
+    
+    heap_destroyr(tempRelation);
+    heap_destroyr(sortedRelation);
+
+    
+    /* ----------------
+     * close the sorted relation and shut down the scan.
+     * ----------------
+     */
+    ExecCloseR((Plan *) node);
+    
+    /* ----------------
+     * shut down the subplan
+     * ----------------
+     */
+    outerPlan = outerPlan((Plan *) node);
+    ExecEndNode(outerPlan, (Plan*)node);
+    
+    /* ----------------
+     * clean out the tuple table
+     * ----------------
+     */
+    ExecClearTuple(sortstate->csstate.css_ScanTupleSlot); 
+    
+    SO1_printf("ExecEndSort: %s\n",
+              "sort node shutdown");
+} 
+
+/* ----------------------------------------------------------------
+ *     ExecSortMarkPos
+ * ----------------------------------------------------------------
+ */
+void
+ExecSortMarkPos(Sort *node)
+{
+    SortState   *sortstate;
+    HeapScanDesc sdesc;
+    
+    /* ----------------
+     * if we haven't sorted yet, just return
+     * ----------------
+     */
+    sortstate =   node->sortstate;
+    if (sortstate->sort_Flag == false)
+       return; 
+    
+    sdesc = sortstate->csstate.css_currentScanDesc;
+    heap_markpos(sdesc);
+    return;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecSortRestrPos
+ * ----------------------------------------------------------------
+ */
+void
+ExecSortRestrPos(Sort *node)
+{
+    SortState   *sortstate;
+    HeapScanDesc sdesc;
+    
+    /* ----------------
+     * if we haven't sorted yet, just return.
+     * ----------------
+     */
+    sortstate =  node->sortstate;
+    if (sortstate->sort_Flag == false)
+       return;
+    
+    /* ----------------
+     * restore the scan to the previously marked position
+     * ----------------
+     */
+    sdesc = sortstate->csstate.css_currentScanDesc;
+    heap_restrpos(sdesc);
+}
diff --git a/src/backend/executor/nodeSort.h b/src/backend/executor/nodeSort.h
new file mode 100644 (file)
index 0000000..e8abbce
--- /dev/null
@@ -0,0 +1,23 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeSort.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        NODESORT_H
+#define        NODESORT_H
+
+extern TupleTableSlot *ExecSort(Sort *node);
+extern bool ExecInitSort(Sort *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsSort(Sort *node);
+extern void ExecEndSort(Sort *node);
+extern void ExecSortMarkPos(Sort *node);
+extern void ExecSortRestrPos(Sort *node);
+
+#endif /* NODESORT_H */
diff --git a/src/backend/executor/nodeTee.c b/src/backend/executor/nodeTee.c
new file mode 100644 (file)
index 0000000..5bdbf06
--- /dev/null
@@ -0,0 +1,503 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeTee.c--
+ *    
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *   DESCRIPTION
+ *      This code provides support for a tee node, which allows multiple
+ *    parent in a megaplan. 
+ *      
+ *   INTERFACE ROUTINES
+ *      ExecTee        
+ *     ExecInitTee
+ *     ExecEndTee
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <sys/file.h>
+#include "utils/palloc.h"
+#include "utils/relcache.h" 
+#include "storage/bufmgr.h"  /* for IncrBufferRefCount */
+#include "optimizer/internal.h"
+#include "executor/executor.h"
+#include "executor/nodeTee.h"
+#include "catalog/catalog.h"
+#include "tcop/pquery.h"
+
+/* ------------------------------------------------------------------
+ *     ExecInitTee
+ *
+ *     Create tee state
+ *
+ * ------------------------------------------------------------------
+ */
+bool
+ExecInitTee(Tee* node, EState *currentEstate, Plan * parent)
+{
+    TeeState      *teeState;
+    Plan          *outerPlan;
+    int           len;
+    Relation      bufferRel;
+    TupleDesc     tupType;
+    EState        *estate;
+    
+    /* it is possible that the Tee has already been initialized
+       since it can be reached by multiple parents.
+       If it is already initialized, simply return and do 
+       not initialize the children nodes again
+    */
+    if (node->plan.state)
+      return TRUE; 
+
+    /* ----------------
+     *  assign the node's execution state
+     * ----------------
+     */
+    /* make a new executor state, because we have a different
+       es_range_table */
+
+/*     node->plan.state = estate;*/
+
+    estate = CreateExecutorState();
+    estate->es_direction = currentEstate->es_direction;
+    estate->es_BaseId = currentEstate->es_BaseId;
+    estate->es_BaseId = currentEstate->es_BaseId;
+    estate->es_tupleTable = currentEstate->es_tupleTable;
+    estate->es_refcount = currentEstate->es_refcount;
+    estate->es_junkFilter = currentEstate->es_junkFilter;
+
+    /* use the range table for Tee subplan since the range tables
+       for the two parents may be different */
+    if (node->rtentries)
+       estate->es_range_table = node->rtentries; 
+    else
+       estate->es_range_table = currentEstate->es_range_table;
+
+    node->plan.state = estate;
+
+
+    /* ----------------
+     * create teeState structure
+     * ----------------
+     */
+    teeState = makeNode(TeeState);
+    teeState->tee_leftPlace = 0;
+    teeState->tee_rightPlace = 0;
+    teeState->tee_lastPlace = 0;
+    teeState->tee_bufferRel = NULL;
+    teeState->tee_leftScanDesc = NULL;
+    teeState->tee_rightScanDesc = NULL;
+
+
+    node->teestate = teeState;
+    
+    /* ----------------
+     *  Miscellanious initialization
+     *
+     *      +  assign node's base_id
+     *       + assign debugging hooks and
+     *       + create expression context for node
+     * ----------------
+     */
+    ExecAssignNodeBaseInfo(estate, &(teeState->cstate), parent);
+    ExecAssignExprContext(estate, &(teeState->cstate));
+
+#define TEE_NSLOTS 2
+    /* ----------------
+     * initialize tuple slots
+     * ----------------
+     */
+    ExecInitResultTupleSlot(estate, &(teeState->cstate));
+    
+    /* initialize child nodes */
+    outerPlan = outerPlan((Plan*) node);
+    ExecInitNode(outerPlan, estate, (Plan*) node);
+
+    /* ----------------
+     *  the tuple type info is from the outer plan of this node
+     *  the result type is also the same as the outerplan  
+     */
+    ExecAssignResultTypeFromOuterPlan((Plan*) node, &(teeState->cstate));
+    ExecAssignProjectionInfo((Plan*)node, &teeState->cstate);
+    
+    /* ---------------------------------------
+       initialize temporary relation to buffer tuples 
+    */
+    tupType = ExecGetResultType(&(teeState->cstate));
+    len =     ExecTargetListLength(((Plan*)node)->targetlist);
+
+/*    bufferRel = ExecCreatR(len, tupType, _TEMP_RELATION_ID_);  */
+
+    /* create a catalogued relation even though this is a temporary relation */
+    /* cleanup of catalogued relations is easier to do */
+    
+    if (node->teeTableName[0] != '\0') {
+       Relation r;
+
+       teeState->tee_bufferRelname = pstrdup(node->teeTableName);
+
+       /* we are given an tee table name, 
+          if a relation by that name exists, then we open it,
+          else we create it and then open it */
+       r = RelationNameGetRelation(teeState->tee_bufferRelname);
+
+       if (RelationIsValid(r))
+           bufferRel = heap_openr(teeState->tee_bufferRelname);
+       else
+           bufferRel = heap_open(heap_create(teeState->tee_bufferRelname,
+/*FIX */                                     NULL,
+                                   'n',
+                                   DEFAULT_SMGR,
+                                   tupType));
+    }
+    else {
+       sprintf(teeState->tee_bufferRelname,
+               "ttemp_%d", /* 'ttemp' for 'tee' temporary*/
+               newoid()); 
+/*     bufferRel = ExecCreatR(len, tupType, _TEMP_RELATION_ID); */
+           bufferRel = heap_open(heap_create(teeState->tee_bufferRelname,
+                                             NULL, /*XXX */
+                                             'n',
+                                             DEFAULT_SMGR,
+                                             tupType));
+    }
+
+    teeState->tee_bufferRel = bufferRel;
+
+    /*initialize a memory context for allocating thing like scan descriptors */
+    /* we do this so that on cleanup of the tee, we can free things.
+       if we didn't have our own memory context, we would be in the memory
+       context of the portal that we happen to be using at the moment */
+
+    teeState->tee_mcxt = (MemoryContext)CreateGlobalMemory(teeState->tee_bufferRelname);
+
+    /* don't initialize the scan descriptors here
+       because it's not good to initialize scan descriptors on empty
+       rels. Wait until the scan descriptors are needed
+       before initializing them. */
+    
+    teeState->tee_leftScanDesc = NULL;
+    teeState->tee_rightScanDesc = NULL;
+    
+    return TRUE;
+}
+
+int 
+ExecCountSlotsTee(Tee *node)
+{
+  /* Tee nodes can't have innerPlans */
+    return ExecCountSlotsNode(outerPlan(node)) + TEE_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+   initTeeScanDescs
+      initializes the left and right scandescs on the temporary
+      relation of a Tee node
+
+      must open two separate scan descriptors,
+      because the left and right scans may be at different points
+* ----------------------------------------------------------------
+*/
+void 
+initTeeScanDescs(Tee* node)
+{
+  TeeState *teeState;
+  Relation bufferRel;
+  ScanDirection dir;
+  MemoryContext       orig;
+
+  teeState = node->teestate;
+  if (teeState->tee_leftScanDesc && teeState->tee_rightScanDesc)  
+    return;
+
+  orig = CurrentMemoryContext;
+  MemoryContextSwitchTo(teeState->tee_mcxt);
+
+  bufferRel = teeState->tee_bufferRel;
+  dir = ((Plan*)node)->state->es_direction; /* backwards not handled yet XXX */
+
+  if (teeState->tee_leftScanDesc == NULL)
+    {
+      teeState->tee_leftScanDesc = heap_beginscan(bufferRel,
+                                                 ScanDirectionIsBackward(dir),
+                                                 NowTimeQual, /* time qual */
+                                                 0,       /* num scan keys */
+                                                 NULL    /* scan keys */
+                                                 );
+    }
+  if (teeState->tee_rightScanDesc == NULL)
+    {
+      teeState->tee_rightScanDesc = heap_beginscan(bufferRel,
+                                                 ScanDirectionIsBackward(dir),
+                                                 NowTimeQual, /* time qual */
+                                                 0,     /* num scan keys */
+                                                 NULL  /* scan keys */
+                                                 );
+    }
+
+    MemoryContextSwitchTo(orig);
+}
+
+/* ----------------------------------------------------------------
+ *     ExecTee(node)
+ *
+ *
+ *      A Tee serves to connect a subplan to multiple parents.
+ *      the subplan is always the outplan of the Tee node.
+ *      
+ *      The Tee gets requests from either leftParent or rightParent,
+ *      fetches the result tuple from the child, and then
+ *      stored the result into a temporary relation (serving as a queue).
+ *      leftPlace and rightPlace keep track of where the left and rightParents
+ *      are.
+ *      If a parent requests a tuple and that parent is not at the end 
+ *      of the temporary relation, then the request is satisfied from
+ *      the queue instead of by executing the child plan
+ *
+ * ----------------------------------------------------------------
+ */
+
+TupleTableSlot*
+ExecTee(Tee *node, Plan *parent)
+{
+    EState             *estate;
+    TeeState            *teeState;
+    int                 leftPlace, rightPlace, lastPlace;
+    int                 branch;
+    TupleTableSlot*     result;
+    TupleTableSlot*     slot;
+    Plan                *childNode;
+    ScanDirection       dir;
+    HeapTuple           heapTuple;
+    Relation            bufferRel;
+    HeapScanDesc        scanDesc;
+    Buffer              buffer;
+
+    estate = ((Plan*)node)->state;
+    teeState = node->teestate;
+    leftPlace = teeState->tee_leftPlace;
+    rightPlace = teeState->tee_rightPlace;
+    lastPlace = teeState->tee_lastPlace;
+    bufferRel = teeState->tee_bufferRel;
+
+    childNode = outerPlan(node);
+
+    dir = estate->es_direction;
+
+    /* XXX doesn't handle backwards direction yet */
+
+    if (parent == node->leftParent) {
+       branch = leftPlace;
+      }
+    else
+      if ( (parent == node->rightParent) || (parent == (Plan*) node)) 
+         /* the tee node could be the root node of the plan,
+            in which case, we treat it like a right-parent pull*/
+       {
+       branch = rightPlace;
+      }
+    else
+      {
+       elog(WARN,"A Tee node can only be executed from its left or right parent\n");
+       return NULL;
+      }
+
+    if (branch == lastPlace)
+      { /* we're at the end of the queue already,
+          - get a new tuple from the child plan,
+          - store it in the queue,
+          - increment lastPlace,
+          - increment leftPlace or rightPlace as appropriate,
+          - and return result
+          */
+       slot = ExecProcNode(childNode, (Plan*)node);
+       if (!TupIsNull(slot)) 
+         {
+           heapTuple = slot->val;
+           
+           /* insert into temporary relation */
+           heap_insert(bufferRel, heapTuple);
+           
+           /* once there is data in the temporary relation,
+              ensure that the left and right scandescs are initialized */
+           initTeeScanDescs(node);
+
+           scanDesc = (parent == node->leftParent) ?
+             teeState->tee_leftScanDesc : teeState->tee_rightScanDesc;
+
+           {
+      /* move the scandesc forward so we don't re-read this tuple later */
+             HeapTuple throwAway;
+             /* Buffer buffer;*/
+             throwAway = heap_getnext(scanDesc,
+                                      ScanDirectionIsBackward(dir),
+                                  /*  &buffer */
+                                      (Buffer*)NULL);
+           }
+
+           /* set the shouldFree field of the child's slot so that
+              when the child's slot is free'd, this tuple isn't free'd also */
+           /* does this mean this tuple has to be garbage collected later??*/
+           slot->ttc_shouldFree = false;
+
+           teeState->tee_lastPlace = lastPlace + 1;
+         }
+       result = slot;
+      }        
+    else 
+      {/* the desired data already exists in the temporary relation */ 
+       scanDesc = (parent == node->leftParent) ?
+         teeState->tee_leftScanDesc : teeState->tee_rightScanDesc;
+
+       heapTuple = heap_getnext(scanDesc,
+                                ScanDirectionIsBackward(dir),
+                                &buffer);
+
+       /* Increase the pin count on the buffer page, because the
+          tuple stored in the slot also points to it (as well as
+          the scan descriptor). If we don't, ExecStoreTuple will
+          decrease the pin count on the next iteration. */
+    
+       if (buffer != InvalidBuffer) 
+         IncrBufferRefCount(buffer);
+    
+       slot = teeState->cstate.cs_ResultTupleSlot;
+       slot->ttc_tupleDescriptor = RelationGetTupleDescriptor(bufferRel);
+
+       result =   ExecStoreTuple(heapTuple,/* tuple to store */
+                                 slot,  /* slot to store in */
+                                 buffer,/* this tuple's buffer */
+                                 false); /* don't free stuff from heap_getnext */
+                                
+      }
+
+    if (parent == node->leftParent)
+      {
+       teeState->tee_leftPlace = leftPlace+1;
+      }
+    else
+      {
+       teeState->tee_rightPlace = rightPlace+1;
+      }
+
+    return result;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecTeeReScan(node)
+ *   
+ *     Rescans the relation.
+ * ----------------------------------------------------------------
+ */
+void
+ExecTeeReScan(Tee *node, ExprContext *exprCtxt, Plan *parent)
+{
+
+    EState             *estate;
+    TeeState            *teeState;
+    ScanDirection       dir;
+
+    estate = ((Plan*)node)->state;
+    teeState = node->teestate;
+
+    dir = estate->es_direction;
+    
+    /* XXX doesn't handle backwards direction yet */
+
+    if (parent == node->leftParent) {
+      if (teeState->tee_leftScanDesc)
+       {
+         heap_rescan(teeState->tee_leftScanDesc,
+                     ScanDirectionIsBackward(dir),
+                     NULL);
+         teeState->tee_leftPlace = 0;
+       }
+    }
+    else
+      {
+       if (teeState->tee_rightScanDesc)
+         {
+         heap_rescan(teeState->tee_leftScanDesc,
+                     ScanDirectionIsBackward(dir),
+                     NULL);
+         teeState->tee_rightPlace = 0;
+         }
+      }
+}
+
+
+/* ---------------------------------------------------------------------
+ *     ExecEndTee
+ *
+ *   End the Tee node, and free up any storage
+ * since a Tee node can be downstream of multiple parent nodes,
+ * only free when both parents are done
+ * --------------------------------------------------------------------
+ */
+
+void 
+ExecEndTee(Tee* node, Plan* parent)
+{
+    EState             *estate;
+    TeeState            *teeState;
+    int                 leftPlace, rightPlace, lastPlace;
+    Relation            bufferRel;
+    MemoryContext       orig;
+
+    estate = ((Plan*)node)->state;
+    teeState = node->teestate;
+    leftPlace = teeState->tee_leftPlace;
+    rightPlace = teeState->tee_rightPlace;
+    lastPlace = teeState->tee_lastPlace;
+
+    if (!node->leftParent || parent == node->leftParent)
+       leftPlace = -1;
+
+    if (!node->rightParent || parent == node->rightParent)
+       rightPlace = -1;
+
+    if (parent == (Plan*)node) 
+      rightPlace = leftPlace = -1;
+
+    teeState->tee_leftPlace = leftPlace;
+    teeState->tee_rightPlace = rightPlace;
+    if ( (leftPlace == -1) && (rightPlace == -1) )
+      {
+       /* remove the temporary relations */
+       /* and close the scan descriptors */
+
+       bufferRel = teeState->tee_bufferRel;
+        if (bufferRel) {
+           heap_destroyr(bufferRel);
+         teeState->tee_bufferRel = NULL;
+         if (teeState->tee_mcxt) {
+           orig = CurrentMemoryContext;
+           MemoryContextSwitchTo(teeState->tee_mcxt);
+         }
+
+         if (teeState->tee_leftScanDesc)
+           {
+           heap_endscan(teeState->tee_leftScanDesc);
+           teeState->tee_leftScanDesc = NULL;
+         }
+         if (teeState->tee_rightScanDesc)
+           {
+           heap_endscan(teeState->tee_rightScanDesc);
+           teeState->tee_rightScanDesc = NULL;
+         }
+
+         if (teeState->tee_mcxt) {
+           MemoryContextSwitchTo(orig);
+           teeState->tee_mcxt = NULL;
+         }
+       }
+     }
+    
+}
+
diff --git a/src/backend/executor/nodeTee.h b/src/backend/executor/nodeTee.h
new file mode 100644 (file)
index 0000000..dab0c44
--- /dev/null
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeTee.h--
+ *    support functions for a Tee executor node
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef        NODETEE_H
+#define        NODETEE_H
+
+extern TupleTableSlot* ExecTee(Tee* node, Plan* parent);
+extern bool ExecInitTee(Tee* node, EState* estate, Plan* parent);
+extern void ExecTeeReScan(Tee *node, ExprContext *exprCtxt, Plan *parent);
+extern void ExecEndTee(Tee* node, Plan* parent);
+extern int ExecCountSlotsTee(Tee* node);
+
+#endif /* NODETEE_H */
diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c
new file mode 100644 (file)
index 0000000..d20093d
--- /dev/null
@@ -0,0 +1,316 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeUnique.c--
+ *    Routines to handle unique'ing of queries where appropriate
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *             ExecUnique      - generate a unique'd temporary relation
+ *             ExecInitUnique  - initialize node and subnodes..
+ *             ExecEndUnique   - shutdown node and subnodes
+ *
+ * NOTES
+ *     Assumes tuples returned from subplan arrive in
+ *     sorted order.
+ *
+ */
+#include "executor/executor.h"
+#include "executor/nodeUnique.h"
+#include "optimizer/clauses.h"
+#include "access/printtup.h" /* for typtoout() */
+#include "utils/builtins.h"  /* for namecpy()*/
+
+/* ----------------------------------------------------------------
+ *     ExecIdenticalTuples
+ *
+ *     This is a hack function used by ExecUnique to see if
+ *     two tuples are identical.  This should be provided
+ *     by the heap tuple code but isn't.  The real problem
+ *     is that we assume we can byte compare tuples to determine
+ *     if they are "equal".  In fact, if we have user defined
+ *     types there may be problems because it's possible that
+ *     an ADT may have multiple representations with the
+ *     same ADT value. -cim
+ * ----------------------------------------------------------------
+ */
+static bool    /* true if tuples are identical, false otherwise */
+ExecIdenticalTuples(TupleTableSlot *t1, TupleTableSlot *t2)
+{
+    HeapTuple  h1;
+    HeapTuple  h2;
+    char       *d1;
+    char       *d2;
+    int        len;
+    
+    h1 = t1->val;
+    h2 = t2->val;
+    
+    /* ----------------
+     * if tuples aren't the same length then they are 
+     *  obviously different (one may have null attributes).
+     * ----------------
+     */
+    if (h1->t_len != h2->t_len)
+       return false;
+    
+    /* ----------------
+     *  if the tuples have different header offsets then
+     *  they are different.  This will prevent us from returning
+     *  true when comparing tuples of one attribute where one of
+     *  two we're looking at is null (t_len - t_hoff == 0).
+     *  THE t_len FIELDS CAN BE THE SAME IN THIS CASE!!
+     * ----------------
+     */
+    if (h1->t_hoff != h2->t_hoff)
+       return false;
+    
+    /* ----------------
+     * ok, now get the pointers to the data and the
+     *  size of the attribute portion of the tuple.
+     * ----------------
+     */
+    d1 = (char *) GETSTRUCT(h1);
+    d2 = (char *) GETSTRUCT(h2);
+    len = (int) h1->t_len - (int) h1->t_hoff;
+    
+    /* ----------------
+     * byte compare the data areas and return the result.
+     * ----------------
+     */
+    if (memcmp(d1, d2, len) != 0)
+       return false;
+    
+    return true;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecUnique
+ *
+ *     This is a very simple node which filters out duplicate
+ *     tuples from a stream of sorted tuples from a subplan.
+ *
+ *     XXX see comments below regarding freeing tuples.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *               /* return: a tuple or NULL */
+ExecUnique(Unique *node)
+{
+    UniqueState                *uniquestate;
+    TupleTableSlot     *resultTupleSlot;
+    TupleTableSlot     *slot;
+    Plan               *outerPlan;
+    char                *uniqueAttr;
+    AttrNumber         uniqueAttrNum;
+    TupleDesc          tupDesc;
+    Oid                 typoutput;
+    
+    /* ----------------
+     * get information from the node
+     * ----------------
+     */
+    uniquestate =      node->uniquestate;
+    outerPlan =        outerPlan((Plan *) node);
+    resultTupleSlot =   uniquestate->cs_ResultTupleSlot;
+    uniqueAttr  =       node->uniqueAttr;
+    uniqueAttrNum =     node->uniqueAttrNum;
+
+    if (uniqueAttr) {
+      tupDesc = ExecGetResultType(uniquestate);
+      typoutput = typtoout((Oid)tupDesc->attrs[uniqueAttrNum]->atttypid);
+    }
+      
+    /* ----------------
+     * now loop, returning only non-duplicate tuples.
+     *  We assume that the tuples arrive in sorted order
+     *  so we can detect duplicates easily.
+     * ----------------
+     */
+    for (;;) {
+       /* ----------------
+        *   fetch a tuple from the outer subplan
+        * ----------------
+        */
+       slot = ExecProcNode(outerPlan, (Plan*)node);
+       if (TupIsNull(slot))
+           return NULL;
+       
+       /* ----------------
+        *   we use the result tuple slot to hold our saved tuples.
+        *   if we haven't a saved tuple to compare our new tuple with,
+        *   then we exit the loop. This new tuple as the saved tuple
+        *   the next time we get here.  
+        * ----------------
+        */
+       if (TupIsNull(resultTupleSlot))
+           break;
+       
+       /* ----------------
+        *   now test if the new tuple and the previous
+        *   tuple match.  If so then we loop back and fetch
+        *   another new tuple from the subplan.
+        * ----------------
+        */
+
+       if (uniqueAttr) {
+         /* to check equality, we check to see if the typoutput
+            of the attributes are equal */
+         bool isNull1,isNull2;
+         char *attr1, *attr2;
+         char *val1, *val2;
+
+         attr1 = heap_getattr(slot->val, InvalidBuffer, 
+                              uniqueAttrNum, tupDesc,&isNull1);
+         attr2 = heap_getattr(resultTupleSlot->val, InvalidBuffer, 
+                              uniqueAttrNum, tupDesc,&isNull2);
+
+         if (isNull1 == isNull2) {
+           if (isNull1) /* both are null, they are equal */
+             continue;
+           val1 = fmgr(typoutput, attr1, gettypelem(tupDesc->attrs[uniqueAttrNum]->atttypid));
+           val2 = fmgr(typoutput, attr2, gettypelem(tupDesc->attrs[uniqueAttrNum]->atttypid));
+           /* now, val1 and val2 are ascii representations so we can
+              use strcmp for comparison */
+           if (strcmp(val1,val2) == 0) /* they are equal */
+             continue;
+           else
+             break;
+         }
+         else /* one is null and the other isn't, they aren't equal */
+           break;
+             
+       }
+       else { 
+         if (! ExecIdenticalTuples(slot, resultTupleSlot))
+           break;
+       }
+         
+    }
+    
+    /* ----------------
+     * we have a new tuple different from the previous saved tuple
+     *  so we save it in the saved tuple slot.  We copy the tuple
+     *  so we don't increment the buffer ref count.
+     * ----------------
+     */
+    ExecStoreTuple(heap_copytuple(slot->val),
+                  resultTupleSlot,
+                  InvalidBuffer,
+                  true);
+    
+    return resultTupleSlot;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecInitUnique
+ *
+ *     This initializes the unique node state structures and
+ *     the node's subplan.
+ * ----------------------------------------------------------------
+ */
+bool   /* return: initialization status */
+ExecInitUnique(Unique *node, EState *estate, Plan *parent)
+{
+    UniqueState            *uniquestate;
+    Plan           *outerPlan;
+    char *uniqueAttr;
+    
+    /* ----------------
+     * assign execution state to node
+     * ----------------
+     */
+    node->plan.state = estate;
+    
+    /* ----------------
+     * create new UniqueState for node
+     * ----------------
+     */
+    uniquestate = makeNode(UniqueState);
+    node->uniquestate = uniquestate;
+    uniqueAttr = node->uniqueAttr;
+
+    /* ----------------
+     *  Miscellanious initialization
+     *
+     *      +  assign node's base_id
+     *       + assign debugging hooks and
+     *
+     *  Unique nodes have no ExprContext initialization because
+     *  they never call ExecQual or ExecTargetList.
+     * ----------------
+     */
+    ExecAssignNodeBaseInfo(estate, uniquestate, parent);
+    
+#define UNIQUE_NSLOTS 1
+    /* ------------
+     * Tuple table initialization
+     * ------------
+     */
+    ExecInitResultTupleSlot(estate, uniquestate);
+    
+    /* ----------------
+     * then initialize outer plan
+     * ----------------
+     */
+    outerPlan = outerPlan((Plan *) node);
+    ExecInitNode(outerPlan, estate, (Plan *) node);
+    
+    /* ----------------
+     * unique nodes do no projections, so initialize
+     *  projection info for this node appropriately
+     * ----------------
+     */
+    ExecAssignResultTypeFromOuterPlan((Plan *)node,uniquestate);
+    uniquestate->cs_ProjInfo = NULL;
+
+    if (uniqueAttr) {
+      TupleDesc tupDesc;
+      int i = 0;
+
+      tupDesc = ExecGetResultType(uniquestate);
+      /* the parser should have ensured that uniqueAttr is a legal attribute name*/
+      while ( strcmp((tupDesc->attrs[i]->attname).data, uniqueAttr) != 0)
+       i++;
+      node->uniqueAttrNum = i+1; /* attribute numbers start from 1 */
+    }
+    else
+      node->uniqueAttrNum = InvalidAttrNumber;
+
+    /* ----------------
+     * all done.
+     * ----------------
+     */
+    return TRUE;
+}
+
+int
+ExecCountSlotsUnique(Unique *node)
+{
+    return ExecCountSlotsNode(outerPlan(node)) +
+       ExecCountSlotsNode(innerPlan(node)) +
+           UNIQUE_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ *     ExecEndUnique
+ *
+ *     This shuts down the subplan and frees resources allocated
+ *     to this node.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndUnique(Unique *node)
+{
+    UniqueState            *uniquestate;
+    
+    uniquestate = node->uniquestate;
+    ExecEndNode(outerPlan((Plan *) node), (Plan*)node);
+    ExecClearTuple(uniquestate->cs_ResultTupleSlot);
+} 
diff --git a/src/backend/executor/nodeUnique.h b/src/backend/executor/nodeUnique.h
new file mode 100644 (file)
index 0000000..c31dbd9
--- /dev/null
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeUnique.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        NODEUNIQUE_H
+#define        NODEUNIQUE_H
+
+extern TupleTableSlot *ExecUnique(Unique *node);
+extern bool ExecInitUnique(Unique *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsUnique(Unique *node);
+extern void ExecEndUnique(Unique *node);
+
+#endif /* NODEUNIQUE_H */
diff --git a/src/backend/executor/tuptable.h b/src/backend/executor/tuptable.h
new file mode 100644 (file)
index 0000000..5cf300e
--- /dev/null
@@ -0,0 +1,72 @@
+/*-------------------------------------------------------------------------
+ *
+ * tuptable.h--
+ *    tuple table support stuff
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    The tuple table interface is getting pretty ugly.
+ *    It should be redesigned soon.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef TUPTABLE_H
+#define TUPTABLE_H
+
+/* ----------------
+ *     Note:  the executor tuple table is managed and manipulated by special
+ *     code and macros in executor/execTuples.c and tupTable.h
+ *
+ *     TupleTableSlot information
+ *
+ *         shouldFree          boolean - should we call pfree() on tuple
+ *         descIsNew           boolean - true when tupleDescriptor changes
+ *         tupleDescriptor     type information kept regarding the tuple data
+ *         buffer              the buffer for tuples pointing to disk pages
+ *
+ *     The executor stores pointers to tuples in a ``tuple table''
+ *     which is composed of TupleTableSlot's.  Some of the tuples
+ *     are pointers to buffer pages and others are pointers to
+ *     palloc'ed memory and the shouldFree variable tells us when
+ *     we may call pfree() on a tuple.  -cim 9/23/90
+ *
+ *     In the implementation of nested-dot queries such as
+ *     "retrieve (EMP.hobbies.all)", a single scan may return tuples
+ *     of many types, so now we return pointers to tuple descriptors
+ *     along with tuples returned via the tuple table.  -cim 1/18/90
+ * ----------------
+ */
+typedef struct TupleTableSlot {
+    NodeTag            type;
+    HeapTuple          val;
+    bool               ttc_shouldFree;
+    bool               ttc_descIsNew;
+    TupleDesc          ttc_tupleDescriptor;
+    Buffer             ttc_buffer;
+    int                        ttc_whichplan;
+} TupleTableSlot;
+
+/* ----------------
+ *     tuple table data structure
+ * ----------------
+ */
+typedef struct TupleTableData {
+    int                size;           /* size of the table */
+    int                next;           /* next available slot number */
+    TupleTableSlot *array;     /* array of TupleTableSlot's */
+} TupleTableData;
+
+typedef TupleTableData *TupleTable;
+
+/* 
+  tuple table macros are all excised from the system now
+  see executor.h for decls of functions defined in execTuples.c
+
+  - jolly
+*/
+
+#endif /* TUPTABLE_H */
diff --git a/src/backend/include/Makefile.inc b/src/backend/include/Makefile.inc
new file mode 100644 (file)
index 0000000..b1c2985
--- /dev/null
@@ -0,0 +1,16 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    this makefile is only use for collecting HEADERS
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+VPATH:= $(VPATH):$(CURDIR)/include
+
+HEADERS+= c.h libpq-fe.h miscadmin.h postgres.h
diff --git a/src/backend/include/c.h b/src/backend/include/c.h
new file mode 100644 (file)
index 0000000..39a1d20
--- /dev/null
@@ -0,0 +1,768 @@
+/*-------------------------------------------------------------------------
+ *
+ * c.h--
+ *    Fundamental C definitions.  This is included by every .c file in
+ *    postgres.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ *   TABLE OF CONTENTS
+ *
+ *     When adding stuff to this file, please try and put stuff
+ *     into the relevant section, or add new sections as appropriate.
+ *
+ *    section  description
+ *    -------  ------------------------------------------------
+ *      1)      bool, true, false, TRUE, FALSE
+ *     2)      __STDC__, non-ansi C definitions:
+ *             Pointer typedef, NULL
+ *             cpp magic macros
+ *             type prefixes: const, signed, volatile, inline
+ *     3)      standard system types
+ *      4)      datum type
+ *     5)      IsValid macros for system types
+ *     6)      offsetof, lengthof, endof 
+ *     7)      exception handling definitions, Assert, Trap, etc macros
+ *     8)      Min, Max, Abs macros
+ *     9)      externs
+ *      10)      Berkeley-specific defs
+ *     11)     system-specific hacks
+ *
+ *   NOTES
+ *
+ *     This file is MACHINE AND COMPILER dependent!!!  (For now.)
+ *
+ * ----------------------------------------------------------------
+ */
+#ifndef        C_H
+#define C_H
+
+/* ----------------------------------------------------------------
+ *             Section 1:  bool, true, false, TRUE, FALSE
+ * ----------------------------------------------------------------
+ */
+/*
+ * bool --
+ *     Boolean value, either true or false.
+ *
+ */
+#define false  ((char) 0)
+#define true   ((char) 1)
+typedef char   bool;
+typedef bool   *BoolPtr;
+
+#ifndef TRUE
+#define TRUE   1
+#endif /* TRUE */
+
+#ifndef FALSE
+#define FALSE  0
+#endif /* FALSE */
+
+/* ----------------------------------------------------------------
+ *             Section 2: __STDC__, non-ansi C definitions:
+ *
+ *             cpp magic macros
+ *             Pointer typedef, NULL
+ *             type prefixes: const, signed, volatile, inline
+ * ----------------------------------------------------------------
+ */
+
+#ifdef __STDC__ /* ANSI C */
+
+/*
+ * Pointer --
+ *     Variable holding address of any memory resident object.
+ */
+
+/*
+ *     XXX Pointer arithmetic is done with this, so it can't be void *
+ *     under "true" ANSI compilers.
+ */
+typedef char *Pointer;
+
+#ifndef        NULL
+/*
+ * NULL --
+ *     Null pointer.
+ */
+#define NULL   ((void *) 0)
+#endif /* !defined(NULL) */
+
+#define        HAVE_ANSI_CPP   /* all ANSI C compilers must have this! */
+#if defined(NEED_STD_HDRS)
+#undef NEED_STD_HDRS   /* all ANSI systems must have stddef/stdlib */
+#endif /* NEED_STD_HDRS */
+
+#else  /* !defined(__STDC__) */ /* NOT ANSI C */
+
+/*
+ * Pointer --
+ *     Variable containing address of any memory resident object.
+ */
+typedef char   *Pointer;
+
+#ifndef        NULL
+/*
+ * NULL --
+ *     Null pointer.
+ */
+#define NULL   0
+#endif /* !defined(NULL) */
+
+/*
+ * const --
+ *     Type modifier.  Identifies read only variables.
+ *
+ * Example:
+ *     extern const Version    RomVersion;
+ */
+#define const          /* const */
+
+/*
+ * signed --
+ *     Type modifier.  Identifies signed integral types.
+ */
+#define signed         /* signed */
+
+/*
+ * volatile --
+ *     Type modifier.  Identifies variables which may change in ways not
+ *     noticeable by the compiler, e.g. via asynchronous interrupts.
+ *
+ * Example:
+ *     extern volatile unsigned int    NumberOfInterrupts;
+ */
+#define volatile       /* volatile */
+
+#endif /* !defined(__STDC__) */ /* NOT ANSI C */
+
+/*
+ * CppAsString --
+ *     Convert the argument to a string, using the C preprocessor.
+ * CppConcat --
+ *     Concatenate two arguments together, using the C preprocessor.
+ */
+#if defined(HAVE_ANSI_CPP)
+
+#define CppAsString(identifier)        #identifier
+#define CppConcat(x, y)                x##y
+#define CppConcat0(x, y)       x##y
+#define CppConcat1(x, y)       x##y
+#define CppConcat2(x, y)       x##y
+#define CppConcat3(x, y)       x##y
+#define CppConcat4(x, y)       x##y
+
+#else /* !HAVE_ANSI_CPP */
+
+#define CppAsString(identifier)        "identifier"
+
+/*
+ * CppIdentity -- On Reiser based cpp's this is used to concatenate
+ *     two tokens.  That is
+ *             CppIdentity(A)B ==> AB
+ *     We renamed it to _private_CppIdentity because it should not
+ *     be referenced outside this file.  On other cpp's it
+ *     produces  A  B.
+ */
+#define _priv_CppIdentity(x)x
+#define CppConcat(x, y)                _priv_CppIdentity(x)y
+#define CppConcat0(x, y)       _priv_CppIdentity(x)y
+#define CppConcat1(x, y)       _priv_CppIdentity(x)y
+#define CppConcat2(x, y)       _priv_CppIdentity(x)y
+#define CppConcat3(x, y)       _priv_CppIdentity(x)y
+#define CppConcat4(x, y)       _priv_CppIdentity(x)y
+
+#endif /* !HAVE_ANSI_CPP */
+
+#ifndef __GNUC__       /* GNU cc */
+# define inline
+#endif
+
+#if defined(NEED_STD_HDRS)
+/*
+ * You're doomed.  We've removed almost all of our own C library 
+ * extern declarations because they conflict on the different
+ * systems.  You'll have to write your own stdlib.h.
+ */
+#include "stdlib.h"
+#else /* NEED_STD_HDRS */
+#include <stddef.h>
+#include <stdlib.h>
+#endif /* NEED_STD_HDRS */
+
+/* ----------------------------------------------------------------
+ *             Section 3:  standard system types
+ * ----------------------------------------------------------------
+ */
+
+/*
+ * intN --
+ *     Signed integer, AT LEAST N BITS IN SIZE,
+ *     used for numerical computations.
+ */
+typedef signed char    int8;           /* >= 8 bits */
+typedef signed short   int16;          /* >= 16 bits */
+typedef signed int     int32;          /* >= 32 bits */
+
+/*
+ * uintN --
+ *     Unsigned integer, AT LEAST N BITS IN SIZE,
+ *     used for numerical computations.
+ */
+typedef unsigned char  uint8;          /* >= 8 bits */
+typedef unsigned short uint16;         /* >= 16 bits */
+typedef unsigned int   uint32;         /* >= 32 bits */
+
+/*
+ * floatN --
+ *     Floating point number, AT LEAST N BITS IN SIZE,
+ *     used for numerical computations.
+ *
+ *     Since sizeof(floatN) may be > sizeof(char *), always pass
+ *     floatN by reference.
+ */
+typedef float          float32data;
+typedef double         float64data;
+typedef float          *float32;
+typedef double         *float64;
+
+/*
+ * boolN --
+ *     Boolean value, AT LEAST N BITS IN SIZE.
+ */
+typedef uint8          bool8;          /* >= 8 bits */
+typedef uint16         bool16;         /* >= 16 bits */
+typedef uint32         bool32;         /* >= 32 bits */
+
+/*
+ * bitsN --
+ *     Unit of bitwise operation, AT LEAST N BITS IN SIZE.
+ */
+typedef uint8          bits8;          /* >= 8 bits */
+typedef uint16         bits16;         /* >= 16 bits */
+typedef uint32         bits32;         /* >= 32 bits */
+
+/*
+ * wordN --
+ *     Unit of storage, AT LEAST N BITS IN SIZE,
+ *     used to fetch/store data.
+ */
+typedef uint8          word8;          /* >= 8 bits */
+typedef uint16         word16;         /* >= 16 bits */
+typedef uint32         word32;         /* >= 32 bits */
+
+/*
+ * Size --
+ *     Size of any memory resident object, as returned by sizeof.
+ */
+typedef unsigned int   Size;
+
+/*
+ * Index --
+ *     Index into any memory resident array.
+ *
+ * Note:
+ *     Indices are non negative.
+ */
+typedef unsigned int   Index;
+
+#define MAXDIM 6
+typedef struct {
+       int indx[MAXDIM];
+} IntArray;
+
+/*
+ * Offset --
+ *     Offset into any memory resident array.
+ *
+ * Note:
+ *     This differs from an Index in that an Index is always
+ *     non negative, whereas Offset may be negative.
+ */
+typedef signed int     Offset;
+
+/* ----------------------------------------------------------------
+ *             Section 4:  datum type + support macros
+ * ----------------------------------------------------------------
+ */
+/*
+ * datum.h --
+ *     POSTGRES abstract data type datum representation definitions.
+ *
+ * Note:
+ *
+ * Port Notes:
+ *  Postgres makes the following assumption about machines:
+ *
+ *  sizeof(Datum) == sizeof(long) >= sizeof(void *) >= 4
+ *
+ *  Postgres also assumes that
+ *
+ *  sizeof(char) == 1
+ *
+ *  and that 
+ *
+ *  sizeof(short) == 2
+ *
+ *  If your machine meets these requirements, Datums should also be checked
+ *  to see if the positioning is correct.
+ *
+ *     This file is MACHINE AND COMPILER dependent!!!
+ */
+
+typedef unsigned long Datum;   /* XXX sizeof(long) >= sizeof(void *) */
+typedef Datum *       DatumPtr;
+
+#define GET_1_BYTE(datum)   (((Datum) (datum)) & 0x000000ff)
+#define GET_2_BYTES(datum)  (((Datum) (datum)) & 0x0000ffff)
+#define GET_4_BYTES(datum)  (((Datum) (datum)) & 0xffffffff)
+#define SET_1_BYTE(value)   (((Datum) (value)) & 0x000000ff)
+#define SET_2_BYTES(value)  (((Datum) (value)) & 0x0000ffff)
+#define SET_4_BYTES(value)  (((Datum) (value)) & 0xffffffff)
+
+/*
+ * DatumGetChar --
+ *     Returns character value of a datum.
+ */
+
+#define DatumGetChar(X) ((char) GET_1_BYTE(X))
+
+/*
+ * CharGetDatum --
+ *     Returns datum representation for a character.
+ */
+
+#define CharGetDatum(X) ((Datum) SET_1_BYTE(X))
+
+/*
+ * Int8GetDatum --
+ *     Returns datum representation for an 8-bit integer.
+ */
+
+#define Int8GetDatum(X) ((Datum) SET_1_BYTE(X))
+
+/*
+ * DatumGetUInt8 --
+ *     Returns 8-bit unsigned integer value of a datum.
+ */
+
+#define DatumGetUInt8(X) ((uint8) GET_1_BYTE(X))
+
+/*
+ * UInt8GetDatum --
+ *     Returns datum representation for an 8-bit unsigned integer.
+ */
+
+#define UInt8GetDatum(X) ((Datum) SET_1_BYTE(X))
+
+/*
+ * DatumGetInt16 --
+ *     Returns 16-bit integer value of a datum.
+ */
+
+#define DatumGetInt16(X) ((int16) GET_2_BYTES(X))
+
+/*
+ * Int16GetDatum --
+ *     Returns datum representation for a 16-bit integer.
+ */
+
+#define Int16GetDatum(X) ((Datum) SET_2_BYTES(X))
+
+/*
+ * DatumGetUInt16 --
+ *     Returns 16-bit unsigned integer value of a datum.
+ */
+
+#define DatumGetUInt16(X) ((uint16) GET_2_BYTES(X))
+
+/*
+ * UInt16GetDatum --
+ *     Returns datum representation for a 16-bit unsigned integer.
+ */
+
+#define UInt16GetDatum(X) ((Datum) SET_2_BYTES(X))
+
+/*
+ * DatumGetInt32 --
+ *     Returns 32-bit integer value of a datum.
+ */
+
+#define DatumGetInt32(X) ((int32) GET_4_BYTES(X))
+
+/*
+ * Int32GetDatum --
+ *     Returns datum representation for a 32-bit integer.
+ */
+
+#define Int32GetDatum(X) ((Datum) SET_4_BYTES(X))
+
+/*
+ * DatumGetUInt32 --
+ *     Returns 32-bit unsigned integer value of a datum.
+ */
+
+#define DatumGetUInt32(X) ((uint32) GET_4_BYTES(X))
+
+/*
+ * UInt32GetDatum --
+ *     Returns datum representation for a 32-bit unsigned integer.
+ */
+
+#define UInt32GetDatum(X) ((Datum) SET_4_BYTES(X))
+
+/*
+ * DatumGetObjectId --
+ *     Returns object identifier value of a datum.
+ */
+
+#define DatumGetObjectId(X) ((Oid) GET_4_BYTES(X))
+
+/*
+ * ObjectIdGetDatum --
+ *     Returns datum representation for an object identifier.
+ */
+
+#define ObjectIdGetDatum(X) ((Datum) SET_4_BYTES(X))
+
+/*
+ * DatumGetPointer --
+ *     Returns pointer value of a datum.
+ */
+
+#define DatumGetPointer(X) ((Pointer) X)
+
+/*
+ * PointerGetDatum --
+ *     Returns datum representation for a pointer.
+ */
+
+#define PointerGetDatum(X) ((Datum) X)
+
+/*
+ * DatumGetName --
+ *     Returns name value of a datum.
+ */
+
+#define DatumGetName(X) ((Name) DatumGetPointer((Datum) X))
+
+/*
+ * NameGetDatum --
+ *     Returns datum representation for a name.
+ */
+
+#define NameGetDatum(X) PointerGetDatum((Pointer) X)
+
+
+/*
+ * DatumGetFloat32 --
+ *     Returns 32-bit floating point value of a datum.
+ *     This is really a pointer, of course.
+ */
+
+#define DatumGetFloat32(X) ((float32) DatumGetPointer((Datum) X))
+
+/*
+ * Float32GetDatum --
+ *     Returns datum representation for a 32-bit floating point number.
+ *     This is really a pointer, of course.
+ */
+
+#define Float32GetDatum(X) PointerGetDatum((Pointer) X)
+
+/*
+ * DatumGetFloat64 --
+ *     Returns 64-bit floating point value of a datum.
+ *     This is really a pointer, of course.
+ */
+
+#define DatumGetFloat64(X) ((float64) DatumGetPointer(X))
+
+/*
+ * Float64GetDatum --
+ *     Returns datum representation for a 64-bit floating point number.
+ *     This is really a pointer, of course.
+ */
+
+#define Float64GetDatum(X) PointerGetDatum((Pointer) X)
+
+/* ----------------------------------------------------------------
+ *             Section 5:  IsValid macros for system types
+ * ----------------------------------------------------------------
+ */
+/*
+ * BoolIsValid --
+ *     True iff bool is valid.
+ */
+#define        BoolIsValid(boolean)    ((boolean) == false || (boolean) == true)
+
+/*
+ * PointerIsValid --
+ *     True iff pointer is valid.
+ */
+#define PointerIsValid(pointer)        (bool)((void*)(pointer) != NULL)
+
+/*
+ * PointerIsInBounds --
+ *     True iff pointer is within given bounds.
+ *
+ * Note:
+ *     Assumes the bounded interval to be [min,max),
+ *     i.e. closed on the left and open on the right.
+ */
+#define PointerIsInBounds(pointer, min, max) \
+       ((min) <= (pointer) && (pointer) < (max))
+
+/*
+ * PointerIsAligned --
+ *     True iff pointer is properly aligned to point to the given type.
+ */
+#define PointerIsAligned(pointer, type)        \
+       (((long)(pointer) % (sizeof (type))) == 0)
+
+/* ----------------------------------------------------------------
+ *             Section 6:  offsetof, lengthof, endof
+ * ----------------------------------------------------------------
+ */
+/*
+ * offsetof --
+ *     Offset of a structure/union field within that structure/union.
+ *
+ *     XXX This is supposed to be part of stddef.h, but isn't on
+ *     some systems (like SunOS 4).
+ */
+#ifndef offsetof
+#define offsetof(type, field)  ((long) &((type *)0)->field)
+#endif /* offsetof */
+
+/*
+ * lengthof --
+ *     Number of elements in an array.
+ */
+#define lengthof(array)        (sizeof (array) / sizeof ((array)[0]))
+
+/*
+ * endof --
+ *     Address of the element one past the last in an array.
+ */
+#define endof(array)   (&array[lengthof(array)])
+
+/* ----------------------------------------------------------------
+ *             Section 7:  exception handling definitions
+ *                         Assert, Trap, etc macros
+ * ----------------------------------------------------------------
+ */
+/*
+ * Exception Handling definitions
+ */
+
+typedef char *ExcMessage;
+typedef struct Exception {
+       ExcMessage      message;
+} Exception;
+
+/*
+ * NO_ASSERT_CHECKING, if defined, turns off all the assertions.
+ * - plai  9/5/90
+ *
+ * It should _NOT_ be undef'ed in releases or in benchmark copies
+ * 
+ * #undef NO_ASSERT_CHECKING
+ */
+
+/*
+ * Trap --
+ *     Generates an exception if the given condition is true.
+ *
+ */
+#define Trap(condition, exception) \
+       { if (condition) \
+               ExceptionalCondition(CppAsString(condition), &(exception), \
+                       (char*)NULL, __FILE__, __LINE__); }
+
+/*    
+ *  TrapMacro is the same as Trap but it's intended for use in macros:
+ *
+ *     #define foo(x) (AssertM(x != 0) && bar(x))
+ *
+ *  Isn't CPP fun?
+ */
+#define TrapMacro(condition, exception) \
+    ((bool) ((! condition) || \
+            (ExceptionalCondition(CppAsString(condition), \
+                                 &(exception), \
+                                 (char*) NULL, __FILE__, __LINE__))))
+    
+#ifdef NO_ASSERT_CHECKING
+#define Assert(condition)
+#define AssertMacro(condition) true
+#define AssertArg(condition)
+#define AssertState(condition)
+#else
+#define Assert(condition) \
+       Trap(!(condition), FailedAssertion)
+
+#define AssertMacro(condition) \
+       TrapMacro(!(condition), FailedAssertion)
+
+#define AssertArg(condition) \
+       Trap(!(condition), BadArg)
+
+#define AssertState(condition) \
+       Trap(!(condition), BadState)
+
+#endif   /* NO_ASSERT_CHECKING */
+
+/*
+ * LogTrap --
+ *     Generates an exception with a message if the given condition is true.
+ *
+ */
+#define LogTrap(condition, exception, printArgs) \
+       { if (condition) \
+               ExceptionalCondition(CppAsString(condition), &(exception), \
+                       form printArgs, __FILE__, __LINE__); }
+
+/*    
+ *  LogTrapMacro is the same as LogTrap but it's intended for use in macros:
+ *
+ *     #define foo(x) (LogAssertMacro(x != 0, "yow!") && bar(x))
+ */
+#define LogTrapMacro(condition, exception, printArgs) \
+    ((bool) ((! condition) || \
+            (ExceptionalCondition(CppAsString(condition), \
+                                  &(exception), \
+                                  form printArgs, __FILE__, __LINE__))))
+    
+#ifdef NO_ASSERT_CHECKING
+#define LogAssert(condition, printArgs)
+#define LogAssertMacro(condition, printArgs) true
+#define LogAssertArg(condition, printArgs)
+#define LogAssertState(condition, printArgs)
+#else
+#define LogAssert(condition, printArgs) \
+       LogTrap(!(condition), FailedAssertion, printArgs)
+
+#define LogAssertMacro(condition, printArgs) \
+       LogTrapMacro(!(condition), FailedAssertion, printArgs)
+
+#define LogAssertArg(condition, printArgs) \
+       LogTrap(!(condition), BadArg, printArgs)
+
+#define LogAssertState(condition, printArgs) \
+       LogTrap(!(condition), BadState, printArgs)
+
+#endif   /* NO_ASSERT_CHECKING */
+
+/* ----------------------------------------------------------------
+ *             Section 8:  Min, Max, Abs macros
+ * ----------------------------------------------------------------
+ */
+/*
+ * Max --
+ *     Return the maximum of two numbers.
+ */
+#define Max(x, y)      ((x) > (y) ? (x) : (y))
+
+/*
+ * Min --
+ *     Return the minimum of two numbers.
+ */
+#define Min(x, y)      ((x) < (y) ? (x) : (y))
+
+/*
+ * Abs --
+ *     Return the absolute value of the argument.
+ */
+#define Abs(x)         ((x) >= 0 ? (x) : -(x))
+
+/* ----------------------------------------------------------------
+ *             Section 9: externs
+ * ----------------------------------------------------------------
+ */
+
+extern Exception       FailedAssertion;
+extern Exception       BadArg;
+extern Exception       BadState;
+
+/* in utils/error/assert.c */
+extern int ExceptionalCondition(char *conditionName,
+                               Exception *exceptionP, char *details,
+                               char *fileName, int lineNumber);
+
+
+/* ----------------
+ *     form is used by assert and the exception handling stuff
+ * ----------------
+ */
+extern char *form(char *fmt, ...);
+
+
+
+/* ----------------------------------------------------------------
+ *             Section 10: berkeley-specific configuration
+ *
+ * this section contains settings which are only relevant to the UC Berkeley
+ * sites.  Other sites can ignore this
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     storage managers
+ *
+ *     These are experimental and are not supported in the code that
+ *     we distribute to other sites.
+ * ----------------
+ */
+#ifdef SEQUOIA
+#define MAIN_MEMORY
+#endif
+
+
+
+/* ----------------------------------------------------------------
+ *             Section 11: system-specific hacks
+ *
+ *     This should be limited to things that absolutely have to be
+ *     included in every source file.  The changes should be factored
+ *     into a separate file so that changes to one port don't require
+ *     changes to c.h (and everyone recompiling their whole system).
+ * ----------------------------------------------------------------
+ */
+
+#if defined(PORTNAME_hpux) 
+#include "port/hpux/fixade.h"          /* for 8.07 unaligned access fixup */
+#endif /* PORTNAME_hpux */
+
+#if defined(PORTNAME_sparc)
+#define        memmove(d, s, l)        bcopy(s, d, l)
+#endif
+
+/* These are for things that are one way on Unix and another on NT */
+#ifndef WIN32
+#define        NULL_DEV        "/dev/null"
+#define COPY_CMD       "cp"
+#define SEP_CHAR       '/'
+#else
+#define NULL_DEV       "NUL"
+#define COPY_CMD       "copy"
+#define SEP_CHAR       '\\'
+#endif /* WIN32 */
+
+#if defined(WIN32)
+#include "port/win32/nt.h"
+#include "port/win32/machine.h"
+#endif /* WIN32 */
+
+/* ----------------
+ *     end of c.h
+ * ----------------
+ */
+#endif /* C_H */
diff --git a/src/backend/include/miscadmin.h b/src/backend/include/miscadmin.h
new file mode 100644 (file)
index 0000000..df93104
--- /dev/null
@@ -0,0 +1,193 @@
+/*-------------------------------------------------------------------------
+ *
+ * miscadmin.h--
+ *    this file contains general postgres administration and initialization
+ *    stuff that used to be spread out between the following files:
+ *     globals.h                       global variables
+ *     magic.h                         PG_RELEASE, PG_VERSION, etc defines
+ *     pdir.h                          directory path crud
+ *     pinit.h                         postgres initialization
+ *     pmod.h                          processing modes
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    some of the information in this file will be moved to
+ *    other files.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MISCADMIN_H
+#define MISCADMIN_H
+
+/* ----------------
+ *     note: <sys/types.h> was in unix.h  This should be moved
+ *     to the .c files.
+ * ----------------
+ */
+#include <sys/types.h>
+
+#include "postgres.h"
+#include "storage/backendid.h"
+
+/*****************************************************************************
+ *    globals.h --                                                           *
+ *****************************************************************************/
+
+/* #include "storage/sinval.h" */
+
+/*
+ * from postmaster/postmaster.c
+ */
+extern int PostmasterMain(int argc, char* argv[]);
+
+/*
+ * from utils/init/globals.c
+ */
+extern int Portfd;
+extern int Noversion;    /* moved from magic.c */
+extern int MasterPid;    /* declared and defined in utils/initglobals.c */
+extern int Quiet;
+extern char *DataDir;   
+
+extern char      OutputFileName[];
+extern void      InitGlobals();
+
+/*
+ * done in storage/backendid.h for now.
+ *
+ * extern BackendId    MyBackendId;
+ * extern BackendTag   MyBackendTag;
+ */
+extern bool        MyDatabaseIdIsInitialized;
+extern Oid         MyDatabaseId;
+extern bool        TransactionInitWasProcessed;
+
+extern bool        IsUnderPostmaster;
+extern bool        IsPostmaster;
+
+extern short       DebugLvl;
+
+extern Oid         LastOidProcessed;   /* for query rewrite */
+
+#define MAX_PARSE_BUFFER 8192
+
+/* 
+ *     default number of buffers in buffer pool
+ * 
+ */
+#define NDBUFS 64
+
+/*****************************************************************************
+ *     magic.h         - definitions of the indexes of the magic numbers    *
+ *****************************************************************************/
+
+#define        PG_RELEASE      5
+#define PG_VERSION     1
+#define        PG_VERFILE      "PG_VERSION"
+
+/*****************************************************************************
+ *    pdir.h --                                                              *
+ *         POSTGRES directory path definitions.                             *
+ *****************************************************************************/
+
+/* now in utils/init/miscinit.c */
+extern char *GetDatabasePath(void);
+extern char *GetDatabaseName(void);
+extern void SetDatabaseName(char *name);
+extern void SetDatabasePath(char *path);
+extern char *GetPgUserName(void);
+extern void SetPgUserName(void);
+extern Oid GetUserId(void);
+extern void SetUserId(void);
+extern char *GetPGHome(void);
+extern char *GetPGData(void);
+extern int ValidateBackend(char *path);
+extern int FindBackend(char *backend, char *argv0);
+extern int CheckPathAccess(char *path, char *name, int open_mode);
+
+
+/*****************************************************************************
+ *    pmod.h --                                                              *
+ *         POSTGRES processing mode definitions.                            *
+ *****************************************************************************/
+/*
+ * Description:
+ *     There are four processing modes in POSTGRES.  They are NoProcessing
+ * or "none," BootstrapProcessing or "bootstrap," InitProcessing or
+ * "initialization," and NormalProcessing or "normal."
+ *
+ *     If a POSTGRES binary is in normal mode, then all code may be executed
+ * normally.  In the none mode, only bookkeeping code may be called.  In
+ * particular, access method calls may not occur in this mode since the
+ * execution state is outside a transaction.
+ *
+ *     The final two processing modes are used during special times.  When the
+ * system state indicates bootstrap processing, transactions are all given
+ * transaction id "one" and are consequently guarenteed to commit.  This mode
+ * is used during the initial generation of template databases.
+ *
+ * Finally, the execution state is in initialization mode until all normal
+ * initialization is complete.  Some code behaves differently when executed in
+ * this mode to enable system bootstrapping.
+ */
+
+typedef enum ProcessingMode {
+    NoProcessing,              /* "nothing" can be done */
+    BootstrapProcessing,       /* bootstrap creation of template database */
+    InitProcessing,            /* initializing system */
+    NormalProcessing           /* normal processing */
+} ProcessingMode;
+
+
+/*****************************************************************************
+ *    pinit.h --                                                             *
+ *         POSTGRES initialization and cleanup definitions.                 *
+ *****************************************************************************/
+/*
+ * Note:
+ *     XXX AddExitHandler not defined yet.
+ */
+
+typedef        int16   ExitStatus;
+
+#define        NormalExitStatus        (0)
+#define        FatalExitStatus         (127)
+/* XXX are there any other meaningful exit codes? */
+
+/* in utils/init/postinit.c */
+extern void InitMyDatabaseId(void);
+extern void DoChdirAndInitDatabaseNameAndPath(char *name, char *path);
+extern void InitUserid(void);
+extern void InitCommunication(void);
+extern void InitStdio(void);
+
+extern bool PostgresIsInitialized;
+
+extern void InitPostgres(char *name);
+
+/* in miscinit.c */
+extern void ExitPostgres(ExitStatus status);
+extern void AbortPostgres(void);
+extern void StatusBackendExit(int status);
+extern void StatusPostmasterExit(int status);
+
+extern bool IsNoProcessingMode(void);
+extern bool IsBootstrapProcessingMode(void);
+extern bool IsInitProcessingMode(void);
+extern bool IsNormalProcessingMode(void);
+extern void SetProcessingMode(ProcessingMode mode);
+extern ProcessingMode GetProcessingMode(void);
+
+
+/*
+ * Prototypes for utils/init/magic.c
+ */
+extern int DatabaseMetaGunkIsConsistent(char database[], char path[]);
+extern int ValidPgVersion(char path []);
+extern void SetPgVersion(char path []);
+
+#endif /* MISCADMIN_H */
diff --git a/src/backend/include/postgres.h b/src/backend/include/postgres.h
new file mode 100644 (file)
index 0000000..79a14f2
--- /dev/null
@@ -0,0 +1,224 @@
+/*-------------------------------------------------------------------------
+ *
+ * postgres.h--
+ *    definition of (and support for) postgres system types.
+ * this file is included by almost every .c in the system
+ *
+ * Copyright (c) 1995, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ *   NOTES
+ *     this file will eventually contain the definitions for the
+ *     following (and perhaps other) system types:
+ *
+ *             int2       int4       float4       float8
+ *              Oid        regproc    RegProcedure
+ *              aclitem
+ *              struct varlena
+ *              char8      char16      int28      oid8
+ *              bytea      text
+ *             NameData   Name   
+ *              oidint4    oidint2    oidname
+ *
+ *   TABLE OF CONTENTS
+ *     1)      simple type definitions
+ *     2)      varlena and array types
+ *     3)      TransactionId and CommandId
+ *     4)      genbki macros used by catalog/pg_xxx.h files
+ *     5)      random SIGNBIT, MAXPGPATH, STATUS macros
+ *
+ * ----------------------------------------------------------------
+ */
+#ifndef POSTGRES_H
+#define POSTGRES_H
+
+#include "c.h"
+
+/* ----------------------------------------------------------------
+ *             Section 1:  simple type definitions
+ * ----------------------------------------------------------------
+ */
+
+typedef        int16   int2;
+typedef int32  int4;
+typedef float  float4;
+typedef double float8;
+
+typedef int4 aclitem;
+
+
+typedef uint32 Oid;
+#define InvalidOid     0
+#define OidIsValid(objectId)  ((bool) (objectId != InvalidOid))
+
+/* unfortunately, both regproc and RegProcedure are used */
+typedef Oid regproc; 
+typedef Oid RegProcedure;
+
+/* ptr to func returning (char *) */
+typedef char * ((*func_ptr)());        
+
+
+#define RegProcedureIsValid(p)  OidIsValid(p)
+
+/* ----------------------------------------------------------------
+ *             Section 2:  variable length and array types
+ * ----------------------------------------------------------------
+ */
+/* ----------------
+ *     struct varlena
+ * ----------------
+ */
+struct varlena {
+       int32   vl_len;
+       char    vl_dat[1];
+};
+
+#define        VARSIZE(PTR)    (((struct varlena *)(PTR))->vl_len)
+#define        VARDATA(PTR)    (((struct varlena *)(PTR))->vl_dat)
+#define        VARHDRSZ        sizeof(int32)
+
+typedef struct varlena bytea;
+typedef struct varlena text;
+
+typedef struct char8 {
+       char    data[8];
+} char8;
+
+/* ----------------
+ *     char16
+ * ----------------
+ */
+typedef struct char16 {
+       char    data[16];
+} char16;
+
+typedef char16 *Char16;
+
+typedef int2 int28[8];
+typedef Oid oid8[8];
+
+/* char16 is distinct from Name.
+   now, you can truly change the max length of system names
+   by altering the NAMEDATALEN define below.
+   don't set the value too high because tuples are still constrained
+   to be less than 8K 
+*/
+
+ /* NAMEDATALEN is the maximum string length (counting terminating null) 
+    of a Name */ 
+/* defined in Makefile.global */
+/* if you change the value of NAMEDATALEN, you may need to change the
+    alignment of the 'name' type in pg_type.h */
+#ifndef NAMEDATALEN
+#define NAMEDATALEN 16
+#endif /* NAMEDATALEN */
+/* OIDNAMELEN should be NAMEDATALEN + sizeof(Oid) */
+#ifndef OIDNAMELEN
+#define OIDNAMELEN 20
+#endif /* OIDNAMELEN */
+
+typedef struct nameData {
+    char data[NAMEDATALEN];
+} NameData;
+typedef NameData       *Name;
+
+/* ----------------
+ *     oidint4
+ *
+ *     this is a new system type used by the file interface.
+ * ----------------
+ */
+typedef struct OidInt4Data {
+       Oid     oi_oid;
+       int32           oi_int4;
+} OidInt4Data;
+
+typedef struct OidInt4Data     *OidInt4;
+
+/* ----------------
+ *     oidint2
+ *
+ *     this is a new system type used to define indices on two attrs.
+ * ----------------
+ */
+typedef struct OidInt2Data {
+       Oid     oi_oid;
+       int16           oi_int2;
+} OidInt2Data;
+
+typedef struct OidInt2Data     *OidInt2;
+
+/* ----------------
+ *     oidname
+ *
+ *     this is a new system type used to define indices on two attrs.
+ * ----------------
+ */
+typedef struct OidNameData {
+       Oid     id;
+       NameData name;
+} OidNameData;
+
+typedef struct OidNameData     *OidName;
+
+/* ----------------------------------------------------------------
+ *             Section 3: TransactionId and CommandId
+ * ----------------------------------------------------------------
+ */
+
+typedef uint32                 TransactionId;
+#define InvalidTransactionId   0
+typedef uint16                 CommandId;
+#define FirstCommandId 0
+
+/* ----------------------------------------------------------------
+ *             Section 4: genbki macros used by the
+ *                        catalog/pg_xxx.h files
+ * ----------------------------------------------------------------
+ */
+#define CATALOG(x) \
+    typedef struct CppConcat(FormData_,x)
+
+#define DATA(x) extern int errno
+#define DECLARE_INDEX(x) extern int errno
+
+#define BUILD_INDICES
+#define BOOTSTRAP
+
+#define BKI_BEGIN
+#define BKI_END
+
+/* ----------------------------------------------------------------
+ *             Section 5:  random stuff
+ *                         SIGNBIT, MAXPGPATH, STATUS...
+ * ----------------------------------------------------------------
+ */
+
+/* msb for int/unsigned */
+#define        SIGNBIT (0x8000)
+
+/* msb for char */
+#define        CSIGNBIT (1 << 7)
+
+/* ----------------
+ *     global variables which should probably go someplace else.
+ * ----------------
+ */
+#define        MAXPGPATH       128
+
+#define STATUS_OK               (0)
+#define STATUS_ERROR            (-1)
+#define STATUS_NOT_FOUND        (-2)
+#define STATUS_INVALID          (-3)
+#define STATUS_UNCATALOGUED     (-4)
+#define STATUS_REPLACED         (-5)
+#define STATUS_NOT_DONE                (-6)
+#define STATUS_BAD_PACKET      (-7)
+#define STATUS_FOUND            (1)
+
+#endif /* POSTGRES_H */
diff --git a/src/backend/lib/Makefile.inc b/src/backend/lib/Makefile.inc
new file mode 100644 (file)
index 0000000..daae670
--- /dev/null
@@ -0,0 +1,20 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the lib module (miscellaneous stuff)
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+VPATH:=$(VPATH):$(CURDIR)/lib
+
+
+SRCS_LIB= bit.c fstack.c hasht.c lispsort.c qsort.c stringinfo.c dllist.c
+
+HEADERS+= fstack.h hasht.h lispsort.h qsort.h stringinfo.h dllist.h
+
diff --git a/src/backend/lib/bit.c b/src/backend/lib/bit.c
new file mode 100644 (file)
index 0000000..0d7d316
--- /dev/null
@@ -0,0 +1,45 @@
+/*-------------------------------------------------------------------------
+ *
+ * bit.c--
+ *    Standard bit array code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * utils/memutils.h contains declarations of the functions in this file
+ */
+#include "utils/memutils.h"    
+
+void
+BitArraySetBit(BitArray bitArray, BitIndex bitIndex)
+{      
+    bitArray[bitIndex/BitsPerByte]
+       |= (1 << (BitsPerByte - (bitIndex % BitsPerByte) - 1));
+    return;
+}
+
+void
+BitArrayClearBit(BitArray bitArray, BitIndex bitIndex)
+{
+    bitArray[bitIndex/BitsPerByte]
+       &= ~(1 << (BitsPerByte - (bitIndex % BitsPerByte) - 1));
+    return;
+}
+
+bool
+BitArrayBitIsSet(BitArray bitArray, BitIndex bitIndex)
+{      
+    return( (bool) (((bitArray[bitIndex / BitsPerByte] &
+                     (1 << (BitsPerByte - (bitIndex % BitsPerByte)
+                            - 1)
+                      )
+                     ) != 0 ) ? 1 : 0) );
+}
+
diff --git a/src/backend/lib/dllist.c b/src/backend/lib/dllist.c
new file mode 100644 (file)
index 0000000..3f70631
--- /dev/null
@@ -0,0 +1,204 @@
+/*-------------------------------------------------------------------------
+ *
+ * dllist.c--
+ *    this is a simple doubly linked list implementation
+ *    replaces the old simplelists stuff
+ *    the elements of the lists are void*
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "c.h"
+#include "lib/dllist.h"
+
+Dllist*
+DLNewList()
+{
+  Dllist* l;
+
+  l = malloc(sizeof(Dllist));
+  l->dll_head = 0;
+  l->dll_tail = 0;
+
+  return l;
+}
+
+ /* free up a list and all the nodes in it*/
+void
+DLFreeList(Dllist* l)
+{
+  Dlelem* curr;
+
+  while ( (curr = DLRemHead(l)) != 0)
+      free(curr);
+
+  free(l);
+}
+
+Dlelem* 
+DLNewElem(void* val)
+{
+ Dlelem* e;
+ e = malloc(sizeof(Dlelem));
+ e->dle_next = 0;
+ e->dle_prev = 0;
+ e->dle_val = val;
+ e->dle_list = 0;
+ return e;
+}
+
+void
+DLFreeElem(Dlelem* e)
+{
+  free(e);
+}
+
+Dlelem*
+DLGetHead(Dllist* l)
+{
+  return (l ? l->dll_head : 0);
+}
+
+/* get the value stored in the first element */
+void*
+DLGetHeadVal(Dllist* l)
+{
+  Dlelem* e = DLGetHead(l);
+  
+  return (e ? e->dle_val : 0);
+}
+
+Dlelem* 
+DLGetTail(Dllist* l)
+{
+  return (l ? l->dll_tail : 0);
+}
+
+/* get the value stored in the first element */
+void*
+DLGetTailVal(Dllist* l)
+{
+  Dlelem* e = DLGetTail(l);
+  
+  return (e ? e->dle_val : 0);
+}
+
+
+Dlelem* 
+DLGetPred(Dlelem* e) /* get predecessor */
+{
+  return (e ? e->dle_prev : 0);
+}
+
+Dlelem* 
+DLGetSucc(Dlelem* e) /* get successor */
+{
+    return (e ? e->dle_next : 0);
+}
+
+void
+DLRemove(Dlelem* e)
+{
+  Dllist* l;
+
+  if (e->dle_prev)
+    e->dle_prev->dle_next = e->dle_next;
+  if (e->dle_next)
+    e->dle_next->dle_prev = e->dle_prev;
+
+  /* check to see if we're removing the head or tail */
+  l = e->dle_list;
+  if (e == l->dll_head)
+    DLRemHead(l);
+  if (e == l->dll_tail)
+    DLRemTail(l);
+
+}
+
+void    
+DLAddHead(Dllist* l, Dlelem* e)
+{
+  e->dle_list = l;
+
+  if (l->dll_head)  {
+    l->dll_head->dle_prev = e;
+    e->dle_next = l->dll_head;
+  }
+  e->dle_prev = 0;
+  l->dll_head = e;
+
+  if (l->dll_tail == 0) /* if this is first element added */
+    l->dll_tail = l->dll_head;
+}
+
+void
+DLAddTail(Dllist* l, Dlelem* e)
+{
+  e->dle_list = l;
+
+  if (l->dll_tail)   {
+    l->dll_tail->dle_next = e;
+    e->dle_prev = l->dll_tail;
+  }
+  e->dle_next = 0;
+  l->dll_tail = e;
+
+  if (l->dll_head == 0) /* if this is first element added */
+    l->dll_head = l->dll_tail;
+}
+
+Dlelem* 
+DLRemHead(Dllist* l) 
+{
+ /* remove and return the head */
+  Dlelem* result;
+
+  if (l->dll_head == 0)
+    return 0;
+
+  result = l->dll_head;
+  if (l->dll_head->dle_next) {
+    l->dll_head->dle_next->dle_prev = 0;
+  }
+
+  l->dll_head = l->dll_head->dle_next;
+
+  result->dle_next = 0;
+  result->dle_list = 0;
+    
+  if (result == l->dll_tail) /* if the head is also the tail */
+    l->dll_tail = 0;
+
+  return result;
+}
+
+Dlelem* 
+DLRemTail(Dllist* l)
+{
+ /* remove and return the tail */
+  Dlelem* result;
+
+  if (l->dll_tail == 0 )
+    return 0;
+
+  result = l->dll_tail;
+  if (l->dll_tail->dle_prev) {
+    l->dll_tail->dle_prev->dle_next = 0;
+  }
+    l->dll_tail = l->dll_tail->dle_prev;
+
+  result->dle_prev = 0;
+  result->dle_list = 0;
+
+  if (result == l->dll_head) /* if the tail is also the head */
+    l->dll_head = 0;
+
+  return result;
+}
+
diff --git a/src/backend/lib/dllist.h b/src/backend/lib/dllist.h
new file mode 100644 (file)
index 0000000..6f4bf8b
--- /dev/null
@@ -0,0 +1,72 @@
+/*-------------------------------------------------------------------------
+ *
+ * dllist.h--
+ *      simple doubly linked list primitives
+ *      the elements of the list are void* so the lists can contain
+ *      anything
+ *      Dlelem can only be in one list at a time
+ *     
+ *
+ *   Here's a small example of how to use Dllist's :
+ * 
+ *   Dllist *lst;
+ *   Dlelem *elt;
+ *   void   *in_stuff;    -- stuff to stick in the list
+ *   void   *out_stuff
+ *
+ *   lst = DLNewList();                -- make a new dllist
+ *   DLAddHead(lst, DLNewElem(in_stuff)); -- add a new element to the list
+ *                                           with in_stuff as the value
+ *    ...  
+ *   elt = DLGetHead(lst);             -- retrieve the head element
+ *   out_stuff = (void*)DLE_VAL(elt);  -- get the stuff out
+ *   DLRemove(elt);                    -- removes the element from its list
+ *   DLFreeElem(elt);                  -- free the element since we don't
+ *                                        use it anymore
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef DLLIST_H
+#define DLLIST_H
+
+#include "c.h"
+
+struct Dllist;
+struct Dlelem;
+
+typedef struct Dlelem {
+  struct Dlelem *dle_next;  /* next element */
+  struct Dlelem *dle_prev;  /* previous element */
+  void   *dle_val; /* value of the element */
+  struct Dllist *dle_list;  /* what list this element is in */
+} Dlelem;
+
+typedef struct Dllist {
+  Dlelem *dll_head;
+  Dlelem *dll_tail;
+} Dllist;
+  
+extern Dllist* DLNewList(); /* initialize a new list */
+extern void    DLFreeList(Dllist*); /* free up a list and all the nodes in it*/
+extern Dlelem* DLNewElem(void* val); 
+extern void    DLFreeElem(Dlelem*); 
+extern Dlelem* DLGetHead(Dllist*);
+extern Dlelem* DLGetTail(Dllist*);
+extern void*   DLGetHeadVal(Dllist*);
+extern void*   DLGetTailVal(Dllist*);
+extern Dlelem* DLGetPred(Dlelem*); /* get predecessor */
+extern Dlelem* DLGetSucc(Dlelem*); /* get successor */
+extern void    DLRemove(Dlelem*); /* removes node from list*/
+extern void    DLAddHead(Dllist* list, Dlelem* node);
+extern void    DLAddTail(Dllist* list, Dlelem* node);
+extern Dlelem* DLRemHead(Dllist* list); /* remove and return the head */
+extern Dlelem* DLRemTail(Dllist* list); /* remove and return the tail */
+
+#define DLE_VAL(x)  (x->dle_val)
+
+#endif /* DLLIST_H */
diff --git a/src/backend/lib/fstack.c b/src/backend/lib/fstack.c
new file mode 100644 (file)
index 0000000..b551943
--- /dev/null
@@ -0,0 +1,153 @@
+/*-------------------------------------------------------------------------
+ *
+ * fstack.c--
+ *    Fixed format stack definitions.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+#include "lib/fstack.h"
+
+/*
+ * Internal function definitions
+ */
+
+/*
+ * FixedItemIsValid --
+ *     True iff item is valid.
+ */
+#define FixedItemIsValid(item) PointerIsValid(item)
+
+/*
+ * FixedStackGetItemBase --
+ *     Returns base of enclosing structure.
+ */
+#define FixedStackGetItemBase(stack, item) \
+       ((Pointer)((char *)(item) - (stack)->offset))
+
+/*
+ * FixedStackGetItem --
+ *     Returns item of given pointer to enclosing structure.
+ */
+#define FixedStackGetItem(stack, pointer) \
+       ((FixedItem)((char *)(pointer) + (stack)->offset))
+
+/*
+ * External functions
+ */
+
+/*
+ * FixedStackIsValid --
+ *     True iff stack is valid.
+ */
+static bool
+FixedStackIsValid(FixedStack stack)
+{
+    return ((bool)PointerIsValid(stack));
+}
+
+
+void
+FixedStackInit(FixedStack stack, Offset offset)
+{
+    AssertArg(PointerIsValid(stack));
+    
+    stack->top = NULL;
+    stack->offset = offset;
+}
+
+Pointer
+FixedStackPop(FixedStack stack)
+{
+    Pointer    pointer;
+    
+    AssertArg(FixedStackIsValid(stack));
+    
+    if (!PointerIsValid(stack->top)) {
+       return (NULL);
+    }
+    
+    pointer = FixedStackGetItemBase(stack, stack->top);
+    stack->top = stack->top->next;
+    
+    return (pointer);
+}
+
+void
+FixedStackPush(FixedStack stack, Pointer pointer)
+{
+    FixedItem  item = FixedStackGetItem(stack, pointer);
+    
+    AssertArg(FixedStackIsValid(stack));
+    AssertArg(PointerIsValid(pointer));
+    
+    item->next = stack->top;
+    stack->top = item;
+}
+
+
+/*
+ * FixedStackContains --
+ *     True iff ordered stack contains given element.
+ *
+ * Note:
+ *     This is inefficient.  It is intended for debugging use only.
+ *
+ * Exceptions:
+ *     BadArg if stack is invalid.
+ *     BadArg if pointer is invalid.
+ */
+static bool
+FixedStackContains(FixedStack stack, Pointer pointer)
+{
+    FixedItem  next;
+    FixedItem  item;
+    
+    AssertArg(FixedStackIsValid(stack));
+    AssertArg(PointerIsValid(pointer));
+    
+    item = FixedStackGetItem(stack, pointer);
+    
+    for (next = stack->top; FixedItemIsValid(next); next = next->next) {
+       if (next == item) {
+           return (true);
+       }
+    }
+    return (false);
+}
+
+Pointer
+FixedStackGetTop(FixedStack stack)
+{
+    AssertArg(FixedStackIsValid(stack));
+    
+    if (!PointerIsValid(stack->top)) {
+       return (NULL);
+    }
+    
+    return (FixedStackGetItemBase(stack, stack->top));
+}
+
+Pointer
+FixedStackGetNext(FixedStack stack, Pointer pointer)
+{
+    FixedItem  item;
+    
+    /* AssertArg(FixedStackIsValid(stack)); */
+    /* AssertArg(PointerIsValid(pointer)); */
+    AssertArg(FixedStackContains(stack, pointer));
+    
+    item = FixedStackGetItem(stack, pointer)->next;
+    
+    if (!PointerIsValid(item)) {
+       return (NULL);
+    }
+    
+    return(FixedStackGetItemBase(stack, item));
+}
diff --git a/src/backend/lib/fstack.h b/src/backend/lib/fstack.h
new file mode 100644 (file)
index 0000000..0deec17
--- /dev/null
@@ -0,0 +1,113 @@
+/*-------------------------------------------------------------------------
+ *
+ * fstack.h--
+ *    Fixed format stack definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * Note:
+ *     Fixed format stacks assist in the construction of FIFO stacks of
+ *     fixed format structures.  Structures which are to be stackable
+ *     should contain a FixedItemData component.  A stack is initilized
+ *     with the offset of the FixedItemData component of the structure
+ *     it will hold.  By doing so, push and pop operations are simplified
+ *     for the callers.  All references to stackable items are pointers
+ *     to the base of the structure instead of pointers to the
+ *     FixedItemData component.
+ *
+ */
+#ifndef        FSTACK_H
+#define FSTACK_H
+
+#include "c.h"
+
+/*
+ * FixedItem --
+ *     Fixed format stackable item chain component.
+ *
+ * Note:
+ *     Structures must contain one FixedItemData component per stack in
+ *     which it will be an item.
+ */
+typedef struct FixedItemData   FixedItemData;
+typedef FixedItemData          *FixedItem;
+
+struct FixedItemData {
+       FixedItem       next;   /* next item or NULL */
+};
+
+/*
+ * FixedStack --
+ *     Fixed format stack.
+ */
+typedef struct FixedStackData {
+       FixedItem       top;    /* Top item on the stack or NULL */
+       Offset          offset; /* Offset from struct base to item */
+       /* this could be signed short int! */
+} FixedStackData;
+
+typedef FixedStackData         *FixedStack;
+
+/*
+ * FixedStackInit --
+ *     Iniitializes stack for structures with given fixed component offset.
+ *
+ * Exceptions:
+ *     BadArg if stack is invalid pointer.
+ */
+extern void FixedStackInit(FixedStack stack, Offset offset);
+
+/*
+ * FixedStackPop --
+ *     Returns pointer to top structure on stack or NULL if empty stack.
+ *
+ * Exceptions:
+ *     BadArg if stack is invalid.
+ */
+Pointer FixedStackPop(FixedStack stack);
+
+/*
+ * FixedStackPush --
+ *     Places structure associated with pointer onto top of stack.
+ *
+ * Exceptions:
+ *     BadArg if stack is invalid.
+ *     BadArg if pointer is invalid.
+ */
+extern void FixedStackPush(FixedStack stack, Pointer pointer);
+
+/*
+ * FixedStackGetTop --
+ *     Returns pointer to top structure of a stack.  This item is not poped.
+ *
+ * Note:
+ *     This is not part of the normal stack interface.  It is intended for
+ *      debugging use only.
+ *
+ * Exceptions:
+ *     BadArg if stack is invalid.
+ */
+extern Pointer FixedStackGetTop(FixedStack stack);
+
+/*
+ * FixedStackGetNext --
+ *     Returns pointer to next structure after pointer of a stack.
+ *
+ * Note:
+ *     This is not part of the normal stack interface.  It is intended for
+ *      debugging use only.
+ *
+ * Exceptions:
+ *     BadArg if stack is invalid.
+ *     BadArg if pointer is invalid.
+ *     BadArg if stack does not contain pointer.
+ */
+extern Pointer FixedStackGetNext(FixedStack stack, Pointer pointer);
+
+#endif /* FSTACK_H */
diff --git a/src/backend/lib/hasht.c b/src/backend/lib/hasht.c
new file mode 100644 (file)
index 0000000..2c613d2
--- /dev/null
@@ -0,0 +1,47 @@
+/*-------------------------------------------------------------------------
+ *
+ * hasht.c--
+ *    hash table related functions that are not directly supported
+ *    by the hashing packages under utils/hash.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+#include "utils/memutils.h"
+#include "utils/elog.h"
+#include "utils/hsearch.h"
+#include "lib/hasht.h"
+
+/* -----------------------------------
+ *     HashTableWalk
+ *
+ *     call function on every element in hashtable
+ *     one extra argument, arg may be supplied
+ * -----------------------------------
+ */
+void
+HashTableWalk(HTAB *hashtable, HashtFunc function, int arg)
+{
+    long *hashent;
+    long *data;
+    int keysize;
+    
+    keysize = hashtable->hctl->keysize;
+    (void)hash_seq((HTAB *)NULL);
+    while ((hashent = hash_seq(hashtable)) != (long *) TRUE) {
+       if (hashent == NULL)
+           elog(FATAL, "error in HashTableWalk.");
+       /* 
+        * XXX the corresponding hash table insertion does NOT
+        * LONGALIGN -- make sure the keysize is ok
+        */
+       data = (long *) LONGALIGN((char*) hashent + keysize);
+       (*function)(data, arg);
+    }
+}
diff --git a/src/backend/lib/hasht.h b/src/backend/lib/hasht.h
new file mode 100644 (file)
index 0000000..09f0af9
--- /dev/null
@@ -0,0 +1,23 @@
+/*-------------------------------------------------------------------------
+ *
+ * hasht.h--
+ *    hash table related functions that are not directly supported
+ *    under utils/hash.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        HASHT_H
+#define HASHT_H
+
+#include "utils/hsearch.h"
+
+typedef void (*HashtFunc)();
+
+extern void HashTableWalk(HTAB *hashtable, HashtFunc function, int arg);
+
+#endif /* HASHT_H */
diff --git a/src/backend/lib/lispsort.c b/src/backend/lib/lispsort.c
new file mode 100644 (file)
index 0000000..726ff6c
--- /dev/null
@@ -0,0 +1,56 @@
+/*-------------------------------------------------------------------------
+ *
+ * lispsort.c--
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/relation.h"
+#include "lib/lispsort.h"
+#include "utils/palloc.h"
+#include "lib/qsort.h"
+
+/*
+** lisp_qsort: Takes a lisp list as input, copies it into an array of lisp 
+**             nodes which it sorts via qsort() with the comparison function
+**             as passed into lisp_qsort(), and returns a new list with 
+**             the nodes sorted.  The old list is *not* freed or modified (?)
+*/
+List *lisp_qsort(List *the_list,    /* the list to be sorted */
+                int (*compare)())      /* function to compare two nodes */
+{
+    int i;
+    size_t num;
+    List **nodearray;
+    List *tmp, *output;
+    
+    /* find size of list */
+    num = length(the_list);
+    if (num < 2)
+       return(copyObject(the_list));
+    
+    /* copy elements of the list into an array */
+    nodearray = (List **) palloc(num * sizeof(List *));
+    
+    for (tmp = the_list, i = 0; tmp != NIL; tmp = lnext(tmp), i++)
+       nodearray[i] = copyObject(lfirst(tmp));
+    
+    /* sort the array */
+    pg_qsort(nodearray, num, sizeof(List *), compare);
+    
+    /* lcons together the array elements */
+    output = NIL;
+    for (i = num - 1; i >= 0; i--)
+       output = lcons(nodearray[i], output);
+    
+    return(output);
+}
diff --git a/src/backend/lib/lispsort.h b/src/backend/lib/lispsort.h
new file mode 100644 (file)
index 0000000..6ae8fd7
--- /dev/null
@@ -0,0 +1,18 @@
+/*-------------------------------------------------------------------------
+ *
+ * lispsort.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        LISPSORT_H
+#define        LISPSORT_H
+
+extern List *lisp_qsort(List *the_list, int (*compare)());
+
+#endif /* LISPSORT_H */
diff --git a/src/backend/lib/qsort.c b/src/backend/lib/qsort.c
new file mode 100644 (file)
index 0000000..40c1134
--- /dev/null
@@ -0,0 +1,281 @@
+/*-------------------------------------------------------------------------
+ *
+ * qsort.c--
+ *    
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*-
+ * Copyright (c) 1980, 1983, 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)qsort.c    5.9 (Berkeley) 2/23/91";
+#endif /* LIBC_SCCS and not lint */
+
+#include "postgres.h"
+#include "lib/qsort.h"
+
+/*
+ * MTHRESH is the smallest partition for which we compare for a median
+ * value instead of using the middle value.
+ */
+#define        MTHRESH 6
+
+/*
+ * THRESH is the minimum number of entries in a partition for continued
+ * partitioning.
+ */
+#define        THRESH  4
+
+static void insertion_sort(char* bot, int nmemb, int size, int (*compar)());
+static void quick_sort(char* bot, int nmemb, int size, int (*compar)());
+
+void pg_qsort(void *bot,
+             size_t nmemb,
+             size_t size, 
+             int (*compar)(void *, void *))
+{
+
+       if (nmemb <= 1)
+               return;
+
+       if (nmemb >= THRESH)
+               quick_sort(bot, nmemb, size, compar);
+       else
+               insertion_sort(bot, nmemb, size, compar);
+}
+
+/*
+ * Swap two areas of size number of bytes.  Although qsort(3) permits random
+ * blocks of memory to be sorted, sorting pointers is almost certainly the
+ * common case (and, were it not, could easily be made so).  Regardless, it
+ * isn't worth optimizing; the SWAP's get sped up by the cache, and pointer
+ * arithmetic gets lost in the time required for comparison function calls.
+ */
+#define        SWAP(a, b) { \
+       cnt = size; \
+       do { \
+               ch = *a; \
+               *a++ = *b; \
+               *b++ = ch; \
+       } while (--cnt); \
+}
+
+/*
+ * Knuth, Vol. 3, page 116, Algorithm Q, step b, argues that a single pass
+ * of straight insertion sort after partitioning is complete is better than
+ * sorting each small partition as it is created.  This isn't correct in this
+ * implementation because comparisons require at least one (and often two)
+ * function calls and are likely to be the dominating expense of the sort.
+ * Doing a final insertion sort does more comparisons than are necessary
+ * because it compares the "edges" and medians of the partitions which are
+ * known to be already sorted.
+ *
+ * This is also the reasoning behind selecting a small THRESH value (see
+ * Knuth, page 122, equation 26), since the quicksort algorithm does less
+ * comparisons than the insertion sort.
+ */
+#define        SORT(bot, n) { \
+       if (n > 1) \
+               if (n == 2) { \
+                       t1 = bot + size; \
+                       if (compar(t1, bot) < 0) \
+                               SWAP(t1, bot); \
+               } else \
+                       insertion_sort(bot, n, size, compar); \
+}
+
+static void
+quick_sort(char* bot, int nmemb, int size, int (*compar)())
+{
+       register int cnt;
+       register u_char ch;
+       register char *top, *mid, *t1, *t2;
+       register int n1, n2;
+       char *bsv;
+
+       /* bot and nmemb must already be set. */
+partition:
+
+       /* find mid and top elements */
+       mid = bot + size * (nmemb >> 1);
+       top = bot + (nmemb - 1) * size;
+
+       /*
+        * Find the median of the first, last and middle element (see Knuth,
+        * Vol. 3, page 123, Eq. 28).  This test order gets the equalities
+        * right.
+        */
+       if (nmemb >= MTHRESH) {
+               n1 = compar(bot, mid);
+               n2 = compar(mid, top);
+               if (n1 < 0 && n2 > 0)
+                       t1 = compar(bot, top) < 0 ? top : bot;
+               else if (n1 > 0 && n2 < 0)
+                       t1 = compar(bot, top) > 0 ? top : bot;
+               else
+                       t1 = mid;
+
+               /* if mid element not selected, swap selection there */
+               if (t1 != mid) {
+                       SWAP(t1, mid);
+                       mid -= size;
+               }
+       }
+
+       /* Standard quicksort, Knuth, Vol. 3, page 116, Algorithm Q. */
+#define        didswap n1
+#define        newbot  t1
+#define        replace t2
+       didswap = 0;
+       for (bsv = bot;;) {
+               for (; bot < mid && compar(bot, mid) <= 0; bot += size);
+               while (top > mid) {
+                       if (compar(mid, top) <= 0) {
+                               top -= size;
+                               continue;
+                       }
+                       newbot = bot + size;    /* value of bot after swap */
+                       if (bot == mid)         /* top <-> mid, mid == top */
+                               replace = mid = top;
+                       else {                  /* bot <-> top */
+                               replace = top;
+                               top -= size;
+                       }
+                       goto swap;
+               }
+               if (bot == mid)
+                       break;
+
+               /* bot <-> mid, mid == bot */
+               replace = mid;
+               newbot = mid = bot;             /* value of bot after swap */
+               top -= size;
+
+swap:          SWAP(bot, replace);
+               bot = newbot;
+               didswap = 1;
+       }
+
+       /*
+        * Quicksort behaves badly in the presence of data which is already
+        * sorted (see Knuth, Vol. 3, page 119) going from O N lg N to O N^2.
+        * To avoid this worst case behavior, if a re-partitioning occurs
+        * without swapping any elements, it is not further partitioned and
+        * is insert sorted.  This wins big with almost sorted data sets and
+        * only loses if the data set is very strangely partitioned.  A fix
+        * for those data sets would be to return prematurely if the insertion
+        * sort routine is forced to make an excessive number of swaps, and
+        * continue the partitioning.
+        */
+       if (!didswap) {
+               insertion_sort(bsv, nmemb, size, compar);
+               return;
+       }
+
+       /*
+        * Re-partition or sort as necessary.  Note that the mid element
+        * itself is correctly positioned and can be ignored.
+        */
+#define        nlower  n1
+#define        nupper  n2
+       bot = bsv;
+       nlower = (mid - bot) / size;    /* size of lower partition */
+       mid += size;
+       nupper = nmemb - nlower - 1;    /* size of upper partition */
+
+       /*
+        * If must call recursively, do it on the smaller partition; this
+        * bounds the stack to lg N entries.
+        */
+       if (nlower > nupper) {
+               if (nupper >= THRESH)
+                       quick_sort(mid, nupper, size, compar);
+               else {
+                       SORT(mid, nupper);
+                       if (nlower < THRESH) {
+                               SORT(bot, nlower);
+                               return;
+                       }
+               }
+               nmemb = nlower;
+       } else {
+               if (nlower >= THRESH)
+                       quick_sort(bot, nlower, size, compar);
+               else {
+                       SORT(bot, nlower);
+                       if (nupper < THRESH) {
+                               SORT(mid, nupper);
+                               return;
+                       }
+               }
+               bot = mid;
+               nmemb = nupper;
+       }
+       goto partition;
+}
+
+static void
+insertion_sort(char* bot, int nmemb, int size, int (*compar)())
+{
+       register int cnt;
+       register u_char ch;
+       register char *s1, *s2, *t1, *t2, *top;
+
+       /*
+        * A simple insertion sort (see Knuth, Vol. 3, page 81, Algorithm
+        * S).  Insertion sort has the same worst case as most simple sorts
+        * (O N^2).  It gets used here because it is (O N) in the case of
+        * sorted data.
+        */
+       top = bot + nmemb * size;
+       for (t1 = bot + size; t1 < top;) {
+               for (t2 = t1; (t2 -= size) >= bot && compar(t1, t2) < 0;);
+               if (t1 != (t2 += size)) {
+                       /* Bubble bytes up through each element. */
+                       for (cnt = size; cnt--; ++t1) {
+                               ch = *t1;
+                               for (s1 = s2 = t1; (s2 -= size) >= t2; s1 = s2)
+                                       *s1 = *s2;
+                               *s1 = ch;
+                       }
+               } else
+                       t1 += size;
+       }
+}
diff --git a/src/backend/lib/qsort.h b/src/backend/lib/qsort.h
new file mode 100644 (file)
index 0000000..6fc4d3d
--- /dev/null
@@ -0,0 +1,24 @@
+/*-------------------------------------------------------------------------
+ *
+ * qsort.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        QSORT_H
+#define        QSORT_H
+
+#include <sys/types.h>
+
+extern void pg_qsort(void *bot,
+                    size_t nmemb,
+                    size_t size, 
+                    int (*compar)(void *, void *));
+
+#endif /* QSORT_H */
+                    
diff --git a/src/backend/lib/stringinfo.c b/src/backend/lib/stringinfo.c
new file mode 100644 (file)
index 0000000..45fbe5f
--- /dev/null
@@ -0,0 +1,116 @@
+/*-------------------------------------------------------------------------
+ *
+ * stringinfo.c--
+ *    These are routines that can be used to write informations to a string,
+ *    without having to worry about string lengths, space allocation etc.
+ *    Ideally the interface should look like the file i/o interface,
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+#include "nodes/pg_list.h"
+#include "lib/stringinfo.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+/*---------------------------------------------------------------------
+ * makeStringInfo
+ *
+ * Create a StringInfoData & return a pointer to it.
+ *
+ *---------------------------------------------------------------------
+ */
+StringInfo
+makeStringInfo()
+{
+    StringInfo res;
+    long size;
+    
+    res = (StringInfo) palloc(sizeof(StringInfoData));
+    if (res == NULL) {
+       elog(WARN, "makeStringInfo: Out of memory!");
+    }
+    
+    size = 100;
+    res->data = palloc(size);
+    if (res->data == NULL) {
+       elog(WARN,
+            "makeStringInfo: Out of memory! (%ld bytes requested)", size);
+    }
+    res->maxlen = size;
+    res->len = 0;
+    /*
+     * NOTE: we must initialize `res->data' to the empty string because
+     * we use 'strcat' in 'appendStringInfo', which of course it always
+     * expects a null terminated string.
+     */
+    res->data[0] = '\0';
+    
+    return(res);
+}
+
+/*---------------------------------------------------------------------
+ * appendStringInfo
+ *
+ * append to the current 'StringInfo' a new string.
+ * If there is not enough space in the current 'data', then reallocate
+ * some more...
+ *
+ * NOTE: if we reallocate space, we pfree the old one!
+ *---------------------------------------------------------------------
+ */
+void
+appendStringInfo(StringInfo str, char *buffer)
+{
+    int buflen, newlen;
+    char *s;
+    
+    Assert((str!=NULL));
+    
+    /*
+     * do we have enough space to append the new string?
+     * (don't forget to count the null string terminating char!)
+     * If no, then reallocate some more.
+     */
+    buflen = strlen(buffer);
+    if (buflen + str->len >= str->maxlen-1) {
+       /*
+        * how much more space to allocate ?
+        * Let's say double the current space...
+        * However we must check if this is enough!
+        */
+       newlen = 2 * str->len;
+       while (buflen + str->len >= newlen-1) {
+           newlen = 2 * newlen;
+       }
+       /*
+        * allocate enough space.
+        */
+       s = palloc(newlen);
+       if (s==NULL) {
+           elog(WARN,
+                "appendStringInfo: Out of memory (%d bytes requested)",
+                newlen);
+       }
+       memmove(s, str->data, str->len+1);
+       pfree(str->data);
+       str->maxlen = newlen;
+       str->data = s;
+    }
+    
+    /*
+     * OK, we have enough space now, append 'buffer' at the
+     * end of the string & update the string length.
+     * NOTE: this is a text string (i.e. printable characters)
+     * so 'strcat' will do the job (no need to use 'bcopy' et all...)
+     */
+    (void) strcat(str->data, buffer);
+    str->len += buflen;
+}
diff --git a/src/backend/lib/stringinfo.h b/src/backend/lib/stringinfo.h
new file mode 100644 (file)
index 0000000..079abc9
--- /dev/null
@@ -0,0 +1,47 @@
+/*-------------------------------------------------------------------------
+ *
+ * stringinfo.h--
+ *    Declarations/definitons for "string" functions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STRINGINFO_H
+#define STRINGINFO_H
+
+/*#include "c.h"               */      /* for 'String' */
+
+/*-------------------------
+ * StringInfoData holds information about a string.
+ *     'data' is the string.
+ *     'len' is the current string length (as returned by 'strlen')
+ *     'maxlen' is the size in bytes of 'data', i.e. the maximum string
+ *             size (includeing the terminating '\0' char) that we can
+ *             currently store in 'data' without having to reallocate
+ *             more space.
+ */
+typedef struct StringInfoData {
+    char *data;
+    int maxlen;
+    int len;
+} StringInfoData;
+
+typedef StringInfoData *StringInfo;
+
+/*------------------------
+ * makeStringInfo
+ * create a 'StringInfoData' & return a pointer to it.
+ */
+extern StringInfo makeStringInfo(void);
+
+/*------------------------
+ * appendStringInfo
+ * similar to 'strcat' but reallocates more space if necessary...
+ */
+extern void appendStringInfo(StringInfo str, char *buffer);
+
+#endif /* STRINGINFO_H */
diff --git a/src/backend/libpq/Makefile.inc b/src/backend/libpq/Makefile.inc
new file mode 100644 (file)
index 0000000..8fc16b2
--- /dev/null
@@ -0,0 +1,26 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the (backend side) libpq module
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+#
+# The frontend libpq interfaces to the backend through these files.
+#
+VPATH:= $(VPATH):$(CURDIR)/libpq
+
+SRCS_LIBPQ= be-dumpdata.c be-fsstubs.c be-pqexec.c
+
+#
+# These files are shared with the frontend library.
+#
+SRCS_LIBPQ+= auth.c pqcomm.c portal.c portalbuf.c pqpacket.c pqsignal.c
+
+HEADERS+= auth.h be-fsstubs.h libpq-be.h libpq-fs.h libpq.h pqcomm.h pqsignal.h
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
new file mode 100644 (file)
index 0000000..2628632
--- /dev/null
@@ -0,0 +1,668 @@
+/*-------------------------------------------------------------------------
+ *
+ * auth.c--
+ *    Routines to handle network authentication
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *
+ *     backend (postmaster) routines:
+ *     be_recvauth             receive authentication information
+ *     be_setauthsvc           do/do not permit an authentication service
+ *     be_getauthsvc           is an authentication service permitted?
+ *
+ *   NOTES
+ *     To add a new authentication system:
+ *     0. If you can't do your authentication over an existing socket,
+ *        you lose -- get ready to hack around this framework instead of 
+ *        using it.  Otherwise, you can assume you have an initialized
+ *        and empty connection to work with.  (Please don't leave leftover
+ *        gunk in the connection after the authentication transactions, or
+ *        the POSTGRES routines that follow will be very unhappy.)
+ *     1. Write a set of routines that:
+ *             let a client figure out what user/principal name to use
+ *             send authentication information (client side)
+ *             receive authentication information (server side)
+ *        You can include both routines in this file, using #ifdef FRONTEND
+ *        to separate them.
+ *     2. Edit libpq/pqcomm.h and assign a MsgType for your protocol.
+ *     3. Edit the static "struct authsvc" array and the generic 
+ *        {be,fe}_{get,set}auth{name,svc} routines in this file to reflect 
+ *        the new service.  You may have to change the arguments of these
+ *        routines; they basically just reflect what Kerberos v4 needs.
+ *     4. Hack on src/{,bin}/Makefile.global and src/{backend,libpq}/Makefile
+ *        to add library and CFLAGS hooks -- basically, grep the Makefile
+ *        hierarchy for KRBVERS to see where you need to add things.
+ *
+ *     Send mail to [email protected] if you have to make 
+ *     any changes to arguments, etc.  Context diffs would be nice, too.
+ *
+ *     Someday, this cruft will go away and magically be replaced by a
+ *     nice interface based on the GSS API or something.  For now, though,
+ *     there's no (stable) UNIX security API to work with...
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h> /* for MAX{HOSTNAME,PATH}LEN, NOFILE */
+#include <pwd.h>
+#include <ctype.h>                     /* isspace() declaration */
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "libpq/auth.h"
+#include "libpq/libpq.h"
+#include "libpq/pqcomm.h"
+#include "libpq/libpq-be.h"
+
+/*----------------------------------------------------------------
+ * common definitions for generic fe/be routines
+ *----------------------------------------------------------------
+ */
+
+struct authsvc {
+    char       name[16];       /* service nickname (for command line) */
+    MsgType    msgtype;        /* startup packet header type */
+    int                allowed;        /* initially allowed (before command line
+                                * option parsing)?
+                                */
+};
+
+/*
+ * Command-line parsing routines use this structure to map nicknames
+ * onto service types (and the startup packets to use with them).
+ *
+ * Programs receiving an authentication request use this structure to
+ * decide which authentication service types are currently permitted.
+ * By default, all authentication systems compiled into the system are
+ * allowed.  Unauthenticated connections are disallowed unless there
+ * isn't any authentication system.
+ */
+static struct authsvc authsvcs[] = {
+#ifdef KRB4
+    { "krb4",     STARTUP_KRB4_MSG, 1 },
+    { "kerberos", STARTUP_KRB4_MSG, 1 },
+#endif /* KRB4 */
+#ifdef KRB5
+    { "krb5",     STARTUP_KRB5_MSG, 1 },
+    { "kerberos", STARTUP_KRB5_MSG, 1 },
+#endif /* KRB5 */
+    { UNAUTHNAME, STARTUP_MSG,
+#if defined(KRB4) || defined(KRB5)
+         0
+#else /* !(KRB4 || KRB5) */
+         1
+#endif /* !(KRB4 || KRB5) */
+    }
+};
+
+static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
+
+#ifdef KRB4
+/*----------------------------------------------------------------
+ * MIT Kerberos authentication system - protocol version 4
+ *----------------------------------------------------------------
+ */
+
+#include "krb.h"
+
+#ifdef FRONTEND
+/* moves to src/libpq/fe-auth.c  */
+#else /* !FRONTEND */
+
+/*
+ * pg_krb4_recvauth -- server routine to receive authentication information
+ *                    from the client
+ *
+ * Nothing unusual here, except that we compare the username obtained from
+ * the client's setup packet to the authenticated name.  (We have to retain
+ * the name in the setup packet since we have to retain the ability to handle
+ * unauthenticated connections.)
+ */
+static int
+pg_krb4_recvauth(int sock,
+                struct sockaddr_in *laddr,
+                struct sockaddr_in *raddr,
+                char *username)
+{
+    long               krbopts = 0;    /* one-way authentication */
+    KTEXT_ST   clttkt;
+    char               instance[INST_SZ];
+    AUTH_DAT   auth_data;
+    Key_schedule       key_sched;
+    char               version[KRB_SENDAUTH_VLEN];
+    int                status;
+    
+    strcpy(instance, "*");     /* don't care, but arg gets expanded anyway */
+    status = krb_recvauth(krbopts,
+                         sock,
+                         &clttkt,
+                         PG_KRB_SRVNAM,
+                         instance,
+                         raddr,
+                         laddr,
+                         &auth_data,
+                         PG_KRB_SRVTAB,
+                         key_sched,
+                         version);
+    if (status != KSUCCESS) {
+       (void) sprintf(PQerrormsg,
+                      "pg_krb4_recvauth: kerberos error: %s\n",
+                      krb_err_txt[status]);
+       fputs(PQerrormsg, stderr);
+       pqdebug("%s", PQerrormsg);
+       return(STATUS_ERROR);
+    }
+    if (strncmp(version, PG_KRB4_VERSION, KRB_SENDAUTH_VLEN)) {
+       (void) sprintf(PQerrormsg,
+                      "pg_krb4_recvauth: protocol version != \"%s\"\n",
+                      PG_KRB4_VERSION);
+       fputs(PQerrormsg, stderr);
+       pqdebug("%s", PQerrormsg);
+       return(STATUS_ERROR);
+    }
+    if (username && *username &&
+       strncmp(username, auth_data.pname, NAMEDATALEN)) {
+       (void) sprintf(PQerrormsg,
+                      "pg_krb4_recvauth: name \"%s\" != \"%s\"\n",
+                      username,
+                      auth_data.pname);
+       fputs(PQerrormsg, stderr);
+       pqdebug("%s", PQerrormsg);
+       return(STATUS_ERROR);
+    }
+    return(STATUS_OK);
+}
+
+#endif /* !FRONTEND */
+
+#endif /* KRB4 */
+
+#ifdef KRB5
+/*----------------------------------------------------------------
+ * MIT Kerberos authentication system - protocol version 5
+ *----------------------------------------------------------------
+ */
+
+#include "krb5/krb5.h"
+
+/*
+ * pg_an_to_ln -- return the local name corresponding to an authentication
+ *               name
+ *
+ * XXX Assumes that the first aname component is the user name.  This is NOT
+ *     necessarily so, since an aname can actually be something out of your
+ *     worst X.400 nightmare, like
+ *       ORGANIZATION=U. C. Berkeley/NAME=Paul M. [email protected]
+ *     Note that the MIT an_to_ln code does the same thing if you don't
+ *     provide an aname mapping database...it may be a better idea to use
+ *     krb5_an_to_ln, except that it punts if multiple components are found,
+ *     and we can't afford to punt.
+ */
+static char *
+pg_an_to_ln(char *aname)
+{
+    char       *p;
+    
+    if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
+       *p = '\0';
+    return(aname);
+}
+
+#ifdef FRONTEND
+/* moves to src/libpq/fe-auth.c  */
+#else /* !FRONTEND */
+
+/*
+ * pg_krb4_recvauth -- server routine to receive authentication information
+ *                    from the client
+ *
+ * We still need to compare the username obtained from the client's setup
+ * packet to the authenticated name, as described in pg_krb4_recvauth.  This
+ * is a bit more problematic in v5, as described above in pg_an_to_ln.
+ *
+ * In addition, as described above in pg_krb5_sendauth, we still need to
+ * canonicalize the server name v4-style before constructing a principal
+ * from it.  Again, this is kind of iffy.
+ *
+ * Finally, we need to tangle with the fact that v5 doesn't let you explicitly
+ * set server keytab file names -- you have to feed lower-level routines a
+ * function to retrieve the contents of a keytab, along with a single argument
+ * that allows them to open the keytab.  We assume that a server keytab is
+ * always a real file so we can allow people to specify their own filenames.
+ * (This is important because the POSTGRES keytab needs to be readable by
+ * non-root users/groups; the v4 tools used to force you do dump a whole
+ * host's worth of keys into a file, effectively forcing you to use one file,
+ * but kdb5_edit allows you to select which principals to dump.  Yay!)
+ */
+static int
+pg_krb5_recvauth(int sock,
+                struct sockaddr_in *laddr,
+                struct sockaddr_in *raddr,
+                char *username)
+{
+    char                       servbuf[MAXHOSTNAMELEN + 1 +
+                                       sizeof(PG_KRB_SRVNAM)];
+    char                       *hostp, *kusername = (char *) NULL;
+    krb5_error_code            code;
+    krb5_principal             client, server;
+    krb5_address               sender_addr;
+    krb5_rdreq_key_proc        keyproc = (krb5_rdreq_key_proc) NULL;
+    krb5_pointer               keyprocarg = (krb5_pointer) NULL;
+    
+    /*
+     * Set up server side -- since we have no ticket file to make this
+     * easy, we construct our own name and parse it.  See note on
+     * canonicalization above.
+     */
+    (void) strcpy(servbuf, PG_KRB_SRVNAM);
+    *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
+    if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
+       (void) strcpy(hostp, "localhost");
+    if (hostp = strchr(hostp, '.'))
+       *hostp = '\0';
+    if (code = krb5_parse_name(servbuf, &server)) {
+       (void) sprintf(PQerrormsg,
+                      "pg_krb5_recvauth: Kerberos error %d in krb5_parse_name\n",
+                      code);
+       com_err("pg_krb5_recvauth", code, "in krb5_parse_name");
+       return(STATUS_ERROR);
+    }
+    
+    /*
+     * krb5_sendauth needs this to verify the address in the client
+     * authenticator.
+     */
+    sender_addr.addrtype = raddr->sin_family;
+    sender_addr.length = sizeof(raddr->sin_addr);
+    sender_addr.contents = (krb5_octet *) &(raddr->sin_addr);
+    
+    if (strcmp(PG_KRB_SRVTAB, "")) {
+       keyproc = krb5_kt_read_service_key;
+       keyprocarg = PG_KRB_SRVTAB;
+    }
+    
+    if (code = krb5_recvauth((krb5_pointer) &sock,
+                            PG_KRB5_VERSION,
+                            server,
+                            &sender_addr,
+                            (krb5_pointer) NULL,
+                            keyproc,
+                            keyprocarg,
+                            (char *) NULL,
+                            (krb5_int32 *) NULL,
+                            &client,
+                            (krb5_ticket **) NULL,
+                            (krb5_authenticator **) NULL)) {
+       (void) sprintf(PQerrormsg,
+                      "pg_krb5_recvauth: Kerberos error %d in krb5_recvauth\n",
+                      code);
+       com_err("pg_krb5_recvauth", code, "in krb5_recvauth");
+       krb5_free_principal(server);
+       return(STATUS_ERROR);
+    }
+    krb5_free_principal(server);
+    
+    /*
+     * The "client" structure comes out of the ticket and is therefore
+     * authenticated.  Use it to check the username obtained from the
+     * postmaster startup packet.
+     */
+    if ((code = krb5_unparse_name(client, &kusername))) {
+       (void) sprintf(PQerrormsg,
+                      "pg_krb5_recvauth: Kerberos error %d in krb5_unparse_name\n",
+                      code);
+       com_err("pg_krb5_recvauth", code, "in krb5_unparse_name");
+       krb5_free_principal(client);
+       return(STATUS_ERROR);
+    }
+    krb5_free_principal(client);
+    if (!kusername) {
+       (void) sprintf(PQerrormsg,
+                      "pg_krb5_recvauth: could not decode username\n");
+       fputs(PQerrormsg, stderr);
+       pqdebug("%s", PQerrormsg);
+       return(STATUS_ERROR);
+    }
+    kusername = pg_an_to_ln(kusername);
+    if (username && strncmp(username, kusername, NAMEDATALEN)) {
+       (void) sprintf(PQerrormsg,
+                      "pg_krb5_recvauth: name \"%s\" != \"%s\"\n",
+                      username, kusername);
+       fputs(PQerrormsg, stderr);
+       pqdebug("%s", PQerrormsg);
+       free(kusername);
+       return(STATUS_ERROR);
+    }
+    free(kusername);
+    return(STATUS_OK);
+}
+
+#endif /* !FRONTEND */
+
+#endif /* KRB5 */
+
+
+/*----------------------------------------------------------------
+ * host based authentication
+ *----------------------------------------------------------------
+ * based on the securelib package originally written by William
+ * LeFebvre, EECS Department, Northwestern University
+ * ([email protected]) - orginal configuration file code handling
+ * by Sam Horrocks ([email protected])
+ *
+ * modified and adapted for use with Postgres95 by Paul Fisher
+ */
+
+#define CONF_FILE "pg_hba"              /* Name of the config file           */
+
+#define MAX_LINES 255                    /* Maximum number of config lines    *
+                                         * that can apply to one database    */
+
+#define ALL_NAME "all"                  /* Name used in config file for      *
+                                         * lines that apply to all databases */
+
+#define MAX_TOKEN 80                    /* Maximum size of one token in the  *
+                                         * configuration file                */
+struct conf_line {                      /* Info about config file line */
+  u_long adr, mask;
+};
+static int next_token(FILE *, char *, int);
+
+/* hba_recvauth */
+/* check for host-based authentication */
+/*
+ * hba_recvauth - check the sockaddr_in "addr" to see if it corresponds
+ *                to an acceptable host for the database that's being
+ *                connected to.  Return STATUS_OK if acceptable,
+ *                otherwise return STATUS_ERROR.
+ */
+
+static int
+hba_recvauth(struct sockaddr_in *addr, PacketBuf *pbuf, StartupInfo *sp)
+{
+    u_long ip_addr;
+    static struct conf_line conf[MAX_LINES];
+    static int nconf;
+    int i;
+
+    char buf[MAX_TOKEN];
+    FILE *file;
+
+    char *conf_file;
+
+    /* put together the full pathname to the config file */
+    conf_file = (char *) malloc((strlen(GetPGData())+strlen(CONF_FILE)+2)*sizeof(char));
+    strcpy(conf_file, GetPGData());
+    strcat(conf_file, "/");
+    strcat(conf_file, CONF_FILE);
+    
+
+    /* Open the config file. */
+    file = fopen(conf_file, "r");
+    if (file)
+    {
+        free(conf_file);
+       nconf = 0;
+
+       /* Grab the "name" */
+       while ((i = next_token(file, buf, sizeof(buf))) != EOF)
+       {
+           /* If only token on the line, ignore */
+           if (i == '\n') continue;
+           
+           /* Comment -- read until end of line then next line */
+           if (buf[0] == '#')
+           {
+               while (next_token(file, buf, sizeof(buf)) == 0) ;
+               continue;
+           }
+
+           /*
+            * Check to make sure this says "all" or that it matches
+            * the database name.
+            */
+           
+           if (strcmp(buf, ALL_NAME) == 0 || (strcmp(buf, sp->database) == 0))
+           {
+               /* Get next token, if last on line, ignore */
+               if (next_token(file, buf, sizeof(buf)) != 0)
+                   continue;
+
+               /* Got address */
+               conf[nconf].adr = inet_addr(buf);
+                   
+               /* Get next token (mask) */
+               i = next_token(file, buf, sizeof(buf));
+
+               /* Only ignore if we got no text at all */
+               if (i != EOF)
+               {
+                   /* Add to list, quit if array is full */
+                   conf[nconf++].mask = inet_addr(buf);
+                   if (nconf == MAX_LINES) break;
+               }
+
+               /* If not at end-of-line, keep reading til we are */
+               while (i == 0)
+                   i = next_token(file, buf, sizeof(buf));
+           }
+       }
+       fclose(file);
+    }
+    else 
+    {  (void) sprintf(PQerrormsg,
+                          "hba_recvauth: config file does not exist or permissions are not setup correctly!\n");
+           fputs(PQerrormsg, stderr);
+           pqdebug("%s", PQerrormsg);
+       free(conf_file);
+        return(STATUS_ERROR); 
+    }
+
+
+    /* Config lines now in memory so start checking address */
+    /* grab just the address */
+    ip_addr = addr->sin_addr.s_addr;
+
+    /*
+     * Go through the conf array, turn off the bits given by the mask
+     * and then compare the result with the address.  A match means
+     * that this address is ok.
+     */
+    for (i = 0; i < nconf; ++i)
+        if ((ip_addr & ~conf[i].mask) == conf[i].adr) return(STATUS_OK);
+    
+    /* no match, so we can't approve the address */
+    return(STATUS_ERROR);
+}
+
+/*
+ * Grab one token out of fp.  Defined as the next string of non-whitespace
+ * in the file.  After we get the token, continue reading until EOF, end of
+ * line or the next token.  If it's the last token on the line, return '\n'
+ * for the value.  If we get EOF before reading a token, return EOF.  In all
+ * other cases return 0.
+ */
+static int 
+next_token(FILE *fp, char *buf, int bufsz)
+{
+    int c;
+    char *eb = buf+(bufsz-1);
+
+    /* Discard inital whitespace */
+    while (isspace(c = getc(fp))) ;
+
+    /* EOF seen before any token so return EOF */
+    if (c == EOF) return -1;
+
+    /* Form a token in buf */
+    do {
+       if (buf < eb) *buf++ = c;
+       c = getc(fp);
+    } while (!isspace(c) && c != EOF);
+    *buf = '\0';
+
+    /* Discard trailing tabs and spaces */
+    while (c == ' ' || c == '\t') c = getc(fp);
+
+    /* Put back the char that was non-whitespace (putting back EOF is ok) */
+    (void) ungetc(c, fp);
+
+    /* If we ended with a newline, return that, otherwise return 0 */
+    return (c == '\n' ? '\n' : 0);
+}
+
+/*
+ * be_recvauth -- server demux routine for incoming authentication information
+ */
+int
+be_recvauth(MsgType msgtype, Port *port, char *username, StartupInfo* sp)
+{
+    if (!username) {
+       (void) sprintf(PQerrormsg,
+                      "be_recvauth: no user name passed\n");
+       fputs(PQerrormsg, stderr);
+       pqdebug("%s", PQerrormsg);
+       return(STATUS_ERROR);
+    }
+    if (!port) {
+       (void) sprintf(PQerrormsg,
+                      "be_recvauth: no port structure passed\n");
+       fputs(PQerrormsg, stderr);
+       pqdebug("%s", PQerrormsg);
+       return(STATUS_ERROR);
+    }
+    
+    switch (msgtype) {
+#ifdef KRB4
+    case STARTUP_KRB4_MSG:
+       if (!be_getauthsvc(msgtype)) {
+           (void) sprintf(PQerrormsg,
+                          "be_recvauth: krb4 authentication disallowed\n");
+           fputs(PQerrormsg, stderr);
+           pqdebug("%s", PQerrormsg);
+           return(STATUS_ERROR);
+       }
+       if (pg_krb4_recvauth(port->sock, &port->laddr, &port->raddr,
+                            username) != STATUS_OK) {
+           (void) sprintf(PQerrormsg,
+                          "be_recvauth: krb4 authentication failed\n");
+           fputs(PQerrormsg, stderr);
+           pqdebug("%s", PQerrormsg);
+           return(STATUS_ERROR);
+       }
+       break;
+#endif
+#ifdef KRB5
+    case STARTUP_KRB5_MSG:
+       if (!be_getauthsvc(msgtype)) {
+           (void) sprintf(PQerrormsg,
+                          "be_recvauth: krb5 authentication disallowed\n");
+           fputs(PQerrormsg, stderr);
+           pqdebug("%s", PQerrormsg);
+           return(STATUS_ERROR);
+       }
+       if (pg_krb5_recvauth(port->sock, &port->laddr, &port->raddr,
+                            username) != STATUS_OK) {
+           (void) sprintf(PQerrormsg,
+                          "be_recvauth: krb5 authentication failed\n");
+           fputs(PQerrormsg, stderr);
+           pqdebug("%s", PQerrormsg);
+           return(STATUS_ERROR);
+       }
+       break;
+#endif
+    case STARTUP_MSG:
+       if (!be_getauthsvc(msgtype)) {
+           (void) sprintf(PQerrormsg,
+                          "be_recvauth: unauthenticated connections disallowed failed\n");
+           fputs(PQerrormsg, stderr);
+           pqdebug("%s", PQerrormsg);
+           return(STATUS_ERROR);
+       }
+       break;
+    case STARTUP_HBA_MSG:
+       if (hba_recvauth(&port->raddr, &port->buf, sp) != STATUS_OK) {
+           (void) sprintf(PQerrormsg,
+                          "be_recvauth: host-based authentication failed\n");
+           fputs(PQerrormsg, stderr);
+           pqdebug("%s", PQerrormsg);
+           return(STATUS_ERROR);
+       }
+       break;
+    default:
+       (void) sprintf(PQerrormsg,
+                      "be_recvauth: unrecognized message type: %d\n",
+                      msgtype);
+       fputs(PQerrormsg, stderr);
+       pqdebug("%s", PQerrormsg);
+       return(STATUS_ERROR);
+    }
+    return(STATUS_OK);
+}
+
+/*
+ * be_setauthsvc -- enable/disable the authentication services currently
+ *                 selected for use by the backend
+ * be_getauthsvc -- returns whether a particular authentication system
+ *                 (indicated by its message type) is permitted by the
+ *                 current selections
+ *
+ * be_setauthsvc encodes the command-line syntax that
+ *     -a "<service-name>"
+ * enables a service, whereas
+ *     -a "no<service-name>"
+ * disables it.
+ */
+void
+be_setauthsvc(char *name)
+{
+    int i, j;
+    int turnon = 1;
+    
+    if (!name)
+       return;
+    if (!strncmp("no", name, 2)) {
+       turnon = 0;
+       name += 2;
+    }
+    if (name[0] == '\0')
+       return;
+    for (i = 0; i < n_authsvcs; ++i)
+       if (!strcmp(name, authsvcs[i].name)) {
+           for (j = 0; j < n_authsvcs; ++j)
+               if (authsvcs[j].msgtype == authsvcs[i].msgtype)
+                   authsvcs[j].allowed = turnon;
+           break;
+       }
+    if (i == n_authsvcs) {
+       (void) sprintf(PQerrormsg,
+                      "be_setauthsvc: invalid name %s, ignoring...\n",
+                      name);
+       fputs(PQerrormsg, stderr);
+       pqdebug("%s", PQerrormsg);
+    }
+    return;
+}
+
+int
+be_getauthsvc(MsgType msgtype)
+{
+    int i;
+    
+    for (i = 0; i < n_authsvcs; ++i)
+       if (msgtype == authsvcs[i].msgtype)
+           return(authsvcs[i].allowed);
+    return(0);
+}
diff --git a/src/backend/libpq/auth.h b/src/backend/libpq/auth.h
new file mode 100644 (file)
index 0000000..2084bf6
--- /dev/null
@@ -0,0 +1,49 @@
+/*-------------------------------------------------------------------------
+ *
+ * auth.h--
+ *    Definitions for network authentication routines
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef AUTH_H
+#define        AUTH_H
+
+#include "c.h"
+#include "libpq/pqcomm.h"
+
+/*----------------------------------------------------------------
+ * Common routines and definitions
+ *----------------------------------------------------------------
+ */
+
+/* what we call "no authentication system" */
+#define        UNAUTHNAME              "unauth"
+
+/* what a frontend uses by default */
+#if !defined(KRB4) && !defined(KRB5)
+#define        DEFAULT_CLIENT_AUTHSVC  UNAUTHNAME
+#else /* KRB4 || KRB5 */
+#define        DEFAULT_CLIENT_AUTHSVC  "kerberos"
+#endif /* KRB4 || KRB5 */
+
+extern int fe_sendauth(MsgType msgtype, Port *port, char *hostname);
+extern void fe_setauthsvc(char *name);
+extern MsgType fe_getauthsvc();
+extern char *fe_getauthname(void);
+extern int be_recvauth(MsgType msgtype, Port *port, char *username, StartupInfo* sp);
+extern void be_setauthsvc(char *name);
+extern int be_getauthsvc(MsgType msgtype);
+
+/* the value that matches any dbName value when doing
+   host based authentication*/
+#define ALL_DBNAME      "*"
+
+#define        PG_KRB4_VERSION "PGVER4.1"      /* at most KRB_SENDAUTH_VLEN chars */
+#define        PG_KRB5_VERSION "PGVER5.1"
+
+#endif /* AUTH_H */
diff --git a/src/backend/libpq/be-dumpdata.c b/src/backend/libpq/be-dumpdata.c
new file mode 100644 (file)
index 0000000..a77b12b
--- /dev/null
@@ -0,0 +1,323 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-dumpdata.c--
+ *    support for collection of returned tuples from an internal
+ *    PQ call into a backend buffer.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *     be_portalinit    - initialize backend portal administration
+ *     be_portalpush    - add a portal to the top of the portal stack
+ *     be_portalpop     - remove portal on the top of the stack & return it
+ *     be_currentportal - return the top portal on the portal stack
+ *     be_newportal     - return a new portal.
+ *     be_portalinit    - initialize backend portal expected to hold results.
+ *     be_printtup      - add a tuple to a backend portal
+ *
+ * NOTES
+ *     Since backend user-defined operators can call queries
+ *     which in turn call user-defined operators can call queries...
+ *     we have to keep track of portals on a stack.  BeginCommand()
+ *     puts portals on the stack and the PQ functions remove them.
+ *
+ */
+#include "postgres.h"
+
+#include "lib/dllist.h"
+#include "libpq/libpq-be.h"
+
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "storage/buf.h"
+#include "utils/memutils.h"
+#include "utils/palloc.h"
+#include "fmgr.h"
+#include "utils/mcxt.h"
+#include "utils/elog.h"
+#include "utils/exc.h"
+
+#include "utils/syscache.h"
+#include "catalog/pg_type.h"
+#include "catalog/catalog.h"
+#include "access/printtup.h"
+
+/* ----------------
+ *     backend portal stack for recursive PQexec calls
+ * ----------------
+ */
+static Dllist *be_portalstack;
+
+/* ----------------
+ *     be_portalinit - initialize backend portal administration
+ *
+ *     This is called once from InitPostgres() to initialize
+ *     the portal stack.
+ * ----------------
+ */
+void
+be_portalinit()
+{
+  be_portalstack = DLNewList();
+}
+
+/* ----------------
+ *     be_portalpush - add a portal to the top of the portal stack
+ *
+ *     used by BeginCommand()
+ * ----------------
+ */
+void
+be_portalpush(PortalEntry *entry)
+{
+  DLAddTail(be_portalstack, DLNewElem(entry));
+}
+
+/* ----------------
+ *     be_portalpop - remove the portal on the top of the stack & return it
+ *
+ *     used by PQexec()
+ * ----------------
+ */
+PortalEntry *
+be_portalpop()
+{
+  PortalEntry *p;
+  Dlelem* elt;
+  elt = DLRemTail(be_portalstack);
+
+  p = (elt ? (PortalEntry*)DLE_VAL(elt) : NULL);
+  DLFreeElem(elt);
+  return p;
+    
+
+}
+
+/* ----------------
+ *     be_currentportal - return the top portal on the portal stack
+ *
+ *     used by be_printtup()
+ * ----------------
+ */
+PortalEntry *
+be_currentportal()
+{
+  Dlelem* elt;
+  elt = DLGetTail(be_portalstack);
+  return (elt ? (PortalEntry*)DLE_VAL(elt) : NULL);
+}
+
+/* ----------------
+ *     be_newportal - return a new portal.
+ *
+ *     If the user-defined function does not specify a portal name,
+ *     we generate a unique one.  Names are generated from a combination
+ *     of a postgres oid and an integer counter which is incremented
+ *     every time we ask for a local portal.
+ *
+ *     used by BeginCommand()
+ * ----------------
+ */
+
+static Oid     be_portaloid;
+static u_int   be_portalcnt = 0;
+
+PortalEntry *
+be_newportal()   
+{
+    PortalEntry *entry;
+    char       buf[PortalNameLength];
+    
+    /* ----------------
+     * generate a new name
+     * ----------------
+     */
+    if (be_portalcnt == 0)
+       be_portaloid = newoid();
+    be_portalcnt++;
+    sprintf(buf, "be_%d_%d", be_portaloid, be_portalcnt);
+    
+    /* ----------------
+     * initialize the new portal entry and keep track
+     *  of the current memory context for be_printtup().
+     *  This is important - otherwise whatever we allocate
+     *  will go away and the contents of the portal after
+     *  PQexec() returns will be meaningless.
+     * ----------------
+     */
+    entry = pbuf_setup(buf);
+    entry->portalcxt = (Pointer) CurrentMemoryContext;
+    
+    return entry;
+}
+
+/* ----------------
+ *     be_typeinit - initialize backend portal expected to hold
+ *                     query results.
+ *
+ *     used by BeginCommand()
+ * ----------------
+ */
+void
+be_typeinit(PortalEntry *entry,
+           TupleDesc tupDesc,
+           int natts)
+{
+    PortalBuffer       *portal;
+    GroupBuffer        *group;
+    int                i;
+    AttributeTupleForm *attrs = tupDesc->attrs;
+    
+    /* ----------------
+     * add a new portal group to the portal
+     * ----------------
+     */
+    portal = entry->portal;
+    portal->no_groups++;
+    portal->groups = group = pbuf_addGroup(portal);
+    group->no_fields = natts;
+    
+    /* ----------------
+     * initialize portal group type info
+     * ----------------
+     */
+    if (natts > 0) {
+       group->types = pbuf_addTypes(natts);
+       for (i = 0; i < natts; ++i) {
+           strncpy(group->types[i].name, attrs[i]->attname.data, NAMEDATALEN); 
+           group->types[i].adtid = attrs[i]->atttypid;
+           group->types[i].adtsize = attrs[i]->attlen;
+       }
+    }
+}
+
+/* ----------------
+ *     be_printtup - add a tuple to a backend portal
+ *
+ *     used indirectly by ExecRetrieve()
+ *
+ *     This code is pretty much copied from printtup(), dump_type()
+ *     and dump_data().  -cim 2/12/91
+ * ----------------
+ */
+void
+be_printtup(HeapTuple tuple, TupleDesc typeinfo)
+{
+    int                i;
+    char       *attr;
+    bool       isnull;
+    Oid        typoutput;
+    
+    PortalEntry  *entry = NULL;
+    PortalBuffer *portal = NULL;
+    GroupBuffer  *group = NULL ;
+    TupleBlock          *tuples = NULL;
+    char        **values;
+    int          *lengths;
+    
+    MemoryContext savecxt;
+    
+    /* ----------------
+     *  get the current portal and group
+     * ----------------
+     */
+    entry = be_currentportal();
+    portal = entry->portal;
+    group = portal->groups;
+    
+    /* ----------------
+     * switch to the portal's memory context so that
+     *  the tuples we allocate are returned to the user.
+     * ----------------
+     */
+    savecxt = MemoryContextSwitchTo((MemoryContext)entry->portalcxt);
+    
+    /* ----------------
+     * If no tuple block yet, allocate one.
+     *  If the current block is full, allocate another one.
+     * ----------------
+     */
+    if (group->tuples == NULL) {
+       tuples = group->tuples = pbuf_addTuples();
+       tuples->tuple_index = 0;
+    } else {
+       tuples = group->tuples;
+       /* walk to the end of the linked list of TupleBlocks */
+       while (tuples->next)
+           tuples = tuples->next;
+       /* now, tuples is the last TupleBlock, check to see if it is full.  
+          If so, allocate a new TupleBlock and add it to the end of 
+          the chain */
+       
+       if (tuples->tuple_index == TupleBlockSize) {
+           tuples->next = pbuf_addTuples();
+           tuples = tuples->next;
+           tuples->tuple_index = 0;
+       }
+    }
+    
+    /* ----------------
+     * Allocate space for a tuple.
+     * ----------------
+     */
+    tuples->values[tuples->tuple_index] = pbuf_addTuple(tuple->t_natts);
+    tuples->lengths[tuples->tuple_index] = pbuf_addTupleValueLengths(tuple->t_natts);
+    /* ----------------
+     * copy printable representations of the tuple's attributes
+     *  to the portal.
+     *
+     *  This seems silly, because the user's function which is calling
+     *  PQexec() or PQfn() will probably just convert this back into the
+     *  internal form anyways, but the point here is to provide a uniform
+     *  libpq interface and this is how the fe libpq interface currently
+     *  works.  Pretty soon we'll have to add code to let the fe or be
+     *  select the desired data representation and then deal with that.
+     *  This should not be too hard, as there already exist typrecieve()
+     *  and typsend() procedures for user-defined types (see pg_type.h)
+     *  -cim 2/11/91
+     * ----------------
+     */
+    
+    values = tuples->values[tuples->tuple_index];
+    lengths = tuples->lengths[tuples->tuple_index];
+    
+    for (i = 0; i < tuple->t_natts; i++) {
+       attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull);
+       typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid);
+       
+       lengths[i] = typeinfo->attrs[i]->attlen;
+       
+       if (lengths[i] == -1) /* variable length attribute */
+           if (!isnull)
+               lengths[i] = VARSIZE(attr)-VARHDRSZ;
+           else
+               lengths[i] = 0;
+       
+       if (!isnull && OidIsValid(typoutput)) {
+         values[i] = fmgr(typoutput, attr, gettypelem(typeinfo->attrs[i]->atttypid));
+       } else 
+         values[i] = NULL;
+       
+    }
+    
+    /* ----------------
+     * increment tuple group counters
+     * ----------------
+     */
+    portal->no_tuples++;
+    group->no_tuples++;
+    tuples->tuple_index++;
+    
+    /* ----------------
+     * return to the original memory context
+     * ----------------
+     */
+    MemoryContextSwitchTo(savecxt);
+}
diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c
new file mode 100644 (file)
index 0000000..a8b2b5a
--- /dev/null
@@ -0,0 +1,351 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-fsstubs.c--
+ *    support for filesystem operations on large objects
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    This should be moved to a more appropriate place.  It is here
+ *    for lack of a better place.
+ *
+ *    Builtin functions for open/close/read/write operations on large objects.
+ *
+ *    These functions operate in the current portal variable context, which 
+ *    means the large object descriptors hang around between transactions and 
+ *    are not deallocated until explicitly closed, or until the portal is 
+ *    closed.
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "lib/dllist.h"
+#include "libpq/libpq.h"
+#include "libpq/libpq-fs.h"
+#include "utils/mcxt.h"
+#include "utils/palloc.h"
+
+#include "storage/fd.h"                /* for O_ */
+#include "storage/large_object.h"
+
+#include "utils/elog.h"
+#include "libpq/be-fsstubs.h"
+
+/*#define FSDB 1*/
+#define MAX_LOBJ_FDS 256
+
+static LargeObjectDesc  *cookies[MAX_LOBJ_FDS];
+
+static GlobalMemory fscxt = NULL;
+
+
+static int newLOfd(LargeObjectDesc *lobjCookie);
+static void deleteLOfd(int fd);
+
+
+/*****************************************************************************
+ *  File Interfaces for Large Objects
+ *****************************************************************************/
+
+int
+lo_open(Oid lobjId, int mode)
+{
+    LargeObjectDesc *lobjDesc;
+    int fd;
+    MemoryContext currentContext;
+    
+#if FSDB
+    elog(NOTICE,"LOopen(%d,%d)",lobjId,mode);
+#endif
+
+    if (fscxt == NULL) {
+       fscxt = CreateGlobalMemory("Filesystem");
+    }
+    currentContext = MemoryContextSwitchTo((MemoryContext)fscxt);
+
+    lobjDesc = inv_open(lobjId, mode);
+    
+    if (lobjDesc == NULL) { /* lookup failed */
+       MemoryContextSwitchTo(currentContext);
+#if FSDB       
+       elog(NOTICE,"cannot open large object %d", lobjId);
+#endif
+       return -1;
+    }
+    
+    fd = newLOfd(lobjDesc);
+
+    /* switch context back to orig. */
+    MemoryContextSwitchTo(currentContext);
+
+    return fd;
+}
+
+int
+lo_close(int fd)
+{
+    MemoryContext currentContext;
+
+    if (fd >= MAX_LOBJ_FDS) {
+       elog(WARN,"lo_close: large obj descriptor (%d) out of range", fd);
+       return -2;
+    }
+    if (cookies[fd] == NULL) {
+       elog(WARN,"lo_close: invalid large obj descriptor (%d)", fd);
+       return -3;
+    }
+#if FSDB
+    elog(NOTICE,"LOclose(%d)",fd);
+#endif
+
+    Assert(fscxt != NULL);
+    currentContext = MemoryContextSwitchTo((MemoryContext)fscxt);
+
+    inv_close(cookies[fd]);
+
+    MemoryContextSwitchTo(currentContext);
+
+    deleteLOfd(fd);
+    return 0;
+}
+
+/*
+ *  We assume the large object supports byte oriented reads and seeks so
+ *  that our work is easier.
+ */
+int
+lo_read(int fd, char *buf, int len)
+{
+    Assert(cookies[fd]!=NULL);
+    return inv_read(cookies[fd], buf, len);
+}
+
+int
+lo_write(int fd, char *buf, int len)
+{
+    Assert(cookies[fd]!=NULL);
+    return inv_write(cookies[fd], buf, len);
+}
+
+
+int
+lo_lseek(int fd, int offset, int whence)
+{
+    if (fd >= MAX_LOBJ_FDS) {
+       elog(WARN,"lo_seek: large obj descriptor (%d) out of range", fd);
+       return -2;
+    }
+    return inv_seek(cookies[fd], offset, whence);
+}
+
+Oid
+lo_creat(int mode)
+{
+    LargeObjectDesc *lobjDesc;
+    MemoryContext currentContext;
+    Oid lobjId;
+    
+    if (fscxt == NULL) {
+       fscxt = CreateGlobalMemory("Filesystem");
+    }
+    
+    currentContext = MemoryContextSwitchTo((MemoryContext)fscxt);
+    
+    lobjDesc = inv_create(mode);
+    
+    if (lobjDesc == NULL) {
+       MemoryContextSwitchTo(currentContext);
+       return InvalidOid;
+    }
+
+    lobjId = lobjDesc->heap_r->rd_id;
+    
+    inv_close(lobjDesc);
+    
+    /* switch context back to original memory context */
+    MemoryContextSwitchTo(currentContext);
+    
+    return lobjId;
+}
+
+int
+lo_tell(int fd)
+{
+    if (fd >= MAX_LOBJ_FDS) {
+       elog(WARN,"lo_tell: large object descriptor (%d) out of range",fd);
+       return -2;
+    }
+    if (cookies[fd] == NULL) {
+       elog(WARN,"lo_tell: invalid large object descriptor (%d)",fd);
+       return -3;
+    }
+    return inv_tell(cookies[fd]);
+}
+
+int
+lo_unlink(Oid lobjId)
+{
+    return (inv_destroy(lobjId));
+}
+
+/*****************************************************************************
+ *  Read/Write using varlena 
+ *****************************************************************************/
+
+struct varlena *
+LOread(int fd, int len)
+{
+    struct varlena *retval;
+    int totalread = 0;
+    
+    retval = (struct varlena *)palloc(sizeof(int32) + len);
+    totalread = lo_read(fd, VARDATA(retval), len);
+    VARSIZE(retval) = totalread + sizeof(int32);
+    
+    return retval;
+}
+
+int LOwrite(int fd, struct varlena *wbuf)
+{
+    int totalwritten;
+    int bytestowrite;
+    
+    bytestowrite = VARSIZE(wbuf) - sizeof(int32);
+    totalwritten = lo_write(fd, VARDATA(wbuf), bytestowrite);
+    return totalwritten;
+}
+
+/*****************************************************************************
+ *   Import/Export of Large Object
+ *****************************************************************************/
+
+/*
+ * lo_import -
+ *    imports a file as an (inversion) large object.
+ */
+Oid
+lo_import(text *filename)
+{
+    int fd;
+    int nbytes, tmp;
+#define BUFSIZE        1024
+    char buf[BUFSIZE];
+    LargeObjectDesc *lobj;
+    Oid lobjOid;
+    
+    /*
+     * open the file to be read in
+     */
+    fd = open(VARDATA(filename), O_RDONLY, 0666);
+    if (fd < 0)  {   /* error */
+       elog(WARN, "lo_import: can't open unix file\"%s\"\n", filename);
+    }
+
+    /*
+     * create an inversion "object"
+     */
+    lobj = inv_create(INV_READ|INV_WRITE);
+    if (lobj == NULL) {
+       elog(WARN, "lo_import: can't create inv object for \"%s\"",
+            VARDATA(filename));
+    }
+
+    /*
+     * the oid for the large object is just the oid of the relation
+     * XInv??? which contains the data.
+     */
+    lobjOid = lobj->heap_r->rd_id;
+       
+    /*
+     * read in from the Unix file and write to the inversion file
+     */
+    while ((nbytes = read(fd, buf, BUFSIZE)) > 0) {
+       tmp = inv_write(lobj, buf, nbytes);
+        if (tmp < nbytes) {
+           elog(WARN, "lo_import: error while reading \"%s\"",
+                VARDATA(filename));
+       }
+    }
+
+    (void) close(fd);
+    (void) inv_close(lobj);
+
+    return lobjOid;
+}
+
+/*
+ * lo_export -
+ *    exports an (inversion) large object.
+ */
+int4
+lo_export(Oid lobjId, text *filename)
+{
+    int fd;
+    int nbytes, tmp;
+#define BUFSIZE        1024
+    char buf[BUFSIZE];
+    LargeObjectDesc *lobj;
+
+    /*
+     * create an inversion "object"
+     */
+    lobj = inv_open(lobjId, INV_READ);
+    if (lobj == NULL) {
+       elog(WARN, "lo_export: can't open inv object %d",
+            lobjId);
+    }
+
+    /*
+     * open the file to be written to
+     */
+    fd = open(VARDATA(filename), O_CREAT|O_WRONLY, 0666);
+    if (fd < 0)  {   /* error */
+       elog(WARN, "lo_export: can't open unix file\"%s\"",
+            VARDATA(filename));
+    }
+
+    /*
+     * read in from the Unix file and write to the inversion file
+     */
+    while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0) {
+       tmp = write(fd, buf, nbytes);
+        if (tmp < nbytes) {
+           elog(WARN, "lo_export: error while writing \"%s\"",
+                VARDATA(filename));
+       }
+    }
+
+    (void) inv_close(lobj);
+    (void) close(fd);
+
+    return 1;
+}
+
+
+/*****************************************************************************
+ *  Support routines for this file
+ *****************************************************************************/
+
+static int
+newLOfd(LargeObjectDesc *lobjCookie)
+{
+    int i;
+    
+    for (i = 0; i < MAX_LOBJ_FDS; i++) {
+       
+       if (cookies[i] == NULL) {
+           cookies[i] = lobjCookie;
+           return i;
+       }
+    }
+    return -1;
+}
+
+static void 
+deleteLOfd(int fd)
+{
+    cookies[fd] = NULL;
+}
diff --git a/src/backend/libpq/be-fsstubs.h b/src/backend/libpq/be-fsstubs.h
new file mode 100644 (file)
index 0000000..0c7c57c
--- /dev/null
@@ -0,0 +1,32 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-fsstubs.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        BE_FSSTUBS_H
+#define        BE_FSSTUBS_H
+
+extern Oid lo_import(text *filename);
+extern int4 lo_export(Oid lobjId, text *filename);
+
+extern Oid lo_creat(int mode);
+
+extern int lo_open(Oid lobjId, int mode);
+extern int lo_close(int fd);
+extern int lo_read(int fd, char *buf, int len);
+extern int lo_write(int fd, char *buf, int len);
+extern int lo_lseek(int fd, int offset, int whence);
+extern int lo_tell(int fd);
+extern int lo_unlink(Oid lobjId);
+
+extern struct varlena *LOread(int fd, int len);
+extern int LOwrite(int fd, struct varlena *wbuf);
+     
+#endif /* BE_FSSTUBS_H */
diff --git a/src/backend/libpq/be-pqexec.c b/src/backend/libpq/be-pqexec.c
new file mode 100644 (file)
index 0000000..61e5149
--- /dev/null
@@ -0,0 +1,382 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-pqexec.c--
+ *    support for executing POSTGRES commands and functions from a
+ *    user-defined function in a backend.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *     PQfn            - call a POSTGRES function
+ *     PQexec          - execute a POSTGRES query
+ *     
+ * NOTES
+ *     These routines are compiled into the postgres backend.
+ */
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "tcop/dest.h"
+#include "tcop/fastpath.h"
+#include "tcop/tcopprot.h"
+#include "lib/dllist.h"
+#include "libpq/libpq-be.h"
+#include "fmgr.h"
+#include "utils/exc.h"
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+/* ----------------------------------------------------------------
+ *                     PQ interface routines
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     PQfn -  Send a function call to the POSTGRES backend.
+ *
+ *     fnid            : function id
+ *     result_buf      : pointer to result buffer (&int if integer)
+ *     result_len      : length of return value.
+ *     result_is_int   : If the result is an integer, this must be non-zero
+ *     args            : pointer to a NULL terminated arg array.
+ *                       (length, if integer, and result-pointer)
+ *     nargs           : # of arguments in args array.
+ *
+ *     This code scavanged from HandleFunctionRequest() in tcop/fastpath.h
+ * ----------------
+ */
+char *
+PQfn(int fnid,
+     int *result_buf,          /* can't use void, dec compiler barfs */
+     int result_len,
+     int result_is_int,
+     PQArgBlock *args,
+     int nargs)
+{
+    char *retval;              /* XXX - should be datum, maybe ? */
+    char *arg[8];
+    int  i;
+    
+    /* ----------------
+     * fill args[] array
+     * ----------------
+     */
+    for (i = 0; i < nargs; i++) {
+       if (args[i].len == VAR_LENGTH_ARG) {
+           arg[i] = (char*) args[i].u.ptr;
+       } else if (args[i].len > 4)  {
+           elog(WARN,"arg_length of argument %d too long",i);
+       } else {
+           arg[i] = (char*)args[i].u.integer;
+       }
+    }
+    
+    /* ----------------
+     * call the postgres function manager
+     * ----------------
+     */
+    retval = (char *)
+       fmgr(fnid, arg[0], arg[1], arg[2], arg[3],
+            arg[4], arg[5], arg[6], arg[7]);
+    
+    /* ----------------
+     * put the result in the buffer the user specified and
+     *  return the proper code.
+     * ----------------
+     */
+    if (retval == (char *) NULL)       /* void retval */
+       return "0";
+    
+    if (result_is_int) {
+       *result_buf = (int) retval;
+    } else {
+       memmove(result_buf, retval, result_len); 
+    }
+    return "G";
+}
+
+/* ----------------
+ *     PQexec -  Send a query to the POSTGRES backend
+ *
+ *     The return value is a string.  
+ *     If 0 or more tuples fetched from the backend, return "P portal-name".
+ *     If a query is does not return tuples, return "C query-command".
+ *     If there is an error: return "E error-message".
+ *
+ *     Note: if we get a serious error or an elog(WARN), then PQexec never
+ *     returns because the system longjmp's back to the main loop.
+ * ----------------
+ */
+char *
+PQexec(char *query)
+{
+    PortalEntry *entry = NULL;
+    char *result = NULL;
+    
+    /* ----------------
+     * create a new portal and put it on top of the portal stack.
+     * ----------------
+     */
+    entry = (PortalEntry *) be_newportal();
+    be_portalpush(entry);
+    
+    /* ----------------
+     * pg_eval_dest will put the query results in a portal which will
+     *  end up on the top of the portal stack.
+     * ----------------
+     */
+    pg_eval_dest(query, (char **) NULL, (Oid *) NULL, 0, Local);
+    
+    /* ----------------
+     * pop the portal off the portal stack and return the
+     *  result.  Note if result is null, we return C.
+     * ----------------
+     */
+    entry = (PortalEntry *) be_portalpop();
+    result = entry->result;
+    if (result == NULL) {
+       char *PQE = "Cnull PQexec result";
+       result = pstrdup(PQE);
+    }
+    
+    if (result[0] != 'P')
+       {
+           /* some successful command was executed,
+              but it's not one where we return the portal name so
+              here we should be sure to clear out the portal 
+              (since the caller has no handle on it)
+              */
+           pbuf_close(entry->name); 
+           
+       }
+    return result;
+}
+
+/* ----------------------------------------------------------------
+ *                     pqtest support
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     pqtest_PQexec takes a text query and returns the number of
+ *     tuples it returns.  Note: there is no need to PQclear()
+ *     here - the memory will go away at end transaction.
+ * ----------------
+ */
+int
+pqtest_PQexec(char *q)
+{
+    PortalBuffer *a;
+    char        *res;
+    int         t;
+    
+    /* ----------------
+     * execute the postgres query
+     * ----------------
+     */
+    res = PQexec(q);
+    
+    /* ----------------
+     * return number of tuples in portal or 0 if command returns no tuples.
+     * ----------------
+     */
+    t = 0;
+    switch(res[0]) {
+    case 'P':
+       a = PQparray(&res[1]);
+       if (a == NULL)
+           elog(WARN, "pqtest_PQexec: PQparray could not find portal %s",
+                res);
+       
+       t = PQntuples(a);
+       break;
+    case 'C':
+       break;
+    default:
+       elog(NOTICE, "pqtest_PQexec: PQexec(%s) returns %s", q, res);
+       break;
+    }
+    
+    return t;
+}
+
+/* ----------------
+ *     utilities for pqtest_PQfn()
+ * ----------------
+ */
+char *
+strmake(char *str, int len)
+{
+    char *newstr;
+    if (str == NULL) return NULL;
+    if (len <= 0) len = strlen(str);
+    
+    newstr = (char *) palloc((unsigned) len+1);
+    (void) strncpy(newstr, str, len);
+    newstr[len] = (char) 0;
+    return newstr;
+}
+
+#define SKIP 0
+#define SCAN 1
+
+static char spacestr[] = " ";
+
+static int
+strparse(char *s, char **fields, int *offsets, int maxfields)
+{
+    int len = strlen(s);
+    char *cp = s, *end = cp + len, *ep;
+    int parsed = 0;
+    int mode = SKIP, i = 0;
+    
+    if (*(end - 1) == '\n') end--;
+    
+    for (i=0; i<maxfields; i++)
+       fields[i] = spacestr;
+    
+    i = 0;
+    while (!parsed) {
+       if (mode == SKIP) {
+           
+           while ((cp < end) &&
+                  (*cp == ' ' || *cp == '\t'))
+               cp++;
+           if (cp < end) mode = SCAN;
+           else parsed = 1;
+           
+       } else {
+           
+           ep = cp;
+           while ((ep < end) && (*ep != ' ' && *ep != '\t'))
+               ep++;
+           
+           if (ep < end) mode = SKIP;
+           else parsed = 1;
+           
+           fields[i] = strmake(cp, ep - cp);
+           if (offsets != NULL)
+               offsets[i] = cp - s;
+           
+           i++;
+           cp = ep;
+           if (i > maxfields)
+               parsed = 1;
+           
+       }
+    }
+    return i;
+}
+
+/* ----------------
+ *     pqtest_PQfn converts it's string into a PQArgBlock and
+ *     calls the specified function, which is assumed to return
+ *     an integer value.
+ * ----------------
+ */
+int
+pqtest_PQfn(char *q)
+{
+    int k, j, i, v, f, offsets;
+    char *fields[8];
+    PQArgBlock pqargs[7];
+    int res;
+    char *pqres;
+    
+    /* ----------------
+     * parse q into fields
+     * ----------------
+     */
+    i = strparse(q, fields, &offsets, 8);
+    printf("pqtest_PQfn: strparse returns %d fields\n", i); /* debug */
+    if (i == 0)
+       return -1;
+    
+    /* ----------------
+     * get the function id
+     * ----------------
+     */
+    f = atoi(fields[0]);
+    printf("pqtest_PQfn: func is %d\n", f); /* debug */
+    if (f == 0)
+       return -1;
+    
+    /* ----------------
+     * build a PQArgBlock
+     * ----------------
+     */
+    for (j=1; j<i && j<8; j++) {
+       k = j-1;
+       v = atoi(fields[j]);
+       if (v != 0 || (v == 0 && fields[j][0] == '0')) {
+           pqargs[k].len = 4;
+           pqargs[k].u.integer = v;
+           printf("pqtest_PQfn: arg %d is int %d\n", k, v); /* debug */
+       } else {
+           pqargs[k].len = VAR_LENGTH_ARG;
+           pqargs[k].u.ptr = (int *) textin(fields[j]);
+           printf("pqtest_PQfn: arg %d is text %s\n", k, fields[j]); /*debug*/
+       }
+    }
+    
+    /* ----------------
+     * call PQfn
+     * ----------------
+     */
+    pqres = PQfn(f, &res, 4, 1, pqargs, i-1);
+    printf("pqtest_PQfn: pqres is %s\n", pqres); /* debug */
+    
+    /* ----------------
+     * free memory used
+     * ----------------
+     */
+    for (j=0; j<i; j++) {
+       pfree(fields[j]);
+       if (pqargs[j].len == VAR_LENGTH_ARG)
+           pfree(pqargs[j].u.ptr);
+    }
+    
+    /* ----------------
+     * return result
+     * ----------------
+     */
+    printf("pqtest_PQfn: res is %d\n", res); /* debugg */
+    return res;
+}
+
+/* ----------------
+ *     pqtest looks at the first character of it's test argument
+ *     and decides which of pqtest_PQexec or pqtest_PQfn to call.
+ * ----------------
+ */
+int32
+pqtest(struct varlena *vlena)
+{
+    char        *q;
+    
+    /* ----------------
+     * get the query
+     * ----------------
+     */
+    q = textout(vlena);
+    if (q == NULL)
+       return -1;
+    
+    switch(q[0]) {
+    case '%':
+       return pqtest_PQfn(&q[1]);
+       break;
+    default:
+       return pqtest_PQexec(q);
+       break;
+    }
+    return(0);
+}
diff --git a/src/backend/libpq/libpq-be.h b/src/backend/libpq/libpq-be.h
new file mode 100644 (file)
index 0000000..4444a31
--- /dev/null
@@ -0,0 +1,51 @@
+/*-------------------------------------------------------------------------
+ *
+ * libpq-be.h--
+ *    This file contains definitions for structures and
+ *    externs for functions used by the POSTGRES backend.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef LIBPQ_BE_H
+#define LIBPQ_BE_H
+
+/* ----------------
+ *     include stuff common to fe and be
+ * ----------------
+ */
+#include "libpq/libpq.h"
+#include "access/htup.h"
+
+#include "access/tupdesc.h"
+
+/* ----------------
+ *     declarations for backend libpq support routines
+ * ----------------
+ */
+
+/* in be-dumpdata.c */
+extern void be_portalinit(void);
+extern void be_portalpush(PortalEntry *entry);
+extern PortalEntry *be_portalpop(void);
+extern PortalEntry *be_currentportal();
+extern PortalEntry *be_newportal(void);
+extern void be_typeinit(PortalEntry *entry, TupleDesc attrs,
+                       int natts);
+extern void be_printtup(HeapTuple tuple, TupleDesc typeinfo);
+
+
+/* in be-pqexec.c */
+extern char *PQfn(int fnid, int *result_buf, int result_len, int result_is_int, 
+                 PQArgBlock *args, int nargs);
+extern char *PQexec(char *query);
+extern int pqtest_PQexec(char *q);
+extern char *strmake(char *str, int len);
+extern int pqtest_PQfn(char *q);
+extern int32 pqtest(struct varlena *vlena);
+
+#endif /* LIBPQ_BE_H */
diff --git a/src/backend/libpq/libpq-fs.h b/src/backend/libpq/libpq-fs.h
new file mode 100644 (file)
index 0000000..47cfbf4
--- /dev/null
@@ -0,0 +1,119 @@
+/*-------------------------------------------------------------------------
+ *
+ * libpq-fs.h--
+ *    definitions for using Inversion file system routines
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef LIBPQ_FS_H
+#define LIBPQ_FS_H
+
+#include "lib/dllist.h"
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>             /* for O_ on some */
+#ifndef WIN32
+#include <unistd.h>            /* for SEEK_ on most */
+#endif /* WIN32 */
+#ifndef SEEK_SET
+#include <stdio.h>             /* for SEEK_ on others */
+#endif /* SEEK_SET */
+
+/* UNIX compatibility junk.  This should be in all systems' include files,
+   but this is not always the case. */
+
+#ifndef MAXNAMLEN
+#define MAXNAMLEN 255
+#endif /* MAXNAMLEN */
+
+struct pgdirent {
+       unsigned long d_ino;
+       unsigned short d_namlen;
+       char d_name[MAXNAMLEN+1];
+};
+
+/*
+ * SysV struct dirent doesn't have d_namlen.
+ * This counts on d_name being last, which is moderately safe (ha) since 
+ * it's the variable-length part of the structure.
+ */
+#ifdef SYSV_DIRENT
+#define        D_NAMLEN(dp) \
+       ((dp)->d_reclen - offsetof(struct dirent, d_name[0]))
+#else /* SYSV_DIRENT */
+#define        D_NAMLEN(dp) \
+       ((dp)->d_namlen)
+#endif /* SYSV_DIRENT */
+
+/* for stat(2) */
+#ifndef S_IRUSR
+/* file modes */
+
+#define S_IRWXU 00700           /* read, write, execute: owner */
+#define S_IRUSR 00400           /*  read permission: owner */
+#define S_IWUSR 00200           /*  write permission: owner */
+#define S_IXUSR 00100           /*  execute permission: owner */
+
+#define S_IRWXG 00070           /* read, write, execute: group */
+#define S_IRGRP 00040           /*  read permission: group */
+#define S_IWGRP 00020           /*  write permission: group */
+#define S_IXGRP 00010           /*  execute permission: group */
+
+#define S_IRWXO 00007           /* read, write, execute: other */
+#define S_IROTH 00004           /*  read permission: other */
+#define S_IWOTH 00002           /*  write permission: other */
+#define S_IXOTH 00001           /*  execute permission: other */
+
+#define _S_IFMT  0170000        /* type of file; sync with S_IFMT */
+#define _S_IFBLK 0060000        /* block special; sync with S_IFBLK */
+#define _S_IFCHR 0020000        /* character special sync with S_IFCHR */
+#define _S_IFDIR 0040000        /* directory; sync with S_IFDIR */
+#define _S_IFIFO 0010000        /* FIFO - named pipe; sync with S_IFIFO */
+#define _S_IFREG 0100000        /* regular; sync with S_IFREG */
+
+#define S_IFDIR _S_IFDIR
+#define S_IFREG _S_IFREG
+
+#define S_ISDIR( mode )         (((mode) & _S_IFMT) == _S_IFDIR)
+
+#endif /* S_IRUSR */
+
+/*
+ * Inversion doesn't have links.
+ */
+#ifndef S_ISLNK
+#define S_ISLNK(x) 0
+#endif
+
+/*
+ *  Flags for inversion file system large objects.  Normally, creat()
+ *  takes mode arguments, but we don't use them in inversion, since
+ *  you get postgres protections.  Instead, we use the low sixteen bits
+ *  of the integer mode argument to store the number of the storage
+ *  manager to be used, and the high sixteen bits for flags.
+ */
+
+#define INV_SMGRMASK   0x0000ffff
+#define        INV_ARCHIVE     0x00010000
+#define        INV_WRITE       0x00020000
+#define        INV_READ        0x00040000
+
+/* Error values for p_errno */
+#define PEPERM           1               /* Not owner */
+#define PENOENT          2               /* No such file or directory */
+#define PEACCES          13              /* Permission denied */
+#define PEEXIST          17              /* File exists */
+#define PENOTDIR         20              /* Not a directory*/
+#define PEISDIR          21              /* Is a directory */
+#define PEINVAL          22              /* Invalid argument */
+#define PENAMETOOLONG    63              /* File name too long */
+#define PENOTEMPTY       66              /* Directory not empty */
+#define PEPGIO           99              /* postgres backend had problems */
+
+#endif /* LIBPQ_FS_H */
diff --git a/src/backend/libpq/libpq.h b/src/backend/libpq/libpq.h
new file mode 100644 (file)
index 0000000..0eb32b5
--- /dev/null
@@ -0,0 +1,261 @@
+/*-------------------------------------------------------------------------
+ *
+ * libpq.h--
+ *    POSTGRES LIBPQ buffer structure definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    This file contains definitions for structures and
+ *    externs for functions used by both frontend applications
+ *    and the POSTGRES backend.  See the files libpq-fe.h and
+ *    libpq-be.h for frontend/backend specific information
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef LIBPQ_H
+#define LIBPQ_H
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifdef WIN32
+#include <winsock.h>
+#else
+#include <netinet/in.h>
+#endif /* WIN32 */
+
+#include "lib/dllist.h"
+#include "utils/exc.h"
+#include "postgres.h"
+
+#include "libpq/pqcomm.h"
+
+/* ----------------
+ * PQArgBlock --
+ *     Information (pointer to array of this structure) required
+ *     for the PQfn() call.
+ * ----------------
+ */
+typedef struct {
+    int len;
+    int isint;
+    union {
+        int *ptr;      /* can't use void (dec compiler barfs)  */
+       int integer;
+    } u;
+} PQArgBlock;
+
+/* ----------------
+ * TypeBlock --
+ *     Information about an attribute.
+ * ----------------
+ */
+#define NameLength 16
+
+typedef struct TypeBlock {
+    char name[NAMEDATALEN];    /* name of the attribute */
+    int adtid;                 /* adtid of the type */
+    int adtsize;               /* adtsize of the type */
+} TypeBlock;
+
+/* ----------------
+ * TupleBlock --
+ *     Data of a tuple.
+ * ----------------
+ */
+#define TupleBlockSize 100
+
+typedef struct TupleBlock {
+    char **values[TupleBlockSize];     /* an array of tuples */
+    int *lengths[TupleBlockSize];       /* an array of length vec. foreach
+                                          tuple */
+    struct TupleBlock *next;           /* next tuple block */
+    int    tuple_index;                        /* current tuple index */
+} TupleBlock;
+
+/* ----------------
+ * GroupBuffer --
+ *     A group of tuples with the same attributes.
+ * ----------------
+ */
+typedef struct GroupBuffer {
+    int no_tuples;             /* number of tuples in this group */
+    int no_fields;             /* number of attributes */
+    TypeBlock *types;                  /* types of the attributes */
+    TupleBlock *tuples;                /* tuples in this group */
+    struct GroupBuffer *next;  /* next group */
+} GroupBuffer;
+
+/* ----------------
+ * PortalBuffer --
+ *     Data structure of a portal buffer.  
+ * ----------------
+ */
+typedef struct PortalBuffer {
+    int rule_p;                        /* 1 if this is an asynchronized portal. */
+    int no_tuples;             /* number of tuples in this portal buffer */
+    int no_groups;             /* number of tuple groups */
+    GroupBuffer *groups;       /* linked list of tuple groups */
+} PortalBuffer;
+
+/* ----------------
+ * PortalEntry --
+ *     an entry in the global portal table
+ *
+ * Note: the portalcxt is only meaningful for PQcalls made from
+ *       within a postgres backend.  frontend apps should ignore it.
+ * ----------------
+ */
+#define PortalNameLength 32
+
+typedef struct PortalEntry {
+    char         name[PortalNameLength]; /* name of this portal */
+    PortalBuffer  *portal;               /* tuples contained in this portal */
+    Pointer      portalcxt;              /* memory context (for backend) */
+    Pointer      result;                 /* result for PQexec */
+} PortalEntry;
+
+#define PORTALS_INITIAL_SIZE 32
+#define PORTALS_GROW_BY      32
+
+/* in portalbuf.c */
+extern PortalEntry** portals;          
+extern size_t portals_array_size;
+
+/*
+ *  Asynchronous notification
+ */
+typedef struct PQNotifyList {
+    char relname[NAMEDATALEN]; /* name of relation containing data */
+    int be_pid;                        /* process id of backend */
+    int valid;                 /* has this already been handled by user. */
+/*    SLNode Node; */
+} PQNotifyList;
+
+/*
+ * Exceptions.
+ */
+
+#define libpq_raise(X, Y) ExcRaise((Exception *)(X), (ExcDetail) (Y),\
+                                  (ExcData)0, (ExcMessage) 0)
+
+/* in portal.c */
+extern Exception MemoryError, PortalError, PostquelError, ProtocolError;
+
+/* 
+ * POSTGRES backend dependent Constants. 
+ */
+
+/* ERROR_MSG_LENGTH should really be the same as ELOG_MAXLEN in utils/elog.h*/
+#define ERROR_MSG_LENGTH 4096
+#define COMMAND_LENGTH 20
+#define REMARK_LENGTH 80
+
+extern char PQerrormsg[ERROR_MSG_LENGTH];      /* in portal.c */
+
+/*
+ * External functions.
+ */
+
+/* 
+ * prototypes for functions in portal.c 
+ */
+extern void pqdebug(char *target, char *msg);
+extern void pqdebug2(char *target, char *msg1, char *msg2);
+extern void PQtrace(void);
+extern void PQuntrace(void);
+extern int PQnportals(int rule_p);
+extern void PQpnames(char **pnames, int rule_p);
+extern PortalBuffer *PQparray(char *pname);
+extern int PQrulep(PortalBuffer *portal);
+extern int PQntuples(PortalBuffer *portal); 
+extern int PQninstances(PortalBuffer *portal);
+extern int PQngroups(PortalBuffer *portal);
+extern int PQntuplesGroup(PortalBuffer *portal, int group_index);
+extern int PQninstancesGroup(PortalBuffer *portal, int group_index);
+extern int PQnfieldsGroup(PortalBuffer *portal, int group_index);
+extern int PQfnumberGroup(PortalBuffer *portal, int group_index, char *field_name);
+extern char *PQfnameGroup(PortalBuffer *portal, int group_index, int field_number);
+extern int PQftypeGroup(PortalBuffer *portal, int group_index,
+                       int field_number);
+extern int PQfsizeGroup(PortalBuffer *portal, int group_index,
+                       int field_number);
+extern GroupBuffer *PQgroup(PortalBuffer *portal, int tuple_index);
+extern int PQgetgroup(PortalBuffer *portal, int tuple_index);
+extern int PQnfields(PortalBuffer *portal, int tuple_index);
+extern int PQfnumber(PortalBuffer *portal, int tuple_index, char *field_name);
+ extern char *PQfname(PortalBuffer *portal, int tuple_index, int field_number); 
+extern int PQftype(PortalBuffer *portal, int tuple_index, int field_number);
+extern int PQfsize(PortalBuffer *portal, int tuple_index, int field_number);
+extern int PQsametype(PortalBuffer *portal, int tuple_index1, int tuple_index2);
+extern char *PQgetvalue(PortalBuffer *portal, int tuple_index, int field_number);
+extern char *PQgetAttr(PortalBuffer *portal, int tuple_index, int field_number);
+extern int PQgetlength(PortalBuffer *portal, int tuple_index, int field_number);
+extern void PQclear(char *pname);
+extern void PQcleanNotify(void);
+extern void PQnotifies_init(void);
+extern PQNotifyList *PQnotifies(void);
+extern void PQremoveNotify(PQNotifyList *nPtr);
+extern void PQappendNotify(char *relname, int pid);
+/* 
+ * prototypes for functions in portalbuf.c 
+ */
+extern caddr_t pbuf_alloc(size_t size);
+extern void pbuf_free(caddr_t pointer);
+extern PortalBuffer *pbuf_addPortal(void);
+extern GroupBuffer *pbuf_addGroup(PortalBuffer *portal);
+extern TypeBlock *pbuf_addTypes(int n);
+extern TupleBlock *pbuf_addTuples(void);
+extern char **pbuf_addTuple(int n);
+extern int *pbuf_addTupleValueLengths(int n);
+extern char *pbuf_addValues(int n);
+extern PortalEntry *pbuf_addEntry(void);
+extern void pbuf_freeEntry(int i);
+extern void pbuf_freeTypes(TypeBlock *types);
+extern void pbuf_freeTuples(TupleBlock *tuples, int no_tuples, int no_fields);
+extern void pbuf_freeGroup(GroupBuffer *group);
+extern void pbuf_freePortal(PortalBuffer *portal);
+extern int pbuf_getIndex(char *pname);
+extern void pbuf_setportalinfo(PortalEntry *entry, char *pname);
+extern PortalEntry *pbuf_setup(char *pname);
+extern void pbuf_close(char *pname);
+extern GroupBuffer *pbuf_findGroup(PortalBuffer *portal, int group_index);
+extern int pbuf_findFnumber(GroupBuffer *group, char *field_name);
+extern void pbuf_checkFnumber(GroupBuffer *group, int field_number);
+extern char *pbuf_findFname(GroupBuffer *group, int field_number);
+
+/* 
+ * prototypes for functions in pqcomm.c 
+ */
+extern void pq_init(int fd);
+extern void pq_gettty(char *tp);
+extern int pq_getport(void);
+extern void pq_close(void);
+extern void pq_flush(void);
+extern int pq_getstr(char *s, int maxlen);
+extern int PQgetline(char *s, int maxlen); 
+extern int PQputline(char *s); 
+extern int pq_getnchar(char *s, int off, int maxlen);
+extern int pq_getint(int b);
+extern void pq_putstr(char *s);
+extern void pq_putnchar(char *s, int n);
+extern void pq_putint(int i, int b);
+extern int pq_sendoob(char *msg, int len);
+extern int pq_recvoob(char *msgPtr, int *lenPtr);
+extern int pq_getinaddr(struct sockaddr_in *sin, char *host, int port);
+extern int pq_getinserv(struct sockaddr_in *sin, char *host, char *serv);
+extern int pq_connect(char *dbname, char *user, char *args, char *hostName, 
+                     char *debugTty, char *execFile, short portName);
+extern int StreamOpen(char *hostName, short portName, Port *port);
+extern void pq_regoob(void (*fptr)());
+extern void pq_unregoob(void);
+extern void pq_async_notify(void);
+extern int StreamServerPort(char *hostName, short portName, int *fdP);
+extern int StreamConnection(int server_fd, Port *port);
+extern void StreamClose(int sock);
+
+#endif /* LIBPQ_H */
diff --git a/src/backend/libpq/portal.c b/src/backend/libpq/portal.c
new file mode 100644 (file)
index 0000000..194225d
--- /dev/null
@@ -0,0 +1,783 @@
+/*-------------------------------------------------------------------------
+ *
+ * portal.c--
+ *    generalized portal support routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ *   UTILITY ROUTINES
+ *     pqdebug         - send a string to the debugging output port
+ *     pqdebug2        - send two strings to stdout
+ *     PQtrace         - turn on pqdebug() tracing
+ *     PQuntrace       - turn off pqdebug() tracing
+ *
+ *   INTERFACE ROUTINES
+ *     PQnportals      - Return the number of open portals. 
+ *     PQpnames        - Return all the portal names
+ *     PQparray        - Return the portal buffer given a portal name
+ *     PQrulep         - Return 1 if an asynchronous portal
+ *     PQntuples       - Return the number of tuples in a portal buffer
+ *     PQninstances    -   same as PQntuples using object terminology
+ *     PQngroups       - Return the number of tuple groups in a portal buffer
+ *     PQntuplesGroup  - Return the number of tuples in a tuple group
+ *     PQninstancesGroup  - same as PQntuplesGroup using object terminology
+ *     PQnfieldsGroup  - Return the number of fields in a tuple group
+ *     PQfnumberGroup  - Return field number given (group index, field name)
+ *     PQftypeGroup    - Return field type given (group index, field index)
+ *     PQfsizeGroup    - Return field size given (group index, field index)
+ *     PQfnameGroup    - Return field name given (group index, field index)
+ *     PQgroup         - Return the tuple group that a particular tuple is in
+ *     PQgetgroup      - Return the index of the group that a tuple is in
+ *     PQnfields       - Return the number of fields in a tuple
+ *     PQfnumber       - Return the field index of a field name in a tuple
+ *     PQfname         - Return the name of a field
+ *     PQftype         - Return the type of a field
+ *     PQfsize         - Return the size of a field
+ *     PQftype         - Return the type of a field
+ *     PQsametype      - Return 1 if the two tuples have the same type
+ *     PQgetvalue      - Return an attribute (field) value
+ *     PQgetlength     - Return an attribute (field) length
+ *     PQclear         - free storage claimed by named portal
+ *      PQnotifies      - Return a list of relations on which notification 
+ *                        has occurred.
+ *      PQremoveNotify  - Remove this notification from the list.
+ *
+ *   NOTES
+ *     These functions may be used by both frontend routines which
+ *     communicate with a backend or by user-defined functions which
+ *     are compiled or dynamically loaded into a backend.
+ *
+ *     the portals[] array should be organized as a hash table for
+ *     quick portal-by-name lookup.
+ *
+ *     Do not confuse "PortalEntry" (or "PortalBuffer") with "Portal"
+ *     see utils/mmgr/portalmem.c for why. -cim 2/22/91
+ *
+ */
+#include <stdio.h>     /* for sprintf() */
+#include <string.h>
+
+#include "c.h"
+#include "lib/dllist.h"
+#include "libpq/libpq.h"       /* where the declarations go */
+#include "utils/exc.h"
+#include "utils/palloc.h"
+
+/* ----------------
+ *     exceptions
+ * ----------------
+ */
+Exception MemoryError = {"Memory Allocation Error"};
+Exception PortalError = {"Invalid arguments to portal functions"};
+Exception PostquelError = {"Sql Error"};
+Exception ProtocolError = {"Protocol Error"};
+char PQerrormsg[ERROR_MSG_LENGTH];
+
+int PQtracep = 0;              /* 1 to print out debugging messages */
+FILE *debug_port = (FILE *) NULL;
+
+static int
+in_range(char *msg, int value, int min, int max)
+{
+    if (value < min || value >= max) {
+       (void) sprintf(PQerrormsg, "FATAL: %s, %d is not in range [%d,%d)\n",
+                      msg, value, min, max);
+       pqdebug("%s", PQerrormsg);
+       fputs(PQerrormsg, stderr);
+       return(0);
+    }
+    return(1);
+}
+
+static int
+valid_pointer(char *msg, void *ptr)
+{
+    if (!ptr) {
+       (void) sprintf(PQerrormsg, "FATAL: %s\n", msg);
+       pqdebug("%s", PQerrormsg);
+       fputs(PQerrormsg, stderr);
+       return(0);
+    }
+    return(1);
+}
+
+/* ----------------------------------------------------------------
+ *                     PQ utility routines
+ * ----------------------------------------------------------------
+ */
+void
+pqdebug(char *target, char *msg)
+{
+    if (!target)
+       return;
+    
+    if (PQtracep) {
+       /*
+        * if nothing else was suggested default to stdout
+        */
+       if (!debug_port)
+           debug_port = stdout;
+       fprintf(debug_port, target, msg);
+       fprintf(debug_port, "\n");
+    }
+}
+
+void
+pqdebug2(char *target, char *msg1, char *msg2)
+{
+    if (!target)
+       return;
+    
+    if (PQtracep) {
+       /*
+        * if nothing else was suggested default to stdout
+        */
+       if (!debug_port)
+           debug_port = stdout;
+       fprintf(debug_port, target, msg1, msg2);
+       fprintf(debug_port, "\n");
+    }
+}
+
+/* --------------------------------
+ *     PQtrace() / PQuntrace()
+ * --------------------------------
+ */
+void
+PQtrace()
+{
+    PQtracep = 1;
+}
+
+void
+PQuntrace()
+{
+    PQtracep = 0;
+}
+
+/* ----------------------------------------------------------------
+ *                 PQ portal interface routines
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     PQnportals - Return the number of open portals. 
+ *     If rule_p, only return asynchronous portals. 
+ * --------------------------------
+ */
+int
+PQnportals(int rule_p)
+{
+    int i, n = 0;
+    
+    for (i = 0; i < portals_array_size; ++i) {
+       if (portals[i] && portals[i]->portal) {
+           if (!rule_p || portals[i]->portal->rule_p) {
+               ++n;
+           }
+       }
+    }
+    return(n);
+}
+
+/* --------------------------------
+ *     PQpnames - Return all the portal names
+ *     If rule_p, only return asynchronous portals. 
+ *
+ *         the caller must have allocated sufficient memory for char** pnames
+ *        (an array of PQnportals strings of length PortalNameLength).
+ *
+ *        notice that this assumes that the user is calling PQnportals and
+ *        PQpnames with the same rule_p argument, and with no intervening
+ *        portal closures.  if not, you can get in heap big trouble..
+ * --------------------------------
+ */
+void
+PQpnames(char **pnames, int rule_p)
+{
+    int i, cur_pname = 0;
+    
+    if (!valid_pointer("PQpnames: invalid name buffer", pnames))
+       return;
+    
+    for (i = 0; i < portals_array_size; ++i) {
+       if (portals[i] && portals[i]->portal) {
+           if (!rule_p || portals[i]->portal->rule_p) {
+               (void) strncpy(pnames[cur_pname], portals[i]->name, PortalNameLength);
+               ++cur_pname;
+           }
+       }
+    }
+}
+
+/* --------------------------------
+ *     PQparray - Return the portal buffer given a portal name
+ * --------------------------------
+ */
+PortalBuffer *
+PQparray(char *pname)
+{
+    int i;
+    
+    if (!valid_pointer("PQparray: invalid name buffer", pname))
+       return NULL;
+    
+    if ((i = pbuf_getIndex(pname)) < 0)
+       return((PortalBuffer *) NULL);
+    return(portals[i]->portal);
+}
+
+/* --------------------------------
+ *     PQrulep - Return 1 if an asynchronous portal
+ * --------------------------------
+ */
+int
+PQrulep(PortalBuffer *portal)
+{
+    if (!valid_pointer("PQrulep: invalid portal pointer", portal))
+       return(-1);
+    
+    return(portal->rule_p);
+}
+
+/* --------------------------------
+ *     PQntuples - Return the number of tuples in a portal buffer
+ * --------------------------------
+ */
+int
+PQntuples(PortalBuffer *portal)
+{
+    if (!valid_pointer("PQntuples: invalid portal pointer", portal))
+       return(-1);
+    
+    return(portal->no_tuples);
+}
+
+int
+PQninstances(PortalBuffer *portal)
+{
+    return(PQntuples(portal));
+}
+
+/* --------------------------------
+ *     PQngroups - Return the number of tuple groups in a portal buffer
+ * --------------------------------
+ */
+int
+PQngroups(PortalBuffer *portal)
+{
+    if (!valid_pointer("PQngroups: invalid portal pointer", portal))
+       return(-1);
+    
+    return(portal->no_groups);
+}
+
+/* --------------------------------
+ *     PQntuplesGroup - Return the number of tuples in a tuple group
+ * --------------------------------
+ */
+int
+PQntuplesGroup(PortalBuffer *portal, int group_index)
+{
+    GroupBuffer *gbp;
+    
+    if (!valid_pointer("PQntuplesGroup: invalid portal pointer", portal) ||
+       !in_range("PQntuplesGroup: group index",
+                 group_index, 0, portal->no_groups))
+       return(-1);
+
+    gbp = pbuf_findGroup(portal, group_index);
+    if (gbp)
+       return(gbp->no_tuples);
+    return(-1);
+}
+
+int
+PQninstancesGroup(PortalBuffer *portal, int group_index)
+{
+    return(PQntuplesGroup(portal, group_index));
+}
+
+/* --------------------------------
+ *     PQnfieldsGroup - Return the number of fields in a tuple group
+ * --------------------------------
+ */
+int
+PQnfieldsGroup(PortalBuffer *portal, int group_index)
+{
+    GroupBuffer *gbp;
+    
+    if (!valid_pointer("PQnfieldsGroup: invalid portal pointer", portal) ||
+       !in_range("PQnfieldsGroup: group index",
+                 group_index, 0, portal->no_groups))
+       return(-1);
+    gbp = pbuf_findGroup(portal, group_index);
+    if (gbp)
+       return(gbp->no_fields);
+    return(-1);
+}
+
+/* --------------------------------
+ *     PQfnumberGroup - Return the field number (index) given
+ *                      the group index and the field name
+ * --------------------------------
+ */
+int
+PQfnumberGroup(PortalBuffer *portal, int group_index, char *field_name)
+{
+    GroupBuffer *gbp;
+    
+    if (!valid_pointer("PQfnumberGroup: invalid portal pointer", portal) ||
+       !valid_pointer("PQfnumberGroup: invalid field name pointer",
+                      field_name) ||
+       !in_range("PQfnumberGroup: group index",
+                 group_index, 0, portal->no_groups))
+       return(-1);
+    gbp = pbuf_findGroup(portal, group_index);
+    if (gbp)
+       return(pbuf_findFnumber(gbp, field_name));
+    return(-1);
+}
+
+/* --------------------------------
+ *     PQfnameGroup - Return the field (attribute) name given
+ *                     the group index and field index. 
+ * --------------------------------
+ */
+char *
+PQfnameGroup(PortalBuffer *portal, int group_index, int field_number)
+{
+    GroupBuffer *gbp;
+    
+    if (!valid_pointer("PQfnameGroup: invalid portal pointer", portal) ||
+       !in_range("PQfnameGroup: group index",
+                 group_index, 0, portal->no_groups))
+       return((char *) NULL);
+    
+    if ((gbp = pbuf_findGroup(portal, group_index)) &&
+       in_range("PQfnameGroup: field number",
+                field_number, 0, gbp->no_fields))
+       return(pbuf_findFname(gbp, field_number));
+    return((char *) NULL);
+}
+
+/* --------------------------------
+ *     PQftypeGroup - Return the type of a field given
+ *                      the group index and field index
+ * --------------------------------
+ */
+int
+PQftypeGroup(PortalBuffer *portal, int group_index, int field_number)
+{
+    GroupBuffer *gbp;
+    
+    if (!valid_pointer("PQftypeGroup: invalid portal pointer", portal) ||
+       !in_range("PQftypeGroup: group index",
+                 group_index, 0, portal->no_groups))
+       return(-1);
+    
+    if ((gbp = pbuf_findGroup(portal, group_index)) &&
+       in_range("PQftypeGroup: field number", field_number, 0, gbp->no_fields))
+       return(gbp->types[field_number].adtid);
+    return(-1);
+}
+
+/* --------------------------------
+ *     PQfsizeGroup - Return the size of a field given
+ *                     the group index and field index
+ * --------------------------------
+ */
+int
+PQfsizeGroup(PortalBuffer *portal, int group_index, int field_number)
+{
+    GroupBuffer *gbp;
+    
+    if (!valid_pointer("PQfsizeGroup: invalid portal pointer", portal) ||
+       !in_range("PQfsizeGroup: tuple index",
+                 group_index, 0, portal->no_groups))
+       return(-1);
+    
+    if ((gbp = pbuf_findGroup(portal, group_index)) &&
+       in_range("PQfsizeGroup: field number", field_number, 0, gbp->no_fields))
+       return(gbp->types[field_number].adtsize);
+    return(-1);
+}
+
+
+/* --------------------------------
+ *     PQgroup - Return the tuple group that a particular tuple is in
+ * --------------------------------
+ */
+GroupBuffer *
+PQgroup(PortalBuffer *portal, int tuple_index)
+{
+    GroupBuffer *gbp;
+    int tuple_count = 0;
+    
+    if (!valid_pointer("PQgroup: invalid portal pointer", portal) ||
+       !in_range("PQgroup: tuple index",
+                 tuple_index, 0, portal->no_tuples))
+       return((GroupBuffer *) NULL);
+    
+    for (gbp = portal->groups;
+        gbp && tuple_index >= (tuple_count += gbp->no_tuples);
+        gbp = gbp->next)
+       ;
+    if (!in_range("PQgroup: tuple not found: tuple index",
+                 tuple_index, 0, tuple_count))
+       return((GroupBuffer *) NULL);
+    return(gbp);
+}
+
+/* --------------------------------
+ *     PQgetgroup - Return the index of the group that a
+ *                  particular tuple is in
+ * --------------------------------
+ */
+int
+PQgetgroup(PortalBuffer *portal, int tuple_index)
+{
+    GroupBuffer *gbp;
+    int tuple_count = 0, group_count = 0;
+    
+    if (!valid_pointer("PQgetgroup: invalid portal pointer", portal) ||
+       !in_range("PQgetgroup: tuple index",
+                 tuple_index, 0, portal->no_tuples))
+       return(-1);
+    
+    for (gbp = portal->groups;
+        gbp && tuple_index >= (tuple_count += gbp->no_tuples);
+        gbp = gbp->next)
+       ++group_count;
+    if (!gbp || !in_range("PQgetgroup: tuple not found: tuple index",
+                         tuple_index, 0, tuple_count))
+       return(-1);
+    return(group_count);
+}
+
+/* --------------------------------
+ *     PQnfields - Return the number of fields in a tuple
+ * --------------------------------
+ */
+int
+PQnfields(PortalBuffer *portal, int    tuple_index)
+{
+    GroupBuffer *gbp;
+    
+    if (!valid_pointer("PQnfields: invalid portal pointer", portal) ||
+       !in_range("PQnfields: tuple index",
+                 tuple_index, 0, portal->no_tuples))
+       return(-1);
+    gbp = PQgroup(portal, tuple_index);
+    if (gbp)
+       return(gbp->no_fields);
+    return(-1);
+}
+
+/* --------------------------------
+ *     PQfnumber - Return the field index of a given
+ *                 field name within a tuple. 
+ * --------------------------------
+ */
+int
+PQfnumber(PortalBuffer *portal, int tuple_index, char *field_name)
+{
+    GroupBuffer *gbp;
+    
+    if (!valid_pointer("PQfnumber: invalid portal pointer", portal) ||
+       !valid_pointer("PQfnumber: invalid field name pointer", field_name) ||
+       !in_range("PQfnumber: tuple index",
+                 tuple_index, 0, portal->no_tuples))
+       return(-1);
+    gbp = PQgroup(portal, tuple_index);
+    if (gbp)
+       return(pbuf_findFnumber(gbp, field_name));
+    return(-1);
+}
+
+/* --------------------------------
+ *     PQfname - Return the name of a field
+ * --------------------------------
+ */
+char *
+PQfname(PortalBuffer *portal, int      tuple_index, int field_number)
+{
+    GroupBuffer *gbp;
+    
+    if (!valid_pointer("PQfname: invalid portal pointer", portal) ||
+       !in_range("PQfname: tuple index",
+                 tuple_index, 0, portal->no_tuples))
+       return((char *) NULL);
+    
+    if ((gbp = PQgroup(portal, tuple_index)) &&
+       in_range("PQfname: field number",
+                field_number, 0, gbp->no_fields))
+       return(pbuf_findFname(gbp, field_number));
+    return((char *) NULL);
+}
+
+/* --------------------------------
+ *     PQftype - Return the type of a field
+ * --------------------------------
+ */
+int
+PQftype(PortalBuffer *portal, int tuple_index, int field_number)
+{
+    GroupBuffer *gbp;
+    
+    if (!valid_pointer("PQftype: invalid portal pointer", portal) ||
+       !in_range("PQfname: tuple index",
+                 tuple_index, 0, portal->no_tuples))
+       return(-1);
+    
+    if ((gbp = PQgroup(portal, tuple_index)) &&
+       in_range("PQftype: field number", field_number, 0, gbp->no_fields))
+       return(gbp->types[field_number].adtid);
+    return(-1);
+}
+
+/* --------------------------------
+ *     PQfsize - Return the size of a field
+ * --------------------------------
+ */
+int
+PQfsize(PortalBuffer *portal, int tuple_index, int field_number)
+{
+    GroupBuffer *gbp;
+    
+    if (!valid_pointer("PQfsize: invalid portal pointer", portal) ||
+       !in_range("PQfsize: tuple index",
+                 tuple_index, 0, portal->no_tuples))
+       return(-1);
+    
+    if ((gbp = PQgroup(portal, tuple_index)) &&
+       in_range("PQfsize: field number", field_number, 0, gbp->no_fields))
+       return(gbp->types[field_number].adtsize);
+    return(-1);
+}
+  
+
+
+/* --------------------------------
+ *     PQsametype - Return 1 if the two tuples have the same type
+ *                     (in the same group)
+ * --------------------------------
+ */
+int
+PQsametype(PortalBuffer *portal, int tuple_index1, int tuple_index2)
+{
+    GroupBuffer *gbp1, *gbp2;
+    
+    if (!valid_pointer("PQsametype: invalid portal pointer", portal) ||
+       !in_range("PQsametype: tuple index 1",
+                 tuple_index1, 0, portal->no_tuples) ||
+       !in_range("PQsametype: tuple index 2",
+                 tuple_index2, 0, portal->no_tuples))
+       return(-1);
+    
+    gbp1 = PQgroup(portal, tuple_index1);
+    gbp2 = PQgroup(portal, tuple_index2);
+    if (gbp1 && gbp2)
+       return(gbp1 == gbp2);
+    return(-1);
+}
+
+static TupleBlock *
+PQGetTupleBlock(PortalBuffer *portal,
+               int tuple_index,
+               int *tuple_offset)
+{
+    GroupBuffer *gbp;
+    TupleBlock  *tbp;
+    int tuple_count = 0;
+    
+    if (!valid_pointer("PQGetTupleBlock: invalid portal pointer", portal) ||
+       !valid_pointer("PQGetTupleBlock: invalid offset pointer",
+                      tuple_offset) ||
+       !in_range("PQGetTupleBlock: tuple index",
+                 tuple_index, 0, portal->no_tuples))
+       return((TupleBlock *) NULL);
+    
+    for (gbp = portal->groups;
+        gbp && tuple_index >= (tuple_count += gbp->no_tuples);
+        gbp = gbp->next)
+       ;
+    if (!gbp ||
+       !in_range("PQGetTupleBlock: tuple not found: tuple index",
+                 tuple_index, 0, tuple_count))
+       return((TupleBlock *) NULL);
+    tuple_count -= gbp->no_tuples;
+    for (tbp = gbp->tuples;
+        tbp && tuple_index >= (tuple_count += TupleBlockSize);
+        tbp = tbp->next)
+       ;
+    if (!tbp ||
+       !in_range("PQGetTupleBlock: tuple not found: tuple index",
+                 tuple_index, 0, tuple_count))
+       return((TupleBlock *) NULL);
+    tuple_count -= TupleBlockSize;
+    
+    *tuple_offset = tuple_index - tuple_count;
+    return(tbp);
+}
+
+/* --------------------------------
+ *     PQgetvalue - Return an attribute (field) value
+ * --------------------------------
+ */
+char *
+PQgetvalue(PortalBuffer *portal,
+          int tuple_index,
+          int field_number)
+{
+    TupleBlock *tbp;
+    int tuple_offset;
+
+    tbp = PQGetTupleBlock(portal, tuple_index, &tuple_offset);
+    if (tbp)
+       return(tbp->values[tuple_offset][field_number]);
+    return((char *) NULL);
+}
+
+/* --------------------------------
+ *     PQgetAttr - Return an attribute (field) value
+ *      this differs from PQgetvalue in that the value returned is
+ *      a copy.  The CALLER is responsible for free'ing the data returned.
+ * --------------------------------
+ */
+char *
+PQgetAttr(PortalBuffer *portal,
+         int tuple_index,
+         int field_number)
+{
+    TupleBlock *tbp;
+    int tuple_offset;
+    int len;
+    char* result = NULL;
+
+    tbp = PQGetTupleBlock(portal, tuple_index, &tuple_offset);
+    if (tbp) {
+      len = tbp->lengths[tuple_offset][field_number];
+      result = malloc(len + 1);
+      memcpy(result, 
+            tbp->values[tuple_offset][field_number],
+            len);
+      result[len] = '\0';
+    }
+    return result;
+}
+
+
+/* --------------------------------
+ *     PQgetlength - Return an attribute (field) length
+ * --------------------------------
+ */
+int
+PQgetlength(PortalBuffer *portal,
+           int tuple_index,
+           int field_number)
+{
+    TupleBlock *tbp;
+    int tuple_offset;
+
+    tbp = PQGetTupleBlock(portal, tuple_index, &tuple_offset);
+    if (tbp)
+       return(tbp->lengths[tuple_offset][field_number]);
+    return(-1);
+}
+
+/* ----------------
+ *     PQclear         - free storage claimed by named portal
+ * ----------------
+ */
+void
+PQclear(char *pname)
+{    
+    if (!valid_pointer("PQclear: invalid portal name pointer", pname))
+       return;
+    pbuf_close(pname);
+}
+
+/*
+ * async notification.
+ * This is going away with pending rewrite of comm. code...
+ */
+/* static SLList pqNotifyList;*/
+static Dllist *pqNotifyList = NULL;
+
+/* remove invalid notifies before returning */
+void
+PQcleanNotify()
+{
+  Dlelem *e, *next;
+  PQNotifyList *p;
+
+  e = DLGetHead(pqNotifyList);
+
+  while (e) {
+    next = DLGetSucc(e);
+    p = (PQNotifyList*)DLE_VAL(e);
+    if (p->valid == 0)  {
+      DLRemove(e);
+      DLFreeElem(e);
+      pfree(p);
+    }
+    e = next;
+  }
+}
+
+void
+PQnotifies_init()
+{
+    Dlelem *e;
+    PQNotifyList *p;
+    
+    if (pqNotifyList == NULL) {
+       pqNotifyList = DLNewList();
+    }
+    else {
+       /* clean all notifies */
+       for (e = DLGetHead(pqNotifyList); e != NULL; e = DLGetSucc(e)) {
+           p = (PQNotifyList*)DLE_VAL(e);
+           p->valid = 0;
+       }
+       PQcleanNotify();
+    }
+}
+
+PQNotifyList *
+PQnotifies()
+{
+    Dlelem *e;
+    PQcleanNotify();
+    e = DLGetHead(pqNotifyList);
+    return (e ? (PQNotifyList*)DLE_VAL(e) : NULL);
+}
+
+void
+PQremoveNotify(PQNotifyList *nPtr)
+{
+    nPtr->valid = 0;           /* remove later */
+}
+
+void
+PQappendNotify(char *relname, int pid)
+{
+    PQNotifyList *p;
+    
+    if (pqNotifyList == NULL) 
+       pqNotifyList = DLNewList();
+    
+    p = (PQNotifyList*)pbuf_alloc(sizeof(PQNotifyList));
+    strncpy(p->relname, relname, NAMEDATALEN);
+    p->be_pid = pid;
+    p->valid = 1;
+    DLAddTail(pqNotifyList, DLNewElem(p));
+}
diff --git a/src/backend/libpq/portalbuf.c b/src/backend/libpq/portalbuf.c
new file mode 100644 (file)
index 0000000..5630256
--- /dev/null
@@ -0,0 +1,511 @@
+/*-------------------------------------------------------------------------
+ *
+ * portalbuf.c--
+ *    portal buffer support routines for src/libpq/portal.c
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *     pbuf_alloc        - allocate memory for libpq routines
+ *     pbuf_free         - free memory for libpq routines
+ *     pbuf_addPortal    - Allocate a new portal buffer
+ *     pbuf_addGroup     - Add a new tuple group to the portal
+ *     pbuf_addTypes     - Allocate n type blocks
+ *     pbuf_addTuples    - Allocate a tuple block
+ *     pbuf_addTuple     - Allocate a tuple of n fields (attributes)
+ *     pbuf_addValues    - Allocate n bytes for a value
+ *     pbuf_addEntry     - Allocate a portal entry
+ *     pbuf_freeEntry    - Free a portal entry in the portal table
+ *     pbuf_freeTypes    - Free up the space used by a portal 
+ *     pbuf_freeTuples   - free space used by tuple block
+ *     pbuf_freeGroup    - free space used by group, types and tuples
+ *     pbuf_freePortal   - free space used by portal and portal's group
+ *     pbuf_getIndex     - Return the index of the portal entry
+ *     pbuf_setup        - Set up a portal for dumping data
+ *     pbuf_close        - Close a portal, remove it from the portal table
+ *     pbuf_findGroup    - Return group given the group_index
+ *     pbuf_findFnumber  - Return field index of a given field within a group
+ *     pbuf_findFname    - Find the field name given the field index
+ *     pbuf_checkFnumber - signal an error if field number is out of bounds
+ *
+ * NOTES
+ *     These functions may be used by both frontend routines which
+ *     communicate with a backend or by user-defined functions which
+ *     are compiled or dynamically loaded into a backend.
+ *
+ *     the portals[] array should be organized as a hash table for
+ *     quick portal-by-name lookup.
+ *
+ *     Do not confuse "PortalEntry" (or "PortalBuffer") with "Portal"
+ *     see utils/mmgr/portalmem.c for why. -cim 2/22/91
+ *
+ */
+#include <sys/types.h>
+#include "c.h"
+
+#include "libpq/libpq.h"               /* where the declarations go */
+#include "utils/exc.h"
+#include "utils/palloc.h"
+
+PortalEntry** portals = (PortalEntry**) NULL;
+size_t portals_array_size = 0;
+
+/* portals array memory is malloc'd instead of using MemoryContexts */
+/* since it will be used by both front and backend programs*/
+/*  GlobalMemory portals_mmcxt = (GlobalMemory) NULL;  */
+
+/* ------------------------------- 
+ * portals_realloc --
+ *    grow the size of the portals array by size
+ *
+ *    also ensures that elements are initially NULL 
+ */
+
+static void
+portals_realloc(size_t size)
+{
+    size_t oldsize;
+    int i;
+    PortalEntry** newp;
+    
+    oldsize = portals_array_size;
+    
+    portals_array_size += size;
+    if (portals)
+       newp= (PortalEntry**)realloc(portals,
+                                    portals_array_size*sizeof(PortalEntry*));
+    else
+       newp= (PortalEntry**)malloc(portals_array_size*sizeof(PortalEntry*));
+    
+    if (newp)
+       portals = newp;
+    else
+       libpq_raise(&PortalError,
+                   form("Cannot alloc more memory in portals_realloc"));
+    
+    for (i=oldsize;i<portals_array_size;i++)
+       portals[i]=(PortalEntry*)NULL;
+    
+}
+
+/* --------------------------------
+ *     pbuf_alloc - allocate memory for portal buffers
+ *
+ *     remember: palloc() in the backend uses the postgres MemoryContext
+ *     library and palloc() in the frontend (fe-pqstubs.c) calls malloc().
+ * --------------------------------
+ */
+caddr_t
+pbuf_alloc(size_t size)
+{
+    caddr_t    addr;
+    
+    if (size <= 0)
+       libpq_raise(&MemoryError, form("Invalid argument to pbuf_alloc()."));
+    
+    addr = (caddr_t) palloc(size);
+    if (addr == (caddr_t) NULL)
+       libpq_raise(&MemoryError, form("Cannot Allocate space."));
+    
+    return (addr);
+}
+
+/* --------------------------------
+ *     pbuf_free - free memory for portal buffers
+ *
+ *     remember: pfree() in the backend uses the postgres MemoryContext
+ *     library and pfree() in the frontend (fe-pqstubs.c) calls free().
+ * --------------------------------
+ */
+void
+pbuf_free(caddr_t pointer)
+{
+    if (pointer)
+       pfree(pointer);
+    else
+       libpq_raise(&MemoryError, form("Tried to free NULL memory pointer"));
+    
+}
+
+/* --------------------------------
+ *     pbuf_addPortal - Allocate a new portal buffer
+ * --------------------------------
+ */
+PortalBuffer *
+pbuf_addPortal()
+{
+    PortalBuffer *portal;
+    
+    portal = (PortalBuffer *)
+       pbuf_alloc(sizeof (PortalBuffer));
+    
+    portal->rule_p = 0;
+    portal->no_tuples = 0;
+    portal->no_groups = 0;
+    portal->groups = NULL;
+    
+    return (portal);
+}
+
+/* --------------------------------
+ *     pbuf_addGroup - Add a new tuple group to the portal
+ * --------------------------------
+ */
+GroupBuffer *
+pbuf_addGroup(PortalBuffer *portal)
+{
+    GroupBuffer *group, *group1;
+    
+    group = (GroupBuffer *)
+       pbuf_alloc(sizeof (GroupBuffer));
+    
+    /* Initialize the new group buffer. */
+    group->no_tuples  = 0;
+    group->no_fields = 0;
+    group->types = NULL;
+    group->tuples = NULL;
+    group->next = NULL;
+    
+    if ((group1 = portal->groups) == NULL)
+       portal->groups = group;
+    else {
+       while (group1->next != NULL) 
+           group1 = group1->next;
+       group1->next = group;
+    }
+    
+    return (group);
+}
+
+/* --------------------------------
+ *     pbuf_addTypes - Allocate n type blocks
+ * --------------------------------
+ */
+TypeBlock *
+pbuf_addTypes(int n)
+{
+    TypeBlock *types;
+    
+    types = (TypeBlock *)
+       pbuf_alloc(n * sizeof (TypeBlock));
+    
+    return (types);
+}
+
+/* --------------------------------
+ *     pbuf_addTuples - Allocate a tuple block
+ * --------------------------------
+ */
+TupleBlock *
+pbuf_addTuples()
+{
+    TupleBlock *tuples;
+    
+    tuples = (TupleBlock *)
+       pbuf_alloc(sizeof (TupleBlock));
+    
+    tuples->next = NULL;
+    tuples->tuple_index = 0;
+    
+    return (tuples);
+}
+
+/* --------------------------------
+ *     pbuf_addTuple - Allocate a tuple of n fields (attributes)
+ * --------------------------------
+ */
+char **
+pbuf_addTuple(int n)
+{
+    return (char **)
+       pbuf_alloc(n * sizeof (char *));
+}
+
+/* --------------------------------
+ *     pbuf_addTupleValueLengths - Allocate a tuple of n lengths (attributes)
+ * --------------------------------
+ */
+int *
+pbuf_addTupleValueLengths(int n)
+{
+    return (int *)
+       pbuf_alloc(n * sizeof(int));
+}
+
+/* --------------------------------
+ *     pbuf_addValues - Allocate n bytes for a value
+ * --------------------------------
+ */
+char *
+pbuf_addValues(int n)
+{
+    return
+       pbuf_alloc(n);
+}
+
+/* --------------------------------
+ *     pbuf_addEntry - Allocate a portal entry
+ * --------------------------------
+ */
+PortalEntry *pbuf_addEntry()
+{
+    return (PortalEntry *)
+       pbuf_alloc (sizeof (PortalEntry));
+}
+
+/* --------------------------------
+ *     pbuf_freeEntry - Free a portal entry in the portal table
+ *     the portal is freed separately.
+ * --------------------------------
+ */
+void
+pbuf_freeEntry(int i)
+{
+    if (portals)
+       {
+           pbuf_free ((caddr_t)portals[i]);
+           portals[i] = NULL;
+       }
+}
+
+
+/* --------------------------------
+ *     pbuf_freeTypes - Free up the space used by a portal 
+ * --------------------------------
+ */
+void
+pbuf_freeTypes(TypeBlock *types)
+{
+    pbuf_free((caddr_t)types);
+}
+
+/* --------------------------------
+ *     pbuf_freeTuples - free space used by tuple block
+ * --------------------------------
+ */
+void
+pbuf_freeTuples(TupleBlock *tuples,
+               int no_tuples,
+               int no_fields)
+{
+    int i, j;
+    
+    if (no_tuples > TupleBlockSize) {
+       pbuf_freeTuples (tuples->next, no_tuples - TupleBlockSize, no_fields);
+       no_tuples = TupleBlockSize;
+    }
+    
+    /* For each tuple, free all its attribute values. */
+    for (i = 0; i < no_tuples; i++) {
+       for (j = 0; j < no_fields; j++)
+           if (tuples->values[i][j] != NULL)
+               pbuf_free((caddr_t)tuples->values[i][j]);
+       if (tuples->lengths[i])
+           pbuf_free((caddr_t)tuples->lengths[i]);
+       if (tuples->values[i])
+           pbuf_free((caddr_t)tuples->values[i]);
+    }
+    
+    pbuf_free((caddr_t)tuples);
+}
+
+/* --------------------------------
+ *     pbuf_freeGroup - free space used by group, types and tuples
+ * --------------------------------
+ */
+void
+pbuf_freeGroup(GroupBuffer *group)
+{
+    if (group->next != NULL)
+       pbuf_freeGroup(group->next);
+    
+    if (group->types != NULL)
+       pbuf_freeTypes(group->types);
+    
+    if (group->tuples != NULL)
+       pbuf_freeTuples(group->tuples, group->no_tuples,group->no_fields);
+    
+    pbuf_free((caddr_t)group);
+}
+
+/* --------------------------------
+ *     pbuf_freePortal - free space used by portal and portal's group
+ * --------------------------------
+ */
+void
+pbuf_freePortal(PortalBuffer *portal)
+{
+    if (portal->groups != NULL)
+       pbuf_freeGroup(portal->groups);
+    
+    pbuf_free((caddr_t)portal);
+}
+
+/* --------------------------------
+ *     pbuf_getIndex - Return the index of the portal entry
+ *     note: portals[] maps portal names to portal buffers.
+ * --------------------------------
+ */
+int
+pbuf_getIndex(char *pname)
+{
+    int i;
+    
+    if (portals) {
+       for (i = 0; i < portals_array_size; i++) 
+           if (portals[i] != NULL &&
+               strncmp(portals[i]->name, pname, PortalNameLength) == 0)
+               return i;
+    }
+    
+    return (-1);
+}
+
+/* --------------------------------
+ *     pbuf_setportalname - assign a user given name to a portal
+ * --------------------------------
+ */
+void
+pbuf_setportalinfo(PortalEntry *entry, char *pname)
+{
+    if (entry)
+       strncpy(entry->name, pname, PortalNameLength-1);
+    entry->name[PortalNameLength-1] = '\0';
+}
+
+/* --------------------------------
+ *     pbuf_setup - Set up a portal for dumping data
+ * --------------------------------
+ */
+PortalEntry *
+pbuf_setup(char *pname)
+{
+    int i;
+    
+    if (!portals) /* the portals array has not been allocated yet */
+       {
+           /* allocate portals[] array here */
+           portals_realloc(PORTALS_INITIAL_SIZE);
+       }
+    
+    /* If a portal with the same name already exists, close it. */
+    /* else look for an empty entry in the portal table. */
+    if ((i = pbuf_getIndex(pname)) != -1) 
+       pbuf_freePortal(portals[i]->portal);
+    else {
+       for (i = 0; i < portals_array_size; i++)
+           if (portals[i] == NULL)
+               break;
+       
+       /* If the portal table is full, enlarge it */
+       if (i >= portals_array_size) 
+           portals_realloc(PORTALS_GROW_BY);
+       
+       portals[i] = pbuf_addEntry();
+       strncpy(portals[i]->name, pname, PortalNameLength);
+    }
+    portals[i]->portal = pbuf_addPortal();
+    portals[i]->portalcxt = NULL;
+    portals[i]->result = NULL;
+    
+    return portals[i];
+}
+
+/* --------------------------------
+ *     pbuf_close - Close a portal, remove it from the portal table
+ *                     and free up the space
+ * --------------------------------
+ */
+void
+pbuf_close(char *pname)
+{
+    int i;
+    
+    if ((i = pbuf_getIndex(pname)) == -1) 
+       libpq_raise(&PortalError, form("Portal %s does not exist.", pname));
+    
+    pbuf_freePortal(portals[i]->portal);
+    pbuf_freeEntry(i);
+}
+
+/* --------------------------------
+ *     pbuf_findGroup - Return the group given the group_index
+ * --------------------------------
+ */
+GroupBuffer *
+pbuf_findGroup(PortalBuffer *portal,
+              int group_index)
+{
+    GroupBuffer *group;
+    
+    group = portal->groups;
+    while (group_index > 0 && group != NULL) {
+       group = group->next;
+       group_index--;
+    }
+    
+    if (group == NULL)
+       libpq_raise(&PortalError, 
+                   form("Group index %d out of bound.", group_index));
+    
+    return (group);
+}
+
+/* --------------------------------
+ * pbuf_findFnumber - Return the field index of a given field within a group
+ * --------------------------------
+ */
+int
+pbuf_findFnumber(GroupBuffer *group,
+                char *field_name)
+{      
+    TypeBlock *types;
+    int i;
+    
+    types = group->types;
+    
+    for (i = 0; i < group->no_fields; i++) 
+       if (strncmp(types[i].name, field_name, NAMEDATALEN) == 0)
+           return (i);
+    
+    libpq_raise(&PortalError, 
+               form("Field-name %s does not exist.", field_name));
+     /* not reached, here to make compiler happy */
+     return 0;
+
+}
+
+/* --------------------------------
+ *     pbuf_checkFnumber - signal an error if field number is out of bounds
+ * --------------------------------
+ */
+void
+pbuf_checkFnumber(GroupBuffer *group,
+                 int field_number)
+{
+    if (field_number < 0 || field_number >= group->no_fields)
+       libpq_raise(&PortalError, 
+                   form("Field number %d out of bound.", field_number));
+}
+
+/* --------------------------------
+ *     pbuf_findFname - Find the field name given the field index
+ * --------------------------------
+ */
+char *
+pbuf_findFname(GroupBuffer *group,
+              int field_number)
+{
+    pbuf_checkFnumber(group, field_number);
+    return
+       (group->types[field_number]).name;
+}
+
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
new file mode 100644 (file)
index 0000000..6a1e2f3
--- /dev/null
@@ -0,0 +1,724 @@
+/*-------------------------------------------------------------------------
+ *
+ * pqcomm.c--
+ *    Communication functions between the Frontend and the Backend
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *     pq_gettty       - return the name of the tty in the given buffer
+ *     pq_getport      - return the PGPORT setting
+ *     pq_close        - close input / output connections
+ *     pq_flush        - flush pending output
+ *     pq_getstr       - get a null terminated string from connection
+ *     pq_getnchar     - get n characters from connection
+ *     pq_getint       - get an integer from connection
+ *     pq_putstr       - send a null terminated string to connection
+ *     pq_putnchar     - send n characters to connection
+ *     pq_putint       - send an integer to connection
+ *     pq_getinaddr    - initialize address from host and port number
+ *     pq_getinserv    - initialize address from host and service name
+ *     pq_connect      - create remote input / output connection
+ *     pq_accept       - accept remote input / output connection
+ *      pq_async_notify - receive notification from backend.
+ *
+ * NOTES
+ *     These functions are used by both frontend applications and
+ *     the postgres backend.
+ *
+ */
+#include "libpq/pqsignal.h"    /* substitute for <signal.h> */
+#include <stdio.h>
+#include <string.h>
+#ifndef WIN32
+#include <unistd.h>            /* for ttyname() */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#else
+#include <winsock.h>
+#endif /* WIN32 */
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef PORTNAME_linux
+#ifndef SOMAXCONN
+#define SOMAXCONN 5            /* from Linux listen(2) man page */
+#endif /* SOMAXCONN */
+#endif /* PORTNAME_linux */
+
+#include "c.h"
+#include "libpq/auth.h"
+#include "libpq/libpq.h"       /* where the declarations go */
+#include "libpq/pqcomm.h"
+#include "utils/elog.h"
+
+/* ----------------
+ *     declarations
+ * ----------------
+ */
+FILE *Pfout, *Pfin;
+FILE *Pfdebug;   /* debugging libpq */
+int PQAsyncNotifyWaiting;      /* for async. notification */
+
+/* --------------------------------
+ *     pq_init - open portal file descriptors
+ * --------------------------------
+ */
+void
+pq_init(int fd)
+{
+#ifdef WIN32
+    int in, out;
+
+    in = _open_osfhandle(fd, _O_RDONLY);
+    out = _open_osfhandle(fd, _O_APPEND);
+    Pfin = fdopen(in, "rb");
+    Pfout = fdopen(out, "wb");
+#else
+    Pfin = fdopen(fd, "r");
+    Pfout = fdopen(dup(fd), "w");
+#endif /* WIN32 */
+    if (!Pfin || !Pfout)
+       elog(FATAL, "pq_init: Couldn't initialize socket connection");
+    PQnotifies_init();
+    if (getenv("LIBPQ_DEBUG")) {
+       Pfdebug = stderr;
+    }else {
+       Pfdebug = NULL;
+    }
+}
+
+/* -------------------------
+ *   pq_getc(File* fin)
+ *  
+ *   get a character from the input file,
+ *
+ *   if Pfdebug is set, also echo the character fetched into Pfdebug
+ *
+ *   used for debugging libpq
+ */
+static int
+pq_getc(FILE* fin)
+{
+  int c;
+
+  c = getc(fin);
+  if (Pfdebug && c != EOF)
+    putc(c,Pfdebug);
+  return c;
+}
+
+/* --------------------------------
+ *     pq_gettty - return the name of the tty in the given buffer
+ * --------------------------------
+ */
+void
+pq_gettty(char *tp)
+{      
+    (void) strncpy(tp, ttyname(0), 19);
+}
+
+/* --------------------------------
+ *     pq_getport - return the PGPORT setting
+ * --------------------------------
+ */
+int
+pq_getport()
+{
+    char *envport = getenv("PGPORT");
+    
+    if (envport)
+       return(atoi(envport));
+    return(atoi(POSTPORT));
+}
+
+/* --------------------------------
+ *     pq_close - close input / output connections
+ * --------------------------------
+ */
+void
+pq_close()
+{
+    if (Pfin) {
+       fclose(Pfin);
+       Pfin = NULL;
+    }
+    if (Pfout) {
+       fclose(Pfout);
+       Pfout = NULL;
+    }
+    PQAsyncNotifyWaiting = 0;
+    PQnotifies_init();
+    pq_unregoob();
+}
+
+/* --------------------------------
+ *     pq_flush - flush pending output
+ * --------------------------------
+ */
+void
+pq_flush()
+{
+    if (Pfout)
+       fflush(Pfout);
+}
+
+/* --------------------------------
+ *     pq_getstr - get a null terminated string from connection
+ * --------------------------------
+ */
+int
+pq_getstr(char *s, int maxlen)
+{
+    int        c;
+    
+    if (Pfin == (FILE *) NULL) {
+/*     elog(DEBUG, "Input descriptor is null"); */
+       return(EOF);
+    }
+    
+    while (maxlen-- && (c = pq_getc(Pfin)) != EOF && c)
+       *s++ = c;
+    *s = '\0';
+    
+    /* -----------------
+     *     If EOF reached let caller know.
+     *     (This will only happen if we hit EOF before the string
+     *     delimiter is reached.)
+     * -----------------
+     */
+    if (c == EOF)
+       return(EOF);
+    return(!EOF);
+}
+
+/*
+ * USER FUNCTION - gets a newline-terminated string from the backend.
+ * 
+ * Chiefly here so that applications can use "COPY <rel> to stdout"
+ * and read the output string.  Returns a null-terminated string in s.
+ *
+ * PQgetline reads up to maxlen-1 characters (like fgets(3)) but strips
+ * the terminating \n (like gets(3)).
+ *
+ * RETURNS:
+ *     EOF if it is detected or invalid arguments are given
+ *     0 if EOL is reached (i.e., \n has been read)
+ *             (this is required for backward-compatibility -- this
+ *              routine used to always return EOF or 0, assuming that
+ *              the line ended within maxlen bytes.)
+ *     1 in other cases
+ */
+int
+PQgetline(char *s, int maxlen)
+{
+    int c = '\0';
+    
+    if (!Pfin || !s || maxlen <= 1)
+       return(EOF);
+    
+    for (; maxlen > 1 && (c = pq_getc(Pfin)) != '\n' && c != EOF; --maxlen) {
+       *s++ = c;
+    }
+    *s = '\0';
+    
+    if (c == EOF) {
+       return(EOF);            /* error -- reached EOF before \n */
+    } else if (c == '\n') {
+       return(0);              /* done with this line */
+    }
+    return(1);                 /* returning a full buffer */
+}
+
+/*
+ * USER FUNCTION - sends a string to the backend.
+ * 
+ * Chiefly here so that applications can use "COPY <rel> from stdin".
+ *
+ * RETURNS:
+ *     0 in all cases.
+ */
+int
+PQputline(char *s)
+{
+    if (Pfout) {
+       (void) fputs(s, Pfout);
+       fflush(Pfout);
+    }
+    return(0);
+}
+
+/* --------------------------------
+ *     pq_getnchar - get n characters from connection
+ * --------------------------------
+ */
+int
+pq_getnchar(char *s, int off, int maxlen)
+{
+    int        c;
+    
+    if (Pfin == (FILE *) NULL) {
+/*     elog(DEBUG, "Input descriptor is null"); */
+       return(EOF);
+    }
+    
+    s += off;
+    while (maxlen-- && (c = pq_getc(Pfin)) != EOF)
+       *s++ = c;
+    
+    /* -----------------
+     *     If EOF reached let caller know
+     * -----------------
+     */
+    if (c == EOF)
+       return(EOF);
+    return(!EOF);
+}
+
+/* --------------------------------
+ *     pq_getint - get an integer from connection
+ *   we receive an integer a byte at a type and reconstruct it so that
+ *   machines with different ENDIAN representations can talk to each
+ *   other
+ * --------------------------------
+ */
+int
+pq_getint(int b)
+{
+    int        n, c, p;
+    
+    if (Pfin == (FILE *) NULL) {
+/*     elog(DEBUG, "pq_getint: Input descriptor is null"); */
+       return(EOF);
+    }
+    
+    n = p = 0;
+    while (b-- && (c = pq_getc(Pfin)) != EOF && p < 32) {
+       n |= (c & 0xff) << p;
+       p += 8;
+    }
+    
+    return(n);
+}
+
+/* --------------------------------
+ *     pq_putstr - send a null terminated string to connection
+ * --------------------------------
+ */
+void
+pq_putstr(char *s)
+{
+    int status;
+    
+    if (Pfout) {
+       status = fputs(s, Pfout);
+       if (status == EOF) {
+           (void) sprintf(PQerrormsg,
+                          "FATAL: pq_putstr: fputs() failed: errno=%d\n",
+                          errno);
+           fputs(PQerrormsg, stderr);
+           pqdebug("%s", PQerrormsg);
+       }
+       status = fputc('\0', Pfout);
+       if (status == EOF) {
+           (void) sprintf(PQerrormsg,
+                          "FATAL: pq_putstr: fputc() failed: errno=%d\n",
+                          errno);
+           fputs(PQerrormsg, stderr);
+           pqdebug("%s", PQerrormsg);
+       }
+    }
+}
+
+/* --------------------------------
+ *     pq_putnchar - send n characters to connection
+ * --------------------------------
+ */
+void
+pq_putnchar(char *s, int n)
+{
+    int status;
+    
+    if (Pfout) {
+       while (n--) {
+           status = fputc(*s++, Pfout);
+           if (status == EOF) {
+               (void) sprintf(PQerrormsg,
+                              "FATAL: pq_putnchar: fputc() failed: errno=%d\n",
+                              errno);
+               fputs(PQerrormsg, stderr);
+               pqdebug("%s", PQerrormsg);
+           }
+       }
+    }
+}
+
+/* --------------------------------
+ *     pq_putint - send an integer to connection
+ *   we chop an integer into bytes and send individual bytes
+ *   machines with different ENDIAN representations can still talk to each
+ *   other
+ * --------------------------------
+ */
+void
+pq_putint(int i, int b)
+{
+    int status;
+    
+    if (b > 4)
+       b = 4;
+    
+    if (Pfout) {
+       while (b--) {
+           status = fputc(i & 0xff, Pfout);
+           i >>= 8;
+           if (status == EOF) {
+               (void) sprintf(PQerrormsg,
+                              "FATAL: pq_putint: fputc() failed: errno=%d\n",
+                              errno);
+               fputs(PQerrormsg, stderr);
+               pqdebug("%s", PQerrormsg);
+           }
+       }
+    }
+}
+
+/* ---
+ *     pq_sendoob - send a string over the out-of-band channel
+ *     pq_recvoob - receive a string over the oob channel
+ *  NB: Fortunately, the out-of-band channel doesn't conflict with
+ *      buffered I/O because it is separate from regular com. channel.
+ * ---
+ */
+int
+pq_sendoob(char *msg, int len)
+{
+    int fd = fileno(Pfout);
+    
+    return(send(fd,msg,len,MSG_OOB));
+}
+
+int
+pq_recvoob(char *msgPtr, int *lenPtr)
+{
+    int fd = fileno(Pfout);
+    int len = 0;
+    
+    len = recv(fd,msgPtr+len,*lenPtr,MSG_OOB);
+    *lenPtr = len;
+    return(len);
+}
+
+/* --------------------------------
+ *     pq_getinaddr - initialize address from host and port number
+ * --------------------------------
+ */
+int
+pq_getinaddr(struct sockaddr_in *sin,
+            char *host,
+            int port)
+{
+    struct hostent     *hs;
+    
+    memset((char *) sin, 0, sizeof(*sin));
+    
+    if (host) {
+       if (*host >= '0' && *host <= '9')
+           sin->sin_addr.s_addr = inet_addr(host);
+       else {
+           if (!(hs = gethostbyname(host))) {
+               perror(host);
+               return(1);
+           }
+           if (hs->h_addrtype != AF_INET) {
+               (void) sprintf(PQerrormsg,
+                              "FATAL: pq_getinaddr: %s not on Internet\n",
+                              host);
+               fputs(PQerrormsg, stderr);
+               pqdebug("%s", PQerrormsg);
+               return(1);
+           }
+           memmove((char *) &sin->sin_addr,
+                   hs->h_addr,
+                   hs->h_length);
+       }
+    }
+    sin->sin_family = AF_INET;
+    sin->sin_port = htons(port);
+    return(0);
+}
+
+/* --------------------------------
+ *     pq_getinserv - initialize address from host and servive name
+ * --------------------------------
+ */
+int
+pq_getinserv(struct sockaddr_in *sin, char *host, char *serv)
+{
+    struct servent *ss;
+    
+    if (*serv >= '0' && *serv <= '9')
+       return(pq_getinaddr(sin, host, atoi(serv)));
+    if (!(ss = getservbyname(serv, NULL))) {
+       (void) sprintf(PQerrormsg,
+                      "FATAL: pq_getinserv: unknown service: %s\n",
+                      serv);
+       fputs(PQerrormsg, stderr);
+       pqdebug("%s", PQerrormsg);
+       return(1);
+    }
+    return(pq_getinaddr(sin, host, ntohs(ss->s_port)));
+}
+
+/*
+ * register an out-of-band listener proc--at most one allowed.
+ * This is used for receiving async. notification from the backend.
+ */
+void
+pq_regoob(void (*fptr)())
+{
+#ifdef WIN32
+    /* Who knows what to do here? */
+    return;
+#else
+    int fd = fileno(Pfout);
+#ifdef PORTNAME_hpux
+    ioctl(fd, FIOSSAIOOWN, getpid());
+#else /* PORTNAME_hpux */
+    fcntl(fd, F_SETOWN, getpid());
+#endif /* PORTNAME_hpux */
+    (void) signal(SIGURG,fptr);
+#endif /* WIN32 */    
+}
+
+void
+pq_unregoob()
+{
+#ifndef WIN32
+    signal(SIGURG,SIG_DFL);
+#endif /* WIN32 */    
+}
+
+
+void
+pq_async_notify()
+{
+    char msg[20];
+    /*    int len = sizeof(msg);*/
+    int len = 20;
+    
+    if (pq_recvoob(msg,&len) >= 0) {
+       /* debugging */
+       printf("received notification: %s\n",msg);
+       PQAsyncNotifyWaiting = 1;
+       /*      PQappendNotify(msg+1);*/
+    } else {
+       extern int errno;
+       printf("SIGURG but no data: len = %d, err=%d\n",len,errno);
+    }
+}
+
+/*
+ * Streams -- wrapper around Unix socket system calls
+ *
+ *
+ *     Stream functions are used for vanilla TCP connection protocol.
+ */
+
+/*
+ * StreamServerPort -- open a sock stream "listening" port.
+ *
+ * This initializes the Postmaster's connection
+ *     accepting port.  
+ *
+ * ASSUME: that this doesn't need to be non-blocking because
+ *     the Postmaster uses select() to tell when the socket
+ *     is ready.
+ *
+ * RETURNS: STATUS_OK or STATUS_ERROR
+ */
+int
+StreamServerPort(char *hostName, short portName, int *fdP)
+{
+    struct sockaddr_in sin;
+    int                        fd;
+    
+#ifdef WIN32
+    /* This is necessary to make it possible for a backend to use
+    ** stdio to read from the socket.
+    */
+    int optionvalue = SO_SYNCHRONOUS_NONALERT;
+
+    setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&optionvalue,
+              sizeof(optionvalue));
+#endif /* WIN32 */
+
+    if (! hostName)
+       hostName = "localhost";
+    
+    memset((char *)&sin, 0, sizeof sin);
+    
+    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+       (void) sprintf(PQerrormsg,
+                      "FATAL: StreamServerPort: socket() failed: errno=%d\n",
+                      errno);
+       fputs(PQerrormsg, stderr);
+       pqdebug("%s", PQerrormsg);
+       return(STATUS_ERROR);
+    }
+    
+    sin.sin_family = AF_INET;
+    sin.sin_port = htons(portName);
+    
+    if (bind(fd, (struct sockaddr *)&sin, sizeof sin) < 0) {
+       (void) sprintf(PQerrormsg,
+                      "FATAL: StreamServerPort: bind() failed: errno=%d\n",
+                      errno);
+       pqdebug("%s", PQerrormsg);
+       (void) strcat(PQerrormsg, "\tIs another postmaster already running on that port?\n");
+       (void) strcat(PQerrormsg, "\tIf not, wait a few seconds and retry.\n");
+       fputs(PQerrormsg, stderr);
+       return(STATUS_ERROR);
+    }
+    
+    listen(fd, SOMAXCONN);
+    
+    /* MS: I took this code from Dillon's version.  It makes the 
+     * listening port non-blocking.  That is not necessary (and
+     * may tickle kernel bugs).
+     
+     (void) fcntl(fd, F_SETFD, 1);
+     (void) fcntl(fd, F_SETFL, FNDELAY);
+     */
+    
+    *fdP = fd;
+    return(STATUS_OK);
+}
+
+/*
+ * StreamConnection -- create a new connection with client using
+ *     server port.
+ *
+ * This one should be non-blocking.
+ * 
+ * RETURNS: STATUS_OK or STATUS_ERROR
+ */
+int
+StreamConnection(int server_fd, Port *port)
+{
+    int        addrlen;
+    
+    /* accept connection (and fill in the client (remote) address) */
+    addrlen = sizeof(struct sockaddr_in);
+    if ((port->sock = accept(server_fd,
+                            (struct sockaddr *) &port->raddr,
+                            &addrlen)) < 0) {
+       elog(WARN, "postmaster: StreamConnection: accept: %m");
+       return(STATUS_ERROR);
+    }
+    
+    /* fill in the server (local) address */
+    addrlen = sizeof(struct sockaddr_in);
+    if (getsockname(port->sock, (struct sockaddr *) &port->laddr,
+                   &addrlen) < 0) {
+       elog(WARN, "postmaster: StreamConnection: getsockname: %m");
+       return(STATUS_ERROR);
+    }
+    
+    port->mask = 1 << port->sock;
+
+#ifndef WIN32    
+    /* reset to non-blocking */
+    fcntl(port->sock, F_SETFL, 1);
+#endif /* WIN32 */    
+    
+    return(STATUS_OK);
+}
+
+/* 
+ * StreamClose -- close a client/backend connection
+ */
+void
+StreamClose(int sock)
+{
+    (void) close(sock); 
+}
+
+/* ---------------------------
+ * StreamOpen -- From client, initiate a connection with the 
+ *     server (Postmaster).
+ *
+ * RETURNS: STATUS_OK or STATUS_ERROR
+ *
+ * NOTE: connection is NOT established just because this
+ *     routine exits.  Local state is ok, but we haven't
+ *     spoken to the postmaster yet.
+ * ---------------------------
+ */
+int
+StreamOpen(char *hostName, short portName, Port *port)
+{
+    struct hostent     *hp;
+    int                        laddrlen = sizeof(struct sockaddr_in);
+    extern int         errno;
+    
+    if (!hostName)
+       hostName = "localhost";
+    
+    /* set up the server (remote) address */
+    if (!(hp = gethostbyname(hostName)) || hp->h_addrtype != AF_INET) {
+       (void) sprintf(PQerrormsg,
+                      "FATAL: StreamOpen: unknown hostname: %s\n",
+                      hostName);
+       fputs(PQerrormsg, stderr);
+       pqdebug("%s", PQerrormsg);
+       return(STATUS_ERROR);
+    }
+    memset((char *) &port->raddr, 0, sizeof(port->raddr));
+    memmove((char *) &(port->raddr.sin_addr),
+           (char *) hp->h_addr, 
+           hp->h_length);
+    port->raddr.sin_family = AF_INET;
+    port->raddr.sin_port = htons(portName);
+    
+    /* connect to the server */
+    if ((port->sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+       (void) sprintf(PQerrormsg,
+                      "FATAL: StreamOpen: socket() failed: errno=%d\n",
+                      errno);
+       fputs(PQerrormsg, stderr);
+       pqdebug("%s", PQerrormsg);
+       return(STATUS_ERROR);
+    }
+    if (connect(port->sock, (struct sockaddr *)&port->raddr,
+               sizeof(port->raddr)) < 0) {
+       (void) sprintf(PQerrormsg,
+                      "FATAL: StreamOpen: connect() failed: errno=%d\n",
+                      errno);
+       fputs(PQerrormsg, stderr);
+       pqdebug("%s", PQerrormsg);
+       return(STATUS_ERROR);
+    }
+    
+    /* fill in the client address */
+    if (getsockname(port->sock, (struct sockaddr *) &port->laddr,
+                   &laddrlen) < 0) {
+       (void) sprintf(PQerrormsg,
+                      "FATAL: StreamOpen: getsockname() failed: errno=%d\n",
+                      errno);
+       fputs(PQerrormsg, stderr);
+       pqdebug("%s", PQerrormsg);
+       return(STATUS_ERROR);
+    }
+    
+    return(STATUS_OK);
+}
diff --git a/src/backend/libpq/pqcomm.h b/src/backend/libpq/pqcomm.h
new file mode 100644 (file)
index 0000000..5cd86b4
--- /dev/null
@@ -0,0 +1,124 @@
+/*-------------------------------------------------------------------------
+ *
+ * pqcomm.h--
+ *    Parameters for the communication module
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    Some of this should move to libpq.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PQCOMM_H
+#define        PQCOMM_H
+
+#include <sys/types.h>
+#ifdef WIN32
+#include <winsock.h>
+#else
+#include <netinet/in.h>
+#endif /* WIN32 */
+
+#include "postgres.h"
+
+/*
+ * startup msg parameters: path length, argument string length
+ */
+#define PATH_SIZE      64
+#define ARGV_SIZE      64
+
+
+typedef enum _MsgType {
+    ACK_MSG = 0,               /* acknowledge a message */
+    ERROR_MSG=1,               /* error response to client from server */
+    RESET_MSG=2,               /* client must reset connection */
+    PRINT_MSG=3,               /* tuples for client from server */
+    NET_ERROR=4,               /* error in net system call */
+    FUNCTION_MSG=5,            /* fastpath call (unused) */
+    QUERY_MSG=6,               /* client query to server */
+    STARTUP_MSG=7,             /* initialize a connection with a backend */
+    DUPLICATE_MSG=8,           /* duplicate msg arrived (errors msg only) */
+    INVALID_MSG=9,             /* for some control functions */
+    STARTUP_KRB4_MSG=10,       /* krb4 session follows startup packet */
+    STARTUP_KRB5_MSG=11,       /* krb5 session follows startup packet */
+    STARTUP_HBA_MSG=12         /* use host-based authentication */
+    /* insert new values here -- DO NOT REORDER OR DELETE ENTRIES */
+} MsgType;
+
+typedef char *Addr;
+typedef int PacketLen; /* packet length */
+
+
+typedef struct StartupInfo {
+/*     PacketHdr       hdr; */
+    char               database[PATH_SIZE]; /* database name */
+    char               user[NAMEDATALEN];   /* user name */
+    char               options[ARGV_SIZE];  /* possible additional args */
+    char               execFile[ARGV_SIZE]; /* possible backend to use */
+    char               tty[PATH_SIZE];      /* possible tty for debug output*/
+} StartupInfo;
+
+/* amount of available data in a packet buffer */
+#define MESSAGE_SIZE   sizeof(StartupInfo) + 5
+
+/* I/O can be blocking or non-blocking */
+#define BLOCKING       (FALSE)
+#define NON_BLOCKING   (TRUE)
+
+/* a PacketBuf gets shipped from client to server so be careful
+   of differences in representation.  
+   Be sure to use htonl() and ntohl() on the len and msgtype fields! */
+typedef struct PacketBuf {
+    int len;
+    MsgType msgtype;
+    char data[MESSAGE_SIZE];
+} PacketBuf;
+
+/* update the conversion routines 
+  StartupInfo2PacketBuf() and PacketBuf2StartupInfo() (decl. below)
+  if StartupInfo or PacketBuf structs ever change */
+
+/*
+ * socket descriptor port 
+ *     we need addresses of both sides to do authentication calls
+ */
+typedef struct Port {
+    int                        sock;   /* file descriptor */
+    int                        mask;   /* select mask */
+    int                nBytes; /* nBytes read in so far */
+    struct sockaddr_in laddr;  /* local addr (us) */
+    struct sockaddr_in raddr;  /* remote addr (them) */
+/*     PacketBufId             id;*/   /* id of packet buf currently in use */ 
+    PacketBuf          buf;    /* stream implementation (curr pack buf) */
+} Port;
+
+/* invalid socket descriptor */
+#define INVALID_SOCK   (-1)
+
+#define INVALID_ID (-1)
+#define MAX_CONNECTIONS        10
+#define N_PACK_BUFS    20
+
+/* no multi-packet messages yet */
+#define MAX_PACKET_BACKLOG     1
+
+#define        DEFAULT_STRING          ""
+
+extern FILE *Pfout, *Pfin;
+extern int PQAsyncNotifyWaiting;
+
+/*
+ * prototypes for functions in pqpacket.c
+ */
+extern int PacketReceive(Port *port, PacketBuf *buf, bool nonBlocking);
+extern int PacketSend(Port *port, PacketBuf *buf,
+                     PacketLen len, bool nonBlocking);
+/* extern PacketBuf* StartupInfo2PacketBuf(StartupInfo*); */
+/* extern StartupInfo* PacketBuf2StartupInfo(PacketBuf*); */
+
+
+#endif /* PQCOMM_H */
diff --git a/src/backend/libpq/pqpacket.c b/src/backend/libpq/pqpacket.c
new file mode 100644 (file)
index 0000000..2b39dcb
--- /dev/null
@@ -0,0 +1,283 @@
+/*-------------------------------------------------------------------------
+ *
+ * pqpacket.c--
+ *    routines for reading and writing data packets sent/received by
+ *    POSTGRES clients and servers
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/* NOTES
+ *    This is the module that understands the lowest-level part
+ *    of the communication protocol.  All of the trickiness in
+ *    this module is for making sure that non-blocking I/O in
+ *    the Postmaster works correctly.   Check the notes in PacketRecv
+ *    on non-blocking I/O.
+ *
+ * Data Structures:
+ *     Port has two important functions. (1) It records the 
+ *     sock/addr used in communication. (2) It holds partially
+ *     read in messages.  This is especially important when
+ *     we haven't seen enough to construct a complete packet 
+ *     header.
+ *
+ * PacketBuf -- None of the clients of this module should know
+ *     what goes into a packet hdr (although they know how big
+ *     it is).  This routine is in charge of host to net order
+ *     conversion for headers.  Data conversion is someone elses
+ *     responsibility.
+ *
+ * IMPORTANT: these routines are called by backends, clients, and
+ *     the Postmaster.
+ *
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#ifndef WIN32
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#else
+#include <winsock.h>
+#endif /*WIN32 */
+#include <fcntl.h>
+#include <errno.h>
+
+#include "postgres.h"
+#include "miscadmin.h" 
+#include "utils/elog.h"
+#include "storage/ipc.h"
+#include "libpq/pqcomm.h"      /* where the declarations go */
+#include "libpq/libpq.h"
+
+/*
+ * PacketReceive -- receive a packet on a port.
+ *
+ * RETURNS: connection id of the packet sender, if one
+ * is available.
+ *
+ */
+int
+PacketReceive(Port *port,      /* receive port */
+             PacketBuf *buf,   /* MAX_PACKET_SIZE-worth of buffer space */
+             bool nonBlocking) /* NON_BLOCKING or BLOCKING i/o */
+{
+    PacketLen   max_size = sizeof(PacketBuf);
+    PacketLen  cc;             /* character count -- bytes recvd */
+    PacketLen  packetLen;      /* remaining packet chars to read */
+    Addr       tmp;            /* curr recv buf pointer */
+    int                addrLen = sizeof(struct sockaddr_in);
+    int         hdrLen;
+    int                flag;
+    int                decr;
+    
+    hdrLen = sizeof(buf->len);
+
+    if (nonBlocking == NON_BLOCKING) {
+       flag = MSG_PEEK;
+       decr = 0;
+    } else {
+       flag = 0;
+       decr = hdrLen;
+    }
+    /*
+     * Assume port->nBytes is zero unless we were interrupted during
+     * non-blocking I/O.  This first recvfrom() is to get the hdr
+     * information so we know how many bytes to read.  Life would
+     * be very complicated if we read too much data (buffering).
+     */
+    tmp = ((Addr)buf) + port->nBytes;
+
+    if (port->nBytes >= hdrLen)  {
+       packetLen = ntohl(buf->len) - port->nBytes;
+    }
+     else {
+      /* peeking into the incoming message */
+       cc = recvfrom(port->sock, (char *)&(buf->len), hdrLen, flag, 
+                      (struct sockaddr*) &(port->raddr), &addrLen);
+       if (cc < hdrLen) {
+           /* if cc is negative, the system call failed */
+           if (cc < 0) {
+               return(STATUS_ERROR);
+           }
+           /* 
+            * cc == 0 means the connection was broken at the 
+            * other end.
+            */
+           else if (! cc) {
+               return(STATUS_INVALID);
+               
+           } else {
+               /*
+                * Worst case.  We didn't even read in enough data to
+                * get the header length.
+                * since we are using a data stream, 
+                * this happens only if the client is mallicious.
+                *
+                * Don't save the number of bytes we've read so far.
+                * Since we only peeked at the incoming message, the
+                * kernel is going to keep it for us.
+                */
+               return(STATUS_NOT_DONE);
+           }
+       } else {
+           /*
+            * great. got the header. now get the true length (including
+            * header size).
+            */
+           packetLen = ntohl(buf->len);
+           /*
+            * if someone is sending us junk, close the connection
+            */
+           if (packetLen > max_size) {
+               port->nBytes = packetLen;
+               return(STATUS_BAD_PACKET);
+           }
+           packetLen -= decr;
+           tmp += decr - port->nBytes;
+       }
+    }
+    
+    /*
+     * Now that we know how big it is, read the packet.  We read
+     * the entire packet, since the last call was just a peek.
+     */
+    while (packetLen) { 
+       cc = recvfrom(port->sock, tmp, packetLen, 0,
+                     (struct sockaddr*) &(port->raddr), &addrLen);
+       if (cc < 0)
+           return(STATUS_ERROR);
+       /* 
+        * cc == 0 means the connection was broken at the 
+        * other end.
+        */
+       else if (! cc) 
+           return(STATUS_INVALID);
+       
+/*
+   fprintf(stderr,"expected packet of %d bytes, got %d bytes\n",
+           packetLen, cc);
+*/
+       tmp += cc;
+       packetLen -= cc;
+       
+       /* if non-blocking, we're done. */
+       if (nonBlocking && packetLen) {
+           port->nBytes += cc;
+           return(STATUS_NOT_DONE);
+       }
+    }
+    
+    port->nBytes = 0;
+    return(STATUS_OK);
+}
+
+/*
+ * PacketSend -- send a single-packet message.
+ *
+ * RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise.
+ * SIDE_EFFECTS: may block.
+ * NOTES: Non-blocking writes would significantly complicate 
+ *     buffer management.  For now, we're not going to do it.
+ *
+ */
+int
+PacketSend(Port *port,
+          PacketBuf *buf,
+          PacketLen len,
+          bool nonBlocking)
+{
+    PacketLen  totalLen;
+    int                addrLen = sizeof(struct sockaddr_in);
+    
+    Assert(!nonBlocking);
+    Assert(buf);
+    
+    totalLen = len;
+    
+    len = sendto(port->sock, (Addr) buf, totalLen, /* flags */ 0,
+                (struct sockaddr *)&(port->raddr), addrLen);
+    
+    if (len < totalLen) {
+       (void) sprintf(PQerrormsg,
+                      "FATAL: PacketSend: couldn't send complete packet: errno=%d\n",
+                      errno);
+       fputs(PQerrormsg, stderr);
+       return(STATUS_ERROR);
+    }
+    
+    return(STATUS_OK);
+}
+
+/*
+ * StartupInfo2PacketBuf -
+ *   convert the fields of the StartupInfo to a PacketBuf
+ *
+ */
+/* moved to src/libpq/fe-connect.c */
+/*
+PacketBuf* 
+StartupInfo2PacketBuf(StartupInfo* s)
+{
+  PacketBuf* res;
+  char* tmp;
+
+  res = (PacketBuf*)malloc(sizeof(PacketBuf));
+  res->len = htonl(sizeof(PacketBuf));
+  res->data[0] = '\0';
+
+  tmp= res->data;
+
+  strncpy(tmp, s->database, sizeof(s->database));
+  tmp += sizeof(s->database);
+  strncpy(tmp, s->user, sizeof(s->user));
+  tmp += sizeof(s->user);
+  strncpy(tmp, s->options, sizeof(s->options));
+  tmp += sizeof(s->options);
+  strncpy(tmp, s->execFile, sizeof(s->execFile));
+  tmp += sizeof(s->execFile);
+  strncpy(tmp, s->tty, sizeof(s->execFile));
+
+  return res;
+}
+*/
+
+/*
+ * PacketBuf2StartupInfo -
+ *   convert the fields of the StartupInfo to a PacketBuf
+ *
+ */
+/* moved to postmaster.c 
+StartupInfo*
+PacketBuf2StartupInfo(PacketBuf* p)
+{
+  StartupInfo* res;
+  char* tmp;
+
+  res = (StartupInfo*)malloc(sizeof(StartupInfo));
+
+  res->database[0]='\0';
+  res->user[0]='\0';
+  res->options[0]='\0';
+  res->execFile[0]='\0';
+  res->tty[0]='\0';
+
+  tmp= p->data;
+  strncpy(res->database,tmp,sizeof(res->database));
+  tmp += sizeof(res->database);
+  strncpy(res->user,tmp, sizeof(res->user));
+  tmp += sizeof(res->user);
+  strncpy(res->options,tmp, sizeof(res->options));
+  tmp += sizeof(res->options);
+  strncpy(res->execFile,tmp, sizeof(res->execFile));
+  tmp += sizeof(res->execFile);
+  strncpy(res->tty,tmp, sizeof(res->tty));
+
+  return res;
+}
+*/
diff --git a/src/backend/libpq/pqsignal.c b/src/backend/libpq/pqsignal.c
new file mode 100644 (file)
index 0000000..b41c975
--- /dev/null
@@ -0,0 +1,40 @@
+/*-------------------------------------------------------------------------
+ *
+ * pqsignal.c--
+ *    reliable BSD-style signal(2) routine stolen from RWW who stole it
+ *    from Stevens...
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *     This shouldn't be in libpq, but the monitor and some other
+ *     things need it...
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "libpq/pqsignal.h"
+
+pqsigfunc
+pqsignal(int signo, pqsigfunc func)
+{
+#if defined(USE_POSIX_SIGNALS)
+    struct sigaction act, oact;
+    
+    act.sa_handler = func;
+    sigemptyset(&act.sa_mask);
+    act.sa_flags = 0;
+    if (signo != SIGALRM) {
+       act.sa_flags |= SA_RESTART;
+    }
+    if (sigaction(signo, &act, &oact) < 0)
+       return(SIG_ERR);
+    return(oact.sa_handler);
+#else /* !USE_POSIX_SIGNALS */
+    Assert(0);
+    return 0;
+#endif /* !USE_POSIX_SIGNALS */
+}
diff --git a/src/backend/libpq/pqsignal.h b/src/backend/libpq/pqsignal.h
new file mode 100644 (file)
index 0000000..8b70494
--- /dev/null
@@ -0,0 +1,32 @@
+/*-------------------------------------------------------------------------
+ *
+ * pqsignal.h--
+ *    prototypes for the reliable BSD-style signal(2) routine.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    This shouldn't be in libpq, but the monitor and some other
+ *    things need it...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PQSIGNAL_H
+#define PQSIGNAL_H
+
+#include <signal.h>
+
+#include "c.h"
+
+typedef void (*pqsigfunc)(int);
+
+extern pqsigfunc pqsignal(int signo, pqsigfunc func);
+
+#if defined(USE_POSIX_SIGNALS)
+#define        signal(signo, handler)  pqsignal(signo, (pqsigfunc)(handler))
+#endif /* USE_POSIX_SIGNALS */
+
+#endif /* PQSIGNAL_H */
diff --git a/src/backend/main/Makefile.inc b/src/backend/main/Makefile.inc
new file mode 100644 (file)
index 0000000..a96de80
--- /dev/null
@@ -0,0 +1,16 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the main() of the postgres backend
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+VPATH:= $(VPATH):$(CURDIR)/main
+
+SRCS_MAIN= main.c
diff --git a/src/backend/main/main.c b/src/backend/main/main.c
new file mode 100644 (file)
index 0000000..ceaa4fe
--- /dev/null
@@ -0,0 +1,45 @@
+/*-------------------------------------------------------------------------
+ *
+ * main.c--
+ *    Stub main() routine for the postgres backend.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "c.h"
+#include "miscadmin.h"
+#include "bootstrap/bootstrap.h"       /* for BootstrapMain() */
+#include "tcop/tcopprot.h"             /* for PostgresMain() */
+#include "port-protos.h"               /* for init_address_fixup() */
+
+int
+main(int argc, char *argv[])
+{
+    int len;
+#if defined(NOFIXADE) || defined(NOPRINTADE)
+    /*
+     * Must be first so that the bootstrap code calls it, too.
+     * (Only needed on some RISC architectures.)
+     */
+    init_address_fixup();
+#endif /* NOFIXADE || NOPRINTADE */
+    
+    /* use one executable for both postgres and postmaster,
+       invoke one or the other depending on the name of the executable */
+    len = strlen(argv[0]);
+    if(len >= 10 && ! strcmp(argv[0] + len - 10, "postmaster"))
+        exit(PostmasterMain(argc, argv));
+
+    /* if the first argument is "-boot", then invoke the backend in
+       bootstrap mode */
+    if (argc > 1 && strcmp(argv[1], "-boot") == 0)
+       exit(BootstrapMain(argc-1, argv+1)); /* remove the -boot arg from the command line */
+    else
+       exit(PostgresMain(argc, argv));
+}
diff --git a/src/backend/makeID b/src/backend/makeID
new file mode 100644 (file)
index 0000000..b42012c
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+# $Header$
+#
+
+
+if test $# -ne 1; then
+    echo "usage: $0 PORTNAME"
+    exit 1
+fi
+PORTNAME=$1
+gfind . port/$PORTNAME -type f  -name '*.[chly]' -print | mkid -S.gen=C - 2>&1 | grep -v 'No scanner for language'
+
+
+
+exit 0
diff --git a/src/backend/nodes/Makefile.inc b/src/backend/nodes/Makefile.inc
new file mode 100644 (file)
index 0000000..4066231
--- /dev/null
@@ -0,0 +1,33 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the nodes module
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+# NOTES
+#    Originally, the nodes module is a home-brew, C++ like inheritance
+#    system. However, the automatically generated tags, accessor functions
+#    and the header files themselves are difficult to maintain. We need
+#    real language support. Emulation doesn't quite do it...
+#
+#    See nodes/README for an explanation of the new no-frills nodes
+#    structures.
+#                                                     - ay 11/5/94
+#
+#-------------------------------------------------------------------------
+
+VPATH:= $(VPATH):$(CURDIR)/nodes
+
+
+SRCS_NODES= nodeFuncs.c nodes.c list.c \
+       copyfuncs.c equalfuncs.c makefuncs.c outfuncs.c readfuncs.c \
+       print.c read.c
+
+HEADERS+= execnodes.h makefuncs.h memnodes.h nodeFuncs.h nodes.h \
+       params.h parsenodes.h pg_list.h plannodes.h primnodes.h relation.h
+
diff --git a/src/backend/nodes/README b/src/backend/nodes/README
new file mode 100644 (file)
index 0000000..6a5bfd4
--- /dev/null
@@ -0,0 +1,65 @@
+*******************************************************************************
+*                                                                             *
+* EXPLANATION OF THE NODE STRUCTURES                                          *
+*    - Andrew Yu (11/94)                                                      *
+*                                                                             *
+* Copyright (c) 1994, Regents of the University of California                 *
+*                                                                             *
+* $Id$
+*                                                                             *
+*******************************************************************************
+
+INTRODUCTION
+
+The current node structures are plain old C structures. "Inheritance" is
+achieved by convention. No additional functions will be generated. Functions
+that manipulate node structures reside in this directory.
+
+
+FILES IN THIS DIRECTORY
+
+    Node manipulation functions:
+       copyfuncs.c     - copying a node
+       equalfuncs.c    - comparing a node
+       outfuncs.c      - convert a node to ascii representation
+       readfuncs.c     - convert ascii representation back to a node
+       makefuncs.c     - creator functions for primitive nodes
+
+    Node definitions:
+       nodes.h         - define node tags (NodeTag)
+       pg_list.h       - generic list 
+       primnodes.h     - primitive nodes
+       parsenodes.h    - parse tree nodes
+       plannodes.h     - plan tree nodes
+       relation.h      - inner plan tree nodes
+       execnodes.h     - executor nodes
+       memnodes.h      - memory nodes
+
+
+STEPS TO ADD A NODE
+
+Suppose you wana define a node Foo:
+
+1. add a tag (T_Foo) to the enum NodeTag in nodes.h (You may have to
+   recompile the whole tree after doing this.)
+2. add the structure definition to the appropriate ???nodes.h file. If you
+   intend to inherit from, say a Plan node, put Plan as the first field of
+   you definition.
+3. if you intend to use copyObject, equal, nodeToString or stringToNode,
+   add an appropriate function to copyfuncs.c, equalfuncs.c, outfuncs.c
+   and readfuncs.c accordingly. (Except for frequently used nodes, don't
+   bother writing a creator function in makefuncs.c)
+
+
+HISTORICAL NOTE
+
+Prior to the current simple C structure definitions, the Node structures 
+uses a pseudo-inheritance system which automatically generates creator and
+accessor functions. Since every node inherits from LispValue, the whole thing
+is a mess. Here's a little anecdote:
+
+    LispValue definition -- class used to support lisp structures
+    in C.  This is here because we did not want to totally rewrite
+    planner and executor code which depended on lisp structures when
+    we ported postgres V1 from lisp to C. -cim 4/23/90
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
new file mode 100644 (file)
index 0000000..0da8aba
--- /dev/null
@@ -0,0 +1,1675 @@
+/*-------------------------------------------------------------------------
+ *
+ * copyfuncs.c--
+ *    Copy functions for Postgres tree nodes.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <string.h>
+
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/execnodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/parsenodes.h"
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+
+#include "utils/syscache.h"
+#include "utils/builtins.h"    /* for namecpy */
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "catalog/pg_type.h"
+#include "storage/lmgr.h"
+
+/*
+ * listCopy--
+ *    this copy function only copies the "lcons-cells" of the list but not
+ *    its contents. (good for list of pointers as well as list of integers).
+ */
+List *
+listCopy(List *list)
+{
+    List *newlist=NIL;
+    List *l, *nl;
+
+    foreach(l, list) {
+       if (newlist==NIL) {
+           newlist = nl = lcons(lfirst(l),NIL);
+       }else {
+           lnext(nl) = lcons(lfirst(l),NIL);
+           nl = lnext(nl);
+       }
+    }
+    return newlist; 
+}
+       
+/*
+ * Node_Copy--
+ *    a macro to simplify calling of copyObject on the specified field
+ */
+#define Node_Copy(from, newnode, field) \
+    newnode->field = copyObject(from->field)
+
+/* ****************************************************************
+ *                  plannodes.h copy functions
+ * ****************************************************************
+ */
+
+/* ----------------
+ *     CopyPlanFields
+ *
+ *     This function copies the fields of the Plan node.  It is used by
+ *     all the copy functions for classes which inherit from Plan.
+ * ----------------
+ */
+static void
+CopyPlanFields(Plan *from, Plan *newnode)
+{
+    newnode->cost = from->cost;
+    newnode->plan_size = from->plan_size;
+    newnode->plan_width = from->plan_width;
+    newnode->state = from->state;
+    newnode->targetlist = copyObject(from->targetlist);
+    newnode->qual = copyObject(from->qual);
+    newnode->lefttree = copyObject(from->lefttree);
+    newnode->righttree = copyObject(from->righttree);
+}
+
+/* ----------------
+ *     _copyPlan
+ * ----------------
+ */
+static Plan *
+_copyPlan(Plan *from)
+{
+    Plan *newnode = makeNode(Plan);
+    
+    /* ----------------
+     * copy the node superclass fields
+     * ----------------
+     */
+    CopyPlanFields(from, newnode);
+    
+    return newnode;
+}
+
+
+/* ----------------
+ *     _copyExistential
+ * ----------------
+ */
+static Existential *
+_copyExistential(Existential *from)
+{
+    Existential        *newnode = makeNode(Existential);
+
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    CopyPlanFields(from, newnode);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyResult
+ * ----------------
+ */
+static Result *
+_copyResult(Result *from)
+{
+    Result *newnode = makeNode(Result);
+    
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    CopyPlanFields((Plan*)from, (Plan*)newnode);
+    
+    /* ----------------
+     * copy remainder of node 
+     * ----------------
+     */
+    Node_Copy(from, newnode, resconstantqual);
+    Node_Copy(from, newnode, resstate);
+
+    return newnode;
+}
+
+/* ----------------
+ *     _copyAppend
+ * ----------------
+ */
+static Append *
+_copyAppend(Append *from)
+{
+    Append *newnode = makeNode(Append);
+    
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    CopyPlanFields((Plan*)from, (Plan*)newnode);
+    
+    /* ----------------
+     * copy remainder of node 
+     * ----------------
+     */
+    Node_Copy(from, newnode, unionplans);
+    newnode->unionrelid = from->unionrelid;
+    Node_Copy(from, newnode, unionrtentries);
+    Node_Copy(from, newnode, unionstate);
+    
+    return newnode;
+}
+
+
+/* ----------------
+ *     CopyScanFields
+ *
+ *     This function copies the fields of the Scan node.  It is used by
+ *     all the copy functions for classes which inherit from Scan.
+ * ----------------
+ */
+static void
+CopyScanFields(Scan *from, Scan *newnode)
+{
+    newnode->scanrelid = from->scanrelid;
+    Node_Copy(from, newnode, scanstate);
+    return;
+}
+
+/* ----------------
+ *     _copyScan
+ * ----------------
+ */
+static Scan *
+_copyScan(Scan *from)
+{
+    Scan *newnode = makeNode(Scan);
+    
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    CopyPlanFields((Plan*)from, (Plan*)newnode);
+    CopyScanFields(from, newnode);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copySeqScan
+ * ----------------
+ */
+static SeqScan *
+_copySeqScan(SeqScan *from)
+{
+    SeqScan *newnode = makeNode(SeqScan);
+    
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    CopyPlanFields((Plan*)from, (Plan*)newnode);
+    CopyScanFields((Scan*)from, (Scan*)newnode);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyIndexScan
+ * ----------------
+ */
+static IndexScan *
+_copyIndexScan(IndexScan *from)
+{
+    IndexScan *newnode = makeNode(IndexScan);
+    
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    CopyPlanFields((Plan*)from, (Plan*)newnode);
+    CopyScanFields((Scan*)from, (Scan*)newnode);
+    
+    /* ----------------
+     * copy remainder of node 
+     * ----------------
+     */
+    newnode->indxid = listCopy(from->indxid);
+    Node_Copy(from, newnode, indxqual);
+    Node_Copy(from, newnode, indxstate);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     CopyJoinFields
+ *
+ *     This function copies the fields of the Join node.  It is used by
+ *     all the copy functions for classes which inherit from Join.
+ * ----------------
+ */
+static void
+CopyJoinFields(Join *from, Join *newnode)
+{
+    /* nothing extra */
+    return;
+}
+
+
+/* ----------------
+ *     _copyJoin
+ * ----------------
+ */
+static Join *
+_copyJoin(Join *from)
+{
+    Join *newnode = makeNode(Join);
+    
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    CopyPlanFields((Plan*)from, (Plan*)newnode);
+    CopyJoinFields(from, newnode);
+    
+    return newnode;
+}
+
+
+/* ----------------
+ *     _copyNestLoop
+ * ----------------
+ */
+static NestLoop *
+_copyNestLoop(NestLoop *from)
+{
+    NestLoop *newnode = makeNode(NestLoop);
+    
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    CopyPlanFields((Plan*)from, (Plan*)newnode);
+    CopyJoinFields((Join*)from, (Join*)newnode);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    Node_Copy(from, newnode, nlstate);
+    
+    return newnode;
+}
+
+
+/* ----------------
+ *     _copyMergeJoin
+ * ----------------
+ */
+static MergeJoin *
+_copyMergeJoin(MergeJoin *from)
+{
+    MergeJoin *newnode = makeNode(MergeJoin);
+    List       *newlist;
+    
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    CopyPlanFields((Plan*)from, (Plan*)newnode);
+    CopyJoinFields((Join*)from, (Join*)newnode);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    Node_Copy(from, newnode, mergeclauses);
+    
+    newnode->mergesortop = from->mergesortop;
+    newlist = NIL;
+
+    newnode->mergerightorder = (Oid *)palloc(sizeof(Oid)*2);
+    newnode->mergerightorder[0] = from->mergerightorder[0];
+    newnode->mergerightorder[1] = 0;
+
+    newnode->mergeleftorder = (Oid *)palloc(sizeof(Oid)*2);
+    newnode->mergeleftorder[0] = from->mergeleftorder[0];
+    newnode->mergeleftorder[1] = 0;
+    
+    Node_Copy(from, newnode, mergestate);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyHashJoin
+ * ----------------
+ */
+static HashJoin *
+_copyHashJoin(HashJoin *from)
+{
+    HashJoin *newnode = makeNode(HashJoin);
+    
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    CopyPlanFields((Plan*)from, (Plan*)newnode);
+    CopyJoinFields((Join*)from, (Join*)newnode);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    Node_Copy(from, newnode, hashclauses);
+    
+    newnode->hashjoinop =              from->hashjoinop;
+    
+    Node_Copy(from, newnode, hashjoinstate);
+    
+    newnode->hashjointable =           from->hashjointable;
+    newnode->hashjointablekey =        from->hashjointablekey;
+    newnode->hashjointablesize =       from->hashjointablesize;
+    newnode->hashdone =                from->hashdone;
+    
+    return newnode;
+}
+
+
+/* ----------------
+ *     CopyTempFields
+ *
+ *     This function copies the fields of the Temp node.  It is used by
+ *     all the copy functions for classes which inherit from Temp.
+ * ----------------
+ */
+static void
+CopyTempFields(Temp *from, Temp *newnode)
+{
+    newnode->tempid =  from->tempid;
+    newnode->keycount = from->keycount;
+    return;
+}
+
+
+/* ----------------
+ *     _copyTemp
+ * ----------------
+ */
+static Temp *
+_copyTemp(Temp *from)
+{
+    Temp *newnode = makeNode(Temp);
+    
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    CopyPlanFields((Plan*)from, (Plan*)newnode);
+    CopyTempFields(from, newnode);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyMaterial
+ * ----------------
+ */
+static Material *
+_copyMaterial(Material *from)
+{
+    Material *newnode = makeNode(Material);
+    
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    CopyPlanFields((Plan*)from, (Plan*)newnode);
+    CopyTempFields((Temp*)from, (Temp*)newnode);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    Node_Copy(from, newnode, matstate);
+    
+    return newnode;
+}
+
+
+/* ----------------
+ *     _copySort
+ * ----------------
+ */
+static Sort *
+_copySort(Sort *from)
+{
+    Sort *newnode = makeNode(Sort);
+    
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    CopyPlanFields((Plan*)from, (Plan*)newnode);
+    CopyTempFields((Temp*)from, (Temp*)newnode);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    Node_Copy(from, newnode, sortstate);
+    
+    return newnode;
+}
+
+/* ---------------
+ *  _copyAgg
+ * --------------
+ */
+static Agg *
+_copyAgg(Agg *from)
+{
+    Agg *newnode = makeNode(Agg);
+    int i;
+    
+    CopyPlanFields((Plan*)from, (Plan*)newnode);
+    CopyTempFields((Temp*)from, (Temp*)newnode);
+
+    newnode->numAgg = from->numAgg;
+    newnode->aggs = malloc(sizeof(Aggreg *));
+    for(i=0; i < from->numAgg; i++) {
+       newnode->aggs[i] = copyObject(from->aggs[i]);
+    }
+       
+    Node_Copy(from, newnode, aggstate);
+    
+    return newnode;
+}
+
+
+/* ----------------
+ *     _copyUnique
+ * ----------------
+ */
+static Unique *
+_copyUnique(Unique *from)
+{
+    Unique *newnode = makeNode(Unique);
+    
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    CopyPlanFields((Plan*)from, (Plan*)newnode);
+    CopyTempFields((Temp*)from, (Temp*)newnode);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    Node_Copy(from, newnode, uniquestate);
+    
+    return newnode;
+}
+
+
+/* ----------------
+ *     _copyHash
+ * ----------------
+ */
+static Hash *
+_copyHash(Hash *from)
+{
+    Hash *newnode = makeNode(Hash);
+    
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    CopyPlanFields((Plan*)from, (Plan*)newnode);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    Node_Copy(from, newnode, hashkey);
+    Node_Copy(from, newnode, hashstate);
+    
+    newnode->hashtable =       from->hashtable;
+    newnode->hashtablekey =    from->hashtablekey;
+    newnode->hashtablesize =   from->hashtablesize;
+    
+    return newnode;
+}
+
+/* ****************************************************************
+ *                    primnodes.h copy functions
+ * ****************************************************************
+ */
+
+/* ----------------
+ *     _copyResdom
+ * ----------------
+ */
+static Resdom *
+_copyResdom(Resdom *from)
+{
+    Resdom *newnode = makeNode(Resdom);
+    
+    newnode->resno   =         from->resno;
+    newnode->restype =         from->restype;
+    newnode->reslen  =         from->reslen;
+    
+    if (from->resname != NULL) {
+       newnode->resname = palloc(strlen(from->resname)+1);
+       strcpy(newnode->resname, from->resname);
+    } else
+       newnode->resname = (char*) NULL;
+    
+    newnode->reskey  =         from->reskey;
+    newnode->reskeyop = from->reskeyop;
+    newnode->resjunk =         from->resjunk;
+    
+    return newnode;
+}
+
+static Fjoin *
+_copyFjoin(Fjoin *from)
+{
+    Fjoin      *newnode;
+    
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    
+    newnode->fj_initialized = from->fj_initialized;
+    newnode->fj_nNodes      = from->fj_nNodes;
+    
+    Node_Copy(from, newnode, fj_innerNode);
+    
+    newnode->fj_results     = (DatumPtr)
+       palloc((from->fj_nNodes)*sizeof(Datum));
+    
+    newnode->fj_alwaysDone  = (BoolPtr)
+       palloc((from->fj_nNodes)*sizeof(bool));
+    
+    memmove(from->fj_results,
+          newnode->fj_results, 
+          (from->fj_nNodes)*sizeof(Datum));
+    
+    memmove(from->fj_alwaysDone,
+           newnode->fj_alwaysDone,
+           (from->fj_nNodes)*sizeof(bool));
+
+
+    return newnode;
+}
+
+/* ----------------
+ *     _copyExpr
+ * ----------------
+ */
+static Expr *
+_copyExpr(Expr *from)
+{
+    Expr *newnode = makeNode(Expr);
+    
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    newnode->typeOid = from->typeOid;
+    newnode->opType = from->opType;
+
+    Node_Copy(from, newnode, oper);
+    Node_Copy(from, newnode, args);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyVar
+ * ----------------
+ */
+static Var *
+_copyVar(Var *from)
+{
+    Var *newnode = makeNode(Var);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    newnode->varno =    from->varno;
+    newnode->varattno =  from->varattno;
+    newnode->vartype =          from->vartype;
+    
+    newnode->varnoold =  from->varnoold;
+    newnode->varoattno = from->varoattno;
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyOper
+ * ----------------
+ */
+static Oper *
+_copyOper(Oper *from)
+{
+    Oper *newnode = makeNode(Oper);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    newnode->opno =            from->opno;
+    newnode->opid =            from->opid;
+    newnode->opresulttype =            from->opresulttype;
+    newnode->opsize =          from->opsize;
+    
+    /*
+     * NOTE: shall we copy the cache structure or just the pointer ?
+     * Alternatively we can set 'op_fcache' to NULL, in which
+     * case the executor will initialize it when it needs it...
+     */
+    newnode->op_fcache =       from->op_fcache;
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyConst
+ * ----------------
+ */
+static Const *
+_copyConst(Const *from)
+{
+    static Oid         cached_type;
+    static bool                cached_typbyval;
+    
+    Const *newnode = makeNode(Const);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    newnode->consttype =       from->consttype;
+    newnode->constlen =        from->constlen;
+    
+    /* ----------------
+     * XXX super cheesy hack until parser/planner
+     *  puts in the right values here.
+     * ----------------
+     */
+    if (cached_type != from->consttype) {
+       HeapTuple       typeTuple;
+       TypeTupleForm   typeStruct;
+       
+       /* ----------------
+        *   get the type tuple corresponding to the paramList->type,
+        *   If this fails, returnValue has been pre-initialized
+        *   to "null" so we just return it.
+        * ----------------
+        */
+       typeTuple = SearchSysCacheTuple(TYPOID,
+                                       ObjectIdGetDatum(from->consttype),
+                                       0,0,0);
+       
+       /* ----------------
+        *   get the type length and by-value from the type tuple and
+        *   save the information in our one element cache.
+        * ----------------
+        */
+       Assert(PointerIsValid(typeTuple));
+       
+       typeStruct = (TypeTupleForm) GETSTRUCT(typeTuple);
+       cached_typbyval = (typeStruct)->typbyval ? true : false ;
+       cached_type = from->consttype;
+    }
+    
+    from->constbyval = cached_typbyval;
+    
+    if (!from->constisnull) {
+       /* ----------------
+        *      copying the Datum in a const node is a bit trickier
+        *  because it might be a pointer and it might also be of
+        *  variable length...
+        * ----------------
+        */
+       if (from->constbyval == true) {
+           /* ----------------
+            *  passed by value so just copy the datum.
+            * ----------------
+            */
+           newnode->constvalue =       from->constvalue;
+       } else {
+           /* ----------------
+            *  not passed by value. datum contains a pointer.
+            * ----------------
+            */
+           if (from->constlen != -1) {
+               /* ----------------
+                *      fixed length structure
+                * ----------------
+                */
+               newnode->constvalue = PointerGetDatum(palloc(from->constlen));
+               memmove((char*)newnode->constvalue, 
+                       (char*)from->constvalue, from->constlen); 
+           } else {
+               /* ----------------
+                *      variable length structure.  here the length is stored
+                *  in the first int pointed to by the constval.
+                * ----------------
+                */
+               int length;
+               length = *((int *) from->constvalue);
+               newnode->constvalue = PointerGetDatum(palloc(length));
+               memmove((char*)newnode->constvalue,
+                       (char*)from->constvalue, length);
+           }
+       }
+    }
+    else {
+       newnode->constvalue = from->constvalue;
+    }
+    newnode->constisnull =     from->constisnull;
+    newnode->constbyval =      from->constbyval;
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyParam
+ * ----------------
+ */
+static Param *
+_copyParam(Param *from)
+{
+    Param *newnode = makeNode(Param);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    newnode->paramkind = from->paramkind;
+    newnode->paramid = from->paramid;
+    
+    if (from->paramname != NULL) {
+       newnode->paramname = pstrdup(from->paramname);
+    } else
+       newnode->paramname = (char*)NULL;
+    
+    newnode->paramtype = from->paramtype;
+    Node_Copy(from, newnode, param_tlist);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyFunc
+ * ----------------
+ */
+static Func *
+_copyFunc(Func *from)
+{
+    Func *newnode = makeNode(Func);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    newnode->funcid =          from->funcid;
+    newnode->functype =        from->functype;
+    newnode->funcisindex =     from->funcisindex;
+    newnode->funcsize =        from->funcsize;
+    newnode->func_fcache =     from->func_fcache;
+    Node_Copy(from, newnode, func_tlist);
+    Node_Copy(from, newnode, func_planlist);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyAggreg
+ * ----------------
+ */
+static Aggreg *
+_copyAggreg(Aggreg *from)
+{
+    Aggreg *newnode = makeNode(Aggreg);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    newnode->aggname =         pstrdup(from->aggname);
+    newnode->basetype = from->basetype;
+    newnode->aggtype =         from->aggtype;
+
+    Node_Copy(from, newnode, target);
+
+    newnode->aggno = from->aggno;
+    
+    return newnode;
+}
+
+static Array *
+_copyArray(Array *from)
+{
+    Array *newnode = makeNode(Array);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    newnode->arrayelemtype =   from->arrayelemtype;
+    newnode->arrayelemlength =         from->arrayelemlength;
+    newnode->arrayelembyval =  from->arrayelembyval;
+    newnode->arrayndim =       from->arrayndim;
+    newnode->arraylow =        from->arraylow;
+    newnode->arrayhigh =       from->arrayhigh;
+    newnode->arraylen =        from->arraylen;
+    
+    return newnode;
+}
+
+static ArrayRef *
+_copyArrayRef(ArrayRef *from)
+{
+    ArrayRef *newnode = makeNode(ArrayRef);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    newnode->refelemtype =     from->refelemtype;
+    newnode->refattrlength =   from->refattrlength;
+    newnode->refelemlength =   from->refelemlength;
+    newnode->refelembyval =    from->refelembyval;
+
+    Node_Copy(from,newnode,refupperindexpr);
+    Node_Copy(from,newnode,reflowerindexpr);
+    Node_Copy(from,newnode,refexpr);
+    Node_Copy(from,newnode,refassgnexpr);
+    
+    return newnode;
+}
+
+/* ****************************************************************
+ *                     relation.h copy functions
+ * ****************************************************************
+ */
+
+/* ----------------
+ *     _copyRel
+ * ----------------
+ */
+/*
+ ** when you change this, also make sure to fix up xfunc_copyRel in 
+ ** planner/path/xfunc.c accordingly!!!
+ **         -- JMH, 8/2/93
+ */
+static Rel *
+_copyRel(Rel *from)
+{
+    Rel *newnode = makeNode(Rel);
+    int i, len;
+
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    newnode->relids = listCopy(from->relids);
+    
+    newnode->indexed = from->indexed;
+    newnode->pages =   from->pages;
+    newnode->tuples =  from->tuples;
+    newnode->size =    from->size;
+    newnode->width =   from->width;
+    newnode->indproc = from->indproc;
+    
+    Node_Copy(from, newnode, targetlist);
+    Node_Copy(from, newnode, pathlist);
+    Node_Copy(from, newnode, unorderedpath);
+    Node_Copy(from, newnode, cheapestpath);
+    newnode->pruneable = from->pruneable;
+    newnode->relam = from->relam;
+
+    if (from->classlist) {
+       for(len=0; from->classlist[len]!=0; len++)
+           ;
+       newnode->classlist = (Oid *)palloc(sizeof(Oid) * (len+1));
+       for(i=0; i < len; i++) {
+           newnode->classlist[i] = from->classlist[i];
+       }
+       newnode->classlist[len] = 0;
+    }
+
+    if (from->indexkeys) {
+       for(len=0; from->indexkeys[len]!=0; len++)
+           ;
+       newnode->indexkeys = (int *)palloc(sizeof(int) * (len+1));
+       for(i=0; i < len; i++) {
+           newnode->indexkeys[i] = from->indexkeys[i];
+       }
+       newnode->indexkeys[len] = 0;
+    }
+    
+    if (from->ordering) {
+       for(len=0; from->ordering[len]!=0; len++)
+           ;
+       newnode->ordering = (Oid *)palloc(sizeof(Oid) * (len+1));
+       for(i=0; i < len; i++) {
+           newnode->ordering[i] = from->ordering[i];
+       }
+       newnode->ordering[len] = 0;
+    }
+
+    Node_Copy(from, newnode, clauseinfo);
+    Node_Copy(from, newnode, joininfo);
+    Node_Copy(from, newnode, innerjoin);
+    Node_Copy(from, newnode, superrels);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     CopyPathFields
+ *
+ *     This function copies the fields of the Path node.  It is used by
+ *     all the copy functions for classes which inherit from Path.
+ * ----------------
+ */
+static void
+CopyPathFields(Path *from, Path *newnode)
+{
+    newnode->pathtype =        from->pathtype;
+    /* Modify the next line, since it causes the copying to cycle
+       (i.e. the parent points right back here! 
+       -- JMH, 7/7/92.
+       Old version:
+       Node_Copy(from, newnode, parent);
+       */
+    newnode->parent =           from->parent;
+    
+    newnode->path_cost =       from->path_cost;
+
+    newnode->p_ordering.ordtype = from->p_ordering.ordtype;
+    if (from->p_ordering.ordtype == SORTOP_ORDER) {
+       int len, i;
+       Oid *ordering = from->p_ordering.ord.sortop;
+
+       if (ordering) {
+           for(len=0; ordering[len]!=0; len++)
+               ;
+           newnode->p_ordering.ord.sortop =
+               (Oid *)palloc(sizeof(Oid) * (len+1));
+           for(i=0; i < len; i++) {
+               newnode->p_ordering.ord.sortop[i] = ordering[i];
+           }
+           newnode->p_ordering.ord.sortop[len] = 0;
+       } else {
+           newnode->p_ordering.ord.sortop = NULL;
+       }
+    } else {
+       Node_Copy(from, newnode, p_ordering.ord.merge);
+    }
+
+    Node_Copy(from, newnode, keys);
+    
+    newnode->outerjoincost =   from->outerjoincost;
+    
+    newnode->joinid = listCopy(from->joinid);
+    Node_Copy(from, newnode, locclauseinfo);
+}
+
+/* ----------------
+ *     _copyPath
+ * ----------------
+ */
+static Path *
+_copyPath(Path *from)
+{
+    Path *newnode = makeNode(Path);
+    
+    CopyPathFields(from, newnode);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyIndexPath
+ * ----------------
+ */
+static IndexPath *
+_copyIndexPath(IndexPath *from)
+{
+    IndexPath *newnode = makeNode(IndexPath);
+    
+    /* ----------------
+     * copy the node superclass fields
+     * ----------------
+     */
+    CopyPathFields((Path*)from, (Path*)newnode);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    newnode->indexid = listCopy(from->indexid);
+    Node_Copy(from, newnode, indexqual);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     CopyJoinPathFields
+ *
+ *     This function copies the fields of the JoinPath node.  It is used by
+ *     all the copy functions for classes which inherit from JoinPath.
+ * ----------------
+ */
+static void
+CopyJoinPathFields(JoinPath *from, JoinPath *newnode)
+{
+    Node_Copy(from, newnode, pathclauseinfo);
+    Node_Copy(from, newnode, outerjoinpath);
+    Node_Copy(from, newnode, innerjoinpath);
+}
+
+/* ----------------
+ *     _copyJoinPath
+ * ----------------
+ */
+static JoinPath *
+_copyJoinPath(JoinPath *from)
+{
+    JoinPath *newnode = makeNode(JoinPath);
+    
+    /* ----------------
+     * copy the node superclass fields
+     * ----------------
+     */
+    CopyPathFields((Path*)from, (Path*)newnode);
+    CopyJoinPathFields(from, newnode);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyMergePath
+ * ----------------
+ */
+static MergePath *
+_copyMergePath(MergePath *from)
+{
+    MergePath *newnode = makeNode(MergePath);
+    
+    /* ----------------
+     * copy the node superclass fields
+     * ----------------
+     */
+    CopyPathFields((Path*)from, (Path*)newnode);
+    CopyJoinPathFields((JoinPath*)from, (JoinPath*)newnode);
+    
+    /* ----------------
+     * copy the remainder of the node
+     * ----------------
+     */
+    Node_Copy(from, newnode, path_mergeclauses);
+    Node_Copy(from, newnode, outersortkeys);
+    Node_Copy(from, newnode, innersortkeys);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyHashPath
+ * ----------------
+ */
+static HashPath *
+_copyHashPath(HashPath *from)
+{
+    HashPath *newnode = makeNode(HashPath);
+    
+    /* ----------------
+     * copy the node superclass fields
+     * ----------------
+     */
+    CopyPathFields((Path*)from, (Path*)newnode);
+    CopyJoinPathFields((JoinPath*)from, (JoinPath*)newnode);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    Node_Copy(from, newnode, path_hashclauses);
+    Node_Copy(from, newnode, outerhashkeys);
+    Node_Copy(from, newnode, innerhashkeys);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyOrderKey
+ * ----------------
+ */
+static OrderKey *
+_copyOrderKey(OrderKey *from)
+{
+    OrderKey *newnode = makeNode(OrderKey);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    newnode->attribute_number =        from->attribute_number;
+    newnode->array_index =             from->array_index;
+    
+    return newnode;
+}
+
+
+/* ----------------
+ *     _copyJoinKey
+ * ----------------
+ */
+static JoinKey *
+_copyJoinKey(JoinKey *from)
+{
+    JoinKey *newnode = makeNode(JoinKey);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    Node_Copy(from, newnode, outer);
+    Node_Copy(from, newnode, inner);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyMergeOrder
+ * ----------------
+ */
+static MergeOrder *
+_copyMergeOrder(MergeOrder *from)
+{
+    MergeOrder *newnode = makeNode(MergeOrder);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    newnode->join_operator =   from->join_operator;
+    newnode->left_operator =   from->left_operator;
+    newnode->right_operator =  from->right_operator;
+    newnode->left_type =       from->left_type;
+    newnode->right_type =      from->right_type;
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyCInfo
+ * ----------------
+ */
+static CInfo *
+_copyCInfo(CInfo *from)
+{
+    CInfo *newnode = makeNode(CInfo);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    Node_Copy(from, newnode, clause);
+    
+    newnode->selectivity =     from->selectivity;
+    newnode->notclause =       from->notclause;
+    
+    Node_Copy(from, newnode, indexids);
+    Node_Copy(from, newnode, mergesortorder);
+    newnode->hashjoinoperator = from->hashjoinoperator;
+    newnode->cinfojoinid = listCopy(from->cinfojoinid);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     CopyJoinMethodFields
+ *
+ *     This function copies the fields of the JoinMethod node.  It is used by
+ *     all the copy functions for classes which inherit from JoinMethod.
+ * ----------------
+ */
+static void
+CopyJoinMethodFields(JoinMethod *from, JoinMethod *newnode)
+{
+    Node_Copy(from, newnode, jmkeys);
+    Node_Copy(from, newnode, clauses);
+    return;
+}
+
+/* ----------------
+ *     _copyJoinMethod
+ * ----------------
+ */
+static JoinMethod *
+_copyJoinMethod(JoinMethod *from)
+{
+    JoinMethod *newnode = makeNode(JoinMethod);
+    
+    CopyJoinMethodFields(from, newnode);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyHInfo
+ * ----------------
+ */
+static HInfo *
+_copyHInfo(HInfo *from)
+{
+    HInfo *newnode = makeNode(HInfo);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    newnode->hashop = from->hashop;
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyMInfo
+ * ----------------
+ */
+static MInfo *
+_copyMInfo(MInfo *from)
+{
+    MInfo *newnode = makeNode(MInfo);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    Node_Copy(from, newnode, m_ordering);
+    
+    return newnode;
+}
+
+/* ----------------
+ *     _copyJInfo
+ * ----------------
+ */
+static JInfo *
+_copyJInfo(JInfo *from)
+{
+    JInfo *newnode = makeNode(JInfo);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    newnode->otherrels = listCopy(from->otherrels);
+    Node_Copy(from, newnode, jinfoclauseinfo);
+    
+    newnode->mergesortable = from->mergesortable;
+    newnode->hashjoinable =  from->hashjoinable;
+    newnode->inactive =      from->inactive;
+    
+    return newnode;
+}
+
+static Iter *
+_copyIter(Iter *from)
+{
+    Iter *newnode = makeNode(Iter);
+
+    Node_Copy(from, newnode, iterexpr);
+    newnode->itertype = from->itertype;
+    
+    return newnode;
+}
+
+static Stream *
+_copyStream(Stream *from)
+{
+    Stream *newnode = makeNode(Stream);
+    
+    newnode->pathptr = from->pathptr;
+    newnode->cinfo = from->cinfo;
+    newnode->clausetype = from->clausetype;
+    newnode->groupup = from->groupup;
+    newnode->groupcost = from->groupcost;
+    newnode->groupsel = from->groupsel;
+    newnode->upstream = (StreamPtr)NULL;  /* only copy nodes downwards! */
+    Node_Copy(from, newnode, downstream);
+    if (newnode->downstream)
+       ((Stream*)newnode->downstream)->upstream = (Stream*)newnode;
+    
+    return newnode;
+}
+
+/* ****************
+ *           parsenodes.h routines have no copy functions
+ * ****************
+ */
+
+static TargetEntry *
+_copyTargetEntry(TargetEntry *from)
+{
+    TargetEntry *newnode = makeNode(TargetEntry);
+
+    Node_Copy(from, newnode, resdom);
+    Node_Copy(from, newnode, fjoin);
+    Node_Copy(from, newnode, expr);
+    return newnode;
+}
+
+static RangeTblEntry *
+_copyRangeTblEntry(RangeTblEntry *from)
+{
+    RangeTblEntry *newnode = makeNode(RangeTblEntry);
+
+    *newnode = *from;  /* ??? quick hack, be careful */
+    
+    return newnode;
+}
+
+static SortClause *
+_copySortClause(SortClause *from)
+{
+    SortClause *newnode = makeNode(SortClause);
+
+    Node_Copy(from, newnode, resdom);
+    newnode->opoid = from->opoid;
+    
+    return newnode;
+}
+
+static Query *
+_copyQuery(Query *from)
+{
+    Query *newnode = makeNode(Query);
+
+    newnode->commandType = from->commandType;
+    newnode->resultRelation = from->resultRelation;
+    newnode->into = from->into;
+    newnode->isPortal = from->isPortal;
+    Node_Copy(from, newnode, rtable);
+    if (from->utilityStmt && nodeTag(from->utilityStmt) == T_NotifyStmt) {
+       NotifyStmt *from_notify = (NotifyStmt*)from->utilityStmt;
+       NotifyStmt *n = makeNode(NotifyStmt);
+       int length = strlen(from_notify->relname);
+
+       n->relname = palloc(length + 1);
+       strcpy(n->relname,from_notify->relname);
+       newnode->utilityStmt = (Node*)n;
+    }
+    if (from->uniqueFlag) {
+      newnode->uniqueFlag = (char*)palloc(strlen(from->uniqueFlag)+1);
+      strcpy(newnode->uniqueFlag, from->uniqueFlag);
+    }
+    else
+      newnode->uniqueFlag = NULL;
+    Node_Copy(from, newnode, sortClause);
+    Node_Copy(from, newnode, targetList);
+    Node_Copy(from, newnode, qual);
+    
+    return newnode;
+}
+
+
+/* ****************
+ *           mnodes.h routines have no copy functions
+ * ****************
+ */
+
+/* ****************************************************************
+ *                 pg_list.h copy functions
+ * ****************************************************************
+ */
+
+static Value *
+_copyValue(Value *from)
+{
+    Value *newnode = makeNode(Value);
+
+    newnode->type = from->type;
+    switch(from->type) {
+    case T_String:
+       newnode->val.str = pstrdup(from->val.str);
+       break;
+    case T_Integer:
+       newnode->val.ival = from->val.ival;
+       break;
+    case T_Float:
+       newnode->val.dval = from->val.dval;
+       break;
+    default:
+       break;
+    }
+    return newnode;
+}
+
+/* ----------------
+ *     copyObject returns a copy of the node or list. If it is a list, it
+ *      recursively copies its items.
+ * ----------------
+ */
+void *
+copyObject(void *from)
+{
+    void *retval;
+    
+    if (from==NULL)
+       return NULL;
+    switch(nodeTag(from)) {
+       /*
+        * PLAN NODES
+        */
+    case T_Plan:
+       retval = _copyPlan(from);
+       break;
+    case T_Existential:
+       retval = _copyExistential(from);
+       break;
+    case T_Result:
+       retval = _copyResult(from);
+       break;
+    case T_Append:
+       retval = _copyAppend(from);
+       break;
+    case T_Scan:
+       retval = _copyScan(from);
+       break;
+    case T_SeqScan:
+       retval = _copySeqScan(from);
+       break;
+    case T_IndexScan:
+       retval = _copyIndexScan(from);
+       break;
+    case T_Join:
+       retval = _copyJoin(from);
+       break;
+    case T_NestLoop:
+       retval = _copyNestLoop(from);
+       break;
+    case T_MergeJoin:
+       retval = _copyMergeJoin(from);
+       break;
+    case T_HashJoin:
+       retval = _copyHashJoin(from);
+       break;
+    case T_Temp:
+       retval = _copyTemp(from);
+       break;
+    case T_Material:
+       retval = _copyMaterial(from);
+       break;
+    case T_Sort:
+       retval = _copySort(from);
+       break;
+    case T_Agg:
+       retval = _copyAgg(from);
+       break;
+    case T_Unique:
+       retval = _copyUnique(from);
+       break;
+    case T_Hash:
+       retval = _copyHash(from);
+       break;
+
+       /*
+        * PRIMITIVE NODES
+        */
+    case T_Resdom:
+       retval = _copyResdom(from);
+       break;
+    case T_Fjoin:
+       retval = _copyFjoin(from);
+       break;
+    case T_Expr:
+       retval = _copyExpr(from);
+       break;
+    case T_Var:
+       retval = _copyVar(from);
+       break;
+    case T_Oper:
+       retval = _copyOper(from);
+       break;
+    case T_Const:
+       retval = _copyConst(from);
+       break;
+    case T_Param:
+       retval = _copyParam(from);
+       break;
+    case T_Func:
+       retval = _copyFunc(from);
+       break;
+    case T_Array:
+       retval = _copyArray(from);
+       break;
+    case T_ArrayRef:
+       retval = _copyArrayRef(from);
+       break;
+    case T_Aggreg:
+       retval = _copyAggreg(from);
+       break;
+       /*
+        * RELATION NODES
+        */
+    case T_Rel:
+       retval = _copyRel(from);
+       break;
+    case T_Path:
+       retval = _copyPath(from);
+       break;
+    case T_IndexPath:
+       retval = _copyIndexPath(from);
+       break;
+    case T_JoinPath:
+       retval = _copyJoinPath(from);
+       break;
+    case T_MergePath:
+       retval = _copyMergePath(from);
+       break;
+    case T_HashPath:
+       retval = _copyHashPath(from);
+       break;
+    case T_OrderKey:
+       retval = _copyOrderKey(from);
+       break;
+    case T_JoinKey:
+       retval = _copyJoinKey(from);
+       break;
+    case T_MergeOrder:
+       retval = _copyMergeOrder(from);
+       break;
+    case T_CInfo:
+       retval = _copyCInfo(from);
+       break;
+    case T_JoinMethod:
+       retval = _copyJoinMethod(from);
+       break;
+    case T_HInfo:
+       retval = _copyHInfo(from);
+       break;
+    case T_MInfo:
+       retval = _copyMInfo(from);
+       break;
+    case T_JInfo:
+       retval = _copyJInfo(from);
+       break;
+    case T_Iter:
+       retval = _copyIter(from);
+       break;
+    case T_Stream:
+       retval = _copyStream(from);
+       break;
+
+       /*
+        * PARSE NODES
+        */
+    case T_Query:
+       retval = _copyQuery(from);
+       break;
+    case T_TargetEntry:
+       retval = _copyTargetEntry(from);
+       break;
+    case T_RangeTblEntry:
+       retval = _copyRangeTblEntry(from);
+       break;
+    case T_SortClause:
+       retval = _copySortClause(from);
+       break;
+       
+       /*
+        * VALUE NODES
+        */
+    case T_Integer: case T_String: case T_Float:
+       retval = _copyValue(from);
+       break;
+    case T_List:
+       {
+           List *list=from, *l;
+           List *newlist = NIL, *nl;
+           foreach(l, list) {
+               if (newlist==NIL) {
+                   newlist = nl = lcons(copyObject(lfirst(l)),NIL);
+               }else {
+                   lnext(nl) = lcons(copyObject(lfirst(l)),NIL);
+                   nl = lnext(nl);
+               }
+           }
+           retval = newlist;
+       }
+       break;
+    default:
+       elog(NOTICE, "copyObject: don't know how to copy %d", nodeTag(from));
+       retval = from;
+       break;
+    }
+    return retval;
+}
+
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
new file mode 100644 (file)
index 0000000..25783d2
--- /dev/null
@@ -0,0 +1,703 @@
+/*-------------------------------------------------------------------------
+ *
+ * equalfuncs.c--
+ *    equal functions to compare the nodes
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/nodes.h"
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+#include "nodes/execnodes.h"
+#include "nodes/plannodes.h"
+
+#include "utils/builtins.h"            /* for namestrcmp() */
+#include "utils/datum.h"
+#include "utils/elog.h"
+#include "storage/itemptr.h"
+
+/*
+ *  Stuff from primnodes.h
+ */
+
+/*
+ *  Resdom is a subclass of Node.
+ */
+static bool
+_equalResdom(Resdom *a, Resdom *b)
+{
+    if (a->resno != b->resno)
+       return (false);
+    if (a->restype != b->restype)
+       return (false);
+    if (a->reslen != b->reslen)
+       return (false);
+    if (strcmp(a->resname, b->resname) != 0)
+       return (false);
+    if (a->reskey != b->reskey)
+       return (false);
+    if (a->reskeyop != b->reskeyop)
+       return (false);
+    
+    return (true);
+}
+
+static bool
+_equalFjoin(Fjoin *a, Fjoin *b)
+{
+    int nNodes;
+    
+    if (a->fj_initialized != b->fj_initialized)
+       return (false);
+    if (a->fj_nNodes != b->fj_nNodes)
+       return (false);
+    if (!equal(a->fj_innerNode, b->fj_innerNode))
+       return (false);
+    
+    nNodes = a->fj_nNodes;
+    if (memcmp(a->fj_results, b->fj_results, nNodes*sizeof(Datum)) != 0)
+       return (false);
+    if (memcmp(a->fj_alwaysDone, b->fj_alwaysDone, nNodes*sizeof(bool)) != 0)
+       return (false);
+    
+    return(true);
+}
+
+/*
+ *  Expr is a subclass of Node.
+ */
+static bool
+_equalExpr(Expr *a, Expr *b)
+{
+    if (a->opType != b->opType)
+       return (false);
+    if (!equal(a->oper, b->oper))
+       return (false);
+    if (!equal(a->args, b->args))
+       return (false);
+
+    return (true);
+}
+
+bool _equalIter(Iter *a, Iter *b)
+{
+    return (equal(a->iterexpr, b->iterexpr));
+}
+
+static bool
+_equalStream(Stream *a, Stream *b)
+{
+    if (a->clausetype != b->clausetype)
+       return(false);
+    if (a->groupup != b->groupup)
+       return(false);
+    if (a->groupcost != b->groupcost)
+       return(false);
+    if (a->groupsel != b->groupsel)
+       return(false);
+    if (!equal(a->pathptr, b->pathptr))
+       return(false);
+    if (!equal(a->cinfo, b->cinfo))
+       return(false);
+    if (!equal(a->upstream, b->upstream))
+       return(false);
+    return(equal(a->downstream, b->downstream));
+}
+
+/*
+ *  Var is a subclass of Expr.
+ */
+static bool
+_equalVar(Var *a, Var *b)
+{
+    if (a->varno != b->varno)
+       return (false);
+    if (a->varattno != b->varattno)
+       return (false);
+    if (a->vartype != b->vartype)
+       return (false);
+    if (a->varnoold != b->varnoold)
+       return (false);
+    if (a->varoattno != b->varoattno)
+       return (false);
+    
+    return (true);
+}
+
+static bool
+_equalArray(Array *a, Array *b)
+{
+    if (a->arrayelemtype != b->arrayelemtype)
+       return (false);
+    if (a->arrayndim != b->arrayndim)
+       return (false);
+    if (a->arraylow.indx[0] != b->arraylow.indx[0])
+       return (false);
+    if (a->arrayhigh.indx[0] != b->arrayhigh.indx[0])
+       return (false);
+    if (a->arraylen != b->arraylen)
+       return (false);
+    return(TRUE);
+}
+
+static bool
+_equalArrayRef(ArrayRef *a, ArrayRef *b)
+{
+    if (a->refelemtype != b->refelemtype)
+       return (false);
+    if (a->refattrlength != b->refattrlength)
+       return (false);
+    if (a->refelemlength != b->refelemlength)
+       return (false);
+    if (a->refelembyval != b->refelembyval)
+       return (false);
+    if (!equal(a->refupperindexpr, b->refupperindexpr))
+       return (false);
+    if (!equal(a->reflowerindexpr, b->reflowerindexpr))
+       return (false);
+    if (!equal(a->refexpr, b->refexpr))
+       return (false);
+    return (equal(a->refassgnexpr, b->refassgnexpr));
+}
+
+/*
+ *  Oper is a subclass of Expr.
+ */
+static bool
+_equalOper(Oper *a, Oper *b)
+{
+    if (a->opno != b->opno)
+       return (false);
+    if (a->opresulttype != b->opresulttype)
+       return (false);
+    
+    return (true);
+}
+
+/*
+ *  Const is a subclass of Expr.
+ */
+static bool
+_equalConst(Const *a, Const *b)
+{
+    /*
+     ** this function used to do a pointer compare on a and b.  That's
+     ** ridiculous.  -- JMH, 7/11/92
+     */
+    if (a->consttype != b->consttype)
+       return(false);
+    if (a->constlen != b->constlen)
+       return(false);
+    if (a->constisnull != b->constisnull)
+       return(false);
+    if (a->constbyval != b->constbyval)
+       return(false);
+    return(datumIsEqual(a->constvalue, b->constvalue,
+                       a->consttype, a->constbyval, a->constlen));
+}
+
+/*
+ *  Param is a subclass of Expr.
+ */
+static bool
+_equalParam(Param *a, Param *b)
+{
+    if (a->paramkind != b->paramkind)
+       return (false);
+    if (a->paramtype != b->paramtype)
+       return (false);
+    if (!equal(a->param_tlist, b->param_tlist))
+       return (false);
+    
+    switch (a->paramkind) {
+    case PARAM_NAMED:
+    case PARAM_NEW:
+    case PARAM_OLD:
+       if (strcmp(a->paramname, b->paramname) != 0)
+           return (false);
+       break;
+    case PARAM_NUM:
+       if (a->paramid != b->paramid)
+           return (false);
+       break;
+    case PARAM_INVALID:
+       /*
+        * XXX: Hmmm... What are we supposed to return
+        * in this case ??
+        */
+       return(true);
+       break;
+    default:
+       elog(WARN, "_equalParam: Invalid paramkind value: %d",
+            a->paramkind);
+    }
+    
+    return (true);
+}
+
+/*
+ *  Func is a subclass of Expr.
+ */
+static bool
+_equalFunc(Func *a, Func *b)
+{
+    if (a->funcid != b->funcid)
+       return (false);
+    if (a->functype != b->functype)
+       return (false);
+    if (a->funcisindex != b->funcisindex)
+       return (false);
+    if (a->funcsize != b->funcsize)
+       return (false);
+    if (!equal(a->func_tlist, b->func_tlist))
+       return (false);
+    if (!equal(a->func_planlist, b->func_planlist))
+       return (false);
+    
+    return (true);
+}
+
+/*
+ * CInfo is a subclass of Node.
+ */
+static bool
+_equalCInfo(CInfo *a, CInfo *b)
+{
+    Assert(IsA(a,CInfo));
+    Assert(IsA(b,CInfo));
+    
+    if (!equal(a->clause, b->clause))
+       return(false);
+    if (a->selectivity != b->selectivity)
+       return(false);
+    if (a->notclause != b->notclause)
+       return(false);
+#ifdef EqualMergeOrderExists
+    if (!EqualMergeOrder(a->mergesortorder,b->mergesortorder))
+       return(false);
+#endif
+    if(a->hashjoinoperator != b->hashjoinoperator)
+       return(false);
+    return(equal((a->indexids),
+                (b->indexids)));
+}
+
+static bool
+_equalJoinMethod(JoinMethod *a, JoinMethod *b)
+{
+    Assert(IsA(a,JoinMethod));
+    Assert(IsA(b,JoinMethod));
+    
+    if (!equal((a->jmkeys),
+              (b->jmkeys)))
+       return(false);
+    if (!equal((a->clauses),
+              (b->clauses)))
+       return(false);
+    return(true);
+}
+
+static bool
+_equalPath(Path *a, Path *b)
+{
+    if (a->pathtype != b->pathtype)
+       return(false);
+    if (a->parent != b->parent)
+       return(false);
+    /*
+      if (a->path_cost != b->path_cost)
+      return(false);
+      */
+    if (a->p_ordering.ordtype == SORTOP_ORDER) {
+       int i = 0;
+       if (a->p_ordering.ord.sortop==NULL ||
+           b->p_ordering.ord.sortop==NULL) {
+
+           if (a->p_ordering.ord.sortop != b->p_ordering.ord.sortop)
+               return false;
+       } else {
+           while(a->p_ordering.ord.sortop[i]!=0 &&
+                 b->p_ordering.ord.sortop[i]!=0) {
+               if (a->p_ordering.ord.sortop[i] != b->p_ordering.ord.sortop[i])
+                   return false;
+               i++;
+           }
+           if (a->p_ordering.ord.sortop[i]!=0 ||
+               b->p_ordering.ord.sortop[i]!=0)
+               return false;
+       }
+    } else {
+       if (!equal((a->p_ordering.ord.merge), 
+                  (b->p_ordering.ord.merge)))
+           return(false);
+    }
+    if (!equal((a->keys),
+              (b->keys)))
+       return(false);
+    /*
+      if (a->outerjoincost != b->outerjoincost)
+      return(false);
+      */
+    if (!equali((a->joinid),
+               (b->joinid)))
+       return(false);
+    return(true);
+}
+
+static bool
+_equalIndexPath(IndexPath *a, IndexPath *b)
+{
+    if (!_equalPath((Path*)a,(Path*)b))
+       return(false);
+    if (!equali((a->indexid), (b->indexid)))
+       return(false);
+    if (!equal((a->indexqual), (b->indexqual)))
+       return(false);
+    return(true);
+}
+
+static bool
+_equalJoinPath(JoinPath *a,JoinPath *b)
+{
+    Assert(IsA_JoinPath(a));
+    Assert(IsA_JoinPath(b));
+    
+    if (!_equalPath((Path*)a,(Path*)b))
+       return(false);
+    if (!equal((a->pathclauseinfo), (b->pathclauseinfo)))
+       return(false);
+    if (!equal((a->outerjoinpath), (b->outerjoinpath)))
+       return(false);
+    if (!equal((a->innerjoinpath), (b->innerjoinpath)))
+       return(false);
+    return(true);
+}
+
+static bool
+_equalMergePath(MergePath *a, MergePath *b)
+{
+    Assert(IsA(a,MergePath));
+    Assert(IsA(b,MergePath));
+    
+    if (!_equalJoinPath((JoinPath*)a,(JoinPath*)b))
+       return(false);
+    if (!equal((a->path_mergeclauses), (b->path_mergeclauses)))
+       return(false);
+    if (!equal((a->outersortkeys), (b->outersortkeys)))
+       return(false);
+    if (!equal((a->innersortkeys), (b->innersortkeys)))
+       return(false);
+    return(true);
+}
+
+static bool
+_equalHashPath(HashPath *a, HashPath *b)
+{
+    Assert(IsA(a,HashPath));
+    Assert(IsA(b,HashPath));
+    
+    if (!_equalJoinPath((JoinPath*)a,(JoinPath*)b))
+       return(false);
+    if (!equal((a->path_hashclauses), (b->path_hashclauses)))
+       return(false);
+    if (!equal((a->outerhashkeys), (b->outerhashkeys)))
+       return(false);
+    if (!equal((a->innerhashkeys), (b->innerhashkeys)))
+       return(false);
+    return(true);
+}
+
+static bool
+_equalJoinKey(JoinKey *a, JoinKey *b)
+{
+    Assert(IsA(a,JoinKey));
+    Assert(IsA(b,JoinKey));
+    
+    if (!equal((a->outer),(b->outer)))
+       return(false);
+    if (!equal((a->inner),(b->inner)))
+       return(false);
+    return(true);
+}
+
+static bool
+_equalMergeOrder(MergeOrder *a, MergeOrder *b)
+{
+    if (a == (MergeOrder*)NULL && b == (MergeOrder*)NULL)
+       return(true);
+    Assert(IsA(a,MergeOrder));
+    Assert(IsA(b,MergeOrder));
+    
+    if (a->join_operator != b->join_operator)
+       return(false);
+    if (a->left_operator != b->left_operator)
+       return(false);
+    if (a->right_operator != b->right_operator)
+       return(false);
+    if (a->left_type != b->left_type)
+       return(false);
+    if (a->right_type != b->right_type)
+       return(false);
+    return(true);
+}
+
+static bool
+_equalHInfo(HInfo *a, HInfo *b)
+{
+    Assert(IsA(a,HInfo));
+    Assert(IsA(b,HInfo));
+    
+    if (a->hashop != b->hashop)
+       return(false);
+    return(true);
+}
+
+/* XXX  This equality function is a quick hack, should be
+ *      fixed to compare all fields.
+ */
+static bool
+_equalIndexScan(IndexScan *a, IndexScan *b)
+{
+    Assert(IsA(a,IndexScan));
+    Assert(IsA(b,IndexScan));
+    
+    /*
+    if(a->scan.plan.cost != b->scan.plan.cost)
+       return(false);
+    */ 
+    
+    if (!equal((a->indxqual),(b->indxqual)))
+       return(false);
+    
+    if (a->scan.scanrelid != b->scan.scanrelid)
+       return(false);
+    
+    if (!equali((a->indxid),(b->indxid)))
+       return(false);
+    return(true);
+}
+
+static bool
+_equalJInfo(JInfo *a, JInfo *b)
+{
+    Assert(IsA(a,JInfo));
+    Assert(IsA(b,JInfo));
+    if (!equal((a->otherrels),(b->otherrels)))
+       return(false);
+    if (!equal((a->jinfoclauseinfo),(b->jinfoclauseinfo)))
+       return(false);
+    if (a->mergesortable != b->mergesortable)
+       return(false);
+    if (a->hashjoinable != b->hashjoinable)
+       return(false);
+    return(true);
+}
+
+/*
+ *  Stuff from execnodes.h
+ */
+
+/*
+ *  EState is a subclass of Node.
+ */
+static bool
+_equalEState(EState *a, EState *b)
+{
+    if (a->es_direction != b->es_direction)
+       return (false);
+
+    if (!equal(a->es_range_table, b->es_range_table))
+       return (false);
+    
+    if (a->es_result_relation_info != b->es_result_relation_info)
+       return (false);
+    
+    return (true);
+}
+
+
+/*
+ *  equal -- are two lists equal?
+ *
+ *     This is a comparison by value.  It would be simpler to write it
+ *     to be recursive, but it should run faster if we iterate.
+ */
+static bool
+_equalValue(Value *a, Value *b)
+{
+    if (a->type != b->type)
+       return (false);
+
+    switch(a->type) {
+    case T_String:
+       return strcmp(a->val.str, b->val.str);
+    case T_Integer:
+       return (a->val.ival==b->val.ival);
+    case T_Float:
+       return (a->val.dval==b->val.dval);
+    default:
+       break;
+    }
+    
+    return (true);
+}
+
+/*
+ * equal--
+ *    returns whether two nodes are equal
+ */
+bool
+equal(void *a, void *b)
+{
+    bool retval;
+    
+    if (a == b)
+       return(true);
+    /*
+     * note that a!=b, so only one of them can be NULL
+     */
+    if (a==NULL || b==NULL)    
+       return (false);
+    /*
+     * are they the same type of nodes?
+     */
+    if (nodeTag(a)!=nodeTag(b))
+       return (false);
+
+    switch(nodeTag(a)) {
+    case T_Resdom:
+       retval = _equalResdom(a, b);
+       break;
+    case T_Fjoin:
+       retval = _equalFjoin(a, b);
+       break;
+    case T_Expr:
+       retval = _equalExpr(a, b);
+       break;
+    case T_Iter:
+       retval = _equalIter(a, b);
+       break;
+    case T_Stream:
+       retval = _equalStream(a, b);
+       break;
+    case T_Var:
+       retval = _equalVar(a, b);
+       break;
+    case T_Array:
+       retval = _equalArray(a, b);
+       break;
+    case T_ArrayRef:
+       retval = _equalArrayRef(a, b);
+       break;
+    case T_Oper:
+       retval = _equalOper(a, b);
+       break;
+    case T_Const:
+       retval = _equalConst(a, b);
+       break;
+    case T_Param:
+       retval = _equalParam(a, b);
+       break;
+    case T_Func:
+       retval = _equalFunc(a, b);
+       break;
+    case T_CInfo:
+       retval = _equalCInfo(a, b);
+       break;
+    case T_JoinMethod:
+       retval = _equalJoinMethod(a, b);
+       break;
+    case T_Path:
+       retval = _equalPath(a, b);
+       break;
+    case T_IndexPath:
+       retval = _equalIndexPath(a, b);
+       break;
+    case T_JoinPath:
+       retval = _equalJoinPath(a, b);
+       break;
+    case T_MergePath:
+       retval = _equalMergePath(a, b);
+       break;
+    case T_HashPath:
+       retval = _equalHashPath(a, b);
+       break;
+    case T_JoinKey:
+       retval = _equalJoinKey(a, b);
+       break;
+    case T_MergeOrder:
+       retval = _equalMergeOrder(a, b);
+       break;
+    case T_HInfo:
+       retval = _equalHInfo(a, b);
+       break;
+    case T_IndexScan:
+       retval = _equalIndexScan(a, b);
+       break;
+    case T_JInfo:
+       retval = _equalJInfo(a, b);
+       break;
+    case T_EState:
+       retval = _equalEState(a, b);
+       break;
+    case T_Integer: case T_String: case T_Float:
+       retval = _equalValue(a, b);
+       break;
+    case T_List:
+       {
+           List *la = (List*)a;
+           List *lb = (List*)b;
+           List *l;
+
+           if (a==NULL && b==NULL)
+               return (true);
+           if (length(a)!=length(b))
+               return (false);
+           foreach(l, la) {
+               if (!equal(lfirst(l), lfirst(lb)))
+                   return (false);
+               lb = lnext(lb);
+           }
+           retval = true;
+       }
+       break;
+    default:
+       elog(NOTICE, "equal: don't know whether nodes of type %d are equal",
+            nodeTag(a));
+       break;
+    }
+           
+    return retval;
+}
+
+/*
+ * equali--
+ *    compares two lists of integers
+ *
+ * XXX temp hack. needs something like T_IntList
+ */
+bool equali(List *a, List *b)
+{     
+    List *la = (List*)a;
+    List *lb = (List*)b;
+    List *l;
+
+    if (a==NULL && b==NULL)
+       return (true);
+    if (length(a)!=length(b))
+       return (false);
+    foreach(l, la) {
+       if (lfirsti(l) != lfirsti(lb))
+           return (false);
+       lb = lnext(lb);
+    }
+    return true;
+}
diff --git a/src/backend/nodes/execnodes.h b/src/backend/nodes/execnodes.h
new file mode 100644 (file)
index 0000000..2790f67
--- /dev/null
@@ -0,0 +1,689 @@
+/*-------------------------------------------------------------------------
+ *
+ * execnodes.h--
+ *    definitions for executor state nodes
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef EXECNODES_H
+#define EXECNODES_H
+
+#include "postgres.h"
+
+#include "nodes/nodes.h"
+#include "nodes/primnodes.h"
+#include "nodes/pg_list.h"
+
+#include "nodes/memnodes.h"
+
+#include "storage/item.h"
+#include "access/sdir.h"
+#include "access/htup.h"
+#include "access/tupdesc.h"
+#include "access/funcindex.h"
+#include "utils/rel.h"
+#include "access/relscan.h"
+#include "executor/hashjoin.h"
+#include "executor/tuptable.h"
+
+/* ----------------
+ *    IndexInfo information
+ *
+ *      this class holds the information saying what attributes
+ *      are the key attributes for this index. -cim 10/15/89
+ *
+ *      NumKeyAttributes        number of key attributes for this index
+ *      KeyAttributeNumbers     array of attribute numbers used as keys
+ *      Predicate               partial-index predicate for this index
+ * ----------------
+ */
+typedef struct IndexInfo {
+    NodeTag            type;
+    int                 ii_NumKeyAttributes;
+    AttrNumber         *ii_KeyAttributeNumbers;
+    FuncIndexInfoPtr   ii_FuncIndexInfo;
+    Node               *ii_Predicate;
+} IndexInfo;
+
+/* ----------------
+ *    RelationInfo information
+ *
+ *      whenever we update an existing relation, we have to
+ *      update indices on the relation.  The RelationInfo class
+ *      is used to hold all the information on result relations,
+ *      including indices.. -cim 10/15/89
+ *
+ *      RangeTableIndex         result relation's range table index
+ *      RelationDesc            relation descriptor for result relation
+ *      NumIndices              number indices existing on result relation
+ *      IndexRelationDescs      array of relation descriptors for indices
+ *      IndexRelationInfo       array of key/attr info for indices
+ * ----------------
+ */
+typedef struct RelationInfo {
+    NodeTag            type;
+    Index               ri_RangeTableIndex;
+    Relation            ri_RelationDesc;
+    int                 ri_NumIndices;
+    RelationPtr         ri_IndexRelationDescs;
+    IndexInfo           **ri_IndexRelationInfo;
+} RelationInfo;
+
+/* ----------------
+ *    ExprContext
+ *
+ *      This class holds the "current context" information
+ *      needed to evaluate expressions for doing tuple qualifications
+ *     and tuple projections.  For example, if an expression refers
+ *     to an attribute in the current inner tuple then we need to know
+ *     what the current inner tuple is and so we look at the expression
+ *     context.
+ * ----------------
+ */
+typedef struct ExprContext {
+    NodeTag       type;
+    TupleTableSlot *ecxt_scantuple;
+    TupleTableSlot *ecxt_innertuple;
+    TupleTableSlot *ecxt_outertuple;
+    Relation       ecxt_relation;
+    Index          ecxt_relid;
+    ParamListInfo  ecxt_param_list_info;
+    List           *ecxt_range_table;
+    Datum         *ecxt_values;        /* precomputed values for aggreg */
+    char           *ecxt_nulls;         /* null flags for aggreg  values */
+} ExprContext;
+
+/* ----------------
+ *     ProjectionInfo node information
+ *
+ *     This is all the information needed to preform projections
+ *     on a tuple.  Nodes which need to do projections create one
+ *     of these.  In theory, when a node wants to preform a projection
+ *     it should just update this information as necessary and then
+ *     call ExecProject().  -cim 6/3/91
+ *
+ *     targetlist      target list for projection
+ *     len             length of target list
+ *     tupValue        array of pointers to projection results
+ *     exprContext     expression context for ExecTargetList
+ *     slot            slot to place projection result in
+ * ----------------
+ */
+typedef struct ProjectionInfo {
+    NodeTag            type;
+    List               *pi_targetlist;
+    int                        pi_len;
+    Datum              *pi_tupValue;
+    ExprContext                *pi_exprContext;
+    TupleTableSlot     *pi_slot;
+} ProjectionInfo;
+
+/* ----------------
+ *    JunkFilter
+ *
+ *    this class is used to store information regarding junk attributes.
+ *    A junk attribute is an attribute in a tuple that is needed only for
+ *    storing intermediate information in the executor, and does not belong
+ *    in the tuple proper.  For example, when we do a delete or replace
+ *    query, the planner adds an entry to the targetlist so that the tuples
+ *    returned to ExecutePlan() contain an extra attribute: the t_ctid of
+ *    the tuple to be deleted/replaced.  This is needed for amdelete() and
+ *    amreplace().  In doing a delete this does not make much of a
+ *    difference, but in doing a replace we have to make sure we disgard
+ *    all the junk in a tuple before calling amreplace().  Otherwise the
+ *    inserted tuple will not have the correct schema.  This solves a
+ *    problem with hash-join and merge-sort replace plans.  -cim 10/10/90
+ *
+ *    targetList:      the original target list (including junk attributes).
+ *    length:          the length of 'targetList'.
+ *    tupType:         the tuple descriptor for the "original" tuple
+ *                     (including the junk attributes).
+ *    cleanTargetList: the "clean" target list (junk attributes removed).
+ *    cleanLength:     the length of 'cleanTargetList'
+ *    cleanTupTyp:     the tuple descriptor of the "clean" tuple (with
+ *                     junk attributes removed).
+ *    cleanMap:                A map with the correspondance between the non junk
+ *                     attributes of the "original" tuple and the 
+ *                     attributes of the "clean" tuple.
+ * ----------------
+ */
+typedef struct JunkFilter {
+    NodeTag            type;
+    List               *jf_targetList;
+    int                        jf_length;
+    TupleDesc          jf_tupType;
+    List               *jf_cleanTargetList;
+    int                        jf_cleanLength;
+    TupleDesc          jf_cleanTupType;
+    AttrNumber         *jf_cleanMap;
+} JunkFilter;
+
+/* ----------------
+ *    EState information
+ *
+ *      direction                       direction of the scan
+ *
+ *      range_table                     array of scan relation information
+ *
+ *      result_relation_information     for update queries
+ *
+ *      into_relation_descriptor        relation being retrieved "into"
+ *
+ *      param_list_info                 information needed to transform
+ *                                      Param nodes into Const nodes
+ *
+ *      BaseId                          during InitPlan(), each node is
+ *                                      given a number.  this is the next
+ *                                      number to be assigned.
+ *
+ *      tupleTable                      this is a pointer to an array
+ *                                      of pointers to tuples used by
+ *                                      the executor at any given moment.
+ *
+ *     junkFilter                      contains information used to
+ *                                     extract junk attributes from a tuple.
+ *                                     (see JunkFilter above)
+ *
+ *     refcount                        local buffer refcounts used in
+ *                                     an ExecMain cycle.  this is introduced
+ *                                     to avoid ExecStart's unpinning each
+ *                                     other's buffers when called recursively
+ * ----------------    
+ */
+typedef struct EState {
+    NodeTag            type;
+    ScanDirection      es_direction;
+    List                *es_range_table;
+    RelationInfo        *es_result_relation_info;
+    Relation            es_into_relation_descriptor;
+    ParamListInfo       es_param_list_info;
+    int                 es_BaseId;
+    TupleTable          es_tupleTable;
+    JunkFilter         *es_junkFilter;
+    int                        *es_refcount;
+} EState;
+
+/* ----------------
+ *      Executor Type information needed by plannodes.h
+ *
+ *|     Note: the bogus classes CommonState and CommonScanState exist only
+ *|           because our inheritance system only allows single inheritance
+ *|           and we have to have unique slot names.  Hence two or more
+ *|           classes which want to have a common slot must ALL inherit
+ *|           the slot from some other class.  (This is a big hack to
+ *|           allow our classes to share slot names..)
+ *|
+ *|     Example:
+ *|           the class Result and the class NestLoop nodes both want
+ *|           a slot called "OuterTuple" so they both have to inherit
+ *|           it from some other class.  In this case they inherit
+ *|           it from CommonState.  "CommonState" and "CommonScanState" are
+ *|           the best names I could come up with for this sort of
+ *|           stuff.
+ *|
+ *|           As a result, many classes have extra slots which they
+ *|           don't use.  These slots are denoted (unused) in the
+ *|           comment preceeding the class definition.  If you
+ *|           comes up with a better idea of a way of doing things
+ *|           along these lines, then feel free to make your idea
+ *|           known to me.. -cim 10/15/89
+ * ----------------
+ */
+
+/* ----------------------------------------------------------------
+ *               Common Executor State Information
+ * ----------------------------------------------------------------
+ */
+
+/* BaseNode removed -- base_id moved into CommonState       - jolly */
+
+/* ----------------
+ *   CommonState information
+ *
+ *|     this is a bogus class used to hold slots so other
+ *|     nodes can inherit them...
+ *
+ *      OuterTupleSlot     pointer to slot containing current "outer" tuple
+ *      ResultTupleSlot    pointer to slot in tuple table for projected tuple
+ *      ExprContext        node's current expression context
+ *     ProjInfo           info this node uses to form tuple projections
+ *      NumScanAttributes  size of ScanAttributes array
+ *      ScanAttributes     attribute numbers of interest in this tuple
+ *
+ * ----------------
+ */
+typedef struct CommonState {
+    NodeTag             type;             /* its first field is NodeTag */ 
+    int                 cs_base_id; 
+    TupleTableSlot      *cs_OuterTupleSlot;
+    TupleTableSlot      *cs_ResultTupleSlot;
+    ExprContext         *cs_ExprContext;
+    ProjectionInfo      *cs_ProjInfo;
+    bool                cs_TupFromTlist;
+} CommonState;
+
+
+/* ----------------------------------------------------------------
+ *               Control Node State Information
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *   ResultState information
+ *
+ *      done               flag which tells us to quit when we
+ *                         have already returned a constant tuple.
+ *
+ *   CommonState information
+ *
+ *      OuterTupleSlot     pointer to slot containing current "outer" tuple
+ *      ResultTupleSlot    pointer to slot in tuple table for projected tuple
+ *      ExprContext        node's current expression context
+ *     ProjInfo           info this node uses to form tuple projections
+ *      NumScanAttributes  size of ScanAttributes array
+ *      ScanAttributes     attribute numbers of interest in this tuple
+ * ----------------
+ */
+typedef struct ResultState {
+    CommonState                cstate;         /* its first field is NodeTag */
+    int                rs_done;
+} ResultState;
+
+/* ----------------
+ *   AppendState information
+ *
+ *      append nodes have this field "unionplans" which is this
+ *      list of plans to execute in sequence..  these variables
+ *      keep track of things..
+ *
+ *      whichplan       which plan is being executed
+ *      nplans          how many plans are in the list
+ *      initialized     array of ExecInitNode() results
+ *      rtentries       range table for the current plan
+ *      result_relation_info_list  array of each subplan's result relation info
+ *      junkFilter_list  array of each subplan's junk filter
+ *
+ *   CommonState information
+ *
+ *      OuterTupleSlot     pointer to slot containing current "outer" tuple
+ *      ResultTupleSlot    pointer to slot in tuple table for projected tuple
+ *      ExprContext        node's current expression context
+ *     ProjInfo           info this node uses to form tuple projections
+ *      NumScanAttributes  size of ScanAttributes array
+ *      ScanAttributes     attribute numbers of interest in this tuple
+ * ----------------
+ */
+typedef struct AppendState {
+    CommonState                cstate;         /* its first field is NodeTag */
+    int                        as_whichplan;
+    int                        as_nplans;
+    bool               *as_initialized;
+    List               *as_rtentries;
+    List                *as_result_relation_info_list;
+    List                *as_junkFilter_list;
+} AppendState;
+
+/* ----------------------------------------------------------------
+ *               Scan State Information
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *   CommonScanState information
+ *
+ *      CommonScanState is a class like CommonState, but is used more
+ *      by the nodes like SeqScan and Sort which want to
+ *      keep track of an underlying relation.
+ *
+ *      currentRelation    relation being scanned
+ *      currentScanDesc    current scan descriptor for scan
+ *      ScanTupleSlot      pointer to slot in tuple table holding scan tuple
+ *
+ *   CommonState information
+ *
+ *      OuterTupleSlot     pointer to slot containing current "outer" tuple
+ *      ResultTupleSlot    pointer to slot in tuple table for projected tuple
+ *      ExprContext        node's current expression context
+ *     ProjInfo           info this node uses to form tuple projections
+ *      NumScanAttributes  size of ScanAttributes array
+ *      ScanAttributes     attribute numbers of interest in this tuple
+ * ----------------
+ */
+typedef struct CommonScanState {
+    CommonState                cstate;         /* its first field is NodeTag */
+    Relation           css_currentRelation;
+    HeapScanDesc       css_currentScanDesc;
+    TupleTableSlot     *css_ScanTupleSlot;
+} CommonScanState;
+
+/* ----------------
+ *   IndexScanState information
+ *
+ *|     index scans don't use CommonScanState because
+ *|     the underlying AM abstractions for heap scans and
+ *|     index scans are too different..  It would be nice
+ *|     if the current abstraction was more useful but ... -cim 10/15/89
+ *
+ *      IndexPtr           current index in use
+ *      NumIndices         number of indices in this scan
+ *      ScanKeys           Skey structures to scan index rels
+ *      NumScanKeys        array of no of keys in each Skey struct
+ *      RuntimeKeyInfo     array of array of flags for Skeys evaled at runtime
+ *      RelationDescs      ptr to array of relation descriptors
+ *      ScanDescs          ptr to array of scan descriptors
+ *
+ *   CommonState information
+ *
+ *      OuterTupleSlot     pointer to slot containing current "outer" tuple
+ *      ResultTupleSlot    pointer to slot in tuple table for projected tuple
+ *      ExprContext        node's current expression context
+ *     ProjInfo           info this node uses to form tuple projections
+ *      NumScanAttributes  size of ScanAttributes array
+ *      ScanAttributes     attribute numbers of interest in this tuple
+ * ----------------
+ */
+typedef struct IndexScanState {
+    CommonState                cstate;         /* its first field is NodeTag */
+    int                        iss_NumIndices;
+    int                        iss_IndexPtr;
+    ScanKey            *iss_ScanKeys;
+    int                        *iss_NumScanKeys;
+    Pointer            iss_RuntimeKeyInfo;
+    RelationPtr                iss_RelationDescs;
+    IndexScanDescPtr   iss_ScanDescs;
+} IndexScanState;
+
+
+/* ----------------------------------------------------------------
+ *               Join State Information
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *   JoinState information
+ *
+ *   CommonState information
+ *
+ *      OuterTupleSlot     pointer to slot containing current "outer" tuple
+ *      ResultTupleSlot    pointer to slot in tuple table for projected tuple
+ *      ExprContext        node's current expression context
+ *     ProjInfo           info this node uses to form tuple projections
+ *      NumScanAttributes  size of ScanAttributes array
+ *      ScanAttributes     attribute numbers of interest in this tuple
+ * ----------------
+ */
+typedef        CommonState     JoinState;
+
+/* ----------------
+ *   NestLoopState information
+ *
+ *      PortalFlag         Set to enable portals to work.
+ *
+ *   JoinState information
+ *
+ *   CommonState information
+ *
+ *      OuterTupleSlot     pointer to slot containing current "outer" tuple
+ *      ResultTupleSlot    pointer to slot in tuple table for projected tuple
+ *      ExprContext        node's current expression context
+ *     ProjInfo           info this node uses to form tuple projections
+ *      NumScanAttributes  size of ScanAttributes array
+ *      ScanAttributes     attribute numbers of interest in this tuple
+ * ----------------
+ */
+typedef struct NestLoopState {
+    JoinState  jstate;         /* its first field is NodeTag */
+    bool        nl_PortalFlag;
+} NestLoopState;
+
+/* ----------------
+ *   MergeJoinState information
+ *
+ *      OSortopI           outerKey1 sortOp innerKey1 ...
+ *      ISortopO           innerkey1 sortOp outerkey1 ...
+ *      JoinState          current "state" of join. see executor.h
+ *      MarkedTupleSlot    pointer to slot in tuple table for marked tuple
+ *
+ *   JoinState information
+ *
+ *   CommonState information
+ *
+ *      OuterTupleSlot     pointer to slot containing current "outer" tuple
+ *      ResultTupleSlot    pointer to slot in tuple table for projected tuple
+ *      ExprContext        node's current expression context
+ *     ProjInfo           info this node uses to form tuple projections
+ *      NumScanAttributes  size of ScanAttributes array
+ *      ScanAttributes     attribute numbers of interest in this tuple
+ * ----------------
+ */
+typedef struct MergeJoinState {
+    JoinState     jstate;              /* its first field is NodeTag */
+    List           *mj_OSortopI;
+    List           *mj_ISortopO;
+    int            mj_JoinState;
+    TupleTableSlot *mj_MarkedTupleSlot;
+} MergeJoinState;
+
+/* ----------------
+ *   HashJoinState information
+ *
+ *      hj_HashTable                   address of the hash table for the hashjoin
+ *     hj_HashTableShmId       shared memory id of hash table
+ *      hj_CurBucket                   the current hash bucket that we are searching
+ *                             for matches of the current outer tuple
+ *      hj_CurTuple                    the current matching inner tuple in the
+ *                             current hash bucket
+ *     hj_CurOTuple            the current matching inner tuple in the
+ *                             current hash overflow chain
+ *      hj_InnerHashKey        the inner hash key in the hashjoin condition
+ *     hj_OuterBatches         file descriptors for outer batches
+ *     hj_InnerBatches         file descriptors for inner batches
+ *     hj_OuterReadPos         current read position of outer batch
+ *     hj_OuterReadBlk         current read block of outer batch
+ *     hj_OuterTupleSlot       tuple slot for outer tuples
+ *      hj_HashTupleSlot        tuple slot for hashed tuples
+ *
+ *     
+ *
+ *   JoinState information
+ *
+ *   CommonState information
+ *
+ *      OuterTupleSlot     pointer to slot containing current "outer" tuple
+ *      ResultTupleSlot    pointer to slot in tuple table for projected tuple
+ *      ExprContext        node's current expression context
+ *     ProjInfo           info this node uses to form tuple projections
+ *      NumScanAttributes  size of ScanAttributes array
+ *      ScanAttributes     attribute numbers of interest in this tuple
+ * ----------------
+ */
+typedef struct HashJoinState {
+    JoinState          jstate;         /* its first field is NodeTag */
+    HashJoinTable      hj_HashTable;
+    IpcMemoryId                hj_HashTableShmId;
+    HashBucket         hj_CurBucket;
+    HeapTuple          hj_CurTuple;
+    OverflowTuple      hj_CurOTuple;
+    Var                *hj_InnerHashKey;
+    File               *hj_OuterBatches;
+    File               *hj_InnerBatches;
+    char               *hj_OuterReadPos;
+    int                        hj_OuterReadBlk;
+    TupleTableSlot     *hj_OuterTupleSlot;
+    TupleTableSlot     *hj_HashTupleSlot;
+} HashJoinState;
+
+
+/* ----------------------------------------------------------------
+ *               Materialization State Information
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *   MaterialState information
+ *
+ *      materialize nodes are used to materialize the results
+ *      of a subplan into a temporary relation.
+ *
+ *      Flag            indicated whether subplan has been materialized
+ *      TempRelation    temporary relation containing result of executing
+ *                      the subplan.
+ *
+ *   CommonScanState information
+ *
+ *      currentRelation    relation descriptor of sorted relation
+ *      currentScanDesc    current scan descriptor for scan
+ *      ScanTupleSlot      pointer to slot in tuple table holding scan tuple
+ *
+ *   CommonState information
+ *
+ *      OuterTupleSlot     pointer to slot containing current "outer" tuple
+ *      ResultTupleSlot    pointer to slot in tuple table for projected tuple
+ *      ExprContext        node's current expression context
+ *     ProjInfo           info this node uses to form tuple projections
+ *      NumScanAttributes  size of ScanAttributes array
+ *      ScanAttributes     attribute numbers of interest in this tuple
+ * ----------------
+ */
+typedef struct MaterialState {
+    CommonScanState    csstate;        /* its first field is NodeTag */
+    bool               mat_Flag;
+    Relation           mat_TempRelation;
+} MaterialState;
+
+/* ---------------------
+ *  AggregateState information
+ *
+ *      done            indicated whether aggregate has been materialized
+ * -------------------------
+ */
+typedef struct AggState {
+    CommonScanState    csstate;        /* its first field is NodeTag */
+    bool               agg_done;
+} AggState;
+
+/* ---------------------
+ *  GroupState information
+ *
+ * -------------------------
+ */
+typedef struct GroupState {
+    CommonScanState    csstate;        /* its first field is NodeTag */
+    bool               grp_useLastTuple; /* last tuple not processed yet */
+    bool               grp_done;
+    TupleTableSlot     *grp_lastSlot;
+} GroupState;
+
+/* ----------------
+ *   SortState information
+ *
+ *|     sort nodes are really just a kind of a scan since
+ *|     we implement sorts by retrieveing the entire subplan
+ *|     into a temp relation, sorting the temp relation into
+ *|     another sorted relation, and then preforming a simple
+ *|     unqualified sequential scan on the sorted relation..
+ *|     -cim 10/15/89
+ *
+ *      Flag            indicated whether relation has been sorted
+ *      Keys            scan key structures used to keep info on sort keys
+ *      TempRelation    temporary relation containing result of executing
+ *                      the subplan.
+ *
+ *   CommonScanState information
+ *
+ *      currentRelation    relation descriptor of sorted relation
+ *      currentScanDesc    current scan descriptor for scan
+ *      ScanTupleSlot      pointer to slot in tuple table holding scan tuple
+ *
+ *   CommonState information
+ *
+ *      OuterTupleSlot     pointer to slot containing current "outer" tuple
+ *      ResultTupleSlot    pointer to slot in tuple table for projected tuple
+ *      ExprContext        node's current expression context
+ *     ProjInfo           info this node uses to form tuple projections
+ *      NumScanAttributes  size of ScanAttributes array
+ *      ScanAttributes     attribute numbers of interest in this tuple
+ * ----------------
+ */
+typedef struct SortState {
+    CommonScanState    csstate;        /* its first field is NodeTag */
+    bool               sort_Flag;
+    ScanKey            sort_Keys;
+    Relation           sort_TempRelation;
+} SortState;
+
+/* ----------------
+ *   UniqueState information
+ *
+ *      Unique nodes are used "on top of" sort nodes to discard
+ *      duplicate tuples returned from the sort phase.  Basically
+ *      all it does is compare the current tuple from the subplan
+ *      with the previously fetched tuple stored in OuterTuple and
+ *      if the two are identical, then we just fetch another tuple
+ *      from the sort and try again.
+ *
+ *   CommonState information
+ *
+ *      OuterTupleSlot     pointer to slot containing current "outer" tuple
+ *      ResultTupleSlot    pointer to slot in tuple table for projected tuple
+ *      ExprContext        node's current expression context
+ *     ProjInfo           info this node uses to form tuple projections
+ *      NumScanAttributes  size of ScanAttributes array
+ *      ScanAttributes     attribute numbers of interest in this tuple
+ * ----------------
+ */
+typedef        CommonState     UniqueState;
+
+
+/* ----------------
+ *   HashState information
+ *
+ *     hashBatches        file descriptors for the batches
+ *
+ *   CommonState information
+ *
+ *      OuterTupleSlot     pointer to slot containing current "outer" tuple
+ *      ResultTupleSlot    pointer to slot in tuple table for projected tuple
+ *      ExprContext        node's current expression context
+ *     ProjInfo           info this node uses to form tuple projections
+ *      NumScanAttributes  size of ScanAttributes array
+ *      ScanAttributes     attribute numbers of interest in this tuple
+ * ----------------
+ */
+typedef struct HashState {
+    CommonState                cstate; /* its first field is NodeTag */
+    File               *hashBatches;
+} HashState;
+
+/* -----------------------
+ *  TeeState information
+ *    leftPlace  :    next item in the queue unseen by the left parent
+ *    rightPlace :    next item in the queue unseen by the right parent
+ *    lastPlace  :    last item in the queue 
+ *    bufferRelname :  name of the relation used as the buffer queue
+ *    bufferRel     :  the relation used as the buffer queue
+ *    mcxt          :  for now, tee's have their own memory context
+ *                     may be cleaned up later if portals are cleaned up
+ *  
+ * initially, a Tee starts with [left/right]Place variables set to  -1.
+ * on cleanup, queue is free'd when both leftPlace and rightPlace = -1
+ * ------------------------- 
+*/
+typedef struct TeeState {
+    CommonState          cstate; /* its first field is NodeTag */
+    int                  tee_leftPlace;
+    int                  tee_rightPlace;
+    int                  tee_lastPlace;
+    char                 *tee_bufferRelname;
+    Relation             tee_bufferRel;
+    MemoryContext        tee_mcxt;                                  
+    HeapScanDesc         tee_leftScanDesc;
+    HeapScanDesc         tee_rightScanDesc;
+} TeeState;
+
+#endif /* EXECNODES_H */
diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c
new file mode 100644 (file)
index 0000000..e8d4d07
--- /dev/null
@@ -0,0 +1,438 @@
+/*-------------------------------------------------------------------------
+ *
+ * list.c--
+ *    various list handling routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    XXX a few of the following functions are duplicated to handle
+ *       List of pointers and List of integers separately. Some day,
+ *        someone should unify them.           - ay 11/2/94
+ *    This file needs cleanup.
+ *
+ * HISTORY
+ *    AUTHOR           DATE            MAJOR EVENT
+ *    Andrew Yu                Oct, 1994       file creation
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdarg.h>
+#include "postgres.h"
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+#include "utils/builtins.h"    /* for namecpy */
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+List *
+makeList(void *elem, ...)
+{
+    va_list args;
+    List *retval = NIL;
+    List *temp = NIL;
+    List *tempcons = NIL;
+
+    va_start(args, elem);
+
+    temp = elem;
+    while (temp != (void *) -1) {
+       temp = lcons(temp, NIL);
+       if (tempcons == NIL)
+           retval = temp;
+       else
+           lnext(tempcons) = temp;
+       tempcons = temp;
+
+       temp = va_arg(args, void *);
+    }
+
+    va_end(args);
+
+    return (retval);
+}
+
+List *
+lcons(void *datum, List *list)
+{
+    List *l = makeNode(List);
+    lfirst(l) = datum;
+    lnext(l) = list;
+    return l;
+}
+
+List *
+lappend(List *list, void *obj)
+{
+    return nconc(list, lcons(obj, NIL));
+}
+
+Value *
+makeInteger(long i)
+{
+    Value *v = makeNode(Value);
+    v->type = T_Integer;
+    v->val.ival = i;
+    return v;
+}
+
+Value *
+makeFloat(double d)
+{
+    Value *v = makeNode(Value);
+    v->type = T_Float;
+    v->val.dval = d;
+    return v;
+}
+
+Value *
+makeString(char *str)
+{
+    Value *v = makeNode(Value);
+    v->type = T_String;
+    v->val.str = str;
+    return v;
+}
+
+/* n starts with 0 */
+void *
+nth(int n, List *l)
+{
+    /* XXX assume list is long enough */
+    while(n > 0) {
+       l = lnext(l);
+       n--;
+    }
+    return lfirst(l);
+}
+
+/* this is here solely for rt_store. Get rid of me some day! */
+void
+set_nth(List *l, int n, void *elem)
+{
+    /* XXX assume list is long enough */
+    while(n > 0) {
+       l = lnext(l);
+       n--;
+    }
+    lfirst(l) = elem;
+    return;
+}
+
+int
+length(List *l)
+{
+    int i=0;
+    while(l!=NIL) {
+       l = lnext(l);
+       i++;
+    }
+    return i;
+}
+
+void
+freeList(List *list)
+{
+    while(list!=NIL) {
+       List *l = list;
+       list = lnext(list);
+       pfree(l);
+    }
+}
+
+/*
+ * below are for backwards compatibility
+ */
+List *
+append(List *l1, List *l2)
+{
+    List *newlist, *newlist2, *p;
+
+    if (l1==NIL)
+       return copyObject(l2);
+
+    newlist = copyObject(l1);
+    newlist2 = copyObject(l2);
+
+    for (p=newlist; lnext(p)!=NIL; p=lnext(p))
+       ;
+    lnext(p) = newlist2;
+    return newlist;
+}
+
+/*
+ * below are for backwards compatibility
+ */
+List *
+intAppend(List *l1, List *l2)
+{
+    List *newlist, *newlist2, *p;
+
+    if (l1==NIL)
+       return listCopy(l2);
+
+    newlist = listCopy(l1);
+    newlist2 = listCopy(l2);
+
+    for (p=newlist; lnext(p)!=NIL; p=lnext(p))
+       ;
+    lnext(p) = newlist2;
+    return newlist;
+}
+
+List *
+nconc(List *l1, List *l2)
+{
+    List *temp;
+
+    if (l1 == NIL)
+       return l2;
+    if (l2 == NIL)
+       return l1;
+    if (l1 == l2)
+       elog(WARN, "tryout to nconc a list to itself");
+
+    for (temp = l1; lnext(temp)!=NULL; temp = lnext(temp))
+       ;
+    
+    lnext(temp) = l2;
+    return(l1);      /* list1 is now list1[]list2  */
+}
+
+
+List *
+nreverse(List *list)
+{
+    List *rlist = NIL;
+    List *p = NIL;
+    
+    if(list==NULL)
+       return(NIL);
+    
+    if (length(list) == 1)
+       return(list);
+    
+    for (p = list; p!=NULL; p = lnext(p)) {
+       rlist = lcons(lfirst(p),rlist);
+    }
+    
+    lfirst(list) = lfirst(rlist);
+    lnext(list)  = lnext(rlist);
+    return(list);
+}
+
+/*    
+ *     same
+ *    
+ *     Returns t if two lists contain the same elements.
+ *       now defined in lispdep.c
+ *
+ * XXX only good for IntList   -ay
+ */
+bool
+same(List *foo, List *bar)
+{
+    List *temp = NIL;
+    
+    if (foo == NULL)
+       return (bar==NULL);
+    if (bar == NULL)
+       return (foo==NULL);
+    if (length(foo) == length(bar)) {
+       foreach (temp,foo) {
+           if (!intMember((int)lfirst(temp),bar))
+               return(false);
+       }
+       return(true);
+    }
+    return(false);
+    
+}        
+
+List *
+LispUnion(List *foo, List *bar)
+{
+    List *retval = NIL;
+    List *i = NIL;
+    List *j = NIL;
+    
+    if (foo==NIL)
+       return(bar); /* XXX - should be copy of bar */
+    
+    if (bar==NIL)
+       return(foo); /* XXX - should be copy of foo */
+    
+    foreach (i,foo) {
+       foreach (j,bar) {
+           if (! equal(lfirst(i), lfirst(j))) {
+               retval = lappend(retval,lfirst(i));
+               break;
+           }
+       }
+    }
+    foreach(i,bar) {
+       retval = lappend(retval,lfirst(i));
+    }
+    
+    return(retval);
+}
+
+List *
+LispUnioni(List *foo, List *bar)
+{
+    List *retval = NIL;
+    List *i = NIL;
+    List *j = NIL;
+    
+    if (foo==NIL)
+       return(bar); /* XXX - should be copy of bar */
+    
+    if (bar==NIL)
+       return(foo); /* XXX - should be copy of foo */
+    
+    foreach (i,foo) {
+       foreach (j,bar) {
+           if (lfirsti(i) != lfirsti(j)) {
+               retval = lappendi(retval,lfirst(i));
+               break;
+           }
+       }
+    }
+    foreach(i,bar) {
+       retval = lappendi(retval, lfirsti(i));
+    }
+    
+    return(retval);
+}
+
+/*
+ * member()
+ * - nondestructive, returns t iff foo is a member of the list
+ *   bar
+ */
+bool
+member(void *foo, List *bar)
+{
+    List *i;
+    foreach (i,bar)
+       if (equal((Node*)(lfirst(i)),(Node*)foo))
+           return(true);
+    return(false);
+}
+
+bool
+intMember(int foo, List *bar)
+{
+    List *i;
+    foreach (i,bar)
+       if (foo == (int)lfirst(i))
+           return(true);
+    return(false);
+}
+
+/*
+ * lremove -
+ *    only does pointer comparisons. Removes 'elem' from the the linked list.
+ */
+List *
+lremove(void *elem, List *list)
+{
+    List *l;
+    List *prev = NIL;
+    List *result = list;
+
+    foreach(l, list) {
+       if (elem == lfirst(l))
+           break;
+       prev = l;
+    }
+    if (l!=NULL) {
+       if (prev == NIL) {
+           result = lnext(list);
+       } else {
+           lnext(prev) = lnext(l);
+       }
+    }
+    return result;
+}
+       
+List *
+LispRemove(void *elem, List *list)
+{
+    List *temp = NIL;
+    List *prev = NIL;
+    
+    if (equal(elem, lfirst(list)))
+       return lnext(list);
+
+    temp = lnext(list);
+    prev = list;
+    while(temp!=NIL) {
+       if (equal(elem, lfirst(temp))) {
+           lnext(prev) = lnext(temp);
+           break;
+       }
+       temp = lnext(temp);
+       prev = lnext(prev);
+    }
+    return(list);
+}
+
+List *
+intLispRemove(int elem, List *list)
+{
+    List *temp = NIL;
+    List *prev = NIL;
+    
+    if (elem == (int)lfirst(list))
+       return lnext(list);
+
+    temp = lnext(list);
+    prev = list;
+    while(temp!=NIL) {
+       if (elem == (int)lfirst(temp)) {
+           lnext(prev) = lnext(temp);
+           break;
+       }
+       temp = lnext(temp);
+       prev = lnext(prev);
+    }
+    return(list);
+}
+
+List *
+set_difference(List *list1, List *list2)
+{
+    List *temp1 = NIL;
+    List *result = NIL;
+    
+    if (list2==NIL)
+       return(list1); 
+    
+    foreach (temp1, list1) {
+       if (!member(lfirst(temp1), list2))
+           result = lappend(result, lfirst(temp1));
+    }
+    return(result);
+}
+
+List *
+set_differencei(List *list1, List *list2)
+{
+    List *temp1 = NIL;
+    List *result = NIL;
+    
+    if (list2==NIL)
+       return(list1); 
+    
+    foreach (temp1, list1) {
+       if (!intMember(lfirsti(temp1), list2))
+           result = lappendi(result, lfirst(temp1));
+    }
+    return(result);
+}
+
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
new file mode 100644 (file)
index 0000000..99e9772
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * makefuncs.c--
+ *    creator functions for primitive nodes. The functions here are for
+ *    the most frequently created nodes.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    Creator functions in POSTGRES 4.2 are generated automatically. Most of
+ *    them are rarely used. Now we don't generate them any more. If you want
+ *    one, you have to write it yourself.
+ *
+ * HISTORY
+ *    AUTHOR           DATE            MAJOR EVENT
+ *    Andrew Yu                Oct 20, 1994    file creation
+ */
+#include "postgres.h"
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
+#include "nodes/makefuncs.h"
+
+/*
+ * makeOper -
+ *    creates an Oper node
+ */
+Oper *
+makeOper(Oid opno,
+        Oid opid,
+        Oid opresulttype,
+        int opsize,
+        FunctionCachePtr op_fcache)
+{
+    Oper *oper = makeNode(Oper);
+
+    oper->opno = opno;
+    oper->opid = opid;
+    oper->opresulttype = opresulttype;
+    oper->opsize = opsize;
+    oper->op_fcache = op_fcache;
+    return oper;
+}
+
+/*
+ * makeVar -
+ *    creates a Var node
+ *
+ */
+Var *
+makeVar(Index varno, 
+       AttrNumber varattno,
+       Oid vartype,
+       Index varnoold,
+       AttrNumber varoattno)
+{
+    Var *var = makeNode(Var);
+
+    var->varno =  varno;
+    var->varattno = varattno;
+    var->vartype = vartype;
+    var->varnoold =  varnoold;
+    var->varoattno = varoattno;
+
+    return var;
+}
+
+/*
+ * makeResdom -
+ *    creates a Resdom (Result Domain) node
+ */
+Resdom *
+makeResdom(AttrNumber resno,
+          Oid restype,
+          int reslen,
+          char *resname,
+          Index reskey,
+          Oid reskeyop,
+          int resjunk)
+{
+    Resdom *resdom = makeNode(Resdom);
+
+    resdom->resno = resno; 
+    resdom->restype = restype;
+    resdom->reslen = reslen;
+    resdom->resname = resname;
+    resdom->reskey = reskey;
+    resdom->reskeyop = reskeyop;
+    resdom->resjunk = resjunk;
+    return resdom;
+}
+
+/*
+ * makeConst -
+ *    creates a Const node
+ */
+Const *
+makeConst(Oid consttype,
+         Size constlen,
+         Datum constvalue,
+         bool constisnull,
+         bool constbyval,
+         bool constisset)
+{
+    Const *cnst = makeNode(Const);
+
+    cnst->consttype = consttype;
+    cnst->constlen = constlen;
+    cnst->constvalue = constvalue;
+    cnst->constisnull = constisnull;
+    cnst->constbyval = constbyval;
+    cnst->constisset = constisset;
+    return cnst;
+}
+
diff --git a/src/backend/nodes/makefuncs.h b/src/backend/nodes/makefuncs.h
new file mode 100644 (file)
index 0000000..e1701ce
--- /dev/null
@@ -0,0 +1,48 @@
+/*-------------------------------------------------------------------------
+ *
+ * makefuncs.h--
+ *    prototypes for the creator functions (for primitive nodes)
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MAKEFUNC_H
+#define MAKEFUNC_H
+
+#include "access/attnum.h"
+#include "catalog/pg_operator.h"
+#include "utils/fcache.h"
+#include "nodes/primnodes.h"
+
+extern Oper *makeOper(Oid opno,
+                     Oid opid,
+                     Oid opresulttype,
+                     int opsize,
+                     FunctionCachePtr op_fcache);
+
+extern Var *makeVar(Index varno, 
+                   AttrNumber varattno,
+                   Oid vartype,
+                   Index varnoold,
+                   AttrNumber varoattno);
+
+extern Resdom *makeResdom(AttrNumber resno,
+                         Oid restype,
+                         int reslen,
+                         char *resname,
+                         Index reskey,
+                         Oid reskeyop,
+                         int resjunk);
+     
+extern Const *makeConst(Oid consttype,
+                       Size constlen,
+                       Datum constvalue,
+                       bool constisnull,
+                       bool constbyval,
+                       bool constisset);
+
+#endif /* MAKEFUNC_H */
diff --git a/src/backend/nodes/memnodes.h b/src/backend/nodes/memnodes.h
new file mode 100644 (file)
index 0000000..9cf4e0f
--- /dev/null
@@ -0,0 +1,101 @@
+/*-------------------------------------------------------------------------
+ *
+ * memnodes.h--
+ *    POSTGRES memory context node definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * XXX the typedefs in this file are different from the other ???nodes.h;
+ *    they are pointers to structures instead of the structures themselves.
+ *    If you're wondering, this is plain laziness. I don't want to touch
+ *    the memory context code which should be revamped altogether some day.
+ *                                                     - ay 10/94
+ *-------------------------------------------------------------------------
+ */
+#ifndef        MEMNODES_H
+#define MEMNODES_H
+
+#include "c.h"
+
+#include "utils/memutils.h"
+#include "lib/fstack.h"
+
+#include "nodes/nodes.h"
+
+/*
+ * MemoryContext --
+ *     A logical context in which memory allocations occur.
+ *
+ * The types of memory contexts can be thought of as members of the
+ * following inheritance hierarchy with properties summarized below.
+ *
+ *                     Node
+ *                     |
+ *             MemoryContext___
+ *             /               \
+ *     GlobalMemory    PortalMemoryContext
+ *                     /               \
+ *     PortalVariableMemory    PortalHeapMemory
+ *
+ *                     Flushed at      Flushed at      Checkpoints
+ *                     Transaction     Portal
+ *                     Commit          Close
+ *
+ * GlobalMemory                        n               n               n
+ * PortalVariableMemory                n               y               n
+ * PortalHeapMemory            y               y               y
+ */
+
+typedef struct MemoryContextMethodsData {
+    Pointer    (*alloc)();
+    void       (*free_p)(); /* need to use free as a #define,
+                               so can't use free */
+    Pointer    (*realloc)();
+    char*      (*getName)();
+    void       (*dump)();
+} *MemoryContextMethods;
+
+typedef struct MemoryContext {
+    NodeTag                    type;
+    MemoryContextMethods       method;
+} *MemoryContext;
+
+/* think about doing this right some time but we'll have explicit fields
+   for now -ay 10/94 */
+typedef struct GlobalMemory {
+    NodeTag                    type;
+    MemoryContextMethods       method;
+    AllocSetData       setData;
+    char               *name;
+    OrderedElemData    elemData;
+} *GlobalMemory;
+
+typedef MemoryContext *PortalMemoryContext;
+
+typedef struct PortalVariableMemory {
+    NodeTag                    type;
+    MemoryContextMethods       method;
+    AllocSetData       setData;
+} *PortalVariableMemory;
+
+typedef struct PortalHeapMemory {
+    NodeTag                    type;
+    MemoryContextMethods       method;
+    Pointer            block;
+    FixedStackData     stackData;
+} *PortalHeapMemory;
+
+/*
+ * MemoryContextIsValid --
+ *     True iff memory context is valid.
+ */
+#define MemoryContextIsValid(context) \
+    (IsA(context,MemoryContext) || IsA(context,GlobalMemory) || \
+     IsA(context,PortalVariableMemory) || IsA(context,PortalHeapMemory))
+
+#endif /* MEMNODES_H */
+
+
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
new file mode 100644 (file)
index 0000000..8c08d5e
--- /dev/null
@@ -0,0 +1,116 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeFuncs.c--
+ *    All node routines more complicated than simple access/modification
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+
+#include "nodes/primnodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/pg_list.h"
+#include "nodes/relation.h"
+#include "nodes/nodeFuncs.h"
+#include "utils/lsyscache.h"
+
+/*    
+ * single_node -
+ *    Returns t if node corresponds to a single-noded expression
+ */
+bool
+single_node(Node *node)
+{
+    if(IsA(node,Ident) || IsA(node,Const) || IsA(node,Var) || IsA(node,Param)) 
+       return(true);
+    else
+       return(false);
+}
+
+/*****************************************************************************
+ *     VAR nodes
+ *****************************************************************************/
+
+/*    
+ *     var_is_outer
+ *     var_is_inner
+ *     var_is_mat
+ *     var_is_rel
+ *    
+ *     Returns t iff the var node corresponds to (respectively):
+ *     the outer relation in a join
+ *     the inner relation of a join
+ *     a materialized relation
+ *     a base relation (i.e., not an attribute reference, a variable from
+ *             some lower join level, or a sort result)
+ *      var node is an array reference
+ *    
+ */
+bool
+var_is_outer (Var *var)
+{
+    return((bool)(var->varno == OUTER));
+}
+
+bool
+var_is_inner (Var *var)
+{
+    return ( (bool) (var->varno == INNER));
+}
+
+bool
+var_is_rel (Var *var)
+{
+    return (bool)
+       ! (var_is_inner (var) ||  var_is_outer (var));
+}
+
+/*****************************************************************************
+ *     OPER nodes
+ *****************************************************************************/
+
+/*    
+ * replace_opid -
+ *    
+ *     Given a oper node, resets the opfid field with the
+ *     procedure OID (regproc id).
+ *    
+ *     Returns the modified oper node.
+ *    
+ */
+Oper *
+replace_opid (Oper *oper)
+{
+    oper->opid = get_opcode(oper->opno);
+    oper->op_fcache = NULL;
+    return(oper);
+}
+
+/*****************************************************************************
+ *     constant (CONST, PARAM) nodes
+ *****************************************************************************/
+
+/*    
+ * non_null -
+ *     Returns t if the node is a non-null constant, e.g., if the node has a
+ *     valid `constvalue' field.
+ *    
+ */
+bool
+non_null (Expr *c)
+{
+    
+    if ( IsA(c,Const) && ! ((Const*)c)->constisnull )
+       return(true);
+    else
+       return(false);
+}
+
+
+
diff --git a/src/backend/nodes/nodeFuncs.h b/src/backend/nodes/nodeFuncs.h
new file mode 100644 (file)
index 0000000..58b7d04
--- /dev/null
@@ -0,0 +1,23 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeFuncs.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODEFUNCS_H
+#define NODEFUNCS_H
+
+extern bool single_node(Node *node);
+extern bool var_is_outer(Var *var);
+extern bool var_is_inner(Var *var);
+extern bool var_is_rel(Var *var);
+extern Oper *replace_opid(Oper *oper);
+extern bool non_null(Expr *c);
+
+#endif /* NODEFUNCS_H */
diff --git a/src/backend/nodes/nodes.c b/src/backend/nodes/nodes.c
new file mode 100644 (file)
index 0000000..8054612
--- /dev/null
@@ -0,0 +1,45 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodes.c--
+ *    support code for nodes (now that we get rid of the home-brew
+ *    inheritance system, our support code for nodes get much simpler)
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * HISTORY
+ *    Andrew Yu                Oct 20, 1994    file creation
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+#include "utils/palloc.h"
+#include "utils/elog.h"
+#include "nodes/nodes.h"    /* where func declarations of this file goes */
+
+/*
+ * newNode -
+ *    create a new node of the specified size and tag the node with the
+ *    specified tag.
+ *
+ * !WARNING!: Avoid using newNode directly. You should be using the
+ *    macro makeNode. eg. to create a Resdom node, use makeNode(Resdom)
+ *
+ */
+Node *
+newNode(Size size, NodeTag tag)
+{
+    Node *newNode;
+    
+    Assert(size >= 4); /* need the tag, at least */
+    
+    newNode = (Node *)palloc(size);
+    memset((char *)newNode, 0, size);
+    newNode->type = tag;
+    return(newNode);
+}
+
diff --git a/src/backend/nodes/nodes.h b/src/backend/nodes/nodes.h
new file mode 100644 (file)
index 0000000..78cd1d8
--- /dev/null
@@ -0,0 +1,299 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodes.h--
+ *    Definitions for tagged nodes.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODES_H
+#define        NODES_H
+
+#include "c.h"
+
+/*
+ * The first field of every node is NodeTag. Each node created (with makeNode)
+ * will have one of the following tags as the value of its first field.
+ *
+ * Note that the number of the node tags are not contiguous. We left holes
+ * here so that we can add more tags without changing the existing enum's.
+ */
+typedef enum NodeTag {
+    T_Invalid = 0,
+
+    /*---------------------
+     * TAGS FOR PLAN NODES (plannodes.h)
+     *---------------------
+     */
+    T_Plan = 10,
+    T_Existential,
+    T_Result,
+    T_Append,
+    T_Scan,
+    T_SeqScan,
+    T_IndexScan,
+    T_Join,
+    T_NestLoop,
+    T_MergeJoin,
+    T_HashJoin,
+    T_Temp,
+    T_Material,
+    T_Sort,
+    T_Agg,
+    T_Unique,
+    T_Hash,
+    T_Choose,
+    T_Tee,
+    T_Group,
+
+    /*---------------------
+     * TAGS FOR PRIMITIVE NODES (primnodes.h)
+     *---------------------
+     */
+    T_Resdom = 100,
+    T_Fjoin,
+    T_Expr,
+    T_Var,
+    T_Oper,
+    T_Const,
+    T_Param,
+    T_Aggreg,
+    T_Func,
+    T_Array,
+    T_ArrayRef,
+
+    /*---------------------
+     * TAGS FOR INNER PLAN NODES (relation.h)
+     *---------------------
+     */
+    T_Rel = 200,
+    T_Path,
+    T_IndexPath,
+    T_JoinPath,
+    T_MergePath,
+    T_HashPath,
+    T_OrderKey,
+    T_JoinKey,
+    T_MergeOrder,
+    T_CInfo,
+    T_JoinMethod,
+    T_HInfo,
+    T_MInfo,
+    T_JInfo,
+    T_Iter,
+    T_Stream,
+
+    /*---------------------
+     * TAGS FOR EXECUTOR NODES (execnodes.h)
+     *---------------------
+     */
+    T_IndexInfo = 300,
+    T_RelationInfo,
+    T_TupleCount,
+    T_TupleTableSlot,
+    T_ExprContext,
+    T_ProjectionInfo,
+    T_JunkFilter,
+    T_EState,
+    T_BaseNode,
+    T_CommonState,
+    T_ResultState,
+    T_AppendState,
+    T_CommonScanState,
+    T_ScanState,
+    T_IndexScanState,
+    T_JoinState,
+    T_NestLoopState,
+    T_MergeJoinState,
+    T_HashJoinState,
+    T_MaterialState,
+    T_AggState,
+    T_GroupState,
+    T_SortState,
+    T_UniqueState,
+    T_HashState,
+    T_TeeState,
+
+    /*---------------------
+     * TAGS FOR MEMORY NODES (memnodes.h)
+     *---------------------
+     */
+    T_MemoryContext = 400,
+    T_GlobalMemory,
+    T_PortalMemoryContext,
+    T_PortalVariableMemory,
+    T_PortalHeapMemory,
+
+    /*---------------------
+     * TAGS FOR VALUE NODES (pg_list.h)
+     *---------------------
+     */
+    T_Value = 500,
+    T_List,
+    T_Integer,
+    T_Float,
+    T_String,
+    T_Null,
+    
+    /*---------------------
+     * TAGS FOR PARSE TREE NODES (parsenode.h)
+     *---------------------
+     */
+    T_Query = 600,
+    T_AppendStmt,
+    T_DeleteStmt,
+    T_ReplaceStmt,
+    T_CursorStmt,
+    T_RetrieveStmt,
+    T_AddAttrStmt,
+    T_AggregateStmt,
+    T_ChangeACLStmt,
+    T_ClosePortalStmt,
+    T_ClusterStmt,
+    T_CopyStmt,
+    T_CreateStmt,
+    T_VersionStmt,
+    T_DefineStmt,
+    T_DestroyStmt,
+    T_ExtendStmt,
+    T_FetchStmt,
+    T_IndexStmt,
+    T_MoveStmt,
+    T_ProcedureStmt,
+    T_PurgeStmt,
+    T_RecipeStmt,
+    T_RemoveFuncStmt,
+    T_RemoveOperStmt,
+    T_RemoveStmt,
+    T_RenameStmt,
+    T_RuleStmt,
+    T_NotifyStmt,
+    T_ListenStmt,
+    T_TransactionStmt,
+    T_ViewStmt,
+    T_LoadStmt,
+    T_CreatedbStmt,
+    T_DestroydbStmt,
+    T_VacuumStmt,
+    T_ExplainStmt,
+
+    T_A_Expr = 700,
+    T_Attr,
+    T_A_Const,
+    T_ParamNo,
+    T_Ident,
+    T_FuncCall,
+    T_A_Indices,
+    T_ResTarget,
+    T_ParamString,
+    T_TimeRange,
+    T_RelExpr,
+    T_SortBy,
+    T_RangeVar,
+    T_TypeName,
+    T_IndexElem,
+    T_ColumnDef,
+    T_DefElem,
+    T_TargetEntry,
+    T_RangeTblEntry,
+    T_SortClause,
+    T_GroupClause
+} NodeTag;
+
+/*
+ * The first field of a node of any type is gauranteed to be the NodeTag.
+ * Hence the type of any node can be gotten by casting it to Node. Declaring
+ * a variable to be of Node * (instead of void *) can also facilitate
+ * debugging.
+ */
+typedef struct Node {
+    NodeTag    type;   
+} Node;
+
+#define        nodeTag(_node_)         ((Node*)_node_)->type
+
+#define        makeNode(_node_)        (_node_*)newNode(sizeof(_node_),T_##_node_)
+#define NodeSetTag(n, t)       ((Node *)n)->type = t
+
+#define IsA(_node_,_tag_)      (nodeTag(_node_) == T_##_tag_)
+
+/* ----------------------------------------------------------------
+ *                   IsA functions (no inheritence any more)
+ * ----------------------------------------------------------------
+ */
+#define IsA_JoinPath(jp) \
+    (nodeTag(jp)==T_JoinPath || nodeTag(jp)==T_MergePath || \
+     nodeTag(jp)==T_HashPath)
+
+#define IsA_Join(j) \
+    (nodeTag(j)==T_Join || nodeTag(j)==T_NestLoop || \
+     nodeTag(j)==T_MergeJoin || nodeTag(j)==T_HashJoin)
+
+#define IsA_Temp(t) \
+    (nodeTag(t)==T_Temp || nodeTag(t)==T_Material || nodeTag(t)==T_Sort || \
+     nodeTag(t)==T_Unique)
+
+/* ----------------------------------------------------------------
+ *                   extern declarations follow
+ * ----------------------------------------------------------------
+ */
+
+/*
+ * nodes/nodes.c
+ */
+extern Node *newNode(Size size, NodeTag tag);
+
+/*
+ * nodes/{outfuncs.c,print.c}
+ */
+#define nodeDisplay    print
+
+extern char *nodeToString(void *obj);
+extern void print(void *obj);
+
+/*
+ * nodes/{readfuncs.c,read.c}
+ */
+extern void *stringToNode(char *str);
+
+/*
+ * nodes/copyfuncs.c
+ */
+extern void *copyObject(void *obj);
+
+/*
+ * nodes/equalfuncs.c
+ */
+extern bool equal(void *a, void *b);
+
+
+/* ----------------
+ *      I don't know why this is here.  Most likely a hack..
+ *      -cim 6/3/90
+ * ----------------
+ */
+typedef float Cost;
+
+/*
+ * CmdType -
+ *    enums for type of operation to aid debugging
+ *
+ * ??? could have put this in parsenodes.h but many files not in the
+ *    optimizer also need this...
+ */
+typedef enum CmdType {
+    CMD_UNKNOWN,
+    CMD_SELECT,                /* select stmt (formerly retrieve) */
+    CMD_UPDATE,                /* update stmt (formerly replace) */
+    CMD_INSERT,                /* insert stmt (formerly append) */
+    CMD_DELETE,
+    CMD_NOTIFY, 
+    CMD_UTILITY /* cmds like create, destroy, copy, vacuum, etc. */
+} CmdType;
+    
+
+#endif /* NODES_H */
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
new file mode 100644 (file)
index 0000000..41e1e86
--- /dev/null
@@ -0,0 +1,1670 @@
+/*-------------------------------------------------------------------------
+ *
+ * outfuncs.c--
+ *    routines to convert a node to ascii representation
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    Every (plan) node in POSTGRES has an associated "out" routine which
+ *    knows how to create its ascii representation. These functions are
+ *    useful for debugging as well as for storing plans in the system
+ *    catalogs (eg. indexes). This is also the plan string sent out in
+ *    Mariposa.
+ *
+ *    These functions update the in/out argument of type StringInfo
+ *    passed to them. This argument contains the string holding the ASCII
+ *    representation plus some other information (string length, etc.)
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "utils/syscache.h"
+#include "utils/lsyscache.h"
+#include "fmgr.h"
+#include "utils/elog.h"
+#include "utils/datum.h"
+#include "utils/palloc.h"
+
+#include "nodes/nodes.h"
+#include "nodes/execnodes.h"
+#include "nodes/pg_list.h"
+#include "nodes/plannodes.h"
+#include "nodes/parsenodes.h"
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+
+#include "catalog/pg_type.h"
+#include "lib/stringinfo.h"
+
+static void _outDatum(StringInfo str, Datum value, Oid type);
+static void _outNode(StringInfo str, void *obj);
+
+/*
+ * _outIntList -
+ *     converts a List of integers
+ */
+void
+_outIntList(StringInfo str, List *list)
+{
+    List *l;
+    char buf[500];
+
+    appendStringInfo(str, "(");
+    foreach(l, list) {
+       sprintf(buf, "%d ", (int)lfirst(l));
+       appendStringInfo(str, buf);
+    }
+    appendStringInfo(str, ")");
+}
+
+static void
+_outQuery(StringInfo str, Query *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "QUERY");
+    appendStringInfo(str,buf);
+
+    sprintf(buf, " :command %d", node->commandType);
+    appendStringInfo(str,buf);
+    if (node->utilityStmt &&
+       nodeTag(node->utilityStmt) == T_NotifyStmt)
+       sprintf(buf," :utility %s", 
+               ((NotifyStmt*)(node->utilityStmt))->relname);
+    else /* use "" to designate  */
+       sprintf(buf," :utility \"\"");
+    appendStringInfo(str,buf);
+
+    sprintf(buf, " :resrel %d", node->resultRelation);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :rtable ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->rtable);
+    if (node->uniqueFlag)
+      sprintf(buf, " :unique %s", node->uniqueFlag);
+    else /* use "" to designate non-unique */
+      sprintf(buf, " :unique \"\"");
+    appendStringInfo(str,buf);
+    sprintf(buf, " :targetlist ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->targetList);
+    sprintf(buf, " :qual ");
+    appendStringInfo(str,buf); 
+    _outNode(str, node->qual);
+    
+}
+
+/*
+ * print the basic stuff of all nodes that inherit from Plan
+ */
+static void
+_outPlanInfo(StringInfo str, Plan *node)
+{
+    char buf[500];
+    
+    sprintf(buf, " :cost %g", node->cost );
+    appendStringInfo(str,buf);
+    sprintf(buf, " :size %d", node->plan_size);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :width %d", node->plan_width);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :state %s", (node->state == (EState*) NULL ?
+                               "nil" : "non-NIL"));
+    appendStringInfo(str,buf);
+    sprintf(buf, " :qptargetlist ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->targetlist);
+    sprintf(buf, " :qpqual ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->qual);
+    sprintf(buf, " :lefttree ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->lefttree);
+    sprintf(buf, " :righttree ");
+    appendStringInfo(str,buf); 
+    _outNode(str, node->righttree);
+    
+}
+
+/*
+ *  Stuff from plannodes.h
+ */
+static void
+_outPlan(StringInfo str, Plan *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "PLAN");
+    appendStringInfo(str,buf);
+    _outPlanInfo(str, (Plan*) node);
+    
+}
+
+static void
+_outResult(StringInfo str, Result *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "RESULT");
+    appendStringInfo(str,buf);
+    _outPlanInfo(str, (Plan*) node);
+    
+    sprintf(buf, " :resconstantqual ");
+    appendStringInfo(str,buf); 
+    _outNode(str, node->resconstantqual);
+    
+}
+
+/*
+ *  Existential is a subclass of Plan.
+ */
+static void
+_outExistential(StringInfo str, Existential *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "EXISTENTIAL");
+    appendStringInfo(str,buf);
+    _outPlanInfo(str, (Plan*) node);
+    
+    
+}
+
+/*
+ *  Append is a subclass of Plan.
+ */
+static void
+_outAppend(StringInfo str, Append *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "APPEND");
+    appendStringInfo(str,buf);
+    _outPlanInfo(str, (Plan*) node);
+    
+    sprintf(buf, " :unionplans ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->unionplans);
+    
+    sprintf(buf, " :unionrelid %d", node->unionrelid);
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :unionrtentries ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->unionrtentries);
+    
+}
+
+/*
+ *  Join is a subclass of Plan
+ */
+static void
+_outJoin(StringInfo str, Join *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "JOIN");
+    appendStringInfo(str,buf);
+    _outPlanInfo(str, (Plan*) node);
+    
+}
+
+/*
+ *  NestLoop is a subclass of Join
+ */
+static void
+_outNestLoop(StringInfo str, NestLoop *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "NESTLOOP");
+    appendStringInfo(str,buf);
+    _outPlanInfo(str, (Plan*) node);
+}
+
+/*
+ *  MergeJoin is a subclass of Join
+ */
+static void
+_outMergeJoin(StringInfo str, MergeJoin *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "MERGEJOIN");
+    appendStringInfo(str,buf);
+    _outPlanInfo(str, (Plan*) node);
+    
+    sprintf(buf, " :mergeclauses ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->mergeclauses);
+    
+    sprintf(buf, " :mergesortop %d", node->mergesortop);
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :mergerightorder %d", node->mergerightorder[0]);
+    appendStringInfo(str, buf);
+
+    sprintf(buf, " :mergeleftorder %d", node->mergeleftorder[0]);
+    appendStringInfo(str, buf);
+}
+
+/*
+ *  HashJoin is a subclass of Join.
+ */
+static void
+_outHashJoin(StringInfo str, HashJoin *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "HASHJOIN");
+    appendStringInfo(str,buf);
+    _outPlanInfo(str, (Plan*) node);
+    
+    sprintf(buf, " :hashclauses ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->hashclauses);
+    
+    sprintf(buf, " :hashjoinop %d",node->hashjoinop);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :hashjointable 0x%x", (int) node->hashjointable);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :hashjointablekey %d", node->hashjointablekey);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :hashjointablesize %d", node->hashjointablesize);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :hashdone %d", node->hashdone);
+    appendStringInfo(str,buf);
+}
+
+/*
+ *  Scan is a subclass of Node
+ */
+static void
+_outScan(StringInfo str, Scan *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "SCAN");
+    appendStringInfo(str,buf);
+    _outPlanInfo(str, (Plan*) node);
+    
+    sprintf(buf, " :scanrelid %d", node->scanrelid);
+    appendStringInfo(str,buf);
+    
+}
+
+/*
+ *  SeqScan is a subclass of Scan
+ */
+static void
+_outSeqScan(StringInfo str, SeqScan *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "SEQSCAN");
+    appendStringInfo(str,buf);
+    _outPlanInfo(str, (Plan*) node);
+    
+    sprintf(buf, " :scanrelid %d", node->scanrelid);
+    appendStringInfo(str,buf);
+    
+    
+}
+
+/*
+ *  IndexScan is a subclass of Scan
+ */
+static void
+_outIndexScan(StringInfo str, IndexScan *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "INDEXSCAN");
+    appendStringInfo(str,buf);
+    _outPlanInfo(str, (Plan*) node);
+    
+    sprintf(buf, " :scanrelid %d", node->scan.scanrelid);
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :indxid ");
+    appendStringInfo(str,buf);
+    _outIntList(str, node->indxid);
+    
+    sprintf(buf, " :indxqual ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->indxqual);
+    
+}
+
+/*
+ *  Temp is a subclass of Plan
+ */
+static void
+_outTemp(StringInfo str, Temp *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "TEMP");
+    appendStringInfo(str,buf);
+    _outPlanInfo(str, (Plan*) node);
+    
+    sprintf(buf, " :tempid %d", node->tempid);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :keycount %d", node->keycount);
+    appendStringInfo(str,buf);
+    
+}
+
+/*
+ *  Sort is a subclass of Temp
+ */
+static void
+_outSort(StringInfo str, Sort *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "SORT");
+    appendStringInfo(str,buf);
+    _outPlanInfo(str, (Plan*) node);
+    
+    sprintf(buf, " :tempid %d", node->tempid);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :keycount %d", node->keycount);
+    appendStringInfo(str,buf);
+    
+}
+
+static void
+_outAgg(StringInfo str, Agg *node)
+{
+    char buf[500];
+    sprintf(buf, "AGG");
+    appendStringInfo(str,buf);
+    _outPlanInfo(str,(Plan*)node);
+    
+    /* the actual Agg fields */
+    sprintf(buf, " :numagg %d ", node->numAgg);
+    appendStringInfo(str, buf);
+}
+
+static void
+_outGroup(StringInfo str, Group *node)
+{
+     char buf[500];
+     sprintf(buf, "GRP");
+     appendStringInfo(str,buf);
+     _outPlanInfo(str,(Plan*)node);
+     
+     /* the actual Group fields */
+     sprintf(buf, " :numCols %d ", node->numCols);
+     appendStringInfo(str, buf);
+     sprintf(buf, " :tuplePerGroup %s", node->tuplePerGroup ? "true" : "nil");
+     appendStringInfo(str, buf);
+}
+  
+
+/*
+ *  For some reason, unique is a subclass of Temp.
+ */
+static void
+_outUnique(StringInfo str, Unique *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "UNIQUE");
+    appendStringInfo(str,buf);
+    _outPlanInfo(str, (Plan*) node);
+    
+    sprintf(buf, " :tempid %d", node->tempid);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :keycount %d", node->keycount);
+    appendStringInfo(str,buf);
+    
+}
+
+
+/*
+ *  Hash is a subclass of Temp
+ */
+static void
+_outHash(StringInfo str, Hash *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "HASH");
+    appendStringInfo(str,buf);
+    _outPlanInfo(str, (Plan*) node);
+    
+    sprintf(buf, " :hashkey ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->hashkey);
+    
+    sprintf(buf, " :hashtable 0x%x", (int) (node->hashtable));
+    appendStringInfo(str,buf);
+    sprintf(buf, " :hashtablekey %d", node->hashtablekey);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :hashtablesize %d", node->hashtablesize);
+    appendStringInfo(str,buf);
+}
+
+static void
+_outTee(StringInfo str, Tee *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "TEE");
+    appendStringInfo(str,buf);
+    _outPlanInfo(str, (Plan*) node);
+    
+    sprintf(buf, " :leftParent %X", (int) (node->leftParent));
+    appendStringInfo(str,buf);
+    sprintf(buf, " :rightParent %X", (int) (node->rightParent));
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :rtentries ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->rtentries);
+}
+
+
+
+/*****************************************************************************
+ *
+ *  Stuff from primnodes.h.
+ *
+ *****************************************************************************/
+
+
+/*
+ *  Resdom is a subclass of Node
+ */
+static void
+_outResdom(StringInfo str, Resdom *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "RESDOM");
+    appendStringInfo(str,buf);
+    sprintf(buf, " :resno %hd", node->resno);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :restype %d", node->restype);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :reslen %d", node->reslen);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :resname \"%.*s\"", NAMEDATALEN,
+           ((node->resname) ? ((char *) node->resname) : "null"));
+    appendStringInfo(str,buf);
+    sprintf(buf, " :reskey %d", node->reskey);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :reskeyop %ld", (long int) node->reskeyop);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :resjunk %d", node->resjunk);
+    appendStringInfo(str,buf);
+    
+}
+
+static void
+_outFjoin(StringInfo str, Fjoin *node)
+{
+    char buf[500];
+    int i;
+    
+    sprintf(buf, "FJOIN");
+    appendStringInfo(str,buf);
+    sprintf(buf, " :initialized %s", node->fj_initialized ? "true":"nil");
+    appendStringInfo(str,buf);
+    sprintf(buf, " :nNodes %d", node->fj_nNodes);
+    appendStringInfo(str,buf);
+
+    appendStringInfo(str," :innerNode ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->fj_innerNode);
+
+    sprintf(buf, " :results @  0x%x ", (int)(node->fj_results));
+    appendStringInfo(str, buf);
+    
+    appendStringInfo( str, " :alwaysdone ");
+    for (i = 0; i<node->fj_nNodes; i++)
+       {
+           sprintf(buf, " %s ", ((node->fj_alwaysDone[i]) ? "true" : "nil"));
+           appendStringInfo(str, buf);
+       }
+}
+
+/*
+ *  Expr is a subclass of Node
+ */
+static void
+_outExpr(StringInfo str, Expr *node)
+{
+    char buf[500];
+    char *opstr;
+    
+    sprintf(buf, "EXPR");
+    appendStringInfo(str,buf);
+
+    sprintf(buf, " :typeOid %d", node->typeOid);
+    appendStringInfo(str,buf);
+    switch(node->opType) {
+    case OP_EXPR:
+       opstr = "op";
+       break;
+    case FUNC_EXPR:
+       opstr = "func";
+       break;
+    case OR_EXPR:
+       opstr = "or";
+       break;
+    case AND_EXPR:
+       opstr = "and";
+       break;
+    case NOT_EXPR:
+       opstr = "not";
+       break;
+    }
+    sprintf(buf, " :opType %s", opstr);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :oper ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->oper);
+    sprintf(buf, " :args ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->args);
+}
+
+/*
+ *  Var is a subclass of Expr
+ */
+static void
+_outVar(StringInfo str, Var *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "VAR");
+    appendStringInfo(str,buf);
+    sprintf(buf, " :varno %d", node->varno);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :varattno %hd", node->varattno);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :vartype %d", node->vartype);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :varnoold %d", node->varnoold);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :varoattno %d", node->varoattno);
+    appendStringInfo(str,buf);
+}
+
+/*
+ *  Const is a subclass of Expr
+ */
+static void
+_outConst(StringInfo str, Const *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "CONST");
+    appendStringInfo(str,buf);
+    sprintf(buf, " :consttype %d", node->consttype);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :constlen %hd", node->constlen);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :constisnull %s", (node->constisnull ? "true" : "nil"));
+    appendStringInfo(str,buf);
+    sprintf(buf, " :constvalue ");
+    appendStringInfo(str,buf);
+    if (node->constisnull) {
+       sprintf(buf, "NIL ");
+       appendStringInfo(str,buf);
+    } else {
+       _outDatum(str, node->constvalue, node->consttype);
+    }
+    sprintf(buf, " :constbyval %s", (node->constbyval ? "true" : "nil"));
+    appendStringInfo(str,buf);
+    
+}
+
+/*
+ *  Aggreg
+ */
+static void
+_outAggreg(StringInfo str, Aggreg *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "AGGREG");
+    appendStringInfo(str,buf);
+    sprintf(buf, " :aggname \"%.*s\"", NAMEDATALEN, (char*)node->aggname);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :basetype %d", node->basetype);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :aggtype %d", node->aggtype);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :aggno %d", node->aggno);
+    appendStringInfo(str,buf);
+
+    sprintf(buf, " :target ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->target);
+}
+
+/*
+ *  Array is a subclass of Expr
+ */
+static void
+_outArray(StringInfo str, Array *node)
+{
+    char buf[500];
+    int i;
+    sprintf(buf, "ARRAY");
+    appendStringInfo(str, buf);
+    sprintf(buf, " :arrayelemtype %d", node->arrayelemtype);
+    appendStringInfo(str, buf);
+    sprintf(buf, " :arrayelemlength %d", node->arrayelemlength);
+    appendStringInfo(str, buf);
+    sprintf(buf, " :arrayelembyval %c", (node->arrayelembyval) ? 't' : 'f');
+    appendStringInfo(str, buf);
+    sprintf(buf, " :arrayndim %d", node->arrayndim);
+    appendStringInfo(str, buf);
+    sprintf(buf, " :arraylow ");
+    appendStringInfo(str, buf);
+    for (i = 0; i < node->arrayndim; i++){
+       sprintf(buf, "  %d", node->arraylow.indx[i]);
+       appendStringInfo(str, buf);
+    }
+    sprintf(buf, " :arrayhigh ");
+    appendStringInfo(str, buf);
+    for (i = 0; i < node->arrayndim; i++){
+       sprintf(buf, " %d", node->arrayhigh.indx[i]);
+       appendStringInfo(str, buf);
+    }
+    sprintf(buf, " :arraylen %d", node->arraylen);
+    appendStringInfo(str, buf);
+}
+
+/*
+ *  ArrayRef is a subclass of Expr
+ */
+static void
+_outArrayRef(StringInfo str, ArrayRef *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "ARRAYREF");
+    appendStringInfo(str, buf);
+    sprintf(buf, " :refelemtype %d", node->refelemtype);
+    appendStringInfo(str, buf);
+    sprintf(buf, " :refattrlength %d", node->refattrlength);
+    appendStringInfo(str, buf);
+    sprintf(buf, " :refelemlength %d", node->refelemlength);
+    appendStringInfo(str, buf);
+    sprintf(buf, " :refelembyval %c", (node->refelembyval) ? 't' : 'f');
+    appendStringInfo(str, buf);
+
+    sprintf(buf, " :refupperindex ");
+    appendStringInfo(str, buf);
+    _outNode(str, node->refupperindexpr);
+
+    sprintf(buf, " :reflowerindex ");
+    appendStringInfo(str, buf);
+    _outNode(str, node->reflowerindexpr);
+
+    sprintf(buf, " :refexpr ");
+    appendStringInfo(str, buf);
+    _outNode(str, node->refexpr);
+
+    sprintf(buf, " :refassgnexpr ");
+    appendStringInfo(str, buf);
+    _outNode(str, node->refassgnexpr);
+}
+
+/*
+ *  Func is a subclass of Expr
+ */
+static void
+_outFunc(StringInfo str, Func *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "FUNC");
+    appendStringInfo(str,buf);
+    sprintf(buf, " :funcid %d", node->funcid);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :functype %d", node->functype);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :funcisindex %s",
+           (node->funcisindex ? "true" : "nil"));
+    appendStringInfo(str,buf);
+    sprintf(buf, " :funcsize %d", node->funcsize);
+    appendStringInfo(str, buf);
+    sprintf(buf, " :func_fcache @ 0x%x", (int)(node->func_fcache));
+    appendStringInfo(str, buf);
+
+    appendStringInfo(str, " :func_tlist ");
+    _outNode(str, node->func_tlist);
+
+    appendStringInfo(str, " :func_planlist ");
+    _outNode(str, node->func_planlist);
+}
+
+/*
+ *  Oper is a subclass of Expr
+ */
+static void
+_outOper(StringInfo str, Oper *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "OPER");
+    appendStringInfo(str,buf);
+    sprintf(buf, " :opno %d", node->opno);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :opid %d", node->opid);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :opresulttype %d", node->opresulttype);
+    appendStringInfo(str,buf);
+    
+}
+
+/*
+ *  Param is a subclass of Expr
+ */
+static void
+_outParam(StringInfo str, Param *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "PARAM");
+    appendStringInfo(str,buf);
+    sprintf(buf, " :paramkind %d", node->paramkind);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :paramid %hd", node->paramid);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :paramname \"%.*s\"", NAMEDATALEN, node->paramname);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :paramtype %d", node->paramtype);
+    appendStringInfo(str,buf);
+    
+    appendStringInfo(str, " :param_tlist ");
+    _outNode(str, node->param_tlist);
+}
+
+/*
+ *  Stuff from execnodes.h
+ */
+
+/*
+ *  EState is a subclass of Node.
+ */
+static void
+_outEState(StringInfo str, EState *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "ESTATE");
+    appendStringInfo(str,buf);
+    sprintf(buf, " :direction %d", node->es_direction);
+    appendStringInfo(str,buf);
+
+    sprintf(buf, " :range_table ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->es_range_table);
+    
+    sprintf(buf, " :result_relation_info @ 0x%x",
+           (int) (node->es_result_relation_info));
+    appendStringInfo(str,buf);
+    
+}
+
+/*
+ *  Stuff from relation.h
+ */
+static void
+_outRel(StringInfo str, Rel *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "REL");
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :relids ");
+    appendStringInfo(str,buf);
+    _outIntList(str, node->relids);
+    
+    sprintf(buf, " :indexed %s", (node->indexed ? "true" : "nil"));
+    appendStringInfo(str,buf);
+    sprintf(buf, " :pages %u", node->pages);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :tuples %u", node->tuples);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :size %u", node->size);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :width %u", node->width);
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :targetlist ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->targetlist);
+    
+    sprintf(buf, " :pathlist ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->pathlist);
+    
+    /*
+     *  Not sure if these are nodes or not.  They're declared as
+     *  struct Path *.  Since i don't know, i'll just print the
+     *  addresses for now.  This can be changed later, if necessary.
+     */
+    
+    sprintf(buf, " :unorderedpath @ 0x%x", (int)(node->unorderedpath));
+    appendStringInfo(str,buf);
+    sprintf(buf, " :cheapestpath @ 0x%x", (int)(node->cheapestpath));
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :pruneable %s", (node->pruneable ? "true" : "nil"));
+    appendStringInfo(str,buf);
+    
+#if 0
+    sprintf(buf, " :classlist ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->classlist);
+    
+    sprintf(buf, " :indexkeys ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->indexkeys);
+    
+    sprintf(buf, " :ordering ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->ordering);
+#endif    
+
+    sprintf(buf, " :clauseinfo ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->clauseinfo);
+    
+    sprintf(buf, " :joininfo ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->joininfo);
+    
+    sprintf(buf, " :innerjoin ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->innerjoin);
+    
+}
+
+/*
+ *  TargetEntry is a subclass of Node.
+ */
+static void
+_outTargetEntry(StringInfo str, TargetEntry *node)
+{
+    char buf[500];
+  
+    sprintf(buf, "TLE");
+    appendStringInfo(str,buf);
+    sprintf(buf, " :resdom ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->resdom);
+  
+    sprintf(buf, " :expr ");
+    appendStringInfo(str,buf);
+    if (node->expr) {
+       _outNode(str, node->expr);
+    }else {
+       appendStringInfo(str, "nil");
+    }
+} 
+
+static void
+_outRangeTblEntry(StringInfo str, RangeTblEntry *node)
+{
+    char buf[500];
+  
+    sprintf(buf, "RTE");
+    appendStringInfo(str,buf);
+
+    sprintf(buf, " :relname \"%.*s\"", NAMEDATALEN,
+           ((node->relname) ? ((char *) node->relname) : "null"));
+    appendStringInfo(str,buf);
+
+    sprintf(buf, " :inh %d ", node->inh);
+    appendStringInfo(str,buf);
+  
+    sprintf(buf, " :refname \"%.*s\"", NAMEDATALEN,
+           ((node->refname) ? ((char *) node->refname) : "null"));
+    appendStringInfo(str,buf);
+
+    sprintf(buf, " :relid %d ", node->relid);
+    appendStringInfo(str,buf);
+} 
+
+/*
+ *  Path is a subclass of Node.
+ */
+static void
+_outPath(StringInfo str, Path *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "PATH");
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :pathtype %d", node->pathtype);
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :cost %f", node->path_cost);
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :keys ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->keys);
+    
+}
+
+/*
+ *  IndexPath is a subclass of Path.
+ */
+static void
+_outIndexPath(StringInfo str, IndexPath *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "INDEXPATH");
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :pathtype %d", node->path.pathtype);
+    appendStringInfo(str,buf);
+    
+    /* sprintf(buf, " :parent ");
+       appendStringInfo(str,buf);
+       _outNode(str, node->parent); */
+    
+    sprintf(buf, " :cost %f", node->path.path_cost);
+    appendStringInfo(str,buf);
+    
+#if 0
+    sprintf(buf, " :p_ordering ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->path.p_ordering);
+#endif    
+    sprintf(buf, " :keys ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->path.keys);
+    
+    sprintf(buf, " :indexid ");
+    appendStringInfo(str,buf);
+    _outIntList(str, node->indexid);
+    
+    sprintf(buf, " :indexqual ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->indexqual);
+    
+}
+
+/*
+ *  JoinPath is a subclass of Path
+ */
+static void
+_outJoinPath(StringInfo str, JoinPath *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "JOINPATH");
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :pathtype %d", node->path.pathtype);
+    appendStringInfo(str,buf);
+    
+    /* sprintf(buf, " :parent ");
+       appendStringInfo(str,buf);
+       _outNode(str, node->parent); */
+    
+    sprintf(buf, " :cost %f", node->path.path_cost);
+    appendStringInfo(str,buf);
+    
+#if 0
+    sprintf(buf, " :p_ordering ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->path.p_ordering);
+#endif    
+    sprintf(buf, " :keys ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->path.keys);
+    
+    sprintf(buf, " :pathclauseinfo ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->pathclauseinfo);
+    
+    /*
+     *  Not sure if these are nodes; they're declared as "struct path *".
+     *  For now, i'll just print the addresses.
+     */
+    
+    sprintf(buf, " :outerjoinpath @ 0x%x", (int)(node->outerjoinpath));
+    appendStringInfo(str,buf);
+    sprintf(buf, " :innerjoinpath @ 0x%x", (int)(node->innerjoinpath));
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :outerjoincost %f", node->path.outerjoincost);
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :joinid ");
+    appendStringInfo(str,buf);
+    _outIntList(str, node->path.joinid);
+    
+}
+
+/*
+ *  MergePath is a subclass of JoinPath.
+ */
+static void
+_outMergePath(StringInfo str, MergePath *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "MERGEPATH");
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :pathtype %d", node->jpath.path.pathtype);
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :cost %f", node->jpath.path.path_cost);
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :keys ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->jpath.path.keys);
+    
+    sprintf(buf, " :pathclauseinfo ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->jpath.pathclauseinfo);
+    
+    /*
+     *  Not sure if these are nodes; they're declared as "struct path *".
+     *  For now, i'll just print the addresses.
+     */
+    
+    sprintf(buf, " :outerjoinpath @ 0x%x", (int)(node->jpath.outerjoinpath));
+    appendStringInfo(str,buf);
+    sprintf(buf, " :innerjoinpath @ 0x%x", (int)(node->jpath.innerjoinpath));
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :outerjoincost %f", node->jpath.path.outerjoincost);
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :joinid ");
+    appendStringInfo(str,buf);
+    _outIntList(str, node->jpath.path.joinid);
+    
+    sprintf(buf, " :path_mergeclauses ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->path_mergeclauses);
+    
+    sprintf(buf, " :outersortkeys ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->outersortkeys);
+    
+    sprintf(buf, " :innersortkeys ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->innersortkeys);
+    
+}
+
+/*
+ *  HashPath is a subclass of JoinPath.
+ */
+static void
+_outHashPath(StringInfo str, HashPath *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "HASHPATH");
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :pathtype %d", node->jpath.path.pathtype);
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :cost %f", node->jpath.path.path_cost);
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :keys ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->jpath.path.keys);
+    
+    sprintf(buf, " :pathclauseinfo ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->jpath.pathclauseinfo);
+    
+    /*
+     *  Not sure if these are nodes; they're declared as "struct path *".
+     *  For now, i'll just print the addresses.
+     */
+    
+    sprintf(buf, " :outerjoinpath @ 0x%x", (int) (node->jpath.outerjoinpath));
+    appendStringInfo(str,buf);
+    sprintf(buf, " :innerjoinpath @ 0x%x", (int) (node->jpath.innerjoinpath));
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :outerjoincost %f", node->jpath.path.outerjoincost);
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :joinid ");
+    appendStringInfo(str,buf);
+    _outIntList(str, node->jpath.path.joinid);
+    
+    sprintf(buf, " :path_hashclauses ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->path_hashclauses);
+    
+    sprintf(buf, " :outerhashkeys ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->outerhashkeys);
+    
+    sprintf(buf, " :innerhashkeys ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->innerhashkeys);
+    
+}
+
+/*
+ *  OrderKey is a subclass of Node.
+ */
+static void
+_outOrderKey(StringInfo str, OrderKey *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "ORDERKEY");
+    appendStringInfo(str,buf);
+    sprintf(buf, " :attribute_number %d", node->attribute_number);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :array_index %d", node->array_index);
+    appendStringInfo(str,buf);
+    
+}
+
+/*
+ *  JoinKey is a subclass of Node.
+ */
+static void
+_outJoinKey(StringInfo str, JoinKey *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "JOINKEY");
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :outer ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->outer);
+    
+    sprintf(buf, " :inner ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->inner);
+    
+}
+
+/*
+ *  MergeOrder is a subclass of Node.
+ */
+static void
+_outMergeOrder(StringInfo str, MergeOrder *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "MERGEORDER");
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :join_operator %d", node->join_operator);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :left_operator %d", node->left_operator);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :right_operator %d", node->right_operator);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :left_type %d", node->left_type);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :right_type %d", node->right_type);
+    appendStringInfo(str,buf);
+    
+}
+
+/*
+ *  CInfo is a subclass of Node.
+ */
+static void
+_outCInfo(StringInfo str, CInfo *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "CINFO");
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :clause ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->clause);
+    
+    sprintf(buf, " :selectivity %f", node->selectivity);
+    appendStringInfo(str,buf);
+    sprintf(buf, " :notclause %s", (node->notclause ? "true" : "nil"));
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :indexids ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->indexids);
+    
+    sprintf(buf, " :mergesortorder ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->mergesortorder);
+    
+    sprintf(buf, " :hashjoinoperator %d", node->hashjoinoperator);
+    appendStringInfo(str,buf);
+    
+}
+
+/*
+ *  JoinMethod is a subclass of Node.
+ */
+static void
+_outJoinMethod(StringInfo str, JoinMethod *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "JOINMETHOD");
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :jmkeys ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->jmkeys);
+    
+    sprintf(buf, " :clauses ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->clauses);
+    
+    
+}
+
+/*
+ * HInfo is a subclass of JoinMethod.
+ */
+static void
+_outHInfo(StringInfo str, HInfo *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "HASHINFO");
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :hashop ");
+    appendStringInfo(str,buf);
+    sprintf(buf, "%d",node->hashop);
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :jmkeys ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->jmethod.jmkeys);
+    
+    sprintf(buf, " :clauses ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->jmethod.clauses);
+    
+}
+
+/*
+ *  JInfo is a subclass of Node.
+ */
+static void
+_outJInfo(StringInfo str, JInfo *node)
+{
+    char buf[500];
+    
+    sprintf(buf, "JINFO");
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :otherrels ");
+    appendStringInfo(str,buf);
+    _outIntList(str, node->otherrels);
+    
+    sprintf(buf, " :jinfoclauseinfo ");
+    appendStringInfo(str,buf);
+    _outNode(str, node->jinfoclauseinfo);
+    
+    sprintf(buf, " :mergesortable %s",
+           (node->mergesortable ? "true" : "nil"));
+    appendStringInfo(str,buf);
+    sprintf(buf, " :hashjoinable %s",
+           (node->hashjoinable ? "true" : "nil"));
+    appendStringInfo(str,buf);
+    
+}
+
+/*
+ * Print the value of a Datum given its type.
+ */
+static void
+_outDatum(StringInfo str, Datum value, Oid type)
+{
+    char buf[500];
+    Size length, typeLength;
+    bool byValue;
+    int i;
+    char *s;
+    
+    /*
+     * find some information about the type and the "real" length
+     * of the datum.
+     */
+    byValue = get_typbyval(type);
+    typeLength = get_typlen(type);
+    length = datumGetSize(value, type, byValue, typeLength);
+    
+    if (byValue) {
+       s = (char *) (&value);
+       sprintf(buf, " %d [ ", length);
+       appendStringInfo(str,buf);
+       for (i=0; i<sizeof(Datum); i++) {
+           sprintf(buf, "%d ", (int) (s[i]) );
+           appendStringInfo(str,buf);
+       }
+       sprintf(buf, "] ");
+       appendStringInfo(str,buf);
+    } else { /* !byValue */
+       s = (char *) DatumGetPointer(value);
+       if (!PointerIsValid(s)) {
+           sprintf(buf, " 0 [ ] ");
+           appendStringInfo(str,buf);
+       } else {
+           /*
+            * length is unsigned - very bad to do < comparison to -1 without
+            * casting it to int first!! -mer 8 Jan 1991
+            */
+           if (((int)length) <= -1) {
+               length = VARSIZE(s);
+           }
+           sprintf(buf, " %d [ ", length);
+           appendStringInfo(str,buf);
+           for (i=0; i<length; i++) {
+               sprintf(buf, "%d ", (int) (s[i]) );
+               appendStringInfo(str,buf);
+           }
+           sprintf(buf, "] ");
+           appendStringInfo(str,buf);
+       }
+    }
+    
+}
+
+static void
+_outIter(StringInfo str, Iter *node)
+{
+    appendStringInfo(str,"ITER");
+    
+    appendStringInfo(str," :iterexpr ");
+    _outNode(str, node->iterexpr);
+}
+
+static void
+_outStream(StringInfo str, Stream *node)
+{
+    char buf[500];
+    
+    appendStringInfo(str,"STREAM");
+    
+    sprintf(buf, " :pathptr @ 0x%x", (int)(node->pathptr));
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :cinfo @ 0x%x", (int)(node->cinfo));
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :clausetype %d", (int)(node->clausetype));
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :upstream @ 0x%x", (int)(node->upstream));
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :downstream @ 0x%x", (int)(node->downstream));
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :groupup %d", node->groupup);
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :groupcost %f", node->groupcost);
+    appendStringInfo(str,buf);
+    
+    sprintf(buf, " :groupsel %f", node->groupsel);
+    appendStringInfo(str,buf);
+}    
+
+static void
+_outValue(StringInfo str, Value *value)
+{
+    char buf[500];
+            
+    switch(value->type) {
+    case T_String:
+       sprintf(buf, "\"%s\"", value->val.str);
+       appendStringInfo(str, buf);
+       break;
+    case T_Integer:
+       sprintf(buf, "%ld", value->val.ival);
+       appendStringInfo(str, buf);
+       break;
+    case T_Float:
+       sprintf(buf, "%f", value->val.dval);
+       appendStringInfo(str, buf);
+       break;
+    default:
+       break;
+    }
+    return;
+}
+
+/*
+ * _outNode -
+ *    converts a Node into ascii string and append it to 'str'
+ */
+static void
+_outNode(StringInfo str, void *obj)
+{
+    if (obj==NULL) {
+       appendStringInfo(str, "nil");
+       return;
+    }
+
+    if (nodeTag(obj)==T_List) {
+       List *l;
+       appendStringInfo(str, "(");
+       foreach(l, (List*)obj) {
+           _outNode(str, lfirst(l));
+           if (lnext(l))
+               appendStringInfo(str, " ");
+       }
+       appendStringInfo(str, ")");
+    }else {
+       appendStringInfo(str, "{");
+       switch(nodeTag(obj)) {
+       case T_Query:
+           _outQuery(str, obj);
+           break;
+       case T_Plan:
+           _outPlan(str, obj);
+           break;
+       case T_Result:
+           _outResult(str, obj);
+           break;
+       case T_Existential:
+           _outExistential(str, obj);
+           break;
+       case T_Append:
+           _outAppend(str, obj);
+           break;
+       case T_Join:
+           _outJoin(str, obj);
+           break;
+       case T_NestLoop:
+           _outNestLoop(str, obj);
+           break;
+       case T_MergeJoin:
+           _outMergeJoin(str, obj);
+           break;
+       case T_HashJoin:
+           _outHashJoin(str, obj);
+           break;
+       case T_Scan:
+           _outScan(str, obj);
+           break;
+       case T_SeqScan:
+           _outSeqScan(str, obj);
+           break;
+       case T_IndexScan:
+           _outIndexScan(str, obj);
+           break;
+       case T_Temp:
+           _outTemp(str, obj);
+           break;
+       case T_Sort:
+           _outSort(str, obj);
+           break;
+       case T_Agg:
+           _outAgg(str, obj);
+           break;
+       case T_Group:
+           _outGroup(str, obj);
+           break;
+       case T_Unique:
+           _outUnique(str, obj);
+           break;
+       case T_Hash:
+           _outHash(str, obj);
+           break;
+       case T_Tee:
+           _outTee(str, obj);
+           break;
+       case T_Resdom:
+           _outResdom(str, obj);
+           break;
+       case T_Fjoin:
+           _outFjoin(str, obj);
+           break;
+       case T_Expr:
+           _outExpr(str, obj);
+           break;
+       case T_Var:
+           _outVar(str, obj);
+           break;
+       case T_Const:
+           _outConst(str, obj);
+           break;
+       case T_Aggreg:
+           _outAggreg(str, obj);
+           break;
+       case T_Array:
+           _outArray(str, obj);
+           break;
+       case T_ArrayRef:
+           _outArrayRef(str, obj);
+           break;
+       case T_Func:
+           _outFunc(str, obj);
+           break;
+       case T_Oper:
+           _outOper(str, obj);
+           break;
+       case T_Param:
+           _outParam(str, obj);
+           break;
+       case T_EState:
+           _outEState(str, obj);
+           break;
+       case T_Rel:
+           _outRel(str, obj);
+           break;
+       case T_TargetEntry:
+           _outTargetEntry(str, obj);
+           break;
+       case T_RangeTblEntry:
+           _outRangeTblEntry(str, obj);
+           break;
+       case T_Path:
+           _outPath(str, obj);
+           break;
+       case T_IndexPath:
+           _outIndexPath (str, obj);
+           break;
+       case T_JoinPath:
+           _outJoinPath(str, obj);
+           break;
+       case T_MergePath:
+           _outMergePath(str, obj);
+           break;
+       case T_HashPath:
+           _outHashPath(str, obj);
+           break;
+       case T_OrderKey:
+           _outOrderKey(str, obj);
+           break;
+       case T_JoinKey:
+           _outJoinKey(str, obj);
+           break;
+       case T_MergeOrder:
+           _outMergeOrder(str, obj);
+           break;
+       case T_CInfo:
+           _outCInfo(str, obj);
+           break;
+       case T_JoinMethod:
+           _outJoinMethod(str, obj);
+           break;
+       case T_HInfo:
+           _outHInfo(str, obj);
+           break;
+       case T_JInfo:
+           _outJInfo(str, obj);
+           break;
+       case T_Iter:
+           _outIter(str, obj);
+           break;
+       case T_Stream:
+           _outStream(str, obj);
+           break;
+       case T_Integer: case T_String: case T_Float:
+           _outValue(str, obj);
+           break;
+       default:
+           elog(NOTICE, "_outNode: don't know how to print type %d",
+                nodeTag(obj));
+           break;
+       }
+       appendStringInfo(str, "}");
+    }
+    return;
+}
+
+/*
+ * nodeToString -
+ *     returns the ascii representation of the Node
+ */
+char *
+nodeToString(void *obj)
+{
+    StringInfo str;
+    char *s;
+    
+    if (obj==NULL)
+       return "";
+    Assert(obj!=NULL);
+    str = makeStringInfo();
+    _outNode(str, obj);
+    s = str->data;
+    pfree(str);
+
+    return s;
+}
diff --git a/src/backend/nodes/params.h b/src/backend/nodes/params.h
new file mode 100644 (file)
index 0000000..79ae945
--- /dev/null
@@ -0,0 +1,90 @@
+/*-------------------------------------------------------------------------
+ *
+ * params.h--
+ *    Declarations/definitions of stuff needed to handle parameterized plans.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PARAMS_H
+#define PARAMS_H
+
+#include "postgres.h"
+#include "access/attnum.h"
+
+/* ----------------------------------------------------------------
+ *
+ * The following are the possible values for the 'paramkind'
+ * field of a Param node.
+ *    
+ * PARAM_NAMED: The parameter has a name, i.e. something
+ *              like `$.salary' or `$.foobar'.
+ *              In this case field `paramname' must be a valid Name.
+ *              and field `paramid' must be == 0.
+ *
+ * PARAM_NUM:   The parameter has only a numeric identifier,
+ *              i.e. something like `$1', `$2' etc.
+ *              The number is contained in the `parmid' field.
+ *
+ * PARAM_NEW:   Used in PRS2 rule, similar to PARAM_NAMED.
+ *              The `paramname' & `paramid' refer to the "NEW" tuple
+ *             `paramname' is the attribute name and `paramid' its
+ *             attribute number.
+ *              
+ * PARAM_OLD:   Same as PARAM_NEW, but in this case we refer to
+ *             the "OLD" tuple.
+ */
+
+#define PARAM_NAMED    11
+#define PARAM_NUM      12
+#define PARAM_NEW      13
+#define PARAM_OLD      14
+#define PARAM_INVALID   100
+
+
+/* ----------------------------------------------------------------
+ *    ParamListInfo
+ *
+ *    Information needed in order for the executor to handle
+ *    parameterized plans (you know,  $.salary, $.name etc. stuff...).
+ *
+ *    ParamListInfoData contains information needed when substituting a
+ *    Param node with a Const node.
+ *
+ *     kind   : the kind of parameter.
+ *      name   : the parameter name (valid if kind == PARAM_NAMED,
+ *               PARAM_NEW or PARAM_OLD)
+ *      id     : the parameter id (valid if kind == PARAM_NUM)
+ *              or the attrno (if kind == PARAM_NEW or PARAM_OLD)
+ *      type   : PG_TYPE OID of the value
+ *      length : length in bytes of the value
+ *      isnull : true if & only if the value is null (if true then
+ *               the fields 'length' and 'value' are undefined).
+ *      value  : the value that has to be substituted in the place
+ *               of the parameter.
+ *
+ *   ParamListInfo is to be used as an array of ParamListInfoData
+ *   records. An 'InvalidName' in the name field of such a record
+ *   indicates that this is the last record in the array.
+ *
+ * ----------------------------------------------------------------
+ */
+
+typedef struct ParamListInfoData {
+    int                        kind;
+    char               *name;
+    AttrNumber         id;
+    Oid                        type;
+    Size               length;
+    bool               isnull;
+    bool               byval;
+    Datum              value;
+} ParamListInfoData;
+
+typedef ParamListInfoData *ParamListInfo;
+
+#endif /* PARAMS_H */
diff --git a/src/backend/nodes/parsenodes.h b/src/backend/nodes/parsenodes.h
new file mode 100644 (file)
index 0000000..3109cda
--- /dev/null
@@ -0,0 +1,731 @@
+/*-------------------------------------------------------------------------
+ *
+ * parsenodes.h--
+ *    definitions for parse tree nodes
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        PARSENODES_H
+#define        PARSENODES_H
+
+#include "nodes/nodes.h"
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
+#include "utils/tqual.h"
+
+/*****************************************************************************
+ *  Query Tree 
+ *****************************************************************************/
+
+/*
+ * Query -
+ *    all statments are turned into a Query tree (via transformStmt)
+ *    for further processing by the optimizer
+ *    utility statements (i.e. non-optimizable statements)
+ *    have the *utilityStmt field set.
+ *
+ * we need the isPortal flag because portal names can be null too; can
+ * get rid of it if we support CURSOR as a commandType.
+ *
+ */
+typedef struct Query {
+    NodeTag    type;
+    
+    CmdType    commandType;    /* select|insert|update|delete|utility */
+    
+    Node       *utilityStmt;   /* non-null if this is a non-optimizable
+                                  statement */
+    
+    int                resultRelation; /* target relation (index to rtable) */
+    char       *into;          /* portal (cursor) name */
+    bool       isPortal;       /* is this a retrieve into portal? */
+    bool       isBinary;       /* binary portal? */
+    
+    char       *uniqueFlag;    /* NULL, '*', or Unique attribute name */
+    List       *sortClause;    /* a list of SortClause's */
+    
+    List       *rtable;        /* list of range table entries */
+    List       *targetList;    /* target list (of TargetEntry) */
+    Node       *qual;          /* qualifications */
+
+    List       *groupClause;   /* list of columns to specified in GROUP BY */
+    Node       *havingQual;    /* qualification of each group */
+
+    int                qry_numAgg;     /* number of aggregates in the target list */
+    Aggreg     **qry_aggs;     /* the aggregates */
+    
+    /* internal to planner */
+    List *base_relation_list_; /* base relation list */
+    List *join_relation_list_; /* list of relations generated by joins */
+    bool  query_is_archival_;  /* archival query flag */
+} Query;
+
+
+/*****************************************************************************
+ *     Other Statements (no optimizations required)
+ *
+ *      Some of them require a little bit of transformation (which is also
+ *      done by transformStmt). The whole structure is then passed on to
+ *      ProcessUtility (by-passing the optimization step) as the utilityStmt
+ *      field in Query.
+ *****************************************************************************/
+
+/* ----------------------
+ *     Add Column Statement
+ * ----------------------
+ */
+typedef struct AddAttrStmt {
+    NodeTag            type;
+    char               *relname;       /* the relation to add attr */
+    bool               inh;            /* add recursively to children? */
+    struct ColumnDef   *colDef;        /* the attribute definition */
+} AddAttrStmt;
+
+/* ----------------------
+ *     Change ACL Statement
+ * ----------------------
+ */
+typedef struct ChangeACLStmt {
+    NodeTag            type;
+    struct AclItem     *aclitem;
+    unsigned           modechg;
+    List               *relNames;
+} ChangeACLStmt;
+
+/* ----------------------
+ *     Close Portal Statement
+ * ----------------------
+ */
+typedef struct ClosePortalStmt {
+    NodeTag            type;
+    char               *portalname;    /* name of the portal (cursor) */
+} ClosePortalStmt;
+
+/* ----------------------
+ *     Copy Statement
+ * ----------------------
+ */
+typedef struct CopyStmt {
+    NodeTag            type;
+    bool               binary;         /* is a binary copy? */
+    char               *relname;       /* the relation to copy */
+    int                        direction;      /* TO or FROM */
+    char               *filename;      /* if NULL, use stdin/stdout */
+    char                *delimiter;     /* delimiter character, \t by default*/
+} CopyStmt;
+
+/* ----------------------
+ *     Create Table Statement
+ * ----------------------
+ */
+typedef enum ArchType {
+    ARCH_NONE, ARCH_LIGHT, ARCH_HEAVY  /* archive mode */
+} ArchType;
+
+typedef struct CreateStmt {
+    NodeTag            type;
+    char               *relname;       /* the relation to create */
+    List               *tableElts;     /* column definitions
+                                          list of ColumnDef */
+    List               *inhRelnames;   /* relations to inherit from
+                                          list of Value (string) */
+    ArchType           archiveType;    /* archive mode (ARCH_NONE if none */
+    int                        location;       /* smgrid (-1 if none) */
+    int                        archiveLoc;     /* smgrid (-1 if none) */
+} CreateStmt;
+
+/* ----------------------
+ *     Create Version Statement
+ * ----------------------
+ */
+typedef struct VersionStmt {
+    NodeTag            type;
+    char               *relname;       /* the new relation */
+    int                        direction;      /* FORWARD | BACKWARD */
+    char               *fromRelname;   /* relation to create a version */
+    char               *date;          /* date of the snapshot */
+} VersionStmt;
+
+/* ----------------------
+ *     Create {Operator|Type|Aggregate} Statement
+ * ----------------------
+ */
+typedef struct DefineStmt {
+    NodeTag            type;
+    int                        defType;        /* OPERATOR|P_TYPE|AGGREGATE*/
+    char               *defname;
+    List               *definition;    /* a list of DefElem */
+} DefineStmt;
+
+/* ----------------------
+ *     Drop Table Statement
+ * ----------------------
+ */
+typedef struct DestroyStmt {
+    NodeTag            type;
+    List               *relNames;      /* relations to be dropped */
+} DestroyStmt;
+
+/* ----------------------
+ *     Extend Index Statement
+ * ----------------------
+ */
+typedef struct ExtendStmt {
+    NodeTag            type;
+    char               *idxname;       /* name of the index */
+    Node               *whereClause;   /* qualifications */
+    List               *rangetable;    /* range table, filled in
+                                          by transformStmt() */
+} ExtendStmt;
+
+/* ----------------------
+ *     Begin Recipe Statement
+ * ----------------------
+ */
+typedef struct RecipeStmt {
+    NodeTag            type;
+    char               *recipeName;    /* name of the recipe*/
+} RecipeStmt;
+
+/* ----------------------
+ *     Fetch Statement
+ * ----------------------
+ */
+typedef struct FetchStmt {
+    NodeTag            type;
+    int                        direction;      /* FORWARD or BACKWARD */
+    int                        howMany;        /* amount to fetch ("ALL" --> 0) */
+    char               *portalname;    /* name of portal (cursor) */
+} FetchStmt;
+
+/* ----------------------
+ *     Create Index Statement
+ * ----------------------
+ */
+typedef struct IndexStmt {
+    NodeTag            type;
+    char               *idxname;       /* name of the index */
+    char               *relname;       /* name of relation to index on */
+    char               *accessMethod;  /* name of acess methood (eg. btree) */
+    List               *indexParams;   /* a list of IndexElem */
+    List               *withClause;    /* a list of ParamString */
+    Node               *whereClause;   /* qualifications */
+    List               *rangetable;    /* range table, filled in
+                                          by transformStmt() */
+} IndexStmt;
+
+/* ----------------------
+ *     Move Statement (Not implemented)
+ * ----------------------
+ */
+typedef struct MoveStmt {
+    NodeTag            type;
+    int                        direction;      /* FORWARD or BACKWARD */
+    bool               to;
+    int                        where;
+    char               *portalname;
+} MoveStmt;
+
+/* ----------------------
+ *     Create Function Statement
+ * ----------------------
+ */
+typedef struct ProcedureStmt {
+    NodeTag            type;
+    char               *funcname;      /* name of function to create */
+    List               *defArgs;       /* list of definitions
+                                          a list of strings (as Value *) */
+    Node               *returnType;    /* the return type (as a string or
+                                          a TypeName (ie.setof) */
+    List               *withClause;    /* a list of ParamString */
+    char               *as;            /* the SQL statement or filename */
+    char               *language;      /* C or SQL */
+} ProcedureStmt;
+
+/* ----------------------
+ *     Purge Statement
+ * ----------------------
+ */
+typedef struct PurgeStmt {
+    NodeTag            type;
+    char               *relname;       /* relation to purge */
+    char               *beforeDate;    /* purge before this date */
+    char               *afterDate;     /* purge after this date */
+} PurgeStmt;
+
+/* ----------------------
+ *     Drop Function Statement
+ * ----------------------
+ */
+typedef struct RemoveFuncStmt {
+    NodeTag            type;
+    char               *funcname;      /* function to drop */
+    List               *args;          /* types of the arguments */
+} RemoveFuncStmt;
+
+/* ----------------------
+ *     Drop Operator Statement
+ * ----------------------
+ */
+typedef struct RemoveOperStmt {
+    NodeTag            type;
+    char               *opname;        /* operator to drop */
+    List               *args;          /* types of the arguments */
+} RemoveOperStmt;
+
+/* ----------------------
+ *     Drop {Aggregate|Type|Index|Rule|View} Statement
+ * ----------------------
+ */
+typedef struct RemoveStmt {
+    NodeTag            type;
+    int                removeType;     /* AGGREGATE|P_TYPE|INDEX|RULE|VIEW */
+    char               *name;          /* name to drop */
+} RemoveStmt;
+
+/* ----------------------
+ *     Alter Table Statement
+ * ----------------------
+ */
+typedef struct RenameStmt {
+    NodeTag            type;
+    char               *relname;       /* relation to be altered */
+    bool               inh;            /* recursively alter children? */
+    char               *column;        /* if NULL, rename the relation name
+                                          to the new name. Otherwise, rename
+                                          this column name. */
+    char               *newname;       /* the new name */
+} RenameStmt;
+
+/* ----------------------
+ *     Create Rule Statement
+ * ----------------------
+ */
+typedef struct RuleStmt {
+    NodeTag            type;
+    char               *rulename;      /* name of the rule */
+    Node               *whereClause;   /* qualifications */
+    CmdType            event;          /* RETRIEVE */
+    struct Attr                *object;        /* object affected */
+    bool               instead;        /* is a 'do instead'? */
+    List               *actions;       /* the action statements */
+} RuleStmt;
+
+/* ----------------------
+ *     Notify Statement
+ * ----------------------
+ */
+typedef struct NotifyStmt {
+    NodeTag            type;
+    char               *relname;       /* relation to notify */
+} NotifyStmt;
+
+/* ----------------------
+ *     Listen Statement
+ * ----------------------
+ */
+typedef struct ListenStmt {
+    NodeTag            type;
+    char               *relname;       /* relation to listen on */
+} ListenStmt;
+
+/* ----------------------
+ *     {Begin|Abort|End} Transaction Statement
+ * ----------------------
+ */
+typedef struct TransactionStmt {
+    NodeTag            type;
+    int                        command;        /* BEGIN|END|ABORT */
+} TransactionStmt;
+
+/* ----------------------
+ *     Create View Statement
+ * ----------------------
+ */
+typedef struct ViewStmt {
+    NodeTag            type;
+    char               *viewname;      /* name of the view */
+    Query              *query;         /* the SQL statement */
+} ViewStmt;
+
+/* ----------------------
+ *     Load Statement
+ * ----------------------
+ */
+typedef struct LoadStmt {
+    NodeTag            type;
+    char               *filename;      /* file to load */
+} LoadStmt;
+
+/* ----------------------
+ *     Createdb Statement
+ * ----------------------
+ */
+typedef struct CreatedbStmt {
+    NodeTag            type;
+    char               *dbname;        /* database to create */
+} CreatedbStmt;
+
+/* ----------------------
+ *     Destroydb Statement
+ * ----------------------
+ */
+typedef struct DestroydbStmt {
+    NodeTag            type;
+    char               *dbname;        /* database to drop */
+} DestroydbStmt;
+
+/* ----------------------
+ *     Cluster Statement (support pbrown's cluster index implementation)
+ * ----------------------
+ */
+typedef struct ClusterStmt {
+    NodeTag            type;
+    char               *relname;       /* relation being indexed */
+    char               *indexname;     /* original index defined */
+} ClusterStmt;
+
+/* ----------------------
+ *     Vacuum Statement
+ * ----------------------
+ */
+typedef struct VacuumStmt {
+    NodeTag            type;
+    char               *vacrel;        /* table to vacuum */
+} VacuumStmt;
+
+/* ----------------------
+ *     Explain Statement
+ * ----------------------
+ */
+typedef struct ExplainStmt {
+    NodeTag            type;
+    Query              *query;         /* the query */
+    List               *options;
+} ExplainStmt;
+
+
+/*****************************************************************************
+ *     Optimizable Statements
+ *****************************************************************************/
+
+/* ----------------------
+ *     Insert Statement
+ * ----------------------
+ */
+typedef struct AppendStmt {
+    NodeTag            type;
+    char               *relname;       /* relation to insert into */
+    List               *cols;          /* names of the columns */
+    List               *exprs;         /* the expressions (same order as
+                                          the columns) */
+    List               *fromClause;    /* the from clause */
+    Node               *whereClause;   /* qualifications */
+} AppendStmt;
+
+/* ----------------------
+ *     Delete Statement
+ * ----------------------
+ */
+typedef struct DeleteStmt {
+    NodeTag            type;
+    char               *relname;       /* relation to delete from */
+    Node               *whereClause;   /* qualifications */
+} DeleteStmt;
+
+/* ----------------------
+ *     Update Statement
+ * ----------------------
+ */
+typedef struct ReplaceStmt {
+    NodeTag            type;
+    char               *relname;       /* relation to update */
+    List               *targetList;    /* the target list (of ResTarget) */
+    Node               *whereClause;   /* qualifications */
+    List               *fromClause;    /* the from clause */
+} ReplaceStmt;
+
+/* ----------------------
+ *     Create Cursor Statement
+ * ----------------------
+ */
+typedef struct CursorStmt {
+    NodeTag            type;
+    char               *portalname;    /* the portal (cursor) to create */
+    bool               binary;         /* a binary (internal) portal? */
+    char               *unique;        /* NULL, "*", or unique attribute name */
+    List               *targetList;    /* the target list (of ResTarget) */
+    List               *fromClause;    /* the from clause */
+    Node               *whereClause;   /* qualifications */
+    List               *orderClause;   /* sort clause (a list of SortBy's) */
+} CursorStmt;    
+
+/* ----------------------
+ *     Select Statement
+ * ----------------------
+ */
+typedef struct RetrieveStmt {
+    NodeTag            type;
+    char                *unique;  /* NULL, '*', or unique attribute name */
+    char               *into;          /* name of table (for select into
+                                          table) */
+    List               *targetList;    /* the target list (of ResTarget) */
+    List               *fromClause;    /* the from clause */
+    Node               *whereClause;   /* qualifications */
+    List               *groupClause;   /* group by clause */
+    Node               *havingClause;  /* having conditional-expression */
+    List               *orderClause;   /* sort clause (a list of SortBy's) */
+} RetrieveStmt;    
+
+
+/****************************************************************************
+ *  Supporting data structures for Parse Trees
+ ****************************************************************************/
+
+/*
+ * TypeName - specifies a type in definitions
+ */
+typedef struct TypeName {
+    NodeTag            type;
+    char               *name;          /* name of the type */
+    bool               setof;          /* is a set? */
+    List               *arrayBounds;   /* array bounds */
+    int                        typlen;         /* length for char() and varchar() */
+} TypeName;
+
+/*
+ * ParamNo - specifies a parameter reference
+ */
+typedef struct ParamNo {
+    NodeTag            type;
+    int                        number;         /* the number of the parameter */
+    TypeName           *typename;      /* the typecast */
+} ParamNo;
+
+/*
+ * A_Expr - binary expressions
+ */
+typedef struct A_Expr {
+    NodeTag            type;
+    int                        oper;           /* type of operation
+                                          {OP,OR,AND,NOT,ISNULL,NOTNULL} */
+    char               *opname;        /* name of operator/function */
+    Node               *lexpr;         /* left argument */
+    Node               *rexpr;         /* right argument */
+} A_Expr;
+
+/*
+ * Attr -
+ *    specifies an Attribute (ie. a Column); could have nested dots or
+ *    array references.
+ *
+ */
+typedef struct Attr {
+    NodeTag            type;
+    char               *relname;       /* name of relation (can be "*") */
+    ParamNo            *paramNo;       /* or a parameter */
+    List               *attrs;         /* attributes (possibly nested);
+                                          list of Values (strings) */
+    List               *indirection;   /* array refs (list of A_Indices') */
+} Attr;
+
+/*
+ * A_Const - a constant expression
+ */
+typedef struct A_Const {
+    NodeTag            type;
+    Value              val;            /* the value (with the tag) */
+    TypeName           *typename;      /* typecast */
+} A_Const;
+
+/*
+ * ColumnDef - column definition (used in various creates)
+ */
+typedef struct ColumnDef {
+    NodeTag            type;
+    char               *colname;       /* name of column */
+    TypeName           *typename;      /* type of column */
+} ColumnDef;
+
+/*
+ * Ident - 
+ *    an identifier (could be an attribute or a relation name). Depending
+ *    on the context at transformStmt time, the identifier is treated as
+ *    either a relation name (in which case, isRel will be set) or an
+ *    attribute (in which case, it will be transformed into an Attr).
+ */
+typedef struct Ident {
+    NodeTag            type;
+    char               *name;          /* its name */
+    List               *indirection;   /* array references */
+    bool               isRel;          /* is a relation - filled in by
+                                          transformExpr() */
+} Ident;
+
+/*
+ * FuncCall - a function/aggregate invocation
+ */
+typedef struct FuncCall {
+    NodeTag            type;
+    char               *funcname;      /* name of function */
+    List               *args;          /* the arguments (list of exprs) */
+} FuncCall;
+
+/*
+ * A_Indices - array reference or bounds ([lidx:uidx] or [uidx])
+ */
+typedef struct A_Indices {
+    NodeTag            type;
+    Node               *lidx;          /* could be NULL */
+    Node               *uidx;
+} A_Indices;
+
+/*
+ * ResTarget - 
+ *    result target (used in target list of pre-transformed Parse trees)
+ */
+typedef struct ResTarget {
+    NodeTag            type;
+    char               *name;          /* name of the result column */
+    List               *indirection;   /* array references */
+    Node               *val;           /* the value of the result
+                                          (A_Expr or Attr) */
+} ResTarget;
+
+/*
+ * ParamString - used in with clauses
+ */
+typedef struct ParamString {
+    NodeTag            type;
+    char               *name;
+    char               *val;
+} ParamString;
+
+/*
+ * TimeRange - specifies a time range
+ */
+typedef struct TimeRange {
+    NodeTag            type;
+    char               *startDate;
+    char               *endDate;       /* snapshot if NULL */
+} TimeRange;
+
+/*
+ * RelExpr - relation expressions
+ */
+typedef struct RelExpr {
+    NodeTag            type;
+    char               *relname;       /* the relation name */
+    bool               inh;            /* inheritance query */
+    TimeRange          *timeRange;     /* the time range */
+} RelExpr;
+
+/*
+ * Sortby - for order by clause
+ */
+typedef struct SortBy {
+    NodeTag            type;
+    char               *name;          /* name of column to sort on */
+    char               *useOp;         /* operator to use */
+} SortBy;
+
+/*
+ * RangeVar - range variable, used in from clauses
+ */
+typedef struct RangeVar {
+    NodeTag            type;
+    RelExpr            *relExpr;       /* the relation expression */
+    char               *name;          /* the name to be referenced
+                                          (optional) */
+} RangeVar;
+
+/*
+ * IndexElem - index parameters (used in create index)
+ */
+typedef struct IndexElem {
+    NodeTag            type;
+    char               *name;          /* name of index */
+    List               *args;          /* if not NULL, function index */
+    char               *class;
+} IndexElem;
+
+/*
+ * DefElem -
+ *    a definition (used in definition lists in the form of defname = arg)
+ */
+typedef struct DefElem {
+    NodeTag            type;
+    char               *defname;       
+    Node               *arg;           /* a (Value *) or a (TypeName *) */
+} DefElem;
+
+
+/****************************************************************************
+ *  Nodes for a Query tree
+ ****************************************************************************/
+
+/*
+ * TargetEntry -
+ *     a target  entry (used in the transformed target list)
+ *
+ * one of resdom or fjoin is not NULL. a target list is
+ *     ((<resdom | fjoin> expr) (<resdom | fjoin> expr) ...)
+ */
+typedef struct TargetEntry {
+    NodeTag            type;
+    Resdom             *resdom;        /* fjoin overload this to be a list??*/
+    Fjoin              *fjoin; 
+    Node               *expr;          /* can be a list too */
+} TargetEntry;
+
+/*
+ * RangeTblEntry -
+ *    used in range tables. Some of the following are only used in one of
+ *    the parsing, optimizing, execution stages.
+ *
+ *    inFromCl marks those range variables that are listed in the from clause.
+ *    In SQL, the targetlist can only refer to range variables listed in the
+ *    from clause but POSTQUEL allows you to refer to tables not specified, in
+ *    which case a range table entry will be generated. We use POSTQUEL
+ *    semantics which is more powerful. However, we need SQL semantics in
+ *    some cases (eg. when expanding a '*')
+ */
+typedef struct RangeTblEntry {
+    NodeTag            type;
+    char                *relname;      /* real name of the relation */
+    TimeRange          *timeRange;     /* time range */
+    char               *refname;       /* the reference name (specified in
+                                          the from clause) */
+    Oid                        relid;          
+    bool               inh;            /* inheritance? */
+    bool               archive;        /* filled in by plan_archive */
+    bool               inFromCl;       /* comes from From Clause */
+    TimeQual           timeQual;       /* filled in by pg_plan */
+} RangeTblEntry;
+
+/*
+ * SortClause -
+ *     used in the sort clause for retrieves and cursors
+ */
+typedef struct SortClause {
+    NodeTag            type;
+    Resdom             *resdom;        /* attributes in tlist to be sorted */
+    Oid                        opoid;          /* sort operators */
+} SortClause;
+
+/*
+ * GroupClause -
+ *     used in the GROUP BY clause
+ */
+typedef struct GroupClause {
+    NodeTag            type;
+    Var                        *grpAttr;       /* attributes to group on */
+    Oid                        grpOpoid;       /* the sort operator to use */
+} GroupClause;
+
+#endif /* PARSENODES_H */
diff --git a/src/backend/nodes/pg_list.h b/src/backend/nodes/pg_list.h
new file mode 100644 (file)
index 0000000..20554dc
--- /dev/null
@@ -0,0 +1,112 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_list.h--
+ *    POSTGRES generic list package
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        PG_LIST_H
+#define        PG_LIST_H
+
+#include <stdio.h>
+#include "c.h"
+#include "nodes/nodes.h"
+
+/* ----------------------------------------------------------------
+ *                     node definitions
+ * ----------------------------------------------------------------
+ */
+
+/*----------------------
+ *     Value node
+ *----------------------
+ */
+typedef struct Value {
+    NodeTag            type;   /* tag appropriately (eg. T_String) */
+    union ValUnion {
+       char            *str;   /* string */ 
+       long            ival;
+       double          dval;
+    } val;
+} Value;
+
+#define        intVal(v)       (((Value *)v)->val.ival)
+#define        floatVal(v)     (((Value *)v)->val.dval)
+#define strVal(v)      (((Value *)v)->val.str)
+
+
+/*----------------------
+ *     List node
+ *----------------------
+ */
+typedef        struct List {
+    NodeTag            type;
+    void               *elem;
+    struct List                *next;
+} List;
+
+#define    NIL         ((List *) NULL)
+
+/* ----------------
+ *     accessor macros
+ * ----------------
+ */
+#define lfirst(l)                              ((l)->elem)
+#define lnext(l)                               ((l)->next)
+#define lsecond(l)                             (lfirst(lnext(l)))
+
+/*
+ * foreach -
+ *    a convenience macro which loops through the list
+ */
+#define foreach(_elt_,_list_)   \
+    for(_elt_=_list_; _elt_!=NIL;_elt_=lnext(_elt_))
+
+
+/*
+ * function prototypes in nodes/list.c
+ */
+extern int length(List *list);
+extern List *append(List *list1, List *list2);
+extern List *nconc(List *list1, List *list2);
+extern List *lcons(void *datum, List *list);
+extern bool member(void *foo, List *bar);
+extern Value *makeInteger(long i);
+extern Value *makeFloat(double d);
+extern Value *makeString(char *str);
+extern List *makeList(void *elem, ...);
+extern List *lappend(List *list, void *obj);
+extern List *lremove(void *elem, List *list);
+extern void freeList(List *list);
+     
+extern void *nth(int n, List *l);
+extern void set_nth(List *l, int n, void *elem);
+                   
+/* hack for now */
+#define        lconsi(i,l)     lcons((void*)i,l)
+#define lfirsti(l)     ((int)lfirst(l))
+#define        lappendi(l,i)   lappend(l,(void*)i)
+extern bool intMember(int, List *);
+extern List *intAppend(List *list1, List *list2);
+
+extern List *nreverse(List *);
+extern List *set_difference(List *, List *);
+extern List *set_differencei(List *, List *);
+extern List *LispRemove(void *, List *);
+extern List *intLispRemove(int, List *);
+extern List *LispUnion(List *foo, List *bar);
+extern List *LispUnioni(List *foo, List *bar);
+extern bool same(List *foo, List *bar);
+
+/* should be in nodes.h but needs List */
+extern bool equali(List *a, List *b);
+
+/* in copyfuncs.c */
+extern List *listCopy(List *);
+
+#endif /* PG_LIST_H */
diff --git a/src/backend/nodes/plannodes.h b/src/backend/nodes/plannodes.h
new file mode 100644 (file)
index 0000000..d78b525
--- /dev/null
@@ -0,0 +1,330 @@
+/*-------------------------------------------------------------------------
+ *
+ * plannodes.h--
+ *    definitions for query plan nodes
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PLANNODES_H
+#define        PLANNODES_H
+
+#include "postgres.h"
+
+#include "nodes/nodes.h"
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
+
+/* ----------------------------------------------------------------
+ *  Executor State types are used in the plannode structures
+ *  so we have to include their definitions too.
+ *
+ *     Node Type               node information used by executor
+ *
+ * control nodes
+ *
+ *     Existential             ExistentialState        exstate;
+ *     Result                  ResultState             resstate;
+ *     Append                  AppendState             unionstate;
+ *
+ * scan nodes
+ *
+ *     Scan ***                CommonScanState         scanstate;
+ *     IndexScan               IndexScanState          indxstate;
+ *
+ *       (*** nodes which inherit Scan also inherit scanstate)
+ *
+ * join nodes
+ *
+ *     NestLoop                NestLoopState           nlstate;
+ *     MergeJoin               MergeJoinState          mergestate;
+ *     HashJoin                HashJoinState           hashjoinstate;
+ *
+ * materialize nodes
+ *
+ *     Material                MaterialState           matstate;
+ *      Sort                   SortState               sortstate;
+ *      Unique                 UniqueState             uniquestate;
+ *      Hash                   HashState               hashstate;
+ *
+ * ----------------------------------------------------------------
+ */
+#include "nodes/execnodes.h"           /* XXX move executor types elsewhere */
+
+
+/* ----------------------------------------------------------------
+ *                     node definitions
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     Plan node
+ * ----------------
+ */
+
+typedef struct Plan {
+    NodeTag            type;
+    Cost               cost;
+    int                        plan_size;
+    int                        plan_width;
+    int                        plan_tupperpage;
+    EState             *state;  /* at execution time, state's of individual
+                                   nodes point to one EState for the 
+                                   whole top-level plan */
+    List               *targetlist;
+    List               *qual;  /* Node* or List* ?? */
+    struct Plan                *lefttree;
+    struct Plan                *righttree;
+} Plan;
+
+/* ----------------
+ *  these are are defined to avoid confusion problems with "left"
+ *  and "right" and "inner" and "outer".  The convention is that
+ *  the "left" plan is the "outer" plan and the "right" plan is
+ *  the inner plan, but these make the code more readable. 
+ * ----------------
+ */
+#define innerPlan(node)                (((Plan *)(node))->righttree)
+#define outerPlan(node)                (((Plan *)(node))->lefttree)
+
+
+/*
+ * ===============
+ * Top-level nodes
+ * ===============
+ */
+
+/* all plan nodes "derive" from the Plan structure by having the
+   Plan structure as the first field.  This ensures that everything works
+   when nodes are cast to Plan's.  (node pointers are frequently cast to Plan*
+   when passed around generically in the executor */
+
+
+/* ----------------
+ *     existential node
+ * ----------------
+ */
+typedef Plan   Existential;
+
+/* ----------------
+ *   result node -
+ *      returns tuples from outer plan that satisfy the qualifications
+ * ----------------
+ */
+typedef struct Result {
+    Plan               plan;
+    Node               *resconstantqual;
+    ResultState                *resstate;
+} Result;
+
+/* ----------------
+ *     append node
+ * ----------------
+ */
+typedef struct Append {
+    Plan               plan;
+    List               *unionplans;
+    Index              unionrelid;
+    List               *unionrtentries;
+    AppendState                *unionstate;
+} Append;
+
+/*
+ * ==========
+ * Scan nodes
+ * ==========
+ */
+typedef struct Scan {
+    Plan               plan;
+    Index              scanrelid; /* relid is index into the range table */
+    CommonScanState    *scanstate;
+} Scan;
+
+/* ----------------
+ *     sequential scan node
+ * ----------------
+ */
+typedef Scan   SeqScan;
+
+/* ----------------
+ *     index scan node
+ * ----------------
+ */
+typedef struct IndexScan {
+    Scan               scan;
+    List               *indxid;
+    List               *indxqual;
+    IndexScanState     *indxstate;
+} IndexScan;
+
+/*
+ * ==========
+ * Join nodes
+ * ==========
+ */
+
+/* ----------------
+ *     Join node
+ * ----------------
+ */
+typedef        Plan    Join;
+
+/* ----------------
+ *     nest loop join node
+ * ----------------
+ */
+typedef struct NestLoop {
+    Join               join;
+    NestLoopState      *nlstate;
+} NestLoop;
+
+/* ----------------
+ *     merge join node
+ * ----------------
+ */
+typedef struct MergeJoin {
+    Join               join;
+    List               *mergeclauses;
+    Oid                        mergesortop;
+    Oid                        *mergerightorder;       /* inner sort operator */
+    Oid                        *mergeleftorder;        /* outer sort operator */
+    MergeJoinState     *mergestate;
+} MergeJoin;
+
+/* ----------------
+ *     hash join (probe) node
+ * ----------------
+ */
+typedef struct HashJoin {
+    Join               join;
+    List               *hashclauses;
+    Oid                        hashjoinop;
+    HashJoinState      *hashjoinstate;
+    HashJoinTable      hashjointable;
+    IpcMemoryKey       hashjointablekey;
+    int                        hashjointablesize;
+    bool               hashdone;
+} HashJoin;
+
+/* ---------------
+ *      aggregate node
+ * ---------------
+ */
+typedef struct Agg {
+    Plan               plan;
+    int                        numAgg;
+    Aggreg             **aggs;
+    AggState            *aggstate;
+} Agg;
+
+/* ---------------
+ *   group node -
+ *      use for queries with GROUP BY specified.
+ *
+ *      If tuplePerGroup is true, one tuple (with group columns only) is
+ *      returned for each group and NULL is returned when there are no more
+ *      groups. Otherwise, all the tuples of a group are returned with a
+ *     NULL returned at the end of each group. (see nodeGroup.c for details)
+ * ---------------
+ */
+typedef struct Group {
+    Plan               plan;
+    bool               tuplePerGroup;  /* what tuples to return (see above) */
+    int                        numCols;        /* number of group columns */
+    AttrNumber         *grpColIdx;     /* index into the target list */
+    GroupState         *grpstate;
+} Group;
+
+/*
+ * ==========
+ * Temp nodes
+ * ==========
+ */
+typedef struct Temp {
+    Plan               plan;
+    Oid                        tempid;
+    int                        keycount;
+} Temp;
+
+/* ----------------
+ *     materialization node
+ * ----------------
+ */
+typedef struct Material {
+    Plan               plan;           /* temp node flattened out */
+    Oid                        tempid;
+    int                        keycount;
+    MaterialState      *matstate;
+} Material;
+
+/* ----------------
+ *     sort node
+ * ----------------
+ */
+typedef struct Sort {
+    Plan               plan;           /* temp node flattened out */
+    Oid                        tempid;
+    int                        keycount;
+    SortState          *sortstate;
+} Sort;
+
+/* ----------------
+ *     unique node
+ * ----------------
+ */
+typedef struct Unique {
+    Plan               plan;           /* temp node flattened out */
+    Oid                        tempid;
+    int                        keycount;
+    char               *uniqueAttr; /* NULL if all attrs,
+                                      or unique attribute name */
+    AttrNumber         uniqueAttrNum; /* attribute number of attribute
+                                          to select distinct on */
+    UniqueState                *uniquestate;
+} Unique;
+
+/* ----------------
+ *     hash build node
+ * ----------------
+ */
+typedef struct Hash {
+    Plan               plan;
+    Var                        *hashkey;
+    HashState          *hashstate;
+    HashJoinTable      hashtable;
+    IpcMemoryKey       hashtablekey;
+    int                        hashtablesize;
+} Hash;
+
+/* ---------------------
+ *     choose node
+ * ---------------------
+ */
+typedef struct Choose {
+    Plan       plan;
+    List       *chooseplanlist;
+} Choose;
+
+/* -------------------
+ *      Tee node information
+ *
+ *    leftParent :              the left parent of this node
+ *    rightParent:              the right parent of this node  
+ * -------------------
+*/
+typedef struct Tee {
+  Plan           plan;
+  Plan*          leftParent;
+  Plan*          rightParent;
+  TeeState       *teestate;
+  char           *teeTableName; /* the name of the table to materialize
+                                 the tee into */
+  List           *rtentries; /* the range table for the plan below the Tee
+                               may be different than the parent plans */
+} Tee;
+
+#endif /* PLANNODES_H */
diff --git a/src/backend/nodes/primnodes.h b/src/backend/nodes/primnodes.h
new file mode 100644 (file)
index 0000000..6596253
--- /dev/null
@@ -0,0 +1,318 @@
+/*-------------------------------------------------------------------------
+ *
+ * primnodes.h--
+ *    Definitions for parse tree/query tree ("primitive") nodes.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PRIMNODES_H
+#define        PRIMNODES_H
+
+#include "postgres.h"
+
+#include "access/attnum.h"
+#include "storage/buf.h"
+#include "utils/rel.h"
+#include "utils/fcache.h"
+#include "nodes/params.h"
+
+#include "nodes/nodes.h"
+#include "nodes/pg_list.h"
+
+/* ----------------------------------------------------------------
+ *                     node definitions
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ * Resdom (Result Domain)
+ *     resno           - attribute number
+ *     restype         - type of the resdom
+ *     reslen          - length (in bytes) of the result
+ *     resname         - name of the resdom (could be NULL)
+ *     reskey          - order of key in a sort (for those > 0)
+ *     reskeyop        - sort operator Oid
+ *     resjunk         - set to nonzero to eliminate the attribute
+ *                       from final target list  e.g., ctid for replace
+ *                       and delete
+ *
+ * ----------------
+ */
+typedef struct Resdom {
+    NodeTag            type;
+    AttrNumber         resno;
+    Oid                        restype;
+    int                        reslen;
+    char               *resname;
+    Index              reskey;
+    Oid                        reskeyop; 
+    int                        resjunk;
+} Resdom;
+
+/* -------------
+ * Fjoin
+ *      initialized    - true if the Fjoin has already been initialized for
+ *                        the current target list evaluation
+ *     nNodes          - The number of Iter nodes returning sets that the
+ *                       node will flatten
+ *     outerList       - 1 or more Iter nodes
+ *     inner           - exactly one Iter node.  We eval every node in the
+ *                       outerList once then eval the inner node to completion
+ *                       pair the outerList result vector with each inner
+ *                       result to form the full result.  When the inner has
+ *                       been exhausted, we get the next outer result vector
+ *                       and reset the inner.
+ *     results         - The complete (flattened) result vector
+ *      alwaysNull     - a null vector to indicate sets with a cardinality of
+ *                       0, we treat them as the set {NULL}.
+ */
+typedef struct Fjoin {
+    NodeTag            type;
+    bool               fj_initialized;
+    int                        fj_nNodes;
+    List               *fj_innerNode;
+    DatumPtr           fj_results;
+    BoolPtr            fj_alwaysDone;
+} Fjoin;
+
+/* ----------------
+ * Expr
+ *     typeOid         - oid of the type of this expression
+ *     opType          - type of this expression
+ *     oper            - the Oper node if it is an OPER_EXPR or the
+ *                       Func node if it is a FUNC_EXPR
+ *     args            - arguments to this expression
+ * ----------------
+ */
+typedef enum OpType {
+    OP_EXPR, FUNC_EXPR, OR_EXPR, AND_EXPR, NOT_EXPR
+} OpType;
+
+typedef struct Expr {
+    NodeTag            type;
+    Oid                        typeOid;        /* oid of the type of this expr */
+    OpType             opType;         /* type of the op */
+    Node               *oper;          /* could be Oper or Func */
+    List               *args;          /* list of argument nodes */
+} Expr;
+
+/* ----------------
+ * Var
+ *     varno           - index of this var's relation in the range table
+ *                       (could be INNER or OUTER)
+ *     varattno        - attribute number of this var
+ *     vartype         - pg_type tuple oid for the type of this var
+ *     varnoold        - keep varno around in case it got changed to INNER/
+ *                       OUTER (see match_varid)
+ *     varoattno       - attribute number of this var
+ *                       [ '(varnoold varoattno) was varid   -ay 2/95]
+ * ----------------
+ */
+#define    INNER       65000
+#define    OUTER       65001
+
+#define    PRS2_CURRENT_VARNO          1
+#define    PRS2_NEW_VARNO              2
+
+typedef struct Var {
+    NodeTag            type;
+    Index              varno; 
+    AttrNumber         varattno;
+    Oid                        vartype;
+    Index              varnoold;       /* only used by optimizer */
+    AttrNumber         varoattno;      /* only used by optimizer */
+} Var;
+
+/* ----------------
+ * Oper
+ *     opno            - PG_OPERATOR OID of the operator
+ *     opid            - PG_PROC OID for the operator
+ *     opresulttype    - PG_TYPE OID of the operator's return value
+ *     opsize          - size of return result (cached by executor)
+ *     op_fcache       - XXX comment me.
+ *
+ * ----
+ * NOTE: in the good old days 'opno' used to be both (or either, or
+ * neither) the pg_operator oid, and/or the pg_proc oid depending 
+ * on the postgres module in question (parser->pg_operator,
+ * executor->pg_proc, planner->both), the mood of the programmer,
+ * and the phase of the moon (rumors that it was also depending on the day
+ * of the week are probably false). To make things even more postgres-like
+ * (i.e. a mess) some comments were referring to 'opno' using the name
+ * 'opid'. Anyway, now we have two separate fields, and of course that
+ * immediately removes all bugs from the code...       [ sp :-) ].
+ * ----------------
+ */
+typedef struct Oper {
+    NodeTag            type;
+    Oid                        opno;
+    Oid                        opid;
+    Oid                        opresulttype;
+    int                        opsize;
+    FunctionCachePtr   op_fcache;
+} Oper;
+
+
+/* ----------------
+ * Const
+ *     consttype - PG_TYPE OID of the constant's value
+ *     constlen - length in bytes of the constant's value
+ *     constvalue - the constant's value
+ *     constisnull - whether the constant is null 
+ *             (if true, the other fields are undefined)
+ *     constbyval - whether the information in constvalue
+ *             if passed by value.  If true, then all the information
+ *             is stored in the datum. If false, then the datum
+ *             contains a pointer to the information.
+ *      constisset - whether the const represents a set.  The const
+ *              value corresponding will be the query that defines
+ *              the set.
+ * ----------------
+ */
+typedef struct Const {
+    NodeTag            type;
+    Oid                        consttype;
+    Size               constlen;
+    Datum              constvalue;
+    bool               constisnull;
+    bool               constbyval;
+    bool               constisset;
+} Const;
+
+/* ----------------
+ * Param
+ *     paramkind - specifies the kind of parameter. The possible values
+ *     for this field are specified in "params.h", and they are:
+ *
+ *     PARAM_NAMED: The parameter has a name, i.e. something
+ *              like `$.salary' or `$.foobar'.
+ *              In this case field `paramname' must be a valid Name.
+ *
+ *     PARAM_NUM:   The parameter has only a numeric identifier,
+ *              i.e. something like `$1', `$2' etc.
+ *              The number is contained in the `paramid' field.
+ *
+ *     PARAM_NEW:   Used in PRS2 rule, similar to PARAM_NAMED.
+ *                  The `paramname' and `paramid' refer to the "NEW" tuple
+ *                  The `pramname' is the attribute name and `paramid'
+ *                  is the attribute number.
+ *
+ *     PARAM_OLD:   Same as PARAM_NEW, but in this case we refer to
+ *              the "OLD" tuple.
+ *
+ *     paramid - numeric identifier for literal-constant parameters ("$1")
+ *     paramname - attribute name for tuple-substitution parameters ("$.foo")
+ *     paramtype - PG_TYPE OID of the parameter's value
+ *      param_tlist - allows for projection in a param node.
+ * ----------------
+ */
+typedef struct Param {
+    NodeTag            type;
+    int                        paramkind;
+    AttrNumber         paramid;
+    char               *paramname;
+    Oid                        paramtype;
+    List               *param_tlist;
+} Param;
+
+
+/* ----------------
+ * Func
+ *     funcid          - PG_FUNCTION OID of the function
+ *     functype        - PG_TYPE OID of the function's return value
+ *     funcisindex     - the function can be evaluated by scanning an index
+ *                       (set during query optimization)
+ *     funcsize        - size of return result (cached by executor)
+ *     func_fcache     - runtime state while running this function.  Where
+ *                        we are in the execution of the function if it
+ *                        returns more than one value, etc.
+ *                        See utils/fcache.h
+ *      func_tlist      - projection of functions returning tuples
+ *      func_planlist   - result of planning this func, if it's a PQ func
+ * ----------------
+ */
+typedef struct Func {
+    NodeTag            type;
+    Oid                        funcid;
+    Oid                        functype;
+    bool               funcisindex;
+    int                        funcsize;
+    FunctionCachePtr   func_fcache;
+    List                *func_tlist;
+    List                *func_planlist;
+} Func;
+
+/* ----------------
+ * Aggreg
+ *     aggname         - name of the aggregate
+ *      basetype        - base type Oid of the aggregate
+ *     aggtype         - type Oid of final result of the aggregate
+ *     query           - XXX comment me
+ *     target          - XXX comment me
+ * ----------------
+ */
+typedef struct Aggreg {
+    NodeTag            type;
+    char               *aggname;
+    Oid                 basetype;       /* base type of the aggregate */
+    Oid                        aggtype;        /* type of final result */
+    Node               *target;        /* attribute to aggreg on */
+    int                        aggno;          /* index to ecxt_values */
+} Aggreg;
+
+/* ----------------
+ * Array
+ *     arrayelemtype   - base type of the array's elements (homogenous!)
+ *     arrayelemlength - length of that type
+ *     arrayelembyval  - can you pass this element by value?
+ *     arrayndim       - number of dimensions of the array
+ *     arraylow        - base for array indexing
+ *     arrayhigh       - limit for array indexing
+ *     arraylen        -
+ * ----------------
+ *
+ *  memo from mao:  the array support we inherited from 3.1 is just
+ *  wrong.  when time exists, we should redesign this stuff to get
+ *  around a bunch of unfortunate implementation decisions made there.
+ */
+typedef struct Array {
+    NodeTag            type;
+    Oid                        arrayelemtype;
+    int                        arrayelemlength;
+    bool               arrayelembyval;
+    int                arrayndim;
+    IntArray           arraylow;
+    IntArray           arrayhigh;
+    int                        arraylen;
+} Array;
+
+/* ----------------
+ *  ArrayRef:
+ *     refelemtype     - type of the element referenced here
+ *     refelemlength   - length of that type
+ *     refelembyval    - can you pass this element type by value?
+ *     refupperindexpr - expressions that evaluate to upper array index
+ *     reflowerexpr- the expressions that evaluate to a lower array index
+ *     refexpr         - the expression that evaluates to an array
+ *     refassignexpr- the expression that evaluates to the new value 
+ *  to be assigned to the array in case of replace.
+ * ----------------
+ */
+typedef struct ArrayRef {
+    NodeTag            type;
+    int                        refattrlength;
+    int                        refelemlength;
+    Oid                        refelemtype;
+    bool               refelembyval;
+    List               *refupperindexpr;
+    List               *reflowerindexpr;
+    Node               *refexpr;
+    Node               *refassgnexpr;
+} ArrayRef;
+
+#endif /* PRIMNODES_H */
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
new file mode 100644 (file)
index 0000000..a7c4cb9
--- /dev/null
@@ -0,0 +1,377 @@
+/*-------------------------------------------------------------------------
+ *
+ * print.c--
+ *    various print routines (used mostly for debugging)
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * HISTORY
+ *    AUTHOR           DATE            MAJOR EVENT
+ *    Andrew Yu                Oct 26, 1994    file creation
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include "postgres.h"
+#include "access/printtup.h"
+#include "nodes/pg_list.h"
+#include "nodes/execnodes.h"
+#include "nodes/parsenodes.h"
+
+#include "parser/parsetree.h"
+#include "parser/catalog_utils.h"
+#include "access/heapam.h"
+#include "utils/lsyscache.h"
+#include "nodes/nodes.h"
+#include "nodes/plannodes.h"
+#include "optimizer/clauses.h"
+/*
+ * print--
+ *    print contents of Node to stdout
+ */
+void
+print(void *obj)
+{
+    char *s;
+
+    s = nodeToString(obj);
+    printf("%s\n", s);
+    fflush(stdout);
+    return;
+}
+
+/*
+ * pretty print hack extraordinaire.  -ay 10/94
+ */
+void
+pprint(void *obj)
+{
+    char *s;
+    int i;
+    char line[80];
+    int indentLev;
+    int j;
+
+    s = nodeToString(obj);
+
+    indentLev = 0;
+    i = 0;
+    for(;;) {
+       for(j=0; j<indentLev*3; j++) {
+           line[j] = ' ';
+       }
+       for( ; j<75 && s[i]!='\0'; i++, j++) {
+           line[j] = s[i];
+           switch (line[j]) {
+           case '}':
+               if (j != indentLev*3) {
+                   line[j] = '\0';
+                   printf("%s\n",line);
+                   line[indentLev*3] = '\0';
+                   printf("%s}\n",line);
+               }else {
+                   line[j] = '\0';
+                   printf("%s}\n",line);
+               }
+               indentLev--;
+               j = indentLev*3-1; /* print the line before : and resets */
+               break;
+           case ')':
+               line[j+1] = '\0';
+               printf("%s\n", line);
+               j = indentLev*3-1;
+               break;
+           case '{':
+               indentLev++;
+               /* !!! FALLS THROUGH */
+           case ':':
+               if (j != 0) {
+                   line[j] = '\0';
+                   printf("%s\n",line);
+                   /* print the line before : and resets */
+                   for(j=0; j<indentLev*3; j++) {
+                       line[j] = ' ';
+                   }
+               }
+               line[j] = s[i];
+               break;
+           }
+       }
+       line[j] = '\0';
+       if (s[i]=='\0')
+           break;
+       printf("%s\n", line);
+    }
+    if (j!=0) {
+       printf("%s\n", line);
+    }
+    fflush(stdout);
+    return;
+}
+
+/*
+ * print_rt--
+ *    print contents of range table
+ */
+void
+print_rt(List *rtable)
+{
+    List *l;
+    int i=1;
+
+    printf("resno\trelname(refname)\trelid\tinFromCl\n");
+    printf("-----\t----------------\t-----\t--------\n");
+    foreach(l, rtable) {
+       RangeTblEntry *rte = lfirst(l);
+       printf("%d\t%s(%s)\t%d\t%d\t%s\n",
+              i,rte->relname,rte->refname,rte->relid,
+              rte->inFromCl,
+              (rte->inh?"inh":""));
+       i++;
+    }
+}
+
+
+/*
+ * print_expr--
+ *    print an expression
+ */
+void
+print_expr(Node *expr, List *rtable)
+{
+    if (expr==NULL) {
+       printf("nil");
+       return;
+    }
+    
+    if (IsA(expr,Var)) {
+       Var *var = (Var*)expr;
+       RangeTblEntry *rt;
+       char *relname, *attname;
+       
+       switch (var->varno) {
+       case INNER:
+           relname = "INNER";
+           attname = "?";
+           break;
+       case OUTER:
+           relname = "OUTER";
+           attname = "?";
+           break;
+       default:
+           {
+               Relation r;
+               rt = rt_fetch(var->varno, rtable);
+               relname = rt->relname;
+               r = heap_openr(relname);
+               if (rt->refname)
+                   relname = rt->refname; /* table renamed */
+               attname = getAttrName(r, var->varattno);
+               heap_close(r);
+           }
+           break;
+       }
+       printf("%s.%s",relname,attname);
+    } else if (IsA(expr,Expr)) {
+       Expr *e = (Expr*)expr;
+       if (is_opclause(expr)) {
+           char *opname;
+
+           print_expr((Node*)get_leftop(e), rtable);
+           opname = get_opname(((Oper*)e->oper)->opno);
+           printf(" %s ", opname);
+           print_expr((Node*)get_rightop(e), rtable);
+       } else {
+           printf("an expr");
+       }
+    } else {
+       printf("not an expr");
+    }
+}
+
+/*
+ * print_keys -
+ *    temporary here. where is keys list of list??
+ */
+void
+print_keys(List *keys, List *rtable)
+{
+    List *k;
+
+    printf("(");
+    foreach(k, keys) {
+       Node *var = lfirst((List*)lfirst(k));
+       print_expr(var, rtable);
+       if (lnext(k)) printf(", ");
+    }
+    printf(")\n");
+}
+
+/*
+ * print_tl --
+ *    print targetlist in a more legible way.
+ */
+void   
+print_tl(List *tlist, List *rtable)
+{
+    List *tl;
+
+    printf("(\n");
+    foreach(tl, tlist) {
+       TargetEntry *tle = lfirst(tl);
+
+       printf("\t%d %s\t", tle->resdom->resno, tle->resdom->resname);
+       if (tle->resdom->reskey!=0) {
+           printf("(%d):\t", tle->resdom->reskey);
+       } else {
+           printf("    :\t");
+       }
+       print_expr(tle->expr, rtable);
+       printf("\n");
+    }
+    printf(")\n");
+}
+
+/*
+ * print_slot--
+ *    print out the tuple with the given TupleTableSlot
+ */
+void
+print_slot(TupleTableSlot *slot)
+{
+    if (!slot->val) {
+       printf("tuple is null.\n");
+       return;
+    }
+    if (!slot->ttc_tupleDescriptor) {
+       printf("no tuple descriptor.\n");
+       return;
+    }
+    
+    debugtup(slot->val, slot->ttc_tupleDescriptor);
+}
+
+char* 
+plannode_type (Plan* p)
+{
+    switch(nodeTag(p)) {
+    case T_Plan:
+       return "PLAN";
+       break;
+    case T_Existential:
+       return "EXISTENTIAL";
+       break;
+    case T_Result:
+       return "RESULT";
+       break;
+    case T_Append:
+       return "APPEND";
+       break;
+    case T_Scan:
+       return "SCAN";
+       break;
+    case T_SeqScan:
+       return "SEQSCAN";
+       break;
+    case T_IndexScan:
+       return "INDEXSCAN";
+       break;
+    case T_Join:
+       return "JOIN";
+       break;
+    case T_NestLoop:
+       return "NESTLOOP";
+       break;
+    case T_MergeJoin:
+       return "MERGEJOIN";
+       break;
+    case T_HashJoin:
+       return "HASHJOIN";
+       break;
+    case T_Temp:
+       return "TEMP";
+       break;
+    case T_Material:
+       return "MATERIAL";
+       break;
+    case T_Sort:
+       return "SORT";
+       break;
+    case T_Agg:
+       return "AGG";
+       break;
+    case T_Unique:
+       return "UNIQUE";
+       break;
+    case T_Hash:
+       return "HASH";
+       break;
+    case T_Tee:
+       return "TEE";
+       break;
+    case T_Choose:
+       return "CHOOSE";
+       break;
+    case T_Group:
+       return "GROUP";
+       break;
+    default:
+       return "UNKNOWN";
+       break;
+    }
+}
+/*
+   prints the ascii description of the plan nodes
+   does this recursively by doing a depth-first traversal of the
+   plan tree.  for SeqScan and IndexScan, the name of the table is also
+   printed out 
+
+*/
+void
+print_plan_recursive (Plan* p, Query *parsetree, int indentLevel, char* label)
+{
+    int i;
+    char extraInfo[100];
+
+    if (!p)
+       return;
+    for (i=0;i<indentLevel;i++)
+       printf(" ");
+    printf("%s%s :c=%.4f :s=%d :w=%d ",label, plannode_type(p), 
+          p->cost, p->plan_size, p->plan_width);
+    if (IsA(p,Scan) || IsA(p,SeqScan)) {
+       RangeTblEntry *rte;
+       rte = rt_fetch(((Scan*)p)->scanrelid, parsetree->rtable);
+       strncpy(extraInfo, rte->relname, NAMEDATALEN);
+       extraInfo[NAMEDATALEN] = '\0';
+    } else 
+       if (IsA(p,IndexScan)) {
+           strncpy(extraInfo,
+                   ((RangeTblEntry*)(nth(((IndexScan*)p)->scan.scanrelid - 1,
+                                         parsetree->rtable)))->relname,
+                   NAMEDATALEN);
+       extraInfo[NAMEDATALEN] = '\0';
+    } else
+       extraInfo[0] = '\0';
+    if (extraInfo[0] != '\0')
+       printf(" ( %s )\n", extraInfo);
+    else
+       printf("\n");
+    print_plan_recursive(p->lefttree, parsetree, indentLevel + 3, "l: ");
+    print_plan_recursive(p->righttree, parsetree, indentLevel + 3, "r: ");
+}
+
+/* print_plan 
+  prints just the plan node types */
+
+void
+print_plan (Plan* p, Query* parsetree)
+{
+    print_plan_recursive(p, parsetree, 0, "");
+}
+
+
diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c
new file mode 100644 (file)
index 0000000..54fb45e
--- /dev/null
@@ -0,0 +1,270 @@
+/*-------------------------------------------------------------------------
+ *
+ * read.c--
+ *    routines to convert a string (legal ascii representation of node) back
+ *    to nodes
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * HISTORY
+ *    AUTHOR           DATE            MAJOR EVENT
+ *    Andrew Yu                Nov 2, 1994     file creation
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "postgres.h"
+#include "nodes/pg_list.h"
+#include "nodes/readfuncs.h"
+#include "utils/elog.h"
+
+/*
+ * stringToNode -
+ *    returns a Node with a given legal ascii representation
+ */
+void *
+stringToNode(char *str)
+{
+    void *retval;
+
+    (void) lsptok(str, NULL);  /* set the string used in lsptok */
+    retval = nodeRead(true);   /* start reading */
+
+    return retval;
+}
+
+/*****************************************************************************
+ *
+ * the lisp token parser
+ *
+ *****************************************************************************/
+
+#define RIGHT_PAREN (1000000 + 1)
+#define LEFT_PAREN  (1000000 + 2)
+#define PLAN_SYM    (1000000 + 3)
+#define AT_SYMBOL   (1000000 + 4)
+#define ATOM_TOKEN  (1000000 + 5)
+
+/*
+ * nodeTokenType -
+ *    returns the type of the node token contained in token.
+ *    It returns one of the following valid NodeTags:
+ *     T_Integer, T_Float, T_String
+ *    and some of its own:
+ *     RIGHT_PAREN, LEFT_PAREN, PLAN_SYM, AT_SYMBOL, ATOM_TOKEN
+ *
+ *    Assumption: the ascii representation is legal
+ */
+static NodeTag
+nodeTokenType(char *token, int length)
+{
+    NodeTag retval;
+    
+    /*
+     * Check if the token is a number (decimal or integer,
+     * positive or negative
+     */
+    if (isdigit(*token) ||
+       (length>=2 && *token=='-' && isdigit(*(token+1)) ))
+       {
+           /*
+            * skip the optional '-' (i.e. negative number)
+            */
+           if (*token == '-') {
+               token++;
+           }
+           
+           /*
+            * See if there is a decimal point
+            */
+           
+           for (; length && *token != '.'; token++, length--);
+           
+           /*
+            * if there isn't, token's an int, otherwise it's a float.
+            */
+           
+           retval = (*token != '.') ? T_Integer : T_Float;
+       }
+    else if (isalpha(*token))
+       retval = ATOM_TOKEN;
+    else if (*token == '(')
+       retval = LEFT_PAREN;
+    else if (*token == ')')
+       retval = RIGHT_PAREN;
+    else if (*token == '@')
+       retval = AT_SYMBOL;
+    else if (*token == '\"')
+       retval = T_String;
+    else if (*token == '{')
+       retval = PLAN_SYM;
+    return(retval);
+}
+
+/*
+ * Works kinda like strtok, except it doesn't put nulls into string.
+ * 
+ * Returns the length in length instead.  The string can be set without
+ * returning a token by calling lsptok with length == NULL.
+ *
+ */
+char *
+lsptok(char *string, int *length)
+{
+    static char *local_str;
+    char *ret_string;
+    
+    if (string != NULL) {
+       local_str = string;
+       if (length == NULL) {
+           return(NULL);
+       }
+    }
+    
+    for (; *local_str == ' '
+        || *local_str == '\n'
+        || *local_str == '\t'; local_str++);
+    
+    /*
+     * Now pointing at next token.
+     */
+    ret_string = local_str;
+    if (*local_str == '\0') return(NULL);
+    *length = 1;
+    
+    if (*local_str == '\"') {
+       for (local_str++; *local_str != '\"'; (*length)++, local_str++);
+       (*length)++; local_str++;
+    }else if (*local_str == ')' || *local_str == '(' ||
+             *local_str == '}' || *local_str == '{') {
+       local_str++;
+    }else {
+       for (; *local_str != ' '
+            && *local_str != '\n'
+            && *local_str != '\t'
+            && *local_str != '{'
+            && *local_str != '}'
+            && *local_str != '('
+            && *local_str != ')'; local_str++, (*length)++);
+       (*length)--;
+    }
+    return(ret_string);
+}
+
+/*
+ * This guy does all the reading.
+ *
+ * Secrets:  He assumes that lsptok already has the string (see below).
+ * Any callers should set read_car_only to true.
+ */
+void *
+nodeRead(bool read_car_only)
+{
+    char *token;
+    NodeTag type;
+    Node *this_value, *return_value;
+    int tok_len;
+    char tmp;
+    bool make_dotted_pair_cell = false;
+    
+    token = lsptok(NULL, &tok_len);
+    
+    if (token == NULL) return(NULL);
+    
+    type = nodeTokenType(token, tok_len);
+    
+    switch(type) {
+    case PLAN_SYM:
+       this_value = parsePlanString();
+       token = lsptok(NULL, &tok_len);
+       if (token[0] != '}') return(NULL);
+
+       if (!read_car_only)
+           make_dotted_pair_cell = true;
+       else
+           make_dotted_pair_cell = false;
+       break;
+    case LEFT_PAREN:
+       if (!read_car_only) {
+           List *l = makeNode(List);
+
+           lfirst(l) = nodeRead(false);
+           lnext(l) = nodeRead(false);
+           this_value = (Node*)l;
+       }else {
+           this_value = nodeRead(false);
+       }
+       break;
+    case RIGHT_PAREN:
+       this_value = NULL;
+       break;
+    case AT_SYMBOL:
+       break;
+    case ATOM_TOKEN:
+       if (!strncmp(token, "nil", 3)) {
+           this_value = NULL;
+           /*
+            * It might be "nil" but it is an atom!
+            */
+           if (read_car_only) {
+               make_dotted_pair_cell = false;
+           } else {
+               make_dotted_pair_cell = true;
+           }
+       }else {
+           tmp = token[tok_len];
+           token[tok_len] = '\0';
+           this_value = (Node*)pstrdup(token); /* !attention! not a Node.
+                                                  use with caution */
+           token[tok_len] = tmp;
+           make_dotted_pair_cell = true;
+       }
+       break;
+    case T_Float:
+       tmp = token[tok_len];
+       token[tok_len] = '\0';
+       this_value = (Node*)makeFloat(atof(token));
+       token[tok_len] = tmp;
+       make_dotted_pair_cell = true;
+       break;
+    case T_Integer:
+       tmp = token[tok_len];
+       token[tok_len] = '\0';
+       this_value = (Node*)makeInteger(atoi(token));
+       token[tok_len] = tmp;
+       make_dotted_pair_cell = true;
+       break;
+    case T_String:
+       tmp = token[tok_len - 1];
+       token[tok_len - 1] = '\0';
+       token++;
+       this_value = (Node*)makeString(token);          /* !! not strdup'd */
+       token[tok_len - 2] = tmp;
+       make_dotted_pair_cell = true;
+       break;
+    default:
+       elog(WARN, "nodeRead: Bad type %d", type);
+       break;
+    }
+    if (make_dotted_pair_cell) {
+       List *l = makeNode(List);
+
+       lfirst(l) = this_value;
+       if (!read_car_only) {
+           lnext(l) = nodeRead(false);
+       }else {
+           lnext(l) = NULL;
+       }
+       return_value = (Node*)l;
+    }else {
+       return_value = this_value;
+    }
+    return(return_value);
+}
+
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
new file mode 100644 (file)
index 0000000..75832d9
--- /dev/null
@@ -0,0 +1,1948 @@
+/*-------------------------------------------------------------------------
+ *
+ * readfuncs.c--
+ *    Reader functions for Postgres tree nodes.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    Most of the read functions for plan nodes are tested. (In fact, they
+ *    pass the regression test as of 11/8/94.) The rest (for path selection)
+ *    are probably never used. No effort has been made to get them to work.
+ *    The simplest way to test these functions is by doing the following in
+ *    ProcessQuery (before executing the plan):
+ *             plan = stringToNode(nodeToString(plan));
+ *    Then, run the regression test. Let's just say you'll notice if either
+ *    of the above function are not properly done.
+ *                                                     - ay 11/94
+ *    
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "fmgr.h"
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+#include "catalog/pg_type.h"
+
+#include "nodes/primnodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/parsenodes.h"
+#include "nodes/execnodes.h"
+#include "nodes/relation.h"
+#include "nodes/readfuncs.h"
+
+/* ----------------
+ *     node creator declarations
+ * ----------------
+ */
+
+static Datum readDatum(Oid type);
+
+static List *toIntList(List *list)
+{
+    List *l;
+    foreach(l, list) {
+       /* ugly manipulation, should probably free the Value node too */
+       lfirst(l) = (void*)intVal(lfirst(l));
+    }
+    return list;
+}
+
+/* ----------------
+ *     _readQuery
+ * ----------------
+ */
+static Query *
+_readQuery()
+{
+    Query *local_node;
+    char *token;
+    int length;
+    
+    local_node = makeNode(Query);
+
+    token = lsptok(NULL, &length);     /* skip the :command */
+    token = lsptok(NULL, &length);     /* get the commandType */
+    local_node->commandType = atoi(token);
+
+    token = lsptok(NULL, &length);      /* skip the :utility */
+    token = lsptok(NULL, &length);      /* get the notify name if any*/
+    if (token[0] == '"' && token[1] == '"') 
+       local_node->utilityStmt = NULL;
+    else {
+          NotifyStmt *n = makeNode(NotifyStmt);
+          n->relname = palloc(length + 1);
+          strncpy(n->relname,token,length);
+          n->relname[length] = '\0';
+          local_node->utilityStmt = (Node*)n;
+       }
+
+    token = lsptok(NULL, &length);     /* skip the :resrel */
+    token = lsptok(NULL, &length);     /* get the resultRelation */
+    local_node->resultRelation = atoi(token);
+    
+    token = lsptok(NULL, &length);     /* skip :rtable */
+    local_node->rtable = nodeRead(true);
+
+    token = lsptok(NULL, &length);     /* skip the :unique */
+    token = lsptok(NULL, &length);     /* get the uniqueFlag */
+/*    local_node->uniqueFlag = (bool)atoi(token); */
+    if (token[0]=='"' && token[1] == '"') /* non-unique */
+      local_node->uniqueFlag = NULL;
+    else {
+      local_node->uniqueFlag = palloc(length + 1);
+      strncpy(local_node->uniqueFlag,token,length);
+      local_node->uniqueFlag[length] = '\0';
+    }
+
+    token = lsptok(NULL, &length);     /* skip :targetlist */
+    local_node->targetList = nodeRead(true);
+    
+    token = lsptok(NULL, &length);     /* skip :qual */
+    local_node->qual = nodeRead(true);
+    
+    return (local_node);
+}
+
+/* ----------------
+ *     _getPlan
+ * ----------------
+ */
+static void
+_getPlan(Plan *node)
+{
+    char *token;
+    int length;
+    
+    token = lsptok(NULL, &length);     /* first token is :cost */
+    token = lsptok(NULL, &length);     /* next is the actual cost */
+    node->cost = (Cost) atof(token);
+    
+    token = lsptok(NULL, &length);             /* skip the :size */
+    token = lsptok(NULL, &length);             /* get the plan_size */
+    node->plan_size = atoi(token);
+    
+    token = lsptok(NULL, &length);             /* skip the :width */
+    token = lsptok(NULL, &length);             /* get the plan_width */
+    node->plan_width = atoi(token);
+    
+    token = lsptok(NULL, &length);     /* eat the :state stuff */
+    token = lsptok(NULL, &length);     /* now get the state */ 
+    
+    if (!strncmp(token, "nil", 3)) {
+       node->state = (EState*) NULL;
+    }else { /* Disgusting hack until I figure out what to do here */
+       node->state = (EState*) ! NULL;
+    }
+    
+    token = lsptok(NULL, &length);     /* eat :qptargetlist */
+    node->targetlist = nodeRead(true);
+    
+    token = lsptok(NULL, &length);     /* eat :qpqual */
+    node->qual = nodeRead(true);
+    
+    token = lsptok(NULL, &length);     /* eat :lefttree */
+    node->lefttree = (Plan*) nodeRead(true);
+    
+    token = lsptok(NULL, &length);     /* eat :righttree */
+    node->righttree = (Plan*) nodeRead(true);
+
+    return;
+}
+
+/*
+ *  Stuff from plannodes.h
+ */
+
+/* ----------------
+ *     _readPlan
+ * ----------------
+ */
+static Plan *
+_readPlan()
+{
+    Plan *local_node;
+    
+    local_node = makeNode(Plan);
+    
+    _getPlan(local_node);
+    
+    return (local_node);
+}
+
+/* ----------------
+ *     _readResult
+ *
+ *     Does some obscene, possibly unportable, magic with
+ *     sizes of things.
+ * ----------------
+ */
+static Result *
+_readResult()
+{
+    Result     *local_node;
+    char *token;
+    int length;
+    
+    local_node = makeNode(Result);
+    
+    _getPlan((Plan*)local_node);
+    
+    token = lsptok(NULL, &length);     /* eat :resconstantqual */
+    local_node->resconstantqual = nodeRead(true);      /* now read it */
+    
+    return( local_node );
+}
+
+/* ----------------
+ *     _readExistential
+ *
+ *     Existential nodes are only used by the planner.
+ * ----------------
+ */
+static Existential *
+_readExistential()
+{
+    Existential        *local_node;
+    
+    local_node = makeNode(Existential);
+    
+    _getPlan((Plan*)local_node);
+    
+    return( local_node );
+}
+
+/* ----------------
+ *     _readAppend
+ *
+ *  Append is a subclass of Plan.
+ * ----------------
+ */
+
+static Append *
+_readAppend()
+{
+    Append *local_node;
+    char *token;
+    int length;
+    
+    local_node = makeNode(Append);
+    
+    _getPlan((Plan*)local_node);
+    
+    token = lsptok(NULL, &length);             /* eat :unionplans */
+    local_node->unionplans = nodeRead(true);   /* now read it */
+    
+    token = lsptok(NULL, &length);             /* eat :unionrelid */
+    token = lsptok(NULL, &length);             /* get unionrelid */
+    local_node->unionrelid = atoi(token);
+    
+    token = lsptok(NULL, &length);     /* eat :unionrtentries */
+    local_node->unionrtentries = nodeRead(true);       /* now read it */
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _getJoin
+ *
+ * In case Join is not the same structure as Plan someday.
+ * ----------------
+ */
+static void
+_getJoin(Join *node)
+{
+    _getPlan((Plan*)node);
+}
+
+
+/* ----------------
+ *     _readJoin
+ *
+ *  Join is a subclass of Plan
+ * ----------------
+ */
+static Join *
+_readJoin()
+{
+    Join       *local_node;
+    
+    local_node = makeNode(Join);
+    
+    _getJoin(local_node);
+    
+    return( local_node );
+}
+
+/* ----------------
+ *     _readNestLoop
+ *     
+ *  NestLoop is a subclass of Join
+ * ----------------
+ */
+
+static NestLoop *
+_readNestLoop()
+{
+    NestLoop   *local_node;
+    
+    local_node = makeNode(NestLoop);
+    
+    _getJoin((Join*)local_node);
+    
+    return( local_node );
+}
+
+/* ----------------
+ *     _readMergeJoin
+ *     
+ *  MergeJoin is a subclass of Join
+ * ----------------
+ */
+static MergeJoin *
+_readMergeJoin()
+{
+    MergeJoin  *local_node;
+    char               *token;
+    int length;
+    
+    local_node = makeNode(MergeJoin);
+    
+    _getJoin((Join*)local_node);
+    token = lsptok(NULL, &length);             /* eat :mergeclauses */
+    local_node->mergeclauses = nodeRead(true); /* now read it */
+    
+    token = lsptok(NULL, &length);             /* eat :mergesortop */
+    token = lsptok(NULL, &length);             /* get mergesortop */
+    local_node->mergesortop = atol(token);
+    
+    return( local_node );
+}
+
+/* ----------------
+ *     _readHashJoin
+ *     
+ *  HashJoin is a subclass of Join.
+ * ----------------
+ */
+static HashJoin *
+_readHashJoin()
+{
+    HashJoin   *local_node;
+    char               *token;
+    int length;
+    
+    local_node = makeNode(HashJoin);
+    
+    _getJoin((Join*)local_node);
+    
+    token = lsptok(NULL, &length);             /* eat :hashclauses */
+    local_node->hashclauses = nodeRead(true);  /* now read it */
+    
+    token = lsptok(NULL, &length);             /* eat :hashjoinop */
+    token = lsptok(NULL, &length);             /* get hashjoinop */
+    local_node->hashjoinop = atoi(token);
+    
+    token = lsptok(NULL, &length);             /* eat :hashjointable */
+    token = lsptok(NULL, &length);             /* eat hashjointable */
+    local_node->hashjointable = NULL;
+    
+    token = lsptok(NULL, &length);             /* eat :hashjointablekey */
+    token = lsptok(NULL, &length);             /* eat hashjointablekey */
+    local_node->hashjointablekey = 0;
+    
+    token = lsptok(NULL, &length);             /* eat :hashjointablesize */
+    token = lsptok(NULL, &length);             /* eat hashjointablesize */
+    local_node->hashjointablesize = 0;
+    
+    token = lsptok(NULL, &length);             /* eat :hashdone */
+    token = lsptok(NULL, &length);             /* eat hashdone */
+    local_node->hashdone = false;
+    
+    return( local_node );
+}
+
+/* ----------------
+ *     _getScan
+ *
+ *  Scan is a subclass of Node
+ *  (Actually, according to the plannodes.h include file, it is a
+ *  subclass of Plan.  This is why _getPlan is used here.)
+ *
+ *  Scan gets its own get function since stuff inherits it.
+ * ----------------
+ */
+static void 
+_getScan(Scan *node)
+{
+    char *token;
+    int length;
+    
+    _getPlan((Plan*)node);
+    
+    token = lsptok(NULL, &length);             /* eat :scanrelid */
+    token = lsptok(NULL, &length);             /* get scanrelid */
+    node->scanrelid = atoi(token);
+}
+
+/* ----------------
+ *     _readScan
+ *     
+ * Scan is a subclass of Plan (Not Node, see above).
+ * ----------------
+ */
+static Scan *
+_readScan()
+{
+    Scan       *local_node;
+    
+    local_node = makeNode(Scan);
+    
+    _getScan(local_node);
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readSeqScan
+ *     
+ *  SeqScan is a subclass of Scan
+ * ----------------
+ */
+static SeqScan *
+_readSeqScan()
+{
+    SeqScan    *local_node;
+    
+    local_node = makeNode(SeqScan);
+    
+    _getScan((Scan*)local_node);
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readIndexScan
+ *     
+ *  IndexScan is a subclass of Scan
+ * ----------------
+ */
+static IndexScan *
+_readIndexScan()
+{
+    IndexScan  *local_node;
+    char               *token;
+    int length;
+    
+    local_node = makeNode(IndexScan);
+    
+    _getScan((Scan*)local_node);
+    
+    token = lsptok(NULL, &length);             /* eat :indxid */
+    local_node->indxid =
+       toIntList(nodeRead(true));              /* now read it */
+    
+    token = lsptok(NULL, &length);             /* eat :indxqual */
+    local_node->indxqual = nodeRead(true);             /* now read it */
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readTemp
+ *     
+ *  Temp is a subclass of Plan
+ * ----------------
+ */
+static Temp *
+_readTemp()
+{
+    Temp               *local_node;
+    char               *token;
+    int length;
+    
+    local_node = makeNode(Temp);
+    
+    _getPlan((Plan*)local_node);
+    
+    token = lsptok(NULL, &length);             /* eat :tempid */
+    token = lsptok(NULL, &length);             /* get tempid */
+    local_node->tempid = atol(token);
+    
+    token = lsptok(NULL, &length);             /* eat :keycount */
+    token = lsptok(NULL, &length);             /* get keycount */
+    local_node->keycount = atoi(token);
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readSort
+ *     
+ *  Sort is a subclass of Temp
+ * ----------------
+ */
+static Sort *
+_readSort()
+{
+    Sort               *local_node;
+    char               *token;
+    int length;
+    
+    local_node = makeNode(Sort);
+    
+    _getPlan((Plan*)local_node);
+    
+    token = lsptok(NULL, &length);             /* eat :tempid */
+    token = lsptok(NULL, &length);             /* get tempid */
+    local_node->tempid = atol(token);
+    
+    token = lsptok(NULL, &length);             /* eat :keycount */
+    token = lsptok(NULL, &length);             /* get keycount */
+    local_node->keycount = atoi(token);
+    
+    return(local_node);
+}
+
+static Agg *
+_readAgg()
+{
+    Agg             *local_node;
+    char            *token;
+    int length;
+    
+    local_node = makeNode(Agg);
+    _getPlan((Plan*)local_node);
+    
+    token = lsptok(NULL, &length);                  /* eat :numagg */
+    token = lsptok(NULL, &length);                  /* get numagg */
+    local_node->numAgg = atoi(token);
+
+    return(local_node);
+}
+
+/* ----------------
+ *     _readUnique
+ *
+ * For some reason, unique is a subclass of Temp.
+ */
+static Unique *
+_readUnique()
+{
+    Unique             *local_node;
+    char               *token;
+    int                length;
+    
+    local_node = makeNode(Unique);
+    
+    _getPlan((Plan*)local_node);
+    
+    token = lsptok(NULL, &length);             /* eat :tempid */
+    token = lsptok(NULL, &length);             /* get :tempid */
+    local_node->tempid = atol(token);
+    
+    token = lsptok(NULL, &length);             /* eat :keycount */
+    token = lsptok(NULL, &length);             /* get :keycount */
+    local_node->keycount = atoi(token);
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readHash
+ *     
+ *  Hash is a subclass of Temp
+ * ----------------
+ */
+static Hash *
+_readHash()
+{
+    Hash               *local_node;
+    char               *token;
+    int length;
+    
+    local_node = makeNode(Hash);
+    
+    _getPlan((Plan*)local_node);
+    
+    token = lsptok(NULL, &length);             /* eat :hashkey */
+    local_node->hashkey = (Var*) nodeRead(true);
+    
+    token = lsptok(NULL, &length);     /* eat :hashtable */
+    token = lsptok(NULL, &length);     /* eat hashtable address*/
+    local_node->hashtable = NULL;
+    
+    token = lsptok(NULL, &length);     /* eat :hashtablekey*/
+    token = lsptok(NULL, &length);     /* get hashtablekey */
+    local_node->hashtablekey = 0;
+    
+    token = lsptok(NULL, &length);     /* eat :hashtablesize*/
+    token = lsptok(NULL, &length);     /* get hashtablesize */
+    local_node->hashtablesize = 0;
+    
+    return(local_node);
+}
+
+/*
+ *  Stuff from primnodes.h.
+ */
+
+/* ----------------
+ *     _readResdom
+ *     
+ *  Resdom is a subclass of Node
+ * ----------------
+ */
+static Resdom *
+_readResdom()
+{
+    Resdom             *local_node;
+    char               *token;
+    int length;
+    
+    local_node = makeNode(Resdom);
+    
+    token = lsptok(NULL, &length);             /* eat :resno */
+    token = lsptok(NULL, &length);             /* get resno */
+    local_node->resno = atoi(token);
+    
+    token = lsptok(NULL, &length);             /* eat :restype */
+    token = lsptok(NULL, &length);             /* get restype */
+    local_node->restype = atol(token);
+    
+    token = lsptok(NULL, &length);             /* eat :reslen */
+    token = lsptok(NULL, &length);             /* get reslen */
+    local_node->reslen = atoi(token);
+    
+    token = lsptok(NULL, &length);             /* eat :resname */
+    token = lsptok(NULL, &length);             /* get the name */
+    
+    if (!strncmp(token, "\"null\"", 5)) {
+       local_node->resname = NULL;
+    }else {
+       /*
+        * Peel off ""'s, then make a true copy.
+        */
+       
+       token++;
+       token[length - 2] = '\0';
+       
+       local_node->resname = palloc(length);
+       strcpy(local_node->resname, token);
+       token[length - 2] = '\"';
+    }
+    
+    token = lsptok(NULL, &length);             /* eat :reskey */
+    token = lsptok(NULL, &length);             /* get reskey */
+    local_node->reskey = atoi(token);
+    
+    token = lsptok(NULL, &length);             /* eat :reskeyop */
+    token = lsptok(NULL, &length);             /* get reskeyop */
+    local_node->reskeyop = (Oid) atol(token);
+    
+    token = lsptok(NULL, &length);             /* eat :resjunk */
+    token = lsptok(NULL, &length);             /* get resjunk */
+    local_node->resjunk = atoi(token);
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readExpr
+ *     
+ *  Expr is a subclass of Node
+ * ----------------
+ */
+static Expr *
+_readExpr()
+{
+    Expr *local_node;
+    char               *token;
+    int length;
+    
+    local_node = makeNode(Expr);
+    
+    token = lsptok(NULL, &length);             /* eat :typeOid */
+    token = lsptok(NULL, &length);             /* get typeOid */
+    local_node->typeOid = (Oid)atol(token);
+    
+    token = lsptok(NULL, &length);             /* eat :opType */
+    token = lsptok(NULL, &length);             /* get opType */
+    if (!strncmp(token, "op", 2)) {
+       local_node->opType = OP_EXPR;
+    } else if (!strncmp(token, "func", 4)) {
+       local_node->opType = FUNC_EXPR;
+    } else if (!strncmp(token, "or", 2)) {
+       local_node->opType = OR_EXPR;
+    } else if (!strncmp(token, "and", 3)) {
+       local_node->opType = AND_EXPR;
+    } else if (!strncmp(token, "not", 3)) {
+       local_node->opType = NOT_EXPR;
+    }
+    
+    token = lsptok(NULL, &length);             /* eat :oper */
+    local_node->oper = nodeRead(true);
+    
+    token = lsptok(NULL, &length);             /* eat :args */
+    local_node->args = nodeRead(true);         /* now read it */
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readVar
+ *     
+ *  Var is a subclass of Expr
+ * ----------------
+ */
+static Var *
+_readVar()
+{
+    Var                *local_node;
+    char               *token;
+    int length;
+    
+    local_node = makeNode(Var);
+    
+    token = lsptok(NULL, &length);             /* eat :varno */
+    token = lsptok(NULL, &length);             /* get varno */
+    local_node->varno = atoi(token);
+    
+    token = lsptok(NULL, &length);             /* eat :varattno */
+    token = lsptok(NULL, &length);             /* get varattno */
+    local_node->varattno = atoi(token);
+    
+    token = lsptok(NULL, &length);             /* eat :vartype */
+    token = lsptok(NULL, &length);             /* get vartype */
+    local_node->vartype = (Oid) atol(token);
+
+    token = lsptok(NULL, &length);             /* eat :varnoold */
+    token = lsptok(NULL, &length);             /* get varnoold */
+    local_node->varnoold = (Oid) atol(token);
+    
+    token = lsptok(NULL, &length);             /* eat :varoattno */
+    token = lsptok(NULL, &length);             /* eat :varoattno */
+    local_node->varoattno = (int) atol(token);
+    
+    return(local_node);
+}
+
+/* ----------------
+ * _readArray
+ *
+ * Array is a subclass of Expr
+ * ----------------
+ */
+static Array *
+_readArray()
+{
+    Array              *local_node;
+    char               *token;
+    int length;
+    
+    local_node = makeNode(Array);
+    
+    token = lsptok(NULL, &length);             /* eat :arrayelemtype */
+    token = lsptok(NULL, &length);             /* get arrayelemtype */
+    local_node->arrayelemtype = (Oid) atoi(token);
+    
+    token = lsptok(NULL, &length);             /* eat :arrayelemlength */
+    token = lsptok(NULL, &length);             /* get arrayelemlength */
+    local_node->arrayelemlength = atoi(token);
+    
+    token = lsptok(NULL, &length);             /* eat :arrayelembyval */
+    token = lsptok(NULL, &length);             /* get arrayelembyval */
+    local_node->arrayelembyval = (token[0] == 't') ? true : false;
+    
+    token = lsptok(NULL, &length);             /* eat :arraylow */
+    token = lsptok(NULL, &length);             /* get arraylow */
+    local_node->arraylow.indx[0] = atoi(token);
+    
+    token = lsptok(NULL, &length);             /* eat :arrayhigh */
+    token = lsptok(NULL, &length);             /* get arrayhigh */
+    local_node->arrayhigh.indx[0] = atoi(token);
+    
+    token = lsptok(NULL, &length);             /* eat :arraylen */
+    token = lsptok(NULL, &length);             /* get arraylen */
+    local_node->arraylen = atoi(token);
+    
+    return(local_node);
+}
+
+/* ----------------
+ * _readArrayRef
+ *
+ * ArrayRef is a subclass of Expr
+ * ----------------
+ */
+static ArrayRef *
+_readArrayRef()
+{
+    ArrayRef   *local_node;
+    char       *token;
+    int                length;
+    
+    local_node = makeNode(ArrayRef);
+    
+    token = lsptok(NULL, &length);             /* eat :refelemtype */
+    token = lsptok(NULL, &length);             /* get refelemtype */
+    local_node->refelemtype = (Oid) atoi(token);
+    
+    token = lsptok(NULL, &length);             /* eat :refattrlength */
+    token = lsptok(NULL, &length);             /* get refattrlength */
+    local_node->refattrlength = atoi(token);
+    
+    token = lsptok(NULL, &length);             /* eat :refelemlength */
+    token = lsptok(NULL, &length);             /* get refelemlength */
+    local_node->refelemlength = atoi(token);
+    
+    token = lsptok(NULL, &length);             /* eat :refelembyval */
+    token = lsptok(NULL, &length);             /* get refelembyval */
+    local_node->refelembyval = (token[0] == 't') ? true : false;
+    
+    token = lsptok(NULL, &length);             /* eat :refupperindex */
+    local_node->refupperindexpr = nodeRead(true);
+    
+    token = lsptok(NULL, &length);             /* eat :reflowerindex */
+    local_node->reflowerindexpr = nodeRead(true);
+    
+    token = lsptok(NULL, &length);             /* eat :refexpr */
+    local_node->refexpr = nodeRead(true);
+    
+    token = lsptok(NULL, &length);             /* eat :refassgnexpr */
+    local_node->refassgnexpr = nodeRead(true);
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readConst
+ *     
+ *  Const is a subclass of Expr
+ * ----------------
+ */
+static Const *
+_readConst()
+{
+    Const      *local_node;
+    char *token;
+    int length;
+    
+    local_node = makeNode(Const);
+    
+    token = lsptok(NULL, &length);      /* get :consttype */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->consttype = atol(token);
+    
+    
+    token = lsptok(NULL, &length);      /* get :constlen */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->constlen = atoi(token);
+    
+    token = lsptok(NULL, &length);      /* get :constisnull */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    if (!strncmp(token, "true", 4)) {
+       local_node->constisnull = true;
+    }else {
+       local_node->constisnull = false;
+    }
+    
+    
+    token = lsptok(NULL, &length);      /* get :constvalue */
+    
+    if (local_node->constisnull) {
+       token = lsptok(NULL, &length);      /* skip "NIL" */
+    }else {
+       /*
+        * read the value
+        */
+       local_node->constvalue = readDatum(local_node->consttype);
+    }
+    
+    token = lsptok(NULL, &length);      /* get :constbyval */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    if (!strncmp(token, "true", 4)) {
+       local_node->constbyval = true;
+    }else {
+       local_node->constbyval = false;
+    }
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readFunc
+ *     
+ *  Func is a subclass of Expr
+ * ----------------
+ */
+static Func *
+_readFunc()
+{
+    Func       *local_node;
+    char *token;
+    int length;
+    
+    local_node = makeNode(Func);
+    
+    token = lsptok(NULL, &length);      /* get :funcid */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->funcid = atol(token);
+    
+    token = lsptok(NULL, &length);      /* get :functype */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->functype = atol(token);
+    
+    token = lsptok(NULL, &length);      /* get :funcisindex */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    if (!strncmp(token, "true", 4)) {
+       local_node->funcisindex = true;
+    }else {
+       local_node->funcisindex = false;
+    }
+    
+    token = lsptok(NULL, &length);      /* get :funcsize */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->funcsize = atol(token);
+    
+    token = lsptok(NULL, &length);         /* get :func_fcache */
+    token = lsptok(NULL, &length);         /* get @ */
+    token = lsptok(NULL, &length);         /* now read it */
+    
+    local_node->func_fcache = (FunctionCache *) NULL;
+    
+    token = lsptok(NULL, &length);            /* get :func_tlist */
+    local_node->func_tlist = nodeRead(true);  /* now read it */
+    
+    token = lsptok(NULL, &length);              /* get :func_planlist */
+    local_node->func_planlist = nodeRead(true); /* now read it */
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readOper
+ *     
+ *  Oper is a subclass of Expr
+ * ----------------
+ */
+static Oper *
+_readOper()
+{
+    Oper       *local_node;
+    char       *token;
+    int length;
+    
+    local_node = makeNode(Oper);
+    
+    token = lsptok(NULL, &length);      /* get :opno */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->opno = atol(token);
+    
+    token = lsptok(NULL, &length);      /* get :opid */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->opid = atol(token);
+    
+    token = lsptok(NULL, &length);      /* get :opresulttype */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->opresulttype = atol(token);
+    
+    /*
+     * NOTE: Alternatively we can call 'replace_opid' 
+     * which initializes both 'opid' and 'op_fcache'.
+     */
+    local_node->op_fcache = (FunctionCache *) NULL;
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readParam
+ *     
+ *  Param is a subclass of Expr
+ * ----------------
+ */
+static Param *
+_readParam()
+{
+    Param      *local_node;
+    char       *token;
+    int length;
+    
+    local_node = makeNode(Param);
+    
+    token = lsptok(NULL, &length);      /* get :paramkind */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->paramkind = atoi(token);
+    
+    token = lsptok(NULL, &length);      /* get :paramid */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->paramid = atol(token);
+    
+    token = lsptok(NULL, &length);      /* get :paramname */
+    token = lsptok(NULL, &length);      /* now read it */
+    token++;                       /* skip the first `"' */
+    token[length - 2] = '\0';      /* this is the 2nd `"' */
+    
+    local_node->paramname = pstrdup(token);
+    token[length - 2] = '\"';  /* restore the 2nd `"' */
+    
+    token = lsptok(NULL, &length);      /* get :paramtype */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->paramtype = atol(token);
+    token = lsptok(NULL, &length);             /* get :param_tlist */
+    local_node->param_tlist = nodeRead(true);  /* now read it */
+    
+    return(local_node);
+}
+
+/*
+ *  Stuff from execnodes.h
+ */
+
+/* ----------------
+ *     _readEState
+ *     
+ *  EState is a subclass of Node.
+ * ----------------
+ */
+static EState *
+_readEState()
+{
+    EState     *local_node;
+    char *token;
+    int length;
+    
+    local_node = makeNode(EState);
+    
+    token = lsptok(NULL, &length);      /* get :direction */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->es_direction = atoi(token);
+
+    token = lsptok(NULL, &length);      /* get :range_table */
+    
+    local_node->es_range_table = nodeRead(true); /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :result_relation_info */
+    token = lsptok(NULL, &length);      /* get @ */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    sscanf(token, "%x", &local_node->es_result_relation_info);
+    
+    return(local_node);
+}
+
+/*
+ *  Stuff from relation.h
+ */
+
+/* ----------------
+ *     _readRel
+ * ----------------
+ */
+static Rel *
+_readRel()
+{
+    Rel        *local_node;
+    char *token;
+    int length;
+    
+    local_node = makeNode(Rel);
+    
+    token = lsptok(NULL, &length);      /* get :relids */
+    local_node->relids =
+       toIntList(nodeRead(true));      /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :indexed */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    if (!strncmp(token, "true", 4))
+       {
+           local_node->indexed = true;
+       }
+    else
+       {
+           local_node->indexed = false;
+       }
+    
+    token = lsptok(NULL, &length);      /* get :pages */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->pages = (unsigned int) atoi(token);
+    
+    token = lsptok(NULL, &length);      /* get :tuples */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->tuples = (unsigned int) atoi(token);
+    
+    token = lsptok(NULL, &length);      /* get :size */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->size = (unsigned int) atoi(token);
+    
+    token = lsptok(NULL, &length);      /* get :width */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->width = (unsigned int) atoi(token);
+    
+    token = lsptok(NULL, &length);      /* get :targetlist */
+    local_node->targetlist = nodeRead(true); /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :pathlist */
+    local_node->pathlist = nodeRead(true); /* now read it */
+    
+    /*
+     *  Not sure if these are nodes or not.  They're declared as
+     *  struct Path *.  Since i don't know, i'll just print the
+     *  addresses for now.  This can be changed later, if necessary.
+     */
+    
+    token = lsptok(NULL, &length);      /* get :unorderpath */
+    token = lsptok(NULL, &length);      /* get @ */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    sscanf(token, "%x", &local_node->unorderedpath);
+    
+    token = lsptok(NULL, &length);      /* get :cheapestpath */
+    token = lsptok(NULL, &length);      /* get @ */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    sscanf(token, "%x", &local_node->cheapestpath);
+    
+    
+    token = lsptok(NULL, &length);      /* get :clauseinfo */
+    local_node->clauseinfo = nodeRead(true); /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :joininfo */
+    local_node->joininfo = nodeRead(true); /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :innerjoin */
+    local_node->innerjoin = nodeRead(true); /* now read it */
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readTargetEntry
+ * ----------------
+ */
+static TargetEntry *
+_readTargetEntry()
+{
+    TargetEntry *local_node;
+    char *token;
+    int length;
+
+    local_node = makeNode(TargetEntry);
+
+    token = lsptok(NULL, &length);     /* get :resdom */
+    local_node->resdom = nodeRead(true);       /* now read it */
+
+    token = lsptok(NULL, &length);     /* get :expr */
+    local_node->expr = nodeRead(true); /* now read it */
+
+    return (local_node);
+}
+
+/* ----------------
+ *     _readTargetEntry
+ * ----------------
+ */
+static RangeTblEntry *
+_readRangeTblEntry()
+{
+    RangeTblEntry *local_node;
+    char *token;
+    int length;
+
+    local_node = makeNode(RangeTblEntry);
+
+    token = lsptok(NULL, &length);     /* eat :relname */
+    token = lsptok(NULL, &length);     /* get :relname */
+    if (!strncmp(token, "\"null\"", 5)) {
+       local_node->relname = NULL;
+    }else {
+       /*
+        * Peel off ""'s, then make a true copy.
+        */
+       
+       token++;
+       token[length - 2] = '\0';
+       
+       local_node->relname = (Name) palloc(NAMEDATALEN);
+       namestrcpy(local_node->relname, token);
+       token[length - 2] = '\"';
+    }
+
+    token = lsptok(NULL, &length);     /* eat :inh */
+    token = lsptok(NULL, &length);     /* get :inh */
+    local_node->inh = atoi(token);
+
+    token = lsptok(NULL, &length);     /* eat :refname */
+    token = lsptok(NULL, &length);     /* get :refname */
+    if (!strncmp(token, "\"null\"", 5)) {
+       local_node->refname = NULL;
+    }else {
+       /*
+        * Peel off ""'s, then make a true copy.
+        */
+       
+       token++;
+       token[length - 2] = '\0';
+
+       local_node->refname = (char*)pstrdup(token);
+       token[length - 2] = '\"';
+    }
+    
+    token = lsptok(NULL, &length);     /* eat :relid */
+    token = lsptok(NULL, &length);     /* get :relid */
+    local_node->relid = atoi(token);
+
+    return (local_node);
+}
+
+/* ----------------
+ *     _readPath
+ *     
+ *  Path is a subclass of Node.
+ * ----------------
+ */
+static Path *
+_readPath()
+{
+    Path       *local_node;
+    char       *token;
+    int length;
+    
+    local_node = makeNode(Path);
+    
+    token = lsptok(NULL, &length);      /* get :pathtype */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->pathtype = atol(token);
+    
+    token = lsptok(NULL, &length);      /* get :cost */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->path_cost = (Cost) atof(token);
+    
+#if 0
+    token = lsptok(NULL, &length);      /* get :p_ordering */
+    local_node->p_ordering =
+       nodeRead(true);                 /* now read it */
+#endif
+    
+    token = lsptok(NULL, &length);      /* get :keys */
+    local_node->keys = nodeRead(true);       /* now read it */
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readIndexPath
+ *     
+ *  IndexPath is a subclass of Path.
+ * ----------------
+ */
+static IndexPath *
+_readIndexPath()
+{
+    IndexPath  *local_node;
+    char               *token;
+    int length;
+    
+    local_node = makeNode(IndexPath);
+    
+    token = lsptok(NULL, &length);      /* get :pathtype */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->path.pathtype = atol(token);
+    
+    token = lsptok(NULL, &length);      /* get :cost */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->path.path_cost = (Cost) atof(token);
+    
+#if 0
+    token = lsptok(NULL, &length);      /* get :p_ordering */
+    local_node->path.p_ordering = nodeRead(true);      /* now read it */
+#endif
+    
+    token = lsptok(NULL, &length);      /* get :keys */
+    local_node->path.keys = nodeRead(true);       /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :indexid */
+    local_node->indexid =
+       toIntList(nodeRead(true));
+    
+    token = lsptok(NULL, &length);      /* get :indexqual */
+    local_node->indexqual = nodeRead(true);      /* now read it */
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readJoinPath
+ *     
+ *  JoinPath is a subclass of Path
+ * ----------------
+ */
+static JoinPath *
+_readJoinPath()
+{
+    JoinPath   *local_node;
+    char               *token;
+    int length;
+    
+    
+    local_node = makeNode(JoinPath);
+    
+    token = lsptok(NULL, &length);      /* get :pathtype */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->path.pathtype = atol(token);
+    
+    token = lsptok(NULL, &length);      /* get :cost */
+    token = lsptok(NULL, &length);      /* now read it */
+    local_node->path.path_cost = (Cost) atof(token);
+    
+#if 0
+    token = lsptok(NULL, &length);      /* get :p_ordering */
+    local_node->path.p_ordering = nodeRead(true);           /* now read it */
+#endif
+    
+    token = lsptok(NULL, &length);      /* get :keys */
+    local_node->path.keys = nodeRead(true);            /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :pathclauseinfo */
+    local_node->pathclauseinfo = nodeRead(true);         /* now read it */
+    
+    /*
+     *  Not sure if these are nodes; they're declared as "struct path *".
+     *  For now, i'll just print the addresses.
+     *
+     * GJK:  Since I am parsing this stuff, I'll just ignore the addresses,
+     * and initialize these pointers to NULL.
+     */
+    
+    token = lsptok(NULL, &length);      /* get :outerjoinpath */
+    token = lsptok(NULL, &length);      /* get @ */
+    token = lsptok(NULL, &length);      /* now read it */
+
+    local_node->outerjoinpath = NULL;
+    
+    token = lsptok(NULL, &length);      /* get :innerjoinpath */
+    token = lsptok(NULL, &length);      /* get @ */
+    token = lsptok(NULL, &length);      /* now read it */
+
+    local_node->innerjoinpath = NULL;
+    
+    token = lsptok(NULL, &length);      /* get :outerjoincost */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->path.outerjoincost = (Cost) atof(token);
+    
+    token = lsptok(NULL, &length);      /* get :joinid */
+    local_node->path.joinid =
+       toIntList(nodeRead(true));          /* now read it */
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readMergePath
+ *     
+ *  MergePath is a subclass of JoinPath.
+ * ----------------
+ */
+static MergePath *
+_readMergePath()
+{
+    MergePath  *local_node;
+    char       *token;
+    int length;
+    
+    local_node = makeNode(MergePath);
+    
+    token = lsptok(NULL, &length);      /* get :pathtype */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->jpath.path.pathtype = atol(token);
+    
+    token = lsptok(NULL, &length);      /* get :cost */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->jpath.path.path_cost = (Cost) atof(token);
+    
+#if 0
+    token = lsptok(NULL, &length);      /* get :p_ordering */
+    local_node->jpath.path.p_ordering = nodeRead(true);           /* now read it */
+#endif
+    
+    token = lsptok(NULL, &length);      /* get :keys */
+    local_node->jpath.path.keys = nodeRead(true);            /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :pathclauseinfo */
+    local_node->jpath.pathclauseinfo = nodeRead(true);        /* now read it */
+    
+    /*
+     *  Not sure if these are nodes; they're declared as "struct path *".
+     *  For now, i'll just print the addresses.
+     *
+     * GJK:  Since I am parsing this stuff, I'll just ignore the addresses,
+     * and initialize these pointers to NULL.
+     */
+    
+    token = lsptok(NULL, &length);      /* get :outerjoinpath */
+    token = lsptok(NULL, &length);      /* get @ */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->jpath.outerjoinpath = NULL;
+    
+    token = lsptok(NULL, &length);      /* get :innerjoinpath */
+    token = lsptok(NULL, &length);      /* get @ */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->jpath.innerjoinpath = NULL;
+    
+    token = lsptok(NULL, &length);      /* get :outerjoincost */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->jpath.path.outerjoincost = (Cost) atof(token);
+    
+    token = lsptok(NULL, &length);      /* get :joinid */
+    local_node->jpath.path.joinid =
+       toIntList(nodeRead(true));          /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :path_mergeclauses */
+    local_node->path_mergeclauses = nodeRead(true);      /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :outersortkeys */
+    local_node->outersortkeys = nodeRead(true);           /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :innersortkeys */
+    local_node->innersortkeys = nodeRead(true);           /* now read it */
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readHashPath
+ *     
+ *  HashPath is a subclass of JoinPath.
+ * ----------------
+ */
+static HashPath *
+_readHashPath()
+{
+    HashPath   *local_node;
+    char       *token;
+    int length;
+    
+    local_node = makeNode(HashPath);
+    
+    token = lsptok(NULL, &length);      /* get :pathtype */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->jpath.path.pathtype = atol(token);
+    
+    token = lsptok(NULL, &length);      /* get :cost */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->jpath.path.path_cost = (Cost) atof(token);
+    
+#if 0
+    token = lsptok(NULL, &length);      /* get :p_ordering */
+    local_node->jpath.path.p_ordering = nodeRead(true); /* now read it */
+#endif
+    
+    token = lsptok(NULL, &length);      /* get :keys */
+    local_node->jpath.path.keys = nodeRead(true);       /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :pathclauseinfo */
+    local_node->jpath.pathclauseinfo = nodeRead(true); /* now read it */
+    
+    /*
+     *  Not sure if these are nodes; they're declared as "struct path *".
+     *  For now, i'll just print the addresses.
+     *
+     * GJK:  Since I am parsing this stuff, I'll just ignore the addresses,
+     * and initialize these pointers to NULL.
+     */
+    
+    token = lsptok(NULL, &length);      /* get :outerjoinpath */
+    token = lsptok(NULL, &length);      /* get @ */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->jpath.outerjoinpath = NULL;
+    
+    token = lsptok(NULL, &length);      /* get :innerjoinpath */
+    token = lsptok(NULL, &length);      /* get @ */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->jpath.innerjoinpath = NULL;
+    
+    token = lsptok(NULL, &length);      /* get :outerjoincost */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->jpath.path.outerjoincost = (Cost) atof(token);
+    
+    token = lsptok(NULL, &length);      /* get :joinid */
+    local_node->jpath.path.joinid =
+       toIntList(nodeRead(true));     /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :path_hashclauses */
+    local_node->path_hashclauses = nodeRead(true); /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :outerhashkeys */
+    local_node->outerhashkeys = nodeRead(true); /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :innerhashkeys */
+    local_node->innerhashkeys = nodeRead(true); /* now read it */
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readOrderKey
+ *     
+ *  OrderKey is a subclass of Node.
+ * ----------------
+ */
+static OrderKey *
+_readOrderKey()
+{
+    OrderKey   *local_node;
+    char       *token;
+    int length;
+    
+    local_node = makeNode(OrderKey);
+    
+    token = lsptok(NULL, &length);      /* get :attribute_number */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->attribute_number = atoi(token);
+    
+    token = lsptok(NULL, &length);      /* get :array_index */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->array_index = atoi(token);
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readJoinKey
+ *     
+ *  JoinKey is a subclass of Node.
+ * ----------------
+ */
+static JoinKey *
+_readJoinKey()
+{
+    JoinKey            *local_node;
+    char               *token;
+    int length;
+    
+    local_node = makeNode(JoinKey);
+    
+    token = lsptok(NULL, &length);      /* get :outer */
+    local_node->outer = nodeRead(true);      /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :inner */
+    local_node->inner = nodeRead(true);      /* now read it */
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readMergeOrder
+ *     
+ *  MergeOrder is a subclass of Node.
+ * ----------------
+ */
+static MergeOrder *
+_readMergeOrder()
+{
+    MergeOrder *local_node;
+    char       *token;
+    int length;
+    
+    local_node = makeNode(MergeOrder);
+    token = lsptok(NULL, &length);      /* get :join_operator */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->join_operator = atol(token);
+    
+    token = lsptok(NULL, &length);      /* get :left_operator */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->left_operator = atol(token);
+    
+    token = lsptok(NULL, &length);      /* get :right_operator */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->right_operator = atol(token);
+    
+    token = lsptok(NULL, &length);      /* get :left_type */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->left_type = atol(token);
+    
+    token = lsptok(NULL, &length);      /* get :right_type */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->right_type = atol(token);
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readCInfo
+ *     
+ *  CInfo is a subclass of Node.
+ * ----------------
+ */
+static CInfo *
+_readCInfo()
+{
+    CInfo      *local_node;
+    char       *token;
+    int length;
+    
+    local_node = makeNode(CInfo);
+    
+    token = lsptok(NULL, &length);      /* get :clause */
+    local_node->clause =  nodeRead(true); /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :selectivity */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->selectivity = atof(token);
+    
+    token = lsptok(NULL, &length);      /* get :notclause */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    if (!strncmp(token, "true", 4))
+       {
+           local_node->notclause = true;
+       }
+    else
+       {
+           local_node->notclause = false;
+       }
+    
+    token = lsptok(NULL, &length);      /* get :indexids */
+    local_node->indexids = nodeRead(true);   /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :mergesortorder */
+    local_node->mergesortorder = (MergeOrder*) nodeRead(true);
+    
+    token = lsptok(NULL, &length);      /* get :hashjoinoperator */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->hashjoinoperator = atol(token);
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readJoinMethod
+ *     
+ *  JoinMethod is a subclass of Node.
+ * ----------------
+ */
+static JoinMethod *
+_readJoinMethod()
+{
+    JoinMethod         *local_node;
+    char       *token;
+    int length;
+    
+    local_node = makeNode(JoinMethod);
+    
+    token = lsptok(NULL, &length);      /* get :jmkeys */
+    local_node->jmkeys = nodeRead(true);/* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :clauses */
+    local_node->clauses = nodeRead(true); /* now read it */
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readHInfo
+ *     
+ * HInfo is a subclass of JoinMethod.
+ * ----------------
+ */
+static HInfo *
+_readHInfo()
+{
+    HInfo      *local_node;
+    char       *token;
+    int length;
+    
+    local_node = makeNode(HInfo);
+    
+    token = lsptok(NULL, &length);      /* get :hashop */
+    token = lsptok(NULL, &length);      /* now read it */
+    
+    local_node->hashop = atoi(token);
+    
+    token = lsptok(NULL, &length);      /* get :jmkeys */
+    local_node->jmethod.jmkeys = nodeRead(true); /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :clauses */
+    local_node->jmethod.clauses = nodeRead(true);           /* now read it */
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readJInfo()
+ *     
+ *  JInfo is a subclass of Node.
+ * ----------------
+ */
+static JInfo *
+_readJInfo()
+{
+    JInfo      *local_node;
+    char       *token;
+    int length;
+    
+    local_node = makeNode(JInfo);
+    
+    token = lsptok(NULL, &length);      /* get :otherrels */
+    local_node->otherrels =
+       toIntList(nodeRead(true));      /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :jinfoclauseinfo */
+    local_node->jinfoclauseinfo = nodeRead(true); /* now read it */
+    
+    token = lsptok(NULL, &length);      /* get :mergesortable */
+    
+    if (!strncmp(token, "true", 4))
+       {
+           local_node->mergesortable = true;
+       }
+    else
+       {
+           local_node->mergesortable = false;
+       }
+    
+    token = lsptok(NULL, &length);      /* get :hashjoinable */
+    
+    if (!strncmp(token, "true", 4))
+       {
+           local_node->hashjoinable = true;
+       }
+    else
+       {
+           local_node->hashjoinable = false;
+       }
+    
+    return(local_node);
+}
+
+/* ----------------
+ *     _readIter()
+ *
+ * ----------------
+ */
+static Iter *
+_readIter()
+{
+    Iter       *local_node;
+    char       *token;
+    int length;
+    
+    local_node = makeNode(Iter);
+    
+    token = lsptok(NULL, &length);      /* eat :iterexpr */
+    local_node->iterexpr = nodeRead(true);     /* now read it */
+    
+    return(local_node);
+}
+
+
+/* ----------------
+ *     parsePlanString
+ *
+ * Given a character string containing a plan, parsePlanString sets up the
+ * plan structure representing that plan.
+ *
+ * The string passed to parsePlanString must be null-terminated.
+ * ----------------
+ */
+Node *
+parsePlanString()
+{
+    char *token;
+    int length;
+    void *return_value;
+    
+    token = lsptok(NULL, &length);
+    
+    if (!strncmp(token, "PLAN", 4)) {
+       return_value = _readPlan();
+    }else if (!strncmp(token, "RESULT", 6)) {
+       return_value = _readResult();
+    }else if (!strncmp(token, "EXISTENTIAL", 11)) {
+       return_value = _readExistential();
+    }else if (!strncmp(token, "APPEND", 6)) {
+       return_value = _readAppend();
+    }else if (!strncmp(token, "JOIN", 4)) {
+       return_value = _readJoin();
+    }else if (!strncmp(token, "NESTLOOP", 8)) {
+       return_value = _readNestLoop();
+    }else if (!strncmp(token, "MERGEJOIN", 9)) {
+       return_value = _readMergeJoin();
+    }else if (!strncmp(token, "HASHJOIN", 8)) {
+       return_value = _readHashJoin();
+    }else if (!strncmp(token, "SCAN", 4)) {
+       return_value = _readScan();
+    }else if (!strncmp(token, "SEQSCAN", 7)) {
+       return_value = _readSeqScan();
+    }else if (!strncmp(token, "INDEXSCAN", 9)) {
+       return_value = _readIndexScan();
+    }else if (!strncmp(token, "TEMP", 4)) {
+       return_value = _readTemp();
+    }else if (!strncmp(token, "SORT", 4)) {
+       return_value = _readSort();
+    }else if (!strncmp(token, "AGG", 3)) {
+       return_value = _readAgg();
+    }else if (!strncmp(token, "UNIQUE", 4)) {
+       return_value = _readUnique();
+    }else if (!strncmp(token, "HASH", 4)) {
+       return_value = _readHash();
+    }else if (!strncmp(token, "RESDOM", 6)) {
+       return_value = _readResdom();
+    }else if (!strncmp(token, "EXPR", 4)) {
+       return_value = _readExpr();
+    }else if (!strncmp(token, "ARRAYREF", 7)) {
+       /* make sure this strncmp is done before that of ARRAY */
+       return_value = _readArrayRef();
+    }else if (!strncmp(token, "ARRAY", 5)) {
+       return_value = _readArray();
+    }else if (!strncmp(token, "VAR", 3)) {
+       return_value = _readVar();
+    }else if (!strncmp(token, "CONST", 5)) {
+       return_value = _readConst();
+    }else if (!strncmp(token, "FUNC", 4)) {
+       return_value = _readFunc();
+    }else if (!strncmp(token, "OPER", 4)) {
+       return_value = _readOper();
+    }else if (!strncmp(token, "PARAM", 5)) {
+       return_value = _readParam();
+    }else if (!strncmp(token, "ESTATE", 6)) {
+       return_value = _readEState();
+    }else if (!strncmp(token, "REL", 3)) {
+       return_value = _readRel();
+    }else if (!strncmp(token, "TLE", 3)) {
+       return_value = _readTargetEntry();
+    }else if (!strncmp(token, "RTE", 3)) {
+       return_value = _readRangeTblEntry();
+    }else if (!strncmp(token, "PATH", 4)) {
+       return_value = _readPath();
+    }else if (!strncmp(token, "INDEXPATH", 9)) {
+       return_value = _readIndexPath();
+    }else if (!strncmp(token, "JOINPATH", 8)) {
+       return_value = _readJoinPath();
+    }else if (!strncmp(token, "MERGEPATH", 9)) {
+       return_value = _readMergePath();
+    }else if (!strncmp(token, "HASHPATH", 8)) {
+       return_value = _readHashPath();
+    }else if (!strncmp(token, "ORDERKEY", 8)) {
+       return_value = _readOrderKey();
+    }else if (!strncmp(token, "JOINKEY", 7)) {
+       return_value = _readJoinKey();
+    }else if (!strncmp(token, "MERGEORDER", 10)) {
+       return_value = _readMergeOrder();
+    }else if (!strncmp(token, "CINFO", 5)) {
+       return_value = _readCInfo();
+    }else if (!strncmp(token, "JOINMETHOD", 10)) {
+       return_value = _readJoinMethod();
+    }else if (!strncmp(token, "JINFO", 5)) {
+       return_value = _readJInfo();
+    }else if (!strncmp(token, "HINFO", 5)) {
+       return_value = _readHInfo();
+    }else if (!strncmp(token, "ITER", 4)) {
+       return_value = _readIter();
+    }else if (!strncmp(token, "QUERY", 5)) {
+       return_value = _readQuery();
+    }else {
+       elog(WARN, "badly formatted planstring \"%.10s\"...\n", token);
+    }
+
+    return ((Node*)return_value);
+}
+/*------------------------------------------------------------*/
+
+/* ----------------
+ *     readDatum
+ *
+ * given a string representation of the value of the given type,
+ * create the appropriate Datum
+ * ----------------
+ */
+static Datum
+readDatum(Oid type)
+{
+    int        length;
+    int        tokenLength;
+    char       *token;
+    bool       byValue;
+    Datum      res;
+    char       *s;
+    int        i;
+    
+    byValue    = get_typbyval(type);
+    
+    /*
+     * read the actual length of the value
+     */
+    token = lsptok(NULL, &tokenLength);
+    length = atoi(token);
+    token = lsptok(NULL, &tokenLength);        /* skip the '[' */
+    
+    if (byValue) {
+       if (length > sizeof(Datum)) {
+           elog(WARN, "readValue: byval & length = %d", length);
+       }
+       s = (char *) (&res);
+       for (i=0; i<sizeof(Datum); i++) {
+           token = lsptok(NULL, &tokenLength);
+           s[i] = (char) atoi(token);
+       }
+    } else if (length <= 0) {
+       s = NULL;
+    } else if (length >= 1) {
+       s = (char*)palloc(length);
+       Assert( s!=NULL );
+       for (i=0; i<length; i++) {
+           token = lsptok(NULL, &tokenLength);
+           s[i] = (char) atoi(token);
+       }
+       res = PointerGetDatum(s);
+    }
+    
+    token = lsptok(NULL, &tokenLength);        /* skip the ']' */
+    if (token[0] != ']') {
+       elog(WARN, "readValue: ']' expected, length =%d", length);
+    }
+    
+    return(res);
+}
diff --git a/src/backend/nodes/readfuncs.h b/src/backend/nodes/readfuncs.h
new file mode 100644 (file)
index 0000000..de8b818
--- /dev/null
@@ -0,0 +1,27 @@
+/*-------------------------------------------------------------------------
+ *
+ * readfuncs.h--
+ *    header file for read.c and readfuncs.c. These functions are internal
+ *    to the stringToNode interface and should not be used by anyone else.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        READFUNCS_H
+#define        READFUNCS_H
+
+/*
+ * prototypes for functions in read.c (the lisp token parser)
+ */
+extern char *lsptok(char *string, int *length);
+extern void *nodeRead(bool read_car_only);
+
+/*
+ * prototypes for functions in readfuncs.c 
+ */
+extern Node *parsePlanString();
+
+#endif /* READFUNCS_H */
diff --git a/src/backend/nodes/relation.h b/src/backend/nodes/relation.h
new file mode 100644 (file)
index 0000000..50b3e62
--- /dev/null
@@ -0,0 +1,279 @@
+/*-------------------------------------------------------------------------
+ *
+ * relation.h--
+ *    Definitions for internal planner nodes.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef RELATION_H
+#define RELATION_H
+
+#include "c.h"
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
+#include "nodes/parsenodes.h"
+#include "nodes/nodes.h"
+
+/*
+ * Relid
+ *     List of relation identifiers (indexes into the rangetable).
+ */
+
+typedef        List    *Relid;
+
+/*
+ * Rel
+ *     Per-base-relation information
+ *
+ *     Parts of this data structure are specific to various scan and join
+ *     mechanisms.  It didn't seem worth creating new node types for them.
+ *
+ *     relids - List of relation indentifiers
+ *     indexed - true if the relation has secondary indices
+ *     pages - number of pages in the relation
+ *     tuples - number of tuples in the relation
+ *     size - number of tuples in the relation after restrictions clauses
+ *            have been applied
+ *     width - number of bytes per tuple in the relation after the
+ *             appropriate projections have been done
+ *     targetlist - List of TargetList nodes
+ *     pathlist - List of Path nodes, one for each possible method of
+ *                generating the relation
+ *     unorderedpath - a Path node generating this relation whose resulting
+ *                     tuples are unordered (this isn't necessarily a
+ *                     sequential scan path, e.g., scanning with a hash index
+ *                     leaves the tuples unordered)
+ *     cheapestpath -  least expensive Path (regardless of final order)
+ *      pruneable - flag to let the planner know whether it can prune the plan
+ *                  space of this Rel or not.  -- JMH, 11/11/92
+ *
+ *   * If the relation is a (secondary) index it will have the following
+ *     three fields:
+ *
+ *     classlist - List of PG_AMOPCLASS OIDs for the index
+ *     indexkeys - List of base-relation attribute numbers that are index keys
+ *     ordering - List of PG_OPERATOR OIDs which order the indexscan result
+ *      relam     - the OID of the pg_am of the index
+ *
+ *   * The presence of the remaining fields depends on the restrictions
+ *     and joins which the relation participates in:
+ *
+ *     clauseinfo - List of ClauseInfo nodes, containing info about each
+ *                  qualification clause in which this relation participates
+ *     joininfo  - List of JoinInfo nodes, containing info about each join
+ *                 clause in which this relation participates
+ *     innerjoin - List of Path nodes that represent indices that may be used
+ *                 as inner paths of nestloop joins
+ *
+ * NB. the last element of the arrays classlist, indexkeys and ordering
+ *     is always 0.                            2/95 - ay
+ */
+
+typedef        struct Rel {
+    NodeTag    type;
+
+    /* all relations: */
+    Relid      relids;
+
+    /* catalog statistics information */
+    bool       indexed;
+    int                pages;
+    int                tuples;
+    int                size;
+    int                width;
+
+    /* materialization information */
+    List       *targetlist;
+    List       *pathlist;
+    struct Path        *unorderedpath;
+    struct Path        *cheapestpath;
+    bool       pruneable;
+
+    /* used solely by indices: */
+    Oid                *classlist;             /* classes of AM operators */
+    int                *indexkeys;             /* keys over which we're indexing */
+    Oid         relam;  /* OID of the access method (in pg_am) */
+                                  
+    Oid                indproc;
+    List       *indpred;
+
+    /* used by various scans and joins: */
+    Oid                *ordering;              /* OID of operators in sort order */
+    List       *clauseinfo;            /* restriction clauses */
+    List       *joininfo;              /* join clauses */
+    List       *innerjoin;
+    List       *superrels;
+} Rel;
+
+extern Var *get_expr(TargetEntry *foo);
+
+typedef struct MergeOrder {
+    NodeTag    type;
+    Oid        join_operator;
+    Oid        left_operator;
+    Oid        right_operator;
+    Oid        left_type;
+    Oid        right_type;
+} MergeOrder;
+
+typedef enum OrderType {
+    MERGE_ORDER, SORTOP_ORDER
+} OrderType;
+
+typedef struct PathOrder {
+    OrderType  ordtype;
+    union {
+       Oid             *sortop;
+       MergeOrder      *merge;
+    } ord;
+} PathOrder;
+
+typedef struct Path {
+    NodeTag    type;
+
+    Rel                *parent;
+    Cost       path_cost;
+
+    NodeTag    pathtype;
+
+    PathOrder  p_ordering;
+
+    List       *keys;
+    Cost       outerjoincost;
+    Relid      joinid;
+    List        *locclauseinfo;
+} Path;
+
+typedef struct IndexPath {
+    Path       path;
+    List       *indexid;
+    List       *indexqual;
+} IndexPath;
+
+typedef struct JoinPath {
+    Path       path;
+    List       *pathclauseinfo;
+    Path       *outerjoinpath;
+    Path       *innerjoinpath;
+} JoinPath;
+
+typedef struct MergePath {
+    JoinPath   jpath;
+    List       *path_mergeclauses;
+    List       *outersortkeys;
+    List       *innersortkeys;
+} MergePath;
+
+typedef struct HashPath {
+    JoinPath   jpath;
+    List       *path_hashclauses;
+    List       *outerhashkeys;
+    List       *innerhashkeys;
+} HashPath;
+
+/******
+ * Keys
+ ******/
+
+typedef struct OrderKey {
+    NodeTag    type;
+    int        attribute_number;
+    Index      array_index;
+} OrderKey;
+
+typedef struct JoinKey {
+    NodeTag    type;
+    Var        *outer;
+    Var        *inner;
+} JoinKey;
+
+/*******
+ * clause info
+ *******/
+
+typedef struct CInfo {
+    NodeTag    type;
+    Expr       *clause;        /* should be an OP clause */
+    Cost       selectivity;
+    bool       notclause;
+    List       *indexids;
+
+    /* mergesort only */
+    MergeOrder *mergesortorder;
+
+    /* hashjoin only */
+    Oid                hashjoinoperator;
+    Relid      cinfojoinid;
+} CInfo;
+
+typedef struct JoinMethod {
+    NodeTag    type;
+    List        *jmkeys;
+    List        *clauses;
+} JoinMethod;
+
+typedef struct HInfo {
+    JoinMethod jmethod;
+    Oid                hashop;
+} HInfo;
+
+typedef struct MInfo {
+    JoinMethod jmethod;
+    MergeOrder *m_ordering;
+} MInfo;
+
+typedef struct JInfo {
+    NodeTag    type;
+    List       *otherrels;
+    List       *jinfoclauseinfo;
+    bool       mergesortable;
+    bool       hashjoinable;
+    bool       inactive;
+} JInfo;
+
+typedef struct Iter {
+    NodeTag    type;
+    Node       *iterexpr;
+    Oid                itertype;       /* type of the iter expr (use for type
+                                  checking) */
+} Iter;
+
+/*
+** Stream:
+**   A stream represents a root-to-leaf path in a plan tree (i.e. a tree of
+** JoinPaths and Paths).  The stream includes pointers to all Path nodes,
+** as well as to any clauses that reside above Path nodes.  This structure
+** is used to make Path nodes and clauses look similar, so that Predicate
+** Migration can run.
+**
+**     pathptr -- pointer to the current path node
+**       cinfo -- if NULL, this stream node referes to the path node.
+**                Otherwise this is a pointer to the current clause.
+**  clausetype -- whether cinfo is in locclauseinfo or pathclauseinfo in the 
+**                path node
+**    upstream -- linked list pointer upwards
+**  downstream -- ditto, downwards
+**     groupup -- whether or not this node is in a group with the node upstream
+**   groupcost -- total cost of the group that node is in
+**    groupsel -- total selectivity of the group that node is in
+*/
+typedef struct Stream *StreamPtr;
+
+typedef struct Stream {
+    NodeTag    type;
+    Path       *pathptr;
+    CInfo      *cinfo;
+    int                *clausetype;
+    struct Stream *upstream;
+    struct Stream *downstream;
+    bool       groupup;
+    Cost       groupcost;
+    Cost        groupsel;
+} Stream;
+
+#endif /* RELATION_H */
diff --git a/src/backend/optimizer/Makefile.inc b/src/backend/optimizer/Makefile.inc
new file mode 100644 (file)
index 0000000..9931697
--- /dev/null
@@ -0,0 +1,29 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the optimizer module
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+optdir=$(CURDIR)/optimizer
+VPATH:=$(VPATH):$(optdir):\
+       $(optdir)/path:$(optdir)/prep:$(optdir)/util:$(optdir)/plan
+
+SUBSRCS=
+include $(optdir)/path/Makefile.inc
+include $(optdir)/prep/Makefile.inc
+include $(optdir)/util/Makefile.inc
+include $(optdir)/plan/Makefile.inc
+SRCS_OPTIMIZER:= $(SUBSRCS)
+
+HEADERS+= clauseinfo.h clauses.h cost.h internal.h joininfo.h keys.h \
+       ordering.h pathnode.h paths.h plancat.h planmain.h \
+       planner.h prep.h tlist.h var.h xfunc.h
+
+
diff --git a/src/backend/optimizer/clauseinfo.h b/src/backend/optimizer/clauseinfo.h
new file mode 100644 (file)
index 0000000..51ab549
--- /dev/null
@@ -0,0 +1,24 @@
+/*-------------------------------------------------------------------------
+ *
+ * clauseinfo.h--
+ *    prototypes for clauseinfo.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CLAUSEINFO_H
+#define CLAUSEINFO_H
+
+extern bool valid_or_clause(CInfo *clauseinfo);
+extern List *get_actual_clauses(List *clauseinfo_list);
+extern void get_relattvals(List *clauseinfo_list, List **attnos,
+                          List **values, List **flags);
+extern void get_joinvars(Oid relid, List *clauseinfo_list,
+                        List **attnos, List **values, List **flags);
+extern List *get_opnos(List *clauseinfo_list);
+
+#endif /* CLAUSEINFO_H */
diff --git a/src/backend/optimizer/clauses.h b/src/backend/optimizer/clauses.h
new file mode 100644 (file)
index 0000000..483552e
--- /dev/null
@@ -0,0 +1,54 @@
+/*-------------------------------------------------------------------------
+ *
+ * clauses.h--
+ *    prototypes for clauses.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CLAUSES_H
+#define CLAUSES_H
+
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
+
+extern Expr *make_clause(int type, Node *oper, List *args);
+extern bool is_opclause(Node *clause);
+extern Expr *make_opclause(Oper *op, Var *leftop, Var *rightop);
+extern Var *get_leftop(Expr *clause);
+extern Var *get_rightop(Expr *clause);
+
+extern bool agg_clause(Node *clause);
+
+extern bool is_funcclause(Node *clause);
+extern Expr *make_funcclause(Func *func, List *funcargs);
+
+extern bool or_clause(Node *clause);
+extern Expr *make_orclause(List *orclauses);
+
+extern bool not_clause(Node *clause);
+extern Expr *make_notclause(Expr *notclause);
+extern Expr *get_notclausearg(Expr *notclause);
+
+extern bool and_clause(Node *clause);
+extern Expr *make_andclause(List *andclauses);
+
+extern List *pull_constant_clauses(List *quals, List **constantQual);
+extern void clause_relids_vars(Node *clause, List **relids, List **vars);
+extern int NumRelids(Node *clause);
+extern bool contains_not(Node *clause);
+extern bool join_clause_p(Node *clause);
+extern bool qual_clause_p(Node *clause);
+extern void fix_opid(Node *clause);
+extern List *fix_opids(List *clauses);
+extern void get_relattval(Node *clause, int *relid,
+       AttrNumber *attno, Datum *constval, int *flag);
+extern void get_rels_atts(Node *clause, int *relid1,
+       AttrNumber *attno1, int *relid2, AttrNumber *attno2);
+extern void CommuteClause(Node *clause);
+
+#endif /* CLAUSES_H */
diff --git a/src/backend/optimizer/cost.h b/src/backend/optimizer/cost.h
new file mode 100644 (file)
index 0000000..a5faded
--- /dev/null
@@ -0,0 +1,59 @@
+/*-------------------------------------------------------------------------
+ *
+ * cost.h--
+ *    prototypes for costsize.c and clausesel.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef COST_H
+#define COST_H
+
+/*
+ * prototypes for costsize.c--
+ *    routines to compute costs and sizes
+ */ 
+extern bool _enable_seqscan_;
+extern bool _enable_indexscan_;
+extern bool _enable_sort_;
+extern bool _enable_hash_;
+extern bool _enable_nestloop_;
+extern bool _enable_mergesort_;
+extern bool _enable_hashjoin_;
+
+extern Cost cost_seqscan(int relid, int relpages, int reltuples);
+extern Cost cost_index(Oid indexid, int expected_indexpages, Cost selec,
+                      int relpages, int reltuples, int indexpages,
+                      int indextuples, bool is_injoin);
+extern Cost cost_sort(List *keys, int tuples, int width, bool noread);
+extern Cost cost_result(int tuples, int width);
+extern Cost cost_nestloop(Cost outercost, Cost innercost, int outertuples,
+               int innertuples, int outerpages, bool is_indexjoin);
+extern Cost cost_mergesort(Cost outercost, Cost innercost,
+               List *outersortkeys, List *innersortkeys,
+               int outersize, int innersize, int outerwidth, int innerwidth);
+extern Cost cost_hashjoin(Cost outercost, Cost innercost, List *outerkeys,
+               List *innerkeys, int outersize, int innersize,
+               int outerwidth, int innerwidth);
+extern int compute_rel_size(Rel *rel);
+extern int compute_rel_width(Rel *rel);
+extern int compute_targetlist_width(List *targetlist);
+extern int compute_joinrel_size(JoinPath *joinpath);
+extern int page_size(int tuples, int width);
+
+/*
+ * prototypes for fuctions in clausesel.h--
+ *    routines to compute clause selectivities
+ */
+extern void set_clause_selectivities(List *clauseinfo_list, Cost new_selectivity);
+extern Cost product_selec(List *clauseinfo_list);
+extern void set_rest_relselec(Query *root, List *rel_list);
+extern void set_rest_selec(Query *root,List *clauseinfo_list);
+extern Cost compute_clause_selec(Query *root,
+                                Node *clause, List *or_selectivities);
+
+#endif /* COST_H */
diff --git a/src/backend/optimizer/internal.h b/src/backend/optimizer/internal.h
new file mode 100644 (file)
index 0000000..d01301b
--- /dev/null
@@ -0,0 +1,92 @@
+/*-------------------------------------------------------------------------
+ *
+ * internal.h--
+ *    Definitions required throughout the query optimizer.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef INTERNAL_H
+#define INTERNAL_H
+
+/*    
+ *     ---------- SHARED MACROS
+ *    
+ *             Macros common to modules for creating, accessing, and modifying
+ *     query tree and query plan components.
+ *     Shared with the executor.
+ *    
+ */
+
+#include "nodes/nodes.h"
+#include "nodes/primnodes.h"
+#include "nodes/plannodes.h"
+#include "parser/parsetree.h"
+#include "nodes/relation.h"
+#include "catalog/pg_index.h"  /* for INDEX_MAX_KEYS */
+#include "utils/syscache.h"    /* for SearchSysCacheGetAttribute, etc. */
+
+/*
+ *     System-dependent tuning constants
+ *    
+ */
+#define _CPU_PAGE_WEIGHT_  0.065      /* CPU-to-page cost weighting factor */
+#define _PAGE_SIZE_    8192           /* BLCKSZ (from ../h/bufmgr.h) */
+#define _MAX_KEYS_     INDEX_MAX_KEYS /* maximum number of keys in an index */
+#define _TID_SIZE_     6              /* sizeof(itemid) (from ../h/itemid.h) */
+
+/*    
+ *     Size estimates
+ *    
+ */
+
+/*     The cost of sequentially scanning a materialized temporary relation
+ */
+#define _TEMP_SCAN_COST_       10
+
+/*     The number of pages and tuples in a materialized relation
+ */
+#define _TEMP_RELATION_PAGES_          1
+#define _TEMP_RELATION_TUPLES_         10
+
+/*     The length of a variable-length field in bytes
+ */
+#define _DEFAULT_ATTRIBUTE_WIDTH_ (2 * _TID_SIZE_)
+
+/*    
+ *     Flags and identifiers
+ *    
+ */
+
+/*     Identifier for (sort) temp relations   */
+/* used to be -1 */
+#define _TEMP_RELATION_ID_   InvalidOid
+
+/*     Identifier for invalid relation OIDs and attribute numbers for use by
+ *     selectivity functions
+ */
+#define _SELEC_VALUE_UNKNOWN_   -1
+
+/*     Flag indicating that a clause constant is really a parameter (or other 
+ *             non-constant?), a non-parameter, or a constant on the right side
+ *     of the clause.
+ */
+#define _SELEC_NOT_CONSTANT_   0
+#define _SELEC_IS_CONSTANT_    1
+#define _SELEC_CONSTANT_LEFT_  0
+#define _SELEC_CONSTANT_RIGHT_ 2
+
+#define TOLERANCE 0.000001
+
+#define FLOAT_EQUAL(X,Y) ((X) - (Y) < TOLERANCE)
+#define FLOAT_IS_ZERO(X) (FLOAT_EQUAL(X,0.0))
+
+extern int BushyPlanFlag;
+/* #define deactivate_joininfo(joininfo)       joininfo->inactive=true*/
+/*#define joininfo_inactive(joininfo)  joininfo->inactive */
+
+#endif /* INTERNAL_H */
diff --git a/src/backend/optimizer/joininfo.h b/src/backend/optimizer/joininfo.h
new file mode 100644 (file)
index 0000000..c9c054b
--- /dev/null
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * joininfo.h--
+ *    prototypes for joininfo.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef JOININFO_H
+#define JOININFO_H
+
+extern JInfo *joininfo_member(List *join_relids, List *joininfo_list);
+extern JInfo *find_joininfo_node(Rel *this_rel, List *join_relids);
+extern Var *other_join_clause_var(Var *var, Expr *clause);
+
+#endif /* JOININFO_H */
diff --git a/src/backend/optimizer/keys.h b/src/backend/optimizer/keys.h
new file mode 100644 (file)
index 0000000..75cfc4e
--- /dev/null
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * keys.h--
+ *    prototypes for keys.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef KEYS_H
+#define KEYS_H
+
+extern bool match_indexkey_operand(int indexkey, Var *operand, Rel *rel);
+extern bool equal_indexkey_var(int index_key, Var *var);
+extern Var *extract_subkey(JoinKey *jk, int which_subkey);
+extern bool samekeys(List *keys1, List *keys2);
+extern List *collect_index_pathkeys(int *index_keys, List *tlist);
+
+#endif /* KEYS_H */
diff --git a/src/backend/optimizer/ordering.h b/src/backend/optimizer/ordering.h
new file mode 100644 (file)
index 0000000..7e19542
--- /dev/null
@@ -0,0 +1,24 @@
+/*-------------------------------------------------------------------------
+ *
+ * ordering.h--
+ *    prototypes for ordering.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef ORDERING_H
+#define ORDERING_H
+
+extern bool equal_path_path_ordering(PathOrder *path_ordering1,
+                                    PathOrder *path_ordering2);
+extern bool equal_path_merge_ordering(Oid *path_ordering,
+                                     MergeOrder *merge_ordering);
+extern bool equal_merge_merge_ordering(MergeOrder *merge_ordering1,
+                                      MergeOrder *merge_ordering2);
+extern bool equal_sortops_order(Oid *ordering1, Oid *ordering2);
+
+#endif /* ORDERING_H */
diff --git a/src/backend/optimizer/path/Makefile.inc b/src/backend/optimizer/path/Makefile.inc
new file mode 100644 (file)
index 0000000..e48ed29
--- /dev/null
@@ -0,0 +1,21 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for optimizer/path
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS= allpaths.c clausesel.c costsize.c hashutils.c indxpath.c \
+       joinpath.c joinrels.c joinutils.c mergeutils.c orindxpath.c \
+       prune.c
+
+# not ready yet: predmig.c xfunc.c
+
+
+
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
new file mode 100644 (file)
index 0000000..66996d1
--- /dev/null
@@ -0,0 +1,351 @@
+/*-------------------------------------------------------------------------
+ *
+ * allpaths.c--
+ *    Routines to find possible search paths for processing a query
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/relation.h"
+#include "nodes/primnodes.h"
+
+#include "optimizer/internal.h"
+
+#include "optimizer/paths.h"
+#include "optimizer/pathnode.h"
+#include "optimizer/clauses.h"
+#include "optimizer/xfunc.h"
+#include "optimizer/cost.h"
+
+#include "commands/creatinh.h"
+
+static void find_rel_paths(Query *root, List *rels);
+static List *find_join_paths(Query *root, List *outer_rels, int levels_left);
+
+/*    
+ * find-paths--
+ *    Finds all possible access paths for executing a query, returning the
+ *    top level list of relation entries.  
+ *    
+ * 'rels' is the list of single relation entries appearing in the query
+ */
+List *
+find_paths(Query *root, List *rels)
+{
+    int levels_left;
+
+    /*
+     * Set the number of join (not nesting) levels yet to be processed.
+     */
+    levels_left = length(rels);
+
+    if (levels_left <= 0)
+       return NIL;
+
+    /*
+     * Find the base relation paths.
+     */
+    find_rel_paths(root, rels);
+       
+    if (levels_left <= 1) {
+       /*
+        * Unsorted single relation, no more processing is required.
+        */
+       return (rels);   
+    }else {
+       /* 
+        * this means that joins or sorts are required.
+        * set selectivities of clauses that have not been set
+        * by an index.
+        */
+       set_rest_relselec(root, rels);
+
+       return(find_join_paths(root, rels, levels_left-1));
+    }
+}
+
+/*    
+ * find-rel-paths--
+ *    Finds all paths available for scanning each relation entry in 
+ *    'rels'.  Sequential scan and any available indices are considered
+ *    if possible(indices are not considered for lower nesting levels).
+ *    All unique paths are attached to the relation's 'pathlist' field.
+ *    
+ *    MODIFIES: rels
+ */
+static void
+find_rel_paths(Query *root, List *rels)
+{
+    List *temp;
+    Rel *rel;
+    List *lastpath;
+     
+    foreach(temp, rels) {
+       List *sequential_scan_list;
+       List *rel_index_scan_list;
+       List *or_index_scan_list;
+
+       rel = (Rel *)lfirst(temp);
+       sequential_scan_list = lcons(create_seqscan_path(rel),
+                                   NIL);
+
+       rel_index_scan_list = 
+           find_index_paths(root, 
+                            rel,
+                            find_relation_indices(root,rel),
+                            rel->clauseinfo,
+                            rel->joininfo);
+
+       or_index_scan_list = 
+           create_or_index_paths(root, rel, rel->clauseinfo);
+
+       rel->pathlist = add_pathlist(rel,
+                                    sequential_scan_list,
+                                    append(rel_index_scan_list, 
+                                           or_index_scan_list));
+    
+       /* The unordered path is always the last in the list.  
+        * If it is not the cheapest path, prune it.
+        */
+       lastpath = rel->pathlist;
+       while(lnext(lastpath)!=NIL)
+           lastpath=lnext(lastpath);
+       prune_rel_path(rel, (Path*)lfirst(lastpath));
+      /*
+       * if there is a qualification of sequential scan the selec.
+       * value is not set -- so set it explicitly -- Sunita
+       */
+      set_rest_selec(root, rel->clauseinfo);
+       rel->size = compute_rel_size(rel);
+       rel->width = compute_rel_width(rel);
+    }
+    return;
+}
+
+/*    
+ * find-join-paths--
+ *    Find all possible joinpaths for a query by successively finding ways
+ *    to join single relations into join relations.  
+ *
+ *    if BushyPlanFlag is set, bushy tree plans will be generated:
+ *    Find all possible joinpaths(bushy trees) for a query by systematically
+ *    finding ways to join relations(both original and derived) together.
+ *    
+ * 'outer-rels' is the current list of relations for which join paths 
+ *             are to be found, i.e., he current list of relations that 
+ *             have already been derived.
+ * 'levels-left' is the current join level being processed, where '1' is
+ *             the "last" level
+ *    
+ * Returns the final level of join relations, i.e., the relation that is
+ * the result of joining all the original relations togehter.
+ */
+static List *
+find_join_paths(Query *root, List *outer_rels, int levels_left)
+{
+    List *x;
+    List *new_rels;
+    Rel *rel;
+
+    /*
+     * Determine all possible pairs of relations to be joined at this level.
+     * Determine paths for joining these relation pairs and modify 'new-rels'
+     * accordingly, then eliminate redundant join relations.
+     */
+    new_rels = find_join_rels(root, outer_rels);
+
+    find_all_join_paths(root, new_rels);
+
+    new_rels = prune_joinrels(new_rels);
+
+#if 0    
+    /* 
+     ** for each expensive predicate in each path in each distinct rel, 
+     ** consider doing pullup  -- JMH 
+     */
+    if (XfuncMode != XFUNC_NOPULL && XfuncMode != XFUNC_OFF)
+       foreach(x, new_rels)
+           xfunc_trypullup((Rel*)lfirst(x));
+#endif
+
+    prune_rel_paths(new_rels);
+
+    if(BushyPlanFlag) {
+       /*
+        * In case of bushy trees
+        * if there is still a join between a join relation and another
+        * relation, add a new joininfo that involves the join relation
+        * to the joininfo list of the other relation
+        */
+       add_new_joininfos(root, new_rels,outer_rels);
+    }
+
+    foreach(x, new_rels) {
+       rel = (Rel*)lfirst(x);
+       rel->size = compute_rel_size(rel);
+       rel->width = compute_rel_width(rel);
+
+/*#define OPTIMIZER_DEBUG*/
+#ifdef OPTIMIZER_DEBUG
+       printf("levels left: %d\n", levels_left);
+       debug_print_rel(root, rel);
+#endif 
+    }
+
+    if(BushyPlanFlag) {
+       /* 
+        * prune rels that have been completely incorporated into
+        * new join rels
+        */
+       outer_rels = prune_oldrels(outer_rels);
+       /* 
+        * merge join rels if then contain the same list of base rels
+        */
+       outer_rels = merge_joinrels(new_rels,outer_rels);
+       root->join_relation_list_ = outer_rels;
+    }
+    else {
+       root->join_relation_list_ = new_rels;
+    }
+
+    if(levels_left == 1) {
+       if(BushyPlanFlag)
+           return(final_join_rels(outer_rels));
+       else
+           return(new_rels);
+    } else {
+       if(BushyPlanFlag)
+           return(find_join_paths(root, outer_rels, levels_left - 1));
+       else
+           return(find_join_paths(root, new_rels, levels_left - 1));
+    } 
+}
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+static void
+print_joinclauses(Query *root, List *clauses)
+{
+    List *l;
+    extern void print_expr(Node *expr, List *rtable); /* in print.c */
+
+    foreach(l, clauses) {
+       CInfo *c = lfirst(l);
+
+       print_expr((Node*)c->clause, root->rtable);
+       if (lnext(l)) printf(" ");
+    }
+}
+
+void
+print_path(Query *root, Path *path, int indent)
+{
+    char *ptype = NULL;
+    JoinPath *jp;
+    bool join;
+    int i;
+
+    for(i=0; i < indent; i++)
+       printf("\t");
+    
+    switch(nodeTag(path)) {
+    case T_Path:
+       ptype = "SeqScan"; join=false; break;
+    case T_IndexPath:
+       ptype = "IdxScan"; join=false; break;
+    case T_JoinPath:
+       ptype = "Nestloop"; join=true; break;
+    case T_MergePath:
+       ptype = "MergeJoin"; join=true; break;
+    case T_HashPath:
+       ptype = "HashJoin"; join=true; break;
+    default:
+       break;
+    }
+    if (join) {
+       int size = path->parent->size;
+       jp = (JoinPath*)path;
+       printf("%s size=%d cost=%f\n", ptype, size, path->path_cost);
+       switch(nodeTag(path)) {
+       case T_MergePath:
+       case T_HashPath:
+           for(i=0; i < indent+1; i++)
+               printf("\t");
+           printf("   clauses=(");
+           print_joinclauses(root,
+                             ((JoinPath*)path)->pathclauseinfo);
+           printf(")\n");
+
+           if (nodeTag(path)==T_MergePath) {
+               MergePath *mp = (MergePath*)path;
+               if (mp->outersortkeys || mp->innersortkeys) {
+                   for(i=0; i < indent+1; i++)
+                       printf("\t");
+                   printf("   sortouter=%d sortinner=%d\n",
+                          ((mp->outersortkeys)?1:0),
+                          ((mp->innersortkeys)?1:0));
+               }
+           }
+           break;
+       default:
+           break;
+       }
+       print_path(root, jp->outerjoinpath, indent+1);
+       print_path(root, jp->innerjoinpath, indent+1);
+    } else {
+       int size = path->parent->size;
+       int relid = lfirsti(path->parent->relids);
+       printf("%s(%d) size=%d cost=%f",
+              ptype, relid, size, path->path_cost);
+
+       if (nodeTag(path)==T_IndexPath) {
+           List *k, *l;
+
+           printf(" keys=");
+           foreach (k, path->keys) {
+               printf("(");
+               foreach (l, lfirst(k)) {
+                   Var *var = lfirst(l);
+                   printf("%d.%d", var->varnoold, var->varoattno);
+                   if (lnext(l)) printf(", ");
+               }
+               printf(")");
+               if (lnext(k)) printf(", ");
+           }
+       }
+       printf("\n");
+    }
+}
+
+#ifdef OPTIMIZER_DEBUG
+static void 
+debug_print_rel(Query *root, Rel *rel)
+{
+    List *l;
+
+    printf("(");
+    foreach(l, rel->relids) {
+       printf("%d ", lfirsti(l));
+    }
+    printf("): size=%d width=%d\n", rel->size, rel->width);
+
+    printf("\tpath list:\n");
+    foreach (l, rel->pathlist) {
+       print_path(root, lfirst(l), 1);
+    }
+    printf("\tcheapest path:\n");
+    print_path(root, rel->cheapestpath, 1);
+}
+#endif /* OPTIMIZER_DEBUG */
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
new file mode 100644 (file)
index 0000000..fdf4dd0
--- /dev/null
@@ -0,0 +1,331 @@
+/*-------------------------------------------------------------------------
+ *
+ * clausesel.c--
+ *    Routines to compute and set clause selectivities 
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/relation.h"
+#include "nodes/primnodes.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/clauses.h"
+#include "optimizer/clauseinfo.h"
+#include "optimizer/cost.h"
+#include "optimizer/plancat.h"
+
+#include "parser/parsetree.h"          /* for getrelid() */
+
+#include "catalog/pg_proc.h"
+#include "catalog/pg_operator.h"
+
+#include "utils/elog.h"
+#include "utils/lsyscache.h"
+
+static Cost compute_selec(Query *root, List *clauses, List *or_selectivities);
+
+/****************************************************************************
+ *     ROUTINES TO SET CLAUSE SELECTIVITIES  
+ ****************************************************************************/
+
+/*    
+ * set_clause_selectivities -
+ *    Sets the selectivity field for each of clause in 'clauseinfo-list'
+ *    to 'new-selectivity'.  If the selectivity has already been set, reset 
+ *    it only if the new one is better.
+ *    
+ * Returns nothing of interest.
+ *
+ */
+void
+set_clause_selectivities(List *clauseinfo_list, Cost new_selectivity)
+{
+    List *temp;
+    CInfo *clausenode;
+    Cost cost_clause;
+
+    foreach (temp,clauseinfo_list) {
+       clausenode = (CInfo*)lfirst(temp);
+       cost_clause = clausenode->selectivity;
+       if ( FLOAT_IS_ZERO(cost_clause) || new_selectivity < cost_clause) {
+           clausenode->selectivity = new_selectivity;
+       }
+    }
+}
+
+/*    
+ * product_selec -
+ *    Multiplies the selectivities of each clause in 'clauseinfo-list'.
+ *    
+ * Returns a flonum corresponding to the selectivity of 'clauseinfo-list'.
+ */
+Cost
+product_selec(List *clauseinfo_list)
+{
+    Cost result = 1.0;
+    if (clauseinfo_list!=NIL) {
+       List *xclausenode = NIL;
+       Cost temp;
+
+       foreach(xclausenode,clauseinfo_list) {
+           temp = ((CInfo *)lfirst(xclausenode))->selectivity;
+           result = result * temp;
+       }
+    }
+    return(result);
+}
+
+/*    
+ * set_rest_relselec -
+ *    Scans through clauses on each relation and assigns a selectivity to
+ *    those clauses that haven't been assigned a selectivity by an index.
+ *    
+ * Returns nothing of interest.
+ * MODIFIES: selectivities of the various rel's clauseinfo
+ *       slots. 
+ */
+void
+set_rest_relselec(Query *root, List *rel_list)
+{
+    Rel *rel;
+    List *x;
+
+    foreach (x,rel_list) {
+       rel = (Rel*)lfirst(x);
+       set_rest_selec(root, rel->clauseinfo);
+    }
+}
+
+/*    
+ * set_rest_selec -
+ *    Sets the selectivity fields for those clauses within a single
+ *    relation's 'clauseinfo-list' that haven't already been set.
+ *    
+ * Returns nothing of interest.
+ *    
+ */
+void
+set_rest_selec(Query *root, List *clauseinfo_list)
+{
+    List *temp = NIL;
+    CInfo *clausenode = (CInfo*)NULL;
+    Cost cost_clause;
+    
+    foreach (temp,clauseinfo_list) {
+       clausenode = (CInfo*)lfirst(temp);
+       cost_clause = clausenode->selectivity;
+
+       /*
+        * Check to see if the selectivity of this clause or any 'or'
+        * subclauses (if any) haven't been set yet.
+        */
+       if (valid_or_clause(clausenode) || FLOAT_IS_ZERO(cost_clause)) {
+           clausenode->selectivity =
+               compute_clause_selec(root, 
+                                    (Node*)clausenode->clause,
+                                    lcons(makeFloat(cost_clause), NIL));
+       }
+    }
+}
+
+/****************************************************************************
+ *     ROUTINES TO COMPUTE SELECTIVITIES
+ ****************************************************************************/
+
+/*    
+ * compute_clause_selec -
+ *    Given a clause, this routine will compute the selectivity of the
+ *    clause by calling 'compute_selec' with the appropriate parameters
+ *    and possibly use that return value to compute the real selectivity
+ *    of a clause.
+ *    
+ * 'or-selectivities' are selectivities that have already been assigned
+ *     to subclauses of an 'or' clause.
+ *    
+ * Returns a flonum corresponding to the clause selectivity.
+ *    
+ */
+Cost
+compute_clause_selec(Query *root, Node *clause, List *or_selectivities)
+{
+    if (!is_opclause (clause)) {
+       /* if it's not an operator clause, then it is a boolean clause -jolly*/
+       /*
+        * Boolean variables get a selectivity of 1/2.
+        */
+       return(0.1);
+    } else if (not_clause (clause)) {
+       /*
+        * 'not' gets "1.0 - selectivity-of-inner-clause".
+        */
+       return (1.000000 - compute_selec(root,
+                                        lcons(get_notclausearg((Expr*)clause),
+                                             NIL),
+                                        or_selectivities));
+    } else if (or_clause(clause)) {
+       /*
+        * Both 'or' and 'and' clauses are evaluated as described in 
+        *    (compute_selec). 
+        */
+       return (compute_selec(root,
+                             ((Expr*)clause)->args, or_selectivities));
+    } else {
+       return(compute_selec(root,
+                            lcons(clause,NIL),or_selectivities));
+    } 
+}
+
+/*    
+ * compute_selec - 
+ *    Computes the selectivity of a clause.
+ *    
+ *    If there is more than one clause in the argument 'clauses', then the
+ *    desired selectivity is that of an 'or' clause.  Selectivities for an
+ *    'or' clause such as (OR a b) are computed by finding the selectivity
+ *    of a (s1) and b (s2) and computing s1+s2 - s1*s2.
+ *    
+ *    In addition, if the clause is an 'or' clause, individual selectivities
+ *    may have already been assigned by indices to subclauses.  These values
+ *    are contained in the list 'or-selectivities'.
+ *    
+ * Returns the clause selectivity as a flonum.
+ *    
+ */
+static Cost
+compute_selec(Query *root, List *clauses, List *or_selectivities)
+{
+    Cost s1 = 0;
+    List *clause = lfirst(clauses);
+
+    if (clauses==NULL) {
+       s1 = 1.0;
+    } else if (IsA(clause,Param)) {
+       /* XXX How're we handling this before?? -ay */
+       s1 = 1.0;
+    } else if (IsA(clause,Const)) {
+       s1 = ((bool) ((Const*) clause)->constvalue) ? 1.0 : 0.0;
+    } else if (IsA(clause,Var)) {
+       Oid relid = getrelid(((Var*)clause)->varno,
+                            root->rtable);
+
+       /*
+        * we have a bool Var.  This is exactly equivalent to the clause:
+        *      reln.attribute = 't'
+        * so we compute the selectivity as if that is what we have. The
+        * magic #define constants are a hack.  I didn't want to have to
+        * do system cache look ups to find out all of that info.
+        */
+
+       s1 = restriction_selectivity(EqualSelectivityProcedure,
+                                    BooleanEqualOperator,
+                                    relid,
+                                    ((Var*)clause)->varoattno,
+                                    "t",
+                                    _SELEC_CONSTANT_RIGHT_);
+    } else if (or_selectivities) {
+       /* If s1 has already been assigned by an index, use that value. */ 
+       List *this_sel = lfirst(or_selectivities);
+
+       s1 = floatVal(this_sel);
+    } else if (is_funcclause((Node*)clause)) {
+       /* this isn't an Oper, it's a Func!! */
+       /*
+        ** This is not an operator, so we guess at the selectivity.  
+        ** THIS IS A HACK TO GET V4 OUT THE DOOR.  FUNCS SHOULD BE
+        ** ABLE TO HAVE SELECTIVITIES THEMSELVES.
+        **     -- JMH 7/9/92
+        */
+       s1 = 0.1;
+    } else if (NumRelids((Node*) clause) == 1) {
+       /* ...otherwise, calculate s1 from 'clauses'. 
+        *    The clause is not a join clause, since there is 
+        *    only one relid in the clause.  The clause 
+        *    selectivity will be based on the operator 
+        *    selectivity and operand values. 
+        */
+       Oid opno = ((Oper*)((Expr*)clause)->oper)->opno;
+       RegProcedure oprrest = get_oprrest(opno);
+       Oid relid;
+       int relidx;
+       AttrNumber attno;
+       Datum constval;
+       int flag;
+
+       get_relattval((Node*)clause, &relidx, &attno, &constval, &flag);
+       relid = getrelid(relidx, root->rtable); 
+       
+       /* if the oprrest procedure is missing for whatever reason,
+          use a selectivity of 0.5*/
+       if (!oprrest)
+           s1 = (Cost) (0.5);
+       else
+           if (attno == InvalidAttrNumber)  {
+               /* attno can be Invalid if the clause had a function in it,
+                  i.e.   WHERE myFunc(f) = 10 */
+               /* this should be FIXED somehow to use function selectivity */
+               s1 = (Cost) (0.5);
+           } else
+               s1 = (Cost) restriction_selectivity(oprrest,
+                                               opno,
+                                               relid,
+                                               attno,
+                                               (char *)constval,
+                                               flag);
+
+    } else {
+       /*    The clause must be a join clause.  The clause 
+        *    selectivity will be based on the relations to be 
+        *    scanned and the attributes they are to be joined 
+        *    on. 
+        */
+       Oid opno = ((Oper*)((Expr*)clause)->oper)->opno;
+       RegProcedure oprjoin = get_oprjoin (opno);
+       int relid1, relid2;
+       AttrNumber attno1, attno2;
+
+       get_rels_atts((Node*)clause, &relid1, &attno1, &relid2, &attno2);
+       relid1 = getrelid(relid1, root->rtable);
+       relid2 = getrelid(relid2, root->rtable);
+
+       /* if the oprjoin procedure is missing for whatever reason,
+          use a selectivity of 0.5*/
+       if (!oprjoin)
+           s1 = (Cost) (0.5);
+       else
+           s1 = (Cost) join_selectivity(oprjoin,
+                                        opno,
+                                        relid1,
+                                        attno1,
+                                        relid2,
+                                        attno2);
+    }
+    
+    /*    A null clause list eliminates no tuples, so return a selectivity 
+     *    of 1.0.  If there is only one clause, the selectivity is not 
+     *    that of an 'or' clause, but rather that of the single clause.
+     */
+    
+    if (length (clauses) < 2) {
+       return(s1);
+    } else {
+       /* Compute selectivity of the 'or'ed subclauses. */
+       /* Added check for taking lnext(NIL).  -- JMH 3/9/92 */
+       Cost s2;
+
+       if (or_selectivities != NIL)
+           s2 = compute_selec(root, lnext(clauses), lnext(or_selectivities));
+       else
+           s2 = compute_selec(root, lnext(clauses), NIL);
+       return(s1 + s2 - s1 * s2);
+    }
+}
+
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
new file mode 100644 (file)
index 0000000..508c49a
--- /dev/null
@@ -0,0 +1,456 @@
+/*-------------------------------------------------------------------------
+ *
+ * costsize.c--
+ *    Routines to compute (and set) relation sizes and path costs
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <math.h>
+#ifdef WIN32
+#include <float.h>
+#include <limits.h>
+#define MAXINT        INT_MAX
+#else
+# if defined(PORTNAME_BSD44_derived) || defined(PORTNAME_bsdi)
+# include <machine/limits.h>
+# define MAXINT        INT_MAX
+# else
+# include <values.h>
+# endif /* !PORTNAME_BSD44_derived */
+#endif /* WIN32 */
+
+#include "postgres.h"
+
+#include "nodes/relation.h"
+
+#include "optimizer/cost.h"
+#include "optimizer/internal.h"
+#include "optimizer/keys.h"
+#include "optimizer/tlist.h"
+
+#include "storage/bufmgr.h"    /* for BLCKSZ */
+
+static int compute_attribute_width(TargetEntry *tlistentry);
+static double base_log(double x, double b);
+
+int _disable_cost_ = 30000000;
+bool _enable_seqscan_ =     true;
+bool _enable_indexscan_ =   true;
+bool _enable_sort_ =        true;
+bool _enable_hash_ =        true;
+bool _enable_nestloop_ =    true;
+bool _enable_mergesort_ =   true;
+bool _enable_hashjoin_ =    true;
+
+/*    
+ * cost_seqscan--
+ *    Determines and returns the cost of scanning a relation sequentially.
+ *    If the relation is a temporary to be materialized from a query
+ *    embedded within a data field (determined by 'relid' containing an
+ *    attribute reference), then a predetermined constant is returned (we
+ *    have NO IDEA how big the result of a POSTQUEL procedure is going to
+ *    be).
+ *    
+ *     disk = p
+ *     cpu = *CPU-PAGE-WEIGHT* * t
+ *    
+ * 'relid' is the relid of the relation to be scanned
+ * 'relpages' is the number of pages in the relation to be scanned
+ *     (as determined from the system catalogs)
+ * 'reltuples' is the number of tuples in the relation to be scanned
+ *    
+ * Returns a flonum.
+ *    
+ */
+Cost
+cost_seqscan(int relid, int relpages, int reltuples)
+{
+    Cost temp = 0;
+
+    if ( !_enable_seqscan_ )
+       temp += _disable_cost_;
+
+    if (relid < 0) {
+       /*
+        * cost of sequentially scanning a materialized temporary relation
+        */
+       temp += _TEMP_SCAN_COST_;
+    } else {
+       temp += relpages;
+       temp += _CPU_PAGE_WEIGHT_ * reltuples;
+    }
+    Assert(temp >= 0);
+    return(temp);
+}
+
+
+/*    
+ * cost_index--
+ *    Determines and returns the cost of scanning a relation using an index.
+ *    
+ *     disk = expected-index-pages + expected-data-pages
+ *     cpu = *CPU-PAGE-WEIGHT* *
+ *             (expected-index-tuples + expected-data-tuples)
+ *    
+ * 'indexid' is the index OID
+ * 'expected-indexpages' is the number of index pages examined in the scan
+ * 'selec' is the selectivity of the index
+ * 'relpages' is the number of pages in the main relation
+ * 'reltuples' is the number of tuples in the main relation
+ * 'indexpages' is the number of pages in the index relation
+ * 'indextuples' is the number of tuples in the index relation
+ *    
+ * Returns a flonum.
+ *    
+ */
+Cost
+cost_index(Oid indexid,
+          int expected_indexpages,
+          Cost selec,
+          int relpages,
+          int reltuples,
+          int indexpages,
+          int indextuples,
+          bool is_injoin)
+{
+    Cost temp;
+    Cost temp2;
+
+    temp = temp2 = (Cost) 0;
+
+    if (!_enable_indexscan_ && !is_injoin)
+       temp += _disable_cost_;
+
+    /* expected index relation pages */
+    temp += expected_indexpages;
+
+    /*   about one base relation page */
+    temp += Min(relpages,(int)ceil((double)selec*indextuples));
+
+    /*
+     * per index tuple
+     */
+    temp2 += selec * indextuples;
+    temp2 += selec * reltuples;
+
+    temp =  temp + (_CPU_PAGE_WEIGHT_ * temp2);
+    Assert(temp >= 0);
+    return(temp);
+}
+
+/*    
+ * cost_sort--
+ *    Determines and returns the cost of sorting a relation by considering
+ *    1. the cost of doing an external sort:   XXX this is probably too low
+ *             disk = (p lg p)
+ *             cpu = *CPU-PAGE-WEIGHT* * (t lg t)
+ *    2. the cost of reading the sort result into memory (another seqscan)
+ *       unless 'noread' is set
+ *    
+ * 'keys' is a list of sort keys
+ * 'tuples' is the number of tuples in the relation
+ * 'width' is the average tuple width in bytes
+ * 'noread' is a flag indicating that the sort result can remain on disk
+ *             (i.e., the sort result is the result relation)
+ *    
+ * Returns a flonum.
+ *    
+ */
+Cost
+cost_sort(List *keys, int tuples, int width, bool noread)
+{
+    Cost temp = 0;
+    int npages = page_size (tuples,width);
+    Cost pages = (Cost)npages;
+    Cost numTuples = tuples;
+    
+    if ( !_enable_sort_ ) 
+       temp += _disable_cost_ ;
+    if (tuples == 0 || keys==NULL)
+       {
+           Assert(temp >= 0);
+           return(temp);
+       }
+    temp += pages * base_log((double)pages, (double)2.0);
+
+    /*
+     * could be base_log(pages, NBuffers), but we are only doing 2-way merges
+     */
+    temp += _CPU_PAGE_WEIGHT_ *
+       numTuples * base_log((double)pages,(double)2.0);
+
+    if( !noread )
+       temp = temp + cost_seqscan(_TEMP_RELATION_ID_, npages, tuples);
+    Assert(temp >= 0);
+
+    return(temp);
+}
+
+
+/*    
+ * cost_result--
+ *    Determines and returns the cost of writing a relation of 'tuples'
+ *    tuples of 'width' bytes out to a result relation.
+ *    
+ * Returns a flonum.
+ *
+ */
+Cost
+cost_result(int tuples, int width)
+{
+    Cost temp =0;
+    temp = temp + page_size(tuples,width);
+    temp = temp + _CPU_PAGE_WEIGHT_ * tuples;
+    Assert(temp >= 0);
+    return(temp);
+}
+
+/*    
+ * cost_nestloop--
+ *    Determines and returns the cost of joining two relations using the 
+ *    nested loop algorithm.
+ *    
+ * 'outercost' is the (disk+cpu) cost of scanning the outer relation
+ * 'innercost' is the (disk+cpu) cost of scanning the inner relation
+ * 'outertuples' is the number of tuples in the outer relation
+ *    
+ * Returns a flonum.
+ *
+ */
+Cost
+cost_nestloop(Cost outercost,
+             Cost innercost,
+             int outertuples,
+             int innertuples,
+             int outerpages,
+             bool is_indexjoin)
+{
+    Cost temp =0;
+
+    if ( !_enable_nestloop_ ) 
+       temp += _disable_cost_;
+    temp += outercost;
+    temp += outertuples * innercost;
+    Assert(temp >= 0);
+
+    return(temp);
+}
+
+/*    
+ * cost_mergesort--
+ *    'outercost' and 'innercost' are the (disk+cpu) costs of scanning the
+ *             outer and inner relations
+ *    'outersortkeys' and 'innersortkeys' are lists of the keys to be used
+ *             to sort the outer and inner relations
+ *    'outertuples' and 'innertuples' are the number of tuples in the outer
+ *             and inner relations
+ *    'outerwidth' and 'innerwidth' are the (typical) widths (in bytes)
+ *             of the tuples of the outer and inner relations
+ *    
+ * Returns a flonum.
+ *    
+ */
+Cost
+cost_mergesort(Cost outercost,
+              Cost innercost,
+              List *outersortkeys,
+              List *innersortkeys,
+              int outersize,
+              int innersize,
+              int outerwidth,
+              int innerwidth)
+{
+    Cost temp = 0;
+
+    if ( !_enable_mergesort_ ) 
+       temp += _disable_cost_;
+       
+    temp += outercost;
+    temp += innercost;
+    temp += cost_sort(outersortkeys,outersize,outerwidth,false);
+    temp += cost_sort(innersortkeys,innersize,innerwidth,false);
+    temp += _CPU_PAGE_WEIGHT_ * (outersize + innersize);
+    Assert(temp >= 0);
+
+    return(temp);
+}
+
+/*    
+ * cost_hashjoin--             XXX HASH
+ *    'outercost' and 'innercost' are the (disk+cpu) costs of scanning the
+ *             outer and inner relations
+ *    'outerkeys' and 'innerkeys' are lists of the keys to be used
+ *             to hash the outer and inner relations
+ *    'outersize' and 'innersize' are the number of tuples in the outer
+ *             and inner relations
+ *    'outerwidth' and 'innerwidth' are the (typical) widths (in bytes)
+ *             of the tuples of the outer and inner relations
+ *    
+ * Returns a flonum.
+ */
+Cost
+cost_hashjoin(Cost outercost,
+             Cost innercost,
+             List *outerkeys,
+             List *innerkeys,
+             int outersize,
+             int innersize,
+             int outerwidth,
+             int innerwidth)
+{
+    Cost temp = 0;
+    int outerpages = page_size (outersize,outerwidth);
+    int innerpages = page_size (innersize,innerwidth);
+    int nrun = ceil((double)outerpages/(double)NBuffers);
+
+    if (outerpages < innerpages)
+       return _disable_cost_;
+    if ( !_enable_hashjoin_ ) 
+       temp += _disable_cost_;
+/*    temp += outercost + (nrun + 1) * innercost; */
+    /* 
+       the innercost shouldn't be used it.  Instead the 
+       cost of hashing the innerpath should be used
+       
+       ASSUME innercost is 1 for now -- a horrible hack 
+                                  - jolly
+    */
+    temp += outercost + (nrun + 1);
+
+    temp += _CPU_PAGE_WEIGHT_ * (outersize + nrun * innersize);
+    Assert(temp >= 0);
+
+    return(temp);
+}
+
+/*    
+ * compute-rel-size--
+ *    Computes the size of each relation in 'rel-list' (after applying 
+ *    restrictions), by multiplying the selectivity of each restriction 
+ *    by the original size of the relation.  
+ *    
+ *    Sets the 'size' field for each relation entry with this computed size.
+ *    
+ * Returns the size.
+ */
+int compute_rel_size(Rel *rel)
+{
+    Cost temp;
+    int temp1;
+
+    temp = rel->tuples * product_selec(rel->clauseinfo); 
+    Assert(temp >= 0);
+    if (temp >= (MAXINT - 1)) {
+       temp1 = MAXINT;
+    } else {
+       temp1 = ceil((double) temp);
+    }
+    Assert(temp1 >= 0);
+    Assert(temp1 <= MAXINT);
+    return(temp1);
+}
+
+/*    
+ * compute-rel-width--
+ *    Computes the width in bytes of a tuple from 'rel'.
+ *    
+ * Returns the width of the tuple as a fixnum.
+ */
+int
+compute_rel_width(Rel *rel)
+{
+    return (compute_targetlist_width(get_actual_tlist(rel->targetlist)));
+}
+
+/*    
+ * compute-targetlist-width--
+ *    Computes the width in bytes of a tuple made from 'targetlist'.
+ *    
+ * Returns the width of the tuple as a fixnum.
+ */
+int
+compute_targetlist_width(List *targetlist)
+{
+    List *temp_tl;
+    int tuple_width = 0;
+
+    foreach (temp_tl, targetlist) {
+       tuple_width = tuple_width + 
+           compute_attribute_width(lfirst(temp_tl));
+    }
+    return(tuple_width);
+}
+
+/*    
+ * compute-attribute-width--
+ *    Given a target list entry, find the size in bytes of the attribute.
+ *    
+ *    If a field is variable-length, it is assumed to be at least the size
+ *    of a TID field.
+ *    
+ * Returns the width of the attribute as a fixnum.
+ */
+static int
+compute_attribute_width(TargetEntry *tlistentry)
+{
+    int width = get_typlen(tlistentry->resdom->restype);
+    if (width < 0) 
+       return(_DEFAULT_ATTRIBUTE_WIDTH_);
+    else 
+       return(width);
+}
+
+/*    
+ * compute-joinrel-size--
+ *    Computes the size of the join relation 'joinrel'.
+ *    
+ * Returns a fixnum.
+ */
+int
+compute_joinrel_size(JoinPath *joinpath)
+{
+    Cost temp = 1.0;
+    int temp1 = 0;
+
+    temp *= ((Path*)joinpath->outerjoinpath)->parent->size;
+    temp *= ((Path*)joinpath->innerjoinpath)->parent->size;
+                     
+    temp = temp * product_selec(joinpath->pathclauseinfo);
+    if (temp >= (MAXINT -1)) {
+       temp1 = MAXINT;
+    } else {
+       /* should be ceil here, we don't want joinrel size's of one, do we? */
+       temp1 = ceil((double)temp);
+    }
+    Assert(temp1 >= 0);
+
+    return(temp1);
+}
+
+/*    
+ * page-size--
+ *    Returns an estimate of the number of pages covered by a given
+ *    number of tuples of a given width (size in bytes).
+ */
+int page_size(int tuples, int width)
+{
+    int temp =0;
+
+    temp = ceil((double)(tuples * (width + sizeof(HeapTupleData))) 
+               / BLCKSZ);
+    Assert(temp >= 0);
+    return(temp);
+}
+
+static double
+base_log(double x, double b)
+{
+    return(log(x)/log(b));
+}
diff --git a/src/backend/optimizer/path/hashutils.c b/src/backend/optimizer/path/hashutils.c
new file mode 100644 (file)
index 0000000..19d70a1
--- /dev/null
@@ -0,0 +1,120 @@
+/*-------------------------------------------------------------------------
+ *
+ * hashutils.c--
+ *    Utilities for finding applicable merge clauses and pathkeys
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "nodes/pg_list.h"
+#include "nodes/relation.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/paths.h"
+#include "optimizer/clauses.h"
+
+
+static HInfo *match_hashop_hashinfo(Oid hashop, List *hashinfo_list);
+
+/*    
+ * group-clauses-by-hashop--
+ *    If a join clause node in 'clauseinfo-list' is hashjoinable, store
+ *    it within a hashinfo node containing other clause nodes with the same
+ *    hash operator.
+ *    
+ * 'clauseinfo-list' is the list of clauseinfo nodes
+ * 'inner-relid' is the relid of the inner join relation
+ *    
+ * Returns the new list of hashinfo nodes.
+ *    
+ */
+List *
+group_clauses_by_hashop(List *clauseinfo_list,
+                       int inner_relid)
+{
+    List *hashinfo_list = NIL;
+    CInfo *clauseinfo = (CInfo*)NULL;
+    List *i = NIL;
+    Oid hashjoinop = 0;
+    
+    foreach (i,clauseinfo_list) {
+       clauseinfo = (CInfo*)lfirst(i);
+       hashjoinop = clauseinfo->hashjoinoperator;
+       
+       /*
+        * Create a new hashinfo node and add it to 'hashinfo-list' if one
+        * does not yet exist for this hash operator.
+        */
+       if (hashjoinop ) {
+           HInfo *xhashinfo = (HInfo*)NULL;
+           Expr *clause = clauseinfo->clause;
+           Var *leftop = get_leftop(clause);
+           Var *rightop = get_rightop(clause);
+           JoinKey *keys = (JoinKey*)NULL;
+           
+           xhashinfo = 
+               match_hashop_hashinfo(hashjoinop,hashinfo_list);
+           
+           if (inner_relid == leftop->varno){
+               keys = makeNode(JoinKey);
+               keys->outer = rightop;
+               keys->inner = leftop;
+           } else {
+               keys = makeNode(JoinKey);
+               keys->outer = leftop;
+               keys->inner = rightop;
+           }
+           
+           if (xhashinfo==NULL) {
+               xhashinfo = makeNode(HInfo);
+               xhashinfo->hashop = hashjoinop;
+
+               xhashinfo->jmethod.jmkeys = NIL;
+               xhashinfo->jmethod.clauses = NIL;
+
+               /* XXX was push  */
+               hashinfo_list = lappend(hashinfo_list,xhashinfo);
+               hashinfo_list = nreverse(hashinfo_list);
+           }
+
+           xhashinfo->jmethod.clauses =
+               lcons(clause, xhashinfo->jmethod.clauses);
+
+           xhashinfo->jmethod.jmkeys =
+               lcons(keys, xhashinfo->jmethod.jmkeys);
+       }
+    }
+    return(hashinfo_list);
+}
+
+
+/*    
+ * match-hashop-hashinfo--
+ *    Searches the list 'hashinfo-list' for a hashinfo node whose hash op
+ *    field equals 'hashop'.
+ *    
+ * Returns the node if it exists.
+ *    
+ */
+static HInfo *
+match_hashop_hashinfo(Oid hashop, List *hashinfo_list)
+{
+    Oid key = 0;
+    HInfo *xhashinfo = (HInfo*)NULL;
+    List *i = NIL;
+    
+    foreach( i, hashinfo_list) {
+       xhashinfo = (HInfo*)lfirst(i);
+       key = xhashinfo->hashop;
+       if (hashop == key) {    /* found */
+           return(xhashinfo);  /* should be a hashinfo node ! */
+       }
+    }
+    return((HInfo*)NIL);
+}
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
new file mode 100644 (file)
index 0000000..19f44cc
--- /dev/null
@@ -0,0 +1,1206 @@
+/*-------------------------------------------------------------------------
+ *
+ * indxpath.c--
+ *    Routines to determine which indices are usable for scanning a
+ *    given relation
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <math.h>
+#include "postgres.h"
+#include "access/attnum.h"
+#include "access/heapam.h"
+#include "access/nbtree.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/relation.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+
+#include "utils/lsyscache.h"
+#include "utils/elog.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/paths.h"
+#include "optimizer/clauses.h"
+#include "optimizer/clauseinfo.h"
+#include "optimizer/plancat.h"
+#include "optimizer/keys.h"
+#include "optimizer/cost.h"
+#include "optimizer/pathnode.h"
+#include "optimizer/xfunc.h"
+#include "optimizer/ordering.h"
+
+
+#include "catalog/catname.h"
+#include "catalog/pg_amop.h"
+#include "catalog/pg_proc.h"
+
+#include "executor/executor.h"
+#include "parser/parsetree.h"          /* for getrelid() */
+
+
+static void match_index_orclauses(Rel *rel, Rel *index, int indexkey,
+    int xclass, List *clauseinfo_list);
+static bool match_index_to_operand(int indexkey, Expr *operand,
+    Rel *rel, Rel *index);
+static List *match_index_orclause(Rel *rel, Rel *index, int indexkey,
+    int xclass, List *or_clauses, List *other_matching_indices);
+static List *group_clauses_by_indexkey(Rel *rel, Rel *index,
+    int *indexkeys, Oid *classes, List *clauseinfo_list,
+    bool join);
+static CInfo *match_clause_to_indexkey(Rel *rel, Rel *index, int indexkey,
+    int xclass, CInfo *clauseInfo, bool join);
+static bool pred_test(List *predicate_list, List *clauseinfo_list,
+    List *joininfo_list);
+static bool one_pred_test(Expr *predicate, List *clauseinfo_list);
+static bool one_pred_clause_expr_test(Expr *predicate, Node *clause);
+static bool one_pred_clause_test(Expr *predicate, Node *clause);
+static bool clause_pred_clause_test(Expr *predicate, Node *clause);
+static List *indexable_joinclauses(Rel *rel, Rel *index, List *joininfo_list);
+static List *index_innerjoin(Query *root, Rel *rel, 
+                            List *clausegroup_list, Rel *index);
+static List *create_index_paths(Query *root, Rel *rel, Rel *index,
+    List *clausegroup_list, bool join);
+static List *add_index_paths(List *indexpaths, List *new_indexpaths);
+static bool function_index_operand(Expr *funcOpnd, Rel *rel, Rel *index);
+static bool SingleAttributeIndex(Rel *index);
+
+/* If Spyros can use a constant PRS2_BOOL_TYPEID, I can use this */
+#define BOOL_TYPEID ((Oid) 16)
+
+/*    
+ * find-index-paths--
+ *    Finds all possible index paths by determining which indices in the
+ *    list 'indices' are usable.
+ *    
+ *    To be usable, an index must match against either a set of 
+ *    restriction clauses or join clauses.
+ *    
+ *    Note that the current implementation requires that there exist
+ *    matching clauses for every key in the index (i.e., no partial
+ *    matches are allowed).
+ *    
+ *    If an index can't be used with restriction clauses, but its keys
+ *    match those of the result sort order (according to information stored
+ *    within 'sortkeys'), then the index is also considered. 
+ *
+ * 'rel' is the relation entry to which these index paths correspond
+ * 'indices' is a list of possible index paths
+ * 'clauseinfo-list' is a list of restriction clauseinfo nodes for 'rel'
+ * 'joininfo-list' is a list of joininfo nodes for 'rel'
+ * 'sortkeys' is a node describing the result sort order (from
+ *     (find_sortkeys))
+ *    
+ * Returns a list of index nodes.
+ *    
+ */
+List *
+find_index_paths (Query *root,
+                 Rel *rel,
+                 List *indices,
+                 List *clauseinfo_list,
+                 List *joininfo_list)
+{
+    List *scanclausegroups = NIL;
+    List *scanpaths = NIL;
+    Rel *index = (Rel *)NULL;
+    List *joinclausegroups = NIL;
+    List *joinpaths = NIL;
+    List *retval = NIL;
+    extern List *add_index_paths();
+    
+    if(indices == NIL)
+       return(NULL);
+       
+    index = (Rel*)lfirst (indices);
+
+    retval = find_index_paths(root,
+                             rel,
+                             lnext (indices),
+                             clauseinfo_list,
+                             joininfo_list);
+
+    /* If this is a partial index, return if it fails the predicate test */
+    if (index->indpred != NIL)
+       if (!pred_test(index->indpred, clauseinfo_list, joininfo_list))
+           return retval;
+
+    /*  1. If this index has only one key, try matching it against 
+     * subclauses of an 'or' clause.  The fields of the clauseinfo
+     * nodes are marked with lists of the matching indices no path
+     * are actually created. 
+     *
+     * XXX NOTE:  Currently btrees dos not support indices with
+     * > 1 key, so the following test will always be true for
+     * now but we have decided not to support index-scans 
+     * on disjunction . -- lp
+     */
+    if (SingleAttributeIndex(index)) 
+       {
+           match_index_orclauses (rel,
+                                  index,
+                                  index->indexkeys[0],
+                                  index->classlist[0],
+                                  clauseinfo_list);
+       }
+
+    /*
+     * 2. If the keys of this index match any of the available
+     * restriction clauses, then create pathnodes corresponding
+     * to each group of usable clauses.
+     */
+    scanclausegroups = group_clauses_by_indexkey(rel,
+                                                index,
+                                                index->indexkeys,
+                                                index->classlist,
+                                                clauseinfo_list,
+                                                false);
+    
+    scanpaths = NIL;
+    if (scanclausegroups != NIL)
+       scanpaths = create_index_paths (root, 
+                                       rel,
+                                       index,
+                                       scanclausegroups,
+                                       false);
+            
+    /*
+     * 3. If this index can be used with any join clause, then 
+     * create pathnodes for each group of usable clauses.  An 
+     * index can be used with a join clause if its ordering is 
+     * useful for a mergejoin, or if the index can possibly be 
+     * used for scanning the inner relation of a nestloop join. 
+     */
+    joinclausegroups = indexable_joinclauses(rel,index,joininfo_list);
+    joinpaths = NIL;
+
+    if (joinclausegroups != NIL)
+       {
+           List *new_join_paths = create_index_paths(root, rel,
+                                                         index,
+                                                         joinclausegroups, 
+                                                         true);
+           List *innerjoin_paths = index_innerjoin(root, rel,joinclausegroups,index);
+
+           rel->innerjoin = nconc (rel->innerjoin, innerjoin_paths);
+           joinpaths = new_join_paths;
+       }
+            
+    /*
+     *  Some sanity checks to make sure that
+     *  the indexpath is valid.
+     */
+    if (joinpaths!=NULL)
+       retval = add_index_paths(joinpaths,retval);
+    if (scanpaths!=NULL)
+       retval = add_index_paths(scanpaths,retval);
+       
+    return retval;
+
+}
+
+
+/****************************************************************************
+ *     ----  ROUTINES TO MATCH 'OR' CLAUSES  ----   
+ ****************************************************************************/
+
+
+/*    
+ * match-index-orclauses--
+ *    Attempt to match an index against subclauses within 'or' clauses.
+ *    If the index does match, then the clause is marked with information
+ *    about the index.
+ *    
+ *    Essentially, this adds 'index' to the list of indices in the
+ *    ClauseInfo field of each of the clauses which it matches.
+ *    
+ * 'rel' is the node of the relation on which the index is defined.
+ * 'index' is the index node.
+ * 'indexkey' is the (single) key of the index
+ * 'class' is the class of the operator corresponding to 'indexkey'.
+ * 'clauseinfo-list' is the list of available restriction clauses.
+ *    
+ * Returns nothing.
+ *    
+ */
+static void
+match_index_orclauses(Rel *rel,
+                     Rel *index,
+                     int indexkey,
+                     int xclass,
+                     List *clauseinfo_list)
+{
+    CInfo *clauseinfo = (CInfo*)NULL;
+    List *i = NIL;
+
+    foreach (i, clauseinfo_list) {
+       clauseinfo = (CInfo*)lfirst(i);
+       if (valid_or_clause(clauseinfo)) {
+           
+           /* Mark the 'or' clause with a list of indices which 
+            * match each of its subclauses.  The list is
+            * generated by adding 'index' to the existing
+            * list where appropriate.
+            */
+           clauseinfo->indexids =
+               match_index_orclause (rel,index,indexkey,
+                                     xclass,
+                                     clauseinfo->clause->args,
+                                     clauseinfo->indexids);
+       }
+    }
+}
+
+/*
+ * match_index_operand--
+ *    Generalize test for a match between an existing index's key
+ *    and the operand on the rhs of a restriction clause.  Now check
+ *    for functional indices as well.
+ */
+static bool
+match_index_to_operand(int indexkey,
+                      Expr *operand,
+                      Rel *rel,
+                      Rel *index)
+{
+    /*
+     * Normal index.
+     */
+    if (index->indproc == InvalidOid)
+       return match_indexkey_operand(indexkey, (Var*)operand, rel);
+
+    /*
+     * functional index check
+     */
+    return (function_index_operand(operand, rel, index));
+}
+
+/*    
+ * match-index-orclause--
+ *    Attempts to match an index against the subclauses of an 'or' clause.
+ *    
+ *    A match means that:
+ *    (1) the operator within the subclause can be used with one
+ *             of the index's operator classes, and
+ *    (2) there is a usable key that matches the variable within a
+ *             sargable clause.
+ *    
+ * 'or-clauses' are the remaining subclauses within the 'or' clause
+ * 'other-matching-indices' is the list of information on other indices
+ *     that have already been matched to subclauses within this
+ *     particular 'or' clause (i.e., a list previously generated by
+ *     this routine)
+ *    
+ * Returns a list of the form ((a b c) (d e f) nil (g h) ...) where
+ * a,b,c are nodes of indices that match the first subclause in
+ * 'or-clauses', d,e,f match the second subclause, no indices
+ * match the third, g,h match the fourth, etc.
+ */
+static List *
+match_index_orclause(Rel *rel,
+                    Rel *index,
+                    int indexkey,
+                    int xclass,
+                    List *or_clauses,
+                    List *other_matching_indices)
+{
+    Node *clause = NULL;
+    List *matched_indices  = other_matching_indices;
+    List *index_list = NIL;
+    List *clist;
+    List *ind;
+
+    if (!matched_indices)
+       matched_indices = lcons(NIL, NIL);
+
+    for (clist = or_clauses, ind = matched_indices; 
+        clist; 
+        clist = lnext(clist), ind = lnext(ind))
+       {
+           clause = lfirst(clist);
+           if (is_opclause (clause) && 
+               op_class(((Oper*)((Expr*)clause)->oper)->opno,
+                        xclass, index->relam) && 
+               match_index_to_operand(indexkey,
+                                      (Expr*)get_leftop((Expr*)clause),
+                                      rel,
+                                      index) &&
+               IsA(get_rightop((Expr*)clause),Const)) {
+         
+               matched_indices =  lcons(index, matched_indices);
+               index_list = lappend(index_list,
+                                     matched_indices);
+           }
+       }
+    return(index_list);
+     
+}
+
+/****************************************************************************
+ *             ----  ROUTINES TO CHECK RESTRICTIONS  ----       
+ ****************************************************************************/
+
+
+/*
+ * DoneMatchingIndexKeys() - MACRO
+ *
+ * Determine whether we should continue matching index keys in a clause.
+ * Depends on if there are more to match or if this is a functional index.
+ * In the latter case we stop after the first match since the there can
+ * be only key (i.e. the function's return value) and the attributes in
+ * keys list represent the arguments to the function.  -mer 3 Oct. 1991
+ */
+#define DoneMatchingIndexKeys(indexkeys, index) \
+       (indexkeys[0] == 0 || \
+        (index->indproc != InvalidOid))
+
+/*    
+ * group-clauses-by-indexkey--
+ *    Determines whether there are clauses which will match each and every
+ *    one of the remaining keys of an index.  
+ *    
+ * 'rel' is the node of the relation corresponding to the index.
+ * 'indexkeys' are the remaining index keys to be matched.
+ * 'classes' are the classes of the index operators on those keys.
+ * 'clauses' is either:
+ *     (1) the list of available restriction clauses on a single
+ *             relation, or
+ *     (2) a list of join clauses between 'rel' and a fixed set of
+ *             relations,
+ *     depending on the value of 'join'.
+ * 'startlist' is a list of those clause nodes that have matched the keys 
+ *     that have already been checked.
+ * 'join' is a flag indicating that the clauses being checked are join
+ *     clauses.
+ *    
+ * Returns all possible groups of clauses that will match (given that
+ * one or more clauses can match any of the remaining keys).
+ * E.g., if you have clauses A, B, and C, ((A B) (A C)) might be 
+ * returned for an index with 2 keys.
+ *    
+ */
+static List *
+group_clauses_by_indexkey(Rel *rel,
+                         Rel *index,
+                         int *indexkeys,
+                         Oid *classes,
+                         List *clauseinfo_list,
+                         bool join)
+{
+    List *curCinfo             = NIL;
+    CInfo *matched_clause      = (CInfo*)NULL;
+    List *clausegroup  = NIL;
+
+
+    if (clauseinfo_list == NIL)
+       return NIL;
+
+    foreach (curCinfo,clauseinfo_list) {
+       CInfo *temp     = (CInfo*)lfirst(curCinfo);
+       int *curIndxKey = indexkeys;
+       Oid *curClass   = classes;
+
+       do {
+           /*
+            * If we can't find any matching clauses for the first of 
+            * the remaining keys, give up.
+            */
+           matched_clause = match_clause_to_indexkey (rel, 
+                                                      index, 
+                                                      curIndxKey[0],
+                                                      curClass[0],
+                                                      temp,
+                                                      join);
+           if (!matched_clause)
+               break;
+
+           clausegroup = lcons(matched_clause, clausegroup);
+           curIndxKey++;
+           curClass++;
+
+       } while ( !DoneMatchingIndexKeys(curIndxKey, index) );
+    }
+
+    if (clausegroup != NIL)
+       return(lcons(clausegroup, NIL));
+    return NIL;
+}
+
+/*
+ * IndexScanableClause ()  MACRO
+ *
+ * Generalize condition on which we match a clause with an index.
+ * Now we can match with functional indices.
+ */
+#define IndexScanableOperand(opnd, indkeys, rel, index) \
+    ((index->indproc == InvalidOid) ? \
+       equal_indexkey_var(indkeys,opnd) : \
+       function_index_operand((Expr*)opnd,rel,index))
+
+/*    
+ * match_clause_to-indexkey--
+ *    Finds the first of a relation's available restriction clauses that
+ *    matches a key of an index.
+ *    
+ *    To match, the clause must:
+ *    (1) be in the form (op var const) if the clause is a single-
+ *             relation clause, and
+ *    (2) contain an operator which is in the same class as the index
+ *             operator for this key.
+ *    
+ *    If the clause being matched is a join clause, then 'join' is t.
+ *    
+ * Returns a single clauseinfo node corresponding to the matching 
+ * clause.
+ *
+ * NOTE:  returns nil if clause is an or_clause.
+ *    
+ */
+static CInfo *
+match_clause_to_indexkey(Rel *rel,
+                        Rel *index,
+                        int indexkey,
+                        int xclass,
+                        CInfo *clauseInfo,
+                        bool join)
+{
+    Expr *clause = clauseInfo->clause;
+    Var *leftop, *rightop;
+    Oid join_op = InvalidOid;
+    bool isIndexable = false;
+
+    if (or_clause((Node*)clause) ||
+       not_clause((Node*)clause) || single_node((Node*)clause))
+       return ((CInfo*)NULL);
+
+    leftop = get_leftop(clause);
+    rightop = get_rightop(clause);
+    /*
+     * If this is not a join clause, check for clauses of the form:
+     * (operator var/func constant) and (operator constant var/func)
+     */
+    if (!join) 
+       {
+           Oid restrict_op = InvalidOid;
+
+           /*
+            * Check for standard s-argable clause
+            */
+           if (IsA(rightop,Const))
+               {
+                   restrict_op = ((Oper*)((Expr*)clause)->oper)->opno;
+                   isIndexable =
+                       ( op_class(restrict_op, xclass, index->relam) &&
+                        IndexScanableOperand(leftop,
+                                             indexkey,
+                                             rel,
+                                             index) );
+               }
+
+           /*
+            * Must try to commute the clause to standard s-arg format.
+            */
+           else if (IsA(leftop,Const))
+               {
+                   restrict_op =
+                       get_commutator(((Oper*)((Expr*)clause)->oper)->opno);
+
+                   if ( (restrict_op != InvalidOid) &&
+                       op_class(restrict_op, xclass, index->relam) &&
+                       IndexScanableOperand(rightop,
+                                            indexkey,rel,index) )
+                       {
+                           isIndexable = true;
+                           /*
+                            * In place list modification.
+                            * (op const var/func) -> (op var/func const)
+                            */
+                           /* BUG!  Old version:
+                              CommuteClause(clause, restrict_op);
+                              */
+                           CommuteClause((Node*)clause);
+                       }
+               }
+       } 
+    /*
+     * Check for an indexable scan on one of the join relations.
+     * clause is of the form (operator var/func var/func)
+     */
+    else
+       {
+           if (match_index_to_operand(indexkey,(Expr*)rightop,rel,index)) {
+                                       
+               join_op = get_commutator(((Oper*)((Expr*)clause)->oper)->opno);
+
+           } else if (match_index_to_operand(indexkey,
+                                             (Expr*)leftop,rel,index)) {
+               join_op = ((Oper*)((Expr*)clause)->oper)->opno;
+           }
+
+           if ( join_op && op_class(join_op,xclass,index->relam) &&
+               join_clause_p((Node*)clause))
+               {
+                   isIndexable = true;
+
+                   /*
+                    * If we're using the operand's commutator we must
+                    * commute the clause.
+                    */
+                   if (join_op != ((Oper*)((Expr*)clause)->oper)->opno)
+                       CommuteClause((Node*)clause);
+               }
+       }
+
+    if (isIndexable)
+       return(clauseInfo);
+
+    return(NULL);
+}
+
+/****************************************************************************
+ *             ----  ROUTINES TO DO PARTIAL INDEX PREDICATE TESTS  ----  
+ ****************************************************************************/
+
+/*    
+ * pred_test--
+ *    Does the "predicate inclusion test" for partial indexes.
+ *
+ *    Recursively checks whether the clauses in clauseinfo_list imply
+ *    that the given predicate is true.
+ *
+ *    This routine (together with the routines it calls) iterates over
+ *    ANDs in the predicate first, then reduces the qualification
+ *    clauses down to their constituent terms, and iterates over ORs
+ *    in the predicate last.  This order is important to make the test
+ *    succeed whenever possible (assuming the predicate has been
+ *    successfully cnfify()-ed). --Nels, Jan '93
+ */
+static bool
+pred_test(List *predicate_list, List *clauseinfo_list, List *joininfo_list)
+{
+    List *pred, *items, *item;
+
+    /*
+     * Note: if Postgres tried to optimize queries by forming equivalence
+     * classes over equi-joined attributes (i.e., if it recognized that a
+     * qualification such as "where a.b=c.d and a.b=5" could make use of
+     * an index on c.d), then we could use that equivalence class info
+     * here with joininfo_list to do more complete tests for the usability
+     * of a partial index.  For now, the test only uses restriction
+     * clauses (those in clauseinfo_list). --Nels, Dec '92
+     */
+
+    if (predicate_list == NULL)
+       return true;    /* no predicate: the index is usable */
+    if (clauseinfo_list == NULL)
+       return false;   /* no restriction clauses: the test must fail */
+
+    foreach (pred, predicate_list) {
+       /* if any clause is not implied, the whole predicate is not implied */
+       if (and_clause(lfirst(pred))) {
+           items = ((Expr*)lfirst(pred))->args;
+           foreach (item, items) {
+               if (!one_pred_test(lfirst(item), clauseinfo_list))
+                   return false;
+           }
+       }
+       else if (!one_pred_test(lfirst(pred), clauseinfo_list))
+           return false;
+    }
+    return true;
+}
+
+
+/*    
+ * one_pred_test--
+ *    Does the "predicate inclusion test" for one conjunct of a predicate
+ *    expression.
+ */
+static bool
+one_pred_test(Expr *predicate, List *clauseinfo_list)
+{
+    CInfo *clauseinfo;
+    List *item;
+
+    Assert(predicate != NULL);
+    foreach (item, clauseinfo_list) {
+       clauseinfo = (CInfo *)lfirst(item);
+       /* if any clause implies the predicate, return true */
+       if (one_pred_clause_expr_test(predicate, (Node*)clauseinfo->clause))
+           return true;
+    }
+    return false;
+}
+
+
+/*    
+ * one_pred_clause_expr_test--
+ *    Does the "predicate inclusion test" for a general restriction-clause
+ *    expression.
+ */
+static bool
+one_pred_clause_expr_test(Expr *predicate, Node *clause)
+{
+    List *items, *item;
+
+    if (is_opclause(clause))
+       return one_pred_clause_test(predicate, clause);
+    else if (or_clause(clause)) {
+       items = ((Expr*)clause)->args;
+       foreach (item, items) {
+           /* if any OR item doesn't imply the predicate, clause doesn't */
+           if (!one_pred_clause_expr_test(predicate, lfirst(item)))
+               return false;
+       }
+       return true;
+    }else if (and_clause(clause)) {
+       items = ((Expr*)clause)->args;
+       foreach (item, items) {
+           /* if any AND item implies the predicate, the whole clause does */
+           if (one_pred_clause_expr_test(predicate, lfirst(item)))
+               return true;
+       }
+       return false;
+    }else {
+       /* unknown clause type never implies the predicate */ 
+       return false;
+    }
+}
+
+
+/*    
+ * one_pred_clause_test--
+ *    Does the "predicate inclusion test" for one conjunct of a predicate
+ *    expression for a simple restriction clause.
+ */
+static bool
+one_pred_clause_test(Expr *predicate, Node *clause)
+{
+    List *items, *item;
+
+    if (is_opclause((Node*)predicate))
+       return clause_pred_clause_test(predicate, clause);
+    else if (or_clause((Node*)predicate)) {
+       items = predicate->args;
+       foreach (item, items) {
+           /* if any item is implied, the whole predicate is implied */
+           if (one_pred_clause_test(lfirst(item), clause))
+               return true;
+       }
+       return false;
+    }else if (and_clause((Node*)predicate)) {
+       items = predicate->args;
+       foreach (item, items) {
+           /*
+            * if any item is not implied, the whole predicate is not
+            * implied
+            */
+           if (!one_pred_clause_test(lfirst(item), clause))
+               return false;
+       }
+       return true;
+    }
+    else {
+       elog(DEBUG, "Unsupported predicate type, index will not be used");
+       return false;
+    }
+}
+
+
+/*
+ * Define an "operator implication table" for btree operators ("strategies").
+ * The "strategy numbers" are:  (1) <   (2) <=   (3) =   (4) >=   (5) >
+ *
+ * The interpretation of:
+ *
+ *     test_op = BT_implic_table[given_op-1][target_op-1]
+ *
+ * where test_op, given_op and target_op are strategy numbers (from 1 to 5)
+ * of btree operators, is as follows:
+ *
+ *   If you know, for some ATTR, that "ATTR given_op CONST1" is true, and you
+ *   want to determine whether "ATTR target_op CONST2" must also be true, then
+ *   you can use "CONST1 test_op CONST2" as a test.  If this test returns true,
+ *   then the target expression must be true; if the test returns false, then
+ *   the target expression may be false.
+ *
+ * An entry where test_op==0 means the implication cannot be determined, i.e.,
+ * this test should always be considered false.
+ */
+
+StrategyNumber BT_implic_table[BTMaxStrategyNumber][BTMaxStrategyNumber] = {
+    {2, 2, 0, 0, 0},
+    {1, 2, 0, 0, 0},
+    {1, 2, 3, 4, 5},
+    {0, 0, 0, 4, 5},
+    {0, 0, 0, 4, 4}
+};
+
+
+/*    
+ * clause_pred_clause_test--
+ *    Use operator class info to check whether clause implies predicate.
+ *    
+ *    Does the "predicate inclusion test" for a "simple clause" predicate
+ *    for a single "simple clause" restriction.  Currently, this only handles
+ *    (binary boolean) operators that are in some btree operator class.
+ *    Eventually, rtree operators could also be handled by defining an
+ *    appropriate "RT_implic_table" array.
+ */
+static bool
+clause_pred_clause_test(Expr *predicate, Node *clause)
+{
+    Var                *pred_var, *clause_var;
+    Const      *pred_const, *clause_const;
+    Oid                pred_op, clause_op, test_op;
+    Oid                opclass_id;
+    StrategyNumber     pred_strategy, clause_strategy, test_strategy;
+    Oper       *test_oper;
+    Expr       *test_expr;
+    bool       test_result, isNull;
+    Relation   relation;
+    HeapScanDesc  scan;
+    HeapTuple  tuple;
+    ScanKeyData        entry[3];
+    Form_pg_amop form;
+
+    pred_var = (Var*)get_leftop(predicate);
+    pred_const = (Const*)get_rightop(predicate);
+    clause_var = (Var*)get_leftop((Expr*)clause);
+    clause_const = (Const*)get_rightop((Expr*)clause);
+
+    /* Check the basic form; for now, only allow the simplest case */
+    if (!is_opclause(clause) ||
+       !IsA(clause_var,Var) ||
+       !IsA(clause_const,Const) ||
+       !IsA(predicate->oper,Oper) ||
+       !IsA(pred_var,Var) ||
+       !IsA(pred_const,Const)) {
+       return false;
+    }
+
+    /*
+     * The implication can't be determined unless the predicate and the clause
+     * refer to the same attribute.
+     */
+    if (clause_var->varattno != pred_var->varattno)
+       return false;
+
+    /* Get the operators for the two clauses we're comparing */
+    pred_op = ((Oper*)((Expr*)predicate)->oper)->opno;
+    clause_op = ((Oper*)((Expr*)clause)->oper)->opno;
+
+
+    /*
+     * 1. Find a "btree" strategy number for the pred_op
+     */
+    /* XXX - hardcoded amopid value 403 to find "btree" operator classes */
+    ScanKeyEntryInitialize(&entry[0], 0,
+                          Anum_pg_amop_amopid,
+                          ObjectIdEqualRegProcedure,
+                          ObjectIdGetDatum(403));
+
+    ScanKeyEntryInitialize(&entry[1], 0,
+                          Anum_pg_amop_amopopr,
+                          ObjectIdEqualRegProcedure,
+                          ObjectIdGetDatum(pred_op));
+
+    relation = heap_openr(AccessMethodOperatorRelationName);
+
+    /*
+     * The following assumes that any given operator will only be in a single
+     * btree operator class.  This is true at least for all the pre-defined
+     * operator classes.  If it isn't true, then whichever operator class
+     * happens to be returned first for the given operator will be used to
+     * find the associated strategy numbers for the test. --Nels, Jan '93
+     */
+    scan = heap_beginscan(relation, false, NowTimeQual, 2, entry);
+    tuple = heap_getnext(scan, false, (Buffer *)NULL);
+    if (! HeapTupleIsValid(tuple)) {
+       elog(DEBUG, "clause_pred_clause_test: unknown pred_op");
+       return false;
+    }
+    form = (Form_pg_amop) GETSTRUCT(tuple);
+
+    /* Get the predicate operator's strategy number (1 to 5) */
+    pred_strategy = (StrategyNumber)form->amopstrategy;
+
+    /* Remember which operator class this strategy number came from */
+    opclass_id = form->amopclaid;
+
+    heap_endscan(scan);
+
+
+    /*
+     * 2. From the same opclass, find a strategy num for the clause_op
+     */
+    ScanKeyEntryInitialize(&entry[1], 0,
+                          Anum_pg_amop_amopclaid,
+                          ObjectIdEqualRegProcedure,
+                          ObjectIdGetDatum(opclass_id));
+
+    ScanKeyEntryInitialize(&entry[2], 0,
+                          Anum_pg_amop_amopopr,
+                          ObjectIdEqualRegProcedure,
+                          ObjectIdGetDatum(clause_op));
+
+    scan = heap_beginscan(relation, false, NowTimeQual, 3, entry);
+    tuple = heap_getnext(scan, false, (Buffer *)NULL);
+    if (! HeapTupleIsValid(tuple)) {
+       elog(DEBUG, "clause_pred_clause_test: unknown clause_op");
+       return false;
+    }
+    form = (Form_pg_amop) GETSTRUCT(tuple);
+
+    /* Get the restriction clause operator's strategy number (1 to 5) */
+    clause_strategy = (StrategyNumber)form->amopstrategy;
+    heap_endscan(scan);
+
+
+    /*
+     * 3. Look up the "test" strategy number in the implication table
+     */
+
+    test_strategy = BT_implic_table[clause_strategy-1][pred_strategy-1];
+    if (test_strategy == 0)
+       return false;           /* the implication cannot be determined */
+
+
+    /*
+     * 4. From the same opclass, find the operator for the test strategy
+     */
+
+    ScanKeyEntryInitialize(&entry[2], 0,
+                          Anum_pg_amop_amopstrategy,
+                          Integer16EqualRegProcedure,
+                          Int16GetDatum(test_strategy));
+
+    scan = heap_beginscan(relation, false, NowTimeQual, 3, entry);
+    tuple = heap_getnext(scan, false, (Buffer *)NULL);
+    if (! HeapTupleIsValid(tuple)) {
+       elog(DEBUG, "clause_pred_clause_test: unknown test_op");
+       return false;
+    }
+    form = (Form_pg_amop) GETSTRUCT(tuple);
+
+    /* Get the test operator */
+    test_op = form->amopopr;
+    heap_endscan(scan);
+
+
+    /*
+     * 5. Evaluate the test
+     */
+    test_oper = makeOper(test_op, /* opno */
+                        InvalidOid, /* opid */
+                        BOOL_TYPEID, /* opresulttype */
+                        0,     /* opsize */
+                        NULL); /* op_fcache */
+    (void) replace_opid(test_oper);
+
+    test_expr = make_opclause(test_oper,
+                             copyObject(clause_const),
+                             copyObject(pred_const));
+
+#ifndef OMIT_PARTIAL_INDEX
+    test_result = ExecEvalExpr((Node*)test_expr, NULL, &isNull, NULL);
+#endif /* OMIT_PARTIAL_INDEX */        
+    if (isNull) {
+       elog(DEBUG, "clause_pred_clause_test: null test result");
+       return false;
+    }
+    return test_result;
+}
+
+
+/****************************************************************************
+ *             ----  ROUTINES TO CHECK JOIN CLAUSES  ----       
+ ****************************************************************************/
+
+/*    
+ * indexable-joinclauses--
+ *    Finds all groups of join clauses from among 'joininfo-list' that can 
+ *    be used in conjunction with 'index'.
+ *    
+ *    The first clause in the group is marked as having the other relation
+ *    in the join clause as its outer join relation.
+ *    
+ * Returns a list of these clause groups.
+ *    
+ */
+static List *
+indexable_joinclauses(Rel *rel, Rel *index, List *joininfo_list)
+{
+    JInfo *joininfo = (JInfo*)NULL;
+    List *cg_list = NIL;
+    List *i = NIL;
+    List *clausegroups = NIL;
+
+    foreach(i,joininfo_list) { 
+       joininfo = (JInfo*)lfirst(i);
+       clausegroups = 
+           group_clauses_by_indexkey (rel,
+                                      index,
+                                      index->indexkeys,
+                                      index->classlist,
+                                      joininfo->jinfoclauseinfo,
+                                      true);
+
+       if (clausegroups != NIL) {
+           List *clauses = lfirst(clausegroups);
+           
+           ((CInfo*)lfirst(clauses))->cinfojoinid =
+               joininfo->otherrels;
+       }
+       cg_list = nconc(cg_list,clausegroups);
+    }
+    return(cg_list);
+}
+
+/****************************************************************************
+ *             ----  PATH CREATION UTILITIES  ----
+ ****************************************************************************/
+
+/*
+ * extract_restrict_clauses -
+ *    the list of clause info contains join clauses and restriction clauses.
+ *    This routine returns the restriction clauses only.
+ */
+static List *
+extract_restrict_clauses(List *clausegroup)
+{
+    List *restrict_cls = NIL;
+    List *l;
+    
+    foreach (l, clausegroup) {
+       CInfo *cinfo = lfirst(l);
+
+       if (!join_clause_p((Node*)cinfo->clause)) {
+           restrict_cls = lappend(restrict_cls, cinfo);
+       }
+    }
+    return restrict_cls;
+}
+
+/*    
+ * index-innerjoin--
+ *    Creates index path nodes corresponding to paths to be used as inner
+ *    relations in nestloop joins.
+ *
+ * 'clausegroup-list' is a list of list of clauseinfo nodes which can use
+ * 'index' on their inner relation.
+ *    
+ * Returns a list of index pathnodes.
+ *    
+ */
+static List *
+index_innerjoin(Query *root, Rel *rel, List *clausegroup_list, Rel *index)
+{
+    List *clausegroup = NIL;
+    List *cg_list = NIL;
+    List *i = NIL;
+    IndexPath *pathnode = (IndexPath*)NULL;
+    Cost       temp_selec;
+    float      temp_pages;
+
+    foreach(i,clausegroup_list) {
+       List *attnos, *values, *flags;
+
+       clausegroup = lfirst(i);
+       pathnode = makeNode(IndexPath);
+
+       get_joinvars(lfirsti(rel->relids),clausegroup,
+                    &attnos, &values, &flags);
+       index_selectivity(lfirsti(index->relids),
+                         index->classlist,
+                         get_opnos(clausegroup),
+                         getrelid((int)lfirst(rel->relids),
+                                  root->rtable),
+                         attnos,
+                         values,
+                         flags,
+                         length(clausegroup),
+                         &temp_pages,
+                         &temp_selec);
+       pathnode->path.pathtype = T_IndexScan;
+       pathnode->path.parent = rel;
+       pathnode->indexid = index->relids;
+       pathnode->indexqual = clausegroup;
+
+       pathnode->path.joinid = ((CInfo*)lfirst(clausegroup))->cinfojoinid;
+
+       pathnode->path.path_cost =
+           cost_index((Oid)lfirst(index->relids),
+                      (int)temp_pages,
+                      temp_selec,
+                      rel->pages,
+                      rel->tuples,
+                      index->pages,
+                      index->tuples,
+                      true);
+
+       /* copy clauseinfo list into path for expensive function processing 
+          -- JMH, 7/7/92 */
+       pathnode->path.locclauseinfo =
+           set_difference(copyObject((Node*)rel->clauseinfo),
+                          clausegroup);
+
+#if 0 /* fix xfunc */
+       /* add in cost for expensive functions!  -- JMH, 7/7/92 */
+       if (XfuncMode != XFUNC_OFF) {
+           ((Path*)pathnode)->path_cost +=
+               xfunc_get_path_cost((Path*)pathnode);
+       }
+#endif
+       cg_list = lappend(cg_list,pathnode);
+    }
+    return(cg_list);
+}
+
+/*    
+ * create-index-paths--
+ *    Creates a list of index path nodes for each group of clauses
+ *    (restriction or join) that can be used in conjunction with an index.
+ *    
+ * 'rel' is the relation for which 'index' is defined
+ * 'clausegroup-list' is the list of clause groups (lists of clauseinfo 
+ *             nodes) grouped by mergesortorder
+ * 'join' is a flag indicating whether or not the clauses are join
+ *             clauses
+ *    
+ * Returns a list of new index path nodes.
+ *    
+ */
+static List *
+create_index_paths(Query *root, 
+                  Rel *rel,
+                  Rel *index,
+                  List *clausegroup_list,
+                  bool join)
+{
+    List *clausegroup = NIL;
+    List *ip_list = NIL;
+    List *i = NIL;
+    List *j = NIL;
+    IndexPath  *temp_path;
+
+    foreach(i, clausegroup_list) {
+       CInfo *clauseinfo;
+       List *temp_node = NIL;
+       bool temp = true;
+
+       clausegroup = lfirst(i);
+
+       foreach (j,clausegroup) {
+           clauseinfo = (CInfo*)lfirst(j); 
+           if (!(join_clause_p((Node*)clauseinfo->clause) &&
+                 equal_path_merge_ordering(index->ordering,
+                                           clauseinfo->mergesortorder))) {
+               temp = false;
+           }
+       }
+         
+       if (!join || temp) {    /* restriction, ordering scan */
+           temp_path = create_index_path (root, rel,index,clausegroup,join);
+           temp_node = 
+               lcons(temp_path, NIL);
+           ip_list = nconc(ip_list,temp_node);
+       } 
+    }
+    return(ip_list);
+}
+
+static List *
+add_index_paths(List *indexpaths, List *new_indexpaths)
+{
+    return append(indexpaths, new_indexpaths); 
+}
+
+static bool
+function_index_operand(Expr *funcOpnd, Rel *rel, Rel *index)
+{
+    Oid heapRelid      = (Oid)lfirst(rel->relids);
+    Func *function;
+    List *funcargs;
+    int *indexKeys     = index->indexkeys;
+    List *arg;
+    int i;
+
+    /*
+     * sanity check, make sure we know what we're dealing with here.
+     */
+    if (funcOpnd==NULL ||
+       nodeTag(funcOpnd)!=T_Expr || funcOpnd->opType!=FUNC_EXPR ||
+       funcOpnd->oper==NULL || indexKeys==NULL)
+       return false;
+
+    function = (Func*)funcOpnd->oper;
+    funcargs = funcOpnd->args;
+
+    if (function->funcid != index->indproc)
+       return false;
+
+    /*
+     * Check that the arguments correspond to the same arguments used
+     * to create the functional index.  To do this we must check that
+     * 1. refer to the right relatiion. 
+     * 2. the args have the right attr. numbers in the right order.
+     *
+     *
+     * Check all args refer to the correct relation (i.e. the one with
+     * the functional index defined on it (rel).  To do this we can
+     * simply compare range table entry numbers, they must be the same.
+     */
+    foreach (arg, funcargs) {
+       if (heapRelid != ((Var*)lfirst(arg))->varno)
+           return false;
+    }
+
+    /*
+     * check attr numbers and order.
+     */
+    i = 0;
+    foreach (arg, funcargs) {
+
+       if (indexKeys[i]==0)
+           return (false);
+
+       if (((Var*)lfirst(arg))->varattno != indexKeys[i])
+           return (false);
+
+       i++;
+    }
+
+    return true;
+}
+
+static bool
+SingleAttributeIndex(Rel *index)
+{
+    /*
+     * return false for now as I don't know if we support index scans
+     * on disjunction and the code doesn't work
+     */
+    return (false);
+
+#if 0
+    /*
+     * Non-functional indices.
+     */
+    if (index->indproc == InvalidOid)
+       return (index->indexkeys[0] != 0 &&
+               index->indexkeys[1] == 0);
+       
+    /*
+     * We have a functional index which is a single attr index
+     */
+    return true;
+#endif
+}
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
new file mode 100644 (file)
index 0000000..5116e24
--- /dev/null
@@ -0,0 +1,623 @@
+/*-------------------------------------------------------------------------
+ *
+ * joinpath.c--
+ *    Routines to find all possible paths for processing a set of joins
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <math.h>
+
+#include "storage/buf_internals.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/relation.h"
+#include "nodes/plannodes.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/paths.h"
+#include "optimizer/pathnode.h"
+#include "optimizer/keys.h"
+#include "optimizer/cost.h"    /* for _enable_{hashjoin, _enable_mergesort} */
+
+static Path *best_innerjoin(List *join_paths, List *outer_relid);
+static List *sort_inner_and_outer(Rel *joinrel, Rel *outerrel, Rel *innerrel,
+                                     List *mergeinfo_list);
+static List *match_unsorted_outer(Rel *joinrel, Rel *outerrel, Rel *innerrel,
+        List *outerpath_list, Path *cheapest_inner, Path *best_innerjoin,
+        List *mergeinfo_list);
+static List *match_unsorted_inner(Rel *joinrel, Rel *outerrel, Rel *innerrel,
+                List *innerpath_list, List *mergeinfo_list);
+static bool EnoughMemoryForHashjoin(Rel *hashrel);
+static List *hash_inner_and_outer(Rel *joinrel, Rel *outerrel, Rel *innerrel,
+                List *hashinfo_list);
+
+/*    
+ * find-all-join-paths--
+ *    Creates all possible ways to process joins for each of the join
+ *    relations in the list 'joinrels.'  Each unique path will be included
+ *    in the join relation's 'pathlist' field.
+ *    
+ *    In postgres, n-way joins are handled left-only(permuting clauseless
+ *    joins doesn't usually win much).
+ *    
+ *    if BushyPlanFlag is true, bushy tree plans will be generated
+ *
+ * 'joinrels' is the list of relation entries to be joined
+ *   
+ * Modifies the pathlist field of the appropriate rel node to contain
+ * the unique join paths.
+ * If bushy trees are considered, may modify the relid field of the
+ * join rel nodes to flatten the lists.
+ *   
+ * Returns nothing of interest. (?) 
+ * It does a destructive modification.
+ */
+void
+find_all_join_paths(Query *root, List *joinrels)
+{
+    List *mergeinfo_list = NIL;
+    List *hashinfo_list = NIL;
+    List *temp_list = NIL;
+    List *path = NIL;
+
+    while (joinrels != NIL) {
+       Rel *joinrel = (Rel *)lfirst(joinrels);
+       List *innerrelids;
+       List *outerrelids;
+       Rel *innerrel;
+       Rel *outerrel;
+       Path *bestinnerjoin;
+       List *pathlist = NIL;
+
+       innerrelids = lsecond(joinrel->relids);
+       outerrelids = lfirst(joinrel->relids);
+
+       /*
+        * base relation id is an integer and join relation relid is a
+        * list of integers.
+        */
+       innerrel = (length(innerrelids)==1)?
+           get_base_rel(root, lfirsti(innerrelids)) : get_join_rel(root,innerrelids);
+       outerrel = (length(outerrelids)==1)?
+           get_base_rel(root, lfirsti(outerrelids)) : get_join_rel(root, outerrelids);
+
+       bestinnerjoin = best_innerjoin(innerrel->innerjoin,
+                                      outerrel->relids);
+       if( _enable_mergesort_ ) {
+           mergeinfo_list = 
+               group_clauses_by_order(joinrel->clauseinfo,
+                                      lfirsti(innerrel->relids));
+       } 
+         
+       if( _enable_hashjoin_ ) {
+           hashinfo_list = 
+               group_clauses_by_hashop(joinrel->clauseinfo,
+                                       lfirsti(innerrel->relids));
+       } 
+         
+       /* need to flatten the relids list */
+       joinrel->relids = intAppend(outerrelids, innerrelids); 
+
+       /*
+        * 1. Consider mergesort paths where both relations must be 
+        *    explicitly sorted. 
+        */
+       pathlist = sort_inner_and_outer(joinrel,outerrel,
+                                       innerrel,mergeinfo_list);
+         
+       /*
+        * 2. Consider paths where the outer relation need not be explicitly  
+        *    sorted. This may include either nestloops and mergesorts where 
+        *    the outer path is already ordered. 
+        */
+       pathlist =
+           add_pathlist(joinrel, pathlist,
+                        match_unsorted_outer(joinrel,
+                                             outerrel,
+                                             innerrel,
+                                             outerrel->pathlist,
+                                             (Path*)innerrel->cheapestpath,
+                                             bestinnerjoin,
+                                             mergeinfo_list));
+
+       /*
+        * 3. Consider paths where the inner relation need not be explicitly  
+        *    sorted.  This may include nestloops and mergesorts  the actual
+        *    nestloop nodes were constructed in (match-unsorted-outer). 
+        */
+       pathlist = 
+           add_pathlist(joinrel,pathlist,
+                        match_unsorted_inner(joinrel,outerrel,
+                                             innerrel,
+                                             innerrel->pathlist,
+                                             mergeinfo_list));
+
+       /*
+        * 4. Consider paths where both outer and inner relations must be 
+        *    hashed before being joined.
+        */
+
+       pathlist = 
+           add_pathlist(joinrel, pathlist,
+                        hash_inner_and_outer(joinrel,outerrel,
+                                             innerrel,hashinfo_list));
+
+       joinrel->pathlist = pathlist;
+
+       /*
+        * 'OuterJoinCost is only valid when calling (match-unsorted-inner) 
+        * with the same arguments as the previous invokation of 
+        * (match-unsorted-outer), so clear the field before going on. 
+        */
+       temp_list = innerrel->pathlist;
+       foreach(path, temp_list) {
+
+           /*
+            * XXX
+            * 
+            * This gross hack is to get around an apparent optimizer bug on
+            * Sparc (or maybe it is a bug of ours?) that causes really wierd
+            * behavior.
+            */
+           if (IsA_JoinPath(path)) {
+               ((Path*)lfirst(path))->outerjoincost = (Cost) 0;
+           }
+
+           /* do it iff it is a join path, which is not always
+              true, esp since the base level */
+       }
+
+       joinrels = lnext(joinrels);
+    }
+}
+
+/*    
+ * best-innerjoin--
+ *    Find the cheapest index path that has already been identified by
+ *    (indexable_joinclauses) as being a possible inner path for the given
+ *    outer relation in a nestloop join.
+ *    
+ * 'join-paths' is a list of join nodes
+ * 'outer-relid' is the relid of the outer join relation
+ *    
+ * Returns the pathnode of the selected path.
+ */
+static Path *
+best_innerjoin(List *join_paths, List *outer_relids)
+{
+    Path *cheapest = (Path*)NULL;
+    List *join_path;
+    
+    foreach(join_path, join_paths) {
+       Path *path = (Path *)lfirst(join_path);
+
+       if (intMember(lfirsti(path->joinid), outer_relids)
+           && ((cheapest==NULL ||
+                path_is_cheaper((Path*)lfirst(join_path),cheapest)))) {
+
+           cheapest = (Path*)lfirst(join_path);
+       }
+    }
+    return(cheapest);
+}
+
+/*    
+ * sort-inner-and-outer--
+ *    Create mergesort join paths by explicitly sorting both the outer and
+ *    inner join relations on each available merge ordering.
+ *    
+ * 'joinrel' is the join relation
+ * 'outerrel' is the outer join relation
+ * 'innerrel' is the inner join relation
+ * 'mergeinfo-list' is a list of nodes containing info on(mergesortable)
+ *             clauses for joining the relations
+ *     
+ * Returns a list of mergesort paths.
+ */
+static List *
+sort_inner_and_outer(Rel *joinrel,
+                    Rel *outerrel,
+                    Rel *innerrel,
+                    List *mergeinfo_list)
+{
+    List *ms_list = NIL;
+    MInfo *xmergeinfo = (MInfo*)NULL;
+    MergePath *temp_node = (MergePath*)NULL;
+    List *i;
+    List *outerkeys = NIL;
+    List *innerkeys = NIL;
+    List *merge_pathkeys = NIL;
+     
+    foreach(i, mergeinfo_list) {
+       xmergeinfo = (MInfo *)lfirst(i);
+
+       outerkeys = 
+           extract_path_keys(xmergeinfo->jmethod.jmkeys,
+                             outerrel->targetlist,
+                             OUTER);
+
+       innerkeys = 
+           extract_path_keys(xmergeinfo->jmethod.jmkeys,
+                             innerrel->targetlist,
+                             INNER);
+
+       merge_pathkeys = 
+           new_join_pathkeys(outerkeys, joinrel->targetlist,
+                             xmergeinfo->jmethod.clauses);
+        
+       temp_node =
+           create_mergesort_path(joinrel,
+                                 outerrel->size,
+                                 innerrel->size,
+                                 outerrel->width,
+                                 innerrel->width,
+                                 (Path*)outerrel->cheapestpath,
+                                 (Path*)innerrel->cheapestpath,
+                                 merge_pathkeys,
+                                 xmergeinfo->m_ordering,
+                                 xmergeinfo->jmethod.clauses,
+                                 outerkeys,
+                                 innerkeys);
+
+       ms_list = lappend(ms_list, temp_node);
+    }
+    return(ms_list);
+}
+
+/*    
+ * match-unsorted-outer--
+ *    Creates possible join paths for processing a single join relation
+ *    'joinrel' by employing either iterative substitution or
+ *    mergesorting on each of its possible outer paths(assuming that the
+ *    outer relation need not be explicitly sorted).
+ *    
+ *    1. The inner path is the cheapest available inner path.
+ *    2. Mergesort wherever possible.  Mergesorts are considered if there
+ *       are mergesortable join clauses between the outer and inner join
+ *       relations such that the outer path is keyed on the variables
+ *       appearing in the clauses.  The corresponding inner merge path is
+ *       either a path whose keys match those of the outer path(if such a
+ *       path is available) or an explicit sort on the appropriate inner
+ *       join keys, whichever is cheaper.
+ *    
+ * 'joinrel' is the join relation
+ * 'outerrel' is the outer join relation
+ * 'innerrel' is the inner join relation
+ * 'outerpath-list' is the list of possible outer paths
+ * 'cheapest-inner' is the cheapest inner path
+ * 'best-innerjoin' is the best inner index path(if any)
+ * 'mergeinfo-list' is a list of nodes containing info on mergesortable
+ *     clauses
+ *    
+ * Returns a list of possible join path nodes.
+ */
+static List *
+match_unsorted_outer(Rel *joinrel,
+                    Rel *outerrel,
+                    Rel *innerrel,
+                    List *outerpath_list,
+                    Path *cheapest_inner,
+                    Path *best_innerjoin,
+                    List *mergeinfo_list)
+{
+    Path *outerpath = (Path*)NULL;
+    List *jp_list = NIL;
+    List *temp_node = NIL;
+    List *merge_pathkeys = NIL;
+    Path *nestinnerpath =(Path*)NULL;
+    List *paths = NIL;
+    List *i = NIL;
+    PathOrder *outerpath_ordering = NULL;
+
+    foreach(i,outerpath_list) {
+       List *clauses = NIL;
+       List *matchedJoinKeys = NIL;
+       List *matchedJoinClauses = NIL;
+        MInfo *xmergeinfo = (MInfo*)NULL;
+
+       outerpath = (Path*)lfirst(i);
+
+       outerpath_ordering = &outerpath->p_ordering;
+       
+       if (outerpath_ordering) {
+           xmergeinfo = 
+               match_order_mergeinfo(outerpath_ordering,
+                                     mergeinfo_list);
+       } 
+       
+       if (xmergeinfo) {
+           clauses = xmergeinfo->jmethod.clauses;
+       } 
+
+       if (clauses) {
+           List *keys = xmergeinfo->jmethod.jmkeys;
+           List *clauses = xmergeinfo->jmethod.clauses;
+
+           matchedJoinKeys = 
+               match_pathkeys_joinkeys(outerpath->keys,
+                                       keys,
+                                       clauses,
+                                       OUTER,
+                                       &matchedJoinClauses);
+           merge_pathkeys = 
+               new_join_pathkeys(outerpath->keys,
+                                 joinrel->targetlist, clauses);
+       } else {
+           merge_pathkeys = outerpath->keys;
+       } 
+       
+       if(best_innerjoin &&
+          path_is_cheaper(best_innerjoin, cheapest_inner)) {
+           nestinnerpath = best_innerjoin;
+       } else {
+           nestinnerpath = cheapest_inner;
+       } 
+       
+       paths = lcons(create_nestloop_path(joinrel,
+                                         outerrel,
+                                         outerpath,
+                                         nestinnerpath,
+                                         merge_pathkeys),
+                    NIL);
+       
+       if (clauses && matchedJoinKeys) {
+           bool path_is_cheaper_than_sort;
+           List *varkeys = NIL;
+           Path *mergeinnerpath = 
+               match_paths_joinkeys(matchedJoinKeys,
+                                    outerpath_ordering,
+                                    innerrel->pathlist,
+                                    INNER);
+
+           path_is_cheaper_than_sort = 
+               (bool) (mergeinnerpath && 
+                       (mergeinnerpath->path_cost < 
+                        (cheapest_inner->path_cost +
+                         cost_sort(matchedJoinKeys,
+                                   innerrel->size,
+                                   innerrel->width,
+                                   false))));
+           if(!path_is_cheaper_than_sort) {
+               varkeys = 
+                   extract_path_keys(matchedJoinKeys,
+                                     innerrel->targetlist,
+                                     INNER);
+           } 
+               
+           
+           /*
+            * Keep track of the cost of the outer path used with 
+            * this ordered inner path for later processing in 
+            * (match-unsorted-inner), since it isn't a sort and 
+            * thus wouldn't otherwise be considered. 
+            */
+           if (path_is_cheaper_than_sort) {
+               mergeinnerpath->outerjoincost = outerpath->path_cost;
+           } else {
+               mergeinnerpath = cheapest_inner;
+           } 
+           
+           temp_node =
+               lcons(create_mergesort_path(joinrel,
+                                          outerrel->size,
+                                          innerrel->size,
+                                          outerrel->width,
+                                          innerrel->width,
+                                          outerpath,
+                                          mergeinnerpath,
+                                          merge_pathkeys,
+                                          xmergeinfo->m_ordering,
+                                          matchedJoinClauses,
+                                          NIL,
+                                          varkeys),
+                    paths);
+       } else {
+           temp_node = paths;
+       } 
+       jp_list = nconc(jp_list, temp_node);
+    }
+    return(jp_list);
+}
+
+/*    
+ * match-unsorted-inner --
+ *    Find the cheapest ordered join path for a given(ordered, unsorted)
+ *    inner join path.
+ *    
+ *    Scans through each path available on an inner join relation and tries
+ *    matching its ordering keys against those of mergejoin clauses.
+ *    If 1. an appropriately-ordered inner path and matching mergeclause are
+ *          found, and
+ *       2. sorting the cheapest outer path is cheaper than using an ordered
+ *           but unsorted outer path(as was considered in
+ *           (match-unsorted-outer)),
+ *    then this merge path is considered.
+ *    
+ * 'joinrel' is the join result relation
+ * 'outerrel' is the outer join relation
+ * 'innerrel' is the inner join relation
+ * 'innerpath-list' is the list of possible inner join paths
+ * 'mergeinfo-list' is a list of nodes containing info on mergesortable
+ *             clauses
+ *    
+ * Returns a list of possible merge paths.
+ */
+static List *
+match_unsorted_inner(Rel *joinrel,
+                    Rel *outerrel,
+                    Rel *innerrel,
+                    List *innerpath_list,
+                    List *mergeinfo_list)
+{
+    Path *innerpath = (Path*)NULL;
+    List *mp_list = NIL;
+    List *temp_node = NIL;
+    PathOrder *innerpath_ordering = NULL;
+    Cost temp1  = 0.0;
+    bool temp2 = false;
+    List *i = NIL;
+    
+    foreach (i, innerpath_list) {
+        MInfo *xmergeinfo = (MInfo*)NULL;
+        List *clauses = NIL;
+       List *matchedJoinKeys = NIL;
+       List *matchedJoinClauses = NIL;
+
+       innerpath = (Path*)lfirst(i);
+
+       innerpath_ordering = &innerpath->p_ordering;
+
+       if (innerpath_ordering) {
+           xmergeinfo = 
+               match_order_mergeinfo(innerpath_ordering,
+                                     mergeinfo_list);
+       } 
+       
+       if (xmergeinfo) {
+           clauses = ((JoinMethod*)xmergeinfo)->clauses;
+       } 
+       
+       if (clauses) {
+           List *keys = xmergeinfo->jmethod.jmkeys;
+           List *cls = xmergeinfo->jmethod.clauses;
+
+           matchedJoinKeys = 
+               match_pathkeys_joinkeys(innerpath->keys,
+                                       keys,
+                                       cls,
+                                       INNER,
+                                       &matchedJoinClauses);
+       } 
+       
+       /*
+        * (match-unsorted-outer) if it is applicable.
+        * 'OuterJoinCost was set above in
+        */
+       if (clauses && matchedJoinKeys) {
+           temp1 = outerrel->cheapestpath->path_cost +
+               cost_sort(matchedJoinKeys, outerrel->size, outerrel->width,
+                         false);
+           
+           temp2 = (bool) (FLOAT_IS_ZERO(innerpath->outerjoincost)
+                           || (innerpath->outerjoincost > temp1));
+       
+           if(temp2) {
+               List *outerkeys = 
+                   extract_path_keys(matchedJoinKeys,
+                                     outerrel->targetlist,
+                                     OUTER);
+               List *merge_pathkeys = 
+                   new_join_pathkeys(outerkeys,
+                                     joinrel->targetlist,
+                                     clauses);
+               
+               temp_node =
+                   lcons(create_mergesort_path(joinrel,
+                                              outerrel->size,
+                                              innerrel->size,
+                                              outerrel->width,
+                                              innerrel->width,
+                                              (Path*)outerrel->cheapestpath,
+                                              innerpath,
+                                              merge_pathkeys,
+                                              xmergeinfo->m_ordering,
+                                              matchedJoinClauses,
+                                              outerkeys,
+                                              NIL),
+                                        NIL);
+               
+               mp_list = nconc(mp_list,temp_node);
+           }
+       }
+    }
+    return(mp_list);
+       
+}
+
+static bool
+EnoughMemoryForHashjoin(Rel *hashrel)
+{
+    int ntuples;
+    int tupsize;
+    int pages;
+
+    ntuples = hashrel->size;
+    if (ntuples == 0) ntuples = 1000;
+    tupsize = hashrel->width + sizeof(HeapTupleData);
+    pages = page_size(ntuples, tupsize);
+    /*
+     * if amount of buffer space below hashjoin threshold,
+     * return false
+     */
+    if (ceil(sqrt((double)pages)) > NBuffers)
+       return false;
+    return true;
+}
+
+/*    
+ * hash-inner-and-outer--              XXX HASH
+ *    Create hashjoin join paths by explicitly hashing both the outer and
+ *    inner join relations on each available hash op.
+ *    
+ * 'joinrel' is the join relation
+ * 'outerrel' is the outer join relation
+ * 'innerrel' is the inner join relation
+ * 'hashinfo-list' is a list of nodes containing info on(hashjoinable)
+ *     clauses for joining the relations
+ *     
+ * Returns a list of hashjoin paths.
+ */
+static List *
+hash_inner_and_outer(Rel *joinrel,
+                    Rel *outerrel,
+                    Rel *innerrel,
+                    List *hashinfo_list)
+{
+    HInfo *xhashinfo = (HInfo*)NULL;
+    List *hjoin_list = NIL;
+    HashPath *temp_node = (HashPath*)NULL;
+    List *i = NIL;
+    List *outerkeys = NIL;
+    List *innerkeys = NIL;
+    List *hash_pathkeys = NIL;
+    
+    foreach (i, hashinfo_list) {
+       xhashinfo = (HInfo*)lfirst(i);
+       outerkeys = 
+           extract_path_keys(((JoinMethod*)xhashinfo)->jmkeys,
+                             outerrel->targetlist,
+                             OUTER);
+       innerkeys = 
+           extract_path_keys(((JoinMethod*)xhashinfo)->jmkeys,
+                             innerrel->targetlist,
+                             INNER);
+       hash_pathkeys = 
+           new_join_pathkeys(outerkeys,
+                             joinrel->targetlist,
+                             ((JoinMethod*)xhashinfo)->clauses);
+       
+       if (EnoughMemoryForHashjoin(innerrel)) {
+           temp_node = create_hashjoin_path(joinrel,
+                                            outerrel->size,
+                                            innerrel->size,
+                                            outerrel->width,
+                                            innerrel->width,
+                                            (Path*)outerrel->cheapestpath,
+                                            (Path*)innerrel->cheapestpath,
+                                            hash_pathkeys,
+                                            xhashinfo->hashop,
+                                            ((JoinMethod*)xhashinfo)->clauses,
+                                            outerkeys,
+                                            innerkeys);
+           hjoin_list = lappend(hjoin_list, temp_node);
+       }
+    }
+    return(hjoin_list);
+}
+
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
new file mode 100644 (file)
index 0000000..ac43b4b
--- /dev/null
@@ -0,0 +1,528 @@
+/*-------------------------------------------------------------------------
+ *
+ * joinrels.c--
+ *    Routines to determine which relations should be joined
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/relation.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/cost.h"
+#include "optimizer/paths.h"
+#include "optimizer/tlist.h"
+#include "optimizer/joininfo.h"
+#include "optimizer/pathnode.h"
+
+
+static List *find_clause_joins(Query *root, Rel *outer_rel, List *joininfo_list);
+static List *find_clauseless_joins(Rel *outer_rel, List *inner_rels);
+static Rel *init_join_rel(Rel *outer_rel, Rel *inner_rel, JInfo *joininfo);
+static List *new_join_tlist(List *tlist, List *other_relids,
+                               int first_resdomno);
+static List *new_joininfo_list(List *joininfo_list, List *join_relids);
+static void add_superrels(Rel *rel, Rel *super_rel);
+static bool nonoverlap_rels(Rel *rel1, Rel *rel2);
+static bool nonoverlap_sets(List *s1, List *s2);
+static void set_joinrel_size(Rel *joinrel, Rel *outer_rel, Rel *inner_rel,
+                            JInfo *jinfo);
+
+/*    
+ * find-join-rels--
+ *    Find all possible joins for each of the outer join relations in
+ *    'outer-rels'.  A rel node is created for each possible join relation,
+ *    and the resulting list of nodes is returned.  If at all possible, only
+ *    those relations for which join clauses exist are considered.  If none
+ *    of these exist for a given relation, all remaining possibilities are
+ *    considered.
+ *    
+ * 'outer-rels' is the list of rel nodes
+ *    
+ * Returns a list of rel nodes corresponding to the new join relations.
+ */
+List *
+find_join_rels(Query *root, List *outer_rels)
+{
+    List *joins = NIL;
+    List *join_list = NIL;
+    List *r = NIL;
+    
+    foreach(r, outer_rels) {
+       Rel *outer_rel = (Rel *)lfirst(r);
+
+       if(!(joins = find_clause_joins(root, outer_rel,outer_rel->joininfo)))
+           if (BushyPlanFlag)
+               joins = find_clauseless_joins(outer_rel,outer_rels);
+           else
+               joins = find_clauseless_joins(outer_rel,root->base_relation_list_);
+
+       join_list = nconc(join_list, joins);
+    }
+
+    return(join_list);
+}
+
+/*    
+ * find-clause-joins--
+ *    Determines whether joins can be performed between an outer relation
+ *    'outer-rel' and those relations within 'outer-rel's joininfo nodes
+ *    (i.e., relations that participate in join clauses that 'outer-rel'
+ *    participates in).  This is possible if all but one of the relations
+ *    contained within the join clauses of the joininfo node are already
+ *    contained within 'outer-rel'.
+ *
+ * 'outer-rel' is the relation entry for the outer relation
+ * 'joininfo-list' is a list of join clauses which 'outer-rel' 
+ *     participates in
+ *    
+ * Returns a list of new join relations.
+ */
+static List *
+find_clause_joins(Query *root, Rel *outer_rel, List *joininfo_list)
+{
+    List *join_list = NIL;
+    List *i = NIL;
+    
+    foreach (i, joininfo_list) {
+       JInfo *joininfo = (JInfo*)lfirst(i);
+       Rel *rel;
+
+       if(!joininfo->inactive) {
+           List *other_rels = joininfo->otherrels;
+
+           if(other_rels != NIL) {
+               if(length(other_rels) == 1) {
+                   rel = init_join_rel(outer_rel,
+                                       get_base_rel(root, lfirsti(other_rels)),
+                                       joininfo);
+               } else if (BushyPlanFlag) {
+                   rel = init_join_rel(outer_rel,
+                                       get_join_rel(root, other_rels),
+                                       joininfo);
+               } else {
+                   rel = NULL;
+               }
+
+               if (rel != NULL)
+                   join_list = lappend(join_list, rel);
+           }
+       }
+    }
+
+    return(join_list);
+}
+
+/*    
+ * find-clauseless-joins--
+ *    Given an outer relation 'outer-rel' and a list of inner relations
+ *    'inner-rels', create a join relation between 'outer-rel' and each
+ *    member of 'inner-rels' that isn't already included in 'outer-rel'.
+ *    
+ * Returns a list of new join relations.
+ */
+static List *
+find_clauseless_joins(Rel *outer_rel, List *inner_rels)
+{
+    Rel *inner_rel;
+    List *t_list = NIL;
+    List *temp_node = NIL;
+    List *i = NIL;
+    
+    foreach (i, inner_rels) {
+       inner_rel = (Rel *)lfirst(i);
+       if(nonoverlap_rels(inner_rel, outer_rel)) {
+           temp_node = lcons(init_join_rel(outer_rel, 
+                                          inner_rel,
+                                          (JInfo*)NULL),
+                            NIL);
+           t_list = nconc(t_list,temp_node);
+       } 
+    }
+
+    return(t_list);
+}
+
+/*    
+ * init-join-rel--
+ *    Creates and initializes a new join relation.
+ *    
+ * 'outer-rel' and 'inner-rel' are relation nodes for the relations to be
+ *     joined
+ * 'joininfo' is the joininfo node(join clause) containing both
+ *     'outer-rel' and 'inner-rel', if any exists
+ *    
+ * Returns the new join relation node.
+ */
+static Rel *
+init_join_rel(Rel *outer_rel, Rel *inner_rel, JInfo *joininfo)
+{
+    Rel *joinrel = makeNode(Rel);
+    List *joinrel_joininfo_list = NIL;
+    List *new_outer_tlist;
+    List *new_inner_tlist;
+    
+    /*
+     * Create a new tlist by removing irrelevant elements from both
+     * tlists of the outer and inner join relations and then merging
+     * the results together.
+     */
+    new_outer_tlist = 
+       new_join_tlist(outer_rel->targetlist,   /*   XXX 1-based attnos */
+                      inner_rel->relids, 1);
+    new_inner_tlist = 
+       new_join_tlist(inner_rel->targetlist,   /*   XXX 1-based attnos */
+                      outer_rel->relids,
+                      length(new_outer_tlist) + 1);
+    
+    joinrel->relids = NIL;
+    joinrel->indexed = false;
+    joinrel->pages = 0;
+    joinrel->tuples = 0;
+    joinrel->width = 0;
+/*    joinrel->targetlist = NIL;*/
+    joinrel->pathlist = NIL;
+    joinrel->unorderedpath = (Path *)NULL;
+    joinrel->cheapestpath = (Path *)NULL;
+    joinrel->pruneable = true;
+    joinrel->classlist = NULL;
+    joinrel->relam = InvalidOid;
+    joinrel->ordering = NULL;
+    joinrel->clauseinfo = NIL;
+    joinrel->joininfo = NULL;
+    joinrel->innerjoin = NIL;
+    joinrel->superrels = NIL;
+
+    joinrel->relids = lcons(outer_rel->relids, /* ??? aren't they lists? -ay */
+                          lcons(inner_rel->relids, NIL));
+
+    new_outer_tlist = nconc(new_outer_tlist,new_inner_tlist);
+    joinrel->targetlist = new_outer_tlist;
+    
+    if (joininfo) {
+       joinrel->clauseinfo = joininfo->jinfoclauseinfo;
+       if (BushyPlanFlag)
+         joininfo->inactive = true;
+    }
+    
+    joinrel_joininfo_list = 
+       new_joininfo_list(append(outer_rel->joininfo, inner_rel->joininfo),
+                         intAppend(outer_rel->relids, inner_rel->relids));
+    
+    joinrel->joininfo = joinrel_joininfo_list;
+
+    set_joinrel_size(joinrel, outer_rel, inner_rel, joininfo);
+                    
+    return(joinrel);
+}
+
+/*    
+ * new-join-tlist--
+ *    Builds a join relations's target list by keeping those elements that 
+ *    will be in the final target list and any other elements that are still 
+ *    needed for future joins.  For a target list entry to still be needed 
+ *    for future joins, its 'joinlist' field must not be empty after removal 
+ *    of all relids in 'other-relids'.
+ *    
+ * 'tlist' is the target list of one of the join relations
+ * 'other-relids' is a list of relids contained within the other
+ *             join relation
+ * 'first-resdomno' is the resdom number to use for the first created
+ *             target list entry
+ *    
+ * Returns the new target list.
+ */
+static List *
+new_join_tlist(List *tlist,
+              List *other_relids,
+              int first_resdomno)
+{
+    int resdomno = first_resdomno - 1;
+    TargetEntry *xtl = NULL;
+    List *temp_node = NIL;
+    List *t_list = NIL;
+    List *i = NIL;
+    List *join_list = NIL;
+    bool in_final_tlist =false;
+    
+    
+    foreach(i,tlist) {
+       xtl= lfirst(i);
+       in_final_tlist = (join_list==NIL);
+       if( in_final_tlist)  {
+           resdomno += 1;
+           temp_node = 
+               lcons(create_tl_element(get_expr(xtl),
+                                      resdomno),
+                    NIL);
+           t_list = nconc(t_list,temp_node);
+       } 
+    }
+
+    return(t_list);
+}
+
+/*    
+ * new-joininfo-list--
+ *    Builds a join relation's joininfo list by checking for join clauses
+ *    which still need to used in future joins involving this relation.  A
+ *    join clause is still needed if there are still relations in the clause
+ *    not contained in the list of relations comprising this join relation.
+ *    New joininfo nodes are only created and added to
+ *    'current-joininfo-list' if a node for a particular join hasn't already
+ *    been created.
+ *
+ * 'current-joininfo-list' contains a list of those joininfo nodes that 
+ *     have already been built 
+ * 'joininfo-list' is the list of join clauses involving this relation
+ * 'join-relids' is a list of relids corresponding to the relations 
+ *     currently being joined
+ *    
+ * Returns a list of joininfo nodes, new and old.
+ */
+static List *
+new_joininfo_list(List *joininfo_list, List *join_relids)
+{
+    List *current_joininfo_list = NIL;
+    List *new_otherrels = NIL;
+    JInfo *other_joininfo = (JInfo*)NULL;
+    List *xjoininfo = NIL;
+    
+    foreach (xjoininfo, joininfo_list) {
+       JInfo *joininfo = (JInfo*)lfirst(xjoininfo);
+
+       new_otherrels = joininfo->otherrels;
+       if (nonoverlap_sets(new_otherrels,join_relids)) {
+           other_joininfo = joininfo_member(new_otherrels,
+                                            current_joininfo_list);
+           if(other_joininfo) {
+               other_joininfo->jinfoclauseinfo =
+                   (List*)LispUnion(joininfo->jinfoclauseinfo,
+                                    other_joininfo->jinfoclauseinfo);
+           }else {
+               other_joininfo = makeNode(JInfo);
+
+               other_joininfo->otherrels =
+                   joininfo->otherrels;
+               other_joininfo->jinfoclauseinfo =
+                   joininfo->jinfoclauseinfo;
+               other_joininfo->mergesortable =
+                   joininfo->mergesortable;
+               other_joininfo->hashjoinable =
+                   joininfo->hashjoinable;
+               other_joininfo->inactive = false;
+                                          
+               current_joininfo_list = lcons(other_joininfo,
+                                            current_joininfo_list);
+           }
+       }
+    }
+
+    return(current_joininfo_list);
+}
+
+/*
+ * add-new-joininfos--
+ *    For each new join relation, create new joininfos that
+ *    use the join relation as inner relation, and add
+ *    the new joininfos to those rel nodes that still
+ *    have joins with the join relation.
+ *
+ * 'joinrels' is a list of join relations.
+ *
+ * Modifies the joininfo field of appropriate rel nodes.
+ */
+void
+add_new_joininfos(Query *root, List *joinrels, List *outerrels)
+{
+    List *xjoinrel = NIL;
+    List *xrelid = NIL;
+    List *xrel = NIL;
+    List *xjoininfo = NIL;
+    
+    foreach(xjoinrel, joinrels) {
+       Rel *joinrel = (Rel *)lfirst(xjoinrel);
+        foreach(xrelid, joinrel->relids) {
+           Relid relid = (Relid)lfirst(xrelid);
+           Rel *rel = get_join_rel(root, relid);
+           add_superrels(rel,joinrel);
+       }
+    }
+    foreach(xjoinrel, joinrels) {
+       Rel *joinrel = (Rel *)lfirst(xjoinrel);
+
+       foreach(xjoininfo, joinrel->joininfo) {
+           JInfo *joininfo = (JInfo*)lfirst(xjoininfo);
+           List *other_rels = joininfo->otherrels;
+           List *clause_info = joininfo->jinfoclauseinfo;
+           bool mergesortable = joininfo->mergesortable;
+           bool hashjoinable = joininfo->hashjoinable;
+
+           foreach(xrelid, other_rels) {
+               Relid relid = (Relid)lfirst(xrelid);
+               Rel *rel = get_join_rel(root, relid);
+               List *super_rels = rel->superrels;
+               List *xsuper_rel = NIL;
+               JInfo *new_joininfo = makeNode(JInfo);
+                                               
+               new_joininfo->otherrels = joinrel->relids;
+               new_joininfo->jinfoclauseinfo = clause_info;
+               new_joininfo->mergesortable = mergesortable;
+               new_joininfo->hashjoinable = hashjoinable;
+               new_joininfo->inactive = false;
+               rel->joininfo = 
+                   lappend(rel->joininfo, new_joininfo);
+
+               foreach(xsuper_rel, super_rels) {
+                   Rel *super_rel = (Rel *)lfirst(xsuper_rel);
+
+                   if( nonoverlap_rels(super_rel,joinrel) ) {
+                       List *new_relids = super_rel->relids;
+                       JInfo *other_joininfo = 
+                           joininfo_member(new_relids,
+                                           joinrel->joininfo);
+
+                       if (other_joininfo) {
+                           other_joininfo->jinfoclauseinfo =
+                               (List*)LispUnion(clause_info,
+                                                other_joininfo->jinfoclauseinfo);
+                       } else {
+                           JInfo *new_joininfo = makeNode(JInfo);
+
+                           new_joininfo->otherrels = new_relids;
+                           new_joininfo->jinfoclauseinfo = clause_info; 
+                           new_joininfo->mergesortable = mergesortable;
+                           new_joininfo->hashjoinable = hashjoinable;
+                           new_joininfo->inactive = false;
+                           joinrel->joininfo =
+                               lappend(joinrel->joininfo,
+                                        new_joininfo);
+                       }
+                   }
+               }
+           }
+       }
+    }
+    foreach(xrel, outerrels)  {
+       Rel *rel = (Rel *)lfirst(xrel);
+       rel->superrels = NIL;
+    }
+}
+
+/*
+ * final-join-rels--
+ *     Find the join relation that includes all the original
+ *     relations, i.e. the final join result.
+ *
+ * 'join-rel-list' is a list of join relations.
+ *
+ * Returns the list of final join relations.
+ */
+List *
+final_join_rels(List *join_rel_list)
+{
+    List *xrel = NIL;
+    List *temp = NIL;
+    List *t_list = NIL;
+    
+    /*
+     * find the relations that has no further joins,
+     * i.e., its joininfos all have otherrels nil.
+     */
+    foreach(xrel,join_rel_list)  {
+       Rel *rel = (Rel *)lfirst(xrel);
+       List *xjoininfo = NIL;
+       bool final = true;
+
+       foreach (xjoininfo, rel->joininfo) {
+           JInfo *joininfo = (JInfo*)lfirst(xjoininfo);
+
+           if (joininfo->otherrels != NIL)  {
+               final = false;
+               break;
+           }
+       }
+       if (final)  {
+           temp = lcons(rel, NIL);
+           t_list = nconc(t_list, temp);
+       }
+    }
+
+    return(t_list);
+}
+
+/*
+ * add_superrels--
+ *    add rel to the temporary property list superrels.
+ *
+ * 'rel' a rel node
+ * 'super-rel' rel node of a join relation that includes rel
+ *
+ * Modifies the superrels field of rel
+ */
+static void
+add_superrels(Rel *rel, Rel *super_rel)
+{
+    rel->superrels = lappend(rel->superrels, super_rel);
+}
+
+/*
+ * nonoverlap-rels--
+ *    test if two join relations overlap, i.e., includes the same
+ *    relation.
+ *
+ * 'rel1' and 'rel2' are two join relations
+ *
+ * Returns non-nil if rel1 and rel2 do not overlap.
+ */
+static bool
+nonoverlap_rels(Rel *rel1, Rel *rel2)
+{
+    return(nonoverlap_sets(rel1->relids, rel2->relids));
+}
+
+static bool
+nonoverlap_sets(List *s1, List *s2)
+{
+    List *x = NIL;
+    
+    foreach(x,s1)  {
+       int e = lfirsti(x);
+       if(intMember(e,s2))
+           return(false);
+    }
+    return(true);
+}
+
+static void
+set_joinrel_size(Rel *joinrel, Rel *outer_rel, Rel *inner_rel, JInfo *jinfo)
+{
+    int ntuples;
+    float selec;
+
+    /* voodoo magic. but better than a size of 0. I have no idea why
+       we didn't set the size before. -ay 2/95 */
+    if (jinfo==NULL) {
+       /* worst case: the cartesian product */
+       ntuples = outer_rel->tuples * inner_rel->tuples;
+    } else {
+       selec = product_selec(jinfo->jinfoclauseinfo);
+/*     ntuples = Min(outer_rel->tuples,inner_rel->tuples) * selec; */
+       ntuples = outer_rel->tuples * inner_rel->tuples  * selec;
+    }
+    
+    /* I bet sizes less than 1 will screw up optimization so
+       make the best case 1 instead of 0  - jolly*/
+    if (ntuples < 1) 
+       ntuples = 1;
+
+    joinrel->tuples = ntuples;
+}
diff --git a/src/backend/optimizer/path/joinutils.c b/src/backend/optimizer/path/joinutils.c
new file mode 100644 (file)
index 0000000..8a59c09
--- /dev/null
@@ -0,0 +1,432 @@
+/*-------------------------------------------------------------------------
+ *
+ * joinutils.c--
+ *    Utilities for matching and building join and path keys
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/relation.h"
+#include "nodes/plannodes.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/paths.h"
+#include "optimizer/var.h"
+#include "optimizer/keys.h"
+#include "optimizer/tlist.h"
+#include "optimizer/joininfo.h"
+#include "optimizer/ordering.h"
+
+
+static int match_pathkey_joinkeys(List *pathkey, List *joinkeys,
+                int which_subkey);
+static bool every_func(List *joinkeys, List *pathkey,
+                int which_subkey);
+static List *new_join_pathkey(List *subkeys,
+                List *considered_subkeys, List *join_rel_tlist,
+                List *joinclauses);
+static List *new_matching_subkeys(Var *subkey, List *considered_subkeys,
+                List *join_rel_tlist, List *joinclauses);
+
+/****************************************************************************
+ *             KEY COMPARISONS
+ ****************************************************************************/
+
+/*    
+ * match-pathkeys-joinkeys--
+ *    Attempts to match the keys of a path against the keys of join clauses.
+ *    This is done by looking for a matching join key in 'joinkeys' for
+ *    every path key in the list 'pathkeys'. If there is a matching join key
+ *    (not necessarily unique) for every path key, then the list of 
+ *    corresponding join keys and join clauses are returned in the order in 
+ *    which the keys matched the path keys.
+ *    
+ * 'pathkeys' is a list of path keys:
+ *     ( ( (var) (var) ... ) ( (var) ... ) )
+ * 'joinkeys' is a list of join keys:
+ *     ( (outer inner) (outer inner) ... )
+ * 'joinclauses' is a list of clauses corresponding to the join keys in
+ *     'joinkeys'
+ * 'which-subkey' is a flag that selects the desired subkey of a join key
+ *     in 'joinkeys'
+ *    
+ * Returns the join keys and corresponding join clauses in a list if all
+ * of the path keys were matched:
+ *     ( 
+ *      ( (outerkey0 innerkey0) ... (outerkeyN innerkeyN) )
+ *      ( clause0 ... clauseN ) 
+ *     )
+ * and nil otherwise.
+ *    
+ * Returns a list of matched join keys and a list of matched join clauses
+ * in matchedJoinClausesPtr.  - ay 11/94
+ */
+List *
+match_pathkeys_joinkeys(List *pathkeys,
+                       List *joinkeys,
+                       List *joinclauses,
+                       int which_subkey,
+                       List **matchedJoinClausesPtr)
+{
+    List *matched_joinkeys = NIL;
+    List *matched_joinclauses = NIL;
+    List *pathkey = NIL;
+    List *i = NIL;
+    int matched_joinkey_index = -1;
+    
+    foreach(i, pathkeys) {
+       pathkey = lfirst(i);
+       matched_joinkey_index = 
+           match_pathkey_joinkeys(pathkey, joinkeys, which_subkey);
+       
+       if (matched_joinkey_index != -1 ) {
+           List *xjoinkey = nth(matched_joinkey_index,joinkeys);
+           List *joinclause = nth(matched_joinkey_index,joinclauses);
+           
+           /* XXX was "push" function */
+           matched_joinkeys = lappend(matched_joinkeys,xjoinkey);
+           matched_joinkeys = nreverse(matched_joinkeys);
+           
+           matched_joinclauses = lappend(matched_joinclauses,joinclause);
+           matched_joinclauses = nreverse(matched_joinclauses);
+           joinkeys = LispRemove(xjoinkey,joinkeys);
+       } else {
+           return(NIL);
+       } 
+       
+    }
+    if(matched_joinkeys==NULL ||
+       length(matched_joinkeys) != length(pathkeys)) {
+       return NIL;
+    }
+
+    *matchedJoinClausesPtr = nreverse(matched_joinclauses);
+    return (nreverse(matched_joinkeys));
+}
+
+/*    
+ * match-pathkey-joinkeys--
+ *    Returns the 0-based index into 'joinkeys' of the first joinkey whose
+ *    outer or inner subkey matches any subkey of 'pathkey'.
+ */
+static int
+match_pathkey_joinkeys(List *pathkey,
+                      List *joinkeys,
+                      int which_subkey) 
+{
+    Var *path_subkey;
+    int pos;
+    List *i = NIL;
+    List *x = NIL;
+    JoinKey *jk;
+    
+    foreach(i, pathkey) {
+       path_subkey = (Var *)lfirst(i);
+       pos = 0;
+       foreach(x, joinkeys) {
+           jk = (JoinKey*)lfirst(x);
+           if(var_equal(path_subkey,
+                        extract_subkey(jk, which_subkey)))
+               return(pos);
+           pos++;
+       }
+    }
+    return(-1);    /* no index found   */
+}
+
+/*    
+ * match-paths-joinkeys--
+ *    Attempts to find a path in 'paths' whose keys match a set of join
+ *    keys 'joinkeys'.  To match,
+ *    1. the path node ordering must equal 'ordering'.
+ *    2. each subkey of a given path must match(i.e., be(var_equal) to) the
+ *       appropriate subkey of the corresponding join key in 'joinkeys',
+ *       i.e., the Nth path key must match its subkeys against the subkey of
+ *       the Nth join key in 'joinkeys'.
+ *    
+ * 'joinkeys' is the list of key pairs to which the path keys must be 
+ *     matched
+ * 'ordering' is the ordering of the(outer) path to which 'joinkeys'
+ *     must correspond
+ * 'paths' is a list of(inner) paths which are to be matched against
+ *     each join key in 'joinkeys'
+ * 'which-subkey' is a flag that selects the desired subkey of a join key
+ *     in 'joinkeys'
+ *    
+ * Returns the matching path node if one exists, nil otherwise.
+ */
+static bool
+every_func(List *joinkeys, List *pathkey, int which_subkey)
+{
+    JoinKey *xjoinkey;
+    Var *temp;
+    Var *tempkey = NULL;
+    bool found = false;
+    List *i = NIL;
+    List *j = NIL;
+    
+    foreach(i,joinkeys) {
+       xjoinkey = (JoinKey*)lfirst(i);
+       found = false;
+       foreach(j,pathkey) {
+           temp = (Var*)lfirst((List*)lfirst(j));
+           if(temp == NULL) continue;
+           tempkey = extract_subkey(xjoinkey,which_subkey);
+           if(var_equal(tempkey, temp)) {
+               found = true;
+               break;
+           }
+       }
+       if(found == false)
+           return(false);
+    }
+    return(found);
+}
+
+
+/*
+ * match_paths_joinkeys -
+ *    find the cheapest path that matches the join keys
+ */
+Path *
+match_paths_joinkeys(List *joinkeys,
+                    PathOrder *ordering,
+                    List *paths,
+                    int which_subkey)
+{
+    Path *matched_path = NULL ;
+    bool key_match = false;
+    List *i = NIL;
+
+    foreach(i,paths) {
+       Path *path = (Path*)lfirst(i);
+
+       key_match = every_func(joinkeys, path->keys, which_subkey);
+       
+       if (equal_path_path_ordering(ordering,
+                                   &path->p_ordering) &&
+           length(joinkeys) == length(path->keys) &&
+           key_match) {
+
+           if (matched_path) {
+               if (path->path_cost < matched_path->path_cost)
+                   matched_path = path;
+           } else {
+               matched_path = path;
+           }
+       }
+    }
+    return matched_path;
+}
+
+
+
+/*    
+ * extract-path-keys--
+ *    Builds a subkey list for a path by pulling one of the subkeys from
+ *    a list of join keys 'joinkeys' and then finding the var node in the
+ *    target list 'tlist' that corresponds to that subkey.
+ *    
+ * 'joinkeys' is a list of join key pairs
+ * 'tlist' is a relation target list
+ * 'which-subkey' is a flag that selects the desired subkey of a join key
+ *     in 'joinkeys'
+ *    
+ * Returns a list of pathkeys: ((tlvar1)(tlvar2)...(tlvarN)).
+ * [I've no idea why they have to be list of lists. Should be fixed. -ay 12/94]
+ */
+List *
+extract_path_keys(List *joinkeys,
+                 List *tlist,
+                 int which_subkey)
+{
+    List *pathkeys = NIL;
+    List *jk;
+    
+    foreach(jk, joinkeys) {
+       JoinKey *jkey = (JoinKey*)lfirst(jk);
+       Var *var, *key;
+       List *p;
+
+       /*
+        * find the right Var in the target list for this key
+        */
+       var = (Var*)extract_subkey(jkey, which_subkey);
+       key = (Var*)matching_tlvar(var, tlist);
+
+       /*
+        * include it in the pathkeys list if we haven't already done so
+        */
+       foreach(p, pathkeys) {
+           Var *pkey = lfirst((List*)lfirst(p));       /* XXX fix me */
+           if (key == pkey)
+               break;
+       }
+       if (p!=NIL)
+           continue;   /* key already in pathkeys */
+
+       pathkeys =
+           lappend(pathkeys, lcons(key,NIL));
+    }
+    return(pathkeys);
+}
+
+
+/****************************************************************************
+ *             NEW PATHKEY FORMATION
+ ****************************************************************************/
+
+/*    
+ * new-join-pathkeys--
+ *    Find the path keys for a join relation by finding all vars in the list
+ *    of join clauses 'joinclauses' such that:
+ *     (1) the var corresponding to the outer join relation is a
+ *         key on the outer path
+ *     (2) the var appears in the target list of the join relation
+ *    In other words, add to each outer path key the inner path keys that
+ *    are required for qualification.
+ *    
+ * 'outer-pathkeys' is the list of the outer path's path keys
+ * 'join-rel-tlist' is the target list of the join relation
+ * 'joinclauses' is the list of restricting join clauses
+ *    
+ * Returns the list of new path keys. 
+ *    
+ */
+List *
+new_join_pathkeys(List *outer_pathkeys,
+                 List *join_rel_tlist,
+                 List *joinclauses)
+{      
+    List *outer_pathkey = NIL;
+    List *t_list = NIL;
+    List *x;
+    List *i = NIL;
+    
+    foreach(i, outer_pathkeys) {
+       outer_pathkey = lfirst(i);
+       x = new_join_pathkey(outer_pathkey, NIL, 
+                            join_rel_tlist,joinclauses);
+       if (x!=NIL) {
+           t_list = lappend(t_list, x);
+       }
+    }
+    return(t_list);
+}
+
+/*    
+ * new-join-pathkey--
+ *    Finds new vars that become subkeys due to qualification clauses that
+ *    contain any previously considered subkeys.  These new subkeys plus the
+ *    subkeys from 'subkeys' form a new pathkey for the join relation.
+ *    
+ *    Note that each returned subkey is the var node found in
+ *    'join-rel-tlist' rather than the joinclause var node.
+ *    
+ * 'subkeys' is a list of subkeys for which matching subkeys are to be
+ *     found
+ * 'considered-subkeys' is the current list of all subkeys corresponding
+ *     to a given pathkey
+ *    
+ * Returns a new pathkey(list of subkeys).
+ *    
+ */
+static List *
+new_join_pathkey(List *subkeys,
+                List *considered_subkeys,
+                List *join_rel_tlist,
+                List *joinclauses)
+{
+    List *t_list = NIL;
+    Var *subkey;
+    List *i = NIL;
+    List *matched_subkeys = NIL;
+    Expr *tlist_key = (Expr*)NULL;
+    List *newly_considered_subkeys = NIL;
+    
+    foreach (i, subkeys) {
+       subkey = (Var *)lfirst(i);
+       if(subkey == NULL)
+           break;    /* XXX something is wrong */
+       matched_subkeys = 
+           new_matching_subkeys(subkey,considered_subkeys,
+                                join_rel_tlist,joinclauses);
+       tlist_key = matching_tlvar(subkey,join_rel_tlist);
+       newly_considered_subkeys = NIL;
+       
+       if (tlist_key) {
+           if(!member(tlist_key, matched_subkeys))
+               newly_considered_subkeys = lcons(tlist_key,
+                                                matched_subkeys);
+       } 
+       else {
+           newly_considered_subkeys = matched_subkeys;
+       } 
+       
+       considered_subkeys = 
+           append(considered_subkeys, newly_considered_subkeys); 
+
+       t_list = nconc(t_list,newly_considered_subkeys);
+    }
+    return(t_list);
+}
+
+/*    
+ * new-matching-subkeys--
+ *    Returns a list of new subkeys:
+ *    (1) which are not listed in 'considered-subkeys'
+ *    (2) for which the "other" variable in some clause in 'joinclauses' is
+ *        'subkey'
+ *    (3) which are mentioned in 'join-rel-tlist'
+ *    
+ *    Note that each returned subkey is the var node found in
+ *    'join-rel-tlist' rather than the joinclause var node.
+ *    
+ * 'subkey' is the var node for which we are trying to find matching
+ *     clauses
+ *    
+ * Returns a list of new subkeys.
+ *
+ */
+static List *
+new_matching_subkeys(Var *subkey,
+                    List *considered_subkeys,
+                    List *join_rel_tlist,
+                    List *joinclauses)
+{
+    Expr *joinclause = NULL;
+    List *t_list = NIL;
+    List *temp = NIL;
+    List *i = NIL;
+    Expr *tlist_other_var = (Expr *)NULL;
+    
+    foreach(i,joinclauses) {
+       joinclause = lfirst(i);
+       tlist_other_var = 
+           matching_tlvar(other_join_clause_var(subkey,joinclause),
+                          join_rel_tlist);
+       
+       if(tlist_other_var && 
+          !(member(tlist_other_var,considered_subkeys))) {
+
+           /* XXX was "push" function  */
+           considered_subkeys = lappend(considered_subkeys,
+                                         tlist_other_var);
+
+           /* considered_subkeys = nreverse(considered_subkeys); 
+              XXX -- I am not sure of this. */
+           
+           temp = lcons(tlist_other_var, NIL);
+           t_list = nconc(t_list,temp);
+       } 
+    }
+    return(t_list);
+}
diff --git a/src/backend/optimizer/path/mergeutils.c b/src/backend/optimizer/path/mergeutils.c
new file mode 100644 (file)
index 0000000..550eef0
--- /dev/null
@@ -0,0 +1,122 @@
+/*-------------------------------------------------------------------------
+ *
+ * mergeutils.c--
+ *    Utilities for finding applicable merge clauses and pathkeys
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/relation.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/paths.h"
+#include "optimizer/clauses.h"
+#include "optimizer/ordering.h"
+
+/*    
+ * group-clauses-by-order--
+ *    If a join clause node in 'clauseinfo-list' is mergesortable, store
+ *    it within a mergeinfo node containing other clause nodes with the same
+ *    mergesort ordering.
+ *    
+ * 'clauseinfo-list' is the list of clauseinfo nodes
+ * 'inner-relid' is the relid of the inner join relation
+ *    
+ * Returns the new list of mergeinfo nodes.
+ *    
+ */
+List *
+group_clauses_by_order(List *clauseinfo_list,
+                      int inner_relid)
+{
+    List *mergeinfo_list = NIL;
+    List *xclauseinfo = NIL;
+    
+    foreach (xclauseinfo, clauseinfo_list) {
+       CInfo *clauseinfo = (CInfo *)lfirst(xclauseinfo);
+       MergeOrder *merge_ordering = clauseinfo->mergesortorder;
+       
+       if (merge_ordering) {
+           /*
+            * Create a new mergeinfo node and add it to
+            * 'mergeinfo-list' if one does not yet exist for this
+            * merge ordering.
+            */
+           PathOrder p_ordering;
+           MInfo *xmergeinfo;
+           Expr *clause = clauseinfo->clause;
+           Var *leftop = get_leftop (clause);
+           Var *rightop = get_rightop (clause);
+           JoinKey *keys;
+
+           p_ordering.ordtype = MERGE_ORDER;
+           p_ordering.ord.merge = merge_ordering;
+           xmergeinfo =
+               match_order_mergeinfo(&p_ordering, mergeinfo_list);
+           if (inner_relid == leftop->varno) {
+               keys = makeNode(JoinKey);
+               keys->outer = rightop;
+               keys->inner = leftop;
+           } else {
+               keys = makeNode(JoinKey);
+               keys->outer = leftop;
+               keys->inner = rightop;
+           } 
+           
+           if (xmergeinfo==NULL) {
+               xmergeinfo = makeNode(MInfo);
+
+               xmergeinfo->m_ordering = merge_ordering;
+               mergeinfo_list = lcons(xmergeinfo,
+                                     mergeinfo_list);
+           }
+           
+           ((JoinMethod *)xmergeinfo)->clauses =
+               lcons(clause,
+                    ((JoinMethod *)xmergeinfo)->clauses);
+           ((JoinMethod *)xmergeinfo)->jmkeys =
+               lcons(keys,
+                    ((JoinMethod *)xmergeinfo)->jmkeys);
+       }
+    }
+    return(mergeinfo_list);
+}
+
+
+/*    
+ * match-order-mergeinfo--
+ *    Searches the list 'mergeinfo-list' for a mergeinfo node whose order
+ *    field equals 'ordering'.
+ *    
+ * Returns the node if it exists.
+ *    
+ */
+MInfo *
+match_order_mergeinfo(PathOrder *ordering, List *mergeinfo_list)
+{
+    MergeOrder *xmergeorder;
+    List *xmergeinfo = NIL;
+
+    foreach(xmergeinfo, mergeinfo_list) {
+       MInfo *mergeinfo = (MInfo*)lfirst(xmergeinfo);
+
+       xmergeorder = mergeinfo->m_ordering;
+
+       if ((ordering->ordtype==MERGE_ORDER &&
+            equal_merge_merge_ordering(ordering->ord.merge, xmergeorder)) ||
+           (ordering->ordtype==SORTOP_ORDER &&
+            equal_path_merge_ordering(ordering->ord.sortop, xmergeorder))) {
+
+           return (mergeinfo);
+       }
+    }
+    return((MInfo*) NIL);
+}
diff --git a/src/backend/optimizer/path/orindxpath.c b/src/backend/optimizer/path/orindxpath.c
new file mode 100644 (file)
index 0000000..9a37605
--- /dev/null
@@ -0,0 +1,271 @@
+/*-------------------------------------------------------------------------
+ *
+ * orindxpath.c--
+ *    Routines to find index paths that match a set of 'or' clauses
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/relation.h"
+#include "nodes/primnodes.h"
+
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/clauses.h"
+#include "optimizer/clauseinfo.h"
+#include "optimizer/paths.h"
+#include "optimizer/cost.h"
+#include "optimizer/plancat.h"
+#include "optimizer/xfunc.h"
+
+#include "parser/parsetree.h"
+
+
+static void best_or_subclause_indices(Query *root, Rel *rel, List *subclauses,
+   List *indices, List *examined_indexids, Cost subcost, List *selectivities,
+   List **indexids, Cost *cost, List **selecs);                              
+static void best_or_subclause_index(Query *root, Rel *rel, Expr *subclause,
+       List *indices, int *indexid, Cost *cost, Cost *selec);
+
+
+/*    
+ * create-or-index-paths--
+ *    Creates index paths for indices that match 'or' clauses.
+ *    
+ * 'rel' is the relation entry for which the paths are to be defined on
+ * 'clauses' is the list of available restriction clause nodes
+ *    
+ * Returns a list of these index path nodes.
+ *    
+ */
+List *
+create_or_index_paths(Query *root,
+                     Rel *rel, List *clauses)
+{
+    List *t_list = NIL;
+    
+    if (clauses != NIL) {
+       CInfo *clausenode = (CInfo *) (lfirst (clauses));
+       
+       /* Check to see if this clause is an 'or' clause, and, if so,
+        * whether or not each of the subclauses within the 'or' clause has
+        * been matched by an index (the 'Index field was set in
+        * (match_or)  if no index matches a given subclause, one of the
+        * lists of index nodes returned by (get_index) will be 'nil').
+        */
+       if (valid_or_clause(clausenode) &&
+           clausenode->indexids) {
+           List *temp = NIL;
+           List *index_list = NIL;
+           bool index_flag = true;
+           
+           index_list = clausenode->indexids;
+           foreach(temp,index_list) {
+               if (!temp) 
+                   index_flag = false;
+           }
+           if (index_flag) {   /* used to be a lisp every function */
+               IndexPath *pathnode = makeNode(IndexPath);
+               List *indexids;
+               Cost cost;
+               List *selecs;
+
+               best_or_subclause_indices(root, 
+                                         rel,
+                                         clausenode->clause->args,
+                                         clausenode->indexids,
+                                         NIL,
+                                         (Cost)0,
+                                         NIL,
+                                         &indexids,
+                                         &cost,
+                                         &selecs);
+               
+               pathnode->path.pathtype = T_IndexScan;
+               pathnode->path.parent = rel;
+               pathnode->indexqual =
+                   lcons(clausenode,NIL);
+               pathnode->indexid = indexids;
+               pathnode->path.path_cost = cost;
+               
+               /* copy clauseinfo list into path for expensive 
+                  function processing    -- JMH, 7/7/92 */
+               pathnode->path.locclauseinfo =
+                   set_difference(clauses,
+                                  copyObject((Node*)
+                                             rel->clauseinfo));
+               
+#if 0 /* fix xfunc */
+               /* add in cost for expensive functions!  -- JMH, 7/7/92 */
+               if (XfuncMode != XFUNC_OFF) {
+                   ((Path*)pathnode)->path_cost +=
+                       xfunc_get_path_cost((Path)pathnode);
+               }
+#endif         
+               clausenode->selectivity = (Cost)floatVal(selecs);
+               t_list = 
+                   lcons(pathnode,
+                        create_or_index_paths(root, rel,lnext(clauses)));
+           } else {
+               t_list = create_or_index_paths(root, rel,lnext(clauses));
+           } 
+       }
+    }
+
+    return(t_list);
+}
+
+/*    
+ * best-or-subclause-indices--
+ *    Determines the best index to be used in conjunction with each subclause
+ *    of an 'or' clause and the cost of scanning a relation using these
+ *    indices.  The cost is the sum of the individual index costs.
+ *    
+ * 'rel' is the node of the relation on which the index is defined
+ * 'subclauses' are the subclauses of the 'or' clause
+ * 'indices' are those index nodes that matched subclauses of the 'or'
+ *     clause
+ * 'examined-indexids' is a list of those index ids to be used with 
+ *     subclauses that have already been examined
+ * 'subcost' is the cost of using the indices in 'examined-indexids'
+ * 'selectivities' is a list of the selectivities of subclauses that
+ *     have already been examined
+ *    
+ * Returns a list of the indexids, cost, and selectivities of each
+ * subclause, e.g., ((i1 i2 i3) cost (s1 s2 s3)), where 'i' is an OID,
+ * 'cost' is a flonum, and 's' is a flonum.
+ */
+static void
+best_or_subclause_indices(Query *root,
+                         Rel *rel,
+                         List *subclauses,
+                         List *indices,
+                         List *examined_indexids,
+                         Cost subcost,
+                         List *selectivities,
+                         List **indexids,      /* return value */
+                         Cost *cost,           /* return value */
+                         List **selecs)        /* return value */
+{
+    if (subclauses==NIL) {
+       *indexids = nreverse(examined_indexids);
+       *cost = subcost;
+       *selecs = nreverse(selectivities);
+    } else {
+       int best_indexid;
+       Cost best_cost;
+       Cost best_selec;
+       
+       best_or_subclause_index(root, rel, lfirst(subclauses), lfirst(indices),
+                               &best_indexid, &best_cost, &best_selec);
+       
+       best_or_subclause_indices(root,
+                                 rel,
+                                 lnext(subclauses),
+                                 lnext(indices),
+                                 lconsi(best_indexid, examined_indexids),
+                                 subcost + best_cost,
+                                 lcons(makeFloat(best_selec), selectivities),
+                                 indexids,
+                                 cost,
+                                 selecs);
+    } 
+    return;
+} 
+
+/*    
+ * best-or-subclause-index--
+ *    Determines which is the best index to be used with a subclause of
+ *    an 'or' clause by estimating the cost of using each index and selecting
+ *    the least expensive.
+ *    
+ * 'rel' is the node of the relation on which the index is defined
+ * 'subclause' is the subclause
+ * 'indices' is a list of index nodes that match the subclause
+ *    
+ * Returns a list (index-id index-subcost index-selectivity)
+ * (a fixnum, a fixnum, and a flonum respectively).
+ *    
+ */
+static void
+best_or_subclause_index(Query *root,
+                       Rel *rel,
+                       Expr *subclause,
+                       List *indices,
+                       int *retIndexid,        /* return value */
+                       Cost *retCost,          /* return value */
+                       Cost *retSelec)         /* return value */
+{
+    if (indices != NIL) {
+       Datum   value;
+       int     flag = 0;
+       Cost    subcost;
+       Rel     *index = (Rel *)lfirst (indices);
+       AttrNumber attno = (get_leftop (subclause))->varattno ;
+       Oid     opno = ((Oper*)subclause->oper)->opno;
+       bool    constant_on_right = non_null((Expr*)get_rightop(subclause));
+       float   npages, selec;
+       int     subclause_indexid;
+       Cost    subclause_cost;
+       Cost    subclause_selec;
+       
+       if(constant_on_right) {
+           value = ((Const*)get_rightop (subclause))->constvalue;
+       } else {
+           value = NameGetDatum("");
+       } 
+       if(constant_on_right) {
+           flag = (_SELEC_IS_CONSTANT_ ||_SELEC_CONSTANT_RIGHT_);
+       } else {
+           flag = _SELEC_CONSTANT_RIGHT_;
+       } 
+       index_selectivity(lfirsti(index->relids),
+                         index->classlist,
+                         lconsi(opno,NIL),
+                         getrelid(lfirsti(rel->relids),
+                                  root->rtable),
+                         lconsi(attno,NIL),
+                         lconsi(value,NIL),
+                         lconsi(flag,NIL),
+                         1,
+                         &npages,
+                         &selec);
+       
+       subcost = cost_index((Oid) lfirsti(index->relids),
+                            (int)npages,
+                            (Cost)selec,
+                            rel->pages,
+                            rel->tuples,
+                            index->pages,
+                            index->tuples,
+                            false);
+       best_or_subclause_index(root,
+                               rel,
+                               subclause,
+                               lnext(indices),
+                               &subclause_indexid,
+                               &subclause_cost,
+                               &subclause_selec);
+
+       if (subclause_indexid==0 || subcost < subclause_cost) {
+           *retIndexid = lfirsti(index->relids);
+           *retCost = subcost;
+           *retSelec = selec;
+       } else { 
+           *retIndexid = 0;
+           *retCost = 0.0;
+           *retSelec = 0.0;
+       } 
+    } 
+    return;
+}
diff --git a/src/backend/optimizer/path/predmig.c b/src/backend/optimizer/path/predmig.c
new file mode 100644 (file)
index 0000000..4ab3c71
--- /dev/null
@@ -0,0 +1,773 @@
+/*-------------------------------------------------------------------------
+ *
+ * predmig.c--
+ *    
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+** DESCRIPTION
+** Main Routines to handle Predicate Migration (i.e. correct optimization
+** of queries with expensive functions.)
+**
+**    The reasoning behind some of these algorithms is rather detailed.
+** Have a look at Sequoia Tech Report 92/13 for more info.  Also
+** see Monma and Sidney's paper "Sequencing with Series-Parallel
+** Precedence Constraints", in "Mathematics of Operations Research",
+** volume 4 (1979),  pp. 215-224.
+**
+**    The main thing that this code does that wasn't handled in xfunc.c is
+** it considers the possibility that two joins in a stream may not
+** be ordered by ascending rank -- in such a scenario, it may be optimal
+** to pullup more restrictions than we did via xfunc_try_pullup.
+**
+**    This code in some sense generalizes xfunc_try_pullup; if you
+** run postgres -x noprune, you'll turn off xfunc_try_pullup, and this
+** code will do everything that xfunc_try_pullup would have, and maybe
+** more.  However, this results in no pruning, which may slow down the 
+** optimizer and/or cause the system to run out of memory.
+**                                         -- JMH, 11/13/92
+*/
+
+#include "nodes/pg_list.h"
+#include "nodes/nodes.h"
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+#include "utils/palloc.h"
+#include "utils/elog.h"
+#include "planner/xfunc.h"
+#include "planner/pathnode.h"
+#include "planner/internal.h"
+#include "planner/cost.h"
+#include "planner/keys.h"
+#include "planner/tlist.h"
+#include "lib/qsort.h"
+
+#define is_clause(node) (get_cinfo(node))  /* a stream node represents a
+                                             clause (not a join) iff it
+                                             has a non-NULL cinfo field */
+
+static void xfunc_predmig(JoinPath pathnode, Stream streamroot,
+                         Stream laststream, bool *progressp);
+static bool xfunc_series_llel(Stream stream);
+static bool xfunc_llel_chains(Stream root, Stream bottom);
+static Stream xfunc_complete_stream(Stream stream);
+static bool xfunc_prdmig_pullup(Stream origstream, Stream pullme,
+                               JoinPath joinpath);
+static void xfunc_form_groups(Stream root, Stream bottom);
+static void xfunc_free_stream(Stream root);
+static Stream xfunc_add_clauses(Stream current);
+static void xfunc_setup_group(Stream node, Stream bottom);
+static Stream xfunc_streaminsert(CInfo clauseinfo, Stream current,
+                                int clausetype);
+static int xfunc_num_relids(Stream node);
+static StreamPtr xfunc_get_downjoin(Stream node);
+static StreamPtr xfunc_get_upjoin(Stream node);
+static Stream xfunc_stream_qsort(Stream root, Stream bottom);
+static int xfunc_stream_compare(void *arg1, void *arg2);
+static bool xfunc_check_stream(Stream node);
+static bool xfunc_in_stream(Stream node, Stream stream);
+
+/* -----------------       MAIN FUNCTIONS       ------------------------ */
+/*
+** xfunc_do_predmig
+**    wrapper for Predicate Migration.  It calls xfunc_predmig until no
+** more progress is made.
+**    return value says if any changes were ever made.
+*/
+bool xfunc_do_predmig(Path root)
+{
+    bool progress, changed = false;
+    
+    if (is_join(root))
+       do
+           {
+               progress = false;
+               Assert(IsA(root,JoinPath));
+               xfunc_predmig((JoinPath)root, (Stream)NULL, (Stream)NULL,
+                             &progress);
+               if (changed && progress)
+                   elog(DEBUG, "Needed to do a second round of predmig!\n");
+               if (progress) changed = true;
+           } while (progress);
+    return(changed);
+}
+
+
+/*
+ ** xfunc_predmig
+ **  The main routine for Predicate Migration.  It traverses a join tree,
+ ** and for each root-to-leaf path in the plan tree it constructs a 
+ ** "Stream", which it passes to xfunc_series_llel for optimization.
+ ** Destructively modifies the join tree (via predicate pullup).
+ */
+static void
+xfunc_predmig(JoinPath pathnode,    /* root of the join tree */
+             Stream streamroot,
+             Stream laststream,  /* for recursive calls -- these are
+                                       the root of the stream under
+                                       construction, and the lowest node
+                                       created so far */
+             bool *progressp)
+{
+    Stream newstream;
+    
+    /* 
+     ** traverse the join tree dfs-style, constructing a stream as you go.
+     ** When you hit a scan node, pass the stream off to xfunc_series_llel.
+     */
+    
+    /* sanity check */
+    if ((!streamroot && laststream) ||
+       (streamroot && !laststream))
+       elog(WARN, "called xfunc_predmig with bad inputs");
+    if (streamroot) Assert(xfunc_check_stream(streamroot));
+    
+    /* add path node to stream */
+    newstream = RMakeStream();
+    if (!streamroot)
+       streamroot = newstream;
+    set_upstream(newstream, (StreamPtr)laststream); 
+    if (laststream)
+       set_downstream(laststream, (StreamPtr)newstream);
+    set_downstream(newstream, (StreamPtr)NULL);
+    set_pathptr(newstream, (pathPtr)pathnode);
+    set_cinfo(newstream, (CInfo)NULL);
+    set_clausetype(newstream, XFUNC_UNKNOWN);
+    
+    /* base case: we're at a leaf, call xfunc_series_llel */
+    if (!is_join(pathnode))
+       {
+           /* form a fleshed-out copy of the stream */
+           Stream fullstream = xfunc_complete_stream(streamroot);
+           
+           /* sort it via series-llel */
+           if (xfunc_series_llel(fullstream))
+               *progressp = true;
+           
+           /* free up the copy */
+           xfunc_free_stream(fullstream);
+       }
+    else
+       {
+           /* visit left child */
+           xfunc_predmig((JoinPath)get_outerjoinpath(pathnode), 
+                         streamroot, newstream, progressp);
+           
+           /* visit right child */
+           xfunc_predmig((JoinPath)get_innerjoinpath(pathnode), 
+                         streamroot, newstream, progressp);
+       }
+    
+    /* remove this node */
+    if (get_upstream(newstream))
+       set_downstream((Stream)get_upstream(newstream), (StreamPtr)NULL);
+    pfree(newstream);
+}
+
+/*
+ ** xfunc_series_llel
+ **    A flavor of Monma and Sidney's Series-Parallel algorithm.
+ ** Traverse stream downwards.  When you find a node with restrictions on it,
+ ** call xfunc_llel_chains on the substream from root to that node.
+ */
+static bool xfunc_series_llel(Stream stream)
+{
+    Stream temp, next;
+    bool progress = false;
+    
+    for (temp = stream; temp != (Stream)NULL; temp = next)
+       {
+           next = (Stream)xfunc_get_downjoin(temp);
+           /* 
+            ** if there are restrictions/secondary join clauses above this
+            ** node, call xfunc_llel_chains 
+            */
+           if (get_upstream(temp) && is_clause((Stream)get_upstream(temp)))
+               if (xfunc_llel_chains(stream, temp))
+                   progress = true;
+       }
+    return(progress);
+}
+
+/*
+ ** xfunc_llel_chains
+ **    A flavor of Monma and Sidney's Parallel Chains algorithm.
+ ** Given a stream which has been well-ordered except for its lowermost
+ ** restrictions/2-ary joins, pull up the restrictions/2-arys as appropriate.  
+ ** What that means here is to form groups in the chain above the lowest
+ ** join node above bottom inclusive, and then take all the restrictions 
+ ** following bottom, and try to pull them up as far as possible.
+ */
+static bool xfunc_llel_chains(Stream root, Stream bottom)
+{
+    bool progress = false;
+    Stream origstream;
+    Stream tmpstream, pathstream;
+    Stream rootcopy = root;
+    
+    Assert(xfunc_check_stream(root));
+    
+    /* xfunc_prdmig_pullup will need an unmodified copy of the stream */
+    origstream = (Stream)copyObject((Node)root);
+    
+    /* form groups among ill-ordered nodes */
+    xfunc_form_groups(root, bottom);
+    
+    /* sort chain by rank */
+    Assert(xfunc_in_stream(bottom, root));
+    rootcopy = xfunc_stream_qsort(root, bottom);
+    
+    /* 
+     ** traverse sorted stream -- if any restriction has moved above a join,
+     ** we must pull it up in the plan.  That is, make plan tree
+     ** reflect order of sorted stream.
+     */
+    for (tmpstream = rootcopy, 
+        pathstream = (Stream)xfunc_get_downjoin(rootcopy);
+        tmpstream != (Stream)NULL && pathstream != (Stream)NULL; 
+        tmpstream = (Stream)get_downstream(tmpstream))
+       {
+           if (is_clause(tmpstream)
+               && get_pathptr(pathstream) != get_pathptr(tmpstream))
+               {
+                   /* 
+                    ** If restriction moved above a Join after sort, we pull it
+                    ** up in the join plan.
+                    **    If restriction moved down, we ignore it.
+                    ** This is because Joey's Sequoia paper proves that
+                    ** restrictions should never move down.  If this
+                    ** one were moved down, it would violate "semantic correctness",
+                    ** i.e. it would be lower than the attributes it references.
+                    */
+                   Assert(xfunc_num_relids(pathstream)>xfunc_num_relids(tmpstream));
+                   progress = 
+                       xfunc_prdmig_pullup(origstream, tmpstream, 
+                                           (JoinPath)get_pathptr(pathstream));
+               }
+           if (get_downstream(tmpstream))
+               pathstream = 
+                   (Stream)xfunc_get_downjoin((Stream)get_downstream(tmpstream));
+       }
+    
+    /* free up origstream */
+    xfunc_free_stream(origstream);
+    return(progress);
+}
+
+/*
+ ** xfunc_complete_stream --
+ **   Given a stream composed of join nodes only, make a copy containing the
+ ** join nodes along with the associated restriction nodes.  
+ */
+static Stream xfunc_complete_stream(Stream stream)
+{
+    Stream tmpstream, copystream, curstream = (Stream)NULL;
+    
+    copystream = (Stream)copyObject((Node)stream);
+    Assert(xfunc_check_stream(copystream));
+    
+    curstream = copystream;
+    Assert(!is_clause(curstream));
+    
+    /*      curstream = (Stream)xfunc_get_downjoin(curstream); */
+    
+    while(curstream != (Stream)NULL)
+       {
+           xfunc_add_clauses(curstream);
+           curstream = (Stream)xfunc_get_downjoin(curstream);
+       }
+    
+    /* find top of stream and return it */
+    for (tmpstream = copystream; get_upstream(tmpstream) != (StreamPtr)NULL;
+         tmpstream = (Stream)get_upstream(tmpstream))
+       /* no body in for loop */;
+    
+    return(tmpstream);
+}
+
+/*
+ ** xfunc_prdmig_pullup
+ **    pullup a clause in a path above joinpath.  Since the JoinPath tree
+ ** doesn't have upward pointers, it's difficult to deal with.  Thus we
+ ** require the original stream, which maintains pointers to all the path
+ ** nodes.  We use the original stream to find out what joins are
+ ** above the clause.
+ */
+static bool
+xfunc_prdmig_pullup(Stream origstream, Stream pullme, JoinPath joinpath)
+{
+    CInfo clauseinfo = get_cinfo(pullme);
+    bool progress = false;
+    Stream upjoin, orignode, temp;
+    int whichchild;
+    
+    /* find node in origstream that contains clause */
+    for (orignode = origstream;  
+        orignode != (Stream) NULL
+        && get_cinfo(orignode) != clauseinfo;
+        orignode = (Stream)get_downstream(orignode))
+       /* empty body in for loop */ ;
+    if (!orignode)
+       elog(WARN, "Didn't find matching node in original stream");
+    
+    
+    /* pull up this node as far as it should go */
+    for (upjoin = (Stream)xfunc_get_upjoin(orignode); 
+        upjoin != (Stream)NULL 
+        && (JoinPath)get_pathptr((Stream)xfunc_get_downjoin(upjoin))
+        != joinpath;
+        upjoin = (Stream)xfunc_get_upjoin(upjoin))
+       {
+#ifdef DEBUG    
+           elog(DEBUG, "pulling up in xfunc_predmig_pullup!");
+#endif
+           /* move clause up in path */
+           if (get_pathptr((Stream)get_downstream(upjoin)) 
+               == (pathPtr)get_outerjoinpath((JoinPath)get_pathptr(upjoin)))
+               whichchild = OUTER;
+           else whichchild = INNER;
+           clauseinfo = xfunc_pullup((Path)get_pathptr((Stream)get_downstream(upjoin)), 
+                                     (JoinPath)get_pathptr(upjoin),
+                                     clauseinfo,
+                                     whichchild,
+                                     get_clausetype(orignode));
+           set_pathptr(pullme, get_pathptr(upjoin));
+           /* pullme has been moved into locclauseinfo */
+           set_clausetype(pullme, XFUNC_LOCPRD);
+           
+           /* 
+            ** xfunc_pullup makes new path nodes for children of 
+            ** get_pathptr(current). We must modify the stream nodes to point
+            ** to these path nodes
+            */
+           if (whichchild == OUTER)
+               {
+                   for(temp = (Stream)get_downstream(upjoin); is_clause(temp);
+                       temp = (Stream)get_downstream(temp))
+                       set_pathptr
+                           (temp, (pathPtr)
+                            get_outerjoinpath((JoinPath)get_pathptr(upjoin)));
+                   set_pathptr
+                       (temp, 
+                        (pathPtr)get_outerjoinpath((JoinPath)get_pathptr(upjoin)));
+               }
+           else
+               {
+                   for(temp = (Stream)get_downstream(upjoin); is_clause(temp);
+                       temp = (Stream)get_downstream(temp))
+                       set_pathptr
+                           (temp, (pathPtr)
+                            get_innerjoinpath((JoinPath)get_pathptr(upjoin)));
+                   set_pathptr
+                       (temp, (pathPtr)
+                        get_innerjoinpath((JoinPath)get_pathptr(upjoin)));
+               }
+           progress = true;
+       }
+    if (!progress) 
+       elog(DEBUG, "didn't succeed in pulling up in xfunc_prdmig_pullup");
+    return(progress);
+}
+
+/*
+ ** xfunc_form_groups --
+ **    A group is a pair of stream nodes a,b such that a is constrained to
+ ** precede b (for instance if a and b are both joins), but rank(a) > rank(b).
+ ** In such a situation, Monma and Sidney prove that no clauses should end
+ ** up between a and b, and therefore we may treat them as a group, with
+ ** selectivity equal to the product of their selectivities, and cost
+ ** equal to the cost of the first plus the selectivity of the first times the
+ ** cost of the second.  We define each node to be in a group by itself,
+ ** and then repeatedly find adjacent groups which are ordered by descending
+ ** rank, and make larger groups.  You know that two adjacent nodes are in a 
+ ** group together if the lower has groupup set to true.  They will both have 
+ ** the same groupcost and groupsel (since they're in the same group!)
+ */
+static void xfunc_form_groups(Query* queryInfo, Stream root, Stream bottom)
+{
+    Stream temp, parent;
+    int lowest = xfunc_num_relids((Stream)xfunc_get_upjoin(bottom));
+    bool progress;
+    LispValue primjoin;
+    int whichchild;
+    
+    if (!lowest) return;  /* no joins in stream, so no groups */
+    
+    /* initialize groups to be single nodes */
+    for (temp = root; 
+        temp != (Stream)NULL && temp != bottom;
+        temp = (Stream)get_downstream(temp))
+       {
+           /* if a Join node */
+           if (!is_clause(temp))
+               {
+                   if (get_pathptr((Stream)get_downstream(temp)) 
+                       == (pathPtr)get_outerjoinpath((JoinPath)get_pathptr(temp)))
+                       whichchild = OUTER;
+                   else whichchild = INNER;
+                   set_groupcost(temp,
+                                 xfunc_join_expense((JoinPath)get_pathptr(temp),
+                                                    whichchild));
+                   if (primjoin = xfunc_primary_join((JoinPath)get_pathptr(temp)))
+                       {
+                           set_groupsel(temp, 
+                                        compute_clause_selec(queryInfo, 
+                                                             primjoin, NIL));
+                       }
+                   else 
+                       {
+                           set_groupsel(temp,1.0);
+                       }
+               }
+           else  /* a restriction, or 2-ary join pred */
+               {
+                   set_groupcost(temp, 
+                                 xfunc_expense(queryInfo,
+                                               get_clause(get_cinfo(temp))));
+                   set_groupsel(temp, 
+                                compute_clause_selec(queryInfo,
+                                                     get_clause(get_cinfo(temp)),
+                                                     NIL));
+               }
+           set_groupup(temp,false);
+       }
+    
+    /* make passes upwards, forming groups */
+    do
+       {
+           progress = false;
+           for (temp = (Stream)get_upstream(bottom); 
+                temp != (Stream)NULL;
+                temp = (Stream)get_upstream(temp))
+               {
+                   /* check for grouping with node upstream */
+                   if (!get_groupup(temp) &&          /* not already grouped */
+                       (parent = (Stream)get_upstream(temp)) != (Stream)NULL &&
+                       /* temp is a join or temp is the top of a group */
+                       (is_join((Path)get_pathptr(temp)) ||
+                        get_downstream(temp) &&
+                        get_groupup((Stream)get_downstream(temp))) &&
+                       get_grouprank(parent) < get_grouprank(temp))
+                       {
+                           progress = true;          /* we formed a new group */
+                           set_groupup(temp,true);
+                           set_groupcost(temp,
+                                         get_groupcost(temp) + 
+                                         get_groupsel(temp) * get_groupcost(parent));
+                           set_groupsel(temp,get_groupsel(temp) * get_groupsel(parent));
+                           
+                           /* fix costs and sels of all members of group */
+                           xfunc_setup_group(temp, bottom);
+                       }
+               }
+       } while(progress);
+}
+
+
+/* -------------------   UTILITY FUNCTIONS     ------------------------- */
+
+/*
+ ** xfunc_free_stream --
+ **   walk down a stream and pfree it
+ */
+static void xfunc_free_stream(Stream root)
+{
+    Stream cur, next;
+    
+    Assert(xfunc_check_stream(root));
+    
+    if (root != (Stream)NULL)
+       for (cur = root; cur != (Stream)NULL; cur = next)
+           {
+               next = (Stream)get_downstream(cur);
+               pfree(cur);
+           }
+}
+
+/*
+ ** xfunc_add<_clauses
+ **    find any clauses above current, and insert them into stream as 
+ ** appropriate.  Return uppermost clause inserted, or current if none.
+ */
+static Stream xfunc_add_clauses(Stream current)
+{
+    Stream topnode = current;
+    LispValue temp;
+    LispValue primjoin;
+    
+    /* first add in the local clauses */
+    foreach(temp, get_locclauseinfo((Path)get_pathptr(current)))
+       {
+           topnode = 
+               xfunc_streaminsert((CInfo)lfirst(temp), topnode, 
+                                  XFUNC_LOCPRD);
+       }
+    
+    /* and add in the join clauses */
+    if (IsA(get_pathptr(current),JoinPath))
+       {
+           primjoin = xfunc_primary_join((JoinPath)get_pathptr(current));
+           foreach(temp, get_pathclauseinfo((JoinPath)get_pathptr(current)))
+               {
+                   if (!equal(get_clause((CInfo)lfirst(temp)), primjoin))
+                       topnode = 
+                           xfunc_streaminsert((CInfo)lfirst(temp), topnode,
+                                              XFUNC_JOINPRD);
+               }
+       }
+    return(topnode);
+}
+
+
+/*
+ ** xfunc_setup_group
+ **   find all elements of stream that are grouped with node and are above
+ ** bottom, and set their groupcost and groupsel to be the same as node's.
+ */
+static void xfunc_setup_group(Stream node, Stream bottom)
+{
+    Stream temp;
+    
+    if (node != bottom)
+       /* traverse downwards */
+       for (temp = (Stream)get_downstream(node); 
+            temp != (Stream)NULL && temp != bottom; 
+            temp = (Stream)get_downstream(temp))
+           {
+               if (!get_groupup(temp)) break;
+               else 
+                   {
+                       set_groupcost(temp, get_groupcost(node));
+                       set_groupsel(temp, get_groupsel(node));
+                   }
+           }
+    
+    /* traverse upwards */
+    for (temp = (Stream)get_upstream(node); temp != (Stream)NULL; 
+        temp = (Stream)get_upstream(temp))
+       {
+           if (!get_groupup((Stream)get_downstream(temp))) break;
+           else
+               {
+                   set_groupcost(temp, get_groupcost(node));
+                   set_groupsel(temp, get_groupsel(node));
+               }
+       }
+}
+
+
+/*
+ ** xfunc_streaminsert
+ **    Make a new Stream node to hold clause, and insert it above current.
+ ** Return new node.
+ */
+static Stream
+xfunc_streaminsert(CInfo clauseinfo,
+                  Stream current,
+                  int clausetype)  /* XFUNC_LOCPRD or XFUNC_JOINPRD */
+{
+    Stream newstream = RMakeStream();
+    set_upstream(newstream, get_upstream(current));
+    if (get_upstream(current))
+       set_downstream((Stream)(get_upstream(current)), (StreamPtr)newstream);
+    set_upstream(current, (StreamPtr)newstream);
+    set_downstream(newstream, (StreamPtr)current);
+    set_pathptr(newstream, get_pathptr(current));
+    set_cinfo(newstream, clauseinfo);
+    set_clausetype(newstream, clausetype);
+    return(newstream);
+}
+
+/*
+ ** Given a Stream node, find the number of relids referenced in the pathnode
+ ** associated with the stream node.  The number of relids gives a unique
+ ** ordering on the joins in a stream, which we use to compare the height of 
+ ** join nodes.
+ */
+static int xfunc_num_relids(Stream node)
+{
+    if (!node || !IsA(get_pathptr(node),JoinPath))
+       return(0);
+    else return(length
+               (get_relids(get_parent((JoinPath)get_pathptr(node)))));
+}
+
+/* 
+ ** xfunc_get_downjoin --
+ **    Given a stream node, find the next lowest node which points to a
+ ** join predicate or a scan node.
+ */
+static StreamPtr xfunc_get_downjoin(Stream node)
+{
+    Stream temp;
+    
+    if (!is_clause(node))  /* if this is a join */
+       node = (Stream)get_downstream(node);
+    for (temp = node; temp && is_clause(temp); 
+        temp = (Stream)get_downstream(temp))
+       /* empty body in for loop */  ;
+    
+    return((StreamPtr)temp);
+}
+
+/*
+ ** xfunc_get_upjoin --
+ **   same as above, but upwards.
+ */
+static StreamPtr xfunc_get_upjoin(Stream node)
+{
+    Stream temp;
+    
+    if (!is_clause(node))  /* if this is a join */
+       node = (Stream)get_upstream(node);
+    for (temp = node; temp && is_clause(temp); 
+        temp = (Stream)get_upstream(temp))
+       /* empty body in for loop */  ;
+    
+    return((StreamPtr)temp);
+}
+
+/*
+ ** xfunc_stream_qsort --
+ **   Given a stream, sort by group rank the elements in the stream from the
+ ** node "bottom" up.  DESTRUCTIVELY MODIFIES STREAM!  Returns new root.
+ */
+static Stream xfunc_stream_qsort(Stream root, Stream bottom)
+{
+    int i;
+    size_t num;
+    Stream *nodearray, output;
+    Stream tmp;
+    
+    /* find size of list */
+    for (num = 0, tmp = root; tmp != bottom; 
+        tmp = (Stream)get_downstream(tmp))
+       num ++;
+    if (num <= 1)  return (root);
+    
+    /* copy elements of the list into an array */
+    nodearray = (Stream *) palloc(num * sizeof(Stream));
+    
+    for (tmp = root, i = 0; tmp != bottom; 
+        tmp = (Stream)get_downstream(tmp), i++)
+       nodearray[i] = tmp;
+    
+    /* sort the array */
+    pg_qsort(nodearray, num, sizeof(LispValue), xfunc_stream_compare);
+    
+    /* paste together the array elements */
+    output = nodearray[num - 1];
+    set_upstream(output, (StreamPtr)NULL);
+    for (i = num - 2; i >= 0; i--)
+       {
+           set_downstream(nodearray[i+1], (StreamPtr)nodearray[i]);
+           set_upstream(nodearray[i], (StreamPtr)nodearray[i+1]);
+       }
+    set_downstream(nodearray[0], (StreamPtr)bottom);
+    if (bottom)
+       set_upstream(bottom, (StreamPtr)nodearray[0]);
+    
+    Assert(xfunc_check_stream(output));
+    return(output);
+}
+
+/*
+ ** xfunc_stream_compare
+ **    comparison function for xfunc_stream_qsort.
+ ** Compare nodes by group rank.  If group ranks are equal, ensure that
+ ** join nodes appear in same order as in plan tree.
+ */
+static int xfunc_stream_compare(void *arg1, void *arg2)
+{
+    Stream stream1 = *(Stream *) arg1;
+    Stream stream2 = *(Stream *) arg2;
+    Cost rank1, rank2;
+    
+    rank1 = get_grouprank(stream1);
+    rank2 = get_grouprank(stream2);
+    
+    if (rank1 > rank2) return(1);
+    else if (rank1 < rank2) return(-1);
+    else
+       {
+           if (is_clause(stream1) && is_clause(stream2))
+               return(0);  /* doesn't matter what order if both are restrictions */
+           else if (!is_clause(stream1) && !is_clause(stream2))
+               {
+                   if (xfunc_num_relids(stream1) < xfunc_num_relids(stream2))
+                       return(-1);
+                   else return(1);
+               }
+           else if (is_clause(stream1) && !is_clause(stream2))
+               {
+                   if (xfunc_num_relids(stream1) == xfunc_num_relids(stream2))
+                       /* stream1 is a restriction over stream2 */
+                       return(1);
+                   else return(-1);
+               }
+           else if (!is_clause(stream1) && is_clause(stream2))
+               {
+                   /* stream2 is a restriction over stream1: never push down */
+                   return(-1);
+               }
+       }
+}
+
+/* ------------------    DEBUGGING ROUTINES     ---------------------------- */
+
+/*
+ ** Make sure all pointers in stream make sense.  Make sure no joins are
+ ** out of order.
+ */
+static bool xfunc_check_stream(Stream node)
+{
+    Stream temp;
+    int numrelids, tmp;
+    
+    /* set numrelids higher than max */
+    if (!is_clause(node))
+       numrelids = xfunc_num_relids(node) + 1;
+    else if (xfunc_get_downjoin(node))
+       numrelids = xfunc_num_relids((Stream)xfunc_get_downjoin(node)) + 1;
+    else numrelids = 1;
+    
+    for (temp = node; get_downstream(temp); temp = (Stream)get_downstream(temp))
+       {
+           if ((Stream)get_upstream((Stream)get_downstream(temp)) != temp)
+               {
+                   elog(WARN, "bad pointers in stream");
+                   return(false);
+               }
+           if (!is_clause(temp))
+               {
+                   if ((tmp = xfunc_num_relids(temp)) >= numrelids)
+                       {
+                           elog(WARN, "Joins got reordered!");
+                           return(false);
+                       }
+                   numrelids = tmp;
+               }
+       }
+    
+    return(true);
+}
+
+/*
+ ** xfunc_in_stream
+ **   check if node is in stream
+ */
+static bool xfunc_in_stream(Stream node, Stream stream)
+{
+    Stream temp;
+    
+    for (temp = stream; temp; temp = (Stream)get_downstream(temp))
+       if (temp == node) return(1);
+    return(0);
+}
diff --git a/src/backend/optimizer/path/prune.c b/src/backend/optimizer/path/prune.c
new file mode 100644 (file)
index 0000000..ecec4e9
--- /dev/null
@@ -0,0 +1,203 @@
+/*-------------------------------------------------------------------------
+ *
+ * prune.c--
+ *    Routines to prune redundant paths and relations
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/relation.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/cost.h"
+#include "optimizer/paths.h"
+#include "optimizer/pathnode.h"
+
+#include "utils/elog.h"
+
+
+static List *prune_joinrel(Rel *rel, List *other_rels);
+
+/*    
+ * prune-joinrels--
+ *    Removes any redundant relation entries from a list of rel nodes
+ *    'rel-list'.
+ *    
+ * Returns the resulting list. 
+ *    
+ */
+List *prune_joinrels(List *rel_list)
+{
+    List *temp_list = NIL;
+
+    if (rel_list != NIL) {
+       temp_list = lcons(lfirst(rel_list),
+                        prune_joinrels(prune_joinrel((Rel*)lfirst(rel_list),
+                                                     lnext(rel_list))));
+    } 
+    return(temp_list);
+}
+
+/*    
+ * prune-joinrel--
+ *    Prunes those relations from 'other-rels' that are redundant with
+ *    'rel'.  A relation is redundant if it is built up of the same
+ *    relations as 'rel'.  Paths for the redundant relation are merged into
+ *    the pathlist of 'rel'.
+ *    
+ * Returns a list of non-redundant relations, and sets the pathlist field
+ * of 'rel' appropriately.
+ *    
+ */
+static List *
+prune_joinrel(Rel *rel, List *other_rels) 
+{
+    List *i = NIL;
+    List *t_list = NIL;
+    List *temp_node = NIL;
+    Rel *other_rel = (Rel *)NULL;
+    
+    foreach(i, other_rels) {
+       other_rel = (Rel*)lfirst(i);
+       if(same(rel->relids, other_rel->relids)) {
+           rel->pathlist = add_pathlist(rel,
+                                        rel->pathlist,
+                                        other_rel->pathlist);
+           t_list = nconc(t_list, NIL);  /* XXX is this right ? */
+       } else {
+           temp_node = lcons(other_rel, NIL);
+           t_list = nconc(t_list,temp_node);
+       } 
+    }
+    return(t_list);
+}
+
+/*    
+ * prune-rel-paths--
+ *    For each relation entry in 'rel-list' (which corresponds to a join
+ *    relation), set pointers to the unordered path and cheapest paths
+ *    (if the unordered path isn't the cheapest, it is pruned), and
+ *    reset the relation's size field to reflect the join.
+ *    
+ * Returns nothing of interest.
+ *    
+ */
+void
+prune_rel_paths(List *rel_list)
+{
+    List *x = NIL;
+    List *y = NIL;
+    Path *path;
+    Rel *rel = (Rel*)NULL;
+    JoinPath *cheapest = (JoinPath*)NULL;
+    
+    foreach(x, rel_list) {
+       rel = (Rel*)lfirst(x);
+       foreach(y, rel->pathlist) {
+           path = (Path*)lfirst(y);
+
+           if(!path->p_ordering.ord.sortop) {
+               break;
+           }       
+       }
+       cheapest = (JoinPath*)prune_rel_path(rel, path);
+       if (IsA_JoinPath(cheapest))
+           {
+               rel->size = compute_joinrel_size(cheapest);
+           }
+       else
+           elog(WARN, "non JoinPath called");
+    }
+}
+
+
+/*    
+ * prune-rel-path--
+ *    Compares the unordered path for a relation with the cheapest path. If
+ *    the unordered path is not cheapest, it is pruned.
+ *    
+ *    Resets the pointers in 'rel' for unordered and cheapest paths.
+ *    
+ * Returns the cheapest path.
+ *    
+ */
+Path *
+prune_rel_path(Rel *rel, Path *unorderedpath)
+{
+    Path *cheapest = set_cheapest(rel, rel->pathlist);
+    
+    /* don't prune if not pruneable  -- JMH, 11/23/92 */
+    if(unorderedpath != cheapest 
+       && rel->pruneable) {
+       
+       rel->unorderedpath = (Path *)NULL;
+       rel->pathlist = lremove(unorderedpath, rel->pathlist);
+    } else {
+       rel->unorderedpath = (Path *)unorderedpath;
+    } 
+    
+    return(cheapest);
+}
+
+/*
+ * merge-joinrels--
+ *    Given two lists of rel nodes that are already
+ *    pruned, merge them into one pruned rel node list
+ *
+ * 'rel-list1' and
+ * 'rel-list2' are the rel node lists
+ *
+ * Returns one pruned rel node list
+ */
+List *
+merge_joinrels(List *rel_list1, List *rel_list2)
+{
+    List *xrel = NIL;
+    
+    foreach(xrel,rel_list1) {
+        Rel *rel = (Rel*)lfirst(xrel);
+        rel_list2 = prune_joinrel(rel,rel_list2);
+    }
+    return(append(rel_list1, rel_list2)); 
+}
+
+/*
+ * prune_oldrels--
+ *    If all the joininfo's in a rel node are inactive,
+ *    that means that this node has been joined into
+ *    other nodes in all possible ways, therefore
+ *    this node can be discarded.  If not, it will cause
+ *    extra complexity of the optimizer.
+ *
+ * old_rels is a list of rel nodes
+ *     
+ * Returns a new list of rel nodes
+ */
+List *prune_oldrels(List *old_rels)
+{
+    Rel *rel;
+    List *joininfo_list, *xjoininfo;
+    
+    if(old_rels == NIL)
+       return(NIL);
+    
+    rel = (Rel*)lfirst(old_rels);
+    joininfo_list = rel->joininfo;
+    if(joininfo_list == NIL)
+       return (lcons(rel, prune_oldrels(lnext(old_rels))));
+
+    foreach(xjoininfo, joininfo_list) {
+       JInfo *joininfo = (JInfo*)lfirst(xjoininfo);
+       if(!joininfo->inactive)
+           return (lcons(rel, prune_oldrels(lnext(old_rels))));
+    }
+    return(prune_oldrels(lnext(old_rels)));
+}
diff --git a/src/backend/optimizer/path/xfunc.c b/src/backend/optimizer/path/xfunc.c
new file mode 100644 (file)
index 0000000..7d2f70b
--- /dev/null
@@ -0,0 +1,1360 @@
+/*-------------------------------------------------------------------------
+ *
+ * xfunc.c--
+ *    Utility routines to handle expensive function optimization.
+ *    Includes xfunc_trypullup(), which attempts early pullup of predicates
+ *    to allow for maximal pruning.
+ *    
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef WIN32
+#include <math.h>      /* for MAXFLOAT on most systems */
+#else
+#include <float.h>
+#define MAXFLOAT DBL_MAX
+#endif /* WIN32 */
+
+#include <values.h>    /* for MAXFLOAT on SunOS */
+#include <string.h>
+
+#include "postgres.h"
+#include "nodes/pg_list.h"
+#include "nodes/nodes.h"
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/syscache.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "utils/syscache.h"
+#include "catalog/pg_language.h"
+#include "planner/xfunc.h"
+#include "planner/clauses.h"
+#include "planner/pathnode.h"
+#include "planner/internal.h"
+#include "planner/cost.h"
+#include "planner/keys.h"
+#include "planner/tlist.h"
+#include "lib/lispsort.h"
+#include "access/heapam.h"
+#include "tcop/dest.h"
+#include "storage/buf_internals.h"     /* for NBuffers */
+#include "optimizer/tlist.h"  /* for get_expr */
+
+#define ever ; 1 ;
+
+/* local funcs */
+static int xfunc_card_unreferenced(Query *queryInfo, 
+                                  Expr *clause, Relid referenced); */
+
+/*
+** xfunc_trypullup --
+**    Preliminary pullup of predicates, to allow for maximal pruning.
+** Given a relation, check each of its paths and see if you can
+** pullup clauses from its inner and outer.
+*/
+
+void xfunc_trypullup(Rel rel)
+{
+    LispValue y;            /* list ptr */
+    CInfo maxcinfo;         /* The CInfo to pull up, as calculated by
+                              xfunc_shouldpull() */
+    JoinPath curpath;       /* current path in list */
+    int progress;           /* has progress been made this time through? */
+    int clausetype;
+    
+    do {
+       progress = false;  /* no progress yet in this iteration */
+       foreach(y, get_pathlist(rel)) {
+           curpath = (JoinPath)lfirst(y);
+           
+           /*
+            ** for each operand, attempt to pullup predicates until first 
+            ** failure.
+            */
+           for(ever) {
+               /* No, the following should NOT be '=='  !! */
+               if (clausetype = 
+                   xfunc_shouldpull((Path)get_innerjoinpath(curpath),
+                                    curpath, INNER, &maxcinfo)) {
+                   
+                   xfunc_pullup((Path)get_innerjoinpath(curpath),
+                                curpath, maxcinfo, INNER, clausetype);
+                   progress = true;
+               }else
+                   break;
+           }
+           for(ever) {
+               
+               /* No, the following should NOT be '=='  !! */
+               if (clausetype = 
+                   xfunc_shouldpull((Path)get_outerjoinpath(curpath), 
+                                    curpath, OUTER, &maxcinfo)) {
+                   
+                   xfunc_pullup((Path)get_outerjoinpath(curpath),
+                                curpath, maxcinfo, OUTER, clausetype);
+                   progress = true;
+               }else
+                   break;
+           }
+           
+           /* 
+            ** make sure the unpruneable flag bubbles up, i.e.
+            ** if anywhere below us in the path pruneable is false,
+            ** then pruneable should be false here
+            */
+           if (get_pruneable(get_parent(curpath)) &&
+               (!get_pruneable(get_parent
+                               ((Path)get_innerjoinpath(curpath))) ||
+                !get_pruneable(get_parent((Path)
+                                          get_outerjoinpath(curpath))))) {
+
+               set_pruneable(get_parent(curpath),false);
+               progress = true;
+           }
+       }
+    } while(progress);
+}
+
+/*
+ ** xfunc_shouldpull --
+ **    find clause with highest rank, and decide whether to pull it up
+ ** from child to parent.  Currently we only pullup secondary join clauses
+ ** that are in the pathclauseinfo.  Secondary hash and sort clauses are
+ ** left where they are.
+ **    If we find an expensive function but decide *not* to pull it up,
+ ** we'd better set the unpruneable flag.  -- JMH, 11/11/92
+ **
+ ** Returns:  0 if nothing left to pullup
+ **           XFUNC_LOCPRD if a local predicate is to be pulled up
+ **           XFUNC_JOINPRD if a secondary join predicate is to be pulled up
+ */
+int xfunc_shouldpull(Query* queryInfo,
+                    Path childpath,
+                    JoinPath parentpath,
+                    int whichchild,
+                    CInfo *maxcinfopt)  /* Out: pointer to clause to pullup */
+{
+    LispValue clauselist, tmplist; /* lists of clauses */
+    CInfo maxcinfo;            /* clause to pullup */
+    LispValue primjoinclause   /* primary join clause */
+       = xfunc_primary_join(parentpath);
+    Cost tmprank, maxrank = (-1 * MAXFLOAT); /* ranks of clauses */
+    Cost joinselec = 0;        /* selectivity of the join predicate */
+    Cost joincost = 0; /* join cost + primjoinclause cost */
+    int retval = XFUNC_LOCPRD;
+    
+    clauselist = get_locclauseinfo(childpath);
+    
+    if (clauselist != LispNil) {
+       /* find local predicate with maximum rank */
+       for (tmplist = clauselist,
+            maxcinfo = (CInfo) lfirst(tmplist),
+            maxrank = xfunc_rank(get_clause(maxcinfo));
+            tmplist != LispNil;
+            tmplist = lnext(tmplist)) {
+       
+       if ((tmprank = xfunc_rank(get_clause((CInfo)lfirst(tmplist)))) 
+           > maxrank) {
+           maxcinfo = (CInfo) lfirst(tmplist);
+           maxrank = tmprank;
+         }
+       }
+    }
+    
+    /* 
+     ** If child is a join path, and there are multiple join clauses,
+     ** see if any join clause has even higher rank than the highest
+     ** local predicate 
+     */
+    if (is_join(childpath) && xfunc_num_join_clauses((JoinPath)childpath) > 1)
+       for (tmplist = get_pathclauseinfo((JoinPath)childpath);
+            tmplist != LispNil;
+            tmplist = lnext(tmplist)) {
+           
+    if (tmplist != LispNil &&
+       (tmprank = xfunc_rank(get_clause((CInfo) lfirst(tmplist)))) 
+       > maxrank) {
+       maxcinfo = (CInfo) lfirst(tmplist);
+       maxrank = tmprank;
+       retval = XFUNC_JOINPRD;
+    }
+  }    
+    if (maxrank == (-1 * MAXFLOAT))    /* no expensive clauses */
+       return(0);
+    
+    /*
+     ** Pullup over join if clause is higher rank than join, or if 
+     ** join is nested loop and current path is inner child (note that
+     ** restrictions on the inner of a nested loop don't buy you anything --
+     ** you still have to scan the entire inner relation each time).
+     ** Note that the cost of a secondary join clause is only what's
+     ** calculated by xfunc_expense(), since the actual joining 
+     ** (i.e. the usual path_cost) is paid for by the primary join clause.
+     */
+    if (primjoinclause != LispNil) {
+       joinselec = compute_clause_selec(queryInfo, primjoinclause, LispNil);
+       joincost = xfunc_join_expense(parentpath, whichchild);
+       
+       if (XfuncMode == XFUNC_PULLALL ||
+           (XfuncMode != XFUNC_WAIT &&
+            ((joincost != 0 &&
+              (maxrank = xfunc_rank(get_clause(maxcinfo))) > 
+              ((joinselec - 1.0) / joincost))
+             || (joincost == 0 && joinselec < 1)
+             || (!is_join(childpath) 
+                 && (whichchild == INNER)
+                 && IsA(parentpath,JoinPath)
+                 && !IsA(parentpath,HashPath) 
+                 && !IsA(parentpath,MergePath))))) {
+
+           *maxcinfopt = maxcinfo;
+           return(retval);
+
+       }else if (maxrank != -(MAXFLOAT)) {
+           /* 
+            ** we've left an expensive restriction below a join.  Since
+            ** we may pullup this restriction in predmig.c, we'd best
+            ** set the Rel of this join to be unpruneable
+            */
+           set_pruneable(get_parent(parentpath), false);
+           /* and fall through */
+       }
+    }
+    return(0);
+}
+
+
+/*
+ ** xfunc_pullup --
+ **    move clause from child pathnode to parent pathnode.   This operation 
+ ** makes the child pathnode produce a larger relation than it used to.
+ ** This means that we must construct a new Rel just for the childpath,
+ ** although this Rel will not be added to the list of Rels to be joined up
+ ** in the query; it's merely a parent for the new childpath.
+ **    We also have to fix up the path costs of the child and parent.
+ **
+ ** Now returns a pointer to the new pulled-up CInfo. -- JMH, 11/18/92
+ */
+CInfo xfunc_pullup(Query* queryInfo,
+                  Path childpath,
+                  JoinPath parentpath,
+                  CInfo cinfo,   /* clause to pull up */
+                  int whichchild,/* whether child is INNER or OUTER of join */
+                  int clausetype)/* whether clause to pull is join or local */
+{
+    Path newkid;
+    Rel newrel;
+    Cost pulled_selec;
+    Cost cost;
+    CInfo newinfo;
+    
+    /* remove clause from childpath */
+    newkid = (Path)copyObject((Node)childpath);
+    if (clausetype == XFUNC_LOCPRD) {
+       set_locclauseinfo(newkid, 
+                         xfunc_LispRemove((LispValue)cinfo, 
+                                          (List)get_locclauseinfo(newkid)));
+    }else {
+       set_pathclauseinfo
+           ((JoinPath)newkid,
+            xfunc_LispRemove((LispValue)cinfo, 
+                             (List)get_pathclauseinfo((JoinPath)newkid)));
+    }
+    
+    /*
+     ** give the new child path its own Rel node that reflects the
+     ** lack of the pulled-up predicate
+     */
+    pulled_selec = compute_clause_selec(queryInfo,
+                                       get_clause(cinfo), LispNil);
+    xfunc_copyrel(get_parent(newkid), &newrel);
+    set_parent(newkid, newrel);
+    set_pathlist(newrel, lcons(newkid, NIL));
+    set_unorderedpath(newrel, (PathPtr)newkid);
+    set_cheapestpath(newrel, (PathPtr)newkid);
+    set_size(newrel, 
+            (Count)((Cost)get_size(get_parent(childpath)) / pulled_selec));
+    
+    /* 
+     ** fix up path cost of newkid.  To do this we subtract away all the
+     ** xfunc_costs of childpath, then recompute the xfunc_costs of newkid
+     */
+    cost = get_path_cost(newkid) - xfunc_get_path_cost(childpath);
+    Assert(cost >= 0);
+    set_path_cost(newkid, cost);
+    cost = get_path_cost(newkid) + xfunc_get_path_cost(newkid);
+    set_path_cost(newkid, cost);
+    
+    /*
+     ** We copy the cinfo, since it may appear in other plans, and we're going
+     ** to munge it.  -- JMH, 7/22/92
+     */
+    newinfo = (CInfo)copyObject((Node)cinfo);
+    
+    /* 
+     ** Fix all vars in the clause 
+     ** to point to the right varno and varattno in parentpath 
+     */
+    xfunc_fixvars(get_clause(newinfo), newrel, whichchild);
+    
+    /*  add clause to parentpath, and fix up its cost. */
+    set_locclauseinfo(parentpath, 
+                     lispCons((LispValue)newinfo, 
+                              (LispValue)get_locclauseinfo(parentpath)));
+    /* put new childpath into the path tree */
+    if (whichchild == INNER) {
+       set_innerjoinpath(parentpath, (pathPtr)newkid);
+    }else {
+       set_outerjoinpath(parentpath, (pathPtr)newkid);
+    }
+    
+    /* 
+     ** recompute parentpath cost from scratch -- the cost
+     ** of the join method has changed
+     */
+    cost = xfunc_total_path_cost(parentpath);
+    set_path_cost(parentpath, cost);
+    
+    return(newinfo);
+}
+
+/*
+ ** calculate (selectivity-1)/cost. 
+ */
+Cost xfunc_rank(Query *queryInfo,LispValue clause)
+{
+    Cost selec = compute_clause_selec(queryInfo, clause, LispNil); 
+    Cost cost = xfunc_expense(queryInfo,clause);
+    
+    if (cost == 0) 
+       if (selec > 1) return(MAXFLOAT);
+       else return(-(MAXFLOAT));
+    return((selec - 1)/cost);
+}
+
+/*
+ ** Find the "global" expense of a clause; i.e. the local expense divided
+ ** by the cardinalities of all the base relations of the query that are *not*
+ ** referenced in the clause.
+ */
+Cost xfunc_expense(Query* queryInfo, clause)
+     LispValue clause;
+{
+    Cost cost = xfunc_local_expense(clause);
+    
+    if (cost)
+       {
+           Count card = xfunc_card_unreferenced(queryInfo, clause, LispNil);
+           if (card)
+               cost /= card;
+       }
+    
+    return(cost);
+}
+
+/*
+ ** xfunc_join_expense --
+ **    Find global expense of a join clause
+ */
+Cost xfunc_join_expense(Query *queryInfo, JoinPath path, int whichchild)
+{
+    LispValue primjoinclause = xfunc_primary_join(path);
+    
+    /*
+     ** the second argument to xfunc_card_unreferenced reflects all the
+     ** relations involved in the join clause, i.e. all the relids in the Rel
+     ** of the join clause
+     */
+    Count card = 0;
+    Cost cost = xfunc_expense_per_tuple(path, whichchild);
+    
+    card = xfunc_card_unreferenced(queryInfo, 
+                                  primjoinclause, 
+                                  get_relids(get_parent(path)));
+    if (primjoinclause)
+       cost += xfunc_local_expense(primjoinclause);
+    
+    if (card) cost /= card;
+    
+    return(cost);
+}
+
+/*
+ ** Recursively find the per-tuple expense of a clause.  See
+ ** xfunc_func_expense for more discussion.
+ */
+Cost xfunc_local_expense(LispValue clause)
+{
+    Cost cost = 0;   /* running expense */
+    LispValue tmpclause;
+    
+    /* First handle the base case */
+    if (IsA(clause,Const) || IsA(clause,Var) || IsA(clause,Param)) 
+       return(0);
+    /* now other stuff */
+    else if (IsA(clause,Iter))
+       /* Too low. Should multiply by the expected number of iterations. */
+       return(xfunc_local_expense(get_iterexpr((Iter)clause)));
+    else if (IsA(clause,ArrayRef))
+       return(xfunc_local_expense(get_refexpr((ArrayRef)clause)));
+    else if (fast_is_clause(clause)) 
+       return(xfunc_func_expense((LispValue)get_op(clause), 
+                                 (LispValue)get_opargs(clause)));
+    else if (fast_is_funcclause(clause))
+       return(xfunc_func_expense((LispValue)get_function(clause),
+                                 (LispValue)get_funcargs(clause)));
+    else if (fast_not_clause(clause))
+       return(xfunc_local_expense(lsecond(clause)));
+    else if (fast_or_clause(clause)) {
+       /* find cost of evaluating each disjunct */
+       for (tmpclause = lnext(clause); tmpclause != LispNil; 
+            tmpclause = lnext(tmpclause))
+           cost += xfunc_local_expense(lfirst(tmpclause));
+       return(cost);
+    }else {
+       elog(WARN, "Clause node of undetermined type");
+       return(-1);
+    }
+}
+
+/*
+ ** xfunc_func_expense --
+ **    given a Func or Oper and its args, find its expense.
+ ** Note: in Stonebraker's SIGMOD '91 paper, he uses a more complicated metric
+ ** than the one here.  We can ignore the expected number of tuples for
+ ** our calculations; we just need the per-tuple expense.  But he also
+ ** proposes components to take into account the costs of accessing disk and
+ ** archive.  We didn't adopt that scheme here; eventually the vacuum
+ ** cleaner should be able to tell us what percentage of bytes to find on
+ ** which storage level, and that should be multiplied in appropriately
+ ** in the cost function below.  Right now we don't model the cost of
+ ** accessing secondary or tertiary storage, since we don't have sufficient
+ ** stats to do it right.
+ */
+Cost xfunc_func_expense(LispValue node, LispValue args)
+{
+    HeapTuple tupl;    /* the pg_proc tuple for each function */
+    Form_pg_proc proc; /* a data structure to hold the pg_proc tuple */
+    int width = 0;     /* byte width of the field referenced by each clause */
+    RegProcedure funcid;    /* ID of function associate with node */
+    Cost cost = 0;   /* running expense */
+    LispValue tmpclause;
+    LispValue operand; /* one operand of an operator */
+    
+    if (IsA(node,Oper)) {
+       /* don't trust the opid in the Oper node.  Use the opno. */
+       if (!(funcid = get_opcode(get_opno((Oper)node))))
+           elog(WARN, "Oper's function is undefined");
+    }else {
+       funcid = get_funcid((Func)node);
+    }
+    
+    /* look up tuple in cache */
+    tupl = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid),0,0,0);
+    if (!HeapTupleIsValid(tupl))
+       elog(WARN, "Cache lookup failed for procedure %d", funcid);
+    proc = (Form_pg_proc) GETSTRUCT(tupl);
+    
+    /* 
+     ** if it's a Postquel function, its cost is stored in the
+     ** associated plan.
+     */
+    if (proc->prolang == SQLlanguageId) {
+       LispValue tmpplan;
+       List planlist;
+       
+       if (IsA(node,Oper) || get_func_planlist((Func)node) == LispNil) {
+           Oid *argOidVect; /* vector of argtypes */
+           char *pq_src;       /* text of PQ function */
+           int nargs;  /* num args to PQ function */
+           QueryTreeList *queryTree_list;      /* dummy variable */
+           
+           /* 
+            ** plan the function, storing it in the Func node for later 
+            ** use by the executor.  
+            */
+           pq_src = (char *) textout(&(proc->prosrc));
+           nargs = proc->pronargs;
+           if (nargs > 0)
+               argOidVect = proc->proargtypes;
+           planlist = (List)pg_plan(pq_src, argOidVect, nargs, 
+                                    &parseTree_list, None);
+           if (IsA(node,Func))
+               set_func_planlist((Func)node, planlist);
+           
+       }else {/* plan has been cached inside the Func node already */
+           planlist = get_func_planlist((Func)node);
+       }
+       
+       /*
+        ** Return the sum of the costs of the plans (the PQ function
+        ** may have many queries in its body).
+        */
+       foreach(tmpplan, planlist)
+           cost += get_cost((Plan)lfirst(tmpplan));
+       return(cost);
+    }else {            /* it's a C function */
+       /*
+        **  find the cost of evaluating the function's arguments
+        **  and the width of the operands
+        */
+       for (tmpclause = args; tmpclause != LispNil; 
+            tmpclause = lnext(tmpclause)) {
+           
+           if ((operand = lfirst(tmpclause)) != LispNil) {
+               cost += xfunc_local_expense(operand);
+               width += xfunc_width(operand);
+           }
+       }
+       
+       /* 
+        ** when stats become available, add in cost of accessing secondary
+        ** and tertiary storage here.
+        */
+       return(cost +  
+              (Cost)proc->propercall_cpu + 
+              (Cost)proc->properbyte_cpu * (Cost)proc->probyte_pct/100.00 * 
+              (Cost)width
+              /* 
+               * Pct_of_obj_in_mem
+               DISK_COST * proc->probyte_pct/100.00 * width
+               * Pct_of_obj_on_disk +
+               ARCH_COST * proc->probyte_pct/100.00 * width
+               * Pct_of_obj_on_arch 
+               */
+              );
+    }
+}
+
+/* 
+ ** xfunc_width --
+ **    recursively find the width of a expression
+ */
+
+int xfunc_width(LispValue clause)
+{
+    Relation rd;         /* Relation Descriptor */
+    HeapTuple tupl;      /* structure to hold a cached tuple */
+    TypeTupleForm type;   /* structure to hold a type tuple */
+    int retval = 0;
+    
+    if (IsA(clause,Const)) {
+       /* base case: width is the width of this constant */
+       retval = get_constlen((Const) clause);
+       goto exit;
+    }else if (IsA(clause,ArrayRef)) {
+       /* base case: width is width of the refelem within the array */
+       retval = get_refelemlength((ArrayRef)clause);
+       goto exit;
+    }else if (IsA(clause,Var)) {
+       /* base case: width is width of this attribute */
+       tupl = SearchSysCacheTuple(TYPOID, 
+                                  PointerGetDatum(get_vartype((Var)clause)),
+                                  0,0,0);
+       if (!HeapTupleIsValid(tupl))
+           elog(WARN, "Cache lookup failed for type %d", 
+                get_vartype((Var)clause));
+       type = (TypeTupleForm) GETSTRUCT(tupl);
+       if (get_varattno((Var)clause) == 0) {
+           /* clause is a tuple.  Get its width */
+           rd = heap_open(type->typrelid);
+           retval = xfunc_tuple_width(rd);
+           heap_close(rd);
+       }else {
+           /* attribute is a base type */
+           retval = type->typlen;
+       }
+       goto exit;
+    }else if (IsA(clause,Param)) {
+       if (typeid_get_relid(get_paramtype((Param)clause))) {
+           /* Param node returns a tuple.  Find its width */
+           rd = heap_open(typeid_get_relid(get_paramtype((Param)clause)));
+           retval = xfunc_tuple_width(rd);
+           heap_close(rd);
+       }else if (get_param_tlist((Param)clause) != LispNil) {
+           /* Param node projects a complex type */
+           Assert(length(get_param_tlist((Param)clause)) == 1); /* sanity */
+           retval = 
+               xfunc_width((LispValue)
+                           get_expr(lfirst(get_param_tlist((Param)clause))));
+       }else {
+           /* Param node returns a base type */
+           retval = tlen(get_id_type(get_paramtype((Param)clause)));
+       }
+       goto exit;       
+    }else if (IsA(clause,Iter)) {
+       /* 
+        ** An Iter returns a setof things, so return the width of a single
+        ** thing.
+        ** Note:  THIS MAY NOT WORK RIGHT WHEN AGGS GET FIXED,
+        ** SINCE AGG FUNCTIONS CHEW ON THE WHOLE SETOF THINGS!!!!
+        **    This whole Iter business is bogus, anyway.
+        */
+       retval = xfunc_width(get_iterexpr((Iter)clause));
+       goto exit;
+    }else if (fast_is_clause(clause)) {
+       /*
+        ** get function associated with this Oper, and treat this as 
+        ** a Func 
+        */
+       tupl = SearchSysCacheTuple(OPROID, 
+                                  ObjectIdGetDatum(get_opno((Oper)get_op(clause))),
+                                  0,0,0);
+       if (!HeapTupleIsValid(tupl))
+           elog(WARN, "Cache lookup failed for procedure %d", 
+                get_opno((Oper)get_op(clause)));
+       return(xfunc_func_width
+              ((RegProcedure)(((OperatorTupleForm)(GETSTRUCT(tupl)))->oprcode), 
+               (LispValue)get_opargs(clause)));
+    }else if (fast_is_funcclause(clause)) {
+       Func func = (Func)get_function(clause);
+       if (get_func_tlist(func) != LispNil) {
+           /* this function has a projection on it.  Get the length
+              of the projected attribute */
+           Assert(length(get_func_tlist(func)) == 1);   /* sanity */
+           retval = 
+               xfunc_width((LispValue)
+                           get_expr(lfirst(get_func_tlist(func))));
+           goto exit;
+       }else {
+           return(xfunc_func_width((RegProcedure)get_funcid(func),
+                                   (LispValue)get_funcargs(clause)));
+       }
+    }else {
+       elog(WARN, "Clause node of undetermined type");
+       return(-1);
+    }
+    
+ exit:
+    if (retval == -1)
+       retval = VARLEN_DEFAULT;
+    return(retval);
+}
+
+/*
+ ** xfunc_card_unreferenced:
+ **   find all relations not referenced in clause, and multiply their 
+ ** cardinalities.  Ignore relation of cardinality 0.
+ ** User may pass in referenced list, if they know it (useful
+ ** for joins).
+ */
+static Count 
+xfunc_card_unreferenced(Query *queryInfo, 
+                             LispValue clause, Relid referenced)
+{
+    Relid unreferenced, allrelids = LispNil;
+    LispValue temp;
+    
+    /* find all relids of base relations referenced in query */
+    foreach (temp,queryInfo->base_relation_list_)
+       {
+           Assert(lnext(get_relids((Rel)lfirst(temp))) == LispNil);
+           allrelids = lappend(allrelids,
+                               lfirst(get_relids((Rel)lfirst(temp))));
+       }
+    
+    /* find all relids referenced in query but not in clause */
+    if (!referenced)
+       referenced = xfunc_find_references(clause);
+    unreferenced = set_difference(allrelids, referenced);
+    
+    return(xfunc_card_product(unreferenced));
+}
+
+/*
+ ** xfunc_card_product 
+ **   multiple together cardinalities of a list relations.
+ */
+Count xfunc_card_product(Query *queryInfo, Relid relids)
+{
+    LispValue cinfonode;
+    LispValue temp;
+    Rel currel;
+    Cost tuples;
+    Count retval = 0;
+    
+    foreach(temp,relids) {
+       currel = get_rel(lfirst(temp));
+       tuples = get_tuples(currel);
+       
+       if (tuples)  { /* not of cardinality 0 */
+           /* factor in the selectivity of all zero-cost clauses */
+           foreach (cinfonode, get_clauseinfo(currel)) {
+               if (!xfunc_expense(queryInfo,get_clause((CInfo)lfirst(cinfonode))))
+                   tuples *= 
+                       compute_clause_selec(queryInfo,
+                                            get_clause((CInfo)lfirst(cinfonode)),
+                                            LispNil);
+           }
+                   
+           if (retval == 0) retval = tuples;
+           else retval *= tuples;
+       }
+    }
+    if (retval == 0) retval = 1;  /* saves caller from dividing by zero */
+    return(retval);
+}
+
+
+/*
+ ** xfunc_find_references:
+ **   Traverse a clause and find all relids referenced in the clause.
+ */
+List xfunc_find_references(LispValue clause)
+{
+    List retval = (List)LispNil;
+    LispValue tmpclause;
+    
+    /* Base cases */
+    if (IsA(clause,Var)) 
+       return(lispCons(lfirst(get_varid((Var)clause)), LispNil));
+    else if (IsA(clause,Const) || IsA(clause,Param))
+       return((List)LispNil);
+    
+    /* recursion */
+    else if (IsA(clause,Iter))
+       /* Too low. Should multiply by the expected number of iterations. maybe */
+       return(xfunc_find_references(get_iterexpr((Iter)clause)));
+    else if (IsA(clause,ArrayRef))
+       return(xfunc_find_references(get_refexpr((ArrayRef)clause)));
+    else if (fast_is_clause(clause)) {
+       /* string together result of all operands of Oper */
+       for (tmpclause = (LispValue)get_opargs(clause); tmpclause != LispNil; 
+            tmpclause = lnext(tmpclause))
+           retval = nconc(retval, xfunc_find_references(lfirst(tmpclause)));
+       return(retval);
+    }else if (fast_is_funcclause(clause)) {
+       /* string together result of all args of Func */
+       for (tmpclause = (LispValue)get_funcargs(clause); 
+            tmpclause != LispNil; 
+            tmpclause = lnext(tmpclause))
+           retval = nconc(retval, xfunc_find_references(lfirst(tmpclause)));
+       return(retval);
+    }else if (fast_not_clause(clause))
+       return(xfunc_find_references(lsecond(clause)));
+    else if (fast_or_clause(clause)) {
+       /* string together result of all operands of OR */
+       for (tmpclause = lnext(clause); tmpclause != LispNil; 
+            tmpclause = lnext(tmpclause))
+           retval = nconc(retval, xfunc_find_references(lfirst(tmpclause)));
+       return(retval);
+    }else {
+       elog(WARN, "Clause node of undetermined type");
+       return((List)LispNil);
+    }
+}
+
+/*
+ ** xfunc_primary_join:
+ **   Find the primary join clause: for Hash and Merge Joins, this is the
+ ** min rank Hash or Merge clause, while for Nested Loop it's the
+ ** min rank pathclause
+ */
+LispValue xfunc_primary_join(JoinPath pathnode)
+{
+    LispValue joinclauselist = get_pathclauseinfo(pathnode);
+    CInfo mincinfo;
+    LispValue tmplist;
+    LispValue minclause = LispNil;
+    Cost minrank, tmprank;
+    
+    if (IsA(pathnode,MergePath)) 
+       {
+           for(tmplist = get_path_mergeclauses((MergePath)pathnode),
+               minclause = lfirst(tmplist),
+               minrank = xfunc_rank(minclause);
+               tmplist != LispNil;
+               tmplist = lnext(tmplist))
+               if ((tmprank = xfunc_rank(lfirst(tmplist)))
+                   < minrank)
+                   {
+                       minrank = tmprank;
+                       minclause = lfirst(tmplist);
+                   }
+           return(minclause);
+       }
+    else if (IsA(pathnode,HashPath)) 
+       {
+           for(tmplist = get_path_hashclauses((HashPath)pathnode),
+               minclause = lfirst(tmplist),
+               minrank = xfunc_rank(minclause);
+               tmplist != LispNil;
+               tmplist = lnext(tmplist))
+               if ((tmprank = xfunc_rank(lfirst(tmplist)))
+                   < minrank)
+                   {
+                       minrank = tmprank;
+                       minclause = lfirst(tmplist);
+                   }
+           return(minclause);
+       }
+    
+    /* if we drop through, it's nested loop join */
+    if (joinclauselist == LispNil)
+       return(LispNil);
+    
+    for(tmplist = joinclauselist, mincinfo = (CInfo) lfirst(joinclauselist),
+       minrank = xfunc_rank(get_clause((CInfo) lfirst(tmplist)));
+       tmplist != LispNil;
+       tmplist = lnext(tmplist))
+       if ((tmprank = xfunc_rank(get_clause((CInfo) lfirst(tmplist))))
+           < minrank)
+           {
+               minrank = tmprank;
+               mincinfo = (CInfo) lfirst(tmplist);
+           }
+    return((LispValue)get_clause(mincinfo));
+}
+
+/*
+ ** xfunc_get_path_cost
+ **   get the expensive function costs of the path
+ */
+Cost xfunc_get_path_cost(Query *queryInfo, Path pathnode)
+{
+    Cost cost = 0;
+    LispValue tmplist;
+    Cost selec = 1.0;
+    
+    /* 
+     ** first add in the expensive local function costs.
+     ** We ensure that the clauses are sorted by rank, so that we
+     ** know (via selectivities) the number of tuples that will be checked
+     ** by each function.  If we're not doing any optimization of expensive
+     ** functions, we don't sort.
+     */
+    if (XfuncMode != XFUNC_OFF)
+       set_locclauseinfo(pathnode, lisp_qsort(get_locclauseinfo(pathnode),
+                                              xfunc_cinfo_compare));
+    for(tmplist = get_locclauseinfo(pathnode), selec = 1.0;
+       tmplist != LispNil;
+       tmplist = lnext(tmplist))
+       {
+           cost += (Cost)(xfunc_local_expense(get_clause((CInfo)lfirst(tmplist)))
+                          * (Cost)get_tuples(get_parent(pathnode)) * selec);
+           selec *= compute_clause_selec(queryInfo,
+                                         get_clause((CInfo)lfirst(tmplist)), 
+                                         LispNil);
+       }
+    
+    /* 
+     ** Now add in any node-specific expensive function costs.
+     ** Again, we must ensure that the clauses are sorted by rank.
+     */
+    if (IsA(pathnode,JoinPath))
+       {
+           if (XfuncMode != XFUNC_OFF)
+               set_pathclauseinfo((JoinPath)pathnode, lisp_qsort
+                                  (get_pathclauseinfo((JoinPath)pathnode),
+                                   xfunc_cinfo_compare));
+           for(tmplist = get_pathclauseinfo((JoinPath)pathnode), selec = 1.0;
+               tmplist != LispNil;
+               tmplist = lnext(tmplist))
+               {
+                   cost += (Cost)(xfunc_local_expense(get_clause((CInfo)lfirst(tmplist)))
+                                  * (Cost)get_tuples(get_parent(pathnode)) * selec);
+                   selec *= compute_clause_selec(queryInfo,
+                                                 get_clause((CInfo)lfirst(tmplist)),
+                                                 LispNil);
+               }
+       }
+    if (IsA(pathnode,HashPath))
+       {
+           if (XfuncMode != XFUNC_OFF)
+               set_path_hashclauses
+                   ((HashPath)pathnode, 
+                    lisp_qsort(get_path_hashclauses((HashPath)pathnode),
+                               xfunc_clause_compare));
+           for(tmplist = get_path_hashclauses((HashPath)pathnode), selec = 1.0;
+               tmplist != LispNil;
+               tmplist = lnext(tmplist))
+               {
+                   cost += (Cost)(xfunc_local_expense(lfirst(tmplist))
+                                  * (Cost)get_tuples(get_parent(pathnode)) * selec);
+                   selec *= compute_clause_selec(queryInfo, 
+                                                 lfirst(tmplist), LispNil);
+               }
+       }
+    if (IsA(pathnode,MergePath))
+       {
+           if (XfuncMode != XFUNC_OFF)
+               set_path_mergeclauses
+                   ((MergePath)pathnode, 
+                    lisp_qsort(get_path_mergeclauses((MergePath)pathnode),
+                               xfunc_clause_compare));
+           for(tmplist = get_path_mergeclauses((MergePath)pathnode), selec = 1.0;
+               tmplist != LispNil;
+               tmplist = lnext(tmplist))
+               {
+                   cost += (Cost)(xfunc_local_expense(lfirst(tmplist))
+                                  * (Cost)get_tuples(get_parent(pathnode)) * selec);
+                   selec *= compute_clause_selec(queryInfo,
+                                                 lfirst(tmplist), LispNil);
+               }
+       }
+    Assert(cost >= 0);
+    return(cost);
+}
+
+/*
+ ** Recalculate the cost of a path node.  This includes the basic cost of the 
+ ** node, as well as the cost of its expensive functions.
+ ** We need to do this to the parent after pulling a clause from a child into a
+ ** parent.  Thus we should only be calling this function on JoinPaths.
+ */
+Cost xfunc_total_path_cost(JoinPath pathnode)
+{
+    Cost cost = xfunc_get_path_cost((Path)pathnode);
+    
+    Assert(IsA(pathnode,JoinPath));
+    if (IsA(pathnode,MergePath))
+       {
+           MergePath mrgnode = (MergePath)pathnode;
+           cost += cost_mergesort(get_path_cost((Path)get_outerjoinpath(mrgnode)),
+                                  get_path_cost((Path)get_innerjoinpath(mrgnode)),
+                                  get_outersortkeys(mrgnode),
+                                  get_innersortkeys(mrgnode),
+                                  get_tuples(get_parent((Path)get_outerjoinpath
+                                                        (mrgnode))),
+                                  get_tuples(get_parent((Path)get_innerjoinpath
+                                                        (mrgnode))),
+                                  get_width(get_parent((Path)get_outerjoinpath
+                                                       (mrgnode))),
+                                  get_width(get_parent((Path)get_innerjoinpath
+                                                       (mrgnode))));
+           Assert(cost >= 0);
+           return(cost);
+       }
+    else if (IsA(pathnode,HashPath))
+       {
+           HashPath hashnode = (HashPath)pathnode;
+           cost += cost_hashjoin(get_path_cost((Path)get_outerjoinpath(hashnode)),
+                                 get_path_cost((Path)get_innerjoinpath(hashnode)),
+                                 get_outerhashkeys(hashnode),
+                                 get_innerhashkeys(hashnode),
+                                 get_tuples(get_parent((Path)get_outerjoinpath
+                                                       (hashnode))),
+                                 get_tuples(get_parent((Path)get_innerjoinpath
+                                                       (hashnode))),
+                                 get_width(get_parent((Path)get_outerjoinpath
+                                                      (hashnode))),
+                                 get_width(get_parent((Path)get_innerjoinpath
+                                                      (hashnode))));
+           Assert (cost >= 0);
+           return(cost);
+       }
+    else /* Nested Loop Join */
+       {
+           cost += cost_nestloop(get_path_cost((Path)get_outerjoinpath(pathnode)),
+                                 get_path_cost((Path)get_innerjoinpath(pathnode)),
+                                 get_tuples(get_parent((Path)get_outerjoinpath
+                                                       (pathnode))),
+                                 get_tuples(get_parent((Path)get_innerjoinpath
+                                                       (pathnode))),
+                                 get_pages(get_parent((Path)get_outerjoinpath
+                                                      (pathnode))),
+                                 IsA(get_innerjoinpath(pathnode),IndexPath));
+           Assert(cost >= 0);
+           return(cost);
+       }
+}
+
+
+/*
+ ** xfunc_expense_per_tuple --
+ **    return the expense of the join *per-tuple* of the input relation.
+ ** The cost model here is that a join costs
+ **     k*card(outer)*card(inner) + l*card(outer) + m*card(inner) + n
+ **
+ ** We treat the l and m terms by considering them to be like restrictions
+ ** constrained to be right under the join.  Thus the cost per inner and
+ ** cost per outer of the join is different, reflecting these virtual nodes.
+ **
+ ** The cost per tuple of outer is k + l/referenced(inner).  Cost per tuple
+ ** of inner is k + m/referenced(outer).
+ ** The constants k, l, m and n depend on the join method.  Measures here are
+ ** based on the costs in costsize.c, with fudging for HashJoin and Sorts to
+ ** make it fit our model (the 'q' in HashJoin results in a
+ ** card(outer)/card(inner) term, and sorting results in a log term.
+ */
+Cost xfunc_expense_per_tuple(JoinPath joinnode, int whichchild)
+{
+    Rel outerrel = get_parent((Path)get_outerjoinpath(joinnode));
+    Rel innerrel = get_parent((Path)get_innerjoinpath(joinnode));
+    Count outerwidth = get_width(outerrel);
+    Count outers_per_page = ceil(BLCKSZ/(outerwidth + sizeof(HeapTupleData)));
+    
+    if (IsA(joinnode,HashPath))
+       {
+           if (whichchild == INNER)
+               return((1 + _CPU_PAGE_WEIGHT_)*outers_per_page/NBuffers);
+           else 
+               return(((1 + _CPU_PAGE_WEIGHT_)*outers_per_page/NBuffers)
+                      + _CPU_PAGE_WEIGHT_
+                      / xfunc_card_product(get_relids(innerrel)));
+       }
+    else if (IsA(joinnode,MergePath))
+       {
+           /* assumes sort exists, and costs one (I/O + CPU) per tuple */
+           if (whichchild == INNER)
+               return((2*_CPU_PAGE_WEIGHT_ + 1)
+                      / xfunc_card_product(get_relids(outerrel)));
+           else
+               return((2*_CPU_PAGE_WEIGHT_ + 1)
+                      / xfunc_card_product(get_relids(innerrel)));
+       }
+    else /* nestloop */
+       {
+           Assert(IsA(joinnode,JoinPath));
+           return(_CPU_PAGE_WEIGHT_);
+       }
+}
+
+/*
+ ** xfunc_fixvars --
+ ** After pulling up a clause, we must walk its expression tree, fixing Var 
+ ** nodes to point to the correct varno (either INNER or OUTER, depending
+ ** on which child the clause was pulled from), and the right varattno in the 
+ ** target list of the child's former relation.  If the target list of the
+ ** child Rel does not contain the attribute we need, we add it.
+ */
+void xfunc_fixvars(LispValue clause,  /* clause being pulled up */
+                  Rel rel,           /* rel it's being pulled from */
+                  int varno)         /* whether rel is INNER or OUTER of join */
+{
+    LispValue tmpclause;  /* temporary variable */
+    TargetEntry *tle;              /* tlist member corresponding to var */
+    
+    
+    if (IsA(clause,Const) || IsA(clause,Param)) return;
+    else if (IsA(clause,Var))
+       {
+           /* here's the meat */
+           tle = tlistentry_member((Var)clause, get_targetlist(rel));
+           if (tle == LispNil)
+               {
+                   /* 
+                    ** The attribute we need is not in the target list,
+                    ** so we have to add it.
+                    **
+                    */
+                   add_tl_element(rel, (Var)clause);
+                   tle = tlistentry_member((Var)clause, get_targetlist(rel));
+               }
+           set_varno(((Var)clause), varno);
+           set_varattno(((Var)clause), get_resno(get_resdom(get_entry(tle))));
+       }
+    else if (IsA(clause,Iter))
+       xfunc_fixvars(get_iterexpr((Iter)clause), rel, varno);
+    else if (fast_is_clause(clause))
+       {
+           xfunc_fixvars(lfirst(lnext(clause)), rel, varno);
+           xfunc_fixvars(lfirst(lnext(lnext(clause))), rel, varno);
+       }
+    else if (fast_is_funcclause(clause))
+       for (tmpclause = lnext(clause); tmpclause != LispNil; 
+            tmpclause = lnext(tmpclause))
+           xfunc_fixvars(lfirst(tmpclause), rel, varno);
+    else if (fast_not_clause(clause))
+       xfunc_fixvars(lsecond(clause), rel, varno);
+    else if (fast_or_clause(clause))
+       for (tmpclause = lnext(clause); tmpclause != LispNil; 
+            tmpclause = lnext(tmpclause))
+           xfunc_fixvars(lfirst(tmpclause), rel, varno);
+    else
+       {
+           elog(WARN, "Clause node of undetermined type");
+       }
+}
+
+
+/*
+ ** Comparison function for lisp_qsort() on a list of CInfo's.
+ ** arg1 and arg2 should really be of type (CInfo *).  
+ */
+int xfunc_cinfo_compare(void *arg1, void *arg2)
+{
+    CInfo info1 = *(CInfo *) arg1;
+    CInfo info2 = *(CInfo *) arg2;
+    
+    LispValue clause1 = (LispValue) get_clause(info1),
+    clause2 = (LispValue) get_clause(info2);
+    
+    return(xfunc_clause_compare((void *) &clause1, (void *) &clause2));
+}
+
+/*
+ ** xfunc_clause_compare: comparison function for lisp_qsort() that compares two 
+ ** clauses based on expense/(1 - selectivity)
+ ** arg1 and arg2 are really pointers to clauses.
+ */
+int xfunc_clause_compare(void *arg1, void *arg2)
+{
+    LispValue clause1 = *(LispValue *) arg1;
+    LispValue clause2 = *(LispValue *) arg2;
+    Cost rank1,             /* total xfunc rank of clause1 */ 
+    rank2;             /* total xfunc rank of clause2 */
+    
+    rank1 = xfunc_rank(clause1);
+    rank2 = xfunc_rank(clause2);
+    
+    if ( rank1 < rank2) 
+       return(-1);
+    else if (rank1 == rank2)
+       return(0);
+    else return(1);
+}
+
+/*
+ ** xfunc_disjunct_sort --
+ **   given a list of clauses, for each clause sort the disjuncts by cost
+ **   (this assumes the predicates have been converted to Conjunctive NF)
+ **   Modifies the clause list!
+ */
+void xfunc_disjunct_sort(LispValue clause_list)
+{
+    LispValue temp;
+    
+    foreach(temp, clause_list)
+       if(or_clause(lfirst(temp)))
+           lnext(lfirst(temp)) =
+               lisp_qsort(lnext(lfirst(temp)), xfunc_disjunct_compare);
+}
+
+
+/*
+ ** xfunc_disjunct_compare: comparison function for qsort() that compares two 
+ ** disjuncts based on cost/selec.
+ ** arg1 and arg2 are really pointers to disjuncts
+ */
+int xfunc_disjunct_compare(Query* queryInfo, void *arg1, void *arg2)
+{
+    LispValue disjunct1 = *(LispValue *) arg1;
+    LispValue disjunct2 = *(LispValue *) arg2;
+    Cost cost1,  /* total cost of disjunct1 */ 
+    cost2,  /* total cost of disjunct2 */
+    selec1,
+    selec2;
+    Cost rank1, rank2;
+    
+    cost1 = xfunc_expense(queryInfo, disjunct1);
+    cost2 = xfunc_expense(queryInfo, disjunct2);
+    selec1 = compute_clause_selec(queryInfo,
+                                 disjunct1, LispNil);
+    selec2 = compute_clause_selec(queryInfo,
+                                 disjunct2, LispNil);
+    
+    if (selec1 == 0)
+       rank1 = MAXFLOAT;
+    else if (cost1 == 0)
+       rank1 = 0;
+    else
+       rank1 = cost1/selec1;
+    
+    if (selec2 == 0)
+       rank2 = MAXFLOAT;
+    else if (cost2 == 0)
+       rank2 = 0;
+    else
+       rank2 = cost2/selec2;
+    
+    if ( rank1 < rank2) 
+       return(-1);
+    else if (rank1 == rank2)
+       return(0);
+    else return(1);
+}
+
+/* ------------------------ UTILITY FUNCTIONS ------------------------------- */
+/*
+ ** xfunc_func_width --
+ **    Given a function OID and operands, find the width of the return value.
+ */
+int xfunc_func_width(RegProcedure funcid, LispValue args)
+{
+    Relation rd;         /* Relation Descriptor */
+    HeapTuple tupl;      /* structure to hold a cached tuple */
+    Form_pg_proc proc;   /* structure to hold the pg_proc tuple */
+    TypeTupleForm type;   /* structure to hold the pg_type tuple */
+    LispValue tmpclause; 
+    int retval;
+    
+    /* lookup function and find its return type */
+    Assert(RegProcedureIsValid(funcid));
+    tupl = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid), 0,0,0);
+    if (!HeapTupleIsValid(tupl))
+       elog(WARN, "Cache lookup failed for procedure %d", funcid);
+    proc = (Form_pg_proc) GETSTRUCT(tupl);
+    
+    /* if function returns a tuple, get the width of that */
+    if (typeid_get_relid(proc->prorettype))
+       {
+           rd = heap_open(typeid_get_relid(proc->prorettype));
+           retval = xfunc_tuple_width(rd);
+           heap_close(rd);
+           goto exit;
+       }
+    else /* function returns a base type */
+       {
+           tupl = SearchSysCacheTuple(TYPOID,
+                                      ObjectIdGetDatum(proc->prorettype),
+                                      0,0,0);
+           if (!HeapTupleIsValid(tupl))
+               elog(WARN, "Cache lookup failed for type %d", proc->prorettype);
+           type = (TypeTupleForm) GETSTRUCT(tupl);
+           /* if the type length is known, return that */
+           if (type->typlen != -1)
+               {
+                   retval = type->typlen;
+                   goto exit;
+               }
+           else /* estimate the return size */
+               {
+                   /* find width of the function's arguments */
+                   for (tmpclause = args; tmpclause != LispNil; 
+                        tmpclause = lnext(tmpclause))
+                       retval += xfunc_width(lfirst(tmpclause));
+                   /* multiply by outin_ratio */
+                   retval = (int)(proc->prooutin_ratio/100.0 * retval);
+                   goto exit;
+               }
+       }
+ exit:
+    return(retval);
+}
+
+/*
+ ** xfunc_tuple_width --
+ **     Return the sum of the lengths of all the attributes of a given relation
+ */
+int xfunc_tuple_width(Relation rd)
+{
+    int i;
+    int retval = 0;
+    TupleDesc tdesc = RelationGetTupleDescriptor(rd);
+    
+    for (i = 0; i < tdesc->natts; i++)
+       {
+           if (tdesc->attrs[i]->attlen != -1)
+               retval += tdesc->attrs[i]->attlen;
+           else retval += VARLEN_DEFAULT;
+       }
+    
+    return(retval);
+}
+
+/*
+ ** xfunc_num_join_clauses --
+ **   Find the number of join clauses associated with this join path
+ */
+int xfunc_num_join_clauses(JoinPath path)
+{
+    int num = length(get_pathclauseinfo(path));
+    if (IsA(path,MergePath))
+       return(num + length(get_path_mergeclauses((MergePath)path)));
+    else if (IsA(path,HashPath))
+       return(num + length(get_path_hashclauses((HashPath)path)));
+    else return(num);
+}
+
+/*
+ ** xfunc_LispRemove --
+ **   Just like LispRemove, but it whines if the item to be removed ain't there
+ */
+LispValue xfunc_LispRemove(LispValue foo, List bar) 
+{
+    LispValue temp = LispNil;
+    LispValue result = LispNil;
+    int sanity = false;
+    
+    for (temp = bar; !null(temp); temp = lnext(temp))
+       if (! equal((Node)(foo),(Node)(lfirst(temp))) ) 
+           {
+               result = lappend(result,lfirst(temp));
+           }
+       else sanity = true; /* found a matching item to remove! */
+    
+    if (!sanity)
+       elog(WARN, "xfunc_LispRemove: didn't find a match!");
+    
+    return(result);
+}
+
+#define Node_Copy(a, b, c, d) \
+    if (NodeCopy((Node)((a)->d), (Node*)&((b)->d), c) != true) { \
+                                                       return false; \
+                                                       } 
+
+/*
+ ** xfunc_copyrel --
+ **   Just like _copyRel, but doesn't copy the paths
+ */
+bool xfunc_copyrel(Rel from, Rel *to)
+{
+    Rel        newnode;
+    Pointer (*alloc)() = palloc;
+    
+    /*  COPY_CHECKARGS() */
+    if (to == NULL) 
+       { 
+           return false;
+       } 
+    
+    /* COPY_CHECKNULL() */
+    if (from == NULL) 
+       {
+           (*to) = NULL;
+           return true;
+       } 
+    
+    /* COPY_NEW(c) */
+    newnode =  (Rel)(*alloc)(classSize(Rel));
+    if (newnode == NULL) 
+       { 
+           return false;
+       } 
+    
+    /* ----------------
+     * copy node superclass fields
+     * ----------------
+     */
+    CopyNodeFields((Node)from, (Node)newnode, alloc);
+    
+    /* ----------------
+     * copy remainder of node
+     * ----------------
+     */
+    Node_Copy(from, newnode, alloc, relids);
+    
+    newnode->indexed = from->indexed;
+    newnode->pages =   from->pages;
+    newnode->tuples =  from->tuples;
+    newnode->size =    from->size;
+    newnode->width =   from->width;
+    
+    Node_Copy(from, newnode, alloc, targetlist);
+    /* No!!!!    Node_Copy(from, newnode, alloc, pathlist);  
+       Node_Copy(from, newnode, alloc, unorderedpath);
+       Node_Copy(from, newnode, alloc, cheapestpath);  */
+#if 0  /* can't use Node_copy now. 2/95 -ay */
+    Node_Copy(from, newnode, alloc, classlist);
+    Node_Copy(from, newnode, alloc, indexkeys);
+    Node_Copy(from, newnode, alloc, ordering);
+#endif
+    Node_Copy(from, newnode, alloc, clauseinfo);
+    Node_Copy(from, newnode, alloc, joininfo);
+    Node_Copy(from, newnode, alloc, innerjoin);
+    Node_Copy(from, newnode, alloc, superrels);
+    
+    (*to) = newnode;
+    return true;
+}
diff --git a/src/backend/optimizer/pathnode.h b/src/backend/optimizer/pathnode.h
new file mode 100644 (file)
index 0000000..90ead0d
--- /dev/null
@@ -0,0 +1,50 @@
+/*-------------------------------------------------------------------------
+ *
+ * pathnode.h--
+ *    prototypes for pathnode.c, indexnode.c, relnode.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PATHNODE_H
+#define PATHNODE_H
+
+/*
+ * prototypes for pathnode.c
+ */
+extern bool path_is_cheaper(Path *path1, Path *path2);
+extern Path *set_cheapest(Rel *parent_rel, List *pathlist);
+extern List *add_pathlist(Rel *parent_rel, List *unique_paths,
+                             List *new_paths);
+extern Path *create_seqscan_path(Rel *rel);
+extern IndexPath *create_index_path(Query *root, Rel *rel, Rel *index,
+                        List *restriction_clauses, bool is_join_scan);
+extern JoinPath *create_nestloop_path(Rel *joinrel, Rel *outer_rel,
+                        Path *outer_path, Path *inner_path, List *keys);
+extern MergePath *create_mergesort_path(Rel *joinrel, int outersize,
+     int innersize, int outerwidth, int innerwidth, Path *outer_path,
+     Path *inner_path, List *keys, MergeOrder *order,
+     List *mergeclauses, List *outersortkeys, List *innersortkeys);
+
+extern HashPath *create_hashjoin_path(Rel *joinrel, int outersize,
+     int innersize, int outerwidth, int innerwidth, Path *outer_path,
+     Path *inner_path, List *keys, Oid operator, List *hashclauses,
+     List *outerkeys, List *innerkeys);
+
+/*
+ * prototypes for rel.c
+ */
+extern Rel *rel_member(List *relid, List *rels);
+extern Rel *get_base_rel(Query* root, int relid);
+extern Rel *get_join_rel(Query* root, List *relid);
+
+/*
+ * prototypes for indexnode.h
+ */
+extern List *find_relation_indices(Query *root,Rel *rel);
+
+#endif /* PATHNODE_H */
diff --git a/src/backend/optimizer/paths.h b/src/backend/optimizer/paths.h
new file mode 100644 (file)
index 0000000..76114cc
--- /dev/null
@@ -0,0 +1,89 @@
+/*-------------------------------------------------------------------------
+ *
+ * paths.h--
+ *    prototypes for various files in optimizer/paths (were separate
+ *    header files
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        PATHS_H
+#define        PATHS_H
+
+/*
+ * allpaths.h
+ */
+extern List *find_paths(Query *root, List *rels);
+
+/*
+ * indxpath.h
+ *    routines to generate index paths
+ */
+extern List *find_index_paths(Query *root, Rel *rel, List *indices,
+                             List *clauseinfo_list, 
+                             List *joininfo_list);
+
+/*
+ * joinpath.h
+ *     routines to create join paths
+ */
+extern void find_all_join_paths(Query *root, List *joinrels);
+                               
+
+/*
+ * orindxpath.h
+ */
+extern List *create_or_index_paths(Query *root, Rel *rel, List *clauses);
+
+/*
+ * hashutils.h
+ *    routines to deal with hash keys and clauses
+ */
+extern List *group_clauses_by_hashop(List *clauseinfo_list,
+                                    int inner_relid);
+
+/*
+ * joinutils.h
+ *    generic join method key/clause routines
+ */
+extern List *match_pathkeys_joinkeys(List *pathkeys,
+                List *joinkeys, List *joinclauses, int which_subkey,
+                List **matchedJoinClausesPtr);              
+extern List *extract_path_keys(List *joinkeys, List *tlist,
+                int which_subkey);
+extern Path *match_paths_joinkeys(List *joinkeys, PathOrder *ordering,
+                List *paths, int which_subkey);
+extern List *new_join_pathkeys(List *outer_pathkeys,
+                List *join_rel_tlist, List *joinclauses);
+
+/*
+ * mergeutils.h
+ *    routines to deal with merge keys and clauses
+ */
+extern List *group_clauses_by_order(List *clauseinfo_list,
+                                   int inner_relid);
+extern MInfo *match_order_mergeinfo(PathOrder *ordering,
+                                   List *mergeinfo_list);
+
+/*
+ * joinrels.h
+ *    routines to determine which relations to join
+ */
+extern List *find_join_rels(Query *root, List *outer_rels);
+extern void add_new_joininfos(Query *root, List *joinrels, List *outerrels);
+extern List *final_join_rels(List *join_rel_list);
+
+/*
+ * prototypes for path/prune.c
+ */
+extern List *prune_joinrels(List *rel_list);
+extern void prune_rel_paths(List *rel_list);
+extern Path *prune_rel_path(Rel *rel, Path *unorderedpath);
+extern List *merge_joinrels(List *rel_list1, List *rel_list2);
+extern List *prune_oldrels(List *old_rels);
+
+#endif /* PATHS_H */
diff --git a/src/backend/optimizer/plan/Makefile.inc b/src/backend/optimizer/plan/Makefile.inc
new file mode 100644 (file)
index 0000000..1fa6ca1
--- /dev/null
@@ -0,0 +1,15 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for optimizer/plan
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= createplan.c initsplan.c planmain.c planner.c \
+        setrefs.c
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
new file mode 100644 (file)
index 0000000..4ede075
--- /dev/null
@@ -0,0 +1,1097 @@
+/*-------------------------------------------------------------------------
+ *
+ * createplan.c--
+ *    Routines to create the desired plan for processing a query
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+
+#include "nodes/execnodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/relation.h"
+#include "nodes/primnodes.h"
+#include "nodes/nodeFuncs.h"
+
+#include "nodes/makefuncs.h"
+
+#include "utils/elog.h"
+#include "utils/lsyscache.h"
+#include "utils/palloc.h"
+#include "utils/builtins.h"
+
+#include "parser/parse_query.h"
+#include "optimizer/clauseinfo.h"
+#include "optimizer/clauses.h"
+#include "optimizer/planmain.h"
+#include "optimizer/tlist.h"
+#include "optimizer/planner.h"
+#include "optimizer/xfunc.h"
+#include "optimizer/internal.h"
+
+
+#define TEMP_SORT      1
+#define TEMP_MATERIAL  2
+
+static List *switch_outer(List *clauses);
+static Scan *create_scan_node(Path *best_path, List *tlist);
+static Join *create_join_node(JoinPath *best_path, List *tlist);
+static SeqScan *create_seqscan_node(Path *best_path, List *tlist,
+                                  List *scan_clauses);
+static IndexScan *create_indexscan_node(IndexPath *best_path, List *tlist,
+                                      List *scan_clauses);
+static NestLoop *create_nestloop_node(JoinPath *best_path, List *tlist,
+            List *clauses, Plan *outer_node, List *outer_tlist,
+            Plan *inner_node, List *inner_tlist);
+static MergeJoin *create_mergejoin_node(MergePath *best_path, List *tlist,
+       List *clauses, Plan *outer_node, List *outer_tlist,
+       Plan *inner_node, List *inner_tlist);
+static HashJoin *create_hashjoin_node(HashPath *best_path, List *tlist,
+            List *clauses, Plan *outer_node, List *outer_tlist,
+             Plan *inner_node, List *inner_tlist);
+static Node *fix_indxqual_references(Node *clause, Path *index_path);
+static Temp *make_temp(List *tlist, List *keys, Oid *operators,
+                      Plan *plan_node, int temptype);
+static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
+                               List *indxid, List *indxqual);
+static NestLoop *make_nestloop(List *qptlist, List *qpqual, Plan *lefttree,
+                             Plan *righttree);
+static HashJoin *make_hashjoin(List *tlist, List *qpqual,
+      List *hashclauses, Plan *lefttree, Plan *righttree);
+static Hash *make_hash(List *tlist, Var *hashkey, Plan *lefttree);
+static MergeJoin *make_mergesort(List * tlist, List *qpqual,
+       List *mergeclauses, Oid opcode, Oid *rightorder,
+       Oid *leftorder, Plan *righttree, Plan *lefttree);
+static Material *make_material(List *tlist, Oid tempid, Plan *lefttree,
+      int keycount);
+
+/*    
+ * create_plan--
+ *    Creates the access plan for a query by tracing backwards through the
+ *    desired chain of pathnodes, starting at the node 'best-path'.  For
+ *    every pathnode found:
+ *    (1) Create a corresponding plan node containing appropriate id,
+ *        target list, and qualification information.
+ *    (2) Modify ALL clauses so that attributes are referenced using
+ *        relative values.
+ *    (3) Target lists are not modified, but will be in another routine.
+ *    
+ *    best-path is the best access path
+ *
+ *    Returns the optimal(?) access plan.
+ */
+Plan *
+create_plan(Path *best_path)
+{
+    List *tlist;
+    Plan *plan_node = (Plan*)NULL;
+    Rel *parent_rel;
+    int size;
+    int width;
+    int pages;
+    int tuples;
+
+    parent_rel = best_path->parent;
+    tlist = get_actual_tlist(parent_rel->targetlist);
+    size = parent_rel->size;
+    width = parent_rel->width;
+    pages = parent_rel->pages;
+    tuples = parent_rel->tuples;
+
+    switch(best_path->pathtype) {
+    case T_IndexScan : 
+    case T_SeqScan :
+       plan_node = (Plan*)create_scan_node(best_path, tlist);
+       break;
+    case T_HashJoin :
+    case T_MergeJoin : 
+    case T_NestLoop:
+       plan_node = (Plan*)create_join_node((JoinPath*)best_path, tlist);
+       break;
+    default:
+       /* do nothing */
+       break;
+    } 
+
+    plan_node->plan_size = size;
+    plan_node->plan_width = width;
+    if (pages == 0) pages = 1;
+    plan_node->plan_tupperpage = tuples/pages;
+
+#if 0 /* fix xfunc */
+    /* sort clauses by cost/(1-selectivity) -- JMH 2/26/92 */
+    if (XfuncMode != XFUNC_OFF)
+       {
+           set_qpqual((Plan) plan_node, 
+                      lisp_qsort( get_qpqual((Plan) plan_node), 
+                                 xfunc_clause_compare));
+           if (XfuncMode != XFUNC_NOR)
+               /* sort the disjuncts within each clause by cost -- JMH 3/4/92 */
+               xfunc_disjunct_sort(plan_node->qpqual);
+       }
+#endif
+    
+    return(plan_node);
+}
+
+/*    
+ * create_scan_node--
+ *   Create a scan path for the parent relation of 'best-path'.
+ *    
+ *   tlist is the targetlist for the base relation scanned by 'best-path'
+ *    
+ *   Returns the scan node.
+ */
+static Scan *
+create_scan_node(Path *best_path, List *tlist)
+{
+
+    Scan *node;
+    List *scan_clauses;
+
+    /*
+     * Extract the relevant clauses from the parent relation and replace the
+     * operator OIDs with the corresponding regproc ids.
+     *
+     * now that local predicate clauses are copied into paths in
+     * find_rel_paths() and then (possibly) pulled up in xfunc_trypullup(),
+     * we get the relevant clauses from the path itself, not its parent
+     * relation.   --- JMH, 6/15/92
+     */
+    scan_clauses = fix_opids(get_actual_clauses(best_path->locclauseinfo));
+
+    switch(best_path->pathtype) {
+    case T_SeqScan : 
+       node = (Scan*)create_seqscan_node(best_path, tlist, scan_clauses);
+       break;
+
+    case T_IndexScan:
+       node = (Scan*)create_indexscan_node((IndexPath*)best_path,
+                                           tlist,
+                                           scan_clauses);
+       break;
+
+    default :
+       elog(WARN, "create_scan_node: unknown node type",
+            best_path->pathtype);
+       break;
+    }
+
+    return node;
+}
+
+/*    
+ * create_join_node --
+ *    Create a join path for 'best-path' and(recursively) paths for its
+ *    inner and outer paths.
+ *    
+ *    'tlist' is the targetlist for the join relation corresponding to
+ *     'best-path'
+ *    
+ *    Returns the join node.
+ */
+static Join *
+create_join_node(JoinPath *best_path, List *tlist)
+{
+    Plan       *outer_node;
+    List       *outer_tlist;
+    Plan       *inner_node;
+    List       *inner_tlist;
+    List       *clauses;
+    Join       *retval;
+
+    outer_node = create_plan((Path*)best_path->outerjoinpath);
+    outer_tlist  = outer_node->targetlist;
+
+    inner_node = create_plan((Path*)best_path->innerjoinpath);
+    inner_tlist = inner_node->targetlist;
+
+    clauses = get_actual_clauses(best_path->pathclauseinfo);
+     
+    switch(best_path->path.pathtype) {
+    case T_MergeJoin: 
+       retval = (Join*)create_mergejoin_node((MergePath*)best_path,
+                                             tlist,
+                                             clauses,
+                                             outer_node,
+                                             outer_tlist,
+                                             inner_node,
+                                             inner_tlist);
+       break;
+    case T_HashJoin: 
+       retval = (Join*)create_hashjoin_node((HashPath*)best_path,
+                                            tlist,
+                                            clauses,
+                                            outer_node,
+                                            outer_tlist,
+                                            inner_node,
+                                            inner_tlist);
+       break;
+    case T_NestLoop: 
+       retval = (Join*)create_nestloop_node((JoinPath*)best_path,
+                                            tlist,
+                                            clauses,
+                                            outer_node,
+                                            outer_tlist,
+                                            inner_node,
+                                            inner_tlist);
+       break;
+    default:
+       /* do nothing */
+       elog(WARN, "create_join_node: unknown node type",
+            best_path->path.pathtype);
+    } 
+
+#if 0
+    /*
+     ** Expensive function pullups may have pulled local predicates
+     ** into this path node.  Put them in the qpqual of the plan node.
+     **        -- JMH, 6/15/92
+     */
+    if (get_locclauseinfo(best_path) != NIL)
+       set_qpqual((Plan)retval,
+                  nconc(get_qpqual((Plan) retval), 
+                        fix_opids(get_actual_clauses
+                                  (get_locclauseinfo(best_path)))));
+#endif
+
+    return(retval);
+}
+
+/*****************************************************************************
+ *
+ *  BASE-RELATION SCAN METHODS
+ *
+ *****************************************************************************/
+
+/*    
+ * create_seqscan_node--
+ *   Returns a seqscan node for the base relation scanned by 'best-path'
+ *   with restriction clauses 'scan-clauses' and targetlist 'tlist'.
+ */
+static SeqScan *
+create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
+{
+    SeqScan *scan_node = (SeqScan*)NULL;
+    Index scan_relid = -1;
+    List *temp;
+
+    temp = best_path->parent->relids;
+    if(temp == NULL)
+       elog(WARN,"scanrelid is empty");
+    else
+       scan_relid = (Index)lfirst(temp); /* ??? who takes care of lnext? - ay */
+    scan_node = make_seqscan(tlist,
+                            scan_clauses,
+                            scan_relid,
+                            (Plan*)NULL);
+    
+    scan_node->plan.cost = best_path->path_cost;
+    
+    return(scan_node);
+}
+
+/*    
+ * create_indexscan_node--
+ *    Returns a indexscan node for the base relation scanned by 'best-path'
+ *    with restriction clauses 'scan-clauses' and targetlist 'tlist'.
+ */
+static IndexScan *
+create_indexscan_node(IndexPath *best_path,
+                     List *tlist,
+                     List *scan_clauses)
+{
+    /*
+     * Extract the(first if conjunct, only if disjunct) clause from the
+     * clauseinfo list.
+     */
+     Expr      *index_clause = (Expr*)NULL;
+     List      *indxqual = NIL;
+     List      *qpqual = NIL;
+     List      *fixed_indxqual = NIL;
+     IndexScan         *scan_node = (IndexScan*)NULL;
+
+
+     /*
+      * If an 'or' clause is to be used with this index, the indxqual
+      * field will contain a list of the 'or' clause arguments, e.g., the
+      * clause(OR a b c) will generate: ((a) (b) (c)).  Otherwise, the
+      * indxqual will simply contain one conjunctive qualification: ((a)).
+      */
+     if (best_path->indexqual != NULL)
+       /* added call to fix_opids, JMH 6/23/92 */
+        index_clause = (Expr*)
+            lfirst(fix_opids(get_actual_clauses(best_path->indexqual)));
+
+     if (or_clause((Node*)index_clause)) {
+         List *temp = NIL;
+       
+         foreach(temp, index_clause->args)
+             indxqual = lappend(indxqual, lcons(lfirst(temp), NIL));
+     } else {
+        indxqual = lcons(get_actual_clauses(best_path->indexqual),
+                        NIL);
+     } 
+
+     /*
+      * The qpqual field contains all restrictions except the indxqual.
+      */
+     if(or_clause((Node*)index_clause))
+        qpqual = set_difference(scan_clauses,
+                                lcons(index_clause,NIL));
+     else 
+        qpqual = set_difference(scan_clauses, lfirst(indxqual));
+     
+     fixed_indxqual =
+        (List*)fix_indxqual_references((Node*)indxqual,(Path*)best_path);
+
+     scan_node =
+        make_indexscan(tlist,
+                       qpqual,
+                       lfirsti(best_path->path.parent->relids),
+                       best_path->indexid,
+                       fixed_indxqual);
+     
+     scan_node->scan.plan.cost = best_path->path.path_cost;
+
+     return(scan_node);
+}
+
+/*****************************************************************************
+ *
+ *  JOIN METHODS
+ *
+ *****************************************************************************/
+
+static NestLoop *
+create_nestloop_node(JoinPath *best_path,
+                    List *tlist,
+                    List *clauses,
+                    Plan *outer_node,
+                    List *outer_tlist,
+                    Plan *inner_node,
+                    List *inner_tlist)
+{
+    NestLoop *join_node = (NestLoop*)NULL;
+
+    if (IsA(inner_node,IndexScan)) {
+       /*  An index is being used to reduce the number of tuples scanned in 
+        *    the inner relation.
+        * There will never be more than one index used in the inner 
+        * scan path, so we need only consider the first set of 
+        *    qualifications in indxqual. 
+        */
+
+       List *inner_indxqual = lfirst(((IndexScan*)inner_node)->indxqual);
+       List *inner_qual = (inner_indxqual == NULL)? NULL:lfirst(inner_indxqual);
+
+       /* If we have in fact found a join index qualification, remove these
+        * index clauses from the nestloop's join clauses and reset the 
+        * inner(index) scan's qualification so that the var nodes refer to
+        * the proper outer join relation attributes.
+        */
+       if  (!(qual_clause_p((Node*)inner_qual))) {
+           List *new_inner_qual = NIL;
+           
+           clauses = set_difference(clauses,inner_indxqual);
+           new_inner_qual =
+               index_outerjoin_references(inner_indxqual,
+                                          outer_node->targetlist,
+                                          ((Scan*)inner_node)->scanrelid);
+           ((IndexScan*)inner_node)->indxqual =
+               lcons(new_inner_qual,NIL);
+       }
+    }else if (IsA_Join(inner_node)) {
+       inner_node = (Plan*)make_temp(inner_tlist,
+                                     NIL,
+                                     NULL, 
+                                     inner_node,
+                                     TEMP_MATERIAL);
+    }
+
+    join_node = make_nestloop(tlist,
+                             join_references(clauses,
+                                             outer_tlist,
+                                             inner_tlist),
+                             outer_node,
+                             inner_node);
+
+    join_node->join.cost = best_path->path.path_cost;
+
+    return(join_node);
+}
+
+static MergeJoin *
+create_mergejoin_node(MergePath *best_path,
+                     List *tlist,
+                     List *clauses,
+                     Plan *outer_node,
+                     List *outer_tlist,
+                     Plan *inner_node,
+                     List *inner_tlist)
+{
+    List *qpqual, *mergeclauses;
+    RegProcedure opcode;
+    Oid *outer_order, *inner_order;
+    MergeJoin *join_node;
+
+
+    /* Separate the mergeclauses from the other join qualification 
+     * clauses and set those clauses to contain references to lower 
+     * attributes. 
+     */
+    qpqual = join_references(set_difference(clauses,
+                                           best_path->path_mergeclauses),
+                            outer_tlist,
+                            inner_tlist);
+
+    /* Now set the references in the mergeclauses and rearrange them so 
+     * that the outer variable is always on the left. 
+     */
+    mergeclauses = switch_outer(join_references(best_path->path_mergeclauses,
+                                               outer_tlist,
+                                               inner_tlist));
+
+    opcode =
+       get_opcode((best_path->jpath.path.p_ordering.ord.merge)->join_operator);
+     
+    outer_order = (Oid *)palloc(sizeof(Oid)*2);
+    outer_order[0] =
+       (best_path->jpath.path.p_ordering.ord.merge)->left_operator;
+    outer_order[1] = 0;
+     
+    inner_order = (Oid *)palloc(sizeof(Oid)*2);
+    inner_order[0] = 
+       (best_path->jpath.path.p_ordering.ord.merge)->right_operator;
+    inner_order[1] = 0;
+     
+    /* Create explicit sort paths for the outer and inner join paths if 
+     * necessary.  The sort cost was already accounted for in the path. 
+     */
+    if (best_path->outersortkeys) {
+       Temp *sorted_outer_node = make_temp(outer_tlist,
+                                           best_path->outersortkeys,
+                                           outer_order,
+                                           outer_node,
+                                           TEMP_SORT);
+       sorted_outer_node->plan.cost = outer_node->cost;
+       outer_node = (Plan*)sorted_outer_node;
+    }
+
+    if (best_path->innersortkeys) {
+       Temp *sorted_inner_node = make_temp(inner_tlist,
+                                           best_path->innersortkeys,
+                                           inner_order,
+                                           inner_node,
+                                           TEMP_SORT);
+       sorted_inner_node->plan.cost = outer_node->cost;
+       inner_node = (Plan*)sorted_inner_node;
+    }
+
+    join_node = make_mergesort(tlist,
+                              qpqual,
+                              mergeclauses,
+                              opcode,
+                              inner_order,
+                              outer_order, 
+                              inner_node,
+                              outer_node);
+
+    join_node->join.cost = best_path->jpath.path.path_cost;
+
+    return(join_node);
+}
+
+/*    
+ * create_hashjoin_node--                      XXX HASH
+ *    
+ *    Returns a new hashjoin node.
+ *    
+ *    XXX hash join ops are totally bogus -- how the hell do we choose
+ *     these??  at runtime?  what about a hash index?
+ */
+static HashJoin *
+create_hashjoin_node(HashPath *best_path,
+                    List *tlist,
+                    List *clauses,
+                    Plan *outer_node,
+                    List *outer_tlist,
+                    Plan *inner_node,
+                    List *inner_tlist)
+{
+    List *qpqual;
+    List *hashclauses;
+    HashJoin *join_node;
+    Hash *hash_node;
+    Var *innerhashkey;
+
+    /* Separate the hashclauses from the other join qualification clauses
+     * and set those clauses to contain references to lower attributes. 
+     */
+    qpqual = 
+       join_references(set_difference(clauses,
+                                      best_path->path_hashclauses),
+                       outer_tlist,
+                       inner_tlist);
+
+    /* Now set the references in the hashclauses and rearrange them so 
+     * that the outer variable is always on the left. 
+     */
+    hashclauses = 
+       switch_outer(join_references(best_path->path_hashclauses,
+                                    outer_tlist,
+                                    inner_tlist));
+
+    innerhashkey = get_rightop(lfirst(hashclauses));
+
+    hash_node = make_hash(inner_tlist, innerhashkey, inner_node);
+    join_node = make_hashjoin(tlist,
+                             qpqual,
+                             hashclauses,
+                             outer_node,
+                             (Plan*)hash_node);
+    join_node->join.cost = best_path->jpath.path.path_cost;
+
+    return(join_node);
+}
+
+
+/*****************************************************************************
+ *
+ *  SUPPORTING ROUTINES
+ *
+ *****************************************************************************/
+
+static Node *
+fix_indxqual_references(Node *clause, Path *index_path)
+{
+    Node *newclause;
+
+    if (IsA(clause,Var)) {
+       if (lfirsti(index_path->parent->relids) == ((Var*)clause)->varno) {
+           int pos = 0;
+           int varatt = ((Var*)clause)->varattno;
+           int *indexkeys = index_path->parent->indexkeys;
+           
+           if (indexkeys) {
+               while (indexkeys[pos] != 0) {
+                   if(varatt == indexkeys[pos]) {
+                       break;
+                   }
+                   pos++;
+               }
+           }
+           newclause = copyObject((Node*)clause);
+           ((Var*)newclause)->varattno = pos + 1;
+           return (newclause);
+       } else {
+           return (clause);
+       }
+    } else if(IsA(clause,Const)) {
+           return(clause);
+    } else if(is_opclause(clause) && 
+             is_funcclause((Node*)get_leftop((Expr*)clause)) && 
+             ((Func*)((Expr*)get_leftop((Expr*)clause))->oper)->funcisindex){
+       Var *newvar =
+           makeVar((Index)lfirst(index_path->parent->relids),
+                   1, /* func indices have one key */
+                   ((Func*)((Expr*)clause)->oper)->functype,
+                   (Index)lfirst(index_path->parent->relids),
+                   0);
+
+       return
+           ((Node*)make_opclause((Oper*)((Expr*)clause)->oper,
+                                 newvar,
+                                 get_rightop((Expr*)clause)));
+
+    } else if (IsA(clause,Expr)) {
+       Expr *expr = (Expr*)clause;
+       List *new_subclauses = NIL;
+       Node *subclause = NULL;
+       List *i = NIL;
+
+       foreach(i, expr->args) {
+           subclause = lfirst(i);
+           if(subclause)
+               new_subclauses =
+                   lappend(new_subclauses,
+                            fix_indxqual_references(subclause,
+                                                    index_path));
+
+       }
+       
+       /* XXX new_subclauses should be a list of the form:
+        * ( (var var) (var const) ...) ?
+        */
+       if(new_subclauses) {
+           return (Node*)
+               make_clause(expr->opType, expr->oper, new_subclauses);
+       } else {
+           return(clause);
+       } 
+    } else {
+       List *oldclauses = (List*)clause;
+       List *new_subclauses = NIL;
+       Node *subclause = NULL;
+       List *i = NIL;
+
+       foreach(i, oldclauses) {
+           subclause = lfirst(i);
+           if(subclause)
+               new_subclauses =
+                   lappend(new_subclauses,
+                            fix_indxqual_references(subclause,
+                                                    index_path));
+
+       }
+       
+       /* XXX new_subclauses should be a list of the form:
+        * ( (var var) (var const) ...) ?
+        */
+       if(new_subclauses) {
+           return (Node*)new_subclauses;
+       } else {
+           return (clause);
+       } 
+    }
+}
+
+
+/*    
+ * switch_outer--
+ *    Given a list of merge clauses, rearranges the elements within the
+ *    clauses so the outer join variable is on the left and the inner is on
+ *    the right.
+ *    
+ *    Returns the rearranged list ?
+ *    
+ *    XXX Shouldn't the operator be commuted?!
+ */
+static List *
+switch_outer(List *clauses)
+{
+    List *t_list = NIL;
+    Expr *temp = NULL;
+    List *i = NIL;
+    Expr *clause;
+
+    foreach(i,clauses) {
+       clause = lfirst(i);
+       if(var_is_outer(get_rightop(clause))) {
+           temp = make_clause(clause->opType, clause->oper,
+                              lcons(get_rightop(clause),
+                                   lcons(get_leftop(clause),
+                                        NIL)));
+           t_list = lappend(t_list,temp);
+       } 
+       else 
+           t_list = lappend(t_list,clause);
+    } 
+    return(t_list);
+}
+
+/*    
+ * set-temp-tlist-operators--
+ *    Sets the key and keyop fields of resdom nodes in a target list.
+ *    
+ *    'tlist' is the target list
+ *    'pathkeys' is a list of N keys in the form((key1) (key2)...(keyn)),
+ *             corresponding to vars in the target list that are to
+ *             be sorted or hashed
+ *    'operators' is the corresponding list of N sort or hash operators
+ *    'keyno' is the first key number 
+ *    XXX - keyno ? doesn't exist - jeff
+ *    
+ *    Returns the modified target list.
+ */
+static List *
+set_temp_tlist_operators(List *tlist, List *pathkeys, Oid *operators)
+{
+    Node       *keys = NULL;
+    int        keyno = 1;
+    Resdom     *resdom = (Resdom*)NULL ;
+    List       *i = NIL;
+
+    foreach(i, pathkeys) {
+       keys = lfirst((List*)lfirst(i));
+       resdom = tlist_member((Var*)keys, tlist);
+       if (resdom) {
+
+           /* Order the resdom keys and replace the operator OID for each 
+            *    key with the regproc OID. 
+            *
+            * XXX Note that the optimizer only generates merge joins 
+            *    with 1 operator (see create_mergejoin_node)  - ay 2/95
+            */
+           resdom->reskey = keyno;
+           resdom->reskeyop = get_opcode(operators[0]);
+       }
+       keyno += 1;
+    }
+    return(tlist);
+}
+
+/*****************************************************************************
+ *
+ *
+ *****************************************************************************/
+
+/*    
+ * make_temp--
+ *    Create plan nodes to sort or materialize relations into temporaries. The
+ *    result returned for a sort will look like (SEQSCAN(SORT(plan-node)))
+ *    or (SEQSCAN(MATERIAL(plan-node)))
+ *    
+ *    'tlist' is the target list of the scan to be sorted or hashed
+ *    'keys' is the list of keys which the sort or hash will be done on
+ *    'operators' is the operators with which the sort or hash is to be done
+ *     (a list of operator OIDs)
+ *    'plan-node' is the node which yields tuples for the sort
+ *    'temptype' indicates which operation(sort or hash) to perform
+ */
+static Temp *
+make_temp(List *tlist,
+         List *keys,
+         Oid *operators,
+         Plan *plan_node,
+         int temptype)
+{
+    List *temp_tlist;
+    Temp *retval;
+
+    /*    Create a new target list for the temporary, with keys set. */
+    temp_tlist = set_temp_tlist_operators(new_unsorted_tlist(tlist),
+                                         keys,
+                                         operators);
+    switch(temptype) {
+    case TEMP_SORT : 
+       retval = (Temp*)make_seqscan(tlist,
+                                    NIL,
+                                    _TEMP_RELATION_ID_,
+                                    (Plan*)make_sort(temp_tlist,
+                                                     _TEMP_RELATION_ID_,
+                                                     plan_node,
+                                                     length(keys)));
+       break;
+        
+    case TEMP_MATERIAL : 
+       retval = (Temp*)make_seqscan(tlist,
+                                    NIL,
+                                    _TEMP_RELATION_ID_,
+                                    (Plan*)make_material(temp_tlist,
+                                                         _TEMP_RELATION_ID_,
+                                                         plan_node,
+                                                         length(keys)));
+       break;
+        
+    default: 
+       elog(WARN,"make_temp: unknown temp type %d", temptype);
+        
+    }
+    return(retval);
+}
+
+
+SeqScan *
+make_seqscan(List *qptlist,
+            List *qpqual,
+            Index scanrelid,
+            Plan *lefttree)
+{
+    SeqScan *node = makeNode(SeqScan);
+    Plan *plan = &node->plan;
+    
+    plan->cost = 0.0;
+    plan->state = (EState *)NULL;
+    plan->targetlist = qptlist;
+    plan->qual = qpqual;
+    plan->lefttree = lefttree;
+    plan->righttree = NULL;
+    node->scanrelid = scanrelid;
+    node->scanstate = (CommonScanState *)NULL;
+
+    return(node);
+}
+
+static IndexScan *
+make_indexscan(List *qptlist,
+              List *qpqual,
+              Index scanrelid,
+              List *indxid,
+              List *indxqual)
+{
+    IndexScan *node = makeNode(IndexScan);
+    Plan *plan = &node->scan.plan;
+
+    plan->cost = 0.0;
+    plan->state = (EState *)NULL;
+    plan->targetlist = qptlist;
+    plan->qual = qpqual;
+    plan->lefttree = NULL;
+    plan->righttree = NULL;
+    node->scan.scanrelid = scanrelid;
+    node->indxid = indxid;
+    node->indxqual = indxqual;
+    node->scan.scanstate = (CommonScanState *)NULL;
+
+    return(node);
+}
+
+
+static NestLoop *
+make_nestloop(List *qptlist,
+             List *qpqual,
+             Plan *lefttree,
+             Plan *righttree)
+{
+    NestLoop *node = makeNode(NestLoop);
+    Plan *plan = &node->join;
+
+    plan->cost = 0.0;
+    plan->state = (EState *)NULL;
+    plan->targetlist = qptlist;
+    plan->qual = qpqual;
+    plan->lefttree = lefttree;
+    plan->righttree = righttree;
+    node->nlstate = (NestLoopState*)NULL;
+
+    return(node);
+}
+
+static HashJoin *
+make_hashjoin(List *tlist,
+             List *qpqual,
+             List *hashclauses,
+             Plan *lefttree,
+             Plan *righttree)
+{
+    HashJoin *node = makeNode(HashJoin);
+    Plan *plan = &node->join;
+    
+    plan->cost = 0.0;
+    plan->state = (EState *)NULL;
+    plan->targetlist = tlist;
+    plan->qual = qpqual;
+    plan->lefttree = lefttree;
+    plan->righttree = righttree;
+    node->hashclauses = hashclauses;
+    node->hashjointable = NULL;
+    node->hashjointablekey = 0;
+    node->hashjointablesize = 0;
+    node->hashdone = false;
+
+    return(node);
+}
+
+static Hash *
+make_hash(List *tlist, Var *hashkey, Plan *lefttree)
+{
+    Hash *node = makeNode(Hash);
+    Plan *plan = &node->plan;
+
+    plan->cost = 0.0;
+    plan->state = (EState *)NULL;
+    plan->targetlist = tlist;
+    plan->qual = NULL;
+    plan->lefttree = lefttree;
+    plan->righttree = NULL;
+    node->hashkey = hashkey;
+    node->hashtable = NULL;
+    node->hashtablekey = 0;
+    node->hashtablesize = 0;
+
+    return(node);
+}
+
+static MergeJoin *
+make_mergesort(List *tlist,
+              List *qpqual,
+              List *mergeclauses,
+              Oid opcode,
+              Oid *rightorder,
+              Oid *leftorder,
+              Plan *righttree,
+              Plan *lefttree)
+{
+    MergeJoin *node = makeNode(MergeJoin);
+    Plan *plan = &node->join;
+    
+    plan->cost = 0.0;
+    plan->state = (EState *)NULL;
+    plan->targetlist = tlist;
+    plan->qual = qpqual;
+    plan->lefttree = lefttree;
+    plan->righttree = righttree;
+    node->mergeclauses = mergeclauses;
+    node->mergesortop = opcode;
+    node->mergerightorder = rightorder;
+    node->mergeleftorder = leftorder;
+
+    return(node);
+}
+
+Sort *
+make_sort(List *tlist, Oid tempid, Plan *lefttree, int keycount)
+{
+    Sort *node = makeNode(Sort);
+    Plan *plan = &node->plan;
+
+    plan->cost = 0.0;
+    plan->state = (EState *)NULL;
+    plan->targetlist = tlist;
+    plan->qual = NIL;
+    plan->lefttree = lefttree;
+    plan->righttree = NULL;
+    node->tempid = tempid;
+    node->keycount = keycount;
+
+    return(node);
+}
+
+static Material *
+make_material(List *tlist,
+             Oid tempid,
+             Plan *lefttree,
+             int keycount)
+{
+    Material *node = makeNode(Material); 
+    Plan *plan = &node->plan;
+
+    plan->cost = 0.0;
+    plan->state = (EState *)NULL;
+    plan->targetlist = tlist;
+    plan->qual = NIL;
+    plan->lefttree = lefttree;
+    plan->righttree = NULL;
+    node->tempid = tempid;
+    node->keycount = keycount;
+
+    return(node);
+}
+
+Agg *
+make_agg(List *tlist, int nagg, Aggreg **aggs)
+{
+    Agg *node = makeNode(Agg);
+
+    node->plan.cost = 0.0;
+    node->plan.state = (EState*)NULL;
+    node->plan.qual = NULL;
+    node->plan.targetlist = tlist;
+    node->plan.lefttree = (Plan*)NULL;
+    node->plan.righttree = (Plan*)NULL;
+    node->numAgg = nagg;
+    node->aggs = aggs;
+
+    return(node);
+}
+
+Group *
+make_group(List *tlist,
+          bool tuplePerGroup,
+          int ngrp,
+          AttrNumber *grpColIdx,
+          Sort *lefttree)
+{
+    Group *node = makeNode(Group);
+
+    node->plan.cost = 0.0;
+    node->plan.state = (EState*)NULL;
+    node->plan.qual = NULL;
+    node->plan.targetlist = tlist;
+    node->plan.lefttree = (Plan*)lefttree;
+    node->plan.righttree = (Plan*)NULL;
+    node->tuplePerGroup = tuplePerGroup;
+    node->numCols = ngrp;
+    node->grpColIdx = grpColIdx;
+
+    return(node);
+}
+
+/*
+ *  A unique node always has a SORT node in the lefttree.
+ *
+ *  the uniqueAttr argument must be a null-terminated string,
+ * either the name of the attribute to select unique on 
+ * or "*"
+ */
+
+Unique *
+make_unique(List *tlist, Plan *lefttree, char* uniqueAttr)
+{
+    Unique *node = makeNode(Unique);
+    Plan *plan = &node->plan;
+
+    plan->cost = 0.0;
+    plan->state = (EState *)NULL;
+    plan->targetlist = tlist;
+    plan->qual = NIL;
+    plan->lefttree = lefttree;
+    plan->righttree = NULL;
+    node->tempid = _TEMP_RELATION_ID_;
+    node->keycount = 0;
+    if (strcmp(uniqueAttr,"*") == 0)
+      node->uniqueAttr = NULL;
+    else
+      {
+       node->uniqueAttr=pstrdup(uniqueAttr);
+      }
+    return(node);
+}
+
+List *generate_fjoin(List *tlist)
+{
+#if 0
+    List tlistP;
+    List newTlist = NIL;
+    List fjoinList = NIL;
+    int  nIters = 0;
+
+    /*
+     * Break the target list into elements with Iter nodes,
+     * and those without them.
+     */
+    foreach(tlistP, tlist) {
+       List tlistElem;
+
+       tlistElem = lfirst(tlistP);
+       if (IsA(lsecond(tlistElem),Iter)) {
+           nIters++;
+           fjoinList = lappend(fjoinList, tlistElem);
+       } else {
+           newTlist = lappend(newTlist, tlistElem);
+       }
+    }
+
+    /*
+     * if we have an Iter node then we need to flatten.
+     */
+    if (nIters > 0) {
+       List *inner;
+       List      *tempList;
+       Fjoin     *fjoinNode;
+       DatumPtr  results = (DatumPtr)palloc(nIters*sizeof(Datum));
+       BoolPtr   alwaysDone = (BoolPtr)palloc(nIters*sizeof(bool));
+
+       inner = lfirst(fjoinList);
+       fjoinList = lnext(fjoinList);
+       fjoinNode = (Fjoin)MakeFjoin(false,
+                                    nIters,
+                                    inner,
+                                    results,
+                                    alwaysDone);
+       tempList = lcons(fjoinNode, NIL);
+       tempList = nconc(tempList, fjoinList);
+       newTlist = lappend(newTlist, tempList);
+    }
+    return newTlist;
+#endif
+    return tlist;      /* do nothing for now - ay 10/94 */
+}
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
new file mode 100644 (file)
index 0000000..738cf36
--- /dev/null
@@ -0,0 +1,391 @@
+/*-------------------------------------------------------------------------
+ *
+ * initsplan.c--
+ *    Target list, qualification, joininfo initialization routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/plannodes.h"
+#include "nodes/parsenodes.h"
+#include "nodes/relation.h"
+#include "nodes/makefuncs.h"
+
+#include "utils/lsyscache.h"
+#include "utils/palloc.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/planmain.h"
+#include "optimizer/joininfo.h"
+#include "optimizer/pathnode.h"
+#include "optimizer/tlist.h"
+#include "optimizer/var.h"
+#include "optimizer/clauses.h"
+#include "optimizer/cost.h"
+
+extern int Quiet;
+
+static void add_clause_to_rels(Query *root, List *clause);
+static void add_join_clause_info_to_rels(Query *root, CInfo *clauseinfo,
+                                        List *join_relids);
+static void add_vars_to_rels(Query *root, List *vars, List *join_relids);
+
+static MergeOrder *mergesortop(Expr *clause);
+static Oid hashjoinop(Expr *clause);
+
+
+/*****************************************************************************
+ *
+ *   TARGET LISTS
+ *
+ *****************************************************************************/
+
+/*    
+ * initialize_rel_nodes--
+ *    Creates rel nodes for every relation mentioned in the target list
+ *    'tlist' (if a node hasn't already been created) and adds them to
+ *    *query-relation-list*.  Creates targetlist entries for each member of
+ *    'tlist' and adds them to the tlist field of the appropriate rel node.
+ *    
+ *    Returns nothing.
+ */
+void
+initialize_base_rels_list(Query *root, List *tlist)
+{
+    List *tlist_vars = NIL;
+    List *l = NIL;
+    List *tvar = NIL;
+    
+    foreach (l, tlist) {
+       TargetEntry *entry = (TargetEntry *) lfirst(l);
+
+        tlist_vars = append(tlist_vars, pull_var_clause(entry->expr)); 
+    }
+
+    /* now, the target list only contains Var nodes */
+    foreach (tvar, tlist_vars) {
+       Var     *var;
+       Index   varno;
+       Rel     *result;
+       
+       var = (Var*)lfirst(tvar);
+       varno = var->varno;
+       result = get_base_rel(root, varno);
+
+       add_tl_element(result, var);
+    }
+}
+
+/*
+ * add_missing_variables_to_base_rels -
+ *    If we have range variable(s) in the FROM clause that does not appear
+ *    in the target list nor qualifications, we add it to the base relation
+ *    list. For instance, "select f.x from foo f, foo f2" is a join of f and
+ *    f2. Note that if we have "select foo.x from foo f", it also gets turned
+ *    into a join.
+ */
+void
+add_missing_vars_to_base_rels(Query *root, List *tlist)
+{
+    List *l;
+    int varno;
+    
+    varno = 1;
+    foreach (l, root->rtable) {
+       RangeTblEntry *rte = (RangeTblEntry *)lfirst(l);
+       List *relids;
+       Rel *result;
+       Var *var;
+
+       relids = lconsi(varno, NIL);
+       if (rte->inFromCl &&
+           !rel_member(relids, root->base_relation_list_)) {
+
+           var = makeVar(varno, -2 , 26, varno, -2);
+           /* add it to base_relation_list_ */
+           result = get_base_rel(root, varno);
+           add_tl_element(result, var);
+       }
+       pfree(relids);
+       varno++;
+    }
+
+    return;
+}
+
+/*****************************************************************************
+ *
+ *    QUALIFICATIONS
+ *
+ *****************************************************************************/
+
+
+
+/*    
+ * initialize-qualification--
+ *    Initializes ClauseInfo and JoinInfo fields of relation entries for all
+ *    relations appearing within clauses.  Creates new relation entries if
+ *    necessary, adding them to *query-relation-list*.
+ *    
+ *    Returns nothing of interest.
+ */
+void
+initialize_base_rels_jinfo(Query *root, List *clauses)
+{
+    List *clause;
+
+    foreach (clause, clauses) {
+       add_clause_to_rels(root, lfirst(clause));
+    }
+    return;
+}
+
+/*    
+ * add-clause-to-rels--
+ *    Add clause information to either the 'ClauseInfo' or 'JoinInfo' field
+ *    of a relation entry(depending on whether or not the clause is a join)
+ *    by creating a new ClauseInfo node and setting appropriate fields
+ *    within the nodes.
+ *    
+ *    Returns nothing of interest.
+ */
+static void
+add_clause_to_rels(Query *root, List *clause)
+{
+    List *relids;
+    List *vars;
+    CInfo *clauseinfo = makeNode(CInfo);
+
+    /*
+     * Retrieve all relids and vars contained within the clause.
+     */
+    clause_relids_vars((Node*)clause, &relids, &vars);
+
+
+    clauseinfo->clause = (Expr*)clause;
+    clauseinfo->notclause = contains_not((Node*)clause);
+    clauseinfo->selectivity = 0;
+    clauseinfo->indexids = NIL;
+    clauseinfo->mergesortorder = (MergeOrder*)NULL;
+    clauseinfo->hashjoinoperator = (Oid)0;
+       
+
+
+    if(length(relids) == 1) {
+       Rel *rel = get_base_rel(root, lfirsti(relids));
+       
+       /*
+        * There is only one relation participating in 'clause',
+        * so 'clause' must be a restriction clause.
+        */
+
+       /* the selectivity of the clause must be computed
+          regardless of whether it's a restriction or a join clause */
+       if (is_funcclause((Node*)clause))
+           {
+               /*
+                * XXX If we have a func clause set selectivity to 1/3, 
+                *     really need a true selectivity function.
+                */
+               clauseinfo->selectivity = (Cost)0.3333333;
+           }
+       else
+           {
+               clauseinfo->selectivity =
+                   compute_clause_selec(root, (Node*)clause,
+                                        NIL);
+           }
+       rel->clauseinfo = lcons(clauseinfo,
+                              rel->clauseinfo);
+    } else {
+       /*
+        * 'clause' is a join clause, since there is more than one
+        * atom in the relid list.
+        */
+       
+       if (is_funcclause((Node*)clause))
+           {
+               /*
+                * XXX If we have a func clause set selectivity to 1/3, 
+                *     really need a true selectivity function.
+                */
+               clauseinfo->selectivity = (Cost)0.3333333;
+           }
+       else
+           {
+               clauseinfo->selectivity =
+                   compute_clause_selec(root, (Node*)clause,
+                                        NIL);
+           }
+       add_join_clause_info_to_rels(root, clauseinfo, relids);
+       add_vars_to_rels(root,vars, relids);
+    }
+}
+
+/*    
+ * add-join-clause-info-to-rels--
+ *    For every relation participating in a join clause, add 'clauseinfo' to
+ *    the appropriate joininfo node(creating a new one and adding it to the
+ *    appropriate rel node if necessary).
+ *    
+ * 'clauseinfo' describes the join clause
+ * 'join-relids' is the list of relations participating in the join clause
+ *    
+ * Returns nothing.
+ *    
+ */
+static void
+add_join_clause_info_to_rels(Query *root, CInfo *clauseinfo, List *join_relids)
+{
+    List *join_relid;
+
+    foreach (join_relid, join_relids) {
+       JInfo *joininfo = 
+           find_joininfo_node(get_base_rel(root, lfirsti(join_relid)),
+                              intLispRemove((int)lfirst(join_relid),
+                                            join_relids));
+       joininfo->jinfoclauseinfo =
+           lcons(clauseinfo, joininfo->jinfoclauseinfo);       
+
+    }
+}
+
+/*    
+ * add-vars-to-rels--
+ *    For each variable appearing in a clause,
+ *    (1) If a targetlist entry for the variable is not already present in
+ *        the appropriate relation's target list, add one.
+ *    (2) If a targetlist entry is already present, but the var is part of a
+ *        join clause, add the relids of the join relations to the JoinList
+ *        entry of the targetlist entry.
+ *    
+ *    'vars' is the list of var nodes
+ *    'join-relids' is the list of relids appearing in the join clause
+ *     (if this is a join clause)
+ *    
+ *    Returns nothing.
+ */
+static void
+add_vars_to_rels(Query *root, List *vars, List *join_relids)
+{
+    Var *var;
+    List *temp = NIL;
+    Rel *rel = (Rel*)NULL;
+    TargetEntry *tlistentry;
+    
+    foreach (temp, vars) {
+       var = (Var*)lfirst(temp);
+       rel = get_base_rel(root, var->varno);
+       tlistentry = tlistentry_member(var, rel->targetlist);
+       if(tlistentry==NULL)
+           /*   add a new entry */
+           add_tl_element(rel, var);
+    }
+}
+
+/*****************************************************************************
+ *
+ *   JOININFO
+ *
+ *****************************************************************************/
+
+/*    
+ * initialize-join-clause-info--
+ *    Set the MergeSortable or HashJoinable field for every joininfo node
+ *    (within a rel node) and the MergeSortOrder or HashJoinOp field for
+ *    each clauseinfo node(within a joininfo node) for all relations in a
+ *    query.
+ *    
+ *    Returns nothing.
+ */
+void
+initialize_join_clause_info(List *rel_list)
+{
+    List *x, *y, *z;
+    Rel *rel;
+    JInfo *joininfo;
+    CInfo *clauseinfo;
+    Expr *clause;
+
+    foreach (x, rel_list) {
+       rel = (Rel*)lfirst(x);
+       foreach (y, rel->joininfo) {
+           joininfo = (JInfo*)lfirst(y);
+           foreach (z, joininfo->jinfoclauseinfo) {
+               clauseinfo = (CInfo*)lfirst(z);
+               clause = clauseinfo->clause;
+               if(join_clause_p((Node*)clause)) {
+                   MergeOrder *sortop = (MergeOrder*)NULL;
+                   Oid hashop = (Oid)NULL;
+
+                   if (_enable_mergesort_) 
+                       sortop = mergesortop(clause);
+                   if (_enable_hashjoin_) 
+                       hashop = hashjoinop(clause);
+
+                   if (sortop) {
+                       clauseinfo->mergesortorder = sortop;
+                       joininfo->mergesortable = true;
+                   }
+                   if (hashop) {
+                       clauseinfo->hashjoinoperator = hashop;
+                       joininfo->hashjoinable = true;
+                   }
+               }
+           }
+       }
+    }
+}
+
+/*    
+ * mergesortop--
+ *    Returns the mergesort operator of an operator iff 'clause' is
+ *    mergesortable, i.e., both operands are single vars and the operator is
+ *    a mergesortable operator.
+ */
+static MergeOrder *
+mergesortop(Expr *clause)
+{
+    Oid leftOp, rightOp;
+    bool sortable;
+
+    sortable = op_mergesortable(((Oper*)clause->oper)->opno,
+                               (get_leftop(clause))->vartype,
+                               (get_rightop(clause))->vartype,
+                               &leftOp,
+                               &rightOp);
+    
+    if (sortable) {
+       MergeOrder *morder = makeNode(MergeOrder);
+
+       morder->join_operator = ((Oper*)clause->oper)->opno;
+       morder->left_operator = leftOp;
+       morder->right_operator = rightOp;
+       morder->left_type = (get_leftop(clause))->vartype;
+       morder->right_type = (get_rightop(clause))->vartype;
+       return (morder);
+    } else 
+       return(NULL);
+}
+
+/*    
+ * hashjoinop--
+ *    Returns the hashjoin operator of an operator iff 'clause' is
+ *    hashjoinable, i.e., both operands are single vars and the operator is
+ *    a hashjoinable operator.
+ */
+static Oid
+hashjoinop(Expr *clause)
+{
+    return(op_hashjoinable(((Oper*)clause->oper)->opno,
+                          (get_leftop(clause))->vartype,
+                          (get_rightop(clause))->vartype));
+}
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
new file mode 100644 (file)
index 0000000..d1d37ef
--- /dev/null
@@ -0,0 +1,422 @@
+/*-------------------------------------------------------------------------
+ *
+ * planmain.c--
+ *    Routines to plan a single query
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/plannodes.h"
+#include "nodes/parsenodes.h"
+#include "nodes/relation.h"
+
+#include "optimizer/planmain.h"
+#include "optimizer/internal.h"
+#include "optimizer/paths.h"
+#include "optimizer/clauses.h"
+#include "optimizer/keys.h"
+#include "optimizer/tlist.h"
+#include "optimizer/xfunc.h"
+#include "optimizer/cost.h"
+
+#include "tcop/dest.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "nodes/memnodes.h"
+#include "utils/mcxt.h"
+#include "utils/lsyscache.h"
+
+static Plan *subplanner(Query *root, List *flat_tlist, List *qual);
+static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
+
+static Plan *make_groupPlan(List *tlist, bool tuplePerGroup,
+                           List *groupClause, Plan *subplan);
+
+/*    
+ * query_planner--
+ *    Routine to create a query plan.  It does so by first creating a
+ *    subplan for the topmost level of attributes in the query.  Then,
+ *    it modifies all target list and qualifications to consider the next
+ *    level of nesting and creates a plan for this modified query by
+ *    recursively calling itself.  The two pieces are then merged together
+ *    by creating a result node that indicates which attributes should
+ *    be placed where and any relation level qualifications to be
+ *    satisfied.
+ *    
+ *    command-type is the query command, e.g., retrieve, delete, etc.
+ *    tlist is the target list of the query
+ *    qual is the qualification of the query
+ *    
+ *    Returns a query plan.
+ */
+Plan *
+query_planner(Query *root,
+             int command_type,
+             List *tlist,
+             List *qual)
+{
+    List       *constant_qual = NIL;
+    List       *flattened_tlist = NIL;
+    List       *level_tlist = NIL;
+    Plan       *subplan = (Plan*)NULL;
+    Agg                *aggplan = NULL;
+    
+    /*
+     * A command without a target list or qualification is an error,
+     * except for "delete foo".
+     */
+    if (tlist==NIL && qual==NULL) {
+       if (command_type == CMD_DELETE ||
+           /* Total hack here. I don't know how to handle
+              statements like notify in action bodies.
+              Notify doesn't return anything but
+              scans a system table. */
+           command_type == CMD_NOTIFY) {
+           return ((Plan*)make_seqscan(NIL,
+                                       NIL,
+                                       root->resultRelation,
+                                       (Plan*)NULL));
+       } else
+           return((Plan*)NULL);
+    }
+    
+    /*
+     * Pull out any non-variable qualifications so these can be put in
+     * the topmost result node.  The opids for the remaining
+     * qualifications will be changed to regprocs later.
+     */
+    qual = pull_constant_clauses(qual, &constant_qual);
+    fix_opids(constant_qual);
+    
+    /*
+     * Create a target list that consists solely of (resdom var) target
+     * list entries, i.e., contains no arbitrary expressions.
+     */
+    flattened_tlist = flatten_tlist(tlist);
+    if (flattened_tlist) {
+       level_tlist = flattened_tlist;
+    } else {
+       /* from old code. the logic is beyond me. - ay 2/95 */
+       level_tlist = tlist;
+    }
+
+    /*
+     * Needs to add the group attribute(s) to the target list so that they
+     * are available to either the Group node or the Agg node. (The target
+     * list may not contain the group attribute(s).)
+     */
+    if (root->groupClause) {
+       AddGroupAttrToTlist(level_tlist, root->groupClause);
+    }
+    
+    if (root->qry_aggs) {
+       aggplan = make_agg(tlist, root->qry_numAgg, root->qry_aggs);
+       tlist = level_tlist;
+    }
+
+    /*
+     * A query may have a non-variable target list and a non-variable
+     * qualification only under certain conditions:
+     *    - the query creates all-new tuples, or
+     *    - the query is a replace (a scan must still be done in this case).
+     */
+    if (flattened_tlist==NULL && qual==NULL) {
+
+       switch (command_type) {
+       case CMD_SELECT: 
+       case CMD_INSERT:
+           return ((Plan*)make_result(tlist,
+                                      (Node*)constant_qual,
+                                      (Plan*)NULL));
+           break;
+
+       case CMD_DELETE:
+       case CMD_UPDATE:
+           {
+               SeqScan *scan = make_seqscan(tlist,
+                                            (List *)NULL,
+                                            root->resultRelation,
+                                            (Plan*)NULL);
+               if (constant_qual!=NULL) {
+                   return ((Plan*)make_result(tlist,
+                                              (Node*)constant_qual,
+                                              (Plan*)scan));
+               } else {
+                   return ((Plan*)scan);
+               } 
+           }
+           break;
+              
+       default:
+           return ((Plan*)NULL);
+       }
+    }
+
+    /*
+     * Find the subplan (access path) and destructively modify the
+     * target list of the newly created subplan to contain the appropriate
+     * join references.
+     */
+    subplan = subplanner(root, level_tlist, qual);
+     
+    set_tlist_references(subplan);
+
+    /*
+     * If we have a GROUP BY clause, insert a group node (with the appropriate
+     * sort node.)
+     */
+    if (root->groupClause != NULL) {
+       bool tuplePerGroup;
+
+       /*
+        * decide whether how many tuples per group the Group node needs
+        * to return. (Needs only one tuple per group if no aggregate is
+        * present. Otherwise, need every tuple from the group to do the
+        * aggregation.)
+        */
+       tuplePerGroup = (aggplan == NULL) ? FALSE : TRUE;
+       
+       subplan =
+           make_groupPlan(tlist, tuplePerGroup, root->groupClause, subplan);
+
+       /* XXX fake it: this works for the Group node too! very very ugly,
+          please change me -ay 2/95 */
+       set_agg_tlist_references((Agg*)subplan);
+    }
+
+    /*
+     * If aggregate is present, insert the agg node 
+     */
+    if (aggplan != NULL) {
+       aggplan->plan.lefttree = subplan;
+       subplan = (Plan*)aggplan;
+
+       /*
+        * set the varno/attno entries to the appropriate references to
+        * the result tuple of the subplans. (We need to set those in the
+        * array of aggreg's in the Agg node also. Even though they're 
+        * pointers, after a few dozen's of copying, they're not the same as
+        * those in the target list.)
+        */
+       set_agg_tlist_references((Agg*)subplan);
+       set_agg_agglist_references((Agg*)subplan);
+
+       tlist = aggplan->plan.targetlist;
+    }
+    
+    /*
+     * Build a result node linking the plan if we have constant quals
+     */
+    if (constant_qual) {
+       Plan *plan;
+
+       plan = (Plan*)make_result(tlist,
+                                 (Node*)constant_qual,
+                                 subplan);
+       /*
+        * Change all varno's of the Result's node target list.
+        */
+       set_result_tlist_references((Result*)plan);
+
+       return (plan);
+    }
+
+    /*
+     * fix up the flattened target list of the plan root node so that
+     * expressions are evaluated.  this forces expression evaluations
+     * that may involve expensive function calls to be delayed to
+     * the very last stage of query execution.  this could be bad.
+     * but it is joey's responsibility to optimally push these
+     * expressions down the plan tree.  -- Wei
+     */
+    subplan->targetlist = flatten_tlist_vars(tlist,
+                                            subplan->targetlist);
+
+    /*
+     * Destructively modify the query plan's targetlist to add fjoin
+     * lists to flatten functions that return sets of base types
+     */
+    subplan->targetlist = generate_fjoin(subplan->targetlist);
+
+    return (subplan);
+}
+
+/*    
+ * subplanner
+ *    
+ *   Subplanner creates an entire plan consisting of joins and scans
+ *   for processing a single level of attributes. 
+ *    
+ *   flat-tlist is the flattened target list
+ *   qual is the qualification to be satisfied
+ *    
+ *   Returns a subplan.
+ *    
+ */
+static Plan *
+subplanner(Query *root,
+          List *flat_tlist,
+          List *qual)
+{
+    Rel *final_relation;
+    List *final_relation_list;
+
+    /* Initialize the targetlist and qualification, adding entries to
+     * *query-relation-list* as relation references are found (e.g., in the
+     *  qualification, the targetlist, etc.)
+     */
+    root->base_relation_list_ = NIL; 
+    root->join_relation_list_ = NIL;
+    initialize_base_rels_list(root, flat_tlist);
+    initialize_base_rels_jinfo(root, qual);
+    add_missing_vars_to_base_rels(root, flat_tlist);
+
+    /* Find all possible scan and join paths.
+     * Mark all the clauses and relations that can be processed using special
+     * join methods, then do the exhaustive path search.
+     */
+    initialize_join_clause_info(root->base_relation_list_);
+    final_relation_list = find_paths(root,
+                                    root->base_relation_list_);
+
+    if (final_relation_list)
+       final_relation = (Rel*)lfirst (final_relation_list);
+    else
+       final_relation = (Rel*)NIL;
+
+#if 0 /* fix xfunc */
+    /*
+     * Perform Predicate Migration on each path, to optimize and correctly
+     * assess the cost of each before choosing the cheapest one.
+     *                                                 -- JMH, 11/16/92
+     *
+     * Needn't do so if the top rel is pruneable: that means there's no
+     * expensive functions left to pull up.  -- JMH, 11/22/92
+     */
+    if (XfuncMode != XFUNC_OFF && XfuncMode != XFUNC_NOPM && 
+       XfuncMode != XFUNC_NOPULL && !final_relation->pruneable)
+       {
+           List *pathnode;
+           foreach(pathnode, final_relation->pathlist)
+               {
+                   if (xfunc_do_predmig((Path*)lfirst(pathnode)))
+                       set_cheapest(final_relation, final_relation->pathlist);
+               }
+       }
+#endif
+    
+    /*
+     * Determine the cheapest path and create a subplan corresponding to it.
+     */
+    if (final_relation) {
+       return (create_plan ((Path*)final_relation->cheapestpath));
+    }else {
+       elog(NOTICE, "final relation is nil");
+       return(create_plan ((Path*)NULL));
+    }
+    
+}
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+static Result *
+make_result(List *tlist,
+           Node *resconstantqual,
+           Plan *subplan)
+{
+    Result *node = makeNode(Result);
+    Plan *plan = &node->plan;
+
+    tlist = generate_fjoin(tlist);
+    plan->cost = 0.0;
+    plan->state = (EState *)NULL;
+    plan->targetlist = tlist;
+    plan->lefttree = subplan;
+    plan->righttree = NULL;
+    node->resconstantqual = resconstantqual;
+    node->resstate = NULL;
+    
+    return(node);
+} 
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+static Plan *
+make_groupPlan(List *tlist,
+              bool tuplePerGroup,
+              List *groupClause,
+              Plan *subplan)
+{
+    List *sort_tlist;
+    List *gl;
+    int keyno;
+    Sort *sortplan;
+    Group *grpplan;
+    int numCols;
+    AttrNumber *grpColIdx;
+
+    numCols = length(groupClause);
+    grpColIdx = (AttrNumber *)palloc(sizeof(AttrNumber)*numCols);
+
+    /*
+     * first, make a sort node. Group node expects the tuples it gets
+     * from the subplan is in the order as specified by the group columns.
+     */
+    keyno = 1;
+    sort_tlist = new_unsorted_tlist(subplan->targetlist);
+
+    {
+       /* if this is a mergejoin node, varno could be OUTER/INNER */
+       List *l;
+       foreach(l, sort_tlist) {
+           TargetEntry *tle;
+           tle = lfirst(l);
+           ((Var*)tle->expr)->varno = 1;
+       }
+    }
+    
+    foreach (gl, groupClause) {
+       GroupClause *grpcl = (GroupClause*)lfirst(gl);
+       TargetEntry *tle;
+
+       tle = match_varid(grpcl->grpAttr, sort_tlist);
+       /*
+        * the parser should have checked to make sure the group attribute
+        * is valid but the optimizer might have screwed up and hence we
+        * check again.
+        */
+       if (tle==NULL) {
+           elog(WARN, "group attribute disappeared from target list");
+       }
+       tle->resdom->reskey = keyno;
+       tle->resdom->reskeyop = get_opcode(grpcl->grpOpoid);
+
+       grpColIdx[keyno-1] = tle->resdom->resno;
+       keyno++;
+    }
+    sortplan = make_sort(sort_tlist,
+                        _TEMP_RELATION_ID_,
+                        subplan,
+                        numCols);
+    sortplan->plan.cost = subplan->cost;       /* XXX assume no cost */
+
+    /*
+     * make the Group node
+     */
+    tlist = copyObject(tlist); /* make a copy */
+    grpplan = make_group(tlist, tuplePerGroup, numCols, grpColIdx, sortplan);
+    
+    return (Plan*)grpplan;
+}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
new file mode 100644 (file)
index 0000000..a3b11a3
--- /dev/null
@@ -0,0 +1,408 @@
+/*-------------------------------------------------------------------------
+ *
+ * planner.c--
+ *    The query optimizer external interface.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/plannodes.h"
+#include "nodes/parsenodes.h"
+#include "nodes/relation.h"
+
+#include "parser/catalog_utils.h"
+#include "parser/parse_query.h"
+#include "utils/elog.h"
+#include "utils/lsyscache.h"
+#include "access/heapam.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/planner.h"
+#include "optimizer/plancat.h"   
+#include "optimizer/prep.h"
+#include "optimizer/planmain.h"
+#include "optimizer/paths.h"
+#include "optimizer/cost.h"
+
+/* DATA STRUCTURE CREATION/MANIPULATION ROUTINES */
+#include "nodes/relation.h"
+#include "optimizer/clauseinfo.h"
+#include "optimizer/joininfo.h"
+#include "optimizer/keys.h"
+#include "optimizer/ordering.h"
+#include "optimizer/pathnode.h"
+#include "optimizer/clauses.h"
+#include "optimizer/tlist.h"
+#include "optimizer/var.h"
+
+#include "executor/executor.h"
+
+static Plan *make_sortplan(List *tlist, List *sortcls, Plan *plannode);
+static Plan *init_query_planner(Query *parse);
+static Existential *make_existential(Plan *left, Plan *right);
+
+/*****************************************************************************
+ *
+ *     Query optimizer entry point
+ *    
+ *****************************************************************************/
+
+
+/*    
+ * planner--
+ *    Main query optimizer routine.
+ *    
+ *    Invokes the planner on union queries if there are any left,
+ *    recursing if necessary to get them all, then processes normal plans.
+ *    
+ * Returns a query plan.
+ *    
+ */
+Plan* 
+planner(Query *parse)
+{
+    List *tlist = parse->targetList;
+    List *rangetable = parse->rtable;
+    char* uniqueflag = parse->uniqueFlag;
+    List *sortclause = parse->sortClause;
+    Plan *special_plans = (Plan*)NULL;
+
+    Plan *result_plan = (Plan*) NULL;
+
+    int rt_index;
+
+    /*
+     * plan inheritance
+     */
+    rt_index = first_matching_rt_entry(rangetable, INHERITS_FLAG);
+    if (rt_index != -1) {
+       special_plans = (Plan *)plan_union_queries((Index)rt_index,
+                                                  parse,
+                                                  INHERITS_FLAG);
+    }
+
+    /*
+     * plan archive queries
+     */
+    rt_index = first_matching_rt_entry(rangetable, ARCHIVE_FLAG);
+    if (rt_index != -1) {
+       special_plans = (Plan *)plan_union_queries((Index)rt_index,
+                                                  parse,
+                                                  ARCHIVE_FLAG);
+    }
+
+    if (special_plans)
+      result_plan = special_plans;
+    else
+      result_plan = init_query_planner(parse); /* regular plans */
+    
+    /*
+     *  For now, before we hand back the plan, check to see if there
+     *  is a user-specified sort that needs to be done.  Eventually, this 
+     *  will be moved into the guts of the planner s.t. user specified 
+     *  sorts will be considered as part of the planning process. 
+     *  Since we can only make use of user-specified sorts in
+     *  special cases, we can do the optimization step later.
+     */
+
+    if (uniqueflag) {
+      Plan *sortplan = make_sortplan(tlist, sortclause, result_plan);
+                                         
+      return((Plan*)make_unique(tlist,sortplan,uniqueflag));
+    } else {
+      if (sortclause) 
+       return(make_sortplan(tlist,sortclause,result_plan));
+      else 
+       return((Plan*)result_plan);
+    }
+
+}
+
+/*
+ * make_sortplan--
+ *    Returns a sortplan which is basically a SORT node attached to the
+ *    top of the plan returned from the planner.  It also adds the 
+ *     cost of sorting into the plan.
+ *      
+ * sortkeys: ( resdom1 resdom2 resdom3 ...)
+ * sortops:  (sortop1 sortop2 sortop3 ...)
+ */
+static Plan *
+make_sortplan(List *tlist, List *sortcls, Plan *plannode)
+{
+    Plan *sortplan = (Plan*)NULL;
+    List *temp_tlist = NIL;
+    List *i = NIL;
+    Resdom *resnode = (Resdom*)NULL;
+    Resdom *resdom = (Resdom*)NULL;
+    int keyno =1;
+
+    /*  First make a copy of the tlist so that we don't corrupt the 
+     *  the original .
+     */
+  
+    temp_tlist = new_unsorted_tlist(tlist);
+  
+    foreach (i, sortcls) {
+       SortClause *sortcl = (SortClause*)lfirst(i);
+
+       resnode = sortcl->resdom;
+       resdom = tlist_resdom(temp_tlist, resnode);
+
+       /*    Order the resdom keys and replace the operator OID for each 
+        *    key with the regproc OID. 
+        */
+       resdom->reskey = keyno;
+       resdom->reskeyop = get_opcode(sortcl->opoid);
+       keyno += 1;
+    }
+
+    sortplan = (Plan*)make_sort(temp_tlist,
+                               _TEMP_RELATION_ID_,
+                               (Plan*)plannode,
+                               length(sortcls));
+
+    /*
+     *  XXX Assuming that an internal sort has no. cost. 
+     *      This is wrong, but given that at this point, we don't
+     *      know the no. of tuples returned, etc, we can't do
+     *      better than to add a constant cost.
+     *      This will be fixed once we move the sort further into the planner,
+     *      but for now ... functionality....
+     */
+
+    sortplan->cost = plannode->cost;
+  
+    return(sortplan);
+}
+
+
+/*    
+ * init-query-planner--
+ *    Deals with all non-union preprocessing, including existential
+ *    qualifications and CNFifying the qualifications.
+ *    
+ * Returns a query plan.
+ * MODIFIES: tlist,qual
+ *    
+ */
+static Plan *
+init_query_planner(Query *root)
+{
+    List *primary_qual;
+    List *existential_qual;
+    Existential *exist_plan;
+    List *tlist = root->targetList;
+
+    tlist = preprocess_targetlist(tlist,
+                                 root->commandType,
+                                 root->resultRelation,
+                                 root->rtable);
+
+    primary_qual =
+       preprocess_qualification((Expr*)root->qual,
+                                tlist,
+                                &existential_qual);
+
+    if(existential_qual==NULL) {
+       return(query_planner(root,
+                            root->commandType,
+                            tlist,
+                            primary_qual));
+    } else {
+       int temp = root->commandType;
+       Plan *existential_plan;
+
+       root->commandType = CMD_SELECT;
+       existential_plan = query_planner(root,
+                                        temp, 
+                                        NIL,
+                                        existential_qual);
+     
+       exist_plan = make_existential(existential_plan,
+                                     query_planner(root,
+                                                   root->commandType,
+                                                   tlist,
+                                                   primary_qual));
+       return((Plan*)exist_plan);
+    }
+}
+
+/* 
+ * make_existential--
+ *    Instantiates an existential plan node and fills in 
+ *    the left and right subtree slots.
+ */
+static Existential *
+make_existential(Plan *left, Plan *right)
+{
+    Existential *node = makeNode(Existential);
+
+    node->lefttree = left;
+    node->righttree = left;
+    return(node);
+}
+
+/*
+ * pg_checkretval() -- check return value of a list of sql parse
+ *                     trees.
+ *
+ * The return value of a sql function is the value returned by
+ * the final query in the function.  We do some ad-hoc define-time
+ * type checking here to be sure that the user is returning the
+ * type he claims.
+ */
+void
+pg_checkretval(Oid rettype, QueryTreeList *queryTreeList)
+{
+    Query *parse;
+    List *tlist;
+    List *rt;
+    int cmd;
+    Type typ;
+    Resdom *resnode;
+    Relation reln;
+    Oid relid;
+    Oid tletype;
+    int relnatts;
+    int i;
+
+    /* find the final query */
+    parse = queryTreeList->qtrees[queryTreeList->len - 1];
+
+    /*
+     *  test 1:  if the last query is a utility invocation, then there
+     *  had better not be a return value declared.
+     */
+    if (parse->commandType == CMD_UTILITY) {
+       if (rettype == InvalidOid)
+           return;
+       else
+           elog(WARN, "return type mismatch in function decl: final query is a catalog utility");
+    }
+
+    /* okay, it's an ordinary query */
+    tlist = parse->targetList;
+    rt = parse->rtable;
+    cmd = parse->commandType;
+    
+    /*
+     *  test 2:  if the function is declared to return no value, then the
+     *  final query had better not be a retrieve.
+     */
+    if (rettype == InvalidOid) {
+       if (cmd == CMD_SELECT)
+           elog(WARN,
+                "function declared with no return type, but final query is a retrieve");
+       else
+           return;
+    }
+
+    /* by here, the function is declared to return some type */
+    if ((typ = (Type)get_id_type(rettype)) == NULL)
+       elog(WARN, "can't find return type %d for function\n", rettype);
+
+    /*
+     *  test 3:  if the function is declared to return a value, then the
+     *  final query had better be a retrieve.
+     */
+    if (cmd != CMD_SELECT)
+       elog(WARN, "function declared to return type %s, but final query is not a retrieve", tname(typ));
+
+    /*
+     *  test 4:  for base type returns, the target list should have exactly
+     *  one entry, and its type should agree with what the user declared.
+     */
+
+    if (get_typrelid(typ) == InvalidOid) {
+       if (exec_tlist_length(tlist) > 1)
+           elog(WARN, "function declared to return %s returns multiple values in final retrieve", tname(typ));
+
+       resnode = (Resdom*) ((TargetEntry*)lfirst(tlist))->resdom;
+       if (resnode->restype != rettype)
+           elog(WARN, "return type mismatch in function: declared to return %s, returns %s", tname(typ), tname(get_id_type(resnode->restype)));
+
+       /* by here, base return types match */
+       return;
+    }
+
+    /*
+     *  If the target list is of length 1, and the type of the varnode
+     *  in the target list is the same as the declared return type, this
+     *  is okay.  This can happen, for example, where the body of the
+     *  function is 'retrieve (x = func2())', where func2 has the same
+     *  return type as the function that's calling it.
+     */
+    if (exec_tlist_length(tlist) == 1) {
+       resnode = (Resdom*) ((TargetEntry*)lfirst(tlist))->resdom;
+       if (resnode->restype == rettype)
+           return;
+    }
+
+    /*
+     *  By here, the procedure returns a (set of) tuples.  This part of
+     *  the typechecking is a hack.  We look up the relation that is
+     *  the declared return type, and be sure that attributes 1 .. n
+     *  in the target list match the declared types.
+     */
+    reln = heap_open(get_typrelid(typ));
+
+    if (!RelationIsValid(reln))
+       elog(WARN, "cannot open relation relid %d", get_typrelid(typ));
+
+    relid = reln->rd_id;
+    relnatts = reln->rd_rel->relnatts;
+
+    if (exec_tlist_length(tlist) != relnatts)
+       elog(WARN, "function declared to return type %s does not retrieve (%s.*)", tname(typ), tname(typ));
+
+    /* expect attributes 1 .. n in order */
+    for (i = 1; i <= relnatts; i++) {
+       TargetEntry *tle = lfirst(tlist);
+       Node *thenode = tle->expr;
+
+       tlist = lnext(tlist);
+       tletype = exprType(thenode);
+
+#if 0 /* fix me */
+       /* this is tedious */
+       if (IsA(thenode,Var))
+           tletype = (Oid) ((Var*)thenode)->vartype;
+       else if (IsA(thenode,Const))
+           tletype = (Oid) ((Const*)thenode)->consttype;
+       else if (IsA(thenode,Param)) {
+           tletype = (Oid) ((Param*)thenode)->paramtype;
+       else if (IsA(thenode,Expr)) {
+           tletype = Expr
+       }
+       } else if (IsA(thenode,LispList)) {
+           thenode = lfirst(thenode);
+           if (IsA(thenode,Oper))
+               tletype = (Oid) get_opresulttype((Oper*)thenode);
+           else if (IsA(thenode,Func))
+               tletype = (Oid) get_functype((Func*)thenode);
+           else
+               elog(WARN, "function declared to return type %s does not retrieve (%s.all)", tname(typ), tname(typ));
+#endif
+/*
+       } else
+           elog(WARN, "function declared to return type %s does not retrieve (%s.all)", tname(typ), tname(typ));
+*/
+       /* reach right in there, why don't you? */
+       if (tletype != reln->rd_att->attrs[i-1]->atttypid)
+           elog(WARN, "function declared to return type %s does not retrieve (%s.all)", tname(typ), tname(typ));
+    }
+
+    heap_close(reln);
+
+    /* success */
+    return;
+}
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
new file mode 100644 (file)
index 0000000..8c9b7d5
--- /dev/null
@@ -0,0 +1,706 @@
+/*-------------------------------------------------------------------------
+ *
+ * setrefs.c--
+ *    Routines to change varno/attno entries to contain references
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/plannodes.h"
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+
+#include "utils/elog.h"
+#include "nodes/nodeFuncs.h"
+#include "nodes/makefuncs.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/clauses.h"
+#include "optimizer/clauseinfo.h"
+#include "optimizer/keys.h"
+#include "optimizer/planmain.h"
+#include "optimizer/tlist.h"
+#include "optimizer/var.h"
+#include "optimizer/tlist.h"
+
+static void set_join_tlist_references(Join *join);
+static void set_tempscan_tlist_references(SeqScan *tempscan);
+static void set_temp_tlist_references(Temp *temp);
+static List *replace_clause_joinvar_refs(Expr *clause,
+                            List *outer_tlist, List *inner_tlist);
+static List *replace_subclause_joinvar_refs(List *clauses,
+                               List *outer_tlist, List *inner_tlist);
+static Var *replace_joinvar_refs(Var *var, List *outer_tlist, List *inner_tlist);
+static List *tlist_temp_references(Oid tempid, List *tlist);
+static void replace_result_clause(List *clause, List *subplanTargetList);
+static bool OperandIsInner(Node *opnd, int inner_relid);
+static void replace_agg_clause(Node *expr, List *targetlist);
+
+/*****************************************************************************
+ *     
+ *             SUBPLAN REFERENCES
+ *     
+ *****************************************************************************/
+
+/*    
+ * set-tlist-references--
+ *    Modifies the target list of nodes in a plan to reference target lists
+ *    at lower levels.
+ *    
+ * 'plan' is the plan whose target list and children's target lists will
+ *     be modified
+ *    
+ * Returns nothing of interest, but modifies internal fields of nodes.
+ *    
+ */
+void
+set_tlist_references(Plan *plan)
+{
+    if(plan==NULL)
+       return;
+
+    if (IsA_Join(plan))  {
+       set_join_tlist_references((Join*)plan);
+    } else if (IsA(plan,SeqScan) && plan->lefttree && 
+              IsA_Temp(plan->lefttree)) {
+       set_tempscan_tlist_references((SeqScan*)plan);
+    } else if (IsA(plan,Sort)) {
+       set_temp_tlist_references ((Temp*)plan);
+    } else if (IsA(plan,Result)) {
+       set_result_tlist_references((Result*)plan);
+    } else if (IsA(plan,Hash)) {
+       set_tlist_references(plan->lefttree);
+    } else if (IsA(plan,Choose)) {
+       List *x;
+       foreach (x, ((Choose*)plan)->chooseplanlist) {
+           set_tlist_references((Plan*)lfirst(x));
+       }
+    }
+}
+
+/*    
+ * set-join-tlist-references--
+ *    Modifies the target list of a join node by setting the varnos and
+ *    varattnos to reference the target list of the outer and inner join
+ *    relations.
+ *    
+ *    Creates a target list for a join node to contain references by setting 
+ *    varno values to OUTER or INNER and setting attno values to the 
+ *    result domain number of either the corresponding outer or inner join 
+ *    tuple.
+ *    
+ * 'join' is a join plan node
+ *    
+ * Returns nothing of interest, but modifies internal fields of nodes.
+ *    
+ */
+static void
+set_join_tlist_references(Join *join)
+{
+    Plan       *outer = ((Plan*)join)->lefttree;
+    Plan       *inner = ((Plan*)join)->righttree;
+    List       *new_join_targetlist = NIL;
+    TargetEntry *temp = (TargetEntry *)NULL;
+    List       *entry = NIL;
+    List       *inner_tlist = NULL;
+    List       *outer_tlist = NULL;
+    TargetEntry *xtl =         (TargetEntry *)NULL;
+    List       *qptlist = ((Plan*)join)->targetlist;
+
+    foreach(entry, qptlist) {
+       List *joinvar;
+
+       xtl = (TargetEntry *)lfirst(entry);
+       inner_tlist = ((inner==NULL) ? NIL : inner->targetlist);
+       outer_tlist = ((outer==NULL) ? NIL : outer->targetlist);
+       joinvar = replace_clause_joinvar_refs((Expr*)get_expr(xtl),
+                                             outer_tlist,
+                                             inner_tlist);
+
+       temp = MakeTLE(xtl->resdom, (Node*)joinvar);
+       new_join_targetlist = lappend(new_join_targetlist,temp);
+    }
+       
+    ((Plan*)join)->targetlist = new_join_targetlist;
+    if (outer!=NULL)
+       set_tlist_references(outer);
+    if (inner!=NULL)
+       set_tlist_references(inner);
+}
+
+/*    
+ * set-tempscan-tlist-references--
+ *    Modifies the target list of a node that scans a temp relation (i.e., a
+ *    sort or hash node) so that the varnos refer to the child temporary.
+ *    
+ * 'tempscan' is a seqscan node
+ *    
+ * Returns nothing of interest, but modifies internal fields of nodes.
+ *    
+ */
+static void
+set_tempscan_tlist_references(SeqScan *tempscan)
+{
+    Temp *temp = (Temp*)((Plan*)tempscan)->lefttree;
+
+    ((Plan*)tempscan)->targetlist =
+       tlist_temp_references(temp->tempid,
+                             ((Plan*)tempscan)->targetlist);
+    set_temp_tlist_references(temp);
+}
+
+/*    
+ * set-temp-tlist-references--
+ *    The temp's vars are made consistent with (actually, identical to) the
+ *    modified version of the target list of the node from which temp node
+ *    receives its tuples.
+ *    
+ * 'temp' is a temp (e.g., sort, hash) plan node
+ *    
+ * Returns nothing of interest, but modifies internal fields of nodes.
+ *    
+ */
+static void
+set_temp_tlist_references(Temp *temp)
+{
+    Plan *source = ((Plan*)temp)->lefttree;
+
+    if (source!=NULL) {
+       set_tlist_references(source);
+       ((Plan*)temp)->targetlist =
+           copy_vars(((Plan*)temp)->targetlist ,
+                     (source)->targetlist);
+    } else {
+       elog(WARN, "calling set_temp_tlist_references with empty lefttree");
+    }
+}
+
+/*    
+ * join-references--
+ *     Creates a new set of join clauses by replacing the varno/varattno
+ *     values of variables in the clauses to reference target list values
+ *     from the outer and inner join relation target lists.
+ *    
+ * 'clauses' is the list of join clauses
+ * 'outer-tlist' is the target list of the outer join relation
+ * 'inner-tlist' is the target list of the inner join relation
+ *    
+ * Returns the new join clauses.
+ *    
+ */
+List *
+join_references(List *clauses,
+               List *outer_tlist,
+               List *inner_tlist)
+{
+    return (replace_subclause_joinvar_refs(clauses,
+                                          outer_tlist,
+                                          inner_tlist));
+}
+
+/*    
+ * index-outerjoin-references--
+ *    Given a list of join clauses, replace the operand corresponding to the
+ *    outer relation in the join with references to the corresponding target
+ *    list element in 'outer-tlist' (the outer is rather obscurely
+ *    identified as the side that doesn't contain a var whose varno equals
+ *    'inner-relid').
+ *    
+ *    As a side effect, the operator is replaced by the regproc id.
+ *    
+ * 'inner-indxqual' is the list of join clauses (so-called because they
+ * are used as qualifications for the inner (inbex) scan of a nestloop)
+ *    
+ * Returns the new list of clauses.
+ *    
+ */
+List *
+index_outerjoin_references(List *inner_indxqual,
+                          List *outer_tlist,
+                          Index inner_relid)
+{
+    List *t_list = NIL;
+    Expr *temp = NULL;
+    List *t_clause = NIL;
+    Expr *clause = NULL;
+
+    foreach (t_clause,inner_indxqual) {
+       clause = lfirst(t_clause);
+       /*
+        * if inner scan on the right.
+        */
+       if (OperandIsInner((Node*)get_rightop(clause), inner_relid)) {
+           Var *joinvar = (Var*)
+               replace_clause_joinvar_refs((Expr*)get_leftop(clause),
+                                           outer_tlist,
+                                           NIL);
+           temp = make_opclause(replace_opid((Oper*)((Expr*)clause)->oper),
+                                joinvar,
+                                get_rightop(clause));
+           t_list = lappend(t_list,temp);
+       } else {
+           /* inner scan on left */
+           Var *joinvar = (Var*)
+               replace_clause_joinvar_refs((Expr*)get_rightop(clause),
+                                           outer_tlist,
+                                           NIL);
+           temp = make_opclause(replace_opid((Oper*)((Expr*)clause)->oper),
+                                joinvar,
+                                get_leftop(clause));
+           t_list = lappend(t_list,temp);
+       } 
+       
+    }
+    return(t_list);
+}
+
+/*    
+ * replace-clause-joinvar-refs
+ * replace-subclause-joinvar-refs
+ * replace-joinvar-refs
+ *    
+ *    Replaces all variables within a join clause with a new var node
+ *    whose varno/varattno fields contain a reference to a target list
+ *    element from either the outer or inner join relation.
+ *    
+ * 'clause' is the join clause
+ * 'outer-tlist' is the target list of the outer join relation
+ * 'inner-tlist' is the target list of the inner join relation
+ *    
+ * Returns the new join clause.
+ *    
+ */
+static List *
+replace_clause_joinvar_refs(Expr *clause,
+                           List *outer_tlist,
+                           List *inner_tlist)
+{
+    List *temp = NULL;
+
+    if(IsA (clause,Var)) {
+       temp = (List*)replace_joinvar_refs((Var*)clause,
+                                          outer_tlist,inner_tlist);
+       if(temp)
+           return(temp);
+       else
+           if (clause != NULL)
+               return((List*)clause);
+           else
+               return(NIL);
+    } else if (single_node((Node*)clause)) {
+       return ((List*)clause);
+    } else if (or_clause((Node*)clause)) {
+       List *orclause =
+           replace_subclause_joinvar_refs(((Expr*)clause)->args,
+                                          outer_tlist,
+                                          inner_tlist);
+       return ((List*)make_orclause(orclause));
+    } else if (IsA(clause,ArrayRef)) {
+       ArrayRef *aref = (ArrayRef *)clause;
+
+       temp = replace_subclause_joinvar_refs(aref->refupperindexpr,
+                                             outer_tlist,
+                                             inner_tlist);
+       aref->refupperindexpr = (List*)temp;
+       temp = replace_subclause_joinvar_refs(aref->reflowerindexpr,
+                                             outer_tlist,
+                                             inner_tlist);
+       aref->reflowerindexpr = (List*)temp;
+       temp = replace_clause_joinvar_refs((Expr*)aref->refexpr,
+                                          outer_tlist,
+                                          inner_tlist);
+       aref->refexpr = (Node*)temp;
+
+       /*
+        *  no need to set refassgnexpr.  we only set that in the
+        *  target list on replaces, and this is an array reference
+        *  in the qualification.  if we got this far, it's 0x0 in
+        *  the ArrayRef structure 'clause'.
+        */
+
+       return((List*)clause);
+    } else if (is_funcclause((Node*)clause)) {
+       List *funcclause =
+           replace_subclause_joinvar_refs(((Expr*)clause)->args,
+                                          outer_tlist,
+                                          inner_tlist);
+       return ((List*)make_funcclause((Func*)((Expr*)clause)->oper,
+                                      funcclause));
+    } else if (not_clause((Node*)clause)) {
+       List *notclause =
+           replace_clause_joinvar_refs(get_notclausearg(clause),
+                                       outer_tlist,
+                                       inner_tlist);
+       return ((List*)make_notclause((Expr*)notclause));
+    } else if (is_opclause((Node*)clause)) {
+       Var *leftvar =
+           (Var*)replace_clause_joinvar_refs((Expr*)get_leftop(clause),
+                                            outer_tlist,
+                                            inner_tlist);
+       Var *rightvar =
+           (Var*)replace_clause_joinvar_refs((Expr*)get_rightop(clause),
+                                            outer_tlist,
+                                            inner_tlist);
+       return ((List*)make_opclause(replace_opid((Oper*)((Expr*)clause)->oper),
+                                    leftvar,
+                                    rightvar));
+    }
+    /* shouldn't reach here */
+    return NULL;
+}
+
+static List *
+replace_subclause_joinvar_refs(List *clauses,
+                              List *outer_tlist,
+                              List *inner_tlist)
+{
+    List *t_list = NIL;
+    List *temp = NIL;
+    List *clause = NIL;
+
+    foreach (clause,clauses) {
+       temp = replace_clause_joinvar_refs(lfirst(clause),
+                                          outer_tlist,
+                                          inner_tlist);
+       t_list = lappend(t_list,temp);
+    }
+    return(t_list);
+}
+
+static Var *
+replace_joinvar_refs(Var *var, List *outer_tlist, List *inner_tlist)
+{
+    Resdom *outer_resdom =(Resdom*)NULL;
+
+    outer_resdom= tlist_member(var,outer_tlist);
+
+    if (outer_resdom!=NULL && IsA (outer_resdom,Resdom) ) {
+       return (makeVar (OUTER,
+                        outer_resdom->resno,
+                        var->vartype,
+                        var->varnoold,
+                        var->varoattno));
+    } else {
+       Resdom *inner_resdom;
+       inner_resdom = tlist_member(var,inner_tlist);
+       if ( inner_resdom!=NULL && IsA (inner_resdom,Resdom) ) {
+           return (makeVar (INNER,
+                            inner_resdom->resno,
+                            var->vartype,
+                            var->varnoold,
+                            var->varoattno));
+       } 
+    } 
+    return (Var*)NULL;
+}
+
+/*    
+ * tlist-temp-references--
+ *    Creates a new target list for a node that scans a temp relation,
+ *    setting the varnos to the id of the temp relation and setting varids
+ *    if necessary (varids are only needed if this is a targetlist internal
+ *    to the tree, in which case the targetlist entry always contains a var
+ *    node, so we can just copy it from the temp).
+ *    
+ * 'tempid' is the id of the temp relation
+ * 'tlist' is the target list to be modified
+ *    
+ * Returns new target list
+ *    
+ */
+static List *
+tlist_temp_references(Oid tempid, 
+                     List *tlist)
+{
+    List *t_list = NIL;
+    TargetEntry *temp = (TargetEntry *)NULL;
+    TargetEntry *xtl = NULL;
+    List *entry;
+     
+    foreach (entry, tlist) {
+       AttrNumber oattno;
+
+       xtl = lfirst(entry);
+       if (IsA(get_expr(xtl), Var))
+           oattno = ((Var*)xtl->expr)->varoattno;
+       else
+           oattno = 0;
+        
+       temp = MakeTLE(xtl->resdom,
+                      (Node*)makeVar(tempid,
+                                     xtl->resdom->resno,
+                                     xtl->resdom->restype,
+                                     tempid,
+                                     oattno));
+        
+       t_list = lappend(t_list,temp);
+    }
+    return(t_list);
+}
+
+/*---------------------------------------------------------
+ *
+ * set_result_tlist_references
+ *
+ * Change the target list of a Result node, so that it correctly
+ * addresses the tuples returned by its left tree subplan.
+ *
+ * NOTE:
+ *  1) we ignore the right tree! (in the current implementation
+ *     it is always nil
+ *  2) this routine will probably *NOT* work with nested dot
+ *     fields....
+ */
+void
+set_result_tlist_references(Result *resultNode)
+{
+    Plan *subplan;
+    List *resultTargetList;
+    List *subplanTargetList;
+    List *t;
+    TargetEntry *entry;
+    Expr *expr;
+
+    resultTargetList= ((Plan*)resultNode)->targetlist;
+
+    /*
+     * NOTE: we only consider the left tree subplan.
+     * This is usually a seq scan.
+     */
+    subplan = ((Plan*)resultNode)->lefttree;
+    if (subplan != NULL) {
+       subplanTargetList = subplan->targetlist;
+    } else {
+       subplanTargetList = NIL;
+    }
+
+    /*
+     * now for traverse all the entris of the target list.
+     * These should be of the form (Resdom_Node Expression).
+     * For every expression clause, call "replace_result_clause()"
+     * to appropriatelly change all the Var nodes.
+     */
+    foreach (t, resultTargetList) {
+       entry = (TargetEntry *)lfirst(t);
+       expr = (Expr*) get_expr(entry);
+       replace_result_clause((List*)expr, subplanTargetList);
+    }
+}
+
+/*---------------------------------------------------------
+ *
+ * replace_result_clause
+ *
+ * This routine is called from set_result_tlist_references().
+ * and modifies the expressions of the target list of a Result
+ * node so that all Var nodes reference the target list of its subplan.
+ * 
+ */
+static void
+replace_result_clause(List *clause,
+                     List *subplanTargetList) /* target list of the
+                                                 subplan */
+{
+    List *t;
+    List *subClause;
+    TargetEntry *subplanVar;
+
+    if (IsA(clause,Var)) {
+       /*
+        * Ha! A Var node!
+        */
+       subplanVar = match_varid((Var*)clause, subplanTargetList);
+       /*
+        * Change the varno & varattno fields of the
+        * var node.
+        *
+        */
+       ((Var*)clause)->varno = (Index)OUTER;
+       ((Var*)clause)->varattno = subplanVar->resdom->resno;
+    } else if (is_funcclause((Node*)clause)) {
+       /*
+        * This is a function. Recursively call this routine
+        * for its arguments...
+        */
+       subClause = ((Expr*)clause)->args;
+       foreach (t, subClause) {
+           replace_result_clause(lfirst(t),subplanTargetList);
+          }
+    } else if (IsA(clause,ArrayRef)) {
+       ArrayRef *aref = (ArrayRef *)clause;
+       /*
+        * This is an arrayref. Recursively call this routine
+        * for its expression and its index expression...
+        */
+       subClause = aref->refupperindexpr;
+       foreach (t, subClause) {
+           replace_result_clause(lfirst(t),subplanTargetList);
+       }
+       subClause = aref->reflowerindexpr;
+       foreach (t, subClause) {
+           replace_result_clause(lfirst(t),subplanTargetList);
+       }
+       replace_result_clause((List*)aref->refexpr,
+                             subplanTargetList);
+       replace_result_clause((List*)aref->refassgnexpr,
+                             subplanTargetList);
+    } else if (is_opclause((Node*)clause)) {
+       /*
+        * This is an operator. Recursively call this routine
+        * for both its left and right operands
+        */
+       subClause = (List*)get_leftop((Expr*)clause);
+       replace_result_clause(subClause,subplanTargetList);
+       subClause = (List*)get_rightop((Expr*)clause);
+       replace_result_clause(subClause,subplanTargetList);
+    } else if (IsA(clause,Param) || IsA(clause,Const)) {
+       /* do nothing! */
+    } else {
+       /*
+        * Ooops! we can not handle that!
+        */
+       elog(WARN,"replace_result_clause: Can not handle this tlist!\n");
+    }
+}
+
+static
+bool OperandIsInner(Node *opnd, int inner_relid)
+{
+    /*
+     * Can be the inner scan if its a varnode or a function and the
+     * inner_relid is equal to the varnode's var number or in the 
+     * case of a function the first argument's var number (all args
+     * in a functional index are from the same relation).
+     */
+    if ( IsA (opnd,Var) && 
+       (inner_relid == ((Var*)opnd)->varno) )
+       {
+           return true;
+       }
+    if (is_funcclause(opnd))
+       {
+           List *firstArg = lfirst(((Expr*)opnd)->args);
+
+           if ( IsA (firstArg,Var) &&
+               (inner_relid == ((Var*)firstArg)->varno) )
+               {
+                   return true;
+               }
+       }
+    return false;
+}
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+/*---------------------------------------------------------
+ *
+ * set_agg_tlist_references -
+ *    changes the target list of an Agg node so that it points to
+ *    the tuples returned by its left tree subplan.
+ *
+ */
+void
+set_agg_tlist_references(Agg *aggNode)
+{
+    List *aggTargetList;
+    List *subplanTargetList;
+    List *tl;
+
+    aggTargetList = aggNode->plan.targetlist;
+    subplanTargetList = aggNode->plan.lefttree->targetlist;
+
+    foreach (tl, aggTargetList) {
+       TargetEntry *tle = lfirst(tl);
+
+       replace_agg_clause(tle->expr, subplanTargetList);
+    }
+}
+
+void
+set_agg_agglist_references(Agg *aggNode)
+{
+    List *subplanTargetList;
+    Aggreg **aggs;
+    int i;
+
+    aggs = aggNode->aggs;
+    subplanTargetList = aggNode->plan.lefttree->targetlist;
+
+    for (i = 0; i < aggNode->numAgg; i++) {
+       replace_agg_clause(aggs[i]->target, subplanTargetList);
+    }
+}
+
+static void
+replace_agg_clause(Node *clause, List *subplanTargetList)
+{
+    List *t;
+    TargetEntry *subplanVar;
+
+    if (IsA(clause,Var)) {
+       /*
+        * Ha! A Var node!
+        */
+       subplanVar = match_varid((Var*)clause, subplanTargetList);
+       /*
+        * Change the varno & varattno fields of the
+        * var node.
+        *
+        */
+       ((Var*)clause)->varattno = subplanVar->resdom->resno;
+    } else if (is_funcclause(clause)) {
+       /*
+        * This is a function. Recursively call this routine
+        * for its arguments...
+        */
+       foreach (t, ((Expr*)clause)->args) {
+           replace_agg_clause(lfirst(t), subplanTargetList);
+          }
+    } else if (IsA(clause,Aggreg)) {
+       replace_agg_clause(((Aggreg*)clause)->target, subplanTargetList);
+    } else if (IsA(clause,ArrayRef)) {
+       ArrayRef *aref = (ArrayRef *)clause;
+
+       /*
+        * This is an arrayref. Recursively call this routine
+        * for its expression and its index expression...
+        */
+       foreach (t, aref->refupperindexpr) {
+           replace_agg_clause(lfirst(t),subplanTargetList);
+       }
+       foreach (t, aref->reflowerindexpr) {
+           replace_agg_clause(lfirst(t),subplanTargetList);
+       }
+       replace_agg_clause(aref->refexpr, subplanTargetList);
+       replace_agg_clause(aref->refassgnexpr, subplanTargetList);
+    } else if (is_opclause(clause)) {
+       /*
+        * This is an operator. Recursively call this routine
+        * for both its left and right operands
+        */
+       replace_agg_clause((Node*)get_leftop((Expr*)clause),
+                          subplanTargetList);
+       replace_agg_clause((Node*)get_rightop((Expr*)clause),
+                          subplanTargetList);
+    } else if (IsA(clause,Param) || IsA(clause,Const)) {
+       /* do nothing! */
+    } else {
+       /*
+        * Ooops! we can not handle that!
+        */
+       elog(WARN,"replace_agg_clause: Can not handle this tlist!\n");
+    }
+
+}
+
+
diff --git a/src/backend/optimizer/plancat.h b/src/backend/optimizer/plancat.h
new file mode 100644 (file)
index 0000000..e13a2a2
--- /dev/null
@@ -0,0 +1,65 @@
+/*-------------------------------------------------------------------------
+ *
+ * plancat.h--
+ *    prototypes for plancat.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PLANCAT_H
+#define PLANCAT_H
+
+#include "c.h"
+
+/*
+ * transient data structure to hold return value of index_info. Note that
+ * indexkeys, orderOprs and classlist is "null-terminated".
+ */
+typedef struct IdxInfoRetval {
+    Oid                relid;          /* OID of the index relation (not the OID
+                                * of the relation being indexed)
+                                */
+    Oid         relam;          /* OID of the pg_am of this index */
+    int                pages;          /* number of pages in the index relation */
+    int                tuples;         /* number of tuples in the index relation */
+    int        *indexkeys;     /* keys over which we're indexing */
+    Oid                *orderOprs;     /* operators used for ordering purposes */
+    Oid        *classlist;     /* classes of AM operators */
+    Oid                indproc;
+    Node       *indpred;
+} IdxInfoRetval;
+
+
+extern void relation_info(Query *root,
+                         Oid relid, 
+                         bool *hashindex, int *pages,
+                         int *tuples);
+
+extern bool index_info(Query *root, 
+                      bool first, int relid, IdxInfoRetval *info);
+
+extern Cost
+restriction_selectivity(Oid functionObjectId,
+                       Oid operatorObjectId,
+                       Oid relationObjectId,
+                       AttrNumber attributeNumber,
+                       char *constValue,
+                       int32 constFlag);
+
+extern void
+index_selectivity(Oid indid, Oid *classes, List *opnos,
+                 Oid relid, List *attnos, List *values, List *flags,
+                 int32 nkeys, float *idxPages, float *idxSelec);
+
+extern Cost join_selectivity(Oid functionObjectId, Oid operatorObjectId,
+       Oid relationObjectId1, AttrNumber attributeNumber1,
+       Oid relationObjectId2, AttrNumber attributeNumber2);
+
+extern List *find_inheritance_children(Oid inhparent);
+extern List *VersionGetParents(Oid verrelid);
+
+#endif /* PLANCAT_H */
diff --git a/src/backend/optimizer/planmain.h b/src/backend/optimizer/planmain.h
new file mode 100644 (file)
index 0000000..dadca63
--- /dev/null
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+ *
+ * planmain.h--
+ *    prototypes for various files in optimizer/plan
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PLANMAIN_H
+#define PLANMAIN_H
+
+
+/*
+ * prototypes for plan/planmain.c
+ */
+extern Plan *query_planner(Query *root,
+                          int command_type, List *tlist, List *qual);
+
+
+/*
+ * prototypes for plan/createplan.c
+ */
+extern Plan *create_plan(Path *best_path);
+extern SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid,
+                            Plan *lefttree);
+extern Sort *make_sort(List *tlist, Oid tempid, Plan *lefttree,
+                      int keycount);
+extern Agg *make_agg(List *tlist, int nagg, Aggreg **aggs);
+extern Group *make_group(List *tlist, bool tuplePerGroup, int ngrp,
+                        AttrNumber *grpColIdx, Sort *lefttree);
+extern Unique *make_unique(List *tlist, Plan *lefttree, char *uniqueAttr);
+extern List *generate_fjoin(List *tlist);
+
+
+/*
+ * prototypes for plan/initsplan.c
+ */
+extern void initialize_base_rels_list(Query *root, List *tlist);
+extern void initialize_base_rels_jinfo(Query *root, List *clauses);
+extern void initialize_join_clause_info(List *rel_list);
+extern void add_missing_vars_to_base_rels(Query *root, List *tlist);
+
+/*
+ * prototypes for plan/setrefs.c
+ */
+extern void set_tlist_references(Plan *plan);
+extern List *join_references(List *clauses, List *outer_tlist,
+                                List *inner_tlist);
+extern List *index_outerjoin_references(List *inner_indxqual,
+                           List *outer_tlist, Index inner_relid);
+extern void set_result_tlist_references(Result *resultNode);
+extern void set_agg_tlist_references(Agg *aggNode);
+extern void set_agg_agglist_references(Agg *aggNode);
+
+
+#endif /* PLANMAIN_H */
diff --git a/src/backend/optimizer/planner.h b/src/backend/optimizer/planner.h
new file mode 100644 (file)
index 0000000..78e3278
--- /dev/null
@@ -0,0 +1,24 @@
+/*-------------------------------------------------------------------------
+ *
+ * planner.h--
+ *    prototypes for planner.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PLANNER_H
+#define PLANNER_H
+
+/*
+#include "optimizer/internal.h"
+#include "parser/parse_query.h"
+*/
+
+extern Plan *planner(Query *parse);
+extern void pg_checkretval(Oid rettype, QueryTreeList *querytree_list);
+
+#endif /* PLANNER_H */
diff --git a/src/backend/optimizer/prep.h b/src/backend/optimizer/prep.h
new file mode 100644 (file)
index 0000000..efdddc4
--- /dev/null
@@ -0,0 +1,51 @@
+/*-------------------------------------------------------------------------
+ *
+ * prep.h--
+ *    prototypes for files in prep.c
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PREP_H
+#define        PREP_H
+
+#include "nodes/primnodes.h"
+#include "nodes/plannodes.h"
+
+/*
+ * prototypes for archive.h
+ */
+extern void plan_archive(List *rt);
+extern List *find_archive_rels(Oid relid);
+
+/*
+ * prototypes for prepqual.h
+ */
+extern List *preprocess_qualification(Expr *qual, List *tlist,
+                                     List **existentialQualPtr);
+extern List *cnfify(Expr *qual, bool removeAndFlag);
+
+/*
+ * prototypes for preptlist.h
+ */
+extern List *preprocess_targetlist(List *tlist, int command_type,
+                         Index result_relation, List *range_table);
+
+/*
+ * prototypes for prepunion.h
+ */
+typedef enum UnionFlag {
+    INHERITS_FLAG, ARCHIVE_FLAG, VERSION_FLAG
+} UnionFlag;
+
+extern List *find_all_inheritors(List *unexamined_relids,
+                                List *examined_relids);
+extern int first_matching_rt_entry(List *rangetable, UnionFlag flag);
+extern Append *plan_union_queries(Index rt_index, Query *parse,
+                                 UnionFlag flag); 
+
+#endif /* PREP_H */
diff --git a/src/backend/optimizer/prep/Makefile.inc b/src/backend/optimizer/prep/Makefile.inc
new file mode 100644 (file)
index 0000000..051bdff
--- /dev/null
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for optimizer/prep
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= archive.c prepqual.c preptlist.c prepunion.c
diff --git a/src/backend/optimizer/prep/archive.c b/src/backend/optimizer/prep/archive.c
new file mode 100644 (file)
index 0000000..1c42980
--- /dev/null
@@ -0,0 +1,66 @@
+/*-------------------------------------------------------------------------
+ *
+ * archive.c--
+ *    Support for planning scans on archived relations
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>             /* for sprintf() */
+#include <sys/types.h>         /* for u_int in relcache.h */
+#include "postgres.h"
+
+#include "utils/rel.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/relcache.h"
+#include "catalog/pg_class.h"
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+#include "optimizer/prep.h"
+#include "commands/creatinh.h"
+
+void
+plan_archive(List *rt)
+{
+    List *rtitem;
+    RangeTblEntry *rte;
+    TimeRange *trange;
+    Relation r;
+    Oid reloid;
+
+    foreach(rtitem, rt) {
+       rte = lfirst(rtitem);
+       trange = rte->timeRange;
+       if (trange) {
+           reloid = rte->relid;
+           r = RelationIdGetRelation(reloid);
+           if (r->rd_rel->relarch != 'n') {
+               rte->archive = true;
+           }
+       }
+    }
+}
+
+
+/*
+ *  find_archive_rels -- Given a particular relid, find the archive
+ *                      relation's relid.
+ */
+List *
+find_archive_rels(Oid relid)
+{
+    Relation arel;
+    char *arelName;
+
+    arelName = MakeArchiveName(relid);
+    arel = RelationNameGetRelation(arelName);
+    pfree(arelName);
+
+    return lconsi(arel->rd_id, lconsi(relid, NIL));
+}
diff --git a/src/backend/optimizer/prep/prepqual.c b/src/backend/optimizer/prep/prepqual.c
new file mode 100644 (file)
index 0000000..1a26e86
--- /dev/null
@@ -0,0 +1,582 @@
+/*-------------------------------------------------------------------------
+ *
+ * prepqual.c--
+ *    Routines for preprocessing the parse tree qualification
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/makefuncs.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/clauses.h"
+#include "optimizer/prep.h"
+
+#include "utils/lsyscache.h"
+
+static Expr *pull_args(Expr *qual);
+static List *pull_ors(List *orlist);
+static List *pull_ands(List *andlist);
+static Expr *find_nots(Expr *qual);
+static Expr *push_nots(Expr *qual);
+static Expr *normalize(Expr *qual);
+static List *or_normalize(List *orlist);
+static List *distribute_args(List *item, List *args);
+static List *qualcleanup(Expr *qual);
+static List *remove_ands(Expr *qual);
+static List *remove_duplicates(List *list);
+
+/*    
+ * preprocess-qualification--
+ *    Driver routine for modifying the parse tree qualification.
+ *    
+ * Returns the new base qualification and the existential qualification
+ * in existentialQualPtr.
+ *    
+ *  XXX right now, update_clauses() does nothing so 
+ *      preprocess-qualification simply converts the qual in conjunctive
+ *      normal form  (see cnfify() below ) 
+ */
+List *
+preprocess_qualification(Expr *qual, List *tlist, List **existentialQualPtr)
+{
+    List *cnf_qual = cnfify(qual, true);
+/*
+    List *existential_qual =
+       update_clauses(intCons(_query_result_relation_,
+                               update_relations(tlist)),
+                      cnf_qual,
+                      _query_command_type_);
+    if (existential_qual) {
+       *existentialQualPtr = existential_qual;
+       return set_difference(cnf_qual, existential_qual);
+    } else {
+       *existentialQualPtr = NIL;
+       return cnf_qual;
+    }
+*/
+ /* update_clauses() is not working right now */
+    *existentialQualPtr = NIL;
+    return cnf_qual;
+
+}
+
+/*****************************************************************************
+ *
+ *             CNF CONVERSION ROUTINES
+ *     
+ *     NOTES:
+ *     The basic algorithms for normalizing the qualification are taken
+ *     from ingres/source/qrymod/norml.c
+ *     
+ *     Remember that the initial qualification may consist of ARBITRARY
+ *     combinations of clauses.  In addition, before this routine is called,
+ *     the qualification will contain explicit "AND"s.
+ *     
+ *****************************************************************************/
+
+
+/*    
+ * cnfify--
+ *    Convert a qualification to conjunctive normal form by applying
+ *    successive normalizations.
+ *    
+ * Returns the modified qualification with an extra level of nesting.
+ *
+ * If 'removeAndFlag' is true then it removes the explicit ANDs.
+ *
+ * NOTE: this routine is called by the planner (removeAndFlag = true)
+ *     and from the rule manager (removeAndFlag = false).
+ *
+ */
+List *
+cnfify(Expr *qual, bool removeAndFlag)
+{
+    Expr *newqual = NULL;
+
+    if (qual != NULL) {
+       newqual = find_nots(pull_args(qual));
+       newqual = normalize(pull_args(newqual));
+       newqual = (Expr*)qualcleanup(pull_args(newqual));
+       newqual = pull_args(newqual);;
+       
+       if (removeAndFlag) {
+           if(and_clause((Node*)newqual)) 
+               newqual=(Expr*)remove_ands(newqual);
+           else 
+               newqual=(Expr*)remove_ands(make_andclause(lcons(newqual,NIL)));
+       }
+    }
+    else if (qual!=NULL)
+       newqual = (Expr*)lcons(qual, NIL);
+
+    return (List*)(newqual);
+}
+
+/*    
+ * pull-args--
+ *    Given a qualification, eliminate nested 'and' and 'or' clauses.
+ *    
+ * Returns the modified qualification.
+ *    
+ */
+static Expr *
+pull_args(Expr *qual)
+{
+    if (qual==NULL)
+       return (NULL);
+
+    if (is_opclause((Node*)qual)) {
+       return(make_clause(qual->opType, qual->oper,
+                          lcons(pull_args((Expr*)get_leftop(qual)),
+                               lcons(pull_args((Expr*)get_rightop(qual)),
+                                    NIL))));
+    } else if (and_clause((Node*)qual)) {
+       List *temp = NIL;
+       List *t_list = NIL;
+       
+       foreach (temp, qual->args)
+           t_list = lappend (t_list, pull_args(lfirst(temp)));
+       return (make_andclause (pull_ands (t_list)));
+    }else if (or_clause((Node*)qual)) {
+       List *temp = NIL;
+       List *t_list = NIL;
+       
+       foreach (temp, qual->args) 
+           t_list = lappend (t_list, pull_args(lfirst(temp)));
+       return (make_orclause (pull_ors (t_list)));
+    } else if (not_clause((Node*)qual)) {
+       return (make_notclause (pull_args (get_notclausearg (qual))));
+    } else {
+       return (qual);
+    }
+}
+
+/*    
+ * pull-ors--
+ *    Pull the arguments of an 'or' clause nested within another 'or'
+ *    clause up into the argument list of the parent.
+ *    
+ * Returns the modified list.
+ */
+static List *
+pull_ors(List *orlist)
+{
+    if (orlist==NIL) 
+       return (NIL);
+
+    if (or_clause(lfirst(orlist))) {
+       List *args = ((Expr*)lfirst(orlist))->args;
+       return (pull_ors(nconc(copyObject((Node*)args),
+                              copyObject((Node*)lnext(orlist)))));
+    } else {
+       return (lcons(lfirst(orlist), pull_ors(lnext(orlist))));
+    }
+}
+
+/*    
+ * pull-ands--
+ *    Pull the arguments of an 'and' clause nested within another 'and'
+ *    clause up into the argument list of the parent.
+ *    
+ * Returns the modified list.
+ */
+static List *
+pull_ands(List *andlist)
+{
+    if (andlist==NIL) 
+       return (NIL);
+
+    if (and_clause (lfirst(andlist))) {
+       List *args = ((Expr*)lfirst(andlist))->args;
+       return (pull_ands(nconc(copyObject((Node*)args),
+                               copyObject((Node*)lnext(andlist)))));
+    } else {
+       return (lcons(lfirst(andlist), pull_ands(lnext(andlist))));
+    }
+}
+
+/*    
+ * find-nots--
+ *    Traverse the qualification, looking for 'not's to take care of.
+ *    For 'not' clauses, remove the 'not' and push it down to the clauses'
+ *    descendants.
+ *    For all other clause types, simply recurse.
+ *    
+ * Returns the modified qualification.
+ *    
+ */
+static Expr *
+find_nots(Expr *qual)
+{
+    if (qual==NULL) 
+       return (NULL);
+    
+    if (is_opclause((Node*)qual)) {
+       return (make_clause(qual->opType, qual->oper,
+                           lcons(find_nots((Expr*)get_leftop(qual)),
+                                lcons(find_nots((Expr*)get_rightop(qual)),
+                                     NIL))));
+    } else if (and_clause ((Node*)qual)) {
+       List *temp = NIL;
+       List *t_list = NIL;
+       
+       foreach (temp, qual->args) {
+           t_list = lappend(t_list,find_nots(lfirst(temp)));
+       }
+       
+       return (make_andclause(t_list));
+    } else if (or_clause((Node*)qual)) {
+       List *temp = NIL;
+       List *t_list = NIL;
+       
+       foreach (temp, qual->args) {
+           t_list = lappend(t_list,find_nots(lfirst(temp)));
+       }
+       return (make_orclause (t_list));
+    } else if (not_clause((Node*)qual)) 
+       return (push_nots(get_notclausearg (qual)));
+    else 
+       return (qual);
+}
+
+/*    
+ * push-nots--
+ *    Negate the descendants of a 'not' clause.
+ *    
+ * Returns the modified qualification.
+ *    
+ */
+static Expr *
+push_nots(Expr *qual)
+{
+    if (qual==NULL) 
+       return (NULL);
+
+    /*
+     * Negate an operator clause if possible: 
+     *         ("NOT" (< A B)) => (> A B) 
+     * Otherwise, retain the clause as it is (the 'not' can't be pushed
+     * down any farther).
+     */
+    if (is_opclause((Node*)qual)) {
+       Oper *oper = (Oper*)((Expr*)qual)->oper;
+       Oid negator = get_negator(oper->opno);
+
+       if(negator) {
+           Oper *op = (Oper*) makeOper(negator,
+                                      InvalidOid,
+                                      oper->opresulttype,
+                                      0, NULL);
+           op->op_fcache = (FunctionCache *) NULL;
+           return
+               (make_opclause(op, get_leftop(qual), get_rightop(qual)));
+       } else {
+           return (make_notclause(qual));
+       }
+    } else if (and_clause((Node*)qual)) {
+       /* Apply DeMorgan's Laws:
+        *      ("NOT" ("AND" A B)) => ("OR" ("NOT" A) ("NOT" B))
+        *      ("NOT" ("OR" A B)) => ("AND" ("NOT" A) ("NOT" B))
+        * i.e., continue negating down through the clause's descendants.
+        */
+       List *temp = NIL;
+       List *t_list = NIL;
+           
+       foreach(temp, qual->args) {
+           t_list = lappend(t_list,push_nots(lfirst(temp)));
+       }
+       return (make_orclause (t_list));
+    } else if (or_clause((Node*)qual)) {
+       List *temp = NIL;
+       List *t_list = NIL;
+               
+       foreach(temp, qual->args) {
+           t_list = lappend(t_list,push_nots(lfirst(temp)));
+       }
+       return (make_andclause (t_list));
+    } else if (not_clause((Node*)qual))  
+       /*  Another 'not' cancels this 'not', so eliminate the 'not' and
+        *    stop negating this branch.
+        */
+       return (find_nots (get_notclausearg (qual)));
+    else  
+       /* We don't know how to negate anything else, place a 'not' at this
+        * level.         
+        */
+       return (make_notclause (qual));
+}
+
+/*    
+ * normalize--
+ *    Given a qualification tree with the 'not's pushed down, convert it
+ *    to a tree in CNF by repeatedly applying the rule:
+ *             ("OR" A ("AND" B C))  => ("AND" ("OR" A B) ("OR" A C))
+ *    bottom-up.
+ *    Note that 'or' clauses will always be turned into 'and' clauses.
+ *    
+ * Returns the modified qualification.
+ *    
+ */
+static Expr *
+normalize(Expr *qual)
+{
+    if (qual==NULL) 
+       return (NULL);
+    
+    if (is_opclause((Node*)qual)) {
+       Expr *expr = (Expr*)qual;
+       return (make_clause(expr->opType, expr->oper,
+                           lcons(normalize((Expr*)get_leftop(qual)),
+                                lcons(normalize((Expr*)get_rightop(qual)),
+                                     NIL))));
+    } else if (and_clause((Node*)qual)) {
+       List *temp = NIL;
+       List *t_list = NIL;
+       
+       foreach (temp, qual->args) {
+           t_list = lappend(t_list,normalize(lfirst(temp)));
+       }
+       return (make_andclause (t_list));
+    } else if (or_clause((Node*)qual)) {
+       /* XXX - let form, maybe incorrect */
+       List *orlist = NIL;
+       List *temp = NIL;
+       bool has_andclause = FALSE;
+       
+       foreach(temp, qual->args) {
+           orlist = lappend(orlist,normalize(lfirst(temp)));
+       }
+       foreach (temp, orlist) {
+           if (and_clause (lfirst(temp))) {
+               has_andclause = TRUE;
+               break;
+           }
+       }
+       if (has_andclause == TRUE)
+           return (make_andclause(or_normalize(orlist)));
+       else 
+           return (make_orclause(orlist));
+       
+    } else if (not_clause((Node*)qual)) 
+       return (make_notclause (normalize (get_notclausearg (qual))));
+    else 
+       return (qual);
+}
+
+/*    
+ * or-normalize--
+ *    Given a list of exprs which are 'or'ed together, distribute any
+ *    'and' clauses.
+ *    
+ * Returns the modified list.
+ *    
+ */
+static List *
+or_normalize(List *orlist)
+{
+    List *distributable = NIL;
+    List *new_orlist = NIL;
+    List *temp = NIL;
+
+    if (orlist==NIL)
+       return NIL;
+    
+    foreach(temp, orlist) {
+       if (and_clause(lfirst(temp)))
+           distributable = lfirst(temp);
+    }
+    if (distributable)
+       new_orlist = LispRemove(distributable,orlist);
+    
+    if(new_orlist) {
+       return
+           (or_normalize(lcons(distribute_args(lfirst(new_orlist),
+                                              ((Expr*)distributable)->args),
+                              lnext(new_orlist))));
+    }else {
+       return (orlist);
+    }
+}
+
+/*    
+ * distribute-args--
+ *    Create new 'or' clauses by or'ing 'item' with each element of 'args'.
+ *    E.g.: (distribute-args A ("AND" B C)) => ("AND" ("OR" A B) ("OR" A C))
+ *    
+ * Returns an 'and' clause.
+ *    
+ */
+static List *
+distribute_args(List *item, List *args)
+{
+    List *or_list = NIL;
+    List *n_list = NIL;
+    List *temp = NIL;
+    List *t_list = NIL;
+
+    if (args==NULL)
+       return (item);
+
+    foreach (temp,args) {
+       n_list = or_normalize(pull_ors(lcons(item,
+                                           lcons(lfirst(temp),NIL))));
+       or_list = (List*)make_orclause(n_list);
+       t_list = lappend(t_list,or_list);
+    }
+    return ((List*)make_andclause(t_list));
+}
+
+/*    
+ * qualcleanup--
+ *    Fix up a qualification by removing duplicate entries (left over from
+ *    normalization), and by removing 'and' and 'or' clauses which have only
+ *    one valid expr (e.g., ("AND" A) => A).
+ *    
+ * Returns the modified qualfication.
+ *    
+ */
+static List *
+qualcleanup(Expr *qual)
+{
+    if (qual==NULL) 
+       return (NIL);
+    
+    if (is_opclause((Node*)qual)) {
+       return ((List*)make_clause(qual->opType, qual->oper,
+                                  lcons(qualcleanup((Expr*)get_leftop(qual)),
+                                       lcons(qualcleanup((Expr*)get_rightop(qual)),
+                                            NIL))));
+    } else if (and_clause((Node*)qual)) {
+       List *temp = NIL;
+       List *t_list = NIL;
+       List *new_and_args = NIL;
+       
+       foreach(temp, qual->args)
+           t_list = lappend(t_list,qualcleanup(lfirst(temp)));
+
+       new_and_args = remove_duplicates(t_list);
+
+       if(length (new_and_args) > 1) 
+           return ((List*)make_andclause(new_and_args));
+       else 
+           return (lfirst(new_and_args));
+    }
+    else if (or_clause((Node*)qual)) {
+       List *temp = NIL;
+       List *t_list = NIL;
+       List *new_or_args = NIL;
+       
+       foreach (temp, qual->args)
+           t_list = lappend(t_list,qualcleanup(lfirst(temp)));
+
+       new_or_args = remove_duplicates(t_list);
+
+       
+       if(length (new_or_args) > 1) 
+           return ((List*)make_orclause (new_or_args));
+       else 
+           return (lfirst (new_or_args));
+    } else if (not_clause((Node*)qual)) 
+       return ((List*)make_notclause((Expr*)qualcleanup((Expr*)get_notclausearg(qual))));
+                               
+    else 
+       return ((List*)qual);
+}
+
+/*    
+ * remove-ands--
+ *    Remove the explicit "AND"s from the qualification:
+ *             ("AND" A B) => (A B)
+ *    
+ * RETURNS : qual
+ * MODIFIES: qual
+ */
+static List *
+remove_ands(Expr *qual)
+{
+    List *t_list = NIL;
+    
+    if (qual==NULL) 
+       return (NIL);
+    if (is_opclause((Node*)qual)) {
+       return ((List*)make_clause(qual->opType, qual->oper,
+                                  lcons(remove_ands((Expr*)get_leftop(qual)),
+                                       lcons(remove_ands((Expr*)get_rightop(qual)),
+                                            NIL))));
+    } else if (and_clause((Node*)qual)) {
+       List *temp = NIL;
+       foreach (temp, qual->args)
+           t_list = lappend(t_list,remove_ands(lfirst(temp)));
+       return(t_list);
+    } else if (or_clause((Node*)qual)) {
+       List *temp = NIL;
+       foreach (temp, qual->args)
+           t_list = lappend(t_list,remove_ands(lfirst(temp)));
+       return ((List*)make_orclause((List*)t_list));
+    } else if (not_clause((Node*)qual)) {
+       return ((List*)make_notclause((Expr*)remove_ands((Expr*)get_notclausearg (qual))));
+    } else {
+       return ((List*)qual);
+    }
+}
+
+/*****************************************************************************
+ *
+ *             EXISTENTIAL QUALIFICATIONS
+ *
+ *****************************************************************************/
+
+/*    
+ * update-relations--
+ *    Returns the range table indices (i.e., varnos) for all relations which
+ *    are referenced in the target list.
+ *    
+ */
+#if 0 
+static List *
+update_relations(List *tlist)
+{
+    return(NIL);
+}
+#endif
+
+/*****************************************************************************
+ *
+ *
+ *
+ *****************************************************************************/
+
+static List *
+remove_duplicates(List *list)
+{
+    List *i;
+    List *j;
+    List *result = NIL;
+    bool there_exists_duplicate = false;
+    
+    if (length(list) == 1)
+       return(list);
+    
+    foreach (i, list) {
+       if (i != NIL) {
+           foreach (j, lnext(i)) {
+               if (equal(lfirst(i), lfirst(j)))
+                   there_exists_duplicate = true;
+           }
+           if (!there_exists_duplicate)
+               result = lappend(result, lfirst(i));
+
+           there_exists_duplicate = false;
+       }
+    }
+    return(result);
+}
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
new file mode 100644 (file)
index 0000000..cbf4c7d
--- /dev/null
@@ -0,0 +1,322 @@
+/*-------------------------------------------------------------------------
+ *
+ * preptlist.c--
+ *    Routines to preprocess the parse tree target list
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/relation.h"
+#include "nodes/primnodes.h"
+#include "nodes/parsenodes.h"
+
+#include "nodes/makefuncs.h"
+
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/palloc.h"
+
+#include "parser/parsetree.h"          /* for getrelid() */
+#include "parser/catalog_utils.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/prep.h"
+#include "optimizer/clauses.h"
+#include "optimizer/tlist.h"
+
+static List *expand_targetlist(List *tlist, Oid relid, int command_type,
+                              Index result_relation);
+static List *replace_matching_resname(List *new_tlist,
+                                     List *old_tlist);
+static List *new_relation_targetlist(Oid relid, Index rt_index,
+                                    NodeTag node_type);
+                                        
+
+/*    
+ * preprocess-targetlist--
+ *    Driver for preprocessing the parse tree targetlist.
+ *    
+ *    1. Deal with appends and replaces by filling missing attributes
+ *       in the target list.
+ *    2. Reset operator OIDs to the appropriate regproc ids.
+ *    
+ *    Returns the new targetlist.
+ */
+List *
+preprocess_targetlist(List *tlist,
+                     int command_type,
+                     Index result_relation,
+                     List *range_table)
+{
+    List *expanded_tlist = NIL;
+    Oid relid = InvalidOid;
+    List *t_list = NIL;
+    List *temp = NIL;
+
+    if (result_relation>=1 && command_type != CMD_SELECT) {
+       relid = getrelid(result_relation, range_table);
+    }
+     
+    /*
+     * for heap_formtuple to work, the targetlist must match the exact
+     * order of the attributes. We also need to fill in the missing
+     * attributes here.                        -ay 10/94
+     */
+    expanded_tlist =
+       expand_targetlist(tlist, relid, command_type, result_relation);
+
+    /*    XXX should the fix-opids be this early?? */
+    /*    was mapCAR  */
+    foreach (temp,expanded_tlist) {
+       TargetEntry *tle = lfirst(temp);
+       if (tle->expr)
+           fix_opid(tle->expr);
+    }
+    t_list = copyObject(expanded_tlist);
+
+    /* ------------------
+     *  for "replace" or "delete" queries, add ctid of the result
+     *  relation into the target list so that the ctid can get
+     *  propogate through the execution and in the end ExecReplace()
+     *  will find the right tuple to replace or delete.  This
+     *  extra field will be removed in ExecReplace().
+     *  For convinient, we append this extra field to the end of
+     *  the target list.
+     * ------------------
+     */
+    if (command_type == CMD_UPDATE || command_type == CMD_DELETE) {
+       TargetEntry *ctid;
+       Resdom *resdom;
+       Var *var;
+
+       resdom = makeResdom(length(t_list) + 1,
+                           27,
+                           6,
+                           "ctid",
+                           0,
+                           0,
+                           1);
+
+       var = makeVar(result_relation, -1, 27, result_relation, -1);
+
+       ctid = makeNode(TargetEntry);
+       ctid->resdom = resdom;
+       ctid->expr = (Node *)var;
+       t_list = lappend(t_list, ctid);
+    }
+
+    return(t_list);
+}
+
+/*****************************************************************************
+ *
+ *             TARGETLIST EXPANSION
+ *
+ *****************************************************************************/
+
+/*    
+ * expand-targetlist--
+ *    Given a target list as generated by the parser and a result relation,
+ *    add targetlist entries for the attributes which have not been used.
+ *    
+ *    XXX This code is only supposed to work with unnested relations.
+ *    
+ *    'tlist' is the original target list
+ *    'relid' is the relid of the result relation
+ *    'command' is the update command
+ *    
+ * Returns the expanded target list, sorted in resno order.
+ */
+static List *
+expand_targetlist(List *tlist,
+                 Oid relid,
+                 int command_type,
+                 Index result_relation)
+{
+    NodeTag node_type = T_Invalid;
+    
+    switch (command_type) {
+    case CMD_INSERT:
+       node_type = (NodeTag)T_Const;
+       break;
+    case CMD_UPDATE:
+       node_type = (NodeTag)T_Var;
+       break;
+    } 
+    
+    if(node_type != T_Invalid) {
+       List *ntlist = new_relation_targetlist(relid,
+                                              result_relation,
+                                              node_type);
+
+       return (replace_matching_resname(ntlist, tlist));
+    } else {
+       return (tlist);
+    }
+    
+}
+
+
+static List *
+replace_matching_resname(List *new_tlist, List *old_tlist)
+{
+    List *temp, *i;
+    List *t_list = NIL;
+     
+    foreach (i,new_tlist) {
+       TargetEntry *new_tle = (TargetEntry *)lfirst(i);
+       TargetEntry *matching_old_tl = NULL;
+
+       foreach (temp, old_tlist) {
+           TargetEntry *old_tle = (TargetEntry *)lfirst(temp);
+           
+           old_tle = lfirst(temp);
+           if (!strcmp(old_tle->resdom->resname,
+                       new_tle->resdom->resname)) {
+               matching_old_tl = old_tle;
+               break;
+           }
+       }
+         
+       if(matching_old_tl) {
+           matching_old_tl->resdom->resno =
+               new_tle->resdom->resno;
+           t_list = lappend(t_list, matching_old_tl);
+       } 
+       else {
+           t_list = lappend(t_list, new_tle);
+       } 
+    }
+
+    /*
+     * It is possible that 'old_tlist' has some negative
+     * attributes (i.e. negative resnos). This only happens
+     * if this is a replace/append command and we explicitly
+     * specify a system attribute. Of course this is not a very good
+     * idea if this is a user query, but on the other hand the rule
+     * manager uses this mechanism to replace rule locks.
+     *
+     * So, copy all these entries to the end of the target list
+     * and set their 'resjunk' value to 1 to show that these are
+     * special attributes and have to be treated specially by the
+     * executor!
+     */
+    foreach (temp, old_tlist) {
+       TargetEntry *old_tle, *new_tl;
+       Resdom *newresno;
+
+       old_tle = lfirst(temp);
+       if (old_tle->resdom->resno < 0) {
+           newresno = (Resdom*) copyObject((Node*)old_tle->resdom);
+           newresno->resno = length(t_list) +1;
+           newresno->resjunk = 1;
+           new_tl = MakeTLE(newresno, old_tle->expr);
+           t_list = lappend(t_list, new_tl);
+       }
+    }
+
+    return (t_list);
+}
+
+/*    
+ * new-relation-targetlist--
+ *    Generate a targetlist for the relation with relation OID 'relid'
+ *    and rangetable index 'rt-index'.
+ *    
+ *    Returns the new targetlist.
+ */
+static List *
+new_relation_targetlist(Oid relid, Index rt_index, NodeTag node_type)
+{
+    AttrNumber attno;
+    List *t_list = NIL;
+    char *attname;
+    Oid atttype = 0;
+    int16 typlen = 0;
+    bool attisset = false;
+/*    Oid type_id; */
+/*    type_id = RelationIdGetTypeId(relid); */
+
+    for(attno=1; attno <= get_relnatts(relid); attno++) {
+       attname = get_attname(/*type_id,*/ relid, attno);
+       atttype = get_atttype(/*type_id,*/ relid, attno);
+       /*
+        * Since this is an append or replace, the size of any set
+        * attribute is the size of the OID used to represent it.
+        */
+       attisset = get_attisset(/* type_id,*/ relid, attname);
+       if (attisset) {
+           typlen = tlen(type("oid"));
+       } else {
+           typlen = get_typlen(atttype);
+       }
+       
+       switch (node_type) {
+       case T_Const:
+           { 
+               struct varlena *typedefault = get_typdefault(atttype);
+               int temp = 0;
+               Const *temp2 = (Const*)NULL;
+               TargetEntry *temp3 = (TargetEntry *)NULL;
+
+               if (typedefault==NULL) 
+                   temp = 0;
+               else 
+                   temp = typlen;
+                
+               temp2 = makeConst (atttype,
+                                  temp,
+                                  (Datum)typedefault,
+                                  (typedefault == (struct varlena *)NULL),
+                                  /* XXX this is bullshit */
+                                  false,
+                                  false /* not a set */);
+                
+               temp3 = MakeTLE (makeResdom(attno,
+                                           atttype,
+                                           typlen,
+                                           attname,
+                                           0,
+                                           (Oid)0,
+                                           0),
+                                (Node*)temp2);
+               t_list = lappend(t_list,temp3);
+               break;
+           } 
+       case T_Var:
+           { 
+               Var *temp_var = (Var*)NULL;
+               TargetEntry *temp_list = NULL;
+
+               temp_var =
+                   makeVar(rt_index, attno, atttype, rt_index, attno);
+
+               temp_list = MakeTLE(makeResdom(attno,
+                                              atttype,
+                                              typlen,
+                                              attname,
+                                              0,
+                                              (Oid)0,
+                                              0),
+                                   (Node*)temp_var);
+               t_list = lappend(t_list,temp_list);
+               break;
+           }
+       default:                /* do nothing */
+           break;
+       }
+    }
+
+    return(t_list);
+}
+
+
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
new file mode 100644 (file)
index 0000000..5dced0e
--- /dev/null
@@ -0,0 +1,400 @@
+/*-------------------------------------------------------------------------
+ *
+ * prepunion.c--
+ *    Routines to plan archive, inheritance, union, and version queries
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/nodes.h"
+#include "nodes/pg_list.h"
+#include "nodes/execnodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/relation.h"
+
+#include "parser/parse_query.h"
+#include "parser/parsetree.h"
+
+#include "utils/elog.h"
+#include "utils/lsyscache.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/prep.h"
+#include "optimizer/plancat.h"
+#include "optimizer/planner.h"
+#include "optimizer/prep.h"
+
+static List *plan_union_query(List *relids, Index rt_index,
+                RangeTblEntry *rt_entry, Query *parse, UnionFlag flag,
+                List **union_rtentriesPtr);
+static RangeTblEntry *new_rangetable_entry(Oid new_relid,
+                                          RangeTblEntry *old_entry);
+static Query *subst_rangetable(Query *root, Index index,
+                              RangeTblEntry *new_entry);
+static void fix_parsetree_attnums(Index rt_index, Oid old_relid,
+                                 Oid new_relid, Query *parsetree);
+static Append *make_append(List *unionplans, Index rt_index,
+                         List *union_rt_entries, List *tlist);
+
+
+/*    
+ * find-all-inheritors -
+ *     Returns a list of relids corresponding to relations that inherit
+ *     attributes from any relations listed in either of the argument relid
+ *     lists.
+ */
+List *
+find_all_inheritors(List *unexamined_relids,
+                   List *examined_relids)
+{
+    List *new_inheritors = NIL;
+    List *new_examined_relids = NIL;
+    List *new_unexamined_relids = NIL;
+    
+    /* Find all relations which inherit from members of
+     * 'unexamined-relids' and store them in 'new-inheritors'.
+     */
+    List *rels = NIL;
+    List *newrels = NIL;
+    
+    foreach(rels,unexamined_relids) {
+       newrels = (List*)LispUnioni(find_inheritance_children(lfirsti(rels)),
+                                   newrels);
+    }
+    new_inheritors = newrels;
+    
+    new_examined_relids = (List*)LispUnioni(examined_relids,unexamined_relids);
+    new_unexamined_relids = set_differencei(new_inheritors,
+                                           new_examined_relids);
+    
+    if (new_unexamined_relids==NULL) {
+       return(new_examined_relids);
+    } else {
+       return (find_all_inheritors (new_unexamined_relids,
+                                    new_examined_relids));
+    }
+}
+
+/*    
+ * first-matching-rt-entry -
+ *     Given a rangetable, find the first rangetable entry that represents
+ *     the appropriate special case.
+ *    
+ *     Returns a rangetable index.,  Returns -1 if no matches
+ */
+int
+first_matching_rt_entry (List *rangetable, UnionFlag flag) 
+{
+    int count = 0;
+    List *temp = NIL;
+
+    foreach(temp, rangetable) {
+       RangeTblEntry *rt_entry = lfirst(temp);
+       
+       switch(flag) {
+       case INHERITS_FLAG:
+           if (rt_entry->inh)
+               return count+1;
+           break;
+       case ARCHIVE_FLAG:
+           if (rt_entry->archive)
+               return count+1;
+           break;
+       default:
+           break;
+       }
+       count++;
+    }
+     
+    return(-1);
+}
+
+
+/*    
+ * plan-union-queries--
+ *    
+ *    Plans the queries for a given parent relation.
+ *    
+ * Returns a list containing a list of plans and a list of rangetable
+ * entries to be inserted into an APPEND node.
+ * XXX - what exactly does this mean, look for make_append
+ */
+Append *
+plan_union_queries(Index rt_index,
+                  Query *parse,
+                  UnionFlag flag)
+{
+    List *rangetable = parse->rtable;
+    RangeTblEntry *rt_entry = rt_fetch(rt_index,rangetable);
+    List *union_relids = NIL;
+    List *union_plans = NIL;
+    List *union_rt_entries = NIL;
+    
+    switch (flag) {
+    case INHERITS_FLAG:
+       union_relids = 
+           find_all_inheritors(lconsi(rt_entry->relid,
+                                       NIL),
+                                NIL);
+       break;
+
+#if 0  
+    case UNION_FLAG:
+       {
+           Index rt_index = 0;
+           union_plans = handleunion(root,rangetable,tlist,qual);
+           return (make_append (union_plans,
+                                rt_index, rangetable,
+                                ((Plan*)lfirst(union_plans))->targetlist ));
+       }
+       break;
+#endif
+       
+    case VERSION_FLAG:
+       union_relids = VersionGetParents(rt_entry->relid);
+       break;
+       
+    case ARCHIVE_FLAG:
+       union_relids = find_archive_rels(rt_entry->relid);
+       break;
+
+    default: 
+       /* do nothing */
+       break;
+    }
+
+    /*
+     * Remove the flag for this relation, since we're about to handle it 
+     * (do it before recursing!).
+     * XXX destructive parse tree change
+     */
+    switch(flag) {
+    case INHERITS_FLAG:
+       rt_fetch(rt_index,rangetable)->inh = false;
+       break;
+    case ARCHIVE_FLAG:
+       rt_fetch(rt_index,rangetable)->archive = false;
+       break;
+    default:
+       break;
+    }
+
+    /* XXX - can't find any reason to sort union-relids
+     * as paul did, so we're leaving it out for now
+     * (maybe forever) - jeff & lp
+     *
+     * [maybe so. btw, jeff & lp did the lisp conversion, according to Paul.
+     *  -- ay 10/94.]
+     */
+    union_plans = plan_union_query(union_relids, rt_index, rt_entry,
+                                  parse, flag, &union_rt_entries);
+
+    return (make_append(union_plans,
+                       rt_index,
+                       union_rt_entries,
+                       ((Plan*)lfirst(union_plans))->targetlist));
+}
+
+
+/*    
+ * plan-union-query--
+ *    Returns a list of plans for 'relids' and a list of range table entries
+ *    in union_rtentries.
+ */
+static List *
+plan_union_query(List *relids,
+                Index rt_index,
+                RangeTblEntry *rt_entry,
+                Query *root,
+                UnionFlag flag,
+                List **union_rtentriesPtr)
+{
+    List *i;
+    List *union_plans = NIL;
+    List *union_rtentries = NIL;
+
+    foreach (i, relids) {
+       int relid = lfirsti(i);
+       RangeTblEntry *new_rt_entry = new_rangetable_entry(relid,
+                                                          rt_entry);
+       Query *new_root = subst_rangetable(root,
+                                          rt_index,
+                                          new_rt_entry);
+
+       /* reset the uniqueflag and sortclause in parse tree root, so that
+        * sorting will only be done once after append
+        */
+/*     new_root->uniqueFlag = false; */
+       new_root->uniqueFlag = NULL; 
+       new_root->sortClause = NULL;
+       if (flag == ARCHIVE_FLAG) {
+           /*
+            * the entire union query uses the same (most recent) schema.
+            * to do otherwise would require either ragged tuples or careful
+            * archiving and interpretation of pg_attribute...
+            */
+       } else {
+           fix_parsetree_attnums(rt_index, 
+                                 rt_entry->relid,
+                                 relid,
+                                 new_root);
+       }
+
+       union_plans = lappend(union_plans, planner(new_root));
+       union_rtentries = lappend(union_rtentries, new_rt_entry);
+    }
+
+    *union_rtentriesPtr = union_rtentries;
+    return(union_plans);
+}
+
+/*    
+ * new-rangetable-entry -
+ *     Replaces the name and relid of 'old-entry' with the values for 
+ *     'new-relid'.
+ *    
+ *     Returns a copy of 'old-entry' with the parameters substituted.
+ */
+static RangeTblEntry *
+new_rangetable_entry(Oid new_relid, RangeTblEntry *old_entry)
+{
+    RangeTblEntry *new_entry = copyObject(old_entry);
+
+    /* ??? someone tell me what the following is doing! - ay 11/94 */
+    if (!strcmp(new_entry->refname, "*CURRENT*") ||
+        !strcmp(new_entry->refname, "*NEW*"))
+       new_entry->refname =  get_rel_name(new_relid);
+    else
+       new_entry->relname =  get_rel_name(new_relid);
+
+    new_entry->relid = new_relid;
+    return(new_entry);
+}
+
+/*    
+ * subst-rangetable--
+ *    Replaces the 'index'th rangetable entry in 'root' with 'new-entry'.   
+ *    
+ * Returns a new copy of 'root'.
+ */
+static Query *
+subst_rangetable(Query *root, Index index, RangeTblEntry *new_entry)
+{
+    Query *new_root = copyObject(root);
+    List *temp = NIL;
+    int i = 0;
+
+    for(temp = new_root->rtable,i =1; i < index; temp =lnext(temp),i++)
+       ;
+    lfirst(temp) = new_entry;
+
+    return (new_root);
+}
+
+static void
+fix_parsetree_attnums_nodes(Index rt_index,
+                           Oid old_relid,
+                           Oid new_relid,
+                           Node *node)
+{
+    if (node==NULL)
+       return;
+    
+    switch(nodeTag(node)) {
+    case T_TargetEntry:
+       {
+           TargetEntry *tle = (TargetEntry *)node;
+           
+           fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid,
+                                       tle->expr);
+       }
+       break;
+    case T_Expr:
+       {
+           Expr *expr = (Expr *)node;
+           fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid,
+                                       (Node*)expr->args);
+       }
+       break;
+    case T_Var:
+       {
+           Var *var = (Var *)node;
+           Oid old_typeid, new_typeid;
+
+/*         old_typeid = RelationIdGetTypeId(old_relid);*/
+/*         new_typeid = RelationIdGetTypeId(new_relid);*/
+           old_typeid = old_relid;
+           new_typeid = new_relid;
+
+           if (var->varno == rt_index && var->varattno != 0) {
+               var->varattno =
+                   get_attnum(new_typeid,
+                              get_attname(old_typeid, var->varattno));
+           }
+       }
+       break;
+    case T_List:
+       {
+           List *l;
+           foreach(l, (List*)node) {
+               fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid,
+                                           (Node*)lfirst(l));
+           }
+       }
+       break;
+    default:
+       break;
+    }
+}
+    
+/*    
+ * fix-parsetree-attnums--
+ *    Replaces attribute numbers from the relation represented by
+ *    'old-relid' in 'parsetree' with the attribute numbers from
+ *    'new-relid'.
+ *    
+ * Returns the destructively-modified parsetree.
+ *    
+ */
+static void
+fix_parsetree_attnums(Index rt_index,
+                     Oid old_relid,
+                     Oid new_relid,
+                     Query *parsetree)
+{
+    if (old_relid == new_relid)
+       return;
+
+    fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid,
+                               (Node*)parsetree->targetList);
+    fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid,
+                               parsetree->qual);
+}
+
+static Append *
+make_append(List *unionplans,
+           Index rt_index,
+           List *union_rt_entries,
+           List *tlist)
+{
+    Append *node = makeNode(Append);
+
+    node->unionplans = unionplans;
+    node->unionrelid = rt_index;
+    node->unionrtentries = union_rt_entries;
+    node->plan.cost = 0.0;
+    node->plan.state = (EState*)NULL;
+    node->plan.targetlist = tlist;
+    node->plan.qual = NIL;
+    node->plan.lefttree = (Plan*)NULL;
+    node->plan.righttree = (Plan*)NULL;
+
+    return(node);
+}
diff --git a/src/backend/optimizer/tlist.h b/src/backend/optimizer/tlist.h
new file mode 100644 (file)
index 0000000..2468cea
--- /dev/null
@@ -0,0 +1,36 @@
+/*-------------------------------------------------------------------------
+ *
+ * tlist.h--
+ *    prototypes for tlist.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef TLIST_H
+#define TLIST_H
+
+extern int exec_tlist_length(List *targelist);
+extern TargetEntry *tlistentry_member(Var *var, List *targetlist);
+extern Expr *matching_tlvar(Var *var, List *targetlist);
+extern void add_tl_element(Rel *rel, Var *var);
+extern TargetEntry *create_tl_element(Var *var, int resdomno);
+extern List *get_actual_tlist(List *tlist);
+extern Resdom *tlist_member(Var *var, List *tlist);
+extern Resdom *tlist_resdom(List *tlist, Resdom *resnode);
+
+extern TargetEntry *MakeTLE(Resdom *resdom, Node *expr);
+extern Var *get_expr(TargetEntry *tle);
+
+extern TargetEntry *match_varid(Var *test_var, List *tlist);
+extern List *new_unsorted_tlist(List *targetlist);
+extern List *copy_vars(List *target, List *source);
+extern List *flatten_tlist(List *tlist);
+extern List *flatten_tlist_vars(List *full_tlist,
+                               List *flat_tlist);
+extern void AddGroupAttrToTlist(List *tlist, List *grpCl);
+
+#endif /* TLIST_H */
diff --git a/src/backend/optimizer/util/Makefile.inc b/src/backend/optimizer/util/Makefile.inc
new file mode 100644 (file)
index 0000000..8decd16
--- /dev/null
@@ -0,0 +1,15 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for optimizer/util
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= clauseinfo.c clauses.c indexnode.c internal.c plancat.c \
+       joininfo.c keys.c ordering.c pathnode.c relnode.c tlist.c var.c
diff --git a/src/backend/optimizer/util/clauseinfo.c b/src/backend/optimizer/util/clauseinfo.c
new file mode 100644 (file)
index 0000000..c72bfa5
--- /dev/null
@@ -0,0 +1,187 @@
+/*-------------------------------------------------------------------------
+ *
+ * clauseinfo.c--
+ *    ClauseInfo node manipulation routines.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/relation.h"
+#include "nodes/nodeFuncs.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/clauses.h"
+#include "optimizer/clauseinfo.h"
+
+/*    
+ * valid-or-clause--
+ *    
+ * Returns t iff the clauseinfo node contains a 'normal' 'or' clause.
+ *    
+ */
+bool
+valid_or_clause(CInfo *clauseinfo)
+{
+     if (clauseinfo != NULL && 
+        !single_node((Node*)clauseinfo->clause) && 
+        !clauseinfo->notclause &&
+        or_clause((Node*)clauseinfo->clause)) 
+       return(true);
+     else
+       return(false);
+}
+
+/*    
+ * get-actual-clauses--
+ *    
+ * Returns a list containing the clauses from 'clauseinfo-list'.
+ *    
+ */
+List *
+get_actual_clauses(List *clauseinfo_list)
+{
+    List *temp = NIL;
+    List *result = NIL;
+    CInfo *clause = (CInfo *)NULL;
+
+    foreach(temp,clauseinfo_list) {
+       clause = (CInfo *)lfirst(temp);
+       result = lappend(result,clause->clause);
+    }
+    return(result);
+}
+
+/*    
+ * XXX NOTE:
+ *     The following routines must return their contents in the same order
+ *     (e.g., the first clause's info should be first, and so on) or else
+ *     get_index_sel() won't work.
+ *    
+ */
+
+/*    
+ * get_relattvals--
+ *    For each member of  a list of clauseinfo nodes to be used with an
+ *    index, create a vectori-long specifying:
+ *             the attnos,
+ *             the values of the clause constants, and 
+ *             flags indicating the type and location of the constant within
+ *                     each clause.
+ *    Each clause is of the form (op var some_type_of_constant), thus the
+ *    flag indicating whether the constant is on the left or right should
+ *    always be *SELEC-CONSTANT-RIGHT*.
+ *    
+ * 'clauseinfo-list' is a list of clauseinfo nodes
+ *    
+ * Returns a list of vectori-longs.
+ *    
+ */
+void
+get_relattvals(List *clauseinfo_list,
+              List **attnos,
+              List **values,
+              List **flags)
+{
+    List *result1 = NIL;
+    List *result2 = NIL;
+    List *result3 = NIL;
+    CInfo *temp = (CInfo *)NULL;
+    List *i = NIL;
+
+    foreach (i,clauseinfo_list) {
+       int dummy;
+       AttrNumber attno;
+       Datum constval;
+       int flag;
+
+       temp = (CInfo *)lfirst(i);
+       get_relattval((Node*)temp->clause, &dummy, &attno, &constval, &flag);
+       result1 = lappendi(result1, attno);
+       result2 = lappendi(result2, constval);
+       result3 = lappendi(result3, flag);
+    }
+
+    *attnos = result1;
+    *values = result2;
+    *flags = result3;
+    return;
+}
+
+/*    
+ * get_joinvars --
+ *    Given a list of join clauseinfo nodes to be used with the index
+ *    of an inner join relation, return three lists consisting of:
+ *             the attributes corresponding to the inner join relation
+ *             the value of the inner var clause (always "")
+ *             whether the attribute appears on the left or right side of
+ *                     the operator.
+ *    
+ * 'relid' is the inner join relation
+ * 'clauseinfo-list' is a list of qualification clauses to be used with
+ *     'rel'
+ *    
+ */
+void
+get_joinvars(Oid relid,
+            List *clauseinfo_list,
+            List **attnos,
+            List **values,
+            List **flags)
+{
+    List *result1 = NIL;
+    List *result2 = NIL;
+    List *result3 = NIL;
+    List *temp;
+     
+    foreach(temp, clauseinfo_list) {
+       CInfo *clauseinfo = lfirst(temp);
+       Expr *clause = clauseinfo->clause;
+
+       if( IsA (get_leftop(clause),Var) &&
+          (relid == (get_leftop(clause))->varno)) {
+
+           result1 = lappendi(result1, (get_leftop(clause))->varattno);
+           result2 = lappend(result2, "");
+           result3 = lappendi(result3, _SELEC_CONSTANT_RIGHT_);
+       } else {
+           result1 = lappendi(result1, (get_rightop(clause))->varattno);
+           result2 = lappend(result2, "");
+           result3 = lappendi(result3, _SELEC_CONSTANT_LEFT_);
+       }
+    }
+    *attnos = result1;
+    *values = result2;
+    *flags = result3;
+    return;
+}
+
+/*    
+ * get_opnos--
+ *    Create and return a list containing the clause operators of each member
+ *    of a list of clauseinfo nodes to be used with an index.
+ *    
+ */
+List *
+get_opnos(List *clauseinfo_list)
+{
+    CInfo *temp = (CInfo *)NULL;
+    List *result = NIL;
+    List *i = NIL;
+
+    foreach(i,clauseinfo_list) {
+       temp = (CInfo *)lfirst(i);
+       result =
+           lappendi(result,
+                    (((Oper*)temp->clause->oper)->opno));
+    }
+    return(result);
+}
+
+
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
new file mode 100644 (file)
index 0000000..286d787
--- /dev/null
@@ -0,0 +1,736 @@
+/*-------------------------------------------------------------------------
+ *
+ * clauses.c--
+ *    routines to manipulate qualification clauses
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * HISTORY
+ *    AUTHOR           DATE            MAJOR EVENT
+ *    Andrew Yu                Nov 3, 1994     clause.c and clauses.c combined
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+#include "nodes/parsenodes.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+
+#include "catalog/pg_aggregate.h"
+
+#include "utils/elog.h"
+#include "utils/syscache.h"
+#include "utils/lsyscache.h"
+
+#include "optimizer/clauses.h"
+#include "optimizer/internal.h"
+#include "optimizer/var.h"
+
+
+Expr *
+make_clause(int type, Node *oper, List *args)
+{
+    if (type == AND_EXPR || type == OR_EXPR || type == NOT_EXPR ||
+       type == OP_EXPR || type == FUNC_EXPR) {
+       Expr *expr = makeNode(Expr);
+
+       /*
+        * assume type checking already done and we don't need the type of
+        * the expr any more.
+        */
+       expr->typeOid = InvalidOid;
+       expr->opType = type;
+       expr->oper = oper;      /* ignored for AND, OR, NOT */
+       expr->args = args;
+       return expr;
+    }else {
+       /* will this ever happen? translated from lispy C code - ay 10/94 */
+       return((Expr*)args);
+    }
+}
+
+
+/*****************************************************************************
+ *     OPERATOR clause functions
+ *****************************************************************************/
+
+
+/*    
+ * is_opclause--
+ *    
+ * Returns t iff the clause is an operator clause:
+ *             (op expr expr) or (op expr).
+ *
+ * [historical note: is_clause has the exact functionality and is used
+ *     throughout the code. They're renamed to is_opclause for clarity.
+ *                                             - ay 10/94.]
+ */
+bool
+is_opclause(Node *clause)
+{
+    return 
+       (clause!=NULL &&
+        nodeTag(clause)==T_Expr && ((Expr*)clause)->opType==OP_EXPR);
+}
+
+/*    
+ * make_opclause--
+ *    Creates a clause given its operator left operand and right
+ *    operand (if it is non-null).
+ *    
+ */
+Expr *
+make_opclause(Oper *op, Var *leftop, Var *rightop)
+{
+    Expr *expr = makeNode(Expr);
+
+    expr->typeOid = InvalidOid;        /* assume type checking done */
+    expr->opType = OP_EXPR;
+    expr->oper = (Node*)op;
+    expr->args = makeList(leftop, rightop, -1);
+    return expr;
+}
+
+/*    
+ * get_leftop--
+ *    
+ * Returns the left operand of a clause of the form (op expr expr)
+ *     or (op expr)
+ * NB: it is assumed (for now) that all expr must be Var nodes    
+ */
+Var *
+get_leftop(Expr *clause)
+{
+    if (clause->args!=NULL)
+       return(lfirst(clause->args));
+    else
+       return NULL;
+}
+
+/*    
+ * get_rightop
+ *    
+ * Returns the right operand in a clause of the form (op expr expr).
+ *    
+ */
+Var *
+get_rightop(Expr *clause)
+{
+    if (clause->args!=NULL && lnext(clause->args)!=NULL)
+       return (lfirst(lnext(clause->args)));
+    else
+       return NULL;
+}
+
+/*****************************************************************************
+ *     AGG clause functions
+ *****************************************************************************/
+
+bool
+agg_clause(Node *clause)
+{
+    return
+       (clause!=NULL && nodeTag(clause)==T_Aggreg);
+}
+
+/*****************************************************************************
+ *     FUNC clause functions
+ *****************************************************************************/
+
+/*    
+ * is_funcclause--
+ *    
+ * Returns t iff the clause is a function clause: (func { expr }).
+ *    
+ */
+bool
+is_funcclause(Node *clause)
+{
+    return
+       (clause!=NULL &&
+        nodeTag(clause)==T_Expr && ((Expr*)clause)->opType==FUNC_EXPR);
+}
+
+/*    
+ * make_funcclause--
+ *    
+ * Creates a function clause given the FUNC node and the functional
+ * arguments.
+ *    
+ */
+Expr *
+make_funcclause(Func *func, List *funcargs)
+{
+    Expr *expr = makeNode(Expr);
+
+    expr->typeOid = InvalidOid;        /* assume type checking done */
+    expr->opType = FUNC_EXPR;
+    expr->oper = (Node*)func;
+    expr->args = funcargs;
+    return expr;
+}
+
+/*****************************************************************************
+ *     OR clause functions
+ *****************************************************************************/
+
+/*    
+ * or_clause--
+ *    
+ * Returns t iff the clause is an 'or' clause: (OR { expr }).
+ *    
+ */
+bool
+or_clause(Node *clause)
+{
+    return
+       (clause!=NULL &&
+        nodeTag(clause)==T_Expr && ((Expr*)clause)->opType==OR_EXPR);
+}
+
+/*    
+ * make_orclause--
+ *    
+ * Creates an 'or' clause given a list of its subclauses.
+ *    
+ */
+Expr *
+make_orclause(List *orclauses)
+{
+    Expr *expr = makeNode(Expr);
+
+    expr->typeOid = InvalidOid;        /* assume type checking done */
+    expr->opType = OR_EXPR;
+    expr->oper = NULL;
+    expr->args = orclauses;
+    return expr;
+}
+
+/*****************************************************************************
+ *     NOT clause functions
+ *****************************************************************************/
+
+/*    
+ * not_clause--
+ *    
+ * Returns t iff this is a 'not' clause: (NOT expr).
+ *    
+ */
+bool
+not_clause(Node *clause)
+{
+    return
+       (clause!=NULL &&
+        nodeTag(clause)==T_Expr && ((Expr*)clause)->opType == NOT_EXPR);
+}
+
+/*    
+ * make_notclause--
+ *    
+ * Create a 'not' clause given the expression to be negated.
+ *    
+ */
+Expr *
+make_notclause(Expr *notclause)
+{
+    Expr *expr = makeNode(Expr);
+
+    expr->typeOid = InvalidOid;        /* assume type checking done */
+    expr->opType = NOT_EXPR;
+    expr->oper = NULL;
+    expr->args = lcons(notclause, NIL);
+    return expr;
+}
+
+/*    
+ * get_notclausearg--
+ *    
+ * Retrieve the clause within a 'not' clause
+ *    
+ */
+Expr *
+get_notclausearg(Expr *notclause)
+{
+     return(lfirst(notclause->args));
+}
+
+/*****************************************************************************
+ *     AND clause functions
+ *****************************************************************************/
+
+
+/*    
+ * and_clause--
+ *    
+ * Returns t iff its argument is an 'and' clause: (AND { expr }).
+ *    
+ */
+bool
+and_clause(Node *clause)
+{
+    return
+       (clause!=NULL &&
+        nodeTag(clause)==T_Expr && ((Expr*)clause)->opType == AND_EXPR);
+}
+/*              
+ * make_andclause--
+ *    
+ * Create an 'and' clause given its arguments in a list.
+ *    
+ */
+Expr *
+make_andclause(List *andclauses)
+{
+    Expr *expr = makeNode(Expr);
+
+    expr->typeOid = InvalidOid;        /* assume type checking done */
+    expr->opType = AND_EXPR;
+    expr->oper = NULL;
+    expr->args = andclauses;
+    return expr;
+}
+
+/*****************************************************************************
+ *                                                                           *
+ *                                                                           *
+ *                                                                           *
+ *****************************************************************************/
+
+
+/*    
+ * pull-constant-clauses--
+ *    Scans through a list of qualifications and find those that
+ *    contain no variables.
+ *    
+ * Returns a list of the constant clauses in constantQual and the remaining
+ * quals as the return value.
+ *    
+ */
+List *
+pull_constant_clauses(List *quals, List **constantQual)
+{
+    List *q;
+    List *constqual=NIL;
+    List *restqual=NIL;
+
+    foreach(q, quals) {
+       if (!contain_var_clause(lfirst(q))) {
+           constqual = lcons(lfirst(q), constqual);
+       }else {
+           restqual = lcons(lfirst(q), restqual);
+       }
+    }
+    freeList(quals);
+    *constantQual = constqual;
+    return restqual;
+}
+
+/*    
+ * clause-relids-vars--
+ *    Retrieves relids and vars appearing within a clause.
+ *    Returns ((relid1 relid2 ... relidn) (var1 var2 ... varm)) where 
+ *    vars appear in the clause  this is done by recursively searching
+ *    through the left and right operands of a clause.
+ *    
+ * Returns the list of relids and vars.
+ *    
+ * XXX take the nreverse's out later
+ *    
+ */
+void
+clause_relids_vars(Node *clause, List **relids, List **vars)
+{
+    List *clvars = pull_var_clause(clause);
+    List *var_list = NIL;
+    List *varno_list = NIL;
+    List *i = NIL;
+
+    foreach (i, clvars) {
+       Var *var = (Var *)lfirst(i);
+
+       if (!intMember(var->varno, varno_list)) {
+           varno_list = lappendi(varno_list, var->varno);
+           var_list = lappend(var_list, var);
+       }
+    }
+
+    *relids = varno_list;
+    *vars = var_list;
+    return;
+}
+
+/*    
+ * NumRelids--
+ *     (formerly clause-relids)
+ *    
+ * Returns the number of different relations referenced in 'clause'.
+ */
+int
+NumRelids(Node *clause)
+{
+    List *vars = pull_var_clause(clause);
+    List *i = NIL;
+    List *var_list = NIL;
+
+    foreach (i, vars) {
+       Var *var = (Var *)lfirst(i);
+
+       if (!intMember(var->varno, var_list)) {
+           var_list = lconsi(var->varno, var_list);
+       }
+    }
+
+    return(length(var_list));
+}
+
+/*    
+ * contains-not--
+ *
+ * Returns t iff the clause is a 'not' clause or if any of the
+ * subclauses within an 'or' clause contain 'not's.
+ *    
+ */
+bool
+contains_not(Node *clause)
+{
+    if (single_node(clause))
+       return (false);
+
+    if (not_clause(clause))
+       return (true);
+
+    if (or_clause(clause)) {
+       List *a;
+       foreach(a, ((Expr*)clause)->args) {
+           if (contains_not(lfirst(a)))
+               return (true);
+       }
+    }
+       
+    return(false);
+}
+
+/*    
+ * join-clause-p--
+ *    
+ * Returns t iff 'clause' is a valid join clause.
+ *    
+ */
+bool
+join_clause_p(Node *clause)
+{
+    Node *leftop, *rightop;
+
+    if (!is_opclause(clause))
+       return false;
+
+    leftop = (Node*)get_leftop((Expr*)clause);
+    rightop = (Node*)get_rightop((Expr*)clause);
+
+    /*
+     * One side of the clause (i.e. left or right operands)
+     * must either be a var node ...
+     */
+    if (IsA(leftop,Var) || IsA(rightop,Var))
+       return true;
+
+    /*
+     * ... or a func node.
+     */
+    if (is_funcclause(leftop) || is_funcclause(rightop))
+       return(true);
+
+    return(false);
+}
+
+/*    
+ * qual-clause-p--
+ *    
+ * Returns t iff 'clause' is a valid qualification clause.
+ *    
+ */
+bool
+qual_clause_p(Node *clause)
+{
+    if (!is_opclause(clause))
+       return false;
+
+    if (IsA (get_leftop((Expr*)clause),Var) &&
+       IsA (get_rightop((Expr*)clause),Const))
+       {
+           return(true);
+       }
+    else if (IsA (get_rightop((Expr*)clause),Var) &&
+            IsA (get_leftop((Expr*)clause),Const))
+       {
+           return(true);
+       }
+    return(false);
+}
+
+/*    
+ * fix-opid--
+ *    Calculate the opfid from the opno...
+ *    
+ * Returns nothing.
+ *    
+ */
+void
+fix_opid(Node *clause)
+{
+    if (clause==NULL || single_node(clause)) {
+       ;
+    } 
+    else if (or_clause (clause)) {
+       fix_opids(((Expr*)clause)->args);
+    } 
+    else if (is_funcclause (clause)) {
+       fix_opids(((Expr*)clause)->args);
+    } 
+    else if (IsA(clause,ArrayRef)) {
+       ArrayRef *aref = (ArrayRef *)clause;
+       
+       fix_opids(aref->refupperindexpr);
+       fix_opids(aref->reflowerindexpr);
+       fix_opid(aref->refexpr);
+       fix_opid(aref->refassgnexpr);
+    }
+    else if (not_clause(clause)) {
+       fix_opid((Node*)get_notclausearg((Expr*)clause));
+    } 
+    else if (is_opclause (clause)) {
+       replace_opid((Oper*)((Expr*)clause)->oper);
+       fix_opid((Node*)get_leftop((Expr*)clause));
+       fix_opid((Node*)get_rightop((Expr*)clause));
+    }
+
+}
+
+/*    
+ * fix-opids--
+ *    Calculate the opfid from the opno for all the clauses...
+ *    
+ * Returns its argument.
+ *    
+ */
+List *
+fix_opids(List *clauses)
+{
+    List *clause;
+
+    foreach(clause, clauses)
+       fix_opid(lfirst(clause));
+
+    return(clauses);
+}
+
+/*    
+ * get_relattval--
+ *     For a non-join clause, returns a list consisting of the 
+ *             relid,
+ *             attno,
+ *             value of the CONST node (if any), and a
+ *             flag indicating whether the value appears on the left or right
+ *                     of the operator and whether the value varied.
+ *
+ * OLD OBSOLETE COMMENT FOLLOWS:
+ *     If 'clause' is not of the format (op var node) or (op node var),
+ *     or if the var refers to a nested attribute, then -1's are returned for
+ *     everything but the value  a blank string "" (pointer to \0) is
+ *     returned for the value if it is unknown or null.
+ * END OF OLD OBSOLETE COMMENT.
+ * NEW COMMENT:
+ * when defining rules one of the attibutes of the operator can
+ * be a Param node (which is supposed to be treated as a constant).
+ * However as there is no value specified for a parameter until run time
+ * this routine used to return "" as value, which made 'compute_selec'
+ * to bomb (because it was expecting a lisp integer and got back a lisp
+ * string). Now the code returns a plain old good "lispInteger(0)".
+ *    
+ */
+void
+get_relattval(Node *clause,
+             int *relid,
+             AttrNumber *attno,
+             Datum *constval,
+             int *flag)
+{
+    Var *left = get_leftop((Expr*)clause);
+    Var *right = get_rightop((Expr*)clause);
+    
+    if(is_opclause(clause) && IsA(left,Var) && 
+       IsA(right,Const)) {
+
+       if(right!=NULL) {
+
+           *relid = left->varno;
+           *attno = left->varattno;
+           *constval = ((Const *)right)->constvalue;
+           *flag = (_SELEC_CONSTANT_RIGHT_ | _SELEC_IS_CONSTANT_);
+
+       } else {
+
+           *relid = left->varno;
+           *attno = left->varattno;
+           *constval = 0;
+           *flag = (_SELEC_CONSTANT_RIGHT_ | _SELEC_NOT_CONSTANT_);
+       
+       } 
+    }else if (is_opclause(clause) &&
+             is_funcclause((Node*)left) &&
+             IsA(right,Const)) {
+       List *args = ((Expr*)left)->args;
+
+
+       *relid = ((Var*)lfirst(args))->varno;
+       *attno = InvalidAttrNumber;
+       *constval = ((Const*)right)->constvalue;
+       *flag = (_SELEC_CONSTANT_RIGHT_ | _SELEC_IS_CONSTANT_);
+    
+       /*
+        * XXX both of these func clause handling if's seem wrong to me.
+        *     they assume that the first argument is the Var.  It could
+        *     not handle (for example) f(1, emp.name).  I think I may have
+        *     been assuming no constants in functional index scans when I
+        *     implemented this originally (still currently true).
+        *     -mer 10 Aug 1992
+        */
+    } else if (is_opclause(clause) &&
+            is_funcclause((Node*)right) &&
+              IsA(left,Const)) {
+       List *args = ((Expr*)right)->args;
+
+       *relid = ((Var*)lfirst(args))->varno;
+       *attno = InvalidAttrNumber;
+       *constval = ((Const*)left)->constvalue;
+       *flag = ( _SELEC_IS_CONSTANT_);
+    
+    } else if (is_opclause (clause) && IsA (right,Var) &&
+             IsA (left,Const)) {
+       if (left!=NULL) {
+
+           *relid = right->varno;
+           *attno = right->varattno;
+           *constval = ((Const*)left)->constvalue;
+           *flag = (_SELEC_IS_CONSTANT_);
+       } else {
+
+           *relid = right->varno;
+           *attno = right->varattno;
+           *constval = 0;
+           *flag = (_SELEC_NOT_CONSTANT_);
+       } 
+    } else {
+       /* One or more of the operands are expressions 
+        * (e.g., oper clauses)
+        */
+       *relid = _SELEC_VALUE_UNKNOWN_;
+       *attno = _SELEC_VALUE_UNKNOWN_;
+       *constval = 0;
+       *flag = (_SELEC_NOT_CONSTANT_);
+    }                 
+}
+
+/*    
+ * get_relsatts--
+ *    
+ * Returns a list 
+ *             ( relid1 attno1 relid2 attno2 )
+ *     for a joinclause.
+ *    
+ * If the clause is not of the form (op var var) or if any of the vars
+ * refer to nested attributes, then -1's are returned.
+ *    
+ */
+void
+get_rels_atts(Node *clause,
+             int *relid1,
+             AttrNumber *attno1,
+             int *relid2,
+             AttrNumber *attno2)
+{
+    Var *left = get_leftop((Expr*)clause);
+    Var *right = get_rightop((Expr*)clause);
+    bool var_left = (IsA(left,Var));
+    bool var_right = (IsA(right,Var));
+    bool varexpr_left = (bool)((IsA(left,Func) || IsA (left,Oper)) &&
+                              contain_var_clause((Node*)left));
+    bool varexpr_right = (bool)(( IsA(right,Func) || IsA (right,Oper)) &&
+                               contain_var_clause((Node*)right));
+    
+    if (is_opclause(clause)) {
+       if(var_left && var_right) {
+
+           *relid1 = left->varno;
+           *attno1 = left->varoattno;
+           *relid2 = right->varno;
+           *attno2 = right->varoattno;
+           return;
+       } else if (var_left && varexpr_right ) {
+
+           *relid1 = left->varno;
+           *attno1 = left->varoattno;
+           *relid2 = _SELEC_VALUE_UNKNOWN_;
+           *attno2 = _SELEC_VALUE_UNKNOWN_;
+           return;
+       } else if (varexpr_left && var_right) {
+
+           *relid1 = _SELEC_VALUE_UNKNOWN_;
+           *attno1 = _SELEC_VALUE_UNKNOWN_;
+           *relid2 = right->varno;
+           *attno2 = right->varoattno;
+           return;
+       }
+    }
+
+    *relid1 = _SELEC_VALUE_UNKNOWN_;
+    *attno1 = _SELEC_VALUE_UNKNOWN_;
+    *relid2 = _SELEC_VALUE_UNKNOWN_;
+    *attno2 = _SELEC_VALUE_UNKNOWN_;
+    return;
+}
+
+void
+CommuteClause(Node *clause)
+{
+    Node *temp;
+    Oper *commu;
+    OperatorTupleForm commuTup;
+    HeapTuple heapTup;
+
+    if (!is_opclause(clause))
+       return;
+
+    heapTup = (HeapTuple)
+       get_operator_tuple(get_commutator(((Oper*)((Expr*)clause)->oper)->opno));
+
+    if (heapTup == (HeapTuple)NULL)
+       return;
+
+    commuTup = (OperatorTupleForm)GETSTRUCT(heapTup);
+
+    commu = makeOper(heapTup->t_oid,
+                    InvalidOid,
+                    commuTup->oprresult,
+                    ((Oper*)((Expr*)clause)->oper)->opsize,
+                    NULL);
+
+    /*
+     * reform the clause -> (operator func/var constant)
+     */
+    ((Expr*)clause)->oper = (Node*)commu;
+    temp = lfirst(((Expr*)clause)->args);
+    lfirst(((Expr*)clause)->args) = lsecond(((Expr*)clause)->args);
+    lsecond(((Expr*)clause)->args) = temp;
+}
+
+
diff --git a/src/backend/optimizer/util/indexnode.c b/src/backend/optimizer/util/indexnode.c
new file mode 100644 (file)
index 0000000..6ab4524
--- /dev/null
@@ -0,0 +1,92 @@
+/*-------------------------------------------------------------------------
+ *
+ * indexnode.c--
+ *    Routines to find all indices on a relation
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/plannodes.h"
+#include "nodes/parsenodes.h"
+#include "nodes/relation.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/plancat.h"
+#include "optimizer/pathnode.h"                /* where the decls go */
+
+
+static List *find_secondary_index(Query *root, Oid relid);
+
+/*    
+ * find-relation-indices--
+ *    Returns a list of index nodes containing appropriate information for
+ *    each (secondary) index defined on a relation.
+ *    
+ */
+List *
+find_relation_indices(Query *root, Rel *rel)
+{
+    if (rel->indexed) {
+       return (find_secondary_index(root, lfirsti(rel->relids)));
+    } else {
+       return (NIL);
+    }
+}
+
+/*    
+ * find-secondary-index--
+ *    Creates a list of index path nodes containing information for each
+ *    secondary index defined on a relation by searching through the index
+ *    catalog.
+ *    
+ * 'relid' is the OID of the relation for which indices are being located
+ *    
+ * Returns a list of new index nodes.
+ *    
+ */
+static List *
+find_secondary_index(Query *root, Oid relid)
+{
+    IdxInfoRetval indexinfo;
+    List *indexes = NIL;
+    bool first = TRUE;
+
+    while (index_info(root, first, relid,&indexinfo)) {
+       Rel *indexnode = makeNode(Rel);
+
+       indexnode->relids = lconsi(indexinfo.relid,NIL);
+       indexnode->relam = indexinfo.relam;
+       indexnode->pages = indexinfo.pages;
+       indexnode->tuples = indexinfo.tuples;
+       indexnode->indexkeys = indexinfo.indexkeys;
+       indexnode->ordering = indexinfo.orderOprs;
+       indexnode->classlist = indexinfo.classlist;
+       indexnode->indproc= indexinfo.indproc;
+       indexnode->indpred = (List*)indexinfo.indpred;
+       
+       indexnode->indexed= false; /* not indexed itself */
+       indexnode->size = 0;
+       indexnode->width= 0;
+       indexnode->targetlist= NIL;
+       indexnode->pathlist= NIL;
+       indexnode->unorderedpath= NULL;
+       indexnode->cheapestpath= NULL;
+       indexnode->pruneable= true;
+       indexnode->clauseinfo= NIL;
+       indexnode->joininfo= NIL;
+       indexnode->innerjoin= NIL;
+
+       indexes = lcons(indexnode, indexes);
+       first = FALSE;
+    }
+
+    return indexes;
+}
+
diff --git a/src/backend/optimizer/util/internal.c b/src/backend/optimizer/util/internal.c
new file mode 100644 (file)
index 0000000..da96c40
--- /dev/null
@@ -0,0 +1,61 @@
+/*-------------------------------------------------------------------------
+ *
+ * internal.c--
+ *    Definitions required throughout the query optimizer.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*    
+ *     ---------- SHARED MACROS
+ *    
+ *             Macros common to modules for creating, accessing, and modifying
+ *     query tree and query plan components.
+ *     Shared with the executor.
+ *    
+ */
+
+
+#include "optimizer/internal.h"
+
+#include "nodes/relation.h"
+#include "nodes/plannodes.h"
+#include "nodes/primnodes.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+#if 0 
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+/* the following should probably be moved elsewhere -ay */
+
+TargetEntry *
+MakeTLE(Resdom *resdom, Node *expr)
+{
+    TargetEntry *rt = makeNode(TargetEntry);
+    rt->resdom = resdom;
+    rt->expr = expr;
+    return rt;
+}
+
+Var *
+get_expr(TargetEntry *tle)
+{
+    Assert(tle!=NULL);
+    Assert(tle->expr!=NULL);
+
+    return ((Var *)tle->expr); 
+}
+
+#endif /* 0 */
+
+
+
diff --git a/src/backend/optimizer/util/joininfo.c b/src/backend/optimizer/util/joininfo.c
new file mode 100644 (file)
index 0000000..03e38ab
--- /dev/null
@@ -0,0 +1,107 @@
+/*-------------------------------------------------------------------------
+ *
+ * joininfo.c--
+ *    JoinInfo node manipulation routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/relation.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/var.h"
+#include "optimizer/clauses.h"
+
+
+/*    
+ * joininfo-member--
+ *    Determines whether a node has already been created for a join
+ *    between a set of join relations and the relation described by
+ *    'joininfo-list'.
+ *    
+ * 'join-relids' is a list of relids corresponding to the join relation
+ * 'joininfo-list' is the list of joininfo nodes against which this is 
+ *             checked
+ *    
+ * Returns the corresponding node in 'joininfo-list' if such a node
+ * exists.
+ *    
+ */
+JInfo *
+joininfo_member(List *join_relids, List *joininfo_list)
+{
+    List *i = NIL;
+    List *other_rels = NIL;
+
+    foreach(i,joininfo_list) {
+       other_rels = lfirst(i);
+       if(same(join_relids, ((JInfo*)other_rels)->otherrels))
+           return((JInfo*)other_rels);
+    }
+    return((JInfo*)NULL);
+}
+
+
+/*    
+ * find-joininfo-node--
+ *    Find the joininfo node within a relation entry corresponding
+ *    to a join between 'this_rel' and the relations in 'join-relids'.  A
+ *    new node is created and added to the relation entry's joininfo
+ *    field if the desired one can't be found.
+ *    
+ * Returns a joininfo node.
+ *    
+ */
+JInfo *
+find_joininfo_node(Rel *this_rel, List *join_relids)
+{
+    JInfo *joininfo = joininfo_member(join_relids,
+                                     this_rel->joininfo);
+    if( joininfo == NULL ) {
+       joininfo = makeNode(JInfo);
+       joininfo->otherrels = join_relids;
+       joininfo->jinfoclauseinfo = NIL;
+       joininfo->mergesortable = false;
+       joininfo->hashjoinable = false;
+       joininfo->inactive = false;
+       this_rel->joininfo = lcons(joininfo, this_rel->joininfo);
+    }
+    return(joininfo);
+}
+
+/*    
+ * other-join-clause-var--
+ *    Determines whether a var node is contained within a joinclause
+ *    of the form(op var var).
+ *    
+ * Returns the other var node in the joinclause if it is, nil if not.
+ *    
+ */
+Var *
+other_join_clause_var(Var *var, Expr *clause)
+{
+     Var *retval;
+     Var *l, *r;
+
+     retval = (Var*) NULL;
+
+     if( var != NULL  && join_clause_p((Node*)clause)) {
+         l = (Var *) get_leftop(clause);
+         r = (Var *) get_rightop(clause);
+
+         if(var_equal(var, l)) {
+              retval = r;
+         } else if(var_equal(var, r)) {
+              retval = l;
+         }
+     }
+
+     return(retval);
+}
diff --git a/src/backend/optimizer/util/keys.c b/src/backend/optimizer/util/keys.c
new file mode 100644 (file)
index 0000000..91a688b
--- /dev/null
@@ -0,0 +1,193 @@
+/*-------------------------------------------------------------------------
+ *
+ * keys.c--
+ *    Key manipulation routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "nodes/pg_list.h"
+#include "nodes/nodes.h"
+#include "nodes/relation.h"
+#include "utils/elog.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/keys.h"
+#include "optimizer/tlist.h"
+
+
+static Expr *matching2_tlvar(int var, List *tlist, bool (*test)());
+
+/*    
+ * 1. index key
+ *     one of:
+ *             attnum
+ *             (attnum arrayindex)
+ * 2. path key
+ *     (subkey1 ... subkeyN)
+ *     where subkeyI is a var node
+ *     note that the 'Keys field is a list of these
+ * 3. join key
+ *     (outer-subkey inner-subkey)
+ *             where each subkey is a var node
+ * 4. sort key
+ *     one of:
+ *             SortKey node
+ *             number
+ *             nil
+ *     (may also refer to the 'SortKey field of a SortKey node,
+ *      which looks exactly like an index key)
+ *    
+ */
+
+/*    
+ * match-indexkey-operand--
+ *    Returns t iff an index key 'index-key' matches the given clause 
+ *    operand.
+ *    
+ */
+bool
+match_indexkey_operand(int indexkey, Var *operand, Rel *rel)
+{
+    if (IsA (operand,Var) &&
+       (lfirsti(rel->relids) == operand->varno) &&
+       equal_indexkey_var(indexkey,operand))
+       return(true);
+    else
+       return(false);
+}
+
+/*    
+ * equal_indexkey_var--
+ *    Returns t iff an index key 'index-key' matches the corresponding 
+ *    fields of var node 'var'.
+ *    
+ */
+bool
+equal_indexkey_var(int index_key, Var *var)
+{
+    if (index_key == var->varattno)
+       return(true);
+    else
+       return(false);
+}
+
+/*    
+ * extract-subkey--
+ *   Returns the subkey in a join key corresponding to the outer or inner
+ *   lelation.
+ *    
+ */
+Var *
+extract_subkey(JoinKey *jk, int which_subkey)
+{
+    Var *retval;
+
+    switch (which_subkey) {
+    case OUTER: 
+       retval = jk->outer;
+       break;
+    case INNER: 
+       retval = jk->inner;
+       break;
+    default:                   /* do nothing */
+       elog(DEBUG,"extract_subkey with neither INNER or OUTER");
+       retval = NULL;
+    }
+    return(retval);
+}
+
+/*    
+ * samekeys--
+ *    Returns t iff two sets of path keys are equivalent.  They are 
+ *    equivalent if the first subkey (var node) within each sublist of 
+ *    list 'keys1' is contained within the corresponding sublist of 'keys2'.
+ *    
+ *    XXX      It isn't necessary to check that each sublist exactly contain 
+ *             the same elements because if the routine that built these
+ *             sublists together is correct, having one element in common 
+ *             implies having all elements in common.
+ *    
+ */
+bool
+samekeys(List *keys1, List *keys2)
+{
+    bool allmember = true;
+    List *key1, *key2;
+
+    for(key1=keys1,key2=keys2 ; key1 != NIL && key2 !=NIL ; 
+       key1=lnext(key1), key2=lnext(key2)) 
+       if (!member(lfirst(key1), lfirst(key2)))
+           allmember = false;
+
+    if ( (length (keys2) >= length (keys1)) && allmember)
+       return(true);
+    else
+       return(false);
+}
+
+/*    
+ * collect-index-pathkeys--
+ *    Creates a list of subkeys by retrieving var nodes corresponding to
+ *    each index key in 'index-keys' from the relation's target list
+ *    'tlist'.  If the key is not in the target list, the key is irrelevant
+ *    and is thrown away.  The returned subkey list is of the form:
+ *             ((var1) (var2) ... (varn))
+ *    
+ * 'index-keys' is a list of index keys
+ * 'tlist' is a relation target list
+ *    
+ * Returns the list of cons'd subkeys.
+ *    
+ */
+/* This function is identical to matching_tlvar and tlistentry_member.
+ * They should be merged.
+ */
+static Expr *
+matching2_tlvar(int var, List *tlist, bool (*test)())
+{
+    TargetEntry *tlentry = NULL;
+
+    if (var) {
+       List *temp;
+       foreach (temp,tlist) {
+           if ((*test)(var, get_expr(lfirst(temp)))) {
+               tlentry = lfirst(temp);
+               break;
+           }
+       }
+    }
+
+    if (tlentry) 
+       return((Expr*)get_expr(tlentry));
+    else
+       return((Expr*)NULL);
+}
+
+
+List *
+collect_index_pathkeys(int *index_keys, List *tlist)
+{
+    List *retval = NIL;
+
+    Assert (index_keys != NULL);
+
+    while(index_keys[0] != 0) {
+       Expr *mvar;
+       mvar = matching2_tlvar(index_keys[0],
+                              tlist,
+                              equal_indexkey_var);
+       if (mvar) 
+           retval = nconc(retval,lcons(lcons(mvar,NIL),
+                                       NIL));
+       index_keys++;
+    }
+    return(retval);
+}
+
diff --git a/src/backend/optimizer/util/ordering.c b/src/backend/optimizer/util/ordering.c
new file mode 100644 (file)
index 0000000..b072a13
--- /dev/null
@@ -0,0 +1,117 @@
+/*-------------------------------------------------------------------------
+ *
+ * ordering.c--
+ *    Routines to manipulate and compare merge and path orderings
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "optimizer/internal.h"
+#include "optimizer/ordering.h"
+
+
+/*    
+ * equal-path-path-ordering--
+ *    Returns t iff two path orderings are equal.
+ *    
+ */
+bool
+equal_path_path_ordering(PathOrder *path_ordering1,
+                        PathOrder *path_ordering2)
+{
+    if (path_ordering1 == path_ordering2)
+       return true;
+
+    if (!path_ordering1 || !path_ordering2)
+       return false;
+
+    if (path_ordering1->ordtype == MERGE_ORDER &&
+       path_ordering2->ordtype == MERGE_ORDER) {
+
+       return equal(path_ordering1->ord.merge, path_ordering2->ord.merge);
+
+    } else if (path_ordering1->ordtype == SORTOP_ORDER &&
+              path_ordering2->ordtype == SORTOP_ORDER) {
+
+       return
+           (equal_sortops_order(path_ordering1->ord.sortop,
+                                path_ordering2->ord.sortop));
+    } else if (path_ordering1->ordtype == MERGE_ORDER &&
+              path_ordering2->ordtype == SORTOP_ORDER) {
+
+       return (path_ordering2->ord.sortop &&
+               (path_ordering1->ord.merge->left_operator ==
+                path_ordering2->ord.sortop[0]));
+    } else {
+
+       return (path_ordering1->ord.sortop &&
+               (path_ordering1->ord.sortop[0] ==
+                path_ordering2->ord.merge->left_operator));
+    }
+}
+
+/*    
+ * equal-path-merge-ordering--
+ *    Returns t iff a path ordering is usable for ordering a merge join.
+ *
+ * XXX Presently, this means that the first sortop of the path matches
+ *     either of the merge sortops.  Is there a "right" and "wrong"
+ *     sortop to match?
+ *    
+ */
+bool
+equal_path_merge_ordering(Oid *path_ordering,
+                         MergeOrder *merge_ordering)
+{
+    if (path_ordering == NULL || merge_ordering == NULL)
+       return(false);
+
+    if (path_ordering[0] == merge_ordering->left_operator ||
+       path_ordering[0] == merge_ordering->right_operator)
+       return(true);
+    else
+       return(false);
+}
+
+/*    
+ * equal-merge-merge-ordering--
+ *    Returns t iff two merge orderings are equal.
+ *    
+ */
+bool
+equal_merge_merge_ordering(MergeOrder *merge_ordering1,
+                          MergeOrder *merge_ordering2)
+{
+    return (equal(merge_ordering1, merge_ordering2));
+}
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+/*
+ * equal_sort_ops_order -
+ *    Returns true iff the sort operators are in the same order.
+ */
+bool
+equal_sortops_order(Oid *ordering1, Oid *ordering2)
+{
+    int i = 0;
+
+    if (ordering1 == NULL || ordering2 == NULL)
+       return (ordering1==ordering2);
+    
+    while (ordering1[i]!=0 && ordering2[i]!=0) {
+       if (ordering1[i] != ordering2[i])
+           break;
+       i++;
+    }
+
+    return (ordering1[i]==0 && ordering2[i]==0);
+}
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
new file mode 100644 (file)
index 0000000..0d99cdd
--- /dev/null
@@ -0,0 +1,566 @@
+/*-------------------------------------------------------------------------
+ *
+ * pathnode.c--
+ *    Routines to manipulate pathlists and create path nodes
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <math.h>
+
+#include "postgres.h"
+
+#include "nodes/relation.h"
+#include "utils/elog.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/pathnode.h"
+#include "optimizer/clauseinfo.h"
+#include "optimizer/plancat.h"
+#include "optimizer/cost.h"
+#include "optimizer/keys.h"
+#include "optimizer/xfunc.h"
+#include "optimizer/ordering.h"
+
+#include "parser/parsetree.h"          /* for getrelid() */
+
+static Path *better_path(Path *new_path, List *unique_paths, bool *noOther);
+
+
+/*****************************************************************************
+ *     MISC. PATH UTILITIES
+ *****************************************************************************/
+
+/*    
+ * path-is-cheaper--
+ *    Returns t iff 'path1' is cheaper than 'path2'.
+ *    
+ */
+bool
+path_is_cheaper(Path *path1, Path *path2)
+{
+    Cost cost1 = path1->path_cost;
+    Cost cost2 = path2->path_cost;
+
+    return((bool)(cost1 < cost2));
+}
+
+/*    
+ * set_cheapest--
+ *    Finds the minimum cost path from among a relation's paths.  
+ *    
+ * 'parent-rel' is the parent relation
+ * 'pathlist' is a list of path nodes corresponding to 'parent-rel'
+ *    
+ * Returns and sets the relation entry field with the pathnode that 
+ * is minimum.
+ *    
+ */
+Path *
+set_cheapest(Rel *parent_rel, List *pathlist)
+{
+    List *p;
+    Path *cheapest_so_far;
+
+    Assert(pathlist!=NIL);
+    Assert(IsA(parent_rel,Rel));
+
+    cheapest_so_far = (Path*)lfirst(pathlist);
+
+    foreach (p, lnext(pathlist)) {
+       Path *path = (Path*)lfirst(p);
+
+       if (path_is_cheaper(path, cheapest_so_far)) {
+           cheapest_so_far = path;
+       }
+    }
+
+    parent_rel->cheapestpath = cheapest_so_far;
+
+    return(cheapest_so_far);
+}
+
+/*    
+ * add_pathlist--
+ *    For each path in the list 'new-paths', add to the list 'unique-paths' 
+ *    only those paths that are unique (i.e., unique ordering and ordering 
+ *    keys).  Should a conflict arise, the more expensive path is thrown out,
+ *    thereby pruning the plan space.  But we don't prune if xfunc
+ *    told us not to.
+ *    
+ * 'parent-rel' is the relation entry to which these paths correspond.
+ *    
+ * Returns the list of unique pathnodes.
+ *    
+ */
+List *
+add_pathlist(Rel *parent_rel, List *unique_paths, List *new_paths)
+{
+    List *x;
+    Path *new_path;
+    Path *old_path;
+    bool noOther;
+
+    foreach (x, new_paths) {
+       new_path = (Path*)lfirst(x);
+       if (member(new_path, unique_paths)) 
+           continue;
+       old_path = better_path(new_path,unique_paths,&noOther);
+
+       if (noOther) {
+           /*  Is a brand new path.  */
+           new_path->parent = parent_rel;
+           unique_paths = lcons(new_path, unique_paths);
+       } else if (old_path==NULL) {
+           ;   /* do nothing if path is not cheaper */
+       } else if (old_path != NULL) { /* (IsA(old_path,Path)) { */
+           new_path->parent = parent_rel;
+           if (!parent_rel->pruneable) {
+               unique_paths = lcons(new_path, unique_paths);
+           }else
+               unique_paths = lcons(new_path,
+                                   LispRemove(old_path,unique_paths));
+       }
+    }
+    return(unique_paths);
+}
+
+/*    
+ * better_path--
+ *    Determines whether 'new-path' has the same ordering and keys as some 
+ *    path in the list 'unique-paths'.  If there is a redundant path,
+ *    eliminate the more expensive path.
+ *    
+ * Returns:
+ *    The old path - if 'new-path' matches some path in 'unique-paths' and is
+ *             cheaper
+ *    nil - if 'new-path' matches but isn't cheaper
+ *    t - if there is no path in the list with the same ordering and keys
+ *    
+ */
+static Path *
+better_path(Path *new_path, List *unique_paths, bool *noOther)
+{
+    Path *old_path = (Path*)NULL;
+    Path *path = (Path*)NULL;
+    List *temp = NIL;
+    Path *retval = NULL;
+
+    /* XXX - added the following two lines which weren't int
+     * the lisp planner, but otherwise, doesn't seem to work
+     * for the case where new_path is 'nil
+     */
+    foreach (temp,unique_paths) {
+       path = (Path*) lfirst(temp);
+
+       if ((equal_path_path_ordering(&new_path->p_ordering,
+                                     &path->p_ordering) &&
+            samekeys(new_path->keys, path->keys))) {
+           old_path = path;
+           break;
+       }
+    }
+
+    if (old_path==NULL) {
+       *noOther = true;
+    } else { 
+       *noOther = false;
+       if (path_is_cheaper(new_path,old_path)) {
+           retval = old_path;
+       }
+    }
+     
+    return(retval);
+}
+
+
+
+/*****************************************************************************
+ *     PATH NODE CREATION ROUTINES
+ *****************************************************************************/
+
+/*    
+ * create_seqscan_path--
+ *    Creates a path corresponding to a sequential scan, returning the
+ *    pathnode.
+ *    
+ */
+Path *
+create_seqscan_path(Rel *rel)
+{
+    int relid=0;
+
+    Path *pathnode = makeNode(Path);
+
+    pathnode->pathtype = T_SeqScan; 
+    pathnode->parent = rel;
+    pathnode->path_cost = 0.0;
+    pathnode->p_ordering.ordtype = SORTOP_ORDER;
+    pathnode->p_ordering.ord.sortop = NULL;
+    pathnode->keys = NIL;
+    /* copy clauseinfo list into path for expensive function processing 
+     * -- JMH, 7/7/92
+     */
+    pathnode->locclauseinfo= 
+       (List*)copyObject((Node*)rel->clauseinfo);
+
+    if (rel->relids !=NULL)
+       relid = lfirsti(rel->relids);
+
+    pathnode->path_cost = cost_seqscan (relid,
+                                       rel->pages, rel->tuples);
+    /* add in expensive functions cost!  -- JMH, 7/7/92 */
+#if 0
+    if (XfuncMode != XFUNC_OFF) {
+       pathnode->path_cost +=
+           xfunc_get_path_cost(pathnode));
+    }
+#endif
+    return (pathnode);
+}
+
+/*    
+ * create_index_path--
+ *    Creates a single path node for an index scan.
+ *    
+ * 'rel' is the parent rel
+ * 'index' is the pathnode for the index on 'rel'
+ * 'restriction-clauses' is a list of restriction clause nodes.
+ * 'is-join-scan' is a flag indicating whether or not the index is being
+ *     considered because of its sort order.
+ *    
+ * Returns the new path node.
+ *    
+ */
+IndexPath *
+create_index_path(Query *root,
+                  Rel *rel,
+                 Rel *index,
+                 List *restriction_clauses,
+                 bool is_join_scan)
+{
+    IndexPath *pathnode = makeNode(IndexPath);
+    
+    pathnode->path.pathtype = T_IndexScan;
+    pathnode->path.parent = rel;
+    pathnode->indexid = index->relids;
+
+    pathnode->path.p_ordering.ordtype = SORTOP_ORDER;
+    pathnode->path.p_ordering.ord.sortop = index->ordering;
+    pathnode->indexqual = NIL;
+
+    /* copy clauseinfo list into path for expensive function processing 
+     *  -- JMH, 7/7/92
+     */
+    pathnode->path.locclauseinfo =
+       set_difference((List*) copyObject((Node*)rel->clauseinfo),
+                      (List*) restriction_clauses);
+
+    /*
+     * The index must have an ordering for the path to have (ordering) keys, 
+     * and vice versa.
+     */
+    if (pathnode->path.p_ordering.ord.sortop) {
+       pathnode->path.keys = collect_index_pathkeys(index->indexkeys,
+                                                    rel->targetlist);
+       /*
+        * Check that the keys haven't 'disappeared', since they may 
+        * no longer be in the target list (i.e., index keys that are not 
+        * relevant to the scan are not applied to the scan path node,
+        * so if no index keys were found, we can't order the path).
+        */
+       if (pathnode->path.keys==NULL) {
+           pathnode->path.p_ordering.ord.sortop = NULL;
+       }
+    } else {
+       pathnode->path.keys = NULL;
+    }
+
+    if (is_join_scan || restriction_clauses==NULL) {
+       /*
+        * Indices used for joins or sorting result nodes don't
+        * restrict the result at all, they simply order it,
+        * so compute the scan cost 
+        * accordingly -- use a selectivity of 1.0.
+        */
+/* is the statement above really true?  what about IndexScan as the 
+   inner of a join? */
+       pathnode->path.path_cost =
+           cost_index (lfirsti(index->relids),
+                       index->pages,
+                       1.0,
+                       rel->pages,
+                       rel->tuples,
+                       index->pages,
+                       index->tuples,
+                       false);
+       /* add in expensive functions cost!  -- JMH, 7/7/92 */
+#if 0
+       if (XfuncMode != XFUNC_OFF) {
+           pathnode->path_cost =
+               (pathnode->path_cost +
+                xfunc_get_path_cost((Path*)pathnode));
+       }
+#endif
+    } else  {
+       /*
+        * Compute scan cost for the case when 'index' is used with a 
+        * restriction clause.
+        */
+       List *attnos;
+       List *values;
+       List *flags;
+       float npages;
+       float selec;
+       Cost clausesel;
+
+       get_relattvals(restriction_clauses,
+                      &attnos,
+                      &values,
+                      &flags);
+       index_selectivity(lfirsti(index->relids),
+                         index->classlist,
+                         get_opnos(restriction_clauses),
+                         getrelid(lfirsti(rel->relids),
+                                  root->rtable),
+                         attnos,
+                         values,
+                         flags,
+                         length(restriction_clauses),
+                         &npages,
+                         &selec);
+       /*   each clause gets an equal selectivity */
+       clausesel = 
+           pow(selec, 
+               1.0 / (double) length(restriction_clauses));
+    
+       pathnode->indexqual = restriction_clauses;
+       pathnode->path.path_cost =
+           cost_index (lfirsti(index->relids),
+                       (int)npages,
+                       selec,
+                       rel->pages,
+                       rel->tuples,
+                       index->pages,
+                       index->tuples,
+                       false);
+
+#if 0
+       /* add in expensive functions cost!  -- JMH, 7/7/92 */
+       if (XfuncMode != XFUNC_OFF) {
+           pathnode->path_cost += 
+               xfunc_get_path_cost((Path*)pathnode);
+       }
+#endif
+       /* Set selectivities of clauses used with index to the selectivity 
+        * of this index, subdividing the selectivity equally over each of 
+        * the clauses. 
+        */
+
+       /* XXX Can this divide the selectivities in a better way? */
+       set_clause_selectivities(restriction_clauses, clausesel);
+    }
+    return(pathnode);
+}
+
+/*    
+ * create_nestloop_path--
+ *    Creates a pathnode corresponding to a nestloop join between two 
+ *    relations.
+ *    
+ * 'joinrel' is the join relation.
+ * 'outer_rel' is the outer join relation
+ * 'outer_path' is the outer join path.
+ * 'inner_path' is the inner join path.
+ * 'keys' are the keys of the path
+ *     
+ * Returns the resulting path node.
+ *    
+ */
+JoinPath *
+create_nestloop_path(Rel *joinrel,
+                    Rel *outer_rel,
+                    Path *outer_path,
+                    Path *inner_path,
+                    List *keys)
+{
+    JoinPath *pathnode = makeNode(JoinPath);
+     
+    pathnode->path.pathtype = T_NestLoop;
+    pathnode->path.parent  = joinrel;
+    pathnode->outerjoinpath = outer_path;
+    pathnode->innerjoinpath = inner_path;
+    pathnode->pathclauseinfo = joinrel->clauseinfo;
+    pathnode->path.keys = keys;
+    pathnode->path.joinid = NIL;
+    pathnode->path.outerjoincost = (Cost)0.0;
+    pathnode->path.locclauseinfo = NIL;
+
+    if (keys) {
+       pathnode->path.p_ordering.ordtype =
+           outer_path->p_ordering.ordtype;
+       if (outer_path->p_ordering.ordtype == SORTOP_ORDER) {
+           pathnode->path.p_ordering.ord.sortop =
+               outer_path->p_ordering.ord.sortop;
+       } else {
+           pathnode->path.p_ordering.ord.merge =
+               outer_path->p_ordering.ord.merge;
+       }
+    } else {
+       pathnode->path.p_ordering.ordtype = SORTOP_ORDER;
+       pathnode->path.p_ordering.ord.sortop = NULL;
+    }
+
+    pathnode->path.path_cost  = 
+       cost_nestloop(outer_path->path_cost,
+                     inner_path->path_cost,
+                     outer_rel->size,
+                     inner_path->parent->size,
+                     page_size(outer_rel->size,
+                               outer_rel->width),
+                     IsA(inner_path,IndexPath));
+    /* add in expensive function costs -- JMH 7/7/92 */
+#if 0
+    if (XfuncMode != XFUNC_OFF) {
+       pathnode->path_cost += xfunc_get_path_cost((Path*)pathnode);
+    }
+#endif
+    return(pathnode);
+}
+
+/*    
+ * create_mergesort_path--
+ *    Creates a pathnode corresponding to a mergesort join between
+ *    two relations
+ *    
+ * 'joinrel' is the join relation
+ * 'outersize' is the number of tuples in the outer relation
+ * 'innersize' is the number of tuples in the inner relation
+ * 'outerwidth' is the number of bytes per tuple in the outer relation
+ * 'innerwidth' is the number of bytes per tuple in the inner relation
+ * 'outer_path' is the outer path
+ * 'inner_path' is the inner path
+ * 'keys' are the new keys of the join relation
+ * 'order' is the sort order required for the merge
+ * 'mergeclauses' are the applicable join/restriction clauses
+ * 'outersortkeys' are the sort varkeys for the outer relation
+ * 'innersortkeys' are the sort varkeys for the inner relation
+ *    
+ */
+MergePath *
+create_mergesort_path(Rel *joinrel,
+                     int outersize,
+                     int innersize,
+                     int outerwidth,
+                     int innerwidth,
+                     Path *outer_path,
+                     Path *inner_path,
+                     List *keys,
+                     MergeOrder *order,
+                     List *mergeclauses,
+                     List *outersortkeys,
+                     List *innersortkeys)
+{
+    MergePath *pathnode = makeNode(MergePath);
+
+    pathnode->jpath.path.pathtype  = T_MergeJoin;
+    pathnode->jpath.path.parent  = joinrel;
+    pathnode->jpath.outerjoinpath = outer_path;
+    pathnode->jpath.innerjoinpath = inner_path;
+    pathnode->jpath.pathclauseinfo = joinrel->clauseinfo;
+    pathnode->jpath.path.keys = keys;
+    pathnode->jpath.path.p_ordering.ordtype = MERGE_ORDER;
+    pathnode->jpath.path.p_ordering.ord.merge  = order;
+    pathnode->path_mergeclauses = mergeclauses;
+    pathnode->jpath.path.locclauseinfo = NIL;
+    pathnode->outersortkeys = outersortkeys;
+    pathnode->innersortkeys = innersortkeys;
+    pathnode->jpath.path.path_cost  =
+       cost_mergesort(outer_path->path_cost,
+                      inner_path->path_cost,
+                      outersortkeys,
+                      innersortkeys,
+                      outersize,
+                      innersize,
+                      outerwidth,
+                      innerwidth);
+    /* add in expensive function costs -- JMH 7/7/92 */
+#if 0
+    if (XfuncMode != XFUNC_OFF) {
+       pathnode->path_cost +=
+           xfunc_get_path_cost((Path*)pathnode);
+    }
+#endif
+    return(pathnode);
+}
+
+/*    
+ * create_hashjoin_path--                      XXX HASH
+ *    Creates a pathnode corresponding to a hash join between two relations.
+ *    
+ * 'joinrel' is the join relation
+ * 'outersize' is the number of tuples in the outer relation
+ * 'innersize' is the number of tuples in the inner relation
+ * 'outerwidth' is the number of bytes per tuple in the outer relation
+ * 'innerwidth' is the number of bytes per tuple in the inner relation
+ * 'outer_path' is the outer path
+ * 'inner_path' is the inner path
+ * 'keys' are the new keys of the join relation
+ * 'operator' is the hashjoin operator
+ * 'hashclauses' are the applicable join/restriction clauses
+ * 'outerkeys' are the sort varkeys for the outer relation
+ * 'innerkeys' are the sort varkeys for the inner relation
+ *    
+ */
+HashPath *
+create_hashjoin_path(Rel *joinrel,
+                    int outersize,
+                    int innersize,
+                    int outerwidth,
+                    int innerwidth,
+                    Path *outer_path,
+                    Path *inner_path,
+                    List *keys,
+                    Oid operator,
+                    List *hashclauses,
+                    List *outerkeys,
+                    List *innerkeys)
+{
+    HashPath *pathnode = makeNode(HashPath);
+
+    pathnode->jpath.path.pathtype  = T_HashJoin; 
+    pathnode->jpath.path.parent  = joinrel;
+    pathnode->jpath.outerjoinpath = outer_path;
+    pathnode->jpath.innerjoinpath = inner_path;
+    pathnode->jpath.pathclauseinfo = joinrel->clauseinfo;
+    pathnode->jpath.path.locclauseinfo = NIL;
+    pathnode->jpath.path.keys = keys;
+    pathnode->jpath.path.p_ordering.ordtype = SORTOP_ORDER;
+    pathnode->jpath.path.p_ordering.ord.sortop = NULL;
+    pathnode->jpath.path.outerjoincost = (Cost)0.0;
+    pathnode->jpath.path.joinid =  (Relid)NULL;
+    /*    pathnode->hashjoinoperator = operator;  */ 
+    pathnode->path_hashclauses = hashclauses;
+    pathnode->outerhashkeys = outerkeys;
+    pathnode->innerhashkeys = innerkeys;
+    pathnode->jpath.path.path_cost  =
+       cost_hashjoin(outer_path->path_cost,
+                     inner_path->path_cost,
+                     outerkeys,
+                     innerkeys,
+                     outersize,innersize,
+                     outerwidth,innerwidth);
+    /* add in expensive function costs -- JMH 7/7/92 */
+#if 0
+    if (XfuncMode != XFUNC_OFF) {
+       pathnode->path_cost += 
+           xfunc_get_path_cost((Path*)pathnode);
+    }
+#endif
+    return(pathnode);
+}
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
new file mode 100644 (file)
index 0000000..d73541d
--- /dev/null
@@ -0,0 +1,582 @@
+/*-------------------------------------------------------------------------
+ *
+ * plancat.c--
+ *     routines for accessing the system catalogs
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/htup.h"
+#include "access/itup.h"
+
+#include "catalog/catname.h"
+#include "catalog/pg_amop.h"
+#include "catalog/pg_index.h"
+#include "catalog/pg_inherits.h"
+#include "catalog/pg_version.h"
+
+#include "nodes/pg_list.h"
+#include "parser/parsetree.h"          /* for getrelid() */
+#include "fmgr.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/plancat.h"
+
+#include "utils/tqual.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/syscache.h"
+
+
+static void IndexSelectivity(Oid indexrelid, Oid indrelid, int32 nIndexKeys,
+       Oid AccessMethodOperatorClasses[], Oid operatorObjectIds[],
+       int32 varAttributeNumbers[], char *constValues[], int32 constFlags[],
+       float *idxPages, float *idxSelec);                   
+
+
+/*
+ * relation-info -
+ *    Retrieves catalog information for a given relation. Given the oid of
+ *    the relation, return the following information:
+ *             whether the relation has secondary indices
+ *             number of pages 
+ *             number of tuples
+ */
+void
+relation_info(Query *root, Index relid,
+             bool *hasindex, int *pages, int *tuples)
+{
+    HeapTuple          relationTuple;
+    Form_pg_class      relation;
+    Oid                 relationObjectId;
+
+    relationObjectId = getrelid(relid, root->rtable);
+    relationTuple = SearchSysCacheTuple(RELOID, 
+                                       ObjectIdGetDatum(relationObjectId),
+                                       0,0,0);
+    if (HeapTupleIsValid(relationTuple)) {
+       relation = (Form_pg_class)GETSTRUCT(relationTuple);
+
+       *hasindex = (relation->relhasindex) ? TRUE : FALSE;
+       *pages = relation->relpages;
+       *tuples = relation->reltuples;
+    } else {
+       elog(WARN, "RelationCatalogInformation: Relation %d not found",
+            relationObjectId);
+    }
+
+    return;
+}
+
+
+/*    
+ * index-info--
+ *    Retrieves catalog information on an index on a given relation.
+ *    
+ *    The index relation is opened on the first invocation. The current
+ *    retrieves the next index relation within the catalog that has not 
+ *    already been retrieved by a previous call.  The index catalog 
+ *    is closed when no more indices for 'relid' can be found.
+ *    
+ * 'first' is 1 if this is the first call 
+ *    
+ * Returns true if successful and false otherwise. Index info is returned
+ * via the transient data structure 'info'.
+ *    
+ */
+bool
+index_info(Query *root, bool first, int relid, IdxInfoRetval *info)
+{
+    register           i;
+    HeapTuple          indexTuple, amopTuple;
+    IndexTupleForm     index;
+    Relation           indexRelation;
+    uint16             amstrategy;
+    Oid                        relam;
+    Oid                        indrelid;
+
+    static Relation    relation = (Relation) NULL;
+    static HeapScanDesc        scan = (HeapScanDesc) NULL;
+    static ScanKeyData indexKey;
+
+
+    /* find the oid of the indexed relation */
+    indrelid = getrelid(relid, root->rtable);
+    
+    memset(info, 0, sizeof(IdxInfoRetval));
+
+    /*
+     * the maximum number of elements in each of the following arrays is
+     * 8. We allocate one more for a terminating 0 to indicate the end
+     * of the array.
+     */
+    info->indexkeys = (int *)palloc(sizeof(int)*9);
+    memset(info->indexkeys, 0, sizeof(int)*9);
+    info->orderOprs = (Oid *)palloc(sizeof(Oid)*9);
+    memset(info->orderOprs, 0, sizeof(Oid)*9);
+    info->classlist = (Oid *)palloc(sizeof(Oid)*9);
+    memset(info->classlist, 0, sizeof(Oid)*9);
+
+    /* Find an index on the given relation */
+    if (first) {
+       if (RelationIsValid(relation))
+           heap_close(relation);
+       if (HeapScanIsValid(scan))
+           heap_endscan(scan);
+
+       ScanKeyEntryInitialize(&indexKey, 0,
+                              Anum_pg_index_indrelid,
+                              F_OIDEQ,
+                              ObjectIdGetDatum(indrelid));
+
+       relation = heap_openr(IndexRelationName);
+       scan = heap_beginscan(relation, 0, NowTimeQual,
+                             1, &indexKey);
+    }
+    if (!HeapScanIsValid(scan))
+       elog(WARN, "index_info: scan not started");
+    indexTuple = heap_getnext(scan, 0, (Buffer *) NULL);
+    if (!HeapTupleIsValid(indexTuple)) {
+       heap_endscan(scan);
+       heap_close(relation);
+       scan = (HeapScanDesc) NULL;
+       relation = (Relation) NULL;
+       return(0);
+    }
+
+    /* Extract info from the index tuple */
+    index = (IndexTupleForm)GETSTRUCT(indexTuple);
+    info->relid = index->indexrelid; /* index relation */
+    for (i = 0; i < 8; i++)
+       info->indexkeys[i] = index->indkey[i];
+    for (i = 0; i < 8; i++)
+       info->classlist[i] = index->indclass[i];
+
+    info->indproc = index->indproc; /* functional index ?? */
+
+    /* partial index ?? */
+    if (VARSIZE(&index->indpred) != 0) {
+       /*
+        * The memory allocated here for the predicate (in lispReadString)
+        * only needs to stay around until it's used in find_index_paths,
+        * which is all within a command, so the automatic pfree at end
+        * of transaction should be ok.
+        */
+       char *predString;
+
+       predString = fmgr(F_TEXTOUT, &index->indpred);
+       info->indpred = (Node*)stringToNode(predString);
+       pfree(predString);
+    }
+
+    /* Extract info from the relation descriptor for the index */
+    indexRelation = index_open(index->indexrelid);
+#ifdef notdef
+    /* XXX should iterate through strategies -- but how?  use #1 for now */
+    amstrategy = indexRelation->rd_am->amstrategies;
+#endif /* notdef */
+    amstrategy = 1;
+    relam = indexRelation->rd_rel->relam;
+    info->relam = relam;
+    info->pages = indexRelation->rd_rel->relpages;
+    info->tuples = indexRelation->rd_rel->reltuples;
+    heap_close(indexRelation);
+       
+    /* 
+     * Find the index ordering keys 
+     *
+     * Must use indclass to know when to stop looking since with
+     * functional indices there could be several keys (args) for
+     * one opclass. -mer 27 Sept 1991
+     */
+    for (i = 0; i < 8 && index->indclass[i]; ++i) {
+       amopTuple = SearchSysCacheTuple(AMOPSTRATEGY,
+                                       ObjectIdGetDatum(relam),
+                                       ObjectIdGetDatum(index->indclass[i]),
+                                       UInt16GetDatum(amstrategy),
+                                       0);
+       if (!HeapTupleIsValid(amopTuple))
+           elog(WARN, "index_info: no amop %d %d %d",
+                relam, index->indclass[i], amstrategy);
+       info->orderOprs[i] =
+           ((Form_pg_amop)GETSTRUCT(amopTuple))->amopopr;
+    }
+    return(TRUE);
+}
+
+/*    
+ * index-selectivity--
+ *    
+ *    Call util/plancat.c:IndexSelectivity with the indicated arguments.
+ *    
+ * 'indid' is the index OID
+ * 'classes' is a list of index key classes
+ * 'opnos' is a list of index key operator OIDs
+ * 'relid' is the OID of the relation indexed
+ * 'attnos' is a list of the relation attnos which the index keys over
+ * 'values' is a list of the values of the clause's constants
+ * 'flags' is a list of fixnums which describe the constants
+ * 'nkeys' is the number of index keys
+ *    
+ * Returns two floats: index pages and index selectivity in 'idxPages' and
+ *     'idxSelec'.
+ *    
+ */
+void
+index_selectivity(Oid indid,
+                 Oid *classes,
+                 List *opnos,
+                 Oid relid,
+                 List *attnos,
+                 List *values,
+                 List *flags,
+                 int32 nkeys,
+                 float *idxPages,
+                 float *idxSelec)
+{
+    Oid *opno_array;
+    int *attno_array, *flag_array;
+    char **value_array;
+    int i = 0;
+    List *xopno, *xattno, *value, *flag;
+
+    if (length(opnos)!=nkeys || length(attnos)!=nkeys ||
+       length(values)!=nkeys || length(flags)!=nkeys) {
+
+       *idxPages = 0.0;
+       *idxSelec = 1.0;
+       return;
+    }
+       
+    opno_array = (Oid *)palloc(nkeys*sizeof(Oid));
+    attno_array = (int *)palloc(nkeys*sizeof(int32));
+    value_array = (char **)palloc(nkeys*sizeof(char *));
+    flag_array = (int *)palloc(nkeys*sizeof(int32));
+    
+    i = 0;
+    foreach(xopno, opnos) {
+       opno_array[i++] = (int)lfirst(xopno);
+    }
+
+    i = 0;
+    foreach(xattno,attnos) {
+       attno_array[i++] = (int)lfirst(xattno);
+    }
+
+    i = 0;
+    foreach(value, values) {
+       value_array[i++] = (char *)lfirst(value);
+    }
+
+    i = 0;
+    foreach(flag,flags) {
+       flag_array[i++] = (int)lfirst(flag);
+    }
+    
+    IndexSelectivity(indid,
+                    relid,
+                    nkeys,
+                    classes,   /* not used */
+                    opno_array,
+                    attno_array,
+                    value_array,
+                    flag_array,
+                    idxPages,
+                    idxSelec);
+    return;
+}
+
+/*
+ * restriction_selectivity in lisp system.--
+ *
+ *    NOTE: The routine is now merged with RestrictionClauseSelectivity
+ *    as defined in plancat.c
+ *
+ * Returns the selectivity of a specified operator.
+ * This code executes registered procedures stored in the
+ * operator relation, by calling the function manager.
+ *
+ * XXX The assumption in the selectivity procedures is that if the
+ *     relation OIDs or attribute numbers are -1, then the clause
+ *     isn't of the form (op var const).
+ */
+Cost
+restriction_selectivity(Oid functionObjectId,
+                       Oid operatorObjectId,
+                       Oid relationObjectId,
+                       AttrNumber attributeNumber,
+                       char *constValue,
+                       int32 constFlag)
+{
+    float64 result;
+
+    result = (float64) fmgr(functionObjectId,
+                           (char *) operatorObjectId,
+                           (char *) relationObjectId, 
+                           (char *) attributeNumber,
+                           (char *) constValue,
+                           (char *) constFlag,
+                           NULL);
+    if (!PointerIsValid(result))
+       elog(WARN, "RestrictionClauseSelectivity: bad pointer");
+
+    if (*result < 0.0 || *result > 1.0)
+       elog(WARN, "RestrictionClauseSelectivity: bad value %lf",
+            *result);
+
+    return ((Cost)*result);
+}
+
+/*
+ * join_selectivity--
+ *    Similarly, this routine is merged with JoinClauseSelectivity in
+ *    plancat.c
+ *
+ *    Returns the selectivity of an operator, given the join clause
+ *    information.
+ *
+ * XXX The assumption in the selectivity procedures is that if the
+ *     relation OIDs or attribute numbers are -1, then the clause
+ *     isn't of the form (op var var).
+ */
+Cost
+join_selectivity (Oid functionObjectId,
+                 Oid operatorObjectId,
+                 Oid relationObjectId1,
+                 AttrNumber attributeNumber1,
+                 Oid relationObjectId2,
+                 AttrNumber attributeNumber2)
+{
+    float64 result;
+    
+    result = (float64) fmgr(functionObjectId,
+                           (char *) operatorObjectId,
+                           (char *) relationObjectId1,
+                           (char *) attributeNumber1,
+                           (char *) relationObjectId2,
+                           (char *) attributeNumber2,
+                           NULL);
+    if (!PointerIsValid(result))
+       elog(WARN, "JoinClauseSelectivity: bad pointer");
+
+    if (*result < 0.0 || *result > 1.0)
+       elog(WARN, "JoinClauseSelectivity: bad value %lf",
+            *result);
+
+    return((Cost)*result);
+}
+
+/*
+ * find_all_inheritors--
+ *
+ * Returns a LISP list containing the OIDs of all relations which
+ * inherits from the relation with OID 'inhparent'.
+ */
+List *
+find_inheritance_children(Oid inhparent)
+{
+    static ScanKeyData key[1] = {
+       { 0, Anum_pg_inherits_inhparent, F_OIDEQ }
+    };
+
+    HeapTuple  inheritsTuple;
+    Relation   relation;
+    HeapScanDesc scan;
+    List       *list = NIL;
+    Oid                inhrelid;
+
+    fmgr_info(F_OIDEQ, &key[0].sk_func, &key[0].sk_nargs);
+
+    key[0].sk_argument = ObjectIdGetDatum((Oid)inhparent);
+    relation = heap_openr(InheritsRelationName);
+    scan = heap_beginscan(relation, 0, NowTimeQual, 1, key);
+    while (HeapTupleIsValid(inheritsTuple =
+                           heap_getnext(scan, 0,
+                                        (Buffer *) NULL))) {
+       inhrelid = ((InheritsTupleForm)GETSTRUCT(inheritsTuple))->inhrel;
+       list = lappendi(list, inhrelid);
+    }
+    heap_endscan(scan);
+    heap_close(relation);
+    return(list);
+}
+
+/*
+ * VersionGetParents--
+ *
+ * Returns a LISP list containing the OIDs of all relations which are
+ * base relations of the relation with OID 'verrelid'.
+ */
+List *
+VersionGetParents(Oid verrelid)
+{
+    static ScanKeyData key[1] = {
+       { 0, Anum_pg_version_verrelid, F_OIDEQ }
+    };
+
+    HeapTuple          versionTuple;
+    Relation           relation;
+    HeapScanDesc       scan;
+    Oid                        verbaseid;
+    List               *list= NIL;
+
+    fmgr_info(F_OIDEQ, &key[0].sk_func, &key[0].sk_nargs);
+    relation = heap_openr(VersionRelationName);
+    key[0].sk_argument = ObjectIdGetDatum(verrelid);
+    scan = heap_beginscan(relation, 0, NowTimeQual, 1, key);
+    for (;;) {
+       versionTuple = heap_getnext(scan, 0,
+                                   (Buffer *) NULL);
+       if (!HeapTupleIsValid(versionTuple))
+           break;
+       verbaseid = ((VersionTupleForm)
+                    GETSTRUCT(versionTuple))->verbaseid;
+
+       list = lconsi(verbaseid, list);
+
+       key[0].sk_argument = ObjectIdGetDatum(verbaseid);
+       heap_rescan(scan, 0, key);
+    }
+    heap_endscan(scan);
+    heap_close(relation);
+    return(list);
+}
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+/*
+ * IdexSelectivity--
+ *
+ *    Retrieves the 'amopnpages' and 'amopselect' parameters for each
+ *    AM operator when a given index (specified by 'indexrelid') is used.
+ *    These two parameters are returned by copying them to into an array of
+ *    floats.
+ *
+ *    Assumption: the attribute numbers and operator ObjectIds are in order
+ *    WRT to each other (otherwise, you have no way of knowing which
+ *    AM operator class or attribute number corresponds to which operator.
+ *
+ * 'varAttributeNumbers' contains attribute numbers for variables
+ * 'constValues' contains the constant values
+ * 'constFlags' describes how to treat the constants in each clause
+ * 'nIndexKeys' describes how many keys the index actually has
+ *
+ * Returns 'selectivityInfo' filled with the sum of all pages touched
+ * and the product of each clause's selectivity.
+ *
+ */
+static void
+IndexSelectivity(Oid indexrelid,
+                Oid indrelid,
+                int32 nIndexKeys,
+                Oid AccessMethodOperatorClasses[], /* XXX not used? */
+                Oid operatorObjectIds[],
+                int32 varAttributeNumbers[],
+                char *constValues[],
+                int32 constFlags[],
+                float *idxPages,
+                float *idxSelec)
+{
+    register   i, n;
+    HeapTuple  indexTuple, amopTuple, indRel;
+    IndexTupleForm     index;
+    Form_pg_amop       amop;
+    Oid                indclass;
+    float64data        npages, select;
+    float64    amopnpages, amopselect;
+    Oid relam;
+
+    indRel = SearchSysCacheTuple(RELOID,
+                                ObjectIdGetDatum(indexrelid),
+                                0,0,0);
+    if (!HeapTupleIsValid(indRel))
+       elog(WARN, "IndexSelectivity: index %d not found",
+            indexrelid);
+    relam = ((Form_pg_class)GETSTRUCT(indRel))->relam;
+
+    indexTuple = SearchSysCacheTuple(INDEXRELID,
+                                    ObjectIdGetDatum(indexrelid),
+                                    0,0,0);
+    if (!HeapTupleIsValid(indexTuple))
+       elog(WARN, "IndexSelectivity: index %d not found",
+            indexrelid);
+    index = (IndexTupleForm)GETSTRUCT(indexTuple);
+
+    npages = 0.0;
+    select = 1.0;
+    for (n = 0; n < nIndexKeys; ++n) {
+       /* 
+        * Find the AM class for this key. 
+        *
+        * If the first attribute number is invalid then we have a
+        * functional index, and AM class is the first one defined
+        * since functional indices have exactly one key.
+        */
+       indclass = (varAttributeNumbers[0] == InvalidAttrNumber) ?
+           index->indclass[0] : InvalidOid;
+       i = 0;
+       while ((i < nIndexKeys) && (indclass == InvalidOid)) {
+           if (varAttributeNumbers[n] == index->indkey[i]) {
+               indclass = index->indclass[i];
+               break;
+           }
+           i++;
+       }
+       if (!OidIsValid(indclass)) {
+           /*
+            * Presumably this means that we are using a functional
+            * index clause and so had no variable to match to
+            * the index key ... if not we are in trouble.
+            */
+           elog(NOTICE, "IndexSelectivity: no key %d in index %d",
+                varAttributeNumbers[n], indexrelid);
+           continue;
+       }
+
+       amopTuple = SearchSysCacheTuple(AMOPOPID,
+                                       ObjectIdGetDatum(indclass),
+                                       ObjectIdGetDatum(operatorObjectIds[n]),
+                                       ObjectIdGetDatum(relam),
+                                       0);
+       if (!HeapTupleIsValid(amopTuple))
+           elog(WARN, "IndexSelectivity: no amop %d %d",
+                indclass, operatorObjectIds[n]);
+       amop = (Form_pg_amop)GETSTRUCT(amopTuple);
+       amopnpages = (float64) fmgr(amop->amopnpages,
+                                   (char *) operatorObjectIds[n],
+                                   (char *) indrelid,
+                                   (char *) varAttributeNumbers[n],
+                                   (char *) constValues[n],
+                                   (char *) constFlags[n],
+                                   (char *) nIndexKeys, 
+                                   (char *) indexrelid);
+       npages += PointerIsValid(amopnpages) ? *amopnpages : 0.0;
+       if ((i = npages) < npages) /* ceil(npages)? */
+           npages += 1.0;
+       amopselect = (float64) fmgr(amop->amopselect,
+                                   (char *) operatorObjectIds[n],
+                                   (char *) indrelid,
+                                   (char *) varAttributeNumbers[n],
+                                   (char *) constValues[n],
+                                   (char *) constFlags[n],
+                                   (char *) nIndexKeys,
+                                   (char *) indexrelid);
+       select *= PointerIsValid(amopselect) ? *amopselect : 1.0;
+    }
+    *idxPages = npages;
+    *idxSelec = select;
+}
+
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
new file mode 100644 (file)
index 0000000..43a7983
--- /dev/null
@@ -0,0 +1,123 @@
+/*-------------------------------------------------------------------------
+ *
+ * relnode.c--
+ *    Relation manipulation routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/relation.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/pathnode.h"                /* where the decls go */
+#include "optimizer/plancat.h"
+
+
+
+/*    
+ * get_base_rel--
+ *    Returns relation entry corresponding to 'relid', creating a new one if
+ *    necessary. This is for base relations.
+ *    
+ */
+Rel *get_base_rel(Query* root, int relid)
+{
+    List *relids;
+    Rel *rel;
+
+    relids = lconsi(relid, NIL);
+    rel = rel_member(relids, root->base_relation_list_);
+    if (rel==NULL) {
+       rel = makeNode(Rel);
+       rel->relids = relids;
+       rel->indexed = false;
+       rel->pages =  0;
+       rel->tuples = 0;
+       rel->width = 0;
+       rel->targetlist = NIL;
+       rel->pathlist = NIL;
+       rel->unorderedpath = (Path *)NULL;
+       rel->cheapestpath = (Path *)NULL;
+       rel->pruneable = true;
+       rel->classlist = NULL;
+       rel->ordering = NULL;
+       rel->relam = InvalidOid;
+       rel->clauseinfo = NIL;
+       rel->joininfo = NIL;
+       rel->innerjoin = NIL;
+       rel->superrels = NIL;
+       
+       root->base_relation_list_ = lcons(rel,
+                                   root->base_relation_list_);
+       
+       /*
+        * ??? the old lispy C code (get_rel) do a listp(relid) here but
+        * that can never happen since we already established relid is not
+        * a list.                              -ay 10/94
+        */
+       if(relid < 0) {
+           /*
+            * If the relation is a materialized relation, assume 
+            * constants for sizes.
+            */
+           rel->pages = _TEMP_RELATION_PAGES_;
+           rel->tuples = _TEMP_RELATION_TUPLES_;
+           
+       } else {
+           bool hasindex;
+           int pages, tuples;
+           
+           /*
+            * Otherwise, retrieve relation characteristics from the
+            * system catalogs.
+            */
+           relation_info(root, relid, &hasindex, &pages, &tuples);
+           rel->indexed = hasindex;
+           rel->pages = pages;
+           rel->tuples = tuples;
+       } 
+    }
+    return rel;
+}
+
+/*    
+ * get_join_rel--
+ *    Returns relation entry corresponding to 'relid' (a list of relids),
+ *    creating a new one if necessary. This is for join relations.
+ *    
+ */
+Rel *get_join_rel(Query *root, List *relid)
+{
+    return rel_member(relid, root->join_relation_list_);
+}
+
+/*    
+ * rel-member--
+ *    Determines whether a relation of id 'relid' is contained within a list
+ *    'rels'.  
+ *    
+ * Returns the corresponding entry in 'rels' if it is there.
+ *    
+ */
+Rel *
+rel_member(List *relid, List *rels)
+{
+    List *temp = NIL;
+    List *temprelid = NIL;
+    
+    if (relid!=NIL && rels!=NIL) {
+       foreach(temp,rels) {
+           temprelid = ((Rel*)lfirst(temp))->relids;
+           if(same(temprelid, relid))   
+               return((Rel*)(lfirst(temp)));
+       }
+    } 
+    return(NULL);
+}
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
new file mode 100644 (file)
index 0000000..36eb3a2
--- /dev/null
@@ -0,0 +1,577 @@
+/*-------------------------------------------------------------------------
+ *
+ * tlist.c--
+ *    Target list manipulation routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/relation.h"
+#include "nodes/primnodes.h"
+#include "nodes/pg_list.h"
+#include "nodes/nodeFuncs.h"
+#include "utils/elog.h"
+#include "utils/lsyscache.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/var.h"
+#include "optimizer/tlist.h"
+#include "optimizer/clauses.h"
+
+#include "nodes/makefuncs.h"
+#include "parser/catalog_utils.h"
+
+static Node *flatten_tlistentry(Node *tlistentry, List *flat_tlist);
+
+/*****************************************************************************
+ *  ---------- RELATION node target list routines ----------
+ *****************************************************************************/
+
+/*    
+ * tlistentry-member--
+ *    
+ * RETURNS:  the leftmost member of sequence "targetlist" that satisfies
+ *           the predicate "var_equal"
+ * MODIFIES: nothing
+ * REQUIRES: test = function which can operate on a lispval union
+ *           var = valid var-node
+ *          targetlist = valid sequence
+ */
+TargetEntry *
+tlistentry_member(Var *var, List *targetlist)
+{
+    if (var) {
+       List *temp = NIL;
+
+       foreach (temp,targetlist) {
+           if (var_equal(var,
+                         get_expr(lfirst(temp))))
+               return((TargetEntry*)lfirst(temp));
+       }
+    }
+    return (NULL);
+}
+
+/*    
+ * matching_tlvar--
+ *    
+ * RETURNS:  var node in a target list which is var_equal to 'var',
+ *          if one exists.
+ * REQUIRES: "test" operates on lispval unions,
+ * 
+ */
+Expr *
+matching_tlvar(Var *var, List *targetlist)
+{
+    TargetEntry *tlentry;
+
+    tlentry = tlistentry_member(var,targetlist);
+    if (tlentry) 
+       return((Expr*)get_expr (tlentry) );
+
+    return((Expr*) NULL);
+}
+
+/*    
+ * add_tl_element--
+ *    Creates a targetlist entry corresponding to the supplied var node
+ *
+ * 'var' and adds the new targetlist entry to the targetlist field of
+ * 'rel' 
+ *    
+ * RETURNS: nothing
+ * MODIFIES: vartype and varid fields of leftmost varnode that matches
+ *           argument "var" (sometimes).
+ * CREATES:  new var-node iff no matching var-node exists in targetlist
+ */
+void
+add_tl_element(Rel *rel, Var *var)
+{
+    Expr *oldvar = (Expr *)NULL;
+    
+    oldvar = matching_tlvar(var, rel->targetlist);
+    
+    /*
+     * If 'var' is not already in 'rel's target list, add a new node. 
+     */
+    if (oldvar==NULL) {
+       List *tlist = rel->targetlist;
+       Var *newvar = makeVar(var->varno,
+                             var->varattno,
+                             var->vartype,
+                             var->varno,
+                             var->varoattno);
+
+       rel->targetlist =
+           lappend (tlist,
+                     create_tl_element(newvar,
+                                       length(tlist) + 1));
+
+    }
+}
+
+/*    
+ * create_tl_element--
+ *    Creates a target list entry node and its associated (resdom var) pair
+ *    with its resdom number equal to 'resdomno' and the joinlist field set
+ *    to 'joinlist'.
+ *    
+ * RETURNS:  newly created tlist-entry
+ * CREATES:  new targetlist entry (always).
+ */
+TargetEntry*
+create_tl_element(Var *var, int resdomno)
+{
+    TargetEntry *tlelement= makeNode(TargetEntry);
+    
+    tlelement->resdom =
+       makeResdom(resdomno, 
+                  var->vartype,
+                  get_typlen(var->vartype),
+                  NULL,
+                  (Index)0,
+                  (Oid)0,
+                  0);
+    tlelement->expr = (Node*)var;
+    
+    return(tlelement);
+}
+
+/*    
+ * get-actual-tlist--
+ *    Returns the targetlist elements from a relation tlist.
+ *    
+ */
+List *
+get_actual_tlist(List *tlist)
+{
+    /*
+     * this function is not making sense. - ay 10/94
+     */
+#if 0 
+    List *element = NIL;
+    List *result = NIL;
+    
+    if (tlist==NULL) {
+       elog(DEBUG,"calling get_actual_tlist with empty tlist");
+       return(NIL);
+    }
+    /* XXX - it is unclear to me what exactly get_entry 
+       should be doing, as it is unclear to me the exact
+       relationship between "TL" "TLE" and joinlists */
+
+    foreach(element,tlist)
+       result = lappend(result, lfirst((List*)lfirst(element)));
+    
+    return(result);
+#endif
+    return tlist;
+}
+
+/*****************************************************************************
+ *     ---------- GENERAL target list routines ----------
+ *****************************************************************************/
+
+/*    
+ * tlist-member--
+ *    Determines whether a var node is already contained within a
+ *    target list.
+ *    
+ * 'var' is the var node
+ * 'tlist' is the target list
+ * 'dots' is t if we must match dotfields to determine uniqueness
+ *    
+ * Returns the resdom entry of the matching var node.
+ *    
+ */
+Resdom *
+tlist_member(Var *var, List *tlist)
+{
+    List *i = NIL;
+    TargetEntry *temp_tle = (TargetEntry *)NULL;
+    TargetEntry *tl_elt = (TargetEntry *)NULL;
+
+    if (var) {
+       foreach (i,tlist) {
+           temp_tle = (TargetEntry *)lfirst(i);
+           if (var_equal(var, get_expr(temp_tle))) {
+               tl_elt = temp_tle;
+               break;
+           }
+       }
+
+       if (tl_elt != NULL)
+           return(tl_elt->resdom);
+       else 
+           return((Resdom*)NULL);
+    }
+    return ((Resdom*)NULL);
+}
+
+/*
+ *   Routine to get the resdom out of a targetlist.
+ */
+Resdom *
+tlist_resdom(List *tlist, Resdom *resnode)
+{
+    Resdom *resdom = (Resdom*)NULL;
+    List *i = NIL;
+    TargetEntry *temp_tle = (TargetEntry *)NULL;
+
+    foreach(i,tlist) {
+       temp_tle = (TargetEntry *)lfirst(i);
+       resdom = temp_tle->resdom;
+       /*  Since resnos are supposed to be unique */
+       if (resnode->resno == resdom->resno)  
+           return(resdom);
+    }
+    return((Resdom*)NULL);
+}
+
+
+/*    
+ * match_varid--
+ *    Searches a target list for an entry with some desired varid.
+ *    
+ * 'varid' is the desired id
+ * 'tlist' is the target list that is searched
+ *    
+ * Returns the target list entry (resdom var) of the matching var.
+ *
+ * Now checks to make sure array references (in addition to range
+ * table indices) are identical - retrieve (a.b[1],a.b[2]) should
+ * not be turned into retrieve (a.b[1],a.b[1]).
+ *    
+ * [what used to be varid is now broken up into two fields varnoold and
+ *  varoattno. Also, nested attnos are long gone. - ay 2/95]
+ */
+TargetEntry *
+match_varid(Var *test_var, List *tlist)
+{
+    List *tl;
+    Oid type_var;
+
+    type_var = (Oid) test_var->vartype;
+
+    foreach (tl, tlist) {
+       TargetEntry *entry;
+       Var *tlvar;
+
+       entry = lfirst(tl);
+       tlvar = get_expr(entry);
+
+       /*
+        * we test the original varno (instead of varno which might
+        * be changed to INNER/OUTER.
+        */
+       if (tlvar->varnoold == test_var->varnoold &&
+           tlvar->varoattno == test_var->varoattno) {
+
+           if (tlvar->vartype == type_var)
+               return(entry);
+       }
+    }
+
+    return (NULL);
+}
+
+
+/*    
+ * new-unsorted-tlist--
+ *    Creates a copy of a target list by creating new resdom nodes
+ *    without sort information.
+ *    
+ * 'targetlist' is the target list to be copied.
+ *    
+ * Returns the resulting target list.
+ *    
+ */
+List *
+new_unsorted_tlist(List *targetlist)
+{
+    List *new_targetlist = (List*)copyObject ((Node*)targetlist);
+    List *x = NIL;
+
+    foreach (x, new_targetlist) {
+       TargetEntry *tle = (TargetEntry *)lfirst(x);
+       tle->resdom->reskey = 0;
+       tle->resdom->reskeyop = (Oid)0;
+    }
+    return(new_targetlist);
+}
+
+/*    
+ * copy-vars--
+ *    Replaces the var nodes in the first target list with those from
+ *    the second target list.  The two target lists are assumed to be
+ *    identical except their actual resdoms and vars are different.
+ *    
+ * 'target' is the target list to be replaced
+ * 'source' is the target list to be copied
+ *    
+ * Returns a new target list.
+ *    
+ */
+List *
+copy_vars(List *target, List *source)
+{
+    List *result = NIL;
+    List *src = NIL;
+    List *dest = NIL;
+    
+    for ( src = source, dest = target; src != NIL &&
+        dest != NIL; src = lnext(src), dest = lnext(dest)) {
+       TargetEntry *temp = MakeTLE(((TargetEntry *)lfirst(dest))->resdom,
+                                (Node*)get_expr(lfirst(src)));
+       result = lappend(result,temp);
+    }
+    return(result);
+}
+
+/*    
+ * flatten-tlist--
+ *    Create a target list that only contains unique variables.
+ *
+ *
+ * 'tlist' is the current target list
+ *    
+ * Returns the "flattened" new target list.
+ *    
+ */
+List *
+flatten_tlist(List *tlist)
+{
+    int last_resdomno = 1;
+    List *new_tlist = NIL;
+    List *tlist_vars = NIL;
+    List *temp;
+
+    foreach (temp, tlist)  {
+       TargetEntry *temp_entry = NULL;
+       List *vars;
+
+       temp_entry = lfirst(temp);
+       vars = pull_var_clause((Node*)get_expr(temp_entry));
+       if(vars != NULL) {
+           tlist_vars = nconc(tlist_vars, vars);
+       }
+    }
+
+    foreach (temp, tlist_vars) {
+       Var *var = lfirst(temp);
+       if (!(tlist_member(var, new_tlist))) {
+           Resdom *r;
+
+           r = makeResdom(last_resdomno,
+                          var->vartype,
+                          get_typlen(var->vartype),
+                          NULL,
+                          (Index)0,
+                          (Oid)0,
+                          0);
+           last_resdomno++;
+           new_tlist = lappend(new_tlist, MakeTLE (r, (Node*)var));
+       }
+    }
+
+    return new_tlist;
+}
+
+/*    
+ * flatten-tlist-vars--
+ *    Redoes the target list of a query with no nested attributes by
+ *    replacing vars within computational expressions with vars from
+ *    the 'flattened' target list of the query.
+ *    
+ * 'full-tlist' is the actual target list
+ * 'flat-tlist' is the flattened (var-only) target list
+ *    
+ * Returns the modified actual target list.
+ *    
+ */
+List *
+flatten_tlist_vars(List *full_tlist, List *flat_tlist)
+{
+    List *x = NIL;
+    List *result = NIL;
+    
+    foreach(x,full_tlist) {
+       TargetEntry *tle= lfirst(x);
+       result =
+           lappend(result,
+                    MakeTLE(tle->resdom,
+                            flatten_tlistentry((Node*)get_expr(tle),
+                                               flat_tlist)));
+    }
+
+    return(result);
+}
+
+/*    
+ * flatten-tlistentry--
+ *    Replaces vars within a target list entry with vars from a flattened
+ *    target list.
+ *    
+ * 'tlistentry' is the target list entry to be modified
+ * 'flat-tlist' is the flattened target list
+ *    
+ * Returns the (modified) target_list entry from the target list.
+ *    
+ */
+static Node *
+flatten_tlistentry(Node *tlistentry, List *flat_tlist)
+{
+    if (tlistentry==NULL) {
+
+       return NULL;
+
+    } else if (IsA (tlistentry,Var)) {
+
+       return
+           ((Node *)get_expr(match_varid((Var*)tlistentry,
+                                         flat_tlist)));
+    } else if (IsA (tlistentry,Iter)) {
+
+       ((Iter*)tlistentry)->iterexpr = 
+           flatten_tlistentry((Node*)((Iter*)tlistentry)->iterexpr,
+                              flat_tlist);
+       return tlistentry;
+
+    } else if (single_node(tlistentry)) {
+
+       return tlistentry;
+
+    } else if (is_funcclause (tlistentry)) {
+       Expr *expr = (Expr*)tlistentry;
+       List *temp_result = NIL;
+       List *elt = NIL;
+       
+       foreach(elt, expr->args)
+           temp_result = lappend(temp_result,
+                                  flatten_tlistentry(lfirst(elt),flat_tlist));
+       
+       return
+           ((Node *)make_funcclause((Func*)expr->oper, temp_result));
+
+    } else if (IsA(tlistentry,Aggreg)) {
+
+       return tlistentry;
+
+    } else if (IsA(tlistentry,ArrayRef)) {
+       ArrayRef *aref = (ArrayRef *)tlistentry;
+       List *temp = NIL;
+       List *elt = NIL;
+
+       foreach(elt, aref->refupperindexpr)
+           temp = lappend(temp, flatten_tlistentry(lfirst(elt), flat_tlist));
+       aref->refupperindexpr = temp;
+
+       temp = NIL;
+       foreach(elt, aref->reflowerindexpr)
+           temp = lappend(temp, flatten_tlistentry(lfirst(elt), flat_tlist));
+       aref->reflowerindexpr = temp;
+
+       aref->refexpr = 
+           flatten_tlistentry(aref->refexpr, flat_tlist);
+
+       aref->refassgnexpr = 
+           flatten_tlistentry(aref->refassgnexpr, flat_tlist);
+
+       return tlistentry;
+    } else {
+       Expr *expr = (Expr*)tlistentry;
+       Var *left =
+           (Var*)flatten_tlistentry((Node*)get_leftop(expr),
+                                    flat_tlist);
+       Var *right =
+           (Var*)flatten_tlistentry((Node*)get_rightop(expr),
+                                    flat_tlist);
+
+       return((Node *)
+              make_opclause((Oper*)expr->oper, left, right));
+    }
+}
+
+
+TargetEntry *
+MakeTLE(Resdom *resdom, Node *expr)
+{
+    TargetEntry *rt = makeNode(TargetEntry);
+
+    rt->resdom = resdom;
+    rt->expr = expr;
+    return rt;
+}
+
+Var *
+get_expr(TargetEntry *tle)
+{
+    Assert(tle!=NULL);
+    Assert(tle->expr!=NULL);
+
+    return ((Var *)tle->expr); 
+}
+
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+/*
+ * AddGroupAttrToTlist -
+ *    append the group attribute to the target list if it's not already
+ *    in there.
+ */
+void
+AddGroupAttrToTlist(List *tlist, List *grpCl)
+{
+    List *gl;
+    int last_resdomno = length(tlist) + 1;
+    
+    foreach (gl, grpCl) {
+       GroupClause *gc = (GroupClause*)lfirst(gl);
+       Var *var = gc->grpAttr;
+
+       if (!(tlist_member(var, tlist))) {
+           Resdom *r;
+
+           r = makeResdom(last_resdomno,
+                          var->vartype,
+                          get_typlen(var->vartype),
+                          NULL,
+                          (Index)0,
+                          (Oid)0,
+                          0);
+           last_resdomno++;
+           tlist = lappend(tlist, MakeTLE(r, (Node*)var));
+       }
+    }
+}
+
+/* was ExecTargetListLength() in execQual.c, 
+   moved here to reduce dependencies on the executor module */
+int
+exec_tlist_length(List *targetlist)
+{
+    int len;
+    List *tl;
+    TargetEntry *curTle;
+    
+    len = 0;
+    foreach (tl, targetlist) {
+       curTle = lfirst(tl);
+       
+       if (curTle->resdom != NULL)
+           len++;
+    }
+    return len;
+}
+
+
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
new file mode 100644 (file)
index 0000000..f4908db
--- /dev/null
@@ -0,0 +1,189 @@
+/*-------------------------------------------------------------------------
+ *
+ * var.c--
+ *    Var node manipulation routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "nodes/primnodes.h"
+#include "nodes/nodeFuncs.h"
+
+#include "optimizer/internal.h"
+#include "optimizer/clauses.h"
+#include "optimizer/var.h"
+
+#include "parser/parsetree.h"
+
+/*
+ *     find_varnos
+ *
+ *     Descends down part of a parsetree (qual or tlist),
+ *
+ *     XXX assumes varno's are always integers, which shouldn't be true...
+ *     (though it currently is, see primnodes.h)
+ */
+List *
+pull_varnos(Node *me)
+{
+    List *i, *result = NIL;
+       
+    if (me == NULL)
+       return (NIL);
+
+    switch (nodeTag(me)) {
+    case T_List:
+       foreach (i, (List*)me) {
+           result = nconc(result, pull_varnos(lfirst(i)));
+       }
+       break;
+    case T_ArrayRef:
+       foreach (i, ((ArrayRef*) me)->refupperindexpr) 
+           result = nconc(result, pull_varnos(lfirst(i)));
+       foreach (i, ((ArrayRef*) me)->reflowerindexpr)
+           result = nconc(result, pull_varnos(lfirst(i)));
+       result = nconc(result, pull_varnos(((ArrayRef*) me)->refassgnexpr));
+       break;
+    case T_Var:
+       result = lconsi(((Var*) me)->varno, NIL);
+       break;
+    default:
+       break;
+    }
+    return(result);
+}
+
+/*    
+ * contain_var_clause--
+ *    Recursively find var nodes from a clause by pulling vars from the
+ *    left and right operands of the clause.  
+ *    
+ *    Returns true if any varnode found.
+ */
+bool contain_var_clause(Node *clause)
+{
+    if (clause==NULL) 
+       return FALSE;
+    else if (IsA(clause,Var))
+       return TRUE;
+    else if (IsA(clause,Iter))
+       return contain_var_clause(((Iter*)clause)->iterexpr);
+    else if (single_node(clause)) 
+       return FALSE;
+    else if (or_clause(clause)) {
+       List *temp;
+
+       foreach (temp, ((Expr*)clause)->args) {
+           if (contain_var_clause(lfirst(temp)))
+               return TRUE;
+       }
+       return FALSE;
+    } else if (is_funcclause (clause)) {
+       List *temp;
+
+       foreach(temp, ((Expr *)clause)->args) {
+           if (contain_var_clause(lfirst(temp)))
+               return TRUE;
+       }
+       return FALSE;
+    } else if (IsA(clause,ArrayRef)) {
+       List *temp;
+
+       foreach(temp, ((ArrayRef*)clause)->refupperindexpr)  {
+           if (contain_var_clause(lfirst(temp)))
+               return TRUE;
+       }
+       foreach(temp, ((ArrayRef*)clause)->reflowerindexpr) {
+           if (contain_var_clause(lfirst(temp)))
+               return TRUE;
+       }
+       if (contain_var_clause(((ArrayRef*)clause)->refexpr))
+           return TRUE;
+       if (contain_var_clause(((ArrayRef*)clause)->refassgnexpr))
+           return TRUE;
+       return FALSE;
+    } else if (not_clause(clause))
+       return contain_var_clause((Node*)get_notclausearg((Expr*)clause));
+    else if (is_opclause(clause))
+       return (contain_var_clause((Node*)get_leftop((Expr*)clause)) ||
+               contain_var_clause((Node*)get_rightop((Expr*)clause)));
+
+    return FALSE;
+}
+
+/*    
+ * pull_var_clause--
+ *    Recursively pulls all var nodes from a clause by pulling vars from the
+ *    left and right operands of the clause.  
+ *    
+ *    Returns list of varnodes found.
+ */
+List *
+pull_var_clause(Node *clause)
+{
+    List *retval = NIL;
+
+    if (clause==NULL) 
+       return(NIL);
+    else if (IsA(clause,Var))
+       retval = lcons(clause,NIL);
+    else if (IsA(clause,Iter))
+       retval = pull_var_clause(((Iter*)clause)->iterexpr);
+    else if (single_node(clause)) 
+       retval = NIL;
+    else if (or_clause(clause)) {
+       List *temp;
+
+       foreach (temp, ((Expr*)clause)->args)
+           retval = nconc(retval, pull_var_clause(lfirst(temp)));
+    } else if (is_funcclause (clause)) {
+       List *temp;
+
+       foreach(temp, ((Expr *)clause)->args)
+           retval = nconc (retval,pull_var_clause(lfirst(temp)));
+    } else if (IsA(clause,Aggreg)) {
+       retval = pull_var_clause(((Aggreg*)clause)->target);
+    } else if (IsA(clause,ArrayRef)) {
+       List *temp;
+
+       foreach(temp, ((ArrayRef*)clause)->refupperindexpr) 
+           retval = nconc (retval,pull_var_clause(lfirst(temp)));
+       foreach(temp, ((ArrayRef*)clause)->reflowerindexpr)
+           retval = nconc (retval,pull_var_clause(lfirst(temp)));
+       retval = nconc(retval,
+                      pull_var_clause(((ArrayRef*)clause)->refexpr));
+       retval = nconc(retval,
+                      pull_var_clause(((ArrayRef*)clause)->refassgnexpr));
+    } else if (not_clause(clause))
+       retval = pull_var_clause((Node*)get_notclausearg((Expr*)clause));
+    else if (is_opclause(clause)) 
+       retval = nconc(pull_var_clause((Node*)get_leftop((Expr*)clause)),
+                      pull_var_clause((Node*)get_rightop((Expr*)clause)));
+    else
+       retval = NIL;
+
+    return (retval);
+}
+
+/*    
+ *     var_equal
+ *    
+ *     Returns t iff two var nodes correspond to the same attribute.
+ */
+bool
+var_equal(Var *var1, Var *var2)
+{
+    if (IsA (var1,Var) && IsA (var2,Var) &&
+       (((Var*)var1)->varno ==  ((Var*)var2)->varno) &&
+       (((Var*)var1)->vartype == ((Var*)var2)->vartype) && 
+       (((Var*)var1)->varattno == ((Var*)var2)->varattno)) {
+
+       return(true);
+    } else 
+       return(false);
+}
diff --git a/src/backend/optimizer/var.h b/src/backend/optimizer/var.h
new file mode 100644 (file)
index 0000000..8e7f6e9
--- /dev/null
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * var.h--
+ *    prototypes for var.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef VAR_H
+#define VAR_H
+
+extern List *pull_varnos(Node *me);
+extern bool contain_var_clause(Node *clause);
+extern List *pull_var_clause(Node *clause);
+extern bool var_equal(Var *var1, Var *var2);
+
+#endif /* VAR_H */
diff --git a/src/backend/optimizer/xfunc.h b/src/backend/optimizer/xfunc.h
new file mode 100644 (file)
index 0000000..0ab8781
--- /dev/null
@@ -0,0 +1,84 @@
+/*-------------------------------------------------------------------------
+ *
+ * xfunc.h--
+ *    prototypes for xfunc.c and predmig.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef XFUNC_H
+#define XFUNC_H
+
+#include "nodes/relation.h"
+
+/* command line arg flags */
+#define XFUNC_OFF -1           /* do no optimization of expensive preds */
+#define XFUNC_NOR 2            /* do no optimization of OR clauses */
+#define XFUNC_NOPULL 4         /* never pull restrictions above joins */
+#define XFUNC_NOPM 8           /* don't do predicate migration */
+#define XFUNC_WAIT 16          /* don't do pullup until predicate migration */
+#define XFUNC_PULLALL 32       /* pull all expensive restrictions up, always */
+
+/* constants for local and join predicates */
+#define XFUNC_LOCPRD 1
+#define XFUNC_JOINPRD 2
+#define XFUNC_UNKNOWN 0
+
+extern int XfuncMode;  /* defined in tcop/postgres.c */
+
+/* defaults for function attributes used for expensive function calculations */
+#define BYTE_PCT 100
+#define PERBYTE_CPU 0
+#define PERCALL_CPU 0
+#define OUTIN_RATIO 100
+
+/* default width assumed for variable length attributes */
+#define VARLEN_DEFAULT 128;
+
+/* Macro to get group rank out of group cost and group sel */
+#define get_grouprank(a) ((get_groupsel(a) - 1) / get_groupcost(a))
+
+/* Macro to see if a path node is actually a Join */
+#define is_join(pathnode) (length(get_relids(get_parent(pathnode))) > 1 ? 1 : 0)
+
+/* function prototypes from planner/path/xfunc.c */
+extern void xfunc_trypullup(Rel *rel);
+extern int xfunc_shouldpull(Path *childpath, JoinPath *parentpath,
+                           int whichchild, CInfo *maxcinfopt);
+extern CInfo *xfunc_pullup(Path *childpath, JoinPath *parentpath, CInfo *cinfo,
+                         int whichchild, int clausetype);
+extern Cost xfunc_rank(Expr *clause);
+extern Cost xfunc_expense(Query* queryInfo, Expr *clause);
+extern Cost xfunc_join_expense(JoinPath *path, int whichchild);
+extern Cost xfunc_local_expense(Expr *clause);
+extern Cost xfunc_func_expense(Expr *node, List *args);
+extern int xfunc_width(Expr *clause);
+/* static, moved to xfunc.c */
+/* extern int xfunc_card_unreferenced(Expr *clause, Relid referenced); */
+extern int xfunc_card_product(Relid relids);
+extern List *xfunc_find_references(List *clause);
+extern List *xfunc_primary_join(JoinPath *pathnode);
+extern Cost xfunc_get_path_cost(Path *pathnode);
+extern Cost xfunc_total_path_cost(JoinPath *pathnode);
+extern Cost xfunc_expense_per_tuple(JoinPath *joinnode, int whichchild);
+extern void xfunc_fixvars(Expr *clause, Rel *rel, int varno);
+extern int xfunc_cinfo_compare(void *arg1, void *arg2);
+extern int xfunc_clause_compare(void *arg1, void *arg2);
+extern void xfunc_disjunct_sort(List *clause_list);
+extern int xfunc_disjunct_compare(void *arg1, void *arg2);
+extern int xfunc_func_width(RegProcedure funcid, List *args);
+extern int xfunc_tuple_width(Relation rd);
+extern int xfunc_num_join_clauses(JoinPath *path);
+extern List *xfunc_LispRemove(List *foo, List *bar);
+extern bool xfunc_copyrel(Rel *from, Rel **to);
+
+/*
+ * function prototypes for path/predmig.c
+ */
+extern bool xfunc_do_predmig(Path root);
+
+#endif /* XFUNC_H */
diff --git a/src/backend/parser/Makefile.inc b/src/backend/parser/Makefile.inc
new file mode 100644 (file)
index 0000000..8d62e50
--- /dev/null
@@ -0,0 +1,46 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the parser module
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+VPATH:= $(VPATH):$(CURDIR)/parser
+
+#PARSEYACCS= gram.c parse.h
+PARSEYACCS= gram.c
+
+$(PARSEYACCS): gram.y
+       cd $(objdir); \
+       $(YACC) $(YFLAGS) $<; \
+       mv y.tab.c gram.c; \
+       mv y.tab.h parse.h
+
+$(objdir)/gram.o: gram.c
+       $(cc_inobjdir)
+
+
+scan.c:        scan.l
+       cd $(objdir); $(LEX) $<; mv lex.yy.c scan.c
+
+$(objdir)/scan.o: scan.c
+       $(cc_inobjdir)
+
+
+SRCS_PARSER+= analyze.c catalog_utils.c dbcommands.c gram.c \
+       keywords.c parser.c parse_query.c scan.c scansup.c 
+
+CLEANFILES+= scan.c ${PARSEYACCS}
+
+POSTGRES_DEPEND+= scan.c $(PARSEYACCS)
+
+HEADERS+= catalog_utils.h io.h parse_query.h parsetree.h \
+       dbcommands.h keywords.h
+
+
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
new file mode 100644 (file)
index 0000000..f8c1793
--- /dev/null
@@ -0,0 +1,2467 @@
+/*-------------------------------------------------------------------------
+ *
+ * analyze.c--
+ *    transform the parse tree into a query tree
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "postgres.h"
+#include "nodes/nodes.h"
+#include "nodes/primnodes.h"
+#include "nodes/parsenodes.h"
+#include "nodes/relation.h"
+#include "parse.h"             /* for AND, OR, etc. */
+#include "catalog/pg_type.h"   /* for INT4OID, etc. */
+#include "utils/elog.h"
+#include "utils/builtins.h"    /* namecmp(), textout() */
+#include "utils/lsyscache.h"
+#include "utils/palloc.h"
+#include "utils/mcxt.h"
+#include "parser/parse_query.h"
+#include "parser/parse_state.h"
+#include "nodes/makefuncs.h"   /* for makeResdom(), etc. */
+#include "nodes/nodeFuncs.h"
+
+#include "optimizer/clauses.h"
+#include "access/heapam.h"
+
+/* convert the parse tree into a query tree */
+static Query *transformStmt(ParseState *pstate, Node *stmt);
+
+static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
+static Query *transformInsertStmt(ParseState *pstate, AppendStmt *stmt);
+static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
+static Query *transformExtendStmt(ParseState *pstate, ExtendStmt *stmt);
+static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt);
+static Query *transformSelectStmt(ParseState *pstate, RetrieveStmt *stmt);
+static Query *transformUpdateStmt(ParseState *pstate, ReplaceStmt *stmt);
+static Query *transformCursorStmt(ParseState *pstate, CursorStmt *stmt);
+static Node *handleNestedDots(ParseState *pstate, Attr *attr, int *curr_resno);
+
+static Node *transformExpr(ParseState *pstate, Node *expr);
+
+static void makeRangeTable(ParseState *pstate, char *relname, List *frmList);
+static List *expandAllTables(ParseState *pstate);
+static char *figureColname(Node *expr, Node *resval);
+static List *makeTargetList(ParseState *pstate, List *cols, List *exprs);
+static List *transformTargetList(ParseState *pstate, 
+                                List *targetlist, bool isInsert,
+                                bool isUpdate);
+static TargetEntry *make_targetlist_expr(ParseState *pstate,
+                                        char *name, Node *expr,
+                                        List *arrayRef,
+                                        bool ResdomNoIsAttrNo);
+static Node *transformWhereClause(ParseState *pstate, Node *a_expr);
+static List *transformGroupClause(ParseState *pstate, List *grouplist);
+static List *transformSortClause(List *orderlist, List *targetlist,
+                                char* uniqueFlag);
+
+static void parseFromClause(ParseState *pstate, List *frmList);
+static Node *ParseFunc(ParseState *pstate, char *funcname, 
+                      List *fargs, int *curr_resno);
+static char *ParseColumnName(ParseState *pstate, char *name, bool *isRelName);
+static List *setup_tlist(char *attname, Oid relid);
+static List *setup_base_tlist(Oid typeid);
+static void make_arguments(int nargs, List *fargs, Oid *input_typeids, Oid *function_typeids);
+static void AddAggToParseState(ParseState *pstate, Aggreg *aggreg);
+static void finalizeAggregates(ParseState *pstate, Query *qry);
+static void parseCheckAggregates(ParseState *pstate, Query *qry);
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+/*
+ * makeParseState() -- 
+ *    allocate and initialize a new ParseState.
+ *  the CALLERS is responsible for freeing the ParseState* returned
+ *
+ */
+
+ParseState* 
+makeParseState() {
+    ParseState *pstate;
+
+    pstate = malloc(sizeof(ParseState));
+    pstate->p_last_resno = 1;
+    pstate->p_target_resnos = NIL;
+    pstate->p_rtable = NIL;
+    pstate->p_query_is_rule = 0;
+    pstate->p_numAgg = 0;
+    pstate->p_aggs = NULL;
+
+    return (pstate);
+}
+/*
+ * parse_analyze -
+ *    analyze a list of parse trees and transform them if necessary.
+ *
+ * Returns a list of transformed parse trees. Optimizable statements are
+ * all transformed to Query while the rest stays the same.
+ *
+ * CALLER is responsible for freeing the QueryTreeList* returned
+ */
+QueryTreeList *
+parse_analyze(List *pl)
+{
+    QueryTreeList *result;
+    ParseState *pstate;
+    int i = 0;
+
+    result = malloc(sizeof(QueryTreeList));
+    result->len = length(pl);
+    result->qtrees = (Query**)malloc(result->len * sizeof(Query*));
+
+    while(pl!=NIL) {
+       pstate = makeParseState();
+       result->qtrees[i++] = transformStmt(pstate, lfirst(pl));
+       pl = lnext(pl);
+       free(pstate);
+    }
+
+    return result;
+}
+
+/*
+ * transformStmt -
+ *    transform a Parse tree. If it is an optimizable statement, turn it
+ *    into a Query tree.
+ */
+static Query *
+transformStmt(ParseState* pstate, Node *parseTree)
+{
+    Query* result = NULL;
+
+    switch(nodeTag(parseTree)) {
+      /*------------------------
+       *  Non-optimizable statements
+       *------------------------
+       */
+    case T_IndexStmt:
+      result = transformIndexStmt(pstate, (IndexStmt *)parseTree);
+      break;
+
+    case T_ExtendStmt:
+      result = transformExtendStmt(pstate, (ExtendStmt *)parseTree);
+      break;
+
+    case T_RuleStmt:
+      result = transformRuleStmt(pstate, (RuleStmt *)parseTree);
+      break;
+
+    case T_ViewStmt:
+      {
+       ViewStmt *n = (ViewStmt *)parseTree;
+       n->query = (Query *)transformStmt(pstate, (Node*)n->query);
+       result = makeNode(Query);
+       result->commandType = CMD_UTILITY;
+       result->utilityStmt = (Node*)n;
+      }
+      break;
+
+    case T_VacuumStmt:
+      {
+       MemoryContext oldcontext;
+       /* make sure that this Query is allocated in TopMemory context
+          because vacuum spans transactions and we don't want to lose
+          the vacuum Query due to end-of-transaction free'ing*/
+       oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+       result = makeNode(Query);
+       result->commandType = CMD_UTILITY;
+       result->utilityStmt = (Node*)parseTree;
+       MemoryContextSwitchTo(oldcontext);
+       break;
+           
+      }
+  case T_ExplainStmt:
+      {
+         ExplainStmt *n = (ExplainStmt *)parseTree;
+         result = makeNode(Query);
+         result->commandType = CMD_UTILITY;
+         n->query = transformStmt(pstate, (Node*)n->query);
+         result->utilityStmt = (Node*)parseTree;
+      }
+      break;
+      
+      /*------------------------
+       *  Optimizable statements
+       *------------------------
+       */
+    case T_AppendStmt:
+      result = transformInsertStmt(pstate, (AppendStmt *)parseTree);
+      break;
+
+    case T_DeleteStmt:
+      result = transformDeleteStmt(pstate, (DeleteStmt *)parseTree);
+      break;
+
+    case T_ReplaceStmt:
+      result = transformUpdateStmt(pstate, (ReplaceStmt *)parseTree);
+      break;
+
+    case T_CursorStmt:
+      result = transformCursorStmt(pstate, (CursorStmt *)parseTree);
+      break;
+
+    case T_RetrieveStmt:
+      result = transformSelectStmt(pstate, (RetrieveStmt *)parseTree);
+      break;
+
+    default:
+      /*
+       * other statments don't require any transformation-- just
+       * return the original parsetree 
+       */
+      result = makeNode(Query);
+      result->commandType = CMD_UTILITY;
+      result->utilityStmt = (Node*)parseTree;
+      break;
+    }
+    return result;
+}
+
+/*
+ * transformDeleteStmt -
+ *    transforms a Delete Statement
+ */
+static Query *
+transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
+{
+    Query *qry = makeNode(Query);
+
+    qry->commandType = CMD_DELETE;
+
+    /* set up a range table */
+    makeRangeTable(pstate, stmt->relname, NULL);
+    
+/*    qry->uniqueFlag = FALSE; */
+    qry->uniqueFlag = NULL; 
+
+    /* fix where clause */
+    qry->qual = transformWhereClause(pstate, stmt->whereClause);
+
+    qry->rtable = pstate->p_rtable;
+    qry->resultRelation = RangeTablePosn(pstate->p_rtable, stmt->relname);
+
+    /* make sure we don't have aggregates in the where clause */
+    if (pstate->p_numAgg > 0)
+       parseCheckAggregates(pstate, qry);
+
+    return (Query *)qry;
+}
+
+/*
+ * transformInsertStmt -
+ *    transform an Insert Statement
+ */
+static Query *
+transformInsertStmt(ParseState *pstate, AppendStmt *stmt)
+{
+    Query *qry = makeNode(Query);      /* make a new query tree */
+    List *targetlist;
+
+    qry->commandType = CMD_INSERT;
+
+    /* set up a range table */
+    makeRangeTable(pstate, stmt->relname, stmt->fromClause);
+
+/*    qry->uniqueFlag = FALSE; */
+    qry->uniqueFlag = NULL; 
+
+    /* fix the target list */
+    targetlist = makeTargetList(pstate, stmt->cols, stmt->exprs);
+    qry->targetList = transformTargetList(pstate, 
+                                         targetlist,
+                                         TRUE /* is insert */,
+                                         FALSE /*not update*/);
+
+    /* fix where clause */
+    qry->qual = transformWhereClause(pstate, stmt->whereClause);
+
+    /* now the range table will not change */
+    qry->rtable = pstate->p_rtable;
+    qry->resultRelation = RangeTablePosn(pstate->p_rtable, stmt->relname);
+
+    if (pstate->p_numAgg > 0)
+       finalizeAggregates(pstate, qry);
+
+    return (Query *)qry;
+}
+
+/*
+ * transformIndexStmt -
+ *    transforms the qualification of the index statement
+ */
+static Query *
+transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
+{
+    Query* q;
+
+    q = makeNode(Query);
+    q->commandType = CMD_UTILITY;
+    
+    /* take care of the where clause */
+    stmt->whereClause = transformWhereClause(pstate,stmt->whereClause);
+    stmt->rangetable = pstate->p_rtable;
+
+    q->utilityStmt = (Node*)stmt;
+
+    return q;
+}
+
+/*
+ * transformExtendStmt -
+ *    transform the qualifications of the Extend Index Statement
+ *
+ */
+static Query *
+transformExtendStmt(ParseState *pstate, ExtendStmt *stmt)
+{
+    Query  *q;
+
+    q = makeNode(Query);
+    q->commandType = CMD_UTILITY;
+
+    /* take care of the where clause */
+    stmt->whereClause = transformWhereClause(pstate,stmt->whereClause);
+    stmt->rangetable = pstate->p_rtable;
+
+    q->utilityStmt = (Node*)stmt;
+    return q;
+}
+
+/*
+ * transformRuleStmt -
+ *    transform a Create Rule Statement. The actions is a list of parse
+ *    trees which is transformed into a list of query trees.
+ */
+static Query *
+transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
+{
+    Query *q;
+    List *actions;
+    
+    q = makeNode(Query);
+    q->commandType = CMD_UTILITY;
+
+    actions = stmt->actions;
+    /*
+     * transform each statment, like parse_analyze()
+     */
+    while (actions != NIL) {
+       RangeTblEntry *curEnt, *newEnt;
+
+       /*
+        * NOTE: 'CURRENT' must always have a varno equal to 1 and 'NEW' 
+        * equal to 2.
+        */
+       curEnt = makeRangeTableEntry(stmt->object->relname, FALSE,
+                                    NULL, "*CURRENT*");
+       newEnt = makeRangeTableEntry(stmt->object->relname, FALSE,
+                                    NULL, "*NEW*");
+       pstate->p_rtable = makeList(curEnt, newEnt, -1);
+
+       pstate->p_last_resno = 1;
+       pstate->p_target_resnos = NIL;
+       pstate->p_query_is_rule = 1;    /* for expand all */
+       pstate->p_numAgg = 0;
+       pstate->p_aggs = NULL;
+       
+       lfirst(actions) =  transformStmt(pstate, lfirst(actions));
+       actions = lnext(actions);
+    }
+
+    /* take care of the where clause */
+    stmt->whereClause = transformWhereClause(pstate,stmt->whereClause);
+
+    q->utilityStmt = (Node*)stmt;
+    return q;
+}
+
+
+/*
+ * transformSelectStmt -
+ *    transforms a Select Statement
+ *
+ */
+static Query *
+transformSelectStmt(ParseState *pstate, RetrieveStmt *stmt)
+{
+    Query *qry = makeNode(Query);
+
+    qry->commandType = CMD_SELECT;
+
+    /* set up a range table */
+    makeRangeTable(pstate, NULL, stmt->fromClause);
+
+    qry->uniqueFlag = stmt->unique;
+
+    qry->into    = stmt->into;
+    qry->isPortal = FALSE;
+
+    /* fix the target list */
+    qry->targetList = transformTargetList(pstate, 
+                                         stmt->targetList,
+                                         FALSE, /*is insert */
+                                         FALSE /*not update*/);
+
+    /* fix where clause */
+    qry->qual = transformWhereClause(pstate,stmt->whereClause);
+
+    /* fix order clause */
+    qry->sortClause = transformSortClause(stmt->orderClause,
+                                         qry->targetList,
+                                         qry->uniqueFlag);
+
+    /* fix group by clause */
+    qry->groupClause = transformGroupClause(pstate,
+                                           stmt->groupClause);
+    qry->rtable = pstate->p_rtable;
+
+    if (pstate->p_numAgg > 0)
+       finalizeAggregates(pstate, qry);
+       
+    return (Query *)qry;
+}
+
+/*
+ * transformUpdateStmt -
+ *    transforms an update statement
+ *
+ */
+static Query *
+transformUpdateStmt(ParseState *pstate, ReplaceStmt *stmt)
+{
+    Query *qry = makeNode(Query);
+
+    qry->commandType = CMD_UPDATE;
+
+    /*
+     * the FROM clause is non-standard SQL syntax. We used to be able to
+     * do this with REPLACE in POSTQUEL so we keep the feature.
+     */
+    makeRangeTable(pstate, stmt->relname, stmt->fromClause); 
+
+    /* fix the target list */
+    qry->targetList = transformTargetList(pstate, 
+                                         stmt->targetList,
+                                         FALSE, /* not insert */
+                                         TRUE   /* is update */);
+
+    /* fix where clause */
+    qry->qual = transformWhereClause(pstate,stmt->whereClause);
+
+    qry->rtable = pstate->p_rtable;
+    qry->resultRelation = RangeTablePosn(pstate->p_rtable, stmt->relname);
+
+    /* make sure we don't have aggregates in the where clause */
+    if (pstate->p_numAgg > 0)
+       parseCheckAggregates(pstate, qry);
+
+    return (Query *)qry;
+}
+
+/*
+ * transformCursorStmt -
+ *    transform a Create Cursor Statement
+ *
+ */
+static Query *
+transformCursorStmt(ParseState *pstate, CursorStmt *stmt)
+{
+    Query *qry = makeNode(Query);
+
+    /*
+     * in the old days, a cursor statement is a 'retrieve into portal';
+     * If you change the following, make sure you also go through the code
+     * in various places that tests the kind of operation.
+     */
+    qry->commandType = CMD_SELECT;
+
+    /* set up a range table */
+    makeRangeTable(pstate, NULL, stmt->fromClause);
+
+    qry->uniqueFlag = stmt->unique;
+
+    qry->into    = stmt->portalname;
+    qry->isPortal = TRUE;
+    qry->isBinary = stmt->binary;      /* internal portal */
+
+    /* fix the target list */
+    qry->targetList = transformTargetList(pstate,
+                                         stmt->targetList,
+                                         FALSE, /*is insert */
+                                         FALSE /*not update*/);
+
+    /* fix where clause */
+    qry->qual = transformWhereClause(pstate,stmt->whereClause);
+
+    /* fix order clause */
+    qry->sortClause = transformSortClause(stmt->orderClause,
+                                         qry->targetList,
+                                         qry->uniqueFlag);
+    qry->rtable = pstate->p_rtable;
+
+    if (pstate->p_numAgg > 0)
+       finalizeAggregates(pstate, qry);
+
+    return (Query *)qry;
+}
+
+/*****************************************************************************
+ *
+ * Transform Exprs, Aggs, etc.
+ *
+ *****************************************************************************/
+
+/*
+ * transformExpr -
+ *    analyze and transform expressions. Type checking and type casting is
+ *    done here. The optimizer and the executor cannot handle the original
+ *    (raw) expressions collected by the parse tree. Hence the transformation
+ *    here.
+ */
+static Node *
+transformExpr(ParseState *pstate, Node *expr)
+{
+    Node *result;
+
+    if (expr==NULL)
+       return NULL;
+    
+    switch(nodeTag(expr)) {
+    case T_Attr: {
+       Attr *att = (Attr *)expr;
+       Node *temp;
+
+       /* what if att.attrs == "*"?? */
+       temp = handleNestedDots(pstate, att, &pstate->p_last_resno);
+       if (att->indirection != NIL) {
+           List *idx = att->indirection;
+           while(idx!=NIL) {
+               A_Indices *ai = (A_Indices *)lfirst(idx);
+               Node *lexpr=NULL, *uexpr;
+               uexpr = transformExpr(pstate, ai->uidx);  /* must exists */
+               if (exprType(uexpr) != INT4OID)
+                   elog(WARN, "array index expressions must be int4's");
+               if (ai->lidx != NULL) {
+                   lexpr = transformExpr(pstate, ai->lidx);
+                   if (exprType(lexpr) != INT4OID)
+                       elog(WARN, "array index expressions must be int4's");
+               }
+#if 0
+               pfree(ai->uidx);
+               if (ai->lidx!=NULL) pfree(ai->lidx);
+#endif
+               ai->lidx = lexpr;
+               ai->uidx = uexpr;
+               /* note we reuse the list of indices, make sure we don't free
+                  them! Otherwise, make a new list here */
+               idx = lnext(idx);
+           }
+           result = (Node*)make_array_ref(temp, att->indirection);
+       }else {
+           result = temp;
+       }
+       break;
+    }
+    case T_A_Const: {
+       A_Const *con= (A_Const *)expr;
+       Value *val = &con->val;
+       if (con->typename != NULL) {
+           result = parser_typecast(val, con->typename, -1);
+       }else {
+           result = (Node *)make_const(val);
+       }
+       break;
+    }
+    case T_ParamNo: {
+       ParamNo *pno = (ParamNo *)expr;
+       Oid toid;
+       int paramno;
+       Param *param;
+
+       paramno = pno->number;
+       toid = param_type(paramno);
+       if (!OidIsValid(toid)) {
+           elog(WARN, "Parameter '$%d' is out of range",
+                paramno);
+       }
+       param = makeNode(Param);
+       param->paramkind = PARAM_NUM;
+       param->paramid = (AttrNumber) paramno;
+       param->paramname = "<unnamed>";
+       param->paramtype = (Oid)toid;
+       param->param_tlist = (List*) NULL;
+
+       result = (Node *)param;
+       break;
+    }
+    case T_A_Expr: {
+       A_Expr *a = (A_Expr *)expr;
+
+       switch(a->oper) {
+       case OP:
+           {
+               Node *lexpr = transformExpr(pstate, a->lexpr);
+               Node *rexpr = transformExpr(pstate, a->rexpr);
+               result = (Node *)make_op(a->opname, lexpr, rexpr);
+           }
+           break;
+       case ISNULL:
+           {
+               Node *lexpr = transformExpr(pstate, a->lexpr);
+               result = ParseFunc(pstate, 
+                                  "NullValue", lcons(lexpr, NIL),
+                                  &pstate->p_last_resno);
+           }
+           break;
+       case NOTNULL:
+           {
+               Node *lexpr = transformExpr(pstate, a->lexpr);
+               result = ParseFunc(pstate,
+                                  "NonNullValue", lcons(lexpr, NIL),
+                                  &pstate->p_last_resno);
+           }
+           break;
+       case AND:
+           {
+               Expr *expr = makeNode(Expr);
+               Node *lexpr = transformExpr(pstate, a->lexpr);
+               Node *rexpr = transformExpr(pstate, a->rexpr);
+               if (exprType(lexpr) != BOOLOID)
+                   elog(WARN,
+                        "left-hand side of AND is type '%s', not bool",
+                        tname(get_id_type(exprType(lexpr))));
+               if (exprType(rexpr) != BOOLOID)
+                   elog(WARN,
+                        "right-hand side of AND is type '%s', not bool",
+                        tname(get_id_type(exprType(rexpr))));
+               expr->typeOid = BOOLOID;
+               expr->opType = AND_EXPR;
+               expr->args = makeList(lexpr, rexpr, -1);
+               result = (Node *)expr;
+           }
+           break;
+       case OR:
+           {
+               Expr *expr = makeNode(Expr);
+               Node *lexpr = transformExpr(pstate, a->lexpr);
+               Node *rexpr = transformExpr(pstate, a->rexpr);
+               if (exprType(lexpr) != BOOLOID)
+                   elog(WARN,
+                        "left-hand side of OR is type '%s', not bool",
+                        tname(get_id_type(exprType(lexpr))));
+               if (exprType(rexpr) != BOOLOID)
+                   elog(WARN,
+                        "right-hand side of OR is type '%s', not bool",
+                        tname(get_id_type(exprType(rexpr))));
+               expr->typeOid = BOOLOID;
+               expr->opType = OR_EXPR;
+               expr->args = makeList(lexpr, rexpr, -1);
+               result = (Node *)expr;
+           }
+           break;
+       case NOT:
+           {
+               Expr *expr = makeNode(Expr);
+               Node *rexpr = transformExpr(pstate, a->rexpr);
+               if (exprType(rexpr) != BOOLOID)
+                   elog(WARN,
+                        "argument to NOT is type '%s', not bool",
+                        tname(get_id_type(exprType(rexpr))));
+               expr->typeOid = BOOLOID;
+               expr->opType = NOT_EXPR;
+               expr->args = makeList(rexpr, -1);
+               result = (Node *)expr;
+           }
+           break;
+       }
+       break;
+    }
+    case T_Ident: {
+       Ident *ident = (Ident*)expr;
+       bool isrel;
+       char *reln= ParseColumnName(pstate,ident->name, &isrel);
+
+       /* could be a column name or a relation_name */
+       if (reln==NULL) {
+           /*
+            * may be a relation_name
+            *
+            * ??? in fact, every ident left after transfromExpr() is called
+            *     will be assumed to be a relation.
+            */
+           if (isrel) {
+               ident->isRel = TRUE;
+               result = (Node*)ident;
+           } else {
+               elog(WARN, "attribute \"%s\" not found", ident->name);
+           }
+       }else {
+           Attr *att = makeNode(Attr);
+           att->relname = reln;
+           att->attrs = lcons(makeString(ident->name), NIL);
+           /*
+            * a column name
+            */
+           result =
+               (Node*)handleNestedDots(pstate, att, &pstate->p_last_resno);
+       }
+       break;
+    }
+    case T_FuncCall: {
+       FuncCall *fn = (FuncCall *)expr;
+       List *args;
+
+       /* transform the list of arguments */
+       foreach(args, fn->args) {
+           lfirst(args) = transformExpr(pstate, (Node*)lfirst(args));
+       }
+       result = ParseFunc(pstate,
+                          fn->funcname, fn->args, &pstate->p_last_resno);
+       break;
+    }
+    default:
+       /* should not reach here */
+       elog(WARN, "transformExpr: does not know how to transform %d\n",
+            nodeTag(expr));
+       break;
+    }
+
+    return result;
+}
+
+/*****************************************************************************
+ *
+ * From Clause
+ *
+ *****************************************************************************/
+
+/*
+ * parseFromClause -
+ *    turns the table references specified in the from-clause into a
+ *    range table. The range table may grow as we transform the expressions
+ *    in the target list. (Note that this happens because in POSTQUEL, we
+ *    allow references to relations not specified in the from-clause. We
+ *    also allow that in our POST-SQL)
+ *
+ */
+static void
+parseFromClause(ParseState *pstate, List *frmList)
+{
+    List *fl= frmList;
+
+    while(fl!=NIL) {
+       RangeVar *r = lfirst(fl);
+       RelExpr *baserel = r->relExpr;
+       RangeTblEntry *ent;
+       char *relname = baserel->relname;
+       char *refname = r->name;
+
+       if (refname==NULL) {
+           refname = relname;
+       } else {
+           /*
+            * check whether refname exists already
+            */
+           if (RangeTablePosn(pstate->p_rtable, refname) != 0)
+               elog(WARN, "parser: range variable \"%s\" duplicated",
+                    refname);
+       }
+
+       ent = makeRangeTableEntry(relname, baserel->inh,
+                                 baserel->timeRange, refname);
+       /*
+        * marks this entry to indicate it comes from the from clause. In
+        * SQL, the target list can only refer to range variables specified
+        * in the from clause but we follow the more powerful POSTQUEL
+        * semantics and automatically generate the range variable if not
+        * specified. However there are times we need to know whether the
+        * entries are legitimate.
+        *
+        * eg. select * from foo f where f.x = 1; will generate wrong answer
+        *     if we expand * to foo.x.
+        */
+       ent->inFromCl = true;
+
+       pstate->p_rtable = lappend(pstate->p_rtable, ent);      
+       fl= lnext(fl);
+    }
+}
+
+/*
+ * makeRangeTable -
+ *    make a range table with the specified relation (optional) and the
+ *    from-clause.
+ */
+static void
+makeRangeTable(ParseState *pstate, char *relname, List *frmList)
+{
+    int x;
+
+    parseFromClause(pstate, frmList);
+
+    if (relname == NULL)
+       return;
+    
+    if (RangeTablePosn(pstate->p_rtable, relname) < 1) {
+       RangeTblEntry *ent;
+
+       ent = makeRangeTableEntry(relname, FALSE, NULL, relname);
+       pstate->p_rtable = lappend(pstate->p_rtable, ent);
+    }
+    x = RangeTablePosn(pstate->p_rtable, relname);
+    pstate->parser_current_rel = heap_openr(VarnoGetRelname(pstate,x));
+    if (pstate->parser_current_rel == NULL)
+       elog(WARN,"invalid relation name");
+}
+
+/*
+ *  exprType -
+ *    returns the Oid of the type of the expression. (Used for typechecking.)
+ */
+Oid
+exprType(Node *expr)
+{
+    Oid type;
+    
+    switch(nodeTag(expr)) {
+    case T_Func:
+       type = ((Func*)expr)->functype;
+       break;
+    case T_Iter:
+       type = ((Iter*)expr)->itertype;
+       break;
+    case T_Var:
+       type = ((Var*)expr)->vartype;
+       break;
+    case T_Expr:
+       type = ((Expr*)expr)->typeOid;
+       break;
+    case T_Const:
+       type = ((Const*)expr)->consttype;
+       break;
+    case T_ArrayRef:
+       type = ((ArrayRef*)expr)->refelemtype;
+       break;
+    case T_Aggreg:
+       type = ((Aggreg*)expr)->aggtype;
+       break;
+    case T_Param:
+       type = ((Param*)expr)->paramtype;
+       break;
+    case T_Ident:
+       /* is this right? */
+       type = UNKNOWNOID;
+       break;
+    default:
+       elog(WARN, "exprType: don't know how to get type for %d node",
+            nodeTag(expr));
+       break;
+    }
+    return type;
+}
+
+/*
+ * expandAllTables -
+ *    turns '*' (in the target list) into a list of attributes (of all
+ *    relations in the range table)
+ */
+static List *
+expandAllTables(ParseState *pstate)
+{
+    List *target= NIL;
+    List *legit_rtable=NIL;
+    List *rt, *rtable;
+
+    rtable = pstate->p_rtable;
+    if (pstate->p_query_is_rule) {
+       /*
+        * skip first two entries, "*new*" and "*current*"
+        */
+       rtable = lnext(lnext(pstate->p_rtable));
+    }
+    
+    /* this should not happen */
+    if (rtable==NULL) 
+       elog(WARN, "cannot expand: null p_rtable");
+
+    /* 
+     * go through the range table and make a list of range table entries
+     * which we will expand.
+     */
+    foreach(rt, rtable) {
+       RangeTblEntry *rte = lfirst(rt);
+
+       /*
+        * we only expand those specify in the from clause. (This will
+        * also prevent us from using the wrong table in inserts: eg. tenk2
+        * in "insert into tenk2 select * from tenk1;")
+        */
+       if (!rte->inFromCl)
+           continue;
+       legit_rtable = lappend(legit_rtable, rte);
+    }
+
+    foreach(rt, legit_rtable) {
+       RangeTblEntry *rte = lfirst(rt);
+       char *rt_name= rte->refname;    /* use refname here so that we
+                                          refer to the right entry */
+       List *temp = target;
+       
+       if(temp == NIL )
+           target = expandAll(pstate, rt_name, &pstate->p_last_resno);
+       else {
+           while (temp != NIL && lnext(temp) != NIL)
+               temp = lnext(temp);
+           lnext(temp) = expandAll(pstate, rt_name, &pstate->p_last_resno);
+       }
+    }
+    return target;
+}
+
+
+/*
+ * figureColname -
+ *    if the name of the resulting column is not specified in the target
+ *    list, we have to guess.
+ *
+ */
+static char *
+figureColname(Node *expr, Node *resval)
+{
+    switch (nodeTag(expr)) {
+    case T_Aggreg:
+       return (char*) /* XXX */
+           ((Aggreg *)expr)->aggname;
+    case T_Expr:
+       if (((Expr*)expr)->opType == FUNC_EXPR) {
+           if (nodeTag(resval)==T_FuncCall)
+               return ((FuncCall*)resval)->funcname;
+       }
+       break;
+    default:
+       break;
+    }
+       
+    return "?column?";
+}
+
+/*****************************************************************************
+ *
+ * Target list
+ *
+ *****************************************************************************/
+
+/*
+ * makeTargetList -
+ *    turn a list of column names and expressions (in the same order) into
+ *    a target list (used exclusively for inserts)
+ */
+static List *
+makeTargetList(ParseState *pstate, List *cols, List *exprs)
+{
+    List *tlist, *tl=NULL;
+    if (cols != NIL) {
+       /* has to transform colElem too (opt_indirection can be exprs) */
+       while(cols!=NIL) {
+           ResTarget *res = makeNode(ResTarget);
+           Ident *id = lfirst(cols);
+           /* Id opt_indirection */
+           res->name = id->name;
+           res->indirection = id->indirection;
+           if (exprs == NIL) {
+               elog(WARN, "insert: number of expressions less than columns");
+           }else {
+               res->val = (Node *)lfirst(exprs);
+           }
+           if (tl==NIL) {
+               tlist = tl = lcons(res, NIL);
+           }else {
+               lnext(tl) = lcons(res,NIL);
+               tl = lnext(tl);
+           }
+           cols = lnext(cols);
+           exprs = lnext(exprs);
+       }
+       if (cols != NIL) {
+           elog(WARN, "insert: number of columns more than expressions");
+       }
+    }else {
+       bool has_star = false;
+       
+       if (exprs==NIL)
+           return NIL;
+       if (IsA(lfirst(exprs),Attr)) {
+           Attr *att = lfirst(exprs);
+
+           if ((att->relname!=NULL && !strcmp(att->relname,"*")) ||
+               (att->attrs!=NIL && !strcmp(strVal(lfirst(att->attrs)),"*")))
+               has_star = true;
+       }
+       if (has_star) {
+           /*
+            * right now, these better be 'relname.*' or '*' (this can happen
+            * in eg. insert into tenk2 values (tenk1.*); or
+            *        insert into tenk2 select * from tenk1;
+            */
+           while(exprs!=NIL) {
+               ResTarget *res = makeNode(ResTarget);
+               res->name = NULL;
+               res->indirection = NULL;
+               res->val = (Node *)lfirst(exprs);
+               if (tl==NIL) {
+                   tlist = tl = lcons(res, NIL);
+               }else {
+                   lnext(tl) = lcons(res,NIL);
+                   tl = lnext(tl);
+               }
+               exprs = lnext(exprs);
+           }
+       } else {
+           Relation insertRel = pstate->parser_current_rel;
+           int numcol;
+           int i;
+           AttributeTupleForm *attr = insertRel->rd_att->attrs;
+           
+           numcol = Min(length(exprs), insertRel->rd_rel->relnatts);
+           for(i=0; i < numcol; i++) {
+               ResTarget *res = makeNode(ResTarget);
+
+               res->name = palloc(NAMEDATALEN+1);
+               strncpy(res->name, attr[i]->attname.data, NAMEDATALEN);
+               res->name[NAMEDATALEN]='\0';
+               res->indirection = NULL;
+               res->val = (Node *)lfirst(exprs);
+               if (tl==NIL) {
+                   tlist = tl = lcons(res, NIL);
+               }else {
+                   lnext(tl) = lcons(res,NIL);
+                   tl = lnext(tl);
+               }
+               exprs = lnext(exprs);
+           }
+       }
+    }
+    return tlist;
+}
+
+/*
+ * transformTargetList -
+ *    turns a list of ResTarget's into a list of TargetEntry's
+ */
+static List *
+transformTargetList(ParseState *pstate, 
+                   List *targetlist,
+                   bool isInsert,
+                   bool isUpdate)
+{
+    List *p_target= NIL;
+    List *temp = NIL;
+
+    while(targetlist != NIL) {
+       ResTarget *res= (ResTarget *)lfirst(targetlist);
+       TargetEntry *tent = makeNode(TargetEntry);
+
+       switch(nodeTag(res->val)) {
+       case T_Ident: {
+           Node *expr;
+           Oid type_id;
+           int type_len;
+           char *identname;
+           char *resname;
+
+           identname = ((Ident*)res->val)->name;
+           expr = transformExpr(pstate, (Node*)res->val);
+           type_id = exprType(expr);
+           type_len = tlen(get_id_type(type_id));
+           resname = (res->name) ? res->name : identname;
+           tent->resdom = makeResdom((AttrNumber)pstate->p_last_resno++,
+                                     (Oid)type_id,
+                                     (Size)type_len,
+                                     resname,
+                                     (Index)0,
+                                     (Oid)0,
+                                     0);
+                                     
+           tent->expr = expr;
+           break;
+       }
+       case T_ParamNo:
+       case T_FuncCall:
+       case T_A_Const:    
+       case T_A_Expr: {
+           Node *expr = transformExpr(pstate, (Node *)res->val);
+
+           if (isInsert && res->name==NULL)
+               elog(WARN, "Sorry, have to specify the column list");
+
+           /* note indirection has not been transformed */
+           if (isInsert && res->indirection!=NIL) {
+               /* this is an array assignment */
+               char *val;
+               char *str, *save_str;
+               List *elt;
+               int i = 0, ndims;
+               int lindx[MAXDIM], uindx[MAXDIM];
+               int resdomno;
+               Relation rd;
+               Value *constval;
+               
+               if (exprType(expr) != UNKNOWNOID ||
+                   !IsA(expr,Const))
+                   elog(WARN, "yyparse: string constant expected");
+
+               val = (char *) textout((struct varlena *)
+                                      ((Const *)expr)->constvalue);
+               str = save_str = (char*)palloc(strlen(val) + MAXDIM * 25 + 2);
+               foreach(elt, res->indirection) {
+                   A_Indices *aind = (A_Indices *)lfirst(elt);
+                   aind->uidx = transformExpr(pstate, aind->uidx);
+                   if (!IsA(aind->uidx,Const)) 
+                       elog(WARN,
+                            "Array Index for Append should be a constant");
+                   uindx[i] = ((Const *)aind->uidx)->constvalue;
+                   if (aind->lidx!=NULL) {
+                       aind->lidx = transformExpr(pstate, aind->lidx);
+                       if (!IsA(aind->lidx,Const))
+                           elog(WARN,
+                               "Array Index for Append should be a constant");
+                       lindx[i] = ((Const*)aind->lidx)->constvalue;
+                   }else {
+                       lindx[i] = 1;
+                   }
+                   if (lindx[i] > uindx[i]) 
+                       elog(WARN, "yyparse: lower index cannot be greater than upper index");
+                   sprintf(str, "[%d:%d]", lindx[i], uindx[i]);
+                   str += strlen(str);
+                   i++;
+               }
+               sprintf(str, "=%s", val);
+               rd = pstate->parser_current_rel;
+               Assert(rd != NULL);
+               resdomno = varattno(rd, res->name);
+               ndims = att_attnelems(rd, resdomno);
+               if (i != ndims)
+                   elog(WARN, "yyparse: array dimensions do not match");
+               constval = makeNode(Value);
+               constval->type = T_String;
+               constval->val.str = save_str;
+               tent = make_targetlist_expr(pstate, res->name,
+                                           (Node*)make_const(constval),
+                                           NULL,
+                                           (isInsert||isUpdate));
+               pfree(save_str);
+           } else {
+               char *colname= res->name;
+               /* this is not an array assignment */
+               if (colname==NULL) {
+                   /* if you're wondering why this is here, look at
+                    * the yacc grammar for why a name can be missing. -ay
+                    */
+                   colname = figureColname(expr, res->val);
+               }
+               if (res->indirection) {
+                   List *ilist = res->indirection;
+                   while (ilist!=NIL) {
+                       A_Indices *ind = lfirst(ilist);
+                       ind->lidx = transformExpr(pstate, ind->lidx);
+                       ind->uidx = transformExpr(pstate, ind->uidx);
+                       ilist = lnext(ilist);
+                   }
+               }
+               tent = make_targetlist_expr(pstate, colname, expr, 
+                                           res->indirection,
+                                           (isInsert||isUpdate));
+           }
+           break;
+       }
+       case T_Attr: {
+           Oid type_id;
+           int type_len;
+           Attr *att = (Attr *)res->val;
+           Node *result;
+           char *attrname;
+           char *resname;
+           Resdom *resnode;
+           List *attrs = att->attrs;
+               
+
+           /*
+            * Target item is a single '*', expand all tables
+            * (eg. SELECT * FROM emp)
+            */
+           if (att->relname!=NULL && !strcmp(att->relname, "*")) {
+               if(lnext(targetlist)!=NULL)
+                   elog(WARN, "cannot expand target list *, ...");
+               p_target = expandAllTables(pstate);
+
+               /*
+                * skip rest of while loop
+                */
+               targetlist = lnext(targetlist);
+               continue;
+           }
+
+           /*
+            * Target item is relation.*, expand the table
+            * (eg. SELECT emp.*, dname FROM emp, dept)
+            */
+           attrname = strVal(lfirst(att->attrs));
+           if (att->attrs!=NIL && !strcmp(attrname,"*")) {
+               /* temp is the target list we're building in the while
+                * loop. Make sure we fix it after appending more nodes.
+                */
+               if (temp == NIL) {
+                   p_target = temp =
+                       expandAll(pstate, att->relname, &pstate->p_last_resno);
+               } else {
+                   lnext(temp) =
+                       expandAll(pstate, att->relname, &pstate->p_last_resno);
+               }
+               while(lnext(temp)!=NIL)
+                   temp = lnext(temp); /* make sure we point to the last
+                                          target entry */
+               /*
+                * skip the rest of the while loop
+                */
+               targetlist = lnext(targetlist);
+               continue; 
+           }
+
+
+           /*
+            * Target item is fully specified: ie. relation.attribute
+            */
+           result = handleNestedDots(pstate, att, &pstate->p_last_resno);
+           if (att->indirection != NIL) {
+               List *ilist = att->indirection;
+               while (ilist!=NIL) {
+                   A_Indices *ind = lfirst(ilist);
+                   ind->lidx = transformExpr(pstate, ind->lidx);
+                   ind->uidx = transformExpr(pstate, ind->uidx);
+                   ilist = lnext(ilist);
+               }
+               result = (Node*)make_array_ref(result, att->indirection);
+           }
+           type_id = exprType(result);
+           type_len = tlen(get_id_type(type_id));
+           while(lnext(attrs)!=NIL)
+               attrs=lnext(attrs);
+           resname = (res->name) ? res->name : strVal(lfirst(attrs));
+           resnode = makeResdom((AttrNumber)pstate->p_last_resno++,
+                                (Oid)type_id,
+                                (Size)type_len,
+                                resname,
+                                (Index)0,
+                                (Oid)0,
+                                0);
+           tent->resdom = resnode;
+           tent->expr = result;
+           break;
+       }
+       default:
+           /* internal error */
+           elog(WARN,
+                "internal error: do not know how to transform targetlist");
+           break;
+       }
+
+       if (p_target==NIL) {
+           p_target = temp = lcons(tent, NIL);
+       }else {
+           lnext(temp) = lcons(tent, NIL);
+           temp = lnext(temp);
+       }
+       targetlist = lnext(targetlist);
+    }
+    return p_target;
+}
+
+
+/*
+ * make_targetlist_expr -
+ *    make a TargetEntry
+ *
+ * arrayRef is a list of transformed A_Indices
+ */
+static TargetEntry *
+make_targetlist_expr(ParseState *pstate,
+                    char *name,
+                    Node *expr,
+                    List *arrayRef,
+                    bool ResdomNoIsAttrNo)
+{
+     int type_id, type_len, attrtype, attrlen;
+     int resdomno;
+     Relation rd;
+     bool attrisset;
+     TargetEntry *tent;
+     Resdom *resnode;
+     
+     if (expr == NULL)
+        elog(WARN, "make_targetlist_expr: invalid use of NULL expression");
+
+     type_id = exprType(expr);
+     type_len = tlen(get_id_type(type_id));
+
+     /* I have no idea what the following does! */
+     if (ResdomNoIsAttrNo) {
+         /*
+          * append or replace query -- 
+          * append, replace work only on one relation,
+          * so multiple occurence of same resdomno is bogus
+          */
+         rd = pstate->parser_current_rel;
+         Assert(rd != NULL);
+         resdomno = varattno(rd,name);
+         attrisset = varisset(rd,name);
+         attrtype = att_typeid(rd,resdomno);
+         if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL))
+              attrtype = GetArrayElementType(attrtype);
+         if (attrtype==BPCHAROID || attrtype==VARCHAROID) {
+             attrlen = rd->rd_att->attrs[resdomno-1]->attlen;
+         } else {
+             attrlen = tlen(get_id_type(attrtype));
+         }
+#if 0
+         if(Input_is_string && Typecast_ok){
+              Datum val;
+              if (type_id == typeid(type("unknown"))){
+                   val = (Datum)textout((struct varlena *)
+                                        ((Const)lnext(expr))->constvalue);
+              }else{
+                   val = ((Const)lnext(expr))->constvalue;
+              }
+              if (attrisset) {
+                   lnext(expr) =  makeConst(attrtype,
+                                          attrlen,
+                                          val,
+                                          false,
+                                          true,
+                                          true /* is set */);
+              } else {
+                   lnext(expr) = 
+                        makeConst(attrtype, 
+                                  attrlen,
+                                  (Datum)fmgr(typeid_get_retinfunc(attrtype),
+                                              val,get_typelem(attrtype),-1),
+                                  false, 
+                                  true /* Maybe correct-- 80% chance */,
+                                  false /* is not a set */);
+              }
+         } else if((Typecast_ok) && (attrtype != type_id)){
+              lnext(expr) = 
+                   parser_typecast2(expr, get_id_type((long)attrtype));
+         } else
+              if (attrtype != type_id) {
+                   if ((attrtype == INT2OID) && (type_id == INT4OID))
+                        lfirst(expr) = lispInteger (INT2OID);
+                    else if ((attrtype == FLOAT4OID) && (type_id == FLOAT8OID))
+                        lfirst(expr) = lispInteger (FLOAT4OID);
+                    else
+                        elog(WARN, "unequal type in tlist : %s \n",
+                             name));
+              }
+         
+         Input_is_string = false;
+         Input_is_integer = false;
+         Typecast_ok = true;
+#endif
+         if (attrtype != type_id) {
+             if (IsA(expr,Const)) {
+                 /* try to cast the constant */
+                 expr = (Node*)parser_typecast2(expr,
+                                                type_id,
+                                                get_id_type((long)attrtype),
+                                                attrlen);
+             } else {
+                 /* currently, we can't handle casting of expressions */
+                 elog(WARN, "parser: attribute '%s' is of type '%.*s' but expression is of type '%.*s'",
+                      name,
+                      NAMEDATALEN, get_id_typname(attrtype),
+                      NAMEDATALEN, get_id_typname(type_id));
+             }
+         }
+
+         if (intMember(resdomno, pstate->p_target_resnos)) {
+              elog(WARN,"two or more occurrences of same attr");
+         } else {
+              pstate->p_target_resnos = lconsi(resdomno,
+                                                pstate->p_target_resnos);
+         }
+         if (arrayRef != NIL) {
+              Expr *target_expr;
+              Attr *att = makeNode(Attr);
+              List *ar = arrayRef;
+              List *upperIndexpr = NIL;
+              List *lowerIndexpr = NIL;
+
+              att->relname = pstrdup(RelationGetRelationName(rd)->data);
+              att->attrs = lcons(makeString(name), NIL);
+              target_expr = (Expr*)handleNestedDots(pstate, att,
+                                                    &pstate->p_last_resno);
+              while(ar!=NIL) {
+                  A_Indices *ind = lfirst(ar);
+                  if (lowerIndexpr) {
+                      /* XXX assume all lowerIndexpr is non-null in
+                       * this case
+                       */
+                      lowerIndexpr = lappend(lowerIndexpr, ind->lidx);
+                  }
+                  upperIndexpr = lappend(upperIndexpr, ind->uidx);
+                  ar = lnext(ar);
+              }
+              
+              expr = (Node*)make_array_set(target_expr,
+                                           upperIndexpr,
+                                           lowerIndexpr,
+                                           (Expr*)expr);       
+              attrtype = att_typeid(rd,resdomno);
+              attrlen = tlen(get_id_type(attrtype)); 
+         }
+     } else {
+         resdomno = pstate->p_last_resno++;
+         attrtype = type_id;
+         attrlen = type_len;
+     }
+     tent = makeNode(TargetEntry);
+
+     resnode = makeResdom((AttrNumber)resdomno,
+                         (Oid) attrtype,
+                         (Size) attrlen,
+                         name, 
+                         (Index)0,
+                         (Oid)0,
+                         0);
+
+     tent->resdom = resnode;
+     tent->expr = expr;
+        
+     return  tent;
+ }
+
+
+/*****************************************************************************
+ *
+ * Where Clause
+ *
+ *****************************************************************************/
+
+/*
+ * transformWhereClause -
+ *    transforms the qualification and make sure it is of type Boolean
+ *
+ */
+static Node *
+transformWhereClause(ParseState *pstate, Node *a_expr)
+{
+    Node *qual;
+
+    if (a_expr == NULL)
+       return (Node *)NULL;            /* no qualifiers */
+
+    qual = transformExpr(pstate, a_expr);
+    if (exprType(qual) != BOOLOID) {
+       elog(WARN,
+            "where clause must return type bool, not %s",
+            tname(get_id_type(exprType(qual))));
+    }
+    return qual;
+}
+
+/*****************************************************************************
+ *
+ * Sort Clause
+ *
+ *****************************************************************************/
+
+/*
+ *  find_tl_elt -
+ *    returns the Resdom in the target list matching the specified varname
+ *
+ */
+static Resdom *
+find_tl_elt(char *varname, List *tlist)
+{
+    List *i;
+    
+    foreach(i, tlist) {
+       TargetEntry *target = (TargetEntry *)lfirst(i);
+       Resdom *resnode = target->resdom;
+       char *resname = resnode->resname;
+       
+       if (!strcmp(resname, varname))
+           return (resnode);
+    }
+    return ((Resdom *)NULL);
+}
+
+static Oid
+any_ordering_op(int restype)
+{
+    Operator order_op;
+    Oid order_opid;
+    
+    order_op = oper("<",restype,restype);
+    order_opid = (Oid)oprid(order_op);
+    
+    return order_opid;
+}
+
+/*
+ * transformGroupClause -
+ *    transform an Group By clause
+ *
+ */
+static List *
+transformGroupClause(ParseState *pstate, List *grouplist)
+{
+    List *glist = NIL, *gl;
+
+    while (grouplist != NIL) {
+       GroupClause *grpcl = makeNode(GroupClause);
+       Var *groupAttr = (Var*)transformExpr(pstate, (Node*)lfirst(grouplist));
+
+       if (nodeTag(groupAttr) != T_Var) {
+           elog(WARN, "parser: can only specify attribute in group by");
+       }
+       grpcl->grpAttr = groupAttr;
+       grpcl->grpOpoid = any_ordering_op(groupAttr->vartype);
+       if (glist == NIL) {
+           gl = glist = lcons(grpcl, NIL);
+       } else {
+           lnext(gl) = lcons(grpcl, NIL);
+           gl = lnext(gl);
+       }
+       grouplist = lnext(grouplist);
+    }
+
+    return glist;
+}
+
+/*
+ * transformSortClause -
+ *    transform an Order By clause
+ *
+ */
+static List *
+transformSortClause(List *orderlist, List *targetlist,
+                   char* uniqueFlag)
+{
+    List *sortlist = NIL;
+    List *s, *i;
+
+    while(orderlist != NIL) {
+       SortBy *sortby = lfirst(orderlist);
+       SortClause *sortcl = makeNode(SortClause);
+       Resdom *resdom;
+       
+       resdom = find_tl_elt(sortby->name, targetlist);
+       if (resdom == NULL)
+           elog(WARN,"The field being sorted by must appear in the target list");
+       
+       sortcl->resdom = resdom;
+       sortcl->opoid = oprid(oper(sortby->useOp,
+                                  resdom->restype,
+                                  resdom->restype));
+       if (sortlist == NIL) {
+           s = sortlist = lcons(sortcl, NIL);
+       }else {
+           lnext(s) = lcons(sortcl, NIL);
+           s = lnext(s);
+       }
+       orderlist = lnext(orderlist);
+    }
+    
+    if (uniqueFlag) {
+      if (uniqueFlag[0] == '*') {
+       /* concatenate all elements from target list
+          that are not already in the sortby list */
+        foreach (i,targetlist) {
+           TargetEntry *tlelt = (TargetEntry *)lfirst(i);
+
+           s = sortlist;
+           while(s != NIL) {
+               SortClause *sortcl = lfirst(s);
+               if (sortcl->resdom==tlelt->resdom)
+                   break;
+               s = lnext(s);
+           }
+           if (s == NIL) {
+               /* not a member of the sortclauses yet */
+               SortClause *sortcl = makeNode(SortClause);
+               
+               sortcl->resdom = tlelt->resdom;
+               sortcl->opoid = any_ordering_op(tlelt->resdom->restype);
+
+               sortlist = lappend(sortlist, sortcl);
+             }
+         }
+      }
+      else {
+       TargetEntry *tlelt;
+       char* uniqueAttrName = uniqueFlag;
+
+         /* only create sort clause with the specified unique attribute */
+         foreach (i, targetlist) {
+           tlelt = (TargetEntry*)lfirst(i);
+           if (strcmp(tlelt->resdom->resname, uniqueAttrName) == 0)
+             break;
+         }
+         if (i == NIL) {
+           elog(WARN, "The field specified in the UNIQUE ON clause is not in the targetlist");
+         }
+         s = sortlist;
+         foreach (s, sortlist) {
+           SortClause *sortcl = lfirst(s);
+           if (sortcl->resdom == tlelt->resdom)
+             break;
+         }
+         if (s == NIL) { 
+               /* not a member of the sortclauses yet */
+               SortClause *sortcl = makeNode(SortClause);
+               
+               sortcl->resdom = tlelt->resdom;
+               sortcl->opoid = any_ordering_op(tlelt->resdom->restype);
+
+               sortlist = lappend(sortlist, sortcl);
+             }
+       }
+
+    }
+    
+    return sortlist;
+}
+
+/*
+ ** HandleNestedDots --
+ **    Given a nested dot expression (i.e. (relation func ... attr), build up
+ ** a tree with of Iter and Func nodes.
+ */
+static Node*
+handleNestedDots(ParseState *pstate, Attr *attr, int *curr_resno)
+{
+    List *mutator_iter;
+    Node *retval = NULL;
+    
+    if (attr->paramNo != NULL) {
+       Param *param = (Param *)transformExpr(pstate, (Node*)attr->paramNo);
+
+       retval = 
+           ParseFunc(pstate, strVal(lfirst(attr->attrs)),
+                     lcons(param, NIL),
+                     curr_resno);
+    } else {
+       Ident *ident = makeNode(Ident);
+
+       ident->name = attr->relname;
+       ident->isRel = TRUE;
+       retval =
+           ParseFunc(pstate, strVal(lfirst(attr->attrs)),
+                     lcons(ident, NIL),
+                     curr_resno);
+    }
+    
+    foreach (mutator_iter, lnext(attr->attrs)) {
+       retval = ParseFunc(pstate,strVal(lfirst(mutator_iter)), 
+                          lcons(retval, NIL),
+                          curr_resno);
+    }
+    
+    return(retval);
+}
+
+/*
+ ** make_arguments --
+ **   Given the number and types of arguments to a function, and the 
+ **   actual arguments and argument types, do the necessary typecasting.
+ */
+static void
+make_arguments(int nargs,
+              List *fargs,
+              Oid *input_typeids,
+              Oid *function_typeids)
+{
+    /*
+     * there are two ways an input typeid can differ from a function typeid :
+     * either the input type inherits the function type, so no typecasting is
+     * necessary, or the input type can be typecast into the function type.
+     * right now, we only typecast unknowns, and that is all we check for.
+     */
+    
+    List *current_fargs;
+    int i;
+    
+    for (i=0, current_fargs = fargs;
+        i<nargs;
+        i++, current_fargs = lnext(current_fargs)) {
+
+       if (input_typeids[i] == UNKNOWNOID && function_typeids[i] != 0) {
+           lfirst(current_fargs) =
+               parser_typecast2(lfirst(current_fargs),
+                                input_typeids[i],
+                                get_id_type(function_typeids[i]),
+                                -1);
+       }
+    }
+}
+
+/*
+ ** setup_tlist --
+ **     Build a tlist that says which attribute to project to.
+ **     This routine is called by ParseFunc() to set up a target list
+ **     on a tuple parameter or return value.  Due to a bug in 4.0,
+ **     it's not possible to refer to system attributes in this case.
+ */
+static List *
+setup_tlist(char *attname, Oid relid)
+{
+    TargetEntry *tle;
+    Resdom *resnode;
+    Var *varnode;
+    Oid typeid;
+    int attno;
+    
+    attno = get_attnum(relid, attname);
+    if (attno < 0)
+       elog(WARN, "cannot reference attribute %s of tuple params/return values for functions", attname);
+    
+    typeid = find_atttype(relid, attname);
+    resnode = makeResdom(1,
+                        typeid,
+                        tlen(get_id_type(typeid)),
+                        get_attname(relid, attno),
+                        0,
+                        (Oid)0,
+                        0);
+    varnode = makeVar(-1, attno, typeid, -1, attno);
+
+    tle = makeNode(TargetEntry);
+    tle->resdom = resnode;
+    tle->expr = (Node*)varnode;
+    return (lcons(tle, NIL));
+}
+
+/*
+ ** setup_base_tlist --
+ **    Build a tlist that extracts a base type from the tuple
+ **    returned by the executor.
+ */
+static List *
+setup_base_tlist(Oid typeid)
+{
+    TargetEntry *tle;
+    Resdom *resnode;
+    Var *varnode;
+    
+    resnode = makeResdom(1,
+                        typeid,
+                        tlen(get_id_type(typeid)),
+                        "<noname>",
+                        0,
+                        (Oid)0,
+                        0);
+    varnode = makeVar(-1, 1, typeid, -1, 1);
+    tle = makeNode(TargetEntry);
+    tle->resdom = resnode;
+    tle->expr = (Node*)varnode;
+
+    return (lcons(tle, NIL));
+}
+
+/*
+ * ParseComplexProjection -
+ *    handles function calls with a single argument that is of complex type.
+ *    This routine returns NULL if it can't handle the projection (eg. sets).
+ */
+static Node *
+ParseComplexProjection(ParseState *pstate,
+                      char *funcname,
+                      Node *first_arg,
+                      bool *attisset)
+{
+    Oid argtype;
+    Oid argrelid;
+    Name relname;
+    Relation rd;
+    Oid relid;
+    int attnum;
+
+    switch (nodeTag(first_arg)) {
+    case T_Iter:
+       {
+           Func *func;
+           Iter *iter;
+
+           iter = (Iter*)first_arg;        
+           func = (Func *)((Expr*)iter->iterexpr)->oper;
+           argtype = funcid_get_rettype(func->funcid);
+           argrelid = typeid_get_relid(argtype);
+           if (argrelid &&
+               ((attnum = get_attnum(argrelid, funcname))
+               != InvalidAttrNumber)) {
+               
+               /* the argument is a function returning a tuple, so funcname
+                  may be a projection */
+
+               /* add a tlist to the func node and return the Iter */
+               rd = heap_openr(tname(get_id_type(argtype)));
+               if (RelationIsValid(rd)) {
+                   relid = RelationGetRelationId(rd);
+                   relname = RelationGetRelationName(rd);
+                   heap_close(rd);
+               }
+               if (RelationIsValid(rd)) {
+                   func->func_tlist =
+                       setup_tlist(funcname, argrelid);
+                   iter->itertype = att_typeid(rd,attnum);
+                   return ((Node*)iter);
+               }else {
+                   elog(WARN, 
+                        "Function %s has bad returntype %d", 
+                        funcname, argtype);
+               }
+           }else { 
+               /* drop through */
+               ;
+           }
+           break;
+       }
+    case T_Var:
+       {
+           /*
+            * The argument is a set, so this is either a projection
+            * or a function call on this set.
+            */
+           *attisset = true;
+           break;
+       }
+    case T_Expr:
+       {
+           Expr *expr = (Expr*)first_arg;
+           Func *funcnode;
+
+           if (expr->opType != FUNC_EXPR)
+               break;
+
+           funcnode= (Func *) expr->oper;
+           argtype = funcid_get_rettype(funcnode->funcid);
+           argrelid = typeid_get_relid(argtype);
+           /*
+            * the argument is a function returning a tuple, so funcname
+            * may be a projection
+            */
+           if (argrelid &&
+               (attnum = get_attnum(argrelid, funcname)) 
+               != InvalidAttrNumber) {
+
+               /* add a tlist to the func node */
+               rd = heap_openr(tname(get_id_type(argtype)));
+               if (RelationIsValid(rd)) {
+                   relid = RelationGetRelationId(rd);
+                   relname = RelationGetRelationName(rd);
+                   heap_close(rd);
+               }
+               if (RelationIsValid(rd)) {
+                   Expr *newexpr;
+                   
+                   funcnode->func_tlist =
+                       setup_tlist(funcname, argrelid);
+                   funcnode->functype = att_typeid(rd,attnum);
+
+                   newexpr = makeNode(Expr);
+                   newexpr->typeOid = funcnode->functype;
+                   newexpr->opType = FUNC_EXPR;
+                   newexpr->oper = (Node *)funcnode;
+                   newexpr->args = lcons(first_arg, NIL);
+
+                   return ((Node*)newexpr);
+               }
+           
+           }
+
+           elog(WARN, "Function %s has bad returntype %d", 
+               funcname, argtype);
+           break;
+       }
+    case T_Param:
+       {
+           Param *param = (Param*)first_arg;
+           /*
+            * If the Param is a complex type, this could be a projection
+            */
+           rd = heap_openr(tname(get_id_type(param->paramtype)));
+           if (RelationIsValid(rd)) {
+               relid = RelationGetRelationId(rd);
+               relname = RelationGetRelationName(rd);
+               heap_close(rd);
+           }
+           if (RelationIsValid(rd) && 
+               (attnum = get_attnum(relid, funcname))
+               != InvalidAttrNumber) {
+
+               param->paramtype = att_typeid(rd, attnum);
+               param->param_tlist = setup_tlist(funcname, relid);
+               return ((Node*)param);
+           }
+           break;
+       }
+    default:
+       break;
+    }
+
+    return NULL;
+}
+                      
+static Node *
+ParseFunc(ParseState *pstate, char *funcname, List *fargs, int *curr_resno)
+{
+    Oid rettype = (Oid)0;
+    Oid argrelid;
+    Oid funcid = (Oid)0;
+    List *i = NIL;
+    Node *first_arg= NULL;
+    char *relname, *oldname;
+    Relation rd;
+    Oid relid;
+    int nargs;
+    Func *funcnode;
+    Oid oid_array[8];
+    Oid *true_oid_array;
+    Node *retval;
+    bool retset;
+    bool exists;
+    bool attisset = false;
+    Oid toid;
+    Expr *expr;
+
+    if (fargs) {
+       first_arg = lfirst(fargs);
+       if (first_arg == NULL)
+           elog (WARN,"function %s does not allow NULL input",funcname);
+    }
+    
+    /*
+     ** check for projection methods: if function takes one argument, and 
+     ** that argument is a relation, param, or PQ function returning a complex 
+     ** type, then the function could be a projection.
+     */
+    if (length(fargs) == 1) {
+       if (nodeTag(first_arg)==T_Ident && ((Ident*)first_arg)->isRel) {
+           Ident *ident = (Ident*)first_arg;
+
+           /*
+            * first arg is a relation. This could be a projection.
+            */
+           relname = ident->name;
+           if (RangeTablePosn(pstate->p_rtable, relname)== 0) {
+               RangeTblEntry *ent;
+
+               ent =
+                   makeRangeTableEntry(relname,
+                                       FALSE, NULL, relname);
+               pstate->p_rtable = lappend(pstate->p_rtable, ent);
+           }
+           oldname = relname;
+           relname = VarnoGetRelname(pstate, 
+                                     RangeTablePosn(pstate->p_rtable,
+                                                    oldname));
+           rd = heap_openr(relname);
+           relid = RelationGetRelationId(rd);
+           heap_close(rd);
+           /* If the attr isn't a set, just make a var for it.  If
+            * it is a set, treat it like a function and drop through.
+            */
+           if (get_attnum(relid, funcname) != InvalidAttrNumber) {
+               int dummyTypeId;
+
+               return
+                   ((Node*)make_var(pstate,
+                                    oldname,
+                                    funcname,
+                                    &dummyTypeId));
+           } else {
+               /* drop through - attr is a set */
+               ;
+           }
+       } else if (ISCOMPLEX(exprType(first_arg))) {
+           /*
+            * Attempt to handle projection of a complex argument. If
+            * ParseComplexProjection can't handle the projection, we
+            * have to keep going.
+            */
+           retval = ParseComplexProjection(pstate,
+                                           funcname,
+                                           first_arg,
+                                           &attisset);
+           if (attisset) {
+               toid = exprType(first_arg);
+               rd = heap_openr(tname(get_id_type(toid)));
+               if (RelationIsValid(rd)) {
+                   relname = RelationGetRelationName(rd)->data;
+                   heap_close(rd);
+               } else
+                   elog(WARN,
+                        "Type %s is not a relation type",
+                        tname(get_id_type(toid)));
+               argrelid = typeid_get_relid(toid);
+               /* A projection contains either an attribute name or the
+                * word "all".
+                */
+               if ((get_attnum(argrelid, funcname) == InvalidAttrNumber) 
+                   && strcmp(funcname, "all")) {
+                   elog(WARN, "Functions on sets are not yet supported");
+               }
+           }
+               
+           if (retval)
+               return retval;
+       } else {
+           /*
+            * Parsing aggregates.
+            */
+           Oid basetype;
+           /* the aggregate count is a special case,
+              ignore its base type.  Treat it as zero */
+           if (strcmp(funcname, "count") == 0)
+               basetype = 0;
+           else
+               basetype = exprType(lfirst(fargs));
+           if (SearchSysCacheTuple(AGGNAME, 
+                                   PointerGetDatum(funcname), 
+                                   ObjectIdGetDatum(basetype),
+                                   0, 0)) {
+               Aggreg *aggreg = ParseAgg(funcname, basetype, lfirst(fargs));
+
+               AddAggToParseState(pstate, aggreg);
+               return (Node*)aggreg;
+           }
+       }
+    }
+    
+    
+    /*
+     ** If we dropped through to here it's really a function (or a set, which
+     ** is implemented as a function.)
+     ** extract arg type info and transform relation name arguments into
+     ** varnodes of the appropriate form.
+     */
+    memset(&oid_array[0], 0, 8 * sizeof(Oid)); 
+
+    nargs=0;
+    foreach ( i , fargs ) {
+       int vnum;
+       Node *pair = lfirst(i);
+           
+       if (nodeTag(pair)==T_Ident && ((Ident*)pair)->isRel) {
+           /*
+            * a relation
+            */
+           relname = ((Ident*)pair)->name;
+                   
+           /* get the range table entry for the var node */
+           vnum = RangeTablePosn(pstate->p_rtable, relname);
+           if (vnum == 0) {
+               pstate->p_rtable =
+                   lappend(pstate->p_rtable ,
+                            makeRangeTableEntry(relname, FALSE,
+                                                NULL, relname));
+               vnum = RangeTablePosn (pstate->p_rtable, relname);
+           }
+                   
+           /*
+            *  We have to do this because the relname in the pair
+            *  may have been a range table variable name, rather
+            *  than a real relation name.
+            */
+           relname = VarnoGetRelname(pstate, vnum);
+                   
+           rd = heap_openr(relname);
+           relid = RelationGetRelationId(rd);
+           heap_close(rd);
+           
+           /*
+            *  for func(relname), the param to the function
+            *  is the tuple under consideration.  we build a special
+            *  VarNode to reflect this -- it has varno set to the 
+            *  correct range table entry, but has varattno == 0 to 
+            *  signal that the whole tuple is the argument.
+            */
+           toid = typeid(type(relname));                  
+           /* replace it in the arg list */
+           lfirst(fargs) =
+               makeVar(vnum, 0, toid, vnum, 0);
+       }else if (!attisset) { /* set functions don't have parameters */
+         /* any functiona args which are typed "unknown", but aren't
+            constants, we don't know what to do with, because we
+            can't cast them    - jolly*/
+         if (exprType(pair) == UNKNOWNOID &&
+              !IsA(pair, Const))
+             {
+                 elog(WARN, "ParseFunc: no function named %s that takes in an unknown type as argument #%d", funcname, nargs);
+             }
+         else
+             toid = exprType(pair);
+       }
+           
+       oid_array[nargs++] = toid;
+    }
+    
+    /*
+     *  func_get_detail looks up the function in the catalogs, does
+     *  disambiguation for polymorphic functions, handles inheritance,
+     *  and returns the funcid and type and set or singleton status of
+     *  the function's return value.  it also returns the true argument
+     *  types to the function.  if func_get_detail returns true,
+     *  the function exists.  otherwise, there was an error.
+     */
+    if (attisset) { /* we know all of these fields already */
+       /* We create a funcnode with a placeholder function SetEval.
+        * SetEval() never actually gets executed.  When the function
+        * evaluation routines see it, they use the funcid projected
+        * out from the relation as the actual function to call.
+        * Example:  retrieve (emp.mgr.name)
+        * The plan for this will scan the emp relation, projecting
+        * out the mgr attribute, which is a funcid.  This function
+        * is then called (instead of SetEval) and "name" is projected
+        * from its result.
+        */
+       funcid = SetEvalRegProcedure;
+       rettype = toid;
+       retset = true;
+       true_oid_array = oid_array;
+       exists = true;
+    } else {
+       exists = func_get_detail(funcname, nargs, oid_array, &funcid,
+                                &rettype, &retset, &true_oid_array);
+    }
+    
+    if (!exists)
+       elog(WARN, "no such attribute or function %s", funcname);
+    
+    /* got it */
+    funcnode = makeNode(Func);
+    funcnode->funcid = funcid;
+    funcnode->functype = rettype;
+    funcnode->funcisindex = false;
+    funcnode->funcsize = 0;
+    funcnode->func_fcache = NULL;
+    funcnode->func_tlist = NIL;
+    funcnode->func_planlist = NIL;
+    
+    /* perform the necessary typecasting */
+    make_arguments(nargs, fargs, oid_array, true_oid_array);
+    
+    /*
+     *  for functions returning base types, we want to project out the
+     *  return value.  set up a target list to do that.  the executor
+     *  will ignore these for c functions, and do the right thing for
+     *  postquel functions.
+     */
+    
+    if (typeid_get_relid(rettype) == InvalidOid)
+       funcnode->func_tlist = setup_base_tlist(rettype);
+    
+    /* For sets, we want to make a targetlist to project out this
+     * attribute of the set tuples.
+     */
+    if (attisset) {
+       if (!strcmp(funcname, "all")) {
+           funcnode->func_tlist =
+               expandAll(pstate, (char*)relname, curr_resno);
+       } else {
+           funcnode->func_tlist = setup_tlist(funcname,argrelid);
+           rettype = find_atttype(argrelid, funcname);
+       }
+    }
+
+    expr = makeNode(Expr);
+    expr->typeOid = rettype;
+    expr->opType = FUNC_EXPR;
+    expr->oper = (Node *)funcnode;
+    expr->args = fargs;
+    retval = (Node*)expr;
+    
+    /*
+     *  if the function returns a set of values, then we need to iterate
+     *  over all the returned values in the executor, so we stick an
+     *  iter node here.  if it returns a singleton, then we don't need
+     *  the iter node.
+     */
+    
+    if (retset) {
+       Iter *iter = makeNode(Iter);
+       iter->itertype = rettype;
+       iter->iterexpr = retval;
+       retval = (Node*)iter;
+    }
+    
+    return(retval);
+}
+
+/*
+ * returns (relname) if found, NIL if not a column
+ */
+static char*
+ParseColumnName(ParseState *pstate, char *name, bool *isRelName)
+{
+    List *et;
+    Relation rd;
+    List *rtable;
+
+    /*
+     * see if it is a relation name. If so, leave it as it is
+     */
+    if (RangeTablePosn(pstate->p_rtable, name)!=0) {
+       *isRelName = TRUE;
+       return NULL;
+    }
+
+    if (pstate->p_query_is_rule) {
+       rtable = lnext(lnext(pstate->p_rtable));
+    } else {
+       rtable = pstate->p_rtable;
+    }
+    /*
+     * search each relation in the FROM list and see if we have a match
+     */
+    foreach(et, rtable) {
+       RangeTblEntry *rte = lfirst(et);
+       char *relname= rte->relname;
+        char *refname= rte->refname;
+       Oid relid;
+
+       rd= heap_openr(relname);
+       relid = RelationGetRelationId(rd);
+       heap_close(rd);
+       if (get_attnum(relid, name) != InvalidAttrNumber) {
+           /* found */
+           *isRelName = FALSE;
+           return refname;
+       }
+
+    }
+
+    /* attribute not found */
+    *isRelName = FALSE;
+    return NULL;
+}
+
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+/*
+ * AddAggToParseState -
+ *    add the aggregate to the list of unique aggregates in pstate. 
+ *
+ * SIDE EFFECT: aggno in target list entry will be modified
+ */
+static void
+AddAggToParseState(ParseState *pstate, Aggreg *aggreg)
+{
+    List *ag;
+    int i;
+
+    /*
+     * see if we have the aggregate already (we only need to record
+     * the aggregate once)
+     */
+    i = 0;
+    foreach(ag, pstate->p_aggs) {
+       Aggreg *a = lfirst(ag);
+       
+       if (!strcmp(a->aggname, aggreg->aggname) &&
+           equal(a->target, aggreg->target)) {
+
+           /* fill in the aggno and we're done */
+           aggreg->aggno = i;
+           return;
+       }
+       i++;
+    }
+
+    /* not found, new aggregate */
+    aggreg->aggno = i;
+    pstate->p_numAgg++;
+    pstate->p_aggs = lappend(pstate->p_aggs, aggreg);
+    return;
+}
+
+/*
+ * finalizeAggregates -
+ *    fill in qry_aggs from pstate. Also checks to make sure that aggregates
+ *    are used in the proper place.
+ */
+static void
+finalizeAggregates(ParseState *pstate, Query *qry)
+{    
+    List *l;
+    int i;
+
+    parseCheckAggregates(pstate, qry);
+       
+    qry->qry_numAgg = pstate->p_numAgg;
+    qry->qry_aggs =
+       (Aggreg **)palloc(sizeof(Aggreg *) * qry->qry_numAgg);
+    i = 0;
+    foreach(l, pstate->p_aggs) {
+       qry->qry_aggs[i++] = (Aggreg*)lfirst(l);
+    }
+}
+
+/*    
+ * contain_agg_clause--
+ *    Recursively find aggreg nodes from a clause.
+ *    
+ *    Returns true if any aggregate found.
+ */
+static bool
+contain_agg_clause(Node *clause)
+{
+    if (clause==NULL) 
+       return FALSE;
+    else if (IsA(clause,Aggreg))
+       return TRUE;
+    else if (IsA(clause,Iter))
+       return contain_agg_clause(((Iter*)clause)->iterexpr);
+    else if (single_node(clause)) 
+       return FALSE;
+    else if (or_clause(clause)) {
+       List *temp;
+
+       foreach (temp, ((Expr*)clause)->args) {
+           if (contain_agg_clause(lfirst(temp)))
+               return TRUE;
+       }
+       return FALSE;
+    } else if (is_funcclause (clause)) {
+       List *temp;
+
+       foreach(temp, ((Expr *)clause)->args) {
+           if (contain_agg_clause(lfirst(temp)))
+               return TRUE;
+       }
+       return FALSE;
+    } else if (IsA(clause,ArrayRef)) {
+       List *temp;
+
+       foreach(temp, ((ArrayRef*)clause)->refupperindexpr)  {
+           if (contain_agg_clause(lfirst(temp)))
+               return TRUE;
+       }
+       foreach(temp, ((ArrayRef*)clause)->reflowerindexpr) {
+           if (contain_agg_clause(lfirst(temp)))
+               return TRUE;
+       }
+       if (contain_agg_clause(((ArrayRef*)clause)->refexpr))
+           return TRUE;
+       if (contain_agg_clause(((ArrayRef*)clause)->refassgnexpr))
+           return TRUE;
+       return FALSE;
+    } else if (not_clause(clause))
+       return contain_agg_clause((Node*)get_notclausearg((Expr*)clause));
+    else if (is_opclause(clause))
+       return (contain_agg_clause((Node*)get_leftop((Expr*)clause)) ||
+               contain_agg_clause((Node*)get_rightop((Expr*)clause)));
+
+    return FALSE;
+}
+
+/*
+ * exprIsAggOrGroupCol -
+ *    returns true if the expression does not contain non-group columns.
+ */
+static bool
+exprIsAggOrGroupCol(Node *expr, List *groupClause)
+{
+    if (expr==NULL)
+       return TRUE;
+    else if (IsA(expr,Const))
+       return TRUE;
+    else if (IsA(expr,Var)) {
+       List *gl;
+       Var *var = (Var*)expr;
+       /*
+        * only group columns are legal
+        */
+       foreach (gl, groupClause) {
+           GroupClause *grpcl = lfirst(gl);
+           if ((grpcl->grpAttr->varno == var->varno) &&
+               (grpcl->grpAttr->varattno == var->varattno))
+               return TRUE;
+       }
+       return FALSE;
+    } else if (IsA(expr,Aggreg))
+       /* aggregates can take group column or non-group column as argument,
+          no further check necessary. */
+       return TRUE;
+    else if (IsA(expr,Expr)) {
+       List *temp;
+
+       foreach (temp, ((Expr*)expr)->args) {
+           if (!exprIsAggOrGroupCol(lfirst(temp),groupClause))
+               return FALSE;
+       }
+       return TRUE;
+    }
+
+    return FALSE;
+}
+
+/*
+ * parseCheckAggregates -
+ *    this should really be done earlier but the current grammar
+ *    cannot differentiate functions from aggregates. So we have do check
+ *    here when the target list and the qualifications are finalized.
+ */
+static void
+parseCheckAggregates(ParseState *pstate, Query *qry)
+{
+    List *tl;
+    Assert(pstate->p_numAgg > 0);
+
+    /*
+     * aggregates never appear in WHERE clauses. (we have to check where
+     * clause first because if there is an aggregate, the check for
+     * non-group column in target list may fail.)
+     */
+    if (contain_agg_clause(qry->qual))
+       elog(WARN, "parser: aggregates not allowed in WHERE clause");
+
+    /*
+     * the target list can only contain aggregates, group columns and
+     * functions thereof.
+     */
+    foreach (tl, qry->targetList) {
+       TargetEntry *tle = lfirst(tl);
+       if (!exprIsAggOrGroupCol(tle->expr, qry->groupClause))
+           elog(WARN,
+                "parser: illegal use of aggregates or non-group column in target list");
+    }
+       
+    /*
+     * the expression specified in the HAVING clause has the same restriction
+     * as those in the target list.
+     */
+    if (!exprIsAggOrGroupCol(qry->havingQual, qry->groupClause))
+       elog(WARN,
+            "parser: illegal use of aggregates or non-group column in HAVING clause");
+    
+    return;
+}
+
+
diff --git a/src/backend/parser/catalog_utils.c b/src/backend/parser/catalog_utils.c
new file mode 100644 (file)
index 0000000..fb7c1d8
--- /dev/null
@@ -0,0 +1,1470 @@
+/*-------------------------------------------------------------------------
+ *
+ * catalog_utils.c--
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "lib/dllist.h"
+#include "utils/datum.h"
+
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "fmgr.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+#include "utils/syscache.h"
+#include "catalog/catname.h"
+
+#include "catalog_utils.h"
+#include "catalog/pg_inherits.h"
+#include "catalog/pg_type.h"
+#include "catalog/indexing.h"
+#include "catalog/catname.h"
+
+#include "access/skey.h"
+#include "access/relscan.h"
+#include "access/tupdesc.h"
+#include "access/htup.h"
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "access/itup.h"
+#include "access/tupmacs.h"
+
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "utils/lsyscache.h"
+#include "storage/lmgr.h"
+
+struct {
+    char *field;
+    int code;
+} special_attr[] = {
+    { "ctid", SelfItemPointerAttributeNumber },
+    { "oid", ObjectIdAttributeNumber },
+    { "xmin", MinTransactionIdAttributeNumber },
+    { "cmin", MinCommandIdAttributeNumber },
+    { "xmax", MaxTransactionIdAttributeNumber },
+    { "cmax", MaxCommandIdAttributeNumber },
+    { "chain", ChainItemPointerAttributeNumber },
+    { "anchor", AnchorItemPointerAttributeNumber },
+    { "tmin", MinAbsoluteTimeAttributeNumber },
+    { "tmax", MaxAbsoluteTimeAttributeNumber },
+    { "vtype", VersionTypeAttributeNumber }
+};
+
+#define SPECIALS (sizeof(special_attr)/sizeof(*special_attr))
+  
+static char *attnum_type[SPECIALS] = {
+    "tid",
+    "oid",
+    "xid",
+    "cid",
+    "xid",
+    "cid",
+    "tid",
+    "tid",
+    "abstime",
+    "abstime",
+    "char"
+  };
+
+#define        MAXFARGS 8              /* max # args to a c or postquel function */
+
+static Oid **argtype_inherit();
+static Oid **genxprod();
+
+static int findsupers(Oid relid, Oid **supervec);
+/*
+ *  This structure is used to explore the inheritance hierarchy above
+ *  nodes in the type tree in order to disambiguate among polymorphic
+ *  functions.
+ */
+
+typedef struct _InhPaths {
+    int                nsupers;        /* number of superclasses */
+    Oid        self;           /* this class */
+    Oid        *supervec;      /* vector of superclasses */
+} InhPaths;
+
+/*
+ *  This structure holds a list of possible functions or operators that
+ *  agree with the known name and argument types of the function/operator.
+ */
+typedef struct _CandidateList {
+    Oid *args;
+    struct _CandidateList *next;
+} *CandidateList;
+
+/* check to see if a type id is valid,
+ * returns true if it is. By using this call before calling 
+ * get_id_type or get_id_typname, more meaningful error messages
+ * can be produced because the caller typically has more context of
+ *  what's going on                 - jolly
+ */
+bool
+check_typeid(long id)
+{
+    return (SearchSysCacheTuple(TYPOID, 
+                               ObjectIdGetDatum(id),
+                               0,0,0) != NULL);
+}
+
+
+/* return a Type structure, given an typid */
+Type
+get_id_type(long id)
+{
+    HeapTuple tup;
+    
+    if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id),
+                                   0,0,0))) { 
+       elog ( WARN, "type id lookup of %d failed", id);
+       return(NULL);
+    }
+    return((Type) tup);
+}
+
+/* return a type name, given a typeid */
+char*
+get_id_typname(long id)
+{
+    HeapTuple tup;
+    TypeTupleForm typetuple;
+    
+    if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id),
+                                   0,0,0))) {
+       elog ( WARN, "type id lookup of %d failed", id);
+       return(NULL);
+    }
+    typetuple = (TypeTupleForm)GETSTRUCT(tup);
+    return (typetuple->typname).data;
+}
+
+/* return a Type structure, given type name */
+Type
+type(char *s)
+{
+    HeapTuple tup;
+    
+    if (s == NULL) {
+       elog ( WARN , "type(): Null type" );
+    }
+    
+    if (!(tup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(s), 0,0,0))) {
+       elog (WARN , "type name lookup of %s failed", s);
+    }
+    return((Type) tup);
+}
+
+/* given attribute id, return type of that attribute */
+/* XXX Special case for pseudo-attributes is a hack */
+Oid
+att_typeid(Relation rd, int attid)
+{
+    
+    if (attid < 0) {
+       return(typeid(type(attnum_type[-attid-1])));
+    }
+    /* -1 because varattno (where attid comes from) returns one
+       more than index */
+    return(rd->rd_att->attrs[attid-1]->atttypid);
+}
+
+
+int
+att_attnelems(Relation rd, int attid)
+{
+    return(rd->rd_att->attrs[attid-1]->attnelems);
+}
+
+/* given type, return the type OID */
+Oid
+typeid(Type tp)
+{
+    if (tp == NULL) {
+       elog ( WARN , "typeid() called with NULL type struct");
+    }
+    return(tp->t_oid);
+}
+
+/* given type (as type struct), return the length of type */
+int16
+tlen(Type t)
+{
+    TypeTupleForm    typ;
+    
+    typ = (TypeTupleForm)GETSTRUCT(t);
+    return(typ->typlen);
+}
+
+/* given type (as type struct), return the value of its 'byval' attribute.*/
+bool
+tbyval(Type t)
+{
+    TypeTupleForm    typ;
+    
+    typ = (TypeTupleForm)GETSTRUCT(t);
+    return(typ->typbyval);
+}
+
+/* given type (as type struct), return the name of type */
+char*
+tname(Type t)
+{
+    TypeTupleForm    typ;
+    
+    typ = (TypeTupleForm)GETSTRUCT(t);
+    return (typ->typname).data;
+}
+
+/* given type (as type struct), return wether type is passed by value */
+int
+tbyvalue(Type t)
+{
+    TypeTupleForm typ;
+    
+    typ = (TypeTupleForm) GETSTRUCT(t);
+    return(typ->typbyval);
+}
+
+/* given a type, return its typetype ('c' for 'c'atalog types) */
+char
+typetypetype(Type t)
+{
+    TypeTupleForm typ;
+    
+    typ = (TypeTupleForm) GETSTRUCT(t);
+    return(typ->typtype);
+}
+
+/* given operator, return the operator OID */
+Oid
+oprid(Operator op)
+{
+    return(op->t_oid);
+}
+
+/*
+ *  given opname, leftTypeId and rightTypeId,
+ *  find all possible (arg1, arg2) pairs for which an operator named
+ *  opname exists, such that leftTypeId can be coerced to arg1 and
+ *  rightTypeId can be coerced to arg2
+ */
+static int
+binary_oper_get_candidates(char *opname,
+                          int leftTypeId,
+                          int rightTypeId,
+                          CandidateList *candidates)
+{
+    CandidateList      current_candidate;
+    Relation            pg_operator_desc;
+    HeapScanDesc        pg_operator_scan;
+    HeapTuple           tup;
+    OperatorTupleForm  oper;
+    Buffer              buffer;
+    int                        nkeys;
+    int                        ncandidates = 0;
+    ScanKeyData        opKey[3];
+    
+    *candidates = NULL;
+
+    ScanKeyEntryInitialize(&opKey[0], 0,
+                          Anum_pg_operator_oprname,
+                          NameEqualRegProcedure,
+                          NameGetDatum(opname));
+
+    ScanKeyEntryInitialize(&opKey[1], 0,
+                          Anum_pg_operator_oprkind,
+                          CharacterEqualRegProcedure,
+                          CharGetDatum('b'));
+
+    
+    if (leftTypeId == UNKNOWNOID) {
+        if (rightTypeId == UNKNOWNOID) {
+           nkeys = 2;
+        } else {
+           nkeys = 3;
+
+           ScanKeyEntryInitialize(&opKey[2], 0,
+                                  Anum_pg_operator_oprright,
+                                  ObjectIdEqualRegProcedure,
+                                  ObjectIdGetDatum(rightTypeId));
+        }
+    } else if (rightTypeId == UNKNOWNOID) {
+       nkeys = 3;
+       
+       ScanKeyEntryInitialize(&opKey[2], 0,
+                              Anum_pg_operator_oprleft,
+                              ObjectIdEqualRegProcedure,
+                              ObjectIdGetDatum(leftTypeId));
+    } else {
+       /* currently only "unknown" can be coerced */
+        return 0;
+    }
+    
+    pg_operator_desc = heap_openr(OperatorRelationName);
+    pg_operator_scan = heap_beginscan(pg_operator_desc,
+                                     0,
+                                     SelfTimeQual,
+                                     nkeys,
+                                     opKey);
+    
+    do {
+        tup = heap_getnext(pg_operator_scan, 0, &buffer);
+       if (HeapTupleIsValid(tup)) {
+           current_candidate = (CandidateList)palloc(sizeof(struct _CandidateList));
+           current_candidate->args = (Oid *)palloc(2 * sizeof(Oid));
+           
+           oper = (OperatorTupleForm)GETSTRUCT(tup);
+           current_candidate->args[0] = oper->oprleft;
+           current_candidate->args[1] = oper->oprright;
+           current_candidate->next = *candidates;
+           *candidates = current_candidate;
+           ncandidates++;
+           ReleaseBuffer(buffer);
+       }
+    } while(HeapTupleIsValid(tup));
+    
+    heap_endscan(pg_operator_scan);
+    heap_close(pg_operator_desc);
+    
+    return ncandidates;
+}
+
+/*
+ * equivalentOpersAfterPromotion -
+ *    checks if a list of candidate operators obtained from
+ *    binary_oper_get_candidates() contain equivalent operators. If
+ *    this routine is called, we have more than 1 candidate and need to
+ *    decided whether to pick one of them. This routine returns true if
+ *    the all the candidates operate on the same data types after
+ *    promotion (int2, int4, float4 -> float8).
+ */
+static bool
+equivalentOpersAfterPromotion(CandidateList candidates)
+{
+    CandidateList result;
+    CandidateList promotedCandidates = NULL;
+    int leftarg, rightarg;
+
+    for (result = candidates; result != NULL; result = result->next) {
+       CandidateList c;
+       c = (CandidateList)palloc(sizeof(*c));
+       c->args = (Oid *)palloc(2 * sizeof(Oid));
+       switch (result->args[0]) {
+       case FLOAT4OID:
+       case INT4OID:
+       case INT2OID:
+           c->args[0] = FLOAT8OID;
+           break;
+       default:
+           c->args[0] = result->args[0];
+           break;
+       }
+       switch (result->args[1]) {
+       case FLOAT4OID:
+       case INT4OID:
+       case INT2OID:
+           c->args[1] = FLOAT8OID;
+           break;
+       default:
+           c->args[1] = result->args[1];
+           break;
+       }
+       c->next = promotedCandidates;
+       promotedCandidates = c;
+    }
+
+    /* if we get called, we have more than 1 candidates so we can do the
+       following safely */
+    leftarg = promotedCandidates->args[0];
+    rightarg = promotedCandidates->args[1];
+
+    for (result=promotedCandidates->next; result!=NULL; result=result->next) {
+       if (result->args[0]!=leftarg || result->args[1]!=rightarg)
+           /*
+            * this list contains operators that operate on different
+            * data types even after promotion. Hence we can't decide on
+            * which one to pick. The user must do explicit type casting.
+            */
+           return FALSE;
+    }
+
+    /* all the candidates are equivalent in the following sense: they operate
+       on equivalent data types and picking any one of them is as good. */
+    return TRUE;
+}
+       
+
+/*
+ *  given a choice of argument type pairs for a binary operator,
+ *  try to choose a default pair
+ */
+static CandidateList
+binary_oper_select_candidate(int arg1,
+                            int arg2,
+                            CandidateList candidates)
+{
+    CandidateList result;
+
+    /*
+     * if both are "unknown", there is no way to select a candidate
+     *
+     * current wisdom holds that the default operator should be one
+     * in which both operands have the same type (there will only
+     * be one such operator)
+     *
+     * 7.27.93 - I have decided not to do this; it's too hard to
+     * justify, and it's easy enough to typecast explicitly -avi
+     * [the rest of this routine were commented out since then -ay]
+     */
+    
+    if (arg1 == UNKNOWNOID && arg2 == UNKNOWNOID)
+       return (NULL);
+
+    /*
+     * 6/23/95 - I don't complete agree with avi. In particular, casting
+     *    floats is a pain for users. Whatever the rationale behind not doing
+     *    this is, I need the following special case to work.
+     *
+     *    In the WHERE clause of a query, if a float is specified without
+     *    quotes, we treat it as float8. I added the float48* operators so
+     *    that we can operate on float4 and float8. But now we have more
+     *    than one matching operator if the right arg is unknown (eg. float
+     *    specified with quotes). This break some stuff in the regression
+     *    test where there are floats in quotes not properly casted. Below
+     *    is the solution. In addition to requiring the operator operates
+     *    on the same type for both operands [as in the code Avi originally
+     *    commented out], we also require that the operators be equivalent
+     *    in some sense. (see equivalentOpersAfterPromotion for details.)
+     *                                                  - ay 6/95
+     */
+    if (!equivalentOpersAfterPromotion(candidates))
+       return NULL;
+
+    /* if we get here, any one will do but we're more picky and require
+       both operands be the same. */
+    for (result = candidates; result != NULL; result = result->next) {
+       if (result->args[0] == result->args[1])
+           return result;
+    }
+
+    return (NULL);
+}
+
+/* Given operator, types of arg1, and arg2, return oper struct */
+/* arg1, arg2 --typeids */
+Operator
+oper(char *op, int arg1, int arg2)
+{
+    HeapTuple tup;
+    CandidateList candidates;
+    int ncandidates;
+
+    if (!(tup = SearchSysCacheTuple(OPRNAME,
+                                   PointerGetDatum(op),
+                                   ObjectIdGetDatum(arg1),
+                                   ObjectIdGetDatum(arg2),
+                                   Int8GetDatum('b')))) {
+       ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates);
+       if (ncandidates == 0) {
+           /*
+            * no operators of the desired types found
+            */
+           op_error(op, arg1, arg2);
+           return(NULL);
+       } else if (ncandidates == 1) {
+           /*
+            * exactly one operator of the desired types found
+            */
+           tup = SearchSysCacheTuple(OPRNAME,
+                                     PointerGetDatum(op),
+                                     ObjectIdGetDatum(candidates->args[0]),
+                                     ObjectIdGetDatum(candidates->args[1]),
+                                     Int8GetDatum('b'));
+           Assert(HeapTupleIsValid(tup));
+       } else {
+           /*
+            * multiple operators of the desired types found
+            */
+           candidates = binary_oper_select_candidate(arg1, arg2, candidates);
+           if (candidates != NULL) {
+               /* we chose one of them */
+               tup = SearchSysCacheTuple(OPRNAME,
+                                         PointerGetDatum(op),
+                                         ObjectIdGetDatum(candidates->args[0]),
+                                         ObjectIdGetDatum(candidates->args[1]),
+                                         Int8GetDatum('b'));
+               Assert(HeapTupleIsValid(tup));
+           } else {
+               Type tp1, tp2;
+               
+               /* we chose none of them */
+               tp1 = get_id_type(arg1);
+               tp2 = get_id_type(arg2);
+               elog(NOTICE, "there is more than one operator %s for types", op);
+               elog(NOTICE, "%s and %s. You will have to retype this query",
+                    tname(tp1), tname(tp2));
+               elog(WARN, "using an explicit cast");
+               
+               return(NULL);
+           }
+       }
+    }
+    return((Operator) tup);
+}
+
+/*
+ *  given opname and typeId, find all possible types for which 
+ *  a right/left unary operator named opname exists,
+ *  such that typeId can be coerced to it
+ */
+static int
+unary_oper_get_candidates(char *op,
+                         int typeId,
+                         CandidateList *candidates,
+                         char rightleft)
+{
+    CandidateList      current_candidate;
+    Relation            pg_operator_desc;
+    HeapScanDesc        pg_operator_scan;
+    HeapTuple           tup;
+    OperatorTupleForm  oper;
+    Buffer              buffer;
+    int                        ncandidates = 0;
+    
+    static ScanKeyData opKey[2] = {
+       { 0, Anum_pg_operator_oprname, NameEqualRegProcedure },
+       { 0, Anum_pg_operator_oprkind, CharacterEqualRegProcedure } };
+    
+    *candidates = NULL;
+    
+    fmgr_info(NameEqualRegProcedure, (func_ptr *) &opKey[0].sk_func,
+             &opKey[0].sk_nargs);
+    opKey[0].sk_argument = NameGetDatum(op);
+    fmgr_info(CharacterEqualRegProcedure, (func_ptr *) &opKey[1].sk_func,
+             &opKey[1].sk_nargs);
+    opKey[1].sk_argument = CharGetDatum(rightleft);
+    
+    /* currently, only "unknown" can be coerced */
+    if (typeId != UNKNOWNOID) {
+        return 0;
+    }
+    
+    pg_operator_desc = heap_openr(OperatorRelationName);
+    pg_operator_scan = heap_beginscan(pg_operator_desc,
+                                     0,
+                                     SelfTimeQual,
+                                     2,
+                                     opKey);
+    
+    do {
+        tup = heap_getnext(pg_operator_scan, 0, &buffer);
+       if (HeapTupleIsValid(tup)) {
+           current_candidate = (CandidateList)palloc(sizeof(struct _CandidateList));
+           current_candidate->args = (Oid *)palloc(sizeof(Oid));
+           
+           oper = (OperatorTupleForm)GETSTRUCT(tup);
+           if (rightleft == 'r')
+               current_candidate->args[0] = oper->oprleft;
+           else
+               current_candidate->args[0] = oper->oprright;
+           current_candidate->next = *candidates;
+           *candidates = current_candidate;
+           ncandidates++;
+           ReleaseBuffer(buffer);
+       }
+    } while(HeapTupleIsValid(tup));
+    
+    heap_endscan(pg_operator_scan);
+    heap_close(pg_operator_desc);
+    
+    return ncandidates;
+}
+
+/* Given unary right-side operator (operator on right), return oper struct */
+/* arg-- type id */
+Operator
+right_oper(char *op, int arg)
+{
+    HeapTuple tup;
+    CandidateList candidates;
+    int ncandidates;
+    
+    /*
+      if (!OpCache) {
+      init_op_cache();
+      }
+      */
+    if (!(tup = SearchSysCacheTuple(OPRNAME,
+                                   PointerGetDatum(op),
+                                   ObjectIdGetDatum(arg),
+                                   ObjectIdGetDatum(InvalidOid),
+                                   Int8GetDatum('r')))) {
+       ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'r');
+       if (ncandidates == 0) {
+           elog ( WARN ,
+                 "Can't find right op: %s for type %d", op, arg );
+           return(NULL);
+       }
+       else if (ncandidates == 1) {
+           tup = SearchSysCacheTuple(OPRNAME,
+                                     PointerGetDatum(op),
+                                     ObjectIdGetDatum(candidates->args[0]),
+                                     ObjectIdGetDatum(InvalidOid),
+                                     Int8GetDatum('r'));
+           Assert(HeapTupleIsValid(tup));
+       }
+       else {
+           elog(NOTICE, "there is more than one right operator %s", op);
+           elog(NOTICE, "you will have to retype this query");
+           elog(WARN, "using an explicit cast");
+           return(NULL);
+       }
+    }
+    return((Operator) tup);
+}
+
+/* Given unary left-side operator (operator on left), return oper struct */
+/* arg--type id */
+Operator
+left_oper(char *op, int arg)
+{
+    HeapTuple tup;
+    CandidateList candidates;
+    int ncandidates;
+    
+    /*
+      if (!OpCache) {
+      init_op_cache();
+      }
+      */
+    if (!(tup = SearchSysCacheTuple(OPRNAME,
+                                   PointerGetDatum(op),
+                                   ObjectIdGetDatum(InvalidOid),
+                                   ObjectIdGetDatum(arg),
+                                   Int8GetDatum('l')))) {
+       ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'l');
+       if (ncandidates == 0) {
+           elog ( WARN ,
+                 "Can't find left op: %s for type %d", op, arg );
+           return(NULL);
+       }
+       else if (ncandidates == 1) {
+           tup = SearchSysCacheTuple(OPRNAME,
+                                     PointerGetDatum(op),
+                                     ObjectIdGetDatum(InvalidOid),
+                                     ObjectIdGetDatum(candidates->args[0]),
+                                     Int8GetDatum('l'));
+           Assert(HeapTupleIsValid(tup));
+       }
+       else {
+           elog(NOTICE, "there is more than one left operator %s", op);
+           elog(NOTICE, "you will have to retype this query");
+           elog(WARN, "using an explicit cast");
+           return(NULL);
+       }
+    }
+    return((Operator) tup);
+}
+
+/* given range variable, return id of variable */
+
+int
+varattno(Relation rd, char *a)
+{
+    int i;
+    
+    for (i = 0; i < rd->rd_rel->relnatts; i++) {
+       if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a)) {
+           return(i+1);
+       }
+    }
+    for (i = 0; i < SPECIALS; i++) {
+       if (!strcmp(special_attr[i].field, a)) {
+           return(special_attr[i].code);
+       }
+    }
+    
+    elog(WARN,"Relation %s does not have attribute %s\n", 
+        RelationGetRelationName(rd), a );
+    return(-1);
+}
+
+/* Given range variable, return whether attribute of this name
+ * is a set.
+ * NOTE the ASSUMPTION here that no system attributes are, or ever
+ * will be, sets.
+ */
+bool
+varisset(Relation rd, char *name)
+{
+    int i;
+    
+    /* First check if this is a system attribute */
+    for (i = 0; i < SPECIALS; i++) {
+       if (! strcmp(special_attr[i].field, name)) {
+           return(false);   /* no sys attr is a set */
+       }
+    }
+    return (get_attisset(rd->rd_id, name));
+}
+
+/* given range variable, return id of variable */
+int
+nf_varattno(Relation rd, char *a)
+{
+    int i;
+    
+    for (i = 0; i < rd->rd_rel->relnatts; i++) {
+       if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a)) {
+           return(i+1);
+       }
+    }
+    for (i = 0; i < SPECIALS; i++) {
+       if (!strcmp(special_attr[i].field, a)) {
+           return(special_attr[i].code);
+       }
+    }
+    return InvalidAttrNumber;
+}
+
+/*-------------
+ * given an attribute number and a relation, return its relation name
+ */
+char*
+getAttrName(Relation rd, int attrno)
+{
+    char *name;
+    int i;
+    
+    if (attrno<0) {
+       for (i = 0; i < SPECIALS; i++) {
+           if (special_attr[i].code == attrno) {
+               name = special_attr[i].field;
+               return(name);
+           }
+       }
+       elog(WARN, "Illegal attr no %d for relation %s\n",
+            attrno, RelationGetRelationName(rd));
+    } else if (attrno >=1 && attrno<= RelationGetNumberOfAttributes(rd)) {
+       name = (rd->rd_att->attrs[attrno-1]->attname).data;
+       return(name);
+    } else {
+       elog(WARN, "Illegal attr no %d for relation %s\n",
+            attrno, RelationGetRelationName(rd));
+    }
+    
+    /*
+     * Shouldn't get here, but we want lint to be happy...
+     */
+    
+    return(NULL);
+}
+
+/* Given a typename and value, returns the ascii form of the value */
+
+char *
+outstr(char *typename, /* Name of type of value */
+       char *value)    /* Could be of any type */
+{
+    TypeTupleForm tp;
+    Oid op;
+    
+    tp = (TypeTupleForm ) GETSTRUCT(type(typename));
+    op = tp->typoutput;
+    return((char *) fmgr(op, value));
+}
+
+/* Given a Type and a string, return the internal form of that string */
+char *
+instr2(Type tp, char *string, int typlen)
+{
+    return(instr1((TypeTupleForm ) GETSTRUCT(tp), string, typlen));
+}
+
+/* Given a type structure and a string, returns the internal form of
+   that string */
+char *
+instr1(TypeTupleForm tp, char *string, int typlen)
+{
+    Oid op;
+    Oid typelem;
+    
+    op = tp->typinput;
+    typelem = tp->typelem; /* XXX - used for array_in */
+    /* typlen is for bpcharin() and varcharin() */
+    return((char *) fmgr(op, string, typelem, typlen));
+}
+
+/* Given the attribute type of an array return the arrtribute type of
+   an element of the array */
+
+Oid
+GetArrayElementType(Oid typearray)
+{
+    HeapTuple type_tuple;
+    TypeTupleForm type_struct_array;
+    
+    type_tuple = SearchSysCacheTuple(TYPOID,
+                                    ObjectIdGetDatum(typearray),
+                                    0,0,0);
+    
+    if (!HeapTupleIsValid(type_tuple))
+       elog(WARN, "GetArrayElementType: Cache lookup failed for type %d\n",
+            typearray);
+    
+    /* get the array type struct from the type tuple */
+    type_struct_array = (TypeTupleForm) GETSTRUCT(type_tuple);
+    
+    if (type_struct_array->typelem == InvalidOid) {
+       elog(WARN, "GetArrayElementType: type %s is not an array",
+            (Name)&(type_struct_array->typname.data[0]));
+    }
+    
+    return(type_struct_array->typelem);
+}
+
+Oid
+funcid_get_rettype(Oid funcid)
+{
+    HeapTuple func_tuple = NULL;
+    Oid funcrettype = (Oid)0;
+    
+    func_tuple = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid),
+                                    0,0,0);
+    
+    if ( !HeapTupleIsValid ( func_tuple )) 
+       elog (WARN, "function  %d does not exist", funcid);
+    
+    funcrettype = (Oid)
+       ((Form_pg_proc)GETSTRUCT(func_tuple))->prorettype ;
+    
+    return (funcrettype);
+}
+
+/*
+ * get a list of all argument type vectors for which a function named
+ * funcname taking nargs arguments exists
+ */
+static CandidateList
+func_get_candidates(char *funcname, int nargs)
+{
+    Relation heapRelation;
+    Relation idesc;
+    ScanKeyData skey;
+    HeapTuple tuple;
+    IndexScanDesc sd;
+    RetrieveIndexResult indexRes;
+    Buffer buffer;
+    Form_pg_proc pgProcP;
+    bool bufferUsed = FALSE;
+    CandidateList candidates = NULL;
+    CandidateList current_candidate;
+    int i;
+    
+    heapRelation = heap_openr(ProcedureRelationName);
+    ScanKeyEntryInitialize(&skey,
+                           (bits16)0x0,
+                           (AttrNumber)1,
+                           (RegProcedure)NameEqualRegProcedure,
+                           (Datum)funcname);
+    
+    idesc = index_openr(ProcedureNameIndex);
+    
+    sd = index_beginscan(idesc, false, 1, &skey);
+    
+    do {  
+        tuple = (HeapTuple)NULL;
+        if (bufferUsed) {
+            ReleaseBuffer(buffer);
+            bufferUsed = FALSE;
+        }
+       
+        indexRes = index_getnext(sd, ForwardScanDirection);
+        if (indexRes) {
+            ItemPointer iptr;
+           
+            iptr = &indexRes->heap_iptr;
+            tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer);
+            pfree(indexRes);
+            if (HeapTupleIsValid(tuple)) {
+                pgProcP = (Form_pg_proc)GETSTRUCT(tuple);
+                bufferUsed = TRUE;
+               if (pgProcP->pronargs == nargs) {
+                   current_candidate = (CandidateList)
+                       palloc(sizeof(struct _CandidateList));
+                   current_candidate->args = (Oid *)
+                       palloc(8 * sizeof(Oid));
+                   memset(current_candidate->args, 0, 8 * sizeof(Oid)); 
+                   for (i=0; i<nargs; i++) {
+                       current_candidate->args[i] = 
+                           pgProcP->proargtypes[i];
+                   }
+                   
+                   current_candidate->next = candidates;
+                   candidates = current_candidate;
+               }
+            }
+       }
+    } while (indexRes);
+    
+    index_endscan(sd);
+    index_close(idesc);
+    heap_close(heapRelation);           
+    
+    return candidates;
+}
+
+/*
+ * can input_typeids be coerced to func_typeids?
+ */
+static bool
+can_coerce(int nargs, Oid *input_typeids, Oid *func_typeids)
+{
+    int i;
+    Type tp;
+    
+    /*
+     * right now, we only coerce "unknown", and we cannot coerce it to a
+     * relation type
+     */
+    for (i=0; i<nargs; i++) {
+       if (input_typeids[i] != func_typeids[i]) {
+           if (input_typeids[i] != UNKNOWNOID || func_typeids[i] == 0)
+               return false;
+           
+           tp = get_id_type(input_typeids[i]);
+           if (typetypetype(tp) == 'c' )
+               return false;
+       }
+    }
+    
+    return true;
+}
+
+/*
+ * given a list of possible typeid arrays to a function and an array of
+ * input typeids, produce a shortlist of those function typeid arrays
+ * that match the input typeids (either exactly or by coercion), and
+ * return the number of such arrays
+ */
+static int
+match_argtypes(int nargs,
+              Oid *input_typeids,
+              CandidateList function_typeids,
+              CandidateList *candidates) /* return value */
+{
+    CandidateList current_candidate;
+    CandidateList matching_candidate;
+    Oid *current_typeids;
+    int ncandidates = 0;
+    
+    *candidates = NULL;
+    
+    for (current_candidate = function_typeids;
+        current_candidate != NULL;
+        current_candidate = current_candidate->next) {
+       current_typeids = current_candidate->args;
+       if (can_coerce(nargs, input_typeids, current_typeids)) {
+           matching_candidate = (CandidateList)
+               palloc(sizeof(struct _CandidateList));
+           matching_candidate->args = current_typeids;
+           matching_candidate->next = *candidates;
+           *candidates = matching_candidate;
+           ncandidates++;
+       }
+    }
+    
+    return ncandidates;
+}
+
+/*
+ * given the input argtype array and more than one candidate
+ * for the function argtype array, attempt to resolve the conflict.
+ * returns the selected argtype array if the conflict can be resolved,
+ * otherwise returns NULL
+ */
+static Oid *
+func_select_candidate(int nargs,
+                     Oid *input_typeids,
+                     CandidateList candidates)
+{
+    /* XXX no conflict resolution implemeneted yet */
+    return (NULL);
+}
+
+bool
+func_get_detail(char *funcname,
+               int nargs,
+               Oid *oid_array,
+               Oid *funcid,    /* return value */
+               Oid *rettype,   /* return value */
+               bool *retset,   /* return value */
+               Oid **true_typeids) /* return value */
+{
+    Oid **input_typeid_vector;
+    Oid *current_input_typeids;
+    CandidateList function_typeids;
+    CandidateList current_function_typeids;
+    HeapTuple ftup;
+    Form_pg_proc pform;
+    
+    /*
+     * attempt to find named function in the system catalogs
+     * with arguments exactly as specified - so that the normal
+     * case is just as quick as before
+     */
+    ftup = SearchSysCacheTuple(PRONAME, 
+                              PointerGetDatum(funcname),
+                              Int32GetDatum(nargs),
+                              PointerGetDatum(oid_array),
+                              0);
+    *true_typeids = oid_array;
+    
+    /*
+     * If an exact match isn't found :
+     * 1) get a vector of all possible input arg type arrays constructed
+     *    from the superclasses of the original input arg types
+     * 2) get a list of all possible argument type arrays to the
+     *   function with given name and number of arguments
+     * 3) for each input arg type array from vector #1 :
+     *   a) find how many of the function arg type arrays from list #2
+     *      it can be coerced to
+     *   b) - if the answer is one, we have our function
+     *      - if the answer is more than one, attempt to resolve the
+     *        conflict
+     *      - if the answer is zero, try the next array from vector #1
+     */
+    if (!HeapTupleIsValid(ftup)) {
+       function_typeids = func_get_candidates(funcname, nargs);
+       
+       if (function_typeids != NULL) {
+           int ncandidates = 0;
+           
+           input_typeid_vector = argtype_inherit(nargs, oid_array);
+           current_input_typeids = oid_array;
+           
+           do {
+               ncandidates = match_argtypes(nargs, current_input_typeids,
+                                            function_typeids,
+                                            &current_function_typeids);
+               if (ncandidates == 1) {
+                   *true_typeids = current_function_typeids->args;
+                   ftup = SearchSysCacheTuple(PRONAME, 
+                                              PointerGetDatum(funcname),
+                                              Int32GetDatum(nargs),
+                                              PointerGetDatum(*true_typeids),
+                                              0);
+                   Assert(HeapTupleIsValid(ftup));
+               }
+               else if (ncandidates > 1) {
+                   *true_typeids =
+                       func_select_candidate(nargs,
+                                             current_input_typeids,
+                                             current_function_typeids);
+                   if (*true_typeids == NULL) {
+                       elog(NOTICE, "there is more than one function named \"%s\"",
+                            funcname);
+                       elog(NOTICE, "that satisfies the given argument types. you will have to");
+                       elog(NOTICE, "retype your query using explicit typecasts.");
+                       func_error("func_get_detail", funcname, nargs, (int*)oid_array);
+                   }
+                   else {
+                       ftup = SearchSysCacheTuple(PRONAME, 
+                                                  PointerGetDatum(funcname),
+                                                  Int32GetDatum(nargs),
+                                                  PointerGetDatum(*true_typeids),
+                                                  0);
+                       Assert(HeapTupleIsValid(ftup));
+                   }
+               }
+               current_input_typeids = *input_typeid_vector++;
+           } 
+           while (current_input_typeids !=
+                  InvalidOid && ncandidates == 0);
+       }
+    }
+    
+    if (!HeapTupleIsValid(ftup)) {
+       Type tp;
+
+       if (nargs == 1) {
+           tp = get_id_type(oid_array[0]);
+           if (typetypetype(tp) == 'c')
+               elog(WARN, "no such attribute or function \"%s\"",
+                    funcname);
+       }
+       func_error("func_get_detail", funcname, nargs, (int*)oid_array);
+    } else {
+       pform = (Form_pg_proc) GETSTRUCT(ftup);
+       *funcid = ftup->t_oid;
+       *rettype = (Oid) pform->prorettype;
+       *retset = (Oid) pform->proretset;
+       
+       return (true);
+    }
+/* shouldn't reach here */
+ return (false);
+
+}
+
+/*
+ *  argtype_inherit() -- Construct an argtype vector reflecting the
+ *                      inheritance properties of the supplied argv.
+ *
+ *     This function is used to disambiguate among functions with the
+ *     same name but different signatures.  It takes an array of eight
+ *     type ids.  For each type id in the array that's a complex type
+ *     (a class), it walks up the inheritance tree, finding all
+ *     superclasses of that type.  A vector of new Oid type arrays
+ *     is returned to the caller, reflecting the structure of the
+ *     inheritance tree above the supplied arguments.
+ *
+ *     The order of this vector is as follows:  all superclasses of the
+ *     rightmost complex class are explored first.  The exploration
+ *     continues from right to left.  This policy means that we favor
+ *     keeping the leftmost argument type as low in the inheritance tree
+ *     as possible.  This is intentional; it is exactly what we need to
+ *     do for method dispatch.  The last type array we return is all
+ *     zeroes.  This will match any functions for which return types are
+ *     not defined.  There are lots of these (mostly builtins) in the
+ *     catalogs.
+ */
+static Oid **
+argtype_inherit(int nargs, Oid *oid_array)
+{
+    Oid relid;
+    int i;
+    InhPaths arginh[MAXFARGS];
+    
+    for (i = 0; i < MAXFARGS; i++) {
+       if (i < nargs) {
+           arginh[i].self = oid_array[i];
+           if ((relid = typeid_get_relid(oid_array[i])) != InvalidOid) {
+               arginh[i].nsupers = findsupers(relid, &(arginh[i].supervec));
+           } else {
+               arginh[i].nsupers = 0;
+               arginh[i].supervec = (Oid *) NULL;
+           }
+       } else {
+           arginh[i].self = InvalidOid;
+           arginh[i].nsupers = 0;
+           arginh[i].supervec = (Oid *) NULL;
+       }
+    }
+    
+    /* return an ordered cross-product of the classes involved */
+    return (genxprod(arginh, nargs));
+}
+
+typedef struct _SuperQE {
+    Oid        sqe_relid;
+} SuperQE;
+
+static int
+findsupers(Oid relid, Oid **supervec)
+{
+    Oid *relidvec;
+    Relation inhrel;
+    HeapScanDesc inhscan;
+    ScanKeyData skey;
+    HeapTuple inhtup;
+    TupleDesc inhtupdesc;
+    int nvisited;
+    SuperQE *qentry, *vnode;
+    Dllist *visited, *queue;
+    Dlelem *qe, *elt;
+
+    Relation rd;
+    Buffer buf;
+    Datum d;
+    bool newrelid;
+    char isNull;
+    
+    nvisited = 0;
+    queue = DLNewList();
+    visited = DLNewList();
+
+
+    inhrel = heap_openr(InheritsRelationName);
+    RelationSetLockForRead(inhrel);
+    inhtupdesc = RelationGetTupleDescriptor(inhrel);
+    
+    /*
+     *  Use queue to do a breadth-first traversal of the inheritance
+     *  graph from the relid supplied up to the root.
+     */
+    do {
+       ScanKeyEntryInitialize(&skey, 0x0, Anum_pg_inherits_inhrel,
+                              ObjectIdEqualRegProcedure,
+                              ObjectIdGetDatum(relid));
+       
+       inhscan = heap_beginscan(inhrel, 0, NowTimeQual, 1, &skey);
+       
+       while (HeapTupleIsValid(inhtup = heap_getnext(inhscan, 0, &buf))) {
+           qentry = (SuperQE *) palloc(sizeof(SuperQE));
+           
+           d = (Datum) fastgetattr(inhtup, Anum_pg_inherits_inhparent,
+                                   inhtupdesc, &isNull);
+           qentry->sqe_relid = DatumGetObjectId(d);
+           
+           /* put this one on the queue */
+           DLAddTail(queue, DLNewElem(qentry));
+           
+           ReleaseBuffer(buf);
+       }
+       
+       heap_endscan(inhscan);
+       
+       /* pull next unvisited relid off the queue */
+       do {
+         qe = DLRemHead(queue);
+         qentry = qe ? (SuperQE*)DLE_VAL(qe) : NULL;
+
+           if (qentry == (SuperQE *) NULL)
+               break;
+           
+           relid = qentry->sqe_relid;
+           newrelid = true;
+           
+          for (elt = DLGetHead(visited); elt; elt = DLGetSucc(elt)) {
+            vnode = (SuperQE*)DLE_VAL(elt);
+            if (vnode && (qentry->sqe_relid == vnode->sqe_relid)) {
+              newrelid = false;
+              break;
+            }
+          }
+       } while (!newrelid);
+       
+       if (qentry != (SuperQE *) NULL) {
+           
+           /* save the type id, rather than the relation id */
+           if ((rd = heap_open(qentry->sqe_relid)) == (Relation) NULL)
+               elog(WARN, "relid %d does not exist", qentry->sqe_relid);
+           qentry->sqe_relid = typeid(type(RelationGetRelationName(rd)->data));
+           heap_close(rd);
+           
+           DLAddTail(visited, qe);
+
+           nvisited++;
+       }
+    } while (qentry != (SuperQE *) NULL);
+    
+    RelationUnsetLockForRead(inhrel);
+    heap_close(inhrel);
+    
+    if (nvisited > 0) {
+       relidvec = (Oid *) palloc(nvisited * sizeof(Oid));
+       *supervec = relidvec;
+
+       for (elt = DLGetHead(visited); elt; elt = DLGetSucc(elt)) {
+            vnode = (SuperQE*)DLE_VAL(elt);
+            *relidvec++ = vnode->sqe_relid;
+          }
+         
+    } else {
+       *supervec = (Oid *) NULL;
+      }
+
+    return (nvisited);
+}
+
+static Oid **
+genxprod(InhPaths *arginh, int nargs)
+{
+    int nanswers;
+    Oid **result, **iter;
+    Oid *oneres;
+    int i, j;
+    int cur[MAXFARGS];
+    
+    nanswers = 1;
+    for (i = 0; i < nargs; i++) {
+       nanswers *= (arginh[i].nsupers + 2);
+       cur[i] = 0;
+    }
+    
+    iter = result = (Oid **) palloc(sizeof(Oid *) * nanswers);
+    
+    /* compute the cross product from right to left */
+    for (;;) {
+       oneres = (Oid *) palloc(MAXFARGS * sizeof(Oid));
+       memset(oneres, 0, MAXFARGS * sizeof(Oid));
+       
+       for (i = nargs - 1; i >= 0 && cur[i] > arginh[i].nsupers; i--)
+           continue;
+       
+       /* if we're done, terminate with NULL pointer */
+       if (i < 0) {
+           *iter = NULL;
+           return (result);
+       }
+       
+       /* no, increment this column and zero the ones after it */
+       cur[i] = cur[i] + 1;
+       for (j = nargs - 1; j > i; j--)
+           cur[j] = 0;
+       
+       for (i = 0; i < nargs; i++) {
+           if (cur[i] == 0)
+               oneres[i] = arginh[i].self;
+           else if (cur[i] > arginh[i].nsupers)
+               oneres[i] = 0;  /* wild card */
+           else
+               oneres[i] = arginh[i].supervec[cur[i] - 1];
+       }
+       
+       *iter++ = oneres;
+    }
+}
+
+/* Given a type id, returns the in-conversion function of the type */
+Oid
+typeid_get_retinfunc(int type_id)
+{
+    HeapTuple     typeTuple;
+    TypeTupleForm   type;
+    Oid             infunc;
+    typeTuple = SearchSysCacheTuple(TYPOID,
+                                   ObjectIdGetDatum(type_id),
+                                   0,0,0);
+    if ( !HeapTupleIsValid ( typeTuple ))
+       elog(WARN,
+            "typeid_get_retinfunc: Invalid type - oid = %d",
+            type_id);
+    
+    type = (TypeTupleForm) GETSTRUCT(typeTuple);
+    infunc = type->typinput;
+    return(infunc);
+}
+
+Oid
+typeid_get_relid(int type_id)
+{
+    HeapTuple     typeTuple;
+    TypeTupleForm   type;
+    Oid             infunc;
+    typeTuple = SearchSysCacheTuple(TYPOID,
+                                   ObjectIdGetDatum(type_id),
+                                   0,0,0);
+    if ( !HeapTupleIsValid ( typeTuple ))
+       elog(WARN, "typeid_get_relid: Invalid type - oid = %d ", type_id);
+    
+    type = (TypeTupleForm) GETSTRUCT(typeTuple);
+    infunc = type->typrelid;
+    return(infunc);
+}
+
+Oid get_typrelid(Type typ)
+{
+    TypeTupleForm typtup;
+    
+    typtup = (TypeTupleForm) GETSTRUCT(typ);
+    
+    return (typtup->typrelid);
+}
+
+Oid
+get_typelem(Oid type_id)
+{
+    HeapTuple     typeTuple;
+    TypeTupleForm   type;
+    
+    if (!(typeTuple = SearchSysCacheTuple(TYPOID,
+                                         ObjectIdGetDatum(type_id),
+                                         0,0,0))) {
+       elog (WARN , "type id lookup of %d failed", type_id);
+    }
+    type = (TypeTupleForm) GETSTRUCT(typeTuple);
+    
+    return (type->typelem);
+}
+
+char
+FindDelimiter(char *typename)
+{
+    char delim;
+    HeapTuple     typeTuple;
+    TypeTupleForm   type;
+    
+    
+    if (!(typeTuple = SearchSysCacheTuple(TYPNAME, 
+                                         PointerGetDatum(typename),
+                                         0,0,0))) {
+        elog (WARN , "type name lookup of %s failed", typename);
+    }
+    type = (TypeTupleForm) GETSTRUCT(typeTuple);
+    
+    delim = type->typdelim;
+    return (delim);
+}
+
+/*
+ * Give a somewhat useful error message when the operator for two types
+ * is not found.
+ */
+void
+op_error(char *op, int arg1, int arg2)
+{
+    Type tp1, tp2;
+
+    if (check_typeid(arg1)) {
+       tp1 = get_id_type(arg1);
+    } else {
+       elog(WARN, "left hand side of operator %s has an unknown type, probably a bad attribute name", op);
+    }
+
+    if (check_typeid(arg2)) {
+       tp2 = get_id_type(arg2);
+    } else {
+       elog(WARN, "right hand side of operator %s has an unknown type, probably a bad attribute name", op);
+    }
+    
+    elog(NOTICE, "there is no operator %s for types %s and %s",
+        op, tname(tp1),tname(tp2));
+    elog(NOTICE, "You will either have to retype this query using an");
+    elog(NOTICE, "explicit cast, or you will have to define the operator");
+    elog(WARN, "%s for %s and %s using DEFINE OPERATOR", 
+        op, tname(tp1),tname(tp2));
+}
+
+/*
+ * Error message when function lookup fails that gives details of the
+ * argument types
+ */
+void
+func_error(char *caller, char *funcname, int nargs, int *argtypes)
+{
+    Type get_id_type();
+    char p[(NAMEDATALEN+2)*MAXFMGRARGS], *ptr;
+    int i;
+       
+    ptr = p;
+    *ptr = '\0';
+    for (i=0; i<nargs; i++) {
+       if (i) {
+           *ptr++ = ',';
+           *ptr++ = ' ';
+       }
+       if (argtypes[i] != 0) {
+           (void) strcpy(ptr, tname(get_id_type(argtypes[i])));
+           *(ptr + NAMEDATALEN) = '\0';
+       } else
+           strcpy(ptr, "opaque");
+       ptr += strlen(ptr);
+    }
+       
+    elog(WARN, "%s: function %s(%s) does not exist", caller, funcname, p);
+}
+
diff --git a/src/backend/parser/catalog_utils.h b/src/backend/parser/catalog_utils.h
new file mode 100644 (file)
index 0000000..4677347
--- /dev/null
@@ -0,0 +1,64 @@
+/*-------------------------------------------------------------------------
+ *
+ * catalog_utils.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        CATALOG_UTILS_H
+#define        CATALOG_UTILS_H
+
+
+#include "postgres.h"
+
+#include "access/htup.h"
+#include "utils/rel.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "utils/syscache.h"
+    
+typedef HeapTuple      Type;
+typedef HeapTuple      Operator;
+
+extern Oid     typeid_get_relid();
+
+extern bool check_typeid(long id);
+extern Type get_id_type(long id);
+extern char *get_id_typname(long id);
+extern Type type(char *);
+extern Oid att_typeid(Relation rd, int attid);
+extern int att_attnelems(Relation rd, int attid);
+extern Oid typeid(Type tp);
+extern int16 tlen(Type t);
+extern bool tbyval(Type t);
+extern char *tname(Type t);
+extern int tbyvalue(Type t);
+extern Oid oprid(Operator op);
+extern Operator oper(char *op, int arg1, int arg2);
+extern Operator right_oper(char *op, int arg);
+extern Operator left_oper(char *op, int arg);
+extern int varattno(Relation rd, char *a);
+extern bool varisset(Relation rd, char *name);
+extern int nf_varattno(Relation rd, char *a);
+extern char *getAttrName(Relation rd, int attrno);
+extern char *outstr(char *typename, char *value);
+extern char *instr2(Type tp, char *string, int typlen);
+extern char *instr1(TypeTupleForm tp, char *string, int typlen);
+extern Oid GetArrayElementType(Oid typearray);
+extern Oid funcid_get_rettype(Oid funcid);
+extern bool func_get_detail(char *funcname, int nargs, Oid *oid_array,
+           Oid *funcid, Oid *rettype, bool *retset, Oid **true_typeids);
+extern Oid typeid_get_retinfunc(int type_id);
+extern Oid typeid_get_relid(int type_id);
+extern Oid get_typrelid(Type typ);
+extern Oid get_typelem(Oid type_id);
+extern char FindDelimiter(char *typename);
+extern void op_error(char *op, int arg1, int arg2);
+extern void func_error(char *caller, char *funcname, int nargs, int *argtypes);
+
+#endif /* CATALOG_UTILS_H */
diff --git a/src/backend/parser/dbcommands.c b/src/backend/parser/dbcommands.c
new file mode 100644 (file)
index 0000000..5c5c047
--- /dev/null
@@ -0,0 +1,259 @@
+/*-------------------------------------------------------------------------
+ *
+ * dbcommands.c--
+ *    
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <signal.h>
+
+#include "postgres.h"
+#include "miscadmin.h"  /* for DataDir */
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/relscan.h"
+#include "utils/rel.h"
+#include "utils/elog.h"
+#include "catalog/catname.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_user.h"
+#include "catalog/pg_database.h"
+#include "utils/syscache.h"
+#include "parser/dbcommands.h"
+#include "tcop/tcopprot.h"
+#include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+
+
+/* non-export function prototypes */
+static void check_permissions(char *command, char *dbname,
+                             Oid *dbIdP, Oid *userIdP);
+static HeapTuple get_pg_dbtup(char *command, char *dbname, Relation dbrel);
+
+void
+createdb(char *dbname)
+{
+    Oid db_id, user_id;
+    char buf[512];
+    
+    /*
+     *  If this call returns, the database does not exist and we're allowed
+     *  to create databases.
+     */
+    check_permissions("createdb", dbname, &db_id, &user_id);
+    
+    /* close virtual file descriptors so we can do system() calls */
+    closeAllVfds();
+    
+    sprintf(buf, "mkdir %s%cbase%c%s", DataDir, SEP_CHAR, SEP_CHAR, dbname);
+    system(buf);
+    sprintf(buf, "%s %s%cbase%ctemplate1%c* %s%cbase%c%s",
+           COPY_CMD, DataDir, SEP_CHAR, SEP_CHAR, SEP_CHAR, DataDir,
+               SEP_CHAR, SEP_CHAR, dbname);
+    system(buf);
+
+/*    sprintf(buf, "insert into pg_database (datname, datdba, datpath) \
+                  values (\'%s\'::char16, \'%d\'::oid, \'%s\'::text);",
+           dbname, user_id, dbname);
+*/
+    sprintf(buf, "insert into pg_database (datname, datdba, datpath) \
+                  values (\'%s\', \'%d\', \'%s\');",
+           dbname, user_id, dbname);
+
+    pg_eval(buf, (char **) NULL, (Oid *) NULL, 0);
+}
+
+void
+destroydb(char *dbname)
+{
+    Oid user_id, db_id;
+    char buf[512];
+    
+    /*
+     *  If this call returns, the database exists and we're allowed to
+     *  remove it.
+     */
+    check_permissions("destroydb", dbname, &db_id, &user_id);
+    
+    if (!OidIsValid(db_id)) {
+       elog(FATAL, "impossible: pg_database instance with invalid OID.");
+    }
+    
+    /* stop the vacuum daemon */
+    stop_vacuum(dbname);
+    
+    /* remove the pg_database tuple FIRST,
+       this may fail due to permissions problems*/
+    sprintf(buf, "delete from pg_database where pg_database.oid = \'%d\'::oid",
+           db_id);
+    pg_eval(buf, (char **) NULL, (Oid *) NULL, 0);
+    
+    /* remove the data directory. If the DELETE above failed, this will
+       not be reached */
+    sprintf(buf, "rm -r %s/base/%s", DataDir, dbname);
+    system(buf);
+    
+    /* drop pages for this database that are in the shared buffer cache */
+    DropBuffers(db_id);
+}
+
+static HeapTuple
+get_pg_dbtup(char *command, char *dbname, Relation dbrel)
+{
+    HeapTuple dbtup;
+    HeapTuple tup;
+    Buffer buf;
+    HeapScanDesc scan;
+    ScanKeyData scanKey;
+    
+    ScanKeyEntryInitialize(&scanKey, 0, Anum_pg_database_datname,
+                          NameEqualRegProcedure, NameGetDatum(dbname));
+    
+    scan = heap_beginscan(dbrel, 0, NowTimeQual, 1, &scanKey);
+    if (!HeapScanIsValid(scan))
+       elog(WARN, "%s: cannot begin scan of pg_database.", command);
+    
+    /*
+     *  since we want to return the tuple out of this proc, and we're
+     *  going to close the relation, copy the tuple and return the copy.
+     */
+    tup = heap_getnext(scan, 0, &buf);
+    
+    if (HeapTupleIsValid(tup)) {
+       dbtup = heap_copytuple(tup);
+       ReleaseBuffer(buf);
+    } else
+       dbtup = tup;
+    
+    heap_endscan(scan);
+    return (dbtup);
+}
+
+/*
+ *  check_permissions() -- verify that the user is permitted to do this.
+ *
+ *  If the user is not allowed to carry out this operation, this routine
+ *  elog(WARN, ...)s, which will abort the xact.  As a side effect, the
+ *  user's pg_user tuple OID is returned in userIdP and the target database's
+ *  OID is returned in dbIdP.
+ */
+
+static void
+check_permissions(char *command,
+                 char *dbname,
+                 Oid *dbIdP,
+                 Oid *userIdP)
+{
+    Relation dbrel;
+    HeapTuple dbtup, utup;
+    Oid dbowner;
+    char use_createdb;
+    bool dbfound;
+    bool use_super;
+    char *userName;
+
+    userName = GetPgUserName();
+    utup = SearchSysCacheTuple(USENAME, PointerGetDatum(userName),
+                              0,0,0);
+    *userIdP = ((Form_pg_user)GETSTRUCT(utup))->usesysid;
+    use_super = ((Form_pg_user)GETSTRUCT(utup))->usesuper;
+    use_createdb = ((Form_pg_user)GETSTRUCT(utup))->usecreatedb;
+    
+    /* Check to make sure user has permission to use createdb */
+    if (!use_createdb) {
+        elog(WARN, "user \"%-.*s\" is not allowed to create/destroy databases",
+             NAMEDATALEN, userName);
+    }
+    
+    /* Make sure we are not mucking with the template database */
+    if (!strcmp(dbname, "template1")) {
+        elog(WARN, "%s cannot be executed on the template database.", command);
+    }
+    
+    /* Check to make sure database is not the currently open database */
+    if (!strcmp(dbname, GetDatabaseName())) {
+        elog(WARN, "%s cannot be executed on an open database", command);
+    }
+    
+    /* Check to make sure database is owned by this user */
+    
+    /* 
+     * need the reldesc to get the database owner out of dbtup 
+     * and to set a write lock on it.
+     */
+    dbrel = heap_openr(DatabaseRelationName);
+    
+    if (!RelationIsValid(dbrel))
+       elog(FATAL, "%s: cannot open relation \"%-.*s\"",
+            command, DatabaseRelationName);
+    
+    /*
+     * Acquire a write lock on pg_database from the beginning to avoid 
+     * upgrading a read lock to a write lock.  Upgrading causes long delays 
+     * when multiple 'createdb's or 'destroydb's are run simult. -mer 7/3/91
+     */
+    RelationSetLockForWrite(dbrel);
+    dbtup = get_pg_dbtup(command, dbname, dbrel);
+    dbfound = HeapTupleIsValid(dbtup);
+    
+    if (dbfound) {
+       dbowner = (Oid) heap_getattr(dbtup, InvalidBuffer,
+                                         Anum_pg_database_datdba,
+                                         RelationGetTupleDescriptor(dbrel),
+                                         (char *) NULL);
+       *dbIdP = dbtup->t_oid;
+    } else {
+       *dbIdP = InvalidOid;
+    }
+    
+    heap_close(dbrel);
+    
+    /*
+     *  Now be sure that the user is allowed to do this.
+     */
+    
+    if (dbfound && !strcmp(command, "createdb")) {
+       
+        elog(WARN, "createdb: database %s already exists.", dbname);
+       
+    } else if (!dbfound && !strcmp(command, "destroydb")) {
+       
+        elog(WARN, "destroydb: database %s does not exist.", dbname);
+       
+    } else if (dbfound && !strcmp(command, "destroydb")
+              && dbowner != *userIdP && use_super == false) {
+       
+        elog(WARN, "%s: database %s is not owned by you.", command, dbname);
+       
+    }
+}
+
+/*
+ *  stop_vacuum() -- stop the vacuum daemon on the database, if one is
+ *                  running.
+ */
+void
+stop_vacuum(char *dbname)
+{
+    char filename[256];
+    FILE *fp;
+    int pid;
+    
+    sprintf(filename, "%s%cbase%c%s%c%s.vacuum", DataDir, SEP_CHAR, SEP_CHAR,
+       dbname, SEP_CHAR, dbname);
+    if ((fp = fopen(filename, "r")) != (FILE *) NULL) {
+       fscanf(fp, "%d", &pid);
+       fclose(fp);
+       if (kill(pid, SIGKILLDAEMON1) < 0) {
+           elog(WARN, "can't kill vacuum daemon (pid %d) on %s",
+                pid, dbname);
+       }
+    }
+}
diff --git a/src/backend/parser/dbcommands.h b/src/backend/parser/dbcommands.h
new file mode 100644 (file)
index 0000000..21af770
--- /dev/null
@@ -0,0 +1,28 @@
+/*-------------------------------------------------------------------------
+ *
+ * dbcommands.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        DBCOMMANDS_H
+#define        DBCOMMANDS_H
+
+/*
+ * Originally from tmp/daemon.h. The functions declared in daemon.h does not
+ * exist; hence removed.       -- AY 7/29/94
+ */
+#define        SIGKILLDAEMON1  SIGINT
+#define        SIGKILLDAEMON2  SIGTERM
+
+extern void createdb(char *dbname);
+extern void destroydb(char *dbname);
+void stop_vacuum(char *dbname);
+
+#endif /* DBCOMMANDS_H */
+
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
new file mode 100644 (file)
index 0000000..6e1c76f
--- /dev/null
@@ -0,0 +1,2113 @@
+%{ /* -*-text-*- */
+
+#define YYDEBUG 1
+/*-------------------------------------------------------------------------
+ * 
+ * gram.y--
+ *    POSTGRES SQL YACC rules/actions
+ * 
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * HISTORY
+ *    AUTHOR           DATE            MAJOR EVENT
+ *    Andrew Yu                Sept, 1994      POSTQUEL to SQL conversion
+ *    Andrew Yu                Oct, 1994       lispy code conversion
+ *
+ * NOTES
+ *    CAPITALS are used to represent terminal symbols.
+ *    non-capitals are used to represent non-terminals.
+ *
+ *    if you use list, make sure the datum is a node so that the printing
+ *    routines work
+ *
+ * WARNING
+ *    sometimes we assign constants to makeStrings. Make sure we don't free
+ *    those.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include <ctype.h>
+#include "postgres.h"
+#include "nodes/parsenodes.h"
+#include "parser/catalog_utils.h"
+#include "parser/parse_query.h"
+#include "utils/acl.h"
+#include "catalog/catname.h"
+#include "utils/elog.h"
+#include "access/xact.h"
+
+static char saved_relname[BUFSIZ];  /* need this for complex attributes */
+static bool QueryIsRule = FALSE;
+
+extern List *parsetree;
+
+/*
+ * If you need access to certain yacc-generated variables and find that 
+ * they're static by default, uncomment the next line.  (this is not a
+ * problem, yet.)
+ */
+/*#define __YYSCLASS*/
+
+extern void yyerror(char message[]);
+
+static char *xlateSqlType(char *);
+static Node *makeA_Expr(int op, char *opname, Node *lexpr, Node *rexpr);
+
+/* old versions of flex define this as a macro */
+#if defined(yywrap)
+#undef yywrap
+#endif /* yywrap */
+%}
+
+
+%union {
+    double             dval;
+    int                        ival;
+    char                chr;
+    char               *str;
+    bool               boolean;
+    List               *list;
+    Node               *node;
+    Value              *value;
+
+    Attr               *attr;
+
+    ColumnDef          *coldef;
+    TypeName           *typnam;
+    DefElem            *defelt;
+    ParamString                *param;
+    SortBy             *sortby;
+    IndexElem          *ielem;
+    RangeVar           *range;
+    RelExpr            *relexp;
+    TimeRange          *trange;
+    A_Indices          *aind;
+    ResTarget          *target;
+    ParamNo            *paramno;
+       
+    VersionStmt                *vstmt;
+    DefineStmt         *dstmt;
+    PurgeStmt          *pstmt;
+    RuleStmt           *rstmt;
+    AppendStmt         *astmt;
+}
+
+%type <node>   query, stmt, AddAttrStmt, ClosePortalStmt,
+       CopyStmt, CreateStmt, DefineStmt, DestroyStmt, 
+       ExtendStmt, FetchStmt,  GrantStmt,
+       IndexStmt, MoveStmt, ListenStmt, OptimizableStmt, 
+        ProcedureStmt, PurgeStmt,
+       RecipeStmt, RemoveOperStmt, RemoveFuncStmt, RemoveStmt, RenameStmt,
+        RevokeStmt, RuleStmt, TransactionStmt, ViewStmt, LoadStmt,
+       CreatedbStmt, DestroydbStmt, VacuumStmt, RetrieveStmt, CursorStmt,
+       ReplaceStmt, AppendStmt, NotifyStmt, DeleteStmt, ClusterStmt,
+       ExplainStmt
+
+%type <str>    relation_name, copy_file_name, copy_delimiter, def_name,
+       database_name, access_method, attr_name, class, index_name,
+       var_name, name, file_name, recipe_name
+
+%type <str>    opt_id, opt_portal_name,
+       before_clause, after_clause, all_Op, MathOp, opt_name, opt_unique
+       result, OptUseOp, opt_class, opt_range_start, opt_range_end,
+       SpecialRuleRelation
+
+%type <str>    privileges, operation_commalist, grantee
+%type <chr>    operation
+
+%type <list>   queryblock, relation_name_list, OptTableElementList,
+       tableElementList, OptInherit, definition,
+       opt_with, def_args, def_name_list, func_argtypes, oper_argtypes,
+       OptStmtList, OptStmtBlock, opt_column_list, columnList,
+       exprList, sort_clause, sortby_list, index_params, 
+       name_list, from_clause, from_list, opt_array_bounds, nest_array_bounds,
+       expr_list, attrs, res_target_list, res_target_list2, def_list,
+       opt_indirection, group_clause, groupby_list, explain_options
+
+%type <boolean>        opt_inh_star, opt_binary, opt_instead
+
+%type <ival>   copy_dirn, archive_type, OptArchiveType, OptArchiveLocation, 
+       def_type, opt_direction, remove_type, opt_column, event
+
+%type <ival>   OptLocation, opt_move_where, fetch_how_many 
+
+%type <dstmt>  def_rest
+%type <pstmt>  purge_quals
+%type <astmt>  insert_rest
+
+%type <typnam> Typename, typname
+%type <coldef> columnDef
+%type <defelt> def_elem
+%type <node>   def_arg, columnElem, exprElem, where_clause, 
+               a_expr, AexprConst, having_clause, groupby
+%type <value>  NumConst
+%type <attr>   event_object, attr
+%type <sortby> sortby
+%type <ielem>  index_elem, func_index
+%type <range>  from_val
+%type <relexp> relation_expr
+%type <trange> time_range
+%type <target> res_target_el, res_target_el2
+%type <paramno>        ParamNo
+
+%type <ival>   Iconst
+%type <str>    Sconst
+%type <str>    Id, date
+
+
+/*
+ * If you make any token changes, remember to:
+ *     - use "yacc -d" and update parse.h
+ *     - update the keyword table in parser/keywords.c
+ */
+
+/* Keywords */
+%token ABORT_TRANS, ACL, ADD, AFTER, AGGREGATE, ALL, ALTER, AND, APPEND,
+       ARCHIVE, ARCH_STORE, AS, ASC, BACKWARD, BEFORE, BEGIN_TRANS, BINARY,
+       BY, CAST, CHANGE, CLOSE, CLUSTER, COLUMN, COMMIT, COPY, CREATE, CURRENT,
+       CURSOR, DATABASE, DECLARE, DELETE, DELIMITERS, DESC, DISTINCT, DO,
+       DROP, END_TRANS,
+       EXTEND, FETCH, FOR, FORWARD, FROM, FUNCTION, GRANT, GROUP, 
+       HAVING, HEAVY, IN, INDEX, INHERITS, INSERT, INSTEAD, INTO, 
+       ISNULL, LANGUAGE, LIGHT, LISTEN, LOAD, MERGE, MOVE, NEW, 
+       NONE, NOT, NOTHING, NOTIFY, NOTNULL, 
+        ON, OPERATOR, OPTION, OR, ORDER, 
+        PNULL, PRIVILEGES, PUBLIC, PURGE, P_TYPE, 
+        RENAME, REPLACE, RETRIEVE, RETURNS, REVOKE, ROLLBACK, RULE, 
+        SELECT, SET, SETOF, STDIN, STDOUT, STORE, 
+       TABLE, TO, TRANSACTION, UPDATE, USING, VACUUM, VALUES
+       VERSION, VIEW, WHERE, WITH, WORK
+%token EXECUTE, RECIPE, EXPLAIN, LIKE 
+
+/* Special keywords, not in the query language - see the "lex" file */
+%token <str>   IDENT, SCONST, Op 
+%token <ival>  ICONST, PARAM
+%token <dval>  FCONST
+
+/* these are not real. they are here so that they gets generated as #define's*/
+%token         OP
+
+/* precedence */
+%left  OR
+%left  AND
+%right NOT
+%right         '='
+%nonassoc LIKE
+%nonassoc Op
+%nonassoc NOTNULL
+%nonassoc ISNULL
+%left          '+' '-'
+%left          '*' '/'
+%left  '|'             /* this is the relation union op, not logical or */
+%right  ';' ':'                /* Unary Operators      */
+%nonassoc  '<' '>'
+%right   UMINUS
+%left  '.'
+%left          '[' ']' 
+%nonassoc TYPECAST
+%nonassoc REDUCE
+%%
+
+queryblock:  query queryblock
+               { parsetree = lcons($1, parsetree); }
+       | query
+               { parsetree = lcons($1, NIL); }
+       ;
+
+query:    stmt 
+       | stmt ';'                              {  $$ = $1;  }
+       ;
+
+stmt :   AddAttrStmt
+       | ClosePortalStmt
+       | CopyStmt
+       | CreateStmt
+       | ClusterStmt
+       | DefineStmt
+       | DestroyStmt
+       | ExtendStmt
+       | ExplainStmt
+       | FetchStmt
+        | GrantStmt
+       | IndexStmt
+       | MoveStmt
+        | ListenStmt
+       | ProcedureStmt
+       | PurgeStmt                     
+       | RecipeStmt
+       | RemoveOperStmt
+       | RemoveFuncStmt
+       | RemoveStmt
+       | RenameStmt
+        | RevokeStmt
+       | OptimizableStmt       
+       | RuleStmt                      
+       | TransactionStmt
+       | ViewStmt
+       | LoadStmt
+       | CreatedbStmt
+       | DestroydbStmt
+       | VacuumStmt
+       ;
+
+/*****************************************************************************
+ *
+ *     QUERY :
+ *             addattr ( attr1 = type1 .. attrn = typen ) to <relname> [*]
+ *
+ *****************************************************************************/
+
+AddAttrStmt:  ALTER TABLE relation_name opt_inh_star ADD COLUMN columnDef
+               {
+                   AddAttrStmt *n = makeNode(AddAttrStmt);
+                   n->relname = $3;
+                   n->inh = $4;
+                   n->colDef = $7;
+                   $$ = (Node *)n;
+               }
+       ;
+
+columnDef:  Id Typename
+               {  
+                   $$ = makeNode(ColumnDef);
+                   $$->colname = $1;
+                   $$->typename = $2;
+               }
+       ;
+
+       
+/*****************************************************************************
+ *     
+ *     QUERY :
+ *             close <optname>
+ *     
+ *****************************************************************************/
+
+ClosePortalStmt:  CLOSE opt_id 
+               {  
+                   ClosePortalStmt *n = makeNode(ClosePortalStmt);
+                   n->portalname = $2;
+                   $$ = (Node *)n;
+               }
+       ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY :
+ *             COPY [BINARY] <relname> FROM/TO 
+ *              [USING DELIMITERS <delimiter>]
+ *
+ *****************************************************************************/
+
+CopyStmt:  COPY opt_binary relation_name copy_dirn copy_file_name copy_delimiter
+               {
+                   CopyStmt *n = makeNode(CopyStmt);
+                   n->binary = $2;
+                   n->relname = $3;
+                   n->direction = $4;
+                   n->filename = $5;
+                   n->delimiter = $6;
+                   $$ = (Node *)n;
+               }
+       ;
+
+copy_dirn:  TO 
+               { $$ = TO; }
+       |  FROM
+               { $$ = FROM; }
+       ;
+
+/* 
+ * copy_file_name NULL indicates stdio is used. Whether stdin or stdout is
+ * used depends on the direction. (It really doesn't make sense to copy from
+ * stdout. We silently correct the "typo".      - AY 9/94
+ */
+copy_file_name:  Sconst                                { $$ = $1; }
+       | STDIN                                 { $$ = NULL; }
+       | STDOUT                                { $$ = NULL; }
+       ;
+
+opt_binary: BINARY                             { $$ = TRUE; }
+       |  /*EMPTY*/                            { $$ = FALSE; }
+       ;
+
+/*
+ * the default copy delimiter is tab but the user can configure it
+ */
+copy_delimiter: USING DELIMITERS Sconst { $$ = $3;}
+       | /* EMPTY */  { $$ = "\t"; }
+       ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY :
+ *             CREATE relname
+ *
+ *****************************************************************************/
+
+CreateStmt:  CREATE TABLE relation_name '(' OptTableElementList ')'
+               OptInherit OptArchiveType OptLocation OptArchiveLocation
+               {
+                   CreateStmt *n = makeNode(CreateStmt);
+                   n->relname = $3;
+                   n->tableElts = $5;
+                   n->inhRelnames = $7;
+                   n->archiveType = $8;
+                   n->location = $9;
+                   n->archiveLoc = $10;
+                   $$ = (Node *)n;
+               }
+       ;
+
+OptTableElementList:  tableElementList         { $$ = $1; }
+       | /* EMPTY */                           { $$ = NULL; }
+       ;
+
+tableElementList :
+         tableElementList ',' columnDef        
+               { $$ = lappend($1, $3); }
+       | columnDef                     
+               { $$ = lcons($1, NIL); }
+       ;
+
+
+OptArchiveType:  ARCHIVE '=' archive_type              { $$ = $3; }
+       | /*EMPTY*/                                     { $$ = ARCH_NONE; }
+       ;
+
+archive_type:  HEAVY                                   { $$ = ARCH_HEAVY; }
+       | LIGHT                                         { $$ = ARCH_LIGHT; }
+       | NONE                                          { $$ = ARCH_NONE; }
+       ;
+
+OptLocation:  STORE '=' Sconst
+               {  $$ = smgrin($3);  }
+       | /*EMPTY*/                             
+               {  $$ = -1;  }
+       ;
+
+OptArchiveLocation: ARCH_STORE '=' Sconst
+               {  $$ = smgrin($3);  }
+       | /*EMPTY*/                             
+               {  $$ = -1;  }
+       ;
+
+OptInherit:  INHERITS '(' relation_name_list ')'       { $$ = $3; }
+       |  /*EMPTY*/                                    { $$ = NIL; }
+       ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY :
+ *             define (type,operator,aggregate)
+ *
+ *****************************************************************************/
+
+DefineStmt:  CREATE def_type def_rest
+               {
+                   $3->defType = $2;
+                   $$ = (Node *)$3;
+               }
+       ;
+
+def_rest:  def_name definition 
+               {
+                   $$ = makeNode(DefineStmt);
+                   $$->defname = $1;
+                   $$->definition = $2;
+               }
+       ;
+
+def_type:  OPERATOR                            { $$ = OPERATOR; }
+       |  Type                                 { $$ = P_TYPE; }
+       |  AGGREGATE                            { $$ = AGGREGATE; }
+       ;
+
+def_name:  Id  |  MathOp |  Op
+       ;
+
+
+definition:  '(' def_list ')'                  { $$ = $2; }
+       ;
+
+
+def_list:  def_elem
+               { $$ = lcons($1, NIL); }
+       |  def_list ',' def_elem
+               { $$ = lappend($1, $3); }
+       ;
+
+def_elem:  def_name '=' def_arg
+               { 
+                   $$ = makeNode(DefElem);
+                   $$->defname = $1;
+                   $$->arg = (Node *)$3;
+               }
+       |  def_name
+               {
+                   $$ = makeNode(DefElem);
+                   $$->defname = $1;
+                   $$->arg = (Node *)NULL;
+               }
+       ;
+
+def_arg:  Id                   {  $$ = (Node *)makeString($1); }
+       | all_Op                {  $$ = (Node *)makeString($1); }
+       | NumConst              {  $$ = (Node *)$1; /* already a Value */ }
+       | Sconst                {  $$ = (Node *)makeString($1); }
+       | SETOF Id              { 
+                                  TypeName *n = makeNode(TypeName);
+                                  n->name = $2;
+                                  n->setof = TRUE;
+                                  n->arrayBounds = NULL;
+                                  $$ = (Node *)n;
+                               }                               
+       ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY:  
+ *             destroy <relname1> [, <relname2> .. <relnameN> ]
+ *
+ *****************************************************************************/
+
+DestroyStmt:  DROP TABLE relation_name_list            
+               { 
+                   DestroyStmt *n = makeNode(DestroyStmt);
+                   n->relNames = $3;
+                   $$ = (Node *)n;
+               }
+       ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             fetch [forward | backward] [number | all ] [ in <portalname> ]
+ *
+ *****************************************************************************/
+
+FetchStmt:  FETCH opt_direction fetch_how_many opt_portal_name
+               {
+                   FetchStmt *n = makeNode(FetchStmt);
+                   n->direction = $2;
+                   n->howMany = $3;
+                   n->portalname = $4;
+                   $$ = (Node *)n;
+               }
+       ;
+
+opt_direction:  FORWARD                                { $$ = FORWARD; }
+       | BACKWARD                              { $$ = BACKWARD; }
+       | /*EMPTY*/                             { $$ = FORWARD; /* default */ }
+       ;
+
+fetch_how_many:  Iconst                        
+              { $$ = $1;
+                if ($1 <= 0) elog(WARN,"Please specify nonnegative count for fetch"); }
+       |  ALL                          { $$ = 0; /* 0 means fetch all tuples*/}
+       |  /*EMPTY*/                    { $$ = 0; /*default*/ }
+       ;
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             GRANT [privileges] ON [relation_name_list] TO [GROUP] grantee
+ *
+ *****************************************************************************/
+
+GrantStmt: GRANT privileges ON relation_name_list TO grantee opt_with_grant
+           {
+               $$ = (Node*)makeAclStmt($2,$4,$6,'+');
+               free($2);
+               free($6);
+           }
+           ;
+
+privileges:  ALL PRIVILEGES
+               {
+                $$ = aclmakepriv("rwaR",0);
+               }
+           | ALL
+               {
+                $$ = aclmakepriv("rwaR",0);
+               }
+           | operation_commalist {
+               $$ = $1;
+               }
+           ;
+
+operation_commalist: operation {
+                       $$ = aclmakepriv("",$1);
+                       }
+                   | operation_commalist ',' operation
+                       {
+                               $$ = aclmakepriv($1,$3);
+                               free($1);
+                       }
+                   ;
+
+operation:    SELECT  {
+               $$ = ACL_MODE_RD_CHR;
+               }
+            | INSERT {
+               $$ = ACL_MODE_AP_CHR;
+               }
+            | UPDATE {
+               $$ = ACL_MODE_WR_CHR;
+               }
+            | DELETE {
+               $$ = ACL_MODE_WR_CHR;
+               }
+           | RULE {
+               $$ = ACL_MODE_RU_CHR;
+               }
+            ;
+
+grantee:      PUBLIC {
+               $$ = aclmakeuser("A","");
+               }
+           | GROUP Id {
+               $$ = aclmakeuser("G",$2);
+               }
+            | Id {
+               $$ = aclmakeuser("U",$1);
+               }
+            ;
+
+opt_with_grant : /* empty */
+            |   WITH GRANT OPTION 
+                {
+                    yyerror("WITH GRANT OPTION is not supported.  Only relation owners can set privileges");
+                }
+            ;
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             REVOKE [privileges] ON [relation_name] FROM [user]
+ *
+ *****************************************************************************/
+
+RevokeStmt: REVOKE privileges ON relation_name_list FROM grantee
+              {
+               $$ = (Node*)makeAclStmt($2,$4,$6,'-');
+               free($2);
+               free($6);
+              }
+            ;
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             move [<dirn>] [<whereto>] [<portalname>]
+ *
+ *****************************************************************************/
+
+MoveStmt:  MOVE opt_direction opt_move_where opt_portal_name
+               { 
+                   MoveStmt *n = makeNode(MoveStmt);
+                   n->direction = $2;
+                   n->to = FALSE;
+                   n->where = $3;
+                   n->portalname = $4;
+                   $$ = (Node *)n;
+               }
+       |  MOVE opt_direction TO Iconst opt_portal_name
+               { 
+                   MoveStmt *n = makeNode(MoveStmt);
+                   n->direction = $2;
+                   n->to = TRUE;
+                   n->where = $4;
+                   n->portalname = $5;
+                   $$ = (Node *)n;
+               }
+       ;
+
+opt_move_where: Iconst                         { $$ = $1; }
+       | /*EMPTY*/                             { $$ = 1; /* default */ }
+       ;
+
+opt_portal_name: IN name                       { $$ = $2;}
+       | /*EMPTY*/                             { $$ = NULL; }
+       ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             define [archive] index <indexname> on <relname>
+ *               using <access> "(" (<col> with <op>)+ ")" [with
+ *               <target_list>]
+ *
+ *  [where <qual>] is not supported anymore
+ *****************************************************************************/
+
+IndexStmt:  CREATE INDEX index_name ON relation_name
+           USING access_method '(' index_params ')'
+               {
+                   /* should check that access_method is valid,
+                      etc ... but doesn't */
+                   IndexStmt *n = makeNode(IndexStmt);
+                   n->idxname = $3;
+                   n->relname = $5;
+                   n->accessMethod = $7;
+                   n->indexParams = $9;
+                   n->withClause = NIL;
+                   n->whereClause = NULL;
+                   $$ = (Node *)n;
+               }
+       ;
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             extend index <indexname> [where <qual>]
+ *
+ *****************************************************************************/
+
+ExtendStmt:  EXTEND INDEX index_name where_clause
+               {
+                   ExtendStmt *n = makeNode(ExtendStmt);
+                   n->idxname = $3;
+                   n->whereClause = $4;
+                   $$ = (Node *)n;
+               }
+       ;
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             execute recipe <recipeName> 
+ *
+ *****************************************************************************/
+
+RecipeStmt:  EXECUTE RECIPE recipe_name 
+               {
+                   RecipeStmt *n;
+                   if (!IsTransactionBlock())
+                       elog(WARN, "EXECUTE RECIPE may only be used in begin/end transaction blocks.");
+
+                   n = makeNode(RecipeStmt);
+                   n->recipeName = $3;
+                   $$ = (Node *)n;
+               }
+       ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *              define function <fname>
+ *                     (language = <lang>, returntype = <typename> 
+ *                      [, arch_pct = <percentage | pre-defined>]
+ *                      [, disk_pct = <percentage | pre-defined>]
+ *                      [, byte_pct = <percentage | pre-defined>]
+ *                      [, perbyte_cpu = <int | pre-defined>]
+ *                      [, percall_cpu = <int | pre-defined>]
+ *                      [, iscachable])
+ *                      [arg is (<type-1> { , <type-n>})]
+ *                      as <filename or code in language as appropriate>
+ *
+ *****************************************************************************/
+
+ProcedureStmt:  CREATE FUNCTION def_name def_args 
+                  RETURNS def_arg opt_with AS Sconst LANGUAGE Sconst
+                {
+                   ProcedureStmt *n = makeNode(ProcedureStmt);
+                   n->funcname = $3;
+                   n->defArgs = $4;
+                   n->returnType = (Node *)$6;
+                   n->withClause = $7;
+                   n->as = $9;
+                   n->language = $11;
+                   $$ = (Node *)n;
+               };
+
+opt_with:  WITH definition                     { $$ = $2; }
+       |  /* EMPTY */                          { $$ = NIL; }
+       ;
+
+def_args:  '(' def_name_list ')'               { $$ = $2; }
+        |  '(' ')'                             { $$ = NIL; }
+       ;
+
+def_name_list:         name_list;      
+
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             purge <relname> [before <date>] [after <date>]
+ *               or
+ *             purge <relname>  [after<date>][before <date>] 
+ *     
+ *****************************************************************************/
+
+PurgeStmt:  PURGE relation_name purge_quals
+               { 
+                   $3->relname = $2;
+                   $$ = (Node *)$3;
+               }
+       ;
+
+purge_quals:  before_clause
+               { 
+                   $$ = makeNode(PurgeStmt);
+                   $$->beforeDate = $1;
+                   $$->afterDate = NULL;
+               }
+       |  after_clause
+               { 
+                   $$ = makeNode(PurgeStmt);
+                   $$->beforeDate = NULL;
+                   $$->afterDate = $1;
+               }
+       |  before_clause after_clause
+               { 
+                   $$ = makeNode(PurgeStmt);
+                   $$->beforeDate = $1;
+                   $$->afterDate = $2;
+               }
+       |  after_clause before_clause
+               { 
+                   $$ = makeNode(PurgeStmt);
+                   $$->beforeDate = $2;
+                   $$->afterDate = $1;
+               }
+       |  /*EMPTY*/
+               { 
+                   $$ = makeNode(PurgeStmt);
+                   $$->beforeDate = NULL;
+                   $$->afterDate = NULL;
+               }
+       ;
+
+before_clause: BEFORE date             { $$ = $2; }
+after_clause:  AFTER date              { $$ = $2; }
+
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *
+ *     remove function <funcname>
+ *             (REMOVE FUNCTION "funcname" (arg1, arg2, ...))
+ *     remove operator <opname>
+ *             (REMOVE OPERATOR "opname" (leftoperand_typ rightoperand_typ))
+ *     remove type <typename>
+ *             (REMOVE TYPE "typename")
+ *     remove rule <rulename>
+ *             (REMOVE RULE "rulename")
+ *
+ *****************************************************************************/
+
+RemoveStmt:  DROP remove_type name
+               {
+                   RemoveStmt *n = makeNode(RemoveStmt);
+                   n->removeType = $2;
+                   n->name = $3;
+                   $$ = (Node *)n;
+               }
+       ;
+
+remove_type:  AGGREGATE                {  $$ = AGGREGATE; }
+       |  Type                         {  $$ = P_TYPE; }
+       |  INDEX                        {  $$ = INDEX; }
+       |  RULE                         {  $$ = RULE; }
+       |  VIEW                         {  $$ = VIEW; }
+       ;
+
+RemoveFuncStmt:  DROP FUNCTION name '(' func_argtypes ')'
+                {
+                   RemoveFuncStmt *n = makeNode(RemoveFuncStmt);
+                   n->funcname = $3;
+                   n->args = $5;
+                   $$ = (Node *)n;
+               }
+          ;
+
+func_argtypes:  name_list                      { $$ = $1; }
+        |  /*EMPTY*/                           { $$ = NIL; }
+       ;
+
+RemoveOperStmt:  DROP OPERATOR all_Op '(' oper_argtypes ')'
+               {
+                   RemoveOperStmt *n = makeNode(RemoveOperStmt);
+                   n->opname = $3;
+                   n->args = $5;
+                   $$ = (Node *)n;
+               }
+        ;
+
+all_Op: Op | MathOp;
+
+MathOp:    '+'                 { $$ = "+"; }
+       |  '-'          { $$ = "-"; }
+       |  '*'          { $$ = "*"; }
+       |  '/'          { $$ = "/"; }
+       |  '<'          { $$ = "<"; }
+       |  '>'          { $$ = ">"; }
+       |  '='          { $$ = "="; }
+       ;
+
+oper_argtypes:  name   
+               { 
+                  elog(WARN, "parser: argument type missing (use NONE for unary operators)");
+               }
+       | name ',' name
+               { $$ = makeList(makeString($1), makeString($3), -1); }
+       | NONE ',' name         /* left unary */
+               { $$ = makeList(NULL, makeString($3), -1); }
+       | name ',' NONE         /* right unary */
+               { $$ = makeList(makeString($1), NULL, -1); }
+       ;
+
+/*****************************************************************************
+ *
+ *     QUERY:   
+ *             rename <attrname1> in <relname> [*] to <attrname2>
+ *             rename <relname1> to <relname2>
+ *     
+ *****************************************************************************/
+
+RenameStmt:  ALTER TABLE relation_name opt_inh_star 
+                 RENAME opt_column opt_name TO name
+               { 
+                   RenameStmt *n = makeNode(RenameStmt);
+                   n->relname = $3;
+                   n->inh = $4;
+                   n->column = $7;
+                   n->newname = $9;
+                   $$ = (Node *)n;
+               }
+       ;
+
+opt_name:  name                                { $$ = $1; }
+       |  /*EMPTY*/                    { $$ = NULL; }
+       ;
+
+opt_column:  COLUMN                    { $$ = COLUMN; }
+       | /*EMPTY*/                     { $$ = 0; }
+       ;
+
+
+/*****************************************************************************
+ *      
+ *     QUERY:  Define Rewrite Rule , Define Tuple Rule 
+ *             Define Rule <old rules >
+ *
+ *      only rewrite rule is supported -- ay 9/94
+ *      
+ *****************************************************************************/
+
+RuleStmt:  CREATE RULE name AS 
+          { QueryIsRule=TRUE; }
+          ON event TO event_object where_clause
+          DO opt_instead OptStmtList
+               {
+                   RuleStmt *n = makeNode(RuleStmt);
+                   n->rulename = $3;
+                   n->event = $7;
+                   n->object = $9;
+                   n->whereClause = $10;
+                   n->instead = $12;
+                   n->actions = $13;
+                   $$ = (Node *)n;
+               }
+       ;
+
+OptStmtList:  NOTHING                  { $$ = NIL; }
+       | OptimizableStmt               { $$ = lcons($1, NIL); }        
+       | '[' OptStmtBlock ']'          { $$ = $2; }
+        ;
+
+OptStmtBlock:  OptimizableStmt                 
+               { $$ = lcons($1, NIL); }
+       |  OptimizableStmt ';'
+               { $$ = lcons($1, NIL); }
+       |  OptStmtBlock OptimizableStmt
+               {  $$ = lappend($1, $2); }
+       ;
+
+event_object: relation_name '.' attr_name
+               { 
+                   $$ = makeNode(Attr);
+                   $$->relname = $1;
+                   $$->paramNo = NULL;
+                   $$->attrs = lcons(makeString($3), NIL);
+                   $$->indirection = NIL;
+               }
+       | relation_name
+               {
+                   $$ = makeNode(Attr);
+                   $$->relname = $1;
+                   $$->paramNo = NULL;
+                   $$->attrs = NIL;
+                   $$->indirection = NIL;
+               }
+       ;
+
+/* change me to select, update, etc. some day */
+event:         SELECT                          { $$ = CMD_SELECT; }
+       | UPDATE                        { $$ = CMD_UPDATE; }
+       | DELETE                        { $$ = CMD_DELETE; }
+       | INSERT                        { $$ = CMD_INSERT; }
+        ;
+
+opt_instead:  INSTEAD                  { $$ = TRUE; }
+       | /* EMPTY */                   { $$ = FALSE; }
+       ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             NOTIFY <relation_name>  can appear both in rule bodies and
+ *             as a query-level command
+ *
+ *****************************************************************************/
+
+NotifyStmt: NOTIFY relation_name 
+               {
+                   NotifyStmt *n = makeNode(NotifyStmt);
+                   n->relname = $2;
+                   $$ = (Node *)n;
+               }
+       ;
+
+ListenStmt: LISTEN relation_name 
+               {
+                   ListenStmt *n = makeNode(ListenStmt);
+                   n->relname = $2;
+                   $$ = (Node *)n;
+               }
+;
+
+
+/*****************************************************************************
+ *
+ *     Transactions:
+ *
+ *     abort transaction
+ *             (ABORT)
+ *     begin transaction
+ *             (BEGIN)
+ *     end transaction
+ *             (END)
+ *     
+ *****************************************************************************/
+
+TransactionStmt:  ABORT_TRANS TRANSACTION
+               { 
+                   TransactionStmt *n = makeNode(TransactionStmt); 
+                   n->command = ABORT_TRANS; 
+                   $$ = (Node *)n;
+               }
+       | BEGIN_TRANS TRANSACTION
+               { 
+                   TransactionStmt *n = makeNode(TransactionStmt); 
+                   n->command = BEGIN_TRANS;
+                   $$ = (Node *)n;
+               }
+       | BEGIN_TRANS WORK
+               {
+                   TransactionStmt *n = makeNode(TransactionStmt); 
+                   n->command = BEGIN_TRANS;
+                   $$ = (Node *)n;
+               }
+       | COMMIT WORK
+               {
+                   TransactionStmt *n = makeNode(TransactionStmt); 
+                   n->command = END_TRANS;
+                   $$ = (Node *)n;
+               }
+       | END_TRANS TRANSACTION
+               { 
+                   TransactionStmt *n = makeNode(TransactionStmt); 
+                   n->command = END_TRANS;
+                   $$ = (Node *)n;
+               }
+       | ROLLBACK WORK
+               {
+                   TransactionStmt *n = makeNode(TransactionStmt); 
+                   n->command = ABORT_TRANS;
+                   $$ = (Node *)n;
+               }
+
+       | ABORT_TRANS
+               { 
+                   TransactionStmt *n = makeNode(TransactionStmt); 
+                   n->command = ABORT_TRANS; 
+                   $$ = (Node *)n;
+               }
+       | BEGIN_TRANS
+               { 
+                   TransactionStmt *n = makeNode(TransactionStmt); 
+                   n->command = BEGIN_TRANS;
+                   $$ = (Node *)n;
+               }
+       | COMMIT
+               {
+                   TransactionStmt *n = makeNode(TransactionStmt); 
+                   n->command = END_TRANS;
+                   $$ = (Node *)n;
+               }
+
+       | END_TRANS
+               { 
+                   TransactionStmt *n = makeNode(TransactionStmt); 
+                   n->command = END_TRANS;
+                   $$ = (Node *)n;
+               }
+       | ROLLBACK
+               {
+                   TransactionStmt *n = makeNode(TransactionStmt); 
+                   n->command = ABORT_TRANS;
+                   $$ = (Node *)n;
+               }
+       ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             define view <viewname> '('target-list ')' [where <quals> ]
+ *
+ *****************************************************************************/
+
+ViewStmt:  CREATE VIEW name AS RetrieveStmt
+               { 
+                   ViewStmt *n = makeNode(ViewStmt);
+                   n->viewname = $3;
+                   n->query = (Query *)$5;
+                   $$ = (Node *)n;
+               }
+       ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             load "filename"
+ *
+ *****************************************************************************/
+
+LoadStmt: LOAD file_name
+                { 
+                   LoadStmt *n = makeNode(LoadStmt);
+                   n->filename = $2;
+                   $$ = (Node *)n;
+               }
+        ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             createdb dbname
+ *
+ *****************************************************************************/
+
+CreatedbStmt:  CREATE DATABASE database_name
+                {
+                   CreatedbStmt *n = makeNode(CreatedbStmt);
+                   n->dbname = $3;
+                   $$ = (Node *)n;
+               }
+        ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             destroydb dbname
+ *
+ *****************************************************************************/
+
+DestroydbStmt:  DROP DATABASE database_name
+                {
+                   DestroydbStmt *n = makeNode(DestroydbStmt);
+                   n->dbname = $3;
+                   $$ = (Node *)n;
+               }
+        ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             cluster <index_name> on <relation_name>
+ *
+ *****************************************************************************/
+
+ClusterStmt:  CLUSTER index_name ON relation_name 
+               {
+                  ClusterStmt *n = makeNode(ClusterStmt);
+                  n->relname = $4;
+                  n->indexname = $2;
+                  $$ = (Node*)n;
+               }
+       ;
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             vacuum
+ *
+ *****************************************************************************/
+
+VacuumStmt:  VACUUM
+               {
+                   $$ = (Node *)makeNode(VacuumStmt);
+               }
+         | VACUUM relation_name
+               {
+                   VacuumStmt *n = makeNode(VacuumStmt);
+                   n->vacrel = $2;
+                   $$ = (Node *)n;
+               }
+       ;
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             EXPLAIN query
+ *
+ *****************************************************************************/
+
+ExplainStmt:  EXPLAIN explain_options OptimizableStmt
+               {
+                   ExplainStmt *n = makeNode(ExplainStmt);
+                   n->query = (Query*)$3;
+                   n->options = $2;
+                   $$ = (Node *)n;
+               }
+       ;
+
+explain_options: WITH name_list
+               { $$ = $2; }
+       |  /*EMPTY*/
+               { $$ = NIL; }
+       ;
+
+/*****************************************************************************
+ *                                                                           *
+ *     Optimizable Stmts:                                                   *
+ *                                                                           *
+ *     one of the five queries processed by the planner                     *
+ *                                                                           *
+ *     [ultimately] produces query-trees as specified                       *
+ *     in the query-spec document in ~postgres/ref                          *
+ *                                                                           *
+ *****************************************************************************/
+
+OptimizableStmt:  RetrieveStmt
+       | CursorStmt    
+       | ReplaceStmt
+       | AppendStmt
+        | NotifyStmt
+        | DeleteStmt                   /* by default all are $$=$1 */
+       ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             INSERT STATEMENTS
+ *  
+ *****************************************************************************/
+
+AppendStmt:  INSERT INTO relation_name opt_column_list insert_rest
+               {
+                   $5->relname = $3;
+                   $5->cols = $4;
+                   $$ = (Node *)$5;
+                }
+       ;
+
+insert_rest: VALUES '(' exprList ')'
+               {
+                   $$ = makeNode(AppendStmt);
+                   $$->exprs = $3;
+                   $$->fromClause = NIL;
+                   $$->whereClause = NULL;
+               }
+       | SELECT exprList from_clause where_clause
+               {
+                   $$ = makeNode(AppendStmt);
+                   $$->exprs = $2;
+                   $$->fromClause = $3;
+                   $$->whereClause = $4;
+               }
+       ;
+
+opt_column_list: '(' columnList ')'            { $$ = $2; }
+       | /*EMPTY*/                             { $$ = NIL; }
+       ;
+
+columnList:    
+         columnList ',' columnElem
+               { $$ = lappend($1, $3); }
+       | columnElem
+               { $$ = lcons($1, NIL); }
+       ;
+
+columnElem: Id opt_indirection
+               {
+                   Ident *id = makeNode(Ident);
+                   id->name = $1;
+                   id->indirection = $2;
+                   $$ = (Node *)id;
+               }
+       ;
+
+exprList:  exprList ',' exprElem
+               { $$ = lappend($1, $3); }
+       | exprElem
+               { $$ = lcons($1, NIL); }
+
+       ;
+
+exprElem: a_expr 
+               {   $$ = (Node *)$1;  }
+       |  relation_name '.' '*'
+               {
+                   Attr *n = makeNode(Attr);
+                   n->relname = $1;
+                   n->paramNo = NULL;
+                   n->attrs = lcons(makeString("*"), NIL);
+                   n->indirection = NIL;
+                   $$ = (Node *)n;
+               }
+       |  '*'
+               {
+                   Attr *n = makeNode(Attr);
+                   n->relname = "*";
+                   n->paramNo = NULL;
+                   n->attrs = NIL;
+                   n->indirection = NIL;
+                   $$ = (Node *)n;
+               }
+       ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             DELETE STATEMENTS
+ *
+ *****************************************************************************/
+   
+DeleteStmt:  DELETE FROM relation_name
+            where_clause
+                {
+                   DeleteStmt *n = makeNode(DeleteStmt);
+                   n->relname = $3;
+                   n->whereClause = $4;
+                   $$ = (Node *)n;
+               }
+        ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             ReplaceStmt (UPDATE)
+ *
+ *****************************************************************************/
+
+ReplaceStmt:  UPDATE relation_name 
+             SET res_target_list
+             from_clause
+             where_clause
+                {
+                   ReplaceStmt *n = makeNode(ReplaceStmt);
+                   n->relname = $2;
+                   n->targetList = $4;
+                   n->fromClause = $5;
+                   n->whereClause = $6;
+                   $$ = (Node *)n;
+                }
+        ;
+                
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             CURSOR STATEMENTS
+ *
+ *****************************************************************************/
+
+CursorStmt:  DECLARE name opt_binary CURSOR FOR 
+            SELECT opt_unique res_target_list2 
+            from_clause where_clause sort_clause
+               {
+                   CursorStmt *n = makeNode(CursorStmt);
+
+                   /* from PORTAL name */
+                   /*
+                    *  15 august 1991 -- since 3.0 postgres does locking
+                    *  right, we discovered that portals were violating
+                    *  locking protocol.  portal locks cannot span xacts.
+                    *  as a short-term fix, we installed the check here. 
+                    *                          -- mao
+                    */
+                   if (!IsTransactionBlock())
+                       elog(WARN, "Named portals may only be used in begin/end transaction blocks.");
+
+                   n->portalname = $2;
+                   n->binary = $3;
+                   n->unique = $7;
+                   n->targetList = $8;
+                   n->fromClause = $9;
+                   n->whereClause = $10;
+                   n->orderClause = $11;
+                   $$ = (Node *)n;
+               }
+       ;
+
+
+/*****************************************************************************
+ *
+ *     QUERY:
+ *             SELECT STATEMENTS
+ *
+ *****************************************************************************/
+
+RetrieveStmt:  SELECT opt_unique res_target_list2
+              result from_clause where_clause 
+              group_clause having_clause
+              sort_clause
+               {
+                   RetrieveStmt *n = makeNode(RetrieveStmt);
+                   n->unique = $2;
+                   n->targetList = $3;
+                   n->into = $4;
+                   n->fromClause = $5;
+                   n->whereClause = $6;
+                   n->groupClause = $7;
+                   n->havingClause = $8;
+                   n->orderClause = $9;
+                   $$ = (Node *)n;
+               }
+       ;
+
+result:  INTO TABLE relation_name
+               {  $$= $3;  /* should check for archive level */  }
+       | /*EMPTY*/
+               {  $$ = NULL;  }
+       ;
+
+opt_unique:  DISTINCT          { $$ = "*"; }
+       | DISTINCT ON Id        { $$ = $3; }
+       | /*EMPTY*/             { $$ = NULL;}
+       ;
+
+sort_clause:  ORDER BY sortby_list                     { $$ = $3; }
+       |  /*EMPTY*/                            { $$ = NIL; }
+       ;
+
+sortby_list:  sortby
+               { $$ = lcons($1, NIL); }
+       | sortby_list ',' sortby
+               { $$ = lappend($1, $3); }
+       ;
+
+sortby:  Id OptUseOp
+               { 
+                   $$ = makeNode(SortBy);
+                   $$->name = $1;
+                   $$->useOp = $2;
+               }
+       | attr OptUseOp
+                { 
+                  yyerror("parse error: use 'sort by attribute_name'");
+                }
+       ;
+
+OptUseOp:  USING Op                            { $$ = $2; }
+       |  USING '<'                            { $$ = "<"; }
+       |  USING '>'                            { $$ = ">"; }
+       |  ASC                                  { $$ = "<"; }
+       |  DESC                                 { $$ = ">"; }
+       |  /*EMPTY*/                            { $$ = "<"; /*default*/ }
+       ;
+
+
+index_params: index_elem                       { $$ = lcons($1,NIL); }
+       | func_index                            { $$ = lcons($1,NIL); }
+       ;
+
+/*index_list:
+         index_list ',' index_elem
+               { $$ = lappend($1, $3); }
+       | index_elem
+               { $$ = lcons($1, NIL); }
+       ;*/
+
+func_index: name '(' name_list ')' opt_class
+               {
+                   $$ = makeNode(IndexElem);
+                   $$->name = $1;
+                   $$->args = $3;
+                   $$->class = $5;
+               }
+         ;
+
+index_elem:  attr_name opt_class
+               {
+                   $$ = makeNode(IndexElem);
+                   $$->name = $1;
+                   $$->args = NIL;
+                   $$->class = $2;
+               }
+       ;
+
+opt_class:  class
+       |  WITH class                           { $$ = $2; }
+       |  /*EMPTY*/                            { $$ = NULL; }
+       ;
+
+/*
+ *  jimmy bell-style recursive queries aren't supported in the
+ *  current system.
+ *
+ *  ...however, recursive addattr and rename supported.  make special 
+ *  cases for these.
+ * 
+ *  XXX i believe '*' should be the default behavior, but...
+ */
+opt_inh_star: '*'                      { $$ = TRUE; }
+       |  /*EMPTY*/                    { $$ = FALSE; }
+       ;
+
+relation_name_list:    name_list ;
+
+name_list: name                        
+               { $$=lcons(makeString($1),NIL); }
+       | name_list ',' name            
+               { $$=lappend($1,makeString($3)); }
+       ;       
+
+group_clause: GROUP BY groupby_list            { $$ = $3; }
+       | /*EMPTY*/                             { $$ = NIL; }
+       ;
+
+groupby_list: groupby                          { $$ = lcons($1, NIL); }
+       | groupby_list ',' groupby              { $$ = lappend($1, $3); }
+       ;
+
+groupby: Id                                    
+               { 
+                  Ident *n = makeNode(Ident);
+                  n->name = $1;
+                  n->indirection = NULL;
+                  $$ = (Node*)n;
+               }
+       | attr
+               {
+                  $$ = (Node*)$1;
+               }
+       ;
+
+having_clause: HAVING a_expr                   { $$ = $2; }
+       | /*EMPTY*/                             { $$ = NULL; }
+       ;
+
+/*****************************************************************************
+ *  
+ *  clauses common to all Optimizable Stmts:
+ *     from_clause     -
+ *      where_clause   -
+ *     
+ *****************************************************************************/
+
+from_clause:  FROM from_list                   { $$ = $2; }
+       | /*EMPTY*/                             { $$ = NIL; }
+       ;
+
+from_list:  from_list ',' from_val
+               { $$ = lappend($1, $3); }
+       |  from_val
+               { $$ = lcons($1, NIL); }
+       ;
+
+from_val:  relation_expr AS var_name
+               {
+                   $$ = makeNode(RangeVar);
+                   $$->relExpr = $1;
+                   $$->name = $3;
+               }       
+       | relation_expr var_name
+               {
+                   $$ = makeNode(RangeVar);
+                   $$->relExpr = $1;
+                   $$->name = $2;
+               }
+       | relation_expr
+               {
+                   $$ = makeNode(RangeVar);
+                   $$->relExpr = $1;
+                   $$->name = NULL;
+               }
+       ;
+
+where_clause:  WHERE a_expr            { $$ = $2; }
+       | /*EMPTY*/                     { $$ = NULL;  /* no qualifiers */ } 
+       ;
+
+relation_expr:  relation_name
+               { 
+                   /* normal relations */
+                   $$ = makeNode(RelExpr);
+                   $$->relname = $1;
+                   $$->inh = FALSE;
+                   $$->timeRange = NULL;
+               }
+       | relation_name '*'               %prec '='
+               { 
+                   /* inheiritance query */
+                   $$ = makeNode(RelExpr);
+                   $$->relname = $1;
+                   $$->inh = TRUE;
+                   $$->timeRange = NULL;
+               }
+       | relation_name time_range 
+               { 
+                   /* time-qualified query */
+                   $$ = makeNode(RelExpr);
+                   $$->relname = $1;
+                   $$->inh = FALSE;
+                   $$->timeRange = $2;
+               }
+       ;
+
+         
+time_range:  '[' opt_range_start ',' opt_range_end ']'
+               { 
+                   $$ = makeNode(TimeRange);
+                   $$->startDate = $2;
+                   $$->endDate = $4;
+               }
+       | '[' date ']'
+               { 
+                   $$ = makeNode(TimeRange);
+                   $$->startDate = $2;
+                   $$->endDate = NULL;
+               }
+        ;
+
+opt_range_start:  date
+       |  /*EMPTY*/                            { $$ = "epoch"; }
+       ;
+
+opt_range_end:  date
+       |  /*EMPTY*/                            { $$ = "now"; }
+       ;
+
+opt_array_bounds:  '[' ']' nest_array_bounds
+               {  $$ = lcons(makeInteger(-1), $3); }
+       | '[' Iconst ']' nest_array_bounds
+               {  $$ = lcons(makeInteger($2), $4); }
+       | /* EMPTY */                           
+               {  $$ = NIL; }
+       ;
+
+nest_array_bounds:  '[' ']' nest_array_bounds
+               {  $$ = lcons(makeInteger(-1), $3); }
+       | '[' Iconst ']' nest_array_bounds 
+               {  $$ = lcons(makeInteger($2), $4); }
+       | /*EMPTY*/
+               {  $$ = NIL; }
+       ;
+
+typname:  name  
+               {
+                   char *tname = xlateSqlType($1);
+                   $$ = makeNode(TypeName);
+                   $$->name = tname;
+
+                   /* Is this the name of a complex type? If so, implement
+                    * it as a set.
+                    */
+                   if (!strcmp(saved_relname, tname)) {
+                       /* This attr is the same type as the relation 
+                        * being defined. The classic example: create
+                        * emp(name=text,mgr=emp)
+                        */
+                       $$->setof = TRUE;
+                   }else if (get_typrelid((Type)type(tname))
+                               != InvalidOid) {
+                        /* (Eventually add in here that the set can only 
+                         * contain one element.)
+                         */
+                       $$->setof = TRUE;
+                   } else {
+                       $$->setof = FALSE;
+                   }
+               }
+        | SETOF name
+               {
+                   $$ = makeNode(TypeName);
+                   $$->name = $2;
+                   $$->setof = TRUE;
+               }
+        ;
+
+Typename:  typname opt_array_bounds            
+               { 
+                   $$ = $1;
+                   $$->arrayBounds = $2;
+               }
+       | name '(' Iconst ')'
+               {
+                   /*
+                    * The following implements char() and varchar().
+                    * We do it here instead of the 'typname:' production
+                    * because we don't want to allow arrays of varchar().
+                    * I haven't thought about whether that will work or not.
+                     *                             - ay 6/95
+                    */
+                   $$ = makeNode(TypeName);
+                   if (!strcasecmp($1, "char")) {
+                       $$->name = "bpchar"; /*  strdup("bpchar"); */
+                   } else if (!strcasecmp($1, "varchar")) {
+                       $$->name = "varchar"; /* strdup("varchar"); */
+                   } else {
+                       yyerror("parse error");
+                   }
+                   if ($3 < 1) {
+                       elog(WARN, "length for '%s' type must be at least 1",
+                            $1);
+                   } else if ($3 > 4096) {
+                       /* we can store a char() of length up to the size
+                          of a page (8KB) - page headers and friends but
+                          just to be safe here...  - ay 6/95 */
+                       elog(WARN, "length for '%s' type cannot exceed 4096",
+                            $1);
+                   }
+                   /* we actually implement this sort of like a varlen, so
+                      the first 4 bytes is the length. (the difference
+                      between this and "text" is that we blank-pad and 
+                      truncate where necessary */
+                   $$->typlen = 4 + $3;
+               }
+       ;
+
+
+/*****************************************************************************
+ *
+ *  expression grammar, still needs some cleanup
+ *
+ *****************************************************************************/
+
+a_expr:  attr opt_indirection
+               {
+                   $1->indirection = $2;
+                   $$ = (Node *)$1;
+               }
+       | AexprConst
+               {   $$ = $1;  }
+       | '-' a_expr %prec UMINUS
+               {   $$ = makeA_Expr(OP, "-", NULL, $2); }
+       | a_expr '+' a_expr
+               {   $$ = makeA_Expr(OP, "+", $1, $3); }
+       | a_expr '-' a_expr
+               {   $$ = makeA_Expr(OP, "-", $1, $3); }
+       | a_expr '/' a_expr
+               {   $$ = makeA_Expr(OP, "/", $1, $3); }
+       | a_expr '*' a_expr
+               {   $$ = makeA_Expr(OP, "*", $1, $3); }
+       | a_expr '<' a_expr
+               {   $$ = makeA_Expr(OP, "<", $1, $3); }
+       | a_expr '>' a_expr
+               {   $$ = makeA_Expr(OP, ">", $1, $3); }
+       | a_expr '=' a_expr
+               {   $$ = makeA_Expr(OP, "=", $1, $3); }
+       | ':' a_expr
+               {   $$ = makeA_Expr(OP, ":", NULL, $2); }
+       | ';' a_expr
+               {   $$ = makeA_Expr(OP, ";", NULL, $2); }
+       | '|' a_expr
+               {   $$ = makeA_Expr(OP, "|", NULL, $2); }
+       | AexprConst TYPECAST Typename
+               { 
+                   /* AexprConst can be either A_Const or ParamNo */
+                   if (nodeTag($1) == T_A_Const) {
+                       ((A_Const *)$1)->typename = $3;
+                   }else {
+                       ((ParamNo *)$1)->typename = $3;
+                   }
+                   $$ = (Node *)$1;
+               }
+       | CAST AexprConst AS Typename
+               {
+                   /* AexprConst can be either A_Const or ParamNo */
+                   if (nodeTag($2) == T_A_Const) {
+                       ((A_Const *)$2)->typename = $4;
+                   }else {
+                       ((ParamNo *)$2)->typename = $4;
+                   }
+                   $$ = (Node *)$2;
+               }
+       | '(' a_expr ')'
+               {   $$ = $2; }
+       | a_expr Op a_expr
+               {   $$ = makeA_Expr(OP, $2, $1, $3); }
+       | a_expr LIKE a_expr
+               {   $$ = makeA_Expr(OP, "~~", $1, $3); }
+       | a_expr NOT LIKE a_expr
+               {   $$ = makeA_Expr(OP, "!~~", $1, $4); }
+       | Op a_expr
+               {   $$ = makeA_Expr(OP, $1, NULL, $2); }
+       | a_expr Op
+               {   $$ = makeA_Expr(OP, $2, $1, NULL); }
+       | Id
+               {   /* could be a column name or a relation_name */
+                   Ident *n = makeNode(Ident);
+                   n->name = $1;
+                   n->indirection = NULL;
+                   $$ = (Node *)n;
+               }
+       | name '(' '*' ')'
+               {
+                   FuncCall *n = makeNode(FuncCall);
+                   Ident *star = makeNode(Ident);
+
+                   /* cheap hack for aggregate (eg. count) */
+                   star->name = "oid"; 
+                   n->funcname = $1;
+                   n->args = lcons(star, NIL);
+                   $$ = (Node *)n;
+               }
+       | name '(' ')'
+               {
+                   FuncCall *n = makeNode(FuncCall);
+                   n->funcname = $1;
+                   n->args = NIL;
+                   $$ = (Node *)n;
+               }
+       | name '(' expr_list ')'
+               {
+                   FuncCall *n = makeNode(FuncCall);
+                   n->funcname = $1;
+                   n->args = $3;
+                   $$ = (Node *)n;
+               }
+       | a_expr ISNULL
+               {   $$ = makeA_Expr(ISNULL, NULL, $1, NULL); }
+       | a_expr NOTNULL
+               {   $$ = makeA_Expr(NOTNULL, NULL, $1, NULL); }
+       | a_expr AND a_expr
+               {   $$ = makeA_Expr(AND, NULL, $1, $3); }
+       | a_expr OR a_expr
+               {   $$ = makeA_Expr(OR, NULL, $1, $3); }
+       | NOT a_expr
+               {   $$ = makeA_Expr(NOT, NULL, NULL, $2); }
+       ;
+
+opt_indirection:  '[' a_expr ']' opt_indirection 
+               {
+                   A_Indices *ai = makeNode(A_Indices);
+                   ai->lidx = NULL;
+                   ai->uidx = $2;
+                   $$ = lcons(ai, $4);
+               }
+       | '[' a_expr ':' a_expr ']' opt_indirection 
+               {
+                   A_Indices *ai = makeNode(A_Indices);
+                   ai->lidx = $2;
+                   ai->uidx = $4;
+                   $$ = lcons(ai, $6);
+               }
+       | /* EMPTY */                   
+               {   $$ = NIL; }
+       ;
+
+expr_list: a_expr
+               { $$ = lcons($1, NIL); }
+       |  expr_list ',' a_expr
+               { $$ = lappend($1, $3); }
+       ;
+
+attr:  relation_name '.' attrs
+               {
+                   $$ = makeNode(Attr);
+                   $$->relname = $1;
+                   $$->paramNo = NULL;
+                   $$->attrs = $3;
+                   $$->indirection = NULL;
+               }
+       | ParamNo '.' attrs
+               {
+                   $$ = makeNode(Attr);
+                   $$->relname = NULL;
+                   $$->paramNo = $1;
+                   $$->attrs = $3;
+                   $$->indirection = NULL;
+               }
+       ;
+
+attrs:    attr_name                            
+               { $$ = lcons(makeString($1), NIL); }
+       | attrs '.' attr_name             
+               { $$ = lappend($1, makeString($3)); }
+       | attrs '.' '*'
+               { $$ = lappend($1, makeString("*")); }
+       ;
+
+
+/*****************************************************************************
+ *
+ *  target lists
+ *
+ *****************************************************************************/
+
+res_target_list:  res_target_list ',' res_target_el    
+               {   $$ = lappend($1,$3);  }
+       | res_target_el                         
+               {   $$ = lcons($1, NIL);  }
+       | '*'
+               {
+                   ResTarget *rt = makeNode(ResTarget);
+                   Attr *att = makeNode(Attr);
+                   att->relname = "*";
+                   att->paramNo = NULL;
+                   att->attrs = NULL;
+                   att->indirection = NIL;
+                   rt->name = NULL;
+                   rt->indirection = NULL;
+                   rt->val = (Node *)att;
+                   $$ = lcons(rt, NIL);
+               }
+       ;
+
+res_target_el: Id opt_indirection '=' a_expr
+               {
+                   $$ = makeNode(ResTarget);
+                   $$->name = $1;
+                   $$->indirection = $2;
+                   $$->val = (Node *)$4;
+               }
+       | attr opt_indirection
+               {
+                   $$ = makeNode(ResTarget);
+                   $$->name = NULL;
+                   $$->indirection = $2;
+                   $$->val = (Node *)$1;
+               }
+       | relation_name '.' '*'
+               {   
+                   Attr *att = makeNode(Attr);
+                   att->relname = $1;
+                   att->paramNo = NULL;
+                   att->attrs = lcons(makeString("*"), NIL);
+                   att->indirection = NIL;
+                   $$ = makeNode(ResTarget);
+                   $$->name = NULL;
+                   $$->indirection = NULL;
+                   $$->val = (Node *)att;
+               }
+       ;                
+
+/*
+** target list for select.
+** should get rid of the other but is still needed by the defunct retrieve into
+** and update (uses a subset)
+*/
+res_target_list2:
+         res_target_list2 ',' res_target_el2   
+               {   $$ = lappend($1, $3);  }
+       | res_target_el2                        
+               {   $$ = lcons($1, NIL);  }
+       | '*'
+               {
+                   ResTarget *rt = makeNode(ResTarget);
+                   Attr *att = makeNode(Attr);
+                   att->relname = "*";
+                   att->paramNo = NULL;
+                   att->attrs = NULL;
+                   att->indirection = NIL;
+                   rt->name = NULL;
+                   rt->indirection = NULL;
+                   rt->val = (Node *)att;
+                   $$ = lcons(rt, NIL);
+               }
+       ;
+
+/* AS is not optional because shift/red conflict with unary ops */
+res_target_el2: a_expr AS Id 
+               {
+                   $$ = makeNode(ResTarget);
+                   $$->name = $3;
+                   $$->indirection = NULL;
+                   $$->val = (Node *)$1;
+               }
+       | a_expr
+               {
+                   $$ = makeNode(ResTarget);
+                   $$->name = NULL;
+                   $$->indirection = NULL;
+                   $$->val = (Node *)$1;
+               }
+       | relation_name '.' '*'
+               {
+                   Attr *att = makeNode(Attr);
+                   att->relname = $1;
+                   att->paramNo = NULL;
+                   att->attrs = lcons(makeString("*"), NIL);
+                   att->indirection = NIL;
+                   $$ = makeNode(ResTarget);
+                   $$->name = NULL;
+                   $$->indirection = NULL;
+                   $$->val = (Node *)att;
+               }
+       ;
+
+opt_id:  Id                                    { $$ = $1; }
+       | /* EMPTY */                           { $$ = NULL; }
+       ;
+
+relation_name:  SpecialRuleRelation
+               {
+                   $$ = $1;
+                   strcpy(saved_relname, $1);
+               }
+       | Id
+               {
+                   /* disallow refs to magic system tables */
+                   if (strcmp(LogRelationName, $1) == 0
+                      || strcmp(VariableRelationName, $1) == 0
+                      || strcmp(TimeRelationName, $1) == 0
+                      || strcmp(MagicRelationName, $1) == 0) {
+                       elog(WARN, "%s cannot be accessed by users", $1);
+                   } else {
+                       $$ = $1;
+                   }
+                   strcpy(saved_relname, $1);
+               }
+       ;
+
+database_name:         Id              { $$ = $1; };
+access_method:                 Id              { $$ = $1; };
+attr_name:             Id              { $$ = $1; };
+class:                         Id              { $$ = $1; };
+index_name:            Id              { $$ = $1; };
+var_name:              Id              { $$ = $1; };
+name:                  Id              { $$ = $1; };
+
+date:                  Sconst          { $$ = $1; };
+file_name:             Sconst          { $$ = $1; };
+recipe_name:           Id              { $$ = $1; };
+
+AexprConst:  Iconst
+               {  
+                   A_Const *n = makeNode(A_Const);
+                   n->val.type = T_Integer;
+                   n->val.val.ival = $1;
+                   $$ = (Node *)n;
+               }
+       | FCONST
+               {  
+                   A_Const *n = makeNode(A_Const);
+                   n->val.type = T_Float;
+                   n->val.val.dval = $1;
+                   $$ = (Node *)n;
+               }
+       | Sconst
+               {  
+                   A_Const *n = makeNode(A_Const);
+                   n->val.type = T_String;
+                   n->val.val.str = $1;
+                   $$ = (Node *)n;
+               }
+       | ParamNo
+               {   $$ = (Node *)$1;  }
+       | Pnull         
+               {       
+                   A_Const *n = makeNode(A_Const);
+                   n->val.type = T_Null;
+                   $$ = (Node *)n;
+               }
+       ;
+
+ParamNo:  PARAM
+               {
+                   $$ = makeNode(ParamNo);
+                   $$->number = $1;
+               }
+       ;
+
+NumConst:  Iconst                      { $$ = makeInteger($1); }
+       |  FCONST                       { $$ = makeFloat($1); }
+       ;
+
+Iconst:  ICONST                                { $$ = $1; };
+Sconst:         SCONST                         { $$ = $1; };
+
+Id:  IDENT                             { $$ = $1; };
+
+SpecialRuleRelation:  CURRENT
+               { 
+                   if (QueryIsRule)
+                       $$ = "*CURRENT*";
+                   else 
+                       elog(WARN,"CURRENT used in non-rule query");
+               }
+       | NEW
+               { 
+                   if (QueryIsRule)
+                       $$ = "*NEW*";
+                   else 
+                       elog(WARN,"NEW used in non-rule query"); 
+               }
+       ;
+
+Type:  P_TYPE;
+Pnull: PNULL;
+
+
+%%
+
+static Node *makeA_Expr(int op, char *opname, Node *lexpr, Node *rexpr)
+{
+    A_Expr *a = makeNode(A_Expr);
+    a->oper = op;
+    a->opname = opname;
+    a->lexpr = lexpr;
+    a->rexpr = rexpr;
+    return (Node *)a;
+}
+
+static char *
+xlateSqlType(char *name)
+{
+    if (!strcasecmp(name,"int") ||
+       !strcasecmp(name,"integer"))
+       return "int4"; /* strdup("int4") --   strdup leaks memory here */
+    else if (!strcasecmp(name, "smallint"))
+       return "int2";
+    else if (!strcasecmp(name, "float") ||
+            !strcasecmp(name, "real"))
+       return "float4";
+    else
+       return name;
+}
+
+void parser_init(Oid *typev, int nargs)
+{
+    QueryIsRule = false;
+    saved_relname[0]= '\0';
+
+    param_type_init(typev, nargs);
+}
+
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
new file mode 100644 (file)
index 0000000..c8cfb20
--- /dev/null
@@ -0,0 +1,179 @@
+/*-------------------------------------------------------------------------
+ *
+ * keywords.c--
+ *    lexical token lookup for reserved words in postgres SQL
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <ctype.h>
+#include <string.h>
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+#include "parse.h"
+#include "utils/elog.h"
+#include "parser/keywords.h"
+#include "parser/dbcommands.h"         /* createdb, destroydb stop_vacuum */
+
+
+/*
+ * List of (keyword-name, keyword-token-value) pairs.
+ *
+ * !!WARNING!!: This list must be sorted, because binary
+ *      search is used to locate entries.
+ */
+static ScanKeyword ScanKeywords[] = {
+       /* name                 value           */
+       { "abort",              ABORT_TRANS     },
+       { "acl",                ACL             },
+       { "add",                ADD             },
+       { "after",              AFTER           },
+       { "aggregate",          AGGREGATE       },
+       { "all",                ALL             },
+       { "alter",              ALTER           },
+       { "and",                AND             },
+       { "append",             APPEND          },
+       { "archIve",            ARCHIVE         },   /* XXX crooked: I < _ */
+       { "arch_store",         ARCH_STORE      },
+       { "archive",            ARCHIVE         },   /* XXX crooked: i > _ */
+        { "as",                 AS              },
+        { "asc",               ASC             },
+       { "backward",           BACKWARD        },
+       { "before",             BEFORE          },
+       { "begin",              BEGIN_TRANS     },
+       { "binary",             BINARY          },
+       { "by",                 BY              },
+       { "cast",               CAST            },
+       { "change",             CHANGE          },
+       { "close",              CLOSE           },
+       { "cluster",            CLUSTER         },
+       { "column",             COLUMN          },
+       { "commit",             COMMIT          },
+       { "copy",               COPY            },
+       { "create",             CREATE          },
+       { "current",            CURRENT         },
+       { "cursor",             CURSOR          },
+       { "database",           DATABASE        },
+       { "declare",            DECLARE         },
+       { "delete",             DELETE          },
+       { "delimiters",         DELIMITERS      },
+       { "desc",               DESC            },
+       { "distinct",           DISTINCT        },
+       { "do",                 DO              },
+       { "drop",               DROP            },
+       { "end",                END_TRANS       },
+       { "execute",            EXECUTE         },
+       { "explain",            EXPLAIN         },
+       { "extend",             EXTEND          },
+       { "fetch",              FETCH           },
+       { "for",                FOR             },
+       { "forward",            FORWARD         },
+       { "from",               FROM            },
+       { "function",           FUNCTION        },
+       { "grant",              GRANT           },
+       { "group",              GROUP           },
+       { "having",             HAVING          },
+       { "heavy",              HEAVY           },
+       { "in",                 IN              },
+       { "index",              INDEX           },
+       { "inherits",           INHERITS        },
+       { "insert",             INSERT          },
+       { "instead",            INSTEAD         },
+       { "into",               INTO            },
+       { "isnull",             ISNULL          },
+       { "language",           LANGUAGE        },
+       { "light",              LIGHT           },
+       { "like",               LIKE            },
+       { "listen",             LISTEN          },
+       { "load",               LOAD            },
+       { "merge",              MERGE           },
+       { "move",               MOVE            },
+       { "new",                NEW             },
+       { "none",               NONE            },
+       { "not",                NOT             },
+       { "nothing",            NOTHING         },
+       { "notify",             NOTIFY          },
+       { "notnull",            NOTNULL         },
+       { "null",               PNULL           },
+       { "on",                 ON              },
+       { "operator",           OPERATOR        },
+        { "option",             OPTION          },
+       { "or",                 OR              },
+       { "order",              ORDER           },
+        { "privileges",         PRIVILEGES      },
+       { "public",             PUBLIC          },
+       { "purge",              PURGE           },
+       { "recipe",             RECIPE          },
+       { "rename",             RENAME          },
+       { "replace",            REPLACE         },
+       { "retrieve",           RETRIEVE        },
+       { "returns",            RETURNS         },
+        { "revoke",             REVOKE          },
+       { "rollback",           ROLLBACK        },
+       { "rule",               RULE            },
+       { "select",             SELECT          },
+       { "set",                SET             },
+       { "setof",              SETOF           },
+       { "stdin",              STDIN           },
+       { "stdout",             STDOUT          },
+       { "store",              STORE           },
+       { "table",              TABLE           },
+       { "to",                 TO              },
+       { "transaction",        TRANSACTION     },
+       { "type",               P_TYPE          },
+       { "update",             UPDATE          },
+       { "using",              USING           },
+       { "vacuum",             VACUUM          },
+       { "values",             VALUES          },
+       { "version",            VERSION         },
+       { "view",               VIEW            },
+       { "where",              WHERE           },
+       { "with",               WITH            },
+       { "work",               WORK            },
+};
+
+ScanKeyword *
+ScanKeywordLookup(char *text)
+{
+    ScanKeyword        *low    = &ScanKeywords[0];
+    ScanKeyword        *high   = endof(ScanKeywords) - 1;
+    ScanKeyword        *middle;
+    int                difference;
+    
+    while (low <= high) {
+       middle = low + (high - low) / 2;
+       /* keywords case-insensitive (for SQL) -- ay 8/94 */
+       difference = strcasecmp(middle->name, text);    
+       if (difference == 0)
+           return (middle);
+       else if (difference < 0)
+           low = middle + 1;
+       else
+           high = middle - 1;
+    }
+    
+    return (NULL);
+}
+
+char*
+AtomValueGetString(int atomval)
+{
+    ScanKeyword *low = &ScanKeywords[0];
+    ScanKeyword *high = endof(ScanKeywords) - 1;
+    int keyword_list_length = (high-low);
+    int i;
+    
+    for (i=0; i < keyword_list_length  ; i++ )
+       if (ScanKeywords[i].value == atomval )
+           return(ScanKeywords[i].name);
+    
+    elog(WARN,"AtomGetString called with bogus atom # : %d", atomval );
+    return(NULL);
+}
diff --git a/src/backend/parser/keywords.h b/src/backend/parser/keywords.h
new file mode 100644 (file)
index 0000000..18c467a
--- /dev/null
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * keywords.h--
+ *    string,atom lookup thingy, reduces strcmp traffic greatly   
+ *    in the bowels of the system.  Look for actual defs in lib/C/atoms.c
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        KEYWORDS_H
+#define        KEYWORDS_H
+
+typedef struct ScanKeyword {
+    char *name;
+    int        value;
+} ScanKeyword;
+
+extern ScanKeyword *ScanKeywordLookup(char *text);
+extern char* AtomValueGetString(int atomval);
+
+#endif /* KEYWORDS_H */
diff --git a/src/backend/parser/parse_query.c b/src/backend/parser/parse_query.c
new file mode 100644 (file)
index 0000000..cc3a8fa
--- /dev/null
@@ -0,0 +1,653 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_query.c--
+ *    take an "optimizable" stmt and make the query tree that
+ *     the planner requires.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <ctype.h>
+#include <string.h>
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "utils/tqual.h"
+#include "access/tupmacs.h"
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/acl.h"          /* for ACL_NO_PRIV_WARNING */
+#include "utils/rel.h"                 /* Relation stuff */
+
+#include "utils/syscache.h"
+#include "catalog/pg_type.h"
+#include "catalog_utils.h"
+#include "parser/parse_query.h"
+/* #include "parser/io.h" */
+#include "utils/lsyscache.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
+#include "nodes/parsenodes.h"
+#include "nodes/makefuncs.h"
+
+Oid *param_type_info;
+int pfunc_num_args;
+
+extern int Quiet;
+
+
+/* given range variable, return id of variable; position starts with 1 */
+int
+RangeTablePosn(List *rtable, char *rangevar)
+{
+    int index;
+    List *temp;
+    
+    index = 1;
+/*    temp = pstate->p_rtable; */
+    temp = rtable;
+    while (temp != NIL) {
+       RangeTblEntry *rt_entry = lfirst(temp);
+
+       if (!strcmp(rt_entry->refname, rangevar))
+           return index;
+
+       temp = lnext(temp);
+       index++;
+    }
+    return(0);
+}
+
+char*
+VarnoGetRelname(ParseState *pstate, int vnum)
+{
+    int i;
+    List *temp = pstate->p_rtable;
+    for( i = 1; i < vnum ; i++) 
+       temp = lnext(temp);
+    return(((RangeTblEntry*)lfirst(temp))->relname);
+}
+
+
+RangeTblEntry *
+makeRangeTableEntry(char *relname,
+                   bool inh,
+                   TimeRange *timeRange,
+                   char *refname)
+{
+    Relation relation;
+    RangeTblEntry *ent = makeNode(RangeTblEntry);
+
+    ent->relname = pstrdup(relname);
+    ent->refname = refname;
+
+    relation = heap_openr(ent->relname);
+    if (relation == NULL) {
+       elog(WARN,"%s: %s",
+            relname, ACL_NO_PRIV_WARNING);
+    }
+
+    /*
+     * Flags - zero or more from archive,inheritance,union,version
+     *  or recursive (transitive closure)
+     * [we don't support them all -- ay 9/94 ]
+     */
+    ent->inh = inh;
+
+    ent->timeRange = timeRange;
+    
+    /* RelOID */
+    ent->relid = RelationGetRelationId(relation);
+
+    /*
+     * close the relation we're done with it for now.
+     */
+    heap_close(relation);
+    return ent;
+}
+
+/*
+ * expandAll -
+ *    makes a list of attributes
+ *    assumes reldesc caching works
+ */
+List *
+expandAll(ParseState* pstate, char *relname, int *this_resno)
+{
+    Relation rdesc;
+    List *tall = NIL;
+    Var *varnode;
+    int i, maxattrs, first_resno;
+    int type_id, type_len, vnum;
+    char *physical_relname;
+    
+    first_resno = *this_resno;
+    
+    /* printf("\nExpanding %.*s.all\n", NAMEDATALEN, relname); */
+    vnum = RangeTablePosn(pstate->p_rtable, relname);
+    if ( vnum == 0 ) {
+       pstate->p_rtable = lappend(pstate->p_rtable,
+                                  makeRangeTableEntry(relname, FALSE, NULL,
+                                                      relname));
+       vnum = RangeTablePosn(pstate->p_rtable, relname);
+    }
+    
+    physical_relname = VarnoGetRelname(pstate, vnum);
+    
+    rdesc = heap_openr(physical_relname);
+    
+    if (rdesc == NULL ) {
+       elog(WARN,"Unable to expand all -- heap_openr failed on %s",
+            physical_relname);
+       return NIL;
+    }
+    maxattrs = RelationGetNumberOfAttributes(rdesc);
+    
+    for ( i = maxattrs-1 ; i > -1 ; --i ) {
+       char *attrname;
+       TargetEntry *rte = makeNode(TargetEntry);
+       
+       attrname = pstrdup ((rdesc->rd_att->attrs[i]->attname).data);
+       varnode = (Var*)make_var(pstate, relname, attrname, &type_id);
+       type_len = (int)tlen(get_id_type(type_id));
+
+       /* Even if the elements making up a set are complex, the
+        * set itself is not. */
+       
+       rte->resdom = makeResdom((AttrNumber) i + first_resno, 
+                                (Oid)type_id,
+                                (Size)type_len,
+                                attrname,
+                                (Index)0,
+                                (Oid)0,
+                                0);
+       rte->expr = (Node *)varnode;
+       tall = lcons(rte, tall);
+    }
+    
+    /*
+     * Close the reldesc - we're done with it now
+     */
+    heap_close(rdesc);
+    *this_resno = first_resno + maxattrs;
+    return(tall);
+}
+
+TimeQual
+makeTimeRange(char *datestring1,
+             char *datestring2,
+             int timecode)     /* 0 = snapshot , 1 = timerange */
+{
+    TimeQual   qual;
+    AbsoluteTime t1,t2;
+    
+    switch (timecode) {
+    case 0:
+       if (datestring1 == NULL) {
+           elog(WARN, "MakeTimeRange: bad snapshot arg");
+       }
+       t1 = nabstimein(datestring1);
+       if (!AbsoluteTimeIsValid(t1)) {
+           elog(WARN, "bad snapshot time: \"%s\"",
+                datestring1);
+       }
+       qual = TimeFormSnapshotTimeQual(t1);
+       break;
+    case 1:
+       if (datestring1 == NULL) {
+           t1 = NOSTART_ABSTIME;
+       } else {
+           t1 = nabstimein(datestring1);
+           if (!AbsoluteTimeIsValid(t1)) {
+               elog(WARN,
+                    "bad range start time: \"%s\"",
+                    datestring1);
+           }
+       }
+       if (datestring2 == NULL) {
+           t2 = NOEND_ABSTIME;
+       } else {
+           t2 = nabstimein(datestring2);
+           if (!AbsoluteTimeIsValid(t2)) {
+               elog(WARN,
+                    "bad range end time: \"%s\"",
+                    datestring2);
+           }
+       }
+       qual = TimeFormRangedTimeQual(t1,t2);
+       break;
+    default:
+       elog(WARN, "MakeTimeRange: internal parser error");
+    }
+    return qual;
+}
+
+static void
+disallow_setop(char *op, Type optype, Node *operand)
+{
+    if (operand==NULL)
+       return;
+    
+    if (nodeTag(operand) == T_Iter) {
+       elog(NOTICE, "An operand to the '%s' operator returns a set of %s,",
+            op, tname(optype));
+       elog(WARN, "but '%s' takes single values, not sets.",
+            op);
+    }
+}
+
+static Node *
+make_operand(char *opname,
+            Node *tree,
+            int orig_typeId,
+            int true_typeId)
+{
+    Node *result;
+    Type true_type;
+    Datum val;
+    Oid infunc;
+    
+    if (tree != NULL) {
+       result = tree;
+       true_type = get_id_type(true_typeId);
+       disallow_setop(opname, true_type, result);
+       if (true_typeId != orig_typeId) {       /* must coerce */
+           Const *con= (Const *)result;
+
+           Assert(nodeTag(result)==T_Const);
+           val = (Datum)textout((struct varlena *)
+                                con->constvalue);
+           infunc = typeid_get_retinfunc(true_typeId);
+           con = makeNode(Const);
+           con->consttype = true_typeId;
+           con->constlen = tlen(true_type);
+           con->constvalue = (Datum)fmgr(infunc,
+                                         val,
+                                         get_typelem(true_typeId),
+                                         -1 /* for varchar() type */);
+           con->constisnull = false;
+           con->constbyval = true;
+           con->constisset = false;
+           result = (Node *)con;
+       }
+    }else {
+       Const *con= makeNode(Const);
+
+       con->consttype = true_typeId;
+       con->constlen = 0;
+       con->constvalue = (Datum)(struct varlena *)NULL;
+       con->constisnull = true;
+       con->constbyval = true;
+       con->constisset = false;
+       result = (Node *)con;
+    }
+    
+    return result;
+}
+
+
+Expr *
+make_op(char *opname, Node *ltree, Node *rtree)
+{
+    int ltypeId, rtypeId;
+    Operator temp;
+    OperatorTupleForm opform;
+    Oper *newop;
+    Node *left, *right;
+    Expr *result;
+    
+    if (rtree == NULL) {
+
+       /* right operator */
+       ltypeId = (ltree==NULL) ? UNKNOWNOID : exprType(ltree);
+       temp = right_oper(opname, ltypeId);
+       opform = (OperatorTupleForm) GETSTRUCT(temp);
+       left = make_operand(opname, ltree, ltypeId, opform->oprleft);
+       right = NULL;
+
+    }else if (ltree == NULL) {
+
+       /* left operator */
+       rtypeId = (rtree==NULL) ? UNKNOWNOID : exprType(rtree);
+       temp = left_oper(opname, rtypeId);
+       opform = (OperatorTupleForm) GETSTRUCT(temp);
+       right = make_operand(opname, rtree, rtypeId, opform->oprright);
+       left = NULL;
+
+    }else {
+
+       /* binary operator */
+       ltypeId = (ltree==NULL) ? UNKNOWNOID : exprType(ltree);
+       rtypeId = (rtree==NULL) ? UNKNOWNOID : exprType(rtree);
+       temp = oper(opname, ltypeId, rtypeId);
+       opform = (OperatorTupleForm) GETSTRUCT(temp);
+       left = make_operand(opname, ltree, ltypeId, opform->oprleft);
+       right = make_operand(opname, rtree, rtypeId, opform->oprright);
+    }
+
+    newop = makeOper(oprid(temp),      /* opno */
+                    InvalidOid,        /* opid */
+                    opform->oprresult, /* operator result type */
+                    0,
+                    NULL);
+
+    result = makeNode(Expr);
+    result->typeOid = opform->oprresult;
+    result->opType = OP_EXPR;
+    result->oper = (Node *)newop;
+
+    if (!left) {
+       result->args = lcons(right, NIL);
+    } else if (!right) {
+       result->args = lcons(left, NIL);
+    } else {
+       result->args = lcons(left, lcons(right, NIL));
+    }
+
+    return result;
+}
+
+int
+find_atttype(Oid relid, char *attrname)
+{
+    int attid, vartype;
+    Relation rd;
+    
+    rd = heap_open(relid);
+    if (!RelationIsValid(rd)) {
+       rd = heap_openr(tname(get_id_type(relid)));
+       if (!RelationIsValid(rd))
+           elog(WARN, "cannot compute type of att %s for relid %d",
+                attrname, relid);
+    }
+    
+    attid =  nf_varattno(rd, attrname);
+    
+    if (attid == InvalidAttrNumber) 
+        elog(WARN, "Invalid attribute %s\n", attrname);
+    
+    vartype = att_typeid(rd , attid);
+    
+    /*
+     * close relation we're done with it now
+     */
+    heap_close(rd);
+    
+    return (vartype);
+}
+
+
+Var *
+make_var(ParseState *pstate, char *relname, char *attrname, int *type_id)
+{
+    Var *varnode;
+    int vnum, attid, vartypeid;
+    Relation rd;
+    
+    vnum = RangeTablePosn(pstate->p_rtable, relname);
+    
+    if (vnum == 0) {
+       pstate->p_rtable =
+           lappend(pstate->p_rtable,
+                    makeRangeTableEntry(relname, FALSE,
+                                        NULL, relname));
+       vnum = RangeTablePosn (pstate->p_rtable, relname);
+       relname = VarnoGetRelname(pstate, vnum);
+    } else {
+       relname = VarnoGetRelname(pstate, vnum);
+    }
+    
+    rd = heap_openr(relname);
+/*    relid = RelationGetRelationId(rd); */
+    attid =  nf_varattno(rd, (char *) attrname);
+    if (attid == InvalidAttrNumber) 
+       elog(WARN, "Invalid attribute %s\n", attrname);
+    vartypeid = att_typeid(rd, attid);
+
+    varnode = makeVar(vnum, attid, vartypeid, vnum, attid);
+
+    /*
+     * close relation we're done with it now
+     */
+    heap_close(rd);
+
+    *type_id = vartypeid;
+    return varnode;
+}
+
+/*
+ *  make_array_ref() -- Make an array reference node.
+ *
+ *     Array references can hang off of arbitrary nested dot (or
+ *     function invocation) expressions.  This routine takes a
+ *     tree generated by ParseFunc() and an array index and
+ *     generates a new array reference tree.  We do some simple
+ *     typechecking to be sure the dereference is valid in the
+ *     type system, but we don't do any bounds checking here.
+ *
+ *  indirection is a list of A_Indices
+ */
+ArrayRef *
+make_array_ref(Node *expr,
+              List *indirection)
+{
+    Oid typearray;
+    HeapTuple type_tuple;
+    TypeTupleForm type_struct_array, type_struct_element;
+    ArrayRef *aref;
+    int reftype;
+    List *upperIndexpr=NIL;
+    List *lowerIndexpr=NIL;
+    
+    typearray = (Oid) exprType(expr);
+    
+    type_tuple = SearchSysCacheTuple(TYPOID, 
+                                    ObjectIdGetDatum(typearray), 
+                                    0,0,0);
+    
+    if (!HeapTupleIsValid(type_tuple))
+       elog(WARN, "make_array_ref: Cache lookup failed for type %d\n",
+            typearray);
+    
+    /* get the array type struct from the type tuple */
+    type_struct_array = (TypeTupleForm) GETSTRUCT(type_tuple);
+    
+    if (type_struct_array->typelem == InvalidOid) {
+       elog(WARN, "make_array_ref: type %s is not an array",
+            (Name)&(type_struct_array->typname.data[0]));
+    }
+    
+    /* get the type tuple for the element type */
+    type_tuple = SearchSysCacheTuple(TYPOID, 
+                            ObjectIdGetDatum(type_struct_array->typelem),
+                                    0,0,0);
+    if (!HeapTupleIsValid(type_tuple))
+       elog(WARN, "make_array_ref: Cache lookup failed for type %d\n",
+            typearray);
+    
+    type_struct_element = (TypeTupleForm) GETSTRUCT(type_tuple);
+
+    while(indirection!=NIL) {
+       A_Indices *ind = lfirst(indirection);
+       if (ind->lidx) {
+           /* XXX assumes all lower indices non null in this case
+            */
+           lowerIndexpr = lappend(lowerIndexpr, ind->lidx);
+       }
+       upperIndexpr = lappend(upperIndexpr, ind->uidx);
+       indirection = lnext(indirection);
+    }
+    aref = makeNode(ArrayRef);
+    aref->refattrlength = type_struct_array->typlen;
+    aref->refelemlength = type_struct_element->typlen;
+    aref->refelemtype = type_struct_array->typelem;
+    aref->refelembyval = type_struct_element->typbyval;
+    aref->refupperindexpr = upperIndexpr;
+    aref->reflowerindexpr = lowerIndexpr;
+    aref->refexpr = expr;
+    aref->refassgnexpr = NULL;
+
+    if (lowerIndexpr == NIL) /* accessing a single array element */
+       reftype = aref->refelemtype;
+    else /* request to clip a part of the array, the result is another array */
+       reftype = typearray;
+
+    /* we change it to reflect the true type; since the original refelemtype
+     * doesn't seem to get used anywhere. - ay 10/94
+     */
+    aref->refelemtype = reftype;       
+
+    return aref;
+}
+
+ArrayRef *
+make_array_set(Expr *target_expr,
+              List *upperIndexpr,
+              List *lowerIndexpr,
+              Expr *expr)
+{
+    Oid typearray;
+    HeapTuple type_tuple;
+    TypeTupleForm type_struct_array;
+    TypeTupleForm type_struct_element;
+    ArrayRef *aref;
+    int reftype;
+    
+    typearray = exprType((Node*)target_expr);
+    
+    type_tuple = SearchSysCacheTuple(TYPOID, 
+                                    ObjectIdGetDatum(typearray), 
+                                    0,0,0);
+    
+    if (!HeapTupleIsValid(type_tuple))
+       elog(WARN, "make_array_ref: Cache lookup failed for type %d\n",
+            typearray);
+    
+    /* get the array type struct from the type tuple */
+    type_struct_array = (TypeTupleForm) GETSTRUCT(type_tuple);
+    
+    if (type_struct_array->typelem == InvalidOid) {
+       elog(WARN, "make_array_ref: type %s is not an array",
+            (Name)&(type_struct_array->typname.data[0]));
+    }
+    /* get the type tuple for the element type */
+    type_tuple = SearchSysCacheTuple(TYPOID, 
+                                    ObjectIdGetDatum(type_struct_array->typelem),
+                                    0,0,0);
+    
+    if (!HeapTupleIsValid(type_tuple))
+       elog(WARN, "make_array_ref: Cache lookup failed for type %d\n",
+            typearray);
+    
+    type_struct_element = (TypeTupleForm) GETSTRUCT(type_tuple);
+    
+    aref = makeNode(ArrayRef);
+    aref->refattrlength = type_struct_array->typlen;
+    aref->refelemlength = type_struct_element->typlen;
+    aref->refelemtype = type_struct_array->typelem;
+    aref->refelembyval = type_struct_element->typbyval;
+    aref->refupperindexpr = upperIndexpr;
+    aref->reflowerindexpr = lowerIndexpr;
+    aref->refexpr = (Node*)target_expr;
+    aref->refassgnexpr = (Node*)expr;
+
+    if (lowerIndexpr == NIL) /* accessing a single array element */
+       reftype = aref->refelemtype;
+    else /* request to set a part of the array, by another array */
+       reftype = typearray;
+
+    aref->refelemtype = reftype;
+    
+    return aref;
+}
+
+/*
+ * 
+ * make_const -
+ * 
+ * - takes a lispvalue, (as returned to the yacc routine by the lexer)
+ *   extracts the type, and makes the appropriate type constant
+ *   by invoking the (c-callable) lisp routine c-make-const
+ *   via the lisp_call() mechanism
+ *
+ * eventually, produces a "const" lisp-struct as per nodedefs.cl
+ */ 
+Const *
+make_const(Value *value)
+{
+    Type tp;
+    Datum val;
+    Const *con;
+    
+    switch(nodeTag(value)) {
+    case T_Integer:
+       tp = type("int4");
+       val = Int32GetDatum(intVal(value));
+       break;
+       
+    case T_Float:
+       {
+           float32 dummy;
+           tp = type("float4");
+           
+           dummy = (float32)palloc(sizeof(float32data));
+           *dummy = floatVal(value);
+           
+           val = Float32GetDatum(dummy);
+       }
+       break;
+       
+    case T_String:
+       tp = type("unknown"); /* unknown for now, will be type coerced */
+       val = PointerGetDatum(textin(strVal(value)));
+       break;
+
+    case T_Null:
+    default:
+       {
+           if (nodeTag(value)!=T_Null)
+               elog(NOTICE,"unknown type : %d\n", nodeTag(value));
+
+           /* null const */
+           con = makeConst(0, 0, (Datum)NULL, TRUE, 0, FALSE);
+           return NULL /*con*/;
+       }
+    }
+
+    con = makeConst(typeid(tp),
+                   tlen(tp),
+                   val,
+                   FALSE,
+                   tbyval(tp),
+                   FALSE); /* not a set */
+
+    return (con);
+}
+
+/*
+ * param_type_init()
+ *
+ * keep enough information around fill out the type of param nodes
+ * used in postquel functions
+ */
+void
+param_type_init(Oid* typev, int nargs)
+{
+    pfunc_num_args = nargs;
+    param_type_info = typev;
+}
+
+Oid
+param_type(int t)
+{
+    if ((t >pfunc_num_args) ||(t ==0)) return InvalidOid;
+    return param_type_info[t-1];
+}
+
diff --git a/src/backend/parser/parse_query.h b/src/backend/parser/parse_query.h
new file mode 100644 (file)
index 0000000..5695dfa
--- /dev/null
@@ -0,0 +1,72 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_query.h--
+ *    prototypes for parse_query.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PARSE_QUERY_H
+#define PARSE_QUERY_H
+
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+#include "parser/catalog_utils.h"
+#include "parser/parse_state.h"
+
+typedef struct QueryTreeList {
+  int len; /* number of queries */
+  Query** qtrees;
+} QueryTreeList;
+
+extern int RangeTablePosn(List *rtable, char *rangevar);
+extern char *VarnoGetRelname(ParseState *pstate, int vnum);
+extern RangeTblEntry *makeRangeTableEntry(char *relname, bool inh,
+                                         TimeRange *timeRange, char *refname);
+extern List *expandAll(ParseState *pstate, char *relname, int *this_resno);
+extern TimeQual makeTimeRange(char *datestring1, char *datestring2,
+                             int timecode);
+extern Expr *make_op(char *opname, Node *ltree, Node *rtree);
+
+extern int find_atttype(Oid relid, char *attrname);
+extern Var *make_var(ParseState *pstate, 
+                    char *relname, char *attrname, int *type_id);
+extern ArrayRef *make_array_ref(Node *array, List *indirection);
+extern ArrayRef *make_array_set(Expr *target_expr, List *upperIndexpr,
+                        List *lowerIndexpr, Expr *expr);
+extern Const *make_const(Value *value);
+
+extern void param_type_init(Oid* typev, int nargs);
+extern Oid param_type(int t);
+
+/* parser.c (was ylib.c) */
+extern QueryTreeList *parser(char *str, Oid *typev, int nargs);
+extern Node *parser_typecast(Value *expr, TypeName *typename, int typlen);
+extern Node *parser_typecast2(Node *expr, int exprType, Type tp, int typlen);
+extern Aggreg *ParseAgg(char *aggname, Oid basetype, Node *target);
+
+/*
+ * analyze.c
+ */
+
+#if 0
+extern List *p_rtable;
+extern int NumLevels;
+#endif
+
+Oid exprType(Node *expr);
+ParseState* makeParseState();
+QueryTreeList *parse_analyze(List *querytree_list);
+
+/* define in parse_query.c, used in gram.y */
+extern Oid *param_type_info;
+extern int pfunc_num_args;
+
+/* useful macros */
+#define ISCOMPLEX(type) (typeid_get_relid((Oid)type) ? true : false)
+
+#endif /* PARSE_QUERY_H */
diff --git a/src/backend/parser/parse_state.h b/src/backend/parser/parse_state.h
new file mode 100644 (file)
index 0000000..ef1ead1
--- /dev/null
@@ -0,0 +1,27 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_state.h--
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef PARSE_STATE_H
+#define PARSE_STATE_H
+
+/* state information used during parse analysis */
+typedef struct ParseState {
+    int        p_last_resno; 
+    List       *p_target_resnos;
+    Relation   parser_current_rel;
+    List       *p_rtable;
+    int        p_query_is_rule;
+    int                p_numAgg;
+    List       *p_aggs;
+} ParseState;
+
+
+#endif /*PARSE_QUERY_H*/
diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c
new file mode 100644 (file)
index 0000000..5643089
--- /dev/null
@@ -0,0 +1,449 @@
+/*-------------------------------------------------------------------------
+ *
+ * ylib.c--
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include <stdio.h>
+#ifndef WIN32
+#include <pwd.h>
+#endif /*WIN32 */
+#include <sys/param.h>         /* for MAXPATHLEN */
+
+#include "utils/elog.h"
+#include "parser/catalog_utils.h"
+#include "nodes/pg_list.h"
+#include "utils/exc.h"
+#include "utils/excid.h"
+#include "utils/palloc.h"
+#include "catalog/pg_aggregate.h"
+#include "catalog/pg_type.h"
+#include "nodes/primnodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
+#include "nodes/relation.h"
+#include "parser/parse_query.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "access/heapam.h"
+#include "nodes/makefuncs.h"
+#include "optimizer/clauses.h" 
+
+char *parseString; /* the char* which holds the string to be parsed */
+char *parseCh;     /* a pointer used during parsing to walk down ParseString*/
+
+List *parsetree = NIL;
+
+static void fixupsets();
+static void define_sets();
+/*
+ * parser-- returns a list of parse trees
+ * 
+ *  CALLER is responsible for free'ing the list returned 
+ */
+QueryTreeList *
+parser(char *str, Oid *typev, int nargs)
+{
+    QueryTreeList* queryList;
+    int yyresult;
+
+#if defined(FLEX_SCANNER)
+    extern void DeleteBuffer(void);
+#endif /* FLEX_SCANNER */
+    
+    init_io();
+    
+    /* Set things up to read from the string, if there is one */
+    if (strlen(str) != 0) {
+       parseString = (char *) palloc(strlen(str) + 1);
+       memmove(parseString,str,strlen(str)+1);
+    }
+    
+    parser_init(typev, nargs);
+    yyresult = yyparse();
+
+#if defined(FLEX_SCANNER)
+    DeleteBuffer();
+#endif /* FLEX_SCANNER */
+    
+    clearerr(stdin);
+    
+    if (yyresult) {    /* error */
+       return((QueryTreeList*)NULL);
+    }
+
+    queryList = parse_analyze(parsetree);
+    
+#ifdef SETS_FIXED
+    /* Fixing up sets calls the parser, so it reassigns the global
+     * variable parsetree. So save the real parsetree.
+     */
+    savetree = parsetree;
+    foreach (parse, savetree) {  /* savetree is really a list of parses */
+
+       /* find set definitions embedded in query */
+       fixupsets((Query *)lfirst(parse));  
+
+    }
+    return savetree;
+#endif    
+
+    return queryList;
+}
+
+static void
+fixupsets(Query *parse)
+{
+    if (parse == NULL)
+       return;
+    if (parse->commandType==CMD_UTILITY)  /* utility */
+       return;
+    if (parse->commandType!=CMD_INSERT)
+       return;
+    define_sets(parse);
+}
+
+/* Recursively find all of the Consts in the parsetree.  Some of
+ * these may represent a set.  The value of the Const will be the
+ * query (a string) which defines the set.  Call SetDefine to define
+ * the set, and store the OID of the new set in the Const instead.
+ */
+static void
+define_sets(Node *clause)
+{
+#ifdef SETS_FIXED
+    Oid setoid;
+    Type t = type("oid");
+    Oid typeoid = typeid(t);
+    Size oidsize = tlen(t);
+    bool oidbyval = tbyval(t);
+    
+    if (clause==NULL) {
+       return;
+    } else if (IsA(clause,LispList)) {
+       define_sets(lfirst(clause));
+       define_sets(lnext(clause));
+    } else if (IsA(clause,Const)) {
+       if (get_constisnull((Const)clause) || 
+           !get_constisset((Const)clause)) {
+           return;
+       }
+       setoid = SetDefine(((Const*)clause)->constvalue,
+                          get_id_typname(((Const*)clause)->consttype));
+       set_constvalue((Const)clause, setoid);
+       set_consttype((Const)clause,typeoid);
+       set_constlen((Const)clause,oidsize);
+       set_constbyval((Const)clause,oidbyval);
+    } else if ( IsA(clause,Iter) ) {
+       define_sets(((Iter*)clause)->iterexpr);
+    } else if (single_node (clause)) {
+       return;
+    } else if (or_clause(clause)) {
+       List *temp;
+       /* mapcan */
+       foreach (temp, ((Expr*)clause)->args) {
+           define_sets(lfirst(temp));
+       }
+    } else if (is_funcclause (clause)) {
+       List *temp;
+       /* mapcan */
+       foreach(temp, ((Expr*)clause)->args) {
+           define_sets(lfirst(temp));
+       }
+    } else if (IsA(clause,ArrayRef)) {
+       define_sets(((ArrayRef*)clause)->refassgnexpr);
+    } else if (not_clause (clause)) {
+       define_sets (get_notclausearg (clause));
+    } else if (is_opclause (clause)) {
+       define_sets(get_leftop (clause));
+       define_sets(get_rightop (clause));
+    }
+#endif
+}
+
+#define    PSIZE(PTR)      (*((int32 *)(PTR) - 1))
+Node *
+parser_typecast(Value *expr, TypeName *typename, int typlen)
+{
+    /* check for passing non-ints */
+    Const *adt;
+    Datum lcp;
+    Type tp;
+    char type_string[16];
+    int32 len;
+    char *cp = NULL;
+    char *const_string; 
+    bool string_palloced = false;
+
+    switch(nodeTag(expr)) {
+    case T_String:
+       const_string = DatumGetPointer(expr->val.str);
+       break;
+    case T_Integer:
+       const_string = (char *) palloc(256);
+       string_palloced = true;
+       sprintf(const_string, "%ld", expr->val.ival);
+       break;
+    default:
+       elog(WARN,
+            "parser_typecast: cannot cast this expression to type \"%s\"",
+            typename->name);
+    }
+
+    if (typename->arrayBounds != NIL) {
+       sprintf(type_string,"_%s", typename->name);
+       tp = (Type) type(type_string);
+    } else {
+       tp = (Type) type(typename->name);
+    }
+    
+    len = tlen(tp);
+    
+#if 0 /* fix me */
+    switch ( CInteger(lfirst(expr)) ) {
+    case 23: /* int4 */
+       const_string = (char *) palloc(256);
+       string_palloced = true;
+       sprintf(const_string,"%d", ((Const*)lnext(expr))->constvalue);
+       break;
+       
+    case 19: /* char16 */
+       const_string = (char *) palloc(256);
+       string_palloced = true;
+       sprintf(const_string,"%s", ((Const*)lnext(expr))->constvalue);
+       break;
+       
+    case 18: /* char */
+       const_string = (char *) palloc(256);
+       string_palloced = true;
+       sprintf(const_string,"%c", ((Const)lnext(expr))->constvalue);
+       break;
+       
+    case 701:/* float8 */
+       const_string = (char *) palloc(256);
+       string_palloced = true;
+       sprintf(const_string,"%f", ((Const)lnext(expr))->constvalue);
+       break;
+       
+    case 25: /* text */
+       const_string = DatumGetPointer(((Const)lnext(expr))->constvalue);
+       const_string = (char *) textout((struct varlena *)const_string);
+       break;
+       
+    case 705: /* unknown */
+        const_string = DatumGetPointer(((Const)lnext(expr))->constvalue);
+        const_string = (char *) textout((struct varlena *)const_string);
+        break;
+       
+    default:
+       elog(WARN,"unknown type %d", CInteger(lfirst(expr)));
+    }
+#endif
+
+    cp = instr2 (tp, const_string, typlen);
+    
+    if (!tbyvalue(tp)) {
+       if (len >= 0 && len != PSIZE(cp)) {
+           char *pp;
+           pp = (char *) palloc(len);
+           memmove(pp, cp, len);
+           cp = pp;
+       }
+       lcp = PointerGetDatum(cp);
+    } else {
+       switch(len) {
+       case 1:
+           lcp = Int8GetDatum(cp);
+           break;
+       case 2:
+           lcp = Int16GetDatum(cp);
+           break;
+       case 4:
+           lcp = Int32GetDatum(cp);
+           break;
+       default:
+           lcp = PointerGetDatum(cp);
+           break;
+       }
+    }
+    
+    adt = makeConst(typeid(tp),
+                   len,
+                   (Datum)lcp ,
+                   0,
+                   tbyvalue(tp), 
+                   0 /* not a set */);
+    
+    if (string_palloced)
+       pfree(const_string);
+    
+    return (Node*)adt;
+}
+
+Node *
+parser_typecast2(Node *expr, int exprType, Type tp, int typlen)
+{
+    /* check for passing non-ints */
+    Const *adt;
+    Datum lcp;
+    int32 len = tlen(tp);
+    char *cp = NULL;
+    
+    char *const_string; 
+    bool string_palloced = false;
+
+    Assert(IsA(expr,Const));
+    
+    switch (exprType) {
+    case 23: /* int4 */
+       const_string = (char *) palloc(256);
+       string_palloced = true;
+       sprintf(const_string,"%d",
+               (int) ((Const*)expr)->constvalue);
+       break;
+    case 19: /* char16 */
+       const_string = (char *) palloc(256);
+       string_palloced = true;
+       sprintf(const_string,"%s",
+               (char*) ((Const*)expr)->constvalue);
+       break;
+    case 18: /* char */
+       const_string = (char *) palloc(256);
+       string_palloced = true;
+       sprintf(const_string,"%c",
+               (char) ((Const*)expr)->constvalue);
+       break;
+    case 700:  /* float4 */
+       {
+           float32 floatVal = 
+               DatumGetFloat32(((Const*)expr)->constvalue);
+           const_string = (char *) palloc(256);
+           string_palloced = true;
+           sprintf(const_string,"%f", *floatVal);
+           break;
+       }
+    case 701:/* float8 */
+       {
+           float64 floatVal = 
+               DatumGetFloat64(((Const*)expr)->constvalue);
+           const_string = (char *) palloc(256);
+           string_palloced = true;
+           sprintf(const_string,"%f", *floatVal);
+           break;
+       }
+    case 25: /* text */
+       const_string = 
+           DatumGetPointer(((Const*)expr)->constvalue );
+       const_string = (char *) textout((struct varlena *)const_string);
+       break;
+    case 705: /* unknown */
+        const_string =
+           DatumGetPointer(((Const*)expr)->constvalue );
+       const_string = (char *) textout((struct varlena *)const_string);
+        break;
+    default:
+       elog(WARN,"unknown type%d ",exprType);
+    }
+    
+    cp = instr2 (tp, const_string, typlen);
+    
+    
+    if (!tbyvalue(tp)) {
+       if (len >= 0 && len != PSIZE(cp)) {
+           char *pp;
+           pp = (char *) palloc(len);
+           memmove(pp, cp, len);
+           cp = pp;
+       }
+       lcp = PointerGetDatum(cp);
+    } else {
+       switch(len) {
+       case 1:
+           lcp = Int8GetDatum(cp);
+           break;
+       case 2:
+           lcp = Int16GetDatum(cp);
+           break;
+       case 4:
+           lcp = Int32GetDatum(cp);
+           break;
+       default:
+           lcp = PointerGetDatum(cp);
+           break;
+       }
+    }
+    
+    adt = makeConst((Oid)typeid(tp),
+                   (Size)len,
+                   (Datum)lcp,
+                   0, 
+                   0 /*was omitted*/,
+                   0 /* not a set */);
+    /*
+      printf("adt %s : %d %d %d\n",CString(expr),typeid(tp) ,
+      len,cp);
+      */
+    if (string_palloced) pfree(const_string);
+
+    return ((Node*) adt);
+}
+
+Aggreg *
+ParseAgg(char *aggname, Oid basetype, Node *target)
+{
+    Oid fintype;
+    Oid vartype;
+    Oid xfn1;
+    Form_pg_aggregate aggform;
+    Aggreg *aggreg;
+    HeapTuple theAggTuple;
+    
+    theAggTuple = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggname),
+                                     ObjectIdGetDatum(basetype), 
+                                     0, 0); 
+    if (!HeapTupleIsValid(theAggTuple)) {
+        elog(WARN, "aggregate %s does not exist", aggname);
+    }
+    
+    aggform = (Form_pg_aggregate) GETSTRUCT(theAggTuple);
+    fintype = aggform->aggfinaltype;
+    xfn1 = aggform->aggtransfn1;
+    
+    if (nodeTag(target) != T_Var)
+       elog(WARN, "parser: aggregate can only be applied on an attribute");
+
+    /* only aggregates with transfn1 need a base type */
+    if (OidIsValid(xfn1)) {    
+       basetype = aggform->aggbasetype;
+       vartype = ((Var*)target)->vartype;
+
+       if (basetype != vartype) {
+           Type tp1, tp2, get_id_type();
+           
+           tp1 = get_id_type(basetype);
+           tp2 = get_id_type(vartype);
+           elog(NOTICE, "Aggregate type mismatch:");
+           elog(WARN, "%s works on %s, not %s", aggname,
+                tname(tp1), tname(tp2));
+        }
+    }
+
+    aggreg = makeNode(Aggreg);
+    aggreg->aggname = pstrdup(aggname);
+    aggreg->basetype = aggform->aggbasetype;
+    aggreg->aggtype = fintype;
+
+    aggreg->target = target;
+
+    return aggreg;
+}
+
+
+    
diff --git a/src/backend/parser/parsetree.h b/src/backend/parser/parsetree.h
new file mode 100644 (file)
index 0000000..772059d
--- /dev/null
@@ -0,0 +1,80 @@
+/*-------------------------------------------------------------------------
+ *
+ * parsetree.h--
+ *    Routines to access various components and subcomponents of
+ *    parse trees.  
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PARSETREE_H
+#define PARSETREE_H            /* include once only */
+
+/* ----------------
+ *     need pg_list.h for definitions of CAR(), etc. macros
+ * ----------------
+ */
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+
+/* ----------------
+ *     range table macros
+ *
+ *  parse tree:
+ *     (root targetlist qual)
+ *      ^^^^
+ *  parse root:
+ *     (numlevels cmdtype resrel rangetable priority ruleinfo nestdotinfo)
+ *                               ^^^^^^^^^^
+ *  range table:
+ *     (rtentry ...)
+ *
+ *  rtentry:
+ *     note: this might be wrong, I don't understand how
+ *     rt_time / rt_archive_time work together.  anyways it
+ *      looks something like:
+ *
+ *        (relname ?       relid timestuff flags rulelocks)
+ *     or (new/cur relname relid timestuff flags rulelocks)
+ *
+ *     someone who knows more should correct this -cim 6/9/91
+ * ----------------
+ */
+
+#define rt_relname(rt_entry) \
+      ((!strcmp(((rt_entry)->refname),"*CURRENT*") ||\
+        !strcmp(((rt_entry)->refname),"*NEW*")) ? ((rt_entry)->refname) : \
+        ((char *)(rt_entry)->relname))
+
+/*
+ *     rt_fetch
+ *     rt_store
+ *
+ *     Access and (destructively) replace rangetable entries.
+ *
+ */
+#define rt_fetch(rangetable_index, rangetable) \
+    ((RangeTblEntry*)nth((rangetable_index)-1, rangetable))
+
+#define rt_store(rangetable_index, rangetable, rt) \
+    set_nth(rangetable, (rangetable_index)-1, rt)
+
+/*
+ *     getrelid
+ *     getrelname
+ *
+ *     Given the range index of a relation, return the corresponding
+ *     relation id or relation name.
+ */
+#define getrelid(rangeindex,rangetable) \
+    ((RangeTblEntry*)nth((rangeindex)-1, rangetable))->relid
+
+#define getrelname(rangeindex, rangetable) \
+    rt_relname((RangeTblEntry*)nth((rangeindex)-1, rangetable))
+
+#endif /* PARSETREE_H */
+            
diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l
new file mode 100644 (file)
index 0000000..7a0d966
--- /dev/null
@@ -0,0 +1,255 @@
+%{
+/*-------------------------------------------------------------------------
+ *
+ * scan.l--
+ *    lexical scanner for POSTGRES
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <ctype.h>
+#ifndef WIN32
+#include <unistd.h>
+#endif /* WIN32 */
+#ifndef __linux__
+#include <math.h>
+#else
+#include <stdlib.h>
+#endif /* __linux__ */
+#include <string.h>
+
+#include "postgres.h"
+#include "miscadmin.h"
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+#include "parser/keywords.h"
+#include "parser/scansup.h"
+#include "parse.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+extern char *parseString;
+extern char *parseCh;
+
+/* some versions of lex define this as a macro */
+#if defined(yywrap)
+#undef yywrap
+#endif /* yywrap */
+
+#if defined(FLEX_SCANNER)
+/* MAX_PARSE_BUFFER is defined in miscadmin.h */
+#define YYLMAX MAX_PARSE_BUFFER
+extern int myinput(char* buf, int max);
+#undef YY_INPUT
+#define YY_INPUT(buf,result,max) {result = myinput(buf,max);}
+#else
+#undef input
+int input();
+#undef unput
+void unput(char);
+#endif /* FLEX_SCANNER */
+
+extern YYSTYPE yylval;
+%}
+
+digit          [0-9]
+letter         [_A-Za-z]
+letter_or_digit        [_A-Za-z0-9]
+
+identifier     {letter}{letter_or_digit}*
+
+self           [,()\[\].;$\:\+\-\*\/\<\>\=\|]
+op_and_self    [\~\!\@\#\%\^\&\|\`\?\$\:\+\-\*\/\<\>\=]
+op_only                [\~\!\@\#\%\^\&\`\?]
+
+operator       ({op_and_self}{op_and_self}+)|{op_only}+
+    /* we used to allow double-quoted strings, but SQL doesn't */
+    /* so we won't either*/
+quote           '
+
+integer                -?{digit}+
+real           -?{digit}+\.{digit}+([Ee][-+]?{digit}+)?
+
+param          \${integer}
+
+comment                "--".*\n
+
+space          [ \t\n\f]
+other          .
+
+%%
+{comment}      { /* ignore */  }
+
+"::"           { return TYPECAST;      }
+
+{self}         { return (yytext[0]);   }
+
+{operator}     {
+                       yylval.str = pstrdup((char*)yytext);
+                       return (Op);
+               }
+{param}                {       yylval.ival = atoi((char*)&yytext[1]);          
+                       return (PARAM);
+                }
+{integer}      {
+                       yylval.ival = atoi((char*)yytext);              
+                       return (ICONST);
+               }
+{real}         {
+                       yylval.dval = atof((char*)yytext);
+                       return (FCONST);
+               }
+{quote}                {       
+                        char literal[MAX_PARSE_BUFFER];
+                        int i = 0;
+                        int c = 0;
+                        /* quote_seen can be either \ or ' because
+                           we handle both cases of \' and '' for
+                           quoting quotes*/
+                        int quote_seen = 0; 
+                                             
+                        while (i < MAX_PARSE_BUFFER - 1) {
+                            c = input();
+                            if (quote_seen != 0) {
+                                 if (quote_seen == '\'' &&
+                                     c != '\'') {
+                                    /* a non-quote follows a single quote */
+                                    /* so we've hit the end of the literal */
+                                    if (c != '\0' && c != EOF)
+                                      unput(c); /* put back the extra char we read*/
+                                    i = i - 1;
+                                    break; /* break out of the while loop */
+                                 }  
+                                 /* if we reach here, we're still in */
+                                 /* the string literal */
+                                 literal[i++] = c;
+                                 quote_seen = 0;
+                                 continue;
+                            }
+                            if (c == '\0' || c == EOF) {
+                               elog(WARN,"unterminated quoted string literal");
+                               /* not reached */
+                            }
+                            literal[i++] = c;
+                            if (c == '\'' || c == '\\')
+                               quote_seen = c;
+                        }
+                        if ( i == MAX_PARSE_BUFFER - 1) {
+                           elog (WARN, "unterminated quote string.  parse buffer of %d chars exceeded", MAX_PARSE_BUFFER);
+                           /* not reached */
+                      }
+                        literal[i] = '\0';
+                       yylval.str = pstrdup(scanstr(literal));
+                       return (SCONST); 
+                   }
+{identifier}   {
+                       ScanKeyword     *keyword;
+
+                       keyword = ScanKeywordLookup((char*)yytext);
+                       if (keyword != NULL) {
+                               return (keyword->value);
+                       } else {
+                               yylval.str = pstrdup((char*)yytext);
+                               return (IDENT);
+                       }
+               }
+{space}                { /* ignore */          }
+
+{other}                { return (yytext[0]);   }
+
+%%
+
+void yyerror(char message[])
+{
+    elog(WARN, "parser: %s at or near \"%s\"\n", message, yytext);
+}
+
+int yywrap()
+{
+    return(1);
+}
+
+/*
+ init_io:
+    called by postgres before any actual parsing is done
+*/
+void
+init_io()
+{
+    /* it's important to set this to NULL
+       because input()/myinput() checks the non-nullness of parseCh
+       to know when to pass the string to lex/flex */
+    parseCh = NULL;
+#if defined(FLEX_SCANNER)
+    if (YY_CURRENT_BUFFER)
+       yy_flush_buffer(YY_CURRENT_BUFFER);
+#endif /* FLEX_SCANNER */
+    BEGIN INITIAL;
+}
+
+
+#if !defined(FLEX_SCANNER)
+/* get lex input from a string instead of from stdin */
+int
+input()
+{
+    if (parseCh == NULL) {
+       parseCh = parseString;
+       return(*parseCh++);
+    } else if (*parseCh == '\0') {
+       return(0);
+    } else {
+       return(*parseCh++);
+    }
+}
+
+/* undo lex input from a string instead of from stdin */
+void
+unput(char c)
+{
+    if (parseCh == NULL) {
+       elog(FATAL, "Unput() failed.\n");
+    } else if (c != 0) {
+       *--parseCh = c;
+    }
+}
+#endif /* !defined(FLEX_SCANNER) */
+
+#ifdef FLEX_SCANNER
+/* input routine for flex to read input from a string instead of a file */
+int 
+myinput(char* buf, int max)
+{
+    int len, copylen;
+
+    if (parseCh == NULL) {
+       len = strlen(parseString);
+       if (len >= max)
+           copylen = max - 1;
+       else
+           copylen = len;
+       if (copylen > 0)
+           memcpy(buf, parseString, copylen);
+       buf[copylen] = '\0';
+       parseCh = parseString;
+       return copylen;
+    } else {
+       return 0; /* end of string */
+    }
+}
+
+char*
+CurScan(void)
+{
+/*
+    return (InputFrag ? InputFrag : parseCh) +
+          (yy_c_buf_p - &yy_current_buffer->yy_ch_buf[yy_n_chars]);
+*/
+}
+#endif /* FLEX_SCANNER */
+
diff --git a/src/backend/parser/scansup.c b/src/backend/parser/scansup.c
new file mode 100644 (file)
index 0000000..991b6f9
--- /dev/null
@@ -0,0 +1,148 @@
+/*-------------------------------------------------------------------------
+ *
+ * scansup.c--
+ *    support routines for the lex/flex scanner, used by both the normal
+ * backend as well as the bootstrap backend
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <ctype.h>
+#include <string.h>
+#include "c.h"
+#include "postgres.h"
+#include "miscadmin.h"
+#include "utils/elog.h"
+#include "parser/scansup.h"
+
+/*
+ *     Scanner error handler.
+ */
+static void
+serror(char *str)
+{
+    elog(WARN, "*** scanner error: %s\n", str);
+}
+
+/* ----------------
+ *     scanstr
+ * 
+ * if the string passed in has escaped codes, map the escape codes to actual
+ * chars
+ *
+ * also, remove leading and ending quotes '"' if any 
+ *
+ * the string passed in must be non-null
+ *
+ * the string returned is a pointer to static storage and should NOT
+ * be freed by the CALLER.
+ * ----------------
+ */
+
+char* 
+scanstr(char *s)
+{
+    static char newStr[MAX_PARSE_BUFFER]; 
+    int len, i, start, j;
+    char delimiter;
+    
+    if (s == NULL || s[0] == '\0')
+       return s;
+
+    len = strlen(s);
+    start = 0;
+
+    /* remove leading and trailing quotes, if any */
+    /* the normal backend lexer only accepts single quotes, but the
+       bootstrap lexer accepts double quotes */
+    delimiter = 0;
+    if (s[0] == '"' || s[0] == '\''){
+       delimiter = s[0];
+       start = 1;
+    }
+    if (delimiter != 0)  {
+       if (s[len-1] == delimiter) 
+           len = len - 1;
+       else
+           serror("mismatched quote delimiters");
+    }
+
+    for (i = start, j = 0; i < len ; i++) {
+       if (s[i] == '\'') {
+           i = i + 1;
+           if (s[i] == '\'')
+               newStr[j] = '\'';
+       }
+       else {
+           if (s[i] == '\\') {
+               i = i + 1;
+               switch (s[i]) {
+               case '\\':
+                   newStr[j] = '\\';
+                   break;
+               case 'b':
+                   newStr[j] = '\b';
+                   break;
+               case 'f':
+                   newStr[j] = '\f';
+                   break;
+               case 'n':
+                   newStr[j] = '\n';
+                   break;
+               case 'r':
+                   newStr[j] = '\r';
+                   break;
+               case 't':
+                   newStr[j] = '\t';
+                   break;
+               case '"':
+                   newStr[j] = '"';
+                   break;
+               case '\'':
+                   newStr[j] = '\'';
+                   break;
+               case '0': 
+               case '1': 
+               case '2': 
+               case '3': 
+               case '4': 
+               case '5':
+               case '6': 
+               case '7':
+                   {
+                       char octal[4];
+                       int k;
+                       long octVal;
+
+                       for (k=0; 
+                            s[i+k] >= '0' && s[i+k] <= '7' && k < 3;
+                            k++)
+                           octal[k] = s[i+k];
+                       i += k-1;
+                       octal[3] = '\0';
+                   
+                       octVal = strtol(octal,0,8);
+/*                     elog (NOTICE, "octal = %s octVal = %d, %od", octal, octVal, octVal);*/
+                       if (octVal <= 0377) {
+                           newStr[j] = ((char)octVal);
+                           break;
+                       }
+                   }
+               default:
+                   elog (WARN, "Bad escape sequence, s[i] = %d", s[i]);
+               } /* switch */
+           } /* s[i] == '\\' */
+           else
+               newStr[j] = s[i];
+       }
+       j++;
+    }
+    newStr[j] = '\0';
+    return newStr;
+}
+
diff --git a/src/backend/parser/scansup.h b/src/backend/parser/scansup.h
new file mode 100644 (file)
index 0000000..45f87d8
--- /dev/null
@@ -0,0 +1,17 @@
+/*-------------------------------------------------------------------------
+ *
+ * scansup.h--
+ *    scanner support routines.  used by both the bootstrap lexer
+ * as well as the normal lexer
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+extern char* scanstr(char *s);
+
+
+
diff --git a/src/backend/port/BSD44_derived/Makefile.inc b/src/backend/port/BSD44_derived/Makefile.inc
new file mode 100644 (file)
index 0000000..79008f5
--- /dev/null
@@ -0,0 +1,28 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for port/BSD44_derived (for OSs derived from 4.4-lite BSD)
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+CFLAGS+= -DUSE_POSIX_TIME
+
+#
+# 4.4-lite BSD-derived OSs require that the lex library be included,
+# in case yywrap is defined
+#
+LDADD+= -ll
+
+#
+# 4.4-lite BSD-derived OSs have a little trouble with partially-implemented
+# dynamic loading soutines. See the comments in port-protos.h.
+#
+SUBSRCS= dl.c
+
+HEADERS+= float.h machine.h port-protos.h
diff --git a/src/backend/port/BSD44_derived/README b/src/backend/port/BSD44_derived/README
new file mode 100644 (file)
index 0000000..acfd1e6
--- /dev/null
@@ -0,0 +1,4 @@
+The NetBSD port was done by Alistair G. Crooks ([email protected]).
+
+It was extended to cover Operating Systems derived from the 4.4-lite
+BSD release by Alistair G. Crooks.
diff --git a/src/backend/port/BSD44_derived/dl.c b/src/backend/port/BSD44_derived/dl.c
new file mode 100644 (file)
index 0000000..394fb8a
--- /dev/null
@@ -0,0 +1,88 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)dl.c       5.4 (Berkeley) 2/23/91";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <nlist.h>
+#include <link.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static char    error_message[BUFSIZ];
+
+char *
+BSD44_derived_dlerror(void)
+{
+       static char     ret[BUFSIZ];
+
+       (void) strcpy(ret, error_message);
+       error_message[0] = 0;
+       return((ret[0] == 0) ? (char *) NULL : ret);
+}
+
+void *
+BSD44_derived_dlopen(char *file, int num)
+{
+       void    *vp;
+
+       if ((vp = dlopen(file, num)) == (void *) NULL) {
+               (void) sprintf(error_message, "dlopen (%s) failed", file);
+       }
+       return(vp);
+}
+
+void *
+BSD44_derived_dlsym(void *handle, char *name)
+{
+       void    *vp;
+       char    buf[BUFSIZ];
+
+       if (*name != '_') {
+               (void) sprintf(buf, "_%s", name);
+               name = buf;
+       }
+       if ((vp = dlsym(handle, name)) == (void *) NULL) {
+               (void) sprintf(error_message, "dlsym (%s) failed", name);
+       }
+       return(vp);
+}
+
+void
+BSD44_derived_dlclose(void *handle)
+{
+       dlclose(handle);
+}
diff --git a/src/backend/port/BSD44_derived/float.h b/src/backend/port/BSD44_derived/float.h
new file mode 100644 (file)
index 0000000..b784afb
--- /dev/null
@@ -0,0 +1,30 @@
+/*-------------------------------------------------------------------------
+ *
+ * float.h--
+ *    definitions for ANSI floating point
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    These come straight out of ANSI X3.159-1989 (p.18) and
+ *    would be unnecessary if SunOS 4 were ANSI-compliant.
+ *
+ *    This is only a partial listing because I'm lazy to type
+ *    the whole thing in.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FLOAT_H
+#define FLOAT_H
+
+#define FLT_DIG        6
+#define FLT_MIN        ((float) 1.17549435e-38)
+#define FLT_MAX        ((float) 3.40282347e+38)
+#define DBL_DIG        15
+#define DBL_MIN        2.2250738585072014e-308
+#define DBL_MAX        1.7976931348623157e+308
+
+#endif /* FLOAT_H */
diff --git a/src/backend/port/BSD44_derived/machine.h b/src/backend/port/BSD44_derived/machine.h
new file mode 100644 (file)
index 0000000..ab761e4
--- /dev/null
@@ -0,0 +1,19 @@
+/*-------------------------------------------------------------------------
+ *
+ * machine.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MACHINE_H
+#define MACHINE_H
+
+#define BLCKSZ 8192
+
+#endif
+
diff --git a/src/backend/port/BSD44_derived/port-protos.h b/src/backend/port/BSD44_derived/port-protos.h
new file mode 100644 (file)
index 0000000..597797a
--- /dev/null
@@ -0,0 +1,41 @@
+/*-------------------------------------------------------------------------
+ *
+ * port-protos.h--
+ *    port-specific prototypes for NetBSD 1.0
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PORT_PROTOS_H
+#define PORT_PROTOS_H
+
+#include <sys/types.h>
+#include <nlist.h>
+#include <link.h>
+
+#include "fmgr.h"                      /* for func_ptr */
+#include "utils/dynamic_loader.h"
+
+/* dynloader.c */
+/*
+ * Dynamic Loader on NetBSD 1.0.
+ *
+ * this dynamic loader uses the system dynamic loading interface for shared 
+ * libraries (ie. dlopen/dlsym/dlclose). The user must specify a shared
+ * library as the file to be dynamically loaded.
+ *
+ * agc - I know this is all a bit crufty, but it does work, is fairly
+ * portable, and works (the stipulation that the d.l. function must
+ * begin with an underscore is fairly tricky, and some versions of
+ * NetBSD (like 1.0, and 1.0A pre June 1995) have no dlerror.)
+ */
+#define        pg_dlopen(f)    BSD44_derived_dlopen(f, 1)
+#define        pg_dlsym        BSD44_derived_dlsym
+#define        pg_dlclose      BSD44_derived_dlclose
+#define        pg_dlerror      BSD44_derived_dlerror
+
+#endif /* PORT_PROTOS_H */
diff --git a/src/backend/port/Makefile.inc b/src/backend/port/Makefile.inc
new file mode 100644 (file)
index 0000000..0d1e57e
--- /dev/null
@@ -0,0 +1,21 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the port module (for code specific to various UNIX 
+#    platforms)
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+portdir= $(CURDIR)/port/$(PORTNAME)
+VPATH:= $(VPATH):$(portdir)
+
+SUBSRCS=
+include $(portdir)/Makefile.inc
+SRCS_PORT:= $(SUBSRCS)
+
diff --git a/src/backend/port/aix/Makefile.inc b/src/backend/port/aix/Makefile.inc
new file mode 100644 (file)
index 0000000..3f69370
--- /dev/null
@@ -0,0 +1,40 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for port/aix (AIX specific stuff)
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+#
+# aix has fast linkers and don't need the BIGOBJ stuff.
+#
+BIGOBJS=false
+
+CFLAGS+= -DCLASS_CONFLICT -DDISABLE_XOPEN_NLS -DNEED_ISINF
+
+LDFLAGS+= -bE:$(objdir)/$(PROG).exp
+
+LDADD+= -ll -lld
+
+HEADERS+= dlfcn.h machine.h port-protos.h
+
+SUBSRCS+= dlfcn.c 
+
+${PROG}.exp: ${PROG}.noexp
+       mv -f $(objdir)/${PROG}.noexp $(objdir)/${PROG}
+       $(CURDIR)/port/aix/mkldexport.sh $(objdir)/${PROG} ${BINDIR} > $(objdir)/${PROG}.exp
+       mv -f $(objdir)/${PROG} $(objdir)/${PROG}.noexp
+
+${PROG}.noexp: ${OBJS}
+       touch -f $(objdir)/${PROG}.exp
+       ${CC} ${LDFLAGS} -o $(objdir)/${PROG}.noexp ${OBJS} ${LDADD}
+
+EXPORTS= ${PROG}.exp
+
+CLEANFILES+= ${PROG}.noexp ${PROG}.exp
diff --git a/src/backend/port/aix/README.dlfcn b/src/backend/port/aix/README.dlfcn
new file mode 100644 (file)
index 0000000..f64446d
--- /dev/null
@@ -0,0 +1,167 @@
+Copyright (c) 1992,1993,1995, Jens-Uwe Mager, Helios Software GmbH
+Not derived from licensed software.
+
+Permission is granted to freely use, copy, modify, and redistribute
+this software, provided that no attempt is made to gain profit from it,
+the author is not construed to be liable for any results of using the
+software, alterations are clearly marked as such, and this notice is
+not modified.
+
+libdl.a
+-------
+
+This is an emulation library to emulate the SunOS/System V.4 functions
+to access the runtime linker. The functions are emulated by using the
+AIX load() function and by reading the .loader section of the loaded
+module to find the exports. The to be loaded module should be linked as
+follows (if using AIX 3):
+
+       cc -o module.so -bM:SRE -bE:module.exp -e _nostart $(OBJS)
+
+For AIX 4:
+
+       cc -o module.so -bM:SRE -bE:module.exp -bnoentry $(OBJS)
+
+The module export file contains the symbols to be exported. Because
+this library uses the loader section, the final module.so file can be
+stripped. C++ users should build their shared objects using the script
+makeC++SharedLib (part of the IBM C++ compiler), this will make sure
+that constructors and destructors for static and global objects will be
+called upon loading and unloading the module.
+
+Usage
+-----
+
+void *dlopen(const char *path, int mode);
+
+This routine loads the module pointed to by path and reads its export
+table. If the path does not contain a '/' character, dlopen will search
+for the module using the LIBPATH environment variable. It returns an
+opaque handle to the module or NULL on error. The mode parameter can be
+either RTLD_LAZY (for lazy function binding) or RTLD_NOW for immediate
+function binding. The AIX implementation currently does treat RTLD_NOW
+the same as RTLD_LAZY. The flag RTLD_GLOBAL might be or'ed into the
+mode parameter to allow loaded modules to bind to global variables or
+functions in other loaded modules loaded by dlopen(). If RTLD_GLOBAL is
+not specified, only globals from the main part of the executable or
+shared libraries are used to look for undefined symbols in loaded
+modules.
+
+
+void *dlsym(void *handle, const char *symbol);
+
+This routine searches for the symbol in the module referred to by
+handle and returns its address. If the symbol could not be found, the
+function returns NULL. The return value must be casted to a proper
+function pointer before it can be used. SunOS/System V.4 allow handle
+to be a NULL pointer to refer to the module the call is made from, this
+is not implemented.
+
+int dlclose(void *handle);
+
+This routine unloads the module referred to by the handle and disposes
+of any local storage. this function returns -1 on failure.
+
+char *dlerror(void);
+
+This routine can be used to retrieve a text message describing the most
+recent error that occured on on of the above routines. This function
+returns NULL if there is not error information.
+
+Initialization and termination handlers
+---------------------------------------
+
+The emulation provides for an initialization and a termination
+handler.  The dlfcn.h file contains a structure declaration named
+dl_info with following members:
+
+       void (*init)(void);
+       void (*fini)(void);
+
+The init function is called upon first referencing the library. The
+fini function is called at dlclose() time or when the process exits.
+The module should declare a variable named dl_info that contains this
+structure which must be exported.  These functions correspond to the
+documented _init() and _fini() functions of SunOS 4.x, but these are
+appearently not implemented in SunOS.  When using SunOS 5.0, these
+correspond to #pragma init and #pragma fini respectively. At the same
+time any static or global C++ object's constructors or destructors will
+be called.
+
+Jens-Uwe Mager
+
+HELIOS Software GmbH
+Lavesstr. 80
+30159 Hannover
+Germany
+
+Phone:         +49 511 36482-0
+FAX:           +49 511 36482-69
+AppleLink:     helios.de       Attn: Jens-Uwe Mager
+Internet:      [email protected]
+
+Revison History
+---------------
+
+SCCS/s.dlfcn.h:
+
+D 1.4 95/04/25 09:36:52 jum 4 3        00018/00004/00028
+MRs:
+COMMENTS:
+added RTLD_GLOBAL, include and C++ guards
+
+D 1.3 92/12/27 20:58:32 jum 3 2        00001/00001/00031
+MRs:
+COMMENTS:
+we always have prototypes on RS/6000
+
+D 1.2 92/08/16 17:45:11 jum 2 1        00009/00000/00023
+MRs:
+COMMENTS:
+added dl_info structure to implement initialize and terminate functions
+
+D 1.1 92/08/02 18:08:45 jum 1 0        00023/00000/00000
+MRs:
+COMMENTS:
+Erstellungsdatum und -uhrzeit 92/08/02 18:08:45 von jum
+
+SCCS/s.dlfcn.c:
+
+D 1.7 95/08/14 19:08:38 jum 8 6        00026/00004/00502
+MRs:
+COMMENTS:
+Integrated the fixes from Kirk Benell ([email protected]) to allow loading of
+shared objects generated under AIX 4. Fixed bug that symbols with exactly
+8 characters would use garbage characters from the following symbol value.
+
+D 1.6 95/04/25 09:38:03 jum 6 5        00046/00006/00460
+MRs:
+COMMENTS:
+added handling of C++ static constructors and destructors, added RTLD_GLOBAL to bind against other loaded modules
+
+D 1.5 93/02/14 20:14:17 jum 5 4        00002/00000/00464
+MRs:
+COMMENTS:
+added path to dlopen error message to make clear where there error occured.
+
+D 1.4 93/01/03 19:13:56 jum 4 3        00061/00005/00403
+MRs:
+COMMENTS:
+to allow calling symbols in the main module call load with L_NOAUTODEFER and 
+do a loadbind later with the main module.
+
+D 1.3 92/12/27 20:59:55 jum 3 2        00066/00008/00342
+MRs:
+COMMENTS:
+added search by L_GETINFO if module got loaded by LIBPATH
+
+D 1.2 92/08/16 17:45:43 jum 2 1        00074/00006/00276
+MRs:
+COMMENTS:
+implemented initialize and terminate functions, added reference counting to avoid multiple loads of the same library
+
+D 1.1 92/08/02 18:08:45 jum 1 0        00282/00000/00000
+MRs:
+COMMENTS:
+Erstellungsdatum und -uhrzeit 92/08/02 18:08:45 von jum
+
diff --git a/src/backend/port/aix/dlfcn.c b/src/backend/port/aix/dlfcn.c
new file mode 100644 (file)
index 0000000..9ae113c
--- /dev/null
@@ -0,0 +1,528 @@
+/*
+ * @(#)dlfcn.c 1.7 revision of 95/08/14  19:08:38
+ * This is an unpublished work copyright (c) 1992 HELIOS Software GmbH
+ * 30159 Hannover, Germany
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ldr.h>
+#include <a.out.h>
+#include <ldfcn.h>
+#include "dlfcn.h"
+
+/*
+ * We simulate dlopen() et al. through a call to load. Because AIX has
+ * no call to find an exported symbol we read the loader section of the
+ * loaded module and build a list of exported symbols and their virtual
+ * address.
+ */
+
+typedef struct {
+       char            *name;          /* the symbols's name */
+       void            *addr;          /* its relocated virtual address */
+} Export, *ExportPtr;
+
+/*
+ * xlC uses the following structure to list its constructors and
+ * destructors. This is gleaned from the output of munch.
+ */
+typedef struct {
+       void (*init)(void);             /* call static constructors */
+       void (*term)(void);             /* call static destructors */
+} Cdtor, *CdtorPtr;
+
+/*
+ * The void * handle returned from dlopen is actually a ModulePtr.
+ */
+typedef struct Module {
+       struct Module   *next;
+       char            *name;          /* module name for refcounting */
+       int             refCnt;         /* the number of references */
+       void            *entry;         /* entry point from load */
+       struct dl_info  *info;          /* optional init/terminate functions */
+       CdtorPtr        cdtors;         /* optional C++ constructors */
+       int             nExports;       /* the number of exports found */
+       ExportPtr       exports;        /* the array of exports */
+} Module, *ModulePtr;
+
+/*
+ * We keep a list of all loaded modules to be able to call the fini
+ * handlers and destructors at atexit() time.
+ */
+static ModulePtr modList;
+
+/*
+ * The last error from one of the dl* routines is kept in static
+ * variables here. Each error is returned only once to the caller.
+ */
+static char errbuf[BUFSIZ];
+static int errvalid;
+
+extern char *strdup(const char *);
+static void caterr(char *);
+static int readExports(ModulePtr);
+static void terminate(void);
+static void *findMain(void);
+
+void *dlopen(const char *path, int mode)
+{
+       register ModulePtr mp;
+       static void *mainModule;
+
+       /*
+        * Upon the first call register a terminate handler that will
+        * close all libraries. Also get a reference to the main module
+        * for use with loadbind.
+        */
+       if (!mainModule) {
+               if ((mainModule = findMain()) == NULL)
+                       return NULL;
+               atexit(terminate);
+       }
+       /*
+        * Scan the list of modules if we have the module already loaded.
+        */
+       for (mp = modList; mp; mp = mp->next)
+               if (strcmp(mp->name, path) == 0) {
+                       mp->refCnt++;
+                       return mp;
+               }
+       if ((mp = (ModulePtr)calloc(1, sizeof(*mp))) == NULL) {
+               errvalid++;
+               strcpy(errbuf, "calloc: ");
+               strcat(errbuf, strerror(errno));
+               return NULL;
+       }
+       if ((mp->name = strdup(path)) == NULL) {
+               errvalid++;
+               strcpy(errbuf, "strdup: ");
+               strcat(errbuf, strerror(errno));
+               free(mp);
+               return NULL;
+       }
+       /*
+        * load should be declared load(const char *...). Thus we
+        * cast the path to a normal char *. Ugly.
+        */
+       if ((mp->entry = (void *)load((char *)path, L_NOAUTODEFER, NULL)) == NULL) {
+               free(mp->name);
+               free(mp);
+               errvalid++;
+               strcpy(errbuf, "dlopen: ");
+               strcat(errbuf, path);
+               strcat(errbuf, ": ");
+               /*
+                * If AIX says the file is not executable, the error
+                * can be further described by querying the loader about
+                * the last error.
+                */
+               if (errno == ENOEXEC) {
+                       char *tmp[BUFSIZ/sizeof(char *)];
+                       if (loadquery(L_GETMESSAGES, tmp, sizeof(tmp)) == -1)
+                               strcpy(errbuf, strerror(errno));
+                       else {
+                               char **p;
+                               for (p = tmp; *p; p++)
+                                       caterr(*p);
+                       }
+               } else
+                       strcat(errbuf, strerror(errno));
+               return NULL;
+       }
+       mp->refCnt = 1;
+       mp->next = modList;
+       modList = mp;
+       if (loadbind(0, mainModule, mp->entry) == -1) {
+               dlclose(mp);
+               errvalid++;
+               strcpy(errbuf, "loadbind: ");
+               strcat(errbuf, strerror(errno));
+               return NULL;
+       }
+       /*
+        * If the user wants global binding, loadbind against all other
+        * loaded modules.
+        */
+       if (mode & RTLD_GLOBAL) {
+               register ModulePtr mp1;
+               for (mp1 = mp->next; mp1; mp1 = mp1->next)
+                       if (loadbind(0, mp1->entry, mp->entry) == -1) {
+                               dlclose(mp);
+                               errvalid++;
+                               strcpy(errbuf, "loadbind: ");
+                               strcat(errbuf, strerror(errno));
+                               return NULL;
+                       }
+       }
+       if (readExports(mp) == -1) {
+               dlclose(mp);
+               return NULL;
+       }
+       /*
+        * If there is a dl_info structure, call the init function.
+        */
+       if (mp->info = (struct dl_info *)dlsym(mp, "dl_info")) {
+               if (mp->info->init)
+                       (*mp->info->init)();
+       } else
+               errvalid = 0;
+       /*
+        * If the shared object was compiled using xlC we will need
+        * to call static constructors (and later on dlclose destructors).
+        */
+       if (mp->cdtors = (CdtorPtr)dlsym(mp, "__cdtors")) {
+               while (mp->cdtors->init) {
+                       (*mp->cdtors->init)();
+                       mp->cdtors++;
+               }
+       } else
+               errvalid = 0;
+       return mp;
+}
+
+/*
+ * Attempt to decipher an AIX loader error message and append it
+ * to our static error message buffer.
+ */
+static void caterr(char *s)
+{
+       register char *p = s;
+
+       while (*p >= '0' && *p <= '9')
+               p++;
+       switch(atoi(s)) {
+       case L_ERROR_TOOMANY:
+               strcat(errbuf, "to many errors");
+               break;
+       case L_ERROR_NOLIB:
+               strcat(errbuf, "can't load library");
+               strcat(errbuf, p);
+               break;
+       case L_ERROR_UNDEF:
+               strcat(errbuf, "can't find symbol");
+               strcat(errbuf, p);
+               break;
+       case L_ERROR_RLDBAD:
+               strcat(errbuf, "bad RLD");
+               strcat(errbuf, p);
+               break;
+       case L_ERROR_FORMAT:
+               strcat(errbuf, "bad exec format in");
+               strcat(errbuf, p);
+               break;
+       case L_ERROR_ERRNO:
+               strcat(errbuf, strerror(atoi(++p)));
+               break;
+       default:
+               strcat(errbuf, s);
+               break;
+       }
+}
+
+void *dlsym(void *handle, const char *symbol)
+{
+       register ModulePtr mp = (ModulePtr)handle;
+       register ExportPtr ep;
+       register int i;
+
+       /*
+        * Could speed up the search, but I assume that one assigns
+        * the result to function pointers anyways.
+        */
+       for (ep = mp->exports, i = mp->nExports; i; i--, ep++)
+               if (strcmp(ep->name, symbol) == 0)
+                       return ep->addr;
+       errvalid++;
+       strcpy(errbuf, "dlsym: undefined symbol ");
+       strcat(errbuf, symbol);
+       return NULL;
+}
+
+char *dlerror(void)
+{
+       if (errvalid) {
+               errvalid = 0;
+               return errbuf;
+       }
+       return NULL;
+}
+
+int dlclose(void *handle)
+{
+       register ModulePtr mp = (ModulePtr)handle;
+       int result;
+       register ModulePtr mp1;
+
+       if (--mp->refCnt > 0)
+               return 0;
+       if (mp->info && mp->info->fini)
+               (*mp->info->fini)();
+       if (mp->cdtors)
+               while (mp->cdtors->term) {
+                       (*mp->cdtors->term)();
+                       mp->cdtors++;
+               }
+       result = unload(mp->entry);
+       if (result == -1) {
+               errvalid++;
+               strcpy(errbuf, strerror(errno));
+       }
+       if (mp->exports) {
+               register ExportPtr ep;
+               register int i;
+               for (ep = mp->exports, i = mp->nExports; i; i--, ep++)
+                       if (ep->name)
+                               free(ep->name);
+               free(mp->exports);
+       }
+       if (mp == modList)
+               modList = mp->next;
+       else {
+               for (mp1 = modList; mp1; mp1 = mp1->next)
+                       if (mp1->next == mp) {
+                               mp1->next = mp->next;
+                               break;
+                       }
+       }
+       free(mp->name);
+       free(mp);
+       return result;
+}
+
+static void terminate(void)
+{
+       while (modList)
+               dlclose(modList);
+}
+
+/*
+ * Build the export table from the XCOFF .loader section.
+ */
+static int readExports(ModulePtr mp)
+{
+       LDFILE *ldp = NULL;
+       SCNHDR sh, shdata;
+       LDHDR *lhp;
+       char *ldbuf;
+       LDSYM *ls;
+       int i;
+       ExportPtr ep;
+
+       if ((ldp = ldopen(mp->name, ldp)) == NULL) {
+               struct ld_info *lp;
+               char *buf;
+               int size = 4*1024;
+               if (errno != ENOENT) {
+                       errvalid++;
+                       strcpy(errbuf, "readExports: ");
+                       strcat(errbuf, strerror(errno));
+                       return -1;
+               }
+               /*
+                * The module might be loaded due to the LIBPATH
+                * environment variable. Search for the loaded
+                * module using L_GETINFO.
+                */
+               if ((buf = malloc(size)) == NULL) {
+                       errvalid++;
+                       strcpy(errbuf, "readExports: ");
+                       strcat(errbuf, strerror(errno));
+                       return -1;
+               }
+               while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
+                       free(buf);
+                       size += 4*1024;
+                       if ((buf = malloc(size)) == NULL) {
+                               errvalid++;
+                               strcpy(errbuf, "readExports: ");
+                               strcat(errbuf, strerror(errno));
+                               return -1;
+                       }
+               }
+               if (i == -1) {
+                       errvalid++;
+                       strcpy(errbuf, "readExports: ");
+                       strcat(errbuf, strerror(errno));
+                       free(buf);
+                       return -1;
+               }
+               /*
+                * Traverse the list of loaded modules. The entry point
+                * returned by load() does actually point to the data
+                * segment origin.
+                */
+               lp = (struct ld_info *)buf;
+               while (lp) {
+                       if (lp->ldinfo_dataorg == mp->entry) {
+                               ldp = ldopen(lp->ldinfo_filename, ldp);
+                               break;
+                       }
+                       if (lp->ldinfo_next == 0)
+                               lp = NULL;
+                       else
+                               lp = (struct ld_info *)((char *)lp + lp->ldinfo_next);
+               }
+               free(buf);
+               if (!ldp) {
+                       errvalid++;
+                       strcpy(errbuf, "readExports: ");
+                       strcat(errbuf, strerror(errno));
+                       return -1;
+               }
+       }
+       if (TYPE(ldp) != U802TOCMAGIC) {
+               errvalid++;
+               strcpy(errbuf, "readExports: bad magic");
+               while(ldclose(ldp) == FAILURE)
+                       ;
+               return -1;
+       }
+       /*
+        * Get the padding for the data section. This is needed for
+        * AIX 4.1 compilers. This is used when building the final
+        * function pointer to the exported symbol.
+        */
+       if (ldnshread(ldp, _DATA, &shdata) != SUCCESS) {
+               errvalid++;
+               strcpy(errbuf, "readExports: cannot read data section header");
+               while(ldclose(ldp) == FAILURE)
+                       ;
+               return -1;
+       }
+       if (ldnshread(ldp, _LOADER, &sh) != SUCCESS) {
+               errvalid++;
+               strcpy(errbuf, "readExports: cannot read loader section header");
+               while(ldclose(ldp) == FAILURE)
+                       ;
+               return -1;
+       }
+       /*
+        * We read the complete loader section in one chunk, this makes
+        * finding long symbol names residing in the string table easier.
+        */
+       if ((ldbuf = (char *)malloc(sh.s_size)) == NULL) {
+               errvalid++;
+               strcpy(errbuf, "readExports: ");
+               strcat(errbuf, strerror(errno));
+               while(ldclose(ldp) == FAILURE)
+                       ;
+               return -1;
+       }
+       if (FSEEK(ldp, sh.s_scnptr, BEGINNING) != OKFSEEK) {
+               errvalid++;
+               strcpy(errbuf, "readExports: cannot seek to loader section");
+               free(ldbuf);
+               while(ldclose(ldp) == FAILURE)
+                       ;
+               return -1;
+       }
+       if (FREAD(ldbuf, sh.s_size, 1, ldp) != 1) {
+               errvalid++;
+               strcpy(errbuf, "readExports: cannot read loader section");
+               free(ldbuf);
+               while(ldclose(ldp) == FAILURE)
+                       ;
+               return -1;
+       }
+       lhp = (LDHDR *)ldbuf;
+       ls = (LDSYM *)(ldbuf+LDHDRSZ);
+       /*
+        * Count the number of exports to include in our export table.
+        */
+       for (i = lhp->l_nsyms; i; i--, ls++) {
+               if (!LDR_EXPORT(*ls))
+                       continue;
+               mp->nExports++;
+       }
+       if ((mp->exports = (ExportPtr)calloc(mp->nExports, sizeof(*mp->exports))) == NULL) {
+               errvalid++;
+               strcpy(errbuf, "readExports: ");
+               strcat(errbuf, strerror(errno));
+               free(ldbuf);
+               while(ldclose(ldp) == FAILURE)
+                       ;
+               return -1;
+       }
+       /*
+        * Fill in the export table. All entries are relative to
+        * the entry point we got from load.
+        */
+       ep = mp->exports;
+       ls = (LDSYM *)(ldbuf+LDHDRSZ);
+       for (i = lhp->l_nsyms; i; i--, ls++) {
+               char *symname;
+               char tmpsym[SYMNMLEN+1];
+               if (!LDR_EXPORT(*ls))
+                       continue;
+               if (ls->l_zeroes == 0)
+                       symname = ls->l_offset+lhp->l_stoff+ldbuf;
+               else {
+                       /*
+                        * The l_name member is not zero terminated, we
+                        * must copy the first SYMNMLEN chars and make
+                        * sure we have a zero byte at the end.
+                        */
+                       strncpy(tmpsym, ls->l_name, SYMNMLEN);
+                       tmpsym[SYMNMLEN] = '\0';
+                       symname = tmpsym;
+               }
+               ep->name = strdup(symname);
+               ep->addr = (void *)((unsigned long)mp->entry +
+                                       ls->l_value - shdata.s_vaddr);
+               ep++;
+       }
+       free(ldbuf);
+       while(ldclose(ldp) == FAILURE)
+               ;
+       return 0;
+}
+
+/*
+ * Find the main modules entry point. This is used as export pointer
+ * for loadbind() to be able to resolve references to the main part.
+ */
+static void * findMain(void)
+{
+       struct ld_info *lp;
+       char *buf;
+       int size = 4*1024;
+       int i;
+       void *ret;
+
+       if ((buf = malloc(size)) == NULL) {
+               errvalid++;
+               strcpy(errbuf, "findMain: ");
+               strcat(errbuf, strerror(errno));
+               return NULL;
+       }
+       while ((i = loadquery(L_GETINFO, buf, size)) == -1 && errno == ENOMEM) {
+               free(buf);
+               size += 4*1024;
+               if ((buf = malloc(size)) == NULL) {
+                       errvalid++;
+                       strcpy(errbuf, "findMain: ");
+                       strcat(errbuf, strerror(errno));
+                       return NULL;
+               }
+       }
+       if (i == -1) {
+               errvalid++;
+               strcpy(errbuf, "findMain: ");
+               strcat(errbuf, strerror(errno));
+               free(buf);
+               return NULL;
+       }
+       /*
+        * The first entry is the main module. The entry point
+        * returned by load() does actually point to the data
+        * segment origin.
+        */
+       lp = (struct ld_info *)buf;
+       ret = lp->ldinfo_dataorg;
+       free(buf);
+       return ret;
+}
diff --git a/src/backend/port/aix/dlfcn.h b/src/backend/port/aix/dlfcn.h
new file mode 100644 (file)
index 0000000..5671e9c
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * @(#)dlfcn.h 1.4 revision of 95/04/25  09:36:52
+ * This is an unpublished work copyright (c) 1992 HELIOS Software GmbH
+ * 30159 Hannover, Germany
+ */
+
+#ifndef __dlfcn_h__
+#define __dlfcn_h__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Mode flags for the dlopen routine.
+ */
+#define RTLD_LAZY      1       /* lazy function call binding */
+#define RTLD_NOW       2       /* immediate function call binding */
+#define RTLD_GLOBAL    0x100   /* allow symbols to be global */
+
+/*
+ * To be able to intialize, a library may provide a dl_info structure
+ * that contains functions to be called to initialize and terminate.
+ */
+struct dl_info {
+       void (*init)(void);
+       void (*fini)(void);
+};
+
+#if __STDC__ || defined(_IBMR2)
+void *dlopen(const char *path, int mode);
+void *dlsym(void *handle, const char *symbol);
+char *dlerror(void);
+int dlclose(void *handle);
+#else
+void *dlopen();
+void *dlsym();
+char *dlerror();
+int dlclose();
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __dlfcn_h__ */
diff --git a/src/backend/port/aix/machine.h b/src/backend/port/aix/machine.h
new file mode 100644 (file)
index 0000000..ab761e4
--- /dev/null
@@ -0,0 +1,19 @@
+/*-------------------------------------------------------------------------
+ *
+ * machine.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MACHINE_H
+#define MACHINE_H
+
+#define BLCKSZ 8192
+
+#endif
+
diff --git a/src/backend/port/aix/mkldexport.sh b/src/backend/port/aix/mkldexport.sh
new file mode 100755 (executable)
index 0000000..378baf9
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# mkldexport
+#      create an AIX exports file from an object file
+#
+# Usage:
+#      mkldexport objectfile [location]
+# where
+#      objectfile is the current location of the object file.
+#      location is the eventual (installed) location of the 
+#              object file (if different from the current
+#              working directory).
+#
+# [This file comes from the Postgres 4.2 distribution. - ay 7/95]
+#
+# Header: /usr/local/devel/postgres/src/tools/mkldexport/RCS/mkldexport.sh,v 1.2 1994/03/13 04:59:12 aoki Exp 
+#
+
+# setting this to nm -B might be better
+NM = /usr/ucb/nm
+
+CMDNAME=`basename $0`
+if [ -z "$1" ]; then
+       echo "Usage: $CMDNAME object [location]"
+       exit 1
+fi
+OBJNAME=`basename $1`
+if [ "`basename $OBJNAME`" != "`basename $OBJNAME .o`" ]; then
+       OBJNAME=`basename $OBJNAME .o`.so
+fi
+if [ -z "$2" ]; then
+       echo '#!'
+else
+       echo '#!' $2/$OBJNAME
+fi
+$NM -g $1 | \
+       egrep ' [TD] ' | \
+       sed -e 's/.* //' | \
+       egrep -v '\$' | \
+       sed -e 's/^[.]//' | \
+       sort | \
+       uniq
diff --git a/src/backend/port/aix/port-protos.h b/src/backend/port/aix/port-protos.h
new file mode 100644 (file)
index 0000000..d503583
--- /dev/null
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * port-protos.h--
+ *    port-specific prototypes for AIX
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PORT_PROTOS_H
+#define PORT_PROTOS_H
+
+#include "dlfcn.h"                     /* this is from jum's libdl package */
+
+/* dynloader.c */
+
+#define  pg_dlopen(f)  dlopen(filename, RTLD_LAZY)
+#define  pg_dlsym(h,f) dlsym(h, f)
+#define  pg_dlclose(h) dlclose(h)
+#define  pg_dlerror()  dlerror()
+
+#endif /* PORT_PROTOS_H */
diff --git a/src/backend/port/alpha/Makefile.inc b/src/backend/port/alpha/Makefile.inc
new file mode 100644 (file)
index 0000000..2371df3
--- /dev/null
@@ -0,0 +1,27 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for port/alpha (Alpha OSF/1 specific stuff)
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+CFLAGS+= -DUSE_POSIX_TIME -DDISABLE_XOPEN_NLS -DNEED_ISINF -DHAS_LONG_LONG
+
+LDADD+= -lln
+
+#
+# The YACC grammar is too big..
+#
+#.if !defined(CDEBUG)
+##CFLAGS+= -Olimit 2000
+#.endif
+
+HEADERS+= machine.h port-protos.h
+
+SUBSRCS= port.c
diff --git a/src/backend/port/alpha/machine.h b/src/backend/port/alpha/machine.h
new file mode 100644 (file)
index 0000000..ab761e4
--- /dev/null
@@ -0,0 +1,19 @@
+/*-------------------------------------------------------------------------
+ *
+ * machine.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MACHINE_H
+#define MACHINE_H
+
+#define BLCKSZ 8192
+
+#endif
+
diff --git a/src/backend/port/alpha/port-protos.h b/src/backend/port/alpha/port-protos.h
new file mode 100644 (file)
index 0000000..658f27b
--- /dev/null
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * port-protos.h--
+ *    prototypes for OSF/1-specific routines
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef PORT_PROTOS_H
+#define PORT_PROTOS_H
+
+#include <dlfcn.h>
+#include "utils/dynamic_loader.h"
+
+/* dynloader.c */
+
+/*
+ * Dynamic Loader on Alpha OSF/1.x
+ *
+ * this dynamic loader uses the system dynamic loading interface for shared 
+ * libraries (ie. dlopen/dlsym/dlclose). The user must specify a shared
+ * library as the file to be dynamically loaded.
+ *
+ */
+#define  pg_dlopen(f)  dlopen(f, RTLD_LAZY)
+#define         pg_dlsym(h, f) ((func_ptr)dlsym(h, f))
+#define         pg_dlclose(h)  dlclose(h)
+#define  pg_dlerror()  dlerror()
+
+/* port.c */
+
+extern void init_address_fixup(void);
+
+#endif  /* PORT_PROTOS_H */
diff --git a/src/backend/port/alpha/port.c b/src/backend/port/alpha/port.c
new file mode 100644 (file)
index 0000000..b984950
--- /dev/null
@@ -0,0 +1,34 @@
+/*-------------------------------------------------------------------------
+ *
+ * port.c--
+ *    OSF/1-specific routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <sys/types.h>
+#include <sys/sysinfo.h>
+#include <sys/proc.h>
+#include "c.h"
+#include "utils/elog.h"
+
+void
+init_address_fixup()
+{
+#ifdef NOFIXADE
+    int buffer[] = { SSIN_UACPROC, UAC_SIGBUS };
+#endif /* NOFIXADE */
+#ifdef NOPRINTADE
+    int buffer[] = { SSIN_UACPROC, UAC_NOPRINT };
+#endif /* NOPRINTADE */
+
+    if (setsysinfo(SSI_NVPAIRS, buffer, 1, (caddr_t) NULL,
+                  (unsigned long) NULL) < 0) {
+       elog(NOTICE, "setsysinfo failed: %d\n", errno);
+    }
+}
diff --git a/src/backend/port/bsdi/Makefile.inc b/src/backend/port/bsdi/Makefile.inc
new file mode 100644 (file)
index 0000000..96eb2c5
--- /dev/null
@@ -0,0 +1,15 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for port/bsdi
+#
+# NOTES
+#    The BSD/OS port is included here by courtesy of Kurt Lidl.
+#
+#    (5) 1994, Kurt Lidl, [email protected]
+#
+#-------------------------------------------------------------------------
+
+CFLAGS+=-DUSE_POSIX_TIME -DNEED_CBRT
+LDADD+= -ldld -lipc
+SUBSRCS= dynloader.c
diff --git a/src/backend/port/bsdi/dynloader.c b/src/backend/port/bsdi/dynloader.c
new file mode 100644 (file)
index 0000000..c167a01
--- /dev/null
@@ -0,0 +1,93 @@
+/*-------------------------------------------------------------------------
+ *
+ * dynloader.c--
+ *    Dynamic Loader for Postgres for Linux, generated from those for
+ *    Ultrix.
+ *
+ *    You need to install the dld library on your Linux system!
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    /usr/local/devel/pglite/cvs/src/backend/port/linux/dynloader.c,v 1.1.1.1 1994/11/07 05:19:37 andrew Exp
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <dld.h>
+#include "postgres.h"
+#include "port-protos.h"
+#include "utils/elog.h"
+#include "fmgr.h"
+
+extern char pg_pathname[];
+
+void *
+pg_dlopen(char *filename)
+{
+    static int dl_initialized= 0;
+
+    /*
+     * initializes the dynamic loader with the executable's pathname.
+     * (only needs to do this the first time pg_dlopen is called.)
+     */
+    if (!dl_initialized) {
+        if (dld_init (dld_find_executable (pg_pathname))) {
+           return NULL;
+       }
+       /*
+        * if there are undefined symbols, we want dl to search from the
+        * following libraries also.
+        */
+       dl_initialized= 1;
+    }
+
+    /*
+     * link the file, then check for undefined symbols!
+     */
+    if (dld_link(filename)) {
+       return NULL;
+    }
+
+    /*
+     * If undefined symbols: try to link with the C and math libraries!
+     * This could be smarter, if the dynamic linker was able to handle
+     * shared libs!
+     */
+    if(dld_undefined_sym_count > 0) {
+       if (dld_link("/usr/lib/libc.a")) {
+           elog(NOTICE, "dld: Cannot link C library!");
+           return NULL;
+       }
+       if(dld_undefined_sym_count > 0) {
+           if (dld_link("/usr/lib/libm.a")) {
+               elog(NOTICE, "dld: Cannot link math library!");
+               return NULL;
+           }
+           if(dld_undefined_sym_count > 0) {
+               int count = dld_undefined_sym_count;
+               char **list= dld_list_undefined_sym();
+
+               /* list the undefined symbols, if any */
+               elog(NOTICE, "dld: Undefined:");
+               do {
+                   elog(NOTICE, "  %s", *list);
+                   list++;
+                   count--;
+               } while(count > 0);
+
+               dld_unlink_by_file(filename, 1);
+               return NULL;
+           }
+       }
+    }
+
+    return (void *) strdup(filename);
+}
+
+char *
+pg_dlerror()
+{
+    return dld_strerror(dld_errno);
+}
diff --git a/src/backend/port/bsdi/machine.h b/src/backend/port/bsdi/machine.h
new file mode 100644 (file)
index 0000000..d53defb
--- /dev/null
@@ -0,0 +1,18 @@
+/*-------------------------------------------------------------------------
+ *
+ * machine.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * machine.h,v 1.1.1.1 1994/11/07 05:19:37 andrew Exp
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MACHINE_H
+#define MACHINE_H
+
+#define BLCKSZ 8192
+
+#endif
diff --git a/src/backend/port/bsdi/port-protos.h b/src/backend/port/bsdi/port-protos.h
new file mode 100644 (file)
index 0000000..6583571
--- /dev/null
@@ -0,0 +1,33 @@
+/*-------------------------------------------------------------------------
+ *
+ * port-protos.h--
+ *    port-specific prototypes for SunOS 4
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * port-protos.h,v 1.2 1995/05/25 22:51:03 andrew Exp
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PORT_PROTOS_H
+#define PORT_PROTOS_H
+
+#include "fmgr.h"                      /* for func_ptr */
+#include "utils/dynamic_loader.h"
+
+/* dynloader.c */
+
+#ifndef LINUX_ELF
+#define pg_dlsym(handle, funcname)     ((func_ptr) dld_get_func((funcname)))
+#define pg_dlclose(handle)             ({ dld_unlink_by_file(handle, 1); free(handle); })
+#else
+#define        pg_dlopen(f)    dlopen(f, 1)
+#define        pg_dlsym        dlsym
+#define        pg_dlclose      dlclose
+#define        pg_dlerror      dlerror
+#endif
+
+/* port.c */
+
+#endif /* PORT_PROTOS_H */
diff --git a/src/backend/port/bsdi/port.c b/src/backend/port/bsdi/port.c
new file mode 100644 (file)
index 0000000..8819b1a
--- /dev/null
@@ -0,0 +1,13 @@
+/*-------------------------------------------------------------------------
+ *
+ * port.c--
+ *    Linux-specific routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    /usr/local/devel/pglite/cvs/src/backend/port/linux/port.c,v 1.1.1.1 1994/11/07 05:19:38 andrew Exp
+ *
+ *-------------------------------------------------------------------------
+ */
diff --git a/src/backend/port/hpux/Makefile.inc b/src/backend/port/hpux/Makefile.inc
new file mode 100644 (file)
index 0000000..91fba41
--- /dev/null
@@ -0,0 +1,68 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for port/hpux (HP-UX specific stuff)
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+#
+# HP-UX needs:
+# -W l,-E      export symbols for linking with the shared libraries 
+#              dynamic loader
+# -W p,-H400000        expand cpp #define table size so the Nodes files don't 
+#              break it
+#
+# -W p,-H400000
+ifeq ($(CC), cc)
+CFLAGS+= -W l,-E 
+LDFLAGS+= -W l,-E
+LDADD+= -ll -ldld
+else
+ifeq ($(CC), gcc)
+LDADD+= -ll /usr/lib/libdld.sl
+endif
+endif
+
+CFLAGS+= -DUSE_POSIX_TIME
+
+#
+# cbrt(3m) and rint(3m) are missing from 8.07.
+# cbrt(3m) and rint(3m) are broken in 9.01.
+# cbrt(3m) seems to be missing on 9.00 even though it is documented.
+#
+CFLAGS+= -DNEED_RINT -DNEED_CBRT
+
+#
+# The #pragma trick required on 8.07 no longer works -- the #pragma
+# is thoroughly broken.  However, the +u flag has been extended to
+# handle alignment requirement arguments (defaulting to 2) for things 
+# other than struct references, so the #pragma is no longer needed.
+#
+
+#
+# (1) The YACC grammar is too big..
+# (HP-UX 9.0x, x<2, added basic block limits for +O2; 9.0x, x>=2, changed
+# the syntax to something else.)
+#
+# (2) The 9.00 optimizer chokes on some of our source.
+#
+#.if (${HPUX_MAJOR} == "09")
+#.  if !defined(CDEBUG)
+#.    if (${HPUX_MINOR} == "00" || ${HPUX_MINOR} == "01")
+#CFLAGS+= +Obb600
+#CFLAGS+= -DWEAK_C_OPTIMIZER
+#.    else
+#CFLAGS+= +Onolimit
+#.    endif
+#.  endif
+#.endif
+
+HEADERS+= fixade.h machine.h port-protos.h
+
+SUBSRCS+= dynloader.c port.c tas.s
diff --git a/src/backend/port/hpux/dynloader.c b/src/backend/port/hpux/dynloader.c
new file mode 100644 (file)
index 0000000..32a0aa3
--- /dev/null
@@ -0,0 +1,57 @@
+/*-------------------------------------------------------------------------
+ *
+ * dynloader.c--
+ *    dynamic loader for HP-UX using the shared library mechanism
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *  NOTES
+ *     all functions are defined here -- it's impossible to trace the 
+ *     shl_* routines from the bundled HP-UX debugger.
+ *
+ *-------------------------------------------------------------------------
+ */
+/* System includes */
+#include <stdio.h>
+#include <a.out.h>
+#include <dl.h>
+#include "c.h"
+#include "fmgr.h"
+#include "utils/dynamic_loader.h"
+#include "port-protos.h"
+
+void *
+pg_dlopen(char *filename)
+{
+    shl_t handle = shl_load(filename, BIND_DEFERRED, 0);
+
+    return((void *) handle);
+}
+
+func_ptr
+pg_dlsym(void *handle, char *funcname)
+{
+    func_ptr f;
+
+    if (shl_findsym((shl_t *) &handle, funcname, TYPE_PROCEDURE, &f) == -1) {
+       f = (func_ptr) NULL;
+    }
+    return(f);
+}
+
+void
+pg_dlclose(void *handle)
+{
+    shl_unload((shl_t) handle);
+}
+
+char *
+pg_dlerror()
+{
+    static char errmsg[]= "shl_load failed";
+    return errmsg;
+}
diff --git a/src/backend/port/hpux/fixade.h b/src/backend/port/hpux/fixade.h
new file mode 100644 (file)
index 0000000..689926e
--- /dev/null
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * fixade.h--
+ *    compiler tricks to make things work while POSTGRES does non-native
+ *    dereferences on PA-RISC.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *   NOTES
+ *     This must be included in EVERY source file.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        FIXADE_H
+#define FIXADE_H
+
+#if !defined(NOFIXADE)
+
+#if defined(HP_S500_ALIGN)
+/* ----------------
+ *     This cheesy hack turns ON unaligned-access fixup on H-P PA-RISC;
+ *     the resulting object files contain code that explicitly handles
+ *     realignment on reference, so it slows memory access down by a 
+ *     considerable factor.  It must be used in conjunction with the +u 
+ *     flag to cc.  The #pragma is included in c.h to be safe since EVERY 
+ *     source file that performs unaligned access must contain the #pragma.
+ * ----------------
+ */
+#pragma HP_ALIGN HPUX_NATURAL_S500
+
+#if defined(BROKEN_STRUCT_INIT)
+/* ----------------
+ *     This is so bogus.  The HP-UX 9.01 compiler has totally broken
+ *     struct initialization code.  It actually length-checks ALL 
+ *     array initializations within structs against the FIRST one that 
+ *     it sees (when #pragma HP_ALIGN HPUX_NATURAL_S500 is defined).. 
+ *     we have to throw in this unused structure before struct varlena
+ *     is defined.
+ *
+ *     XXX guess you don't need the #pragma anymore after all :-)
+ *     since no one looks at this except me i think i'll just leave
+ *     this here for now..
+ * ----------------
+ */
+struct HP_WAY_BOGUS {
+       char    hpwb_bogus[8192];
+};
+struct HP_TOO_BOGUS {
+       int     hptb_bogus[8192];
+};
+#endif /* BROKEN_STRUCT_INIT */
+#endif /* HP_S500_ALIGN */
+
+#if defined(WEAK_C_OPTIMIZER)
+#pragma OPT_LEVEL 1
+#endif /* WEAK_C_OPTIMIZER */
+
+#endif /* !NOFIXADE */
+
+#endif /* FIXADE_H */
diff --git a/src/backend/port/hpux/machine.h b/src/backend/port/hpux/machine.h
new file mode 100644 (file)
index 0000000..ec55064
--- /dev/null
@@ -0,0 +1,18 @@
+/*-------------------------------------------------------------------------
+ *
+ * machine.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MACHINE_H
+#define MACHINE_H
+
+#define BLCKSZ 8192
+
+#endif
diff --git a/src/backend/port/hpux/port-protos.h b/src/backend/port/hpux/port-protos.h
new file mode 100644 (file)
index 0000000..ef81263
--- /dev/null
@@ -0,0 +1,34 @@
+/*-------------------------------------------------------------------------
+ *
+ * port-protos.h--
+ *    port-specific prototypes for HP-UX
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PORT_PROTOS_H
+#define PORT_PROTOS_H
+
+#include <sys/resource.h>              /* for struct rusage */
+#include <dl.h>                                /* for shl_t */
+
+#include "utils/dynamic_loader.h"
+
+/* dynloader.c */
+
+/* pg_dl{open,close,sym} prototypes are in utils/dynamic_loader.h */
+
+/* port.c */
+
+extern int init_address_fixup(void);
+extern double rint(double x);
+extern double cbrt(double x);
+extern long random(void);
+extern void srandom(int seed);
+extern int getrusage(int who, struct rusage *ru);
+
+#endif /* PORT_PROTOS_H */
diff --git a/src/backend/port/hpux/port.c b/src/backend/port/hpux/port.c
new file mode 100644 (file)
index 0000000..8e109b5
--- /dev/null
@@ -0,0 +1,47 @@
+/*-------------------------------------------------------------------------
+ *
+ * port.c--
+ *    port-specific routines for HP-UX
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    For the most part, this file gets around some non-POSIX calls 
+ *    in POSTGRES.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <unistd.h>            /* for rand()/srand() prototypes */
+#include <math.h>              /* for pow() prototype */
+#include <sys/syscall.h>       /* for syscall #defines */
+
+#include "c.h"
+
+void
+init_address_fixup()
+{
+    /*
+     * On PA-RISC, unaligned access fixup is handled by the compiler,
+     * not by the kernel.
+     */
+}
+
+long
+random()
+{
+       return(lrand48());
+}
+
+void srandom(int seed)
+{
+       srand48((long int) seed);
+}
+
+getrusage(int who, struct rusage *ru)
+{
+       return(syscall(SYS_GETRUSAGE, who, ru));
+}
diff --git a/src/backend/port/hpux/tas.c.template b/src/backend/port/hpux/tas.c.template
new file mode 100644 (file)
index 0000000..3ab37eb
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * To generate tas.s using this template:
+ *     1. cc +O2 -S -c tas.c
+ *     2. edit tas.s:
+ *             - replace the LDW with LDCWX
+ * For details about the LDCWX instruction, see the "Precision
+ * Architecture and Instruction Reference Manual" (09740-90014 of June
+ * 1987), p. 5-38.
+ */
+
+int
+tas(lock)
+    int *lock; /* LDCWX is a word instruction */
+{
+    /*
+     * LDCWX requires that we align the "semaphore" to a 16-byte
+     * boundary.  The actual datum is a single word (4 bytes).
+     */
+    lock = ((long) lock + 15) & ~15;
+
+    /*
+     * The LDCWX instruction atomically clears the target word and
+     * returns the previous value.  Hence, if the instruction returns
+     * 0, someone else has already acquired the lock before we tested
+     * it (i.e., we have failed).
+     *
+     * Notice that this means that we actually clear the word to set
+     * the lock and set the word to clear the lock.  This is the
+     * opposite behavior from the SPARC LDSTUB instruction.  For some
+     * reason everything that H-P does is rather baroque...
+     */
+    if (*lock) {       /* this generates the LDW */
+       return(0);      /* success */
+    }
+    return(1);         /* failure */
+}
diff --git a/src/backend/port/hpux/tas.s b/src/backend/port/hpux/tas.s
new file mode 100644 (file)
index 0000000..d978a7c
--- /dev/null
@@ -0,0 +1,28 @@
+
+        .SPACE  $TEXT$,SORT=8
+        .SUBSPA $CODE$,QUAD=0,ALIGN=4,ACCESS=44,CODE_ONLY,SORT=24
+tas
+        .PROC
+        .CALLINFO CALLER,FRAME=0,ENTRY_SR=3
+        .ENTRY
+        LDO     15(%r26),%r31   ;offset 0x0
+        DEPI    0,31,4,%r31     ;offset 0x4
+        LDCWX   0(0,%r31),%r23  ;offset 0x8
+        COMICLR,=       0,%r23,%r0      ;offset 0xc
+        DEP,TR  %r0,31,32,%r28  ;offset 0x10
+$00000001
+        LDI     1,%r28  ;offset 0x14
+$L0
+        .EXIT
+        BV,N    %r0(%r2)        ;offset 0x18
+        .PROCEND ;in=26;out=28;
+
+
+        .SPACE  $TEXT$
+        .SUBSPA $CODE$
+        .SPACE  $PRIVATE$,SORT=16
+        .SUBSPA $DATA$,QUAD=1,ALIGN=8,ACCESS=31,SORT=16
+        .SPACE  $TEXT$
+        .SUBSPA $CODE$
+        .EXPORT tas,ENTRY,PRIV_LEV=3,ARGW0=GR,RTNVAL=GR
+        .END
diff --git a/src/backend/port/irix5/Makefile.inc b/src/backend/port/irix5/Makefile.inc
new file mode 100644 (file)
index 0000000..40527b3
--- /dev/null
@@ -0,0 +1,20 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for port/irix5 (IRIX 5 specific stuff)
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    /usr/local/devel/pglite/cvs/src/backend/port/sparc_solaris/Makefile.inc,v 1.3 1995/03/21 06:51:21 andrew Exp
+#
+#-------------------------------------------------------------------------
+
+CFLAGS+= -DUSE_POSIX_TIME -DNEED_ISINF -DNO_EMPTY_STMTS
+
+LDADD+= -ll
+
+SUBSRCS+= port.c
+
+HEADERS+= machine.h port-protos.h 
diff --git a/src/backend/port/irix5/README b/src/backend/port/irix5/README
new file mode 100644 (file)
index 0000000..2c66463
--- /dev/null
@@ -0,0 +1,2 @@
+The IRIX 5 port was contributed by
+       Paul 'Shag' Walmsley <[email protected]>
diff --git a/src/backend/port/irix5/machine.h b/src/backend/port/irix5/machine.h
new file mode 100644 (file)
index 0000000..fd1f22c
--- /dev/null
@@ -0,0 +1,19 @@
+/*-------------------------------------------------------------------------
+ *
+ * machine.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * machine.h,v 1.1.1.1 1994/11/07 05:19:38 andrew Exp
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MACHINE_H
+#define MACHINE_H
+
+#define BLCKSZ 8192
+
+#endif
+
diff --git a/src/backend/port/irix5/port-protos.h b/src/backend/port/irix5/port-protos.h
new file mode 100644 (file)
index 0000000..7313e49
--- /dev/null
@@ -0,0 +1,36 @@
+/*-------------------------------------------------------------------------
+ *
+ * port-protos.h--
+ *    port-specific prototypes for Irix 5
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * port-protos.h,v 1.2 1995/03/17 06:40:18 andrew Exp
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PORT_PROTOS_H
+#define PORT_PROTOS_H
+
+#include "fmgr.h"                      /* for func_ptr */
+#include "utils/dynamic_loader.h"
+
+/* dynloader.c */
+/*
+ * Dynamic Loader on SunOS 4.
+ *
+ * this dynamic loader uses the system dynamic loading interface for shared 
+ * libraries (ie. dlopen/dlsym/dlclose). The user must specify a shared
+ * library as the file to be dynamically loaded.
+ *
+ */
+#define pg_dlopen(f)   dlopen(f,1)
+#define        pg_dlsym        dlsym
+#define        pg_dlclose      dlclose
+#define        pg_dlerror      dlerror
+
+/* port.c */
+extern long random(void);
+
+#endif /* PORT_PROTOS_H */
diff --git a/src/backend/port/irix5/port.c b/src/backend/port/irix5/port.c
new file mode 100644 (file)
index 0000000..82303ed
--- /dev/null
@@ -0,0 +1,16 @@
+/*-------------------------------------------------------------------------
+ *
+ * port.c--
+ *    Irix5-specific routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    /usr/local/devel/pglite/cvs/src/backend/port/sparc_solaris/port.c,v 1.2 1995/03/17 06:40:19 andrew Exp
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <math.h>              /* for pow() prototype */
+
+#include <errno.h>
diff --git a/src/backend/port/linux/Makefile.inc b/src/backend/port/linux/Makefile.inc
new file mode 100644 (file)
index 0000000..bcb68d4
--- /dev/null
@@ -0,0 +1,36 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for port/linux (Linux specific stuff)
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+# NOTES
+#    The Linux port is included here by courtesy of Kai Petzke.
+#
+#    (C) 1994, Kai Petzke, [email protected]
+#
+#-------------------------------------------------------------------------
+
+#
+# linux has fast linkers and don't need the BIGOBJ stuff.
+#
+BIGOBJS= false
+
+
+ifdef LINUX_ELF
+CC=gcc
+LDADD+= -ldl
+CFLAGS+= -DLINUX_ELF
+else
+LDADD+= -ldld
+SUBSRCS+= dynloader.c
+endif
+
+HEADERS+= machine.h port-protos.h
+CFLAGS+= -DNEED_CBRT
+
diff --git a/src/backend/port/linux/dynloader.c b/src/backend/port/linux/dynloader.c
new file mode 100644 (file)
index 0000000..fbe3d74
--- /dev/null
@@ -0,0 +1,93 @@
+/*-------------------------------------------------------------------------
+ *
+ * dynloader.c--
+ *    Dynamic Loader for Postgres for Linux, generated from those for
+ *    Ultrix.
+ *
+ *    You need to install the dld library on your Linux system!
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <dld.h>
+#include "postgres.h"
+#include "port-protos.h"
+#include "utils/elog.h"
+#include "fmgr.h"
+
+extern char pg_pathname[];
+
+void *
+pg_dlopen(char *filename)
+{
+    static int dl_initialized= 0;
+
+    /*
+     * initializes the dynamic loader with the executable's pathname.
+     * (only needs to do this the first time pg_dlopen is called.)
+     */
+    if (!dl_initialized) {
+        if (dld_init (dld_find_executable (pg_pathname))) {
+           return NULL;
+       }
+       /*
+        * if there are undefined symbols, we want dl to search from the
+        * following libraries also.
+        */
+       dl_initialized= 1;
+    }
+
+    /*
+     * link the file, then check for undefined symbols!
+     */
+    if (dld_link(filename)) {
+       return NULL;
+    }
+
+    /*
+     * If undefined symbols: try to link with the C and math libraries!
+     * This could be smarter, if the dynamic linker was able to handle
+     * shared libs!
+     */
+    if(dld_undefined_sym_count > 0) {
+       if (dld_link("/usr/lib/libc.a")) {
+           elog(NOTICE, "dld: Cannot link C library!");
+           return NULL;
+       }
+       if(dld_undefined_sym_count > 0) {
+           if (dld_link("/usr/lib/libm.a")) {
+               elog(NOTICE, "dld: Cannot link math library!");
+               return NULL;
+           }
+           if(dld_undefined_sym_count > 0) {
+               int count = dld_undefined_sym_count;
+               char **list= dld_list_undefined_sym();
+
+               /* list the undefined symbols, if any */
+               elog(NOTICE, "dld: Undefined:");
+               do {
+                   elog(NOTICE, "  %s", *list);
+                   list++;
+                   count--;
+               } while(count > 0);
+
+               dld_unlink_by_file(filename, 1);
+               return NULL;
+           }
+       }
+    }
+
+    return (void *) strdup(filename);
+}
+
+char *
+pg_dlerror()
+{
+    return dld_strerror(dld_errno);
+}
diff --git a/src/backend/port/linux/machine.h b/src/backend/port/linux/machine.h
new file mode 100644 (file)
index 0000000..ec55064
--- /dev/null
@@ -0,0 +1,18 @@
+/*-------------------------------------------------------------------------
+ *
+ * machine.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MACHINE_H
+#define MACHINE_H
+
+#define BLCKSZ 8192
+
+#endif
diff --git a/src/backend/port/linux/port-protos.h b/src/backend/port/linux/port-protos.h
new file mode 100644 (file)
index 0000000..a37bb14
--- /dev/null
@@ -0,0 +1,37 @@
+/*-------------------------------------------------------------------------
+ *
+ * port-protos.h--
+ *    port-specific prototypes for SunOS 4
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PORT_PROTOS_H
+#define PORT_PROTOS_H
+
+#include "fmgr.h"                      /* for func_ptr */
+#include "utils/dynamic_loader.h"
+#ifdef LINUX_ELF
+#include "dlfcn.h"
+#endif
+
+/* dynloader.c */
+
+#ifndef LINUX_ELF
+#define pg_dlsym(handle, funcname)     ((func_ptr) dld_get_func((funcname)))
+#define pg_dlclose(handle)             ({ dld_unlink_by_file(handle, 1); free(handle); })
+#else
+/* #define     pg_dlopen(f)    dlopen(f, 1) */
+#define        pg_dlopen(f)    dlopen(f, 2)
+#define        pg_dlsym        dlsym
+#define        pg_dlclose      dlclose
+#define        pg_dlerror      dlerror
+#endif
+
+/* port.c */
+
+#endif /* PORT_PROTOS_H */
diff --git a/src/backend/port/linux/port.c b/src/backend/port/linux/port.c
new file mode 100644 (file)
index 0000000..e4b3871
--- /dev/null
@@ -0,0 +1,13 @@
+/*-------------------------------------------------------------------------
+ *
+ * port.c--
+ *    Linux-specific routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
diff --git a/src/backend/port/sparc/Makefile.inc b/src/backend/port/sparc/Makefile.inc
new file mode 100644 (file)
index 0000000..3d3873a
--- /dev/null
@@ -0,0 +1,23 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for port/sparc (SPARC/SunOS 4 specific stuff)
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+CFLAGS+= -DUSE_POSIX_TIME
+
+LDADD+= -lln -ldl
+
+#
+# SunOS 4 strtol is broken -- doesn't report overflow using errno.
+#
+SUBSRCS= strtol.c
+
+HEADERS+= float.h machine.h port-protos.h
diff --git a/src/backend/port/sparc/float.h b/src/backend/port/sparc/float.h
new file mode 100644 (file)
index 0000000..b784afb
--- /dev/null
@@ -0,0 +1,30 @@
+/*-------------------------------------------------------------------------
+ *
+ * float.h--
+ *    definitions for ANSI floating point
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    These come straight out of ANSI X3.159-1989 (p.18) and
+ *    would be unnecessary if SunOS 4 were ANSI-compliant.
+ *
+ *    This is only a partial listing because I'm lazy to type
+ *    the whole thing in.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FLOAT_H
+#define FLOAT_H
+
+#define FLT_DIG        6
+#define FLT_MIN        ((float) 1.17549435e-38)
+#define FLT_MAX        ((float) 3.40282347e+38)
+#define DBL_DIG        15
+#define DBL_MIN        2.2250738585072014e-308
+#define DBL_MAX        1.7976931348623157e+308
+
+#endif /* FLOAT_H */
diff --git a/src/backend/port/sparc/machine.h b/src/backend/port/sparc/machine.h
new file mode 100644 (file)
index 0000000..ab761e4
--- /dev/null
@@ -0,0 +1,19 @@
+/*-------------------------------------------------------------------------
+ *
+ * machine.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MACHINE_H
+#define MACHINE_H
+
+#define BLCKSZ 8192
+
+#endif
+
diff --git a/src/backend/port/sparc/port-protos.h b/src/backend/port/sparc/port-protos.h
new file mode 100644 (file)
index 0000000..66190d6
--- /dev/null
@@ -0,0 +1,34 @@
+/*-------------------------------------------------------------------------
+ *
+ * port-protos.h--
+ *    port-specific prototypes for SunOS 4
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PORT_PROTOS_H
+#define PORT_PROTOS_H
+
+#include <dlfcn.h>
+#include "fmgr.h"                      /* for func_ptr */
+#include "utils/dynamic_loader.h"
+
+/* dynloader.c */
+/*
+ * Dynamic Loader on SunOS 4.
+ *
+ * this dynamic loader uses the system dynamic loading interface for shared 
+ * libraries (ie. dlopen/dlsym/dlclose). The user must specify a shared
+ * library as the file to be dynamically loaded.
+ *
+ */
+#define        pg_dlopen(f)    dlopen(f, 1)
+#define        pg_dlsym        dlsym
+#define        pg_dlclose      dlclose
+#define        pg_dlerror      dlerror
+
+#endif /* PORT_PROTOS_H */
diff --git a/src/backend/port/sparc/strtol.c b/src/backend/port/sparc/strtol.c
new file mode 100644 (file)
index 0000000..5850848
--- /dev/null
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strtol.c   5.4 (Berkeley) 2/23/91";
+#endif /* LIBC_SCCS and not lint */
+
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#define const
+
+/*
+ * Convert a string to a long integer.
+ *
+ * Ignores `locale' stuff.  Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+long
+strtol(nptr, endptr, base)
+       const char *nptr;
+       char **endptr;
+       register int base;
+{
+       register const char *s = nptr;
+       register unsigned long acc;
+       register int c;
+       register unsigned long cutoff;
+       register int neg = 0, any, cutlim;
+
+       /*
+        * Skip white space and pick up leading +/- sign if any.
+        * If base is 0, allow 0x for hex and 0 for octal, else
+        * assume decimal; if base is already 16, allow 0x.
+        */
+       do {
+               c = *s++;
+       } while (isspace(c));
+       if (c == '-') {
+               neg = 1;
+               c = *s++;
+       } else if (c == '+')
+               c = *s++;
+       if ((base == 0 || base == 16) &&
+           c == '0' && (*s == 'x' || *s == 'X')) {
+               c = s[1];
+               s += 2;
+               base = 16;
+       }
+       if (base == 0)
+               base = c == '0' ? 8 : 10;
+
+       /*
+        * Compute the cutoff value between legal numbers and illegal
+        * numbers.  That is the largest legal value, divided by the
+        * base.  An input number that is greater than this value, if
+        * followed by a legal input character, is too big.  One that
+        * is equal to this value may be valid or not; the limit
+        * between valid and invalid numbers is then based on the last
+        * digit.  For instance, if the range for longs is
+        * [-2147483648..2147483647] and the input base is 10,
+        * cutoff will be set to 214748364 and cutlim to either
+        * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
+        * a value > 214748364, or equal but the next digit is > 7 (or 8),
+        * the number is too big, and we will return a range error.
+        *
+        * Set any if any `digits' consumed; make it negative to indicate
+        * overflow.
+        */
+       cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
+       cutlim = cutoff % (unsigned long)base;
+       cutoff /= (unsigned long)base;
+       for (acc = 0, any = 0;; c = *s++) {
+               if (isdigit(c))
+                       c -= '0';
+               else if (isalpha(c))
+                       c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+               else
+                       break;
+               if (c >= base)
+                       break;
+               if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
+                       any = -1;
+               else {
+                       any = 1;
+                       acc *= base;
+                       acc += c;
+               }
+       }
+       if (any < 0) {
+               acc = neg ? LONG_MIN : LONG_MAX;
+               errno = ERANGE;
+       } else if (neg)
+               acc = -acc;
+       if (endptr != 0)
+               *endptr = any ? s - 1 : (char *)nptr;
+       return (acc);
+}
diff --git a/src/backend/port/sparc_solaris/Makefile.inc b/src/backend/port/sparc_solaris/Makefile.inc
new file mode 100644 (file)
index 0000000..3900c5c
--- /dev/null
@@ -0,0 +1,20 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for port/sparc_solaris (SPARC/Solaris 2.x specific stuff)
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+CFLAGS+= -DUSE_POSIX_TIME -DNEED_ISINF -DNEED_RUSAGE -DNO_EMPTY_STMTS
+
+LDADD+= -ll -ldl
+
+SUBSRCS+= port.c tas.s
+
+HEADERS+= machine.h port-protos.h rusagestub.h
diff --git a/src/backend/port/sparc_solaris/machine.h b/src/backend/port/sparc_solaris/machine.h
new file mode 100644 (file)
index 0000000..ab761e4
--- /dev/null
@@ -0,0 +1,19 @@
+/*-------------------------------------------------------------------------
+ *
+ * machine.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MACHINE_H
+#define MACHINE_H
+
+#define BLCKSZ 8192
+
+#endif
+
diff --git a/src/backend/port/sparc_solaris/port-protos.h b/src/backend/port/sparc_solaris/port-protos.h
new file mode 100644 (file)
index 0000000..29489a6
--- /dev/null
@@ -0,0 +1,38 @@
+/*-------------------------------------------------------------------------
+ *
+ * port-protos.h--
+ *    port-specific prototypes for SunOS 4
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PORT_PROTOS_H
+#define PORT_PROTOS_H
+
+#include <dlfcn.h>
+#include "fmgr.h"                      /* for func_ptr */
+#include "utils/dynamic_loader.h"
+
+/* dynloader.c */
+/*
+ * Dynamic Loader on SunOS 4.
+ *
+ * this dynamic loader uses the system dynamic loading interface for shared 
+ * libraries (ie. dlopen/dlsym/dlclose). The user must specify a shared
+ * library as the file to be dynamically loaded.
+ *
+ */
+#define pg_dlopen(f)   dlopen(f,1)
+#define        pg_dlsym        dlsym
+#define        pg_dlclose      dlclose
+#define        pg_dlerror      dlerror
+
+/* port.c */
+extern long random(void);
+extern void srandom(int seed);
+
+#endif /* PORT_PROTOS_H */
diff --git a/src/backend/port/sparc_solaris/port.c b/src/backend/port/sparc_solaris/port.c
new file mode 100644 (file)
index 0000000..72ea2b2
--- /dev/null
@@ -0,0 +1,66 @@
+/*-------------------------------------------------------------------------
+ *
+ * port.c--
+ *    SunOS5-specific routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <math.h>              /* for pow() prototype */
+
+#include <errno.h>
+#include "rusagestub.h"
+
+long
+random()
+{
+    return(lrand48());
+}
+
+void
+srandom(int seed)
+{
+    srand48((long int) seed);
+}
+
+int
+getrusage(int who, struct rusage *rusage)
+{
+    struct tms tms;
+    register int tick_rate = CLK_TCK;  /* ticks per second */
+    clock_t u, s;
+
+    if (rusage == (struct rusage *) NULL) {
+       errno = EFAULT;
+       return(-1);
+    }
+    if (times(&tms) < 0) {
+       /* errno set by times */
+       return(-1);
+    }
+    switch (who) {
+    case RUSAGE_SELF:
+       u = tms.tms_utime;
+       s = tms.tms_stime;
+       break;
+    case RUSAGE_CHILDREN:
+       u = tms.tms_cutime;
+       s = tms.tms_cstime;
+       break;
+    default:
+       errno = EINVAL;
+       return(-1);
+    }
+#define TICK_TO_SEC(T, RATE)   ((T)/(RATE))
+#define        TICK_TO_USEC(T,RATE)    (((T)%(RATE)*1000000)/RATE)
+    rusage->ru_utime.tv_sec = TICK_TO_SEC(u, tick_rate);
+    rusage->ru_utime.tv_usec = TICK_TO_USEC(u, tick_rate);
+    rusage->ru_stime.tv_sec = TICK_TO_SEC(s, tick_rate);
+    rusage->ru_stime.tv_usec = TICK_TO_USEC(u, tick_rate);
+    return(0);
+}
diff --git a/src/backend/port/sparc_solaris/rusagestub.h b/src/backend/port/sparc_solaris/rusagestub.h
new file mode 100644 (file)
index 0000000..11c8667
--- /dev/null
@@ -0,0 +1,30 @@
+/*-------------------------------------------------------------------------
+ *
+ * rusagestub.h--
+ *    Stubs for getrusage(3).
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef RUSAGESTUB_H
+#define RUSAGESTUB_H
+
+#include <sys/time.h>  /* for struct timeval */
+#include <sys/times.h> /* for struct tms */
+#include <limits.h>    /* for CLK_TCK */
+
+#define        RUSAGE_SELF     0
+#define        RUSAGE_CHILDREN -1
+
+struct rusage {
+    struct timeval ru_utime;           /* user time used */
+    struct timeval ru_stime;           /* system time used */
+};
+
+extern int getrusage(int who, struct rusage *rusage);
+
+#endif /* RUSAGESTUB_H */
diff --git a/src/backend/port/sparc_solaris/tas.s b/src/backend/port/sparc_solaris/tas.s
new file mode 100644 (file)
index 0000000..b3cafef
--- /dev/null
@@ -0,0 +1,50 @@
+       !!
+       !! $Header$
+       !!
+       !! this would be a piece of inlined assembler but it appears
+       !! to be easier to just write the assembler than to try to 
+       !! figure out how to make sure that in/out registers are kept
+       !! straight in the asm's.
+       !!
+       .file   "tas.c"
+.section       ".text"
+       .align 4
+       .global tas
+       .type    tas,#function
+       .proc   04
+tas:
+       !!
+       !! this is a leaf procedure - no need to save windows and 
+       !! diddle the CWP.
+       !!
+       !#PROLOGUE# 0
+       !#PROLOGUE# 1
+       
+       !!
+       !! write 0xFF into the lock address, saving the old value in %o0.
+       !! this is an atomic action, even on multiprocessors.
+       !!
+       ldstub [%o0],%o0
+       
+       !!
+       !! if it was already set when we set it, somebody else already
+       !! owned the lock -- return 1.
+       !!
+       cmp %o0,0
+       bne .LL2
+       mov 1,%o0
+               
+       !!
+       !! otherwise, it was clear and we now own the lock -- return 0.
+       !!
+       mov 0,%o0
+.LL2:
+       !!
+       !! this is a leaf procedure - no need to restore windows and 
+       !! diddle the CWP.
+       !!
+       retl
+       nop
+.LLfe1:
+       .size    tas,.LLfe1-tas
+       .ident  "GCC: (GNU) 2.5.8"
diff --git a/src/backend/port/ultrix4/Makefile.inc b/src/backend/port/ultrix4/Makefile.inc
new file mode 100644 (file)
index 0000000..fad247d
--- /dev/null
@@ -0,0 +1,27 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for port/ultrix (Ultrix4.x specific stuff)
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+CFLAGS+= -DNEED_ISINF -DUSE_POSIX_TIME
+
+LDADD+= -ldl -lln
+
+#
+# The YACC grammar is too big..
+#
+#.if !defined(CDEBUG)
+#CFLAGS+= -Olimit 2000
+#.endif
+
+HEADERS+= dl.h machine.h port-protos.h
+
+SUBSRCS+= dynloader.c port.c strdup.c
diff --git a/src/backend/port/ultrix4/dl.h b/src/backend/port/ultrix4/dl.h
new file mode 100644 (file)
index 0000000..e656714
--- /dev/null
@@ -0,0 +1,117 @@
+/*-------------------------------------------------------------------------
+ *
+ * dl.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ *  Ultrix 4.x Dynamic Loader Library Version 1.0
+ *
+ *  dl.h--
+ *      header file for the Dynamic Loader Library
+ *
+ *
+ *  Copyright (c) 1993 Andrew K. Yu, University of California at Berkeley
+ *  All rights reserved.
+ *
+ *  Permission to use, copy, modify, and distribute this software and its
+ *  documentation for educational, research, and non-profit purposes and
+ *  without fee is hereby granted, provided that the above copyright
+ *  notice appear in all copies and that both that copyright notice and
+ *  this permission notice appear in supporting documentation. Permission
+ *  to incorporate this software into commercial products can be obtained
+ *  from the author. The University of California and the author make
+ *  no representations about the suitability of this software for any 
+ *  purpose. It is provided "as is" without express or implied warranty. 
+ *
+ */
+#ifndef _DL_HEADER_
+#define _DL_HEADER_
+
+#include <filehdr.h>
+#include <syms.h>
+#include <ldfcn.h>
+#include <reloc.h>
+#include <scnhdr.h>
+
+
+typedef long CoreAddr;
+
+
+typedef struct ScnInfo {
+    CoreAddr   addr;           /* starting address of the section */
+    SCNHDR     hdr;            /* section header */
+    RELOC      *relocEntries;  /* relocation entries */
+} ScnInfo;
+
+typedef enum {
+    DL_NEEDRELOC,              /* still need relocation */
+    DL_RELOCATED,              /* no relocation necessary */
+    DL_INPROG                  /* relocation in progress */
+} dlRStatus;
+
+typedef struct JmpTbl {
+    char *block;               /* the jump table memory block */
+    struct JmpTbl *next;       /* next block */
+} JmpTbl;
+
+typedef struct dlFile {
+    char       *filename;      /* file name of the object file */
+
+    int                textSize;       /* used by mprotect */
+    CoreAddr   textAddress;    /* start addr of text section */
+    long       textVaddr;      /* vaddr of text section in obj file */
+    CoreAddr   rdataAddress;   /* start addr of rdata section */
+    long       rdataVaddr;     /* vaddr of text section in obj file */
+    CoreAddr   dataAddress;    /* start addr of data section */
+    long       dataVaddr;      /* vaddr of text section in obj file */
+    CoreAddr   bssAddress;     /* start addr of bss section */
+    long       bssVaddr;       /* vaddr of text section in obj file */
+
+    int                nsect;          /* number of sections */
+    ScnInfo    *sect;          /* details of each section (array) */
+
+    int                issExtMax;      /* size of string space */
+    char       *extss;         /* extern sym string space (in core) */
+    int                iextMax;        /* maximum number of Symbols */
+    pEXTR      extsyms;        /* extern syms */
+
+    dlRStatus  relocStatus;    /* what relocation needed? */
+    int        needReloc;
+
+    JmpTbl     *jmptable;      /* the jump table for R_JMPADDR */
+
+    struct dlFile *next;       /* next member of the archive */
+} dlFile;
+
+typedef struct dlSymbol {
+    char *name;                        /* name of the symbol */
+    long addr;                 /* address of the symbol */
+    dlFile *objFile;           /* from which file */
+} dlSymbol;
+
+/*
+ * prototypes for the dl* interface
+ */
+extern void *dl_open(/* char *filename, int mode */);
+extern void *dl_sym(/* void *handle, char *name */);
+extern void dl_close(/* void *handle */);
+extern char *dl_error(/* void */);
+
+#define   DL_LAZY      0       /* lazy resolution */
+#define   DL_NOW       1       /* immediate resolution */
+
+/*
+ * Miscellaneous utility routines:
+ */
+extern char **dl_undefinedSymbols(/* int *count */);
+extern void dl_printAllSymbols(/* void *handle */);
+extern void dl_setLibraries(/* char *libs */);
+
+#endif  _DL_HEADER_
diff --git a/src/backend/port/ultrix4/dynloader.c b/src/backend/port/ultrix4/dynloader.c
new file mode 100644 (file)
index 0000000..31c7178
--- /dev/null
@@ -0,0 +1,68 @@
+/*-------------------------------------------------------------------------
+ *
+ * dynloader.c--
+ *    This dynamic loader uses Andrew Yu's libdl-1.0 package for Ultrix 4.x.
+ *    (Note that pg_dlsym and pg_dlclose are actually macros defined in
+ *    "port-protos.h".)
+ *    
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include "dl.h"
+#include "c.h"
+#include "fmgr.h"
+#include "port-protos.h"
+#include "utils/elog.h"
+
+extern char pg_pathname[];
+
+void *
+pg_dlopen(char *filename)
+{
+    static int dl_initialized= 0;
+    void *handle;
+
+    /*
+     * initializes the dynamic loader with the executable's pathname.
+     * (only needs to do this the first time pg_dlopen is called.)
+     */
+    if (!dl_initialized) {
+        if (!dl_init(pg_pathname)) {
+           return NULL;
+       }
+       /*
+        * if there are undefined symbols, we want dl to search from the
+        * following libraries also.
+        */
+       dl_setLibraries("/usr/lib/libm_G0.a:/usr/lib/libc_G0.a");
+       dl_initialized= 1;
+    }
+
+    /*
+     * open the file. We do the symbol resolution right away so that we
+     * will know if there are undefined symbols. (This is in fact the
+     * same semantics as "ld -A". ie. you cannot have undefined symbols.
+     */
+    if ((handle=dl_open(filename, DL_NOW))==NULL) {
+       int count;
+       char **list= dl_undefinedSymbols(&count);
+
+       /* list the undefined symbols, if any */
+       if(count) {
+           elog(NOTICE, "dl: Undefined:");
+           while(*list) {
+               elog(NOTICE, "  %s", *list);
+               list++;
+           }
+       }
+    }
+
+    return (void *)handle;
+}
+
diff --git a/src/backend/port/ultrix4/machine.h b/src/backend/port/ultrix4/machine.h
new file mode 100644 (file)
index 0000000..ab761e4
--- /dev/null
@@ -0,0 +1,19 @@
+/*-------------------------------------------------------------------------
+ *
+ * machine.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MACHINE_H
+#define MACHINE_H
+
+#define BLCKSZ 8192
+
+#endif
+
diff --git a/src/backend/port/ultrix4/port-protos.h b/src/backend/port/ultrix4/port-protos.h
new file mode 100644 (file)
index 0000000..849af54
--- /dev/null
@@ -0,0 +1,36 @@
+/*-------------------------------------------------------------------------
+ *
+ * port-protos.h--
+ *    prototypes for Ultrix-specific routines
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PORT_PORTOS_H
+#define PORT_PORTOS_H
+
+#include "utils/dynamic_loader.h"
+#include "dl.h"
+
+/* dynloader.c */
+/*
+ * New dynamic loader.
+ *
+ * This dynamic loader uses Andrew Yu's libdl-1.0 package for Ultrix 4.x.
+ * (Note that pg_dlsym and pg_dlclose are actually macros defined in
+ * "port-protos.h".)
+ */ 
+
+#define pg_dlsym(h, f) ((func_ptr)dl_sym(h, f))
+#define pg_dlclose(h)  dl_close(h)
+#define        pg_dlerror()    dl_error()
+
+/* port.c */
+
+extern void init_address_fixup(void);
+
+#endif         /* PORT_PORTOS_H */
diff --git a/src/backend/port/ultrix4/port.c b/src/backend/port/ultrix4/port.c
new file mode 100644 (file)
index 0000000..1ce7b21
--- /dev/null
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * port.c--
+ *    Ultrix-specific routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <sys/syscall.h>
+#include <sys/sysmips.h>
+
+#include "c.h"
+
+void
+init_address_fixup()
+{
+#ifdef NOFIXADE
+    syscall(SYS_sysmips, MIPS_FIXADE, 0, NULL, NULL, NULL);
+#endif /* NOFIXADE */
+}
diff --git a/src/backend/port/ultrix4/strdup.c b/src/backend/port/ultrix4/strdup.c
new file mode 100644 (file)
index 0000000..6b2170a
--- /dev/null
@@ -0,0 +1,23 @@
+/*-------------------------------------------------------------------------
+ *
+ * strdup.c--
+ *    copies a null-terminated string.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+
+char *
+strdup(char *string)
+{
+    char *nstr;
+
+    nstr = strcpy((char *)palloc(strlen(string)+1), string);
+    return nstr;
+}
diff --git a/src/backend/port/win32/machine.h b/src/backend/port/win32/machine.h
new file mode 100644 (file)
index 0000000..43e4057
--- /dev/null
@@ -0,0 +1,2 @@
+#define BLCKSZ        8192
+#define NOFILE         100
diff --git a/src/backend/port/win32/nt.c b/src/backend/port/win32/nt.c
new file mode 100644 (file)
index 0000000..b594005
--- /dev/null
@@ -0,0 +1,625 @@
+#include <windows.h>
+#include <time.h>
+#include "postgres.h"
+#include "storage/ipc.h"
+
+/* The name of the Postgres 95 ipc file mapping object */
+#define IPC_NAME       "PG95_IPC"
+
+/* The name of the Postgres 95 ipc file mapping object semaphore */
+#define IPC_SEM_NAME   "PG95_IPC_SEM"
+
+/* The maximum length of a shared memory object name */
+#define IPC_MAX_SHMEM_NAME     32
+
+/* The maximum number of emulated Unix shared memory segments */
+#define IPC_NMAXSHM    10
+
+/* The Maximum number of elements in a semaphore set. Note that this
+** is just a guess.
+*/
+#define IPC_NMAXSEMGRP 7
+
+/* The various states of a semaphore */
+#define SIGNALED       1
+#define UNSIGNALED     0
+#define UNUSED         -1
+
+/* The security attribute structure necessary for handles to be inhereted */
+SECURITY_ATTRIBUTES sec_attrib = { sizeof (LPSECURITY_ATTRIBUTES),
+       NULL, TRUE};
+
+/*
+Postgres95 uses semaphores and shared memory. Both are provided by
+Unix and NT, although NT uses a different method for referencing
+them. Rather than changing the function calls used by Postgres95
+to use NT system services, we've written code to emulate the Unix
+system calls. We deliberately don't do a complete emulation of the
+Unix calls, partly because it doesn't appear possible, but also
+because only a few options of the Unix calls are actually used by
+Postgres95.
+
+The most noticable difference between the way Unix and NT use semaphores
+is that the central entity on Unix is a semaphore set consisting of
+potientially many actual semaphores whereas on NT a semaphore handle
+represents just one actual semaphore. Furthermore, a Unix semaphore set
+is identified by one semaphore id no matter how many elements there
+are in the set. Given a Unix semaphore id, the Unix API provides a way
+to index into the set to reference a specific semaphore.
+
+You might think that since both a semaphore id and a semaphore handle
+is just an integer there won't be any changes necessary to the Postgres95
+code to deal with NT semaphores. If it weren't for the existence of
+multi-semaphore semaphore sets this would be true.
+
+To handle semaphore sets a fixed-size table, whose size is partially
+based on the sum of the maximum number of semaphores times the maximum
+number of semaphores per semaphore set, is created and kept in shared
+memory that is visable to every backend started by the Postmaster.
+
+Each semaphore set entry consists of an arbitrary key value, which serves
+to identify the semaphore set, and IPC_NMAXSEMGRP array elements to
+store the NT semaphore handles representing the NT semaphore used for
+the semaphore set. Semaphore IDs are just indices into this table.
+In order to distinguish occupied entries in this table -1 is always
+considered an invalid semaphore ID.
+
+This table is also used to store information about shared memory
+segments. Fortunately, there is a one-to-one mapping between Unix
+shared memory IDs and NT shared memory handles so the code to emulate
+Unix shared memory is simple.
+*/
+
+/* We need one of these for each emulated semaphore set */
+struct Pg_sem
+{
+       key_t   Pg_sem_key;
+       HANDLE  Pg_sem_handle[IPC_NMAXSEMGRP];
+       int     Pg_sem_nsems;
+};
+
+/* We need one of these for each emulated shared memory segment */
+struct Pg_shm
+{
+       key_t   Pg_shm_key;
+       HANDLE  Pg_shm_handle;
+};
+
+/* This structure is what's stored in shared memory. Note that
+** since both the shared memory and semaphore data is in the same
+** table, and the table is protected by a single NT semaphore, there's
+** a chance that semaphore manipulation could be slowed down by
+** shared memory manipulation, and vice versa. But, since both are
+** allocated primarily when the Postmaster starts up, which isn't time
+** critical, I don't think this will prove to be a problem.
+*/
+
+static struct Pg_shared
+{
+       int Pg_next_sem;
+       int Pg_next_shm;
+       struct Pg_sem Pg_sem[IPC_NMAXSEM];
+       struct Pg_shm Pg_shm[IPC_NMAXSHM];
+} *Pg_shared_ptr;
+
+/* The semaphore that protects the shared memory table */
+HANDLE Pg_shared_hnd;
+
+/*
+** Perform a semaphore operation. We're passed a semaphore set id,
+** a pointer to an array of sembuf structures, and the number
+** of elements in the array. Each element in the sembuf structure
+** describes a specific semaphore within the semaphore set and the
+** operation to perform on it.
+*/
+
+int
+semop(int semid, struct sembuf *sops, u_int nsops)
+{
+       u_int i;
+       int result;
+       HANDLE hndl;
+
+       /* Go through all the sops structures */
+       for (i = 0; i < nsops; i++)
+       {
+               struct sembuf *sptr;
+               int semval;
+               int av_sem_op;
+
+               sptr = &sops[i];
+               /*
+               printf("performing %d in sem # %d\n", sptr->sem_op, sptr->sem_num);
+               */
+
+               /*
+               ** Postgres95 uses -255 to represent a lock request
+               ** and 255 to show a lock release. Changing these values
+               ** to -1 and 1 make it easier to keep track of the state
+               ** of the semaphore.
+               */
+               if (sptr->sem_op == -255)
+                       sptr->sem_op = -1;
+               else if (sptr->sem_op == 255)
+                       sptr->sem_op = 1;
+               else
+                       printf("invalid sem_op %d\n", sptr->sem_op);
+
+               _get_ipc_sem();
+               hndl = Pg_shared_ptr->Pg_sem[semid].Pg_sem_handle[sptr->sem_num];
+               _rel_ipc_sem();
+               semval = _get_sem_val(hndl);
+
+               if (sptr->sem_op == 0)
+               {
+                       if (semval == UNSIGNALED)
+                               return(semval);
+                       else
+                       {
+                               if (sptr->sem_flg & IPC_NOWAIT)
+                                       return(SIGNALED);
+                               else
+                                       result = WaitForSingleObject(hndl, 5000);
+                       }
+               }
+
+               av_sem_op = abs(sptr->sem_op);
+
+               /* If a lock is being attempted */
+               if (sptr->sem_op < 0)
+               {
+                       if (semval >= av_sem_op)
+                       {
+                               semval -= av_sem_op;
+                               if (semval <= UNSIGNALED)
+                                       result = WaitForSingleObject(hndl, 5000);
+                       }
+                       else
+                       {
+                               if (sptr->sem_flg & IPC_NOWAIT)
+                                       return(SIGNALED);
+                               else
+                                       result = WaitForSingleObject(hndl, 5000);
+                       }
+               }
+
+               /* If a lock is being released */
+               if (sptr->sem_op > 0)
+               {
+                       semval += av_sem_op;
+                       if (semval > 0)
+                               ReleaseSemaphore(hndl, 1, NULL);
+               }
+       }
+}
+
+int
+semget(key_t key, int nsems, int semflg)
+{
+       int id, new_sem, ret_val;
+
+       /* If nmsems is 0 then assume that we're just checking whether
+       ** the semaphore identified by key exists. Assume that
+       ** if key is IPC_PRIVATE that this should always fail.
+       */
+       if (nsems == 0)
+       {
+               if (key == IPC_PRIVATE)
+                       ret_val = -1;
+               else
+               {
+                       _get_ipc_sem();
+                       id = _get_sem_id(key);
+                       _rel_ipc_sem();
+                       ret_val = id;
+               }
+               return(ret_val);
+       }
+
+       /* See if there's already a semaphore with the key.
+       ** If not, record the key, allocate enough space for the
+       ** handles of the semaphores, and then create the semaphores.
+       */
+       _get_ipc_sem();
+       id = _get_sem_id(key);
+       if (id == UNUSED)
+       {
+               register int i;
+               struct Pg_sem *pg_ptr;
+
+               new_sem = Pg_shared_ptr->Pg_next_sem++;
+
+               pg_ptr = &(Pg_shared_ptr->Pg_sem[new_sem]);
+               pg_ptr->Pg_sem_key = key;
+               pg_ptr->Pg_sem_nsems = nsems;
+
+               for (i = 0; i < nsems; i++)
+                       pg_ptr->Pg_sem_handle[i] = CreateSemaphore(&sec_attrib, 1, 255, NULL);
+               ret_val = new_sem;
+       }
+       else
+               ret_val = id;
+       _rel_ipc_sem();
+       return(ret_val);
+}
+
+/* These next two functions could be written as one function, although
+** doing so would require some additional logic.
+*/
+
+/* Given a semaphore key, return the corresponding id.
+** This function assumes that the shared memory table is being
+** protected by the shared memory table semaphore.
+*/
+_get_sem_id(key_t key)
+{
+       register int i;
+
+       /* Go through the shared memory table looking for a semaphore
+       ** whose key matches what we're looking for
+       */
+       for (i = 0; i < Pg_shared_ptr->Pg_next_sem; i++)
+               if (Pg_shared_ptr->Pg_sem[i].Pg_sem_key == key)
+                       return(i);
+
+       /* Return UNUSED if we didn't find a match */
+       return(UNUSED);
+}
+
+/* Given a shared memory key, return the corresponding id
+** This function assumes that the shared memory table is being
+** protected by the shared memory table semaphore.
+*/
+_get_shm_id(key_t key)
+{
+       register int i;
+
+       /* Go through the shared memory table looking for a semaphore
+       ** whose key matches what we're looking for
+       */
+       for (i = 0; i < Pg_shared_ptr->Pg_next_shm; i++)
+               if (Pg_shared_ptr->Pg_shm[i].Pg_shm_key == key)
+                       return(i);
+
+       /* Return UNUSED if we didn't find a match */
+       return(UNUSED);
+}
+
+int
+semctl(int semid, int semnum, int cmd, void *y)
+{
+       int old_val;
+       HANDLE hndl;
+
+       switch (cmd)
+       {
+       case SETALL:
+       case SETVAL:
+               /* We can't change the value of a semaphore under
+               ** NT except by releasing it or waiting for it.
+               */
+               return(0);
+
+       case GETVAL:
+               _get_ipc_sem();
+               hndl = Pg_shared_ptr->Pg_sem[semid].Pg_sem_handle[semnum];
+               _rel_ipc_sem();
+               old_val = _get_sem_val(hndl);
+               return(old_val);
+       }
+}
+
+/* Get the current value of the semaphore whose handle is passed in hnd
+** This function does NOT assume that the shared memory table is being
+** protected by the shared memory table semaphore.
+*/
+
+int
+_get_sem_val(HANDLE hnd)
+{
+       DWORD waitresult;
+
+       /* Try to get the semaphore */
+       waitresult = WaitForSingleObject(hnd, 0L);
+
+       /* Check what the value of the semaphore was */
+       switch(waitresult)
+       {
+       /* The semaphore was signaled so we just got it.
+       ** Since we don't really want to keep it, since we just
+       ** wanted to test its value, go ahead and release it.
+       */
+       case WAIT_OBJECT_0:
+               ReleaseSemaphore(hnd, 1, NULL);
+               return(SIGNALED);
+
+       /* The semaphore was non-signaled meaning someone else had it. */
+       case WAIT_TIMEOUT:
+               return(UNSIGNALED);
+       }
+}
+
+int
+shmget(key_t key, uint32 size, int flags)
+{
+       HANDLE hnd;
+       char name[IPC_MAX_SHMEM_NAME];
+       int id;
+
+       /* Get the handle for the key, if any. */
+       _get_ipc_sem();
+       id = _get_shm_id(key);
+       _rel_ipc_sem();
+
+       /* If we're really going to create a new mapping */
+       if (flags != 0)
+       {
+               /* if the key is already being used return an error */
+               if (id != UNUSED)
+                       return(-1);
+
+               /* convert the key to a character string */
+               sprintf(name, "%d", key);
+       
+               hnd = CreateFileMapping((HANDLE)0xffffffff,
+                       &sec_attrib, PAGE_READWRITE,
+                       0, size, name);
+       
+               if (hnd == NULL)
+                       return(-1);
+               else
+               {
+                       int new_ipc;
+                       struct Pg_shm *pg_ptr;
+
+                       _get_ipc_sem();
+                       new_ipc = Pg_shared_ptr->Pg_next_shm++;
+
+                       pg_ptr = &(Pg_shared_ptr->Pg_shm[new_ipc]);
+                       pg_ptr->Pg_shm_key = key;
+                       pg_ptr->Pg_shm_handle = hnd;
+                       _rel_ipc_sem();
+                       return(new_ipc);
+               }
+       }
+
+       /* flags is 0 so we just want the id for the existing mapping */
+       else
+               return(id);
+}
+
+shmdt(char *shmaddr)
+{
+       UnmapViewOfFile(shmaddr);
+}
+
+int
+shmctl(IpcMemoryId shmid, int cmd, struct shmid_ds *buf)
+{
+       int x = 0;
+
+       if (cmd == IPC_RMID)
+       {
+               _get_ipc_sem();
+               CloseHandle(Pg_shared_ptr->Pg_shm[shmid].Pg_shm_handle);
+               _rel_ipc_sem();
+               return(0);
+       }
+       x = x / x;
+}
+
+/* Attach to the already created shared memory segment */
+LPVOID *
+shmat(int shmid, void *shmaddr, int shmflg)
+{
+       LPVOID *ret_addr;
+
+       _get_ipc_sem();
+       ret_addr = MapViewOfFile(Pg_shared_ptr->Pg_shm[shmid].Pg_shm_handle,
+               FILE_MAP_ALL_ACCESS, 0, 0, 0);
+       _rel_ipc_sem();
+       if (ret_addr == NULL)
+       {
+               int jon;
+
+               jon = GetLastError();
+       }
+       return(ret_addr);
+}
+
+/* This is the function that is called when the postmaster starts up.
+** It is here that the shared memory table is created. Also, create
+** the semaphore that will be used to protect the shared memory table.
+** TODO - do something with the return value.
+*/
+_nt_init()
+{
+       HANDLE hnd;
+       int size = sizeof (struct Pg_shared);
+
+       /* Create the file mapping for the shared memory to be
+       ** used to store the ipc table.
+       */
+       hnd = CreateFileMapping((HANDLE)0xffffffff,
+               &sec_attrib, PAGE_READWRITE,
+               0, size, IPC_NAME);
+
+       if (hnd == NULL)
+       {
+               size = GetLastError();
+               return(-1);
+       }
+
+       Pg_shared_hnd = CreateSemaphore(&sec_attrib, 1, 255, IPC_SEM_NAME);
+       if (Pg_shared_hnd == NULL)
+       {
+               size = GetLastError();
+               return(-1);
+       }
+}
+
+/* This function gets called by every backend at startup time. Its
+** main duty is to put the address of the shared memory table pointed
+** to by Pg_shared_ptr. There's no need to get the IPC_SEM_NAME semaphore
+** because this function is called before we start manipulating the
+** shared memory table.
+*/
+void
+_nt_attach()
+{
+       HANDLE hnd;
+
+       /* Get a handle to the shared memory table */
+       hnd = OpenFileMapping(FILE_MAP_ALL_ACCESS,
+               FALSE, IPC_NAME);
+
+       /* Map the ipc shared memory table into the address space
+       ** of this process at an address returned by MapViewOfFile
+       */
+       Pg_shared_ptr = (struct Pg_shared *) MapViewOfFile(hnd,
+               FILE_MAP_ALL_ACCESS, 0, 0, 0);
+
+       if (Pg_shared_ptr == NULL)
+       {
+               hnd = GetLastError();
+               return(-1);
+       }
+}
+
+_get_ipc_sem()
+{
+       WaitForSingleObject(Pg_shared_hnd, 5000);
+}
+
+_rel_ipc_sem()
+{
+       ReleaseSemaphore(Pg_shared_hnd, 1, NULL);
+}
+
+pg_dlerror(void)
+{
+       int x = 0;
+       x = x / x;
+}
+
+pg_dlclose(void *handle)
+{
+       FreeLibrary(handle);
+}
+
+void *
+pg_dlopen(char *filename)
+{
+       HINSTANCE hinstlib;
+
+       hinstlib = LoadLibrary(filename);
+       return (hinstlib);
+}
+
+void *
+pg_dlsym(void *handle, char *funcname)
+{
+       void *proc;
+
+       proc = GetProcAddress(handle, funcname);
+       return (proc);
+}
+
+void
+ftruncate(int fd, int offset)
+{
+       HANDLE hnd;
+
+       _lseek(fd, offset, SEEK_SET);
+       hnd = _get_osfhandle(fd);
+       SetEndOfFile(hnd);
+}
+
+/* The rest are just stubs that are intended to serve as place holders
+** in case we want to set breakpoints to see what's happening when
+** these routines are called. They'll eventually have to be filled
+** in but they're not necessary to get Postgres95 going.
+*/
+setuid(int i)
+{
+       int x = 1;
+       x = x / x;
+}
+
+setsid()
+{
+       int x = 1;
+       x = x / x;
+}
+
+vfork(void)
+{
+       int x = 0;
+       x = x / x;
+}
+
+ttyname(int y)
+{
+       int x = 0;
+       x = x / x;
+}
+
+step(char *string, char *expbuf)
+{
+       int x = 0;
+       x = x / x;
+}
+
+siglongjmp(int env, int value)
+{
+       int x = 0;
+       x = x / x;
+}
+
+pause(void)
+{
+       int x = 0;
+       x = x / x;
+}
+
+kill(int process, int signal)
+{
+       int x = 1;
+       x = x / x;
+}
+
+getuid(void)
+{
+       int x = 1;
+       x = x / x;
+}
+
+geteuid( void )
+{
+       int x = 1;
+       x = x / x;
+}
+
+int
+fsync(int filedes)
+{
+}
+
+fork(void)
+{
+       int x = 0;
+       x = x / x;
+}
+
+char *
+compile(char *instring,char *expbuf,char *endbuf,int eof)
+{
+       int x = 0;
+       x = x / x;
+}
+
+beginRecipe(char *s)
+{
+       int x = 0;
+       x = x / x;
+}
diff --git a/src/backend/port/win32/nt.h b/src/backend/port/win32/nt.h
new file mode 100644 (file)
index 0000000..abe3519
--- /dev/null
@@ -0,0 +1,54 @@
+typedef char * caddr_t;
+typedef unsigned long u_long;
+typedef unsigned int u_int;
+typedef unsigned short u_short;
+typedef unsigned char u_char;
+typedef unsigned int mode_t;
+
+typedef u_int uid_t;
+typedef u_int gid_t;
+typedef int key_t;
+#define IPC_PRIVATE ((key_t)0)
+
+/* Common IPC operation flag definitions. We'll use
+** the Unix values unless we find a reason not to.
+*/
+#define IPC_CREAT       0001000         /* create entry if key doesn't exist */
+#define IPC_EXCL        0002000         /* fail if key exists */
+#define IPC_NOWAIT      0004000         /* error if request must wait */
+
+
+struct sembuf
+{
+       u_short sem_num;
+       short   sem_op;
+       short   sem_flg;
+};
+
+#define USE_POSIX_TIME
+#define NEED_RINT
+
+#define MAXHOSTNAMELEN 12      /* where is the official definition of this? */
+#define MAXPATHLEN _MAX_PATH   /* in winsock.h */
+#define POSTPORT "5432"
+
+/* NT has stricmp not strcasecmp. Which is ANSI? */
+#define strcasecmp(a,b)        _stricmp(a,b)
+
+#define isascii(a)     __isascii(a)
+
+#define random()       rand()
+
+/* These are bogus values used so that we can compile ipc.c */
+#define SETALL 2
+#define SETVAL 3
+#define IPC_RMID 4
+#define GETNCNT 5
+#define GETVAL 6
+
+/* for float.c */
+#define NEED_CBRT
+#define NEED_ISINF
+
+#define POSTGRESDIR "d:\\pglite"
+#define PGDATADIR "d:\\pglite\\data"
diff --git a/src/backend/port/win32/pglite.mak b/src/backend/port/win32/pglite.mak
new file mode 100644 (file)
index 0000000..c922191
--- /dev/null
@@ -0,0 +1,3323 @@
+# Microsoft Visual C++ Generated NMAKE File, Format Version 2.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+!IF "$(CFG)" == ""
+CFG=Win32 Debug
+!MESSAGE No configuration specified.  Defaulting to Win32 Debug.
+!ENDIF 
+
+!IF "$(CFG)" != "Win32 Release" && "$(CFG)" != "Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line.  For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "pglite.mak" CFG="Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+!ERROR An invalid configuration is specified.
+!ENDIF 
+
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "Win32 Debug"
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "WinRel"
+# PROP BASE Intermediate_Dir "WinRel"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "g:\users\forrest\pglite"
+# PROP Intermediate_Dir "g:\users\forrest\pglite"
+OUTDIR=g:\users\forrest\pglite
+INTDIR=g:\users\forrest\pglite
+
+ALL : $(OUTDIR)/pglite.exe $(OUTDIR)/pglite.bsc
+
+$(OUTDIR) : 
+    if not exist $(OUTDIR)/nul mkdir $(OUTDIR)
+
+# ADD BASE CPP /nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /FR /c
+# ADD CPP /nologo /W3 /GX /YX /Od /I "g:\pglite\src\backend\include" /I "g:\pglite\src\backend" /I "g:\pglite\src\backend\port\win32" /I "g:\pglite\src\backend\obj" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "__STDC__" /D "_POSIX_" /c
+# SUBTRACT CPP /Fr
+CPP_PROJ=/nologo /W3 /GX /YX /Od /I "g:\pglite\src\backend\include" /I\
+ "g:\pglite\src\backend" /I "g:\pglite\src\backend\port\win32" /I\
+ "g:\pglite\src\backend\obj" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "__STDC__"\
+ /Fp$(OUTDIR)/"pglite.pch" /Fo$(INTDIR)/ /D "_POSIX_"  /c 
+CPP_OBJS=g:\users\forrest\pglite/
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o$(OUTDIR)/"pglite.bsc" 
+BSC32_SBRS= \
+       
+
+$(OUTDIR)/pglite.bsc : $(OUTDIR)  $(BSC32_SBRS)
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NOLOGO /SUBSYSTEM:console /MACHINE:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NOLOGO /SUBSYSTEM:console /MACHINE:I386
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib /NOLOGO /SUBSYSTEM:console /INCREMENTAL:no\
+ /PDB:$(OUTDIR)/"pglite.pdb" /MACHINE:I386 /OUT:$(OUTDIR)/"pglite.exe"
+DEF_FILE=
+LINK32_OBJS= \
+       $(INTDIR)/scankey.obj \
+       $(INTDIR)/printtup.obj \
+       $(INTDIR)/indexvalid.obj \
+       $(INTDIR)/heaptuple.obj \
+       $(INTDIR)/tupdesc.obj \
+       $(INTDIR)/indextuple.obj \
+       $(INTDIR)/heapvalid.obj \
+       $(INTDIR)/hashinsert.obj \
+       $(INTDIR)/hashstrat.obj \
+       $(INTDIR)/hashutil.obj \
+       $(INTDIR)/hashpage.obj \
+       $(INTDIR)/hashsearch.obj \
+       $(INTDIR)/hashscan.obj \
+       $(INTDIR)/hashfunc.obj \
+       $(INTDIR)/hash.obj \
+       $(INTDIR)/hashovfl.obj \
+       $(INTDIR)/bootstrap.obj \
+       $(INTDIR)/genam.obj \
+       $(INTDIR)/creatinh.obj \
+       $(INTDIR)/nodeSeqscan.obj \
+       $(INTDIR)/nodeUnique.obj \
+       $(INTDIR)/rename.obj \
+       $(INTDIR)/transsup.obj \
+       $(INTDIR)/transam.obj \
+       $(INTDIR)/define.obj \
+       $(INTDIR)/execMain.obj \
+       $(INTDIR)/xid.obj \
+       $(INTDIR)/nodeAgg.obj \
+       $(INTDIR)/nbtpage.obj \
+       $(INTDIR)/execScan.obj \
+       $(INTDIR)/nbtree.obj \
+       $(INTDIR)/rtscan.obj \
+       $(INTDIR)/indexam.obj \
+       $(INTDIR)/execQual.obj \
+       $(INTDIR)/nodeHash.obj \
+       $(INTDIR)/nbtscan.obj \
+       $(INTDIR)/hio.obj \
+       $(INTDIR)/pg_proc.obj \
+       $(INTDIR)/stats.obj \
+       $(INTDIR)/nodeMaterial.obj \
+       $(INTDIR)/varsup.obj \
+       $(INTDIR)/copy.obj \
+       $(INTDIR)/rtproc.obj \
+       $(INTDIR)/functions.obj \
+       $(INTDIR)/nodeHashjoin.obj \
+       $(INTDIR)/catalog.obj \
+       $(INTDIR)/nbtinsert.obj \
+       $(INTDIR)/rtree.obj \
+       $(INTDIR)/version.obj \
+       $(INTDIR)/async.obj \
+       $(INTDIR)/nbtutils.obj \
+       $(INTDIR)/vacuum.obj \
+       $(INTDIR)/rtstrat.obj \
+       $(INTDIR)/execFlatten.obj \
+       $(INTDIR)/nodeTee.obj \
+       $(INTDIR)/nodeIndexscan.obj \
+       $(INTDIR)/remove.obj \
+       $(INTDIR)/indexing.obj \
+       $(INTDIR)/command.obj \
+       $(INTDIR)/nbtsearch.obj \
+       $(INTDIR)/heapam.obj \
+       $(INTDIR)/nodeSort.obj \
+       $(INTDIR)/execProcnode.obj \
+       $(INTDIR)/nodeResult.obj \
+       $(INTDIR)/index.obj \
+       $(INTDIR)/xact.obj \
+       $(INTDIR)/nodeMergejoin.obj \
+       $(INTDIR)/pg_operator.obj \
+       $(INTDIR)/execJunk.obj \
+       $(INTDIR)/pg_aggregate.obj \
+       $(INTDIR)/istrat.obj \
+       $(INTDIR)/execUtils.obj \
+       $(INTDIR)/purge.obj \
+       $(INTDIR)/heap.obj \
+       $(INTDIR)/nbtstrat.obj \
+       $(INTDIR)/execAmi.obj \
+       $(INTDIR)/execTuples.obj \
+       $(INTDIR)/pg_type.obj \
+       $(INTDIR)/view.obj \
+       $(INTDIR)/nodeAppend.obj \
+       $(INTDIR)/defind.obj \
+       $(INTDIR)/nodeNestloop.obj \
+       $(INTDIR)/nbtcompare.obj \
+       $(INTDIR)/rtget.obj \
+       $(INTDIR)/catalog_utils.obj \
+       $(INTDIR)/setrefs.obj \
+       $(INTDIR)/mergeutils.obj \
+       $(INTDIR)/oset.obj \
+       $(INTDIR)/arrayutils.obj \
+       $(INTDIR)/nodeFuncs.obj \
+       $(INTDIR)/rewriteSupport.obj \
+       $(INTDIR)/bufpage.obj \
+       $(INTDIR)/fd.obj \
+       $(INTDIR)/clauseinfo.obj \
+       $(INTDIR)/nabstime.obj \
+       $(INTDIR)/mcxt.obj \
+       $(INTDIR)/ipci.obj \
+       $(INTDIR)/qsort.obj \
+       $(INTDIR)/outfuncs.obj \
+       $(INTDIR)/tqual.obj \
+       $(INTDIR)/keys.obj \
+       $(INTDIR)/clauses.obj \
+       $(INTDIR)/print.obj \
+       $(INTDIR)/postinit.obj \
+       $(INTDIR)/oidchar16.obj \
+       $(INTDIR)/name.obj \
+       $(INTDIR)/tid.obj \
+       $(INTDIR)/"be-fsstubs.obj" \
+       $(INTDIR)/elog.obj \
+       $(INTDIR)/bufmgr.obj \
+       $(INTDIR)/portalbuf.obj \
+       $(INTDIR)/psort.obj \
+       $(INTDIR)/syscache.obj \
+       $(INTDIR)/exc.obj \
+       $(INTDIR)/selfuncs.obj \
+       $(INTDIR)/var.obj \
+       $(INTDIR)/oid.obj \
+       $(INTDIR)/"be-pqexec.obj" \
+       $(INTDIR)/ordering.obj \
+       $(INTDIR)/inv_api.obj \
+       $(INTDIR)/buf_table.obj \
+       $(INTDIR)/acl.obj \
+       $(INTDIR)/costsize.obj \
+       $(INTDIR)/catcache.obj \
+       $(INTDIR)/rewriteRemove.obj \
+       $(INTDIR)/parse_query.obj \
+       $(INTDIR)/excabort.obj \
+       $(INTDIR)/lmgr.obj \
+       $(INTDIR)/excid.obj \
+       $(INTDIR)/int.obj \
+       $(INTDIR)/auth.obj \
+       $(INTDIR)/regexp.obj \
+       $(INTDIR)/proc.obj \
+       $(INTDIR)/dbcommands.obj \
+       $(INTDIR)/dynahash.obj \
+       $(INTDIR)/shmem.obj \
+       $(INTDIR)/relnode.obj \
+       $(INTDIR)/fstack.obj \
+       $(INTDIR)/smgr.obj \
+       $(INTDIR)/magic.obj \
+       $(INTDIR)/relcache.obj \
+       $(INTDIR)/varlena.obj \
+       $(INTDIR)/allpaths.obj \
+       $(INTDIR)/portalmem.obj \
+       $(INTDIR)/bit.obj \
+       $(INTDIR)/readfuncs.obj \
+       $(INTDIR)/nodes.obj \
+       $(INTDIR)/chunk.obj \
+       $(INTDIR)/datum.obj \
+       $(INTDIR)/analyze.obj \
+       $(INTDIR)/oidint4.obj \
+       $(INTDIR)/hasht.obj \
+       $(INTDIR)/numutils.obj \
+       $(INTDIR)/pqcomm.obj \
+       $(INTDIR)/indxpath.obj \
+       $(INTDIR)/lispsort.obj \
+       $(INTDIR)/arrayfuncs.obj \
+       $(INTDIR)/copyfuncs.obj \
+       $(INTDIR)/planmain.obj \
+       $(INTDIR)/makefuncs.obj \
+       $(INTDIR)/lsyscache.obj \
+       $(INTDIR)/multi.obj \
+       $(INTDIR)/freelist.obj \
+       $(INTDIR)/aclchk.obj \
+       $(INTDIR)/initsplan.obj \
+       $(INTDIR)/prune.obj \
+       $(INTDIR)/sinvaladt.obj \
+       $(INTDIR)/orindxpath.obj \
+       $(INTDIR)/joinrels.obj \
+       $(INTDIR)/rewriteManip.obj \
+       $(INTDIR)/itemptr.obj \
+       $(INTDIR)/s_lock.obj \
+       $(INTDIR)/miscinit.obj \
+       $(INTDIR)/postgres.obj \
+       $(INTDIR)/parser.obj \
+       $(INTDIR)/tlist.obj \
+       $(INTDIR)/dt.obj \
+       $(INTDIR)/sinval.obj \
+       $(INTDIR)/pqpacket.obj \
+       $(INTDIR)/assert.obj \
+       $(INTDIR)/utility.obj \
+       $(INTDIR)/bool.obj \
+       $(INTDIR)/md.obj \
+       $(INTDIR)/pqsignal.obj \
+       $(INTDIR)/globals.obj \
+       $(INTDIR)/postmaster.obj \
+       $(INTDIR)/joinpath.obj \
+       $(INTDIR)/fastpath.obj \
+       $(INTDIR)/archive.obj \
+       $(INTDIR)/fcache.obj \
+       $(INTDIR)/mm.obj \
+       $(INTDIR)/createplan.obj \
+       $(INTDIR)/read.obj \
+       $(INTDIR)/stringinfo.obj \
+       $(INTDIR)/hashfn.obj \
+       $(INTDIR)/regproc.obj \
+       $(INTDIR)/main.obj \
+       $(INTDIR)/enbl.obj \
+       $(INTDIR)/prepunion.obj \
+       $(INTDIR)/prepqual.obj \
+       $(INTDIR)/planner.obj \
+       $(INTDIR)/clausesel.obj \
+       $(INTDIR)/portal.obj \
+       $(INTDIR)/spin.obj \
+       $(INTDIR)/lock.obj \
+       $(INTDIR)/single.obj \
+       $(INTDIR)/io.obj \
+       $(INTDIR)/"geo-ops.obj" \
+       $(INTDIR)/dest.obj \
+       $(INTDIR)/rewriteDefine.obj \
+       $(INTDIR)/keywords.obj \
+       $(INTDIR)/hashutils.obj \
+       $(INTDIR)/format.obj \
+       $(INTDIR)/scanner.obj \
+       $(INTDIR)/aset.obj \
+       $(INTDIR)/"geo-selfuncs.obj" \
+       $(INTDIR)/float.obj \
+       $(INTDIR)/pquery.obj \
+       $(INTDIR)/"be-dumpdata.obj" \
+       $(INTDIR)/filename.obj \
+       $(INTDIR)/misc.obj \
+       $(INTDIR)/pathnode.obj \
+       $(INTDIR)/inval.obj \
+       $(INTDIR)/smgrtype.obj \
+       $(INTDIR)/joininfo.obj \
+       $(INTDIR)/lselect.obj \
+       $(INTDIR)/rel.obj \
+       $(INTDIR)/internal.obj \
+       $(INTDIR)/preptlist.obj \
+       $(INTDIR)/joinutils.obj \
+       $(INTDIR)/shmqueue.obj \
+       $(INTDIR)/date.obj \
+       $(INTDIR)/locks.obj \
+       $(INTDIR)/not_in.obj \
+       $(INTDIR)/char.obj \
+       $(INTDIR)/rewriteHandler.obj \
+       $(INTDIR)/sets.obj \
+       $(INTDIR)/palloc.obj \
+       $(INTDIR)/indexnode.obj \
+       $(INTDIR)/equalfuncs.obj \
+       $(INTDIR)/oidint2.obj \
+       $(INTDIR)/list.obj \
+       $(INTDIR)/plancat.obj \
+       $(INTDIR)/fmgr.obj \
+       $(INTDIR)/fmgrtab.obj \
+       $(INTDIR)/dllist.obj \
+       $(INTDIR)/nodeGroup.obj \
+       $(INTDIR)/localbuf.obj \
+       $(INTDIR)/cluster.obj \
+       $(INTDIR)/ipc.obj \
+       $(INTDIR)/nt.obj \
+       $(INTDIR)/getopt.obj \
+       $(INTDIR)/bootscanner.obj \
+       $(INTDIR)/scan.obj \
+       $(INTDIR)/bootparse.obj \
+       $(INTDIR)/gram.obj \
+       $(INTDIR)/findbe.obj \
+       $(INTDIR)/regerror.obj \
+       $(INTDIR)/regfree.obj \
+       $(INTDIR)/regcomp.obj \
+       $(INTDIR)/regexec.obj \
+       $(INTDIR)/nbtsort.obj \
+       $(INTDIR)/buf_init.obj \
+       $(INTDIR)/dfmgr.obj
+
+$(OUTDIR)/pglite.exe : $(OUTDIR)  $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF  "$(CFG)" == "Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "WinDebug"
+# PROP BASE Intermediate_Dir "WinDebug"
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "d:\local\forrest\pglite"
+# PROP Intermediate_Dir "d:\local\forrest\pglite"
+OUTDIR=d:\local\forrest\pglite
+INTDIR=d:\local\forrest\pglite
+
+ALL : $(OUTDIR)/pglite.exe $(OUTDIR)/pglite.bsc
+
+$(OUTDIR) : 
+    if not exist $(OUTDIR)/nul mkdir $(OUTDIR)
+
+# ADD BASE CPP /nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /FR /c
+# ADD CPP /nologo /G5 /W3 /GX /Zi /YX /Od /I "g:\pglite\src\backend" /I "g:\pglite\src\backend\port\win32" /I "g:\pglite\src\backend\obj" /I "g:\pglite\src\backend\include" /I "g:\pglite\src\backend\port/win32/regex" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "__STDC__" /D "_POSIX_" /D "_NTSDK" /D "NO_SECURITY" /D "NEED_RUSAGE" /FR /c
+CPP_PROJ=/nologo /G5 /W3 /GX /Zi /YX /Od /I "g:\pglite\src\backend" /I\
+ "g:\pglite\src\backend\port\win32" /I "g:\pglite\src\backend\obj" /I\
+ "g:\pglite\src\backend\include" /I "g:\pglite\src\backend\port/win32/regex" /D\
+ "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "__STDC__" /D "_POSIX_" /D "_NTSDK" /D\
+ "NO_SECURITY" /D "NEED_RUSAGE" /FR$(INTDIR)/ /Fp$(OUTDIR)/"pglite.pch"\
+ /Fo$(INTDIR)/ /Fd$(OUTDIR)/"pglite.pdb" /c 
+CPP_OBJS=d:\local\forrest\pglite/
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o$(OUTDIR)/"pglite.bsc" 
+BSC32_SBRS= \
+       $(INTDIR)/scankey.sbr \
+       $(INTDIR)/printtup.sbr \
+       $(INTDIR)/indexvalid.sbr \
+       $(INTDIR)/heaptuple.sbr \
+       $(INTDIR)/tupdesc.sbr \
+       $(INTDIR)/indextuple.sbr \
+       $(INTDIR)/heapvalid.sbr \
+       $(INTDIR)/hashinsert.sbr \
+       $(INTDIR)/hashstrat.sbr \
+       $(INTDIR)/hashutil.sbr \
+       $(INTDIR)/hashpage.sbr \
+       $(INTDIR)/hashsearch.sbr \
+       $(INTDIR)/hashscan.sbr \
+       $(INTDIR)/hashfunc.sbr \
+       $(INTDIR)/hash.sbr \
+       $(INTDIR)/hashovfl.sbr \
+       $(INTDIR)/bootstrap.sbr \
+       $(INTDIR)/genam.sbr \
+       $(INTDIR)/creatinh.sbr \
+       $(INTDIR)/nodeSeqscan.sbr \
+       $(INTDIR)/nodeUnique.sbr \
+       $(INTDIR)/rename.sbr \
+       $(INTDIR)/transsup.sbr \
+       $(INTDIR)/transam.sbr \
+       $(INTDIR)/define.sbr \
+       $(INTDIR)/execMain.sbr \
+       $(INTDIR)/xid.sbr \
+       $(INTDIR)/nodeAgg.sbr \
+       $(INTDIR)/nbtpage.sbr \
+       $(INTDIR)/execScan.sbr \
+       $(INTDIR)/nbtree.sbr \
+       $(INTDIR)/rtscan.sbr \
+       $(INTDIR)/indexam.sbr \
+       $(INTDIR)/execQual.sbr \
+       $(INTDIR)/nodeHash.sbr \
+       $(INTDIR)/nbtscan.sbr \
+       $(INTDIR)/hio.sbr \
+       $(INTDIR)/pg_proc.sbr \
+       $(INTDIR)/stats.sbr \
+       $(INTDIR)/nodeMaterial.sbr \
+       $(INTDIR)/varsup.sbr \
+       $(INTDIR)/copy.sbr \
+       $(INTDIR)/rtproc.sbr \
+       $(INTDIR)/functions.sbr \
+       $(INTDIR)/nodeHashjoin.sbr \
+       $(INTDIR)/catalog.sbr \
+       $(INTDIR)/nbtinsert.sbr \
+       $(INTDIR)/rtree.sbr \
+       $(INTDIR)/version.sbr \
+       $(INTDIR)/async.sbr \
+       $(INTDIR)/nbtutils.sbr \
+       $(INTDIR)/vacuum.sbr \
+       $(INTDIR)/rtstrat.sbr \
+       $(INTDIR)/execFlatten.sbr \
+       $(INTDIR)/nodeTee.sbr \
+       $(INTDIR)/nodeIndexscan.sbr \
+       $(INTDIR)/remove.sbr \
+       $(INTDIR)/indexing.sbr \
+       $(INTDIR)/command.sbr \
+       $(INTDIR)/nbtsearch.sbr \
+       $(INTDIR)/heapam.sbr \
+       $(INTDIR)/nodeSort.sbr \
+       $(INTDIR)/execProcnode.sbr \
+       $(INTDIR)/nodeResult.sbr \
+       $(INTDIR)/index.sbr \
+       $(INTDIR)/xact.sbr \
+       $(INTDIR)/nodeMergejoin.sbr \
+       $(INTDIR)/pg_operator.sbr \
+       $(INTDIR)/execJunk.sbr \
+       $(INTDIR)/pg_aggregate.sbr \
+       $(INTDIR)/istrat.sbr \
+       $(INTDIR)/execUtils.sbr \
+       $(INTDIR)/purge.sbr \
+       $(INTDIR)/heap.sbr \
+       $(INTDIR)/nbtstrat.sbr \
+       $(INTDIR)/execAmi.sbr \
+       $(INTDIR)/execTuples.sbr \
+       $(INTDIR)/pg_type.sbr \
+       $(INTDIR)/view.sbr \
+       $(INTDIR)/nodeAppend.sbr \
+       $(INTDIR)/defind.sbr \
+       $(INTDIR)/nodeNestloop.sbr \
+       $(INTDIR)/nbtcompare.sbr \
+       $(INTDIR)/rtget.sbr \
+       $(INTDIR)/catalog_utils.sbr \
+       $(INTDIR)/setrefs.sbr \
+       $(INTDIR)/mergeutils.sbr \
+       $(INTDIR)/oset.sbr \
+       $(INTDIR)/arrayutils.sbr \
+       $(INTDIR)/nodeFuncs.sbr \
+       $(INTDIR)/rewriteSupport.sbr \
+       $(INTDIR)/bufpage.sbr \
+       $(INTDIR)/fd.sbr \
+       $(INTDIR)/clauseinfo.sbr \
+       $(INTDIR)/nabstime.sbr \
+       $(INTDIR)/mcxt.sbr \
+       $(INTDIR)/ipci.sbr \
+       $(INTDIR)/qsort.sbr \
+       $(INTDIR)/outfuncs.sbr \
+       $(INTDIR)/tqual.sbr \
+       $(INTDIR)/keys.sbr \
+       $(INTDIR)/clauses.sbr \
+       $(INTDIR)/print.sbr \
+       $(INTDIR)/postinit.sbr \
+       $(INTDIR)/oidchar16.sbr \
+       $(INTDIR)/name.sbr \
+       $(INTDIR)/tid.sbr \
+       $(INTDIR)/"be-fsstubs.sbr" \
+       $(INTDIR)/elog.sbr \
+       $(INTDIR)/bufmgr.sbr \
+       $(INTDIR)/portalbuf.sbr \
+       $(INTDIR)/psort.sbr \
+       $(INTDIR)/syscache.sbr \
+       $(INTDIR)/exc.sbr \
+       $(INTDIR)/selfuncs.sbr \
+       $(INTDIR)/var.sbr \
+       $(INTDIR)/oid.sbr \
+       $(INTDIR)/"be-pqexec.sbr" \
+       $(INTDIR)/ordering.sbr \
+       $(INTDIR)/inv_api.sbr \
+       $(INTDIR)/buf_table.sbr \
+       $(INTDIR)/acl.sbr \
+       $(INTDIR)/costsize.sbr \
+       $(INTDIR)/catcache.sbr \
+       $(INTDIR)/rewriteRemove.sbr \
+       $(INTDIR)/parse_query.sbr \
+       $(INTDIR)/excabort.sbr \
+       $(INTDIR)/lmgr.sbr \
+       $(INTDIR)/excid.sbr \
+       $(INTDIR)/int.sbr \
+       $(INTDIR)/auth.sbr \
+       $(INTDIR)/regexp.sbr \
+       $(INTDIR)/proc.sbr \
+       $(INTDIR)/dbcommands.sbr \
+       $(INTDIR)/dynahash.sbr \
+       $(INTDIR)/shmem.sbr \
+       $(INTDIR)/relnode.sbr \
+       $(INTDIR)/fstack.sbr \
+       $(INTDIR)/smgr.sbr \
+       $(INTDIR)/magic.sbr \
+       $(INTDIR)/relcache.sbr \
+       $(INTDIR)/varlena.sbr \
+       $(INTDIR)/allpaths.sbr \
+       $(INTDIR)/portalmem.sbr \
+       $(INTDIR)/bit.sbr \
+       $(INTDIR)/readfuncs.sbr \
+       $(INTDIR)/nodes.sbr \
+       $(INTDIR)/chunk.sbr \
+       $(INTDIR)/datum.sbr \
+       $(INTDIR)/analyze.sbr \
+       $(INTDIR)/oidint4.sbr \
+       $(INTDIR)/hasht.sbr \
+       $(INTDIR)/numutils.sbr \
+       $(INTDIR)/pqcomm.sbr \
+       $(INTDIR)/indxpath.sbr \
+       $(INTDIR)/lispsort.sbr \
+       $(INTDIR)/arrayfuncs.sbr \
+       $(INTDIR)/copyfuncs.sbr \
+       $(INTDIR)/planmain.sbr \
+       $(INTDIR)/makefuncs.sbr \
+       $(INTDIR)/lsyscache.sbr \
+       $(INTDIR)/multi.sbr \
+       $(INTDIR)/freelist.sbr \
+       $(INTDIR)/aclchk.sbr \
+       $(INTDIR)/initsplan.sbr \
+       $(INTDIR)/prune.sbr \
+       $(INTDIR)/sinvaladt.sbr \
+       $(INTDIR)/orindxpath.sbr \
+       $(INTDIR)/joinrels.sbr \
+       $(INTDIR)/rewriteManip.sbr \
+       $(INTDIR)/itemptr.sbr \
+       $(INTDIR)/s_lock.sbr \
+       $(INTDIR)/miscinit.sbr \
+       $(INTDIR)/postgres.sbr \
+       $(INTDIR)/parser.sbr \
+       $(INTDIR)/tlist.sbr \
+       $(INTDIR)/dt.sbr \
+       $(INTDIR)/sinval.sbr \
+       $(INTDIR)/pqpacket.sbr \
+       $(INTDIR)/assert.sbr \
+       $(INTDIR)/utility.sbr \
+       $(INTDIR)/bool.sbr \
+       $(INTDIR)/md.sbr \
+       $(INTDIR)/pqsignal.sbr \
+       $(INTDIR)/globals.sbr \
+       $(INTDIR)/postmaster.sbr \
+       $(INTDIR)/joinpath.sbr \
+       $(INTDIR)/fastpath.sbr \
+       $(INTDIR)/archive.sbr \
+       $(INTDIR)/fcache.sbr \
+       $(INTDIR)/mm.sbr \
+       $(INTDIR)/createplan.sbr \
+       $(INTDIR)/read.sbr \
+       $(INTDIR)/stringinfo.sbr \
+       $(INTDIR)/hashfn.sbr \
+       $(INTDIR)/regproc.sbr \
+       $(INTDIR)/main.sbr \
+       $(INTDIR)/enbl.sbr \
+       $(INTDIR)/prepunion.sbr \
+       $(INTDIR)/prepqual.sbr \
+       $(INTDIR)/planner.sbr \
+       $(INTDIR)/clausesel.sbr \
+       $(INTDIR)/portal.sbr \
+       $(INTDIR)/spin.sbr \
+       $(INTDIR)/lock.sbr \
+       $(INTDIR)/single.sbr \
+       $(INTDIR)/io.sbr \
+       $(INTDIR)/"geo-ops.sbr" \
+       $(INTDIR)/dest.sbr \
+       $(INTDIR)/rewriteDefine.sbr \
+       $(INTDIR)/keywords.sbr \
+       $(INTDIR)/hashutils.sbr \
+       $(INTDIR)/format.sbr \
+       $(INTDIR)/scanner.sbr \
+       $(INTDIR)/aset.sbr \
+       $(INTDIR)/"geo-selfuncs.sbr" \
+       $(INTDIR)/float.sbr \
+       $(INTDIR)/pquery.sbr \
+       $(INTDIR)/"be-dumpdata.sbr" \
+       $(INTDIR)/filename.sbr \
+       $(INTDIR)/misc.sbr \
+       $(INTDIR)/pathnode.sbr \
+       $(INTDIR)/inval.sbr \
+       $(INTDIR)/smgrtype.sbr \
+       $(INTDIR)/joininfo.sbr \
+       $(INTDIR)/lselect.sbr \
+       $(INTDIR)/rel.sbr \
+       $(INTDIR)/internal.sbr \
+       $(INTDIR)/preptlist.sbr \
+       $(INTDIR)/joinutils.sbr \
+       $(INTDIR)/shmqueue.sbr \
+       $(INTDIR)/date.sbr \
+       $(INTDIR)/locks.sbr \
+       $(INTDIR)/not_in.sbr \
+       $(INTDIR)/char.sbr \
+       $(INTDIR)/rewriteHandler.sbr \
+       $(INTDIR)/sets.sbr \
+       $(INTDIR)/palloc.sbr \
+       $(INTDIR)/indexnode.sbr \
+       $(INTDIR)/equalfuncs.sbr \
+       $(INTDIR)/oidint2.sbr \
+       $(INTDIR)/list.sbr \
+       $(INTDIR)/plancat.sbr \
+       $(INTDIR)/fmgr.sbr \
+       $(INTDIR)/fmgrtab.sbr \
+       $(INTDIR)/dllist.sbr \
+       $(INTDIR)/nodeGroup.sbr \
+       $(INTDIR)/localbuf.sbr \
+       $(INTDIR)/cluster.sbr \
+       $(INTDIR)/ipc.sbr \
+       $(INTDIR)/nt.sbr \
+       $(INTDIR)/getopt.sbr \
+       $(INTDIR)/bootscanner.sbr \
+       $(INTDIR)/scan.sbr \
+       $(INTDIR)/bootparse.sbr \
+       $(INTDIR)/gram.sbr \
+       $(INTDIR)/findbe.sbr \
+       $(INTDIR)/regerror.sbr \
+       $(INTDIR)/regfree.sbr \
+       $(INTDIR)/regcomp.sbr \
+       $(INTDIR)/regexec.sbr \
+       $(INTDIR)/nbtsort.sbr \
+       $(INTDIR)/buf_init.sbr \
+       $(INTDIR)/dfmgr.sbr
+
+$(OUTDIR)/pglite.bsc : $(OUTDIR)  $(BSC32_SBRS)
+    $(BSC32) @<<
+  $(BSC32_FLAGS) $(BSC32_SBRS)
+<<
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NOLOGO /SUBSYSTEM:console /DEBUG /MACHINE:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /NOLOGO /SUBSYSTEM:console /DEBUG /MACHINE:I386
+# SUBTRACT LINK32 /PDB:none
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib wsock32.lib /NOLOGO /SUBSYSTEM:console /INCREMENTAL:yes\
+ /PDB:$(OUTDIR)/"pglite.pdb" /DEBUG /MACHINE:I386 /OUT:$(OUTDIR)/"pglite.exe"
+DEF_FILE=
+LINK32_OBJS= \
+       $(INTDIR)/scankey.obj \
+       $(INTDIR)/printtup.obj \
+       $(INTDIR)/indexvalid.obj \
+       $(INTDIR)/heaptuple.obj \
+       $(INTDIR)/tupdesc.obj \
+       $(INTDIR)/indextuple.obj \
+       $(INTDIR)/heapvalid.obj \
+       $(INTDIR)/hashinsert.obj \
+       $(INTDIR)/hashstrat.obj \
+       $(INTDIR)/hashutil.obj \
+       $(INTDIR)/hashpage.obj \
+       $(INTDIR)/hashsearch.obj \
+       $(INTDIR)/hashscan.obj \
+       $(INTDIR)/hashfunc.obj \
+       $(INTDIR)/hash.obj \
+       $(INTDIR)/hashovfl.obj \
+       $(INTDIR)/bootstrap.obj \
+       $(INTDIR)/genam.obj \
+       $(INTDIR)/creatinh.obj \
+       $(INTDIR)/nodeSeqscan.obj \
+       $(INTDIR)/nodeUnique.obj \
+       $(INTDIR)/rename.obj \
+       $(INTDIR)/transsup.obj \
+       $(INTDIR)/transam.obj \
+       $(INTDIR)/define.obj \
+       $(INTDIR)/execMain.obj \
+       $(INTDIR)/xid.obj \
+       $(INTDIR)/nodeAgg.obj \
+       $(INTDIR)/nbtpage.obj \
+       $(INTDIR)/execScan.obj \
+       $(INTDIR)/nbtree.obj \
+       $(INTDIR)/rtscan.obj \
+       $(INTDIR)/indexam.obj \
+       $(INTDIR)/execQual.obj \
+       $(INTDIR)/nodeHash.obj \
+       $(INTDIR)/nbtscan.obj \
+       $(INTDIR)/hio.obj \
+       $(INTDIR)/pg_proc.obj \
+       $(INTDIR)/stats.obj \
+       $(INTDIR)/nodeMaterial.obj \
+       $(INTDIR)/varsup.obj \
+       $(INTDIR)/copy.obj \
+       $(INTDIR)/rtproc.obj \
+       $(INTDIR)/functions.obj \
+       $(INTDIR)/nodeHashjoin.obj \
+       $(INTDIR)/catalog.obj \
+       $(INTDIR)/nbtinsert.obj \
+       $(INTDIR)/rtree.obj \
+       $(INTDIR)/version.obj \
+       $(INTDIR)/async.obj \
+       $(INTDIR)/nbtutils.obj \
+       $(INTDIR)/vacuum.obj \
+       $(INTDIR)/rtstrat.obj \
+       $(INTDIR)/execFlatten.obj \
+       $(INTDIR)/nodeTee.obj \
+       $(INTDIR)/nodeIndexscan.obj \
+       $(INTDIR)/remove.obj \
+       $(INTDIR)/indexing.obj \
+       $(INTDIR)/command.obj \
+       $(INTDIR)/nbtsearch.obj \
+       $(INTDIR)/heapam.obj \
+       $(INTDIR)/nodeSort.obj \
+       $(INTDIR)/execProcnode.obj \
+       $(INTDIR)/nodeResult.obj \
+       $(INTDIR)/index.obj \
+       $(INTDIR)/xact.obj \
+       $(INTDIR)/nodeMergejoin.obj \
+       $(INTDIR)/pg_operator.obj \
+       $(INTDIR)/execJunk.obj \
+       $(INTDIR)/pg_aggregate.obj \
+       $(INTDIR)/istrat.obj \
+       $(INTDIR)/execUtils.obj \
+       $(INTDIR)/purge.obj \
+       $(INTDIR)/heap.obj \
+       $(INTDIR)/nbtstrat.obj \
+       $(INTDIR)/execAmi.obj \
+       $(INTDIR)/execTuples.obj \
+       $(INTDIR)/pg_type.obj \
+       $(INTDIR)/view.obj \
+       $(INTDIR)/nodeAppend.obj \
+       $(INTDIR)/defind.obj \
+       $(INTDIR)/nodeNestloop.obj \
+       $(INTDIR)/nbtcompare.obj \
+       $(INTDIR)/rtget.obj \
+       $(INTDIR)/catalog_utils.obj \
+       $(INTDIR)/setrefs.obj \
+       $(INTDIR)/mergeutils.obj \
+       $(INTDIR)/oset.obj \
+       $(INTDIR)/arrayutils.obj \
+       $(INTDIR)/nodeFuncs.obj \
+       $(INTDIR)/rewriteSupport.obj \
+       $(INTDIR)/bufpage.obj \
+       $(INTDIR)/fd.obj \
+       $(INTDIR)/clauseinfo.obj \
+       $(INTDIR)/nabstime.obj \
+       $(INTDIR)/mcxt.obj \
+       $(INTDIR)/ipci.obj \
+       $(INTDIR)/qsort.obj \
+       $(INTDIR)/outfuncs.obj \
+       $(INTDIR)/tqual.obj \
+       $(INTDIR)/keys.obj \
+       $(INTDIR)/clauses.obj \
+       $(INTDIR)/print.obj \
+       $(INTDIR)/postinit.obj \
+       $(INTDIR)/oidchar16.obj \
+       $(INTDIR)/name.obj \
+       $(INTDIR)/tid.obj \
+       $(INTDIR)/"be-fsstubs.obj" \
+       $(INTDIR)/elog.obj \
+       $(INTDIR)/bufmgr.obj \
+       $(INTDIR)/portalbuf.obj \
+       $(INTDIR)/psort.obj \
+       $(INTDIR)/syscache.obj \
+       $(INTDIR)/exc.obj \
+       $(INTDIR)/selfuncs.obj \
+       $(INTDIR)/var.obj \
+       $(INTDIR)/oid.obj \
+       $(INTDIR)/"be-pqexec.obj" \
+       $(INTDIR)/ordering.obj \
+       $(INTDIR)/inv_api.obj \
+       $(INTDIR)/buf_table.obj \
+       $(INTDIR)/acl.obj \
+       $(INTDIR)/costsize.obj \
+       $(INTDIR)/catcache.obj \
+       $(INTDIR)/rewriteRemove.obj \
+       $(INTDIR)/parse_query.obj \
+       $(INTDIR)/excabort.obj \
+       $(INTDIR)/lmgr.obj \
+       $(INTDIR)/excid.obj \
+       $(INTDIR)/int.obj \
+       $(INTDIR)/auth.obj \
+       $(INTDIR)/regexp.obj \
+       $(INTDIR)/proc.obj \
+       $(INTDIR)/dbcommands.obj \
+       $(INTDIR)/dynahash.obj \
+       $(INTDIR)/shmem.obj \
+       $(INTDIR)/relnode.obj \
+       $(INTDIR)/fstack.obj \
+       $(INTDIR)/smgr.obj \
+       $(INTDIR)/magic.obj \
+       $(INTDIR)/relcache.obj \
+       $(INTDIR)/varlena.obj \
+       $(INTDIR)/allpaths.obj \
+       $(INTDIR)/portalmem.obj \
+       $(INTDIR)/bit.obj \
+       $(INTDIR)/readfuncs.obj \
+       $(INTDIR)/nodes.obj \
+       $(INTDIR)/chunk.obj \
+       $(INTDIR)/datum.obj \
+       $(INTDIR)/analyze.obj \
+       $(INTDIR)/oidint4.obj \
+       $(INTDIR)/hasht.obj \
+       $(INTDIR)/numutils.obj \
+       $(INTDIR)/pqcomm.obj \
+       $(INTDIR)/indxpath.obj \
+       $(INTDIR)/lispsort.obj \
+       $(INTDIR)/arrayfuncs.obj \
+       $(INTDIR)/copyfuncs.obj \
+       $(INTDIR)/planmain.obj \
+       $(INTDIR)/makefuncs.obj \
+       $(INTDIR)/lsyscache.obj \
+       $(INTDIR)/multi.obj \
+       $(INTDIR)/freelist.obj \
+       $(INTDIR)/aclchk.obj \
+       $(INTDIR)/initsplan.obj \
+       $(INTDIR)/prune.obj \
+       $(INTDIR)/sinvaladt.obj \
+       $(INTDIR)/orindxpath.obj \
+       $(INTDIR)/joinrels.obj \
+       $(INTDIR)/rewriteManip.obj \
+       $(INTDIR)/itemptr.obj \
+       $(INTDIR)/s_lock.obj \
+       $(INTDIR)/miscinit.obj \
+       $(INTDIR)/postgres.obj \
+       $(INTDIR)/parser.obj \
+       $(INTDIR)/tlist.obj \
+       $(INTDIR)/dt.obj \
+       $(INTDIR)/sinval.obj \
+       $(INTDIR)/pqpacket.obj \
+       $(INTDIR)/assert.obj \
+       $(INTDIR)/utility.obj \
+       $(INTDIR)/bool.obj \
+       $(INTDIR)/md.obj \
+       $(INTDIR)/pqsignal.obj \
+       $(INTDIR)/globals.obj \
+       $(INTDIR)/postmaster.obj \
+       $(INTDIR)/joinpath.obj \
+       $(INTDIR)/fastpath.obj \
+       $(INTDIR)/archive.obj \
+       $(INTDIR)/fcache.obj \
+       $(INTDIR)/mm.obj \
+       $(INTDIR)/createplan.obj \
+       $(INTDIR)/read.obj \
+       $(INTDIR)/stringinfo.obj \
+       $(INTDIR)/hashfn.obj \
+       $(INTDIR)/regproc.obj \
+       $(INTDIR)/main.obj \
+       $(INTDIR)/enbl.obj \
+       $(INTDIR)/prepunion.obj \
+       $(INTDIR)/prepqual.obj \
+       $(INTDIR)/planner.obj \
+       $(INTDIR)/clausesel.obj \
+       $(INTDIR)/portal.obj \
+       $(INTDIR)/spin.obj \
+       $(INTDIR)/lock.obj \
+       $(INTDIR)/single.obj \
+       $(INTDIR)/io.obj \
+       $(INTDIR)/"geo-ops.obj" \
+       $(INTDIR)/dest.obj \
+       $(INTDIR)/rewriteDefine.obj \
+       $(INTDIR)/keywords.obj \
+       $(INTDIR)/hashutils.obj \
+       $(INTDIR)/format.obj \
+       $(INTDIR)/scanner.obj \
+       $(INTDIR)/aset.obj \
+       $(INTDIR)/"geo-selfuncs.obj" \
+       $(INTDIR)/float.obj \
+       $(INTDIR)/pquery.obj \
+       $(INTDIR)/"be-dumpdata.obj" \
+       $(INTDIR)/filename.obj \
+       $(INTDIR)/misc.obj \
+       $(INTDIR)/pathnode.obj \
+       $(INTDIR)/inval.obj \
+       $(INTDIR)/smgrtype.obj \
+       $(INTDIR)/joininfo.obj \
+       $(INTDIR)/lselect.obj \
+       $(INTDIR)/rel.obj \
+       $(INTDIR)/internal.obj \
+       $(INTDIR)/preptlist.obj \
+       $(INTDIR)/joinutils.obj \
+       $(INTDIR)/shmqueue.obj \
+       $(INTDIR)/date.obj \
+       $(INTDIR)/locks.obj \
+       $(INTDIR)/not_in.obj \
+       $(INTDIR)/char.obj \
+       $(INTDIR)/rewriteHandler.obj \
+       $(INTDIR)/sets.obj \
+       $(INTDIR)/palloc.obj \
+       $(INTDIR)/indexnode.obj \
+       $(INTDIR)/equalfuncs.obj \
+       $(INTDIR)/oidint2.obj \
+       $(INTDIR)/list.obj \
+       $(INTDIR)/plancat.obj \
+       $(INTDIR)/fmgr.obj \
+       $(INTDIR)/fmgrtab.obj \
+       $(INTDIR)/dllist.obj \
+       $(INTDIR)/nodeGroup.obj \
+       $(INTDIR)/localbuf.obj \
+       $(INTDIR)/cluster.obj \
+       $(INTDIR)/ipc.obj \
+       $(INTDIR)/nt.obj \
+       $(INTDIR)/getopt.obj \
+       $(INTDIR)/bootscanner.obj \
+       $(INTDIR)/scan.obj \
+       $(INTDIR)/bootparse.obj \
+       $(INTDIR)/gram.obj \
+       $(INTDIR)/findbe.obj \
+       $(INTDIR)/regerror.obj \
+       $(INTDIR)/regfree.obj \
+       $(INTDIR)/regcomp.obj \
+       $(INTDIR)/regexec.obj \
+       $(INTDIR)/nbtsort.obj \
+       $(INTDIR)/buf_init.obj \
+       $(INTDIR)/dfmgr.obj
+
+$(OUTDIR)/pglite.exe : $(OUTDIR)  $(DEF_FILE) $(LINK32_OBJS)
+    $(LINK32) @<<
+  $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF 
+
+.c{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cpp{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+.cxx{$(CPP_OBJS)}.obj:
+   $(CPP) $(CPP_PROJ) $<  
+
+################################################################################
+# Begin Group "Source Files"
+
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\common\scankey.c
+
+$(INTDIR)/scankey.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\common\printtup.c
+
+$(INTDIR)/printtup.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\common\indexvalid.c
+
+$(INTDIR)/indexvalid.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\common\heaptuple.c
+
+$(INTDIR)/heaptuple.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\common\tupdesc.c
+
+$(INTDIR)/tupdesc.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\common\indextuple.c
+
+$(INTDIR)/indextuple.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\common\heapvalid.c
+
+$(INTDIR)/heapvalid.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\hash\hashinsert.c
+
+$(INTDIR)/hashinsert.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\hash\hashstrat.c
+
+$(INTDIR)/hashstrat.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\hash\hashutil.c
+
+$(INTDIR)/hashutil.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\hash\hashpage.c
+
+$(INTDIR)/hashpage.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\hash\hashsearch.c
+
+$(INTDIR)/hashsearch.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\hash\hashscan.c
+
+$(INTDIR)/hashscan.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\hash\hashfunc.c
+
+$(INTDIR)/hashfunc.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\hash\hash.c
+
+$(INTDIR)/hash.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\hash\hashovfl.c
+
+$(INTDIR)/hashovfl.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\bootstrap\bootstrap.c
+
+$(INTDIR)/bootstrap.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\index\genam.c
+
+$(INTDIR)/genam.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\commands\creatinh.c
+
+$(INTDIR)/creatinh.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\nodeSeqscan.c
+
+$(INTDIR)/nodeSeqscan.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\nodeUnique.c
+
+$(INTDIR)/nodeUnique.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\commands\rename.c
+
+$(INTDIR)/rename.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\transam\transsup.c
+
+$(INTDIR)/transsup.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\transam\transam.c
+
+$(INTDIR)/transam.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\commands\define.c
+
+$(INTDIR)/define.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\execMain.c
+
+$(INTDIR)/execMain.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\transam\xid.c
+
+$(INTDIR)/xid.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\nodeAgg.c
+
+$(INTDIR)/nodeAgg.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\nbtree\nbtpage.c
+
+$(INTDIR)/nbtpage.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\execScan.c
+
+$(INTDIR)/execScan.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\nbtree\nbtree.c
+
+$(INTDIR)/nbtree.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\rtree\rtscan.c
+
+$(INTDIR)/rtscan.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\index\indexam.c
+
+$(INTDIR)/indexam.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\execQual.c
+
+$(INTDIR)/execQual.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\nodeHash.c
+
+$(INTDIR)/nodeHash.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\nbtree\nbtscan.c
+
+$(INTDIR)/nbtscan.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\heap\hio.c
+
+$(INTDIR)/hio.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\catalog\pg_proc.c
+
+$(INTDIR)/pg_proc.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\heap\stats.c
+
+$(INTDIR)/stats.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\nodeMaterial.c
+
+$(INTDIR)/nodeMaterial.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\transam\varsup.c
+
+$(INTDIR)/varsup.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\commands\copy.c
+
+$(INTDIR)/copy.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\rtree\rtproc.c
+
+$(INTDIR)/rtproc.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\functions.c
+
+$(INTDIR)/functions.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\nodeHashjoin.c
+
+$(INTDIR)/nodeHashjoin.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\catalog\catalog.c
+
+$(INTDIR)/catalog.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\nbtree\nbtinsert.c
+
+$(INTDIR)/nbtinsert.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\rtree\rtree.c
+
+$(INTDIR)/rtree.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\commands\version.c
+
+$(INTDIR)/version.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\commands\async.c
+
+$(INTDIR)/async.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\nbtree\nbtutils.c
+
+$(INTDIR)/nbtutils.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\commands\vacuum.c
+
+$(INTDIR)/vacuum.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\rtree\rtstrat.c
+
+$(INTDIR)/rtstrat.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\execFlatten.c
+
+$(INTDIR)/execFlatten.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\nodeTee.c
+
+$(INTDIR)/nodeTee.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\nodeIndexscan.c
+
+$(INTDIR)/nodeIndexscan.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\commands\remove.c
+
+$(INTDIR)/remove.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\catalog\indexing.c
+
+$(INTDIR)/indexing.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\commands\command.c
+
+$(INTDIR)/command.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\nbtree\nbtsearch.c
+
+$(INTDIR)/nbtsearch.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\heap\heapam.c
+
+$(INTDIR)/heapam.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\nodeSort.c
+
+$(INTDIR)/nodeSort.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\execProcnode.c
+
+$(INTDIR)/execProcnode.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\nodeResult.c
+
+$(INTDIR)/nodeResult.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\catalog\index.c
+
+$(INTDIR)/index.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\transam\xact.c
+
+$(INTDIR)/xact.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\nodeMergejoin.c
+
+$(INTDIR)/nodeMergejoin.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\catalog\pg_operator.c
+
+$(INTDIR)/pg_operator.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\execJunk.c
+
+$(INTDIR)/execJunk.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\catalog\pg_aggregate.c
+
+$(INTDIR)/pg_aggregate.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\index\istrat.c
+
+$(INTDIR)/istrat.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\execUtils.c
+
+$(INTDIR)/execUtils.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\commands\purge.c
+
+$(INTDIR)/purge.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\catalog\heap.c
+
+$(INTDIR)/heap.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\nbtree\nbtstrat.c
+
+$(INTDIR)/nbtstrat.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\execAmi.c
+
+$(INTDIR)/execAmi.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\execTuples.c
+
+$(INTDIR)/execTuples.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\catalog\pg_type.c
+
+$(INTDIR)/pg_type.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\commands\view.c
+
+$(INTDIR)/view.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\nodeAppend.c
+
+$(INTDIR)/nodeAppend.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\commands\defind.c
+
+$(INTDIR)/defind.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\nodeNestloop.c
+
+$(INTDIR)/nodeNestloop.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\nbtree\nbtcompare.c
+
+$(INTDIR)/nbtcompare.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\rtree\rtget.c
+
+$(INTDIR)/rtget.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\parser\catalog_utils.c
+
+$(INTDIR)/catalog_utils.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\plan\setrefs.c
+
+$(INTDIR)/setrefs.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\path\mergeutils.c
+
+$(INTDIR)/mergeutils.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\mmgr\oset.c
+
+$(INTDIR)/oset.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\arrayutils.c
+
+$(INTDIR)/arrayutils.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\nodes\nodeFuncs.c
+
+$(INTDIR)/nodeFuncs.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\rewrite\rewriteSupport.c
+
+$(INTDIR)/rewriteSupport.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\page\bufpage.c
+
+$(INTDIR)/bufpage.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\file\fd.c
+
+$(INTDIR)/fd.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\util\clauseinfo.c
+
+$(INTDIR)/clauseinfo.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\nabstime.c
+
+$(INTDIR)/nabstime.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\mmgr\mcxt.c
+
+$(INTDIR)/mcxt.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\ipc\ipci.c
+
+$(INTDIR)/ipci.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\lib\qsort.c
+
+$(INTDIR)/qsort.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\nodes\outfuncs.c
+
+$(INTDIR)/outfuncs.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\time\tqual.c
+
+$(INTDIR)/tqual.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\util\keys.c
+
+$(INTDIR)/keys.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\util\clauses.c
+
+$(INTDIR)/clauses.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\nodes\print.c
+
+$(INTDIR)/print.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\init\postinit.c
+
+$(INTDIR)/postinit.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\oidchar16.c
+
+$(INTDIR)/oidchar16.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\name.c
+
+$(INTDIR)/name.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\tid.c
+
+$(INTDIR)/tid.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE="G:\pglite\src\backend\libpq\be-fsstubs.c"
+
+$(INTDIR)/"be-fsstubs.obj" :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\error\elog.c
+
+$(INTDIR)/elog.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\buffer\bufmgr.c
+
+$(INTDIR)/bufmgr.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\libpq\portalbuf.c
+
+$(INTDIR)/portalbuf.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\sort\psort.c
+
+$(INTDIR)/psort.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\cache\syscache.c
+
+$(INTDIR)/syscache.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\error\exc.c
+
+$(INTDIR)/exc.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\selfuncs.c
+
+$(INTDIR)/selfuncs.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\util\var.c
+
+$(INTDIR)/var.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\oid.c
+
+$(INTDIR)/oid.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE="G:\pglite\src\backend\libpq\be-pqexec.c"
+
+$(INTDIR)/"be-pqexec.obj" :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\util\ordering.c
+
+$(INTDIR)/ordering.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\large_object\inv_api.c
+
+$(INTDIR)/inv_api.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\buffer\buf_table.c
+
+$(INTDIR)/buf_table.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\acl.c
+
+$(INTDIR)/acl.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\path\costsize.c
+
+$(INTDIR)/costsize.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\cache\catcache.c
+
+$(INTDIR)/catcache.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\rewrite\rewriteRemove.c
+
+$(INTDIR)/rewriteRemove.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\parser\parse_query.c
+
+$(INTDIR)/parse_query.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\error\excabort.c
+
+$(INTDIR)/excabort.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\lmgr\lmgr.c
+
+$(INTDIR)/lmgr.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\error\excid.c
+
+$(INTDIR)/excid.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\int.c
+
+$(INTDIR)/int.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\libpq\auth.c
+
+$(INTDIR)/auth.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\regexp.c
+
+$(INTDIR)/regexp.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\lmgr\proc.c
+
+$(INTDIR)/proc.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\parser\dbcommands.c
+
+$(INTDIR)/dbcommands.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\hash\dynahash.c
+
+$(INTDIR)/dynahash.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\ipc\shmem.c
+
+$(INTDIR)/shmem.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\util\relnode.c
+
+$(INTDIR)/relnode.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\lib\fstack.c
+
+$(INTDIR)/fstack.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\smgr\smgr.c
+
+$(INTDIR)/smgr.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\init\magic.c
+
+$(INTDIR)/magic.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\cache\relcache.c
+
+$(INTDIR)/relcache.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\varlena.c
+
+$(INTDIR)/varlena.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\path\allpaths.c
+
+$(INTDIR)/allpaths.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\mmgr\portalmem.c
+
+$(INTDIR)/portalmem.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\lib\bit.c
+
+$(INTDIR)/bit.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\nodes\readfuncs.c
+
+$(INTDIR)/readfuncs.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\nodes\nodes.c
+
+$(INTDIR)/nodes.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\chunk.c
+
+$(INTDIR)/chunk.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\datum.c
+
+$(INTDIR)/datum.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\parser\analyze.c
+
+$(INTDIR)/analyze.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\oidint4.c
+
+$(INTDIR)/oidint4.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\lib\hasht.c
+
+$(INTDIR)/hasht.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\numutils.c
+
+$(INTDIR)/numutils.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\libpq\pqcomm.c
+
+$(INTDIR)/pqcomm.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\path\indxpath.c
+
+$(INTDIR)/indxpath.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\lib\lispsort.c
+
+$(INTDIR)/lispsort.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\arrayfuncs.c
+
+$(INTDIR)/arrayfuncs.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\nodes\copyfuncs.c
+
+$(INTDIR)/copyfuncs.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\plan\planmain.c
+
+$(INTDIR)/planmain.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\nodes\makefuncs.c
+
+$(INTDIR)/makefuncs.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\cache\lsyscache.c
+
+$(INTDIR)/lsyscache.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\lmgr\multi.c
+
+$(INTDIR)/multi.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\buffer\freelist.c
+
+$(INTDIR)/freelist.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\tcop\aclchk.c
+
+$(INTDIR)/aclchk.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\plan\initsplan.c
+
+$(INTDIR)/initsplan.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\path\prune.c
+
+$(INTDIR)/prune.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\ipc\sinvaladt.c
+
+$(INTDIR)/sinvaladt.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\path\orindxpath.c
+
+$(INTDIR)/orindxpath.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\path\joinrels.c
+
+$(INTDIR)/joinrels.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\rewrite\rewriteManip.c
+
+$(INTDIR)/rewriteManip.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\page\itemptr.c
+
+$(INTDIR)/itemptr.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\ipc\s_lock.c
+
+$(INTDIR)/s_lock.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\init\miscinit.c
+
+$(INTDIR)/miscinit.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\tcop\postgres.c
+
+$(INTDIR)/postgres.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\parser\parser.c
+
+$(INTDIR)/parser.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\util\tlist.c
+
+$(INTDIR)/tlist.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\dt.c
+
+$(INTDIR)/dt.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\ipc\sinval.c
+
+$(INTDIR)/sinval.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\libpq\pqpacket.c
+
+$(INTDIR)/pqpacket.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\error\assert.c
+
+$(INTDIR)/assert.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\tcop\utility.c
+
+$(INTDIR)/utility.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\bool.c
+
+$(INTDIR)/bool.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\smgr\md.c
+
+$(INTDIR)/md.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\libpq\pqsignal.c
+
+$(INTDIR)/pqsignal.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\init\globals.c
+
+$(INTDIR)/globals.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\postmaster\postmaster.c
+
+$(INTDIR)/postmaster.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\path\joinpath.c
+
+$(INTDIR)/joinpath.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\tcop\fastpath.c
+
+$(INTDIR)/fastpath.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\prep\archive.c
+
+$(INTDIR)/archive.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\cache\fcache.c
+
+$(INTDIR)/fcache.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\smgr\mm.c
+
+$(INTDIR)/mm.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\plan\createplan.c
+
+$(INTDIR)/createplan.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\nodes\read.c
+
+$(INTDIR)/read.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\lib\stringinfo.c
+
+$(INTDIR)/stringinfo.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\hash\hashfn.c
+
+$(INTDIR)/hashfn.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\regproc.c
+
+$(INTDIR)/regproc.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\main\main.c
+
+$(INTDIR)/main.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\init\enbl.c
+
+$(INTDIR)/enbl.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\prep\prepunion.c
+
+$(INTDIR)/prepunion.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\prep\prepqual.c
+
+$(INTDIR)/prepqual.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\plan\planner.c
+
+$(INTDIR)/planner.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\path\clausesel.c
+
+$(INTDIR)/clausesel.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\libpq\portal.c
+
+$(INTDIR)/portal.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\ipc\spin.c
+
+$(INTDIR)/spin.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\lmgr\lock.c
+
+$(INTDIR)/lock.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\lmgr\single.c
+
+$(INTDIR)/single.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\parser\io.c
+
+$(INTDIR)/io.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE="G:\pglite\src\backend\utils\adt\geo-ops.c"
+
+$(INTDIR)/"geo-ops.obj" :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\tcop\dest.c
+
+$(INTDIR)/dest.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\rewrite\rewriteDefine.c
+
+$(INTDIR)/rewriteDefine.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\parser\keywords.c
+
+$(INTDIR)/keywords.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\path\hashutils.c
+
+$(INTDIR)/hashutils.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\error\format.c
+
+$(INTDIR)/format.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\parser\scanner.c
+
+$(INTDIR)/scanner.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\mmgr\aset.c
+
+$(INTDIR)/aset.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE="G:\pglite\src\backend\utils\adt\geo-selfuncs.c"
+
+$(INTDIR)/"geo-selfuncs.obj" :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\float.c
+
+$(INTDIR)/float.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\tcop\pquery.c
+
+$(INTDIR)/pquery.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE="G:\pglite\src\backend\libpq\be-dumpdata.c"
+
+$(INTDIR)/"be-dumpdata.obj" :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\filename.c
+
+$(INTDIR)/filename.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\misc.c
+
+$(INTDIR)/misc.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\util\pathnode.c
+
+$(INTDIR)/pathnode.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\cache\inval.c
+
+$(INTDIR)/inval.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\smgr\smgrtype.c
+
+$(INTDIR)/smgrtype.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\util\joininfo.c
+
+$(INTDIR)/joininfo.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\sort\lselect.c
+
+$(INTDIR)/lselect.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\cache\rel.c
+
+$(INTDIR)/rel.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\util\internal.c
+
+$(INTDIR)/internal.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\prep\preptlist.c
+
+$(INTDIR)/preptlist.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\path\joinutils.c
+
+$(INTDIR)/joinutils.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\ipc\shmqueue.c
+
+$(INTDIR)/shmqueue.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\date.c
+
+$(INTDIR)/date.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\rewrite\locks.c
+
+$(INTDIR)/locks.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\not_in.c
+
+$(INTDIR)/not_in.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\char.c
+
+$(INTDIR)/char.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\rewrite\rewriteHandler.c
+
+$(INTDIR)/rewriteHandler.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\sets.c
+
+$(INTDIR)/sets.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\mmgr\palloc.c
+
+$(INTDIR)/palloc.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\util\indexnode.c
+
+$(INTDIR)/indexnode.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\nodes\equalfuncs.c
+
+$(INTDIR)/equalfuncs.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\adt\oidint2.c
+
+$(INTDIR)/oidint2.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\nodes\list.c
+
+$(INTDIR)/list.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\optimizer\util\plancat.c
+
+$(INTDIR)/plancat.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\fmgr\fmgr.c
+
+$(INTDIR)/fmgr.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\obj\fmgrtab.c
+
+$(INTDIR)/fmgrtab.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\lib\dllist.c
+
+$(INTDIR)/dllist.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\executor\nodeGroup.c
+
+$(INTDIR)/nodeGroup.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\buffer\localbuf.c
+
+$(INTDIR)/localbuf.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\commands\cluster.c
+
+$(INTDIR)/cluster.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\ipc\ipc.c
+
+$(INTDIR)/ipc.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\port\win32\nt.c
+
+$(INTDIR)/nt.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\port\win32\getopt.c
+
+$(INTDIR)/getopt.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\obj\bootscanner.c
+
+$(INTDIR)/bootscanner.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\obj\scan.c
+
+$(INTDIR)/scan.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\obj\bootparse.c
+
+$(INTDIR)/bootparse.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\obj\gram.c
+
+$(INTDIR)/gram.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\init\findbe.c
+
+$(INTDIR)/findbe.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\port\win32\regex\regerror.c
+
+$(INTDIR)/regerror.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\port\win32\regex\regfree.c
+
+$(INTDIR)/regfree.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\port\win32\regex\regcomp.c
+
+$(INTDIR)/regcomp.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\port\win32\regex\regexec.c
+
+$(INTDIR)/regexec.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\access\nbtree\nbtsort.c
+
+$(INTDIR)/nbtsort.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\storage\buffer\buf_init.c
+
+$(INTDIR)/buf_init.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=G:\pglite\src\backend\utils\fmgr\dfmgr.c
+
+$(INTDIR)/dfmgr.obj :  $(SOURCE)  $(INTDIR)
+   $(CPP) $(CPP_PROJ)  $(SOURCE) 
+
+# End Source File
+# End Group
+# End Project
+################################################################################
diff --git a/src/backend/port/win32/port-protos.h b/src/backend/port/win32/port-protos.h
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/src/backend/port/win32/pwd.h b/src/backend/port/win32/pwd.h
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/src/backend/port/win32/regex/COPYRIGHT b/src/backend/port/win32/regex/COPYRIGHT
new file mode 100644 (file)
index 0000000..574f6bc
--- /dev/null
@@ -0,0 +1,56 @@
+Copyright 1992, 1993, 1994 Henry Spencer.  All rights reserved.
+This software is not subject to any license of the American Telephone
+and Telegraph Company or of the Regents of the University of California.
+
+Permission is granted to anyone to use this software for any purpose on
+any computer system, and to alter it and redistribute it, subject
+to the following restrictions:
+
+1. The author is not responsible for the consequences of use of this
+   software, no matter how awful, even if they arise from flaws in it.
+
+2. The origin of this software must not be misrepresented, either by
+   explicit claim or by omission.  Since few users ever read sources,
+   credits must appear in the documentation.
+
+3. Altered versions must be plainly marked as such, and must not be
+   misrepresented as being the original software.  Since few users
+   ever read sources, credits must appear in the documentation.
+
+4. This notice may not be removed or altered.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+/*-
+ * Copyright (c) 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)COPYRIGHT   8.1 (Berkeley) 3/16/94
+ */
diff --git a/src/backend/port/win32/regex/Makefile.inc b/src/backend/port/win32/regex/Makefile.inc
new file mode 100644 (file)
index 0000000..f9853f3
--- /dev/null
@@ -0,0 +1,14 @@
+#      @(#)Makefile.inc        8.1 (Berkeley) 6/4/93
+
+# regex sources
+.PATH: ${.CURDIR}/regex
+
+CFLAGS+=-DPOSIX_MISTAKE
+
+SRCS+= regcomp.c regerror.c regexec.c regfree.c
+
+MAN3+= regex.0
+MAN7+= re_format.0
+
+MLINKS+=regex.3 regcomp.3 regex.3 regexec.3 regex.3 regerror.3
+MLINKS+=regexec.3 regfree.3
diff --git a/src/backend/port/win32/regex/WHATSNEW b/src/backend/port/win32/regex/WHATSNEW
new file mode 100644 (file)
index 0000000..f4301d3
--- /dev/null
@@ -0,0 +1,94 @@
+# @(#)WHATSNEW 8.3 (Berkeley) 3/18/94
+
+New in alpha3.4:  The complex bug alluded to below has been fixed (in a
+slightly kludgey temporary way that may hurt efficiency a bit; this is
+another "get it out the door for 4.4" release).  The tests at the end of
+the tests file have accordingly been uncommented.  The primary sign of
+the bug was that something like a?b matching ab matched b rather than ab.
+(The bug was essentially specific to this exact situation, else it would
+have shown up earlier.)
+
+New in alpha3.3:  The definition of word boundaries has been altered
+slightly, to more closely match the usual programming notion that "_"
+is an alphabetic.  Stuff used for pre-ANSI systems is now in a subdir,
+and the makefile no longer alludes to it in mysterious ways.  The
+makefile has generally been cleaned up some.  Fixes have been made
+(again!) so that the regression test will run without -DREDEBUG, at
+the cost of weaker checking.  A workaround for a bug in some folks'
+<assert.h> has been added.  And some more things have been added to
+tests, including a couple right at the end which are commented out
+because the code currently flunks them (complex bug; fix coming).
+Plus the usual minor cleanup.
+
+New in alpha3.2:  Assorted bits of cleanup and portability improvement
+(the development base is now a BSDI system using GCC instead of an ancient
+Sun system, and the newer compiler exposed some glitches).  Fix for a
+serious bug that affected REs using many [] (including REG_ICASE REs
+because of the way they are implemented), *sometimes*, depending on
+memory-allocation patterns.  The header-file prototypes no longer name
+the parameters, avoiding possible name conflicts.  The possibility that
+some clot has defined CHAR_MIN as (say) `-128' instead of `(-128)' is
+now handled gracefully.  "uchar" is no longer used as an internal type
+name (too many people have the same idea).  Still the same old lousy
+performance, alas.
+
+New in alpha3.1:  Basically nothing, this release is just a bookkeeping
+convenience.  Stay tuned.
+
+New in alpha3.0:  Performance is no better, alas, but some fixes have been
+made and some functionality has been added.  (This is basically the "get
+it out the door in time for 4.4" release.)  One bug fix:  regfree() didn't
+free the main internal structure (how embarrassing).  It is now possible
+to put NULs in either the RE or the target string, using (resp.) a new
+REG_PEND flag and the old REG_STARTEND flag.  The REG_NOSPEC flag to
+regcomp() makes all characters ordinary, so you can match a literal
+string easily (this will become more useful when performance improves!).
+There are now primitives to match beginnings and ends of words, although
+the syntax is disgusting and so is the implementation.  The REG_ATOI
+debugging interface has changed a bit.  And there has been considerable
+internal cleanup of various kinds.
+
+New in alpha2.3:  Split change list out of README, and moved flags notes
+into Makefile.  Macro-ized the name of regex(7) in regex(3), since it has
+to change for 4.4BSD.  Cleanup work in engine.c, and some new regression
+tests to catch tricky cases thereof.
+
+New in alpha2.2:  Out-of-date manpages updated.  Regerror() acquires two
+small extensions -- REG_ITOA and REG_ATOI -- which avoid debugging kludges
+in my own test program and might be useful to others for similar purposes.
+The regression test will now compile (and run) without REDEBUG.  The
+BRE \$ bug is fixed.  Most uses of "uchar" are gone; it's all chars now.
+Char/uchar parameters are now written int/unsigned, to avoid possible
+portability problems with unpromoted parameters.  Some unsigned casts have
+been introduced to minimize portability problems with shifting into sign
+bits.
+
+New in alpha2.1:  Lots of little stuff, cleanup and fixes.  The one big
+thing is that regex.h is now generated, using mkh, rather than being
+supplied in the distribution; due to circularities in dependencies,
+you have to build regex.h explicitly by "make h".  The two known bugs
+have been fixed (and the regression test now checks for them), as has a
+problem with assertions not being suppressed in the absence of REDEBUG.
+No performance work yet.
+
+New in alpha2:  Backslash-anything is an ordinary character, not an
+error (except, of course, for the handful of backslashed metacharacters
+in BREs), which should reduce script breakage.  The regression test
+checks *where* null strings are supposed to match, and has generally
+been tightened up somewhat.  Small bug fixes in parameter passing (not
+harmful, but technically errors) and some other areas.  Debugging
+invoked by defining REDEBUG rather than not defining NDEBUG.
+
+New in alpha+3:  full prototyping for internal routines, using a little
+helper program, mkh, which extracts prototypes given in stylized comments.
+More minor cleanup.  Buglet fix:  it's CHAR_BIT, not CHAR_BITS.  Simple
+pre-screening of input when a literal string is known to be part of the
+RE; this does wonders for performance.
+
+New in alpha+2:  minor bits of cleanup.  Notably, the number "32" for the
+word width isn't hardwired into regexec.c any more, the public header
+file prototypes the functions if __STDC__ is defined, and some small typos
+in the manpages have been fixed.
+
+New in alpha+1:  improvements to the manual pages, and an important
+extension, the REG_STARTEND option to regexec().
diff --git a/src/backend/port/win32/regex/cclass.h b/src/backend/port/win32/regex/cclass.h
new file mode 100644 (file)
index 0000000..a29a92e
--- /dev/null
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)cclass.h    8.3 (Berkeley) 3/20/94
+ */
+
+/* character-class table */
+static struct cclass {
+       char *name;
+       char *chars;
+       char *multis;
+} cclasses[] = {
+       "alnum",        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\
+0123456789",                           "",
+       "alpha",        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
+                                       "",
+       "blank",        " \t",          "",
+       "cntrl",        "\007\b\t\n\v\f\r\1\2\3\4\5\6\16\17\20\21\22\23\24\
+\25\26\27\30\31\32\33\34\35\36\37\177",        "",
+       "digit",        "0123456789",   "",
+       "graph",        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\
+0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
+                                       "",
+       "lower",        "abcdefghijklmnopqrstuvwxyz",
+                                       "",
+       "print",        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\
+0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ",
+                                       "",
+       "punct",        "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
+                                       "",
+       "space",        "\t\n\v\f\r ",  "",
+       "upper",        "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+                                       "",
+       "xdigit",       "0123456789ABCDEFabcdef",
+                                       "",
+       NULL,           0,              ""
+};
diff --git a/src/backend/port/win32/regex/cname.h b/src/backend/port/win32/regex/cname.h
new file mode 100644 (file)
index 0000000..c1632eb
--- /dev/null
@@ -0,0 +1,141 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)cname.h     8.3 (Berkeley) 3/20/94
+ */
+
+/* character-name table */
+static struct cname {
+       char *name;
+       char code;
+} cnames[] = {
+       "NUL",  '\0',
+       "SOH",  '\001',
+       "STX",  '\002',
+       "ETX",  '\003',
+       "EOT",  '\004',
+       "ENQ",  '\005',
+       "ACK",  '\006',
+       "BEL",  '\007',
+       "alert",        '\007',
+       "BS",           '\010',
+       "backspace",    '\b',
+       "HT",           '\011',
+       "tab",          '\t',
+       "LF",           '\012',
+       "newline",      '\n',
+       "VT",           '\013',
+       "vertical-tab", '\v',
+       "FF",           '\014',
+       "form-feed",    '\f',
+       "CR",           '\015',
+       "carriage-return",      '\r',
+       "SO",   '\016',
+       "SI",   '\017',
+       "DLE",  '\020',
+       "DC1",  '\021',
+       "DC2",  '\022',
+       "DC3",  '\023',
+       "DC4",  '\024',
+       "NAK",  '\025',
+       "SYN",  '\026',
+       "ETB",  '\027',
+       "CAN",  '\030',
+       "EM",   '\031',
+       "SUB",  '\032',
+       "ESC",  '\033',
+       "IS4",  '\034',
+       "FS",   '\034',
+       "IS3",  '\035',
+       "GS",   '\035',
+       "IS2",  '\036',
+       "RS",   '\036',
+       "IS1",  '\037',
+       "US",   '\037',
+       "space",                ' ',
+       "exclamation-mark",     '!',
+       "quotation-mark",       '"',
+       "number-sign",          '#',
+       "dollar-sign",          '$',
+       "percent-sign",         '%',
+       "ampersand",            '&',
+       "apostrophe",           '\'',
+       "left-parenthesis",     '(',
+       "right-parenthesis",    ')',
+       "asterisk",     '*',
+       "plus-sign",    '+',
+       "comma",        ',',
+       "hyphen",       '-',
+       "hyphen-minus", '-',
+       "period",       '.',
+       "full-stop",    '.',
+       "slash",        '/',
+       "solidus",      '/',
+       "zero",         '0',
+       "one",          '1',
+       "two",          '2',
+       "three",        '3',
+       "four",         '4',
+       "five",         '5',
+       "six",          '6',
+       "seven",        '7',
+       "eight",        '8',
+       "nine",         '9',
+       "colon",        ':',
+       "semicolon",    ';',
+       "less-than-sign",       '<',
+       "equals-sign",          '=',
+       "greater-than-sign",    '>',
+       "question-mark",        '?',
+       "commercial-at",        '@',
+       "left-square-bracket",  '[',
+       "backslash",            '\\',
+       "reverse-solidus",      '\\',
+       "right-square-bracket", ']',
+       "circumflex",           '^',
+       "circumflex-accent",    '^',
+       "underscore",           '_',
+       "low-line",             '_',
+       "grave-accent",         '`',
+       "left-brace",           '{',
+       "left-curly-bracket",   '{',
+       "vertical-line",        '|',
+       "right-brace",          '}',
+       "right-curly-bracket",  '}',
+       "tilde",                '~',
+       "DEL",  '\177',
+       NULL,   0,
+};
diff --git a/src/backend/port/win32/regex/engine.c b/src/backend/port/win32/regex/engine.c
new file mode 100644 (file)
index 0000000..02c841a
--- /dev/null
@@ -0,0 +1,1091 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)engine.c    8.5 (Berkeley) 3/20/94
+ */
+
+/*
+ * The matching engine and friends.  This file is #included by regexec.c
+ * after suitable #defines of a variety of macros used herein, so that
+ * different state representations can be used without duplicating masses
+ * of code.
+ */
+
+#ifdef SNAMES
+#define        matcher smatcher
+#define        fast    sfast
+#define        slow    sslow
+#define        dissect sdissect
+#define        backref sbackref
+#define        step    sstep
+#define        print   sprint
+#define        at      sat
+#define        match   smat
+#endif
+#ifdef LNAMES
+#define        matcher lmatcher
+#define        fast    lfast
+#define        slow    lslow
+#define        dissect ldissect
+#define        backref lbackref
+#define        step    lstep
+#define        print   lprint
+#define        at      lat
+#define        match   lmat
+#endif
+
+/* another structure passed up and down to avoid zillions of parameters */
+struct match {
+       struct re_guts *g;
+       int eflags;
+       regmatch_t *pmatch;     /* [nsub+1] (0 element unused) */
+       char *offp;             /* offsets work from here */
+       char *beginp;           /* start of string -- virtual NUL precedes */
+       char *endp;             /* end of string -- virtual NUL here */
+       char *coldp;            /* can be no match starting before here */
+       char **lastpos;         /* [nplus+1] */
+       STATEVARS;
+       states st;              /* current states */
+       states fresh;           /* states for a fresh start */
+       states tmp;             /* temporary */
+       states empty;           /* empty set of states */
+};
+
+/* ========= begin header generated by ./mkh ========= */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* === engine.c === */
+static int matcher __P((struct re_guts *g, char *string, size_t nmatch, regmatch_t pmatch[], int eflags));
+static char *dissect __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst));
+static char *backref __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst, sopno lev));
+static char *fast __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst));
+static char *slow __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst));
+static states step __P((struct re_guts *g, sopno start, sopno stop, states bef, int ch, states aft));
+#define        BOL     (OUT+1)
+#define        EOL     (BOL+1)
+#define        BOLEOL  (BOL+2)
+#define        NOTHING (BOL+3)
+#define        BOW     (BOL+4)
+#define        EOW     (BOL+5)
+#define        CODEMAX (BOL+5)         /* highest code used */
+#define        NONCHAR(c)      ((c) > CHAR_MAX)
+#define        NNONCHAR        (CODEMAX-CHAR_MAX)
+#ifdef REDEBUG
+static void print __P((struct match *m, char *caption, states st, int ch, FILE *d));
+#endif
+#ifdef REDEBUG
+static void at __P((struct match *m, char *title, char *start, char *stop, sopno startst, sopno stopst));
+#endif
+#ifdef REDEBUG
+static char *pchar __P((int ch));
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+/* ========= end header generated by ./mkh ========= */
+
+#ifdef REDEBUG
+#define        SP(t, s, c)     print(m, t, s, c, stdout)
+#define        AT(t, p1, p2, s1, s2)   at(m, t, p1, p2, s1, s2)
+#define        NOTE(str)       { if (m->eflags&REG_TRACE) printf("=%s\n", (str)); }
+#else
+#define        SP(t, s, c)     /* nothing */
+#define        AT(t, p1, p2, s1, s2)   /* nothing */
+#define        NOTE(s) /* nothing */
+#endif
+
+/*
+ - matcher - the actual matching engine
+ == static int matcher(register struct re_guts *g, char *string, \
+ ==    size_t nmatch, regmatch_t pmatch[], int eflags);
+ */
+static int                     /* 0 success, REG_NOMATCH failure */
+matcher(g, string, nmatch, pmatch, eflags)
+register struct re_guts *g;
+char *string;
+size_t nmatch;
+regmatch_t pmatch[];
+int eflags;
+{
+       register char *endp;
+       register int i;
+       struct match mv;
+       register struct match *m = &mv;
+       register char *dp;
+       const register sopno gf = g->firststate+1;      /* +1 for OEND */
+       const register sopno gl = g->laststate;
+       char *start;
+       char *stop;
+
+       /* simplify the situation where possible */
+       if (g->cflags&REG_NOSUB)
+               nmatch = 0;
+       if (eflags&REG_STARTEND) {
+               start = string + pmatch[0].rm_so;
+               stop = string + pmatch[0].rm_eo;
+       } else {
+               start = string;
+               stop = start + strlen(start);
+       }
+       if (stop < start)
+               return(REG_INVARG);
+
+       /* prescreening; this does wonders for this rather slow code */
+       if (g->must != NULL) {
+               for (dp = start; dp < stop; dp++)
+                       if (*dp == g->must[0] && stop - dp >= g->mlen &&
+                               memcmp(dp, g->must, (size_t)g->mlen) == 0)
+                               break;
+               if (dp == stop)         /* we didn't find g->must */
+                       return(REG_NOMATCH);
+       }
+
+       /* match struct setup */
+       m->g = g;
+       m->eflags = eflags;
+       m->pmatch = NULL;
+       m->lastpos = NULL;
+       m->offp = string;
+       m->beginp = start;
+       m->endp = stop;
+       STATESETUP(m, 4);
+       SETUP(m->st);
+       SETUP(m->fresh);
+       SETUP(m->tmp);
+       SETUP(m->empty);
+       CLEAR(m->empty);
+
+       /* this loop does only one repetition except for backrefs */
+       for (;;) {
+               endp = fast(m, start, stop, gf, gl);
+               if (endp == NULL) {             /* a miss */
+                       STATETEARDOWN(m);
+                       return(REG_NOMATCH);
+               }
+               if (nmatch == 0 && !g->backrefs)
+                       break;          /* no further info needed */
+
+               /* where? */
+               assert(m->coldp != NULL);
+               for (;;) {
+                       NOTE("finding start");
+                       endp = slow(m, m->coldp, stop, gf, gl);
+                       if (endp != NULL)
+                               break;
+                       assert(m->coldp < m->endp);
+                       m->coldp++;
+               }
+               if (nmatch == 1 && !g->backrefs)
+                       break;          /* no further info needed */
+
+               /* oh my, he wants the subexpressions... */
+               if (m->pmatch == NULL)
+                       m->pmatch = (regmatch_t *)malloc((m->g->nsub + 1) *
+                                                       sizeof(regmatch_t));
+               if (m->pmatch == NULL) {
+                       STATETEARDOWN(m);
+                       return(REG_ESPACE);
+               }
+               for (i = 1; i <= m->g->nsub; i++)
+                       m->pmatch[i].rm_so = m->pmatch[i].rm_eo = -1;
+               if (!g->backrefs && !(m->eflags&REG_BACKR)) {
+                       NOTE("dissecting");
+                       dp = dissect(m, m->coldp, endp, gf, gl);
+               } else {
+                       if (g->nplus > 0 && m->lastpos == NULL)
+                               m->lastpos = (char **)malloc((g->nplus+1) *
+                                                       sizeof(char *));
+                       if (g->nplus > 0 && m->lastpos == NULL) {
+                               free(m->pmatch);
+                               STATETEARDOWN(m);
+                               return(REG_ESPACE);
+                       }
+                       NOTE("backref dissect");
+                       dp = backref(m, m->coldp, endp, gf, gl, (sopno)0);
+               }
+               if (dp != NULL)
+                       break;
+
+               /* uh-oh... we couldn't find a subexpression-level match */
+               assert(g->backrefs);    /* must be back references doing it */
+               assert(g->nplus == 0 || m->lastpos != NULL);
+               for (;;) {
+                       if (dp != NULL || endp <= m->coldp)
+                               break;          /* defeat */
+                       NOTE("backoff");
+                       endp = slow(m, m->coldp, endp-1, gf, gl);
+                       if (endp == NULL)
+                               break;          /* defeat */
+                       /* try it on a shorter possibility */
+#ifndef NDEBUG
+                       for (i = 1; i <= m->g->nsub; i++) {
+                               assert(m->pmatch[i].rm_so == -1);
+                               assert(m->pmatch[i].rm_eo == -1);
+                       }
+#endif
+                       NOTE("backoff dissect");
+                       dp = backref(m, m->coldp, endp, gf, gl, (sopno)0);
+               }
+               assert(dp == NULL || dp == endp);
+               if (dp != NULL)         /* found a shorter one */
+                       break;
+
+               /* despite initial appearances, there is no match here */
+               NOTE("false alarm");
+               start = m->coldp + 1;   /* recycle starting later */
+               assert(start <= stop);
+       }
+
+       /* fill in the details if requested */
+       if (nmatch > 0) {
+               pmatch[0].rm_so = m->coldp - m->offp;
+               pmatch[0].rm_eo = endp - m->offp;
+       }
+       if (nmatch > 1) {
+               assert(m->pmatch != NULL);
+               for (i = 1; i < nmatch; i++)
+                       if (i <= m->g->nsub)
+                               pmatch[i] = m->pmatch[i];
+                       else {
+                               pmatch[i].rm_so = -1;
+                               pmatch[i].rm_eo = -1;
+                       }
+       }
+
+       if (m->pmatch != NULL)
+               free((char *)m->pmatch);
+       if (m->lastpos != NULL)
+               free((char *)m->lastpos);
+       STATETEARDOWN(m);
+       return(0);
+}
+
+/*
+ - dissect - figure out what matched what, no back references
+ == static char *dissect(register struct match *m, char *start, \
+ ==    char *stop, sopno startst, sopno stopst);
+ */
+static char *                  /* == stop (success) always */
+dissect(m, start, stop, startst, stopst)
+register struct match *m;
+char *start;
+char *stop;
+sopno startst;
+sopno stopst;
+{
+       register int i;
+       register sopno ss;      /* start sop of current subRE */
+       register sopno es;      /* end sop of current subRE */
+       register char *sp;      /* start of string matched by it */
+       register char *stp;     /* string matched by it cannot pass here */
+       register char *rest;    /* start of rest of string */
+       register char *tail;    /* string unmatched by rest of RE */
+       register sopno ssub;    /* start sop of subsubRE */
+       register sopno esub;    /* end sop of subsubRE */
+       register char *ssp;     /* start of string matched by subsubRE */
+       register char *sep;     /* end of string matched by subsubRE */
+       register char *oldssp;  /* previous ssp */
+       register char *dp;
+
+       AT("diss", start, stop, startst, stopst);
+       sp = start;
+       for (ss = startst; ss < stopst; ss = es) {
+               /* identify end of subRE */
+               es = ss;
+               switch (OP(m->g->strip[es])) {
+               case OPLUS_:
+               case OQUEST_:
+                       es += OPND(m->g->strip[es]);
+                       break;
+               case OCH_:
+                       while (OP(m->g->strip[es]) != O_CH)
+                               es += OPND(m->g->strip[es]);
+                       break;
+               }
+               es++;
+
+               /* figure out what it matched */
+               switch (OP(m->g->strip[ss])) {
+               case OEND:
+                       assert(nope);
+                       break;
+               case OCHAR:
+                       sp++;
+                       break;
+               case OBOL:
+               case OEOL:
+               case OBOW:
+               case OEOW:
+                       break;
+               case OANY:
+               case OANYOF:
+                       sp++;
+                       break;
+               case OBACK_:
+               case O_BACK:
+                       assert(nope);
+                       break;
+               /* cases where length of match is hard to find */
+               case OQUEST_:
+                       stp = stop;
+                       for (;;) {
+                               /* how long could this one be? */
+                               rest = slow(m, sp, stp, ss, es);
+                               assert(rest != NULL);   /* it did match */
+                               /* could the rest match the rest? */
+                               tail = slow(m, rest, stop, es, stopst);
+                               if (tail == stop)
+                                       break;          /* yes! */
+                               /* no -- try a shorter match for this one */
+                               stp = rest - 1;
+                               assert(stp >= sp);      /* it did work */
+                       }
+                       ssub = ss + 1;
+                       esub = es - 1;
+                       /* did innards match? */
+                       if (slow(m, sp, rest, ssub, esub) != NULL) {
+                               dp = dissect(m, sp, rest, ssub, esub);
+                               assert(dp == rest);
+                       } else          /* no */
+                               assert(sp == rest);
+                       sp = rest;
+                       break;
+               case OPLUS_:
+                       stp = stop;
+                       for (;;) {
+                               /* how long could this one be? */
+                               rest = slow(m, sp, stp, ss, es);
+                               assert(rest != NULL);   /* it did match */
+                               /* could the rest match the rest? */
+                               tail = slow(m, rest, stop, es, stopst);
+                               if (tail == stop)
+                                       break;          /* yes! */
+                               /* no -- try a shorter match for this one */
+                               stp = rest - 1;
+                               assert(stp >= sp);      /* it did work */
+                       }
+                       ssub = ss + 1;
+                       esub = es - 1;
+                       ssp = sp;
+                       oldssp = ssp;
+                       for (;;) {      /* find last match of innards */
+                               sep = slow(m, ssp, rest, ssub, esub);
+                               if (sep == NULL || sep == ssp)
+                                       break;  /* failed or matched null */
+                               oldssp = ssp;   /* on to next try */
+                               ssp = sep;
+                       }
+                       if (sep == NULL) {
+                               /* last successful match */
+                               sep = ssp;
+                               ssp = oldssp;
+                       }
+                       assert(sep == rest);    /* must exhaust substring */
+                       assert(slow(m, ssp, sep, ssub, esub) == rest);
+                       dp = dissect(m, ssp, sep, ssub, esub);
+                       assert(dp == sep);
+                       sp = rest;
+                       break;
+               case OCH_:
+                       stp = stop;
+                       for (;;) {
+                               /* how long could this one be? */
+                               rest = slow(m, sp, stp, ss, es);
+                               assert(rest != NULL);   /* it did match */
+                               /* could the rest match the rest? */
+                               tail = slow(m, rest, stop, es, stopst);
+                               if (tail == stop)
+                                       break;          /* yes! */
+                               /* no -- try a shorter match for this one */
+                               stp = rest - 1;
+                               assert(stp >= sp);      /* it did work */
+                       }
+                       ssub = ss + 1;
+                       esub = ss + OPND(m->g->strip[ss]) - 1;
+                       assert(OP(m->g->strip[esub]) == OOR1);
+                       for (;;) {      /* find first matching branch */
+                               if (slow(m, sp, rest, ssub, esub) == rest)
+                                       break;  /* it matched all of it */
+                               /* that one missed, try next one */
+                               assert(OP(m->g->strip[esub]) == OOR1);
+                               esub++;
+                               assert(OP(m->g->strip[esub]) == OOR2);
+                               ssub = esub + 1;
+                               esub += OPND(m->g->strip[esub]);
+                               if (OP(m->g->strip[esub]) == OOR2)
+                                       esub--;
+                               else
+                                       assert(OP(m->g->strip[esub]) == O_CH);
+                       }
+                       dp = dissect(m, sp, rest, ssub, esub);
+                       assert(dp == rest);
+                       sp = rest;
+                       break;
+               case O_PLUS:
+               case O_QUEST:
+               case OOR1:
+               case OOR2:
+               case O_CH:
+                       assert(nope);
+                       break;
+               case OLPAREN:
+                       i = OPND(m->g->strip[ss]);
+                       assert(0 < i && i <= m->g->nsub);
+                       m->pmatch[i].rm_so = sp - m->offp;
+                       break;
+               case ORPAREN:
+                       i = OPND(m->g->strip[ss]);
+                       assert(0 < i && i <= m->g->nsub);
+                       m->pmatch[i].rm_eo = sp - m->offp;
+                       break;
+               default:                /* uh oh */
+                       assert(nope);
+                       break;
+               }
+       }
+
+       assert(sp == stop);
+       return(sp);
+}
+
+/*
+ - backref - figure out what matched what, figuring in back references
+ == static char *backref(register struct match *m, char *start, \
+ ==    char *stop, sopno startst, sopno stopst, sopno lev);
+ */
+static char *                  /* == stop (success) or NULL (failure) */
+backref(m, start, stop, startst, stopst, lev)
+register struct match *m;
+char *start;
+char *stop;
+sopno startst;
+sopno stopst;
+sopno lev;                     /* PLUS nesting level */
+{
+       register int i;
+       register sopno ss;      /* start sop of current subRE */
+       register char *sp;      /* start of string matched by it */
+       register sopno ssub;    /* start sop of subsubRE */
+       register sopno esub;    /* end sop of subsubRE */
+       register char *ssp;     /* start of string matched by subsubRE */
+       register char *dp;
+       register size_t len;
+       register int hard;
+       register sop s;
+       register regoff_t offsave;
+       register cset *cs;
+
+       AT("back", start, stop, startst, stopst);
+       sp = start;
+
+       /* get as far as we can with easy stuff */
+       hard = 0;
+       for (ss = startst; !hard && ss < stopst; ss++)
+               switch (OP(s = m->g->strip[ss])) {
+               case OCHAR:
+                       if (sp == stop || *sp++ != (char)OPND(s))
+                               return(NULL);
+                       break;
+               case OANY:
+                       if (sp == stop)
+                               return(NULL);
+                       sp++;
+                       break;
+               case OANYOF:
+                       cs = &m->g->sets[OPND(s)];
+                       if (sp == stop || !CHIN(cs, *sp++))
+                               return(NULL);
+                       break;
+               case OBOL:
+                       if ( (sp == m->beginp && !(m->eflags&REG_NOTBOL)) ||
+                                       (sp < m->endp && *(sp-1) == '\n' &&
+                                               (m->g->cflags&REG_NEWLINE)) )
+                               { /* yes */ }
+                       else
+                               return(NULL);
+                       break;
+               case OEOL:
+                       if ( (sp == m->endp && !(m->eflags&REG_NOTEOL)) ||
+                                       (sp < m->endp && *sp == '\n' &&
+                                               (m->g->cflags&REG_NEWLINE)) )
+                               { /* yes */ }
+                       else
+                               return(NULL);
+                       break;
+               case OBOW:
+                       if (( (sp == m->beginp && !(m->eflags&REG_NOTBOL)) ||
+                                       (sp < m->endp && *(sp-1) == '\n' &&
+                                               (m->g->cflags&REG_NEWLINE)) ||
+                                       (sp > m->beginp &&
+                                                       !ISWORD(*(sp-1))) ) &&
+                                       (sp < m->endp && ISWORD(*sp)) )
+                               { /* yes */ }
+                       else
+                               return(NULL);
+                       break;
+               case OEOW:
+                       if (( (sp == m->endp && !(m->eflags&REG_NOTEOL)) ||
+                                       (sp < m->endp && *sp == '\n' &&
+                                               (m->g->cflags&REG_NEWLINE)) ||
+                                       (sp < m->endp && !ISWORD(*sp)) ) &&
+                                       (sp > m->beginp && ISWORD(*(sp-1))) )
+                               { /* yes */ }
+                       else
+                               return(NULL);
+                       break;
+               case O_QUEST:
+                       break;
+               case OOR1:      /* matches null but needs to skip */
+                       ss++;
+                       s = m->g->strip[ss];
+                       do {
+                               assert(OP(s) == OOR2);
+                               ss += OPND(s);
+                       } while (OP(s = m->g->strip[ss]) != O_CH);
+                       /* note that the ss++ gets us past the O_CH */
+                       break;
+               default:        /* have to make a choice */
+                       hard = 1;
+                       break;
+               }
+       if (!hard) {            /* that was it! */
+               if (sp != stop)
+                       return(NULL);
+               return(sp);
+       }
+       ss--;                   /* adjust for the for's final increment */
+
+       /* the hard stuff */
+       AT("hard", sp, stop, ss, stopst);
+       s = m->g->strip[ss];
+       switch (OP(s)) {
+       case OBACK_:            /* the vilest depths */
+               i = OPND(s);
+               assert(0 < i && i <= m->g->nsub);
+               if (m->pmatch[i].rm_eo == -1)
+                       return(NULL);
+               assert(m->pmatch[i].rm_so != -1);
+               len = m->pmatch[i].rm_eo - m->pmatch[i].rm_so;
+               assert(stop - m->beginp >= len);
+               if (sp > stop - len)
+                       return(NULL);   /* not enough left to match */
+               ssp = m->offp + m->pmatch[i].rm_so;
+               if (memcmp(sp, ssp, len) != 0)
+                       return(NULL);
+               while (m->g->strip[ss] != SOP(O_BACK, i))
+                       ss++;
+               return(backref(m, sp+len, stop, ss+1, stopst, lev));
+               break;
+       case OQUEST_:           /* to null or not */
+               dp = backref(m, sp, stop, ss+1, stopst, lev);
+               if (dp != NULL)
+                       return(dp);     /* not */
+               return(backref(m, sp, stop, ss+OPND(s)+1, stopst, lev));
+               break;
+       case OPLUS_:
+               assert(m->lastpos != NULL);
+               assert(lev+1 <= m->g->nplus);
+               m->lastpos[lev+1] = sp;
+               return(backref(m, sp, stop, ss+1, stopst, lev+1));
+               break;
+       case O_PLUS:
+               if (sp == m->lastpos[lev])      /* last pass matched null */
+                       return(backref(m, sp, stop, ss+1, stopst, lev-1));
+               /* try another pass */
+               m->lastpos[lev] = sp;
+               dp = backref(m, sp, stop, ss-OPND(s)+1, stopst, lev);
+               if (dp == NULL)
+                       return(backref(m, sp, stop, ss+1, stopst, lev-1));
+               else
+                       return(dp);
+               break;
+       case OCH_:              /* find the right one, if any */
+               ssub = ss + 1;
+               esub = ss + OPND(s) - 1;
+               assert(OP(m->g->strip[esub]) == OOR1);
+               for (;;) {      /* find first matching branch */
+                       dp = backref(m, sp, stop, ssub, esub, lev);
+                       if (dp != NULL)
+                               return(dp);
+                       /* that one missed, try next one */
+                       if (OP(m->g->strip[esub]) == O_CH)
+                               return(NULL);   /* there is none */
+                       esub++;
+                       assert(OP(m->g->strip[esub]) == OOR2);
+                       ssub = esub + 1;
+                       esub += OPND(m->g->strip[esub]);
+                       if (OP(m->g->strip[esub]) == OOR2)
+                               esub--;
+                       else
+                               assert(OP(m->g->strip[esub]) == O_CH);
+               }
+               break;
+       case OLPAREN:           /* must undo assignment if rest fails */
+               i = OPND(s);
+               assert(0 < i && i <= m->g->nsub);
+               offsave = m->pmatch[i].rm_so;
+               m->pmatch[i].rm_so = sp - m->offp;
+               dp = backref(m, sp, stop, ss+1, stopst, lev);
+               if (dp != NULL)
+                       return(dp);
+               m->pmatch[i].rm_so = offsave;
+               return(NULL);
+               break;
+       case ORPAREN:           /* must undo assignment if rest fails */
+               i = OPND(s);
+               assert(0 < i && i <= m->g->nsub);
+               offsave = m->pmatch[i].rm_eo;
+               m->pmatch[i].rm_eo = sp - m->offp;
+               dp = backref(m, sp, stop, ss+1, stopst, lev);
+               if (dp != NULL)
+                       return(dp);
+               m->pmatch[i].rm_eo = offsave;
+               return(NULL);
+               break;
+       default:                /* uh oh */
+               assert(nope);
+               break;
+       }
+
+       /* "can't happen" */
+       assert(nope);
+       /* NOTREACHED */
+}
+
+/*
+ - fast - step through the string at top speed
+ == static char *fast(register struct match *m, char *start, \
+ ==    char *stop, sopno startst, sopno stopst);
+ */
+static char *                  /* where tentative match ended, or NULL */
+fast(m, start, stop, startst, stopst)
+register struct match *m;
+char *start;
+char *stop;
+sopno startst;
+sopno stopst;
+{
+       register states st = m->st;
+       register states fresh = m->fresh;
+       register states tmp = m->tmp;
+       register char *p = start;
+       register int c = (start == m->beginp) ? OUT : *(start-1);
+       register int lastc;     /* previous c */
+       register int flagch;
+       register int i;
+       register char *coldp;   /* last p after which no match was underway */
+
+       CLEAR(st);
+       SET1(st, startst);
+       st = step(m->g, startst, stopst, st, NOTHING, st);
+       ASSIGN(fresh, st);
+       SP("start", st, *p);
+       coldp = NULL;
+       for (;;) {
+               /* next character */
+               lastc = c;
+               c = (p == m->endp) ? OUT : *p;
+               if (EQ(st, fresh))
+                       coldp = p;
+
+               /* is there an EOL and/or BOL between lastc and c? */
+               flagch = '\0';
+               i = 0;
+               if ( (lastc == '\n' && m->g->cflags&REG_NEWLINE) ||
+                               (lastc == OUT && !(m->eflags&REG_NOTBOL)) ) {
+                       flagch = BOL;
+                       i = m->g->nbol;
+               }
+               if ( (c == '\n' && m->g->cflags&REG_NEWLINE) ||
+                               (c == OUT && !(m->eflags&REG_NOTEOL)) ) {
+                       flagch = (flagch == BOL) ? BOLEOL : EOL;
+                       i += m->g->neol;
+               }
+               if (i != 0) {
+                       for (; i > 0; i--)
+                               st = step(m->g, startst, stopst, st, flagch, st);
+                       SP("boleol", st, c);
+               }
+
+               /* how about a word boundary? */
+               if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) &&
+                                       (c != OUT && ISWORD(c)) ) {
+                       flagch = BOW;
+               }
+               if ( (lastc != OUT && ISWORD(lastc)) &&
+                               (flagch == EOL || (c != OUT && !ISWORD(c))) ) {
+                       flagch = EOW;
+               }
+               if (flagch == BOW || flagch == EOW) {
+                       st = step(m->g, startst, stopst, st, flagch, st);
+                       SP("boweow", st, c);
+               }
+
+               /* are we done? */
+               if (ISSET(st, stopst) || p == stop)
+                       break;          /* NOTE BREAK OUT */
+
+               /* no, we must deal with this character */
+               ASSIGN(tmp, st);
+               ASSIGN(st, fresh);
+               assert(c != OUT);
+               st = step(m->g, startst, stopst, tmp, c, st);
+               SP("aft", st, c);
+               assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st));
+               p++;
+       }
+
+       assert(coldp != NULL);
+       m->coldp = coldp;
+       if (ISSET(st, stopst))
+               return(p+1);
+       else
+               return(NULL);
+}
+
+/*
+ - slow - step through the string more deliberately
+ == static char *slow(register struct match *m, char *start, \
+ ==    char *stop, sopno startst, sopno stopst);
+ */
+static char *                  /* where it ended */
+slow(m, start, stop, startst, stopst)
+register struct match *m;
+char *start;
+char *stop;
+sopno startst;
+sopno stopst;
+{
+       register states st = m->st;
+       register states empty = m->empty;
+       register states tmp = m->tmp;
+       register char *p = start;
+       register int c = (start == m->beginp) ? OUT : *(start-1);
+       register int lastc;     /* previous c */
+       register int flagch;
+       register int i;
+       register char *matchp;  /* last p at which a match ended */
+
+       AT("slow", start, stop, startst, stopst);
+       CLEAR(st);
+       SET1(st, startst);
+       SP("sstart", st, *p);
+       st = step(m->g, startst, stopst, st, NOTHING, st);
+       matchp = NULL;
+       for (;;) {
+               /* next character */
+               lastc = c;
+               c = (p == m->endp) ? OUT : *p;
+
+               /* is there an EOL and/or BOL between lastc and c? */
+               flagch = '\0';
+               i = 0;
+               if ( (lastc == '\n' && m->g->cflags&REG_NEWLINE) ||
+                               (lastc == OUT && !(m->eflags&REG_NOTBOL)) ) {
+                       flagch = BOL;
+                       i = m->g->nbol;
+               }
+               if ( (c == '\n' && m->g->cflags&REG_NEWLINE) ||
+                               (c == OUT && !(m->eflags&REG_NOTEOL)) ) {
+                       flagch = (flagch == BOL) ? BOLEOL : EOL;
+                       i += m->g->neol;
+               }
+               if (i != 0) {
+                       for (; i > 0; i--)
+                               st = step(m->g, startst, stopst, st, flagch, st);
+                       SP("sboleol", st, c);
+               }
+
+               /* how about a word boundary? */
+               if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) &&
+                                       (c != OUT && ISWORD(c)) ) {
+                       flagch = BOW;
+               }
+               if ( (lastc != OUT && ISWORD(lastc)) &&
+                               (flagch == EOL || (c != OUT && !ISWORD(c))) ) {
+                       flagch = EOW;
+               }
+               if (flagch == BOW || flagch == EOW) {
+                       st = step(m->g, startst, stopst, st, flagch, st);
+                       SP("sboweow", st, c);
+               }
+
+               /* are we done? */
+               if (ISSET(st, stopst))
+                       matchp = p;
+               if (EQ(st, empty) || p == stop)
+                       break;          /* NOTE BREAK OUT */
+
+               /* no, we must deal with this character */
+               ASSIGN(tmp, st);
+               ASSIGN(st, empty);
+               assert(c != OUT);
+               st = step(m->g, startst, stopst, tmp, c, st);
+               SP("saft", st, c);
+               assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st));
+               p++;
+       }
+
+       return(matchp);
+}
+
+
+/*
+ - step - map set of states reachable before char to set reachable after
+ == static states step(register struct re_guts *g, sopno start, sopno stop, \
+ ==    register states bef, int ch, register states aft);
+ == #define    BOL     (OUT+1)
+ == #define    EOL     (BOL+1)
+ == #define    BOLEOL  (BOL+2)
+ == #define    NOTHING (BOL+3)
+ == #define    BOW     (BOL+4)
+ == #define    EOW     (BOL+5)
+ == #define    CODEMAX (BOL+5)         // highest code used
+ == #define    NONCHAR(c)      ((c) > CHAR_MAX)
+ == #define    NNONCHAR        (CODEMAX-CHAR_MAX)
+ */
+static states
+step(g, start, stop, bef, ch, aft)
+register struct re_guts *g;
+sopno start;                   /* start state within strip */
+sopno stop;                    /* state after stop state within strip */
+register states bef;           /* states reachable before */
+int ch;                                /* character or NONCHAR code */
+register states aft;           /* states already known reachable after */
+{
+       register cset *cs;
+       register sop s;
+       register sopno pc;
+       register onestate here;         /* note, macros know this name */
+       register sopno look;
+       register int i;
+
+       for (pc = start, INIT(here, pc); pc != stop; pc++, INC(here)) {
+               s = g->strip[pc];
+               switch (OP(s)) {
+               case OEND:
+                       assert(pc == stop-1);
+                       break;
+               case OCHAR:
+                       /* only characters can match */
+                       assert(!NONCHAR(ch) || ch != (char)OPND(s));
+                       if (ch == (char)OPND(s))
+                               FWD(aft, bef, 1);
+                       break;
+               case OBOL:
+                       if (ch == BOL || ch == BOLEOL)
+                               FWD(aft, bef, 1);
+                       break;
+               case OEOL:
+                       if (ch == EOL || ch == BOLEOL)
+                               FWD(aft, bef, 1);
+                       break;
+               case OBOW:
+                       if (ch == BOW)
+                               FWD(aft, bef, 1);
+                       break;
+               case OEOW:
+                       if (ch == EOW)
+                               FWD(aft, bef, 1);
+                       break;
+               case OANY:
+                       if (!NONCHAR(ch))
+                               FWD(aft, bef, 1);
+                       break;
+               case OANYOF:
+                       cs = &g->sets[OPND(s)];
+                       if (!NONCHAR(ch) && CHIN(cs, ch))
+                               FWD(aft, bef, 1);
+                       break;
+               case OBACK_:            /* ignored here */
+               case O_BACK:
+                       FWD(aft, aft, 1);
+                       break;
+               case OPLUS_:            /* forward, this is just an empty */
+                       FWD(aft, aft, 1);
+                       break;
+               case O_PLUS:            /* both forward and back */
+                       FWD(aft, aft, 1);
+                       i = ISSETBACK(aft, OPND(s));
+                       BACK(aft, aft, OPND(s));
+                       if (!i && ISSETBACK(aft, OPND(s))) {
+                               /* oho, must reconsider loop body */
+                               pc -= OPND(s) + 1;
+                               INIT(here, pc);
+                       }
+                       break;
+               case OQUEST_:           /* two branches, both forward */
+                       FWD(aft, aft, 1);
+                       FWD(aft, aft, OPND(s));
+                       break;
+               case O_QUEST:           /* just an empty */
+                       FWD(aft, aft, 1);
+                       break;
+               case OLPAREN:           /* not significant here */
+               case ORPAREN:
+                       FWD(aft, aft, 1);
+                       break;
+               case OCH_:              /* mark the first two branches */
+                       FWD(aft, aft, 1);
+                       assert(OP(g->strip[pc+OPND(s)]) == OOR2);
+                       FWD(aft, aft, OPND(s));
+                       break;
+               case OOR1:              /* done a branch, find the O_CH */
+                       if (ISSTATEIN(aft, here)) {
+                               for (look = 1;
+                                               OP(s = g->strip[pc+look]) != O_CH;
+                                               look += OPND(s))
+                                       assert(OP(s) == OOR2);
+                               FWD(aft, aft, look);
+                       }
+                       break;
+               case OOR2:              /* propagate OCH_'s marking */
+                       FWD(aft, aft, 1);
+                       if (OP(g->strip[pc+OPND(s)]) != O_CH) {
+                               assert(OP(g->strip[pc+OPND(s)]) == OOR2);
+                               FWD(aft, aft, OPND(s));
+                       }
+                       break;
+               case O_CH:              /* just empty */
+                       FWD(aft, aft, 1);
+                       break;
+               default:                /* ooooops... */
+                       assert(nope);
+                       break;
+               }
+       }
+
+       return(aft);
+}
+
+#ifdef REDEBUG
+/*
+ - print - print a set of states
+ == #ifdef REDEBUG
+ == static void print(struct match *m, char *caption, states st, \
+ ==    int ch, FILE *d);
+ == #endif
+ */
+static void
+print(m, caption, st, ch, d)
+struct match *m;
+char *caption;
+states st;
+int ch;
+FILE *d;
+{
+       register struct re_guts *g = m->g;
+       register int i;
+       register int first = 1;
+
+       if (!(m->eflags&REG_TRACE))
+               return;
+
+       fprintf(d, "%s", caption);
+       if (ch != '\0')
+               fprintf(d, " %s", pchar(ch));
+       for (i = 0; i < g->nstates; i++)
+               if (ISSET(st, i)) {
+                       fprintf(d, "%s%d", (first) ? "\t" : ", ", i);
+                       first = 0;
+               }
+       fprintf(d, "\n");
+}
+
+/* 
+ - at - print current situation
+ == #ifdef REDEBUG
+ == static void at(struct match *m, char *title, char *start, char *stop, \
+ ==                                            sopno startst, sopno stopst);
+ == #endif
+ */
+static void
+at(m, title, start, stop, startst, stopst)
+struct match *m;
+char *title;
+char *start;
+char *stop;
+sopno startst;
+sopno stopst;
+{
+       if (!(m->eflags&REG_TRACE))
+               return;
+
+       printf("%s %s-", title, pchar(*start));
+       printf("%s ", pchar(*stop));
+       printf("%ld-%ld\n", (long)startst, (long)stopst);
+}
+
+#ifndef PCHARDONE
+#define        PCHARDONE       /* never again */
+/*
+ - pchar - make a character printable
+ == #ifdef REDEBUG
+ == static char *pchar(int ch);
+ == #endif
+ *
+ * Is this identical to regchar() over in debug.c?  Well, yes.  But a
+ * duplicate here avoids having a debugging-capable regexec.o tied to
+ * a matching debug.o, and this is convenient.  It all disappears in
+ * the non-debug compilation anyway, so it doesn't matter much.
+ */
+static char *                  /* -> representation */
+pchar(ch)
+int ch;
+{
+       static char pbuf[10];
+
+       if (isprint(ch) || ch == ' ')
+               sprintf(pbuf, "%c", ch);
+       else
+               sprintf(pbuf, "\\%o", ch);
+       return(pbuf);
+}
+#endif
+#endif
+
+#undef matcher
+#undef fast
+#undef slow
+#undef dissect
+#undef backref
+#undef step
+#undef print
+#undef at
+#undef match
diff --git a/src/backend/port/win32/regex/re_format.7 b/src/backend/port/win32/regex/re_format.7
new file mode 100644 (file)
index 0000000..db2f634
--- /dev/null
@@ -0,0 +1,269 @@
+.\" Copyright (c) 1992, 1993, 1994 Henry Spencer.
+.\" Copyright (c) 1992, 1993, 1994
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Henry Spencer.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"    @(#)re_format.7 8.3 (Berkeley) 3/20/94
+.\"
+.TH RE_FORMAT 7 "March 20, 1994"
+.SH NAME
+re_format \- POSIX 1003.2 regular expressions
+.SH DESCRIPTION
+Regular expressions (``RE''s),
+as defined in POSIX 1003.2, come in two forms:
+modern REs (roughly those of
+.IR egrep ;
+1003.2 calls these ``extended'' REs)
+and obsolete REs (roughly those of
+.IR ed ;
+1003.2 ``basic'' REs).
+Obsolete REs mostly exist for backward compatibility in some old programs;
+they will be discussed at the end.
+1003.2 leaves some aspects of RE syntax and semantics open;
+`\(dg' marks decisions on these aspects that
+may not be fully portable to other 1003.2 implementations.
+.PP
+A (modern) RE is one\(dg or more non-empty\(dg \fIbranches\fR,
+separated by `|'.
+It matches anything that matches one of the branches.
+.PP
+A branch is one\(dg or more \fIpieces\fR, concatenated.
+It matches a match for the first, followed by a match for the second, etc.
+.PP
+A piece is an \fIatom\fR possibly followed
+by a single\(dg `*', `+', `?', or \fIbound\fR.
+An atom followed by `*' matches a sequence of 0 or more matches of the atom.
+An atom followed by `+' matches a sequence of 1 or more matches of the atom.
+An atom followed by `?' matches a sequence of 0 or 1 matches of the atom.
+.PP
+A \fIbound\fR is `{' followed by an unsigned decimal integer,
+possibly followed by `,'
+possibly followed by another unsigned decimal integer,
+always followed by `}'.
+The integers must lie between 0 and RE_DUP_MAX (255\(dg) inclusive,
+and if there are two of them, the first may not exceed the second.
+An atom followed by a bound containing one integer \fIi\fR
+and no comma matches
+a sequence of exactly \fIi\fR matches of the atom.
+An atom followed by a bound
+containing one integer \fIi\fR and a comma matches
+a sequence of \fIi\fR or more matches of the atom.
+An atom followed by a bound
+containing two integers \fIi\fR and \fIj\fR matches
+a sequence of \fIi\fR through \fIj\fR (inclusive) matches of the atom.
+.PP
+An atom is a regular expression enclosed in `()' (matching a match for the
+regular expression),
+an empty set of `()' (matching the null string)\(dg,
+a \fIbracket expression\fR (see below), `.'
+(matching any single character), `^' (matching the null string at the
+beginning of a line), `$' (matching the null string at the
+end of a line), a `\e' followed by one of the characters
+`^.[$()|*+?{\e'
+(matching that character taken as an ordinary character),
+a `\e' followed by any other character\(dg
+(matching that character taken as an ordinary character,
+as if the `\e' had not been present\(dg),
+or a single character with no other significance (matching that character).
+A `{' followed by a character other than a digit is an ordinary
+character, not the beginning of a bound\(dg.
+It is illegal to end an RE with `\e'.
+.PP
+A \fIbracket expression\fR is a list of characters enclosed in `[]'.
+It normally matches any single character from the list (but see below).
+If the list begins with `^',
+it matches any single character
+(but see below) \fInot\fR from the rest of the list.
+If two characters in the list are separated by `\-', this is shorthand
+for the full \fIrange\fR of characters between those two (inclusive) in the
+collating sequence,
+e.g. `[0-9]' in ASCII matches any decimal digit.
+It is illegal\(dg for two ranges to share an
+endpoint, e.g. `a-c-e'.
+Ranges are very collating-sequence-dependent,
+and portable programs should avoid relying on them.
+.PP
+To include a literal `]' in the list, make it the first character
+(following a possible `^').
+To include a literal `\-', make it the first or last character,
+or the second endpoint of a range.
+To use a literal `\-' as the first endpoint of a range,
+enclose it in `[.' and `.]' to make it a collating element (see below).
+With the exception of these and some combinations using `[' (see next
+paragraphs), all other special characters, including `\e', lose their
+special significance within a bracket expression.
+.PP
+Within a bracket expression, a collating element (a character,
+a multi-character sequence that collates as if it were a single character,
+or a collating-sequence name for either)
+enclosed in `[.' and `.]' stands for the
+sequence of characters of that collating element.
+The sequence is a single element of the bracket expression's list.
+A bracket expression containing a multi-character collating element 
+can thus match more than one character,
+e.g. if the collating sequence includes a `ch' collating element,
+then the RE `[[.ch.]]*c' matches the first five characters
+of `chchcc'.
+.PP
+Within a bracket expression, a collating element enclosed in `[=' and
+`=]' is an equivalence class, standing for the sequences of characters
+of all collating elements equivalent to that one, including itself.
+(If there are no other equivalent collating elements,
+the treatment is as if the enclosing delimiters were `[.' and `.]'.)
+For example, if o and \o'o^' are the members of an equivalence class,
+then `[[=o=]]', `[[=\o'o^'=]]', and `[o\o'o^']' are all synonymous.
+An equivalence class may not\(dg be an endpoint
+of a range.
+.PP
+Within a bracket expression, the name of a \fIcharacter class\fR enclosed
+in `[:' and `:]' stands for the list of all characters belonging to that
+class.
+Standard character class names are:
+.PP
+.RS
+.nf
+.ta 3c 6c 9c
+alnum  digit   punct
+alpha  graph   space
+blank  lower   upper
+cntrl  print   xdigit
+.fi
+.RE
+.PP
+These stand for the character classes defined in
+.IR ctype (3).
+A locale may provide others.
+A character class may not be used as an endpoint of a range.
+.PP
+There are two special cases\(dg of bracket expressions:
+the bracket expressions `[[:<:]]' and `[[:>:]]' match the null string at
+the beginning and end of a word respectively.
+A word is defined as a sequence of
+word characters
+which is neither preceded nor followed by
+word characters.
+A word character is an
+.I alnum
+character (as defined by
+.IR ctype (3))
+or an underscore.
+This is an extension,
+compatible with but not specified by POSIX 1003.2,
+and should be used with
+caution in software intended to be portable to other systems.
+.PP
+In the event that an RE could match more than one substring of a given
+string,
+the RE matches the one starting earliest in the string.
+If the RE could match more than one substring starting at that point,
+it matches the longest.
+Subexpressions also match the longest possible substrings, subject to
+the constraint that the whole match be as long as possible,
+with subexpressions starting earlier in the RE taking priority over
+ones starting later.
+Note that higher-level subexpressions thus take priority over
+their lower-level component subexpressions.
+.PP
+Match lengths are measured in characters, not collating elements.
+A null string is considered longer than no match at all.
+For example,
+`bb*' matches the three middle characters of `abbbc',
+`(wee|week)(knights|nights)' matches all ten characters of `weeknights',
+when `(.*).*' is matched against `abc' the parenthesized subexpression
+matches all three characters, and
+when `(a*)*' is matched against `bc' both the whole RE and the parenthesized
+subexpression match the null string.
+.PP
+If case-independent matching is specified,
+the effect is much as if all case distinctions had vanished from the
+alphabet.
+When an alphabetic that exists in multiple cases appears as an
+ordinary character outside a bracket expression, it is effectively
+transformed into a bracket expression containing both cases,
+e.g. `x' becomes `[xX]'.
+When it appears inside a bracket expression, all case counterparts
+of it are added to the bracket expression, so that (e.g.) `[x]'
+becomes `[xX]' and `[^x]' becomes `[^xX]'.
+.PP
+No particular limit is imposed on the length of REs\(dg.
+Programs intended to be portable should not employ REs longer
+than 256 bytes,
+as an implementation can refuse to accept such REs and remain
+POSIX-compliant.
+.PP
+Obsolete (``basic'') regular expressions differ in several respects.
+`|', `+', and `?' are ordinary characters and there is no equivalent
+for their functionality.
+The delimiters for bounds are `\e{' and `\e}',
+with `{' and `}' by themselves ordinary characters.
+The parentheses for nested subexpressions are `\e(' and `\e)',
+with `(' and `)' by themselves ordinary characters.
+`^' is an ordinary character except at the beginning of the
+RE or\(dg the beginning of a parenthesized subexpression,
+`$' is an ordinary character except at the end of the
+RE or\(dg the end of a parenthesized subexpression,
+and `*' is an ordinary character if it appears at the beginning of the
+RE or the beginning of a parenthesized subexpression
+(after a possible leading `^').
+Finally, there is one new type of atom, a \fIback reference\fR:
+`\e' followed by a non-zero decimal digit \fId\fR
+matches the same sequence of characters
+matched by the \fId\fRth parenthesized subexpression
+(numbering subexpressions by the positions of their opening parentheses,
+left to right),
+so that (e.g.) `\e([bc]\e)\e1' matches `bb' or `cc' but not `bc'.
+.SH SEE ALSO
+regex(3)
+.PP
+POSIX 1003.2, section 2.8 (Regular Expression Notation).
+.SH BUGS
+Having two kinds of REs is a botch.
+.PP
+The current 1003.2 spec says that `)' is an ordinary character in
+the absence of an unmatched `(';
+this was an unintentional result of a wording error,
+and change is likely.
+Avoid relying on it.
+.PP
+Back references are a dreadful botch,
+posing major problems for efficient implementations.
+They are also somewhat vaguely defined
+(does
+`a\e(\e(b\e)*\e2\e)*d' match `abbbd'?).
+Avoid using them.
+.PP
+1003.2's specification of case-independent matching is vague.
+The ``one case implies all cases'' definition given above
+is current consensus among implementors as to the right interpretation.
+.PP
+The syntax for word boundaries is incredibly ugly.
diff --git a/src/backend/port/win32/regex/regcomp.c b/src/backend/port/win32/regex/regcomp.c
new file mode 100644 (file)
index 0000000..feb24ec
--- /dev/null
@@ -0,0 +1,1698 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)regcomp.c   8.5 (Berkeley) 3/20/94
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)regcomp.c  8.5 (Berkeley) 3/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <regex.h>
+
+#include "utils.h"
+#include "regex2.h"
+
+#include "cclass.h"
+#include "cname.h"
+
+/*
+ * parse structure, passed up and down to avoid global variables and
+ * other clumsinesses
+ */
+struct parse {
+       char *next;             /* next character in RE */
+       char *end;              /* end of string (-> NUL normally) */
+       int error;              /* has an error been seen? */
+       sop *strip;             /* malloced strip */
+       sopno ssize;            /* malloced strip size (allocated) */
+       sopno slen;             /* malloced strip length (used) */
+       int ncsalloc;           /* number of csets allocated */
+       struct re_guts *g;
+#      define  NPAREN  10      /* we need to remember () 1-9 for back refs */
+       sopno pbegin[NPAREN];   /* -> ( ([0] unused) */
+       sopno pend[NPAREN];     /* -> ) ([0] unused) */
+};
+
+/* ========= begin header generated by ./mkh ========= */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* === regcomp.c === */
+static void p_ere __P((struct parse *p, int stop));
+static void p_ere_exp __P((struct parse *p));
+static void p_str __P((struct parse *p));
+static void p_bre __P((struct parse *p, int end1, int end2));
+static int p_simp_re __P((struct parse *p, int starordinary));
+static int p_count __P((struct parse *p));
+static void p_bracket __P((struct parse *p));
+static void p_b_term __P((struct parse *p, cset *cs));
+static void p_b_cclass __P((struct parse *p, cset *cs));
+static void p_b_eclass __P((struct parse *p, cset *cs));
+static char p_b_symbol __P((struct parse *p));
+static char p_b_coll_elem __P((struct parse *p, int endc));
+static char othercase __P((int ch));
+static void bothcases __P((struct parse *p, int ch));
+static void ordinary __P((struct parse *p, int ch));
+static void nonnewline __P((struct parse *p));
+static void repeat __P((struct parse *p, sopno start, int from, int to));
+static int seterr __P((struct parse *p, int e));
+static cset *allocset __P((struct parse *p));
+static void freeset __P((struct parse *p, cset *cs));
+static int freezeset __P((struct parse *p, cset *cs));
+static int firstch __P((struct parse *p, cset *cs));
+static int nch __P((struct parse *p, cset *cs));
+static void mcadd __P((struct parse *p, cset *cs, char *cp));
+static void mcsub __P((cset *cs, char *cp));
+static int mcin __P((cset *cs, char *cp));
+static char *mcfind __P((cset *cs, char *cp));
+static void mcinvert __P((struct parse *p, cset *cs));
+static void mccase __P((struct parse *p, cset *cs));
+static int isinsets __P((struct re_guts *g, int c));
+static int samesets __P((struct re_guts *g, int c1, int c2));
+static void categorize __P((struct parse *p, struct re_guts *g));
+static sopno dupl __P((struct parse *p, sopno start, sopno finish));
+static void doemit __P((struct parse *p, sop op, size_t opnd));
+static void doinsert __P((struct parse *p, sop op, size_t opnd, sopno pos));
+static void dofwd __P((struct parse *p, sopno pos, sop value));
+static void enlarge __P((struct parse *p, sopno size));
+static void stripsnug __P((struct parse *p, struct re_guts *g));
+static void findmust __P((struct parse *p, struct re_guts *g));
+static sopno pluscount __P((struct parse *p, struct re_guts *g));
+
+#ifdef __cplusplus
+}
+#endif
+/* ========= end header generated by ./mkh ========= */
+
+static char nuls[10];          /* place to point scanner in event of error */
+
+/*
+ * macros for use with parse structure
+ * BEWARE:  these know that the parse structure is named `p' !!!
+ */
+#define        PEEK()  (*p->next)
+#define        PEEK2() (*(p->next+1))
+#define        MORE()  (p->next < p->end)
+#define        MORE2() (p->next+1 < p->end)
+#define        SEE(c)  (MORE() && PEEK() == (c))
+#define        SEETWO(a, b)    (MORE() && MORE2() && PEEK() == (a) && PEEK2() == (b))
+#define        EAT(c)  ((SEE(c)) ? (NEXT(), 1) : 0)
+#define        EATTWO(a, b)    ((SEETWO(a, b)) ? (NEXT2(), 1) : 0)
+#define        NEXT()  (p->next++)
+#define        NEXT2() (p->next += 2)
+#define        NEXTn(n)        (p->next += (n))
+#define        GETNEXT()       (*p->next++)
+#define        SETERROR(e)     seterr(p, (e))
+#define        REQUIRE(co, e)  ((co) || SETERROR(e))
+#define        MUSTSEE(c, e)   (REQUIRE(MORE() && PEEK() == (c), e))
+#define        MUSTEAT(c, e)   (REQUIRE(MORE() && GETNEXT() == (c), e))
+#define        MUSTNOTSEE(c, e)        (REQUIRE(!MORE() || PEEK() != (c), e))
+#define        EMIT(op, sopnd) doemit(p, (sop)(op), (size_t)(sopnd))
+#define        INSERT(op, pos) doinsert(p, (sop)(op), HERE()-(pos)+1, pos)
+#define        AHEAD(pos)              dofwd(p, pos, HERE()-(pos))
+#define        ASTERN(sop, pos)        EMIT(sop, HERE()-pos)
+#define        HERE()          (p->slen)
+#define        THERE()         (p->slen - 1)
+#define        THERETHERE()    (p->slen - 2)
+#define        DROP(n) (p->slen -= (n))
+
+#ifndef NDEBUG
+static int never = 0;          /* for use in asserts; shuts lint up */
+#else
+#define        never   0               /* some <assert.h>s have bugs too */
+#endif
+
+/*
+ - regcomp - interface for parser and compilation
+ = extern int regcomp(regex_t *, const char *, int);
+ = #define     REG_BASIC       0000
+ = #define     REG_EXTENDED    0001
+ = #define     REG_ICASE       0002
+ = #define     REG_NOSUB       0004
+ = #define     REG_NEWLINE     0010
+ = #define     REG_NOSPEC      0020
+ = #define     REG_PEND        0040
+ = #define     REG_DUMP        0200
+ */
+int                            /* 0 success, otherwise REG_something */
+regcomp(preg, pattern, cflags)
+regex_t *preg;
+const char *pattern;
+int cflags;
+{
+       struct parse pa;
+       register struct re_guts *g;
+       register struct parse *p = &pa;
+       register int i;
+       register size_t len;
+#ifdef REDEBUG
+#      define  GOODFLAGS(f)    (f)
+#else
+#      define  GOODFLAGS(f)    ((f)&~REG_DUMP)
+#endif
+
+       cflags = GOODFLAGS(cflags);
+       if ((cflags&REG_EXTENDED) && (cflags&REG_NOSPEC))
+               return(REG_INVARG);
+
+       if (cflags&REG_PEND) {
+               if (preg->re_endp < pattern)
+                       return(REG_INVARG);
+               len = preg->re_endp - pattern;
+       } else
+               len = strlen((char *)pattern);
+
+       /* do the mallocs early so failure handling is easy */
+       g = (struct re_guts *)malloc(sizeof(struct re_guts) +
+                                                       (NC-1)*sizeof(cat_t));
+       if (g == NULL)
+               return(REG_ESPACE);
+       p->ssize = len/(size_t)2*(size_t)3 + (size_t)1; /* ugh */
+       p->strip = (sop *)malloc(p->ssize * sizeof(sop));
+       p->slen = 0;
+       if (p->strip == NULL) {
+               free((char *)g);
+               return(REG_ESPACE);
+       }
+
+       /* set things up */
+       p->g = g;
+       p->next = (char *)pattern;      /* convenience; we do not modify it */
+       p->end = p->next + len;
+       p->error = 0;
+       p->ncsalloc = 0;
+       for (i = 0; i < NPAREN; i++) {
+               p->pbegin[i] = 0;
+               p->pend[i] = 0;
+       }
+       g->csetsize = NC;
+       g->sets = NULL;
+       g->setbits = NULL;
+       g->ncsets = 0;
+       g->cflags = cflags;
+       g->iflags = 0;
+       g->nbol = 0;
+       g->neol = 0;
+       g->must = NULL;
+       g->mlen = 0;
+       g->nsub = 0;
+       g->ncategories = 1;     /* category 0 is "everything else" */
+       g->categories = &g->catspace[-(CHAR_MIN)];
+       (void) memset((char *)g->catspace, 0, NC*sizeof(cat_t));
+       g->backrefs = 0;
+
+       /* do it */
+       EMIT(OEND, 0);
+       g->firststate = THERE();
+       if (cflags&REG_EXTENDED)
+               p_ere(p, OUT);
+       else if (cflags&REG_NOSPEC)
+               p_str(p);
+       else
+               p_bre(p, OUT, OUT);
+       EMIT(OEND, 0);
+       g->laststate = THERE();
+
+       /* tidy up loose ends and fill things in */
+       categorize(p, g);
+       stripsnug(p, g);
+       findmust(p, g);
+       g->nplus = pluscount(p, g);
+       g->magic = MAGIC2;
+       preg->re_nsub = g->nsub;
+       preg->re_g = g;
+       preg->re_magic = MAGIC1;
+#ifndef REDEBUG
+       /* not debugging, so can't rely on the assert() in regexec() */
+       if (g->iflags&BAD)
+               SETERROR(REG_ASSERT);
+#endif
+
+       /* win or lose, we're done */
+       if (p->error != 0)      /* lose */
+               regfree(preg);
+       return(p->error);
+}
+
+/*
+ - p_ere - ERE parser top level, concatenation and alternation
+ == static void p_ere(register struct parse *p, int stop);
+ */
+static void
+p_ere(p, stop)
+register struct parse *p;
+int stop;                      /* character this ERE should end at */
+{
+       register char c;
+       register sopno prevback;
+       register sopno prevfwd;
+       register sopno conc;
+       register int first = 1;         /* is this the first alternative? */
+
+       for (;;) {
+               /* do a bunch of concatenated expressions */
+               conc = HERE();
+               while (MORE() && (c = PEEK()) != '|' && c != stop)
+                       p_ere_exp(p);
+               REQUIRE(HERE() != conc, REG_EMPTY);     /* require nonempty */
+
+               if (!EAT('|'))
+                       break;          /* NOTE BREAK OUT */
+
+               if (first) {
+                       INSERT(OCH_, conc);     /* offset is wrong */
+                       prevfwd = conc;
+                       prevback = conc;
+                       first = 0;
+               }
+               ASTERN(OOR1, prevback);
+               prevback = THERE();
+               AHEAD(prevfwd);                 /* fix previous offset */
+               prevfwd = HERE();
+               EMIT(OOR2, 0);                  /* offset is very wrong */
+       }
+
+       if (!first) {           /* tail-end fixups */
+               AHEAD(prevfwd);
+               ASTERN(O_CH, prevback);
+       }
+
+       assert(!MORE() || SEE(stop));
+}
+
+/*
+ - p_ere_exp - parse one subERE, an atom possibly followed by a repetition op
+ == static void p_ere_exp(register struct parse *p);
+ */
+static void
+p_ere_exp(p)
+register struct parse *p;
+{
+       register char c;
+       register sopno pos;
+       register int count;
+       register int count2;
+       register sopno subno;
+       int wascaret = 0;
+
+       assert(MORE());         /* caller should have ensured this */
+       c = GETNEXT();
+
+       pos = HERE();
+       switch (c) {
+       case '(':
+               REQUIRE(MORE(), REG_EPAREN);
+               p->g->nsub++;
+               subno = p->g->nsub;
+               if (subno < NPAREN)
+                       p->pbegin[subno] = HERE();
+               EMIT(OLPAREN, subno);
+               if (!SEE(')'))
+                       p_ere(p, ')');
+               if (subno < NPAREN) {
+                       p->pend[subno] = HERE();
+                       assert(p->pend[subno] != 0);
+               }
+               EMIT(ORPAREN, subno);
+               MUSTEAT(')', REG_EPAREN);
+               break;
+#ifndef POSIX_MISTAKE
+       case ')':               /* happens only if no current unmatched ( */
+               /*
+                * You may ask, why the ifndef?  Because I didn't notice
+                * this until slightly too late for 1003.2, and none of the
+                * other 1003.2 regular-expression reviewers noticed it at
+                * all.  So an unmatched ) is legal POSIX, at least until
+                * we can get it fixed.
+                */
+               SETERROR(REG_EPAREN);
+               break;
+#endif
+       case '^':
+               EMIT(OBOL, 0);
+               p->g->iflags |= USEBOL;
+               p->g->nbol++;
+               wascaret = 1;
+               break;
+       case '$':
+               EMIT(OEOL, 0);
+               p->g->iflags |= USEEOL;
+               p->g->neol++;
+               break;
+       case '|':
+               SETERROR(REG_EMPTY);
+               break;
+       case '*':
+       case '+':
+       case '?':
+               SETERROR(REG_BADRPT);
+               break;
+       case '.':
+               if (p->g->cflags&REG_NEWLINE)
+                       nonnewline(p);
+               else
+                       EMIT(OANY, 0);
+               break;
+       case '[':
+               p_bracket(p);
+               break;
+       case '\\':
+               REQUIRE(MORE(), REG_EESCAPE);
+               c = GETNEXT();
+               ordinary(p, c);
+               break;
+       case '{':               /* okay as ordinary except if digit follows */
+               REQUIRE(!MORE() || !isdigit(PEEK()), REG_BADRPT);
+               /* FALLTHROUGH */
+       default:
+               ordinary(p, c);
+               break;
+       }
+
+       if (!MORE())
+               return;
+       c = PEEK();
+       /* we call { a repetition if followed by a digit */
+       if (!( c == '*' || c == '+' || c == '?' ||
+                               (c == '{' && MORE2() && isdigit(PEEK2())) ))
+               return;         /* no repetition, we're done */
+       NEXT();
+
+       REQUIRE(!wascaret, REG_BADRPT);
+       switch (c) {
+       case '*':       /* implemented as +? */
+               /* this case does not require the (y|) trick, noKLUDGE */
+               INSERT(OPLUS_, pos);
+               ASTERN(O_PLUS, pos);
+               INSERT(OQUEST_, pos);
+               ASTERN(O_QUEST, pos);
+               break;
+       case '+':
+               INSERT(OPLUS_, pos);
+               ASTERN(O_PLUS, pos);
+               break;
+       case '?':
+               /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */
+               INSERT(OCH_, pos);              /* offset slightly wrong */
+               ASTERN(OOR1, pos);              /* this one's right */
+               AHEAD(pos);                     /* fix the OCH_ */
+               EMIT(OOR2, 0);                  /* offset very wrong... */
+               AHEAD(THERE());                 /* ...so fix it */
+               ASTERN(O_CH, THERETHERE());
+               break;
+       case '{':
+               count = p_count(p);
+               if (EAT(',')) {
+                       if (isdigit(PEEK())) {
+                               count2 = p_count(p);
+                               REQUIRE(count <= count2, REG_BADBR);
+                       } else          /* single number with comma */
+                               count2 = INFINITY;
+               } else          /* just a single number */
+                       count2 = count;
+               repeat(p, pos, count, count2);
+               if (!EAT('}')) {        /* error heuristics */
+                       while (MORE() && PEEK() != '}')
+                               NEXT();
+                       REQUIRE(MORE(), REG_EBRACE);
+                       SETERROR(REG_BADBR);
+               }
+               break;
+       }
+
+       if (!MORE())
+               return;
+       c = PEEK();
+       if (!( c == '*' || c == '+' || c == '?' ||
+                               (c == '{' && MORE2() && isdigit(PEEK2())) ) )
+               return;
+       SETERROR(REG_BADRPT);
+}
+
+/*
+ - p_str - string (no metacharacters) "parser"
+ == static void p_str(register struct parse *p);
+ */
+static void
+p_str(p)
+register struct parse *p;
+{
+       REQUIRE(MORE(), REG_EMPTY);
+       while (MORE())
+               ordinary(p, GETNEXT());
+}
+
+/*
+ - p_bre - BRE parser top level, anchoring and concatenation
+ == static void p_bre(register struct parse *p, register int end1, \
+ ==    register int end2);
+ * Giving end1 as OUT essentially eliminates the end1/end2 check.
+ *
+ * This implementation is a bit of a kludge, in that a trailing $ is first
+ * taken as an ordinary character and then revised to be an anchor.  The
+ * only undesirable side effect is that '$' gets included as a character
+ * category in such cases.  This is fairly harmless; not worth fixing.
+ * The amount of lookahead needed to avoid this kludge is excessive.
+ */
+static void
+p_bre(p, end1, end2)
+register struct parse *p;
+register int end1;             /* first terminating character */
+register int end2;             /* second terminating character */
+{
+       register sopno start = HERE();
+       register int first = 1;                 /* first subexpression? */
+       register int wasdollar = 0;
+
+       if (EAT('^')) {
+               EMIT(OBOL, 0);
+               p->g->iflags |= USEBOL;
+               p->g->nbol++;
+       }
+       while (MORE() && !SEETWO(end1, end2)) {
+               wasdollar = p_simp_re(p, first);
+               first = 0;
+       }
+       if (wasdollar) {        /* oops, that was a trailing anchor */
+               DROP(1);
+               EMIT(OEOL, 0);
+               p->g->iflags |= USEEOL;
+               p->g->neol++;
+       }
+
+       REQUIRE(HERE() != start, REG_EMPTY);    /* require nonempty */
+}
+
+/*
+ - p_simp_re - parse a simple RE, an atom possibly followed by a repetition
+ == static int p_simp_re(register struct parse *p, int starordinary);
+ */
+static int                     /* was the simple RE an unbackslashed $? */
+p_simp_re(p, starordinary)
+register struct parse *p;
+int starordinary;              /* is a leading * an ordinary character? */
+{
+       register int c;
+       register int count;
+       register int count2;
+       register sopno pos;
+       register int i;
+       register sopno subno;
+#      define  BACKSL  (1<<CHAR_BIT)
+
+       pos = HERE();           /* repetion op, if any, covers from here */
+
+       assert(MORE());         /* caller should have ensured this */
+       c = GETNEXT();
+       if (c == '\\') {
+               REQUIRE(MORE(), REG_EESCAPE);
+               c = BACKSL | (unsigned char)GETNEXT();
+       }
+       switch (c) {
+       case '.':
+               if (p->g->cflags&REG_NEWLINE)
+                       nonnewline(p);
+               else
+                       EMIT(OANY, 0);
+               break;
+       case '[':
+               p_bracket(p);
+               break;
+       case BACKSL|'{':
+               SETERROR(REG_BADRPT);
+               break;
+       case BACKSL|'(':
+               p->g->nsub++;
+               subno = p->g->nsub;
+               if (subno < NPAREN)
+                       p->pbegin[subno] = HERE();
+               EMIT(OLPAREN, subno);
+               /* the MORE here is an error heuristic */
+               if (MORE() && !SEETWO('\\', ')'))
+                       p_bre(p, '\\', ')');
+               if (subno < NPAREN) {
+                       p->pend[subno] = HERE();
+                       assert(p->pend[subno] != 0);
+               }
+               EMIT(ORPAREN, subno);
+               REQUIRE(EATTWO('\\', ')'), REG_EPAREN);
+               break;
+       case BACKSL|')':        /* should not get here -- must be user */
+       case BACKSL|'}':
+               SETERROR(REG_EPAREN);
+               break;
+       case BACKSL|'1':
+       case BACKSL|'2':
+       case BACKSL|'3':
+       case BACKSL|'4':
+       case BACKSL|'5':
+       case BACKSL|'6':
+       case BACKSL|'7':
+       case BACKSL|'8':
+       case BACKSL|'9':
+               i = (c&~BACKSL) - '0';
+               assert(i < NPAREN);
+               if (p->pend[i] != 0) {
+                       assert(i <= p->g->nsub);
+                       EMIT(OBACK_, i);
+                       assert(p->pbegin[i] != 0);
+                       assert(OP(p->strip[p->pbegin[i]]) == OLPAREN);
+                       assert(OP(p->strip[p->pend[i]]) == ORPAREN);
+                       (void) dupl(p, p->pbegin[i]+1, p->pend[i]);
+                       EMIT(O_BACK, i);
+               } else
+                       SETERROR(REG_ESUBREG);
+               p->g->backrefs = 1;
+               break;
+       case '*':
+               REQUIRE(starordinary, REG_BADRPT);
+               /* FALLTHROUGH */
+       default:
+               ordinary(p, c &~ BACKSL);
+               break;
+       }
+
+       if (EAT('*')) {         /* implemented as +? */
+               /* this case does not require the (y|) trick, noKLUDGE */
+               INSERT(OPLUS_, pos);
+               ASTERN(O_PLUS, pos);
+               INSERT(OQUEST_, pos);
+               ASTERN(O_QUEST, pos);
+       } else if (EATTWO('\\', '{')) {
+               count = p_count(p);
+               if (EAT(',')) {
+                       if (MORE() && isdigit(PEEK())) {
+                               count2 = p_count(p);
+                               REQUIRE(count <= count2, REG_BADBR);
+                       } else          /* single number with comma */
+                               count2 = INFINITY;
+               } else          /* just a single number */
+                       count2 = count;
+               repeat(p, pos, count, count2);
+               if (!EATTWO('\\', '}')) {       /* error heuristics */
+                       while (MORE() && !SEETWO('\\', '}'))
+                               NEXT();
+                       REQUIRE(MORE(), REG_EBRACE);
+                       SETERROR(REG_BADBR);
+               }
+       } else if (c == (unsigned char)'$')     /* $ (but not \$) ends it */
+               return(1);
+
+       return(0);
+}
+
+/*
+ - p_count - parse a repetition count
+ == static int p_count(register struct parse *p);
+ */
+static int                     /* the value */
+p_count(p)
+register struct parse *p;
+{
+       register int count = 0;
+       register int ndigits = 0;
+
+       while (MORE() && isdigit(PEEK()) && count <= DUPMAX) {
+               count = count*10 + (GETNEXT() - '0');
+               ndigits++;
+       }
+
+       REQUIRE(ndigits > 0 && count <= DUPMAX, REG_BADBR);
+       return(count);
+}
+
+/*
+ - p_bracket - parse a bracketed character list
+ == static void p_bracket(register struct parse *p);
+ *
+ * Note a significant property of this code:  if the allocset() did SETERROR,
+ * no set operations are done.
+ */
+static void
+p_bracket(p)
+register struct parse *p;
+{
+       register char c;
+       register cset *cs = allocset(p);
+       register int invert = 0;
+
+       /* Dept of Truly Sickening Special-Case Kludges */
+       if (p->next + 5 < p->end && strncmp(p->next, "[:<:]]", 6) == 0) {
+               EMIT(OBOW, 0);
+               NEXTn(6);
+               return;
+       }
+       if (p->next + 5 < p->end && strncmp(p->next, "[:>:]]", 6) == 0) {
+               EMIT(OEOW, 0);
+               NEXTn(6);
+               return;
+       }
+
+       if (EAT('^'))
+               invert++;       /* make note to invert set at end */
+       if (EAT(']'))
+               CHadd(cs, ']');
+       else if (EAT('-'))
+               CHadd(cs, '-');
+       while (MORE() && PEEK() != ']' && !SEETWO('-', ']'))
+               p_b_term(p, cs);
+       if (EAT('-'))
+               CHadd(cs, '-');
+       MUSTEAT(']', REG_EBRACK);
+
+       if (p->error != 0)      /* don't mess things up further */
+               return;
+
+       if (p->g->cflags&REG_ICASE) {
+               register int i;
+               register int ci;
+
+               for (i = p->g->csetsize - 1; i >= 0; i--)
+                       if (CHIN(cs, i) && isalpha(i)) {
+                               ci = othercase(i);
+                               if (ci != i)
+                                       CHadd(cs, ci);
+                       }
+               if (cs->multis != NULL)
+                       mccase(p, cs);
+       }
+       if (invert) {
+               register int i;
+
+               for (i = p->g->csetsize - 1; i >= 0; i--)
+                       if (CHIN(cs, i))
+                               CHsub(cs, i);
+                       else
+                               CHadd(cs, i);
+               if (p->g->cflags&REG_NEWLINE)
+                       CHsub(cs, '\n');
+               if (cs->multis != NULL)
+                       mcinvert(p, cs);
+       }
+
+       assert(cs->multis == NULL);             /* xxx */
+
+       if (nch(p, cs) == 1) {          /* optimize singleton sets */
+               ordinary(p, firstch(p, cs));
+               freeset(p, cs);
+       } else
+               EMIT(OANYOF, freezeset(p, cs));
+}
+
+/*
+ - p_b_term - parse one term of a bracketed character list
+ == static void p_b_term(register struct parse *p, register cset *cs);
+ */
+static void
+p_b_term(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       register char c;
+       register char start, finish;
+       register int i;
+
+       /* classify what we've got */
+       switch ((MORE()) ? PEEK() : '\0') {
+       case '[':
+               c = (MORE2()) ? PEEK2() : '\0';
+               break;
+       case '-':
+               SETERROR(REG_ERANGE);
+               return;                 /* NOTE RETURN */
+               break;
+       default:
+               c = '\0';
+               break;
+       }
+
+       switch (c) {
+       case ':':               /* character class */
+               NEXT2();
+               REQUIRE(MORE(), REG_EBRACK);
+               c = PEEK();
+               REQUIRE(c != '-' && c != ']', REG_ECTYPE);
+               p_b_cclass(p, cs);
+               REQUIRE(MORE(), REG_EBRACK);
+               REQUIRE(EATTWO(':', ']'), REG_ECTYPE);
+               break;
+       case '=':               /* equivalence class */
+               NEXT2();
+               REQUIRE(MORE(), REG_EBRACK);
+               c = PEEK();
+               REQUIRE(c != '-' && c != ']', REG_ECOLLATE);
+               p_b_eclass(p, cs);
+               REQUIRE(MORE(), REG_EBRACK);
+               REQUIRE(EATTWO('=', ']'), REG_ECOLLATE);
+               break;
+       default:                /* symbol, ordinary character, or range */
+/* xxx revision needed for multichar stuff */
+               start = p_b_symbol(p);
+               if (SEE('-') && MORE2() && PEEK2() != ']') {
+                       /* range */
+                       NEXT();
+                       if (EAT('-'))
+                               finish = '-';
+                       else
+                               finish = p_b_symbol(p);
+               } else
+                       finish = start;
+/* xxx what about signed chars here... */
+               REQUIRE(start <= finish, REG_ERANGE);
+               for (i = start; i <= finish; i++)
+                       CHadd(cs, i);
+               break;
+       }
+}
+
+/*
+ - p_b_cclass - parse a character-class name and deal with it
+ == static void p_b_cclass(register struct parse *p, register cset *cs);
+ */
+static void
+p_b_cclass(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       register char *sp = p->next;
+       register struct cclass *cp;
+       register size_t len;
+       register char *u;
+       register char c;
+
+       while (MORE() && isalpha(PEEK()))
+               NEXT();
+       len = p->next - sp;
+       for (cp = cclasses; cp->name != NULL; cp++)
+               if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0')
+                       break;
+       if (cp->name == NULL) {
+               /* oops, didn't find it */
+               SETERROR(REG_ECTYPE);
+               return;
+       }
+
+       u = cp->chars;
+       while ((c = *u++) != '\0')
+               CHadd(cs, c);
+       for (u = cp->multis; *u != '\0'; u += strlen(u) + 1)
+               MCadd(p, cs, u);
+}
+
+/*
+ - p_b_eclass - parse an equivalence-class name and deal with it
+ == static void p_b_eclass(register struct parse *p, register cset *cs);
+ *
+ * This implementation is incomplete. xxx
+ */
+static void
+p_b_eclass(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       register char c;
+
+       c = p_b_coll_elem(p, '=');
+       CHadd(cs, c);
+}
+
+/*
+ - p_b_symbol - parse a character or [..]ed multicharacter collating symbol
+ == static char p_b_symbol(register struct parse *p);
+ */
+static char                    /* value of symbol */
+p_b_symbol(p)
+register struct parse *p;
+{
+       register char value;
+
+       REQUIRE(MORE(), REG_EBRACK);
+       if (!EATTWO('[', '.'))
+               return(GETNEXT());
+
+       /* collating symbol */
+       value = p_b_coll_elem(p, '.');
+       REQUIRE(EATTWO('.', ']'), REG_ECOLLATE);
+       return(value);
+}
+
+/*
+ - p_b_coll_elem - parse a collating-element name and look it up
+ == static char p_b_coll_elem(register struct parse *p, int endc);
+ */
+static char                    /* value of collating element */
+p_b_coll_elem(p, endc)
+register struct parse *p;
+int endc;                      /* name ended by endc,']' */
+{
+       register char *sp = p->next;
+       register struct cname *cp;
+       register int len;
+       register char c;
+
+       while (MORE() && !SEETWO(endc, ']'))
+               NEXT();
+       if (!MORE()) {
+               SETERROR(REG_EBRACK);
+               return(0);
+       }
+       len = p->next - sp;
+       for (cp = cnames; cp->name != NULL; cp++)
+               if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0')
+                       return(cp->code);       /* known name */
+       if (len == 1)
+               return(*sp);    /* single character */
+       SETERROR(REG_ECOLLATE);                 /* neither */
+       return(0);
+}
+
+/*
+ - othercase - return the case counterpart of an alphabetic
+ == static char othercase(int ch);
+ */
+static char                    /* if no counterpart, return ch */
+othercase(ch)
+int ch;
+{
+       assert(isalpha(ch));
+       if (isupper(ch))
+               return(tolower(ch));
+       else if (islower(ch))
+               return(toupper(ch));
+       else                    /* peculiar, but could happen */
+               return(ch);
+}
+
+/*
+ - bothcases - emit a dualcase version of a two-case character
+ == static void bothcases(register struct parse *p, int ch);
+ *
+ * Boy, is this implementation ever a kludge...
+ */
+static void
+bothcases(p, ch)
+register struct parse *p;
+int ch;
+{
+       register char *oldnext = p->next;
+       register char *oldend = p->end;
+       char bracket[3];
+
+       assert(othercase(ch) != ch);    /* p_bracket() would recurse */
+       p->next = bracket;
+       p->end = bracket+2;
+       bracket[0] = ch;
+       bracket[1] = ']';
+       bracket[2] = '\0';
+       p_bracket(p);
+       assert(p->next == bracket+2);
+       p->next = oldnext;
+       p->end = oldend;
+}
+
+/*
+ - ordinary - emit an ordinary character
+ == static void ordinary(register struct parse *p, register int ch);
+ */
+static void
+ordinary(p, ch)
+register struct parse *p;
+register int ch;
+{
+       register cat_t *cap = p->g->categories;
+
+       if ((p->g->cflags&REG_ICASE) && isalpha(ch) && othercase(ch) != ch)
+               bothcases(p, ch);
+       else {
+               EMIT(OCHAR, (unsigned char)ch);
+               if (cap[ch] == 0)
+                       cap[ch] = p->g->ncategories++;
+       }
+}
+
+/*
+ - nonnewline - emit REG_NEWLINE version of OANY
+ == static void nonnewline(register struct parse *p);
+ *
+ * Boy, is this implementation ever a kludge...
+ */
+static void
+nonnewline(p)
+register struct parse *p;
+{
+       register char *oldnext = p->next;
+       register char *oldend = p->end;
+       char bracket[4];
+
+       p->next = bracket;
+       p->end = bracket+3;
+       bracket[0] = '^';
+       bracket[1] = '\n';
+       bracket[2] = ']';
+       bracket[3] = '\0';
+       p_bracket(p);
+       assert(p->next == bracket+3);
+       p->next = oldnext;
+       p->end = oldend;
+}
+
+/*
+ - repeat - generate code for a bounded repetition, recursively if needed
+ == static void repeat(register struct parse *p, sopno start, int from, int to);
+ */
+static void
+repeat(p, start, from, to)
+register struct parse *p;
+sopno start;                   /* operand from here to end of strip */
+int from;                      /* repeated from this number */
+int to;                                /* to this number of times (maybe INFINITY) */
+{
+       register sopno finish = HERE();
+#      define  N       2
+#      define  INF     3
+#      define  REP(f, t)       ((f)*8 + (t))
+#      define  MAP(n)  (((n) <= 1) ? (n) : ((n) == INFINITY) ? INF : N)
+       register sopno copy;
+
+       if (p->error != 0)      /* head off possible runaway recursion */
+               return;
+
+       assert(from <= to);
+
+       switch (REP(MAP(from), MAP(to))) {
+       case REP(0, 0):                 /* must be user doing this */
+               DROP(finish-start);     /* drop the operand */
+               break;
+       case REP(0, 1):                 /* as x{1,1}? */
+       case REP(0, N):                 /* as x{1,n}? */
+       case REP(0, INF):               /* as x{1,}? */
+               /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */
+               INSERT(OCH_, start);            /* offset is wrong... */
+               repeat(p, start+1, 1, to);
+               ASTERN(OOR1, start);
+               AHEAD(start);                   /* ... fix it */
+               EMIT(OOR2, 0);
+               AHEAD(THERE());
+               ASTERN(O_CH, THERETHERE());
+               break;
+       case REP(1, 1):                 /* trivial case */
+               /* done */
+               break;
+       case REP(1, N):                 /* as x?x{1,n-1} */
+               /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */
+               INSERT(OCH_, start);
+               ASTERN(OOR1, start);
+               AHEAD(start);
+               EMIT(OOR2, 0);                  /* offset very wrong... */
+               AHEAD(THERE());                 /* ...so fix it */
+               ASTERN(O_CH, THERETHERE());
+               copy = dupl(p, start+1, finish+1);
+               assert(copy == finish+4);
+               repeat(p, copy, 1, to-1);
+               break;
+       case REP(1, INF):               /* as x+ */
+               INSERT(OPLUS_, start);
+               ASTERN(O_PLUS, start);
+               break;
+       case REP(N, N):                 /* as xx{m-1,n-1} */
+               copy = dupl(p, start, finish);
+               repeat(p, copy, from-1, to-1);
+               break;
+       case REP(N, INF):               /* as xx{n-1,INF} */
+               copy = dupl(p, start, finish);
+               repeat(p, copy, from-1, to);
+               break;
+       default:                        /* "can't happen" */
+               SETERROR(REG_ASSERT);   /* just in case */
+               break;
+       }
+}
+
+/*
+ - seterr - set an error condition
+ == static int seterr(register struct parse *p, int e);
+ */
+static int                     /* useless but makes type checking happy */
+seterr(p, e)
+register struct parse *p;
+int e;
+{
+       if (p->error == 0)      /* keep earliest error condition */
+               p->error = e;
+       p->next = nuls;         /* try to bring things to a halt */
+       p->end = nuls;
+       return(0);              /* make the return value well-defined */
+}
+
+/*
+ - allocset - allocate a set of characters for []
+ == static cset *allocset(register struct parse *p);
+ */
+static cset *
+allocset(p)
+register struct parse *p;
+{
+       register int no = p->g->ncsets++;
+       register size_t nc;
+       register size_t nbytes;
+       register cset *cs;
+       register size_t css = (size_t)p->g->csetsize;
+       register int i;
+
+       if (no >= p->ncsalloc) {        /* need another column of space */
+               p->ncsalloc += CHAR_BIT;
+               nc = p->ncsalloc;
+               assert(nc % CHAR_BIT == 0);
+               nbytes = nc / CHAR_BIT * css;
+               if (p->g->sets == NULL)
+                       p->g->sets = (cset *)malloc(nc * sizeof(cset));
+               else
+                       p->g->sets = (cset *)realloc((char *)p->g->sets,
+                                                       nc * sizeof(cset));
+               if (p->g->setbits == NULL)
+                       p->g->setbits = (uch *)malloc(nbytes);
+               else {
+                       p->g->setbits = (uch *)realloc((char *)p->g->setbits,
+                                                               nbytes);
+                       /* xxx this isn't right if setbits is now NULL */
+                       for (i = 0; i < no; i++)
+                               p->g->sets[i].ptr = p->g->setbits + css*(i/CHAR_BIT);
+               }
+               if (p->g->sets != NULL && p->g->setbits != NULL)
+                       (void) memset((char *)p->g->setbits + (nbytes - css),
+                                                               0, css);
+               else {
+                       no = 0;
+                       SETERROR(REG_ESPACE);
+                       /* caller's responsibility not to do set ops */
+               }
+       }
+
+       assert(p->g->sets != NULL);     /* xxx */
+       cs = &p->g->sets[no];
+       cs->ptr = p->g->setbits + css*((no)/CHAR_BIT);
+       cs->mask = 1 << ((no) % CHAR_BIT);
+       cs->hash = 0;
+       cs->smultis = 0;
+       cs->multis = NULL;
+
+       return(cs);
+}
+
+/*
+ - freeset - free a now-unused set
+ == static void freeset(register struct parse *p, register cset *cs);
+ */
+static void
+freeset(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       register int i;
+       register cset *top = &p->g->sets[p->g->ncsets];
+       register size_t css = (size_t)p->g->csetsize;
+
+       for (i = 0; i < css; i++)
+               CHsub(cs, i);
+       if (cs == top-1)        /* recover only the easy case */
+               p->g->ncsets--;
+}
+
+/*
+ - freezeset - final processing on a set of characters
+ == static int freezeset(register struct parse *p, register cset *cs);
+ *
+ * The main task here is merging identical sets.  This is usually a waste
+ * of time (although the hash code minimizes the overhead), but can win
+ * big if REG_ICASE is being used.  REG_ICASE, by the way, is why the hash
+ * is done using addition rather than xor -- all ASCII [aA] sets xor to
+ * the same value!
+ */
+static int                     /* set number */
+freezeset(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       register uch h = cs->hash;
+       register int i;
+       register cset *top = &p->g->sets[p->g->ncsets];
+       register cset *cs2;
+       register size_t css = (size_t)p->g->csetsize;
+
+       /* look for an earlier one which is the same */
+       for (cs2 = &p->g->sets[0]; cs2 < top; cs2++)
+               if (cs2->hash == h && cs2 != cs) {
+                       /* maybe */
+                       for (i = 0; i < css; i++)
+                               if (!!CHIN(cs2, i) != !!CHIN(cs, i))
+                                       break;          /* no */
+                       if (i == css)
+                               break;                  /* yes */
+               }
+
+       if (cs2 < top) {        /* found one */
+               freeset(p, cs);
+               cs = cs2;
+       }
+
+       return((int)(cs - p->g->sets));
+}
+
+/*
+ - firstch - return first character in a set (which must have at least one)
+ == static int firstch(register struct parse *p, register cset *cs);
+ */
+static int                     /* character; there is no "none" value */
+firstch(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       register int i;
+       register size_t css = (size_t)p->g->csetsize;
+
+       for (i = 0; i < css; i++)
+               if (CHIN(cs, i))
+                       return((char)i);
+       assert(never);
+       return(0);              /* arbitrary */
+}
+
+/*
+ - nch - number of characters in a set
+ == static int nch(register struct parse *p, register cset *cs);
+ */
+static int
+nch(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       register int i;
+       register size_t css = (size_t)p->g->csetsize;
+       register int n = 0;
+
+       for (i = 0; i < css; i++)
+               if (CHIN(cs, i))
+                       n++;
+       return(n);
+}
+
+/*
+ - mcadd - add a collating element to a cset
+ == static void mcadd(register struct parse *p, register cset *cs, \
+ ==    register char *cp);
+ */
+static void
+mcadd(p, cs, cp)
+register struct parse *p;
+register cset *cs;
+register char *cp;
+{
+       register size_t oldend = cs->smultis;
+
+       cs->smultis += strlen(cp) + 1;
+       if (cs->multis == NULL)
+               cs->multis = malloc(cs->smultis);
+       else
+               cs->multis = realloc(cs->multis, cs->smultis);
+       if (cs->multis == NULL) {
+               SETERROR(REG_ESPACE);
+               return;
+       }
+
+       (void) strcpy(cs->multis + oldend - 1, cp);
+       cs->multis[cs->smultis - 1] = '\0';
+}
+
+/*
+ - mcsub - subtract a collating element from a cset
+ == static void mcsub(register cset *cs, register char *cp);
+ */
+static void
+mcsub(cs, cp)
+register cset *cs;
+register char *cp;
+{
+       register char *fp = mcfind(cs, cp);
+       register size_t len = strlen(fp);
+
+       assert(fp != NULL);
+       (void) memmove(fp, fp + len + 1,
+                               cs->smultis - (fp + len + 1 - cs->multis));
+       cs->smultis -= len;
+
+       if (cs->smultis == 0) {
+               free(cs->multis);
+               cs->multis = NULL;
+               return;
+       }
+
+       cs->multis = realloc(cs->multis, cs->smultis);
+       assert(cs->multis != NULL);
+}
+
+/*
+ - mcin - is a collating element in a cset?
+ == static int mcin(register cset *cs, register char *cp);
+ */
+static int
+mcin(cs, cp)
+register cset *cs;
+register char *cp;
+{
+       return(mcfind(cs, cp) != NULL);
+}
+
+/*
+ - mcfind - find a collating element in a cset
+ == static char *mcfind(register cset *cs, register char *cp);
+ */
+static char *
+mcfind(cs, cp)
+register cset *cs;
+register char *cp;
+{
+       register char *p;
+
+       if (cs->multis == NULL)
+               return(NULL);
+       for (p = cs->multis; *p != '\0'; p += strlen(p) + 1)
+               if (strcmp(cp, p) == 0)
+                       return(p);
+       return(NULL);
+}
+
+/*
+ - mcinvert - invert the list of collating elements in a cset
+ == static void mcinvert(register struct parse *p, register cset *cs);
+ *
+ * This would have to know the set of possibilities.  Implementation
+ * is deferred.
+ */
+static void
+mcinvert(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       assert(cs->multis == NULL);     /* xxx */
+}
+
+/*
+ - mccase - add case counterparts of the list of collating elements in a cset
+ == static void mccase(register struct parse *p, register cset *cs);
+ *
+ * This would have to know the set of possibilities.  Implementation
+ * is deferred.
+ */
+static void
+mccase(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       assert(cs->multis == NULL);     /* xxx */
+}
+
+/*
+ - isinsets - is this character in any sets?
+ == static int isinsets(register struct re_guts *g, int c);
+ */
+static int                     /* predicate */
+isinsets(g, c)
+register struct re_guts *g;
+int c;
+{
+       register uch *col;
+       register int i;
+       register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT;
+       register unsigned uc = (unsigned char)c;
+
+       for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize)
+               if (col[uc] != 0)
+                       return(1);
+       return(0);
+}
+
+/*
+ - samesets - are these two characters in exactly the same sets?
+ == static int samesets(register struct re_guts *g, int c1, int c2);
+ */
+static int                     /* predicate */
+samesets(g, c1, c2)
+register struct re_guts *g;
+int c1;
+int c2;
+{
+       register uch *col;
+       register int i;
+       register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT;
+       register unsigned uc1 = (unsigned char)c1;
+       register unsigned uc2 = (unsigned char)c2;
+
+       for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize)
+               if (col[uc1] != col[uc2])
+                       return(0);
+       return(1);
+}
+
+/*
+ - categorize - sort out character categories
+ == static void categorize(struct parse *p, register struct re_guts *g);
+ */
+static void
+categorize(p, g)
+struct parse *p;
+register struct re_guts *g;
+{
+       register cat_t *cats = g->categories;
+       register int c;
+       register int c2;
+       register cat_t cat;
+
+       /* avoid making error situations worse */
+       if (p->error != 0)
+               return;
+
+       for (c = CHAR_MIN; c <= CHAR_MAX; c++)
+               if (cats[c] == 0 && isinsets(g, c)) {
+                       cat = g->ncategories++;
+                       cats[c] = cat;
+                       for (c2 = c+1; c2 <= CHAR_MAX; c2++)
+                               if (cats[c2] == 0 && samesets(g, c, c2))
+                                       cats[c2] = cat;
+               }
+}
+
+/*
+ - dupl - emit a duplicate of a bunch of sops
+ == static sopno dupl(register struct parse *p, sopno start, sopno finish);
+ */
+static sopno                   /* start of duplicate */
+dupl(p, start, finish)
+register struct parse *p;
+sopno start;                   /* from here */
+sopno finish;                  /* to this less one */
+{
+       register sopno ret = HERE();
+       register sopno len = finish - start;
+
+       assert(finish >= start);
+       if (len == 0)
+               return(ret);
+       enlarge(p, p->ssize + len);     /* this many unexpected additions */
+       assert(p->ssize >= p->slen + len);
+       (void) memcpy((char *)(p->strip + p->slen),
+               (char *)(p->strip + start), (size_t)len*sizeof(sop));
+       p->slen += len;
+       return(ret);
+}
+
+/*
+ - doemit - emit a strip operator
+ == static void doemit(register struct parse *p, sop op, size_t opnd);
+ *
+ * It might seem better to implement this as a macro with a function as
+ * hard-case backup, but it's just too big and messy unless there are
+ * some changes to the data structures.  Maybe later.
+ */
+static void
+doemit(p, op, opnd)
+register struct parse *p;
+sop op;
+size_t opnd;
+{
+       /* avoid making error situations worse */
+       if (p->error != 0)
+               return;
+
+       /* deal with oversize operands ("can't happen", more or less) */
+       assert(opnd < 1<<OPSHIFT);
+
+       /* deal with undersized strip */
+       if (p->slen >= p->ssize)
+               enlarge(p, (p->ssize+1) / 2 * 3);       /* +50% */
+       assert(p->slen < p->ssize);
+
+       /* finally, it's all reduced to the easy case */
+       p->strip[p->slen++] = SOP(op, opnd);
+}
+
+/*
+ - doinsert - insert a sop into the strip
+ == static void doinsert(register struct parse *p, sop op, size_t opnd, sopno pos);
+ */
+static void
+doinsert(p, op, opnd, pos)
+register struct parse *p;
+sop op;
+size_t opnd;
+sopno pos;
+{
+       register sopno sn;
+       register sop s;
+       register int i;
+
+       /* avoid making error situations worse */
+       if (p->error != 0)
+               return;
+
+       sn = HERE();
+       EMIT(op, opnd);         /* do checks, ensure space */
+       assert(HERE() == sn+1);
+       s = p->strip[sn];
+
+       /* adjust paren pointers */
+       assert(pos > 0);
+       for (i = 1; i < NPAREN; i++) {
+               if (p->pbegin[i] >= pos) {
+                       p->pbegin[i]++;
+               }
+               if (p->pend[i] >= pos) {
+                       p->pend[i]++;
+               }
+       }
+
+       memmove((char *)&p->strip[pos+1], (char *)&p->strip[pos],
+                                               (HERE()-pos-1)*sizeof(sop));
+       p->strip[pos] = s;
+}
+
+/*
+ - dofwd - complete a forward reference
+ == static void dofwd(register struct parse *p, sopno pos, sop value);
+ */
+static void
+dofwd(p, pos, value)
+register struct parse *p;
+register sopno pos;
+sop value;
+{
+       /* avoid making error situations worse */
+       if (p->error != 0)
+               return;
+
+       assert(value < 1<<OPSHIFT);
+       p->strip[pos] = OP(p->strip[pos]) | value;
+}
+
+/*
+ - enlarge - enlarge the strip
+ == static void enlarge(register struct parse *p, sopno size);
+ */
+static void
+enlarge(p, size)
+register struct parse *p;
+register sopno size;
+{
+       register sop *sp;
+
+       if (p->ssize >= size)
+               return;
+
+       sp = (sop *)realloc(p->strip, size*sizeof(sop));
+       if (sp == NULL) {
+               SETERROR(REG_ESPACE);
+               return;
+       }
+       p->strip = sp;
+       p->ssize = size;
+}
+
+/*
+ - stripsnug - compact the strip
+ == static void stripsnug(register struct parse *p, register struct re_guts *g);
+ */
+static void
+stripsnug(p, g)
+register struct parse *p;
+register struct re_guts *g;
+{
+       g->nstates = p->slen;
+       g->strip = (sop *)realloc((char *)p->strip, p->slen * sizeof(sop));
+       if (g->strip == NULL) {
+               SETERROR(REG_ESPACE);
+               g->strip = p->strip;
+       }
+}
+
+/*
+ - findmust - fill in must and mlen with longest mandatory literal string
+ == static void findmust(register struct parse *p, register struct re_guts *g);
+ *
+ * This algorithm could do fancy things like analyzing the operands of |
+ * for common subsequences.  Someday.  This code is simple and finds most
+ * of the interesting cases.
+ *
+ * Note that must and mlen got initialized during setup.
+ */
+static void
+findmust(p, g)
+struct parse *p;
+register struct re_guts *g;
+{
+       register sop *scan;
+       sop *start;
+       register sop *newstart;
+       register sopno newlen;
+       register sop s;
+       register char *cp;
+       register sopno i;
+
+       /* avoid making error situations worse */
+       if (p->error != 0)
+               return;
+
+       /* find the longest OCHAR sequence in strip */
+       newlen = 0;
+       scan = g->strip + 1;
+       do {
+               s = *scan++;
+               switch (OP(s)) {
+               case OCHAR:             /* sequence member */
+                       if (newlen == 0)                /* new sequence */
+                               newstart = scan - 1;
+                       newlen++;
+                       break;
+               case OPLUS_:            /* things that don't break one */
+               case OLPAREN:
+               case ORPAREN:
+                       break;
+               case OQUEST_:           /* things that must be skipped */
+               case OCH_:
+                       scan--;
+                       do {
+                               scan += OPND(s);
+                               s = *scan;
+                               /* assert() interferes w debug printouts */
+                               if (OP(s) != O_QUEST && OP(s) != O_CH &&
+                                                       OP(s) != OOR2) {
+                                       g->iflags |= BAD;
+                                       return;
+                               }
+                       } while (OP(s) != O_QUEST && OP(s) != O_CH);
+                       /* fallthrough */
+               default:                /* things that break a sequence */
+                       if (newlen > g->mlen) {         /* ends one */
+                               start = newstart;
+                               g->mlen = newlen;
+                       }
+                       newlen = 0;
+                       break;
+               }
+       } while (OP(s) != OEND);
+
+       if (g->mlen == 0)               /* there isn't one */
+               return;
+
+       /* turn it into a character string */
+       g->must = malloc((size_t)g->mlen + 1);
+       if (g->must == NULL) {          /* argh; just forget it */
+               g->mlen = 0;
+               return;
+       }
+       cp = g->must;
+       scan = start;
+       for (i = g->mlen; i > 0; i--) {
+               while (OP(s = *scan++) != OCHAR)
+                       continue;
+               assert(cp < g->must + g->mlen);
+               *cp++ = (char)OPND(s);
+       }
+       assert(cp == g->must + g->mlen);
+       *cp++ = '\0';           /* just on general principles */
+}
+
+/*
+ - pluscount - count + nesting
+ == static sopno pluscount(register struct parse *p, register struct re_guts *g);
+ */
+static sopno                   /* nesting depth */
+pluscount(p, g)
+struct parse *p;
+register struct re_guts *g;
+{
+       register sop *scan;
+       register sop s;
+       register sopno plusnest = 0;
+       register sopno maxnest = 0;
+
+       if (p->error != 0)
+               return(0);      /* there may not be an OEND */
+
+       scan = g->strip + 1;
+       do {
+               s = *scan++;
+               switch (OP(s)) {
+               case OPLUS_:
+                       plusnest++;
+                       break;
+               case O_PLUS:
+                       if (plusnest > maxnest)
+                               maxnest = plusnest;
+                       plusnest--;
+                       break;
+               }
+       } while (OP(s) != OEND);
+       if (plusnest != 0)
+               g->iflags |= BAD;
+       return(maxnest);
+}
diff --git a/src/backend/port/win32/regex/regerror.c b/src/backend/port/win32/regex/regerror.c
new file mode 100644 (file)
index 0000000..a8d0945
--- /dev/null
@@ -0,0 +1,180 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)regerror.c  8.4 (Berkeley) 3/20/94
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)regerror.c 8.4 (Berkeley) 3/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <regex.h>
+
+#include "utils.h"
+
+/* ========= begin header generated by ./mkh ========= */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* === regerror.c === */
+static char *regatoi __P((const regex_t *preg, char *localbuf));
+
+#ifdef __cplusplus
+}
+#endif
+/* ========= end header generated by ./mkh ========= */
+/*
+ = #define     REG_NOMATCH      1
+ = #define     REG_BADPAT       2
+ = #define     REG_ECOLLATE     3
+ = #define     REG_ECTYPE       4
+ = #define     REG_EESCAPE      5
+ = #define     REG_ESUBREG      6
+ = #define     REG_EBRACK       7
+ = #define     REG_EPAREN       8
+ = #define     REG_EBRACE       9
+ = #define     REG_BADBR       10
+ = #define     REG_ERANGE      11
+ = #define     REG_ESPACE      12
+ = #define     REG_BADRPT      13
+ = #define     REG_EMPTY       14
+ = #define     REG_ASSERT      15
+ = #define     REG_INVARG      16
+ = #define     REG_ATOI        255     // convert name to number (!)
+ = #define     REG_ITOA        0400    // convert number to name (!)
+ */
+static struct rerr {
+       int code;
+       char *name;
+       char *explain;
+} rerrs[] = {
+       REG_NOMATCH,    "REG_NOMATCH",  "regexec() failed to match",
+       REG_BADPAT,     "REG_BADPAT",   "invalid regular expression",
+       REG_ECOLLATE,   "REG_ECOLLATE", "invalid collating element",
+       REG_ECTYPE,     "REG_ECTYPE",   "invalid character class",
+       REG_EESCAPE,    "REG_EESCAPE",  "trailing backslash (\\)",
+       REG_ESUBREG,    "REG_ESUBREG",  "invalid backreference number",
+       REG_EBRACK,     "REG_EBRACK",   "brackets ([ ]) not balanced",
+       REG_EPAREN,     "REG_EPAREN",   "parentheses not balanced",
+       REG_EBRACE,     "REG_EBRACE",   "braces not balanced",
+       REG_BADBR,      "REG_BADBR",    "invalid repetition count(s)",
+       REG_ERANGE,     "REG_ERANGE",   "invalid character range",
+       REG_ESPACE,     "REG_ESPACE",   "out of memory",
+       REG_BADRPT,     "REG_BADRPT",   "repetition-operator operand invalid",
+       REG_EMPTY,      "REG_EMPTY",    "empty (sub)expression",
+       REG_ASSERT,     "REG_ASSERT",   "\"can't happen\" -- you found a bug",
+       REG_INVARG,     "REG_INVARG",   "invalid argument to regex routine",
+       0,              "",             "*** unknown regexp error code ***",
+};
+
+/*
+ - regerror - the interface to error numbers
+ = extern size_t regerror(int, const regex_t *, char *, size_t);
+ */
+/* ARGSUSED */
+size_t
+regerror(errcode, preg, errbuf, errbuf_size)
+int errcode;
+const regex_t *preg;
+char *errbuf;
+size_t errbuf_size;
+{
+       register struct rerr *r;
+       register size_t len;
+       register int target = errcode &~ REG_ITOA;
+       register char *s;
+       char convbuf[50];
+
+       if (errcode == REG_ATOI)
+               s = regatoi(preg, convbuf);
+       else {
+               for (r = rerrs; r->code != 0; r++)
+                       if (r->code == target)
+                               break;
+       
+               if (errcode&REG_ITOA) {
+                       if (r->code != 0)
+                               (void) strcpy(convbuf, r->name);
+                       else
+                               sprintf(convbuf, "REG_0x%x", target);
+                       assert(strlen(convbuf) < sizeof(convbuf));
+                       s = convbuf;
+               } else
+                       s = r->explain;
+       }
+
+       len = strlen(s) + 1;
+       if (errbuf_size > 0) {
+               if (errbuf_size > len)
+                       (void) strcpy(errbuf, s);
+               else {
+                       (void) strncpy(errbuf, s, errbuf_size-1);
+                       errbuf[errbuf_size-1] = '\0';
+               }
+       }
+
+       return(len);
+}
+
+/*
+ - regatoi - internal routine to implement REG_ATOI
+ == static char *regatoi(const regex_t *preg, char *localbuf);
+ */
+static char *
+regatoi(preg, localbuf)
+const regex_t *preg;
+char *localbuf;
+{
+       register struct rerr *r;
+       register size_t siz;
+       register char *p;
+
+       for (r = rerrs; r->code != 0; r++)
+               if (strcmp(r->name, preg->re_endp) == 0)
+                       break;
+       if (r->code == 0)
+               return("0");
+
+       sprintf(localbuf, "%d", r->code);
+       return(localbuf);
+}
diff --git a/src/backend/port/win32/regex/regex.3 b/src/backend/port/win32/regex/regex.3
new file mode 100644 (file)
index 0000000..66a7285
--- /dev/null
@@ -0,0 +1,538 @@
+.\" Copyright (c) 1992, 1993, 1994 Henry Spencer.
+.\" Copyright (c) 1992, 1993, 1994
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Henry Spencer.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"    @(#)regex.3     8.4 (Berkeley) 3/20/94
+.\"
+.TH REGEX 3 "March 20, 1994"
+.de ZR
+.\" one other place knows this name:  the SEE ALSO section
+.IR re_format (7) \\$1
+..
+.SH NAME
+regcomp, regexec, regerror, regfree \- regular-expression library
+.SH SYNOPSIS
+.ft B
+.\".na
+#include <sys/types.h>
+.br
+#include <regex.h>
+.HP 10
+int regcomp(regex_t\ *preg, const\ char\ *pattern, int\ cflags);
+.HP
+int\ regexec(const\ regex_t\ *preg, const\ char\ *string,
+size_t\ nmatch, regmatch_t\ pmatch[], int\ eflags);
+.HP
+size_t\ regerror(int\ errcode, const\ regex_t\ *preg,
+char\ *errbuf, size_t\ errbuf_size);
+.HP
+void\ regfree(regex_t\ *preg);
+.\".ad
+.ft
+.SH DESCRIPTION
+These routines implement POSIX 1003.2 regular expressions (``RE''s);
+see
+.ZR .
+.I Regcomp
+compiles an RE written as a string into an internal form,
+.I regexec
+matches that internal form against a string and reports results,
+.I regerror
+transforms error codes from either into human-readable messages,
+and
+.I regfree
+frees any dynamically-allocated storage used by the internal form
+of an RE.
+.PP
+The header
+.I <regex.h>
+declares two structure types,
+.I regex_t
+and
+.IR regmatch_t ,
+the former for compiled internal forms and the latter for match reporting.
+It also declares the four functions,
+a type
+.IR regoff_t ,
+and a number of constants with names starting with ``REG_''.
+.PP
+.I Regcomp
+compiles the regular expression contained in the
+.I pattern
+string,
+subject to the flags in
+.IR cflags ,
+and places the results in the
+.I regex_t
+structure pointed to by
+.IR preg .
+.I Cflags
+is the bitwise OR of zero or more of the following flags:
+.IP REG_EXTENDED \w'REG_EXTENDED'u+2n
+Compile modern (``extended'') REs,
+rather than the obsolete (``basic'') REs that
+are the default.
+.IP REG_BASIC
+This is a synonym for 0,
+provided as a counterpart to REG_EXTENDED to improve readability.
+.IP REG_NOSPEC
+Compile with recognition of all special characters turned off.
+All characters are thus considered ordinary,
+so the ``RE'' is a literal string.
+This is an extension,
+compatible with but not specified by POSIX 1003.2,
+and should be used with
+caution in software intended to be portable to other systems.
+REG_EXTENDED and REG_NOSPEC may not be used
+in the same call to
+.IR regcomp .
+.IP REG_ICASE
+Compile for matching that ignores upper/lower case distinctions.
+See
+.ZR .
+.IP REG_NOSUB
+Compile for matching that need only report success or failure,
+not what was matched.
+.IP REG_NEWLINE
+Compile for newline-sensitive matching.
+By default, newline is a completely ordinary character with no special
+meaning in either REs or strings.
+With this flag,
+`[^' bracket expressions and `.' never match newline,
+a `^' anchor matches the null string after any newline in the string
+in addition to its normal function,
+and the `$' anchor matches the null string before any newline in the
+string in addition to its normal function.
+.IP REG_PEND
+The regular expression ends,
+not at the first NUL,
+but just before the character pointed to by the
+.I re_endp
+member of the structure pointed to by
+.IR preg .
+The
+.I re_endp
+member is of type
+.IR const\ char\ * .
+This flag permits inclusion of NULs in the RE;
+they are considered ordinary characters.
+This is an extension,
+compatible with but not specified by POSIX 1003.2,
+and should be used with
+caution in software intended to be portable to other systems.
+.PP
+When successful,
+.I regcomp
+returns 0 and fills in the structure pointed to by
+.IR preg .
+One member of that structure
+(other than
+.IR re_endp )
+is publicized:
+.IR re_nsub ,
+of type
+.IR size_t ,
+contains the number of parenthesized subexpressions within the RE
+(except that the value of this member is undefined if the
+REG_NOSUB flag was used).
+If
+.I regcomp
+fails, it returns a non-zero error code;
+see DIAGNOSTICS.
+.PP
+.I Regexec
+matches the compiled RE pointed to by
+.I preg
+against the
+.IR string ,
+subject to the flags in
+.IR eflags ,
+and reports results using
+.IR nmatch ,
+.IR pmatch ,
+and the returned value.
+The RE must have been compiled by a previous invocation of
+.IR regcomp .
+The compiled form is not altered during execution of
+.IR regexec ,
+so a single compiled RE can be used simultaneously by multiple threads.
+.PP
+By default,
+the NUL-terminated string pointed to by
+.I string
+is considered to be the text of an entire line, minus any terminating
+newline.
+The
+.I eflags
+argument is the bitwise OR of zero or more of the following flags:
+.IP REG_NOTBOL \w'REG_STARTEND'u+2n
+The first character of
+the string
+is not the beginning of a line, so the `^' anchor should not match before it.
+This does not affect the behavior of newlines under REG_NEWLINE.
+.IP REG_NOTEOL
+The NUL terminating
+the string
+does not end a line, so the `$' anchor should not match before it.
+This does not affect the behavior of newlines under REG_NEWLINE.
+.IP REG_STARTEND
+The string is considered to start at
+\fIstring\fR\ + \fIpmatch\fR[0].\fIrm_so\fR
+and to have a terminating NUL located at
+\fIstring\fR\ + \fIpmatch\fR[0].\fIrm_eo\fR
+(there need not actually be a NUL at that location),
+regardless of the value of
+.IR nmatch .
+See below for the definition of
+.IR pmatch
+and
+.IR nmatch .
+This is an extension,
+compatible with but not specified by POSIX 1003.2,
+and should be used with
+caution in software intended to be portable to other systems.
+Note that a non-zero \fIrm_so\fR does not imply REG_NOTBOL;
+REG_STARTEND affects only the location of the string,
+not how it is matched.
+.PP
+See
+.ZR
+for a discussion of what is matched in situations where an RE or a
+portion thereof could match any of several substrings of
+.IR string .
+.PP
+Normally,
+.I regexec
+returns 0 for success and the non-zero code REG_NOMATCH for failure.
+Other non-zero error codes may be returned in exceptional situations;
+see DIAGNOSTICS.
+.PP
+If REG_NOSUB was specified in the compilation of the RE,
+or if
+.I nmatch
+is 0,
+.I regexec
+ignores the
+.I pmatch
+argument (but see below for the case where REG_STARTEND is specified).
+Otherwise,
+.I pmatch
+points to an array of
+.I nmatch
+structures of type
+.IR regmatch_t .
+Such a structure has at least the members
+.I rm_so
+and
+.IR rm_eo ,
+both of type
+.I regoff_t
+(a signed arithmetic type at least as large as an
+.I off_t
+and a
+.IR ssize_t ),
+containing respectively the offset of the first character of a substring
+and the offset of the first character after the end of the substring.
+Offsets are measured from the beginning of the
+.I string
+argument given to
+.IR regexec .
+An empty substring is denoted by equal offsets,
+both indicating the character following the empty substring.
+.PP
+The 0th member of the
+.I pmatch
+array is filled in to indicate what substring of
+.I string
+was matched by the entire RE.
+Remaining members report what substring was matched by parenthesized
+subexpressions within the RE;
+member
+.I i
+reports subexpression
+.IR i ,
+with subexpressions counted (starting at 1) by the order of their opening
+parentheses in the RE, left to right.
+Unused entries in the array\(emcorresponding either to subexpressions that
+did not participate in the match at all, or to subexpressions that do not
+exist in the RE (that is, \fIi\fR\ > \fIpreg\fR\->\fIre_nsub\fR)\(emhave both
+.I rm_so
+and
+.I rm_eo
+set to \-1.
+If a subexpression participated in the match several times,
+the reported substring is the last one it matched.
+(Note, as an example in particular, that when the RE `(b*)+' matches `bbb',
+the parenthesized subexpression matches each of the three `b's and then
+an infinite number of empty strings following the last `b',
+so the reported substring is one of the empties.)
+.PP
+If REG_STARTEND is specified,
+.I pmatch
+must point to at least one
+.I regmatch_t
+(even if
+.I nmatch
+is 0 or REG_NOSUB was specified),
+to hold the input offsets for REG_STARTEND.
+Use for output is still entirely controlled by
+.IR nmatch ;
+if
+.I nmatch
+is 0 or REG_NOSUB was specified,
+the value of
+.IR pmatch [0]
+will not be changed by a successful
+.IR regexec .
+.PP
+.I Regerror
+maps a non-zero
+.I errcode
+from either
+.I regcomp
+or
+.I regexec
+to a human-readable, printable message.
+If
+.I preg
+is non-NULL,
+the error code should have arisen from use of
+the
+.I regex_t
+pointed to by
+.IR preg ,
+and if the error code came from
+.IR regcomp ,
+it should have been the result from the most recent
+.I regcomp
+using that
+.IR regex_t .
+.RI ( Regerror
+may be able to supply a more detailed message using information
+from the
+.IR regex_t .)
+.I Regerror
+places the NUL-terminated message into the buffer pointed to by
+.IR errbuf ,
+limiting the length (including the NUL) to at most
+.I errbuf_size
+bytes.
+If the whole message won't fit,
+as much of it as will fit before the terminating NUL is supplied.
+In any case,
+the returned value is the size of buffer needed to hold the whole
+message (including terminating NUL).
+If
+.I errbuf_size
+is 0,
+.I errbuf
+is ignored but the return value is still correct.
+.PP
+If the
+.I errcode
+given to
+.I regerror
+is first ORed with REG_ITOA,
+the ``message'' that results is the printable name of the error code,
+e.g. ``REG_NOMATCH'',
+rather than an explanation thereof.
+If
+.I errcode
+is REG_ATOI,
+then
+.I preg
+shall be non-NULL and the
+.I re_endp
+member of the structure it points to
+must point to the printable name of an error code;
+in this case, the result in
+.I errbuf
+is the decimal digits of
+the numeric value of the error code
+(0 if the name is not recognized).
+REG_ITOA and REG_ATOI are intended primarily as debugging facilities;
+they are extensions,
+compatible with but not specified by POSIX 1003.2,
+and should be used with
+caution in software intended to be portable to other systems.
+Be warned also that they are considered experimental and changes are possible.
+.PP
+.I Regfree
+frees any dynamically-allocated storage associated with the compiled RE
+pointed to by
+.IR preg .
+The remaining
+.I regex_t
+is no longer a valid compiled RE
+and the effect of supplying it to
+.I regexec
+or
+.I regerror
+is undefined.
+.PP
+None of these functions references global variables except for tables
+of constants;
+all are safe for use from multiple threads if the arguments are safe.
+.SH IMPLEMENTATION CHOICES
+There are a number of decisions that 1003.2 leaves up to the implementor,
+either by explicitly saying ``undefined'' or by virtue of them being
+forbidden by the RE grammar.
+This implementation treats them as follows.
+.PP
+See
+.ZR
+for a discussion of the definition of case-independent matching.
+.PP
+There is no particular limit on the length of REs,
+except insofar as memory is limited.
+Memory usage is approximately linear in RE size, and largely insensitive
+to RE complexity, except for bounded repetitions.
+See BUGS for one short RE using them
+that will run almost any system out of memory.
+.PP
+A backslashed character other than one specifically given a magic meaning
+by 1003.2 (such magic meanings occur only in obsolete [``basic''] REs)
+is taken as an ordinary character.
+.PP
+Any unmatched [ is a REG_EBRACK error.
+.PP
+Equivalence classes cannot begin or end bracket-expression ranges.
+The endpoint of one range cannot begin another.
+.PP
+RE_DUP_MAX, the limit on repetition counts in bounded repetitions, is 255.
+.PP
+A repetition operator (?, *, +, or bounds) cannot follow another
+repetition operator.
+A repetition operator cannot begin an expression or subexpression
+or follow `^' or `|'.
+.PP
+`|' cannot appear first or last in a (sub)expression or after another `|',
+i.e. an operand of `|' cannot be an empty subexpression.
+An empty parenthesized subexpression, `()', is legal and matches an
+empty (sub)string.
+An empty string is not a legal RE.
+.PP
+A `{' followed by a digit is considered the beginning of bounds for a
+bounded repetition, which must then follow the syntax for bounds.
+A `{' \fInot\fR followed by a digit is considered an ordinary character.
+.PP
+`^' and `$' beginning and ending subexpressions in obsolete (``basic'')
+REs are anchors, not ordinary characters.
+.SH SEE ALSO
+grep(1), re_format(7)
+.PP
+POSIX 1003.2, sections 2.8 (Regular Expression Notation)
+and
+B.5 (C Binding for Regular Expression Matching).
+.SH DIAGNOSTICS
+Non-zero error codes from
+.I regcomp
+and
+.I regexec
+include the following:
+.PP
+.nf
+.ta \w'REG_ECOLLATE'u+3n
+REG_NOMATCH    regexec() failed to match
+REG_BADPAT     invalid regular expression
+REG_ECOLLATE   invalid collating element
+REG_ECTYPE     invalid character class
+REG_EESCAPE    \e applied to unescapable character
+REG_ESUBREG    invalid backreference number
+REG_EBRACK     brackets [ ] not balanced
+REG_EPAREN     parentheses ( ) not balanced
+REG_EBRACE     braces { } not balanced
+REG_BADBR      invalid repetition count(s) in { }
+REG_ERANGE     invalid character range in [ ]
+REG_ESPACE     ran out of memory
+REG_BADRPT     ?, *, or + operand invalid
+REG_EMPTY      empty (sub)expression
+REG_ASSERT     ``can't happen''\(emyou found a bug
+REG_INVARG     invalid argument, e.g. negative-length string
+.fi
+.SH HISTORY
+Originally written by Henry Spencer.
+Altered for inclusion in the 4.4BSD distribution.
+.SH BUGS
+This is an alpha release with known defects.
+Please report problems.
+.PP
+There is one known functionality bug.
+The implementation of internationalization is incomplete:
+the locale is always assumed to be the default one of 1003.2,
+and only the collating elements etc. of that locale are available.
+.PP
+The back-reference code is subtle and doubts linger about its correctness
+in complex cases.
+.PP
+.I Regexec
+performance is poor.
+This will improve with later releases.
+.I Nmatch
+exceeding 0 is expensive;
+.I nmatch
+exceeding 1 is worse.
+.I Regexec
+is largely insensitive to RE complexity \fIexcept\fR that back
+references are massively expensive.
+RE length does matter; in particular, there is a strong speed bonus
+for keeping RE length under about 30 characters,
+with most special characters counting roughly double.
+.PP
+.I Regcomp
+implements bounded repetitions by macro expansion,
+which is costly in time and space if counts are large
+or bounded repetitions are nested.
+An RE like, say,
+`((((a{1,100}){1,100}){1,100}){1,100}){1,100}'
+will (eventually) run almost any existing machine out of swap space.
+.PP
+There are suspected problems with response to obscure error conditions.
+Notably,
+certain kinds of internal overflow,
+produced only by truly enormous REs or by multiply nested bounded repetitions,
+are probably not handled well.
+.PP
+Due to a mistake in 1003.2, things like `a)b' are legal REs because `)' is
+a special character only in the presence of a previous unmatched `('.
+This can't be fixed until the spec is fixed.
+.PP
+The standard's definition of back references is vague.
+For example, does
+`a\e(\e(b\e)*\e2\e)*d' match `abbbd'?
+Until the standard is clarified,
+behavior in such cases should not be relied on.
+.PP
+The implementation of word-boundary matching is a bit of a kludge,
+and bugs may lurk in combinations of word-boundary matching and anchoring.
diff --git a/src/backend/port/win32/regex/regex.h b/src/backend/port/win32/regex/regex.h
new file mode 100644 (file)
index 0000000..1611d4b
--- /dev/null
@@ -0,0 +1,106 @@
+/*-
+ * Copyright (c) 1992 Henry Spencer.
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer of the University of Toronto.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)regex.h     8.2 (Berkeley) 1/3/94
+ */
+
+#ifndef _REGEX_H_
+#define        _REGEX_H_
+
+#include <sys/cdefs.h>
+
+/* types */
+typedef off_t regoff_t;
+
+typedef struct {
+       int re_magic;
+       size_t re_nsub;         /* number of parenthesized subexpressions */
+       __const char *re_endp;  /* end pointer for REG_PEND */
+       struct re_guts *re_g;   /* none of your business :-) */
+} regex_t;
+
+typedef struct {
+       regoff_t rm_so;         /* start of match */
+       regoff_t rm_eo;         /* end of match */
+} regmatch_t;
+
+/* regcomp() flags */
+#define        REG_BASIC       0000
+#define        REG_EXTENDED    0001
+#define        REG_ICASE       0002
+#define        REG_NOSUB       0004
+#define        REG_NEWLINE     0010
+#define        REG_NOSPEC      0020
+#define        REG_PEND        0040
+#define        REG_DUMP        0200
+
+/* regerror() flags */
+#define        REG_NOMATCH      1
+#define        REG_BADPAT       2
+#define        REG_ECOLLATE     3
+#define        REG_ECTYPE       4
+#define        REG_EESCAPE      5
+#define        REG_ESUBREG      6
+#define        REG_EBRACK       7
+#define        REG_EPAREN       8
+#define        REG_EBRACE       9
+#define        REG_BADBR       10
+#define        REG_ERANGE      11
+#define        REG_ESPACE      12
+#define        REG_BADRPT      13
+#define        REG_EMPTY       14
+#define        REG_ASSERT      15
+#define        REG_INVARG      16
+#define        REG_ATOI        255     /* convert name to number (!) */
+#define        REG_ITOA        0400    /* convert number to name (!) */
+
+/* regexec() flags */
+#define        REG_NOTBOL      00001
+#define        REG_NOTEOL      00002
+#define        REG_STARTEND    00004
+#define        REG_TRACE       00400   /* tracing of execution */
+#define        REG_LARGE       01000   /* force large representation */
+#define        REG_BACKR       02000   /* force use of backref code */
+
+__BEGIN_DECLS
+int    regcomp __P((regex_t *, const char *, int));
+size_t regerror __P((int, const regex_t *, char *, size_t));
+int    regexec __P((const regex_t *,
+           const char *, size_t, regmatch_t [], int));
+void   regfree __P((regex_t *));
+__END_DECLS
+
+#endif /* !_REGEX_H_ */
diff --git a/src/backend/port/win32/regex/regex2.h b/src/backend/port/win32/regex/regex2.h
new file mode 100644 (file)
index 0000000..0261b53
--- /dev/null
@@ -0,0 +1,175 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)regex2.h    8.4 (Berkeley) 3/20/94
+ */
+
+/*
+ * First, the stuff that ends up in the outside-world include file
+*/
+/*
+ typedef off_t regoff_t;
+ typedef struct {
+       int re_magic;
+       size_t re_nsub;         // number of parenthesized subexpressions
+       const char *re_endp;    // end pointer for REG_PEND
+       struct re_guts *re_g;   // none of your business :-)
+ } regex_t;
+ typedef struct {
+       regoff_t rm_so;         // start of match
+       regoff_t rm_eo;         // end of match
+ } regmatch_t;
+*/
+/*
+ * internals of regex_t
+ */
+#define        MAGIC1  ((('r'^0200)<<8) | 'e')
+
+/*
+ * The internal representation is a *strip*, a sequence of
+ * operators ending with an endmarker.  (Some terminology etc. is a
+ * historical relic of earlier versions which used multiple strips.)
+ * Certain oddities in the representation are there to permit running
+ * the machinery backwards; in particular, any deviation from sequential
+ * flow must be marked at both its source and its destination.  Some
+ * fine points:
+ *
+ * - OPLUS_ and O_PLUS are *inside* the loop they create.
+ * - OQUEST_ and O_QUEST are *outside* the bypass they create.
+ * - OCH_ and O_CH are *outside* the multi-way branch they create, while
+ *   OOR1 and OOR2 are respectively the end and the beginning of one of
+ *   the branches.  Note that there is an implicit OOR2 following OCH_
+ *   and an implicit OOR1 preceding O_CH.
+ *
+ * In state representations, an operator's bit is on to signify a state
+ * immediately *preceding* "execution" of that operator.
+ */
+typedef unsigned long sop;     /* strip operator */
+typedef long sopno;
+#define        OPRMASK 0xf8000000
+#define        OPDMASK 0x07ffffff
+#define        OPSHIFT ((unsigned)27)
+#define        OP(n)   ((n)&OPRMASK)
+#define        OPND(n) ((n)&OPDMASK)
+#define        SOP(op, opnd)   ((op)|(opnd))
+/* operators                      meaning      operand                 */
+/*                                             (back, fwd are offsets) */
+#define        OEND    (1<<OPSHIFT)    /* endmarker    -                       */
+#define        OCHAR   (2<<OPSHIFT)    /* character    unsigned char           */
+#define        OBOL    (3<<OPSHIFT)    /* left anchor  -                       */
+#define        OEOL    (4<<OPSHIFT)    /* right anchor -                       */
+#define        OANY    (5<<OPSHIFT)    /* .            -                       */
+#define        OANYOF  (6<<OPSHIFT)    /* [...]        set number              */
+#define        OBACK_  (7<<OPSHIFT)    /* begin \d     paren number            */
+#define        O_BACK  (8<<OPSHIFT)    /* end \d       paren number            */
+#define        OPLUS_  (9<<OPSHIFT)    /* + prefix     fwd to suffix           */
+#define        O_PLUS  (10<<OPSHIFT)   /* + suffix     back to prefix          */
+#define        OQUEST_ (11<<OPSHIFT)   /* ? prefix     fwd to suffix           */
+#define        O_QUEST (12<<OPSHIFT)   /* ? suffix     back to prefix          */
+#define        OLPAREN (13<<OPSHIFT)   /* (            fwd to )                */
+#define        ORPAREN (14<<OPSHIFT)   /* )            back to (               */
+#define        OCH_    (15<<OPSHIFT)   /* begin choice fwd to OOR2             */
+#define        OOR1    (16<<OPSHIFT)   /* | pt. 1      back to OOR1 or OCH_    */
+#define        OOR2    (17<<OPSHIFT)   /* | pt. 2      fwd to OOR2 or O_CH     */
+#define        O_CH    (18<<OPSHIFT)   /* end choice   back to OOR1            */
+#define        OBOW    (19<<OPSHIFT)   /* begin word   -                       */
+#define        OEOW    (20<<OPSHIFT)   /* end word     -                       */
+
+/*
+ * Structure for [] character-set representation.  Character sets are
+ * done as bit vectors, grouped 8 to a byte vector for compactness.
+ * The individual set therefore has both a pointer to the byte vector
+ * and a mask to pick out the relevant bit of each byte.  A hash code
+ * simplifies testing whether two sets could be identical.
+ *
+ * This will get trickier for multicharacter collating elements.  As
+ * preliminary hooks for dealing with such things, we also carry along
+ * a string of multi-character elements, and decide the size of the
+ * vectors at run time.
+ */
+typedef struct {
+       uch *ptr;               /* -> uch [csetsize] */
+       uch mask;               /* bit within array */
+       uch hash;               /* hash code */
+       size_t smultis;
+       char *multis;           /* -> char[smulti]  ab\0cd\0ef\0\0 */
+} cset;
+/* note that CHadd and CHsub are unsafe, and CHIN doesn't yield 0/1 */
+#define        CHadd(cs, c)    ((cs)->ptr[(uch)(c)] |= (cs)->mask, (cs)->hash += (c))
+#define        CHsub(cs, c)    ((cs)->ptr[(uch)(c)] &= ~(cs)->mask, (cs)->hash -= (c))
+#define        CHIN(cs, c)     ((cs)->ptr[(uch)(c)] & (cs)->mask)
+#define        MCadd(p, cs, cp)        mcadd(p, cs, cp)        /* regcomp() internal fns */
+#define        MCsub(p, cs, cp)        mcsub(p, cs, cp)
+#define        MCin(p, cs, cp) mcin(p, cs, cp)
+
+/* stuff for character categories */
+typedef unsigned char cat_t;
+
+/*
+ * main compiled-expression structure
+ */
+struct re_guts {
+       int magic;
+#              define  MAGIC2  ((('R'^0200)<<8)|'E')
+       sop *strip;             /* malloced area for strip */
+       int csetsize;           /* number of bits in a cset vector */
+       int ncsets;             /* number of csets in use */
+       cset *sets;             /* -> cset [ncsets] */
+       uch *setbits;           /* -> uch[csetsize][ncsets/CHAR_BIT] */
+       int cflags;             /* copy of regcomp() cflags argument */
+       sopno nstates;          /* = number of sops */
+       sopno firststate;       /* the initial OEND (normally 0) */
+       sopno laststate;        /* the final OEND */
+       int iflags;             /* internal flags */
+#              define  USEBOL  01      /* used ^ */
+#              define  USEEOL  02      /* used $ */
+#              define  BAD     04      /* something wrong */
+       int nbol;               /* number of ^ used */
+       int neol;               /* number of $ used */
+       int ncategories;        /* how many character categories */
+       cat_t *categories;      /* ->catspace[-CHAR_MIN] */
+       char *must;             /* match must contain this string */
+       int mlen;               /* length of must */
+       size_t nsub;            /* copy of re_nsub */
+       int backrefs;           /* does it use back references? */
+       sopno nplus;            /* how deep does it nest +s? */
+       /* catspace must be last */
+       cat_t catspace[1];      /* actually [NC] */
+};
+
+/* misc utilities */
+#define        OUT     (CHAR_MAX+1)    /* a non-character value */
+#define        ISWORD(c)       (isalnum(c) || (c) == '_')
diff --git a/src/backend/port/win32/regex/regexec.c b/src/backend/port/win32/regex/regexec.c
new file mode 100644 (file)
index 0000000..3890b61
--- /dev/null
@@ -0,0 +1,181 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)regexec.c   8.3 (Berkeley) 3/20/94
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)regexec.c  8.3 (Berkeley) 3/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * the outer shell of regexec()
+ *
+ * This file includes engine.c *twice*, after muchos fiddling with the
+ * macros that code uses.  This lets the same code operate on two different
+ * representations for state sets.
+ */
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include "utils.h"
+#include "regex2.h"
+
+static int nope = 0;           /* for use in asserts; shuts lint up */
+
+/* macros for manipulating states, small version */
+#define        states  long
+#define        states1 states          /* for later use in regexec() decision */
+#define        CLEAR(v)        ((v) = 0)
+#define        SET0(v, n)      ((v) &= ~(1 << (n)))
+#define        SET1(v, n)      ((v) |= 1 << (n))
+#define        ISSET(v, n)     ((v) & (1 << (n)))
+#define        ASSIGN(d, s)    ((d) = (s))
+#define        EQ(a, b)        ((a) == (b))
+#define        STATEVARS       int dummy       /* dummy version */
+#define        STATESETUP(m, n)        /* nothing */
+#define        STATETEARDOWN(m)        /* nothing */
+#define        SETUP(v)        ((v) = 0)
+#define        onestate        int
+#define        INIT(o, n)      ((o) = (unsigned)1 << (n))
+#define        INC(o)  ((o) <<= 1)
+#define        ISSTATEIN(v, o) ((v) & (o))
+/* some abbreviations; note that some of these know variable names! */
+/* do "if I'm here, I can also be there" etc without branches */
+#define        FWD(dst, src, n)        ((dst) |= ((unsigned)(src)&(here)) << (n))
+#define        BACK(dst, src, n)       ((dst) |= ((unsigned)(src)&(here)) >> (n))
+#define        ISSETBACK(v, n) ((v) & ((unsigned)here >> (n)))
+/* function names */
+#define SNAMES                 /* engine.c looks after details */
+
+#include "engine.c"
+
+/* now undo things */
+#undef states
+#undef CLEAR
+#undef SET0
+#undef SET1
+#undef ISSET
+#undef ASSIGN
+#undef EQ
+#undef STATEVARS
+#undef STATESETUP
+#undef STATETEARDOWN
+#undef SETUP
+#undef onestate
+#undef INIT
+#undef INC
+#undef ISSTATEIN
+#undef FWD
+#undef BACK
+#undef ISSETBACK
+#undef SNAMES
+
+/* macros for manipulating states, large version */
+#define        states  char *
+#define        CLEAR(v)        memset(v, 0, m->g->nstates)
+#define        SET0(v, n)      ((v)[n] = 0)
+#define        SET1(v, n)      ((v)[n] = 1)
+#define        ISSET(v, n)     ((v)[n])
+#define        ASSIGN(d, s)    memcpy(d, s, m->g->nstates)
+#define        EQ(a, b)        (memcmp(a, b, m->g->nstates) == 0)
+#define        STATEVARS       int vn; char *space
+#define        STATESETUP(m, nv)       { (m)->space = malloc((nv)*(m)->g->nstates); \
+                               if ((m)->space == NULL) return(REG_ESPACE); \
+                               (m)->vn = 0; }
+#define        STATETEARDOWN(m)        { free((m)->space); }
+#define        SETUP(v)        ((v) = &m->space[m->vn++ * m->g->nstates])
+#define        onestate        int
+#define        INIT(o, n)      ((o) = (n))
+#define        INC(o)  ((o)++)
+#define        ISSTATEIN(v, o) ((v)[o])
+/* some abbreviations; note that some of these know variable names! */
+/* do "if I'm here, I can also be there" etc without branches */
+#define        FWD(dst, src, n)        ((dst)[here+(n)] |= (src)[here])
+#define        BACK(dst, src, n)       ((dst)[here-(n)] |= (src)[here])
+#define        ISSETBACK(v, n) ((v)[here - (n)])
+/* function names */
+#define        LNAMES                  /* flag */
+
+#include "engine.c"
+
+/*
+ - regexec - interface for matching
+ = extern int regexec(const regex_t *, const char *, size_t, \
+ =                                     regmatch_t [], int);
+ = #define     REG_NOTBOL      00001
+ = #define     REG_NOTEOL      00002
+ = #define     REG_STARTEND    00004
+ = #define     REG_TRACE       00400   // tracing of execution
+ = #define     REG_LARGE       01000   // force large representation
+ = #define     REG_BACKR       02000   // force use of backref code
+ *
+ * We put this here so we can exploit knowledge of the state representation
+ * when choosing which matcher to call.  Also, by this point the matchers
+ * have been prototyped.
+ */
+int                            /* 0 success, REG_NOMATCH failure */
+regexec(preg, string, nmatch, pmatch, eflags)
+const regex_t *preg;
+const char *string;
+size_t nmatch;
+regmatch_t pmatch[];
+int eflags;
+{
+       register struct re_guts *g = preg->re_g;
+#ifdef REDEBUG
+#      define  GOODFLAGS(f)    (f)
+#else
+#      define  GOODFLAGS(f)    ((f)&(REG_NOTBOL|REG_NOTEOL|REG_STARTEND))
+#endif
+
+       if (preg->re_magic != MAGIC1 || g->magic != MAGIC2)
+               return(REG_BADPAT);
+       assert(!(g->iflags&BAD));
+       if (g->iflags&BAD)              /* backstop for no-debug case */
+               return(REG_BADPAT);
+       eflags = GOODFLAGS(eflags);
+
+       if (g->nstates <= CHAR_BIT*sizeof(states1) && !(eflags&REG_LARGE))
+               return(smatcher(g, (char *)string, nmatch, pmatch, eflags));
+       else
+               return(lmatcher(g, (char *)string, nmatch, pmatch, eflags));
+}
diff --git a/src/backend/port/win32/regex/regexp.h b/src/backend/port/win32/regex/regexp.h
new file mode 100644 (file)
index 0000000..47c8e88
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1986 by University of Toronto.
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)regexp.h    8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef        _REGEXP_H_
+#define        _REGEXP_H_
+
+/*
+ * Definitions etc. for regexp(3) routines.
+ *
+ * Caveat:  this is V8 regexp(3) [actually, a reimplementation thereof],
+ * not the System V one.
+ */
+#define NSUBEXP  10
+typedef struct regexp {
+       char *startp[NSUBEXP];
+       char *endp[NSUBEXP];
+       char regstart;          /* Internal use only. */
+       char reganch;           /* Internal use only. */
+       char *regmust;          /* Internal use only. */
+       int regmlen;            /* Internal use only. */
+       char program[1];        /* Unwarranted chumminess with compiler. */
+} regexp;
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+regexp *regcomp __P((const char *));
+int regexec __P((const  regexp *, const char *));
+void regsub __P((const  regexp *, const char *, char *));
+void regerror __P((const char *));
+__END_DECLS
+
+#endif /* !_REGEXP_H_ */
diff --git a/src/backend/port/win32/regex/regfree.c b/src/backend/port/win32/regex/regfree.c
new file mode 100644 (file)
index 0000000..580fb75
--- /dev/null
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)regfree.c   8.3 (Berkeley) 3/20/94
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)regfree.c  8.3 (Berkeley) 3/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <regex.h>
+
+#include "utils.h"
+#include "regex2.h"
+
+/*
+ - regfree - free everything
+ = extern void regfree(regex_t *);
+ */
+void
+regfree(preg)
+regex_t *preg;
+{
+       register struct re_guts *g;
+
+       if (preg->re_magic != MAGIC1)   /* oops */
+               return;                 /* nice to complain, but hard */
+
+       g = preg->re_g;
+       if (g == NULL || g->magic != MAGIC2)    /* oops again */
+               return;
+       preg->re_magic = 0;             /* mark it invalid */
+       g->magic = 0;                   /* mark it invalid */
+
+       if (g->strip != NULL)
+               free((char *)g->strip);
+       if (g->sets != NULL)
+               free((char *)g->sets);
+       if (g->setbits != NULL)
+               free((char *)g->setbits);
+       if (g->must != NULL)
+               free(g->must);
+       free((char *)g);
+}
diff --git a/src/backend/port/win32/regex/utils.h b/src/backend/port/win32/regex/utils.h
new file mode 100644 (file)
index 0000000..6fb1e89
--- /dev/null
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)utils.h     8.3 (Berkeley) 3/20/94
+ */
+
+/* utility definitions */
+#define        DUPMAX          100000000       /* xxx is this right? */
+#define        INFINITY        (DUPMAX + 1)
+#define        NC              (CHAR_MAX - CHAR_MIN + 1)
+typedef unsigned char uch;
+
+/* switch off assertions (if not already off) if no REDEBUG */
+#ifndef REDEBUG
+#ifndef NDEBUG
+#define        NDEBUG  /* no assertions please */
+#endif
+#endif
+#include <assert.h>
+
+/* for old systems with bcopy() but no memmove() */
+#ifdef USEBCOPY
+#define        memmove(d, s, c)        bcopy(s, d, c)
+#endif
diff --git a/src/backend/port/win32/rusagestub.h b/src/backend/port/win32/rusagestub.h
new file mode 100644 (file)
index 0000000..0272c44
--- /dev/null
@@ -0,0 +1,29 @@
+/*-------------------------------------------------------------------------
+ *
+ * rusagestub.h--
+ *    Stubs for getrusage(3).
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef RUSAGESTUB_H
+#define RUSAGESTUB_H
+
+#include <sys/time.h>  /* for struct timeval */
+#include <limits.h>    /* for CLK_TCK */
+
+#define        RUSAGE_SELF     0
+#define        RUSAGE_CHILDREN -1
+
+struct rusage {
+    struct timeval ru_utime;           /* user time used */
+    struct timeval ru_stime;           /* system time used */
+};
+
+extern int getrusage(int who, struct rusage *rusage);
+
+#endif /* RUSAGESTUB_H */
diff --git a/src/backend/port/win32/sys/cdefs.h b/src/backend/port/win32/sys/cdefs.h
new file mode 100644 (file)
index 0000000..d98596b
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Berkeley Software Design, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)cdefs.h     8.7 (Berkeley) 1/21/94
+ */
+
+#ifndef        _CDEFS_H_
+#define        _CDEFS_H_
+
+#if defined(__cplusplus)
+#define        __BEGIN_DECLS   extern "C" {
+#define        __END_DECLS     };
+#else
+#define        __BEGIN_DECLS
+#define        __END_DECLS
+#endif
+
+/*
+ * The __CONCAT macro is used to concatenate parts of symbol names, e.g.
+ * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo.
+ * The __CONCAT macro is a bit tricky -- make sure you don't put spaces
+ * in between its arguments.  __CONCAT can also concatenate double-quoted
+ * strings produced by the __STRING macro, but this only works with ANSI C.
+ */
+#if defined(__STDC__) || defined(__cplusplus)
+#define        __P(protos)     protos          /* full-blown ANSI C */
+#define        __CONCAT(x,y)   x ## y
+#define        __STRING(x)     #x
+
+#define        __const         const           /* define reserved names to standard */
+#define        __signed        signed
+#define        __volatile      volatile
+#if defined(__cplusplus)
+#define        __inline        inline          /* convert to C++ keyword */
+#else
+#ifndef __GNUC__
+#define        __inline                        /* delete GCC keyword */
+#endif /* !__GNUC__ */
+#endif /* !__cplusplus */
+
+#else  /* !(__STDC__ || __cplusplus) */
+#define        __P(protos)     ()              /* traditional C preprocessor */
+#define        __CONCAT(x,y)   x/**/y
+#define        __STRING(x)     "x"
+
+#ifndef __GNUC__
+#define        __const                         /* delete pseudo-ANSI C keywords */
+#define        __inline
+#define        __signed
+#define        __volatile
+/*
+ * In non-ANSI C environments, new programs will want ANSI-only C keywords
+ * deleted from the program and old programs will want them left alone.
+ * When using a compiler other than gcc, programs using the ANSI C keywords
+ * const, inline etc. as normal identifiers should define -DNO_ANSI_KEYWORDS.
+ * When using "gcc -traditional", we assume that this is the intent; if
+ * __GNUC__ is defined but __STDC__ is not, we leave the new keywords alone.
+ */
+#ifndef        NO_ANSI_KEYWORDS
+#define        const                           /* delete ANSI C keywords */
+#define        inline
+#define        signed
+#define        volatile
+#endif
+#endif /* !__GNUC__ */
+#endif /* !(__STDC__ || __cplusplus) */
+
+/*
+ * GCC1 and some versions of GCC2 declare dead (non-returning) and
+ * pure (no side effects) functions using "volatile" and "const";
+ * unfortunately, these then cause warnings under "-ansi -pedantic".
+ * GCC2 uses a new, peculiar __attribute__((attrs)) style.  All of
+ * these work for GNU C++ (modulo a slight glitch in the C++ grammar
+ * in the distribution version of 2.5.5).
+ */
+#if !defined(__GNUC__) || __GNUC__ < 2 || __GNUC_MINOR__ < 5
+#define        __attribute__(x)        /* delete __attribute__ if non-gcc or gcc1 */
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+#define        __dead          __volatile
+#define        __pure          __const
+#endif
+#endif
+
+/* Delete pseudo-keywords wherever they are not available or needed. */
+#ifndef __dead
+#define        __dead
+#define        __pure
+#endif
+
+typedef long off_t;
+
+#endif /* !_CDEFS_H_ */
diff --git a/src/backend/port/win32/sys/file.h b/src/backend/port/win32/sys/file.h
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/src/backend/port/win32/sys/ipc.h b/src/backend/port/win32/sys/ipc.h
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/src/backend/port/win32/sys/param.h b/src/backend/port/win32/sys/param.h
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/src/backend/port/win32/sys/sem.h b/src/backend/port/win32/sys/sem.h
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/src/backend/port/win32/sys/shm.h b/src/backend/port/win32/sys/shm.h
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/src/backend/port/win32/sys/time.h b/src/backend/port/win32/sys/time.h
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/src/backend/postmaster/Makefile.inc b/src/backend/postmaster/Makefile.inc
new file mode 100644 (file)
index 0000000..cbd8218
--- /dev/null
@@ -0,0 +1,16 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the postmaster module
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+VPATH:= $(VPATH):$(CURDIR)/postmaster
+
+SRCS_POSTMASTER= postmaster.c
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
new file mode 100644 (file)
index 0000000..e2607ed
--- /dev/null
@@ -0,0 +1,1122 @@
+/*-------------------------------------------------------------------------
+ *
+ * postmaster.c--
+ *    This program acts as a clearing house for requests to the
+ *    POSTGRES system.  Frontend programs send a startup message
+ *    to the Postmaster and the postmaster uses the info in the
+ *    message to setup a backend process.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *
+ * Initialization:
+ *     The Postmaster sets up a few shared memory data structures 
+ *     for the backends.  It should at the very least initialize the
+ *     lock manager.
+ *
+ * Synchronization:
+ *     The Postmaster shares memory with the backends and will have to lock
+ *     the shared memory it accesses.  The Postmaster should never block
+ *     on messages from clients.
+ *     
+ * Garbage Collection:
+ *     The Postmaster cleans up after backends if they have an emergency
+ *     exit and/or core dump.
+ *
+ * Communication:
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "libpq/pqsignal.h"    /* substitute for <signal.h> */
+#include <string.h>
+#include <stdlib.h>
+#ifndef WIN32
+#include <unistd.h>
+#endif /* WIN32 */
+#include <ctype.h>
+#include <sys/types.h>         /* for fd_set stuff */
+#include <sys/stat.h>          /* for umask */
+#include <sys/time.h>
+#include <sys/param.h>         /* for MAXHOSTNAMELEN on most */
+#ifdef WIN32
+#include <winsock.h>
+#include <limits.h>
+#define MAXINT        INT_MAX
+#else
+#include <netdb.h>             /* for MAXHOSTNAMELEN on some */
+# if defined(PORTNAME_BSD44_derived) || defined(PORTNAME_bsdi)
+# include <machine/limits.h>
+# define MAXINT                INT_MAX
+# else
+# include <values.h>
+# endif /* !PORTNAME_BSD44_derived */
+#include <sys/wait.h>
+#endif /* WIN32 */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#if defined(PORTNAME_aix)
+#include <sys/select.h>
+#endif /* PORTNAME_aix */
+
+#include "storage/ipc.h"
+#include "libpq/libpq.h"
+#include "libpq/auth.h"
+#include "libpq/pqcomm.h"
+#include "miscadmin.h"
+#include "lib/dllist.h"
+#include "utils/mcxt.h"
+#include "storage/proc.h"
+#include "utils/elog.h"
+
+#ifdef DBX_VERSION
+#define FORK() (0)
+#else
+#if defined(PORTNAME_irix5)
+/* IRIX 5 does not have vfork() */
+#define FORK() fork()
+#else
+#define FORK() vfork()
+#endif
+#endif
+
+/*
+ * Info for garbage collection.  Whenever a process dies, the Postmaster
+ * cleans up after it.  Currently, NO information is required for cleanup,
+ * but I left this structure around in case that changed.
+ */
+typedef struct bkend {
+    int        pid;            /* process id of backend */
+} Backend;
+
+/* list of active backends.  For garbage collection only now. */
+
+static Dllist*  BackendList;
+
+/* list of ports associated with still open, but incomplete connections */
+static Dllist*  PortList;
+
+static short   PostPortName = -1;
+static short   ActiveBackends = FALSE;
+static int     NextBackendId = MAXINT;         /* XXX why? */
+static char    *progname = (char *) NULL;
+
+char           *DataDir = (char *) NULL;
+    
+/*
+ * Default Values
+ */
+static char    Execfile[MAXPATHLEN] = "";
+
+static int     ServerSock = INVALID_SOCK;      /* stream socket server */
+
+/*
+ * Set by the -o option
+ */
+static char    ExtraOptions[ARGV_SIZE] = "";
+
+/*
+ * These globals control the behavior of the postmaster in case some
+ * backend dumps core.  Normally, it kills all peers of the dead backend
+ * and reinitializes shared memory.  By specifying -s or -n, we can have
+ * the postmaster stop (rather than kill) peers and not reinitialize
+ * shared data structures.
+ */
+static int     Reinit = 1;
+static int     SendStop = 0;
+
+static int MultiplexedBackends = 0;
+static int MultiplexedBackendPort;
+
+#ifdef HBA
+static int useHostBasedAuth = 1;
+#else
+static int useHostBasedAuth = 0;
+#endif
+
+/* 
+ * postmaster.c - function prototypes
+ */
+static void pmdaemonize(void);
+static int ConnStartup(Port *port);
+static int ConnCreate(int serverFd, int *newFdP);
+static void reset_shared(short port);
+#if defined(PORTNAME_linux)
+static void pmdie(int);
+static void reaper(int);
+static void dumpstatus(int);
+#else
+static void pmdie(void);
+static void reaper(void);
+static void dumpstatus();
+#endif
+static void CleanupProc(int pid, int exitstatus);
+static int DoExec(StartupInfo *packet, int portFd);
+static void ExitPostmaster(int status);
+static void usage();
+static void checkDataDir();
+
+int ServerLoop(void);
+int BackendStartup(StartupInfo *packet, Port *port, int *pidPtr);
+
+extern char *optarg;
+extern int optind, opterr;
+
+int
+PostmasterMain(int argc, char *argv[])
+{
+    extern int NBuffers;       /* from buffer/bufmgr.c */
+    extern bool        IsPostmaster;   /* from smgr/mm.c */
+    int        opt;
+    char       *hostName;
+    int                status;
+    int                silentflag = 0;
+    char       hostbuf[MAXHOSTNAMELEN];
+#ifdef WIN32
+    WSADATA WSAData;
+#endif /* WIN32 */
+    
+    progname = argv[0];
+    
+    /* for security, no dir or file created can be group or other accessible */
+    (void) umask((mode_t) 0077);
+    
+    if (!(hostName = getenv("PGHOST"))) {
+       if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0)
+           (void) strcpy(hostbuf, "localhost");
+       hostName = hostbuf;
+    }
+    
+    opterr = 0;
+    while ((opt = getopt(argc, argv, "a:B:b:D:dmM:no:p:Ss")) != EOF) {
+       switch (opt) {
+       case 'a': 
+           /* Set the authentication system. */
+           be_setauthsvc(optarg);
+           break;
+       case 'B': 
+           /*
+            * The number of buffers to create.  Setting this
+            * option means we have to start each backend with
+            * a -B # to make sure they know how many buffers
+            * were allocated. 
+            */
+           NBuffers = atol(optarg);
+           (void) strcat(ExtraOptions, " -B ");
+           (void) strcat(ExtraOptions, optarg);
+           break;
+       case 'b': 
+           /* Set the backend executable file to use. */
+           if (!ValidateBackend(optarg))
+               strcpy(Execfile, optarg);
+           else {
+               fprintf(stderr, "%s: invalid backend \"%s\"\n",
+                       progname, optarg);
+               exit(2);
+           }
+           break;
+       case 'D': 
+           /* Set PGDATA from the command line. */
+           DataDir = optarg;
+           break;
+       case 'd': 
+           /*
+            * Turn on debugging for the postmaster and the backend
+            * servers descended from it.
+            */
+           if ((optind < argc) && *argv[optind] != '-') {
+               DebugLvl = atoi(argv[optind]);
+               optind++;
+           }
+           else
+               DebugLvl = 1;
+           break;
+         case 'm':
+           MultiplexedBackends = 1;
+           MultiplexedBackendPort = atoi(optarg);
+           break;
+       case 'M':
+           /* ignore this flag.  This may be passed in because the
+              program was run as 'postgres -M' instead of 'postmaster' */
+           break;
+       case 'n': 
+           /* Don't reinit shared mem after abnormal exit */
+           Reinit = 0;
+           break;
+       case 'o': 
+           /*
+            * Other options to pass to the backend on the
+            * command line -- useful only for debugging.
+            */
+           (void) strcat(ExtraOptions, " ");
+           (void) strcat(ExtraOptions, optarg);
+           break;
+       case 'p': 
+           /* Set PGPORT by hand. */
+           PostPortName = (short) atoi(optarg);
+           break;
+       case 'S':
+           /*
+            * Start in 'S'ilent mode (disassociate from controlling tty).
+            * You may also think of this as 'S'ysV mode since it's most
+            * badly needed on SysV-derived systems like SVR4 and HP-UX.
+            */
+           silentflag = 1;
+           break;
+       case 's':
+           /*
+            * In the event that some backend dumps core,
+            * send SIGSTOP, rather than SIGUSR1, to all
+            * its peers.  This lets the wily post_hacker
+            * collect core dumps from everyone.
+            */
+           SendStop = 1;
+           break;
+       default: 
+           /* usage() never returns */
+           usage(progname);
+           break;
+       }
+    }
+    if (PostPortName == -1)
+       PostPortName = pq_getport();
+    
+    IsPostmaster = true;
+    
+    if (!DataDir)
+       DataDir = GetPGData();
+
+    /*
+     * check whether the data directory exists. Passing this test doesn't
+     * gaurantee we are accessing the right data base but is a first barrier
+     * to site administrators who starts up the postmaster without realizing
+     * it cannot access the data base.
+     */
+    checkDataDir();
+    
+    if (!Execfile[0] && FindBackend(Execfile, argv[0]) < 0) {
+       fprintf(stderr, "%s: could not find backend to execute...\n",
+               argv[0]);
+       exit(1);
+    }
+    
+
+#ifdef WIN32
+    if ((status = WSAStartup(MAKEWORD(1,1), &WSAData)) == 0)
+      (void) printf("%s\nInitializing WinSock: %s\n", WSAData.szDescription, WSAData.szSystemStatus);
+    else
+    {
+      fprintf(stderr, "Error initializing WinSock: %d is the err", status);
+      exit(1);
+    }
+     _nt_init();
+     _nt_attach();
+#endif /* WIN32 */
+
+    status = StreamServerPort(hostName, PostPortName, &ServerSock);
+    if (status != STATUS_OK) {
+       fprintf(stderr, "%s: cannot create stream port\n",
+               progname);
+       exit(1);
+    }
+    
+    /* set up shared memory and semaphores */
+    EnableMemoryContext(TRUE);
+    reset_shared(PostPortName);
+    
+    /* 
+     * Initialize the list of active backends.  This list is only
+     * used for garbage collecting the backend processes.
+     */
+    BackendList = DLNewList();
+    PortList = DLNewList();
+
+    if (silentflag)
+       pmdaemonize();
+    
+    signal(SIGINT, pmdie);
+#ifndef WIN32
+    signal(SIGCHLD, reaper);
+    signal(SIGTTIN, SIG_IGN);
+    signal(SIGTTOU, SIG_IGN);
+    signal(SIGHUP, pmdie);
+    signal(SIGTERM, pmdie);
+    signal(SIGCONT, dumpstatus);
+#endif /* WIN32 */
+    
+
+    status = ServerLoop();
+    
+    ExitPostmaster(status != STATUS_OK);
+    return 0; /* not reached */
+}
+
+static void
+pmdaemonize()
+{
+    int i;
+    
+    if (fork())
+       exit(0);
+    
+    if (setsid() < 0) {
+       fprintf(stderr, "%s: ", progname);
+       perror("cannot disassociate from controlling TTY");
+       exit(1);
+    }
+    i = open(NULL_DEV, O_RDWR);
+    (void) dup2(i, 0);
+    (void) dup2(i, 1);
+    (void) dup2(i, 2);
+    (void) close(i);
+}
+
+static void
+usage(char *progname)
+{
+    fprintf(stderr, "usage: %s [options..]\n", progname);
+    fprintf(stderr, "\t-a authsys\tdo/do not permit use of an authentication system\n");
+    fprintf(stderr, "\t-B nbufs\tset number of shared buffers\n");
+    fprintf(stderr, "\t-b backend\tuse a specific backend server executable\n");
+    fprintf(stderr, "\t-d [1|2|3]\tset debugging level\n");
+    fprintf(stderr, "\t-D datadir\tset data directory\n");
+    fprintf(stderr, "\t-m \tstart up multiplexing backends\n");
+    fprintf(stderr, "\t-n\t\tdon't reinitialize shared memory after abnormal exit\n");
+    fprintf(stderr, "\t-o option\tpass 'option' to each backend servers\n");
+    fprintf(stderr, "\t-p port\t\tspecify port for postmaster to listen on\n");
+    fprintf(stderr, "\t-S\t\tsilent mode (disassociate from tty)\n");
+    fprintf(stderr, "\t-s\t\tsend SIGSTOP to all backend servers if one dies\n");
+    exit(1);
+}
+
+int
+ServerLoop()
+{
+    int                serverFd = ServerSock;
+    fd_set     rmask, basemask;
+    int                nSockets, nSelected, status, newFd;
+    Dlelem   *prev, *curr;
+/*    int orgsigmask = sigblock(0); */
+    sigset_t oldsigmask, newsigmask;
+    
+    nSockets = ServerSock + 1;
+    FD_ZERO(&basemask);
+    FD_SET(ServerSock, &basemask);
+    
+    sigprocmask(0,0,&oldsigmask);
+    sigemptyset(&newsigmask);
+    sigaddset(&newsigmask,SIGCHLD);
+    for (;;) {
+/*     sigsetmask(orgsigmask); */
+       sigprocmask(SIG_SETMASK,&oldsigmask,0);
+       newFd = -1;
+       memmove((char *) &rmask, (char *) &basemask, sizeof(fd_set));
+       if ((nSelected = select(nSockets, &rmask,
+                               (fd_set *) NULL,
+                               (fd_set *) NULL,
+                               (struct timeval *) NULL)) < 0) {
+           if (errno == EINTR)
+               continue;
+           fprintf(stderr, "%s: ServerLoop: select failed\n",
+                   progname);
+           return(STATUS_ERROR);
+           /* [TRH]
+            * To avoid race conditions, block SIGCHLD signals while we are
+            * handling the request. (both reaper() and ConnCreate()
+            * manipulate the BackEnd list, and reaper() calls free() which is
+            * usually non-reentrant.)
+            */
+           sigprocmask(SIG_BLOCK, &newsigmask, &oldsigmask);
+/*         sigblock(sigmask(SIGCHLD)); */      /* XXX[TRH] portability */
+           
+       }
+       if (DebugLvl > 1) {
+           fprintf(stderr, "%s: ServerLoop: %d sockets pending\n",
+                   progname, nSelected);
+       }
+       
+       /* new connection pending on our well-known port's socket */
+       if (FD_ISSET(ServerSock, &rmask)) {
+           /*
+            * connect and make an addition to PortList.  If
+            * the connection dies and we notice it, just forget
+            * about the whole thing.
+            */
+           if (ConnCreate(serverFd, &newFd) == STATUS_OK) {
+               if (newFd >= nSockets)
+                   nSockets = newFd + 1;
+               FD_SET(newFd, &rmask);
+               FD_SET(newFd, &basemask);
+               if (DebugLvl)
+                   fprintf(stderr, "%s: ServerLoop: connect on %d\n",
+                           progname, newFd);
+           }
+           --nSelected;
+           FD_CLR(ServerSock, &rmask);
+         }
+
+       if (DebugLvl > 1) {
+           fprintf(stderr, "%s: ServerLoop:\tnSelected=%d\n",
+                   progname, nSelected);
+           curr = DLGetHead(PortList);
+           while (curr) {
+               Port *port = DLE_VAL(curr);
+               
+               fprintf(stderr, "%s: ServerLoop:\t\tport %d%s pending\n",
+                       progname, port->sock,
+                       FD_ISSET(port->sock, &rmask)
+                       ? "" :
+                       " not");
+               curr = DLGetSucc(curr);
+           }
+       }
+       
+       curr = DLGetHead(PortList);
+
+       while (curr) {
+           Port *port = (Port*)DLE_VAL(curr);
+           int lastbytes = port->nBytes;
+           
+           if (FD_ISSET(port->sock, &rmask) && port->sock != newFd) {
+               if (DebugLvl > 1)
+                   fprintf(stderr, "%s: ServerLoop:\t\thandling %d\n",
+                           progname, port->sock);
+               --nSelected;
+               
+               /*
+                * Read the incoming packet into its packet buffer.
+                * Read the connection id out of the packet so we
+                * know who the packet is from.
+                */
+               status = PacketReceive(port, &port->buf, NON_BLOCKING);
+               switch (status) {
+               case STATUS_OK: 
+                   ConnStartup(port);
+                   ActiveBackends = TRUE;
+                   /*FALLTHROUGH*/
+               case STATUS_INVALID: 
+                   if (DebugLvl)
+                       fprintf(stderr, "%s: ServerLoop:\t\tdone with %d\n",
+                               progname, port->sock);
+                   break;
+               case STATUS_BAD_PACKET:
+                   /*
+                    * This is a bogus client, kill the connection 
+                    * and forget the whole thing.
+                    */
+                   if (DebugLvl)
+                       fprintf(stderr, "%s: ServerLoop:\t\tbad packet format (reported packet size of %d read on port %d\n", progname, port->nBytes, port->sock);
+                   break;
+               case STATUS_NOT_DONE:
+                   if (DebugLvl)
+                       fprintf(stderr, "%s: ServerLoop:\t\tpartial packet (%d bytes actually read) on %d\n",
+                               progname, port->nBytes, port->sock);
+                   /*
+                    * If we've received at least a PacketHdr's worth of data
+                    * and we're still receiving data each time we read, we're
+                    * ok.  If the client gives us less than a PacketHdr at
+                    * the beginning, just kill the connection and forget
+                    * about the whole thing.
+                    */
+                   if (lastbytes < port->nBytes) {
+                       if (DebugLvl)
+                           fprintf(stderr, "%s: ServerLoop:\t\tpartial packet on %d ok\n",
+                                   progname, port->sock);
+                       curr = DLGetSucc(curr);
+                       continue;
+                   }
+                   break;
+               case STATUS_ERROR:      /* system call error - die */
+                   fprintf(stderr, "%s: ServerLoop:\t\terror receiving packet\n",
+                           progname);
+                   return(STATUS_ERROR);
+               }
+               FD_CLR(port->sock, &basemask);
+               StreamClose(port->sock);
+               prev = DLGetPred(curr);
+               DLRemove(curr);
+               DLFreeElem(curr);
+               curr = 0;
+           }
+           curr = DLGetSucc(curr);
+       }
+       Assert(nSelected == 0);
+    }
+}
+
+static int
+ConnStartup(Port *port)                /* receiving port */
+{
+    MsgType            msgType;
+    char               namebuf[NAMEDATALEN + 1];
+/*    StartupInfo      *sp;*/
+    int                        pid;
+    PacketBuf *p;
+/*    sp = PacketBuf2StartupInfo(&port->buf);*/
+    StartupInfo sp;
+    char *tmp;
+
+    p = &port->buf;
+
+    sp.database[0]='\0';
+    sp.user[0]='\0';
+    sp.options[0]='\0';
+    sp.execFile[0]='\0';
+    sp.tty[0]='\0';
+
+    tmp= p->data;
+    strncpy(sp.database,tmp,sizeof(sp.database));
+    tmp += sizeof(sp.database);
+    strncpy(sp.user,tmp, sizeof(sp.user));
+    tmp += sizeof(sp.user);
+    strncpy(sp.options,tmp, sizeof(sp.options));
+    tmp += sizeof(sp.options);
+    strncpy(sp.execFile,tmp, sizeof(sp.execFile));
+    tmp += sizeof(sp.execFile);
+    strncpy(sp.tty,tmp, sizeof(sp.tty));
+
+    msgType = ntohl(port->buf.msgtype);
+
+    (void) strncpy(namebuf, sp.user, NAMEDATALEN);
+    namebuf[NAMEDATALEN] = '\0';
+    if (!namebuf[0]) {
+       fprintf(stderr, "%s: ConnStartup: no user name specified\n",
+               progname);
+       return(STATUS_ERROR);
+    }
+    
+    if (msgType == STARTUP_MSG && useHostBasedAuth)
+       msgType = STARTUP_HBA_MSG;
+    if (be_recvauth(msgType, port, namebuf,&sp) != STATUS_OK) {
+       fprintf(stderr, "%s: ConnStartup: authentication failed\n",
+               progname);
+       return(STATUS_ERROR);
+    }
+    
+    if (BackendStartup(&sp, port, &pid) != STATUS_OK) {
+       fprintf(stderr, "%s: ConnStartup: couldn't start backend\n",
+               progname);
+       return(STATUS_ERROR);
+    }
+    
+    return(STATUS_OK);
+}
+
+/*
+ * ConnCreate -- create a local connection data structure
+ */
+static int
+ConnCreate(int serverFd, int *newFdP)
+{
+    int                status;
+    Port       *port;
+    
+
+    if (!(port = (Port *) calloc(1, sizeof(Port)))) { 
+       fprintf(stderr, "%s: ConnCreate: malloc failed\n",
+               progname);
+       ExitPostmaster(1);
+    }
+
+    if ((status = StreamConnection(serverFd, port)) != STATUS_OK) {
+       StreamClose(port->sock);
+       free(port);
+    }
+    else {
+       DLAddHead(PortList, DLNewElem(port));
+       *newFdP = port->sock;
+    }
+    
+    return (status);
+}
+
+/*
+ * reset_shared -- reset shared memory and semaphores
+ */
+static void
+reset_shared(short port)
+{
+    IPCKey     key;
+    
+    key = SystemPortAddressCreateIPCKey((SystemPortAddress) port);
+    CreateSharedMemoryAndSemaphores(key);
+    ActiveBackends = FALSE;
+}
+
+/*
+ * pmdie -- signal handler for cleaning up after a kill signal.
+ */
+static void
+#if defined(PORTNAME_linux)
+pmdie(int i)
+#else
+pmdie()
+#endif
+{
+    exitpg(0);
+}
+
+/*
+ * Reaper -- signal handler to cleanup after a backend (child) dies.
+ */
+static void
+#if defined(PORTNAME_linux)
+reaper(int i)
+#else
+reaper()
+#endif
+{
+    int        status;         /* backend exit status */
+    int        pid;            /* process id of dead backend */
+    
+    if (DebugLvl)
+       fprintf(stderr, "%s: reaping dead processes...\n",
+               progname);
+#ifndef WIN32
+    while((pid = waitpid(-1, &status, WNOHANG)) > 0)
+       CleanupProc(pid, status);
+#endif /* WIN32 */
+}
+
+/*
+ * CleanupProc -- cleanup after terminated backend.
+ *
+ * Remove all local state associated with backend.
+ *
+ * Dillon's note: should log child's exit status in the system log.
+ */
+static void
+CleanupProc(int pid,
+           int exitstatus)     /* child's exit status. */
+{
+    Dlelem *prev, *curr;
+    Backend    *bp;
+    int                sig;
+    
+    if (DebugLvl) {
+       fprintf(stderr, "%s: CleanupProc: pid %d exited with status %d\n",
+               progname, pid, exitstatus);
+    }
+    /*
+     * -------------------------
+     * If a backend dies in an ugly way (i.e. exit status not 0) then
+     * we must signal all other backends to quickdie.  If exit status
+     * is zero we assume everything is hunky dory and simply remove the
+     * backend from the active backend list.
+     * -------------------------
+     */
+    if (!exitstatus) {
+       curr = DLGetHead(BackendList);
+       while (curr) {
+           bp = (Backend*)DLE_VAL(curr);
+           if (bp->pid == pid) {
+               DLRemove(curr);
+               DLFreeElem(curr);
+               break;
+           }
+           curr = DLGetSucc(curr);
+       }
+
+       ProcRemove(pid);
+
+       return;
+    }
+    
+    curr = DLGetHead(BackendList);
+    while (curr) {
+       bp = (Backend*)DLE_VAL(curr);
+       
+       /*
+        * -----------------
+        * SIGUSR1 is the special signal that sez exit without exitpg
+        * and let the user know what's going on. ProcSemaphoreKill()
+        * cleans up the backends semaphore.  If SendStop is set (-s on
+                                                                 * the command line), then we send a SIGSTOP so that we can
+                                                                 * collect core dumps from all backends by hand.
+                                                                 * -----------------
+                                                                 */
+#ifndef WIN32
+       sig = (SendStop) ? SIGSTOP : SIGUSR1;
+       if (bp->pid != pid) {
+           if (DebugLvl)
+               fprintf(stderr, "%s: CleanupProc: sending %s to process %d\n",
+                       progname,
+                       (sig == SIGUSR1)
+                       ? "SIGUSR1" : "SIGSTOP",
+                       bp->pid);
+           (void) kill(bp->pid, sig);
+       }
+#endif /* WIN32 */
+       ProcRemove(bp->pid);
+       
+       prev = DLGetPred(curr);
+       DLRemove(curr);
+       DLFreeElem(curr);
+       if (!prev) {            /* removed head */
+           curr = DLGetHead(BackendList); 
+           continue;
+       }
+       curr = DLGetSucc(curr);
+    }
+    /*
+     * -------------
+     * Quasi_exit means run all of the on_exitpg routines but don't
+     * acutally call exit().  The on_exit list of routines to do is
+     * also truncated.
+     *
+     * Nothing up my sleeve here, ActiveBackends means that since the
+     * last time we recreated shared memory and sems another frontend
+     * has requested and received a connection and I have forked off
+     * another backend.  This prevents me from reinitializing shared
+     * stuff more than once for the set of backends that caused the
+     * failure and were killed off.
+     * ----------------
+     */
+    if (ActiveBackends == TRUE && Reinit) {
+       if (DebugLvl)
+           fprintf(stderr, "%s: CleanupProc: reinitializing shared memory and semaphores\n",
+                   progname);
+       quasi_exitpg();
+       reset_shared(PostPortName);
+    }
+}
+
+/*
+ * BackendStartup -- start backend process
+ *
+ * returns: STATUS_ERROR if the fork/exec failed, STATUS_OK
+ *     otherwise.
+ *
+ */
+int
+BackendStartup(StartupInfo *packet, /* client's startup packet */
+              Port *port,
+              int *pidPtr)
+{
+    Backend*      bn; /* for backend cleanup */
+    int                pid, i;
+    static char        envEntry[4][2 * ARGV_SIZE];
+    
+    for (i = 0; i < 4; ++i) {
+       memset(envEntry[i], 2*ARGV_SIZE,0);
+    }
+    /*
+     * Set up the necessary environment variables for the backend
+     * This should really be some sort of message....
+     */
+    sprintf(envEntry[0], "POSTPORT=%d", PostPortName);
+    putenv(envEntry[0]);
+    sprintf(envEntry[1], "POSTID=%d", NextBackendId);
+    putenv(envEntry[1]);
+    sprintf(envEntry[2], "PG_USER=%s", packet->user);
+    putenv(envEntry[2]);
+    if (!getenv("PGDATA")) {
+       sprintf(envEntry[3], "PGDATA=%s", DataDir);
+       putenv(envEntry[3]);
+    }
+    if (DebugLvl > 2) {
+       char            **p;
+       extern char     **environ;
+       
+       fprintf(stderr, "%s: BackendStartup: environ dump:\n",
+               progname);
+       fprintf(stderr, "-----------------------------------------\n");
+       for (p = environ; *p; ++p)
+           fprintf(stderr, "\t%s\n", *p);
+       fprintf(stderr, "-----------------------------------------\n");
+    }
+    
+#ifndef WIN32
+    if ((pid = FORK()) == 0) { /* child */
+       if (DoExec(packet, port->sock))
+           fprintf(stderr, "%s child[%d]: BackendStartup: execv failed\n",
+                   progname, pid);
+       /* use _exit to keep from double-flushing stdio */
+       _exit(1);
+    }
+
+    /* in parent */
+    if (pid < 0) {
+       fprintf(stderr, "%s: BackendStartup: fork failed\n",
+               progname);
+       return(STATUS_ERROR);
+    }
+#else
+    pid = DoExec(packet, port->sock);
+    if (pid == FALSE) {
+       fprintf(stderr, "%s: BackendStartup: CreateProcess failed\n",
+               progname);
+       return(STATUS_ERROR);
+    }
+#endif /* WIN32 */
+    
+    if (DebugLvl)
+       fprintf(stderr, "%s: BackendStartup: pid %d user %s db %s socket %d\n",
+               progname, pid, packet->user,
+               (packet->database[0] == '\0' ? packet->user : packet->database),
+               port->sock);
+    
+    /* adjust backend counter */
+    /* XXX Don't know why this is done, but for now backend needs it */
+    NextBackendId -= 1;
+    
+    /*
+     * Everything's been successful, it's safe to add this backend to our
+     * list of backends.
+     */
+    if (!(bn = (Backend *) calloc(1, sizeof (Backend))))  {
+       fprintf(stderr, "%s: BackendStartup: malloc failed\n",
+               progname);
+       ExitPostmaster(1);
+    }
+  
+    bn->pid = pid;
+    DLAddHead(BackendList,DLNewElem(bn));
+
+    if (MultiplexedBackends)
+       MultiplexedBackendPort++;
+  
+    *pidPtr = pid;
+    return(STATUS_OK);
+}
+
+/*
+ * split_opts -- destructively load a string into an argv array
+ *
+ * Since no current POSTGRES arguments require any quoting characters,
+ * we can use the simple-minded tactic of assuming each set of space-
+ * delimited characters is a separate argv element.
+ *
+ * If you don't like that, well, we *used* to pass the whole option string
+ * as ONE argument to execl(), which was even less intelligent...
+ */
+void
+split_opts(char **argv, int *argcp, char *s)
+{
+    int        i = *argcp;
+    
+    while (s && *s) {
+       while (isspace(*s))
+           ++s;
+       if (*s)
+           argv[i++] = s;
+       while (*s && !isspace(*s))
+           ++s;
+       if (isspace(*s))
+           *s++ = '\0';
+    }
+    *argcp = i;
+}
+
+/*
+ * DoExec -- set up the argument list and perform an execv system call
+ *
+ * Tries fairly hard not to dork with anything that isn't automatically
+ * allocated so we don't do anything weird to the postmaster when it gets
+ * its thread back.  (This is vfork() we're talking about.  If we're using
+ * fork() because we don't have vfork(), then we don't really care.)
+ *
+ * returns: 
+ *     Shouldn't return at all.
+ *     If execv() fails, return status.
+ */
+static int
+DoExec(StartupInfo *packet, int portFd)
+{
+    char       execbuf[MAXPATHLEN];
+    char       portbuf[ARGV_SIZE];
+    char        mbbuf[ARGV_SIZE];
+    char       debugbuf[ARGV_SIZE];
+    char       ttybuf[ARGV_SIZE + 1];
+    char       argbuf[(2 * ARGV_SIZE) + 1];
+    /*
+     * each argument takes at least three chars, so we can't
+     * have more than ARGV_SIZE arguments in (2 * ARGV_SIZE)
+     * chars (i.e., packet->options plus ExtraOptions)...
+     */
+    char       *av[ARGV_SIZE];
+    char       dbbuf[ARGV_SIZE + 1];
+    int        ac = 0;
+    int i;
+#ifdef WIN32
+    char      win32_args[(2 * ARGV_SIZE) + 1];
+    PROCESS_INFORMATION piProcInfo;
+    STARTUPINFO siStartInfo;
+    BOOL fSuccess;
+#endif /* WIN32 */
+
+    (void) strncpy(execbuf, Execfile, MAXPATHLEN);
+    execbuf[MAXPATHLEN - 1] = '\0';
+    av[ac++] = execbuf;
+    
+    /* Tell the backend it is being called from the postmaster */
+    av[ac++] = "-p";
+    
+    /*
+     *  Pass the requested debugging level along to the backend.  We
+     *  decrement by one; level one debugging in the postmaster traces
+     *  postmaster connection activity, and levels two and higher
+     *  are passed along to the backend.  This allows us to watch only
+     *  the postmaster or the postmaster and the backend.
+     */
+    
+    if (DebugLvl > 1) {
+       (void) sprintf(debugbuf, "-d%d", DebugLvl - 1);
+       av[ac++] = debugbuf;
+    }
+    else
+       av[ac++] = "-Q";
+    
+    /* Pass the requested debugging output file */
+    if (packet->tty[0]) {
+       (void) strncpy(ttybuf, packet->tty, ARGV_SIZE);
+       av[ac++] = "-o";
+#ifdef WIN32
+     /* BIG HACK - The front end is passing "/dev/null" here which
+     ** causes new backends to fail. So, as a very special case,
+     ** use a real NT filename.
+     */
+        av[ac++] = "CON";
+#else
+        av[ac++] = ttybuf;
+#endif /* WIN32 */
+
+    }
+    
+    /* tell the multiplexed backend to start on a certain port */
+    if (MultiplexedBackends) {
+      sprintf(mbbuf, "-m %d", MultiplexedBackendPort);
+      av[ac++] = mbbuf;
+    }
+    /* Tell the backend the descriptor of the fe/be socket */
+    (void) sprintf(portbuf, "-P%d", portFd);
+    av[ac++] = portbuf;
+    
+    (void) strncpy(argbuf, packet->options, ARGV_SIZE);
+    argbuf[ARGV_SIZE] = '\0';
+    (void) strncat(argbuf, ExtraOptions, ARGV_SIZE);
+    argbuf[(2 * ARGV_SIZE) + 1] = '\0';
+    split_opts(av, &ac, argbuf);
+    
+    if (packet->database[0])
+       (void) strncpy(dbbuf, packet->database, ARGV_SIZE);
+    else
+       (void) strncpy(dbbuf, packet->user, NAMEDATALEN);
+    dbbuf[ARGV_SIZE] = '\0';
+    av[ac++] = dbbuf;
+    
+    av[ac] = (char *) NULL;
+    
+    if (DebugLvl > 1) {
+       fprintf(stderr, "%s child[%d]: execv(",
+               progname, getpid());
+       for (i = 0; i < ac; ++i)
+           fprintf(stderr, "%s, ", av[i]);
+       fprintf(stderr, ")\n");
+    }
+    
+#ifndef WIN32
+    return(execv(av[0], av));
+#else
+
+    /* Copy all the arguments into one char array */
+    win32_args[0] = '\0';
+    for (i = 0; i < ac; i++)
+    {
+      strcat(win32_args, av[i]);
+      strcat(win32_args, " ");
+    }
+
+    siStartInfo.cb = sizeof(STARTUPINFO);
+    siStartInfo.lpReserved = NULL;
+    siStartInfo.lpDesktop = NULL;
+    siStartInfo.lpTitle = NULL;
+    siStartInfo.lpReserved2 = NULL;
+    siStartInfo.cbReserved2 = 0;
+    siStartInfo.dwFlags = 0;
+
+
+     fSuccess = CreateProcess(progname, win32_args, NULL, NULL,
+               TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo);
+     if (fSuccess)
+     {
+       /* The parent process doesn't need the handles */
+       CloseHandle(piProcInfo.hThread);
+       CloseHandle(piProcInfo.hProcess);
+       return (piProcInfo.dwProcessId);
+     }
+     else
+       return (FALSE);
+#endif /* WIN32 */
+}
+
+/*
+ * ExitPostmaster -- cleanup
+ */
+static void
+ExitPostmaster(int status)
+{
+    /* should cleanup shared memory and kill all backends */
+    
+    /* 
+     * Not sure of the semantics here.  When the Postmaster dies,
+     * should the backends all be killed? probably not.
+     */
+    if (ServerSock != INVALID_SOCK)
+       close(ServerSock);
+    exitpg(status);
+}
+
+static void
+#if defined(PORTNAME_linux)
+dumpstatus(int i)
+#else
+dumpstatus()
+#endif
+{
+    Dlelem *curr = DLGetHead(PortList); 
+    
+    while (curr) {
+       Port *port = DLE_VAL(curr);
+       
+       fprintf(stderr, "%s: dumpstatus:\n", progname);
+       fprintf(stderr, "\tsock %d: nBytes=%d, laddr=0x%x, raddr=0x%x\n",
+               port->sock, port->nBytes, 
+               port->laddr, 
+               port->raddr);
+       curr = DLGetSucc(curr);
+    }
+}
+
+static void
+checkDataDir()
+{
+    char path[MAXPATHLEN];
+    FILE *fp;
+    
+    sprintf(path, "%s%cbase%ctemplate1%cpg_class", DataDir, SEP_CHAR, SEP_CHAR,
+           SEP_CHAR);
+    if ((fp=fopen(path, "r")) == NULL) {
+       fprintf(stderr, "%s: data base not found in directory \"%s\"\n",
+               progname, DataDir);
+       exit(2);
+    }
+    fclose(fp);
+
+#ifndef WIN32    
+    if (!ValidPgVersion(DataDir)) {
+       fprintf(stderr, "%s: data base in \"%s\" is of a different version.\n",
+               progname, DataDir);
+       exit(2);
+    }
+#endif /* WIN32 */
+}
+
+
diff --git a/src/backend/regex/COPYRIGHT b/src/backend/regex/COPYRIGHT
new file mode 100644 (file)
index 0000000..574f6bc
--- /dev/null
@@ -0,0 +1,56 @@
+Copyright 1992, 1993, 1994 Henry Spencer.  All rights reserved.
+This software is not subject to any license of the American Telephone
+and Telegraph Company or of the Regents of the University of California.
+
+Permission is granted to anyone to use this software for any purpose on
+any computer system, and to alter it and redistribute it, subject
+to the following restrictions:
+
+1. The author is not responsible for the consequences of use of this
+   software, no matter how awful, even if they arise from flaws in it.
+
+2. The origin of this software must not be misrepresented, either by
+   explicit claim or by omission.  Since few users ever read sources,
+   credits must appear in the documentation.
+
+3. Altered versions must be plainly marked as such, and must not be
+   misrepresented as being the original software.  Since few users
+   ever read sources, credits must appear in the documentation.
+
+4. This notice may not be removed or altered.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+/*-
+ * Copyright (c) 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)COPYRIGHT   8.1 (Berkeley) 3/16/94
+ */
diff --git a/src/backend/regex/Makefile.inc b/src/backend/regex/Makefile.inc
new file mode 100644 (file)
index 0000000..229fd2d
--- /dev/null
@@ -0,0 +1,14 @@
+#      @(#)Makefile.inc        8.1 (Berkeley) 6/4/93
+
+# regex sources
+VPATH:=$(VPATH):$(CURDIR)/regex
+
+CFLAGS+=-DPOSIX_MISTAKE -I$(CURDIR)/regex
+
+SRCS_REGEX=    regcomp.c regerror.c regexec.c regfree.c
+
+MAN3+= regex.0
+MAN7+= re_format.0
+
+MLINKS+=regex.3 regcomp.3 regex.3 regexec.3 regex.3 regerror.3
+MLINKS+=regexec.3 regfree.3
diff --git a/src/backend/regex/WHATSNEW b/src/backend/regex/WHATSNEW
new file mode 100644 (file)
index 0000000..f4301d3
--- /dev/null
@@ -0,0 +1,94 @@
+# @(#)WHATSNEW 8.3 (Berkeley) 3/18/94
+
+New in alpha3.4:  The complex bug alluded to below has been fixed (in a
+slightly kludgey temporary way that may hurt efficiency a bit; this is
+another "get it out the door for 4.4" release).  The tests at the end of
+the tests file have accordingly been uncommented.  The primary sign of
+the bug was that something like a?b matching ab matched b rather than ab.
+(The bug was essentially specific to this exact situation, else it would
+have shown up earlier.)
+
+New in alpha3.3:  The definition of word boundaries has been altered
+slightly, to more closely match the usual programming notion that "_"
+is an alphabetic.  Stuff used for pre-ANSI systems is now in a subdir,
+and the makefile no longer alludes to it in mysterious ways.  The
+makefile has generally been cleaned up some.  Fixes have been made
+(again!) so that the regression test will run without -DREDEBUG, at
+the cost of weaker checking.  A workaround for a bug in some folks'
+<assert.h> has been added.  And some more things have been added to
+tests, including a couple right at the end which are commented out
+because the code currently flunks them (complex bug; fix coming).
+Plus the usual minor cleanup.
+
+New in alpha3.2:  Assorted bits of cleanup and portability improvement
+(the development base is now a BSDI system using GCC instead of an ancient
+Sun system, and the newer compiler exposed some glitches).  Fix for a
+serious bug that affected REs using many [] (including REG_ICASE REs
+because of the way they are implemented), *sometimes*, depending on
+memory-allocation patterns.  The header-file prototypes no longer name
+the parameters, avoiding possible name conflicts.  The possibility that
+some clot has defined CHAR_MIN as (say) `-128' instead of `(-128)' is
+now handled gracefully.  "uchar" is no longer used as an internal type
+name (too many people have the same idea).  Still the same old lousy
+performance, alas.
+
+New in alpha3.1:  Basically nothing, this release is just a bookkeeping
+convenience.  Stay tuned.
+
+New in alpha3.0:  Performance is no better, alas, but some fixes have been
+made and some functionality has been added.  (This is basically the "get
+it out the door in time for 4.4" release.)  One bug fix:  regfree() didn't
+free the main internal structure (how embarrassing).  It is now possible
+to put NULs in either the RE or the target string, using (resp.) a new
+REG_PEND flag and the old REG_STARTEND flag.  The REG_NOSPEC flag to
+regcomp() makes all characters ordinary, so you can match a literal
+string easily (this will become more useful when performance improves!).
+There are now primitives to match beginnings and ends of words, although
+the syntax is disgusting and so is the implementation.  The REG_ATOI
+debugging interface has changed a bit.  And there has been considerable
+internal cleanup of various kinds.
+
+New in alpha2.3:  Split change list out of README, and moved flags notes
+into Makefile.  Macro-ized the name of regex(7) in regex(3), since it has
+to change for 4.4BSD.  Cleanup work in engine.c, and some new regression
+tests to catch tricky cases thereof.
+
+New in alpha2.2:  Out-of-date manpages updated.  Regerror() acquires two
+small extensions -- REG_ITOA and REG_ATOI -- which avoid debugging kludges
+in my own test program and might be useful to others for similar purposes.
+The regression test will now compile (and run) without REDEBUG.  The
+BRE \$ bug is fixed.  Most uses of "uchar" are gone; it's all chars now.
+Char/uchar parameters are now written int/unsigned, to avoid possible
+portability problems with unpromoted parameters.  Some unsigned casts have
+been introduced to minimize portability problems with shifting into sign
+bits.
+
+New in alpha2.1:  Lots of little stuff, cleanup and fixes.  The one big
+thing is that regex.h is now generated, using mkh, rather than being
+supplied in the distribution; due to circularities in dependencies,
+you have to build regex.h explicitly by "make h".  The two known bugs
+have been fixed (and the regression test now checks for them), as has a
+problem with assertions not being suppressed in the absence of REDEBUG.
+No performance work yet.
+
+New in alpha2:  Backslash-anything is an ordinary character, not an
+error (except, of course, for the handful of backslashed metacharacters
+in BREs), which should reduce script breakage.  The regression test
+checks *where* null strings are supposed to match, and has generally
+been tightened up somewhat.  Small bug fixes in parameter passing (not
+harmful, but technically errors) and some other areas.  Debugging
+invoked by defining REDEBUG rather than not defining NDEBUG.
+
+New in alpha+3:  full prototyping for internal routines, using a little
+helper program, mkh, which extracts prototypes given in stylized comments.
+More minor cleanup.  Buglet fix:  it's CHAR_BIT, not CHAR_BITS.  Simple
+pre-screening of input when a literal string is known to be part of the
+RE; this does wonders for performance.
+
+New in alpha+2:  minor bits of cleanup.  Notably, the number "32" for the
+word width isn't hardwired into regexec.c any more, the public header
+file prototypes the functions if __STDC__ is defined, and some small typos
+in the manpages have been fixed.
+
+New in alpha+1:  improvements to the manual pages, and an important
+extension, the REG_STARTEND option to regexec().
diff --git a/src/backend/regex/cclass.h b/src/backend/regex/cclass.h
new file mode 100644 (file)
index 0000000..cabe7fa
--- /dev/null
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)cclass.h    8.3 (Berkeley) 3/20/94
+ */
+
+/* character-class table */
+static struct cclass {
+       char *name;
+       char *chars;
+       char *multis;
+} cclasses[] = {
+       {"alnum",       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\
+0123456789",                           ""},
+       {"alpha",       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
+                                       ""},
+       {"blank",       " \t",          ""},
+       {"cntrl",       "\007\b\t\n\v\f\r\1\2\3\4\5\6\16\17\20\21\22\23\24\
+\25\26\27\30\31\32\33\34\35\36\37\177",        ""},
+       {"digit",       "0123456789",   ""},
+       {"graph",       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\
+0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
+                                       ""},
+       {"lower",       "abcdefghijklmnopqrstuvwxyz",
+                                       ""},
+       {"print",       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\
+0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ",
+                                       ""},
+       {"punct",       "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
+                                       ""},
+       {"space",       "\t\n\v\f\r ",  ""},
+       {"upper",       "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+                                       ""},
+       {"xdigit",      "0123456789ABCDEFabcdef",
+                                       ""},
+       {NULL,          0,              ""}
+};
diff --git a/src/backend/regex/cdefs.h b/src/backend/regex/cdefs.h
new file mode 100644 (file)
index 0000000..f950e21
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * ++Copyright++ 1991, 1993
+ * -
+ * Copyright (c) 1991, 1993
+ *    The Regents of the University of California.  All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * -
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ * 
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * -
+ * --Copyright--
+ */
+
+/*
+ *     @(#)cdefs.h     8.1 (Berkeley) 6/2/93
+ *     $Id$
+ */
+
+#ifndef        _CDEFS_H_
+#define        _CDEFS_H_
+
+#if defined(__cplusplus)
+#define        __BEGIN_DECLS   extern "C" {
+#define        __END_DECLS     };
+#else
+#define        __BEGIN_DECLS
+#define        __END_DECLS
+#endif
+
+/*
+ * The __CONCAT macro is used to concatenate parts of symbol names, e.g.
+ * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo.
+ * The __CONCAT macro is a bit tricky -- make sure you don't put spaces
+ * in between its arguments.  __CONCAT can also concatenate double-quoted
+ * strings produced by the __STRING macro, but this only works with ANSI C.
+ */
+#if defined(__STDC__) || defined(__cplusplus)
+#define        __P(protos)     protos          /* full-blown ANSI C */
+#define        __CONCAT(x,y)   x ## y
+#define        __STRING(x)     #x
+
+#define        __const         const           /* define reserved names to standard */
+#define        __signed        signed
+#define        __volatile      volatile
+#if defined(__cplusplus)
+#define        __inline        inline          /* convert to C++ keyword */
+#else
+#ifndef __GNUC__
+#define        __inline                        /* delete GCC keyword */
+#endif /* !__GNUC__ */
+#endif /* !__cplusplus */
+
+#else  /* !(__STDC__ || __cplusplus) */
+#define        __P(protos)     ()              /* traditional C preprocessor */
+#define        __CONCAT(x,y)   x/**/y
+#define        __STRING(x)     "x"
+
+#ifndef __GNUC__
+#define        __const                         /* delete pseudo-ANSI C keywords */
+#define        __inline
+#define        __signed
+#define        __volatile
+/*
+ * In non-ANSI C environments, new programs will want ANSI-only C keywords
+ * deleted from the program and old programs will want them left alone.
+ * When using a compiler other than gcc, programs using the ANSI C keywords
+ * const, inline etc. as normal identifiers should define -DNO_ANSI_KEYWORDS.
+ * When using "gcc -traditional", we assume that this is the intent; if
+ * __GNUC__ is defined but __STDC__ is not, we leave the new keywords alone.
+ */
+#ifndef        NO_ANSI_KEYWORDS
+#define        const                           /* delete ANSI C keywords */
+#define        inline
+#define        signed
+#define        volatile
+#endif
+#endif /* !__GNUC__ */
+#endif /* !(__STDC__ || __cplusplus) */
+
+/*
+ * GCC1 and some versions of GCC2 declare dead (non-returning) and
+ * pure (no side effects) functions using "volatile" and "const";
+ * unfortunately, these then cause warnings under "-ansi -pedantic".
+ * GCC2 uses a new, peculiar __attribute__((attrs)) style.  All of
+ * these work for GNU C++ (modulo a slight glitch in the C++ grammar
+ * in the distribution version of 2.5.5).
+ */
+#if !defined(__GNUC__) || __GNUC__ < 2 || __GNUC_MINOR__ < 5
+#define        __attribute__(x)        /* delete __attribute__ if non-gcc or gcc1 */
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+#define        __dead          __volatile
+#define        __pure          __const
+#endif
+#endif
+
+/* Delete pseudo-keywords wherever they are not available or needed. */
+#ifndef __dead
+#define        __dead
+#define        __pure
+#endif
+
+#endif /* !_CDEFS_H_ */
diff --git a/src/backend/regex/cname.h b/src/backend/regex/cname.h
new file mode 100644 (file)
index 0000000..c6ba15b
--- /dev/null
@@ -0,0 +1,141 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)cname.h     8.3 (Berkeley) 3/20/94
+ */
+
+/* character-name table */
+static struct cname {
+       char *name;
+       char code;
+} cnames[] = {
+       {"NUL", '\0'},
+       {"SOH", '\001'},
+       {"STX", '\002'},
+       {"ETX", '\003'},
+       {"EOT", '\004'},
+       {"ENQ", '\005'},
+       {"ACK", '\006'},
+       {"BEL", '\007'},
+       {"alert",       '\007'},
+       {"BS",          '\010'},
+       {"backspace",   '\b'},
+       {"HT",          '\011'},
+       {"tab",         '\t'},
+       {"LF",          '\012'},
+       {"newline",     '\n'},
+       {"VT",          '\013'},
+       {"vertical-tab",        '\v'},
+       {"FF",          '\014'},
+       {"form-feed",   '\f'},
+       {"CR",          '\015'},
+       {"carriage-return",     '\r'},
+       {"SO",  '\016'},
+       {"SI",  '\017'},
+       {"DLE", '\020'},
+       {"DC1", '\021'},
+       {"DC2", '\022'},
+       {"DC3", '\023'},
+       {"DC4", '\024'},
+       {"NAK", '\025'},
+       {"SYN", '\026'},
+       {"ETB", '\027'},
+       {"CAN", '\030'},
+       {"EM",  '\031'},
+       {"SUB", '\032'},
+       {"ESC", '\033'},
+       {"IS4", '\034'},
+       {"FS",  '\034'},
+       {"IS3", '\035'},
+       {"GS",  '\035'},
+       {"IS2", '\036'},
+       {"RS",  '\036'},
+       {"IS1", '\037'},
+       {"US",  '\037'},
+       {"space",               ' '},
+       {"exclamation-mark",    '!'},
+       {"quotation-mark",      '"'},
+       {"number-sign",         '#'},
+       {"dollar-sign",         '$'},
+       {"percent-sign",                '%'},
+       {"ampersand",           '&'},
+       {"apostrophe",          '\''},
+       {"left-parenthesis",    '('},
+       {"right-parenthesis",   ')'},
+       {"asterisk",    '*'},
+       {"plus-sign",   '+'},
+       {"comma",       ','},
+       {"hyphen",      '-'},
+       {"hyphen-minus",        '-'},
+       {"period",      '.'},
+       {"full-stop",   '.'},
+       {"slash",       '/'},
+       {"solidus",     '/'},
+       {"zero",                '0'},
+       {"one",         '1'},
+       {"two",         '2'},
+       {"three",       '3'},
+       {"four",                '4'},
+       {"five",                '5'},
+       {"six",         '6'},
+       {"seven",       '7'},
+       {"eight",       '8'},
+       {"nine",                '9'},
+       {"colon",       ':'},
+       {"semicolon",   ';'},
+       {"less-than-sign",      '<'},
+       {"equals-sign",         '='},
+       {"greater-than-sign",   '>'},
+       {"question-mark",       '?'},
+       {"commercial-at",       '@'},
+       {"left-square-bracket", '['},
+       {"backslash",           '\\'},
+       {"reverse-solidus",     '\\'},
+       {"right-square-bracket",        ']'},
+       {"circumflex",          '^'},
+       {"circumflex-accent",   '^'},
+       {"underscore",          '_'},
+       {"low-line",            '_'},
+       {"grave-accent",                '`'},
+       {"left-brace",          '{'},
+       {"left-curly-bracket",  '{'},
+       {"vertical-line",       '|'},
+       {"right-brace",         '}'},
+       {"right-curly-bracket", '}'},
+       {"tilde",               '~'},
+       {"DEL", '\177'},
+       {NULL,  0}
+};
diff --git a/src/backend/regex/engine.c b/src/backend/regex/engine.c
new file mode 100644 (file)
index 0000000..45b37a4
--- /dev/null
@@ -0,0 +1,1092 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)engine.c    8.5 (Berkeley) 3/20/94
+ */
+
+/*
+ * The matching engine and friends.  This file is #included by regexec.c
+ * after suitable #defines of a variety of macros used herein, so that
+ * different state representations can be used without duplicating masses
+ * of code.
+ */
+
+#ifdef SNAMES
+#define        matcher smatcher
+#define        fast    sfast
+#define        slow    sslow
+#define        dissect sdissect
+#define        backref sbackref
+#define        step    sstep
+#define        print   sprint
+#define        at      sat
+#define        match   smat
+#endif
+#ifdef LNAMES
+#define        matcher lmatcher
+#define        fast    lfast
+#define        slow    lslow
+#define        dissect ldissect
+#define        backref lbackref
+#define        step    lstep
+#define        print   lprint
+#define        at      lat
+#define        match   lmat
+#endif
+
+/* another structure passed up and down to avoid zillions of parameters */
+struct match {
+       struct re_guts *g;
+       int eflags;
+       regmatch_t *pmatch;     /* [nsub+1] (0 element unused) */
+       char *offp;             /* offsets work from here */
+       char *beginp;           /* start of string -- virtual NUL precedes */
+       char *endp;             /* end of string -- virtual NUL here */
+       char *coldp;            /* can be no match starting before here */
+       char **lastpos;         /* [nplus+1] */
+       STATEVARS;
+       states st;              /* current states */
+       states fresh;           /* states for a fresh start */
+       states tmp;             /* temporary */
+       states empty;           /* empty set of states */
+};
+
+/* ========= begin header generated by ./mkh ========= */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* === engine.c === */
+static int matcher __P((struct re_guts *g, char *string, size_t nmatch, regmatch_t pmatch[], int eflags));
+static char *dissect __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst));
+static char *backref __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst, sopno lev));
+static char *fast __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst));
+static char *slow __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst));
+static states step __P((struct re_guts *g, sopno start, sopno stop, states bef, int ch, states aft));
+#define        BOL     (OUT+1)
+#define        EOL     (BOL+1)
+#define        BOLEOL  (BOL+2)
+#define        NOTHING (BOL+3)
+#define        BOW     (BOL+4)
+#define        EOW     (BOL+5)
+#define        CODEMAX (BOL+5)         /* highest code used */
+#define        NONCHAR(c)      ((c) > CHAR_MAX)
+#define        NNONCHAR        (CODEMAX-CHAR_MAX)
+#ifdef REDEBUG
+static void print __P((struct match *m, char *caption, states st, int ch, FILE *d));
+#endif
+#ifdef REDEBUG
+static void at __P((struct match *m, char *title, char *start, char *stop, sopno startst, sopno stopst));
+#endif
+#ifdef REDEBUG
+static char *pchar __P((int ch));
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+/* ========= end header generated by ./mkh ========= */
+
+#ifdef REDEBUG
+#define        SP(t, s, c)     print(m, t, s, c, stdout)
+#define        AT(t, p1, p2, s1, s2)   at(m, t, p1, p2, s1, s2)
+#define        NOTE(str)       { if (m->eflags&REG_TRACE) printf("=%s\n", (str)); }
+#else
+#define        SP(t, s, c)     /* nothing */
+#define        AT(t, p1, p2, s1, s2)   /* nothing */
+#define        NOTE(s) /* nothing */
+#endif
+
+/*
+ - matcher - the actual matching engine
+ == static int matcher(register struct re_guts *g, char *string, \
+ ==    size_t nmatch, regmatch_t pmatch[], int eflags);
+ */
+static int                     /* 0 success, REG_NOMATCH failure */
+matcher(g, string, nmatch, pmatch, eflags)
+register struct re_guts *g;
+char *string;
+size_t nmatch;
+regmatch_t pmatch[];
+int eflags;
+{
+       register char *endp;
+       register int i;
+       struct match mv;
+       register struct match *m = &mv;
+       register char *dp;
+       const register sopno gf = g->firststate+1;      /* +1 for OEND */
+       const register sopno gl = g->laststate;
+       char *start;
+       char *stop;
+
+       /* simplify the situation where possible */
+       if (g->cflags&REG_NOSUB)
+               nmatch = 0;
+       if (eflags&REG_STARTEND) {
+               start = string + pmatch[0].rm_so;
+               stop = string + pmatch[0].rm_eo;
+       } else {
+               start = string;
+               stop = start + strlen(start);
+       }
+       if (stop < start)
+               return(REG_INVARG);
+
+       /* prescreening; this does wonders for this rather slow code */
+       if (g->must != NULL) {
+               for (dp = start; dp < stop; dp++)
+                       if (*dp == g->must[0] && stop - dp >= g->mlen &&
+                               memcmp(dp, g->must, (size_t)g->mlen) == 0)
+                               break;
+               if (dp == stop)         /* we didn't find g->must */
+                       return(REG_NOMATCH);
+       }
+
+       /* match struct setup */
+       m->g = g;
+       m->eflags = eflags;
+       m->pmatch = NULL;
+       m->lastpos = NULL;
+       m->offp = string;
+       m->beginp = start;
+       m->endp = stop;
+       STATESETUP(m, 4);
+       SETUP(m->st);
+       SETUP(m->fresh);
+       SETUP(m->tmp);
+       SETUP(m->empty);
+       CLEAR(m->empty);
+
+       /* this loop does only one repetition except for backrefs */
+       for (;;) {
+               endp = fast(m, start, stop, gf, gl);
+               if (endp == NULL) {             /* a miss */
+                       STATETEARDOWN(m);
+                       return(REG_NOMATCH);
+               }
+               if (nmatch == 0 && !g->backrefs)
+                       break;          /* no further info needed */
+
+               /* where? */
+               assert(m->coldp != NULL);
+               for (;;) {
+                       NOTE("finding start");
+                       endp = slow(m, m->coldp, stop, gf, gl);
+                       if (endp != NULL)
+                               break;
+                       assert(m->coldp < m->endp);
+                       m->coldp++;
+               }
+               if (nmatch == 1 && !g->backrefs)
+                       break;          /* no further info needed */
+
+               /* oh my, he wants the subexpressions... */
+               if (m->pmatch == NULL)
+                       m->pmatch = (regmatch_t *)malloc((m->g->nsub + 1) *
+                                                       sizeof(regmatch_t));
+               if (m->pmatch == NULL) {
+                       STATETEARDOWN(m);
+                       return(REG_ESPACE);
+               }
+               for (i = 1; i <= m->g->nsub; i++)
+                       m->pmatch[i].rm_so = m->pmatch[i].rm_eo = -1;
+               if (!g->backrefs && !(m->eflags&REG_BACKR)) {
+                       NOTE("dissecting");
+                       dp = dissect(m, m->coldp, endp, gf, gl);
+               } else {
+                       if (g->nplus > 0 && m->lastpos == NULL)
+                               m->lastpos = (char **)malloc((g->nplus+1) *
+                                                       sizeof(char *));
+                       if (g->nplus > 0 && m->lastpos == NULL) {
+                               free(m->pmatch);
+                               STATETEARDOWN(m);
+                               return(REG_ESPACE);
+                       }
+                       NOTE("backref dissect");
+                       dp = backref(m, m->coldp, endp, gf, gl, (sopno)0);
+               }
+               if (dp != NULL)
+                       break;
+
+               /* uh-oh... we couldn't find a subexpression-level match */
+               assert(g->backrefs);    /* must be back references doing it */
+               assert(g->nplus == 0 || m->lastpos != NULL);
+               for (;;) {
+                       if (dp != NULL || endp <= m->coldp)
+                               break;          /* defeat */
+                       NOTE("backoff");
+                       endp = slow(m, m->coldp, endp-1, gf, gl);
+                       if (endp == NULL)
+                               break;          /* defeat */
+                       /* try it on a shorter possibility */
+#ifndef NDEBUG
+                       for (i = 1; i <= m->g->nsub; i++) {
+                               assert(m->pmatch[i].rm_so == -1);
+                               assert(m->pmatch[i].rm_eo == -1);
+                       }
+#endif
+                       NOTE("backoff dissect");
+                       dp = backref(m, m->coldp, endp, gf, gl, (sopno)0);
+               }
+               assert(dp == NULL || dp == endp);
+               if (dp != NULL)         /* found a shorter one */
+                       break;
+
+               /* despite initial appearances, there is no match here */
+               NOTE("false alarm");
+               start = m->coldp + 1;   /* recycle starting later */
+               assert(start <= stop);
+       }
+
+       /* fill in the details if requested */
+       if (nmatch > 0) {
+               pmatch[0].rm_so = m->coldp - m->offp;
+               pmatch[0].rm_eo = endp - m->offp;
+       }
+       if (nmatch > 1) {
+               assert(m->pmatch != NULL);
+               for (i = 1; i < nmatch; i++)
+                       if (i <= m->g->nsub)
+                               pmatch[i] = m->pmatch[i];
+                       else {
+                               pmatch[i].rm_so = -1;
+                               pmatch[i].rm_eo = -1;
+                       }
+       }
+
+       if (m->pmatch != NULL)
+               free((char *)m->pmatch);
+       if (m->lastpos != NULL)
+               free((char *)m->lastpos);
+       STATETEARDOWN(m);
+       return(0);
+}
+
+/*
+ - dissect - figure out what matched what, no back references
+ == static char *dissect(register struct match *m, char *start, \
+ ==    char *stop, sopno startst, sopno stopst);
+ */
+static char *                  /* == stop (success) always */
+dissect(m, start, stop, startst, stopst)
+register struct match *m;
+char *start;
+char *stop;
+sopno startst;
+sopno stopst;
+{
+       register int i;
+       register sopno ss;      /* start sop of current subRE */
+       register sopno es;      /* end sop of current subRE */
+       register char *sp;      /* start of string matched by it */
+       register char *stp;     /* string matched by it cannot pass here */
+       register char *rest;    /* start of rest of string */
+       register char *tail;    /* string unmatched by rest of RE */
+       register sopno ssub;    /* start sop of subsubRE */
+       register sopno esub;    /* end sop of subsubRE */
+       register char *ssp;     /* start of string matched by subsubRE */
+       register char *sep;     /* end of string matched by subsubRE */
+       register char *oldssp;  /* previous ssp */
+       register char *dp;
+
+       AT("diss", start, stop, startst, stopst);
+       sp = start;
+       for (ss = startst; ss < stopst; ss = es) {
+               /* identify end of subRE */
+               es = ss;
+               switch (OP(m->g->strip[es])) {
+               case OPLUS_:
+               case OQUEST_:
+                       es += OPND(m->g->strip[es]);
+                       break;
+               case OCH_:
+                       while (OP(m->g->strip[es]) != O_CH)
+                               es += OPND(m->g->strip[es]);
+                       break;
+               }
+               es++;
+
+               /* figure out what it matched */
+               switch (OP(m->g->strip[ss])) {
+               case OEND:
+                       assert(nope);
+                       break;
+               case OCHAR:
+                       sp++;
+                       break;
+               case OBOL:
+               case OEOL:
+               case OBOW:
+               case OEOW:
+                       break;
+               case OANY:
+               case OANYOF:
+                       sp++;
+                       break;
+               case OBACK_:
+               case O_BACK:
+                       assert(nope);
+                       break;
+               /* cases where length of match is hard to find */
+               case OQUEST_:
+                       stp = stop;
+                       for (;;) {
+                               /* how long could this one be? */
+                               rest = slow(m, sp, stp, ss, es);
+                               assert(rest != NULL);   /* it did match */
+                               /* could the rest match the rest? */
+                               tail = slow(m, rest, stop, es, stopst);
+                               if (tail == stop)
+                                       break;          /* yes! */
+                               /* no -- try a shorter match for this one */
+                               stp = rest - 1;
+                               assert(stp >= sp);      /* it did work */
+                       }
+                       ssub = ss + 1;
+                       esub = es - 1;
+                       /* did innards match? */
+                       if (slow(m, sp, rest, ssub, esub) != NULL) {
+                               dp = dissect(m, sp, rest, ssub, esub);
+                               assert(dp == rest);
+                       } else          /* no */
+                               assert(sp == rest);
+                       sp = rest;
+                       break;
+               case OPLUS_:
+                       stp = stop;
+                       for (;;) {
+                               /* how long could this one be? */
+                               rest = slow(m, sp, stp, ss, es);
+                               assert(rest != NULL);   /* it did match */
+                               /* could the rest match the rest? */
+                               tail = slow(m, rest, stop, es, stopst);
+                               if (tail == stop)
+                                       break;          /* yes! */
+                               /* no -- try a shorter match for this one */
+                               stp = rest - 1;
+                               assert(stp >= sp);      /* it did work */
+                       }
+                       ssub = ss + 1;
+                       esub = es - 1;
+                       ssp = sp;
+                       oldssp = ssp;
+                       for (;;) {      /* find last match of innards */
+                               sep = slow(m, ssp, rest, ssub, esub);
+                               if (sep == NULL || sep == ssp)
+                                       break;  /* failed or matched null */
+                               oldssp = ssp;   /* on to next try */
+                               ssp = sep;
+                       }
+                       if (sep == NULL) {
+                               /* last successful match */
+                               sep = ssp;
+                               ssp = oldssp;
+                       }
+                       assert(sep == rest);    /* must exhaust substring */
+                       assert(slow(m, ssp, sep, ssub, esub) == rest);
+                       dp = dissect(m, ssp, sep, ssub, esub);
+                       assert(dp == sep);
+                       sp = rest;
+                       break;
+               case OCH_:
+                       stp = stop;
+                       for (;;) {
+                               /* how long could this one be? */
+                               rest = slow(m, sp, stp, ss, es);
+                               assert(rest != NULL);   /* it did match */
+                               /* could the rest match the rest? */
+                               tail = slow(m, rest, stop, es, stopst);
+                               if (tail == stop)
+                                       break;          /* yes! */
+                               /* no -- try a shorter match for this one */
+                               stp = rest - 1;
+                               assert(stp >= sp);      /* it did work */
+                       }
+                       ssub = ss + 1;
+                       esub = ss + OPND(m->g->strip[ss]) - 1;
+                       assert(OP(m->g->strip[esub]) == OOR1);
+                       for (;;) {      /* find first matching branch */
+                               if (slow(m, sp, rest, ssub, esub) == rest)
+                                       break;  /* it matched all of it */
+                               /* that one missed, try next one */
+                               assert(OP(m->g->strip[esub]) == OOR1);
+                               esub++;
+                               assert(OP(m->g->strip[esub]) == OOR2);
+                               ssub = esub + 1;
+                               esub += OPND(m->g->strip[esub]);
+                               if (OP(m->g->strip[esub]) == OOR2)
+                                       esub--;
+                               else
+                                       assert(OP(m->g->strip[esub]) == O_CH);
+                       }
+                       dp = dissect(m, sp, rest, ssub, esub);
+                       assert(dp == rest);
+                       sp = rest;
+                       break;
+               case O_PLUS:
+               case O_QUEST:
+               case OOR1:
+               case OOR2:
+               case O_CH:
+                       assert(nope);
+                       break;
+               case OLPAREN:
+                       i = OPND(m->g->strip[ss]);
+                       assert(0 < i && i <= m->g->nsub);
+                       m->pmatch[i].rm_so = sp - m->offp;
+                       break;
+               case ORPAREN:
+                       i = OPND(m->g->strip[ss]);
+                       assert(0 < i && i <= m->g->nsub);
+                       m->pmatch[i].rm_eo = sp - m->offp;
+                       break;
+               default:                /* uh oh */
+                       assert(nope);
+                       break;
+               }
+       }
+
+       assert(sp == stop);
+       return(sp);
+}
+
+/*
+ - backref - figure out what matched what, figuring in back references
+ == static char *backref(register struct match *m, char *start, \
+ ==    char *stop, sopno startst, sopno stopst, sopno lev);
+ */
+static char *                  /* == stop (success) or NULL (failure) */
+backref(m, start, stop, startst, stopst, lev)
+register struct match *m;
+char *start;
+char *stop;
+sopno startst;
+sopno stopst;
+sopno lev;                     /* PLUS nesting level */
+{
+       register int i;
+       register sopno ss;      /* start sop of current subRE */
+       register char *sp;      /* start of string matched by it */
+       register sopno ssub;    /* start sop of subsubRE */
+       register sopno esub;    /* end sop of subsubRE */
+       register char *ssp;     /* start of string matched by subsubRE */
+       register char *dp;
+       register size_t len;
+       register int hard;
+       register sop s;
+       register regoff_t offsave;
+       register cset *cs;
+
+       AT("back", start, stop, startst, stopst);
+       sp = start;
+
+       /* get as far as we can with easy stuff */
+       hard = 0;
+       for (ss = startst; !hard && ss < stopst; ss++)
+               switch (OP(s = m->g->strip[ss])) {
+               case OCHAR:
+                       if (sp == stop || *sp++ != (char)OPND(s))
+                               return(NULL);
+                       break;
+               case OANY:
+                       if (sp == stop)
+                               return(NULL);
+                       sp++;
+                       break;
+               case OANYOF:
+                       cs = &m->g->sets[OPND(s)];
+                       if (sp == stop || !CHIN(cs, *sp++))
+                               return(NULL);
+                       break;
+               case OBOL:
+                       if ( (sp == m->beginp && !(m->eflags&REG_NOTBOL)) ||
+                                       (sp < m->endp && *(sp-1) == '\n' &&
+                                               (m->g->cflags&REG_NEWLINE)) )
+                               { /* yes */ }
+                       else
+                               return(NULL);
+                       break;
+               case OEOL:
+                       if ( (sp == m->endp && !(m->eflags&REG_NOTEOL)) ||
+                                       (sp < m->endp && *sp == '\n' &&
+                                               (m->g->cflags&REG_NEWLINE)) )
+                               { /* yes */ }
+                       else
+                               return(NULL);
+                       break;
+               case OBOW:
+                       if (( (sp == m->beginp && !(m->eflags&REG_NOTBOL)) ||
+                                       (sp < m->endp && *(sp-1) == '\n' &&
+                                               (m->g->cflags&REG_NEWLINE)) ||
+                                       (sp > m->beginp &&
+                                                       !ISWORD(*(sp-1))) ) &&
+                                       (sp < m->endp && ISWORD(*sp)) )
+                               { /* yes */ }
+                       else
+                               return(NULL);
+                       break;
+               case OEOW:
+                       if (( (sp == m->endp && !(m->eflags&REG_NOTEOL)) ||
+                                       (sp < m->endp && *sp == '\n' &&
+                                               (m->g->cflags&REG_NEWLINE)) ||
+                                       (sp < m->endp && !ISWORD(*sp)) ) &&
+                                       (sp > m->beginp && ISWORD(*(sp-1))) )
+                               { /* yes */ }
+                       else
+                               return(NULL);
+                       break;
+               case O_QUEST:
+                       break;
+               case OOR1:      /* matches null but needs to skip */
+                       ss++;
+                       s = m->g->strip[ss];
+                       do {
+                               assert(OP(s) == OOR2);
+                               ss += OPND(s);
+                       } while (OP(s = m->g->strip[ss]) != O_CH);
+                       /* note that the ss++ gets us past the O_CH */
+                       break;
+               default:        /* have to make a choice */
+                       hard = 1;
+                       break;
+               }
+       if (!hard) {            /* that was it! */
+               if (sp != stop)
+                       return(NULL);
+               return(sp);
+       }
+       ss--;                   /* adjust for the for's final increment */
+
+       /* the hard stuff */
+       AT("hard", sp, stop, ss, stopst);
+       s = m->g->strip[ss];
+       switch (OP(s)) {
+       case OBACK_:            /* the vilest depths */
+               i = OPND(s);
+               assert(0 < i && i <= m->g->nsub);
+               if (m->pmatch[i].rm_eo == -1)
+                       return(NULL);
+               assert(m->pmatch[i].rm_so != -1);
+               len = m->pmatch[i].rm_eo - m->pmatch[i].rm_so;
+               assert(stop - m->beginp >= len);
+               if (sp > stop - len)
+                       return(NULL);   /* not enough left to match */
+               ssp = m->offp + m->pmatch[i].rm_so;
+               if (memcmp(sp, ssp, len) != 0)
+                       return(NULL);
+               while (m->g->strip[ss] != SOP(O_BACK, i))
+                       ss++;
+               return(backref(m, sp+len, stop, ss+1, stopst, lev));
+               break;
+       case OQUEST_:           /* to null or not */
+               dp = backref(m, sp, stop, ss+1, stopst, lev);
+               if (dp != NULL)
+                       return(dp);     /* not */
+               return(backref(m, sp, stop, ss+OPND(s)+1, stopst, lev));
+               break;
+       case OPLUS_:
+               assert(m->lastpos != NULL);
+               assert(lev+1 <= m->g->nplus);
+               m->lastpos[lev+1] = sp;
+               return(backref(m, sp, stop, ss+1, stopst, lev+1));
+               break;
+       case O_PLUS:
+               if (sp == m->lastpos[lev])      /* last pass matched null */
+                       return(backref(m, sp, stop, ss+1, stopst, lev-1));
+               /* try another pass */
+               m->lastpos[lev] = sp;
+               dp = backref(m, sp, stop, ss-OPND(s)+1, stopst, lev);
+               if (dp == NULL)
+                       return(backref(m, sp, stop, ss+1, stopst, lev-1));
+               else
+                       return(dp);
+               break;
+       case OCH_:              /* find the right one, if any */
+               ssub = ss + 1;
+               esub = ss + OPND(s) - 1;
+               assert(OP(m->g->strip[esub]) == OOR1);
+               for (;;) {      /* find first matching branch */
+                       dp = backref(m, sp, stop, ssub, esub, lev);
+                       if (dp != NULL)
+                               return(dp);
+                       /* that one missed, try next one */
+                       if (OP(m->g->strip[esub]) == O_CH)
+                               return(NULL);   /* there is none */
+                       esub++;
+                       assert(OP(m->g->strip[esub]) == OOR2);
+                       ssub = esub + 1;
+                       esub += OPND(m->g->strip[esub]);
+                       if (OP(m->g->strip[esub]) == OOR2)
+                               esub--;
+                       else
+                               assert(OP(m->g->strip[esub]) == O_CH);
+               }
+               break;
+       case OLPAREN:           /* must undo assignment if rest fails */
+               i = OPND(s);
+               assert(0 < i && i <= m->g->nsub);
+               offsave = m->pmatch[i].rm_so;
+               m->pmatch[i].rm_so = sp - m->offp;
+               dp = backref(m, sp, stop, ss+1, stopst, lev);
+               if (dp != NULL)
+                       return(dp);
+               m->pmatch[i].rm_so = offsave;
+               return(NULL);
+               break;
+       case ORPAREN:           /* must undo assignment if rest fails */
+               i = OPND(s);
+               assert(0 < i && i <= m->g->nsub);
+               offsave = m->pmatch[i].rm_eo;
+               m->pmatch[i].rm_eo = sp - m->offp;
+               dp = backref(m, sp, stop, ss+1, stopst, lev);
+               if (dp != NULL)
+                       return(dp);
+               m->pmatch[i].rm_eo = offsave;
+               return(NULL);
+               break;
+       default:                /* uh oh */
+               assert(nope);
+               break;
+       }
+
+       /* "can't happen" */
+       assert(nope);
+       /* NOTREACHED */
+       return 0;
+}
+
+/*
+ - fast - step through the string at top speed
+ == static char *fast(register struct match *m, char *start, \
+ ==    char *stop, sopno startst, sopno stopst);
+ */
+static char *                  /* where tentative match ended, or NULL */
+fast(m, start, stop, startst, stopst)
+register struct match *m;
+char *start;
+char *stop;
+sopno startst;
+sopno stopst;
+{
+       register states st = m->st;
+       register states fresh = m->fresh;
+       register states tmp = m->tmp;
+       register char *p = start;
+       register int c = (start == m->beginp) ? OUT : *(start-1);
+       register int lastc;     /* previous c */
+       register int flagch;
+       register int i;
+       register char *coldp;   /* last p after which no match was underway */
+
+       CLEAR(st);
+       SET1(st, startst);
+       st = step(m->g, startst, stopst, st, NOTHING, st);
+       ASSIGN(fresh, st);
+       SP("start", st, *p);
+       coldp = NULL;
+       for (;;) {
+               /* next character */
+               lastc = c;
+               c = (p == m->endp) ? OUT : *p;
+               if (EQ(st, fresh))
+                       coldp = p;
+
+               /* is there an EOL and/or BOL between lastc and c? */
+               flagch = '\0';
+               i = 0;
+               if ( (lastc == '\n' && m->g->cflags&REG_NEWLINE) ||
+                               (lastc == OUT && !(m->eflags&REG_NOTBOL)) ) {
+                       flagch = BOL;
+                       i = m->g->nbol;
+               }
+               if ( (c == '\n' && m->g->cflags&REG_NEWLINE) ||
+                               (c == OUT && !(m->eflags&REG_NOTEOL)) ) {
+                       flagch = (flagch == BOL) ? BOLEOL : EOL;
+                       i += m->g->neol;
+               }
+               if (i != 0) {
+                       for (; i > 0; i--)
+                               st = step(m->g, startst, stopst, st, flagch, st);
+                       SP("boleol", st, c);
+               }
+
+               /* how about a word boundary? */
+               if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) &&
+                                       (c != OUT && ISWORD(c)) ) {
+                       flagch = BOW;
+               }
+               if ( (lastc != OUT && ISWORD(lastc)) &&
+                               (flagch == EOL || (c != OUT && !ISWORD(c))) ) {
+                       flagch = EOW;
+               }
+               if (flagch == BOW || flagch == EOW) {
+                       st = step(m->g, startst, stopst, st, flagch, st);
+                       SP("boweow", st, c);
+               }
+
+               /* are we done? */
+               if (ISSET(st, stopst) || p == stop)
+                       break;          /* NOTE BREAK OUT */
+
+               /* no, we must deal with this character */
+               ASSIGN(tmp, st);
+               ASSIGN(st, fresh);
+               assert(c != OUT);
+               st = step(m->g, startst, stopst, tmp, c, st);
+               SP("aft", st, c);
+               assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st));
+               p++;
+       }
+
+       assert(coldp != NULL);
+       m->coldp = coldp;
+       if (ISSET(st, stopst))
+               return(p+1);
+       else
+               return(NULL);
+}
+
+/*
+ - slow - step through the string more deliberately
+ == static char *slow(register struct match *m, char *start, \
+ ==    char *stop, sopno startst, sopno stopst);
+ */
+static char *                  /* where it ended */
+slow(m, start, stop, startst, stopst)
+register struct match *m;
+char *start;
+char *stop;
+sopno startst;
+sopno stopst;
+{
+       register states st = m->st;
+       register states empty = m->empty;
+       register states tmp = m->tmp;
+       register char *p = start;
+       register int c = (start == m->beginp) ? OUT : *(start-1);
+       register int lastc;     /* previous c */
+       register int flagch;
+       register int i;
+       register char *matchp;  /* last p at which a match ended */
+
+       AT("slow", start, stop, startst, stopst);
+       CLEAR(st);
+       SET1(st, startst);
+       SP("sstart", st, *p);
+       st = step(m->g, startst, stopst, st, NOTHING, st);
+       matchp = NULL;
+       for (;;) {
+               /* next character */
+               lastc = c;
+               c = (p == m->endp) ? OUT : *p;
+
+               /* is there an EOL and/or BOL between lastc and c? */
+               flagch = '\0';
+               i = 0;
+               if ( (lastc == '\n' && m->g->cflags&REG_NEWLINE) ||
+                               (lastc == OUT && !(m->eflags&REG_NOTBOL)) ) {
+                       flagch = BOL;
+                       i = m->g->nbol;
+               }
+               if ( (c == '\n' && m->g->cflags&REG_NEWLINE) ||
+                               (c == OUT && !(m->eflags&REG_NOTEOL)) ) {
+                       flagch = (flagch == BOL) ? BOLEOL : EOL;
+                       i += m->g->neol;
+               }
+               if (i != 0) {
+                       for (; i > 0; i--)
+                               st = step(m->g, startst, stopst, st, flagch, st);
+                       SP("sboleol", st, c);
+               }
+
+               /* how about a word boundary? */
+               if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) &&
+                                       (c != OUT && ISWORD(c)) ) {
+                       flagch = BOW;
+               }
+               if ( (lastc != OUT && ISWORD(lastc)) &&
+                               (flagch == EOL || (c != OUT && !ISWORD(c))) ) {
+                       flagch = EOW;
+               }
+               if (flagch == BOW || flagch == EOW) {
+                       st = step(m->g, startst, stopst, st, flagch, st);
+                       SP("sboweow", st, c);
+               }
+
+               /* are we done? */
+               if (ISSET(st, stopst))
+                       matchp = p;
+               if (EQ(st, empty) || p == stop)
+                       break;          /* NOTE BREAK OUT */
+
+               /* no, we must deal with this character */
+               ASSIGN(tmp, st);
+               ASSIGN(st, empty);
+               assert(c != OUT);
+               st = step(m->g, startst, stopst, tmp, c, st);
+               SP("saft", st, c);
+               assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st));
+               p++;
+       }
+
+       return(matchp);
+}
+
+
+/*
+ - step - map set of states reachable before char to set reachable after
+ == static states step(register struct re_guts *g, sopno start, sopno stop, \
+ ==    register states bef, int ch, register states aft);
+ == #define    BOL     (OUT+1)
+ == #define    EOL     (BOL+1)
+ == #define    BOLEOL  (BOL+2)
+ == #define    NOTHING (BOL+3)
+ == #define    BOW     (BOL+4)
+ == #define    EOW     (BOL+5)
+ == #define    CODEMAX (BOL+5)         // highest code used
+ == #define    NONCHAR(c)      ((c) > CHAR_MAX)
+ == #define    NNONCHAR        (CODEMAX-CHAR_MAX)
+ */
+static states
+step(g, start, stop, bef, ch, aft)
+register struct re_guts *g;
+sopno start;                   /* start state within strip */
+sopno stop;                    /* state after stop state within strip */
+register states bef;           /* states reachable before */
+int ch;                                /* character or NONCHAR code */
+register states aft;           /* states already known reachable after */
+{
+       register cset *cs;
+       register sop s;
+       register sopno pc;
+       register onestate here;         /* note, macros know this name */
+       register sopno look;
+       register int i;
+
+       for (pc = start, INIT(here, pc); pc != stop; pc++, INC(here)) {
+               s = g->strip[pc];
+               switch (OP(s)) {
+               case OEND:
+                       assert(pc == stop-1);
+                       break;
+               case OCHAR:
+                       /* only characters can match */
+                       assert(!NONCHAR(ch) || ch != (char)OPND(s));
+                       if (ch == (char)OPND(s))
+                               FWD(aft, bef, 1);
+                       break;
+               case OBOL:
+                       if (ch == BOL || ch == BOLEOL)
+                               FWD(aft, bef, 1);
+                       break;
+               case OEOL:
+                       if (ch == EOL || ch == BOLEOL)
+                               FWD(aft, bef, 1);
+                       break;
+               case OBOW:
+                       if (ch == BOW)
+                               FWD(aft, bef, 1);
+                       break;
+               case OEOW:
+                       if (ch == EOW)
+                               FWD(aft, bef, 1);
+                       break;
+               case OANY:
+                       if (!NONCHAR(ch))
+                               FWD(aft, bef, 1);
+                       break;
+               case OANYOF:
+                       cs = &g->sets[OPND(s)];
+                       if (!NONCHAR(ch) && CHIN(cs, ch))
+                               FWD(aft, bef, 1);
+                       break;
+               case OBACK_:            /* ignored here */
+               case O_BACK:
+                       FWD(aft, aft, 1);
+                       break;
+               case OPLUS_:            /* forward, this is just an empty */
+                       FWD(aft, aft, 1);
+                       break;
+               case O_PLUS:            /* both forward and back */
+                       FWD(aft, aft, 1);
+                       i = ISSETBACK(aft, OPND(s));
+                       BACK(aft, aft, OPND(s));
+                       if (!i && ISSETBACK(aft, OPND(s))) {
+                               /* oho, must reconsider loop body */
+                               pc -= OPND(s) + 1;
+                               INIT(here, pc);
+                       }
+                       break;
+               case OQUEST_:           /* two branches, both forward */
+                       FWD(aft, aft, 1);
+                       FWD(aft, aft, OPND(s));
+                       break;
+               case O_QUEST:           /* just an empty */
+                       FWD(aft, aft, 1);
+                       break;
+               case OLPAREN:           /* not significant here */
+               case ORPAREN:
+                       FWD(aft, aft, 1);
+                       break;
+               case OCH_:              /* mark the first two branches */
+                       FWD(aft, aft, 1);
+                       assert(OP(g->strip[pc+OPND(s)]) == OOR2);
+                       FWD(aft, aft, OPND(s));
+                       break;
+               case OOR1:              /* done a branch, find the O_CH */
+                       if (ISSTATEIN(aft, here)) {
+                               for (look = 1;
+                                               OP(s = g->strip[pc+look]) != O_CH;
+                                               look += OPND(s))
+                                       assert(OP(s) == OOR2);
+                               FWD(aft, aft, look);
+                       }
+                       break;
+               case OOR2:              /* propagate OCH_'s marking */
+                       FWD(aft, aft, 1);
+                       if (OP(g->strip[pc+OPND(s)]) != O_CH) {
+                               assert(OP(g->strip[pc+OPND(s)]) == OOR2);
+                               FWD(aft, aft, OPND(s));
+                       }
+                       break;
+               case O_CH:              /* just empty */
+                       FWD(aft, aft, 1);
+                       break;
+               default:                /* ooooops... */
+                       assert(nope);
+                       break;
+               }
+       }
+
+       return(aft);
+}
+
+#ifdef REDEBUG
+/*
+ - print - print a set of states
+ == #ifdef REDEBUG
+ == static void print(struct match *m, char *caption, states st, \
+ ==    int ch, FILE *d);
+ == #endif
+ */
+static void
+print(m, caption, st, ch, d)
+struct match *m;
+char *caption;
+states st;
+int ch;
+FILE *d;
+{
+       register struct re_guts *g = m->g;
+       register int i;
+       register int first = 1;
+
+       if (!(m->eflags&REG_TRACE))
+               return;
+
+       fprintf(d, "%s", caption);
+       if (ch != '\0')
+               fprintf(d, " %s", pchar(ch));
+       for (i = 0; i < g->nstates; i++)
+               if (ISSET(st, i)) {
+                       fprintf(d, "%s%d", (first) ? "\t" : ", ", i);
+                       first = 0;
+               }
+       fprintf(d, "\n");
+}
+
+/* 
+ - at - print current situation
+ == #ifdef REDEBUG
+ == static void at(struct match *m, char *title, char *start, char *stop, \
+ ==                                            sopno startst, sopno stopst);
+ == #endif
+ */
+static void
+at(m, title, start, stop, startst, stopst)
+struct match *m;
+char *title;
+char *start;
+char *stop;
+sopno startst;
+sopno stopst;
+{
+       if (!(m->eflags&REG_TRACE))
+               return;
+
+       printf("%s %s-", title, pchar(*start));
+       printf("%s ", pchar(*stop));
+       printf("%ld-%ld\n", (long)startst, (long)stopst);
+}
+
+#ifndef PCHARDONE
+#define        PCHARDONE       /* never again */
+/*
+ - pchar - make a character printable
+ == #ifdef REDEBUG
+ == static char *pchar(int ch);
+ == #endif
+ *
+ * Is this identical to regchar() over in debug.c?  Well, yes.  But a
+ * duplicate here avoids having a debugging-capable regexec.o tied to
+ * a matching debug.o, and this is convenient.  It all disappears in
+ * the non-debug compilation anyway, so it doesn't matter much.
+ */
+static char *                  /* -> representation */
+pchar(ch)
+int ch;
+{
+       static char pbuf[10];
+
+       if (isprint(ch) || ch == ' ')
+               sprintf(pbuf, "%c", ch);
+       else
+               sprintf(pbuf, "\\%o", ch);
+       return(pbuf);
+}
+#endif
+#endif
+
+#undef matcher
+#undef fast
+#undef slow
+#undef dissect
+#undef backref
+#undef step
+#undef print
+#undef at
+#undef match
diff --git a/src/backend/regex/re_format.7 b/src/backend/regex/re_format.7
new file mode 100644 (file)
index 0000000..db2f634
--- /dev/null
@@ -0,0 +1,269 @@
+.\" Copyright (c) 1992, 1993, 1994 Henry Spencer.
+.\" Copyright (c) 1992, 1993, 1994
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Henry Spencer.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"    @(#)re_format.7 8.3 (Berkeley) 3/20/94
+.\"
+.TH RE_FORMAT 7 "March 20, 1994"
+.SH NAME
+re_format \- POSIX 1003.2 regular expressions
+.SH DESCRIPTION
+Regular expressions (``RE''s),
+as defined in POSIX 1003.2, come in two forms:
+modern REs (roughly those of
+.IR egrep ;
+1003.2 calls these ``extended'' REs)
+and obsolete REs (roughly those of
+.IR ed ;
+1003.2 ``basic'' REs).
+Obsolete REs mostly exist for backward compatibility in some old programs;
+they will be discussed at the end.
+1003.2 leaves some aspects of RE syntax and semantics open;
+`\(dg' marks decisions on these aspects that
+may not be fully portable to other 1003.2 implementations.
+.PP
+A (modern) RE is one\(dg or more non-empty\(dg \fIbranches\fR,
+separated by `|'.
+It matches anything that matches one of the branches.
+.PP
+A branch is one\(dg or more \fIpieces\fR, concatenated.
+It matches a match for the first, followed by a match for the second, etc.
+.PP
+A piece is an \fIatom\fR possibly followed
+by a single\(dg `*', `+', `?', or \fIbound\fR.
+An atom followed by `*' matches a sequence of 0 or more matches of the atom.
+An atom followed by `+' matches a sequence of 1 or more matches of the atom.
+An atom followed by `?' matches a sequence of 0 or 1 matches of the atom.
+.PP
+A \fIbound\fR is `{' followed by an unsigned decimal integer,
+possibly followed by `,'
+possibly followed by another unsigned decimal integer,
+always followed by `}'.
+The integers must lie between 0 and RE_DUP_MAX (255\(dg) inclusive,
+and if there are two of them, the first may not exceed the second.
+An atom followed by a bound containing one integer \fIi\fR
+and no comma matches
+a sequence of exactly \fIi\fR matches of the atom.
+An atom followed by a bound
+containing one integer \fIi\fR and a comma matches
+a sequence of \fIi\fR or more matches of the atom.
+An atom followed by a bound
+containing two integers \fIi\fR and \fIj\fR matches
+a sequence of \fIi\fR through \fIj\fR (inclusive) matches of the atom.
+.PP
+An atom is a regular expression enclosed in `()' (matching a match for the
+regular expression),
+an empty set of `()' (matching the null string)\(dg,
+a \fIbracket expression\fR (see below), `.'
+(matching any single character), `^' (matching the null string at the
+beginning of a line), `$' (matching the null string at the
+end of a line), a `\e' followed by one of the characters
+`^.[$()|*+?{\e'
+(matching that character taken as an ordinary character),
+a `\e' followed by any other character\(dg
+(matching that character taken as an ordinary character,
+as if the `\e' had not been present\(dg),
+or a single character with no other significance (matching that character).
+A `{' followed by a character other than a digit is an ordinary
+character, not the beginning of a bound\(dg.
+It is illegal to end an RE with `\e'.
+.PP
+A \fIbracket expression\fR is a list of characters enclosed in `[]'.
+It normally matches any single character from the list (but see below).
+If the list begins with `^',
+it matches any single character
+(but see below) \fInot\fR from the rest of the list.
+If two characters in the list are separated by `\-', this is shorthand
+for the full \fIrange\fR of characters between those two (inclusive) in the
+collating sequence,
+e.g. `[0-9]' in ASCII matches any decimal digit.
+It is illegal\(dg for two ranges to share an
+endpoint, e.g. `a-c-e'.
+Ranges are very collating-sequence-dependent,
+and portable programs should avoid relying on them.
+.PP
+To include a literal `]' in the list, make it the first character
+(following a possible `^').
+To include a literal `\-', make it the first or last character,
+or the second endpoint of a range.
+To use a literal `\-' as the first endpoint of a range,
+enclose it in `[.' and `.]' to make it a collating element (see below).
+With the exception of these and some combinations using `[' (see next
+paragraphs), all other special characters, including `\e', lose their
+special significance within a bracket expression.
+.PP
+Within a bracket expression, a collating element (a character,
+a multi-character sequence that collates as if it were a single character,
+or a collating-sequence name for either)
+enclosed in `[.' and `.]' stands for the
+sequence of characters of that collating element.
+The sequence is a single element of the bracket expression's list.
+A bracket expression containing a multi-character collating element 
+can thus match more than one character,
+e.g. if the collating sequence includes a `ch' collating element,
+then the RE `[[.ch.]]*c' matches the first five characters
+of `chchcc'.
+.PP
+Within a bracket expression, a collating element enclosed in `[=' and
+`=]' is an equivalence class, standing for the sequences of characters
+of all collating elements equivalent to that one, including itself.
+(If there are no other equivalent collating elements,
+the treatment is as if the enclosing delimiters were `[.' and `.]'.)
+For example, if o and \o'o^' are the members of an equivalence class,
+then `[[=o=]]', `[[=\o'o^'=]]', and `[o\o'o^']' are all synonymous.
+An equivalence class may not\(dg be an endpoint
+of a range.
+.PP
+Within a bracket expression, the name of a \fIcharacter class\fR enclosed
+in `[:' and `:]' stands for the list of all characters belonging to that
+class.
+Standard character class names are:
+.PP
+.RS
+.nf
+.ta 3c 6c 9c
+alnum  digit   punct
+alpha  graph   space
+blank  lower   upper
+cntrl  print   xdigit
+.fi
+.RE
+.PP
+These stand for the character classes defined in
+.IR ctype (3).
+A locale may provide others.
+A character class may not be used as an endpoint of a range.
+.PP
+There are two special cases\(dg of bracket expressions:
+the bracket expressions `[[:<:]]' and `[[:>:]]' match the null string at
+the beginning and end of a word respectively.
+A word is defined as a sequence of
+word characters
+which is neither preceded nor followed by
+word characters.
+A word character is an
+.I alnum
+character (as defined by
+.IR ctype (3))
+or an underscore.
+This is an extension,
+compatible with but not specified by POSIX 1003.2,
+and should be used with
+caution in software intended to be portable to other systems.
+.PP
+In the event that an RE could match more than one substring of a given
+string,
+the RE matches the one starting earliest in the string.
+If the RE could match more than one substring starting at that point,
+it matches the longest.
+Subexpressions also match the longest possible substrings, subject to
+the constraint that the whole match be as long as possible,
+with subexpressions starting earlier in the RE taking priority over
+ones starting later.
+Note that higher-level subexpressions thus take priority over
+their lower-level component subexpressions.
+.PP
+Match lengths are measured in characters, not collating elements.
+A null string is considered longer than no match at all.
+For example,
+`bb*' matches the three middle characters of `abbbc',
+`(wee|week)(knights|nights)' matches all ten characters of `weeknights',
+when `(.*).*' is matched against `abc' the parenthesized subexpression
+matches all three characters, and
+when `(a*)*' is matched against `bc' both the whole RE and the parenthesized
+subexpression match the null string.
+.PP
+If case-independent matching is specified,
+the effect is much as if all case distinctions had vanished from the
+alphabet.
+When an alphabetic that exists in multiple cases appears as an
+ordinary character outside a bracket expression, it is effectively
+transformed into a bracket expression containing both cases,
+e.g. `x' becomes `[xX]'.
+When it appears inside a bracket expression, all case counterparts
+of it are added to the bracket expression, so that (e.g.) `[x]'
+becomes `[xX]' and `[^x]' becomes `[^xX]'.
+.PP
+No particular limit is imposed on the length of REs\(dg.
+Programs intended to be portable should not employ REs longer
+than 256 bytes,
+as an implementation can refuse to accept such REs and remain
+POSIX-compliant.
+.PP
+Obsolete (``basic'') regular expressions differ in several respects.
+`|', `+', and `?' are ordinary characters and there is no equivalent
+for their functionality.
+The delimiters for bounds are `\e{' and `\e}',
+with `{' and `}' by themselves ordinary characters.
+The parentheses for nested subexpressions are `\e(' and `\e)',
+with `(' and `)' by themselves ordinary characters.
+`^' is an ordinary character except at the beginning of the
+RE or\(dg the beginning of a parenthesized subexpression,
+`$' is an ordinary character except at the end of the
+RE or\(dg the end of a parenthesized subexpression,
+and `*' is an ordinary character if it appears at the beginning of the
+RE or the beginning of a parenthesized subexpression
+(after a possible leading `^').
+Finally, there is one new type of atom, a \fIback reference\fR:
+`\e' followed by a non-zero decimal digit \fId\fR
+matches the same sequence of characters
+matched by the \fId\fRth parenthesized subexpression
+(numbering subexpressions by the positions of their opening parentheses,
+left to right),
+so that (e.g.) `\e([bc]\e)\e1' matches `bb' or `cc' but not `bc'.
+.SH SEE ALSO
+regex(3)
+.PP
+POSIX 1003.2, section 2.8 (Regular Expression Notation).
+.SH BUGS
+Having two kinds of REs is a botch.
+.PP
+The current 1003.2 spec says that `)' is an ordinary character in
+the absence of an unmatched `(';
+this was an unintentional result of a wording error,
+and change is likely.
+Avoid relying on it.
+.PP
+Back references are a dreadful botch,
+posing major problems for efficient implementations.
+They are also somewhat vaguely defined
+(does
+`a\e(\e(b\e)*\e2\e)*d' match `abbbd'?).
+Avoid using them.
+.PP
+1003.2's specification of case-independent matching is vague.
+The ``one case implies all cases'' definition given above
+is current consensus among implementors as to the right interpretation.
+.PP
+The syntax for word boundaries is incredibly ugly.
diff --git a/src/backend/regex/regcomp.c b/src/backend/regex/regcomp.c
new file mode 100644 (file)
index 0000000..27c8678
--- /dev/null
@@ -0,0 +1,1701 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)regcomp.c   8.5 (Berkeley) 3/20/94
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)regcomp.c  8.5 (Berkeley) 3/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <regex.h>
+
+#include "utils.h"
+#include "regex2.h"
+
+#include "cclass.h"
+#include "cname.h"
+
+/*
+ * parse structure, passed up and down to avoid global variables and
+ * other clumsinesses
+ */
+struct parse {
+       char *next;             /* next character in RE */
+       char *end;              /* end of string (-> NUL normally) */
+       int error;              /* has an error been seen? */
+       sop *strip;             /* malloced strip */
+       sopno ssize;            /* malloced strip size (allocated) */
+       sopno slen;             /* malloced strip length (used) */
+       int ncsalloc;           /* number of csets allocated */
+       struct re_guts *g;
+#      define  NPAREN  10      /* we need to remember () 1-9 for back refs */
+       sopno pbegin[NPAREN];   /* -> ( ([0] unused) */
+       sopno pend[NPAREN];     /* -> ) ([0] unused) */
+};
+
+/* ========= begin header generated by ./mkh ========= */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* === regcomp.c === */
+static void p_ere __P((struct parse *p, int stop));
+static void p_ere_exp __P((struct parse *p));
+static void p_str __P((struct parse *p));
+static void p_bre __P((struct parse *p, int end1, int end2));
+static int p_simp_re __P((struct parse *p, int starordinary));
+static int p_count __P((struct parse *p));
+static void p_bracket __P((struct parse *p));
+static void p_b_term __P((struct parse *p, cset *cs));
+static void p_b_cclass __P((struct parse *p, cset *cs));
+static void p_b_eclass __P((struct parse *p, cset *cs));
+static char p_b_symbol __P((struct parse *p));
+static char p_b_coll_elem __P((struct parse *p, int endc));
+static char othercase __P((int ch));
+static void bothcases __P((struct parse *p, int ch));
+static void ordinary __P((struct parse *p, int ch));
+static void nonnewline __P((struct parse *p));
+static void repeat __P((struct parse *p, sopno start, int from, int to));
+static int seterr __P((struct parse *p, int e));
+static cset *allocset __P((struct parse *p));
+static void freeset __P((struct parse *p, cset *cs));
+static int freezeset __P((struct parse *p, cset *cs));
+static int firstch __P((struct parse *p, cset *cs));
+static int nch __P((struct parse *p, cset *cs));
+static void mcadd __P((struct parse *p, cset *cs, char *cp));
+/* static void mcsub __P((cset *cs, char *cp)); */
+/*static int mcin __P((cset *cs, char *cp));*/
+/*static char *mcfind __P((cset *cs, char *cp)); */
+static void mcinvert __P((struct parse *p, cset *cs));
+static void mccase __P((struct parse *p, cset *cs));
+static int isinsets __P((struct re_guts *g, int c));
+static int samesets __P((struct re_guts *g, int c1, int c2));
+static void categorize __P((struct parse *p, struct re_guts *g));
+static sopno dupl __P((struct parse *p, sopno start, sopno finish));
+static void doemit __P((struct parse *p, sop op, size_t opnd));
+static void doinsert __P((struct parse *p, sop op, size_t opnd, sopno pos));
+static void dofwd __P((struct parse *p, sopno pos, sop value));
+static void enlarge __P((struct parse *p, sopno size));
+static void stripsnug __P((struct parse *p, struct re_guts *g));
+static void findmust __P((struct parse *p, struct re_guts *g));
+static sopno pluscount __P((struct parse *p, struct re_guts *g));
+
+#ifdef __cplusplus
+}
+#endif
+/* ========= end header generated by ./mkh ========= */
+
+static char nuls[10];          /* place to point scanner in event of error */
+
+/*
+ * macros for use with parse structure
+ * BEWARE:  these know that the parse structure is named `p' !!!
+ */
+#define        PEEK()  (*p->next)
+#define        PEEK2() (*(p->next+1))
+#define        MORE()  (p->next < p->end)
+#define        MORE2() (p->next+1 < p->end)
+#define        SEE(c)  (MORE() && PEEK() == (c))
+#define        SEETWO(a, b)    (MORE() && MORE2() && PEEK() == (a) && PEEK2() == (b))
+#define        EAT(c)  ((SEE(c)) ? (NEXT(), 1) : 0)
+#define        EATTWO(a, b)    ((SEETWO(a, b)) ? (NEXT2(), 1) : 0)
+#define        NEXT()  (p->next++)
+#define        NEXT2() (p->next += 2)
+#define        NEXTn(n)        (p->next += (n))
+#define        GETNEXT()       (*p->next++)
+#define        SETERROR(e)     seterr(p, (e))
+#define        REQUIRE(co, e)  if (!(co)) SETERROR(e)
+#define        MUSTSEE(c, e)   REQUIRE(MORE() && PEEK() == (c), e)
+#define        MUSTEAT(c, e)   REQUIRE(MORE() && GETNEXT() == (c), e)
+#define        MUSTNOTSEE(c, e)        REQUIRE(!MORE() || PEEK() != (c), e)
+#define        EMIT(op, sopnd) doemit(p, (sop)(op), (size_t)(sopnd))
+#define        INSERT(op, pos) doinsert(p, (sop)(op), HERE()-(pos)+1, pos)
+#define        AHEAD(pos)              dofwd(p, pos, HERE()-(pos))
+#define        ASTERN(sop, pos)        EMIT(sop, HERE()-pos)
+#define        HERE()          (p->slen)
+#define        THERE()         (p->slen - 1)
+#define        THERETHERE()    (p->slen - 2)
+#define        DROP(n) (p->slen -= (n))
+
+#ifndef NDEBUG
+static int never = 0;          /* for use in asserts; shuts lint up */
+#else
+#define        never   0               /* some <assert.h>s have bugs too */
+#endif
+
+/*
+ - regcomp - interface for parser and compilation
+ = extern int regcomp(regex_t *, const char *, int);
+ = #define     REG_BASIC       0000
+ = #define     REG_EXTENDED    0001
+ = #define     REG_ICASE       0002
+ = #define     REG_NOSUB       0004
+ = #define     REG_NEWLINE     0010
+ = #define     REG_NOSPEC      0020
+ = #define     REG_PEND        0040
+ = #define     REG_DUMP        0200
+ */
+int                            /* 0 success, otherwise REG_something */
+pg95_regcomp(preg, pattern, cflags)
+regex_t *preg;
+const char *pattern;
+int cflags;
+{
+       struct parse pa;
+       register struct re_guts *g;
+       register struct parse *p = &pa;
+       register int i;
+       register size_t len;
+#ifdef REDEBUG
+#      define  GOODFLAGS(f)    (f)
+#else
+#      define  GOODFLAGS(f)    ((f)&~REG_DUMP)
+#endif
+
+       cflags = GOODFLAGS(cflags);
+       if ((cflags&REG_EXTENDED) && (cflags&REG_NOSPEC))
+               return(REG_INVARG);
+
+       if (cflags&REG_PEND) {
+               if (preg->re_endp < pattern)
+                       return(REG_INVARG);
+               len = preg->re_endp - pattern;
+       } else
+               len = strlen((char *)pattern);
+
+       /* do the mallocs early so failure handling is easy */
+       g = (struct re_guts *)malloc(sizeof(struct re_guts) +
+                                                       (NC-1)*sizeof(cat_t));
+       if (g == NULL)
+               return(REG_ESPACE);
+       p->ssize = len/(size_t)2*(size_t)3 + (size_t)1; /* ugh */
+       p->strip = (sop *)malloc(p->ssize * sizeof(sop));
+       p->slen = 0;
+       if (p->strip == NULL) {
+               free((char *)g);
+               return(REG_ESPACE);
+       }
+
+       /* set things up */
+       p->g = g;
+       p->next = (char *)pattern;      /* convenience; we do not modify it */
+       p->end = p->next + len;
+       p->error = 0;
+       p->ncsalloc = 0;
+       for (i = 0; i < NPAREN; i++) {
+               p->pbegin[i] = 0;
+               p->pend[i] = 0;
+       }
+       g->csetsize = NC;
+       g->sets = NULL;
+       g->setbits = NULL;
+       g->ncsets = 0;
+       g->cflags = cflags;
+       g->iflags = 0;
+       g->nbol = 0;
+       g->neol = 0;
+       g->must = NULL;
+       g->mlen = 0;
+       g->nsub = 0;
+       g->ncategories = 1;     /* category 0 is "everything else" */
+       g->categories = &g->catspace[-(CHAR_MIN)];
+       (void) memset((char *)g->catspace, 0, NC*sizeof(cat_t));
+       g->backrefs = 0;
+
+       /* do it */
+       EMIT(OEND, 0);
+       g->firststate = THERE();
+       if (cflags&REG_EXTENDED)
+               p_ere(p, OUT);
+       else if (cflags&REG_NOSPEC)
+               p_str(p);
+       else
+               p_bre(p, OUT, OUT);
+       EMIT(OEND, 0);
+       g->laststate = THERE();
+
+       /* tidy up loose ends and fill things in */
+       categorize(p, g);
+       stripsnug(p, g);
+       findmust(p, g);
+       g->nplus = pluscount(p, g);
+       g->magic = MAGIC2;
+       preg->re_nsub = g->nsub;
+       preg->re_g = g;
+       preg->re_magic = MAGIC1;
+#ifndef REDEBUG
+       /* not debugging, so can't rely on the assert() in regexec() */
+       if (g->iflags&BAD)
+               SETERROR(REG_ASSERT);
+#endif
+
+       /* win or lose, we're done */
+       if (p->error != 0)      /* lose */
+               pg95_regfree(preg);
+       return(p->error);
+}
+
+/*
+ - p_ere - ERE parser top level, concatenation and alternation
+ == static void p_ere(register struct parse *p, int stop);
+ */
+static void
+p_ere(p, stop)
+register struct parse *p;
+int stop;                      /* character this ERE should end at */
+{
+       register char c;
+       register sopno prevback;
+       register sopno prevfwd;
+       register sopno conc;
+       register int first = 1;         /* is this the first alternative? */
+
+       for (;;) {
+               /* do a bunch of concatenated expressions */
+               conc = HERE();
+               while (MORE() && (c = PEEK()) != '|' && c != stop)
+                       p_ere_exp(p);
+               REQUIRE(HERE() != conc, REG_EMPTY);     /* require nonempty */
+
+               if (!EAT('|'))
+                       break;          /* NOTE BREAK OUT */
+
+               if (first) {
+                       INSERT(OCH_, conc);     /* offset is wrong */
+                       prevfwd = conc;
+                       prevback = conc;
+                       first = 0;
+               }
+               ASTERN(OOR1, prevback);
+               prevback = THERE();
+               AHEAD(prevfwd);                 /* fix previous offset */
+               prevfwd = HERE();
+               EMIT(OOR2, 0);                  /* offset is very wrong */
+       }
+
+       if (!first) {           /* tail-end fixups */
+               AHEAD(prevfwd);
+               ASTERN(O_CH, prevback);
+       }
+
+       assert(!MORE() || SEE(stop));
+}
+
+/*
+ - p_ere_exp - parse one subERE, an atom possibly followed by a repetition op
+ == static void p_ere_exp(register struct parse *p);
+ */
+static void
+p_ere_exp(p)
+register struct parse *p;
+{
+       register char c;
+       register sopno pos;
+       register int count;
+       register int count2;
+       register sopno subno;
+       int wascaret = 0;
+
+       assert(MORE());         /* caller should have ensured this */
+       c = GETNEXT();
+
+       pos = HERE();
+       switch (c) {
+       case '(':
+               REQUIRE(MORE(), REG_EPAREN);
+               p->g->nsub++;
+               subno = p->g->nsub;
+               if (subno < NPAREN)
+                       p->pbegin[subno] = HERE();
+               EMIT(OLPAREN, subno);
+               if (!SEE(')'))
+                       p_ere(p, ')');
+               if (subno < NPAREN) {
+                       p->pend[subno] = HERE();
+                       assert(p->pend[subno] != 0);
+               }
+               EMIT(ORPAREN, subno);
+               MUSTEAT(')', REG_EPAREN);
+               break;
+#ifndef POSIX_MISTAKE
+       case ')':               /* happens only if no current unmatched ( */
+               /*
+                * You may ask, why the ifndef?  Because I didn't notice
+                * this until slightly too late for 1003.2, and none of the
+                * other 1003.2 regular-expression reviewers noticed it at
+                * all.  So an unmatched ) is legal POSIX, at least until
+                * we can get it fixed.
+                */
+               SETERROR(REG_EPAREN);
+               break;
+#endif
+       case '^':
+               EMIT(OBOL, 0);
+               p->g->iflags |= USEBOL;
+               p->g->nbol++;
+               wascaret = 1;
+               break;
+       case '$':
+               EMIT(OEOL, 0);
+               p->g->iflags |= USEEOL;
+               p->g->neol++;
+               break;
+       case '|':
+               SETERROR(REG_EMPTY);
+               break;
+       case '*':
+       case '+':
+       case '?':
+               SETERROR(REG_BADRPT);
+               break;
+       case '.':
+               if (p->g->cflags&REG_NEWLINE)
+                       nonnewline(p);
+               else
+                       EMIT(OANY, 0);
+               break;
+       case '[':
+               p_bracket(p);
+               break;
+       case '\\':
+               REQUIRE(MORE(), REG_EESCAPE);
+               c = GETNEXT();
+               ordinary(p, c);
+               break;
+       case '{':               /* okay as ordinary except if digit follows */
+               REQUIRE(!MORE() || !isdigit(PEEK()), REG_BADRPT);
+               /* FALLTHROUGH */
+       default:
+               ordinary(p, c);
+               break;
+       }
+
+       if (!MORE())
+               return;
+       c = PEEK();
+       /* we call { a repetition if followed by a digit */
+       if (!( c == '*' || c == '+' || c == '?' ||
+                               (c == '{' && MORE2() && isdigit(PEEK2())) ))
+               return;         /* no repetition, we're done */
+       NEXT();
+
+       REQUIRE(!wascaret, REG_BADRPT);
+       switch (c) {
+       case '*':       /* implemented as +? */
+               /* this case does not require the (y|) trick, noKLUDGE */
+               INSERT(OPLUS_, pos);
+               ASTERN(O_PLUS, pos);
+               INSERT(OQUEST_, pos);
+               ASTERN(O_QUEST, pos);
+               break;
+       case '+':
+               INSERT(OPLUS_, pos);
+               ASTERN(O_PLUS, pos);
+               break;
+       case '?':
+               /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */
+               INSERT(OCH_, pos);              /* offset slightly wrong */
+               ASTERN(OOR1, pos);              /* this one's right */
+               AHEAD(pos);                     /* fix the OCH_ */
+               EMIT(OOR2, 0);                  /* offset very wrong... */
+               AHEAD(THERE());                 /* ...so fix it */
+               ASTERN(O_CH, THERETHERE());
+               break;
+       case '{':
+               count = p_count(p);
+               if (EAT(',')) {
+                       if (isdigit(PEEK())) {
+                               count2 = p_count(p);
+                               REQUIRE(count <= count2, REG_BADBR);
+                       } else          /* single number with comma */
+                               count2 = INFINITY;
+               } else          /* just a single number */
+                       count2 = count;
+               repeat(p, pos, count, count2);
+               if (!EAT('}')) {        /* error heuristics */
+                       while (MORE() && PEEK() != '}')
+                               NEXT();
+                       REQUIRE(MORE(), REG_EBRACE);
+                       SETERROR(REG_BADBR);
+               }
+               break;
+       }
+
+       if (!MORE())
+               return;
+       c = PEEK();
+       if (!( c == '*' || c == '+' || c == '?' ||
+                               (c == '{' && MORE2() && isdigit(PEEK2())) ) )
+               return;
+       SETERROR(REG_BADRPT);
+}
+
+/*
+ - p_str - string (no metacharacters) "parser"
+ == static void p_str(register struct parse *p);
+ */
+static void
+p_str(p)
+register struct parse *p;
+{
+       REQUIRE(MORE(), REG_EMPTY);
+       while (MORE())
+               ordinary(p, GETNEXT());
+}
+
+/*
+ - p_bre - BRE parser top level, anchoring and concatenation
+ == static void p_bre(register struct parse *p, register int end1, \
+ ==    register int end2);
+ * Giving end1 as OUT essentially eliminates the end1/end2 check.
+ *
+ * This implementation is a bit of a kludge, in that a trailing $ is first
+ * taken as an ordinary character and then revised to be an anchor.  The
+ * only undesirable side effect is that '$' gets included as a character
+ * category in such cases.  This is fairly harmless; not worth fixing.
+ * The amount of lookahead needed to avoid this kludge is excessive.
+ */
+static void
+p_bre(p, end1, end2)
+register struct parse *p;
+register int end1;             /* first terminating character */
+register int end2;             /* second terminating character */
+{
+       register sopno start = HERE();
+       register int first = 1;                 /* first subexpression? */
+       register int wasdollar = 0;
+
+       if (EAT('^')) {
+               EMIT(OBOL, 0);
+               p->g->iflags |= USEBOL;
+               p->g->nbol++;
+       }
+       while (MORE() && !SEETWO(end1, end2)) {
+               wasdollar = p_simp_re(p, first);
+               first = 0;
+       }
+       if (wasdollar) {        /* oops, that was a trailing anchor */
+               DROP(1);
+               EMIT(OEOL, 0);
+               p->g->iflags |= USEEOL;
+               p->g->neol++;
+       }
+
+       REQUIRE(HERE() != start, REG_EMPTY);    /* require nonempty */
+}
+
+/*
+ - p_simp_re - parse a simple RE, an atom possibly followed by a repetition
+ == static int p_simp_re(register struct parse *p, int starordinary);
+ */
+static int                     /* was the simple RE an unbackslashed $? */
+p_simp_re(p, starordinary)
+register struct parse *p;
+int starordinary;              /* is a leading * an ordinary character? */
+{
+       register int c;
+       register int count;
+       register int count2;
+       register sopno pos;
+       register int i;
+       register sopno subno;
+#      define  BACKSL  (1<<CHAR_BIT)
+
+       pos = HERE();           /* repetion op, if any, covers from here */
+
+       assert(MORE());         /* caller should have ensured this */
+       c = GETNEXT();
+       if (c == '\\') {
+               REQUIRE(MORE(), REG_EESCAPE);
+               c = BACKSL | (unsigned char)GETNEXT();
+       }
+       switch (c) {
+       case '.':
+               if (p->g->cflags&REG_NEWLINE)
+                       nonnewline(p);
+               else
+                       EMIT(OANY, 0);
+               break;
+       case '[':
+               p_bracket(p);
+               break;
+       case BACKSL|'{':
+               SETERROR(REG_BADRPT);
+               break;
+       case BACKSL|'(':
+               p->g->nsub++;
+               subno = p->g->nsub;
+               if (subno < NPAREN)
+                       p->pbegin[subno] = HERE();
+               EMIT(OLPAREN, subno);
+               /* the MORE here is an error heuristic */
+               if (MORE() && !SEETWO('\\', ')'))
+                       p_bre(p, '\\', ')');
+               if (subno < NPAREN) {
+                       p->pend[subno] = HERE();
+                       assert(p->pend[subno] != 0);
+               }
+               EMIT(ORPAREN, subno);
+               REQUIRE(EATTWO('\\', ')'), REG_EPAREN);
+               break;
+       case BACKSL|')':        /* should not get here -- must be user */
+       case BACKSL|'}':
+               SETERROR(REG_EPAREN);
+               break;
+       case BACKSL|'1':
+       case BACKSL|'2':
+       case BACKSL|'3':
+       case BACKSL|'4':
+       case BACKSL|'5':
+       case BACKSL|'6':
+       case BACKSL|'7':
+       case BACKSL|'8':
+       case BACKSL|'9':
+               i = (c&~BACKSL) - '0';
+               assert(i < NPAREN);
+               if (p->pend[i] != 0) {
+                       assert(i <= p->g->nsub);
+                       EMIT(OBACK_, i);
+                       assert(p->pbegin[i] != 0);
+                       assert(OP(p->strip[p->pbegin[i]]) == OLPAREN);
+                       assert(OP(p->strip[p->pend[i]]) == ORPAREN);
+                       (void) dupl(p, p->pbegin[i]+1, p->pend[i]);
+                       EMIT(O_BACK, i);
+               } else
+                       SETERROR(REG_ESUBREG);
+               p->g->backrefs = 1;
+               break;
+       case '*':
+               REQUIRE(starordinary, REG_BADRPT);
+               /* FALLTHROUGH */
+       default:
+               ordinary(p, c &~ BACKSL);
+               break;
+       }
+
+       if (EAT('*')) {         /* implemented as +? */
+               /* this case does not require the (y|) trick, noKLUDGE */
+               INSERT(OPLUS_, pos);
+               ASTERN(O_PLUS, pos);
+               INSERT(OQUEST_, pos);
+               ASTERN(O_QUEST, pos);
+       } else if (EATTWO('\\', '{')) {
+               count = p_count(p);
+               if (EAT(',')) {
+                       if (MORE() && isdigit(PEEK())) {
+                               count2 = p_count(p);
+                               REQUIRE(count <= count2, REG_BADBR);
+                       } else          /* single number with comma */
+                               count2 = INFINITY;
+               } else          /* just a single number */
+                       count2 = count;
+               repeat(p, pos, count, count2);
+               if (!EATTWO('\\', '}')) {       /* error heuristics */
+                       while (MORE() && !SEETWO('\\', '}'))
+                               NEXT();
+                       REQUIRE(MORE(), REG_EBRACE);
+                       SETERROR(REG_BADBR);
+               }
+       } else if (c == (unsigned char)'$')     /* $ (but not \$) ends it */
+               return(1);
+
+       return(0);
+}
+
+/*
+ - p_count - parse a repetition count
+ == static int p_count(register struct parse *p);
+ */
+static int                     /* the value */
+p_count(p)
+register struct parse *p;
+{
+       register int count = 0;
+       register int ndigits = 0;
+
+       while (MORE() && isdigit(PEEK()) && count <= DUPMAX) {
+               count = count*10 + (GETNEXT() - '0');
+               ndigits++;
+       }
+
+       REQUIRE(ndigits > 0 && count <= DUPMAX, REG_BADBR);
+       return(count);
+}
+
+/*
+ - p_bracket - parse a bracketed character list
+ == static void p_bracket(register struct parse *p);
+ *
+ * Note a significant property of this code:  if the allocset() did SETERROR,
+ * no set operations are done.
+ */
+static void
+p_bracket(p)
+register struct parse *p;
+{
+       register cset *cs = allocset(p);
+       register int invert = 0;
+
+       /* Dept of Truly Sickening Special-Case Kludges */
+       if (p->next + 5 < p->end && strncmp(p->next, "[:<:]]", 6) == 0) {
+               EMIT(OBOW, 0);
+               NEXTn(6);
+               return;
+       }
+       if (p->next + 5 < p->end && strncmp(p->next, "[:>:]]", 6) == 0) {
+               EMIT(OEOW, 0);
+               NEXTn(6);
+               return;
+       }
+
+       if (EAT('^'))
+               invert++;       /* make note to invert set at end */
+       if (EAT(']'))
+               CHadd(cs, ']');
+       else if (EAT('-'))
+               CHadd(cs, '-');
+       while (MORE() && PEEK() != ']' && !SEETWO('-', ']'))
+               p_b_term(p, cs);
+       if (EAT('-'))
+               CHadd(cs, '-');
+       MUSTEAT(']', REG_EBRACK);
+
+       if (p->error != 0)      /* don't mess things up further */
+               return;
+
+       if (p->g->cflags&REG_ICASE) {
+               register int i;
+               register int ci;
+
+               for (i = p->g->csetsize - 1; i >= 0; i--)
+                       if (CHIN(cs, i) && isalpha(i)) {
+                               ci = othercase(i);
+                               if (ci != i)
+                                       CHadd(cs, ci);
+                       }
+               if (cs->multis != NULL)
+                       mccase(p, cs);
+       }
+       if (invert) {
+               register int i;
+
+               for (i = p->g->csetsize - 1; i >= 0; i--)
+                       if (CHIN(cs, i))
+                               CHsub(cs, i);
+                       else
+                               CHadd(cs, i);
+               if (p->g->cflags&REG_NEWLINE)
+                       CHsub(cs, '\n');
+               if (cs->multis != NULL)
+                       mcinvert(p, cs);
+       }
+
+       assert(cs->multis == NULL);             /* xxx */
+
+       if (nch(p, cs) == 1) {          /* optimize singleton sets */
+               ordinary(p, firstch(p, cs));
+               freeset(p, cs);
+       } else
+               EMIT(OANYOF, freezeset(p, cs));
+}
+
+/*
+ - p_b_term - parse one term of a bracketed character list
+ == static void p_b_term(register struct parse *p, register cset *cs);
+ */
+static void
+p_b_term(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       register char c;
+       register char start, finish;
+       register int i;
+
+       /* classify what we've got */
+       switch ((MORE()) ? PEEK() : '\0') {
+       case '[':
+               c = (MORE2()) ? PEEK2() : '\0';
+               break;
+       case '-':
+               SETERROR(REG_ERANGE);
+               return;                 /* NOTE RETURN */
+               break;
+       default:
+               c = '\0';
+               break;
+       }
+
+       switch (c) {
+       case ':':               /* character class */
+               NEXT2();
+               REQUIRE(MORE(), REG_EBRACK);
+               c = PEEK();
+               REQUIRE(c != '-' && c != ']', REG_ECTYPE);
+               p_b_cclass(p, cs);
+               REQUIRE(MORE(), REG_EBRACK);
+               REQUIRE(EATTWO(':', ']'), REG_ECTYPE);
+               break;
+       case '=':               /* equivalence class */
+               NEXT2();
+               REQUIRE(MORE(), REG_EBRACK);
+               c = PEEK();
+               REQUIRE(c != '-' && c != ']', REG_ECOLLATE);
+               p_b_eclass(p, cs);
+               REQUIRE(MORE(), REG_EBRACK);
+               REQUIRE(EATTWO('=', ']'), REG_ECOLLATE);
+               break;
+       default:                /* symbol, ordinary character, or range */
+/* xxx revision needed for multichar stuff */
+               start = p_b_symbol(p);
+               if (SEE('-') && MORE2() && PEEK2() != ']') {
+                       /* range */
+                       NEXT();
+                       if (EAT('-'))
+                               finish = '-';
+                       else
+                               finish = p_b_symbol(p);
+               } else
+                       finish = start;
+/* xxx what about signed chars here... */
+               REQUIRE(start <= finish, REG_ERANGE);
+               for (i = start; i <= finish; i++)
+                       CHadd(cs, i);
+               break;
+       }
+}
+
+/*
+ - p_b_cclass - parse a character-class name and deal with it
+ == static void p_b_cclass(register struct parse *p, register cset *cs);
+ */
+static void
+p_b_cclass(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       register char *sp = p->next;
+       register struct cclass *cp;
+       register size_t len;
+       register char *u;
+       register char c;
+
+       while (MORE() && isalpha(PEEK()))
+               NEXT();
+       len = p->next - sp;
+       for (cp = cclasses; cp->name != NULL; cp++)
+               if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0')
+                       break;
+       if (cp->name == NULL) {
+               /* oops, didn't find it */
+               SETERROR(REG_ECTYPE);
+               return;
+       }
+
+       u = cp->chars;
+       while ((c = *u++) != '\0')
+               CHadd(cs, c);
+       for (u = cp->multis; *u != '\0'; u += strlen(u) + 1)
+               MCadd(p, cs, u);
+}
+
+/*
+ - p_b_eclass - parse an equivalence-class name and deal with it
+ == static void p_b_eclass(register struct parse *p, register cset *cs);
+ *
+ * This implementation is incomplete. xxx
+ */
+static void
+p_b_eclass(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       register char c;
+
+       c = p_b_coll_elem(p, '=');
+       CHadd(cs, c);
+}
+
+/*
+ - p_b_symbol - parse a character or [..]ed multicharacter collating symbol
+ == static char p_b_symbol(register struct parse *p);
+ */
+static char                    /* value of symbol */
+p_b_symbol(p)
+register struct parse *p;
+{
+       register char value;
+
+       REQUIRE(MORE(), REG_EBRACK);
+       if (!EATTWO('[', '.'))
+               return(GETNEXT());
+
+       /* collating symbol */
+       value = p_b_coll_elem(p, '.');
+       REQUIRE(EATTWO('.', ']'), REG_ECOLLATE);
+       return(value);
+}
+
+/*
+ - p_b_coll_elem - parse a collating-element name and look it up
+ == static char p_b_coll_elem(register struct parse *p, int endc);
+ */
+static char                    /* value of collating element */
+p_b_coll_elem(p, endc)
+register struct parse *p;
+int endc;                      /* name ended by endc,']' */
+{
+       register char *sp = p->next;
+       register struct cname *cp;
+       register int len;
+
+       while (MORE() && !SEETWO(endc, ']'))
+               NEXT();
+       if (!MORE()) {
+               SETERROR(REG_EBRACK);
+               return(0);
+       }
+       len = p->next - sp;
+       for (cp = cnames; cp->name != NULL; cp++)
+               if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0')
+                       return(cp->code);       /* known name */
+       if (len == 1)
+               return(*sp);    /* single character */
+       SETERROR(REG_ECOLLATE);                 /* neither */
+       return(0);
+}
+
+/*
+ - othercase - return the case counterpart of an alphabetic
+ == static char othercase(int ch);
+ */
+static char                    /* if no counterpart, return ch */
+othercase(ch)
+int ch;
+{
+       assert(isalpha(ch));
+       if (isupper(ch))
+               return(tolower(ch));
+       else if (islower(ch))
+               return(toupper(ch));
+       else                    /* peculiar, but could happen */
+               return(ch);
+}
+
+/*
+ - bothcases - emit a dualcase version of a two-case character
+ == static void bothcases(register struct parse *p, int ch);
+ *
+ * Boy, is this implementation ever a kludge...
+ */
+static void
+bothcases(p, ch)
+register struct parse *p;
+int ch;
+{
+       register char *oldnext = p->next;
+       register char *oldend = p->end;
+       char bracket[3];
+
+       assert(othercase(ch) != ch);    /* p_bracket() would recurse */
+       p->next = bracket;
+       p->end = bracket+2;
+       bracket[0] = ch;
+       bracket[1] = ']';
+       bracket[2] = '\0';
+       p_bracket(p);
+       assert(p->next == bracket+2);
+       p->next = oldnext;
+       p->end = oldend;
+}
+
+/*
+ - ordinary - emit an ordinary character
+ == static void ordinary(register struct parse *p, register int ch);
+ */
+static void
+ordinary(p, ch)
+register struct parse *p;
+register int ch;
+{
+       register cat_t *cap = p->g->categories;
+
+       if ((p->g->cflags&REG_ICASE) && isalpha(ch) && othercase(ch) != ch)
+               bothcases(p, ch);
+       else {
+               EMIT(OCHAR, (unsigned char)ch);
+               if (cap[ch] == 0)
+                       cap[ch] = p->g->ncategories++;
+       }
+}
+
+/*
+ - nonnewline - emit REG_NEWLINE version of OANY
+ == static void nonnewline(register struct parse *p);
+ *
+ * Boy, is this implementation ever a kludge...
+ */
+static void
+nonnewline(p)
+register struct parse *p;
+{
+       register char *oldnext = p->next;
+       register char *oldend = p->end;
+       char bracket[4];
+
+       p->next = bracket;
+       p->end = bracket+3;
+       bracket[0] = '^';
+       bracket[1] = '\n';
+       bracket[2] = ']';
+       bracket[3] = '\0';
+       p_bracket(p);
+       assert(p->next == bracket+3);
+       p->next = oldnext;
+       p->end = oldend;
+}
+
+/*
+ - repeat - generate code for a bounded repetition, recursively if needed
+ == static void repeat(register struct parse *p, sopno start, int from, int to);
+ */
+static void
+repeat(p, start, from, to)
+register struct parse *p;
+sopno start;                   /* operand from here to end of strip */
+int from;                      /* repeated from this number */
+int to;                                /* to this number of times (maybe INFINITY) */
+{
+       register sopno finish = HERE();
+#      define  N       2
+#      define  INF     3
+#      define  REP(f, t)       ((f)*8 + (t))
+#      define  MAP(n)  (((n) <= 1) ? (n) : ((n) == INFINITY) ? INF : N)
+       register sopno copy;
+
+       if (p->error != 0)      /* head off possible runaway recursion */
+               return;
+
+       assert(from <= to);
+
+       switch (REP(MAP(from), MAP(to))) {
+       case REP(0, 0):                 /* must be user doing this */
+               DROP(finish-start);     /* drop the operand */
+               break;
+       case REP(0, 1):                 /* as x{1,1}? */
+       case REP(0, N):                 /* as x{1,n}? */
+       case REP(0, INF):               /* as x{1,}? */
+               /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */
+               INSERT(OCH_, start);            /* offset is wrong... */
+               repeat(p, start+1, 1, to);
+               ASTERN(OOR1, start);
+               AHEAD(start);                   /* ... fix it */
+               EMIT(OOR2, 0);
+               AHEAD(THERE());
+               ASTERN(O_CH, THERETHERE());
+               break;
+       case REP(1, 1):                 /* trivial case */
+               /* done */
+               break;
+       case REP(1, N):                 /* as x?x{1,n-1} */
+               /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */
+               INSERT(OCH_, start);
+               ASTERN(OOR1, start);
+               AHEAD(start);
+               EMIT(OOR2, 0);                  /* offset very wrong... */
+               AHEAD(THERE());                 /* ...so fix it */
+               ASTERN(O_CH, THERETHERE());
+               copy = dupl(p, start+1, finish+1);
+               assert(copy == finish+4);
+               repeat(p, copy, 1, to-1);
+               break;
+       case REP(1, INF):               /* as x+ */
+               INSERT(OPLUS_, start);
+               ASTERN(O_PLUS, start);
+               break;
+       case REP(N, N):                 /* as xx{m-1,n-1} */
+               copy = dupl(p, start, finish);
+               repeat(p, copy, from-1, to-1);
+               break;
+       case REP(N, INF):               /* as xx{n-1,INF} */
+               copy = dupl(p, start, finish);
+               repeat(p, copy, from-1, to);
+               break;
+       default:                        /* "can't happen" */
+               SETERROR(REG_ASSERT);   /* just in case */
+               break;
+       }
+}
+
+/*
+ - seterr - set an error condition
+ == static int seterr(register struct parse *p, int e);
+ */
+static int                     /* useless but makes type checking happy */
+seterr(p, e)
+register struct parse *p;
+int e;
+{
+       if (p->error == 0)      /* keep earliest error condition */
+               p->error = e;
+       p->next = nuls;         /* try to bring things to a halt */
+       p->end = nuls;
+       return(0);              /* make the return value well-defined */
+}
+
+/*
+ - allocset - allocate a set of characters for []
+ == static cset *allocset(register struct parse *p);
+ */
+static cset *
+allocset(p)
+register struct parse *p;
+{
+       register int no = p->g->ncsets++;
+       register size_t nc;
+       register size_t nbytes;
+       register cset *cs;
+       register size_t css = (size_t)p->g->csetsize;
+       register int i;
+
+       if (no >= p->ncsalloc) {        /* need another column of space */
+               p->ncsalloc += CHAR_BIT;
+               nc = p->ncsalloc;
+               assert(nc % CHAR_BIT == 0);
+               nbytes = nc / CHAR_BIT * css;
+               if (p->g->sets == NULL)
+                       p->g->sets = (cset *)malloc(nc * sizeof(cset));
+               else
+                       p->g->sets = (cset *)realloc((char *)p->g->sets,
+                                                       nc * sizeof(cset));
+               if (p->g->setbits == NULL)
+                       p->g->setbits = (uch *)malloc(nbytes);
+               else {
+                       p->g->setbits = (uch *)realloc((char *)p->g->setbits,
+                                                               nbytes);
+                       /* xxx this isn't right if setbits is now NULL */
+                       for (i = 0; i < no; i++)
+                               p->g->sets[i].ptr = p->g->setbits + css*(i/CHAR_BIT);
+               }
+               if (p->g->sets != NULL && p->g->setbits != NULL)
+                       (void) memset((char *)p->g->setbits + (nbytes - css),
+                                                               0, css);
+               else {
+                       no = 0;
+                       SETERROR(REG_ESPACE);
+                       /* caller's responsibility not to do set ops */
+               }
+       }
+
+       assert(p->g->sets != NULL);     /* xxx */
+       cs = &p->g->sets[no];
+       cs->ptr = p->g->setbits + css*((no)/CHAR_BIT);
+       cs->mask = 1 << ((no) % CHAR_BIT);
+       cs->hash = 0;
+       cs->smultis = 0;
+       cs->multis = NULL;
+
+       return(cs);
+}
+
+/*
+ - freeset - free a now-unused set
+ == static void freeset(register struct parse *p, register cset *cs);
+ */
+static void
+freeset(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       register int i;
+       register cset *top = &p->g->sets[p->g->ncsets];
+       register size_t css = (size_t)p->g->csetsize;
+
+       for (i = 0; i < css; i++)
+               CHsub(cs, i);
+       if (cs == top-1)        /* recover only the easy case */
+               p->g->ncsets--;
+}
+
+/*
+ - freezeset - final processing on a set of characters
+ == static int freezeset(register struct parse *p, register cset *cs);
+ *
+ * The main task here is merging identical sets.  This is usually a waste
+ * of time (although the hash code minimizes the overhead), but can win
+ * big if REG_ICASE is being used.  REG_ICASE, by the way, is why the hash
+ * is done using addition rather than xor -- all ASCII [aA] sets xor to
+ * the same value!
+ */
+static int                     /* set number */
+freezeset(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       register uch h = cs->hash;
+       register int i;
+       register cset *top = &p->g->sets[p->g->ncsets];
+       register cset *cs2;
+       register size_t css = (size_t)p->g->csetsize;
+
+       /* look for an earlier one which is the same */
+       for (cs2 = &p->g->sets[0]; cs2 < top; cs2++)
+               if (cs2->hash == h && cs2 != cs) {
+                       /* maybe */
+                       for (i = 0; i < css; i++)
+                               if (!!CHIN(cs2, i) != !!CHIN(cs, i))
+                                       break;          /* no */
+                       if (i == css)
+                               break;                  /* yes */
+               }
+
+       if (cs2 < top) {        /* found one */
+               freeset(p, cs);
+               cs = cs2;
+       }
+
+       return((int)(cs - p->g->sets));
+}
+
+/*
+ - firstch - return first character in a set (which must have at least one)
+ == static int firstch(register struct parse *p, register cset *cs);
+ */
+static int                     /* character; there is no "none" value */
+firstch(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       register int i;
+       register size_t css = (size_t)p->g->csetsize;
+
+       for (i = 0; i < css; i++)
+               if (CHIN(cs, i))
+                       return((char)i);
+       assert(never);
+       return(0);              /* arbitrary */
+}
+
+/*
+ - nch - number of characters in a set
+ == static int nch(register struct parse *p, register cset *cs);
+ */
+static int
+nch(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       register int i;
+       register size_t css = (size_t)p->g->csetsize;
+       register int n = 0;
+
+       for (i = 0; i < css; i++)
+               if (CHIN(cs, i))
+                       n++;
+       return(n);
+}
+
+/*
+ - mcadd - add a collating element to a cset
+ == static void mcadd(register struct parse *p, register cset *cs, \
+ ==    register char *cp);
+ */
+static void
+mcadd(p, cs, cp)
+register struct parse *p;
+register cset *cs;
+register char *cp;
+{
+       register size_t oldend = cs->smultis;
+
+       cs->smultis += strlen(cp) + 1;
+       if (cs->multis == NULL)
+               cs->multis = malloc(cs->smultis);
+       else
+               cs->multis = realloc(cs->multis, cs->smultis);
+       if (cs->multis == NULL) {
+               SETERROR(REG_ESPACE);
+               return;
+       }
+
+       (void) strcpy(cs->multis + oldend - 1, cp);
+       cs->multis[cs->smultis - 1] = '\0';
+}
+
+/*
+ - mcsub - subtract a collating element from a cset
+ == static void mcsub(register cset *cs, register char *cp);
+ */
+/*
+static void
+mcsub(cs, cp)
+register cset *cs;
+register char *cp;
+{
+       register char *fp = mcfind(cs, cp);
+       register size_t len = strlen(fp);
+
+       assert(fp != NULL);
+       (void) memmove(fp, fp + len + 1,
+                               cs->smultis - (fp + len + 1 - cs->multis));
+       cs->smultis -= len;
+
+       if (cs->smultis == 0) {
+               free(cs->multis);
+               cs->multis = NULL;
+               return;
+       }
+
+       cs->multis = realloc(cs->multis, cs->smultis);
+       assert(cs->multis != NULL);
+}
+*/
+
+/*
+ - mcin - is a collating element in a cset?
+ == static int mcin(register cset *cs, register char *cp);
+ */
+/*
+static int
+mcin(cs, cp)
+register cset *cs;
+register char *cp;
+{
+       return(mcfind(cs, cp) != NULL);
+}
+*/
+
+/*
+ - mcfind - find a collating element in a cset
+ == static char *mcfind(register cset *cs, register char *cp);
+ */
+/*
+static char *
+mcfind(cs, cp)
+register cset *cs;
+register char *cp;
+{
+       register char *p;
+
+       if (cs->multis == NULL)
+               return(NULL);
+       for (p = cs->multis; *p != '\0'; p += strlen(p) + 1)
+               if (strcmp(cp, p) == 0)
+                       return(p);
+       return(NULL);
+}
+*/
+/*
+ - mcinvert - invert the list of collating elements in a cset
+ == static void mcinvert(register struct parse *p, register cset *cs);
+ *
+ * This would have to know the set of possibilities.  Implementation
+ * is deferred.
+ */
+static void
+mcinvert(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       assert(cs->multis == NULL);     /* xxx */
+}
+
+/*
+ - mccase - add case counterparts of the list of collating elements in a cset
+ == static void mccase(register struct parse *p, register cset *cs);
+ *
+ * This would have to know the set of possibilities.  Implementation
+ * is deferred.
+ */
+static void
+mccase(p, cs)
+register struct parse *p;
+register cset *cs;
+{
+       assert(cs->multis == NULL);     /* xxx */
+}
+
+/*
+ - isinsets - is this character in any sets?
+ == static int isinsets(register struct re_guts *g, int c);
+ */
+static int                     /* predicate */
+isinsets(g, c)
+register struct re_guts *g;
+int c;
+{
+       register uch *col;
+       register int i;
+       register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT;
+       register unsigned uc = (unsigned char)c;
+
+       for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize)
+               if (col[uc] != 0)
+                       return(1);
+       return(0);
+}
+
+/*
+ - samesets - are these two characters in exactly the same sets?
+ == static int samesets(register struct re_guts *g, int c1, int c2);
+ */
+static int                     /* predicate */
+samesets(g, c1, c2)
+register struct re_guts *g;
+int c1;
+int c2;
+{
+       register uch *col;
+       register int i;
+       register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT;
+       register unsigned uc1 = (unsigned char)c1;
+       register unsigned uc2 = (unsigned char)c2;
+
+       for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize)
+               if (col[uc1] != col[uc2])
+                       return(0);
+       return(1);
+}
+
+/*
+ - categorize - sort out character categories
+ == static void categorize(struct parse *p, register struct re_guts *g);
+ */
+static void
+categorize(p, g)
+struct parse *p;
+register struct re_guts *g;
+{
+       register cat_t *cats = g->categories;
+       register int c;
+       register int c2;
+       register cat_t cat;
+
+       /* avoid making error situations worse */
+       if (p->error != 0)
+               return;
+
+       for (c = CHAR_MIN; c <= CHAR_MAX; c++)
+               if (cats[c] == 0 && isinsets(g, c)) {
+                       cat = g->ncategories++;
+                       cats[c] = cat;
+                       for (c2 = c+1; c2 <= CHAR_MAX; c2++)
+                               if (cats[c2] == 0 && samesets(g, c, c2))
+                                       cats[c2] = cat;
+               }
+}
+
+/*
+ - dupl - emit a duplicate of a bunch of sops
+ == static sopno dupl(register struct parse *p, sopno start, sopno finish);
+ */
+static sopno                   /* start of duplicate */
+dupl(p, start, finish)
+register struct parse *p;
+sopno start;                   /* from here */
+sopno finish;                  /* to this less one */
+{
+       register sopno ret = HERE();
+       register sopno len = finish - start;
+
+       assert(finish >= start);
+       if (len == 0)
+               return(ret);
+       enlarge(p, p->ssize + len);     /* this many unexpected additions */
+       assert(p->ssize >= p->slen + len);
+       (void) memcpy((char *)(p->strip + p->slen),
+               (char *)(p->strip + start), (size_t)len*sizeof(sop));
+       p->slen += len;
+       return(ret);
+}
+
+/*
+ - doemit - emit a strip operator
+ == static void doemit(register struct parse *p, sop op, size_t opnd);
+ *
+ * It might seem better to implement this as a macro with a function as
+ * hard-case backup, but it's just too big and messy unless there are
+ * some changes to the data structures.  Maybe later.
+ */
+static void
+doemit(p, op, opnd)
+register struct parse *p;
+sop op;
+size_t opnd;
+{
+       /* avoid making error situations worse */
+       if (p->error != 0)
+               return;
+
+       /* deal with oversize operands ("can't happen", more or less) */
+       assert(opnd < 1<<OPSHIFT);
+
+       /* deal with undersized strip */
+       if (p->slen >= p->ssize)
+               enlarge(p, (p->ssize+1) / 2 * 3);       /* +50% */
+       assert(p->slen < p->ssize);
+
+       /* finally, it's all reduced to the easy case */
+       p->strip[p->slen++] = SOP(op, opnd);
+}
+
+/*
+ - doinsert - insert a sop into the strip
+ == static void doinsert(register struct parse *p, sop op, size_t opnd, sopno pos);
+ */
+static void
+doinsert(p, op, opnd, pos)
+register struct parse *p;
+sop op;
+size_t opnd;
+sopno pos;
+{
+       register sopno sn;
+       register sop s;
+       register int i;
+
+       /* avoid making error situations worse */
+       if (p->error != 0)
+               return;
+
+       sn = HERE();
+       EMIT(op, opnd);         /* do checks, ensure space */
+       assert(HERE() == sn+1);
+       s = p->strip[sn];
+
+       /* adjust paren pointers */
+       assert(pos > 0);
+       for (i = 1; i < NPAREN; i++) {
+               if (p->pbegin[i] >= pos) {
+                       p->pbegin[i]++;
+               }
+               if (p->pend[i] >= pos) {
+                       p->pend[i]++;
+               }
+       }
+
+       memmove((char *)&p->strip[pos+1], (char *)&p->strip[pos],
+                                               (HERE()-pos-1)*sizeof(sop));
+       p->strip[pos] = s;
+}
+
+/*
+ - dofwd - complete a forward reference
+ == static void dofwd(register struct parse *p, sopno pos, sop value);
+ */
+static void
+dofwd(p, pos, value)
+register struct parse *p;
+register sopno pos;
+sop value;
+{
+       /* avoid making error situations worse */
+       if (p->error != 0)
+               return;
+
+       assert(value < 1<<OPSHIFT);
+       p->strip[pos] = OP(p->strip[pos]) | value;
+}
+
+/*
+ - enlarge - enlarge the strip
+ == static void enlarge(register struct parse *p, sopno size);
+ */
+static void
+enlarge(p, size)
+register struct parse *p;
+register sopno size;
+{
+       register sop *sp;
+
+       if (p->ssize >= size)
+               return;
+
+       sp = (sop *)realloc(p->strip, size*sizeof(sop));
+       if (sp == NULL) {
+               SETERROR(REG_ESPACE);
+               return;
+       }
+       p->strip = sp;
+       p->ssize = size;
+}
+
+/*
+ - stripsnug - compact the strip
+ == static void stripsnug(register struct parse *p, register struct re_guts *g);
+ */
+static void
+stripsnug(p, g)
+register struct parse *p;
+register struct re_guts *g;
+{
+       g->nstates = p->slen;
+       g->strip = (sop *)realloc((char *)p->strip, p->slen * sizeof(sop));
+       if (g->strip == NULL) {
+               SETERROR(REG_ESPACE);
+               g->strip = p->strip;
+       }
+}
+
+/*
+ - findmust - fill in must and mlen with longest mandatory literal string
+ == static void findmust(register struct parse *p, register struct re_guts *g);
+ *
+ * This algorithm could do fancy things like analyzing the operands of |
+ * for common subsequences.  Someday.  This code is simple and finds most
+ * of the interesting cases.
+ *
+ * Note that must and mlen got initialized during setup.
+ */
+static void
+findmust(p, g)
+struct parse *p;
+register struct re_guts *g;
+{
+       register sop *scan;
+       sop *start;
+       register sop *newstart;
+       register sopno newlen;
+       register sop s;
+       register char *cp;
+       register sopno i;
+
+       /* avoid making error situations worse */
+       if (p->error != 0)
+               return;
+
+       /* find the longest OCHAR sequence in strip */
+       newlen = 0;
+       scan = g->strip + 1;
+       do {
+               s = *scan++;
+               switch (OP(s)) {
+               case OCHAR:             /* sequence member */
+                       if (newlen == 0)                /* new sequence */
+                               newstart = scan - 1;
+                       newlen++;
+                       break;
+               case OPLUS_:            /* things that don't break one */
+               case OLPAREN:
+               case ORPAREN:
+                       break;
+               case OQUEST_:           /* things that must be skipped */
+               case OCH_:
+                       scan--;
+                       do {
+                               scan += OPND(s);
+                               s = *scan;
+                               /* assert() interferes w debug printouts */
+                               if (OP(s) != O_QUEST && OP(s) != O_CH &&
+                                                       OP(s) != OOR2) {
+                                       g->iflags |= BAD;
+                                       return;
+                               }
+                       } while (OP(s) != O_QUEST && OP(s) != O_CH);
+                       /* fallthrough */
+               default:                /* things that break a sequence */
+                       if (newlen > g->mlen) {         /* ends one */
+                               start = newstart;
+                               g->mlen = newlen;
+                       }
+                       newlen = 0;
+                       break;
+               }
+       } while (OP(s) != OEND);
+
+       if (g->mlen == 0)               /* there isn't one */
+               return;
+
+       /* turn it into a character string */
+       g->must = malloc((size_t)g->mlen + 1);
+       if (g->must == NULL) {          /* argh; just forget it */
+               g->mlen = 0;
+               return;
+       }
+       cp = g->must;
+       scan = start;
+       for (i = g->mlen; i > 0; i--) {
+               while (OP(s = *scan++) != OCHAR)
+                       continue;
+               assert(cp < g->must + g->mlen);
+               *cp++ = (char)OPND(s);
+       }
+       assert(cp == g->must + g->mlen);
+       *cp++ = '\0';           /* just on general principles */
+}
+
+/*
+ - pluscount - count + nesting
+ == static sopno pluscount(register struct parse *p, register struct re_guts *g);
+ */
+static sopno                   /* nesting depth */
+pluscount(p, g)
+struct parse *p;
+register struct re_guts *g;
+{
+       register sop *scan;
+       register sop s;
+       register sopno plusnest = 0;
+       register sopno maxnest = 0;
+
+       if (p->error != 0)
+               return(0);      /* there may not be an OEND */
+
+       scan = g->strip + 1;
+       do {
+               s = *scan++;
+               switch (OP(s)) {
+               case OPLUS_:
+                       plusnest++;
+                       break;
+               case O_PLUS:
+                       if (plusnest > maxnest)
+                               maxnest = plusnest;
+                       plusnest--;
+                       break;
+               }
+       } while (OP(s) != OEND);
+       if (plusnest != 0)
+               g->iflags |= BAD;
+       return(maxnest);
+}
diff --git a/src/backend/regex/regerror.c b/src/backend/regex/regerror.c
new file mode 100644 (file)
index 0000000..2e19fa0
--- /dev/null
@@ -0,0 +1,178 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)regerror.c  8.4 (Berkeley) 3/20/94
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)regerror.c 8.4 (Berkeley) 3/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <regex.h>
+
+#include "utils.h"
+
+/* ========= begin header generated by ./mkh ========= */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* === regerror.c === */
+static char *regatoi __P((const regex_t *preg, char *localbuf));
+
+#ifdef __cplusplus
+}
+#endif
+/* ========= end header generated by ./mkh ========= */
+/*
+ = #define     REG_NOMATCH      1
+ = #define     REG_BADPAT       2
+ = #define     REG_ECOLLATE     3
+ = #define     REG_ECTYPE       4
+ = #define     REG_EESCAPE      5
+ = #define     REG_ESUBREG      6
+ = #define     REG_EBRACK       7
+ = #define     REG_EPAREN       8
+ = #define     REG_EBRACE       9
+ = #define     REG_BADBR       10
+ = #define     REG_ERANGE      11
+ = #define     REG_ESPACE      12
+ = #define     REG_BADRPT      13
+ = #define     REG_EMPTY       14
+ = #define     REG_ASSERT      15
+ = #define     REG_INVARG      16
+ = #define     REG_ATOI        255     // convert name to number (!)
+ = #define     REG_ITOA        0400    // convert number to name (!)
+ */
+static struct rerr {
+       int code;
+       char *name;
+       char *explain;
+} rerrs[] = {
+       {REG_NOMATCH,   "REG_NOMATCH",  "regexec() failed to match"},
+       {REG_BADPAT,    "REG_BADPAT",   "invalid regular expression"},
+       {REG_ECOLLATE,  "REG_ECOLLATE", "invalid collating element"},
+       {REG_ECTYPE,    "REG_ECTYPE",   "invalid character class"},
+       {REG_EESCAPE,   "REG_EESCAPE",  "trailing backslash (\\)"},
+       {REG_ESUBREG,   "REG_ESUBREG",  "invalid backreference number"},
+       {REG_EBRACK,    "REG_EBRACK",   "brackets ([ ]) not balanced"},
+       {REG_EPAREN,    "REG_EPAREN",   "parentheses not balanced"},
+       {REG_EBRACE,    "REG_EBRACE",   "braces not balanced"},
+       {REG_BADBR,     "REG_BADBR",    "invalid repetition count(s)"},
+       {REG_ERANGE,    "REG_ERANGE",   "invalid character range"},
+       {REG_ESPACE,    "REG_ESPACE",   "out of memory"},
+       {REG_BADRPT,    "REG_BADRPT",   "repetition-operator operand invalid"},
+       {REG_EMPTY,     "REG_EMPTY",    "empty (sub)expression"},
+       {REG_ASSERT,    "REG_ASSERT",   "\"can't happen\" -- you found a bug"},
+       {REG_INVARG,    "REG_INVARG",   "invalid argument to regex routine"},
+       {0,             "",             "*** unknown regexp error code ***"}
+};
+
+/*
+ - regerror - the interface to error numbers
+ = extern size_t regerror(int, const regex_t *, char *, size_t);
+ */
+/* ARGSUSED */
+size_t
+pg95_regerror(errcode, preg, errbuf, errbuf_size)
+int errcode;
+const regex_t *preg;
+char *errbuf;
+size_t errbuf_size;
+{
+       register struct rerr *r;
+       register size_t len;
+       register int target = errcode &~ REG_ITOA;
+       register char *s;
+       char convbuf[50];
+
+       if (errcode == REG_ATOI)
+               s = regatoi(preg, convbuf);
+       else {
+               for (r = rerrs; r->code != 0; r++)
+                       if (r->code == target)
+                               break;
+       
+               if (errcode&REG_ITOA) {
+                       if (r->code != 0)
+                               (void) strcpy(convbuf, r->name);
+                       else
+                               sprintf(convbuf, "REG_0x%x", target);
+                       assert(strlen(convbuf) < sizeof(convbuf));
+                       s = convbuf;
+               } else
+                       s = r->explain;
+       }
+
+       len = strlen(s) + 1;
+       if (errbuf_size > 0) {
+               if (errbuf_size > len)
+                       (void) strcpy(errbuf, s);
+               else {
+                       (void) strncpy(errbuf, s, errbuf_size-1);
+                       errbuf[errbuf_size-1] = '\0';
+               }
+       }
+
+       return(len);
+}
+
+/*
+ - regatoi - internal routine to implement REG_ATOI
+ == static char *regatoi(const regex_t *preg, char *localbuf);
+ */
+static char *
+regatoi(preg, localbuf)
+const regex_t *preg;
+char *localbuf;
+{
+       register struct rerr *r;
+
+       for (r = rerrs; r->code != 0; r++)
+               if (strcmp(r->name, preg->re_endp) == 0)
+                       break;
+       if (r->code == 0)
+               return("0");
+
+       sprintf(localbuf, "%d", r->code);
+       return(localbuf);
+}
diff --git a/src/backend/regex/regex.3 b/src/backend/regex/regex.3
new file mode 100644 (file)
index 0000000..66a7285
--- /dev/null
@@ -0,0 +1,538 @@
+.\" Copyright (c) 1992, 1993, 1994 Henry Spencer.
+.\" Copyright (c) 1992, 1993, 1994
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Henry Spencer.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"    @(#)regex.3     8.4 (Berkeley) 3/20/94
+.\"
+.TH REGEX 3 "March 20, 1994"
+.de ZR
+.\" one other place knows this name:  the SEE ALSO section
+.IR re_format (7) \\$1
+..
+.SH NAME
+regcomp, regexec, regerror, regfree \- regular-expression library
+.SH SYNOPSIS
+.ft B
+.\".na
+#include <sys/types.h>
+.br
+#include <regex.h>
+.HP 10
+int regcomp(regex_t\ *preg, const\ char\ *pattern, int\ cflags);
+.HP
+int\ regexec(const\ regex_t\ *preg, const\ char\ *string,
+size_t\ nmatch, regmatch_t\ pmatch[], int\ eflags);
+.HP
+size_t\ regerror(int\ errcode, const\ regex_t\ *preg,
+char\ *errbuf, size_t\ errbuf_size);
+.HP
+void\ regfree(regex_t\ *preg);
+.\".ad
+.ft
+.SH DESCRIPTION
+These routines implement POSIX 1003.2 regular expressions (``RE''s);
+see
+.ZR .
+.I Regcomp
+compiles an RE written as a string into an internal form,
+.I regexec
+matches that internal form against a string and reports results,
+.I regerror
+transforms error codes from either into human-readable messages,
+and
+.I regfree
+frees any dynamically-allocated storage used by the internal form
+of an RE.
+.PP
+The header
+.I <regex.h>
+declares two structure types,
+.I regex_t
+and
+.IR regmatch_t ,
+the former for compiled internal forms and the latter for match reporting.
+It also declares the four functions,
+a type
+.IR regoff_t ,
+and a number of constants with names starting with ``REG_''.
+.PP
+.I Regcomp
+compiles the regular expression contained in the
+.I pattern
+string,
+subject to the flags in
+.IR cflags ,
+and places the results in the
+.I regex_t
+structure pointed to by
+.IR preg .
+.I Cflags
+is the bitwise OR of zero or more of the following flags:
+.IP REG_EXTENDED \w'REG_EXTENDED'u+2n
+Compile modern (``extended'') REs,
+rather than the obsolete (``basic'') REs that
+are the default.
+.IP REG_BASIC
+This is a synonym for 0,
+provided as a counterpart to REG_EXTENDED to improve readability.
+.IP REG_NOSPEC
+Compile with recognition of all special characters turned off.
+All characters are thus considered ordinary,
+so the ``RE'' is a literal string.
+This is an extension,
+compatible with but not specified by POSIX 1003.2,
+and should be used with
+caution in software intended to be portable to other systems.
+REG_EXTENDED and REG_NOSPEC may not be used
+in the same call to
+.IR regcomp .
+.IP REG_ICASE
+Compile for matching that ignores upper/lower case distinctions.
+See
+.ZR .
+.IP REG_NOSUB
+Compile for matching that need only report success or failure,
+not what was matched.
+.IP REG_NEWLINE
+Compile for newline-sensitive matching.
+By default, newline is a completely ordinary character with no special
+meaning in either REs or strings.
+With this flag,
+`[^' bracket expressions and `.' never match newline,
+a `^' anchor matches the null string after any newline in the string
+in addition to its normal function,
+and the `$' anchor matches the null string before any newline in the
+string in addition to its normal function.
+.IP REG_PEND
+The regular expression ends,
+not at the first NUL,
+but just before the character pointed to by the
+.I re_endp
+member of the structure pointed to by
+.IR preg .
+The
+.I re_endp
+member is of type
+.IR const\ char\ * .
+This flag permits inclusion of NULs in the RE;
+they are considered ordinary characters.
+This is an extension,
+compatible with but not specified by POSIX 1003.2,
+and should be used with
+caution in software intended to be portable to other systems.
+.PP
+When successful,
+.I regcomp
+returns 0 and fills in the structure pointed to by
+.IR preg .
+One member of that structure
+(other than
+.IR re_endp )
+is publicized:
+.IR re_nsub ,
+of type
+.IR size_t ,
+contains the number of parenthesized subexpressions within the RE
+(except that the value of this member is undefined if the
+REG_NOSUB flag was used).
+If
+.I regcomp
+fails, it returns a non-zero error code;
+see DIAGNOSTICS.
+.PP
+.I Regexec
+matches the compiled RE pointed to by
+.I preg
+against the
+.IR string ,
+subject to the flags in
+.IR eflags ,
+and reports results using
+.IR nmatch ,
+.IR pmatch ,
+and the returned value.
+The RE must have been compiled by a previous invocation of
+.IR regcomp .
+The compiled form is not altered during execution of
+.IR regexec ,
+so a single compiled RE can be used simultaneously by multiple threads.
+.PP
+By default,
+the NUL-terminated string pointed to by
+.I string
+is considered to be the text of an entire line, minus any terminating
+newline.
+The
+.I eflags
+argument is the bitwise OR of zero or more of the following flags:
+.IP REG_NOTBOL \w'REG_STARTEND'u+2n
+The first character of
+the string
+is not the beginning of a line, so the `^' anchor should not match before it.
+This does not affect the behavior of newlines under REG_NEWLINE.
+.IP REG_NOTEOL
+The NUL terminating
+the string
+does not end a line, so the `$' anchor should not match before it.
+This does not affect the behavior of newlines under REG_NEWLINE.
+.IP REG_STARTEND
+The string is considered to start at
+\fIstring\fR\ + \fIpmatch\fR[0].\fIrm_so\fR
+and to have a terminating NUL located at
+\fIstring\fR\ + \fIpmatch\fR[0].\fIrm_eo\fR
+(there need not actually be a NUL at that location),
+regardless of the value of
+.IR nmatch .
+See below for the definition of
+.IR pmatch
+and
+.IR nmatch .
+This is an extension,
+compatible with but not specified by POSIX 1003.2,
+and should be used with
+caution in software intended to be portable to other systems.
+Note that a non-zero \fIrm_so\fR does not imply REG_NOTBOL;
+REG_STARTEND affects only the location of the string,
+not how it is matched.
+.PP
+See
+.ZR
+for a discussion of what is matched in situations where an RE or a
+portion thereof could match any of several substrings of
+.IR string .
+.PP
+Normally,
+.I regexec
+returns 0 for success and the non-zero code REG_NOMATCH for failure.
+Other non-zero error codes may be returned in exceptional situations;
+see DIAGNOSTICS.
+.PP
+If REG_NOSUB was specified in the compilation of the RE,
+or if
+.I nmatch
+is 0,
+.I regexec
+ignores the
+.I pmatch
+argument (but see below for the case where REG_STARTEND is specified).
+Otherwise,
+.I pmatch
+points to an array of
+.I nmatch
+structures of type
+.IR regmatch_t .
+Such a structure has at least the members
+.I rm_so
+and
+.IR rm_eo ,
+both of type
+.I regoff_t
+(a signed arithmetic type at least as large as an
+.I off_t
+and a
+.IR ssize_t ),
+containing respectively the offset of the first character of a substring
+and the offset of the first character after the end of the substring.
+Offsets are measured from the beginning of the
+.I string
+argument given to
+.IR regexec .
+An empty substring is denoted by equal offsets,
+both indicating the character following the empty substring.
+.PP
+The 0th member of the
+.I pmatch
+array is filled in to indicate what substring of
+.I string
+was matched by the entire RE.
+Remaining members report what substring was matched by parenthesized
+subexpressions within the RE;
+member
+.I i
+reports subexpression
+.IR i ,
+with subexpressions counted (starting at 1) by the order of their opening
+parentheses in the RE, left to right.
+Unused entries in the array\(emcorresponding either to subexpressions that
+did not participate in the match at all, or to subexpressions that do not
+exist in the RE (that is, \fIi\fR\ > \fIpreg\fR\->\fIre_nsub\fR)\(emhave both
+.I rm_so
+and
+.I rm_eo
+set to \-1.
+If a subexpression participated in the match several times,
+the reported substring is the last one it matched.
+(Note, as an example in particular, that when the RE `(b*)+' matches `bbb',
+the parenthesized subexpression matches each of the three `b's and then
+an infinite number of empty strings following the last `b',
+so the reported substring is one of the empties.)
+.PP
+If REG_STARTEND is specified,
+.I pmatch
+must point to at least one
+.I regmatch_t
+(even if
+.I nmatch
+is 0 or REG_NOSUB was specified),
+to hold the input offsets for REG_STARTEND.
+Use for output is still entirely controlled by
+.IR nmatch ;
+if
+.I nmatch
+is 0 or REG_NOSUB was specified,
+the value of
+.IR pmatch [0]
+will not be changed by a successful
+.IR regexec .
+.PP
+.I Regerror
+maps a non-zero
+.I errcode
+from either
+.I regcomp
+or
+.I regexec
+to a human-readable, printable message.
+If
+.I preg
+is non-NULL,
+the error code should have arisen from use of
+the
+.I regex_t
+pointed to by
+.IR preg ,
+and if the error code came from
+.IR regcomp ,
+it should have been the result from the most recent
+.I regcomp
+using that
+.IR regex_t .
+.RI ( Regerror
+may be able to supply a more detailed message using information
+from the
+.IR regex_t .)
+.I Regerror
+places the NUL-terminated message into the buffer pointed to by
+.IR errbuf ,
+limiting the length (including the NUL) to at most
+.I errbuf_size
+bytes.
+If the whole message won't fit,
+as much of it as will fit before the terminating NUL is supplied.
+In any case,
+the returned value is the size of buffer needed to hold the whole
+message (including terminating NUL).
+If
+.I errbuf_size
+is 0,
+.I errbuf
+is ignored but the return value is still correct.
+.PP
+If the
+.I errcode
+given to
+.I regerror
+is first ORed with REG_ITOA,
+the ``message'' that results is the printable name of the error code,
+e.g. ``REG_NOMATCH'',
+rather than an explanation thereof.
+If
+.I errcode
+is REG_ATOI,
+then
+.I preg
+shall be non-NULL and the
+.I re_endp
+member of the structure it points to
+must point to the printable name of an error code;
+in this case, the result in
+.I errbuf
+is the decimal digits of
+the numeric value of the error code
+(0 if the name is not recognized).
+REG_ITOA and REG_ATOI are intended primarily as debugging facilities;
+they are extensions,
+compatible with but not specified by POSIX 1003.2,
+and should be used with
+caution in software intended to be portable to other systems.
+Be warned also that they are considered experimental and changes are possible.
+.PP
+.I Regfree
+frees any dynamically-allocated storage associated with the compiled RE
+pointed to by
+.IR preg .
+The remaining
+.I regex_t
+is no longer a valid compiled RE
+and the effect of supplying it to
+.I regexec
+or
+.I regerror
+is undefined.
+.PP
+None of these functions references global variables except for tables
+of constants;
+all are safe for use from multiple threads if the arguments are safe.
+.SH IMPLEMENTATION CHOICES
+There are a number of decisions that 1003.2 leaves up to the implementor,
+either by explicitly saying ``undefined'' or by virtue of them being
+forbidden by the RE grammar.
+This implementation treats them as follows.
+.PP
+See
+.ZR
+for a discussion of the definition of case-independent matching.
+.PP
+There is no particular limit on the length of REs,
+except insofar as memory is limited.
+Memory usage is approximately linear in RE size, and largely insensitive
+to RE complexity, except for bounded repetitions.
+See BUGS for one short RE using them
+that will run almost any system out of memory.
+.PP
+A backslashed character other than one specifically given a magic meaning
+by 1003.2 (such magic meanings occur only in obsolete [``basic''] REs)
+is taken as an ordinary character.
+.PP
+Any unmatched [ is a REG_EBRACK error.
+.PP
+Equivalence classes cannot begin or end bracket-expression ranges.
+The endpoint of one range cannot begin another.
+.PP
+RE_DUP_MAX, the limit on repetition counts in bounded repetitions, is 255.
+.PP
+A repetition operator (?, *, +, or bounds) cannot follow another
+repetition operator.
+A repetition operator cannot begin an expression or subexpression
+or follow `^' or `|'.
+.PP
+`|' cannot appear first or last in a (sub)expression or after another `|',
+i.e. an operand of `|' cannot be an empty subexpression.
+An empty parenthesized subexpression, `()', is legal and matches an
+empty (sub)string.
+An empty string is not a legal RE.
+.PP
+A `{' followed by a digit is considered the beginning of bounds for a
+bounded repetition, which must then follow the syntax for bounds.
+A `{' \fInot\fR followed by a digit is considered an ordinary character.
+.PP
+`^' and `$' beginning and ending subexpressions in obsolete (``basic'')
+REs are anchors, not ordinary characters.
+.SH SEE ALSO
+grep(1), re_format(7)
+.PP
+POSIX 1003.2, sections 2.8 (Regular Expression Notation)
+and
+B.5 (C Binding for Regular Expression Matching).
+.SH DIAGNOSTICS
+Non-zero error codes from
+.I regcomp
+and
+.I regexec
+include the following:
+.PP
+.nf
+.ta \w'REG_ECOLLATE'u+3n
+REG_NOMATCH    regexec() failed to match
+REG_BADPAT     invalid regular expression
+REG_ECOLLATE   invalid collating element
+REG_ECTYPE     invalid character class
+REG_EESCAPE    \e applied to unescapable character
+REG_ESUBREG    invalid backreference number
+REG_EBRACK     brackets [ ] not balanced
+REG_EPAREN     parentheses ( ) not balanced
+REG_EBRACE     braces { } not balanced
+REG_BADBR      invalid repetition count(s) in { }
+REG_ERANGE     invalid character range in [ ]
+REG_ESPACE     ran out of memory
+REG_BADRPT     ?, *, or + operand invalid
+REG_EMPTY      empty (sub)expression
+REG_ASSERT     ``can't happen''\(emyou found a bug
+REG_INVARG     invalid argument, e.g. negative-length string
+.fi
+.SH HISTORY
+Originally written by Henry Spencer.
+Altered for inclusion in the 4.4BSD distribution.
+.SH BUGS
+This is an alpha release with known defects.
+Please report problems.
+.PP
+There is one known functionality bug.
+The implementation of internationalization is incomplete:
+the locale is always assumed to be the default one of 1003.2,
+and only the collating elements etc. of that locale are available.
+.PP
+The back-reference code is subtle and doubts linger about its correctness
+in complex cases.
+.PP
+.I Regexec
+performance is poor.
+This will improve with later releases.
+.I Nmatch
+exceeding 0 is expensive;
+.I nmatch
+exceeding 1 is worse.
+.I Regexec
+is largely insensitive to RE complexity \fIexcept\fR that back
+references are massively expensive.
+RE length does matter; in particular, there is a strong speed bonus
+for keeping RE length under about 30 characters,
+with most special characters counting roughly double.
+.PP
+.I Regcomp
+implements bounded repetitions by macro expansion,
+which is costly in time and space if counts are large
+or bounded repetitions are nested.
+An RE like, say,
+`((((a{1,100}){1,100}){1,100}){1,100}){1,100}'
+will (eventually) run almost any existing machine out of swap space.
+.PP
+There are suspected problems with response to obscure error conditions.
+Notably,
+certain kinds of internal overflow,
+produced only by truly enormous REs or by multiply nested bounded repetitions,
+are probably not handled well.
+.PP
+Due to a mistake in 1003.2, things like `a)b' are legal REs because `)' is
+a special character only in the presence of a previous unmatched `('.
+This can't be fixed until the spec is fixed.
+.PP
+The standard's definition of back references is vague.
+For example, does
+`a\e(\e(b\e)*\e2\e)*d' match `abbbd'?
+Until the standard is clarified,
+behavior in such cases should not be relied on.
+.PP
+The implementation of word-boundary matching is a bit of a kludge,
+and bugs may lurk in combinations of word-boundary matching and anchoring.
diff --git a/src/backend/regex/regex.h b/src/backend/regex/regex.h
new file mode 100644 (file)
index 0000000..fd5484f
--- /dev/null
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 1992 Henry Spencer.
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer of the University of Toronto.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)regex.h     8.2 (Berkeley) 1/3/94
+ */
+
+#ifndef _REGEX_H_
+#define        _REGEX_H_
+
+/* #include <sys/cdefs.h> */
+/* since not all systems have cdefs.h, we'll use our own here - jolly */
+#include "cdefs.h"
+
+/* types */
+typedef off_t regoff_t;
+
+typedef struct {
+       int re_magic;
+       size_t re_nsub;         /* number of parenthesized subexpressions */
+       __const char *re_endp;  /* end pointer for REG_PEND */
+       struct re_guts *re_g;   /* none of your business :-) */
+} regex_t;
+
+typedef struct {
+       regoff_t rm_so;         /* start of match */
+       regoff_t rm_eo;         /* end of match */
+} regmatch_t;
+
+/* regcomp() flags */
+#define        REG_BASIC       0000
+#define        REG_EXTENDED    0001
+#define        REG_ICASE       0002
+#define        REG_NOSUB       0004
+#define        REG_NEWLINE     0010
+#define        REG_NOSPEC      0020
+#define        REG_PEND        0040
+#define        REG_DUMP        0200
+
+/* regerror() flags */
+#define        REG_NOMATCH      1
+#define        REG_BADPAT       2
+#define        REG_ECOLLATE     3
+#define        REG_ECTYPE       4
+#define        REG_EESCAPE      5
+#define        REG_ESUBREG      6
+#define        REG_EBRACK       7
+#define        REG_EPAREN       8
+#define        REG_EBRACE       9
+#define        REG_BADBR       10
+#define        REG_ERANGE      11
+#define        REG_ESPACE      12
+#define        REG_BADRPT      13
+#define        REG_EMPTY       14
+#define        REG_ASSERT      15
+#define        REG_INVARG      16
+#define        REG_ATOI        255     /* convert name to number (!) */
+#define        REG_ITOA        0400    /* convert number to name (!) */
+
+/* regexec() flags */
+#define        REG_NOTBOL      00001
+#define        REG_NOTEOL      00002
+#define        REG_STARTEND    00004
+#define        REG_TRACE       00400   /* tracing of execution */
+#define        REG_LARGE       01000   /* force large representation */
+#define        REG_BACKR       02000   /* force use of backref code */
+
+__BEGIN_DECLS
+int    pg95_regcomp __P((regex_t *, const char *, int));
+size_t pg95_regerror __P((int, const regex_t *, char *, size_t));
+int    pg95_regexec __P((const regex_t *,
+           const char *, size_t, regmatch_t [], int));
+void   pg95_regfree __P((regex_t *));
+__END_DECLS
+
+#endif /* !_REGEX_H_ */
diff --git a/src/backend/regex/regex2.h b/src/backend/regex/regex2.h
new file mode 100644 (file)
index 0000000..0261b53
--- /dev/null
@@ -0,0 +1,175 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)regex2.h    8.4 (Berkeley) 3/20/94
+ */
+
+/*
+ * First, the stuff that ends up in the outside-world include file
+*/
+/*
+ typedef off_t regoff_t;
+ typedef struct {
+       int re_magic;
+       size_t re_nsub;         // number of parenthesized subexpressions
+       const char *re_endp;    // end pointer for REG_PEND
+       struct re_guts *re_g;   // none of your business :-)
+ } regex_t;
+ typedef struct {
+       regoff_t rm_so;         // start of match
+       regoff_t rm_eo;         // end of match
+ } regmatch_t;
+*/
+/*
+ * internals of regex_t
+ */
+#define        MAGIC1  ((('r'^0200)<<8) | 'e')
+
+/*
+ * The internal representation is a *strip*, a sequence of
+ * operators ending with an endmarker.  (Some terminology etc. is a
+ * historical relic of earlier versions which used multiple strips.)
+ * Certain oddities in the representation are there to permit running
+ * the machinery backwards; in particular, any deviation from sequential
+ * flow must be marked at both its source and its destination.  Some
+ * fine points:
+ *
+ * - OPLUS_ and O_PLUS are *inside* the loop they create.
+ * - OQUEST_ and O_QUEST are *outside* the bypass they create.
+ * - OCH_ and O_CH are *outside* the multi-way branch they create, while
+ *   OOR1 and OOR2 are respectively the end and the beginning of one of
+ *   the branches.  Note that there is an implicit OOR2 following OCH_
+ *   and an implicit OOR1 preceding O_CH.
+ *
+ * In state representations, an operator's bit is on to signify a state
+ * immediately *preceding* "execution" of that operator.
+ */
+typedef unsigned long sop;     /* strip operator */
+typedef long sopno;
+#define        OPRMASK 0xf8000000
+#define        OPDMASK 0x07ffffff
+#define        OPSHIFT ((unsigned)27)
+#define        OP(n)   ((n)&OPRMASK)
+#define        OPND(n) ((n)&OPDMASK)
+#define        SOP(op, opnd)   ((op)|(opnd))
+/* operators                      meaning      operand                 */
+/*                                             (back, fwd are offsets) */
+#define        OEND    (1<<OPSHIFT)    /* endmarker    -                       */
+#define        OCHAR   (2<<OPSHIFT)    /* character    unsigned char           */
+#define        OBOL    (3<<OPSHIFT)    /* left anchor  -                       */
+#define        OEOL    (4<<OPSHIFT)    /* right anchor -                       */
+#define        OANY    (5<<OPSHIFT)    /* .            -                       */
+#define        OANYOF  (6<<OPSHIFT)    /* [...]        set number              */
+#define        OBACK_  (7<<OPSHIFT)    /* begin \d     paren number            */
+#define        O_BACK  (8<<OPSHIFT)    /* end \d       paren number            */
+#define        OPLUS_  (9<<OPSHIFT)    /* + prefix     fwd to suffix           */
+#define        O_PLUS  (10<<OPSHIFT)   /* + suffix     back to prefix          */
+#define        OQUEST_ (11<<OPSHIFT)   /* ? prefix     fwd to suffix           */
+#define        O_QUEST (12<<OPSHIFT)   /* ? suffix     back to prefix          */
+#define        OLPAREN (13<<OPSHIFT)   /* (            fwd to )                */
+#define        ORPAREN (14<<OPSHIFT)   /* )            back to (               */
+#define        OCH_    (15<<OPSHIFT)   /* begin choice fwd to OOR2             */
+#define        OOR1    (16<<OPSHIFT)   /* | pt. 1      back to OOR1 or OCH_    */
+#define        OOR2    (17<<OPSHIFT)   /* | pt. 2      fwd to OOR2 or O_CH     */
+#define        O_CH    (18<<OPSHIFT)   /* end choice   back to OOR1            */
+#define        OBOW    (19<<OPSHIFT)   /* begin word   -                       */
+#define        OEOW    (20<<OPSHIFT)   /* end word     -                       */
+
+/*
+ * Structure for [] character-set representation.  Character sets are
+ * done as bit vectors, grouped 8 to a byte vector for compactness.
+ * The individual set therefore has both a pointer to the byte vector
+ * and a mask to pick out the relevant bit of each byte.  A hash code
+ * simplifies testing whether two sets could be identical.
+ *
+ * This will get trickier for multicharacter collating elements.  As
+ * preliminary hooks for dealing with such things, we also carry along
+ * a string of multi-character elements, and decide the size of the
+ * vectors at run time.
+ */
+typedef struct {
+       uch *ptr;               /* -> uch [csetsize] */
+       uch mask;               /* bit within array */
+       uch hash;               /* hash code */
+       size_t smultis;
+       char *multis;           /* -> char[smulti]  ab\0cd\0ef\0\0 */
+} cset;
+/* note that CHadd and CHsub are unsafe, and CHIN doesn't yield 0/1 */
+#define        CHadd(cs, c)    ((cs)->ptr[(uch)(c)] |= (cs)->mask, (cs)->hash += (c))
+#define        CHsub(cs, c)    ((cs)->ptr[(uch)(c)] &= ~(cs)->mask, (cs)->hash -= (c))
+#define        CHIN(cs, c)     ((cs)->ptr[(uch)(c)] & (cs)->mask)
+#define        MCadd(p, cs, cp)        mcadd(p, cs, cp)        /* regcomp() internal fns */
+#define        MCsub(p, cs, cp)        mcsub(p, cs, cp)
+#define        MCin(p, cs, cp) mcin(p, cs, cp)
+
+/* stuff for character categories */
+typedef unsigned char cat_t;
+
+/*
+ * main compiled-expression structure
+ */
+struct re_guts {
+       int magic;
+#              define  MAGIC2  ((('R'^0200)<<8)|'E')
+       sop *strip;             /* malloced area for strip */
+       int csetsize;           /* number of bits in a cset vector */
+       int ncsets;             /* number of csets in use */
+       cset *sets;             /* -> cset [ncsets] */
+       uch *setbits;           /* -> uch[csetsize][ncsets/CHAR_BIT] */
+       int cflags;             /* copy of regcomp() cflags argument */
+       sopno nstates;          /* = number of sops */
+       sopno firststate;       /* the initial OEND (normally 0) */
+       sopno laststate;        /* the final OEND */
+       int iflags;             /* internal flags */
+#              define  USEBOL  01      /* used ^ */
+#              define  USEEOL  02      /* used $ */
+#              define  BAD     04      /* something wrong */
+       int nbol;               /* number of ^ used */
+       int neol;               /* number of $ used */
+       int ncategories;        /* how many character categories */
+       cat_t *categories;      /* ->catspace[-CHAR_MIN] */
+       char *must;             /* match must contain this string */
+       int mlen;               /* length of must */
+       size_t nsub;            /* copy of re_nsub */
+       int backrefs;           /* does it use back references? */
+       sopno nplus;            /* how deep does it nest +s? */
+       /* catspace must be last */
+       cat_t catspace[1];      /* actually [NC] */
+};
+
+/* misc utilities */
+#define        OUT     (CHAR_MAX+1)    /* a non-character value */
+#define        ISWORD(c)       (isalnum(c) || (c) == '_')
diff --git a/src/backend/regex/regexec.c b/src/backend/regex/regexec.c
new file mode 100644 (file)
index 0000000..32c59b1
--- /dev/null
@@ -0,0 +1,181 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)regexec.c   8.3 (Berkeley) 3/20/94
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)regexec.c  8.3 (Berkeley) 3/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * the outer shell of regexec()
+ *
+ * This file includes engine.c *twice*, after muchos fiddling with the
+ * macros that code uses.  This lets the same code operate on two different
+ * representations for state sets.
+ */
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include "utils.h"
+#include "regex2.h"
+
+static int nope = 0;           /* for use in asserts; shuts lint up */
+
+/* macros for manipulating states, small version */
+#define        states  long
+#define        states1 states          /* for later use in regexec() decision */
+#define        CLEAR(v)        ((v) = 0)
+#define        SET0(v, n)      ((v) &= ~(1 << (n)))
+#define        SET1(v, n)      ((v) |= 1 << (n))
+#define        ISSET(v, n)     ((v) & (1 << (n)))
+#define        ASSIGN(d, s)    ((d) = (s))
+#define        EQ(a, b)        ((a) == (b))
+#define        STATEVARS       int dummy       /* dummy version */
+#define        STATESETUP(m, n)        /* nothing */
+#define        STATETEARDOWN(m)        /* nothing */
+#define        SETUP(v)        ((v) = 0)
+#define        onestate        int
+#define        INIT(o, n)      ((o) = (unsigned)1 << (n))
+#define        INC(o)  ((o) <<= 1)
+#define        ISSTATEIN(v, o) ((v) & (o))
+/* some abbreviations; note that some of these know variable names! */
+/* do "if I'm here, I can also be there" etc without branches */
+#define        FWD(dst, src, n)        ((dst) |= ((unsigned)(src)&(here)) << (n))
+#define        BACK(dst, src, n)       ((dst) |= ((unsigned)(src)&(here)) >> (n))
+#define        ISSETBACK(v, n) ((v) & ((unsigned)here >> (n)))
+/* function names */
+#define SNAMES                 /* engine.c looks after details */
+
+#include "engine.c"
+
+/* now undo things */
+#undef states
+#undef CLEAR
+#undef SET0
+#undef SET1
+#undef ISSET
+#undef ASSIGN
+#undef EQ
+#undef STATEVARS
+#undef STATESETUP
+#undef STATETEARDOWN
+#undef SETUP
+#undef onestate
+#undef INIT
+#undef INC
+#undef ISSTATEIN
+#undef FWD
+#undef BACK
+#undef ISSETBACK
+#undef SNAMES
+
+/* macros for manipulating states, large version */
+#define        states  char *
+#define        CLEAR(v)        memset(v, 0, m->g->nstates)
+#define        SET0(v, n)      ((v)[n] = 0)
+#define        SET1(v, n)      ((v)[n] = 1)
+#define        ISSET(v, n)     ((v)[n])
+#define        ASSIGN(d, s)    memcpy(d, s, m->g->nstates)
+#define        EQ(a, b)        (memcmp(a, b, m->g->nstates) == 0)
+#define        STATEVARS       int vn; char *space
+#define        STATESETUP(m, nv)       { (m)->space = malloc((nv)*(m)->g->nstates); \
+                               if ((m)->space == NULL) return(REG_ESPACE); \
+                               (m)->vn = 0; }
+#define        STATETEARDOWN(m)        { free((m)->space); }
+#define        SETUP(v)        ((v) = &m->space[m->vn++ * m->g->nstates])
+#define        onestate        int
+#define        INIT(o, n)      ((o) = (n))
+#define        INC(o)  ((o)++)
+#define        ISSTATEIN(v, o) ((v)[o])
+/* some abbreviations; note that some of these know variable names! */
+/* do "if I'm here, I can also be there" etc without branches */
+#define        FWD(dst, src, n)        ((dst)[here+(n)] |= (src)[here])
+#define        BACK(dst, src, n)       ((dst)[here-(n)] |= (src)[here])
+#define        ISSETBACK(v, n) ((v)[here - (n)])
+/* function names */
+#define        LNAMES                  /* flag */
+
+#include "engine.c"
+
+/*
+ - regexec - interface for matching
+ = extern int regexec(const regex_t *, const char *, size_t, \
+ =                                     regmatch_t [], int);
+ = #define     REG_NOTBOL      00001
+ = #define     REG_NOTEOL      00002
+ = #define     REG_STARTEND    00004
+ = #define     REG_TRACE       00400   // tracing of execution
+ = #define     REG_LARGE       01000   // force large representation
+ = #define     REG_BACKR       02000   // force use of backref code
+ *
+ * We put this here so we can exploit knowledge of the state representation
+ * when choosing which matcher to call.  Also, by this point the matchers
+ * have been prototyped.
+ */
+int                            /* 0 success, REG_NOMATCH failure */
+pg95_regexec(preg, string, nmatch, pmatch, eflags)
+const regex_t *preg;
+const char *string;
+size_t nmatch;
+regmatch_t pmatch[];
+int eflags;
+{
+       register struct re_guts *g = preg->re_g;
+#ifdef REDEBUG
+#      define  GOODFLAGS(f)    (f)
+#else
+#      define  GOODFLAGS(f)    ((f)&(REG_NOTBOL|REG_NOTEOL|REG_STARTEND))
+#endif
+
+       if (preg->re_magic != MAGIC1 || g->magic != MAGIC2)
+               return(REG_BADPAT);
+       assert(!(g->iflags&BAD));
+       if (g->iflags&BAD)              /* backstop for no-debug case */
+               return(REG_BADPAT);
+       eflags = GOODFLAGS(eflags);
+
+       if (g->nstates <= CHAR_BIT*sizeof(states1) && !(eflags&REG_LARGE))
+               return(smatcher(g, (char *)string, nmatch, pmatch, eflags));
+       else
+               return(lmatcher(g, (char *)string, nmatch, pmatch, eflags));
+}
diff --git a/src/backend/regex/regexp.h b/src/backend/regex/regexp.h
new file mode 100644 (file)
index 0000000..bf39dab
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1986 by University of Toronto.
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley
+ * by Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)regexp.h    8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef        _REGEXP_H_
+#define        _REGEXP_H_
+
+/*
+ * Definitions etc. for regexp(3) routines.
+ *
+ * Caveat:  this is V8 regexp(3) [actually, a reimplementation thereof],
+ * not the System V one.
+ */
+#define NSUBEXP  10
+typedef struct regexp {
+       char *startp[NSUBEXP];
+       char *endp[NSUBEXP];
+       char regstart;          /* Internal use only. */
+       char reganch;           /* Internal use only. */
+       char *regmust;          /* Internal use only. */
+       int regmlen;            /* Internal use only. */
+       char program[1];        /* Unwarranted chumminess with compiler. */
+} regexp;
+
+/* #include <sys/cdefs.h> */
+/* since not all systems have cdefs.h, we'll use our own here - jolly */
+#include "cdefs.h"
+
+__BEGIN_DECLS
+regexp *pg95_regcomp __P((const char *));
+int pg95_regexec __P((const  regexp *, const char *));
+void pg95_regsub __P((const  regexp *, const char *, char *));
+void pg95_regerror __P((const char *));
+__END_DECLS
+
+#endif /* !_REGEXP_H_ */
diff --git a/src/backend/regex/regfree.c b/src/backend/regex/regfree.c
new file mode 100644 (file)
index 0000000..b2fd1e0
--- /dev/null
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)regfree.c   8.3 (Berkeley) 3/20/94
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)regfree.c  8.3 (Berkeley) 3/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <regex.h>
+
+#include "utils.h"
+#include "regex2.h"
+
+/*
+ - regfree - free everything
+ = extern void regfree(regex_t *);
+ */
+void
+pg95_regfree(preg)
+regex_t *preg;
+{
+       register struct re_guts *g;
+
+       if (preg->re_magic != MAGIC1)   /* oops */
+               return;                 /* nice to complain, but hard */
+
+       g = preg->re_g;
+       if (g == NULL || g->magic != MAGIC2)    /* oops again */
+               return;
+       preg->re_magic = 0;             /* mark it invalid */
+       g->magic = 0;                   /* mark it invalid */
+
+       if (g->strip != NULL)
+               free((char *)g->strip);
+       if (g->sets != NULL)
+               free((char *)g->sets);
+       if (g->setbits != NULL)
+               free((char *)g->setbits);
+       if (g->must != NULL)
+               free(g->must);
+       free((char *)g);
+}
diff --git a/src/backend/regex/utils.h b/src/backend/regex/utils.h
new file mode 100644 (file)
index 0000000..cbab442
--- /dev/null
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)utils.h     8.3 (Berkeley) 3/20/94
+ */
+
+/* utility definitions */
+#define        DUPMAX          100000000       /* xxx is this right? */
+#define        INFINITY        (DUPMAX + 1)
+#define        NC              (CHAR_MAX - CHAR_MIN + 1)
+typedef unsigned char uch;
+
+/* switch off assertions (if not already off) if no REDEBUG */
+#ifndef REDEBUG
+#ifndef NDEBUG
+#define        NDEBUG  /* no assertions please */
+#endif
+#endif
+#include <assert.h>
+
+/* for old systems with bcopy() but no memmove() */
+#if defined(PORTNAME_sparc)
+#define        memmove(d, s, c)        bcopy(s, d, c)
+#endif
diff --git a/src/backend/rewrite/Makefile.inc b/src/backend/rewrite/Makefile.inc
new file mode 100644 (file)
index 0000000..e077f38
--- /dev/null
@@ -0,0 +1,22 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the rewrite rules module
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+VPATH:= $(VPATH):$(CURDIR)/rewrite
+
+
+SRCS_REWRITE= rewriteRemove.c rewriteDefine.c \
+        rewriteHandler.c rewriteManip.c rewriteSupport.c locks.c
+
+HEADERS+= rewriteRemove.h rewriteDefine.h rewriteHandler.h \
+       rewriteManip.h rewriteSupport.h locks.h prs2lock.h
+
diff --git a/src/backend/rewrite/locks.c b/src/backend/rewrite/locks.c
new file mode 100644 (file)
index 0000000..5b9ac54
--- /dev/null
@@ -0,0 +1,131 @@
+/*-------------------------------------------------------------------------
+ *
+ * locks.c--
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"                  /* for oid defs */
+#include "utils/elog.h"                        /* for elog */
+#include "nodes/pg_list.h"             /* lisp support package */
+#include "nodes/parsenodes.h"
+#include "nodes/primnodes.h"           /* Var node def */
+#include "utils/syscache.h"            /* for SearchSysCache */
+#include "rewrite/locks.h"             /* for rewrite specific lock defns */
+
+/*
+ * ThisLockWasTriggered
+ *
+ * walk the tree, if there we find a varnode,
+ * we check the varattno against the attnum
+ * if we find at least one such match, we return true
+ * otherwise, we return false
+ */
+bool
+nodeThisLockWasTriggered(Node *node, int varno, AttrNumber attnum)
+{
+    if (node==NULL)
+       return FALSE;
+    switch(nodeTag(node)) {
+    case T_Var:
+       {
+           Var *var = (Var *)node;
+           if (varno == var->varno &&
+               (attnum == var->varattno || attnum == -1))
+               return TRUE;
+       }
+       break;
+    case T_Expr:
+       {
+           Expr *expr = (Expr*)node;
+           return
+               nodeThisLockWasTriggered((Node*)expr->args, varno, attnum);
+       }
+       break;
+    case T_TargetEntry:
+       {
+           TargetEntry *tle = (TargetEntry *)node;
+           return
+               nodeThisLockWasTriggered(tle->expr, varno, attnum);
+       }
+       break;
+    case T_List:
+       {
+           List *l;
+
+           foreach(l, (List*)node) {
+               if (nodeThisLockWasTriggered(lfirst(l), varno, attnum))
+                   return TRUE;
+           }
+           return FALSE;
+       }
+       break;
+    default:
+       break;
+    }
+    return (FALSE);
+}
+
+/*
+ * thisLockWasTriggered -
+ *     walk the tree, if there we find a varnode, we check the varattno
+ *     against the attnum if we find at least one such match, we return true
+ *     otherwise, we return false
+ */
+static bool
+thisLockWasTriggered(int varno,
+                    AttrNumber attnum,
+                    Query *parsetree)
+{
+    return
+       (nodeThisLockWasTriggered(parsetree->qual, varno, attnum) ||
+        nodeThisLockWasTriggered((Node*)parsetree->targetList,
+                                 varno, attnum));
+}
+
+/*
+ * matchLocks -
+ *    match the list of locks and returns the matching rules
+ */
+List *
+matchLocks(CmdType event,
+          RuleLock *rulelocks,
+          int varno,
+          Query *parsetree)
+{
+    List *real_locks           = NIL;
+    int nlocks;
+    int i;
+    
+    Assert(rulelocks != NULL); /* we get called iff there is some lock */
+    Assert(parsetree != NULL);
+    
+    if (parsetree->commandType != CMD_SELECT) {
+       if (parsetree->resultRelation != varno) {
+           return ( NULL );
+       }
+    }
+    
+    nlocks = rulelocks->numLocks;
+    
+    for (i = 0; i < nlocks; i++) {
+       RewriteRule *oneLock = rulelocks->rules[i];
+
+       if (oneLock->event == event) {
+           if (parsetree->commandType != CMD_SELECT ||
+               thisLockWasTriggered(varno,
+                                    oneLock->attrno,
+                                    parsetree)) {
+               real_locks = lappend(real_locks, oneLock);
+           }
+       }
+    }
+    
+    return (real_locks);
+}
+
diff --git a/src/backend/rewrite/locks.h b/src/backend/rewrite/locks.h
new file mode 100644 (file)
index 0000000..7246bbe
--- /dev/null
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * locks.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        LOCKS_H
+#define        LOCKS_H
+
+#include "rewrite/prs2lock.h"
+
+extern List *matchLocks(CmdType event, RuleLock *rulelocks, int varno,
+                       Query *parsetree);
+
+#endif /* LOCKS_H */
diff --git a/src/backend/rewrite/prs2lock.h b/src/backend/rewrite/prs2lock.h
new file mode 100644 (file)
index 0000000..fd315ec
--- /dev/null
@@ -0,0 +1,43 @@
+/*-------------------------------------------------------------------------
+ *
+ * prs2lock.h--
+ *    data structures for POSTGRES Rule System II (rewrite rules only)
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PRS2LOCK_H
+#define PRS2LOCK_H
+
+#include "access/attnum.h"
+#include "nodes/pg_list.h"
+
+/*
+ * RewriteRule -
+ *    holds a info for a rewrite rule
+ *
+ */
+typedef struct RewriteRule {
+    Oid                        ruleId;
+    CmdType            event;
+    AttrNumber         attrno;
+    Node               *qual;
+    List               *actions;
+    bool               isInstead;
+} RewriteRule;
+
+/*
+ * RuleLock -
+ *    all rules that apply to a particular relation. Even though we only
+ *    have the rewrite rule system left and these are not really "locks",
+ *    the name is kept for historical reasons.
+ */
+typedef struct RuleLock {
+    int                        numLocks;
+    RewriteRule                **rules;
+} RuleLock;
+
+#endif /* REWRITE_H */
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
new file mode 100644 (file)
index 0000000..e69287a
--- /dev/null
@@ -0,0 +1,255 @@
+/*-------------------------------------------------------------------------
+ *
+ * rewriteDefine.c--
+ *    routines for defining a rewrite rule
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <string.h>
+#include "postgres.h"
+
+#include "utils/rel.h"                 /* for Relation stuff */
+#include "access/heapam.h"             /* access methods like amopenr */
+#include "utils/builtins.h"
+#include "utils/elog.h"                        /* for elog */
+#include "utils/palloc.h"
+#include "utils/lsyscache.h"           /* for get_typlen */
+#include "nodes/pg_list.h"             /* for Lisp support */
+#include "nodes/parsenodes.h"
+#include "parser/catalog_utils.h"
+#include "rewrite/locks.h"                     
+#include "rewrite/rewriteRemove.h"
+#include "rewrite/rewriteSupport.h"
+#include "tcop/tcopprot.h"
+
+Oid LastOidProcessed = InvalidOid;
+
+/*
+ * This is too small for many rule plans, but it'll have to do for now.
+ * Rule plans, etc will eventually have to be large objects.
+ * 
+ * should this be smaller?
+ */
+#define RULE_PLAN_SIZE 8192 
+
+static void
+strcpyq(char *dest, char *source)
+{
+    char *current=source,*destp= dest; 
+    
+    for(current=source; *current; current++) {
+       if (*current == '\"')  {
+           *destp = '\\';
+           destp++;
+       }
+       *destp = *current;
+       destp++;
+    }
+    *destp = '\0';
+}
+
+/*
+ * InsertRule -
+ *    takes the arguments and inserts them as attributes into the system 
+ *    relation "pg_rewrite"
+ *
+ *     MODS :  changes the value of LastOidProcessed as a side
+ *             effect of inserting the rule tuple
+ *
+ *     ARGS :  rulname         -       name of the rule
+ *             evtype          -       one of RETRIEVE,REPLACE,DELETE,APPEND
+ *             evobj           -       name of relation
+ *             evslot          -       comma delimited list of slots
+ *                                     if null => multi-attr rule
+ *             evinstead       -       is an instead rule
+ *             actiontree      -       parsetree(s) of rule action
+ */
+static Oid
+InsertRule(char *rulname,
+          int evtype,
+          char *evobj,
+          char *evslot,
+          char *evqual,
+          bool evinstead,
+          char *actiontree)
+{
+    static char        rulebuf[RULE_PLAN_SIZE];
+    static char actionbuf[RULE_PLAN_SIZE];
+    static char qualbuf[RULE_PLAN_SIZE];
+    Oid eventrel_oid = InvalidOid;
+    AttrNumber evslot_index = InvalidAttrNumber;
+    Relation eventrel = NULL;
+    char *is_instead = "f";
+    extern void eval_as_new_xact();
+    char *template;
+    
+    eventrel = heap_openr(evobj);
+    if (eventrel == NULL) {
+       elog(WARN, "rules cannot be defined on relations not in schema");
+    }
+    eventrel_oid = RelationGetRelationId(eventrel);
+    
+    /*
+     * if the slotname is null, we know that this is a multi-attr
+     * rule
+     */
+    if (evslot == NULL)
+       evslot_index = -1;
+    else
+       evslot_index = varattno(eventrel, (char*)evslot);
+    heap_close(eventrel);
+
+    if (evinstead)
+       is_instead = "t";
+
+    if (evqual == NULL)
+       evqual = "nil";
+
+    if (IsDefinedRewriteRule(rulname)) 
+       elog(WARN, "Attempt to insert rule '%s' failed: already exists",
+            rulname);
+    strcpyq(actionbuf,actiontree);     
+    strcpyq(qualbuf, evqual);
+
+    template = "INSERT INTO pg_rewrite \
+(rulename, ev_type, ev_class, ev_attr, action, ev_qual, is_instead) VALUES \
+('%s', %d::char, %d::oid, %d::int2, '%s'::text, '%s'::text, \
+ '%s'::bool);";
+    if (strlen(template) + strlen(rulname) + strlen(actionbuf) +
+       strlen(qualbuf) + 20 /* fudge fac */ >  RULE_PLAN_SIZE) {
+       elog(WARN, "DefineQueryRewrite: rule plan string too big.");
+    }
+    sprintf(rulebuf, template,
+           rulname, evtype, eventrel_oid, evslot_index, actionbuf,
+           qualbuf, is_instead);
+
+    pg_eval(rulebuf, (char **) NULL, (Oid *) NULL, 0);
+    
+    return (LastOidProcessed);
+}
+
+/*
+ *     for now, event_object must be a single attribute
+ */
+static void
+ValidateRule(int event_type,
+            char *eobj_string,
+            char *eslot_string,
+            Node *event_qual,
+            List **action,
+            int is_instead,
+            Oid event_attype)
+{
+    if (((event_type == CMD_INSERT) || (event_type == CMD_DELETE)) &&
+       eslot_string) {
+       elog(WARN,
+            "rules not allowed for insert or delete events to an attribute");
+    }
+
+    if (event_qual && !*action && is_instead)
+       elog(WARN,
+            "event_quals on 'instead nothing' rules not currently supported");
+
+#if 0    
+    /* on retrieve to class.attribute do instead nothing is converted
+     * to 'on retrieve to class.attribute do instead
+     *        retrieve (attribute = NULL)'
+     * --- this is also a terrible hack that works well -- glass*/
+    if (is_instead && !*action && eslot_string && event_type == CMD_SELECT) {
+       char *temp_buffer = (char *) palloc(strlen(template)+80);
+       sprintf(temp_buffer, template, event_attype,
+               get_typlen(event_attype), eslot_string,
+               event_attype);
+
+       *action = (List*) stringToNode(temp_buffer);
+
+       pfree(temp_buffer);
+    }
+#endif    
+}
+
+void
+DefineQueryRewrite(RuleStmt *stmt)
+{
+    CmdType event_type = stmt->event;
+    Attr *event_obj    = stmt->object;
+    Node *event_qual   = stmt->whereClause;
+    bool is_instead    = stmt->instead;
+    List *action       = stmt->actions;
+    Relation event_relation = NULL ;
+    Oid ruleId;
+    Oid ev_relid       = 0;
+    char *eslot_string = NULL;
+    int event_attno    = 0;
+    Oid event_attype   = 0;
+    char *actionP, *event_qualP;
+    
+    extern Oid att_typeid();
+    
+    if (event_obj->attrs)
+       eslot_string = strVal(lfirst(event_obj->attrs));
+    else
+       eslot_string = NULL;
+    
+    event_relation = heap_openr(event_obj->relname);
+    if ( event_relation == NULL ) {
+       elog(WARN, "virtual relations not supported yet");
+    }
+    ev_relid = RelationGetRelationId(event_relation);
+    
+    if (eslot_string == NULL) {
+       event_attno = -1;
+       event_attype = -1; /* XXX - don't care */
+    } else {
+       event_attno = varattno(event_relation, eslot_string);
+       event_attype = att_typeid(event_relation,event_attno);
+    }
+    heap_close(event_relation);
+
+    /* fix bug about instead nothing */
+    ValidateRule(event_type, event_obj->relname,
+                eslot_string, event_qual, &action,
+                is_instead,event_attype);
+
+    if (action == NULL) {
+       if (!is_instead) return;        /* doesn't do anything */
+       
+       event_qualP = nodeToString(event_qual);
+       
+       ruleId = InsertRule(stmt->rulename,
+                           event_type,
+                           event_obj->relname,
+                           eslot_string,
+                           event_qualP,
+                           true,
+                           "nil");
+       prs2_addToRelation(ev_relid, ruleId, event_type, event_attno, TRUE,
+                          event_qual, NIL);
+
+    } else {
+       event_qualP = nodeToString(event_qual);
+       actionP = nodeToString(action);
+
+       ruleId = InsertRule(stmt->rulename,
+                           event_type,
+                           event_obj->relname,
+                           eslot_string,
+                           event_qualP,
+                           is_instead,
+                           actionP);
+       
+       /* what is the max size of type text? XXX -- glass */
+       if (length(action) > 15 )
+           elog(WARN,"max # of actions exceeded");
+       prs2_addToRelation(ev_relid, ruleId, event_type, event_attno,
+                          is_instead, event_qual, action);
+    }
+}
+
diff --git a/src/backend/rewrite/rewriteDefine.h b/src/backend/rewrite/rewriteDefine.h
new file mode 100644 (file)
index 0000000..a33af9c
--- /dev/null
@@ -0,0 +1,18 @@
+/*-------------------------------------------------------------------------
+ *
+ * rewriteDefine.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        REWRITEDEFINE_H
+#define        REWRITEDEFINE_H
+
+extern void DefineQueryRewrite(RuleStmt *args); 
+
+#endif /* REWRITEDEFINE_H */
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
new file mode 100644 (file)
index 0000000..7c2dbc3
--- /dev/null
@@ -0,0 +1,622 @@
+/*-------------------------------------------------------------------------
+ *
+ * rewriteHandler.c--
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "miscadmin.h"
+#include "utils/palloc.h"
+#include "utils/elog.h"
+#include "utils/rel.h"
+#include "nodes/pg_list.h"     
+#include "nodes/primnodes.h"
+
+#include "parser/parsetree.h"          /* for parsetree manipulation */
+#include "nodes/parsenodes.h"
+
+#include "rewrite/rewriteSupport.h"    
+#include "rewrite/rewriteHandler.h"
+#include "rewrite/rewriteManip.h"
+#include "rewrite/locks.h"
+
+#include "commands/creatinh.h"
+#include "access/heapam.h"
+
+static void ApplyRetrieveRule(Query *parsetree, RewriteRule *rule, 
+                             int rt_index, int relation_level, int *modified);
+static List *fireRules(Query *parsetree, int rt_index, CmdType event,
+                      bool *instead_flag, List *locks, List **qual_products);
+static List *deepRewriteQuery(Query *parsetree);
+
+/*
+ * gatherRewriteMeta -
+ *    Gather meta information about parsetree, and rule. Fix rule body
+ *    and qualifier so that they can be mixed with the parsetree and
+ *    maintain semantic validity
+ */
+static RewriteInfo *
+gatherRewriteMeta(Query *parsetree,
+                 Query *rule_action,
+                 Node *rule_qual,
+                 int rt_index,
+                 CmdType event,
+                 bool *instead_flag)
+{
+    RewriteInfo *info;    
+    int rt_length;
+    int result_reln;
+    
+    info = (RewriteInfo *) palloc(sizeof(RewriteInfo));
+    info->rt_index = rt_index;
+    info->event = event;
+    info->instead_flag = *instead_flag;
+/*    info->rule_action = rule_action;  this needs to be a copy here, I think! - jolly*/
+    info->rule_action = (Query*)copyObject(rule_action);
+    info->rule_qual = (Node*)copyObject(rule_qual);
+    info->nothing = FALSE;
+    info->action = info->rule_action->commandType;
+    if (info->rule_action == NULL) info->nothing = TRUE;
+    if (info->nothing)
+       return info;
+
+    info->current_varno = rt_index;
+    info->rt = parsetree->rtable;
+    rt_length = length(info->rt);
+    info->rt = append(info->rt, info->rule_action->rtable); 
+
+
+    info->new_varno = PRS2_NEW_VARNO + rt_length;
+    OffsetVarNodes(info->rule_action->qual, rt_length);
+    OffsetVarNodes((Node*)info->rule_action->targetList, rt_length);
+    OffsetVarNodes(info->rule_qual, rt_length);
+    ChangeVarNodes((Node*)info->rule_action->qual,
+                  PRS2_CURRENT_VARNO+rt_length, rt_index);
+    ChangeVarNodes((Node*)info->rule_action->targetList,
+                  PRS2_CURRENT_VARNO+rt_length, rt_index);
+    ChangeVarNodes(info->rule_qual, PRS2_CURRENT_VARNO+rt_length, rt_index);
+
+    /*
+     * bug here about replace CURRENT  -- sort of
+     * replace current is deprecated now so this code shouldn't really
+     * need to be so clutzy but.....
+     */
+    if (info->action != CMD_SELECT) {  /* i.e update XXXXX */
+       int new_result_reln = 0;
+       result_reln = info->rule_action->resultRelation;
+       switch (result_reln) {
+       case PRS2_CURRENT_VARNO: new_result_reln = rt_index;
+           break; 
+       case PRS2_NEW_VARNO:    /* XXX */
+       default:
+           new_result_reln = result_reln + rt_length;
+           break;
+       }
+       info->rule_action->resultRelation = new_result_reln;
+    }
+    
+    return info;
+}
+
+static List *
+OptimizeRIRRules(List *locks)
+{
+    List *attr_level = NIL, *i;
+    List *relation_level = NIL;
+    
+    foreach (i, locks) {
+       RewriteRule *rule_lock  = lfirst(i);
+
+       if (rule_lock->attrno == -1) 
+           relation_level = lappend(relation_level, rule_lock);
+       else
+           attr_level = lappend(attr_level, rule_lock);
+    }
+    return nconc(relation_level, attr_level);
+}
+
+/*
+ * idea is to put instead rules before regular rules so that
+ * excess semantically queasy queries aren't processed
+ */
+static List *
+orderRules(List *locks)
+{
+    List *regular = NIL, *i;
+    List *instead_rules = NIL;
+    
+    foreach (i, locks) {
+       RewriteRule *rule_lock  = (RewriteRule *)lfirst(i);
+
+       if (rule_lock->isInstead)
+           instead_rules = lappend(instead_rules, rule_lock);
+       else
+           regular = lappend(regular, rule_lock);
+    }
+    return nconc(regular, instead_rules);
+}
+
+static int
+AllRetrieve(List *actions)
+{
+    List *n;
+    
+    foreach(n, actions) {
+        Query *pt = lfirst(n);
+
+       /*
+        * in the old postgres code, we check whether command_type is
+        * a consp of '('*'.commandType). but we've never supported transitive
+        * closures. Hence removed    - ay 10/94.
+        */
+       if (pt->commandType != CMD_SELECT)
+           return false;
+    }
+    return true;
+}
+
+static List *
+FireRetrieveRulesAtQuery(Query *parsetree,
+                        int rt_index,
+                        Relation relation,
+                        bool *instead_flag,
+                        int rule_flag)
+{
+    List *i, *locks;
+    RuleLock *rt_entry_locks = NULL;
+    List *work = NIL;
+
+    if ((rt_entry_locks = relation->rd_rules) == NULL)
+       return NIL;
+    
+    locks = matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
+
+    /* find all retrieve instead */
+    foreach (i, locks) {
+       RewriteRule *rule_lock  = (RewriteRule *)lfirst(i);
+       
+       if (!rule_lock->isInstead)
+           continue;
+       work = lappend(work, rule_lock);
+    }
+    if (work != NIL) {
+       work = OptimizeRIRRules(locks);
+       foreach (i, work) {
+           RewriteRule *rule_lock = lfirst(i);
+           int relation_level;
+           int modified = FALSE;
+
+           relation_level = (rule_lock->attrno == -1);
+           if (rule_lock->actions == NIL) {
+               *instead_flag = TRUE;
+               return NIL;
+           }
+           if (!rule_flag &&
+               length(rule_lock->actions) >= 2 &&
+               AllRetrieve(rule_lock->actions)) {
+               *instead_flag = TRUE;
+               return rule_lock->actions;
+           }
+           ApplyRetrieveRule(parsetree, rule_lock, rt_index, relation_level,
+                             &modified);
+           if (modified) {
+               *instead_flag = TRUE;
+               FixResdomTypes(parsetree->targetList);
+               return lcons(parsetree,NIL);
+           }
+       }
+    }
+    return NIL;
+}      
+
+
+/* Idea is like this:
+ *
+ * retrieve-instead-retrieve rules have different semantics than update nodes
+ * Separate RIR rules from others.  Pass others to FireRules.
+ * Order RIR rules and process.
+ *
+ * side effect: parsetree's rtable field might be changed
+ */
+static void
+ApplyRetrieveRule(Query *parsetree,
+                 RewriteRule *rule, 
+                 int rt_index,
+                 int relation_level,
+                 int *modified)
+{
+    Query *rule_action = NULL;
+    Node *rule_qual;
+    List *rtable, *rt;
+    int nothing, rt_length;
+    int badsql= FALSE;
+
+    rule_qual = rule->qual;
+    if (rule->actions) {
+       if (length(rule->actions) > 1)  /* ??? because we don't handle rules
+                                          with more than one action? -ay */
+           return;
+       rule_action = copyObject(lfirst(rule->actions));
+       nothing = FALSE;
+    } else {
+       nothing = TRUE;
+    }
+
+    rtable = copyObject(parsetree->rtable);
+    foreach (rt, rtable) {
+       RangeTblEntry *rte = lfirst(rt);
+       /*
+        * this is to prevent add_missing_vars_to_base_rels() from
+        * adding a bogus entry to the new target list.
+        */
+       rte->inFromCl = false;
+    }
+    rt_length = length(rtable);
+    rtable = nconc(rtable, copyObject(rule_action->rtable)); 
+    parsetree->rtable = rtable;
+
+    rule_action->rtable = rtable;
+    OffsetVarNodes(rule_action->qual, rt_length);
+    OffsetVarNodes((Node*)rule_action->targetList, rt_length);
+    OffsetVarNodes(rule_qual, rt_length);
+    ChangeVarNodes(rule_action->qual,
+                  PRS2_CURRENT_VARNO+rt_length, rt_index);
+    ChangeVarNodes((Node*)rule_action->targetList,
+                  PRS2_CURRENT_VARNO+rt_length, rt_index);
+    ChangeVarNodes(rule_qual, PRS2_CURRENT_VARNO+rt_length, rt_index);
+    if (relation_level) {
+       HandleViewRule(parsetree, rtable, rule_action->targetList, rt_index,
+                      modified);
+    } else {
+       HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
+                              rt_index, rule->attrno, modified, &badsql);
+    }
+    if (*modified && !badsql)
+       AddQual(parsetree, rule_action->qual);
+}
+
+static List *
+ProcessRetrieveQuery(Query *parsetree,
+                    List *rtable,
+                    bool *instead_flag,
+                    bool rule)
+{
+    List *rt;
+    List *product_queries = NIL;
+    int rt_index = 0;
+    
+    foreach (rt, rtable) {
+       RangeTblEntry *rt_entry = lfirst(rt);
+       Relation rt_entry_relation = NULL;
+       List *result = NIL;
+       
+       rt_index++;
+       rt_entry_relation = heap_openr(rt_entry->relname);
+
+       if (rt_entry_relation->rd_rules != NULL) {
+           result = 
+               FireRetrieveRulesAtQuery(parsetree,
+                                        rt_index,
+                                        rt_entry_relation,
+                                        instead_flag,
+                                        rule);
+       }
+       heap_close(rt_entry_relation);
+       if (*instead_flag)
+           return result;
+    }
+    if (rule)
+       return NIL;
+
+    foreach (rt, rtable) {
+       RangeTblEntry *rt_entry = lfirst(rt);
+       Relation rt_entry_relation = NULL;
+       RuleLock *rt_entry_locks = NULL;
+       List *result = NIL;
+       List *locks = NIL;
+       List *dummy_products;
+
+       rt_index++;
+       rt_entry_relation = heap_openr(rt_entry->relname);
+       rt_entry_locks = rt_entry_relation->rd_rules;
+       heap_close(rt_entry_relation);
+
+       if (rt_entry_locks) {
+           locks =
+               matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
+       }
+       if (locks != NIL) {
+           result = fireRules(parsetree, rt_index, CMD_SELECT,
+                              instead_flag, locks, &dummy_products);
+           if (*instead_flag)
+               return lappend(NIL, result);
+           if (result != NIL)
+               product_queries = nconc(product_queries, result);
+       }
+    }
+    return product_queries;
+}
+
+static Query *
+CopyAndAddQual(Query *parsetree,
+              List *actions,
+              Node *rule_qual,
+              int rt_index,
+              CmdType event)
+{
+    Query *new_tree = (Query *) copyObject(parsetree);
+    Node *new_qual = NULL;
+    Query *rule_action = NULL;
+    
+    if (actions)
+       rule_action = lfirst(actions);
+    if (rule_qual != NULL)
+       new_qual = (Node *)copyObject(rule_qual);
+    if (rule_action != NULL) {
+       List *rtable;
+       int rt_length;
+       
+       rtable = new_tree->rtable;
+       rt_length = length(rtable);
+       rtable = append(rtable,listCopy(rule_action->rtable));
+       new_tree->rtable = rtable;
+       OffsetVarNodes(new_qual, rt_length);
+       ChangeVarNodes(new_qual, PRS2_CURRENT_VARNO+rt_length, rt_index);
+    }
+    /* XXX -- where current doesn't work for instead nothing.... yet*/
+    AddNotQual(new_tree, new_qual);
+
+    return new_tree;
+}
+
+
+/*
+ *  fireRules -
+ *     Iterate through rule locks applying rules.  After an instead rule
+ *     rule has been applied, return just new parsetree and let RewriteQuery
+ *     start the process all over again.  The locks are reordered to maintain
+ *     sensible semantics.  remember: reality is for dead birds -- glass
+ *
+ */
+static List *
+fireRules(Query *parsetree,
+         int rt_index,
+         CmdType event,
+         bool *instead_flag,
+         List *locks,
+         List **qual_products)
+{
+    RewriteInfo *info;
+    List *results = NIL;
+    List *i;
+    
+    /* choose rule to fire from list of rules */
+    if (locks == NIL) {
+       (void) ProcessRetrieveQuery(parsetree,
+                                   parsetree->rtable,
+                                   instead_flag, TRUE);
+       if (*instead_flag)
+           return lappend(NIL, parsetree);
+       else
+           return NIL;
+    }
+    
+    locks = orderRules(locks); /* instead rules first */
+    foreach (i, locks) {
+       RewriteRule *rule_lock = (RewriteRule *)lfirst(i);
+       Node *qual, *event_qual;
+       List *actions;
+       List *r;
+       bool orig_instead_flag = *instead_flag;
+
+       /* multiple rule action time */
+       *instead_flag = rule_lock->isInstead;
+       event_qual = rule_lock->qual;
+       actions = rule_lock->actions;
+       if (event_qual != NULL && *instead_flag)
+           *qual_products =
+               lappend(*qual_products,
+                        CopyAndAddQual(parsetree, actions, event_qual,
+                                       rt_index, event));
+       foreach (r, actions) {
+           Query *rule_action  = lfirst(r);
+           Node *rule_qual = copyObject(event_qual);
+           
+           /*--------------------------------------------------
+            * Step 1:
+            *    Rewrite current.attribute or current to tuple variable
+            *    this appears to be done in parser?
+            *--------------------------------------------------
+            */
+           info = gatherRewriteMeta(parsetree, rule_action, rule_qual,
+                                    rt_index,event,instead_flag);
+           
+           /* handle escapable cases, or those handled by other code */
+           if (info->nothing) {
+               if (*instead_flag)
+                   return NIL;
+               else
+                   continue;
+           }
+
+           if (info->action == info->event &&
+               info->event == CMD_SELECT)
+               continue;
+
+           /*
+            * Event Qualification forces copying of parsetree --- XXX
+            * and splitting into two queries one w/rule_qual, one
+            * w/NOT rule_qual. Also add user query qual onto rule action
+            */
+           qual = parsetree->qual;
+           AddQual(info->rule_action, qual);
+           
+           if (info->rule_qual != NULL)
+               AddQual(info->rule_action, info->rule_qual);
+           
+           /*--------------------------------------------------
+            * Step 2:
+            *    Rewrite new.attribute w/ right hand side of target-list
+            *    entry for appropriate field name in insert/update
+            *--------------------------------------------------
+            */  
+           if ((info->event == CMD_INSERT) || (info->event == CMD_UPDATE)) {
+               FixNew(info, parsetree);
+           }
+           
+           /*--------------------------------------------------
+            * Step 3:
+            *    rewriting due to retrieve rules 
+            *--------------------------------------------------
+            */
+           info->rule_action->rtable = info->rt;                  
+           (void) ProcessRetrieveQuery(info->rule_action, info->rt,
+                                       &orig_instead_flag, TRUE);
+           
+           /*--------------------------------------------------  
+            * Step 4
+            *    Simplify? hey, no algorithm for simplification... let
+            *    the planner do it.
+            *--------------------------------------------------  
+            */
+           results = lappend(results, info->rule_action);
+
+           pfree(info);
+       }
+       if (*instead_flag) break;
+    }
+    return results;
+}
+
+static List *
+RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
+{
+    CmdType event;
+    List *product_queries = NIL;
+    int result_relation =  0;
+    
+    Assert(parsetree != NULL);
+    
+    event = parsetree->commandType;
+
+    if (event == CMD_UTILITY)
+       return NIL;
+
+    /*
+     * only for a delete may the targetlist be NULL
+     */
+    if (event != CMD_DELETE) {
+       Assert(parsetree->targetList != NULL);
+    }
+    
+    result_relation = parsetree->resultRelation;
+
+    if (event != CMD_SELECT) {
+       /*
+        * the statement is an update, insert or delete
+        */
+       RangeTblEntry *rt_entry;
+       Relation rt_entry_relation = NULL;
+       RuleLock *rt_entry_locks = NULL;
+       
+       rt_entry = rt_fetch(result_relation, parsetree->rtable);
+       rt_entry_relation = heap_openr(rt_entry->relname);
+       rt_entry_locks = rt_entry_relation->rd_rules;
+       heap_close(rt_entry_relation);
+
+       if (rt_entry_locks != NULL) {
+           List *locks =
+               matchLocks(event, rt_entry_locks, result_relation, parsetree);
+           
+           product_queries =
+               fireRules(parsetree, 
+                         result_relation,
+                         event,
+                         instead_flag,
+                         locks,
+                         qual_products);
+       }
+       return product_queries;
+    }else {
+       /*
+        * the statement is a select
+        */
+       Query *other;
+
+       other = copyObject(parsetree);  /* ApplyRetrieveRule changes the
+                                          range table */
+       return
+           ProcessRetrieveQuery(other, parsetree->rtable,
+                                instead_flag, FALSE);
+    }
+}
+
+/*
+ * to avoid infinite recursion, we restrict the number of times a query
+ * can be rewritten. Detecting cycles is left for the reader as an excercise.
+ */
+#ifndef REWRITE_INVOKE_MAX
+#define REWRITE_INVOKE_MAX     10
+#endif
+
+static int numQueryRewriteInvoked = 0;
+
+/*
+ * QueryRewrite -
+ *    rewrite one query via QueryRewrite system, possibly returning 0, or many
+ *    queries
+ */  
+List *
+QueryRewrite(Query *parsetree)
+{
+    numQueryRewriteInvoked = 0;
+
+    /*
+     * take a deep breath and apply all the rewrite rules - ay
+     */
+    return deepRewriteQuery(parsetree);
+}
+
+/*
+ * deepRewriteQuery -
+ *    rewrites the query and apply the rules again on the queries rewritten
+ */
+static List *
+deepRewriteQuery(Query *parsetree)
+{
+    List *n;
+    List *rewritten = NIL;
+    List *result = NIL;
+    bool instead;
+    List *qual_products = NIL;
+
+    if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX) {
+       elog(WARN, "query rewritten %d times, may contain cycles",
+            numQueryRewriteInvoked-1);
+    }
+    
+    instead = FALSE;
+    result = RewriteQuery(parsetree, &instead, &qual_products);
+    if (!instead)
+       rewritten = lcons(parsetree, NIL);
+
+    foreach(n, result) {
+        Query *pt = lfirst(n);
+        List *newstuff = NIL;
+       
+        newstuff = deepRewriteQuery(pt);
+        if (newstuff != NIL)
+           rewritten = nconc(rewritten, newstuff);
+    }
+    if (qual_products != NIL)
+       rewritten = nconc(rewritten, qual_products);
+
+    return rewritten;
+}
+
diff --git a/src/backend/rewrite/rewriteHandler.h b/src/backend/rewrite/rewriteHandler.h
new file mode 100644 (file)
index 0000000..758f2f9
--- /dev/null
@@ -0,0 +1,35 @@
+/*-------------------------------------------------------------------------
+ *
+ * rewriteHandler.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        REWRITEHANDLER_H
+#define        REWRITEHANDLER_H
+
+
+struct _rewrite_meta_knowledge {
+    List *rt;
+    int rt_index;
+    bool instead_flag;
+    int event;
+    CmdType action;
+    int current_varno;
+    int new_varno;
+    Query *rule_action;
+    Node *rule_qual;
+    bool nothing;
+};
+
+typedef struct _rewrite_meta_knowledge RewriteInfo;
+
+
+extern List *QueryRewrite(Query *parsetree);
+
+#endif /*REWRITEHANDLER_H */
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
new file mode 100644 (file)
index 0000000..54e7508
--- /dev/null
@@ -0,0 +1,435 @@
+/*-------------------------------------------------------------------------
+ *
+ * rewriteManip.c--
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "nodes/pg_list.h"
+#include "utils/elog.h"
+#include "nodes/nodes.h"
+#include "nodes/relation.h"
+#include "nodes/primnodes.h"
+#include "parser/parsetree.h"          /* for getrelid() */
+#include "utils/lsyscache.h"
+#include "utils/builtins.h"
+#include "rewrite/rewriteHandler.h"
+#include "rewrite/rewriteSupport.h"
+#include "rewrite/locks.h"
+
+#include "nodes/plannodes.h"
+#include "optimizer/clauses.h"
+
+static void ResolveNew(RewriteInfo *info, List *targetlist, Node **node);
+
+
+
+void
+OffsetVarNodes(Node *node, int offset)
+{
+    if (node==NULL)
+       return;
+    switch (nodeTag(node)) {
+    case T_TargetEntry:
+       {
+           TargetEntry *tle = (TargetEntry *)node;
+           OffsetVarNodes(tle->expr, offset);
+       }
+       break;
+    case T_Expr:
+       {
+           Expr *expr = (Expr*)node;
+           OffsetVarNodes((Node*)expr->args, offset);
+       }
+       break;
+    case T_Var:
+       {
+           Var *var = (Var*)node;
+           var->varno += offset;
+           var->varnoold += offset;
+       }
+       break;
+    case T_List:
+       {
+           List *l;
+
+           foreach(l, (List*)node) {
+               OffsetVarNodes(lfirst(l), offset);
+           }
+       }
+       break;
+    default:
+       /* ignore the others */
+       break;
+    }
+}
+
+void
+ChangeVarNodes(Node *node, int old_varno, int new_varno)
+{
+    if (node==NULL)
+       return;
+    switch (nodeTag(node)) {
+    case T_TargetEntry:
+       {
+           TargetEntry *tle = (TargetEntry *)node;
+           ChangeVarNodes(tle->expr, old_varno, new_varno);
+       }
+       break;
+    case T_Expr:
+       {
+           Expr *expr = (Expr*)node;
+           ChangeVarNodes((Node*)expr->args, old_varno, new_varno);
+       }
+       break;
+    case T_Var:
+       {
+           Var *var = (Var*)node;
+           if (var->varno == old_varno) {
+               var->varno = new_varno;
+               var->varnoold = new_varno;
+           }
+       }
+       break;
+    case T_List:
+       {
+           List *l;
+           foreach (l, (List*)node) {
+               ChangeVarNodes(lfirst(l), old_varno, new_varno);
+           }
+       }
+       break;
+    default:
+       /* ignore the others */
+       break;
+    }
+}
+
+void
+AddQual(Query *parsetree, Node *qual)
+{
+    Node *copy, *old;
+    
+    if (qual == NULL)
+       return;
+    
+    copy = copyObject(qual);
+    old = parsetree->qual;
+    if (old == NULL)
+       parsetree->qual = copy;
+    else 
+       parsetree->qual = 
+           (Node*)make_andclause(makeList(parsetree->qual, copy, -1));
+}
+
+void
+AddNotQual(Query *parsetree, Node *qual)
+{
+    Node *copy;
+    
+    if (qual == NULL) return;
+    
+    copy = (Node*)make_notclause(copyObject(qual));
+
+    AddQual(parsetree,copy);
+}
+
+static Node *
+make_null(Oid type)
+{
+    Const *c = makeNode(Const);
+    
+    c->consttype = type;
+    c->constlen = get_typlen(type);
+    c->constvalue = PointerGetDatum(NULL);
+    c->constisnull = true;
+    c->constbyval = get_typbyval(type);
+    return (Node*)c;
+}
+
+void
+FixResdomTypes (List *tlist)
+{
+    List *i;
+
+    foreach (i, tlist) {
+       TargetEntry *tle = lfirst(i);
+
+       if (nodeTag(tle->expr) == T_Var) {
+           Var *var = (Var*)tle->expr;
+
+           tle->resdom->restype = var->vartype;
+           tle->resdom->reslen = get_typlen(var->vartype);
+       }
+    }
+}
+
+static Node *
+FindMatchingNew(List *tlist, int attno)
+{
+    List *i;
+    
+    foreach (i, tlist ) {
+       TargetEntry *tle = lfirst(i);
+
+       if (tle->resdom->resno == attno ) {
+           return (tle->expr);
+       }
+    }
+    return NULL;
+}
+
+static Node *
+FindMatchingTLEntry(List *tlist, char *e_attname)
+{
+    List *i;
+    
+    foreach (i, tlist) {
+       TargetEntry *tle = lfirst(i);
+       char *resname;
+
+       resname = tle->resdom->resname;
+       if (!strcmp(e_attname, resname))
+           return (tle->expr);
+    }
+    return NULL;
+}
+
+static void
+ResolveNew(RewriteInfo *info, List *targetlist, Node **nodePtr)
+{
+    Node *node = *nodePtr;
+
+    if (node == NULL)
+       return;
+
+    switch(nodeTag(node)) {
+    case T_TargetEntry:
+       ResolveNew(info, targetlist, &((TargetEntry*)node)->expr);
+       break;
+    case T_Expr:
+       ResolveNew(info, targetlist, (Node**)(&(((Expr*)node)->args)));
+       break;
+    case T_Var: {
+       int this_varno = (int)((Var*)node)->varno;
+       Node *n;
+           
+       if (this_varno == info->new_varno) {
+           n = FindMatchingNew(targetlist,
+                               ((Var*)node)->varattno);
+           if (n == NULL) {
+               if (info->event == CMD_UPDATE) {
+                   ((Var*)node)->varno = info->current_varno;
+                   ((Var*)node)->varnoold = info->current_varno;
+               } else {
+                   *nodePtr = make_null(((Var*)node)->vartype);
+               }
+           } else {
+               *nodePtr = n;
+           }
+       }
+       break;
+    }
+    case T_List: {
+       List *l;
+       foreach(l, (List*)node) {
+           ResolveNew(info, targetlist, (Node**)&(lfirst(l)));
+       }
+       break;
+    }
+    default:
+       /* ignore the others */
+       break;
+    }
+}
+
+void
+FixNew(RewriteInfo* info, Query *parsetree)
+{
+    ResolveNew(info, parsetree->targetList,
+              (Node**)&(info->rule_action->targetList));
+    ResolveNew(info, parsetree->targetList, &info->rule_action->qual);
+}
+
+static void
+nodeHandleRIRAttributeRule(Node **nodePtr,
+                          List *rtable,
+                          List *targetlist,
+                          int rt_index,
+                          int attr_num,
+                          int *modified,
+                          int *badsql)
+{
+    Node *node = *nodePtr;
+
+    if (node == NULL)
+       return;
+    switch (nodeTag(node)) {
+    case T_List:
+       {
+           List *i;
+           foreach(i, (List*)node) {
+               nodeHandleRIRAttributeRule((Node**)(&(lfirst(i))), rtable,
+                                          targetlist, rt_index, attr_num,
+                                          modified, badsql);
+           }
+       }
+       break;
+    case T_TargetEntry:
+       {
+           TargetEntry *tle = (TargetEntry *)node;
+           nodeHandleRIRAttributeRule(&tle->expr, rtable, targetlist,
+                                      rt_index, attr_num, modified, badsql);
+       }
+       break;
+    case T_Expr:
+       {
+           Expr *expr = (Expr *)node;
+           nodeHandleRIRAttributeRule((Node**)(&(expr->args)), rtable,
+                                      targetlist, rt_index, attr_num,
+                                      modified, badsql);
+       }
+       break;
+    case T_Var:
+       {
+           int this_varno = (int) ((Var*)node)->varno;
+           NameData name_to_look_for;
+           memset(name_to_look_for.data, 0, NAMEDATALEN);
+
+           if (this_varno == rt_index &&
+               ((Var*) node)->varattno == attr_num) {
+               if (((Var*)node)->vartype == 32) { /* HACK */
+                   *nodePtr = make_null(((Var*)node)->vartype);
+                   *modified = TRUE;
+                   *badsql = TRUE;
+                   break;
+               } else {
+                   namestrcpy(&name_to_look_for,
+                              (char *)get_attname(getrelid(this_varno,
+                                                           rtable),
+                                                  attr_num));
+               }
+           }
+           if (name_to_look_for.data[0]) {
+               Node *n;
+               
+               n = FindMatchingTLEntry(targetlist, &name_to_look_for);
+               if (n == NULL) {
+                   *nodePtr = make_null(((Var*) node)->vartype);
+               } else {
+                   *nodePtr = n;
+               }
+               *modified = TRUE;
+           }
+       }
+       break;
+    default:
+       /* ignore the others */
+       break;
+    }
+}
+
+/* 
+ * Handles 'on retrieve to relation.attribute
+ *          do instead retrieve (attribute = expression) w/qual'
+ */
+void
+HandleRIRAttributeRule(Query *parsetree,
+                      List *rtable,
+                      List *targetlist,
+                      int rt_index,
+                      int attr_num,
+                      int *modified,
+                      int *badsql)
+{
+    nodeHandleRIRAttributeRule((Node**)(&(parsetree->targetList)), rtable,
+                              targetlist, rt_index, attr_num,
+                              modified, badsql);
+    nodeHandleRIRAttributeRule(&parsetree->qual, rtable, targetlist,
+                              rt_index, attr_num, modified, badsql);
+}
+
+
+static void
+nodeHandleViewRule(Node **nodePtr,
+                  List *rtable,
+                  List *targetlist,
+                  int rt_index,
+                  int *modified)
+{
+    Node *node = *nodePtr;
+    
+    if (node == NULL)
+       return;
+
+    switch (nodeTag(node)) {
+    case T_List:
+       {
+           List *l;
+           foreach (l, (List*)node) {
+               nodeHandleViewRule((Node**) (&(lfirst(l))),
+                                  rtable, targetlist,
+                                  rt_index, modified);
+           }
+       }
+       break;
+    case T_TargetEntry:
+       {
+           TargetEntry *tle = (TargetEntry *)node;
+           nodeHandleViewRule(&(tle->expr), rtable, targetlist,
+                              rt_index, modified);
+       }
+       break;
+    case T_Expr:
+       {
+           Expr *expr = (Expr*)node;
+           nodeHandleViewRule((Node**)(&(expr->args)),
+                              rtable, targetlist,
+                              rt_index, modified);
+       }
+       break;
+    case T_Var:
+       {
+           Var *var = (Var*)node;
+           int this_varno = var->varno;
+           Node *n;
+
+           if (this_varno == rt_index) {
+               n = FindMatchingTLEntry(targetlist,
+                                       get_attname(getrelid(this_varno,
+                                                            rtable),
+                                                   var->varattno));
+               if (n == NULL) {
+                   *nodePtr = make_null(((Var*) node)->vartype);
+               } else {
+                   *nodePtr = n;
+               }
+               *modified = TRUE;
+           }
+           break;
+       }
+    default:
+       /* ignore the others */
+       break;
+    }
+}
+
+void
+HandleViewRule(Query *parsetree,
+              List *rtable,
+              List *targetlist,
+              int rt_index,
+              int *modified)
+{
+    nodeHandleViewRule(&parsetree->qual, rtable, targetlist, rt_index,
+                      modified);
+    nodeHandleViewRule((Node**)(&(parsetree->targetList)), rtable, targetlist,
+                      rt_index, modified);
+}
+
diff --git a/src/backend/rewrite/rewriteManip.h b/src/backend/rewrite/rewriteManip.h
new file mode 100644 (file)
index 0000000..fce3b37
--- /dev/null
@@ -0,0 +1,31 @@
+/*-------------------------------------------------------------------------
+ *
+ * rewriteManip.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        REWRITEMANIP_H
+#define        REWRITEMANIP_H
+
+/* RewriteManip.c */
+void OffsetVarNodes(Node *node, int offset);
+void ChangeVarNodes(Node *node, int old_varno, int new_varno);
+void AddQual(Query *parsetree, Node *qual);
+void AddNotQual(Query *parsetree, Node *qual);
+void FixResdomTypes(List *user_tlist);
+void FixNew(RewriteInfo *info, Query *parsetree);
+
+void HandleRIRAttributeRule(Query *parsetree, List *rtable, List *targetlist,
+                           int rt_index, int attr_num, int *modified,
+                           int *badpostquel);
+void HandleViewRule(Query *parsetree, List *rtable, List *targetlist,
+                   int rt_index, int *modified);
+
+#endif /* REWRITEMANIP_H */
+
diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c
new file mode 100644 (file)
index 0000000..fac4a2e
--- /dev/null
@@ -0,0 +1,181 @@
+/*-------------------------------------------------------------------------
+ *
+ * rewriteRemove.c--
+ *    routines for removing rewrite rules
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "access/skey.h"
+#include "catalog/pg_rewrite.h"
+#include "catalog/catname.h"   /* for RewriteRelationName */
+#include "utils/syscache.h"
+#include "utils/elog.h"                /* for elog stuff */
+#include "utils/palloc.h"
+#include "utils/tqual.h"       /* 'NowTimeQual' defined here.. */
+#include "access/heapam.h"     /* heap AM calls defined here */
+#include "fmgr.h"              /* for CHAR_16_EQ */
+
+#include "rewrite/rewriteRemove.h"     /* where the decls go */
+#include "rewrite/rewriteSupport.h"
+
+/*-----------------------------------------------------------------------
+ * RewriteGetRuleEventRel
+ *-----------------------------------------------------------------------
+ */
+char*
+RewriteGetRuleEventRel(char *rulename)
+{
+    HeapTuple htp;
+    Oid eventrel;
+    
+    htp = SearchSysCacheTuple(REWRITENAME, PointerGetDatum(rulename),
+                             0,0,0);
+    if (!HeapTupleIsValid(htp))
+       elog(WARN, "RewriteGetRuleEventRel: rule \"%s\" not found",
+            rulename);
+    eventrel = ((Form_pg_rewrite) GETSTRUCT(htp))->ev_class;
+    htp = SearchSysCacheTuple(RELOID, PointerGetDatum(eventrel), 
+                             0,0,0);
+    if (!HeapTupleIsValid(htp))
+       elog(WARN, "RewriteGetRuleEventRel: class %d not found",
+            eventrel);
+    return ((Form_pg_class) GETSTRUCT(htp))->relname.data;
+}
+
+/* ----------------------------------------------------------------
+ *
+ * RemoveRewriteRule
+ *
+ * Delete a rule given its rulename.
+ *
+ * There are three steps.
+ *   1) Find the corresponding tuple in 'pg_rewrite' relation.
+ *      Find the rule Id (i.e. the Oid of the tuple) and finally delete
+ *      the tuple.
+ *   3) Delete the locks from the 'pg_class' relation.
+ *
+ *
+ * ----------------------------------------------------------------
+ */
+void
+RemoveRewriteRule(char *ruleName)
+{
+    Relation RewriteRelation   = NULL;
+    HeapScanDesc scanDesc      = NULL;
+    ScanKeyData scanKeyData;
+    HeapTuple tuple            = NULL;
+    Oid ruleId                 = (Oid)0;
+    Oid eventRelationOid       = (Oid)NULL;
+    Datum eventRelationOidDatum        = (Datum)NULL;
+    Buffer buffer              = (Buffer)NULL;
+    bool isNull                = false;
+
+    /*
+     * Open the pg_rewrite relation. 
+     */
+    RewriteRelation = heap_openr(RewriteRelationName);
+    
+    /*
+     * Scan the RuleRelation ('pg_rewrite') until we find a tuple
+     */
+    ScanKeyEntryInitialize(&scanKeyData, 0, Anum_pg_rewrite_rulename,
+                          F_CHAR16EQ, NameGetDatum(ruleName));
+    scanDesc = heap_beginscan(RewriteRelation,
+                             0, NowTimeQual, 1, &scanKeyData);
+    
+    tuple = heap_getnext(scanDesc, 0, (Buffer *)NULL);
+    
+    /*
+     * complain if no rule with such name existed
+     */
+    if (!HeapTupleIsValid(tuple)) {
+       heap_close(RewriteRelation);
+       elog(WARN, "No rule with name = '%s' was found.\n", ruleName);
+    }
+    
+    /*
+     * Store the OID of the rule (i.e. the tuple's OID)
+     * and the event relation's OID
+     */
+    ruleId = tuple->t_oid;
+    eventRelationOidDatum =
+       PointerGetDatum(heap_getattr(tuple,
+                                    buffer,
+                                    Anum_pg_rewrite_ev_class,
+                    RelationGetTupleDescriptor(RewriteRelation),
+                                    &isNull));
+    if (isNull) {
+       /* XXX strange!!! */
+       elog(WARN, "RemoveRewriteRule: null event target relation!");
+    }
+    eventRelationOid = DatumGetObjectId(eventRelationOidDatum);
+    
+    /*
+     * Now delete the relation level locks from the updated relation.
+     * (Make sure we do this before we remove the rule from pg_rewrite.
+     * Otherwise, heap_openr on eventRelationOid which reads pg_rwrite
+     * for the rules will fail.)
+     */
+    prs2_deleteFromRelation(eventRelationOid, ruleId);
+
+    /*
+     * Now delete the tuple...
+     */
+    heap_delete(RewriteRelation, &(tuple->t_ctid));
+    heap_close(RewriteRelation);
+    heap_endscan(scanDesc);
+}
+
+/*
+ * RelationRemoveRules -
+ *    removes all rules associated with the relation when the relation is
+ *    being removed.
+ */
+void
+RelationRemoveRules(Oid relid)
+{
+    Relation RewriteRelation   = NULL;
+    HeapScanDesc scanDesc      = NULL;
+    ScanKeyData scanKeyData;
+    HeapTuple tuple            = NULL;
+
+    /*
+     * Open the pg_rewrite relation. 
+     */
+    RewriteRelation = heap_openr(RewriteRelationName);
+    
+    /*
+     * Scan the RuleRelation ('pg_rewrite') for all the tuples that
+     * has the same ev_class as relid (the relation to be removed).
+     */
+    ScanKeyEntryInitialize(&scanKeyData,
+                          0,
+                          Anum_pg_rewrite_ev_class,
+                          F_OIDEQ,
+                          ObjectIdGetDatum(relid));
+    scanDesc = heap_beginscan(RewriteRelation,
+                             0, NowTimeQual, 1, &scanKeyData);
+
+    for(;;) {
+       tuple = heap_getnext(scanDesc, 0, (Buffer *)NULL);
+    
+       if (!HeapTupleIsValid(tuple)) {
+           break; /* we're done */
+       }
+    
+       /*
+        * delete the tuple...
+        */
+       heap_delete(RewriteRelation, &(tuple->t_ctid));
+    }
+
+    heap_endscan(scanDesc);
+    heap_close(RewriteRelation);
+}
+
diff --git a/src/backend/rewrite/rewriteRemove.h b/src/backend/rewrite/rewriteRemove.h
new file mode 100644 (file)
index 0000000..46599ea
--- /dev/null
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * rewriteRemove.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        REWRITEREMOVE_H
+#define        REWRITEREMOVE_H
+
+extern char *RewriteGetRuleEventRel(char *rulename);
+extern void RemoveRewriteRule(char *ruleName);
+extern void RelationRemoveRules(Oid relid);
+
+#endif /* REWRITEREMOVE_H */
diff --git a/src/backend/rewrite/rewriteSupport.c b/src/backend/rewrite/rewriteSupport.c
new file mode 100644 (file)
index 0000000..444d678
--- /dev/null
@@ -0,0 +1,270 @@
+/*-------------------------------------------------------------------------
+ *
+ * rewriteSupport.c--
+ *    
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "catalog/catname.h"
+#include "catalog/pg_rewrite.h"
+#include "utils/syscache.h"            /* for SearchSysCache */
+#include "nodes/pg_list.h"
+#include "nodes/parsenodes.h"
+#include "utils/builtins.h"            /* for textout */
+#include "utils/rel.h"                 /* for Relation, RelationData ... */
+#include "utils/elog.h"                        /* for elog */
+#include "storage/buf.h"               /* for InvalidBuffer */
+#include "rewrite/rewriteSupport.h"
+#include "access/heapam.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_proc.h"
+#include "catalog/indexing.h"
+#include "utils/catcache.h"            /* for CacheContext */
+#include "utils/mcxt.h"                        /* MemoryContext stuff */
+#include "utils/palloc.h"
+#include "fmgr.h"
+
+/* 
+ * RuleIdGetActionInfo -
+ *     given a rule oid, look it up and return the rule-event-qual and
+ *     list of parsetrees for the rule (in parseTrees)
+ */
+static Node *
+RuleIdGetActionInfo(Oid ruleoid, bool *instead_flag, Query **parseTrees)
+{
+    HeapTuple          ruletuple;
+    char               *ruleaction = NULL;
+    bool               action_is_null = false;
+    bool               instead_is_null = false;
+    Relation           ruleRelation = NULL;
+    TupleDesc          ruleTupdesc = NULL;
+    Query              *ruleparse = NULL;
+    char               *rule_evqual_string = NULL;
+    Node               *rule_evqual = NULL;
+
+    ruleRelation = heap_openr (RewriteRelationName);
+    ruleTupdesc = RelationGetTupleDescriptor(ruleRelation);
+    ruletuple = SearchSysCacheTuple (RULOID,
+                                    ObjectIdGetDatum(ruleoid),
+                                    0,0,0);
+    if (ruletuple == NULL)
+       elog(WARN, "rule %d isn't in rewrite system relation");
+
+    ruleaction = heap_getattr(ruletuple,
+                             InvalidBuffer,
+                             Anum_pg_rewrite_action,
+                             ruleTupdesc,
+                             &action_is_null ) ;
+    rule_evqual_string = heap_getattr(ruletuple, InvalidBuffer, 
+                                     Anum_pg_rewrite_ev_qual, 
+                                     ruleTupdesc, &action_is_null) ;
+    *instead_flag = (bool) heap_getattr(ruletuple, InvalidBuffer, 
+                                       Anum_pg_rewrite_is_instead, 
+                                       ruleTupdesc, &instead_is_null) ;
+
+    if (action_is_null || instead_is_null) {
+       elog(WARN, "internal error: rewrite rule not properly set up");
+    }
+
+    ruleaction = textout((struct varlena *)ruleaction);
+    rule_evqual_string = textout((struct varlena *)rule_evqual_string);
+
+    ruleparse = (Query*)stringToNode(ruleaction);
+    rule_evqual = (Node*)stringToNode(rule_evqual_string);
+
+    heap_close(ruleRelation);
+
+    *parseTrees = ruleparse;
+    return rule_evqual;
+}
+
+int
+IsDefinedRewriteRule(char *ruleName)
+{
+    Relation RewriteRelation   = NULL;
+    HeapScanDesc scanDesc      = NULL;
+    ScanKeyData scanKey;
+    HeapTuple tuple            = NULL;
+    
+    
+    /*
+     * Open the pg_rewrite relation. 
+     */
+    RewriteRelation = heap_openr(RewriteRelationName);
+    
+    /*
+     * Scan the RuleRelation ('pg_rewrite') until we find a tuple
+     */
+    ScanKeyEntryInitialize(&scanKey, 0, Anum_pg_rewrite_rulename,
+                          NameEqualRegProcedure, PointerGetDatum(ruleName));
+    scanDesc = heap_beginscan(RewriteRelation,
+                             0, NowTimeQual, 1, &scanKey);
+    
+    tuple = heap_getnext(scanDesc, 0, (Buffer *)NULL);
+    
+    /*
+     * return whether or not the rewrite rule existed
+     */
+    heap_close(RewriteRelation);
+    heap_endscan(scanDesc);
+    return (HeapTupleIsValid(tuple));
+}
+
+static void
+setRelhasrulesInRelation(Oid relationId, bool relhasrules)
+{
+    Relation relationRelation;
+    HeapTuple tuple;
+    HeapTuple newTuple;
+    Relation idescs[Num_pg_class_indices];
+    Form_pg_class      relp;
+    
+    /*
+     * Lock a relation given its Oid.
+     * Go to the RelationRelation (i.e. pg_relation), find the
+     * appropriate tuple, and add the specified lock to it.
+     */
+    relationRelation = heap_openr(RelationRelationName);
+    tuple = ClassOidIndexScan(relationRelation, relationId);
+    
+    /*
+     * Create a new tuple (i.e. a copy of the old tuple
+     * with its rule lock field changed and replace the old
+     * tuple in the RelationRelation
+     * NOTE: XXX ??? do we really need to make that copy ????
+     */
+    newTuple = heap_copytuple(tuple);
+
+    relp = (Form_pg_class) GETSTRUCT(newTuple);
+    relp->relhasrules = relhasrules;
+
+    (void) heap_replace(relationRelation, &(tuple->t_ctid), newTuple);
+    
+    /* keep the catalog indices up to date */
+    CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
+    CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation,
+                      newTuple);
+    CatalogCloseIndices(Num_pg_class_indices, idescs);
+    
+    /* be tidy */
+    pfree(tuple);
+    pfree(newTuple);
+    
+    heap_close(relationRelation);
+}
+
+void
+prs2_addToRelation(Oid relid,
+                  Oid ruleId,
+                  CmdType event_type,
+                  AttrNumber attno,
+                  bool isInstead,
+                  Node *qual,
+                  List *actions)
+{
+    Relation relation;
+    RewriteRule *thisRule;
+    RuleLock *rulelock;
+    MemoryContext      oldcxt;
+
+    /*
+     * create an in memory RewriteRule data structure which is cached by
+     * every Relation descriptor. (see utils/cache/relcache.c)
+     */
+    oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+    thisRule = (RewriteRule *)palloc(sizeof(RewriteRule));
+    MemoryContextSwitchTo(oldcxt);
+
+    thisRule->ruleId = ruleId;
+    thisRule->event = event_type;
+    thisRule->attrno = attno;
+    thisRule->qual = qual;
+    thisRule->actions = actions;
+    thisRule->isInstead = isInstead;
+
+    relation = heap_open(relid);
+
+    /*
+     * modify or create a RuleLock cached by Relation
+     */
+    if (relation->rd_rules == NULL) {
+
+       oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+       rulelock = (RuleLock *)palloc(sizeof(RuleLock));
+       rulelock->numLocks = 1;
+       rulelock->rules = (RewriteRule **)palloc(sizeof(RewriteRule*));
+       rulelock->rules[0] = thisRule;
+       relation->rd_rules = rulelock;
+       MemoryContextSwitchTo(oldcxt);
+
+       /*
+        * the fact that relation->rd_rules is NULL means the relhasrules
+        * attribute of the tuple of this relation in pg_class is false. We
+        * need to set it to true.
+        */
+       setRelhasrulesInRelation(relid, TRUE);
+    } else {
+       int numlock;
+       
+       rulelock = relation->rd_rules;
+       numlock = rulelock->numLocks;
+       /* expand, for safety reasons */
+       oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+       rulelock->rules =
+           (RewriteRule **)repalloc(rulelock->rules,
+                                    sizeof(RewriteRule*)*(numlock+1));
+       MemoryContextSwitchTo(oldcxt);
+       rulelock->rules[numlock] = thisRule;
+       rulelock->numLocks++;
+    }
+
+    heap_close(relation);
+
+    return;
+}
+
+void
+prs2_deleteFromRelation(Oid relid, Oid ruleId)
+{
+    RuleLock *rulelock;
+    Relation relation;
+    int numlock;
+    int i;
+    MemoryContext      oldcxt;
+
+    relation = heap_open(relid);
+    rulelock = relation->rd_rules;
+    Assert(rulelock != NULL);
+
+    numlock = rulelock->numLocks;
+    for(i=0; i < numlock; i++) {
+       if (rulelock->rules[i]->ruleId == ruleId)
+           break;
+    }
+    Assert(i<numlock);
+    oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+    pfree(rulelock->rules[i]);
+    MemoryContextSwitchTo(oldcxt);
+    if (numlock==1) {
+       relation->rd_rules = NULL;
+       /*
+        * we don't have rules any more, flag the relhasrules attribute of
+        * the tuple of this relation in pg_class false.
+        */
+       setRelhasrulesInRelation(relid, FALSE);
+    } else {
+       rulelock->rules[i] = rulelock->rules[numlock-1];
+       rulelock->rules[numlock-1] = NULL;
+       rulelock->numLocks--;
+    }
+    
+    heap_close(relation);
+}
+
diff --git a/src/backend/rewrite/rewriteSupport.h b/src/backend/rewrite/rewriteSupport.h
new file mode 100644 (file)
index 0000000..bafc04b
--- /dev/null
@@ -0,0 +1,27 @@
+/*-------------------------------------------------------------------------
+ *
+ * rewriteSupport.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        REWRITESUPPORT_H
+#define        REWRITESUPPORT_H
+
+#include "nodes/pg_list.h"
+
+extern int IsDefinedRewriteRule(char *ruleName);
+
+extern void prs2_addToRelation(Oid relid, Oid ruleId, CmdType event_type,
+                  AttrNumber attno, bool isInstead, Node *qual,
+                  List *actions);
+extern void prs2_deleteFromRelation(Oid relid, Oid ruleId);
+
+
+#endif /* REWRITESUPPORT_H */
+
diff --git a/src/backend/storage/Makefile.inc b/src/backend/storage/Makefile.inc
new file mode 100644 (file)
index 0000000..183ec1b
--- /dev/null
@@ -0,0 +1,31 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the storage modules
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+stordir= $(CURDIR)/storage
+VPATH:= $(VPATH):$(stordir):$(stordir)/buffer:$(stordir)/file:$(stordir)/ipc:\
+       $(stordir)/large_object:$(stordir)/lmgr:$(stordir)/page:$(stordir)/smgr
+
+SUBSRCS=
+include $(stordir)/buffer/Makefile.inc
+include $(stordir)/file/Makefile.inc
+include $(stordir)/ipc/Makefile.inc
+include $(stordir)/large_object/Makefile.inc
+include $(stordir)/lmgr/Makefile.inc
+include $(stordir)/page/Makefile.inc
+include $(stordir)/smgr/Makefile.inc
+SRCS_STORAGE:= $(SUBSRCS)
+
+HEADERS+= backendid.h block.h buf.h buf_internals.h bufmgr.h bufpage.h \
+       fd.h ipc.h item.h itemid.h itempos.h \
+       itemptr.h large_object.h lmgr.h lock.h multilev.h off.h page.h \
+       pagenum.h pos.h proc.h shmem.h sinval.h sinvaladt.h smgr.h spin.h
diff --git a/src/backend/storage/backendid.h b/src/backend/storage/backendid.h
new file mode 100644 (file)
index 0000000..fdf7f6f
--- /dev/null
@@ -0,0 +1,32 @@
+/*-------------------------------------------------------------------------
+ *
+ * backendid.h--
+ *    POSTGRES backend id communication definitions
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        BACKENDID_H
+#define BACKENDID_H
+
+/* ----------------
+ *     pulled out of sinval.h to temporarily reduce #include nesting.
+ *     -cim 8/17/90
+ * ----------------
+ */
+typedef int16  BackendId;      /* unique currently active backend identifier */
+
+#define InvalidBackendId       (-1)
+
+typedef int32  BackendTag;     /* unique backend identifier */
+
+#define InvalidBackendTag      (-1)
+
+extern BackendId       MyBackendId;    /* backend id of this backend */
+extern BackendTag      MyBackendTag;   /* backend tag of this backend */
+
+#endif /* BACKENDID_H */
diff --git a/src/backend/storage/block.h b/src/backend/storage/block.h
new file mode 100644 (file)
index 0000000..4b6c491
--- /dev/null
@@ -0,0 +1,114 @@
+/*-------------------------------------------------------------------------
+ *
+ * block.h--
+ *    POSTGRES disk block definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        BLOCK_H
+#define BLOCK_H
+
+#include "c.h"
+
+/*
+ * BlockNumber:
+ *
+ * each data file (heap or index) is divided into postgres disk blocks
+ * (which may be thought of as the unit of i/o -- a postgres buffer
+ * contains exactly one disk block).  the blocks are numbered
+ * sequentially, 0 to 0xFFFFFFFE.
+ *
+ * InvalidBlockNumber is the same thing as P_NEW in buf.h.
+ *
+ * the access methods, the buffer manager and the storage manager are
+ * more or less the only pieces of code that should be accessing disk
+ * blocks directly.
+ */
+typedef uint32 BlockNumber;
+
+#define InvalidBlockNumber     ((BlockNumber) 0xFFFFFFFF)
+
+/*
+ * BlockId:
+ *
+ * this is a storage type for BlockNumber.  in other words, this type
+ * is used for on-disk structures (e.g., in HeapTupleData) whereas
+ * BlockNumber is the type on which calculations are performed (e.g.,
+ * in access method code).
+ *
+ * there doesn't appear to be any reason to have separate types except
+ * for the fact that BlockIds can be SHORTALIGN'd (and therefore any
+ * structures that contains them, such as ItemPointerData, can also be
+ * SHORTALIGN'd).  this is an important consideration for reducing the
+ * space requirements of the line pointer (ItemIdData) array on each
+ * page and the header of each heap or index tuple, so it doesn't seem
+ * wise to change this without good reason.
+ */
+typedef struct BlockIdData {
+    uint16     bi_hi;
+    uint16     bi_lo;
+} BlockIdData;
+
+typedef BlockIdData    *BlockId;       /* block identifier */
+
+/* ----------------
+ *     support macros
+ * ----------------
+ */
+
+/*
+ * BlockNumberIsValid --
+ *     True iff blockNumber is valid.
+ */
+#define BlockNumberIsValid(blockNumber) \
+    ((bool) ((int32) (blockNumber) != InvalidBlockNumber))
+
+/*
+ * BlockIdIsValid --
+ *     True iff the block identifier is valid.
+ */
+#define BlockIdIsValid(blockId) \
+    ((bool) PointerIsValid(blockId))
+
+/*
+ * BlockIdSet --
+ *     Sets a block identifier to the specified value.
+ */
+#define BlockIdSet(blockId, blockNumber) \
+    Assert(PointerIsValid(blockId)); \
+    (blockId)->bi_hi = (blockNumber) >> 16; \
+    (blockId)->bi_lo = (blockNumber) & 0xffff
+
+/*
+ * BlockIdCopy --
+ *     Copy a block identifier.
+ */
+#define BlockIdCopy(toBlockId, fromBlockId) \
+    Assert(PointerIsValid(toBlockId)); \
+    Assert(PointerIsValid(fromBlockId)); \
+    (toBlockId)->bi_hi = (fromBlockId)->bi_hi; \
+    (toBlockId)->bi_lo = (fromBlockId)->bi_lo
+
+/*
+ * BlockIdEquals --
+ *     Check for block number equality.
+ */
+#define BlockIdEquals(blockId1, blockId2) \
+    ((blockId1)->bi_hi == (blockId2)->bi_hi && \
+     (blockId1)->bi_lo == (blockId2)->bi_lo)
+
+/*
+ * BlockIdGetBlockNumber --
+ *     Retrieve the block number from a block identifier.
+ */
+#define BlockIdGetBlockNumber(blockId) \
+    (AssertMacro(BlockIdIsValid(blockId)) ? \
+     (BlockNumber) (((blockId)->bi_hi << 16) | ((uint16) (blockId)->bi_lo)) : \
+     (BlockNumber) InvalidBlockNumber)
+
+#endif /* BLOCK_H */
diff --git a/src/backend/storage/buf.h b/src/backend/storage/buf.h
new file mode 100644 (file)
index 0000000..855ae44
--- /dev/null
@@ -0,0 +1,47 @@
+/*-------------------------------------------------------------------------
+ *
+ * buf.h--
+ *    Basic buffer manager data types.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        BUF_H
+#define BUF_H
+
+#define InvalidBuffer  (0)
+#define UnknownBuffer  (-99999)
+
+typedef long   Buffer;
+
+/*
+ * BufferIsInvalid --
+ *     True iff the buffer is invalid.
+ */
+#define BufferIsInvalid(buffer)        ((buffer) == InvalidBuffer)
+
+/*
+ * BufferIsUnknown --
+ *     True iff the buffer is unknown.
+ */
+#define BufferIsUnknown(buffer)        ((buffer) == UnknownBuffer)
+
+/*
+ * BufferIsLocal --
+ *     True iff the buffer is local (not visible to other servers).
+ */
+#define BufferIsLocal(buffer)  ((buffer) < 0)
+
+/*
+ * If NO_BUFFERISVALID is defined, all error checking using BufferIsValid()
+ * are suppressed.  Decision-making using BufferIsValid is not affected.
+ * This should be set only if one is sure there will be no errors.
+ * - plai 9/10/90
+ */
+#undef NO_BUFFERISVALID
+
+#endif /* BUF_H */
diff --git a/src/backend/storage/buf_internals.h b/src/backend/storage/buf_internals.h
new file mode 100644 (file)
index 0000000..60183f1
--- /dev/null
@@ -0,0 +1,220 @@
+/*-------------------------------------------------------------------------
+ *
+ * buf_internals.h--
+ *    Internal definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTE
+ *     If BUFFERPAGE0 is defined, then 0 will be used as a
+ *     valid buffer page number.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        BUFMGR_INTERNALS_H
+#define BUFMGR_INTERNALS_H
+
+#include "postgres.h"
+#include "storage/buf.h"
+#include "storage/ipc.h"
+#include "storage/shmem.h"
+#include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "utils/rel.h"
+#include "utils/relcache.h"
+
+/* Buf Mgr constants */
+/* in bufmgr.c */
+extern int NBuffers;
+extern int Data_Descriptors;
+extern int Free_List_Descriptor;
+extern int Lookup_List_Descriptor;
+extern int Num_Descriptors;
+
+/*
+ * Flags for buffer descriptors
+ */
+#define BM_DIRTY               (1 << 0)
+#define BM_PRIVATE             (1 << 1)
+#define BM_VALID               (1 << 2)
+#define BM_DELETED             (1 << 3)
+#define BM_FREE                        (1 << 4)
+#define BM_IO_IN_PROGRESS      (1 << 5)
+#define BM_IO_ERROR            (1 << 6)
+
+typedef bits16 BufFlags;
+
+typedef struct sbufdesc BufferDesc;
+typedef struct sbufdesc BufferHdr;
+typedef struct buftag BufferTag;
+/* long * so alignment will be correct */
+typedef long **BufferBlock;
+
+struct buftag{
+  LRelId       relId;
+  BlockNumber   blockNum;  /* blknum relative to begin of reln */
+};
+
+#define CLEAR_BUFFERTAG(a)\
+  (a)->relId.dbId = InvalidOid; \
+  (a)->relId.relId = InvalidOid; \
+  (a)->blockNum = InvalidBlockNumber
+
+#define INIT_BUFFERTAG(a,xx_reln,xx_blockNum) \
+{ \
+  (a)->blockNum = xx_blockNum;\
+  (a)->relId = RelationGetLRelId(xx_reln); \
+}
+
+#define COPY_BUFFERTAG(a,b)\
+{ \
+  (a)->blockNum = (b)->blockNum;\
+  LRelIdAssign(*(a),*(b));\
+}
+
+#define EQUAL_BUFFERTAG(a,b) \
+  (((a)->blockNum == (b)->blockNum) &&\
+   (OID_Equal((a)->relId.relId,(b)->relId.relId)))
+
+
+#define BAD_BUFFER_ID(bid) ((bid<1) || (bid>(NBuffers)))
+#define INVALID_DESCRIPTOR (-3)
+
+/*
+ *  bletch hack -- anyplace that we declare space for relation or
+ *  database names, we just use '16', not a symbolic constant, to
+ *  specify their lengths.  BM_NAMESIZE is the length of these names,
+ *  and is used in the buffer manager code.  somebody with lots of
+ *  spare time should do this for all the other modules, too.
+ */
+#define BM_NAMESIZE    16
+
+/*
+ *  struct sbufdesc -- shared buffer cache metadata for a single
+ *                    shared buffer descriptor.
+ *
+ *     We keep the name of the database and relation in which this
+ *     buffer appears in order to avoid a catalog lookup on cache
+ *     flush if we don't have the reldesc in the cache.  It is also
+ *     possible that the relation to which this buffer belongs is
+ *     not visible to all backends at the time that it gets flushed.
+ *     Dbname, relname, dbid, and relid are enough to determine where
+ *     to put the buffer, for all storage managers.
+ */
+
+struct sbufdesc {
+    Buffer             freeNext;       /* link for freelist chain */
+    Buffer             freePrev;
+    SHMEM_OFFSET       data;           /* pointer to data in buf pool */
+
+    /* tag and id must be together for table lookup to work */
+    BufferTag          tag;            /* file/block identifier */
+    int                        buf_id;         /* maps global desc to local desc */
+
+    BufFlags           flags;          /* described below */
+    int16              bufsmgr;        /* storage manager id for buffer */
+    unsigned           refcount;       /* # of times buffer is pinned */
+
+    char *sb_dbname;   /* name of db in which buf belongs */
+    char *sb_relname;  /* name of reln */
+#ifdef HAS_TEST_AND_SET
+    /* can afford a dedicated lock if test-and-set locks are available */
+    slock_t    io_in_progress_lock;
+#endif /* HAS_TEST_AND_SET */
+
+    /*
+     * I padded this structure to a power of 2 (128 bytes on a MIPS) because
+     * BufferDescriptorGetBuffer is called a billion times and it does an
+     * C pointer subtraction (i.e., "x - y" -> array index of x relative
+     * to y, which is calculated using division by struct size).  Integer
+     * ".div" hits you for 35 cycles, as opposed to a 1-cycle "sra" ...
+     * this hack cut 10% off of the time to create the Wisconsin database!
+     * It eats up more shared memory, of course, but we're (allegedly)
+     * going to make some of these types bigger soon anyway... -pma 1/2/93
+     */
+#if defined(PORTNAME_ultrix4)
+    char               sb_pad[60];     /* no slock_t */
+#endif /* mips */
+#if defined(PORTNAME_sparc) || defined(PORTNAME_sparc_solaris) || defined(PORTNAME_irix5)
+    char               sb_pad[56];     /* has slock_t */
+#endif /* sparc || irix5 */
+#if defined(PORTNAME_hpux)
+    char               sb_pad[44];     /* has slock_t */
+#endif /* alpha */
+#if defined(PORTNAME_alpha)
+    char               sb_pad[40];     /* has slock_t */
+#endif /* alpha */
+};
+
+/*
+ *  mao tracing buffer allocation
+ */
+
+/*#define BMTRACE*/
+#ifdef BMTRACE
+
+typedef struct _bmtrace {
+    int                bmt_pid;
+    long       bmt_buf;
+    long       bmt_dbid;
+    long       bmt_relid;
+    int                bmt_blkno;
+    int                bmt_op;
+
+#define BMT_NOTUSED    0
+#define BMT_ALLOCFND   1
+#define BMT_ALLOCNOTFND        2
+#define        BMT_DEALLOC     3
+
+} bmtrace;
+
+#endif /* BMTRACE */
+
+
+/* 
+ * Bufmgr Interface:
+ */
+
+/* Internal routines: only called by buf.c */
+
+/*freelist.c*/
+extern void AddBufferToFreelist(BufferDesc *bf);
+extern void PinBuffer(BufferDesc *buf);
+extern void PinBuffer_Debug(char *file, int line, BufferDesc *buf);
+extern void UnpinBuffer(BufferDesc *buf);
+extern void UnpinBuffer_Debug(char *file, int line, BufferDesc *buf);
+extern BufferDesc *GetFreeBuffer(void);
+extern void InitFreeList(bool init);
+extern void DBG_FreeListCheck(int nfree);
+
+/* buf_table.c */
+extern void InitBufTable(void);
+extern BufferDesc *BufTableLookup(BufferTag *tagPtr);
+extern bool BufTableDelete(BufferDesc *buf);
+extern bool BufTableInsert(BufferDesc *buf);
+extern void DBG_LookupListCheck(int nlookup);
+
+/* bufmgr.c */
+extern BufferDesc      *BufferDescriptors;
+extern BufferBlock     BufferBlocks;
+extern long            *PrivateRefCount;
+extern long            *LastRefCount;
+extern SPINLOCK                BufMgrLock;
+
+/* localbuf.c */
+extern long *LocalRefCount;
+extern BufferDesc *LocalBufferDescriptors;
+extern int NLocBuffer;
+
+extern BufferDesc *LocalBufferAlloc(Relation reln, BlockNumber blockNum,
+                                   bool *foundPtr);
+extern int WriteLocalBuffer(Buffer buffer, bool release);
+extern int FlushLocalBuffer(Buffer buffer);
+extern void InitLocalBuffer();
+extern void LocalBufferSync();
+extern void ResetLocalBufferPool();
+     
+#endif /* BUFMGR_INTERNALS_H */
diff --git a/src/backend/storage/buffer/Makefile.inc b/src/backend/storage/buffer/Makefile.inc
new file mode 100644 (file)
index 0000000..48b142d
--- /dev/null
@@ -0,0 +1,16 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for storage/buffer
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= buf_table.c buf_init.c bufmgr.c freelist.c localbuf.c
+
+SRCS_SITEMGR+= buf_table.c buf_init.c freelist.c
diff --git a/src/backend/storage/buffer/buf_init.c b/src/backend/storage/buffer/buf_init.c
new file mode 100644 (file)
index 0000000..3e88435
--- /dev/null
@@ -0,0 +1,280 @@
+/*-------------------------------------------------------------------------
+ *
+ * buf_init.c--
+ *    buffer manager initialization routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <sys/file.h>
+#include <stdio.h>
+#include <math.h>
+#include <signal.h>
+
+/* declarations split between these three files */
+#include "storage/buf.h"
+#include "storage/buf_internals.h"
+#include "storage/bufmgr.h"
+
+#include "storage/fd.h"
+#include "storage/ipc.h"
+#include "storage/shmem.h"
+#include "storage/spin.h"
+#include "storage/smgr.h"
+#include "storage/lmgr.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/hsearch.h"
+#include "utils/elog.h"
+#include "utils/memutils.h"
+#include "executor/execdebug.h"        /* for NDirectFileRead */
+#include "catalog/catalog.h"
+
+/*
+ *  if BMTRACE is defined, we trace the last 200 buffer allocations and
+ *  deallocations in a circular buffer in shared memory.
+ */
+#ifdef BMTRACE
+bmtrace        *TraceBuf;
+long   *CurTraceBuf;
+#define        BMT_LIMIT       200
+#endif /* BMTRACE */
+int ShowPinTrace = 0;
+
+int            NBuffers = NDBUFS;  /* NDBUFS defined in miscadmin.h */
+int            Data_Descriptors;
+int            Free_List_Descriptor;
+int            Lookup_List_Descriptor;
+int            Num_Descriptors;
+
+BufferDesc     *BufferDescriptors;
+BufferBlock    BufferBlocks;
+#ifndef HAS_TEST_AND_SET
+long   *NWaitIOBackendP;
+#endif
+
+extern IpcSemaphoreId      WaitIOSemId;
+
+long   *PrivateRefCount;       /* also used in freelist.c */
+long   *LastRefCount;  /* refcounts of last ExecMain level */
+
+/*
+ * Data Structures:
+ *      buffers live in a freelist and a lookup data structure.
+ *     
+ *
+ * Buffer Lookup:
+ *     Two important notes.  First, the buffer has to be
+ *     available for lookup BEFORE an IO begins.  Otherwise
+ *     a second process trying to read the buffer will 
+ *     allocate its own copy and the buffeer pool will 
+ *     become inconsistent.
+ *
+ * Buffer Replacement:
+ *     see freelist.c.  A buffer cannot be replaced while in
+ *     use either by data manager or during IO.
+ *
+ * WriteBufferBack:
+ *     currently, a buffer is only written back at the time
+ *     it is selected for replacement.  It should 
+ *     be done sooner if possible to reduce latency of 
+ *     BufferAlloc().  Maybe there should be a daemon process.
+ *
+ * Synchronization/Locking:
+ *
+ * BufMgrLock lock -- must be acquired before manipulating the 
+ *     buffer queues (lookup/freelist).  Must be released 
+ *     before exit and before doing any IO.  
+ *
+ * IO_IN_PROGRESS -- this is a flag in the buffer descriptor.
+ *      It must be set when an IO is initiated and cleared at
+ *      the end of  the IO.  It is there to make sure that one
+ *     process doesn't start to use a buffer while another is
+ *     faulting it in.  see IOWait/IOSignal.
+ *
+ * refcount --  A buffer is pinned during IO and immediately
+ *     after a BufferAlloc().  A buffer is always either pinned
+ *     or on the freelist but never both.  The buffer must be
+ *     released, written, or flushed before the end of 
+ *     transaction.
+ *
+ * PrivateRefCount -- Each buffer also has a private refcount the keeps
+ *     track of the number of times the buffer is pinned in the current
+ *     processes.  This is used for two purposes, first, if we pin a
+ *     a buffer more than once, we only need to change the shared refcount
+ *     once, thus only lock the buffer pool once, second, when a transaction
+ *     aborts, it should only unpin the buffers exactly the number of times it
+ *     has pinned them, so that it will not blow away buffers of another
+ *     backend.
+ *
+ */
+
+SPINLOCK BufMgrLock;
+
+/* delayed write: TRUE on, FALSE off */
+int LateWrite = TRUE;
+
+int ReadBufferCount;
+int BufferHitCount;
+int BufferFlushCount;
+
+
+/*
+ * Initialize module:
+ *
+ * should calculate size of pool dynamically based on the
+ * amount of available memory.
+ */
+void
+InitBufferPool(IPCKey key)
+{
+    bool foundBufs,foundDescs;
+    int i;
+    
+    Data_Descriptors = NBuffers;
+    Free_List_Descriptor = Data_Descriptors;
+    Lookup_List_Descriptor = Data_Descriptors + 1;
+    Num_Descriptors = Data_Descriptors + 1;
+    
+    SpinAcquire(BufMgrLock);
+    
+#ifdef BMTRACE
+    CurTraceBuf = (long *) ShmemInitStruct("Buffer trace",
+                                          (BMT_LIMIT * sizeof(bmtrace)) + sizeof(long),
+                                          &foundDescs);
+    if (!foundDescs)
+       memset(CurTraceBuf, 0, (BMT_LIMIT * sizeof(bmtrace)) + sizeof(long));
+    
+    TraceBuf = (bmtrace *) &(CurTraceBuf[1]);
+#endif
+    
+    BufferDescriptors = (BufferDesc *)
+       ShmemInitStruct("Buffer Descriptors",
+                       Num_Descriptors*sizeof(BufferDesc),&foundDescs);
+    
+    BufferBlocks = (BufferBlock)
+       ShmemInitStruct("Buffer Blocks",
+                       NBuffers*BLCKSZ,&foundBufs);
+    
+#ifndef HAS_TEST_AND_SET
+    {
+       bool foundNWaitIO;
+       
+       NWaitIOBackendP = (long *)ShmemInitStruct("#Backends Waiting IO",
+                                                 sizeof(long),
+                                                 &foundNWaitIO);
+       if (!foundNWaitIO)
+           *NWaitIOBackendP = 0;
+    }
+#endif
+    
+    if (foundDescs || foundBufs) {
+       
+       /* both should be present or neither */
+       Assert(foundDescs && foundBufs);
+       
+    } else {
+       BufferDesc *buf;
+       unsigned long block;
+       
+       buf = BufferDescriptors;
+       block = (unsigned long) BufferBlocks;
+       
+       /*
+        * link the buffers into a circular, doubly-linked list to
+        * initialize free list.  Still don't know anything about
+        * replacement strategy in this file.
+        */
+       for (i = 0; i < Data_Descriptors; block+=BLCKSZ,buf++,i++) {
+           Assert(ShmemIsValid((unsigned long)block));
+           
+           buf->freeNext = i+1;
+           buf->freePrev = i-1;
+           
+           CLEAR_BUFFERTAG(&(buf->tag));
+           buf->data = MAKE_OFFSET(block);
+           buf->flags = (BM_DELETED | BM_FREE | BM_VALID);
+           buf->refcount = 0;
+           buf->buf_id = i;
+#ifdef HAS_TEST_AND_SET
+           S_INIT_LOCK(&(buf->io_in_progress_lock));
+#endif
+       }
+       
+       /* close the circular queue */
+       BufferDescriptors[0].freePrev = Data_Descriptors-1;
+       BufferDescriptors[Data_Descriptors-1].freeNext = 0;
+    }
+    
+    /* Init the rest of the module */
+    InitBufTable();
+    InitFreeList(!foundDescs);
+    
+    SpinRelease(BufMgrLock);
+    
+#ifndef HAS_TEST_AND_SET
+    {
+       int status;
+       WaitIOSemId = IpcSemaphoreCreate(IPCKeyGetWaitIOSemaphoreKey(key),
+                                        1, IPCProtection, 0, 1, &status);
+    }
+#endif
+    PrivateRefCount = (long *) calloc(NBuffers, sizeof(long));
+    LastRefCount = (long *) calloc(NBuffers, sizeof(long));
+}
+
+/* -----------------------------------------------------
+ * BufferShmemSize
+ *
+ * compute the size of shared memory for the buffer pool including
+ * data pages, buffer descriptors, hash tables, etc.
+ * ----------------------------------------------------
+ */
+int
+BufferShmemSize()
+{
+    int size = 0;
+    int nbuckets;
+    int nsegs;
+    int tmp;
+    
+    nbuckets = 1 << (int)my_log2((NBuffers - 1) / DEF_FFACTOR + 1);
+    nsegs = 1 << (int)my_log2((nbuckets - 1) / DEF_SEGSIZE + 1);
+    
+    /* size of shmem binding table */
+    size += MAXALIGN(my_log2(BTABLE_SIZE) * sizeof(void *)); /* HTAB->dir */
+    size += MAXALIGN(sizeof(HHDR));                         /* HTAB->hctl */
+    size += MAXALIGN(DEF_SEGSIZE * sizeof(SEGMENT));
+    size += BUCKET_ALLOC_INCR * 
+       (MAXALIGN(sizeof(BUCKET_INDEX)) +
+        MAXALIGN(BTABLE_KEYSIZE) +
+        MAXALIGN(BTABLE_DATASIZE));
+    
+    /* size of buffer descriptors */
+    size += MAXALIGN((NBuffers + 1) * sizeof(BufferDesc));
+    
+    /* size of data pages */
+    size += NBuffers * MAXALIGN(BLCKSZ);
+    
+    /* size of buffer hash table */
+    size += MAXALIGN(my_log2(NBuffers) * sizeof(void *)); /* HTAB->dir */
+    size += MAXALIGN(sizeof(HHDR));                      /* HTAB->hctl */
+    size += nsegs * MAXALIGN(DEF_SEGSIZE * sizeof(SEGMENT));
+    tmp = (int)ceil((double)NBuffers/BUCKET_ALLOC_INCR);
+    size += tmp * BUCKET_ALLOC_INCR * 
+       (MAXALIGN(sizeof(BUCKET_INDEX)) +
+        MAXALIGN(sizeof(BufferTag)) +
+        MAXALIGN(sizeof(Buffer)));
+    
+#ifdef BMTRACE
+    size += (BMT_LIMIT * sizeof(bmtrace)) + sizeof(long);
+#endif
+    return size;
+}
+
+
diff --git a/src/backend/storage/buffer/buf_table.c b/src/backend/storage/buffer/buf_table.c
new file mode 100644 (file)
index 0000000..912d7da
--- /dev/null
@@ -0,0 +1,162 @@
+/*-------------------------------------------------------------------------
+ *
+ * buf_table.c--
+ *    routines for finding buffers in the buffer pool.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * OLD COMMENTS
+ *
+ * Data Structures:
+ *
+ *     Buffers are identified by their BufferTag (buf.h).  This
+ * file contains routines for allocating a shmem hash table to
+ * map buffer tags to buffer descriptors.
+ *
+ * Synchronization:
+ *  
+ *  All routines in this file assume buffer manager spinlock is
+ *  held by their caller.
+ */
+#include "storage/bufmgr.h"
+#include "storage/buf_internals.h"     /* where the declarations go */
+#include "storage/shmem.h"
+#include "storage/spin.h"
+#include "utils/hsearch.h"
+#include "utils/elog.h"
+
+static HTAB *SharedBufHash;
+
+extern HTAB *ShmemInitHash();
+
+typedef struct lookup { 
+    BufferTag  key; 
+    Buffer     id; 
+} LookupEnt;
+
+/*
+ * Initialize shmem hash table for mapping buffers
+ */
+void
+InitBufTable()
+{
+    HASHCTL info;
+    int hash_flags;
+    
+    /* assume lock is held */
+    
+    /* BufferTag maps to Buffer */
+    info.keysize = sizeof(BufferTag);
+    info.datasize = sizeof(Buffer);
+    info.hash = tag_hash;
+    
+    hash_flags = (HASH_ELEM | HASH_FUNCTION);
+    
+    
+    SharedBufHash = (HTAB *) ShmemInitHash("Shared Buf Lookup Table",
+                                          NBuffers,NBuffers,
+                                          &info,hash_flags);
+    
+    if (! SharedBufHash) {
+       elog(FATAL,"couldn't initialize shared buffer pool Hash Tbl");
+       exit(1);
+    }
+    
+}
+
+BufferDesc *
+BufTableLookup(BufferTag *tagPtr)
+{
+    LookupEnt *        result;
+    bool       found;
+    
+    if (tagPtr->blockNum == P_NEW)
+       return(NULL);
+    
+    result = (LookupEnt *) 
+       hash_search(SharedBufHash,(char *) tagPtr,HASH_FIND,&found);
+    
+    if (! result){
+       elog(WARN,"BufTableLookup: BufferLookup table corrupted");
+       return(NULL);
+    }
+    if (! found) {
+       return(NULL);
+    }
+    return(&(BufferDescriptors[result->id]));
+}
+
+/*
+ * BufTableDelete
+ */
+bool
+BufTableDelete(BufferDesc *buf)
+{
+    LookupEnt *        result;
+    bool       found;
+    
+    /* buffer not initialized or has been removed from
+     * table already.  BM_DELETED keeps us from removing 
+     * buffer twice.
+     */
+    if (buf->flags & BM_DELETED) {
+       return(TRUE);
+    }
+    
+    buf->flags |= BM_DELETED;
+    
+    result = (LookupEnt *)
+       hash_search(SharedBufHash,(char *) &(buf->tag),HASH_REMOVE,&found);
+    
+    if (! (result && found)) {
+       elog(WARN,"BufTableDelete: BufferLookup table corrupted");    
+       return(FALSE);
+    }
+    
+    return(TRUE);
+}
+
+bool
+BufTableInsert(BufferDesc *buf)
+{
+    LookupEnt *        result;
+    bool       found;
+    
+    /* cannot insert it twice */
+    Assert (buf->flags & BM_DELETED);
+    buf->flags &= ~(BM_DELETED);
+    
+    result = (LookupEnt *)
+       hash_search(SharedBufHash,(char *) &(buf->tag),HASH_ENTER,&found);
+    
+    if (! result) {
+       Assert(0);
+       elog(WARN,"BufTableInsert: BufferLookup table corrupted");
+       return(FALSE);
+    }
+    /* found something else in the table ! */
+    if (found) {
+       Assert(0);
+       elog(WARN,"BufTableInsert: BufferLookup table corrupted");
+       return(FALSE);
+    } 
+    
+    result->id = buf->buf_id;
+    return(TRUE);
+}
+
+/* prints out collision stats for the buf table */
+void
+DBG_LookupListCheck(int nlookup)
+{
+    nlookup = 10;
+    
+    hash_stats("Shared",SharedBufHash);
+}
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
new file mode 100644 (file)
index 0000000..12eb337
--- /dev/null
@@ -0,0 +1,1581 @@
+/*-------------------------------------------------------------------------
+ *
+ * bufmgr.c--
+ *    buffer manager interface routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ *
+ * BufferAlloc() -- lookup a buffer in the buffer table.  If
+ *     it isn't there add it, but do not read it into memory.
+ *     This is used when we are about to reinitialize the
+ *     buffer so don't care what the current disk contents are.
+ *     BufferAlloc() pins the new buffer in memory.
+ *
+ * ReadBuffer() -- same as BufferAlloc() but reads the data
+ *     on a buffer cache miss.
+ *
+ * ReleaseBuffer() -- unpin the buffer
+ *
+ * WriteNoReleaseBuffer() -- mark the buffer contents as "dirty"
+ *     but don't unpin.  The disk IO is delayed until buffer
+ *     replacement if LateWrite flag is set.
+ *
+ * WriteBuffer() -- WriteNoReleaseBuffer() + ReleaseBuffer() 
+ *
+ * DirtyBufferCopy() -- For a given dbid/relid/blockno, if the buffer is
+ *                     in the cache and is dirty, mark it clean and copy
+ *                     it to the requested location.  This is a logical
+ *                     write, and has been installed to support the cache
+ *                     management code for write-once storage managers.
+ *
+ * FlushBuffer() -- as above but never delayed write.
+ *
+ * BufferSync() -- flush all dirty buffers in the buffer pool.
+ * 
+ * InitBufferPool() -- Init the buffer module.
+ *
+ * See other files:  
+ *     freelist.c -- chooses victim for buffer replacement 
+ *     buf_table.c -- manages the buffer lookup table
+ */
+#include <sys/file.h>
+#include <stdio.h>
+#include <math.h>
+#include <signal.h>
+
+/* declarations split between these three files */
+#include "storage/buf.h"
+#include "storage/buf_internals.h"
+#include "storage/bufmgr.h"
+
+#include "storage/fd.h"
+#include "storage/ipc.h"
+#include "storage/shmem.h"
+#include "storage/spin.h"
+#include "storage/smgr.h"
+#include "storage/lmgr.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/hsearch.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/memutils.h"
+#include "executor/execdebug.h"        /* for NDirectFileRead */
+#include "catalog/catalog.h"
+
+extern int LateWrite;
+extern SPINLOCK BufMgrLock;
+extern int ReadBufferCount;
+extern int BufferHitCount;
+extern int BufferFlushCount;
+
+static void WaitIO(BufferDesc *buf, SPINLOCK spinlock);
+#ifndef HAS_TEST_AND_SET
+static void SignalIO(BufferDesc *buf);
+extern long *NWaitIOBackendP; /* defined in buf_init.c */
+#endif /* HAS_TEST_AND_SET */
+
+static Buffer ReadBufferWithBufferLock(Relation relation, BlockNumber blockNum,
+                                      bool bufferLockHeld);
+static BufferDesc *BufferAlloc(Relation reln, BlockNumber blockNum,
+                              bool *foundPtr, bool bufferLockHeld);
+static int FlushBuffer(Buffer buffer);
+static void BufferSync(void);
+static int BufferReplace(BufferDesc *bufHdr, bool bufferLockHeld);
+
+/* ---------------------------------------------------
+ * RelationGetBufferWithBuffer
+ *     see if the given buffer is what we want
+ *     if yes, we don't need to bother the buffer manager
+ * ---------------------------------------------------
+ */
+Buffer
+RelationGetBufferWithBuffer(Relation relation,
+                           BlockNumber blockNumber,
+                           Buffer buffer)
+{
+    BufferDesc *bufHdr;
+    LRelId lrelId;
+    
+    if (BufferIsValid(buffer)) {
+       if (!BufferIsLocal(buffer)) {
+           bufHdr = &BufferDescriptors[buffer-1];
+           lrelId = RelationGetLRelId(relation);
+           SpinAcquire(BufMgrLock);
+           if (bufHdr->tag.blockNum == blockNumber &&
+               bufHdr->tag.relId.relId == lrelId.relId &&
+               bufHdr->tag.relId.dbId == lrelId.dbId) {
+               SpinRelease(BufMgrLock);
+               return(buffer);
+           }
+           return(ReadBufferWithBufferLock(relation, blockNumber, true));
+       } else {
+           bufHdr = &LocalBufferDescriptors[-buffer-1];
+           if (bufHdr->tag.relId.relId == relation->rd_id &&
+               bufHdr->tag.blockNum == blockNumber) {
+               return(buffer);
+           }
+       }
+    }
+    return(ReadBuffer(relation, blockNumber));
+}
+
+/*
+ * ReadBuffer -- returns a buffer containing the requested
+ *     block of the requested relation.  If the blknum
+ *     requested is P_NEW, extend the relation file and
+ *     allocate a new block.
+ *
+ * Returns: the buffer number for the buffer containing
+ *     the block read or NULL on an error.
+ *
+ * Assume when this function is called, that reln has been
+ *     opened already.
+ */
+
+extern int ShowPinTrace;
+
+
+#undef ReadBuffer      /* conflicts with macro when BUFMGR_DEBUG defined */
+
+/*
+ * ReadBuffer --
+ *
+ */
+Buffer
+ReadBuffer(Relation reln, BlockNumber blockNum)
+{
+    return ReadBufferWithBufferLock(reln, blockNum, false);
+}
+
+/*
+ * is_userbuffer
+ *
+ * XXX caller must have already acquired BufMgrLock
+ */
+static bool
+is_userbuffer(Buffer buffer)
+{
+    BufferDesc *buf = &BufferDescriptors[buffer-1];
+    
+    if (IsSystemRelationName(buf->sb_relname))
+       return false;
+    return true;
+}
+
+Buffer
+ReadBuffer_Debug(char *file,
+                int line,
+                Relation reln,
+                BlockNumber blockNum)
+{
+    Buffer buffer;
+    
+    buffer = ReadBufferWithBufferLock(reln, blockNum, false);
+    if (ShowPinTrace && !BufferIsLocal(buffer) && is_userbuffer(buffer)) {
+       BufferDesc *buf = &BufferDescriptors[buffer-1];
+       
+       fprintf(stderr, "PIN(RD) %ld relname = %s, blockNum = %d, \
+refcount = %ld, file: %s, line: %d\n",
+               buffer, buf->sb_relname, buf->tag.blockNum,
+               PrivateRefCount[buffer - 1], file, line);
+    }
+    return buffer;
+}
+
+/*
+ * ReadBufferWithBufferLock -- does the work of 
+ *     ReadBuffer() but with the possibility that
+ *     the buffer lock has already been held. this
+ *     is yet another effort to reduce the number of
+ *     semops in the system.
+ */
+static Buffer
+ReadBufferWithBufferLock(Relation reln,
+                        BlockNumber blockNum,
+                        bool bufferLockHeld)
+{
+    BufferDesc *bufHdr;          
+    int                extend;   /* extending the file by one block */
+    int                status;
+    bool       found;
+    bool       isLocalBuf;
+
+    extend = (blockNum == P_NEW);
+    isLocalBuf = reln->rd_islocal;
+
+    if (isLocalBuf) {
+       bufHdr = LocalBufferAlloc(reln, blockNum, &found);
+    } else {
+       ReadBufferCount++;
+
+       /* lookup the buffer.  IO_IN_PROGRESS is set if the requested
+        * block is not currently in memory.
+        */
+       bufHdr = BufferAlloc(reln, blockNum, &found, bufferLockHeld);
+       if (found) BufferHitCount++;
+    }
+
+    if (!bufHdr) {
+       return(InvalidBuffer);
+    }
+    
+    /* if its already in the buffer pool, we're done */
+    if (found) {
+       /*
+        * This happens when a bogus buffer was returned previously and is
+        * floating around in the buffer pool.  A routine calling this would
+        * want this extended.
+        */
+       if (extend) {
+           /* new buffers are zero-filled */
+           memset((char *) MAKE_PTR(bufHdr->data), 0, BLCKSZ);
+           (void) smgrextend(bufHdr->bufsmgr, reln,
+                             (char *) MAKE_PTR(bufHdr->data));
+       }
+       return (BufferDescriptorGetBuffer(bufHdr));
+               
+    }
+    
+    /* 
+     * if we have gotten to this point, the reln pointer must be ok
+     * and the relation file must be open.
+     */
+    if (extend) {
+       /* new buffers are zero-filled */
+       (void) memset((char *) MAKE_PTR(bufHdr->data), 0, BLCKSZ);
+       status = smgrextend(bufHdr->bufsmgr, reln,
+                           (char *) MAKE_PTR(bufHdr->data));
+    } else {
+       status = smgrread(bufHdr->bufsmgr, reln, blockNum,
+                         (char *) MAKE_PTR(bufHdr->data));
+    }
+
+    if (isLocalBuf)
+       return (BufferDescriptorGetBuffer(bufHdr));
+
+    /* lock buffer manager again to update IO IN PROGRESS */
+    SpinAcquire(BufMgrLock);
+    
+    if (status == SM_FAIL) {
+       /* IO Failed.  cleanup the data structures and go home */
+       
+       if (! BufTableDelete(bufHdr)) {
+           SpinRelease(BufMgrLock);
+           elog(FATAL,"BufRead: buffer table broken after IO error\n");
+       }
+       /* remember that BufferAlloc() pinned the buffer */
+       UnpinBuffer(bufHdr);
+       
+       /* 
+        * Have to reset the flag so that anyone waiting for
+        * the buffer can tell that the contents are invalid.
+        */
+       bufHdr->flags |= BM_IO_ERROR;
+       
+    } else {
+       /* IO Succeeded.  clear the flags, finish buffer update */
+       
+       bufHdr->flags &= ~(BM_IO_ERROR | BM_IO_IN_PROGRESS);
+    }
+    
+    /* If anyone was waiting for IO to complete, wake them up now */
+#ifdef HAS_TEST_AND_SET
+    S_UNLOCK(&(bufHdr->io_in_progress_lock));
+#else
+    if (bufHdr->refcount > 1)
+       SignalIO(bufHdr);
+#endif
+    
+    SpinRelease(BufMgrLock);
+    
+    return(BufferDescriptorGetBuffer(bufHdr));
+}
+
+/*
+ * BufferAlloc -- Get a buffer from the buffer pool but dont
+ *     read it.
+ *
+ * Returns: descriptor for buffer
+ *
+ * When this routine returns, the BufMgrLock is guaranteed NOT be held.
+ */
+static BufferDesc *
+BufferAlloc(Relation reln,
+           BlockNumber blockNum,
+           bool        *foundPtr,
+           bool bufferLockHeld)
+{
+    BufferDesc                 *buf, *buf2;      
+    BufferTag          newTag;  /* identity of requested block */
+    bool               inProgress; /* buffer undergoing IO */
+    bool               newblock = FALSE;
+    
+    /* create a new tag so we can lookup the buffer */
+    /* assume that the relation is already open */
+    if (blockNum == P_NEW) {
+       newblock = TRUE;
+       blockNum = smgrnblocks(reln->rd_rel->relsmgr, reln);
+    }
+    
+    INIT_BUFFERTAG(&newTag,reln,blockNum);
+    
+    if (!bufferLockHeld)
+       SpinAcquire(BufMgrLock);
+    
+    /* see if the block is in the buffer pool already */
+    buf = BufTableLookup(&newTag);
+    if (buf != NULL) {
+       /* Found it.  Now, (a) pin the buffer so no
+        * one steals it from the buffer pool, 
+        * (b) check IO_IN_PROGRESS, someone may be
+        * faulting the buffer into the buffer pool.
+        */
+       
+       PinBuffer(buf);
+       inProgress = (buf->flags & BM_IO_IN_PROGRESS);
+       
+       *foundPtr = TRUE;
+       if (inProgress) {
+           WaitIO(buf, BufMgrLock);
+           if (buf->flags & BM_IO_ERROR) {
+               /* wierd race condition: 
+                *
+                * We were waiting for someone else to read the buffer.  
+                * While we were waiting, the reader boof'd in some
+                *  way, so the contents of the buffer are still
+                * invalid.  By saying that we didn't find it, we can
+                * make the caller reinitialize the buffer.  If two
+                * processes are waiting for this block, both will
+                * read the block.  The second one to finish may overwrite 
+                * any updates made by the first.  (Assume higher level
+                * synchronization prevents this from happening).
+                *
+                * This is never going to happen, don't worry about it.
+                */
+               *foundPtr = FALSE;
+           }
+       }
+#ifdef BMTRACE
+       _bm_trace((reln->rd_rel->relisshared ? 0 : MyDatabaseId), reln->rd_id, blockNum, BufferDescriptorGetBuffer(buf), BMT_ALLOCFND);
+#endif /* BMTRACE */
+       
+       SpinRelease(BufMgrLock);
+       
+       return(buf);
+    }
+    
+    *foundPtr = FALSE;
+    
+    /*
+     * Didn't find it in the buffer pool.  We'll have
+     * to initialize a new buffer.  First, grab one from
+     * the free list.  If it's dirty, flush it to disk.
+     * Remember to unlock BufMgr spinlock while doing the IOs.
+     */
+    inProgress = FALSE;
+    for (buf = (BufferDesc *) NULL; buf == (BufferDesc *) NULL; ) {
+       
+       /* GetFreeBuffer will abort if it can't find a free buffer */
+       buf = GetFreeBuffer();
+       
+       /*
+        * There should be exactly one pin on the buffer after
+        * it is allocated -- ours.  If it had a pin it wouldn't
+        * have been on the free list.  No one else could have
+        * pinned it between GetFreeBuffer and here because we
+        * have the BufMgrLock.
+        */
+       Assert(buf->refcount == 0);
+       buf->refcount = 1;             
+       PrivateRefCount[BufferDescriptorGetBuffer(buf) - 1] = 1;
+       
+       if (buf->flags & BM_DIRTY) {
+           /*
+            * Set BM_IO_IN_PROGRESS to keep anyone from doing anything
+            * with the contents of the buffer while we write it out.
+            * We don't really care if they try to read it, but if they
+            * can complete a BufferAlloc on it they can then scribble
+            * into it, and we'd really like to avoid that while we are
+            * flushing the buffer.  Setting this flag should block them
+            * in WaitIO until we're done.
+            */
+           inProgress = TRUE;
+           buf->flags |= BM_IO_IN_PROGRESS; 
+#ifdef HAS_TEST_AND_SET
+           /*
+            * All code paths that acquire this lock pin the buffer
+            * first; since no one had it pinned (it just came off the
+            * free list), no one else can have this lock.
+            */
+           Assert(S_LOCK_FREE(&(buf->io_in_progress_lock)));
+           S_LOCK(&(buf->io_in_progress_lock));
+#endif /* HAS_TEST_AND_SET */
+           
+           /*
+            * Write the buffer out, being careful to release BufMgrLock
+            * before starting the I/O.
+            *
+            * This #ifndef is here because a few extra semops REALLY kill
+            * you on machines that don't have spinlocks.  If you don't
+            * operate with much concurrency, well...
+            */
+           (void) BufferReplace(buf, true);
+           BufferFlushCount++;
+#ifndef OPTIMIZE_SINGLE
+           SpinAcquire(BufMgrLock); 
+#endif /* OPTIMIZE_SINGLE */
+           
+           /*
+            * Somebody could have pinned the buffer while we were
+            * doing the I/O and had given up the BufMgrLock (though
+            * they would be waiting for us to clear the BM_IO_IN_PROGRESS
+            * flag).  That's why this is a loop -- if so, we need to clear
+            * the I/O flags, remove our pin and start all over again.
+            *
+            * People may be making buffers free at any time, so there's
+            * no reason to think that we have an immediate disaster on
+            * our hands.
+            */
+           if (buf->refcount > 1) {
+               inProgress = FALSE;
+               buf->flags &= ~BM_IO_IN_PROGRESS;
+#ifdef HAS_TEST_AND_SET
+               S_UNLOCK(&(buf->io_in_progress_lock));
+#else /* !HAS_TEST_AND_SET */
+               if (buf->refcount > 1)
+                   SignalIO(buf);
+#endif /* !HAS_TEST_AND_SET */
+               PrivateRefCount[BufferDescriptorGetBuffer(buf) - 1] = 0;
+               buf->refcount--;
+               buf = (BufferDesc *) NULL;
+           }
+
+           /*
+            * Somebody could have allocated another buffer for the
+            * same block we are about to read in. (While we flush out
+            * the dirty buffer, we don't hold the lock and someone could
+            * have allocated another buffer for the same block. The problem
+            * is we haven't gotten around to insert the new tag into
+            * the buffer table. So we need to check here.      -ay 3/95
+            */
+           buf2 = BufTableLookup(&newTag);
+           if (buf2 != NULL) {
+               /* Found it. Someone has already done what we're about
+                * to do. We'll just handle this as if it were found in
+                * the buffer pool in the first place.
+                */
+               
+               PinBuffer(buf2);
+               inProgress = (buf2->flags & BM_IO_IN_PROGRESS);
+               
+               *foundPtr = TRUE;
+               if (inProgress) {
+                   WaitIO(buf2, BufMgrLock);
+                   if (buf2->flags & BM_IO_ERROR) {
+                       *foundPtr = FALSE;
+                   }
+               }
+               
+#ifdef HAS_TEST_AND_SET
+               S_UNLOCK(&(buf->io_in_progress_lock));
+#else /* !HAS_TEST_AND_SET */
+               if (buf->refcount > 1)
+                   SignalIO(buf);
+#endif /* !HAS_TEST_AND_SET */
+               
+               /* give up the buffer since we don't need it any more */
+               buf->refcount--;
+               PrivateRefCount[BufferDescriptorGetBuffer(buf) - 1] = 0;
+               AddBufferToFreelist(buf);
+               buf->flags |= BM_FREE;
+               buf->flags &= ~BM_DIRTY;
+               buf->flags &= ~BM_IO_IN_PROGRESS;
+               
+               SpinRelease(BufMgrLock);
+               
+               return(buf2);
+           }
+       }
+    }
+    /*
+     * At this point we should have the sole pin on a non-dirty
+     * buffer and we may or may not already have the BM_IO_IN_PROGRESS
+     * flag set.
+     */
+    
+    /* 
+     * Change the name of the buffer in the lookup table:
+     *  
+     * Need to update the lookup table before the read starts.
+     * If someone comes along looking for the buffer while
+     * we are reading it in, we don't want them to allocate
+     * a new buffer.  For the same reason, we didn't want
+     * to erase the buf table entry for the buffer we were
+     * writing back until now, either.
+     */
+    
+    if (! BufTableDelete(buf)) {
+       SpinRelease(BufMgrLock);
+       elog(FATAL,"buffer wasn't in the buffer table\n");
+
+    }
+    
+    if (buf->flags & BM_DIRTY) {
+       /* must clear flag first because of wierd race 
+        * condition described below.  
+        */
+       buf->flags &= ~BM_DIRTY;
+    }
+    
+    /* record the database name and relation name for this buffer */
+    buf->sb_relname = pstrdup(reln->rd_rel->relname.data);
+    buf->sb_dbname = pstrdup(GetDatabaseName());
+    
+    /* remember which storage manager is responsible for it */
+    buf->bufsmgr = reln->rd_rel->relsmgr;
+    
+    INIT_BUFFERTAG(&(buf->tag),reln,blockNum);
+    if (! BufTableInsert(buf)) {
+       SpinRelease(BufMgrLock);
+       elog(FATAL,"Buffer in lookup table twice \n");
+    } 
+    
+    /* Buffer contents are currently invalid.  Have
+     * to mark IO IN PROGRESS so no one fiddles with
+     * them until the read completes.  If this routine
+     * has been called simply to allocate a buffer, no
+     * io will be attempted, so the flag isnt set.
+     */
+    if (!inProgress) {
+       buf->flags |= BM_IO_IN_PROGRESS; 
+#ifdef HAS_TEST_AND_SET
+       Assert(S_LOCK_FREE(&(buf->io_in_progress_lock)));
+       S_LOCK(&(buf->io_in_progress_lock));
+#endif /* HAS_TEST_AND_SET */
+    }
+    
+#ifdef BMTRACE
+    _bm_trace((reln->rd_rel->relisshared ? 0 : MyDatabaseId), reln->rd_id, blockNum, BufferDescriptorGetBuffer(buf), BMT_ALLOCNOTFND);
+#endif /* BMTRACE */
+    
+    SpinRelease(BufMgrLock);
+    
+    return (buf);
+}
+
+/*
+ * WriteBuffer--
+ *
+ *     Pushes buffer contents to disk if LateWrite is
+ * not set.  Otherwise, marks contents as dirty.  
+ *
+ * Assume that buffer is pinned.  Assume that reln is
+ *     valid.
+ *
+ * Side Effects:
+ *     Pin count is decremented.
+ */
+
+#undef WriteBuffer
+
+int
+WriteBuffer(Buffer buffer)
+{
+    BufferDesc *bufHdr;
+
+    if (! LateWrite) {
+       return(FlushBuffer(buffer));
+    } else {
+
+       if (BufferIsLocal(buffer))
+           return WriteLocalBuffer(buffer, TRUE);
+    
+       if (BAD_BUFFER_ID(buffer))
+           return(FALSE);
+
+       bufHdr = &BufferDescriptors[buffer-1];
+       
+       SpinAcquire(BufMgrLock);
+       Assert(bufHdr->refcount > 0);
+       bufHdr->flags |= BM_DIRTY; 
+       UnpinBuffer(bufHdr);
+       SpinRelease(BufMgrLock);
+    }
+    return(TRUE);
+} 
+
+void
+WriteBuffer_Debug(char *file, int line, Buffer buffer)
+{
+    WriteBuffer(buffer);
+    if (ShowPinTrace && BufferIsLocal(buffer) && is_userbuffer(buffer)) {
+       BufferDesc *buf;
+       buf = &BufferDescriptors[buffer-1];
+       fprintf(stderr, "UNPIN(WR) %ld relname = %s, blockNum = %d, \
+refcount = %ld, file: %s, line: %d\n",
+               buffer, buf->sb_relname, buf->tag.blockNum,
+               PrivateRefCount[buffer - 1], file, line);
+    }
+}
+
+/*
+ *  DirtyBufferCopy() -- Copy a given dirty buffer to the requested
+ *                      destination.
+ *
+ *     We treat this as a write.  If the requested buffer is in the pool
+ *     and is dirty, we copy it to the location requested and mark it
+ *     clean.  This routine supports the Sony jukebox storage manager,
+ *     which agrees to take responsibility for the data once we mark
+ *     it clean.
+ *
+ *  NOTE: used by sony jukebox code in postgres 4.2   - ay 2/95
+ */
+void
+DirtyBufferCopy(Oid dbid, Oid relid, BlockNumber blkno, char *dest)
+{
+    BufferDesc *buf;
+    BufferTag btag;
+    
+    btag.relId.relId = relid;
+    btag.relId.dbId = dbid;
+    btag.blockNum = blkno;
+    
+    SpinAcquire(BufMgrLock);
+    buf = BufTableLookup(&btag);
+    
+    if (buf == (BufferDesc *) NULL
+       || !(buf->flags & BM_DIRTY)
+       || !(buf->flags & BM_VALID)) {
+       SpinRelease(BufMgrLock);
+       return;
+    }
+    
+    /* hate to do this holding the lock, but release and reacquire is slower */
+    memmove(dest, (char *) MAKE_PTR(buf->data), BLCKSZ);
+    
+    buf->flags &= ~BM_DIRTY;
+    
+    SpinRelease(BufMgrLock);
+}
+
+/*
+ * FlushBuffer -- like WriteBuffer, but force the page to disk.
+ *
+ * 'buffer' is known to be dirty/pinned, so there should not be a
+ * problem reading the BufferDesc members without the BufMgrLock
+ * (nobody should be able to change tags, flags, etc. out from under
+ * us).
+ */
+static int
+FlushBuffer(Buffer buffer)
+{
+    BufferDesc *bufHdr;
+
+    if (BufferIsLocal(buffer))
+       return FlushLocalBuffer(buffer);
+           
+    if (BAD_BUFFER_ID(buffer))
+       return (STATUS_ERROR);
+    
+    bufHdr = &BufferDescriptors[buffer-1];
+    
+    if (!BufferReplace(bufHdr, false)) {
+       elog(WARN, "FlushBuffer: cannot flush %d", bufHdr->tag.blockNum);
+       return (STATUS_ERROR);
+    }
+    
+    SpinAcquire(BufMgrLock); 
+    bufHdr->flags &= ~BM_DIRTY; 
+    UnpinBuffer(bufHdr);
+    SpinRelease(BufMgrLock);
+    
+    return(STATUS_OK);
+}
+
+/*
+ * WriteNoReleaseBuffer -- like WriteBuffer, but do not unpin the buffer
+ *                        when the operation is complete.
+ *
+ *     We know that the buffer is for a relation in our private cache,
+ *     because this routine is called only to write out buffers that
+ *     were changed by the executing backend.
+ */
+int
+WriteNoReleaseBuffer(Buffer buffer)
+{
+    BufferDesc *bufHdr;
+    
+    if (! LateWrite) {
+       return(FlushBuffer(buffer));
+    } else {
+
+       if (BufferIsLocal(buffer))
+           return WriteLocalBuffer(buffer, FALSE);
+           
+       if (BAD_BUFFER_ID(buffer))
+           return (STATUS_ERROR);
+
+       bufHdr = &BufferDescriptors[buffer-1];
+       
+       SpinAcquire(BufMgrLock);
+       bufHdr->flags |= BM_DIRTY; 
+       SpinRelease(BufMgrLock);
+    }
+    return(STATUS_OK);
+}
+
+
+#undef ReleaseAndReadBuffer
+/*
+ * ReleaseAndReadBuffer -- combine ReleaseBuffer() and ReadBuffer()
+ *     so that only one semop needs to be called.
+ *
+ */
+Buffer
+ReleaseAndReadBuffer(Buffer buffer,
+                    Relation relation,
+                    BlockNumber blockNum)
+{
+    BufferDesc *bufHdr;
+    Buffer retbuf;
+
+    if (BufferIsLocal(buffer)) {
+       Assert(LocalRefCount[-buffer - 1] > 0);
+       LocalRefCount[-buffer - 1]--;
+    } else {
+       if (BufferIsValid(buffer)) {
+           bufHdr = &BufferDescriptors[buffer-1];
+           Assert(PrivateRefCount[buffer - 1] > 0);
+           PrivateRefCount[buffer - 1]--;
+           if (PrivateRefCount[buffer - 1] == 0 &&
+               LastRefCount[buffer - 1] == 0) {
+               /* only release buffer if it is not pinned in previous ExecMain
+                  level */
+               SpinAcquire(BufMgrLock);
+               bufHdr->refcount--;
+               if (bufHdr->refcount == 0) {
+                   AddBufferToFreelist(bufHdr);
+                   bufHdr->flags |= BM_FREE;
+               }
+               retbuf = ReadBufferWithBufferLock(relation, blockNum, true);
+               return retbuf;
+           }
+       }
+    }
+
+    return (ReadBuffer(relation, blockNum));
+}
+
+/*
+ * BufferSync -- Flush all dirty buffers in the pool.
+ *
+ *     This is called at transaction commit time.  It does the wrong thing,
+ *     right now.  We should flush only our own changes to stable storage,
+ *     and we should obey the lock protocol on the buffer manager metadata
+ *     as we do it.  Also, we need to be sure that no other transaction is
+ *     modifying the page as we flush it.  This is only a problem for objects
+ *     that use a non-two-phase locking protocol, like btree indices.  For
+ *     those objects, we would like to set a write lock for the duration of
+ *     our IO.  Another possibility is to code updates to btree pages
+ *     carefully, so that writing them out out of order cannot cause
+ *     any unrecoverable errors.
+ *
+ *     I don't want to think hard about this right now, so I will try
+ *     to come back to it later.
+ */
+static void
+BufferSync()
+{ 
+    int i;
+    Oid bufdb;
+    Oid bufrel;
+    Relation reln;
+    BufferDesc *bufHdr;
+    int status;
+    
+    SpinAcquire(BufMgrLock);
+    for (i=0, bufHdr = BufferDescriptors; i < NBuffers; i++, bufHdr++) {
+       if ((bufHdr->flags & BM_VALID) && (bufHdr->flags & BM_DIRTY)) {
+           bufdb = bufHdr->tag.relId.dbId;
+           bufrel = bufHdr->tag.relId.relId;
+           if (bufdb == MyDatabaseId || bufdb == (Oid) 0) {
+               reln = RelationIdCacheGetRelation(bufrel);
+               
+               /*
+                *  If we didn't have the reldesc in our local cache, flush this
+                *  page out using the 'blind write' storage manager routine.  If
+                *  we did find it, use the standard interface.
+                */
+               
+#ifndef OPTIMIZE_SINGLE
+               SpinRelease(BufMgrLock);
+#endif /* OPTIMIZE_SINGLE */
+               if (reln == (Relation) NULL) {
+                   status = smgrblindwrt(bufHdr->bufsmgr, bufHdr->sb_dbname,
+                                         bufHdr->sb_relname, bufdb, bufrel,
+                                         bufHdr->tag.blockNum,
+                                         (char *) MAKE_PTR(bufHdr->data));
+               } else {
+                   status = smgrwrite(bufHdr->bufsmgr, reln,
+                                      bufHdr->tag.blockNum,
+                                      (char *) MAKE_PTR(bufHdr->data));
+               }
+#ifndef OPTIMIZE_SINGLE
+               SpinAcquire(BufMgrLock);
+#endif /* OPTIMIZE_SINGLE */
+               
+               if (status == SM_FAIL) {
+                   elog(WARN, "cannot write %d for %16s",
+                        bufHdr->tag.blockNum, bufHdr->sb_relname);
+               }
+               
+               bufHdr->flags &= ~BM_DIRTY;
+               if (reln != (Relation)NULL)
+                   RelationDecrementReferenceCount(reln);
+           }
+       }
+    }
+    SpinRelease(BufMgrLock);
+
+    LocalBufferSync();
+}
+
+
+/*
+ * WaitIO -- Block until the IO_IN_PROGRESS flag on 'buf'
+ *     is cleared.  Because IO_IN_PROGRESS conflicts are
+ *     expected to be rare, there is only one BufferIO
+ *     lock in the entire system.  All processes block
+ *     on this semaphore when they try to use a buffer
+ *     that someone else is faulting in.  Whenever a
+ *     process finishes an IO and someone is waiting for
+ *     the buffer, BufferIO is signaled (SignalIO).  All
+ *     waiting processes then wake up and check to see
+ *     if their buffer is now ready.  This implementation
+ *     is simple, but efficient enough if WaitIO is
+ *     rarely called by multiple processes simultaneously.
+ *
+ *  ProcSleep atomically releases the spinlock and goes to
+ *     sleep.
+ *
+ *  Note: there is an easy fix if the queue becomes long.
+ *     save the id of the buffer we are waiting for in
+ *     the queue structure.  That way signal can figure
+ *     out which proc to wake up.
+ */
+#ifdef HAS_TEST_AND_SET
+static void
+WaitIO(BufferDesc *buf, SPINLOCK spinlock)
+{
+    SpinRelease(spinlock);
+    S_LOCK(&(buf->io_in_progress_lock));
+    S_UNLOCK(&(buf->io_in_progress_lock));
+    SpinAcquire(spinlock);
+}
+
+#else /* HAS_TEST_AND_SET */
+IpcSemaphoreId        WaitIOSemId;
+
+static void
+WaitIO(BufferDesc *buf, SPINLOCK spinlock)
+{
+    bool       inProgress;
+    
+    for (;;) {
+       
+       /* wait until someone releases IO lock */
+       (*NWaitIOBackendP)++;
+       SpinRelease(spinlock);
+       IpcSemaphoreLock(WaitIOSemId, 0, 1);
+       SpinAcquire(spinlock);
+       inProgress = (buf->flags & BM_IO_IN_PROGRESS);
+       if (!inProgress) break;
+    }
+}
+
+/*
+ * SignalIO --
+ */
+static void
+SignalIO(BufferDesc *buf)
+{
+    /* somebody better be waiting. */
+    Assert( buf->refcount > 1);
+    IpcSemaphoreUnlock(WaitIOSemId, 0, *NWaitIOBackendP);
+    *NWaitIOBackendP = 0;
+}
+#endif /* HAS_TEST_AND_SET */
+
+long NDirectFileRead;  /* some I/O's are direct file access.  bypass bufmgr */
+long NDirectFileWrite;   /* e.g., I/O in psort and hashjoin.                */
+
+void
+PrintBufferUsage(FILE *statfp)
+{
+    float hitrate;
+    
+    if (ReadBufferCount==0)
+       hitrate = 0.0;
+    else
+       hitrate = (float)BufferHitCount * 100.0/ReadBufferCount;
+    
+    fprintf(statfp, "!\t%ld blocks read, %ld blocks written, buffer hit rate = %.2f%%\n", 
+           ReadBufferCount - BufferHitCount + NDirectFileRead,
+           BufferFlushCount + NDirectFileWrite,
+           hitrate);
+}
+
+void
+ResetBufferUsage()
+{
+    BufferHitCount = 0;
+    ReadBufferCount = 0;
+    BufferFlushCount = 0;
+    NDirectFileRead = 0;
+    NDirectFileWrite = 0;
+}
+
+/* ----------------------------------------------
+ *     ResetBufferPool
+ *
+ *     this routine is supposed to be called when a transaction aborts.
+ *     it will release all the buffer pins held by the transaciton.
+ *
+ * ----------------------------------------------
+ */
+void
+ResetBufferPool()
+{
+    register int i;
+    for (i=1; i<=NBuffers; i++) {
+       if (BufferIsValid(i)) {
+           while(PrivateRefCount[i - 1] > 0) {
+               ReleaseBuffer(i);
+           }
+       }
+       LastRefCount[i - 1] = 0;
+    }
+
+    ResetLocalBufferPool();
+}
+
+/* -----------------------------------------------
+ *     BufferPoolCheckLeak
+ *
+ *     check if there is buffer leak
+ *
+ * -----------------------------------------------
+ */
+int
+BufferPoolCheckLeak()
+{
+    register int i;
+    void PrintBufferDescs();
+    
+    for (i = 1; i <= NBuffers; i++) {
+       if (BufferIsValid(i)) {
+           elog(NOTICE, "buffer leak detected in BufferPoolCheckLeak()");
+           PrintBufferDescs();
+           return(1);
+       }
+    }
+    return(0);
+}
+
+/* ------------------------------------------------
+ *     FlushBufferPool
+ *
+ *     flush all dirty blocks in buffer pool to disk
+ *
+ * ------------------------------------------------
+ */
+void
+FlushBufferPool(int StableMainMemoryFlag)
+{
+    if (!StableMainMemoryFlag) {
+        BufferSync();
+       smgrcommit();
+    }
+}
+
+/*
+ * BufferIsValid --
+ *     True iff the refcnt of the local buffer is > 0
+ * Note:
+ *     BufferIsValid(InvalidBuffer) is False.
+ *     BufferIsValid(UnknownBuffer) is False.
+ */
+bool
+BufferIsValid(Buffer bufnum)
+{
+    if (BufferIsLocal(bufnum)) 
+       return (bufnum >= -NLocBuffer && LocalRefCount[-bufnum - 1] > 0);
+    
+    if (BAD_BUFFER_ID(bufnum))
+        return(false);
+
+    return ((bool)(PrivateRefCount[bufnum - 1] > 0));
+}
+
+/*
+ * BufferGetBlockNumber --
+ *     Returns the block number associated with a buffer.
+ *
+ * Note:
+ *     Assumes that the buffer is valid.
+ */
+BlockNumber
+BufferGetBlockNumber(Buffer buffer)
+{
+    Assert(BufferIsValid(buffer));
+
+    /* XXX should be a critical section */
+    if (BufferIsLocal(buffer))
+       return (LocalBufferDescriptors[-buffer-1].tag.blockNum);
+    else
+       return (BufferDescriptors[buffer-1].tag.blockNum);
+}
+
+/*
+ * BufferGetRelation --
+ *     Returns the relation desciptor associated with a buffer.
+ *
+ * Note:
+ *     Assumes buffer is valid.
+ */
+Relation
+BufferGetRelation(Buffer buffer)
+{
+    Relation    relation;
+    Oid                relid;
+
+    Assert(BufferIsValid(buffer));
+    Assert(!BufferIsLocal(buffer));    /* not supported for local buffers */
+    
+    /* XXX should be a critical section */
+    relid = LRelIdGetRelationId(BufferDescriptors[buffer-1].tag.relId);
+    relation = RelationIdGetRelation(relid);
+    
+    RelationDecrementReferenceCount(relation);
+    
+    if (RelationHasReferenceCountZero(relation)) {
+       /*
+         elog(NOTICE, "BufferGetRelation: 0->1");
+         */
+       
+        RelationIncrementReferenceCount(relation);
+    }
+    
+    return (relation);
+}
+
+/*
+ * BufferReplace
+ *
+ * Flush the buffer corresponding to 'bufHdr'
+ *
+ * Assumes that the BufMgrLock has NOT been acquired.
+ */
+static int
+BufferReplace(BufferDesc *bufHdr, bool bufferLockHeld)
+{
+    Relation   reln;
+    Oid        bufdb, bufrel;
+    int                status;
+    
+    if (!bufferLockHeld)
+       SpinAcquire(BufMgrLock);
+    
+    /*
+     * first try to find the reldesc in the cache, if no luck,
+     * don't bother to build the reldesc from scratch, just do
+     * a blind write.
+     */
+    
+    bufdb = bufHdr->tag.relId.dbId;
+    bufrel = bufHdr->tag.relId.relId;
+    
+    if (bufdb == MyDatabaseId || bufdb == (Oid) NULL)
+       reln = RelationIdCacheGetRelation(bufrel);
+    else
+       reln = (Relation) NULL;
+    
+    SpinRelease(BufMgrLock); 
+    
+    if (reln != (Relation) NULL) {
+       status = smgrflush(bufHdr->bufsmgr, reln, bufHdr->tag.blockNum,
+                          (char *) MAKE_PTR(bufHdr->data));
+    } else {
+       
+       /* blind write always flushes */
+       status = smgrblindwrt(bufHdr->bufsmgr, bufHdr->sb_dbname,
+                             bufHdr->sb_relname, bufdb, bufrel,
+                             bufHdr->tag.blockNum,
+                             (char *) MAKE_PTR(bufHdr->data));
+    }
+    
+    if (status == SM_FAIL)
+       return (FALSE);
+    
+    return (TRUE);
+}
+
+/*
+ * RelationGetNumberOfBlocks --
+ *     Returns the buffer descriptor associated with a page in a relation.
+ *
+ * Note:
+ *      XXX may fail for huge relations.
+ *      XXX should be elsewhere.
+ *      XXX maybe should be hidden
+ */
+BlockNumber
+RelationGetNumberOfBlocks(Relation relation)
+{
+    return
+       ((relation->rd_islocal) ? relation->rd_nblocks :
+           smgrnblocks(relation->rd_rel->relsmgr, relation));
+}
+
+/*
+ * BufferGetBlock --
+ *     Returns a reference to a disk page image associated with a buffer.
+ *
+ * Note:
+ *     Assumes buffer is valid.
+ */
+Block
+BufferGetBlock(Buffer buffer)
+{
+    Assert(BufferIsValid(buffer));
+
+    if (BufferIsLocal(buffer))
+       return((Block)MAKE_PTR(LocalBufferDescriptors[-buffer-1].data));
+    else
+       return((Block)MAKE_PTR(BufferDescriptors[buffer-1].data));
+}
+
+/* ---------------------------------------------------------------------
+ *      ReleaseTmpRelBuffers
+ *
+ *      this function unmarks all the dirty pages of a temporary
+ *      relation in the buffer pool so that at the end of transaction
+ *      these pages will not be flushed.
+ *      XXX currently it sequentially searches the buffer pool, should be
+ *      changed to more clever ways of searching.
+ * --------------------------------------------------------------------
+ */
+void
+ReleaseTmpRelBuffers(Relation tempreldesc)
+{
+    register int i;
+    int holding = 0;
+    BufferDesc *buf;
+    
+    for (i=1; i<=NBuffers; i++) {
+       buf = &BufferDescriptors[i-1];
+       if (!holding) {
+           SpinAcquire(BufMgrLock);
+           holding = 1;
+       }
+        if ((buf->flags & BM_DIRTY) &&
+            (buf->tag.relId.dbId == MyDatabaseId) &&
+            (buf->tag.relId.relId == tempreldesc->rd_id)) {
+            buf->flags &= ~BM_DIRTY;
+            if (!(buf->flags & BM_FREE)) {
+               SpinRelease(BufMgrLock);
+               holding = 0;
+               ReleaseBuffer(i);
+           }
+       }
+    }
+    if (holding)
+       SpinRelease(BufMgrLock);
+}
+
+/* ---------------------------------------------------------------------
+ *      DropBuffers
+ *
+ *     This function marks all the buffers in the buffer cache for a
+ *     particular database as clean.  This is used when we destroy a
+ *     database, to avoid trying to flush data to disk when the directory
+ *     tree no longer exists.
+ *
+ *     This is an exceedingly non-public interface.
+ * --------------------------------------------------------------------
+ */
+void
+DropBuffers(Oid dbid)
+{
+    register int i;
+    BufferDesc *buf;
+    
+    SpinAcquire(BufMgrLock);
+    for (i=1; i<=NBuffers; i++) {
+       buf = &BufferDescriptors[i-1];
+        if ((buf->tag.relId.dbId == dbid) && (buf->flags & BM_DIRTY)) {
+            buf->flags &= ~BM_DIRTY;
+        }
+    }
+    SpinRelease(BufMgrLock);
+}
+
+/* -----------------------------------------------------------------
+ *     PrintBufferDescs
+ *
+ *     this function prints all the buffer descriptors, for debugging
+ *     use only.
+ * -----------------------------------------------------------------
+ */
+void
+PrintBufferDescs()
+{
+    int i;
+    BufferDesc *buf = BufferDescriptors;
+
+    if (IsUnderPostmaster) {
+       SpinAcquire(BufMgrLock);
+       for (i = 0; i < NBuffers; ++i, ++buf) {
+           elog(NOTICE, "[%02d] (freeNext=%d, freePrev=%d, relname=%.*s, \
+blockNum=%d, flags=0x%x, refcount=%d %d)",
+                i, buf->freeNext, buf->freePrev, NAMEDATALEN,
+                &(buf->sb_relname), buf->tag.blockNum, buf->flags,
+                buf->refcount, PrivateRefCount[i]);
+       }
+       SpinRelease(BufMgrLock);
+    } else {
+       /* interactive backend */
+       for (i = 0; i < NBuffers; ++i, ++buf) {
+           printf("[%-2d] (%s, %d) flags=0x%x, refcnt=%d %ld)\n",
+                  i, buf->sb_relname, buf->tag.blockNum, 
+                  buf->flags, buf->refcount, PrivateRefCount[i]);
+       }
+    }
+}
+
+void
+PrintPinnedBufs()
+{
+    int i;
+    BufferDesc *buf = BufferDescriptors;
+    
+    SpinAcquire(BufMgrLock);
+    for (i = 0; i < NBuffers; ++i, ++buf) {
+       if (PrivateRefCount[i] > 0)
+           elog(NOTICE, "[%02d] (freeNext=%d, freePrev=%d, relname=%.*s, \
+blockNum=%d, flags=0x%x, refcount=%d %d)\n",
+                i, buf->freeNext, buf->freePrev, NAMEDATALEN, &(buf->sb_relname),
+                buf->tag.blockNum, buf->flags,
+                buf->refcount, PrivateRefCount[i]);
+    }
+    SpinRelease(BufMgrLock);
+}
+
+/*
+ * BufferPoolBlowaway
+ *
+ * this routine is solely for the purpose of experiments -- sometimes
+ * you may want to blowaway whatever is left from the past in buffer
+ * pool and start measuring some performance with a clean empty buffer
+ * pool.
+ */
+void
+BufferPoolBlowaway()
+{
+    register int i;
+    
+    BufferSync();
+    for (i=1; i<=NBuffers; i++) {
+        if (BufferIsValid(i)) {
+            while(BufferIsValid(i))
+                ReleaseBuffer(i);
+        }
+        BufTableDelete(&BufferDescriptors[i-1]);
+    }
+}
+
+#undef IncrBufferRefCount
+#undef ReleaseBuffer
+
+void
+IncrBufferRefCount(Buffer buffer)
+{
+    if (BufferIsLocal(buffer)) {
+       Assert(LocalRefCount[-buffer - 1] >= 0);
+       LocalRefCount[-buffer - 1]++;
+    } else {
+       Assert(!BAD_BUFFER_ID(buffer));
+       Assert(PrivateRefCount[buffer - 1] >= 0);
+       PrivateRefCount[buffer - 1]++;
+    }
+}
+
+/*
+ * ReleaseBuffer -- remove the pin on a buffer without
+ *     marking it dirty.
+ *
+ */
+int
+ReleaseBuffer(Buffer buffer)
+{
+    BufferDesc *bufHdr;
+    
+    if (BufferIsLocal(buffer)) {
+       Assert(LocalRefCount[-buffer - 1] > 0);
+       LocalRefCount[-buffer - 1]--;
+       return (STATUS_OK);
+    }
+    
+    if (BAD_BUFFER_ID(buffer))
+       return(STATUS_ERROR);
+
+    bufHdr = &BufferDescriptors[buffer-1];
+    
+    Assert(PrivateRefCount[buffer - 1] > 0);
+    PrivateRefCount[buffer - 1]--;
+    if (PrivateRefCount[buffer - 1] == 0 && LastRefCount[buffer - 1] == 0) {
+       /* only release buffer if it is not pinned in previous ExecMain
+          levels */
+       SpinAcquire(BufMgrLock);
+       bufHdr->refcount--;
+       if (bufHdr->refcount == 0) {
+           AddBufferToFreelist(bufHdr);
+           bufHdr->flags |= BM_FREE;
+       }
+       SpinRelease(BufMgrLock);
+    }
+    
+    return(STATUS_OK);
+}
+
+void
+IncrBufferRefCount_Debug(char *file, int line, Buffer buffer)
+{
+    IncrBufferRefCount(buffer);
+    if (ShowPinTrace && !BufferIsLocal(buffer) && is_userbuffer(buffer)) {
+        BufferDesc *buf = &BufferDescriptors[buffer-1];
+       
+        fprintf(stderr, "PIN(Incr) %ld relname = %s, blockNum = %d, \
+refcount = %ld, file: %s, line: %d\n",
+               buffer, buf->sb_relname, buf->tag.blockNum,
+               PrivateRefCount[buffer - 1], file, line);
+    }
+}
+
+void
+ReleaseBuffer_Debug(char *file, int line, Buffer buffer)
+{
+    ReleaseBuffer(buffer);
+    if (ShowPinTrace && !BufferIsLocal(buffer) && is_userbuffer(buffer)) {
+        BufferDesc *buf = &BufferDescriptors[buffer-1];
+       
+        fprintf(stderr, "UNPIN(Rel) %ld relname = %s, blockNum = %d, \
+refcount = %ld, file: %s, line: %d\n",
+               buffer, buf->sb_relname, buf->tag.blockNum,
+               PrivateRefCount[buffer - 1], file, line);
+    }
+}
+
+int
+ReleaseAndReadBuffer_Debug(char *file,
+                          int line,
+                          Buffer buffer,
+                          Relation relation,
+                          BlockNumber blockNum)
+{
+    bool bufferValid;
+    Buffer b;
+    
+    bufferValid = BufferIsValid(buffer);
+    b = ReleaseAndReadBuffer(buffer, relation, blockNum);
+    if (ShowPinTrace && bufferValid && BufferIsLocal(buffer)
+       && is_userbuffer(buffer)) {
+       BufferDesc *buf = &BufferDescriptors[buffer-1];
+       
+        fprintf(stderr, "UNPIN(Rel&Rd) %ld relname = %s, blockNum = %d, \
+refcount = %ld, file: %s, line: %d\n",
+               buffer, buf->sb_relname, buf->tag.blockNum,
+               PrivateRefCount[buffer - 1], file, line);
+    }
+    if (ShowPinTrace && BufferIsLocal(buffer) && is_userbuffer(buffer)) {
+       BufferDesc *buf = &BufferDescriptors[b-1];
+       
+        fprintf(stderr, "PIN(Rel&Rd) %ld relname = %s, blockNum = %d, \
+refcount = %ld, file: %s, line: %d\n",
+               b, buf->sb_relname, buf->tag.blockNum,
+               PrivateRefCount[b - 1], file, line);
+    }
+    return b;
+}
+
+#ifdef BMTRACE
+
+/*
+ *  trace allocations and deallocations in a circular buffer in
+ *  shared memory.  check the buffer before doing the allocation,
+ *  and die if there's anything fishy.
+ */
+
+_bm_trace(Oid dbId, Oid relId, int blkNo, int bufNo, int allocType)
+{
+    static int mypid = 0;
+    long start, cur;
+    bmtrace *tb;
+    
+    if (mypid == 0)
+       mypid = getpid();
+    
+    start = *CurTraceBuf;
+    
+    if (start > 0)
+       cur = start - 1;
+    else
+       cur = BMT_LIMIT - 1;
+    
+    for (;;) {
+       tb = &TraceBuf[cur];
+       if (tb->bmt_op != BMT_NOTUSED) {
+           if (tb->bmt_buf == bufNo) {
+               if ((tb->bmt_op == BMT_DEALLOC)
+                   || (tb->bmt_dbid == dbId && tb->bmt_relid == relId
+                       && tb->bmt_blkno == blkNo))
+                   goto okay;
+               
+               /* die holding the buffer lock */
+               _bm_die(dbId, relId, blkNo, bufNo, allocType, start, cur);
+           }
+       }
+       
+       if (cur == start)
+           goto okay;
+       
+       if (cur == 0)
+           cur = BMT_LIMIT - 1;
+       else
+           cur--;
+    }
+    
+ okay:
+    tb = &TraceBuf[start];
+    tb->bmt_pid = mypid;
+    tb->bmt_buf = bufNo;
+    tb->bmt_dbid = dbId;
+    tb->bmt_relid = relId;
+    tb->bmt_blkno = blkNo;
+    tb->bmt_op = allocType;
+    
+    *CurTraceBuf = (start + 1) % BMT_LIMIT;
+}
+
+_bm_die(Oid dbId, Oid relId, int blkNo, int bufNo,
+       int allocType, long start, long cur)
+{
+    FILE *fp;
+    bmtrace *tb;
+    int i;
+    
+    tb = &TraceBuf[cur];
+    
+    if ((fp = fopen("/tmp/death_notice", "w")) == (FILE *) NULL)
+       elog(FATAL, "buffer alloc trace error and can't open log file");
+    
+    fprintf(fp, "buffer alloc trace detected the following error:\n\n");
+    fprintf(fp, "    buffer %d being %s inconsistently with a previous %s\n\n",
+           bufNo, (allocType == BMT_DEALLOC ? "deallocated" : "allocated"),
+           (tb->bmt_op == BMT_DEALLOC ? "deallocation" : "allocation"));
+    
+    fprintf(fp, "the trace buffer contains:\n");
+    
+    i = start;
+    for (;;) {
+       tb = &TraceBuf[i];
+       if (tb->bmt_op != BMT_NOTUSED) {
+           fprintf(fp, "     [%3d]%spid %d buf %2d for <%d,%d,%d> ",
+                   i, (i == cur ? " ---> " : "\t"),
+                   tb->bmt_pid, tb->bmt_buf,
+                   tb->bmt_dbid, tb->bmt_relid, tb->bmt_blkno);
+           
+           switch (tb->bmt_op) {
+           case BMT_ALLOCFND:
+               fprintf(fp, "allocate (found)\n");
+               break;
+               
+           case BMT_ALLOCNOTFND:
+               fprintf(fp, "allocate (not found)\n");
+               break;
+               
+           case BMT_DEALLOC:
+               fprintf(fp, "deallocate\n");
+               break;
+               
+           default:
+               fprintf(fp, "unknown op type %d\n", tb->bmt_op);
+               break;
+           }
+       }
+       
+       i = (i + 1) % BMT_LIMIT;
+       if (i == start)
+           break;
+    }
+    
+    fprintf(fp, "\noperation causing error:\n");
+    fprintf(fp, "\tpid %d buf %d for <%d,%d,%d> ",
+           getpid(), bufNo, dbId, relId, blkNo);
+    
+    switch (allocType) {
+    case BMT_ALLOCFND:
+       fprintf(fp, "allocate (found)\n");
+       break;
+       
+    case BMT_ALLOCNOTFND:
+       fprintf(fp, "allocate (not found)\n");
+       break;
+       
+    case BMT_DEALLOC:
+       fprintf(fp, "deallocate\n");
+       break;
+       
+    default:
+       fprintf(fp, "unknown op type %d\n", allocType);
+       break;
+    }
+    
+    (void) fclose(fp);
+    
+    kill(getpid(), SIGILL);
+}
+
+#endif /* BMTRACE */
+
+void
+BufferRefCountReset(int *refcountsave)
+{
+    int i;
+    for (i=0; i<NBuffers; i++) {
+       refcountsave[i] = PrivateRefCount[i];
+       LastRefCount[i] += PrivateRefCount[i];
+       PrivateRefCount[i] = 0;
+    }
+}
+
+void
+BufferRefCountRestore(int *refcountsave)
+{
+    int i;
+    for (i=0; i<NBuffers; i++) {
+       PrivateRefCount[i] = refcountsave[i];
+       LastRefCount[i] -= refcountsave[i];
+       refcountsave[i] = 0;
+    }
+}
+
diff --git a/src/backend/storage/buffer/freelist.c b/src/backend/storage/buffer/freelist.c
new file mode 100644 (file)
index 0000000..6f19a1b
--- /dev/null
@@ -0,0 +1,285 @@
+/*-------------------------------------------------------------------------
+ *
+ * freelist.c--
+ *    routines for manipulating the buffer pool's replacement strategy
+ *    freelist.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * OLD COMMENTS
+ *
+ * Data Structures:
+ *     SharedFreeList is a circular queue.  Notice that this
+ *     is a shared memory queue so the next/prev "ptrs" are
+ *     buffer ids, not addresses.
+ *
+ * Sync: all routines in this file assume that the buffer
+ *     semaphore has been acquired by the caller.
+ */
+#include <stdio.h>
+#include "storage/bufmgr.h"
+#include "storage/buf_internals.h"     /* where declarations go */
+#include "storage/spin.h"
+#include "utils/elog.h"
+
+
+static BufferDesc      *SharedFreeList;
+
+/* only actually used in debugging.  The lock
+ * should be acquired before calling the freelist manager.
+ */
+extern SPINLOCK BufMgrLock;
+
+#define IsInQueue(bf) \
+    Assert((bf->freeNext != INVALID_DESCRIPTOR));\
+    Assert((bf->freePrev != INVALID_DESCRIPTOR));\
+    Assert((bf->flags & BM_FREE))
+
+#define NotInQueue(bf) \
+    Assert((bf->freeNext == INVALID_DESCRIPTOR));\
+    Assert((bf->freePrev == INVALID_DESCRIPTOR));\
+    Assert(! (bf->flags & BM_FREE))
+
+
+/*
+ * AddBufferToFreelist --  
+ *
+ * In theory, this is the only routine that needs to be changed
+ * if the buffer replacement strategy changes.  Just change
+ * the manner in which buffers are added to the freelist queue.
+ * Currently, they are added on an LRU basis.
+ */
+void
+AddBufferToFreelist(BufferDesc *bf)
+{
+#ifdef BMTRACE
+    _bm_trace(bf->tag.relId.dbId, bf->tag.relId.relId, bf->tag.blockNum,
+             BufferDescriptorGetBuffer(bf), BMT_DEALLOC);
+#endif /* BMTRACE */
+    NotInQueue(bf);
+    
+    /* change bf so it points to inFrontOfNew and its successor */
+    bf->freePrev = SharedFreeList->freePrev;
+    bf->freeNext = Free_List_Descriptor;
+    
+    /* insert new into chain */
+    BufferDescriptors[bf->freeNext].freePrev = bf->buf_id;
+    BufferDescriptors[bf->freePrev].freeNext = bf->buf_id;
+}
+
+#undef PinBuffer
+
+/*
+ * PinBuffer -- make buffer unavailable for replacement.
+ */
+void
+PinBuffer(BufferDesc *buf)
+{
+    long b;
+    
+    /* Assert (buf->refcount < 25); */
+    
+    if (buf->refcount == 0) {
+       IsInQueue(buf);
+       
+       /* remove from freelist queue */
+       BufferDescriptors[buf->freeNext].freePrev = buf->freePrev;
+       BufferDescriptors[buf->freePrev].freeNext = buf->freeNext;
+       buf->freeNext = buf->freePrev = INVALID_DESCRIPTOR;
+       
+       /* mark buffer as no longer free */
+       buf->flags &= ~BM_FREE;
+    } else {
+       NotInQueue(buf);
+    }
+    
+    b = BufferDescriptorGetBuffer(buf) - 1;
+    Assert(PrivateRefCount[b] >= 0);
+    if (PrivateRefCount[b] == 0 && LastRefCount[b] == 0)
+       buf->refcount++;
+    PrivateRefCount[b]++;
+}
+
+void
+PinBuffer_Debug(char *file, int line, BufferDesc *buf)
+{
+    PinBuffer(buf);
+    if (ShowPinTrace) {
+       Buffer buffer = BufferDescriptorGetBuffer(buf);
+       
+       fprintf(stderr, "PIN(Pin) %ld relname = %s, blockNum = %d, \
+refcount = %ld, file: %s, line: %d\n",
+               buffer, buf->sb_relname, buf->tag.blockNum,
+               PrivateRefCount[buffer - 1], file, line);
+    }
+}
+
+#undef UnpinBuffer
+
+/*
+ * UnpinBuffer -- make buffer available for replacement.
+ */
+void
+UnpinBuffer(BufferDesc *buf)
+{
+    long b = BufferDescriptorGetBuffer(buf) - 1;
+    
+    Assert(buf->refcount);
+    Assert(PrivateRefCount[b] > 0);
+    PrivateRefCount[b]--;
+    if (PrivateRefCount[b] == 0 && LastRefCount[b] == 0)
+       buf->refcount--;
+    NotInQueue(buf);
+    
+    if (buf->refcount == 0) {
+       AddBufferToFreelist(buf);
+       buf->flags |= BM_FREE;
+    } else {
+       /* do nothing */
+    }
+}
+
+void
+UnpinBuffer_Debug(char *file, int line, BufferDesc *buf)
+{
+    UnpinBuffer(buf);
+    if (ShowPinTrace) {
+       Buffer buffer = BufferDescriptorGetBuffer(buf);
+       
+       fprintf(stderr, "UNPIN(Unpin) %ld relname = %s, blockNum = %d, \
+refcount = %ld, file: %s, line: %d\n",
+               buffer, buf->sb_relname, buf->tag.blockNum,
+               PrivateRefCount[buffer - 1], file, line);
+    }
+}
+
+/*
+ * GetFreeBuffer() -- get the 'next' buffer from the freelist.
+ *
+ */
+BufferDesc *
+GetFreeBuffer()
+{
+    BufferDesc *buf;
+    
+    if (Free_List_Descriptor == SharedFreeList->freeNext) {
+       
+       /* queue is empty. All buffers in the buffer pool are pinned. */
+       elog(WARN,"out of free buffers: time to abort !\n");
+       return(NULL);
+    }
+    buf = &(BufferDescriptors[SharedFreeList->freeNext]);
+    
+    /* remove from freelist queue */
+    BufferDescriptors[buf->freeNext].freePrev = buf->freePrev;
+    BufferDescriptors[buf->freePrev].freeNext = buf->freeNext;
+    buf->freeNext = buf->freePrev = INVALID_DESCRIPTOR;
+    
+    buf->flags &= ~(BM_FREE);
+    
+    return(buf);
+}
+
+/*
+ * InitFreeList -- initialize the dummy buffer descriptor used
+ *     as a freelist head.
+ *
+ * Assume: All of the buffers are already linked in a circular
+ *     queue.   Only called by postmaster and only during 
+ *     initialization.
+ */
+void
+InitFreeList(bool init)
+{
+    SharedFreeList = &(BufferDescriptors[Free_List_Descriptor]);
+    
+    if (init) {
+       /* we only do this once, normally the postmaster */
+       SharedFreeList->data = INVALID_OFFSET;
+       SharedFreeList->flags = 0;
+       SharedFreeList->flags &= ~(BM_VALID | BM_DELETED | BM_FREE);
+       SharedFreeList->buf_id = Free_List_Descriptor;
+       
+       /* insert it into a random spot in the circular queue */
+       SharedFreeList->freeNext = BufferDescriptors[0].freeNext;
+       SharedFreeList->freePrev = 0;
+       BufferDescriptors[SharedFreeList->freeNext].freePrev = 
+           BufferDescriptors[SharedFreeList->freePrev].freeNext = 
+               Free_List_Descriptor;
+    }
+}
+
+
+/*
+ * print out the free list and check for breaks.
+ */
+void
+DBG_FreeListCheck(int nfree)
+{
+    int i;
+    BufferDesc *buf;
+    
+    buf = &(BufferDescriptors[SharedFreeList->freeNext]);
+    for (i=0;i<nfree;i++,buf = &(BufferDescriptors[buf->freeNext])) {
+       
+       if (! (buf->flags & (BM_FREE))){
+           if (buf != SharedFreeList) {
+               printf("\tfree list corrupted: %d flags %x\n",
+                      buf->buf_id,buf->flags);
+           } else  {
+               printf("\tfree list corrupted: too short -- %d not %d\n",
+                      i,nfree);
+               
+           }
+           
+           
+       }
+       if ((BufferDescriptors[buf->freeNext].freePrev != buf->buf_id) ||
+           (BufferDescriptors[buf->freePrev].freeNext != buf->buf_id)) {
+           printf("\tfree list links corrupted: %d %ld %ld\n",
+                  buf->buf_id,buf->freePrev,buf->freeNext);
+       }
+       
+    }
+    if (buf != SharedFreeList) {
+       printf("\tfree list corrupted: %d-th buffer is %d\n",
+              nfree,buf->buf_id);
+       
+    }
+}
+
+/*
+ * PrintBufferFreeList -
+ *    prints the buffer free list, for debugging
+ */
+void
+PrintBufferFreeList()
+{
+    BufferDesc *buf;
+
+    if (SharedFreeList->freeNext == Free_List_Descriptor) {
+       printf("free list is empty.\n");
+       return;
+    }
+    
+    buf = &(BufferDescriptors[SharedFreeList->freeNext]);
+    for (;;) {
+       int i = (buf - BufferDescriptors);
+       printf("[%-2d] (%s, %d) flags=0x%x, refcnt=%d %ld, nxt=%ld prv=%ld)\n",
+              i, buf->sb_relname, buf->tag.blockNum,
+              buf->flags, buf->refcount, PrivateRefCount[i],
+              buf->freeNext, buf->freePrev);
+       
+       if (buf->freeNext == Free_List_Descriptor)
+           break;
+
+       buf = &(BufferDescriptors[buf->freeNext]);
+    }
+}
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
new file mode 100644 (file)
index 0000000..5e54669
--- /dev/null
@@ -0,0 +1,284 @@
+/*-------------------------------------------------------------------------
+ *
+ * localbuf.c--
+ *    local buffer manager. Fast buffer manager for temporary tables
+ *    or special cases when the operation is not visible to other backends.
+ *
+ *    When a relation is being created, the descriptor will have rd_islocal
+ *    set to indicate that the local buffer manager should be used. During
+ *    the same transaction the relation is being created, any inserts or
+ *    selects from the newly created relation will use the local buffer
+ *    pool. rd_islocal is reset at the end of a transaction (commit/abort).
+ *    This is useful for queries like SELECT INTO TABLE and create index.
+ *
+ * Copyright (c) 1994-5, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <sys/file.h>
+#include <stdio.h>
+#include <math.h>
+#include <signal.h>
+
+/* declarations split between these three files */
+#include "storage/buf.h"
+#include "storage/buf_internals.h"
+#include "storage/bufmgr.h"
+
+#include "storage/fd.h"
+#include "storage/ipc.h"
+#include "storage/shmem.h"
+#include "storage/spin.h"
+#include "storage/smgr.h"
+#include "storage/lmgr.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/hsearch.h"
+#include "utils/elog.h"
+#include "utils/memutils.h"
+#include "executor/execdebug.h"        /* for NDirectFileRead */
+#include "catalog/catalog.h"
+
+int NLocBuffer = 64;
+BufferDesc *LocalBufferDescriptors = NULL;
+long *LocalRefCount = NULL;
+
+static int nextFreeLocalBuf = 0;
+
+/*#define LBDEBUG*/
+
+/*
+ * LocalBufferAlloc -
+ *    allocate a local buffer. We do round robin allocation for now.
+ */
+BufferDesc *
+LocalBufferAlloc(Relation reln, BlockNumber blockNum, bool *foundPtr)
+{
+    int i;
+    BufferDesc *bufHdr = (BufferDesc *) NULL;
+
+    if (blockNum == P_NEW) {
+       blockNum = reln->rd_nblocks;
+       reln->rd_nblocks++;
+    } 
+
+    /* a low tech search for now -- not optimized for scans */
+    for (i=0; i < NLocBuffer; i++) {
+       if (LocalBufferDescriptors[i].tag.relId.relId == reln->rd_id &&
+           LocalBufferDescriptors[i].tag.blockNum == blockNum) {
+
+#ifdef LBDEBUG
+           fprintf(stderr, "LB ALLOC (%d,%d) %d\n",
+                   reln->rd_id, blockNum, -i-1);
+#endif    
+           LocalRefCount[i]++;
+           *foundPtr = TRUE;
+           return &LocalBufferDescriptors[i];
+       }
+    }
+
+#ifdef LBDEBUG
+    fprintf(stderr, "LB ALLOC (%d,%d) %d\n",
+           reln->rd_id, blockNum, -nextFreeLocalBuf-1);
+#endif    
+    
+    /* need to get a new buffer (round robin for now) */
+    for(i=0; i < NLocBuffer; i++) {
+       int b = (nextFreeLocalBuf + i) % NLocBuffer;
+
+       if (LocalRefCount[b]==0) {
+           bufHdr = &LocalBufferDescriptors[b];
+           LocalRefCount[b]++;
+           nextFreeLocalBuf = (b + 1) % NLocBuffer;
+           break;
+       }
+    }
+    if (bufHdr==NULL)
+       elog(WARN, "no empty local buffer.");
+
+    /*
+     * this buffer is not referenced but it might still be dirty (the
+     * last transaction to touch it doesn't need its contents but has
+     * not flushed it).  if that's the case, write it out before
+     * reusing it!
+     */
+    if (bufHdr->flags & BM_DIRTY) {
+       Relation bufrel = RelationIdCacheGetRelation(bufHdr->tag.relId.relId);
+
+       Assert(bufrel != NULL);
+       
+       /* flush this page */
+       smgrwrite(bufrel->rd_rel->relsmgr, bufrel, bufHdr->tag.blockNum,
+                 (char *) MAKE_PTR(bufHdr->data));
+    }
+
+    /*
+     * it's all ours now.
+     */
+    bufHdr->tag.relId.relId = reln->rd_id;
+    bufHdr->tag.blockNum = blockNum;
+    bufHdr->flags &= ~BM_DIRTY;
+
+    /*
+     * lazy memory allocation. (see MAKE_PTR for why we need to do 
+     * MAKE_OFFSET.)
+     */
+    if (bufHdr->data == (SHMEM_OFFSET)0) {
+       char *data = (char *)malloc(BLCKSZ);
+
+       bufHdr->data = MAKE_OFFSET(data);
+    }
+    
+    *foundPtr = FALSE;
+    return bufHdr;
+}
+
+/*
+ * WriteLocalBuffer -
+ *    writes out a local buffer
+ */
+int
+WriteLocalBuffer(Buffer buffer, bool release)
+{
+    int bufid;
+
+    Assert(BufferIsLocal(buffer));
+
+#ifdef LBDEBUG
+    fprintf(stderr, "LB WRITE %d\n", buffer);
+#endif    
+    
+    bufid = - (buffer + 1);
+    LocalBufferDescriptors[bufid].flags |= BM_DIRTY;
+
+    if (release) {
+       Assert(LocalRefCount[bufid] > 0);
+       LocalRefCount[bufid]--;
+    }
+
+    return true;
+}
+
+/*
+ * FlushLocalBuffer -
+ *    flushes a local buffer
+ */
+int
+FlushLocalBuffer(Buffer buffer)
+{
+    int bufid;
+    Relation bufrel;
+    BufferDesc *bufHdr;
+
+    Assert(BufferIsLocal(buffer));
+
+#ifdef LBDEBUG
+    fprintf(stderr, "LB FLUSH %d\n", buffer);
+#endif    
+
+    bufid = - (buffer + 1);
+    bufHdr = &LocalBufferDescriptors[bufid];
+    bufHdr->flags &= ~BM_DIRTY;
+    bufrel = RelationIdCacheGetRelation(bufHdr->tag.relId.relId);
+
+    Assert(bufrel != NULL);
+    smgrflush(bufrel->rd_rel->relsmgr, bufrel, bufHdr->tag.blockNum,
+             (char *) MAKE_PTR(bufHdr->data));
+
+    Assert(LocalRefCount[bufid] > 0);
+    LocalRefCount[bufid]--;
+    
+    return true;
+}
+
+/*
+ * InitLocalBuffer -
+ *    init the local buffer cache. Since most queries (esp. multi-user ones)
+ *    don't involve local buffers, we delay allocating memory for actual the
+ *    buffer until we need it.
+ */
+void
+InitLocalBuffer()
+{
+    int i;
+    
+    /*
+     * these aren't going away. I'm not gonna use palloc.
+     */
+    LocalBufferDescriptors =
+       (BufferDesc *)malloc(sizeof(BufferDesc) * NLocBuffer);
+    memset(LocalBufferDescriptors, 0, sizeof(BufferDesc) * NLocBuffer);
+    nextFreeLocalBuf = 0;
+
+    for (i = 0; i < NLocBuffer; i++) {
+       BufferDesc *buf = &LocalBufferDescriptors[i];
+
+       /*
+        * negative to indicate local buffer. This is tricky: shared buffers
+        * start with 0. We have to start with -2. (Note that the routine
+        * BufferDescriptorGetBuffer adds 1 to buf_id so our first buffer id
+        * is -1.)
+        */
+       buf->buf_id = - i - 2;  
+    }
+
+    LocalRefCount =
+       (long *)malloc(sizeof(long) * NLocBuffer);
+    memset(LocalRefCount, 0, sizeof(long) * NLocBuffer);
+}
+
+/*
+ * LocalBufferSync -
+ *    flush all dirty buffers in the local buffer cache. Since the buffer
+ *    cache is only used for keeping relations visible during a transaction,
+ *    we will not need these buffers again.
+ */
+void
+LocalBufferSync()
+{
+    int i;
+    
+    for (i = 0; i < NLocBuffer; i++) {
+       BufferDesc *buf = &LocalBufferDescriptors[i];
+       Relation bufrel;
+
+       if (buf->flags & BM_DIRTY) {
+#ifdef LBDEBUG
+           fprintf(stderr, "LB SYNC %d\n", -i-1);
+#endif     
+           bufrel = RelationIdCacheGetRelation(buf->tag.relId.relId);
+
+           Assert(bufrel != NULL);
+           
+           smgrwrite(bufrel->rd_rel->relsmgr, bufrel, buf->tag.blockNum,
+                     (char *) MAKE_PTR(buf->data));
+
+           buf->tag.relId.relId = InvalidOid;
+           buf->flags &= ~BM_DIRTY;
+       }
+    }
+
+    memset(LocalRefCount, 0, sizeof(long) * NLocBuffer);
+}
+
+void
+ResetLocalBufferPool()
+{
+    int i;
+
+    memset(LocalBufferDescriptors, 0, sizeof(BufferDesc) * NLocBuffer);
+    nextFreeLocalBuf = 0;
+
+    for (i = 0; i < NLocBuffer; i++) {
+       BufferDesc *buf = &LocalBufferDescriptors[i];
+
+       /* just like InitLocalBuffer() */
+       buf->buf_id = - i - 2;  
+    }
+
+    memset(LocalRefCount, 0, sizeof(long) * NLocBuffer);
+}
diff --git a/src/backend/storage/bufmgr.h b/src/backend/storage/bufmgr.h
new file mode 100644 (file)
index 0000000..9cd59ca
--- /dev/null
@@ -0,0 +1,112 @@
+/*-------------------------------------------------------------------------
+ *
+ * bufmgr.h--
+ *    POSTGRES buffer manager definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        BUFMGR_H
+#define BUFMGR_H
+
+#include "c.h"
+
+#include "machine.h"           /* for BLCKSZ */
+#include "utils/rel.h"
+
+#include "storage/buf_internals.h"     /* UGLY! -- ay */
+
+/*
+ * the maximum size of a disk block for any possible installation.
+ *
+ * in theory this could be anything, but in practice this is actually
+ * limited to 2^13 bytes because we have limited ItemIdData.lp_off and
+ * ItemIdData.lp_len to 13 bits (see itemid.h).
+ */
+#define        MAXBLCKSZ       8192
+
+typedef void *Block;
+
+
+/* special pageno for bget */
+#define P_NEW  InvalidBlockNumber      /* grow the file to get a new page */
+
+typedef bits16 BufferLock;
+
+/**********************************************************************
+
+  the rest is function defns in the bufmgr that are externally callable
+
+ **********************************************************************/
+
+/*
+ * These routines are beaten on quite heavily, hence the macroization.
+ * See buf_internals.h for a related comment.
+ */
+#define BufferDescriptorGetBuffer(bdesc) ((bdesc)->buf_id + 1)
+
+/*
+ * BufferIsPinned --
+ *     True iff the buffer is pinned (and therefore valid)
+ *
+ * Note:
+ *     Smenatics are identical to BufferIsValid 
+ *      XXX - need to remove either one eventually.
+ */
+#define BufferIsPinned BufferIsValid
+
+
+extern int ShowPinTrace;
+
+/*
+ * prototypes for functions in bufmgr.c 
+ */
+extern Buffer RelationGetBufferWithBuffer(Relation relation,
+                 BlockNumber blockNumber, Buffer buffer);
+extern Buffer ReadBuffer(Relation reln, BlockNumber blockNum);
+extern Buffer ReadBuffer_Debug(char *file, int line, Relation reln,
+                              BlockNumber blockNum);
+extern int WriteBuffer(Buffer buffer);
+extern void WriteBuffer_Debug(char *file, int line, Buffer buffer);
+extern void DirtyBufferCopy(Oid dbid, Oid relid, BlockNumber blkno,
+                           char *dest);
+extern int WriteNoReleaseBuffer(Buffer buffer);
+extern Buffer ReleaseAndReadBuffer(Buffer buffer, Relation relation,
+                                  BlockNumber blockNum);
+
+extern void InitBufferPool(IPCKey key);
+extern void PrintBufferUsage(FILE *statfp);
+extern void ResetBufferUsage(void);
+extern void ResetBufferPool(void);
+extern int BufferPoolCheckLeak(void);
+extern void FlushBufferPool(int StableMainMemoryFlag);
+extern bool BufferIsValid(Buffer bufnum);
+extern BlockNumber BufferGetBlockNumber(Buffer buffer);
+extern Relation BufferGetRelation(Buffer buffer);
+extern BlockNumber RelationGetNumberOfBlocks(Relation relation);
+extern Block BufferGetBlock(Buffer buffer);
+extern void ReleaseTmpRelBuffers(Relation tempreldesc);
+extern void DropBuffers(Oid dbid);
+extern void PrintBufferDescs(void);
+extern void PrintPinnedBufs(void);
+extern int BufferShmemSize(void);
+extern void BufferPoolBlowaway(void);
+extern void IncrBufferRefCount(Buffer buffer);
+extern int ReleaseBuffer(Buffer buffer);
+
+extern void IncrBufferRefCount_Debug(char *file, int line, Buffer buffer);
+extern void ReleaseBuffer_Debug(char *file, int line, Buffer buffer);
+extern int ReleaseAndReadBuffer_Debug(char *file,
+                               int line,
+                               Buffer buffer,
+                               Relation relation,
+                               BlockNumber blockNum);
+extern void BufferRefCountReset(int *refcountsave);
+extern void BufferRefCountRestore(int *refcountsave);
+
+#endif /* !defined(BufMgrIncluded) */
+
diff --git a/src/backend/storage/bufpage.h b/src/backend/storage/bufpage.h
new file mode 100644 (file)
index 0000000..3b24c75
--- /dev/null
@@ -0,0 +1,256 @@
+/*-------------------------------------------------------------------------
+ *
+ * bufpage.h--
+ *    Standard POSTGRES buffer page definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        BUFPAGE_H
+#define BUFPAGE_H
+
+#include "c.h"
+#include "machine.h"           /* for BLCKSZ */
+
+#include "storage/buf.h"
+#include "storage/item.h"
+#include "storage/itemid.h"
+#include "storage/itemptr.h"
+
+/*
+ * a postgres disk page is an abstraction layered on top of a postgres
+ * disk block (which is simply a unit of i/o, see block.h).
+ *
+ * specifically, while a disk block can be unformatted, a postgres
+ * disk page is always a slotted page of the form:
+ *
+ * +----------------+---------------------------------+
+ * | PageHeaderData | linp0 linp1 linp2 ...           |
+ * +-----------+----+---------------------------------+
+ * | ... linpN |                                      |
+ * +-----------+--------------------------------------+
+ * |           ^ pd_lower                             |
+ * |                                                  |
+ * |             v pd_upper                           |
+ * +-------------+------------------------------------+
+ * |             | tupleN ...                         |
+ * +-------------+------------------+-----------------+
+ * |       ... tuple2 tuple1 tuple0 | "special space" |
+ * +--------------------------------+-----------------+
+ *                                  ^ pd_special
+ *
+ * a page is full when nothing can be added between pd_lower and
+ * pd_upper.
+ *
+ * all blocks written out by an access method must be disk pages.
+ *
+ * EXCEPTIONS:
+ *
+ * obviously, a page is not formatted before it is initialized with by
+ * a call to PageInit.
+ *
+ * the contents of the special pg_variable/pg_time/pg_log tables are
+ * raw disk blocks with special formats.  these are the only "access
+ * methods" that need not write disk pages.
+ *
+ * NOTES:
+ *
+ * linp0..N form an ItemId array.  ItemPointers point into this array
+ * rather than pointing directly to a tuple.
+ *
+ * tuple0..N are added "backwards" on the page.  because a tuple's
+ * ItemPointer points to its ItemId entry rather than its actual
+ * byte-offset position, tuples can be physically shuffled on a page
+ * whenever the need arises.
+ *
+ * AM-generic per-page information is kept in the pd_opaque field of
+ * the PageHeaderData.  (this is currently only the page size.)
+ * AM-specific per-page data is kept in the area marked "special
+ * space"; each AM has an "opaque" structure defined somewhere that is
+ * stored as the page trailer.  an access method should always
+ * initialize its pages with PageInit and then set its own opaque
+ * fields.
+ */
+typedef Pointer        Page;
+
+/*
+ * PageIsValid --
+ *     True iff page is valid.
+ */
+#define        PageIsValid(page) PointerIsValid(page)
+
+
+/*
+ * location (byte offset) within a page.
+ *
+ * note that this is actually limited to 2^13 because we have limited
+ * ItemIdData.lp_off and ItemIdData.lp_len to 13 bits (see itemid.h).
+ */
+typedef uint16 LocationIndex;
+
+
+/*
+ * space management information generic to any page
+ *
+ *     od_pagesize     - size in bytes.
+ *                       in reality, we need at least 64B to fit the 
+ *                       page header, opaque space and a minimal tuple;
+ *                       on the high end, we can only support pages up
+ *                       to 8KB because lp_off/lp_len are 13 bits.
+ */
+typedef struct OpaqueData {
+    uint16 od_pagesize;
+} OpaqueData;
+    
+typedef OpaqueData     *Opaque;
+
+
+/*
+ * disk page organization
+ */
+typedef struct PageHeaderData {
+    LocationIndex      pd_lower;       /* offset to start of free space */
+    LocationIndex      pd_upper;       /* offset to end of free space */
+    LocationIndex      pd_special;     /* offset to start of special space */
+    OpaqueData         pd_opaque;      /* AM-generic information */
+    ItemIdData         pd_linp[1];     /* line pointers */
+} PageHeaderData;
+
+typedef PageHeaderData *PageHeader;
+
+typedef enum {
+    ShufflePageManagerMode,
+    OverwritePageManagerMode
+} PageManagerMode;
+
+/* ----------------
+ *     misc support macros
+ * ----------------
+ */
+
+/*
+ * XXX this is wrong -- ignores padding/alignment, variable page size,
+ * AM-specific opaque space at the end of the page (as in btrees), ...
+ * however, it at least serves as an upper bound for heap pages.
+ */
+#define MAXTUPLEN      (BLCKSZ - sizeof (PageHeaderData))
+
+/* ----------------------------------------------------------------
+ *                     page support macros
+ * ----------------------------------------------------------------
+ */
+/*
+ * PageIsValid -- This is defined in page.h.
+ */
+
+/*
+ * PageIsUsed --
+ *     True iff the page size is used.
+ *
+ * Note:
+ *     Assumes page is valid.
+ */
+#define PageIsUsed(page) \
+    (AssertMacro(PageIsValid(page)) ? \
+     ((bool) (((PageHeader) (page))->pd_lower != 0)) : false)
+
+/*
+ * PageIsEmpty --
+ *     returns true iff no itemid has been allocated on the page
+ */
+#define PageIsEmpty(page) \
+    (((PageHeader) (page))->pd_lower == \
+     (sizeof(PageHeaderData) - sizeof(ItemIdData)) ? true : false)
+
+/*
+ * PageGetItemId --
+ *     Returns an item identifier of a page.
+ */
+#define PageGetItemId(page, offsetNumber) \
+    ((ItemId) (&((PageHeader) (page))->pd_linp[(-1) + (offsetNumber)]))
+
+/* ----------------
+ *     macros to access opaque space
+ * ----------------
+ */
+
+/*
+ * PageSizeIsValid --
+ *     True iff the page size is valid.
+ *
+ * XXX currently all page sizes are "valid" but we only actually
+ *     use BLCKSZ.
+ */
+#define PageSizeIsValid(pageSize) 1
+
+/*
+ * PageGetPageSize --
+ *     Returns the page size of a page.
+ *
+ * this can only be called on a formatted page (unlike
+ * BufferGetPageSize, which can be called on an unformatted page).
+ * however, it can be called on a page for which there is no buffer.
+ */
+#define PageGetPageSize(page) \
+    ((Size) ((PageHeader) (page))->pd_opaque.od_pagesize)
+
+/*
+ * PageSetPageSize --
+ *     Sets the page size of a page.
+ */
+#define PageSetPageSize(page, size) \
+    ((PageHeader) (page))->pd_opaque.od_pagesize = (size)
+
+/* ----------------
+ *     page special data macros
+ * ----------------
+ */
+/*
+ * PageGetSpecialSize --
+ *     Returns size of special space on a page.
+ *
+ * Note:
+ *     Assumes page is locked.
+ */
+#define PageGetSpecialSize(page) \
+    ((uint16) (PageGetPageSize(page) - ((PageHeader)page)->pd_special))
+
+/*
+ * PageGetSpecialPointer --
+ *     Returns pointer to special space on a page.
+ *
+ * Note:
+ *     Assumes page is locked.
+ */
+#define PageGetSpecialPointer(page) \
+    (AssertMacro(PageIsValid(page)) ? \
+     (char *) ((char *) (page) + ((PageHeader) (page))->pd_special) \
+     : (char *) 0)
+
+/* ----------------------------------------------------------------
+ *     extern declarations
+ * ----------------------------------------------------------------
+ */
+
+extern Size BufferGetPageSize(Buffer buffer);
+extern Page BufferGetPage(Buffer buffer);
+extern void PageInit(Page page, Size pageSize, Size specialSize);
+extern Item PageGetItem(Page page, ItemId itemId);
+extern OffsetNumber PageAddItem(Page page, Item item, Size size,
+                        OffsetNumber offsetNumber, ItemIdFlags flags);
+extern Page PageGetTempPage(Page page, Size specialSize);
+extern void PageRestoreTempPage(Page tempPage, Page oldPage);
+extern OffsetNumber PageGetMaxOffsetNumber(Page page);
+extern void PageRepairFragmentation(Page page);
+extern Size PageGetFreeSpace(Page page);
+extern void PageManagerModeSet(PageManagerMode mode);
+extern void PageIndexTupleDelete(Page page, OffsetNumber offset);
+extern void PageIndexTupleDeleteAdjustLinePointers(PageHeader phdr,
+                                      char *location, Size size);
+
+
+#endif /* BUFPAGE_H */
diff --git a/src/backend/storage/fd.h b/src/backend/storage/fd.h
new file mode 100644 (file)
index 0000000..17c0ab6
--- /dev/null
@@ -0,0 +1,96 @@
+/*-------------------------------------------------------------------------
+ *
+ * fd.h--
+ *    Virtual file descriptor definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * calls:
+ * 
+ *  File {Close, Read, Write, Seek, Tell, Sync}
+ *  {File Name Open, Allocate, Free} File
+ *
+ * These are NOT JUST RENAMINGS OF THE UNIX ROUTINES.
+ * use them for all file activity...
+ *
+ *  fd = FilePathOpenFile("foo", O_RDONLY);
+ *  File fd;
+ *
+ * use AllocateFile if you need a file descriptor in some other context.
+ * it will make sure that there is a file descriptor free
+ *
+ * use FreeFile to let the virtual file descriptor package know that 
+ * there is now a free fd (when you are done with it)
+ *
+ *  AllocateFile();
+ *  FreeFile();
+ */
+#ifndef        FD_H
+#define FD_H
+
+/*
+ * FileOpen uses the standard UNIX open(2) flags.
+ */
+#include <fcntl.h>     /* for O_ on most */
+#ifndef O_RDONLY
+#include <sys/file.h>  /* for O_ on the rest */
+#endif /* O_RDONLY */
+
+/*
+ * FileSeek uses the standard UNIX lseek(2) flags.
+ */
+#ifndef WIN32
+#include <unistd.h>    /* for SEEK_ on most */
+#else
+#ifndef SEEK_SET
+#include <stdio.h>     /* for SEEK_ on the rest */
+#endif /* SEEK_SET */
+#endif /* WIN32 */
+
+#include "c.h"
+#include "storage/block.h"
+
+typedef char   *FileName;
+
+typedef int    File;
+
+/* originally in libpq-fs.h */
+struct pgstat { /* just the fields we need from stat structure */
+    int st_ino;
+    int st_mode;
+    unsigned int st_size;
+    unsigned int st_sizehigh;  /* high order bits */
+/* 2^64 == 1.8 x 10^20 bytes */
+    int st_uid;
+    int st_atime_s;    /* just the seconds */
+    int st_mtime_s;    /* since SysV and the new BSD both have */
+    int st_ctime_s;    /* usec fields.. */
+};
+
+/*
+ * prototypes for functions in fd.c
+ */
+extern void FileInvalidate(File file);
+extern File FileNameOpenFile(FileName fileName, int fileFlags, int fileMode);
+extern File PathNameOpenFile(FileName fileName, int fileFlags, int fileMode);
+extern void FileClose(File file);
+extern void FileUnlink(File file);
+extern int FileRead(File file, char *buffer, int amount);
+extern int FileWrite(File file, char *buffer, int amount);
+extern long FileSeek(File file, long offset, int whence);
+extern long FileTell(File file);
+extern int FileTruncate(File file, int offset);
+extern int FileSync(File file);
+extern int FileNameUnlink(char *filename);
+extern void AllocateFile(void);
+extern void FreeFile(void);
+extern void closeAllVfds(void);
+extern void closeOneVfd(void);
+
+#endif /* FD_H */
diff --git a/src/backend/storage/file/Makefile.inc b/src/backend/storage/file/Makefile.inc
new file mode 100644 (file)
index 0000000..402d983
--- /dev/null
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for storage/file
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= fd.c
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
new file mode 100644 (file)
index 0000000..87c62a5
--- /dev/null
@@ -0,0 +1,888 @@
+/*-------------------------------------------------------------------------
+ *
+ * fd.c--
+ *    Virtual file descriptor code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *    $Id$
+ *
+ * NOTES:
+ *
+ * This code manages a cache of 'virtual' file descriptors (VFDs).
+ * The server opens many file descriptors for a variety of reasons,
+ * including base tables, scratch files (e.g., sort and hash spool
+ * files), and random calls to C library routines like system(3); it
+ * is quite easy to exceed system limits on the number of open files a
+ * single process can have.  (This is around 256 on many modern
+ * operating systems, but can be as low as 32 on others.)
+ *
+ * VFDs are managed as an LRU pool, with actual OS file descriptors
+ * being opened and closed as needed.  Obviously, if a routine is
+ * opened using these interfaces, all subsequent operations must also
+ * be through these interfaces (the File type is not a real file
+ * descriptor).
+ *
+ * For this scheme to work, most (if not all) routines throughout the
+ * server should use these interfaces instead of calling the C library
+ * routines (e.g., open(2) and fopen(3)) themselves.  Otherwise, we
+ * may find ourselves short of real file descriptors anyway.
+ *
+ * This file used to contain a bunch of stuff to support RAID levels 0
+ * (jbod), 1 (duplex) and 5 (xor parity).  That stuff is all gone
+ * because the parallel query processing code that called it is all
+ * gone.  If you really need it you could get it from the original
+ * POSTGRES source.
+ *-------------------------------------------------------------------------
+ */
+
+#include <stdio.h>
+#include <sys/file.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "miscadmin.h" /* for DataDir */
+#include "utils/palloc.h"
+
+#ifdef PORTNAME_sparc
+/*
+ * the SunOS 4 NOFILE is a lie, because the default limit is *not* the
+ * maximum number of file descriptors you can have open.
+ *
+ * we have to either use this number (the default dtablesize) or
+ * explicitly call setrlimit(RLIMIT_NOFILE, NOFILE).
+ */
+#include <sys/user.h>
+#undef NOFILE
+#define NOFILE NOFILE_IN_U
+#endif /* PORTNAME_sparc */
+
+/*
+ * Problem: Postgres does a system(ld...) to do dynamic loading.  This
+ * will open several extra files in addition to those used by
+ * Postgres.  We need to do this hack to guarentee that there are file
+ * descriptors free for ld to use.
+ *
+ * The current solution is to limit the number of files descriptors
+ * that this code will allocated at one time.  (it leaves
+ * RESERVE_FOR_LD free).
+ *
+ * (Even though most dynamic loaders now use dlopen(3) or the
+ * equivalent, the OS must still open several files to perform the
+ * dynamic loading.  Keep this here.)
+ */
+#define RESERVE_FOR_LD 10
+
+/*
+ * If we are using weird storage managers, we may need to keep real
+ * file descriptors open so that the jukebox server doesn't think we
+ * have gone away (and no longer care about a platter or file that
+ * we've been using).  This might be an actual file descriptor for a
+ * local jukebox interface that uses paths, or a socket connection for
+ * a network jukebox server.  Since we can't be opening and closing
+ * these descriptors at whim, we must make allowances for them.
+ */
+#ifdef HP_JUKEBOX
+#define RESERVE_FOR_JB 25
+#define        MAXFILES        ((NOFILE - RESERVE_FOR_LD) - RESERVE_FOR_JB)
+#else /* HP_JUKEBOX */
+#define        MAXFILES        (NOFILE - RESERVE_FOR_LD)
+#endif /* HP_JUKEBOX */
+
+/* Debugging.... */
+
+#ifdef FDDEBUG
+# define DO_DB(A) A
+#else
+# define DO_DB(A) /* A */
+#endif
+
+#define VFD_CLOSED -1
+
+#include "storage/fd.h"
+#include "utils/elog.h"
+
+#define FileIsNotOpen(file) (VfdCache[file].fd == VFD_CLOSED)
+
+typedef struct vfd {
+    signed short       fd;
+    unsigned short     fdstate;
+
+#define FD_DIRTY       (1 << 0)
+
+    File       nextFree;
+    File       lruMoreRecently;
+    File       lruLessRecently;
+    long       seekPos;
+    char       *fileName;
+    int                fileFlags;
+    int                fileMode;
+} Vfd;
+
+/*
+ * Virtual File Descriptor array pointer and size.  This grows as
+ * needed.
+ */
+static Vfd     *VfdCache;
+static Size    SizeVfdCache = 0;
+
+/*
+ * Minimum number of file descriptors known to be free.
+ */
+static int     FreeFd = 0;
+
+/*
+ * Number of file descriptors known to be open.
+ */
+static int     nfile = 0;
+
+/*
+ * we use the name of the null device in various places, mostly so
+ * that we can open it and find out if we really have any descriptors
+ * available or not.
+ */
+#ifndef WIN32
+static char *Nulldev = "/dev/null";
+static char Sep_char = '/';
+#else
+static char *Nulldev = "NUL";
+static char Sep_char = '\\';
+#endif /* WIN32 */
+
+/*
+ * Private Routines
+ *
+ * Delete         - delete a file from the Lru ring
+ * LruDelete      - remove a file from the Lru ring and close
+ * Insert         - put a file at the front of the Lru ring
+ * LruInsert      - put a file at the front of the Lru ring and open
+ * AssertLruRoom   - make sure that there is a free fd.
+ *
+ * the Last Recently Used ring is a doubly linked list that begins and
+ * ends on element zero.
+ *
+ * example:
+ *
+ *     /--less----\                /---------\
+ *     v           \              v           \
+ *   #0 --more---> LeastRecentlyUsed --more-\ \
+ *    ^\                                    | |
+ *     \\less--> MostRecentlyUsedFile   <---/ |
+ *      \more---/                    \--less--/
+ *
+ * AllocateVfd    - grab a free (or new) file record (from VfdArray)
+ * FreeVfd        - free a file record
+ *
+ */
+static void Delete(File file);
+static void LruDelete(File file);
+static void Insert(File file);
+static int LruInsert (File file);
+static void AssertLruRoom(void);
+static File AllocateVfd(void);
+static void FreeVfd(File file);
+
+static int FileAccess(File file);
+static File fileNameOpenFile(FileName fileName, int fileFlags, int fileMode);
+static char *filepath(char *filename);
+
+#if defined(FDDEBUG)
+static void
+_dump_lru()
+{
+    int mru = VfdCache[0].lruLessRecently;
+    Vfd *vfdP = &VfdCache[mru];
+    
+    printf("MOST %d ", mru);
+    while (mru != 0)
+       {
+           mru = vfdP->lruLessRecently;
+           vfdP = &VfdCache[mru];
+           printf("%d ", mru);
+       }
+    printf("LEAST\n");
+}
+#endif /* FDDEBUG */
+
+static void
+Delete(File file)
+{
+    Vfd        *fileP;
+    
+    DO_DB(printf("DEBUG:       Delete %d (%s)\n",
+                file, VfdCache[file].fileName));
+    DO_DB(_dump_lru());
+    
+    Assert(file != 0);
+    
+    fileP = &VfdCache[file];
+
+    VfdCache[fileP->lruLessRecently].lruMoreRecently =
+       VfdCache[file].lruMoreRecently;
+    VfdCache[fileP->lruMoreRecently].lruLessRecently =
+       VfdCache[file].lruLessRecently;
+    
+    DO_DB(_dump_lru());
+}
+
+static void
+LruDelete(File file)
+{
+    Vfd     *fileP;
+    int        returnValue;
+    
+    DO_DB(printf("DEBUG:       LruDelete %d (%s)\n",
+                file, VfdCache[file].fileName));
+    
+    Assert(file != 0);
+    
+    fileP = &VfdCache[file];
+    
+    /* delete the vfd record from the LRU ring */
+    Delete(file);
+    
+    /* save the seek position */
+    fileP->seekPos = lseek(fileP->fd, 0L, SEEK_CUR);
+    Assert( fileP->seekPos != -1);
+    
+    /* if we have written to the file, sync it */
+    if (fileP->fdstate & FD_DIRTY) {
+       returnValue = fsync(fileP->fd);
+       Assert(returnValue != -1);
+       fileP->fdstate &= ~FD_DIRTY;
+    }
+    
+    /* close the file */
+    returnValue = close(fileP->fd);
+    Assert(returnValue != -1);
+    
+    --nfile;
+    fileP->fd = VFD_CLOSED;
+    
+    /* note that there is now one more free real file descriptor */
+    FreeFd++;
+}
+
+static void
+Insert(File file)
+{
+    Vfd        *vfdP;
+    
+    DO_DB(printf("DEBUG:       Insert %d (%s)\n",
+                file, VfdCache[file].fileName));
+    DO_DB(_dump_lru());
+    
+    vfdP = &VfdCache[file];
+    
+    vfdP->lruMoreRecently = 0;
+    vfdP->lruLessRecently = VfdCache[0].lruLessRecently;
+    VfdCache[0].lruLessRecently = file;
+    VfdCache[vfdP->lruLessRecently].lruMoreRecently = file;
+    
+    DO_DB(_dump_lru());
+}
+
+static int
+LruInsert (File file)
+{
+    Vfd        *vfdP;
+    int        returnValue;
+    
+    DO_DB(printf("DEBUG:       LruInsert %d (%s)\n",
+                file, VfdCache[file].fileName));
+    
+    vfdP = &VfdCache[file];
+    
+    if (FileIsNotOpen(file)) {
+       int tmpfd;
+       
+        /*
+        * Note, we check to see if there's a free file descriptor
+        * before attempting to open a file. One general way to do
+        * this is to try to open the null device which everybody
+        * should be able to open all the time. If this fails, we
+        * assume this is because there's no free file descriptors.
+        */
+    tryAgain:
+       tmpfd = open(Nulldev, O_CREAT|O_RDWR, 0666);
+       if (tmpfd < 0) {
+           FreeFd = 0;
+           errno = 0;
+           AssertLruRoom();
+           goto tryAgain;
+       } else {
+           close(tmpfd);
+       }
+       vfdP->fd = open(vfdP->fileName,vfdP->fileFlags,vfdP->fileMode);
+       
+       if (vfdP->fd < 0) {
+           DO_DB(printf("RE_OPEN FAILED: %d\n",
+                        errno));
+           return (vfdP->fd);
+       } else {
+           DO_DB(printf("RE_OPEN SUCCESS\n"));
+           ++nfile;
+       }
+       
+       /* seek to the right position */
+       if (vfdP->seekPos != 0L) {
+           returnValue =
+               lseek(vfdP->fd, vfdP->seekPos, SEEK_SET);
+           Assert(returnValue != -1);
+       }
+       
+       /* init state on open */
+       vfdP->fdstate = 0x0;
+       
+       /* note that a file descriptor has been used up */
+       if (FreeFd > 0)
+           FreeFd--;
+    }
+    
+    /*
+     * put it at the head of the Lru ring
+     */
+    
+    Insert(file);
+    
+    return (0);
+}
+
+static void
+AssertLruRoom()
+{
+    DO_DB(printf("DEBUG:       AssertLruRoom (FreeFd = %d)\n",
+                FreeFd));
+    
+    if (FreeFd <= 0 || nfile >= MAXFILES) {
+       LruDelete(VfdCache[0].lruMoreRecently);
+    }
+}
+
+static File
+AllocateVfd()
+{
+    Index      i;
+    File       file;
+    
+    DO_DB(printf("DEBUG:       AllocateVfd\n"));
+    
+    if (SizeVfdCache == 0) {
+       
+       /* initialize */
+       VfdCache = (Vfd *)malloc(sizeof(Vfd));
+       
+       VfdCache->nextFree = 0;
+       VfdCache->lruMoreRecently = 0;
+       VfdCache->lruLessRecently = 0;
+       VfdCache->fd = VFD_CLOSED;
+       VfdCache->fdstate = 0x0;
+       
+       SizeVfdCache = 1;
+    }
+    
+    if (VfdCache[0].nextFree == 0) {
+       
+       /*
+        * The free list is empty so it is time to increase the
+        * size of the array
+        */
+       
+       VfdCache =(Vfd *)realloc(VfdCache, sizeof(Vfd)*SizeVfdCache*2);
+       Assert(VfdCache != NULL);
+       
+       /*
+        * Set up the free list for the new entries
+        */
+       
+       for (i = SizeVfdCache; i < 2*SizeVfdCache; i++)  {
+           memset((char *) &(VfdCache[i]), 0, sizeof(VfdCache[0]));
+           VfdCache[i].nextFree = i+1;
+           VfdCache[i].fd = VFD_CLOSED;
+       }
+       
+       /*
+        * Element 0 is the first and last element of the free
+        * list
+        */
+       
+       VfdCache[0].nextFree = SizeVfdCache;
+       VfdCache[2*SizeVfdCache-1].nextFree = 0;
+       
+       /*
+        * Record the new size
+        */
+       
+       SizeVfdCache *= 2;
+    }
+    file = VfdCache[0].nextFree;
+    
+    VfdCache[0].nextFree = VfdCache[file].nextFree;
+    
+    return file;
+}
+
+static void
+FreeVfd(File file)
+{
+    DO_DB(printf("DB: FreeVfd: %d (%s)\n",
+                file, VfdCache[file].fileName));
+    
+    VfdCache[file].nextFree = VfdCache[0].nextFree;
+    VfdCache[0].nextFree = file;
+}
+
+static char *
+filepath(char *filename)
+{
+    char *buf;
+    char basename[16];
+    int len;
+
+#ifndef WIN32    
+    if (*filename != Sep_char) {
+#else
+    if (!(filename[1] == ':' && filename[2] == Sep_char)) {
+#endif /* WIN32 */     
+
+       /* Either /base/ or \base\ */
+       sprintf(basename, "%cbase%c", Sep_char, Sep_char);
+
+       len = strlen(DataDir) + strlen(basename) + strlen(GetDatabaseName())
+           + strlen(filename) + 2;
+       buf = (char*) palloc(len);
+       sprintf(buf, "%s%s%s%c%s",
+               DataDir, basename, GetDatabaseName(), Sep_char, filename);
+    } else {
+       buf = (char *) palloc(strlen(filename) + 1);
+       strcpy(buf, filename);
+    }
+    
+    return(buf);
+}
+
+static int
+FileAccess(File file)
+{
+    int        returnValue;
+    
+    DO_DB(printf("DB: FileAccess %d (%s)\n",
+                file, VfdCache[file].fileName));
+    
+    /*
+     * Is the file open?  If not, close the least recently used,
+     * then open it and stick it at the head of the used ring
+     */
+    
+    if (FileIsNotOpen(file)) {
+       
+       AssertLruRoom();
+       
+       returnValue = LruInsert(file);
+       if (returnValue != 0)
+           return returnValue;
+       
+    } else {
+       
+       /*
+        * We now know that the file is open and that it is not the
+        * last one accessed, so we need to more it to the head of
+        * the Lru ring.
+        */
+       
+       Delete(file);
+       Insert(file);
+    }
+    
+    return (0);
+}
+
+/*
+ *  Called when we get a shared invalidation message on some relation.
+ */
+void
+FileInvalidate(File file)
+{
+    if (!FileIsNotOpen(file)) {
+       LruDelete(file);
+    }
+}
+
+/* VARARGS2 */
+static File
+fileNameOpenFile(FileName fileName,
+                int fileFlags,
+                int fileMode)
+{
+    static int osRanOut = 0;
+    File       file;
+    Vfd        *vfdP;
+    int     tmpfd;
+    
+    DO_DB(printf("DEBUG: FileNameOpenFile: %s %x %o\n",
+                fileName, fileFlags, fileMode));
+    
+    file = AllocateVfd();
+    vfdP = &VfdCache[file];
+    
+    if (nfile >= MAXFILES || (FreeFd == 0 && osRanOut)) {
+       AssertLruRoom();
+    }
+    
+ tryAgain:
+    tmpfd = open(Nulldev, O_CREAT|O_RDWR, 0666);
+    if (tmpfd < 0) {
+       DO_DB(printf("DB: not enough descs, retry, er= %d\n",
+                    errno));
+       errno = 0;
+       FreeFd = 0;
+       osRanOut = 1;
+       AssertLruRoom();
+       goto tryAgain;
+    } else {
+       close(tmpfd);
+    }
+    
+#ifdef WIN32
+      fileFlags |= _O_BINARY;
+#endif /* WIN32 */
+    vfdP->fd = open(fileName,fileFlags,fileMode);
+    vfdP->fdstate = 0x0;
+    
+    if (vfdP->fd < 0) {
+       FreeVfd(file);
+       return -1;
+    }
+    ++nfile;
+    DO_DB(printf("DB: FNOF success %d\n",
+                vfdP->fd));
+    
+    (void)LruInsert(file);
+    
+    if (fileName==NULL) {
+       elog(WARN, "fileNameOpenFile: NULL fname");
+    }
+    vfdP->fileName = malloc(strlen(fileName)+1);
+    strcpy(vfdP->fileName,fileName);
+    
+    vfdP->fileFlags = fileFlags & ~(O_TRUNC|O_EXCL);
+    vfdP->fileMode = fileMode;
+    vfdP->seekPos = 0;
+    
+    return file;
+}
+
+/*
+ * open a file in the database directory ($PGDATA/base/...)
+ */
+File
+FileNameOpenFile(FileName fileName, int fileFlags, int fileMode)
+{
+    File fd;
+    char *fname;
+    
+    fname = filepath(fileName);
+    fd = fileNameOpenFile(fname, fileFlags, fileMode);
+    pfree(fname);
+    return(fd);
+}
+
+/*
+ * open a file in an arbitrary directory
+ */
+File
+PathNameOpenFile(FileName fileName, int fileFlags, int fileMode)
+{
+    return(fileNameOpenFile(fileName, fileFlags, fileMode));
+}
+
+void
+FileClose(File file)
+{
+    int        returnValue;
+    
+    DO_DB(printf("DEBUG: FileClose: %d (%s)\n",
+                file, VfdCache[file].fileName));
+    
+    if (!FileIsNotOpen(file)) {
+       
+       /* remove the file from the lru ring */
+       Delete(file);
+       
+       /* record the new free operating system file descriptor */
+       FreeFd++;
+       
+       /* if we did any writes, sync the file before closing */
+       if (VfdCache[file].fdstate & FD_DIRTY) {
+           returnValue = fsync(VfdCache[file].fd);
+           Assert(returnValue != -1);
+           VfdCache[file].fdstate &= ~FD_DIRTY;
+       }
+       
+       /* close the file */
+       returnValue = close(VfdCache[file].fd);
+       Assert(returnValue != -1);
+       
+       --nfile;
+       VfdCache[file].fd = VFD_CLOSED;
+    }
+    /*
+     * Add the Vfd slot to the free list
+     */
+    FreeVfd(file);
+    /*
+     * Free the filename string
+     */
+    free(VfdCache[file].fileName);
+}
+
+void
+FileUnlink(File file)
+{
+    int returnValue;
+    
+    DO_DB(printf("DB: FileClose: %d (%s)\n",
+                file, VfdCache[file].fileName));
+    
+    if (!FileIsNotOpen(file)) {
+       
+       /* remove the file from the lru ring */
+       Delete(file);
+       
+       /* record the new free operating system file descriptor */
+       FreeFd++;
+       
+       /* if we did any writes, sync the file before closing */
+       if (VfdCache[file].fdstate & FD_DIRTY) {
+           returnValue = fsync(VfdCache[file].fd);
+           Assert(returnValue != -1);
+           VfdCache[file].fdstate &= ~FD_DIRTY;
+       }
+       
+       /* close the file */
+       returnValue = close(VfdCache[file].fd);
+       Assert(returnValue != -1);
+       
+       --nfile;
+       VfdCache[file].fd = VFD_CLOSED;
+    }
+    /* add the Vfd slot to the free list */
+    FreeVfd(file);
+    
+    /* free the filename string */
+    unlink(VfdCache[file].fileName);
+    free(VfdCache[file].fileName);
+}
+
+int
+FileRead(File file, char *buffer, int amount)
+{
+    int        returnCode;
+
+    DO_DB(printf("DEBUG: FileRead: %d (%s) %d 0x%x\n",
+                file, VfdCache[file].fileName, amount, buffer));
+    
+    FileAccess(file);
+    returnCode = read(VfdCache[file].fd, buffer, amount);
+    if (returnCode > 0) {
+       VfdCache[file].seekPos += returnCode;
+    }
+    
+    return returnCode;
+}
+
+int
+FileWrite(File file, char *buffer, int amount)
+{
+    int        returnCode;
+
+    DO_DB(printf("DB: FileWrite: %d (%s) %d 0x%lx\n",
+                file, VfdCache[file].fileName, amount, buffer));
+    
+    FileAccess(file);
+    returnCode = write(VfdCache[file].fd, buffer, amount);
+    if (returnCode > 0) {  /* changed by Boris with Mao's advice */
+       VfdCache[file].seekPos += returnCode;
+    }
+    
+    /* record the write */
+    VfdCache[file].fdstate |= FD_DIRTY;
+    
+    return returnCode;
+}
+
+long
+FileSeek(File file, long offset, int whence)
+{
+    int        returnCode;
+    
+    DO_DB(printf("DEBUG: FileSeek: %d (%s) %d %d\n",
+                file, VfdCache[file].fileName, offset, whence));
+    
+    if (FileIsNotOpen(file)) {
+       switch(whence) {
+       case SEEK_SET:
+           VfdCache[file].seekPos = offset;
+           return offset;
+       case SEEK_CUR:
+           VfdCache[file].seekPos = VfdCache[file].seekPos +offset;
+           return VfdCache[file].seekPos;
+       case SEEK_END:
+           FileAccess(file);
+           returnCode = VfdCache[file].seekPos = 
+               lseek(VfdCache[file].fd, offset, whence);
+           return returnCode;
+       default:
+           elog(WARN, "FileSeek: invalid whence: %d", whence);
+           break;
+       }
+    } else {
+       returnCode = VfdCache[file].seekPos = 
+           lseek(VfdCache[file].fd, offset, whence);
+       return returnCode;
+    }
+    /*NOTREACHED*/
+    return(-1L);
+}
+
+/*
+ * XXX not actually used but here for completeness
+ */
+long
+FileTell(File file)
+{
+    DO_DB(printf("DEBUG: FileTell %d (%s)\n",
+                file, VfdCache[file].fileName));
+    return VfdCache[file].seekPos;
+}
+
+int
+FileTruncate(File file, int offset)
+{
+    int returnCode;
+
+    DO_DB(printf("DEBUG: FileTruncate %d (%s)\n",
+                file, VfdCache[file].fileName));
+    
+    (void) FileSync(file);
+    (void) FileAccess(file);
+    returnCode = ftruncate(VfdCache[file].fd, offset);
+    return(returnCode);
+}
+
+int
+FileSync(File file)
+{
+    int        returnCode;
+    
+    /*
+     *  If the file isn't open, then we don't need to sync it; we
+     *  always sync files when we close them.  Also, if we haven't
+     *  done any writes that we haven't already synced, we can ignore
+     *  the request.
+     */
+    
+    if (VfdCache[file].fd < 0 || !(VfdCache[file].fdstate & FD_DIRTY)) {
+       returnCode = 0;
+    } else {
+       returnCode = fsync(VfdCache[file].fd);
+       VfdCache[file].fdstate &= ~FD_DIRTY;
+    }
+    
+    return returnCode;
+}
+
+int
+FileNameUnlink(char *filename)
+{
+    int retval;
+    char *fname;
+
+    fname = filepath(filename);
+    retval = unlink(fname);
+    pfree(fname);
+    return(retval);
+}
+
+/*
+ * if we want to be sure that we have a real file descriptor available
+ * (e.g., we want to know this in psort) we call AllocateFile to force
+ * availability.  when we are done we call FreeFile to deallocate the
+ * descriptor.
+ *
+ * allocatedFiles keeps track of how many have been allocated so we
+ * can give a warning if there are too few left.
+ */
+static int allocatedFiles = 0;
+
+void
+AllocateFile()
+{
+    int fd;
+    int fdleft;
+
+    while ((fd = open(Nulldev,O_WRONLY,0)) < 0) {
+       if (errno == EMFILE) {
+           errno = 0;
+           FreeFd = 0;
+           AssertLruRoom();
+       } else {
+           elog(WARN,"Open: %s in %s line %d\n", Nulldev,
+                __FILE__, __LINE__);
+       }
+    }
+    close(fd);
+    ++allocatedFiles;
+    fdleft = MAXFILES - allocatedFiles;
+    if (fdleft < 6) {
+       elog(DEBUG,"warning: few usable file descriptors left (%d)", fdleft);
+    }
+    
+    DO_DB(printf("DEBUG: AllocatedFile.  FreeFd = %d\n",
+                FreeFd));
+}
+
+/*
+ * XXX What happens if FreeFile() is called without a previous
+ * AllocateFile()?
+ */
+void
+FreeFile()
+{
+    DO_DB(printf("DEBUG: FreeFile.  FreeFd now %d\n",
+                FreeFd));
+    FreeFd++;
+    nfile++;                   /* dangerous */
+    Assert(allocatedFiles > 0);
+    --allocatedFiles;
+}
+
+void
+closeAllVfds()
+{
+    int i;
+    for (i=0; i<SizeVfdCache; i++) {
+       if (!FileIsNotOpen(i))
+           LruDelete(i);
+    }
+}
+
+void
+closeOneVfd()
+{
+    int tmpfd;
+    
+    tmpfd = open(Nulldev, O_CREAT | O_RDWR, 0666);
+    if (tmpfd < 0) {
+       FreeFd = 0;
+       AssertLruRoom();
+       FreeFd = 0;
+    }
+    else
+       close(tmpfd);
+}
diff --git a/src/backend/storage/ipc.h b/src/backend/storage/ipc.h
new file mode 100644 (file)
index 0000000..6f9c659
--- /dev/null
@@ -0,0 +1,285 @@
+/*-------------------------------------------------------------------------
+ *
+ * ipc.h--
+ *    POSTGRES inter-process communication definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    This file is very architecture-specific.  This stuff should actually
+ *    be factored into the port/ directories.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        IPC_H
+#define IPC_H
+
+#include <sys/types.h>
+#ifndef        _IPC_
+#define _IPC_
+#include <sys/ipc.h>
+#endif
+
+#include "c.h"
+
+/*
+ * Many architectures have support for user-level spinlocks (i.e., an
+ * atomic test-and-set instruction).  However, we have only written
+ * spinlock code for the architectures listed.
+ */
+#if defined(PORTNAME_aix) || \
+    defined(PORTNAME_alpha) || \
+    defined(PORTNAME_hpux) || \
+    defined(PORTNAME_irix5) || \
+    defined(PORTNAME_next) || \
+    defined(PORTNAME_sparc) || \
+    defined(PORTNAME_sparc_solaris) || \
+    (defined(__i386__) && defined(__GNUC__))
+#define HAS_TEST_AND_SET
+#endif
+
+#if defined(HAS_TEST_AND_SET)
+
+#if defined(PORTNAME_next)
+/*
+ * Use Mach mutex routines since these are, in effect, test-and-set
+ * spinlocks.
+ */
+#undef NEVER   /* definition in cthreads.h conflicts with parse.h */
+#include <mach/cthreads.h>
+
+typedef struct mutex   slock_t;
+#else /* next */
+#if defined(PORTNAME_aix)
+/*
+ * The AIX C library has the cs(3) builtin for compare-and-set that 
+ * operates on ints.
+ */
+typedef unsigned int   slock_t;
+#else /* aix */
+#if defined(PORTNAME_alpha)
+#include <sys/mman.h>
+typedef msemaphore     slock_t;
+#else /* alpha */
+#if defined(PORTNAME_hpux)
+/*
+ * The PA-RISC "semaphore" for the LDWCX instruction is 4 bytes aligned
+ * to a 16-byte boundary.
+ */
+typedef struct { int sem[4]; } slock_t;
+#else /* hpux */
+#if defined(PORTNAME_irix5)
+#include <abi_mutex.h>
+typedef abilock_t      slock_t;
+#else /* irix5 */
+/*
+ * On all other architectures spinlocks are a single byte.
+ */
+typedef unsigned char   slock_t;
+#endif /* irix5 */
+#endif /* hpux */
+#endif /* alpha */
+#endif /* aix */
+#endif /* next */
+
+extern void S_LOCK(slock_t *lock);
+extern void S_UNLOCK(slock_t *lock);
+extern void S_INIT_LOCK(slock_t *lock);
+
+#if defined(PORTNAME_hpux) || defined(PORTNAME_alpha) || defined(PORTNAME_irix5) || defined(PORTNAME_next)
+extern int S_LOCK_FREE(slock_t *lock);
+#else /* PORTNAME_hpux */
+#define S_LOCK_FREE(lock)      ((*lock) == 0)
+#endif /* PORTNAME_hpux */
+
+#endif /* HAS_TEST_AND_SET */
+
+/*
+ * On architectures for which we have not implemented spinlocks (or
+ * cannot do so), we use System V semaphores.  We also use them for 
+ * long locks.  For some reason union semun is never defined in the 
+ * System V header files so we must do it ourselves.
+ */
+#if defined(sequent) || \
+    defined(PORTNAME_aix) || \
+    defined(PORTNAME_alpha) || \
+    defined(PORTNAME_hpux) || \
+    defined(PORTNAME_sparc_solaris) || \
+    defined(WIN32) || \
+    defined(PORTNAME_ultrix4)
+union semun {
+    int val;
+    struct semid_ds *buf;
+    unsigned short *array;
+};
+#endif
+
+typedef uint16 SystemPortAddress;
+
+/* semaphore definitions */
+
+#define IPCProtection  (0600)          /* access/modify by user only */
+
+#define IPC_NMAXSEM    25              /* maximum number of semaphores */
+#define IpcSemaphoreDefaultStartValue  255
+#define IpcSharedLock                                  (-1)
+#define IpcExclusiveLock                         (-255)
+
+#define IpcUnknownStatus       (-1)
+#define IpcInvalidArgument     (-2)
+#define IpcSemIdExist          (-3)
+#define IpcSemIdNotExist       (-4)
+
+typedef uint32 IpcSemaphoreKey;                /* semaphore key */
+typedef int    IpcSemaphoreId;
+
+/* shared memory definitions */ 
+
+#define IpcMemCreationFailed   (-1)
+#define IpcMemIdGetFailed      (-2)
+#define IpcMemAttachFailed     0
+
+typedef uint32 IPCKey;
+#define PrivateIPCKey  IPC_PRIVATE
+#define DefaultIPCKey  17317
+
+typedef uint32  IpcMemoryKey;                  /* shared memory key */
+typedef int    IpcMemoryId;
+
+
+/* ipc.c */
+extern void exitpg(int code);
+extern void quasi_exitpg(void);
+extern on_exitpg(void (*function)(), caddr_t arg);
+
+extern IpcSemaphoreId IpcSemaphoreCreate(IpcSemaphoreKey semKey,
+               int semNum, int permission, int semStartValue,
+               int removeOnExit, int *status);
+extern void IpcSemaphoreSet(int semId, int semno, int value);
+extern void IpcSemaphoreKill(IpcSemaphoreKey key);
+extern void IpcSemaphoreLock(IpcSemaphoreId semId, int sem, int lock);
+extern void IpcSemaphoreUnlock(IpcSemaphoreId semId, int sem, int lock);
+extern int IpcSemaphoreGetCount(IpcSemaphoreId semId, int sem);
+extern int IpcSemaphoreGetValue(IpcSemaphoreId semId, int sem);
+extern IpcMemoryId IpcMemoryCreate(IpcMemoryKey memKey, uint32 size,
+                                  int permission);
+extern IpcMemoryId IpcMemoryIdGet(IpcMemoryKey memKey, uint32 size);
+extern void IpcMemoryDetach(int status, char *shmaddr);
+extern char *IpcMemoryAttach(IpcMemoryId memId);
+extern void IpcMemoryKill(IpcMemoryKey memKey);
+extern void CreateAndInitSLockMemory(IPCKey key);
+extern void AttachSLockMemory(IPCKey key);
+
+
+#ifdef HAS_TEST_AND_SET
+
+#define NSLOCKS                2048
+#define        NOLOCK          0
+#define SHAREDLOCK     1
+#define EXCLUSIVELOCK  2
+
+typedef enum _LockId_ {
+    BUFMGRLOCKID,
+    LOCKLOCKID,
+    OIDGENLOCKID,
+    SHMEMLOCKID,
+    BINDINGLOCKID,
+    LOCKMGRLOCKID,
+    SINVALLOCKID,
+
+#ifdef MAIN_MEMORY
+    MMCACHELOCKID,
+#endif /* MAIN_MEMORY */
+
+    PROCSTRUCTLOCKID,
+    FIRSTFREELOCKID
+} _LockId_;
+
+#define MAX_SPINS      FIRSTFREELOCKID
+
+typedef struct slock {
+    slock_t            locklock;
+    unsigned char      flag;
+    short              nshlocks;
+    slock_t            shlock;
+    slock_t            exlock;
+    slock_t            comlock;
+    struct slock       *next;
+} SLock;
+
+extern void ExclusiveLock(int lockid);
+extern void ExclusiveUnlock(int lockid);
+extern bool LockIsFree(int lockid);
+#else /* HAS_TEST_AND_SET */
+
+typedef enum _LockId_ {
+    SHMEMLOCKID,
+    BINDINGLOCKID,
+    BUFMGRLOCKID,
+    LOCKMGRLOCKID,
+    SINVALLOCKID,
+
+#ifdef MAIN_MEMORY
+    MMCACHELOCKID,
+#endif /* MAIN_MEMORY */
+
+    PROCSTRUCTLOCKID,
+    OIDGENLOCKID,
+    FIRSTFREELOCKID
+} _LockId_;
+
+#define MAX_SPINS      FIRSTFREELOCKID
+
+#endif /* HAS_TEST_AND_SET */
+
+/*
+ * the following are originally in ipci.h but the prototypes have circular
+ * dependencies and most files include both ipci.h and ipc.h anyway, hence
+ * combined.
+ *
+ */
+
+/*
+ * Note:
+ *     These must not hash to DefaultIPCKey or PrivateIPCKey.
+ */
+#define SystemPortAddressGetIPCKey(address) \
+       (28597 * (address) + 17491)
+
+/*
+ * these keys are originally numbered from 1 to 12 consecutively but not
+ * all are used. The unused ones are removed.          - ay 4/95.
+ */
+#define IPCKeyGetBufferMemoryKey(key) \
+       ((key == PrivateIPCKey) ? key : 1 + (key))
+
+#define IPCKeyGetSIBufferMemoryBlock(key) \
+       ((key == PrivateIPCKey) ? key : 7 + (key))
+
+#define IPCKeyGetSLockSharedMemoryKey(key) \
+       ((key == PrivateIPCKey) ? key : 10 + (key))
+
+#define IPCKeyGetSpinLockSemaphoreKey(key) \
+       ((key == PrivateIPCKey) ? key : 11 + (key))
+#define IPCKeyGetWaitIOSemaphoreKey(key) \
+       ((key == PrivateIPCKey) ? key : 12 + (key))
+
+/* --------------------------
+ * NOTE: This macro must always give the highest numbered key as every backend
+ * process forked off by the postmaster will be trying to acquire a semaphore
+ * with a unique key value starting at key+14 and incrementing up.  Each
+ * backend uses the current key value then increments it by one.
+ * --------------------------
+ */
+#define IPCGetProcessSemaphoreInitKey(key) \
+       ((key == PrivateIPCKey) ? key : 14 + (key))
+
+/* ipci.c */
+extern IPCKey SystemPortAddressCreateIPCKey(SystemPortAddress address);
+extern void CreateSharedMemoryAndSemaphores(IPCKey key);
+extern void AttachSharedMemoryAndSemaphores(IPCKey key);
+
+#endif /* IPC_H */
diff --git a/src/backend/storage/ipc/Makefile.inc b/src/backend/storage/ipc/Makefile.inc
new file mode 100644 (file)
index 0000000..b7077f1
--- /dev/null
@@ -0,0 +1,15 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for storage/ipc
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= ipc.c ipci.c s_lock.c shmem.c shmqueue.c sinval.c \
+       sinvaladt.c spin.c
diff --git a/src/backend/storage/ipc/README b/src/backend/storage/ipc/README
new file mode 100644 (file)
index 0000000..281597d
--- /dev/null
@@ -0,0 +1,31 @@
+$Header$
+Mon Jul 18 11:09:22 PDT 1988  W.KLAS
+
+Cache invalidation synchronization routines:
+===========================================
+
+The cache synchronization is done using a message queue. Every
+backend can register a message which then has to be read by
+all backends. A message read by all backends is removed from the 
+queue automatically. If a message has been lost because the buffer
+was full, all backends that haven't read this message will be
+noticed that they have to reset their cache state. This is done
+at the time when they try to read the message queue.
+
+The message queue is implemented as a shared buffer segment. Actually,
+the queue is a circle to allow fast inserting, reading (invalidate data) and
+maintaining the buffer.
+
+Access to this shared message buffer is synchronized by the lock manager.
+The lock manager treats the buffer as a regular relation and sets
+relation level locks (with mode = LockWait) to block backends while 
+another backend is writing or reading the buffer. The identifiers used
+for this special 'relation' are database id = 0 and relation id = 0.
+
+The current implementation prints regular (e)log information
+when a message has been removed from the buffer because the buffer 
+is full, and a backend has to reset its cache state. The elog level
+is NOTICE. This can be used to improve teh behavior of backends
+when invalidating or reseting their cache state.
+
+
diff --git a/src/backend/storage/ipc/ipc.c b/src/backend/storage/ipc/ipc.c
new file mode 100644 (file)
index 0000000..6715f65
--- /dev/null
@@ -0,0 +1,718 @@
+/*-------------------------------------------------------------------------
+ *
+ * ipc.c--
+ *    POSTGRES inter-process communication definitions.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *
+ *    Currently, semaphores are used (my understanding anyway) in two
+ *    different ways:
+ *      1. as mutexes on machines that don't have test-and-set (eg.
+ *         mips R3000).
+ *      2. for putting processes to sleep when waiting on a lock 
+ *         and waking them up when the lock is free.
+ *    The number of semaphores in (1) is fixed and those are shared
+ *    among all backends. In (2), there is 1 semaphore per process and those
+ *    are not shared with anyone else.
+ *                                                        -ay 4/95
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <sys/types.h>
+#include <sys/file.h>
+#include <stdio.h>
+#include <errno.h>
+
+/* XXX - the following  dependency should be moved into the defaults.mk file */
+#ifndef        _IPC_
+#define _IPC_
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#endif
+
+#include "storage/ipc.h"
+#include "utils/memutils.h"
+#include "utils/elog.h"
+
+#if defined(PORTNAME_bsd44)
+int UsePrivateMemory = 1;
+#else
+int UsePrivateMemory = 0;
+#endif
+
+#if defined(PORTNAME_bsdi)
+/* hacka, hacka, hacka (XXX) */
+union semun {
+       int val; /* value for SETVAL */
+       struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */
+       ushort *array; /* array for GETALL & SETALL */
+};
+#endif
+
+
+/* ----------------------------------------------------------------
+ *                     exit() handling stuff
+ * ----------------------------------------------------------------
+ */
+
+#define MAX_ON_EXITS 20
+
+static struct ONEXIT {
+    void (*function)();
+    caddr_t arg;
+} onexit_list[ MAX_ON_EXITS ];
+
+static int onexit_index;
+
+typedef struct _PrivateMemStruct {
+    int id;
+    char *memptr;
+} PrivateMem;
+
+PrivateMem IpcPrivateMem[16];
+
+static int
+PrivateMemoryCreate(IpcMemoryKey memKey,
+                   uint32 size)
+{
+    static int memid = 0;
+    
+    UsePrivateMemory = 1;
+    
+    IpcPrivateMem[memid].id = memid;
+    IpcPrivateMem[memid].memptr = malloc(size);
+    if (IpcPrivateMem[memid].memptr == NULL)
+       elog(WARN, "PrivateMemoryCreate: not enough memory to malloc");
+    memset(IpcPrivateMem[memid].memptr, 0, size);      /* XXX PURIFY */
+    
+    return (memid++);
+}
+
+static char *
+PrivateMemoryAttach(IpcMemoryId memid)
+{
+    return ( IpcPrivateMem[memid].memptr );
+}
+
+
+/* ----------------------------------------------------------------
+ *     exitpg
+ *
+ *     this function calls all the callbacks registered
+ *     for it (to free resources) and then calls exit.
+ *     This should be the only function to call exit().
+ *     -cim 2/6/90
+ * ----------------------------------------------------------------
+ */
+static int exitpg_inprogress = 0;
+
+void
+exitpg(int code)
+{
+    int i;
+    
+    /* ----------------
+     * if exitpg_inprocess is true, then it means that we
+     *  are being invoked from within an on_exit() handler
+     *  and so we return immediately to avoid recursion.
+     * ----------------
+     */
+    if (exitpg_inprogress)
+       return;
+    
+    exitpg_inprogress = 1;
+    
+    /* ----------------
+     * call all the callbacks registered before calling exit().
+     * ----------------
+     */
+    for (i = onexit_index - 1; i >= 0; --i)
+       (*onexit_list[i].function)(code, onexit_list[i].arg);
+    
+    exit(code);
+}
+
+/* ------------------
+ * Run all of the on_exitpg routines but don't exit in the end.
+ * This is used by the postmaster to re-initialize shared memory and
+ * semaphores after a backend dies horribly
+ * ------------------
+ */
+void
+quasi_exitpg()
+{
+    int i;
+    
+    /* ----------------
+     * if exitpg_inprocess is true, then it means that we
+     *  are being invoked from within an on_exit() handler
+     *  and so we return immediately to avoid recursion.
+     * ----------------
+     */
+    if (exitpg_inprogress)
+       return;
+    
+    exitpg_inprogress = 1;
+    
+    /* ----------------
+     * call all the callbacks registered before calling exit().
+     * ----------------
+     */
+    for (i = onexit_index - 1; i >= 0; --i)
+       (*onexit_list[i].function)(0, onexit_list[i].arg);
+    
+    onexit_index = 0;
+    exitpg_inprogress = 0;
+}
+
+/* ----------------------------------------------------------------
+ *     on_exitpg
+ *
+ *     this function adds a callback function to the list of
+ *     functions invoked by exitpg().  -cim 2/6/90
+ * ----------------------------------------------------------------
+ */
+int
+on_exitpg(void (*function)(), caddr_t arg)
+{
+    if (onexit_index >= MAX_ON_EXITS)
+       return(-1);
+    
+    onexit_list[ onexit_index ].function = function;
+    onexit_list[ onexit_index ].arg = arg;
+    
+    ++onexit_index;
+    
+    return(0);
+}
+
+/****************************************************************************/
+/*   IPCPrivateSemaphoreKill(status, semId)                                */
+/*                                                                         */
+/****************************************************************************/
+static void
+IPCPrivateSemaphoreKill(int status,
+                       int semId) /* caddr_t */
+{
+    union semun        semun;
+    semctl(semId, 0, IPC_RMID, semun);
+}
+
+
+/****************************************************************************/
+/*   IPCPrivateMemoryKill(status, shmId)                                   */
+/*                                                                         */
+/****************************************************************************/
+static void
+IPCPrivateMemoryKill(int status,
+                    int shmId) /* caddr_t */
+{
+    if ( UsePrivateMemory ) {
+       /* free ( IpcPrivateMem[shmId].memptr ); */
+    } else {
+       if (shmctl(shmId, IPC_RMID, (struct shmid_ds *) NULL) < 0) {
+           elog(NOTICE, "IPCPrivateMemoryKill: shmctl(%d, %d, 0) failed: %m",
+                shmId, IPC_RMID);
+       }
+    } 
+}
+
+
+/****************************************************************************/
+/*   IpcSemaphoreCreate(semKey, semNum, permission, semStartValue)          */
+/*                                                                         */
+/*    - returns a semaphore identifier:                                            */
+/*                                                                         */
+/* if key doesn't exist: return a new id,      status:= IpcSemIdNotExist    */
+/* if key exists:        return the old id,    status:= IpcSemIdExist      */
+/* if semNum > MAX :     return # of argument, status:=IpcInvalidArgument   */
+/*                                                                         */
+/****************************************************************************/
+
+/*
+ * Note:
+ * XXX This should be split into two different calls.  One should
+ * XXX be used to create a semaphore set.  The other to "attach" a
+ * XXX existing set.  It should be an error for the semaphore set
+ * XXX to to already exist or for it not to, respectively.
+ *
+ *     Currently, the semaphore sets are "attached" and an error
+ *     is detected only when a later shared memory attach fails.
+ */
+
+IpcSemaphoreId
+IpcSemaphoreCreate(IpcSemaphoreKey semKey,
+                  int semNum,
+                  int permission,
+                  int semStartValue,
+                  int removeOnExit,
+                  int *status)
+{
+    int                i;
+    int                errStatus;
+    int                semId;
+    u_short    array[IPC_NMAXSEM];
+    union semun        semun;
+
+    /* get a semaphore if non-existent */
+    /* check arguments */
+    if (semNum > IPC_NMAXSEM || semNum <= 0)  {
+       *status = IpcInvalidArgument;
+       return(2);      /* returns the number of the invalid argument   */
+    }
+    
+    semId = semget(semKey, 0, 0);
+
+    if (semId == -1) {
+       *status = IpcSemIdNotExist;     /* there doesn't exist a semaphore */
+#ifdef DEBUG_IPC
+       fprintf(stderr,"calling semget with %d, %d , %d\n",
+               semKey,
+               semNum,
+               IPC_CREAT|permission );
+#endif
+       semId = semget(semKey, semNum, IPC_CREAT|permission);
+
+       if (semId < 0) {
+           perror("semget");
+           exitpg(3);
+       }
+       for (i = 0; i < semNum; i++) {
+           array[i] = semStartValue;
+       }
+       semun.array = array;
+       errStatus = semctl(semId, 0, SETALL, semun);
+       if (errStatus == -1) {
+           perror("semctl");
+       }
+       
+       if (removeOnExit)
+           on_exitpg(IPCPrivateSemaphoreKill, (caddr_t)semId);
+       
+    } else {
+       /* there is a semaphore id for this key */
+       *status = IpcSemIdExist;
+    }
+    
+#ifdef DEBUG_IPC
+    fprintf(stderr,"\nIpcSemaphoreCreate, status %d, returns %d\n",
+           *status,
+           semId );
+    fflush(stdout);
+    fflush(stderr);
+#endif
+    return(semId);
+}
+
+
+/****************************************************************************/
+/*   IpcSemaphoreSet()         - sets the initial value of the semaphore   */
+/*                                                                         */
+/*     note: the xxx_return variables are only used for debugging.         */
+/****************************************************************************/
+static int IpcSemaphoreSet_return;
+
+void
+IpcSemaphoreSet(int semId, int semno, int value)
+{
+    int                errStatus;
+    union semun        semun;
+    
+    semun.val = value;
+    errStatus = semctl(semId, semno, SETVAL, semun);
+    IpcSemaphoreSet_return = errStatus;
+    
+    if (errStatus == -1)
+       perror("semctl");
+}
+
+/****************************************************************************/
+/*   IpcSemaphoreKill(key)     - removes a semaphore                       */
+/*                                                                         */
+/****************************************************************************/
+void
+IpcSemaphoreKill(IpcSemaphoreKey key)
+{
+    int        semId;
+    union semun        semun;
+    
+    /* kill semaphore if existent */
+    
+    semId = semget(key, 0, 0);
+    if (semId != -1)
+       semctl(semId, 0, IPC_RMID, semun);
+}
+
+/****************************************************************************/
+/*   IpcSemaphoreLock(semId, sem, lock)        - locks a semaphore                 */
+/*                                                                         */
+/*     note: the xxx_return variables are only used for debugging.         */
+/****************************************************************************/
+static int IpcSemaphoreLock_return;
+
+void
+IpcSemaphoreLock(IpcSemaphoreId semId, int sem, int lock)
+{
+    extern int         errno;
+    int                        errStatus;
+    struct sembuf      sops;
+    
+    sops.sem_op = lock;
+    sops.sem_flg = 0;
+    sops.sem_num = sem;
+    
+    /* ----------------
+     * Note: if errStatus is -1 and errno == EINTR then it means we
+     *        returned from the operation prematurely because we were
+     *       sent a signal.  So we try and lock the semaphore again.
+     *       I am not certain this is correct, but the semantics aren't
+     *       clear it fixes problems with parallel abort synchronization,
+     *       namely that after processing an abort signal, the semaphore
+     *       call returns with -1 (and errno == EINTR) before it should.
+     *       -cim 3/28/90
+     * ----------------
+     */
+    do {
+       errStatus = semop(semId, &sops, 1);
+    } while (errStatus == -1 && errno == EINTR);
+    
+    IpcSemaphoreLock_return = errStatus;
+    
+    if (errStatus == -1) {
+       perror("semop");
+       exitpg(255);
+    }
+}
+
+/****************************************************************************/
+/*   IpcSemaphoreUnlock(semId, sem, lock)      - unlocks a semaphore       */
+/*                                                                         */
+/*     note: the xxx_return variables are only used for debugging.         */
+/****************************************************************************/
+static int IpcSemaphoreUnlock_return;
+
+void
+IpcSemaphoreUnlock(IpcSemaphoreId semId, int sem, int lock)
+{
+    extern int         errno;
+    int                        errStatus;
+    struct sembuf      sops;
+    
+    sops.sem_op = -lock;
+    sops.sem_flg = 0;
+    sops.sem_num = sem;
+    
+    
+    /* ----------------
+     * Note: if errStatus is -1 and errno == EINTR then it means we
+     *        returned from the operation prematurely because we were
+     *       sent a signal.  So we try and lock the semaphore again.
+     *       I am not certain this is correct, but the semantics aren't
+     *       clear it fixes problems with parallel abort synchronization,
+     *       namely that after processing an abort signal, the semaphore
+     *       call returns with -1 (and errno == EINTR) before it should.
+     *       -cim 3/28/90
+     * ----------------
+     */
+    do {
+       errStatus = semop(semId, &sops, 1);
+    } while (errStatus == -1 && errno == EINTR);
+    
+    IpcSemaphoreUnlock_return = errStatus;
+    
+    if (errStatus == -1) {
+       perror("semop");
+       exitpg(255);
+    }
+}
+
+int
+IpcSemaphoreGetCount(IpcSemaphoreId    semId, int sem)
+{
+    int semncnt;
+    union semun dummy; /* for Solaris */
+    
+    semncnt = semctl(semId, sem, GETNCNT, dummy);
+    return semncnt;
+}
+
+int
+IpcSemaphoreGetValue(IpcSemaphoreId    semId, int sem)
+{
+    int semval;
+    union semun dummy; /* for Solaris */
+    
+    semval = semctl(semId, sem, GETVAL, dummy);
+    return semval;
+}
+
+/****************************************************************************/
+/*   IpcMemoryCreate(memKey)                                               */
+/*                                                                         */
+/*    - returns the memory identifier, if creation succeeds                */
+/*     returns IpcMemCreationFailed, if failure                            */
+/****************************************************************************/
+
+IpcMemoryId
+IpcMemoryCreate(IpcMemoryKey memKey, uint32 size, int permission)
+{
+    IpcMemoryId         shmid;
+    
+    if (memKey == PrivateIPCKey) {
+       /* private */
+       shmid = PrivateMemoryCreate(memKey, size);
+    }else {
+       shmid = shmget(memKey, size, IPC_CREAT|permission); 
+    }
+
+    if (shmid < 0) {
+       fprintf(stderr,"IpcMemoryCreate: memKey=%d , size=%d , permission=%d", 
+               memKey, size , permission );
+       perror("IpcMemoryCreate: shmget(..., create, ...) failed");
+       return(IpcMemCreationFailed);
+    }
+    
+    /* if (memKey == PrivateIPCKey) */
+    on_exitpg(IPCPrivateMemoryKill, (caddr_t)shmid);
+    
+    return(shmid);
+}
+
+/****************************************************************************/
+/*  IpcMemoryIdGet(memKey, size)    returns the shared memory Id           */
+/*                                 or IpcMemIdGetFailed                    */
+/****************************************************************************/
+IpcMemoryId
+IpcMemoryIdGet(IpcMemoryKey memKey, uint32 size)
+{
+    IpcMemoryId        shmid;
+    
+    shmid = shmget(memKey, size, 0);
+    
+    if (shmid < 0) {
+       fprintf(stderr,"IpcMemoryIdGet: memKey=%d , size=%d , permission=%d", 
+               memKey, size , 0 );
+       perror("IpcMemoryIdGet:  shmget() failed");
+       return(IpcMemIdGetFailed);
+    }
+    
+    return(shmid);
+}
+
+/****************************************************************************/
+/*  IpcMemoryDetach(status, shmaddr)   removes a shared memory segment     */
+/*                                     from a backend address space        */
+/*  (only called by backends running under the postmaster)                 */
+/****************************************************************************/
+void
+IpcMemoryDetach(int status, char *shmaddr)
+{
+    if (shmdt(shmaddr) < 0) {
+       elog(NOTICE, "IpcMemoryDetach: shmdt(0x%x): %m", shmaddr);
+    }
+}
+
+/****************************************************************************/
+/*  IpcMemoryAttach(memId)    returns the adress of shared memory          */
+/*                           or IpcMemAttachFailed                         */
+/*                                                                         */
+/* CALL IT:  addr = (struct <MemoryStructure> *) IpcMemoryAttach(memId);    */
+/*                                                                         */
+/****************************************************************************/
+char *
+IpcMemoryAttach(IpcMemoryId memId)
+{
+    char       *memAddress;
+    
+    if (UsePrivateMemory) {
+       memAddress = (char *) PrivateMemoryAttach(memId);
+    } else {
+       memAddress = (char *) shmat(memId, 0, 0);
+    }
+    
+    /* if ( *memAddress == -1) { XXX ??? */
+    if ( memAddress == (char *)-1) {
+       perror("IpcMemoryAttach: shmat() failed");
+       return(IpcMemAttachFailed);
+    }
+    
+    if (!UsePrivateMemory)
+       on_exitpg(IpcMemoryDetach, (caddr_t) memAddress);
+    
+    return((char *) memAddress);
+}
+
+
+/****************************************************************************/
+/*  IpcMemoryKill(memKey)              removes a shared memory segment     */
+/*  (only called by the postmaster and standalone backends)                */
+/****************************************************************************/
+void
+IpcMemoryKill(IpcMemoryKey memKey)
+{      
+    IpcMemoryId                shmid;
+    
+    if (!UsePrivateMemory && (shmid = shmget(memKey, 0, 0)) >= 0) {
+       if (shmctl(shmid, IPC_RMID, (struct shmid_ds *) NULL) < 0) {
+           elog(NOTICE, "IpcMemoryKill: shmctl(%d, %d, 0) failed: %m",
+                shmid, IPC_RMID);
+       }
+    }
+} 
+
+#ifdef HAS_TEST_AND_SET
+/* ------------------
+ *  use hardware locks to replace semaphores for sequent machines
+ *  to avoid costs of swapping processes and to provide unlimited
+ *  supply of locks.
+ * ------------------
+ */
+static SLock *SLockArray = NULL;
+static SLock **FreeSLockPP;
+static int *UnusedSLockIP;
+static slock_t *SLockMemoryLock;
+static IpcMemoryId SLockMemoryId = -1;
+
+struct ipcdummy {              /* to get alignment/size right */
+    SLock      *free;
+    int                unused;
+    slock_t    memlock;
+    SLock      slocks[NSLOCKS];
+};
+static int SLockMemorySize = sizeof(struct ipcdummy);
+
+void
+CreateAndInitSLockMemory(IPCKey key)
+{
+    int id;
+    SLock *slckP;
+    
+    SLockMemoryId = IpcMemoryCreate(key,
+                                   SLockMemorySize,
+                                   0700);
+    AttachSLockMemory(key);
+    *FreeSLockPP = NULL;
+    *UnusedSLockIP = (int)FIRSTFREELOCKID;
+    for (id=0; id<(int)FIRSTFREELOCKID; id++) {
+       slckP = &(SLockArray[id]);
+       S_INIT_LOCK(&(slckP->locklock));
+       slckP->flag = NOLOCK;
+       slckP->nshlocks = 0;
+       S_INIT_LOCK(&(slckP->shlock));
+       S_INIT_LOCK(&(slckP->exlock));
+       S_INIT_LOCK(&(slckP->comlock));
+       slckP->next = NULL;
+    }
+    return;
+}
+
+void
+AttachSLockMemory(IPCKey key)
+{
+    struct ipcdummy *slockM;
+    
+    if (SLockMemoryId == -1)
+       SLockMemoryId = IpcMemoryIdGet(key,SLockMemorySize);
+    if (SLockMemoryId == -1)
+       elog(FATAL, "SLockMemory not in shared memory");
+    slockM = (struct ipcdummy *) IpcMemoryAttach(SLockMemoryId);
+    if (slockM == IpcMemAttachFailed)
+       elog(FATAL, "AttachSLockMemory: could not attach segment");
+    FreeSLockPP = (SLock **) &(slockM->free);
+    UnusedSLockIP = (int *) &(slockM->unused);
+    SLockMemoryLock = (slock_t *) &(slockM->memlock);
+    S_INIT_LOCK(SLockMemoryLock);
+    SLockArray = (SLock *) &(slockM->slocks[0]);
+    return;
+}
+
+
+#ifdef LOCKDEBUG
+#define PRINT_LOCK(LOCK) printf("(locklock = %d, flag = %d, nshlocks = %d, \
+shlock = %d, exlock =%d)\n", LOCK->locklock, \
+                               LOCK->flag, LOCK->nshlocks, LOCK->shlock, \
+                               LOCK->exlock)
+#endif
+
+void
+ExclusiveLock(int lockid)
+{
+    SLock *slckP;
+    slckP = &(SLockArray[lockid]);
+#ifdef LOCKDEBUG
+    printf("ExclusiveLock(%d)\n", lockid);
+    printf("IN: ");
+    PRINT_LOCK(slckP);
+#endif
+ ex_try_again:
+    S_LOCK(&(slckP->locklock));
+    switch (slckP->flag) {
+    case NOLOCK:
+       slckP->flag = EXCLUSIVELOCK;
+       S_LOCK(&(slckP->exlock));
+       S_LOCK(&(slckP->shlock));
+       S_UNLOCK(&(slckP->locklock));
+#ifdef LOCKDEBUG
+       printf("OUT: ");
+       PRINT_LOCK(slckP);
+#endif
+       return;
+    case SHAREDLOCK:
+    case EXCLUSIVELOCK:
+       S_UNLOCK(&(slckP->locklock));
+       S_LOCK(&(slckP->exlock));
+       S_UNLOCK(&(slckP->exlock));
+       goto ex_try_again;
+    }
+}
+
+void
+ExclusiveUnlock(int lockid)
+{
+    SLock *slckP;
+    
+    slckP = &(SLockArray[lockid]);
+#ifdef LOCKDEBUG
+    printf("ExclusiveUnlock(%d)\n", lockid);
+    printf("IN: ");
+    PRINT_LOCK(slckP);
+#endif
+    S_LOCK(&(slckP->locklock));
+    /* -------------
+     *  give favor to read processes
+     * -------------
+     */
+    slckP->flag = NOLOCK;
+    if (slckP->nshlocks > 0) {
+       while (slckP->nshlocks > 0) {
+           S_UNLOCK(&(slckP->shlock));
+           S_LOCK(&(slckP->comlock));
+       }
+       S_UNLOCK(&(slckP->shlock));
+    }
+    else {
+       S_UNLOCK(&(slckP->shlock));
+    }
+    S_UNLOCK(&(slckP->exlock));
+    S_UNLOCK(&(slckP->locklock));
+#ifdef LOCKDEBUG
+    printf("OUT: ");
+    PRINT_LOCK(slckP);
+#endif
+    return;
+}
+
+bool
+LockIsFree(int lockid)
+{
+    return(SLockArray[lockid].flag == NOLOCK);
+}
+
+#endif /* HAS_TEST_AND_SET */
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
new file mode 100644 (file)
index 0000000..d111cee
--- /dev/null
@@ -0,0 +1,149 @@
+/*-------------------------------------------------------------------------
+ *
+ * ipci.c--
+ *    POSTGRES inter-process communication initialization code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+
+#include "storage/ipc.h"
+#include "storage/multilev.h"
+#include "utils/elog.h"
+#include "storage/sinval.h"
+#include "storage/bufmgr.h"
+#include "storage/proc.h"
+#include "storage/smgr.h"
+#include "storage/lock.h"
+#include "miscadmin.h"         /* for DebugLvl */
+
+/*
+ * SystemPortAddressCreateMemoryKey --
+ *     Returns a memory key given a port address.
+ */
+IPCKey
+SystemPortAddressCreateIPCKey(SystemPortAddress address)
+{
+    Assert(address < 32768);   /* XXX */
+    
+    return (SystemPortAddressGetIPCKey(address));
+}
+
+/*
+ * CreateSharedMemoryAndSemaphores --
+ *     Creates and initializes shared memory and semaphores.
+ */
+/**************************************************
+  
+  CreateSharedMemoryAndSemaphores
+  is called exactly *ONCE* by the postmaster.
+  It is *NEVER* called by the postgres backend
+  
+  0) destroy any existing semaphores for both buffer
+  and lock managers.
+  1) create the appropriate *SHARED* memory segments
+  for the two resource managers.
+  
+  **************************************************/
+
+void
+CreateSharedMemoryAndSemaphores(IPCKey key)
+{
+    int                size;
+    
+#ifdef HAS_TEST_AND_SET
+    /* ---------------
+     *  create shared memory for slocks
+     * --------------
+     */
+    CreateAndInitSLockMemory(IPCKeyGetSLockSharedMemoryKey(key));
+#endif
+    /* ----------------
+     * kill and create the buffer manager buffer pool (and semaphore)
+     * ----------------
+     */
+    CreateSpinlocks(IPCKeyGetSpinLockSemaphoreKey(key));
+    size = BufferShmemSize() + LockShmemSize();
+    
+#ifdef MAIN_MEMORY
+    size += MMShmemSize();
+#endif /* MAIN_MEMORY */
+    
+    if (DebugLvl > 1) {
+       fprintf(stderr, "binding ShmemCreate(key=%x, size=%d)\n",
+               IPCKeyGetBufferMemoryKey(key), size);
+    }
+    ShmemCreate(IPCKeyGetBufferMemoryKey(key), size);
+    ShmemBindingTabReset();
+    InitShmem(key, size);
+    InitBufferPool(key);
+    
+    /* ----------------
+     * do the lock table stuff
+     * ----------------
+     */
+    InitLocks();
+    InitMultiLevelLockm();
+    if (InitMultiLevelLockm() == INVALID_TABLEID)
+       elog(FATAL, "Couldn't create the lock table");
+
+    /* ----------------
+     *  do process table stuff
+     * ----------------
+     */
+    InitProcGlobal(key);
+    on_exitpg(ProcFreeAllSemaphores, 0);
+    
+    CreateSharedInvalidationState(key);
+}
+
+
+/*
+ * AttachSharedMemoryAndSemaphores --
+ *     Attachs existant shared memory and semaphores.
+ */
+void
+AttachSharedMemoryAndSemaphores(IPCKey key)
+{
+    int size;
+    
+    /* ----------------
+     * create rather than attach if using private key
+     * ----------------
+     */
+    if (key == PrivateIPCKey) {
+       CreateSharedMemoryAndSemaphores(key);
+       return;
+    }
+    
+#ifdef HAS_TEST_AND_SET
+    /* ----------------
+     *  attach the slock shared memory
+     * ----------------
+     */
+    AttachSLockMemory(IPCKeyGetSLockSharedMemoryKey(key));
+#endif
+    /* ----------------
+     * attach the buffer manager buffer pool (and semaphore)
+     * ----------------
+     */
+    size = BufferShmemSize() + LockShmemSize();
+    InitShmem(key, size);
+    InitBufferPool(key);
+    
+    /* ----------------
+     * initialize lock table stuff
+     * ----------------
+     */
+    InitLocks();
+    if (InitMultiLevelLockm() == INVALID_TABLEID)
+       elog(FATAL, "Couldn't attach to the lock table");
+    
+    AttachSharedInvalidationState(key);
+}
diff --git a/src/backend/storage/ipc/s_lock.c b/src/backend/storage/ipc/s_lock.c
new file mode 100644 (file)
index 0000000..1f3dadc
--- /dev/null
@@ -0,0 +1,440 @@
+/*-------------------------------------------------------------------------
+ *
+ * s_lock.c--
+ *     This file contains the implementation (if any) for spinlocks.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ *   DESCRIPTION
+ *     The following code fragment should be written (in assembly 
+ *     language) on machines that have a native test-and-set instruction:
+ *
+ *     void
+ *     S_LOCK(char_address)
+ *         char *char_address;
+ *     {
+ *         while (test_and_set(char_address))
+ *             ;
+ *     }
+ *
+ *     If this is not done, POSTGRES will default to using System V
+ *     semaphores (and take a large performance hit -- around 40% of
+ *     its time on a DS5000/240 is spent in semop(3)...).
+ *
+ *   NOTES
+ *     AIX has a test-and-set but the recommended interface is the cs(3)
+ *     system call.  This provides an 8-instruction (plus system call 
+ *     overhead) uninterruptible compare-and-set operation.  True 
+ *     spinlocks might be faster but using cs(3) still speeds up the 
+ *     regression test suite by about 25%.  I don't have an assembler
+ *     manual for POWER in any case.
+ *
+ */
+#ifdef WIN32
+#include <windows.h>
+#endif /* WIN32 */
+#include "storage/ipc.h"
+
+
+#if defined(HAS_TEST_AND_SET)
+
+#if defined (PORTNAME_next)
+/*
+ * NEXTSTEP (mach)
+ * slock_t is defined as a struct mutex.
+ */
+void
+S_LOCK(slock_t *lock)
+{
+       mutex_lock(lock);
+}
+void
+S_UNLOCK(slock_t *lock)
+{
+       mutex_unlock(lock);
+}
+void
+S_INIT_LOCK(slock_t *lock)
+{
+       mutex_init(lock);       
+}
+
+ /* S_LOCK_FREE should return 1 if lock is free; 0 if lock is locked */
+int
+ S_LOCK_FREE(slock_t *lock)
+{
+       /* For Mach, we have to delve inside the entrails of `struct  
+mutex'.  Ick! */
+       return (lock->lock == 0);
+}
+
+#endif /* PORTNAME_next */
+
+
+
+#if defined(PORTNAME_irix5)
+/*
+ * SGI IRIX 5
+ * slock_t is defined as a struct abilock_t, which has a single unsigned long 
+ * member.
+ * 
+ * This stuff may be supplemented in the future with Masato Kataoka's MIPS-II
+ * assembly from his NECEWS SVR4 port, but we probably ought to retain this
+ * for the R3000 chips out there.
+ */
+void
+S_LOCK(slock_t *lock)
+{
+       /* spin_lock(lock); */
+       while (!acquire_lock(lock))
+           ;
+}
+
+void
+S_UNLOCK(slock_t *lock)
+{
+       (void)release_lock(lock);
+}
+
+void
+S_INIT_LOCK(slock_t *lock)
+{
+       (void)init_lock(lock);  
+}
+
+/* S_LOCK_FREE should return 1 if lock is free; 0 if lock is locked */
+int
+S_LOCK_FREE(slock_t *lock)
+{
+       return(stat_lock(lock)==UNLOCKED); 
+}
+
+#endif /* PORTNAME_irix5 */
+
+
+/*
+ * OSF/1 (Alpha AXP)
+ *
+ * Note that slock_t on the Alpha AXP is msemaphore instead of char
+ * (see storage/ipc.h).
+ */
+
+#if defined(PORTNAME_alpha)
+
+void
+S_LOCK(slock_t *lock)
+{
+    while (msem_lock(lock, MSEM_IF_NOWAIT) < 0)
+       ;
+}
+
+void
+S_UNLOCK(slock_t *lock)
+{
+    (void) msem_unlock(lock, 0);
+}
+
+void
+S_INIT_LOCK(slock_t *lock)
+{
+    (void) msem_init(lock, MSEM_UNLOCKED);
+}
+
+int
+S_LOCK_FREE(slock_t *lock)
+{
+    return(lock->msem_state ? 0 : 1);
+}
+
+#endif /* PORTNAME_alpha */
+
+/*
+ * Solaris 2
+ */
+
+#if defined(PORTNAME_sparc_solaris)
+
+/* defined in port/.../tas.s */
+extern int tas(slock_t *lock);
+
+void
+S_LOCK(slock_t *lock)
+{
+    while (tas(lock))
+       ;
+}
+
+void
+S_UNLOCK(slock_t *lock)
+{
+    *lock = 0;
+}
+
+void
+S_INIT_LOCK(slock_t *lock)
+{
+    S_UNLOCK(lock);
+}
+
+#endif /* PORTNAME_sparc_solaris */
+
+/*
+ * AIX (POWER)
+ *
+ * Note that slock_t on POWER/POWER2/PowerPC is int instead of char
+ * (see storage/ipc.h).
+ */
+
+#if defined(PORTNAME_aix)
+
+void
+S_LOCK(slock_t *lock)
+{
+    while (cs((int *) lock, 0, 1))
+       ;
+}
+
+void
+S_UNLOCK(slock_t *lock)
+{
+    *lock = 0;
+}
+
+void
+S_INIT_LOCK(slock_t *lock)
+{
+    S_UNLOCK(lock);
+}
+
+#endif /* PORTNAME_aix */
+
+/*
+ * HP-UX (PA-RISC)
+ *
+ * Note that slock_t on PA-RISC is a structure instead of char
+ * (see storage/ipc.h).
+ */
+
+#if defined(PORTNAME_hpux)
+
+/* defined in port/.../tas.s */
+extern int tas(slock_t *lock);
+
+/*
+* a "set" slock_t has a single word cleared.  a "clear" slock_t has 
+* all words set to non-zero.
+*/
+static slock_t clear_lock = { -1, -1, -1, -1 };
+
+void
+S_LOCK(slock_t *lock)
+{
+    while (tas(lock))
+       ;
+}
+
+void
+S_UNLOCK(slock_t *lock)
+{
+    *lock = clear_lock;        /* struct assignment */
+}
+
+void
+S_INIT_LOCK(slock_t *lock)
+{
+    S_UNLOCK(lock);
+}
+
+int
+S_LOCK_FREE(slock_t *lock)
+{
+    register int *lock_word = (int *) (((long) lock + 15) & ~15);
+
+    return(*lock_word != 0);
+}
+
+#endif /* PORTNAME_hpux */
+
+/*
+ * sun3
+ */
+#if (defined(sun) && ! defined(sparc))
+
+void    
+S_LOCK(slock_t *lock)
+{
+    while (tas(lock));
+}
+
+void
+S_UNLOCK(slock_t *lock)
+{
+    *lock = 0;
+}
+
+void
+S_INIT_LOCK(slock_t *lock)
+{
+    S_UNLOCK(lock);
+}
+
+static int
+tas_dummy()
+{
+    asm("LLA0:");
+    asm("      .data");
+    asm("      .text");
+    asm("|#PROC# 04");
+    asm("      .globl  _tas");
+    asm("_tas:");
+    asm("|#PROLOGUE# 1");
+    asm("      movel   sp@(0x4),a0");
+    asm("      tas     a0@");
+    asm("      beq     LLA1");
+    asm("      moveq   #-128,d0");
+    asm("      rts");
+    asm("LLA1:");
+    asm("      moveq   #0,d0");
+    asm("      rts");
+    asm("      .data");
+}
+
+#endif
+
+/*
+ * SPARC (SunOS 4)
+ */
+
+#if defined(PORTNAME_sparc)
+
+/* if we're using -ansi w/ gcc, use __asm__ instead of asm */
+#if defined(__STRICT_ANSI__)
+#define asm(x)  __asm__(x)
+#endif 
+
+static int
+tas_dummy()
+{
+    asm(".seg \"data\"");
+    asm(".seg \"text\"");
+    asm(".global _tas");
+    asm("_tas:");
+    
+    /*
+     * Sparc atomic test and set (sparc calls it "atomic load-store")
+     */
+    
+    asm("ldstub [%r8], %r8");
+    
+    /*
+     * Did test and set actually do the set?
+     */
+    
+    asm("tst %r8");
+    
+    asm("be,a ReturnZero");
+    
+    /*
+     * otherwise, just return.
+     */
+    
+    asm("clr %r8");
+    asm("mov 0x1, %r8");
+    asm("ReturnZero:");
+    asm("retl");
+    asm("nop");
+}
+
+void
+S_LOCK(unsigned char *addr)
+{
+    while (tas(addr));
+}
+
+
+/*
+ * addr should be as in the above S_LOCK routine
+ */
+void
+S_UNLOCK(unsigned char *addr)
+{
+    *addr = 0;
+}
+
+void
+S_INIT_LOCK(unsigned char *addr)
+{
+    *addr = 0;
+}
+
+#endif /* PORTNAME_sparc */
+
+/*
+ * Linux and friends
+ */
+
+#if defined(PORTNAME_linux) || defined(PORTNAME_BSD44_derived)
+
+int
+tas(slock_t *m)
+{
+    slock_t res;
+    __asm__("xchgb %0,%1":"=q" (res),"=m" (*m):"0" (0x1));
+    return(res);
+}
+
+void
+S_LOCK(slock_t *lock)
+{
+    while (tas(lock))
+       ;
+}
+
+void
+S_UNLOCK(slock_t *lock)
+{
+    *lock = 0;
+}
+
+void
+S_INIT_LOCK(slock_t *lock)
+{
+    S_UNLOCK(lock);
+}
+
+#endif /* PORTNAME_linux || PORTNAME_BSD44_derived */
+
+
+#endif /* HAS_TEST_AND_SET */
+
+
+#ifdef WIN32
+void
+S_LOCK(HANDLE *lock)
+{
+      int x = 0;
+      x = x / x;
+}
+
+void
+S_UNLOCK(HANDLE *lock)
+{
+      int x = 0;
+      x = x / x;
+}
+
+void
+S_INIT_LOCK(HANDLE *lock)
+{
+      int x = 0;
+      x = x / x;
+}
+#endif /*WIN32*/
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
new file mode 100644 (file)
index 0000000..30f43a0
--- /dev/null
@@ -0,0 +1,561 @@
+/*-------------------------------------------------------------------------
+ *
+ * shmem.c--
+ *    create shared memory and initialize shared memory data structures.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * POSTGRES processes share one or more regions of shared memory.
+ * The shared memory is created by a postmaster and is "attached to"
+ * by each of the backends.  The routines in this file are used for
+ * allocating and binding to shared memory data structures.
+ *
+ * NOTES:
+ *     (a) There are three kinds of shared memory data structures
+ *  available to POSTGRES: fixed-size structures, queues and hash 
+ *  tables.  Fixed-size structures contain things like global variables
+ *  for a module and should never be allocated after the process 
+ *  initialization phase.  Hash tables have a fixed maximum size, but
+ *  their actual size can vary dynamically.  When entries are added
+ *  to the table, more space is allocated.  Queues link data structures 
+ *  that have been allocated either as fixed size structures or as hash 
+ *  buckets.  Each shared data structure has a string name to identify 
+ *  it (assigned in the module that declares it).
+ *
+ *     (b) During initialization, each module looks for its
+ *  shared data structures in a hash table called the "Binding Table".
+ *  If the data structure is not present, the caller can allocate
+ *  a new one and initialize it.  If the data structure is present, 
+ *  the caller "attaches" to the structure by initializing a pointer 
+ *  in the local address space.  
+ *     The binding table has two purposes: first, it gives us
+ *  a simple model of how the world looks when a backend process 
+ *  initializes.  If something is present in the binding table,
+ *  it is initialized.  If it is not, it is uninitialized.  Second,
+ *  the binding table allows us to allocate shared memory on demand
+ *  instead of trying to preallocate structures and hard-wire the
+ *  sizes and locations in header files.  If you are using a lot
+ *  of shared memory in a lot of different places (and changing
+ *  things during development), this is important.
+ *
+ *     (c) memory allocation model: shared memory can never be 
+ *  freed, once allocated.   Each hash table has its own free list,
+ *  so hash buckets can be reused when an item is deleted.  However,
+ *  if one hash table grows very large and then shrinks, its space
+ *  cannot be redistributed to other tables.  We could build a simple
+ *  hash bucket garbage collector if need be.  Right now, it seems
+ *  unnecessary.
+ *
+ *     See InitSem() in sem.c for an example of how to use the
+ *  binding table.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include "postgres.h"
+#include "storage/ipc.h"
+#include "storage/shmem.h"
+#include "storage/spin.h"
+#include "utils/hsearch.h"
+#include "utils/elog.h"
+
+/* shared memory global variables */
+
+unsigned long  ShmemBase = 0;  /* start and end address of
+                                * shared memory
+                                */
+static unsigned long  ShmemEnd = 0;
+static unsigned long  ShmemSize = 0;   /* current size (and default) */
+
+SPINLOCK      ShmemLock;       /* lock for shared memory allocation */
+
+SPINLOCK      BindingLock;     /* lock for binding table access */
+
+static unsigned long *ShmemFreeStart = NULL;   /* pointer to the OFFSET of
+                                                * first free shared memory
+                                                */
+static unsigned long *ShmemBindingTabOffset = NULL; /* start of the binding
+                                                    * table (for bootstrap)
+                                                    */
+static int  ShmemBootstrap = FALSE;    /* flag becomes true when shared mem
+                                        * is created by POSTMASTER
+                                        */
+
+static HTAB *BindingTable = NULL;
+
+/* ---------------------
+ * ShmemBindingTabReset() - Resets the binding table to NULL....
+ * useful when the postmaster destroys existing shared memory
+ * and creates all new segments after a backend crash.
+ * ----------------------
+ */
+void
+ShmemBindingTabReset()
+{
+    BindingTable = (HTAB *)NULL;
+}
+
+/*
+ *  CreateSharedRegion() --
+ *
+ *  This routine is called once by the postmaster to
+ *  initialize the shared buffer pool.  Assume there is
+ *  only one postmaster so no synchronization is necessary
+ *  until after this routine completes successfully.
+ *
+ * key is a unique identifier for the shmem region.
+ * size is the size of the region.
+ */
+static IpcMemoryId ShmemId;
+
+void
+ShmemCreate(unsigned int key, unsigned int size)
+{
+    if (size)
+       ShmemSize = size;
+    /* create shared mem region */
+    if ((ShmemId=IpcMemoryCreate(key,ShmemSize,IPCProtection))
+       ==IpcMemCreationFailed) {
+       elog(FATAL,"ShmemCreate: cannot create region");
+       exit(1);
+    }
+    
+    /* ShmemBootstrap is true if shared memory has been
+     * created, but not yet initialized.  Only the 
+     * postmaster/creator-of-all-things should have
+     * this flag set.
+     */
+    ShmemBootstrap = TRUE;
+}
+
+/*
+ *  InitShmem() -- map region into process address space
+ *     and initialize shared data structures.
+ *
+ */
+int
+InitShmem(unsigned int key, unsigned int size)
+{
+    Pointer    sharedRegion;
+    unsigned long currFreeSpace;
+    
+    HASHCTL    info;
+    int                hash_flags;
+    BindingEnt *       result,item;
+    bool       found;
+    IpcMemoryId        shmid;
+    
+    /* if zero key, use default memory size */
+    if (size)
+       ShmemSize = size;
+    
+    /* default key is 0 */
+    
+    /* attach to shared memory region (SysV or BSD OS specific) */
+    if (ShmemBootstrap && key == PrivateIPCKey)
+       /* if we are running backend alone */
+       shmid = ShmemId;
+    else
+       shmid = IpcMemoryIdGet(IPCKeyGetBufferMemoryKey(key), ShmemSize);
+    sharedRegion = IpcMemoryAttach(shmid);
+    if (sharedRegion == NULL) {
+       elog(FATAL,"AttachSharedRegion: couldn't attach to shmem\n");
+       return(FALSE);
+    }
+    
+    /* get pointers to the dimensions of shared memory */
+    ShmemBase = (unsigned long) sharedRegion;
+    ShmemEnd  = (unsigned long) sharedRegion + ShmemSize;
+    currFreeSpace = 0;
+    
+    /* First long in shared memory is the count of available space */
+    ShmemFreeStart = (unsigned long *) ShmemBase;
+    /* next is a shmem pointer to the binding table */
+    ShmemBindingTabOffset = ShmemFreeStart + 1;
+    
+    currFreeSpace += 
+       sizeof(ShmemFreeStart) + sizeof(ShmemBindingTabOffset);
+    
+    /* bootstrap initialize spin locks so we can start to use the
+     * allocator and binding table.
+     */
+    if (! InitSpinLocks(ShmemBootstrap, IPCKeyGetSpinLockSemaphoreKey(key))) {
+       return(FALSE);
+    }
+    
+    /* We have just allocated additional space for two spinlocks.
+     * Now setup the global free space count 
+     */
+    if (ShmemBootstrap) {
+       *ShmemFreeStart = currFreeSpace;
+    }
+    
+    /* if ShmemFreeStart is NULL, then the allocator won't work */
+    Assert(*ShmemFreeStart);
+    
+    /* create OR attach to the shared memory binding table */
+    info.keysize = BTABLE_KEYSIZE;
+    info.datasize = BTABLE_DATASIZE;
+    hash_flags = (HASH_ELEM);
+    
+    /* This will acquire the binding table lock, but not release it. */
+    BindingTable = ShmemInitHash("BindingTable",
+                                BTABLE_SIZE,BTABLE_SIZE,
+                                &info,hash_flags);
+    
+    if (! BindingTable) {
+       elog(FATAL,"InitShmem: couldn't initialize Binding Table");
+       return(FALSE);
+    }
+    
+    /* Now, check the binding table for an entry to the binding
+     * table.  If there is an entry there, someone else created
+     * the table.  Otherwise, we did and we have to initialize it.
+     */
+    memset(item.key, 0, BTABLE_KEYSIZE);
+    strncpy(item.key,"BindingTable",BTABLE_KEYSIZE);
+    
+    result = (BindingEnt *) 
+       hash_search(BindingTable,(char *) &item,HASH_ENTER, &found);
+    
+    
+    if (! result ) {
+       elog(FATAL,"InitShmem: corrupted binding table");
+       return(FALSE);
+    }
+    
+    if (! found) {
+       /* bootstrapping shmem: we have to initialize the 
+        * binding table now.
+        */
+       
+       Assert(ShmemBootstrap);
+       result->location = MAKE_OFFSET(BindingTable->hctl);
+       *ShmemBindingTabOffset = result->location;
+       result->size = BTABLE_SIZE;
+       
+       ShmemBootstrap = FALSE;
+       
+    }  else {
+       Assert(! ShmemBootstrap);
+    }
+    /* now release the lock acquired in ShmemHashInit */
+    SpinRelease (BindingLock);
+    
+    Assert (result->location == MAKE_OFFSET(BindingTable->hctl));
+    
+    return(TRUE);
+}
+
+/*
+ * ShmemAlloc -- allocate word-aligned byte string from
+ *     shared memory
+ *
+ * Assumes ShmemLock and ShmemFreeStart are initialized.
+ * Returns: real pointer to memory or NULL if we are out
+ *     of space.  Has to return a real pointer in order 
+ *     to be compatable with malloc().
+ */
+long *
+ShmemAlloc(unsigned long size)
+{
+    unsigned long tmpFree;
+    long *newSpace;
+    
+    /*
+     * ensure space is word aligned.
+     *
+     * Word-alignment is not good enough. We have to be more
+     * conservative: doubles need 8-byte alignment. (We probably only need
+     * this on RISC platforms but this is not a big waste of space.) 
+     *                                                - ay 12/94
+     */
+    if (size % sizeof(double))
+       size += sizeof(double) - (size % sizeof(double));
+    
+    Assert(*ShmemFreeStart);
+    
+    SpinAcquire(ShmemLock);
+    
+    tmpFree = *ShmemFreeStart + size;
+    if (tmpFree <= ShmemSize) {
+       newSpace = (long *)MAKE_PTR(*ShmemFreeStart);
+       *ShmemFreeStart += size;
+    } else {
+       newSpace = NULL;
+    }
+    
+    SpinRelease(ShmemLock); 
+    
+    if (! newSpace) {
+       elog(NOTICE,"ShmemAlloc: out of memory ");
+    }
+    return(newSpace);
+}
+
+/*
+ * ShmemIsValid -- test if an offset refers to valid shared memory 
+ * 
+ * Returns TRUE if the pointer is valid.
+ */
+int
+ShmemIsValid(unsigned long addr)
+{
+    return ((addr<ShmemEnd) && (addr>=ShmemBase));
+}
+
+/*
+ * ShmemInitHash -- Create/Attach to and initialize 
+ *     shared memory hash table.
+ *
+ * Notes:
+ *
+ * assume caller is doing some kind of synchronization
+ * so that two people dont try to create/initialize the
+ * table at once.  Use SpinAlloc() to create a spinlock
+ * for the structure before creating the structure itself.
+ */
+HTAB *
+ShmemInitHash(char *name,      /* table string name for binding */
+             long init_size,   /* initial size */
+             long max_size,    /* max size of the table */
+             HASHCTL *infoP,   /* info about key and bucket size */
+             int hash_flags)   /* info about infoP */
+{
+    bool       found;
+    long  *    location;
+    
+    /* shared memory hash tables have a fixed max size so that the
+     * control structures don't try to grow.  The segbase is for
+     * calculating pointer values.  The shared memory allocator
+     * must be specified.
+     */
+    infoP->segbase = (long *) ShmemBase;
+    infoP->alloc = ShmemAlloc;
+    infoP->max_size = max_size;
+    hash_flags |= HASH_SHARED_MEM;
+    
+    /* look it up in the binding table */
+    location = 
+       ShmemInitStruct(name,my_log2(max_size) + sizeof(HHDR),&found);
+    
+    /* binding table is corrupted.  Let someone else give the 
+     * error message since they have more information 
+     */
+    if (location == NULL) {
+       return(0);
+    }
+    
+    /* it already exists, attach to it rather than allocate and
+     * initialize new space 
+     */
+    if (found) {
+       hash_flags |= HASH_ATTACH;
+    }
+    
+    /* these structures were allocated or bound in ShmemInitStruct */
+    /* control information and parameters */
+    infoP->hctl = (long *) location;
+    /* directory for hash lookup */
+    infoP->dir = (long *) (location + sizeof(HHDR));
+    
+    return(hash_create(init_size, infoP, hash_flags));;
+}
+
+/*
+ * ShmemPIDLookup -- lookup process data structure using process id
+ *
+ * Returns: TRUE if no error.  locationPtr is initialized if PID is
+ *     found in the binding table.
+ *
+ * NOTES:
+ *     only information about success or failure is the value of
+ *     locationPtr.
+ */
+bool
+ShmemPIDLookup(int pid, SHMEM_OFFSET* locationPtr)
+{
+    BindingEnt *       result,item;
+    bool       found;
+    
+    Assert (BindingTable);
+    memset(item.key, 0, BTABLE_KEYSIZE);
+    sprintf(item.key,"PID %d",pid);
+    
+    SpinAcquire(BindingLock);
+    result = (BindingEnt *) 
+       hash_search(BindingTable,(char *) &item, HASH_ENTER, &found);
+    
+    if (! result) {
+       
+       SpinRelease(BindingLock);
+       elog(WARN,"ShmemInitPID: BindingTable corrupted");
+       return(FALSE);
+       
+    } 
+    
+    if (found) {
+       *locationPtr = result->location;
+    } else {
+       result->location = *locationPtr;
+    }
+    
+    SpinRelease(BindingLock);
+    return (TRUE);
+}
+
+/*
+ * ShmemPIDDestroy -- destroy binding table entry for process
+ *     using process id
+ *
+ * Returns: offset of the process struct in shared memory or
+ *     INVALID_OFFSET if not found.
+ *
+ * Side Effect: removes the entry from the binding table
+ */
+SHMEM_OFFSET
+ShmemPIDDestroy(int pid)
+{
+    BindingEnt *       result,item;
+    bool       found;
+    SHMEM_OFFSET  location;
+    
+    Assert(BindingTable);
+    
+    memset(item.key, 0, BTABLE_KEYSIZE);
+    sprintf(item.key,"PID %d",pid);
+    
+    SpinAcquire(BindingLock);
+    result = (BindingEnt *) 
+       hash_search(BindingTable,(char *) &item, HASH_REMOVE, &found);
+    
+    if (found)
+       location = result->location;
+    SpinRelease(BindingLock);
+    
+    if (! result) {
+       
+       elog(WARN,"ShmemPIDDestroy: PID table corrupted");
+       return(INVALID_OFFSET);
+       
+    } 
+    
+    if (found)
+       return (location);
+    else {
+       return(INVALID_OFFSET);
+    }
+}
+
+/*
+ * ShmemInitStruct -- Create/attach to a structure in shared
+ *     memory.
+ *
+ *  This is called during initialization to find or allocate
+ *             a data structure in shared memory.  If no other processes
+ *     have created the structure, this routine allocates space
+ *     for it.  If it exists already, a pointer to the existing
+ *     table is returned.  
+ *
+ *  Returns: real pointer to the object.  FoundPtr is TRUE if
+ *     the object is already in the binding table (hence, already
+ *     initialized).
+ */
+long *
+ShmemInitStruct(char *name, unsigned long size, bool *foundPtr)
+{
+    BindingEnt *       result,item;
+    long * structPtr;
+
+    strncpy(item.key,name,BTABLE_KEYSIZE);
+    item.location = BAD_LOCATION;
+    
+    SpinAcquire(BindingLock);
+    
+    if (! BindingTable) {
+       /* Assert() is a macro now. substitutes inside quotes. */
+       char *strname = "BindingTable";
+       
+       /* If the binding table doesnt exist, we fake it.
+        *
+        * If we are creating the first binding table, then let 
+        * shmemalloc() allocate the space for a new HTAB.  Otherwise,
+        * find the old one and return that.  Notice that the
+        * BindingLock is held until the binding table has been completely
+        * initialized.
+        */
+       Assert (! strcmp(name,strname)) ;
+       if (ShmemBootstrap) {
+           /* in POSTMASTER/Single process */
+           
+           *foundPtr = FALSE;
+           return((long *)ShmemAlloc(size));
+           
+       } else {
+           Assert (ShmemBindingTabOffset);
+           
+           *foundPtr = TRUE;
+           return((long *)MAKE_PTR(*ShmemBindingTabOffset));
+       }
+       
+       
+    } else {
+       /* look it up in the bindint table */
+       result = (BindingEnt *) 
+           hash_search(BindingTable,(char *) &item,HASH_ENTER, foundPtr);
+    }
+    
+    if (! result) {
+       
+       SpinRelease(BindingLock);
+       
+       elog(WARN,"ShmemInitStruct: Binding Table corrupted");
+       return(NULL);
+       
+    } else if (*foundPtr) {
+       /*
+        * Structure is in the binding table so someone else has allocated 
+        * it already.  The size better be the same as the size we are 
+        * trying to initialize to or there is a name conflict (or worse).
+        */
+       if (result->size != size) {
+           SpinRelease(BindingLock);
+           
+           elog(NOTICE,"ShmemInitStruct: BindingTable entry size is wrong");
+           /* let caller print its message too */
+           return(NULL);
+       }
+       structPtr = (long *)MAKE_PTR(result->location);
+    } else {
+       
+       /* It isn't in the table yet. allocate and initialize it */
+       structPtr = ShmemAlloc((long)size);
+       if (! structPtr) {
+           /* out of memory */
+           Assert (BindingTable);
+           (void) hash_search(BindingTable,(char *) &item,HASH_REMOVE, foundPtr);
+           SpinRelease(BindingLock);
+           *foundPtr = FALSE;
+           
+           elog(NOTICE,"ShmemInitStruct: cannot allocate '%s'",
+                name);
+           return(NULL);
+       } 
+       result->size = size;
+       result->location = MAKE_OFFSET(structPtr);
+    }
+    Assert (ShmemIsValid((unsigned long)structPtr));
+    
+    SpinRelease(BindingLock);
+    return(structPtr);
+}
+
+
+
diff --git a/src/backend/storage/ipc/shmqueue.c b/src/backend/storage/ipc/shmqueue.c
new file mode 100644 (file)
index 0000000..297c2a0
--- /dev/null
@@ -0,0 +1,251 @@
+/*-------------------------------------------------------------------------
+ *
+ * shmqueue.c--
+ *    shared memory linked lists
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *
+ * Package for managing doubly-linked lists in shared memory.
+ * The only tricky thing is that SHM_QUEUE will usually be a field 
+ * in a larger record.  SHMQueueGetFirst has to return a pointer
+ * to the record itself instead of a pointer to the SHMQueue field
+ * of the record.  It takes an extra pointer and does some extra
+ * pointer arithmetic to do this correctly.
+ *
+ * NOTE: These are set up so they can be turned into macros some day.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>             /* for sprintf() */
+#include "postgres.h"
+#include "storage/shmem.h"     /* where the declarations go */
+#include "utils/elog.h"
+
+/*#define SHMQUEUE_DEBUG*/
+#ifdef SHMQUEUE_DEBUG
+#define SHMQUEUE_DEBUG_DEL     /* deletions */
+#define SHMQUEUE_DEBUG_HD      /* head inserts */
+#define SHMQUEUE_DEBUG_TL      /* tail inserts */
+#define SHMQUEUE_DEBUG_ELOG NOTICE
+#endif /* SHMQUEUE_DEBUG */
+
+/*
+ * ShmemQueueInit -- make the head of a new queue point
+ *     to itself
+ */
+void
+SHMQueueInit(SHM_QUEUE *queue)
+{
+    Assert(SHM_PTR_VALID(queue));
+    (queue)->prev = (queue)->next = MAKE_OFFSET(queue);
+}
+
+/*
+ * SHMQueueIsDetached -- TRUE if element is not currently
+ *     in a queue.
+ */
+bool
+SHMQueueIsDetached(SHM_QUEUE *queue)
+{
+    Assert(SHM_PTR_VALID(queue));
+    return ((queue)->prev == INVALID_OFFSET);
+}
+
+/*
+ * SHMQueueElemInit -- clear an element's links
+ */
+void
+SHMQueueElemInit(SHM_QUEUE *queue)
+{
+    Assert(SHM_PTR_VALID(queue));
+    (queue)->prev = (queue)->next = INVALID_OFFSET;
+}
+
+/*
+ * SHMQueueDelete -- remove an element from the queue and
+ *     close the links
+ */
+void
+SHMQueueDelete(SHM_QUEUE *queue)
+{
+    SHM_QUEUE *nextElem = (SHM_QUEUE *) MAKE_PTR((queue)->next);
+    SHM_QUEUE *prevElem = (SHM_QUEUE *) MAKE_PTR((queue)->prev);
+    
+    Assert(SHM_PTR_VALID(queue));
+    Assert(SHM_PTR_VALID(nextElem));
+    Assert(SHM_PTR_VALID(prevElem));
+    
+#ifdef SHMQUEUE_DEBUG_DEL
+    dumpQ(queue, "in SHMQueueDelete: begin");
+#endif /* SHMQUEUE_DEBUG_DEL */
+    
+    prevElem->next =  (queue)->next;
+    nextElem->prev =  (queue)->prev;
+    
+#ifdef SHMQUEUE_DEBUG_DEL
+    dumpQ((SHM_QUEUE *)MAKE_PTR(queue->prev), "in SHMQueueDelete: end");
+#endif /* SHMQUEUE_DEBUG_DEL */
+}
+
+#ifdef SHMQUEUE_DEBUG
+void
+dumpQ(SHM_QUEUE *q, char *s)
+{
+    char elem[16];
+    char buf[1024];
+    SHM_QUEUE  *start = q;
+    int count = 0;
+    
+    sprintf(buf, "q prevs: %x", MAKE_OFFSET(q));
+    q = (SHM_QUEUE *)MAKE_PTR(q->prev);
+    while (q != start)
+       {
+           sprintf(elem, "--->%x", MAKE_OFFSET(q));
+           strcat(buf, elem);
+           q = (SHM_QUEUE *)MAKE_PTR(q->prev);
+           if (q->prev == MAKE_OFFSET(q))
+               break;
+           if (count++ > 40)
+               {
+                   strcat(buf, "BAD PREV QUEUE!!");
+                   break;
+               }
+       }
+    sprintf(elem, "--->%x", MAKE_OFFSET(q));
+    strcat(buf, elem);
+    elog(SHMQUEUE_DEBUG_ELOG, "%s: %s", s, buf);
+    
+    sprintf(buf, "q nexts: %x", MAKE_OFFSET(q));
+    count = 0;
+    q = (SHM_QUEUE *)MAKE_PTR(q->next);
+    while (q != start)
+       {
+           sprintf(elem, "--->%x", MAKE_OFFSET(q));
+           strcat(buf, elem);
+           q = (SHM_QUEUE *)MAKE_PTR(q->next);
+           if (q->next == MAKE_OFFSET(q))
+               break;
+           if (count++ > 10)
+               {
+                   strcat(buf, "BAD NEXT QUEUE!!");
+                   break;
+               }
+       }
+    sprintf(elem, "--->%x", MAKE_OFFSET(q));
+    strcat(buf, elem);
+    elog(SHMQUEUE_DEBUG_ELOG, "%s: %s", s, buf);
+}
+#endif /* SHMQUEUE_DEBUG */
+
+/*
+ * SHMQueueInsertHD -- put elem in queue between the queue head
+ *     and its "prev" element.
+ */
+void
+SHMQueueInsertHD(SHM_QUEUE *queue, SHM_QUEUE *elem)
+{
+    SHM_QUEUE *prevPtr = (SHM_QUEUE *) MAKE_PTR((queue)->prev);
+    SHMEM_OFFSET       elemOffset = MAKE_OFFSET(elem);
+    
+    Assert(SHM_PTR_VALID(queue));
+    Assert(SHM_PTR_VALID(elem));
+    
+#ifdef SHMQUEUE_DEBUG_HD
+    dumpQ(queue, "in SHMQueueInsertHD: begin");
+#endif /* SHMQUEUE_DEBUG_HD */
+    
+    (elem)->next = prevPtr->next;
+    (elem)->prev = queue->prev;
+    (queue)->prev = elemOffset;
+    prevPtr->next = elemOffset;
+    
+#ifdef SHMQUEUE_DEBUG_HD
+    dumpQ(queue, "in SHMQueueInsertHD: end");
+#endif /* SHMQUEUE_DEBUG_HD */
+}
+
+void
+SHMQueueInsertTL(SHM_QUEUE *queue, SHM_QUEUE *elem)
+{
+    SHM_QUEUE *nextPtr = (SHM_QUEUE *) MAKE_PTR((queue)->next);
+    SHMEM_OFFSET       elemOffset = MAKE_OFFSET(elem);
+    
+    Assert(SHM_PTR_VALID(queue));
+    Assert(SHM_PTR_VALID(elem));
+    
+#ifdef SHMQUEUE_DEBUG_TL
+    dumpQ(queue, "in SHMQueueInsertTL: begin");
+#endif /* SHMQUEUE_DEBUG_TL */
+    
+    (elem)->prev = nextPtr->prev;
+    (elem)->next = queue->next;
+    (queue)->next = elemOffset;
+    nextPtr->prev = elemOffset;
+    
+#ifdef SHMQUEUE_DEBUG_TL
+    dumpQ(queue, "in SHMQueueInsertTL: end");
+#endif /* SHMQUEUE_DEBUG_TL */
+}
+
+/*
+ * SHMQueueFirst -- Get the first element from a queue
+ *
+ * First element is queue->next.  If SHMQueue is part of
+ * a larger structure, we want to return a pointer to the
+ * whole structure rather than a pointer to its SHMQueue field.
+ * I.E. struct {
+ *     int             stuff;
+ *     SHMQueue        elem;
+ * } ELEMType; 
+ * when this element is in a queue (queue->next) is struct.elem.
+ * nextQueue allows us to calculate the offset of the SHMQueue
+ * field in the structure.
+ *
+ * call to SHMQueueFirst should take these parameters:
+ *
+ *   &(queueHead),&firstElem,&(firstElem->next)
+ *
+ * Note that firstElem may well be uninitialized.  if firstElem
+ * is initially K, &(firstElem->next) will be K+ the offset to
+ * next.
+ */
+void
+SHMQueueFirst(SHM_QUEUE *queue, Pointer *nextPtrPtr, SHM_QUEUE *nextQueue)
+{
+    SHM_QUEUE *elemPtr = (SHM_QUEUE *) MAKE_PTR((queue)->next);
+    
+    Assert(SHM_PTR_VALID(queue));
+    *nextPtrPtr = (Pointer) (((unsigned long) *nextPtrPtr) +
+                         ((unsigned long) elemPtr) - ((unsigned long) nextQueue)); 
+    
+    /*
+      nextPtrPtr a ptr to a structure linked in the queue
+      nextQueue is the SHMQueue field of the structure
+      *nextPtrPtr - nextQueue is 0 minus the offset of the queue 
+      field n the record 
+      elemPtr + (*nextPtrPtr - nexQueue) is the start of the
+      structure containing elemPtr.
+      */
+}
+
+/*
+ * SHMQueueEmpty -- TRUE if queue head is only element, FALSE otherwise
+ */
+bool
+SHMQueueEmpty(SHM_QUEUE *queue)
+{
+    Assert(SHM_PTR_VALID(queue));
+    
+    if (queue->prev == MAKE_OFFSET(queue)) 
+       {
+           Assert(queue->next = MAKE_OFFSET(queue));
+           return(TRUE);
+       }
+    return(FALSE);
+}
diff --git a/src/backend/storage/ipc/sinval.c b/src/backend/storage/ipc/sinval.c
new file mode 100644 (file)
index 0000000..73bd176
--- /dev/null
@@ -0,0 +1,169 @@
+/*-------------------------------------------------------------------------
+ *
+ * sinval.c--
+ *    POSTGRES shared cache invalidation communication code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/* #define INVALIDDEBUG        1 */
+
+#include "postgres.h"
+
+#include "storage/sinval.h"
+#include "storage/sinvaladt.h"
+#include "storage/spin.h"
+#include "utils/elog.h"
+
+extern SISeg           *shmInvalBuffer;/* the shared buffer segment, set by*/
+                                       /*   SISegmentAttach()              */
+extern BackendId       MyBackendId;
+extern BackendTag      MyBackendTag;
+
+SPINLOCK               SInvalLock = (SPINLOCK) NULL;
+
+/****************************************************************************/
+/*  CreateSharedInvalidationState(key)   Create a buffer segment           */
+/*                                                                         */
+/*  should be called only by the POSTMASTER                                */
+/****************************************************************************/
+void
+CreateSharedInvalidationState(IPCKey key)
+{
+    int        status;
+    
+    /* REMOVED
+       SISyncKill(IPCKeyGetSIBufferMemorySemaphoreKey(key));
+       SISyncInit(IPCKeyGetSIBufferMemorySemaphoreKey(key));
+       */
+    
+    /* SInvalLock gets set in spin.c, during spinlock init */
+    status = SISegmentInit(true, IPCKeyGetSIBufferMemoryBlock(key));
+    
+    if (status == -1) {
+       elog(FATAL, "CreateSharedInvalidationState: failed segment init");
+    }
+}
+/****************************************************************************/
+/*  AttachSharedInvalidationState(key)   Attach a buffer segment           */
+/*                                                                         */
+/*  should be called only by the POSTMASTER                                */
+/****************************************************************************/
+void
+AttachSharedInvalidationState(IPCKey key)
+{
+    int        status;
+    
+    if (key == PrivateIPCKey) {
+       CreateSharedInvalidationState(key);
+       return;
+    }
+    /* SInvalLock gets set in spin.c, during spinlock init */
+    status = SISegmentInit(false, IPCKeyGetSIBufferMemoryBlock(key));
+    
+    if (status == -1) {
+       elog(FATAL, "AttachSharedInvalidationState: failed segment init");
+    }
+}
+
+void
+InitSharedInvalidationState()
+{
+    SpinAcquire(SInvalLock);
+    if (!SIBackendInit(shmInvalBuffer))
+       {
+           SpinRelease(SInvalLock);
+           elog(FATAL, "Backend cache invalidation initialization failed");
+       }
+    SpinRelease(SInvalLock);
+}
+
+/*
+ * RegisterSharedInvalid --
+ *  Returns a new local cache invalidation state containing a new entry.
+ *
+ * Note:
+ *  Assumes hash index is valid.
+ *  Assumes item pointer is valid.
+ */
+/****************************************************************************/
+/*  RegisterSharedInvalid(cacheId, hashIndex, pointer)                     */
+/*                                                                         */
+/*  register a message in the buffer                                       */
+/*  should be called by a backend                                          */
+/****************************************************************************/
+void
+RegisterSharedInvalid(int cacheId, /* XXX */
+                     Index hashIndex,
+                     ItemPointer pointer)
+{
+    SharedInvalidData   newInvalid;
+    
+    /*
+     * This code has been hacked to accept two types of messages.  This might
+     * be treated more generally in the future.
+     *
+     * (1)
+     * cacheId= system cache id
+     * hashIndex= system cache hash index for a (possibly) cached tuple
+     * pointer= pointer of (possibly) cached tuple
+     *
+     * (2)
+     * cacheId= special non-syscache id
+     * hashIndex= object id contained in (possibly) cached relation descriptor
+     * pointer= null
+     */
+    
+    newInvalid.cacheId = cacheId;
+    newInvalid.hashIndex = hashIndex;
+    
+    if (ItemPointerIsValid(pointer)) {
+       ItemPointerCopy(pointer, &newInvalid.pointerData);
+    } else {
+       ItemPointerSetInvalid(&newInvalid.pointerData);
+    }
+    
+    SpinAcquire(SInvalLock);
+    if (!SISetDataEntry(shmInvalBuffer, &newInvalid)) {
+       /* buffer full */
+       /* release a message, mark process cache states to be invalid */
+       SISetProcStateInvalid(shmInvalBuffer);
+       
+       if (!SIDelDataEntry(shmInvalBuffer)) {
+           /* inconsistent buffer state -- shd never happen */
+           SpinRelease(SInvalLock);
+           elog(FATAL, "RegisterSharedInvalid: inconsistent buffer state");
+       }
+       
+       /* write again */
+       (void) SISetDataEntry(shmInvalBuffer, &newInvalid);
+    }
+    SpinRelease(SInvalLock);
+}
+
+/*
+ * InvalidateSharedInvalid --
+ *  Processes all entries in a shared cache invalidation state.
+ */
+/****************************************************************************/
+/*  InvalidateSharedInvalid(invalFunction, resetFunction)                  */
+/*                                                                         */
+/*  invalidate a message in the buffer  (read and clean up)                */
+/*  should be called by a backend                                          */
+/****************************************************************************/
+void
+InvalidateSharedInvalid(void (*invalFunction)(),
+                       void (*resetFunction)())
+{
+    SpinAcquire(SInvalLock);
+    SIReadEntryData(shmInvalBuffer, MyBackendId, 
+                   invalFunction, resetFunction);  
+    
+    SIDelExpiredDataEntries(shmInvalBuffer);
+    SpinRelease(SInvalLock);
+}
diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c
new file mode 100644 (file)
index 0000000..69212ea
--- /dev/null
@@ -0,0 +1,797 @@
+/*-------------------------------------------------------------------------
+ *
+ * sinvaladt.c--
+ *    POSTGRES shared cache invalidation segment definitions.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "storage/ipc.h"
+#include "storage/sinvaladt.h"
+#include "storage/lmgr.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+/* ----------------
+ *     global variable notes
+ *
+ *     SharedInvalidationSemaphore
+ *
+ *     shmInvalBuffer
+ *             the shared buffer segment, set by SISegmentAttach()
+ *
+ *     MyBackendId
+ *             might be removed later, used only for
+ *             debugging in debug routines (end of file)
+ *
+ *     SIDbId
+ *             identification of buffer (disappears)
+ *
+ *     SIRelId         \ 
+ *     SIDummyOid       \  identification of buffer
+ *     SIXidData        /
+ *     SIXid           /
+ *
+ *  XXX This file really needs to be cleaned up.  We switched to using
+ *     spinlocks to protect critical sections (as opposed to using fake
+ *     relations and going through the lock manager) and some of the old
+ *     cruft was 'ifdef'ed out, while other parts (now unused) are still
+ *     compiled into the system. -mer 5/24/92
+ * ----------------
+ */
+#ifdef HAS_TEST_AND_SET
+int SharedInvalidationLockId;
+#else
+IpcSemaphoreId SharedInvalidationSemaphore;
+#endif
+
+SISeg          *shmInvalBuffer;        
+extern BackendId MyBackendId;
+
+static void CleanupInvalidationState(int status, SISeg *segInOutP);
+static BackendId SIAssignBackendId(SISeg *segInOutP, BackendTag backendTag);
+static int SIGetNumEntries(SISeg *segP);
+
+/************************************************************************/
+/* SISetActiveProcess(segP, backendId) set the backend status active   */
+/*     should be called only by the postmaster when creating a backend */
+/************************************************************************/
+/* XXX I suspect that the segP parameter is extraneous. -hirohama */
+static void
+SISetActiveProcess(SISeg *segInOutP, BackendId backendId)
+{
+    /* mark all messages as read */
+    
+    /* Assert(segP->procState[backendId - 1].tag == MyBackendTag); */
+    
+    segInOutP->procState[backendId - 1].resetState = false;
+    segInOutP->procState[backendId - 1].limit = SIGetNumEntries(segInOutP);
+}
+
+/****************************************************************************/
+/* SIBackendInit()  initializes a backend to operate on the buffer         */
+/****************************************************************************/
+int
+SIBackendInit(SISeg *segInOutP)
+{
+    LRelId                 LtCreateRelId();
+    TransactionId           LMITransactionIdCopy();
+    
+    Assert(MyBackendTag > 0);
+    
+    MyBackendId = SIAssignBackendId(segInOutP, MyBackendTag);
+    if (MyBackendId == InvalidBackendTag)
+       return 0;
+    
+#ifdef INVALIDDEBUG
+    elog(DEBUG, "SIBackendInit: backend tag %d; backend id %d.",
+        MyBackendTag, MyBackendId);
+#endif /* INVALIDDEBUG */
+    
+    SISetActiveProcess(segInOutP, MyBackendId);
+    on_exitpg(CleanupInvalidationState, (caddr_t)segInOutP);
+    return 1;
+}
+
+/* ----------------
+ *     SIAssignBackendId
+ * ----------------
+ */
+static BackendId
+SIAssignBackendId(SISeg *segInOutP, BackendTag backendTag)
+{
+    Index              index;
+    ProcState  *stateP;
+    
+    stateP = NULL;
+    
+    for (index = 0; index < MaxBackendId; index += 1) {
+       if (segInOutP->procState[index].tag == InvalidBackendTag ||
+           segInOutP->procState[index].tag == backendTag)
+           {
+               stateP = &segInOutP->procState[index];
+               break;
+           }
+       
+       if (!PointerIsValid(stateP) ||
+           (segInOutP->procState[index].resetState &&
+            (!stateP->resetState ||
+             stateP->tag < backendTag)) ||
+           (!stateP->resetState &&
+            (segInOutP->procState[index].limit <
+             stateP->limit ||
+             stateP->tag < backendTag)))
+           {
+               stateP = &segInOutP->procState[index];
+           }
+    }
+    
+    /* verify that all "procState" entries checked for matching tags */
+    
+    for (index += 1; index < MaxBackendId; index += 1) {
+       if (segInOutP->procState[index].tag == backendTag) {
+           elog (FATAL, "SIAssignBackendId: tag %d found twice",
+                 backendTag);
+       }
+    }
+    
+    if (stateP->tag != InvalidBackendTag) {
+       if (stateP->tag == backendTag) {
+           elog(NOTICE, "SIAssignBackendId: reusing tag %d",
+                backendTag);
+       } else {
+           elog(NOTICE,
+                "SIAssignBackendId: discarding tag %d",
+                stateP->tag);
+           return InvalidBackendTag;
+       }
+    }
+    
+    stateP->tag = backendTag;
+    
+    return (1 + stateP - &segInOutP->procState[0]);
+}
+
+
+/************************************************************************/
+/* The following function should be called only by the postmaster !!    */
+/************************************************************************/
+
+/************************************************************************/
+/* SISetDeadProcess(segP, backendId)  set the backend status DEAD      */
+/*     should be called only by the postmaster when a backend died     */
+/************************************************************************/
+static void
+SISetDeadProcess(SISeg *segP, int backendId)
+{
+    /* XXX call me.... */
+    
+    segP->procState[backendId - 1].resetState = false;
+    segP->procState[backendId - 1].limit = -1;
+    segP->procState[backendId - 1].tag = InvalidBackendTag;
+}
+
+/*
+ * CleanupInvalidationState --
+ * Note:
+ *     This is a temporary hack.  ExitBackend should call this instead
+ *     of exit (via on_exitpg).
+ */
+static void
+CleanupInvalidationState(int status, /* XXX */
+                        SISeg *segInOutP) /* XXX style */
+{
+    Assert(PointerIsValid(segInOutP));
+    
+    SISetDeadProcess(segInOutP, MyBackendId);
+}
+
+
+/************************************************************************/
+/* SIComputeSize()  - retuns the size of a buffer segment              */
+/************************************************************************/
+static SISegOffsets *
+SIComputeSize(int *segSize)
+{
+    int         A, B, a, b, totalSize;
+    SISegOffsets *oP;
+    
+    A = 0;
+    a = SizeSISeg;     /* offset to first data entry */
+    b = SizeOfOneSISegEntry * MAXNUMMESSAGES;
+    B = A + a + b;
+    totalSize = B - A;
+    *segSize = totalSize;
+    
+    oP = (SISegOffsets *) palloc(sizeof(SISegOffsets));
+    oP->startSegment = A;
+    oP->offsetToFirstEntry = a; /* relatiove to A */
+    oP->offsetToEndOfSegemnt = totalSize; /* relative to A */
+    return(oP);
+}
+
+
+/************************************************************************/
+/* SISetStartEntrySection(segP, offset)     - sets the offset          */
+/************************************************************************/
+static void
+SISetStartEntrySection(SISeg *segP, Offset offset)
+{
+    segP->startEntrySection = offset;
+}
+
+/************************************************************************/
+/* SIGetStartEntrySection(segP)     - returnss the offset              */
+/************************************************************************/
+static Offset
+SIGetStartEntrySection(SISeg *segP)
+{
+    return(segP->startEntrySection);
+}
+
+
+/************************************************************************/
+/* SISetEndEntrySection(segP, offset)  - sets the offset               */
+/************************************************************************/
+static void
+SISetEndEntrySection(SISeg *segP, Offset offset)
+{
+    segP->endEntrySection = offset;
+}
+
+/************************************************************************/
+/* SISetEndEntryChain(segP, offset)    - sets the offset               */
+/************************************************************************/
+static void
+SISetEndEntryChain(SISeg *segP, Offset offset)
+{
+    segP->endEntryChain = offset;
+}
+
+/************************************************************************/
+/* SIGetEndEntryChain(segP)    - returnss the offset                   */
+/************************************************************************/
+static Offset
+SIGetEndEntryChain(SISeg *segP)
+{
+    return(segP->endEntryChain);
+}
+
+/************************************************************************/
+/* SISetStartEntryChain(segP, offset)  - sets the offset               */
+/************************************************************************/
+static void
+SISetStartEntryChain(SISeg *segP, Offset offset)
+{
+    segP->startEntryChain = offset;
+}
+
+/************************************************************************/
+/* SIGetStartEntryChain(segP)  - returns  the offset                   */
+/************************************************************************/
+static Offset
+SIGetStartEntryChain(SISeg *segP)
+{
+    return(segP->startEntryChain);
+}
+
+/************************************************************************/
+/* SISetNumEntries(segP, num)  sets the current nuber of entries       */
+/************************************************************************/
+static bool
+SISetNumEntries(SISeg *segP, int num)
+{
+    if ( num <= MAXNUMMESSAGES) {
+        segP->numEntries =  num;
+        return(true);
+    } else {
+        return(false);  /* table full */
+    }    
+}
+
+/************************************************************************/
+/* SIGetNumEntries(segP)    - returns the current nuber of entries     */
+/************************************************************************/
+static int
+SIGetNumEntries(SISeg *segP)
+{
+    return(segP->numEntries);
+}
+
+
+/************************************************************************/
+/* SISetMaxNumEntries(segP, num)    sets the maximal number of entries */
+/************************************************************************/
+static bool
+SISetMaxNumEntries(SISeg *segP, int num)
+{
+    if ( num <= MAXNUMMESSAGES) {
+        segP->maxNumEntries =  num;
+        return(true);
+    } else {
+        return(false);  /* wrong number */
+    }   
+}
+
+
+/************************************************************************/
+/* SIGetProcStateLimit(segP, i)        returns the limit of read messages      */
+/************************************************************************/
+static int
+SIGetProcStateLimit(SISeg *segP, int i)
+{
+    return(segP->procState[i].limit);
+}
+
+/************************************************************************/
+/* SIIncNumEntries(segP, num)  increments the current nuber of entries */
+/************************************************************************/
+static bool
+SIIncNumEntries(SISeg *segP, int num)
+{
+    if ((segP->numEntries + num) <= MAXNUMMESSAGES) {
+        segP->numEntries = segP->numEntries + num;
+        return(true);
+    } else {
+        return(false);  /* table full */
+    }   
+}
+
+/************************************************************************/
+/* SIDecNumEntries(segP, num)  decrements the current nuber of entries */
+/************************************************************************/
+static bool
+SIDecNumEntries(SISeg *segP, int num)
+{
+    if ((segP->numEntries - num) >=  0) {
+        segP->numEntries = segP->numEntries - num;
+        return(true);
+    } else {
+        return(false);  /* not enough entries in table */
+    }   
+}
+
+/************************************************************************/
+/* SISetStartFreeSpace(segP, offset)  - sets the offset                        */
+/************************************************************************/
+static void
+SISetStartFreeSpace(SISeg *segP, Offset offset)
+{
+    segP->startFreeSpace = offset;
+}
+
+/************************************************************************/
+/* SIGetStartFreeSpace(segP)  - returns the offset                     */
+/************************************************************************/
+static Offset
+SIGetStartFreeSpace(SISeg *segP)
+{
+    return(segP->startFreeSpace);
+}
+
+
+
+/************************************************************************/
+/* SIGetFirstDataEntry(segP)  returns first data entry                 */
+/************************************************************************/
+static SISegEntry *
+SIGetFirstDataEntry(SISeg *segP)
+{
+    SISegEntry  *eP;
+    Offset      startChain;
+    
+    startChain = SIGetStartEntryChain(segP);
+    
+    if (startChain == InvalidOffset)
+       return(NULL);
+    
+    eP = (SISegEntry  *) ((Pointer) segP + 
+                         SIGetStartEntrySection(segP) +
+                         startChain );
+    return(eP);
+}
+
+
+/************************************************************************/
+/* SIGetLastDataEntry(segP)  returns last data entry in the chain      */
+/************************************************************************/
+static SISegEntry *
+SIGetLastDataEntry(SISeg *segP)
+{
+    SISegEntry  *eP;
+    Offset      endChain;
+    
+    endChain = SIGetEndEntryChain(segP);
+    
+    if (endChain == InvalidOffset)
+       return(NULL);
+    
+    eP = (SISegEntry  *) ((Pointer) segP + 
+                         SIGetStartEntrySection(segP) +
+                         endChain );
+    return(eP);
+}
+
+/************************************************************************/
+/* SIGetNextDataEntry(segP, offset)  returns next data entry           */
+/************************************************************************/
+static SISegEntry *
+SIGetNextDataEntry(SISeg *segP, Offset offset)
+{
+    SISegEntry  *eP;
+    
+    if (offset == InvalidOffset)
+       return(NULL);
+    
+    eP = (SISegEntry  *) ((Pointer) segP +
+                          SIGetStartEntrySection(segP) + 
+                          offset);
+    return(eP);
+}
+
+
+/************************************************************************/
+/* SIGetNthDataEntry(segP, n)  returns the n-th data entry in chain    */
+/************************************************************************/
+static SISegEntry *
+SIGetNthDataEntry(SISeg *segP,
+                 int n)        /* must range from 1 to MaxMessages */
+{
+    SISegEntry  *eP;
+    int                i;
+    
+    if (n <= 0) return(NULL);
+    
+    eP = SIGetFirstDataEntry(segP);
+    for (i = 1; i < n; i++) {
+       /* skip one and get the next    */
+       eP = SIGetNextDataEntry(segP, eP->next);
+    }
+    
+    return(eP);
+}
+
+/************************************************************************/
+/* SIEntryOffset(segP, entryP)   returns the offset for an pointer     */
+/************************************************************************/
+static Offset
+SIEntryOffset(SISeg *segP, SISegEntry *entryP)
+{
+    /* relative to B !! */
+    return ((Offset) ((Pointer) entryP -
+                      (Pointer) segP - 
+                      SIGetStartEntrySection(segP) ));
+}
+
+
+/************************************************************************/
+/* SISetDataEntry(segP, data)  - sets a message in the segemnt         */
+/************************************************************************/
+bool
+SISetDataEntry(SISeg *segP, SharedInvalidData  *data)
+{
+    Offset         offsetToNewData;
+    SISegEntry             *eP, *lastP;
+    bool           SISegFull();
+    Offset         SIEntryOffset();
+    Offset         SIGetStartFreeSpace();
+    SISegEntry             *SIGetFirstDataEntry();
+    SISegEntry             *SIGetNextDataEntry();
+    SISegEntry             *SIGetLastDataEntry();
+    
+    if (!SIIncNumEntries(segP, 1)) 
+       return(false);  /* no space */
+    
+    /* get a free entry */
+    offsetToNewData = SIGetStartFreeSpace(segP);
+    eP = SIGetNextDataEntry(segP, offsetToNewData); /* it's a free one */
+    SISetStartFreeSpace(segP, eP->next);
+    /* fill it up */
+    eP->entryData = *data;
+    eP->isfree = false;
+    eP->next = InvalidOffset;
+    
+    /* handle insertion point at the end of the chain !!*/
+    lastP = SIGetLastDataEntry(segP);
+    if (lastP == NULL) {
+       /* there is no chain, insert the first entry */
+       SISetStartEntryChain(segP, SIEntryOffset(segP, eP));
+    } else {
+       /* there is a last entry in the chain */
+       lastP->next = SIEntryOffset(segP, eP);
+    }
+    SISetEndEntryChain(segP, SIEntryOffset(segP, eP));
+    return(true);
+}
+
+
+/************************************************************************/
+/* SIDecProcLimit(segP, num)  decrements all process limits            */
+/************************************************************************/
+static void
+SIDecProcLimit(SISeg *segP, int num)
+{
+    int i;
+    for (i=0; i < MaxBackendId; i++) {
+       /* decrement only, if there is a limit > 0  */
+       if (segP->procState[i].limit > 0) {
+           segP->procState[i].limit = segP->procState[i].limit - num;
+           if (segP->procState[i].limit < 0) {
+               /* limit was not high enough, reset to zero */
+               /* negative means it's a dead backend       */
+               segP->procState[i].limit = 0;
+           }
+       }
+    }
+}
+
+
+/************************************************************************/
+/* SIDelDataEntry(segP)            - free the FIRST entry                      */
+/************************************************************************/
+bool
+SIDelDataEntry(SISeg *segP)
+{
+    SISegEntry             *e1P;
+    SISegEntry             *SIGetFirstDataEntry();
+    
+    if (!SIDecNumEntries(segP, 1))  {
+       /* no entries in buffer */
+       return(false);
+    }
+    
+    e1P = SIGetFirstDataEntry(segP);
+    SISetStartEntryChain(segP, e1P->next);
+    if (SIGetStartEntryChain(segP) == InvalidOffset) {
+       /* it was the last entry */
+       SISetEndEntryChain(segP, InvalidOffset);
+    }
+    /* free the entry */
+    e1P->isfree = true;
+    e1P->next = SIGetStartFreeSpace(segP);
+    SISetStartFreeSpace(segP, SIEntryOffset(segP, e1P));
+    SIDecProcLimit(segP, 1);
+    return(true); 
+}
+
+
+
+/************************************************************************/
+/* SISetProcStateInvalid(segP) checks and marks a backends state as    */
+/*                                 invalid                             */
+/************************************************************************/
+void
+SISetProcStateInvalid(SISeg *segP)
+{
+    int i;
+    
+    for (i=0; i < MaxBackendId; i++) {
+       if (segP->procState[i].limit == 0) {
+           /* backend i didn't read any message                        */
+           segP->procState[i].resetState = true;
+           /*XXX signal backend that it has to reset its internal cache ? */
+       }
+    }
+}
+
+/************************************************************************/
+/* SIReadEntryData(segP, backendId, function)                          */
+/*                     - marks messages to be read by id               */
+/*                       and executes function                         */
+/************************************************************************/
+void
+SIReadEntryData(SISeg *segP,
+               int backendId,
+               void (*invalFunction)(),
+               void (*resetFunction)())
+{
+    int i = 0;
+    SISegEntry *data;
+    
+    Assert(segP->procState[backendId - 1].tag == MyBackendTag);
+    
+    if (!segP->procState[backendId - 1].resetState) {
+       /* invalidate data, but only those, you have not seen yet !!*/
+       /* therefore skip read messages */
+       data = SIGetNthDataEntry(segP, 
+                                SIGetProcStateLimit(segP, backendId - 1) + 1);
+       while (data != NULL) {
+           i++;
+           segP->procState[backendId - 1].limit++;  /* one more message read */
+           invalFunction(data->entryData.cacheId, 
+                         data->entryData.hashIndex,
+                         &data->entryData.pointerData);
+           data = SIGetNextDataEntry(segP, data->next);
+       }
+       /* SIDelExpiredDataEntries(segP); */
+    } else {
+       /*backend must not read messages, its own state has to be reset     */
+       elog(NOTICE, "SIMarkEntryData: cache state reset");
+        resetFunction(); /* XXXX call it here, parameters? */
+       
+       /* new valid state--mark all messages "read" */
+       segP->procState[backendId - 1].resetState = false;
+       segP->procState[backendId - 1].limit = SIGetNumEntries(segP);
+    }
+    /* check whether we can remove dead messages                           */
+    if (i > MAXNUMMESSAGES) {
+       elog(FATAL, "SIReadEntryData: Invalid segment state");
+    }
+}
+
+/************************************************************************/
+/* SIDelExpiredDataEntries  (segP)  - removes irrelevant messages      */
+/************************************************************************/
+void
+SIDelExpiredDataEntries(SISeg *segP)
+{
+    int   min, i, h;
+    
+    min = 9999999;
+    for (i = 0; i < MaxBackendId; i++) {
+       h = SIGetProcStateLimit(segP, i);
+       if (h >= 0)  { /* backend active */
+           if (h < min ) min = h;
+       }
+    }
+    if (min != 9999999) {
+       /* we can remove min messages */
+       for (i = 1; i <= min; i++) {
+           /* this  adjusts also the state limits!*/
+           if (!SIDelDataEntry(segP)) { 
+               elog(FATAL, "SIDelExpiredDataEntries: Invalid segment state");
+           }
+       }
+    }
+}
+
+
+
+/************************************************************************/
+/* SISegInit(segP)  - initializes the segment                          */
+/************************************************************************/
+static void
+SISegInit(SISeg *segP)
+{
+    SISegOffsets    *oP;
+    int                    segSize, i;
+    SISegEntry      *eP;
+    
+    oP = SIComputeSize(&segSize);
+    /* set sempahore ids in the segment */
+    /* XXX */
+    SISetStartEntrySection(segP, oP->offsetToFirstEntry);
+    SISetEndEntrySection(segP, oP->offsetToEndOfSegemnt);
+    SISetStartFreeSpace(segP, 0);
+    SISetStartEntryChain(segP, InvalidOffset);
+    SISetEndEntryChain(segP, InvalidOffset);
+    (void) SISetNumEntries(segP, 0);
+    (void) SISetMaxNumEntries(segP, MAXNUMMESSAGES);
+    for (i = 0; i < MaxBackendId; i++) {
+       segP->procState[i].limit = -1;      /* no backend active  !!*/
+       segP->procState[i].resetState = false;
+       segP->procState[i].tag = InvalidBackendTag;
+    }
+    /* construct a chain of free entries                           */
+    for (i = 1; i < MAXNUMMESSAGES; i++)  {
+       eP = (SISegEntry  *) ((Pointer) segP +
+                             SIGetStartEntrySection(segP) +
+                             (i - 1) * sizeof(SISegEntry));
+       eP->isfree = true;
+       eP->next = i * sizeof(SISegEntry); /* relative to B */
+    }
+    /* handle the last free entry separate                         */
+    eP = (SISegEntry  *) ((Pointer) segP +
+                         SIGetStartEntrySection(segP) +
+                         (MAXNUMMESSAGES - 1) * sizeof(SISegEntry));
+    eP->isfree = true;
+    eP->next = InvalidOffset;  /* it's the end of the chain !! */
+    /*
+     * Be tidy
+     */
+    pfree(oP);
+    
+}
+
+
+
+/************************************************************************/
+/* SISegmentKill(key)   - kill any segment                              */
+/************************************************************************/
+static void
+SISegmentKill(int key) /* the corresponding key for the segment */
+{   
+    IpcMemoryKill(key);
+}      
+
+
+/************************************************************************/
+/* SISegmentGet(key, size)  - get a shared segment of size <size>       */
+/*                returns a segment id                                  */
+/************************************************************************/
+static IpcMemoryId
+SISegmentGet(int key,          /* the corresponding key for the segment */
+            int size,          /* size of segment in bytes              */
+            bool create)
+{
+    IpcMemoryId   shmid;
+    
+    if (create) {
+       shmid = IpcMemoryCreate(key, size, IPCProtection);
+    } else {
+       shmid = IpcMemoryIdGet(key, size);
+    }
+    return(shmid);
+}
+
+/************************************************************************/
+/* SISegmentAttach(shmid)   - attach a shared segment with id shmid     */
+/************************************************************************/
+static void
+SISegmentAttach(IpcMemoryId shmid)
+{
+    shmInvalBuffer = (struct SISeg *) IpcMemoryAttach(shmid);
+    if (shmInvalBuffer == IpcMemAttachFailed) {   
+       /* XXX use validity function */
+       elog(NOTICE, "SISegmentAttach: Could not attach segment");
+       elog(FATAL, "SISegmentAttach: %m");
+    }
+}
+
+
+/************************************************************************/
+/* SISegmentInit(killExistingSegment, key)  initialize segment         */
+/************************************************************************/
+int
+SISegmentInit(bool killExistingSegment, IPCKey key)
+{ 
+    SISegOffsets       *oP;
+    int                segSize;
+    IpcMemoryId                shmId;
+    bool               create;
+    
+    if (killExistingSegment) {
+        /* Kill existing segment */
+        /* set semaphore */
+       SISegmentKill(key);
+       
+        /* Get a shared segment */
+       
+        oP = SIComputeSize(&segSize);
+       /*
+        * Be tidy
+        */
+       pfree(oP);
+       
+        create = true;
+        shmId = SISegmentGet(key,segSize, create);
+        if (shmId < 0) {
+            perror("SISegmentGet: failed");
+            return(-1);                                     /* an error */
+        }
+       
+        /* Attach the shared cache invalidation  segment */
+        /* sets the global variable shmInvalBuffer */
+        SISegmentAttach(shmId);
+       
+        /* Init shared memory table */
+        SISegInit(shmInvalBuffer);  
+    } else {
+       /* use an existing segment */
+       create = false;
+       shmId = SISegmentGet(key, 0, create);
+       if (shmId < 0) {
+           perror("SISegmentGet: getting an existent segment failed");
+           return(-1);                                     /* an error */
+       }
+       /* Attach the shared cache invalidation segment */
+       SISegmentAttach(shmId);
+    }
+    return(1);
+}
+
diff --git a/src/backend/storage/ipc/spin.c b/src/backend/storage/ipc/spin.c
new file mode 100644 (file)
index 0000000..9411f4f
--- /dev/null
@@ -0,0 +1,247 @@
+/*-------------------------------------------------------------------------
+ *
+ * spin.c--
+ *    routines for managing spin locks
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * POSTGRES has two kinds of locks: semaphores (which put the
+ * process to sleep) and spinlocks (which are supposed to be
+ * short term locks).  Currently both are implemented as SysV
+ * semaphores, but presumably this can change if we move to
+ * a machine with a test-and-set (TAS) instruction.  Its probably
+ * a good idea to think about (and allocate) short term and long
+ * term semaphores separately anyway.
+ *
+ * NOTE: These routines are not supposed to be widely used in Postgres.
+ *      They are preserved solely for the purpose of porting Mark Sullivan's
+ *      buffer manager to Postgres.
+ */
+#include <errno.h>
+#include "postgres.h"
+#include "storage/ipc.h"
+#include "storage/shmem.h"
+#include "storage/spin.h"
+#include "storage/proc.h"
+#include "utils/elog.h"
+
+/* globals used in this file */
+IpcSemaphoreId SpinLockId;
+
+#ifdef HAS_TEST_AND_SET
+/* real spin lock implementations */
+
+bool
+CreateSpinlocks(IPCKey key)
+{ 
+    /* the spin lock shared memory must have been created by now */
+    return(TRUE); 
+}
+
+bool
+AttachSpinLocks(IPCKey key)
+{
+    /* the spin lock shared memory must have been attached by now */
+    return(TRUE);
+}
+
+bool
+InitSpinLocks(int init, IPCKey key)
+{
+    extern SPINLOCK ShmemLock;
+    extern SPINLOCK BindingLock;
+    extern SPINLOCK BufMgrLock;
+    extern SPINLOCK LockMgrLock;
+    extern SPINLOCK ProcStructLock;
+    extern SPINLOCK SInvalLock;
+    extern SPINLOCK OidGenLockId;
+    
+#ifdef MAIN_MEMORY
+    extern SPINLOCK MMCacheLock;
+#endif /* SONY_JUKEBOX */
+    
+    /* These six spinlocks have fixed location is shmem */
+    ShmemLock = (SPINLOCK) SHMEMLOCKID;
+    BindingLock = (SPINLOCK) BINDINGLOCKID;
+    BufMgrLock = (SPINLOCK) BUFMGRLOCKID;
+    LockMgrLock = (SPINLOCK) LOCKMGRLOCKID;
+    ProcStructLock = (SPINLOCK) PROCSTRUCTLOCKID;
+    SInvalLock = (SPINLOCK) SINVALLOCKID;
+    OidGenLockId = (SPINLOCK) OIDGENLOCKID;
+    
+#ifdef MAIN_MEMORY
+    MMCacheLock = (SPINLOCK) MMCACHELOCKID;
+#endif /* MAIN_MEMORY */
+    
+    return(TRUE);
+}
+
+void
+SpinAcquire(SPINLOCK lock)
+{
+    ExclusiveLock(lock);
+    PROC_INCR_SLOCK(lock);
+}
+
+void
+SpinRelease(SPINLOCK lock)
+{
+    PROC_DECR_SLOCK(lock);
+    ExclusiveUnlock(lock);
+}
+
+bool
+SpinIsLocked(SPINLOCK lock)
+{
+    return(!LockIsFree(lock));
+}
+
+#else /* HAS_TEST_AND_SET */
+/* Spinlocks are implemented using SysV semaphores */
+
+
+/*
+ * SpinAcquire -- try to grab a spinlock
+ *
+ * FAILS if the semaphore is corrupted.
+ */
+void
+SpinAcquire(SPINLOCK lock)
+{
+    IpcSemaphoreLock(SpinLockId, lock, IpcExclusiveLock);
+    PROC_INCR_SLOCK(lock);
+}
+
+/*
+ * SpinRelease -- release a spin lock
+ * 
+ * FAILS if the semaphore is corrupted
+ */
+void
+SpinRelease(SPINLOCK lock)
+{
+    Assert(SpinIsLocked(lock))
+       PROC_DECR_SLOCK(lock);
+    IpcSemaphoreUnlock(SpinLockId, lock, IpcExclusiveLock);
+}
+
+bool
+SpinIsLocked(SPINLOCK lock)
+{
+    int semval;
+    
+    semval = IpcSemaphoreGetValue(SpinLockId, lock);
+    return(semval < IpcSemaphoreDefaultStartValue);
+}
+
+/*
+ * CreateSpinlocks -- Create a sysV semaphore array for
+ *     the spinlocks
+ *
+ */
+bool
+CreateSpinlocks(IPCKey key)
+{
+    
+    int status;
+    IpcSemaphoreId semid;
+    semid = IpcSemaphoreCreate(key, MAX_SPINS, IPCProtection, 
+                              IpcSemaphoreDefaultStartValue, 1, &status);
+    if (status == IpcSemIdExist) {
+       IpcSemaphoreKill(key);
+       elog(NOTICE,"Destroying old spinlock semaphore");
+       semid = IpcSemaphoreCreate(key, MAX_SPINS, IPCProtection, 
+                                  IpcSemaphoreDefaultStartValue, 1, &status);
+    }
+    
+    if (semid >= 0) {
+       SpinLockId = semid;
+       return(TRUE);
+    }
+    /* cannot create spinlocks */
+    elog(FATAL,"CreateSpinlocks: cannot create spin locks");
+    return(FALSE);
+}
+
+/*
+ * Attach to existing spinlock set
+ */
+bool
+AttachSpinLocks(IPCKey key)
+{
+    IpcSemaphoreId id;
+    
+    id = semget (key, MAX_SPINS, 0);
+    if (id < 0) {
+       if (errno == EEXIST) {
+           /* key is the name of someone else's semaphore */
+           elog (FATAL,"AttachSpinlocks: SPIN_KEY belongs to someone else");
+       }
+       /* cannot create spinlocks */
+       elog(FATAL,"AttachSpinlocks: cannot create spin locks");
+       return(FALSE);
+    }
+    SpinLockId = id;
+    return(TRUE);
+}
+
+/*
+ * InitSpinLocks -- Spinlock bootstrapping
+ * 
+ * We need several spinlocks for bootstrapping:
+ * BindingLock (for the shmem binding table) and
+ * ShmemLock (for the shmem allocator), BufMgrLock (for buffer
+ * pool exclusive access), LockMgrLock (for the lock table), and
+ * ProcStructLock (a spin lock for the shared process structure).
+ * If there's a Sony WORM drive attached, we also have a spinlock
+ * (SJCacheLock) for it.  Same story for the main memory storage mgr.
+ *
+ */
+bool
+InitSpinLocks(int init, IPCKey key)
+{
+    extern SPINLOCK ShmemLock;
+    extern SPINLOCK BindingLock;
+    extern SPINLOCK BufMgrLock;
+    extern SPINLOCK LockMgrLock;
+    extern SPINLOCK ProcStructLock;
+    extern SPINLOCK SInvalLock;
+    extern SPINLOCK OidGenLockId;
+    
+#ifdef MAIN_MEMORY
+    extern SPINLOCK MMCacheLock;
+#endif /* MAIN_MEMORY */
+    
+    if (!init || key != IPC_PRIVATE) {
+       /* if bootstrap and key is IPC_PRIVATE, it means that we are running
+        * backend by itself.  no need to attach spinlocks
+        */
+       if (! AttachSpinLocks(key)) {
+           elog(FATAL,"InitSpinLocks: couldnt attach spin locks");
+           return(FALSE);
+       }
+    }
+    
+    /* These five (or six) spinlocks have fixed location is shmem */
+    ShmemLock = (SPINLOCK) SHMEMLOCKID;
+    BindingLock = (SPINLOCK) BINDINGLOCKID;
+    BufMgrLock = (SPINLOCK) BUFMGRLOCKID;
+    LockMgrLock = (SPINLOCK) LOCKMGRLOCKID;
+    ProcStructLock = (SPINLOCK) PROCSTRUCTLOCKID;
+    SInvalLock = (SPINLOCK) SINVALLOCKID;
+    OidGenLockId = (SPINLOCK) OIDGENLOCKID;
+    
+#ifdef MAIN_MEMORY
+    MMCacheLock = (SPINLOCK) MMCACHELOCKID;
+#endif /* MAIN_MEMORY */
+    
+    return(TRUE);
+}
+#endif /* HAS_TEST_AND_SET */
diff --git a/src/backend/storage/item.h b/src/backend/storage/item.h
new file mode 100644 (file)
index 0000000..3ad903c
--- /dev/null
@@ -0,0 +1,20 @@
+/*-------------------------------------------------------------------------
+ *
+ * item.h--
+ *    POSTGRES disk item definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        ITEM_H
+#define ITEM_H
+
+#include "c.h"
+
+typedef Pointer        Item;
+
+#endif /* ITEM_H */
diff --git a/src/backend/storage/itemid.h b/src/backend/storage/itemid.h
new file mode 100644 (file)
index 0000000..8be2fc3
--- /dev/null
@@ -0,0 +1,75 @@
+/*-------------------------------------------------------------------------
+ *
+ * itemid.h--
+ *    Standard POSTGRES buffer page item identifier definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        ITEMID_H
+#define ITEMID_H
+
+typedef uint16 ItemOffset;
+typedef uint16 ItemLength;
+
+typedef bits16 ItemIdFlags;
+
+
+
+typedef struct ItemIdData {            /* line pointers */
+       unsigned        lp_off:13,      /* offset to find tup */
+                                       /* can be reduced by 2 if necc. */
+                       lp_flags:6,     /* flags on tuple */
+                       lp_len:13;      /* length of tuple */
+} ItemIdData;
+
+typedef struct ItemIdData      *ItemId;
+
+#ifndef        LP_USED
+#define LP_USED                0x01    /* this line pointer is being used */
+#endif
+
+/* ----------------
+ *     support macros
+ * ----------------
+ */
+/* 
+ *     ItemIdGetLength
+ */
+#define ItemIdGetLength(itemId) \
+   ((itemId)->lp_len)
+
+/* 
+ *     ItemIdGetOffset
+ */
+#define ItemIdGetOffset(itemId) \
+   ((itemId)->lp_off)
+
+/* 
+ *     ItemIdGetFlags
+ */
+#define ItemIdGetFlags(itemId) \
+   ((itemId)->lp_flags)
+
+/*
+ * ItemIdIsValid --
+ *     True iff disk item identifier is valid.
+ */
+#define        ItemIdIsValid(itemId)   PointerIsValid(itemId)
+
+/*
+ * ItemIdIsUsed --
+ *     True iff disk item identifier is in use.
+ *
+ * Note:
+ *     Assumes disk item identifier is valid.
+ */
+#define ItemIdIsUsed(itemId) \
+    (AssertMacro(ItemIdIsValid(itemId)) ? \
+     (bool) (((itemId)->lp_flags & LP_USED) != 0) : false)
+
+#endif /* ITEMID_H */
diff --git a/src/backend/storage/itempos.h b/src/backend/storage/itempos.h
new file mode 100644 (file)
index 0000000..b709eb0
--- /dev/null
@@ -0,0 +1,44 @@
+/*-------------------------------------------------------------------------
+ *
+ * itempos.h--
+ *    Standard POSTGRES buffer page long item subposition definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        ITEMPOS_H
+#define ITEMPOS_H
+
+#include "c.h"
+#include "storage/buf.h"
+#include "storage/itemid.h"
+
+typedef struct ItemSubpositionData {
+       Buffer          op_db;
+       ItemId          op_lpp;
+       char            *op_cp;         /* XXX */
+       uint32          op_len;
+} ItemSubpositionData;
+
+typedef ItemSubpositionData    *ItemSubposition;
+
+/*
+ *     PNOBREAK(OBJP, LEN)
+ *     struct  objpos  *OBJP;
+ *     unsigned        LEN;
+ */
+#define PNOBREAK(OBJP, LEN)    ((OBJP)->op_len >= LEN)
+
+/*
+ *     PSKIP(OBJP, LEN)
+ *     struct  objpos  *OBJP;
+ *     unsigned        LEN;
+ */
+#define PSKIP(OBJP, LEN)\
+       { (OBJP)->op_cp += (LEN); (OBJP)->op_len -= (LEN); }
+
+#endif /* ITEMPOS_H */
diff --git a/src/backend/storage/itemptr.h b/src/backend/storage/itemptr.h
new file mode 100644 (file)
index 0000000..a17c3c7
--- /dev/null
@@ -0,0 +1,115 @@
+/*-------------------------------------------------------------------------
+ *
+ * itemptr.h--
+ *    POSTGRES disk item pointer definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        ITEMPTR_H
+#define ITEMPTR_H
+
+#include "c.h"
+#include "storage/block.h"
+#include "storage/off.h"
+#include "storage/itemid.h"
+
+/*
+ * ItemPointer:
+ *
+ * this is a pointer to an item on another disk page in the same file.
+ * blkid tells us which block, posid tells us which entry in the linp
+ * (ItemIdData) array we want.
+ */
+typedef struct ItemPointerData {
+    BlockIdData                ip_blkid;
+    OffsetNumber       ip_posid;
+} ItemPointerData;
+
+typedef ItemPointerData        *ItemPointer;
+
+/* ----------------
+ *     support macros
+ * ----------------
+ */
+
+/*
+ * ItemPointerIsValid --
+ *     True iff the disk item pointer is not NULL.
+ */
+#define ItemPointerIsValid(pointer) \
+    ((bool) (PointerIsValid(pointer) && ((pointer)->ip_posid != 0)))
+
+/*
+ * ItemPointerGetBlockNumber --
+ *     Returns the block number of a disk item pointer.
+ */
+#define ItemPointerGetBlockNumber(pointer) \
+    (AssertMacro(ItemPointerIsValid(pointer)) ? \
+     BlockIdGetBlockNumber(&(pointer)->ip_blkid) : (BlockNumber) 0)
+
+/*
+ * ItemPointerGetOffsetNumber --
+ *     Returns the offset number of a disk item pointer.
+ */
+#define ItemPointerGetOffsetNumber(pointer) \
+    (AssertMacro(ItemPointerIsValid(pointer)) ? \
+     (pointer)->ip_posid : \
+     InvalidOffsetNumber)
+
+/*
+ * ItemPointerSet --
+ *     Sets a disk item pointer to the specified block and offset.
+ */
+#define ItemPointerSet(pointer, blockNumber, offNum) \
+    Assert(PointerIsValid(pointer)); \
+    BlockIdSet(&((pointer)->ip_blkid), blockNumber); \
+    (pointer)->ip_posid = offNum
+
+/*
+ * ItemPointerSetBlockNumber --
+ *     Sets a disk item pointer to the specified block.
+ */
+#define ItemPointerSetBlockNumber(pointer, blockNumber) \
+    Assert(PointerIsValid(pointer)); \
+    BlockIdSet(&((pointer)->ip_blkid), blockNumber)
+
+/*
+ * ItemPointerSetOffsetNumber --
+ *     Sets a disk item pointer to the specified offset.
+ */
+#define ItemPointerSetOffsetNumber(pointer, offsetNumber) \
+    AssertMacro(PointerIsValid(pointer)); \
+    (pointer)->ip_posid = (offsetNumber)
+
+/*
+ * ItemPointerCopy --
+ *     Copies the contents of one disk item pointer to another.
+ */
+#define ItemPointerCopy(fromPointer, toPointer) \
+    Assert(PointerIsValid(toPointer)); \
+    Assert(PointerIsValid(fromPointer)); \
+    *(toPointer) = *(fromPointer)
+
+/*
+ * ItemPointerSetInvalid --
+ *     Sets a disk item pointer to be invalid.
+ */
+#define ItemPointerSetInvalid(pointer) \
+    Assert(PointerIsValid(pointer)); \
+    BlockIdSet(&((pointer)->ip_blkid), InvalidBlockNumber); \
+    (pointer)->ip_posid = InvalidOffsetNumber
+
+/* ----------------
+ *     externs
+ * ----------------
+ */
+
+extern bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2);
+
+#endif /* ITEMPTR_H */
+
diff --git a/src/backend/storage/large_object.h b/src/backend/storage/large_object.h
new file mode 100644 (file)
index 0000000..f8cb234
--- /dev/null
@@ -0,0 +1,58 @@
+/*-------------------------------------------------------------------------
+ *
+ * large_object.h--
+ *    file of info for Postgres large objects. POSTGRES 4.2 supports
+ *    zillions of large objects (internal, external, jaquith, inversion).
+ *    Now we only support inversion.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        LARGE_OBJECT_H
+#define        LARGE_OBJECT_H
+
+#include "c.h"
+#include "utils/rel.h"
+#include "access/relscan.h"
+
+/*
+ * This structure will eventually have lots more stuff associated with it.
+ */
+typedef struct LargeObjectDesc
+{
+    Relation heap_r;           /* heap relation */
+    Relation index_r;          /* index relation on seqno attribute */
+    IndexScanDesc iscan;       /* index scan we're using */
+    TupleDesc hdesc;           /* heap relation tuple desc */
+    TupleDesc idesc;           /* index relation tuple desc */
+    uint32 lowbyte;            /* low byte on the current page */
+    uint32 highbyte;           /* high byte on the current page */
+    uint32 offset;             /* current seek pointer */
+    ItemPointerData htid;      /* tid of current heap tuple */
+
+#define IFS_RDLOCK     (1 << 0)
+#define IFS_WRLOCK     (1 << 1)
+#define IFS_ATEOF      (1 << 2)
+
+    u_long flags;              /* locking info, etc */
+} LargeObjectDesc;
+
+/*
+ * Function definitions...
+ */
+
+/* inversion stuff in inv_api.c */
+extern LargeObjectDesc *inv_create(int flags);
+extern LargeObjectDesc *inv_open(Oid lobjId, int flags);
+extern void inv_close(LargeObjectDesc *obj_desc);
+extern int inv_destroy(Oid lobjId);
+extern int inv_stat(LargeObjectDesc *obj_desc, struct pgstat *stbuf);
+extern int inv_seek(LargeObjectDesc *obj_desc, int offset, int whence);
+extern int inv_tell(LargeObjectDesc *obj_desc);
+extern int inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes);
+extern int inv_write(LargeObjectDesc *obj_desc, char *buf, int nbytes);
+
+#endif /* LARGE_OBJECT_H */
diff --git a/src/backend/storage/large_object/Makefile.inc b/src/backend/storage/large_object/Makefile.inc
new file mode 100644 (file)
index 0000000..e7907c2
--- /dev/null
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for storage/large_object
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= inv_api.c 
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
new file mode 100644 (file)
index 0000000..5c67874
--- /dev/null
@@ -0,0 +1,1165 @@
+/*-------------------------------------------------------------------------
+ *
+ * inv_api.c--
+ *    routines for manipulating inversion fs large objects. This file
+ *    contains the user-level large object application interface routines.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>             /* for sprintf() */
+#include <sys/file.h>
+#include "c.h"
+#include "libpq/libpq-fs.h"
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/relscan.h"
+#include "access/tupdesc.h"
+#include "access/xact.h"
+#include "access/nbtree.h"
+#include "access/tupdesc.h"
+#include "catalog/index.h"     /* for index_create() */
+#include "catalog/catalog.h"   /* for newoid() */
+#include "catalog/pg_am.h" /* for BTREE_AM_OID */
+#include "catalog/pg_opclass.h" /* for INT4_OPS_OID */
+#include "catalog/pg_proc.h" /* for INT4GE_PROC_OID */
+#include "storage/itemptr.h"
+#include "storage/bufpage.h"
+#include "storage/bufmgr.h"
+#include "utils/rel.h"
+#include "utils/palloc.h"
+#include "storage/large_object.h"
+#include "utils/elog.h"
+#include "utils/syscache.h"
+#include "utils/builtins.h"    /* for namestrcpy() */
+#include "catalog/heap.h"
+#include "nodes/pg_list.h"
+
+/*
+ *  Warning, Will Robinson...  In order to pack data into an inversion
+ *  file as densely as possible, we violate the class abstraction here.
+ *  When we're appending a new tuple to the end of the table, we check
+ *  the last page to see how much data we can put on it.  If it's more
+ *  than IMINBLK, we write enough to fill the page.  This limits external
+ *  fragmentation.  In no case can we write more than IMAXBLK, since
+ *  the 8K postgres page size less overhead leaves only this much space
+ *  for data.
+ */
+
+#define IFREESPC(p)    (PageGetFreeSpace(p) - sizeof(HeapTupleData) - sizeof(struct varlena) - sizeof(int32))
+#define IMAXBLK                8092
+#define IMINBLK                512
+
+/* non-export function prototypes */
+static HeapTuple inv_fetchtup();
+static HeapTuple inv_newtuple();
+static int inv_wrnew(LargeObjectDesc *obj_desc, char *buf, int nbytes);
+static int inv_wrold(LargeObjectDesc *obj_desc, char *dbuf, int nbytes,
+                    HeapTuple htup, Buffer buffer);
+static void inv_indextup(LargeObjectDesc *obj_desc, HeapTuple htup);
+static int _inv_getsize(Relation hreln, TupleDesc hdesc, Relation ireln);
+
+/*
+ *  inv_create -- create a new large object.
+ *
+ *     Arguments:
+ *       flags -- storage manager to use, archive mode, etc.
+ *
+ *     Returns:
+ *       large object descriptor, appropriately filled in.
+ */
+LargeObjectDesc *
+inv_create(int flags)
+{
+    int file_oid;
+    LargeObjectDesc *retval;
+    Relation r;
+    Relation indr;
+    int smgr;
+    char archchar;
+    TupleDesc tupdesc;
+    AttrNumber attNums[1];
+    Oid classObjectId[1];
+    char objname[NAMEDATALEN];
+    char indname[NAMEDATALEN];
+
+    /* parse flags */
+    smgr = flags & INV_SMGRMASK;
+    if (flags & INV_ARCHIVE)
+       archchar = 'h';
+    else
+       archchar = 'n';
+
+    /* add one here since the pg_class tuple created 
+       will have the next oid and we want to have the relation name
+       to correspond to the tuple OID */
+    file_oid = newoid()+1;
+    
+    /* come up with some table names */
+    sprintf(objname, "Xinv%d", file_oid);
+    sprintf(indname, "Xinx%d", file_oid);
+
+    if (SearchSysCacheTuple(RELNAME, PointerGetDatum(objname),
+                           0,0,0) != NULL) {
+       elog(WARN,
+            "internal error: %s already exists -- cannot create large obj",
+            objname);
+    }
+    if (SearchSysCacheTuple(RELNAME, PointerGetDatum(indname),
+                           0,0,0) != NULL) {
+       elog(WARN,
+            "internal error: %s already exists -- cannot create large obj",
+            indname);
+    }
+
+    /* this is pretty painful...  want a tuple descriptor */
+    tupdesc = CreateTemplateTupleDesc(2);
+    (void) TupleDescInitEntry(tupdesc, (AttrNumber) 1,
+                             "olastbye",
+                             "int4",
+                             0, false);
+    (void) TupleDescInitEntry(tupdesc, (AttrNumber) 2,
+                             "odata",
+                             "bytea",
+                             0, false);
+    /*
+     *  First create the table to hold the inversion large object.  It
+     *  will be located on whatever storage manager the user requested.
+     */
+
+    (void) heap_create(objname, 
+                      objname,
+                      (int) archchar, smgr,
+                      tupdesc);
+
+    /* make the relation visible in this transaction */
+    CommandCounterIncrement();
+    r = heap_openr(objname);
+
+    if (!RelationIsValid(r)) {
+       elog(WARN, "cannot create large object on %s under inversion",
+            smgrout(smgr));
+    }
+
+    /*
+     *  Now create a btree index on the relation's olastbyte attribute to
+     *  make seeks go faster.  The hardwired constants are embarassing
+     *  to me, and are symptomatic of the pressure under which this code
+     *  was written.
+     *
+     *  ok, mao, let's put in some symbolic constants - jolly
+     */
+
+    attNums[0] = 1;
+    classObjectId[0] = INT4_OPS_OID;
+    index_create(objname, indname, NULL, BTREE_AM_OID,
+                1, &attNums[0], &classObjectId[0],
+                0, (Datum) NULL, NULL);
+
+    /* make the index visible in this transaction */
+    CommandCounterIncrement();
+    indr = index_openr(indname);
+
+    if (!RelationIsValid(indr)) {
+       elog(WARN, "cannot create index for large obj on %s under inversion",
+            smgrout(smgr));
+    }
+
+    retval = (LargeObjectDesc *) palloc(sizeof(LargeObjectDesc));
+
+    retval->heap_r = r;
+    retval->index_r = indr;
+    retval->iscan = (IndexScanDesc) NULL;
+    retval->hdesc = RelationGetTupleDescriptor(r);
+    retval->idesc = RelationGetTupleDescriptor(indr);
+    retval->offset = retval->lowbyte =
+       retval->highbyte = 0;
+    ItemPointerSetInvalid(&(retval->htid));
+
+    if (flags & INV_WRITE) {
+       RelationSetLockForWrite(r);
+       retval->flags = IFS_WRLOCK|IFS_RDLOCK;
+    } else if (flags & INV_READ) {
+       RelationSetLockForRead(r);
+       retval->flags = IFS_RDLOCK;
+    }
+    retval->flags |= IFS_ATEOF;
+
+    return(retval);
+}
+
+LargeObjectDesc *
+inv_open(Oid lobjId, int flags)
+{
+    LargeObjectDesc *retval;
+    Relation r;
+    char *indname;
+    Relation indrel;
+    
+    r = heap_open(lobjId);
+
+    if (!RelationIsValid(r))
+       return ((LargeObjectDesc *) NULL);
+
+    indname = pstrdup((r->rd_rel->relname).data);
+    
+    /*
+     *  hack hack hack...  we know that the fourth character of the relation
+     *  name is a 'v', and that the fourth character of the index name is an
+     *  'x', and that they're otherwise identical.
+     */
+    indname[3] = 'x';
+    indrel = index_openr(indname);
+
+    if (!RelationIsValid(indrel))
+       return ((LargeObjectDesc *) NULL);
+
+    retval = (LargeObjectDesc *) palloc(sizeof(LargeObjectDesc));
+
+    retval->heap_r = r;
+    retval->index_r = indrel;
+    retval->iscan = (IndexScanDesc) NULL;
+    retval->hdesc = RelationGetTupleDescriptor(r);
+    retval->idesc = RelationGetTupleDescriptor(indrel);
+    retval->offset = retval->lowbyte = retval->highbyte = 0;
+    ItemPointerSetInvalid(&(retval->htid));
+
+    if (flags & INV_WRITE) {
+       RelationSetLockForWrite(r);
+       retval->flags = IFS_WRLOCK|IFS_RDLOCK;
+    } else if (flags & INV_READ) {
+       RelationSetLockForRead(r);
+       retval->flags = IFS_RDLOCK;
+    }
+
+    return(retval);
+}
+
+/*
+ * Closes an existing large object descriptor.
+ */
+void
+inv_close(LargeObjectDesc *obj_desc)
+{
+    Assert(PointerIsValid(obj_desc));
+
+    if (obj_desc->iscan != (IndexScanDesc) NULL)
+       index_endscan(obj_desc->iscan);
+
+    heap_close(obj_desc->heap_r);
+    index_close(obj_desc->index_r);
+
+    pfree(obj_desc);
+}
+
+/*
+ * Destroys an existing large object, and frees its associated pointers.
+ *
+ * returns -1 if failed
+ */
+int
+inv_destroy(Oid lobjId)
+{
+    Relation r;
+
+    r = (Relation) RelationIdGetRelation(lobjId);
+    if (!RelationIsValid(r) || r->rd_rel->relkind == RELKIND_INDEX)
+       return -1;
+
+    heap_destroy(r->rd_rel->relname.data);
+    return 1;
+}
+
+/*
+ *  inv_stat() -- do a stat on an inversion file.
+ *
+ *     For the time being, this is an insanely expensive operation.  In
+ *     order to find the size of the file, we seek to the last block in
+ *     it and compute the size from that.  We scan pg_class to determine
+ *     the file's owner and create time.  We don't maintain mod time or
+ *     access time, yet.
+ *
+ *     These fields aren't stored in a table anywhere because they're
+ *     updated so frequently, and postgres only appends tuples at the
+ *     end of relations.  Once clustering works, we should fix this.
+ */
+int
+inv_stat(LargeObjectDesc *obj_desc, struct pgstat *stbuf)
+{
+    Assert(PointerIsValid(obj_desc));
+    Assert(stbuf != NULL);
+
+    /* need read lock for stat */
+    if (!(obj_desc->flags & IFS_RDLOCK)) {
+       RelationSetLockForRead(obj_desc->heap_r);
+       obj_desc->flags |= IFS_RDLOCK;
+    }
+
+    stbuf->st_ino = obj_desc->heap_r->rd_id;
+#if 1
+    stbuf->st_mode = (S_IFREG | 0666); /* IFREG|rw-rw-rw- */
+#else
+    stbuf->st_mode = 100666; /* IFREG|rw-rw-rw- */
+#endif
+    stbuf->st_size = _inv_getsize(obj_desc->heap_r,
+                                 obj_desc->hdesc,
+                                 obj_desc->index_r);
+
+    stbuf->st_uid = obj_desc->heap_r->rd_rel->relowner;
+
+    /* we have no good way of computing access times right now */
+    stbuf->st_atime_s = stbuf->st_mtime_s = stbuf->st_ctime_s = 0;
+
+    return (0);
+}
+
+int
+inv_seek(LargeObjectDesc *obj_desc, int offset, int whence)
+{
+    int oldOffset;
+    Datum d;
+    ScanKeyData skey;
+
+    Assert(PointerIsValid(obj_desc));
+
+    if (whence == SEEK_CUR) {
+        offset += obj_desc->offset;  /* calculate absolute position */
+        return (inv_seek(obj_desc, offset, SEEK_SET));
+    }
+
+    /*
+     * if you seek past the end (offset > 0) I have
+     * no clue what happens  :-(               B.L.   9/1/93
+     */
+    if (whence == SEEK_END) {
+        /* need read lock for getsize */
+        if (!(obj_desc->flags & IFS_RDLOCK)) {
+            RelationSetLockForRead(obj_desc->heap_r);
+            obj_desc->flags |= IFS_RDLOCK;
+        }
+        offset += _inv_getsize(obj_desc->heap_r,
+                               obj_desc->hdesc,
+                               obj_desc->index_r );
+        return (inv_seek(obj_desc, offset, SEEK_SET));
+    }
+
+    /*
+     *  Whenever we do a seek, we turn off the EOF flag bit to force
+     *  ourselves to check for real on the next read.
+     */
+
+    obj_desc->flags &= ~IFS_ATEOF;
+    oldOffset = obj_desc->offset;
+    obj_desc->offset = offset;
+
+    /* try to avoid doing any work, if we can manage it */
+    if (offset >= obj_desc->lowbyte
+       && offset <= obj_desc->highbyte
+        && oldOffset <= obj_desc->highbyte
+       && obj_desc->iscan != (IndexScanDesc) NULL)
+        return (offset);
+
+    /*
+     *  To do a seek on an inversion file, we start an index scan that
+     *  will bring us to the right place.  Each tuple in an inversion file
+     *  stores the offset of the last byte that appears on it, and we have
+     *  an index on this.
+     */
+
+
+    /* right now, just assume that the operation is SEEK_SET */
+    if (obj_desc->iscan != (IndexScanDesc) NULL) {
+       d = Int32GetDatum(offset);
+       btmovescan(obj_desc->iscan, d);
+    } else {
+
+       ScanKeyEntryInitialize(&skey, 0x0, 1, INT4GE_PROC_OID,
+                              Int32GetDatum(offset));
+
+       obj_desc->iscan = index_beginscan(obj_desc->index_r,
+                                         (bool) 0, (uint16) 1,
+                                         &skey);
+    }
+
+    return (offset);
+}
+
+int
+inv_tell(LargeObjectDesc *obj_desc)
+{
+    Assert(PointerIsValid(obj_desc));
+
+    return (obj_desc->offset);
+}
+
+int
+inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
+{
+    HeapTuple htup;
+    Buffer b;
+    int nread;
+    int off;
+    int ncopy;
+    Datum d;
+    struct varlena *fsblock;
+    bool isNull;
+
+    Assert(PointerIsValid(obj_desc));
+    Assert(buf != NULL);
+
+    /* if we're already at EOF, we don't need to do any work here */
+    if (obj_desc->flags & IFS_ATEOF)
+       return (0);
+
+    /* make sure we obey two-phase locking */
+    if (!(obj_desc->flags & IFS_RDLOCK)) {
+       RelationSetLockForRead(obj_desc->heap_r);
+       obj_desc->flags |= IFS_RDLOCK;
+    }
+
+    nread = 0;
+
+    /* fetch a block at a time */
+    while (nread < nbytes) {
+
+       /* fetch an inversion file system block */
+       htup = inv_fetchtup(obj_desc, &b);
+
+       if (!HeapTupleIsValid(htup)) {
+           obj_desc->flags |= IFS_ATEOF;
+           break;
+       }
+
+       /* copy the data from this block into the buffer */
+       d = (Datum) heap_getattr(htup, b, 2, obj_desc->hdesc, &isNull);
+       fsblock = (struct varlena *) DatumGetPointer(d);
+
+       off = obj_desc->offset - obj_desc->lowbyte;
+       ncopy = obj_desc->highbyte - obj_desc->offset + 1;
+       if (ncopy > (nbytes - nread))
+           ncopy = (nbytes - nread);
+       memmove(buf, &(fsblock->vl_dat[off]), ncopy);
+
+       /* be a good citizen */
+       ReleaseBuffer(b);
+
+       /* move pointers past the amount we just read */
+       buf += ncopy;
+       nread += ncopy;
+       obj_desc->offset += ncopy;
+    }
+
+    /* that's it */
+    return (nread);
+}
+
+int
+inv_write(LargeObjectDesc *obj_desc, char *buf, int nbytes)
+{
+    HeapTuple htup;
+    Buffer b;
+    int nwritten;
+    int tuplen;
+
+    Assert(PointerIsValid(obj_desc));
+    Assert(buf != NULL);
+
+    /*
+     *  Make sure we obey two-phase locking.  A write lock entitles you
+     *  to read the relation, as well.
+     */
+
+    if (!(obj_desc->flags & IFS_WRLOCK)) {
+       RelationSetLockForRead(obj_desc->heap_r);
+       obj_desc->flags |= (IFS_WRLOCK|IFS_RDLOCK);
+    }
+
+    nwritten = 0;
+
+    /* write a block at a time */
+    while (nwritten < nbytes) {
+
+       /*
+        *  Fetch the current inversion file system block.  If the
+        *  class storing the inversion file is empty, we don't want
+        *  to do an index lookup, since index lookups choke on empty
+        *  files (should be fixed someday).
+        */
+
+       if ((obj_desc->flags & IFS_ATEOF)
+           || obj_desc->heap_r->rd_nblocks == 0)
+           htup = (HeapTuple) NULL;
+       else
+           htup = inv_fetchtup(obj_desc, &b);
+
+       /* either append or replace a block, as required */
+       if (!HeapTupleIsValid(htup)) {
+           tuplen = inv_wrnew(obj_desc, buf, nbytes - nwritten);
+       } else {
+           if (obj_desc->offset > obj_desc->highbyte) 
+               tuplen = inv_wrnew(obj_desc, buf, nbytes - nwritten);
+           else
+               tuplen = inv_wrold(obj_desc, buf, nbytes - nwritten, htup, b);
+       }
+
+       /* move pointers past the amount we just wrote */
+       buf += tuplen;
+       nwritten += tuplen;
+       obj_desc->offset += tuplen;
+    }
+
+    /* that's it */
+    return (nwritten);
+}
+
+/*
+ *  inv_fetchtup -- Fetch an inversion file system block.
+ *
+ *     This routine finds the file system block containing the offset
+ *     recorded in the obj_desc structure.  Later, we need to think about
+ *     the effects of non-functional updates (can you rewrite the same
+ *     block twice in a single transaction?), but for now, we won't bother.
+ *
+ *     Parameters:
+ *             obj_desc -- the object descriptor.
+ *             bufP -- pointer to a buffer in the buffer cache; caller
+ *                     must free this.
+ *
+ *     Returns:
+ *             A heap tuple containing the desired block, or NULL if no
+ *             such tuple exists.
+ */
+static HeapTuple
+inv_fetchtup(LargeObjectDesc *obj_desc, Buffer *bufP)
+{
+    HeapTuple htup;
+    RetrieveIndexResult res;
+    Datum d;
+    int firstbyte, lastbyte;
+    struct varlena *fsblock;
+    bool isNull;
+
+    /*
+     *  If we've exhausted the current block, we need to get the next one.
+     *  When we support time travel and non-functional updates, we will
+     *  need to loop over the blocks, rather than just have an 'if', in
+     *  order to find the one we're really interested in.
+     */
+
+    if (obj_desc->offset > obj_desc->highbyte
+       || obj_desc->offset < obj_desc->lowbyte
+       || !ItemPointerIsValid(&(obj_desc->htid))) {
+
+       /* initialize scan key if not done */
+       if (obj_desc->iscan==(IndexScanDesc)NULL) {
+           ScanKeyData skey;
+
+           ScanKeyEntryInitialize(&skey, 0x0, 1, INT4GE_PROC_OID,
+                                  Int32GetDatum(0));
+           obj_desc->iscan = 
+               index_beginscan(obj_desc->index_r,
+                               (bool) 0, (uint16) 1,
+                               &skey);
+       }
+
+       do {
+           res = index_getnext(obj_desc->iscan, ForwardScanDirection);
+
+           if (res == (RetrieveIndexResult) NULL) {
+               ItemPointerSetInvalid(&(obj_desc->htid));
+               return ((HeapTuple) NULL);
+           }
+
+           /*
+            *  For time travel, we need to use the actual time qual here,
+            *  rather that NowTimeQual.  We currently have no way to pass
+            *  a time qual in.
+            */
+
+           htup = heap_fetch(obj_desc->heap_r, NowTimeQual,
+                             &(res->heap_iptr), bufP);
+
+       } while (htup == (HeapTuple) NULL);
+
+       /* remember this tid -- we may need it for later reads/writes */
+       ItemPointerCopy(&(res->heap_iptr), &(obj_desc->htid));
+
+    } else {
+       htup = heap_fetch(obj_desc->heap_r, NowTimeQual,
+                         &(obj_desc->htid), bufP);
+    }
+
+    /*
+     *  By here, we have the heap tuple we're interested in.  We cache
+     *  the upper and lower bounds for this block in the object descriptor
+     *  and return the tuple.
+     */
+
+    d = (Datum)heap_getattr(htup, *bufP, 1, obj_desc->hdesc, &isNull);
+    lastbyte = (int32) DatumGetInt32(d);
+    d = (Datum)heap_getattr(htup, *bufP, 2, obj_desc->hdesc, &isNull);
+    fsblock = (struct varlena *) DatumGetPointer(d);
+
+    /* order of + and - is important -- these are unsigned quantites near 0 */
+    firstbyte = (lastbyte + 1 + sizeof(fsblock->vl_len)) - fsblock->vl_len;
+
+    obj_desc->lowbyte = firstbyte;
+    obj_desc->highbyte = lastbyte;
+
+    /* done */
+    return (htup);
+}
+
+/*
+ *  inv_wrnew() -- append a new filesystem block tuple to the inversion
+ *                 file.
+ *
+ *     In response to an inv_write, we append one or more file system
+ *     blocks to the class containing the large object.  We violate the
+ *     class abstraction here in order to pack things as densely as we
+ *     are able.  We examine the last page in the relation, and write
+ *     just enough to fill it, assuming that it has above a certain
+ *     threshold of space available.  If the space available is less than
+ *     the threshold, we allocate a new page by writing a big tuple.
+ *
+ *     By the time we get here, we know all the parameters passed in
+ *     are valid, and that we hold the appropriate lock on the heap
+ *     relation.
+ *
+ *     Parameters:
+ *             obj_desc: large object descriptor for which to append block.
+ *             buf: buffer containing data to write.
+ *             nbytes: amount to write
+ *
+ *     Returns:
+ *             number of bytes actually written to the new tuple.
+ */
+static int
+inv_wrnew(LargeObjectDesc *obj_desc, char *buf, int nbytes)
+{
+    Relation hr;
+    HeapTuple ntup;
+    Buffer buffer;
+    Page page;
+    int nblocks;
+    int nwritten;
+
+    hr = obj_desc->heap_r;
+
+    /*
+     *  Get the last block in the relation.  If there's no data in the
+     *  relation at all, then we just get a new block.  Otherwise, we
+     *  check the last block to see whether it has room to accept some
+     *  or all of the data that the user wants to write.  If it doesn't,
+     *  then we allocate a new block.
+     */
+
+    nblocks = RelationGetNumberOfBlocks(hr);
+
+    if (nblocks > 0)
+       buffer = ReadBuffer(hr, nblocks - 1);
+    else
+       buffer = ReadBuffer(hr, P_NEW);
+
+    page = BufferGetPage(buffer);
+
+    /*
+     *  If the last page is too small to hold all the data, and it's too
+     *  small to hold IMINBLK, then we allocate a new page.  If it will
+     *  hold at least IMINBLK, but less than all the data requested, then
+     *  we write IMINBLK here.  The caller is responsible for noticing that
+     *  less than the requested number of bytes were written, and calling
+     *  this routine again.
+     */
+
+    nwritten = IFREESPC(page);
+    if (nwritten < nbytes) {
+       if (nwritten < IMINBLK) {
+            ReleaseBuffer(buffer);
+            buffer = ReadBuffer(hr, P_NEW);
+            page = BufferGetPage(buffer);
+           PageInit(page, BufferGetPageSize(buffer), 0);
+           if (nbytes > IMAXBLK)
+               nwritten = IMAXBLK;
+           else
+               nwritten = nbytes;
+       }
+    } else {
+       nwritten = nbytes;
+    }
+
+    /*
+     *  Insert a new file system block tuple, index it, and write it out.
+     */
+
+    ntup = inv_newtuple(obj_desc, buffer, page, buf, nwritten);
+    inv_indextup(obj_desc, ntup);
+
+    /* new tuple is inserted */
+    WriteBuffer(buffer);
+
+    return (nwritten);
+}
+
+static int
+inv_wrold(LargeObjectDesc *obj_desc,
+         char *dbuf,
+         int nbytes,
+         HeapTuple htup,
+         Buffer buffer)
+{
+    Relation hr;
+    HeapTuple ntup;
+    Buffer newbuf;
+    Page page;
+    Page newpage;
+    int tupbytes;
+    Datum d;
+    struct varlena *fsblock;
+    int nwritten, nblocks, freespc;
+    bool isNull;
+    int keep_offset;
+
+    /*
+     *  Since we're using a no-overwrite storage manager, the way we
+     *  overwrite blocks is to mark the old block invalid and append
+     *  a new block.  First mark the old block invalid.  This violates
+     *  the tuple abstraction.
+     */
+
+    TransactionIdStore(GetCurrentTransactionId(), &(htup->t_xmax));
+    htup->t_cmax = GetCurrentCommandId();
+
+    /*
+     *  If we're overwriting the entire block, we're lucky.  All we need
+     *  to do is to insert a new block.
+     */
+
+    if (obj_desc->offset == obj_desc->lowbyte
+       && obj_desc->lowbyte + nbytes >= obj_desc->highbyte) {
+       WriteBuffer(buffer);
+       return (inv_wrnew(obj_desc, dbuf, nbytes));
+    }
+
+     /*
+     *  By here, we need to overwrite part of the data in the current
+     *  tuple.  In order to reduce the degree to which we fragment blocks,
+     *  we guarantee that no block will be broken up due to an overwrite.
+     *  This means that we need to allocate a tuple on a new page, if
+     *  there's not room for the replacement on this one.
+     */
+
+    newbuf = buffer;
+    page = BufferGetPage(buffer);
+    newpage = BufferGetPage(newbuf);
+    hr = obj_desc->heap_r;
+    freespc = IFREESPC(page);
+    d = (Datum)heap_getattr(htup, buffer, 2, obj_desc->hdesc, &isNull);
+    fsblock = (struct varlena *) DatumGetPointer(d);
+    tupbytes = fsblock->vl_len - sizeof(fsblock->vl_len);
+
+    if (freespc < tupbytes) {
+
+       /*
+        *  First see if there's enough space on the last page of the
+        *  table to put this tuple.
+        */
+
+       nblocks = RelationGetNumberOfBlocks(hr);
+
+       if (nblocks > 0)
+           newbuf = ReadBuffer(hr, nblocks - 1);
+       else
+           newbuf = ReadBuffer(hr, P_NEW);
+
+        newpage = BufferGetPage(newbuf);
+       freespc = IFREESPC(newpage);
+
+       /*
+        *  If there's no room on the last page, allocate a new last
+        *  page for the table, and put it there.
+        */
+
+       if (freespc < tupbytes) {
+           ReleaseBuffer(newbuf);
+           newbuf = ReadBuffer(hr, P_NEW);
+           newpage = BufferGetPage(newbuf);
+           PageInit(newpage, BufferGetPageSize(newbuf), 0);
+       }
+    }
+    
+    nwritten = nbytes;
+    if (nwritten > obj_desc->highbyte - obj_desc->offset + 1)
+       nwritten = obj_desc->highbyte - obj_desc->offset + 1;
+    memmove(VARDATA(fsblock)+ (obj_desc->offset - obj_desc->lowbyte),
+           dbuf,nwritten);
+    /* we are rewriting the entire old block, therefore
+       we reset offset to the lowbyte of the original block
+       before jumping into inv_newtuple() */
+    keep_offset = obj_desc->offset;
+    obj_desc->offset = obj_desc->lowbyte;
+    ntup = inv_newtuple(obj_desc, newbuf, newpage, VARDATA(fsblock),
+                       tupbytes);
+    /* after we are done, we restore to the true offset */
+    obj_desc->offset = keep_offset;
+
+    /*
+     *  By here, we have a page (newpage) that's guaranteed to have
+     *  enough space on it to put the new tuple.  Call inv_newtuple
+     *  to do the work.  Passing NULL as a buffer to inv_newtuple()
+     *  keeps it from copying any data into the new tuple.  When it
+     *  returns, the tuple is ready to receive data from the old
+     *  tuple and the user's data buffer.
+     */
+/*
+    ntup = inv_newtuple(obj_desc, newbuf, newpage, (char *) NULL, tupbytes);
+    dptr = ((char *) ntup) + ntup->t_hoff - sizeof(ntup->t_bits) + sizeof(int4)
+               + sizeof(fsblock->vl_len);
+
+    if (obj_desc->offset > obj_desc->lowbyte) {
+       memmove(dptr,
+               &(fsblock->vl_dat[0]),
+               obj_desc->offset - obj_desc->lowbyte);
+       dptr += obj_desc->offset - obj_desc->lowbyte;
+    }
+
+
+    nwritten = nbytes;
+    if (nwritten > obj_desc->highbyte - obj_desc->offset + 1)
+       nwritten = obj_desc->highbyte - obj_desc->offset + 1;
+
+    memmove(dptr, dbuf, nwritten);
+    dptr += nwritten;
+
+    if (obj_desc->offset + nwritten < obj_desc->highbyte + 1) {
+*/
+/*
+       loc = (obj_desc->highbyte - obj_desc->offset)
+               + nwritten;
+       sz = obj_desc->highbyte - (obj_desc->lowbyte + loc);
+
+       what's going on here?? - jolly
+*/
+/*
+       sz = (obj_desc->highbyte + 1) - (obj_desc->offset + nwritten);
+       memmove(&(fsblock->vl_dat[0]), dptr, sz);
+    }
+*/
+
+
+    /* index the new tuple */
+    inv_indextup(obj_desc, ntup);
+
+    /* move the scandesc forward so we don't reread the newly inserted
+       tuple on the next index scan */
+    if (obj_desc->iscan)
+       index_getnext(obj_desc->iscan, ForwardScanDirection);
+
+    /*
+     *  Okay, by here, a tuple for the new block is correctly placed,
+     *  indexed, and filled.  Write the changed pages out.
+     */
+
+    WriteBuffer(buffer);
+    if (newbuf != buffer)
+       WriteBuffer(newbuf);
+
+    /* done */
+    return (nwritten);
+}
+
+static HeapTuple
+inv_newtuple(LargeObjectDesc *obj_desc,
+            Buffer buffer,
+            Page page,
+            char *dbuf,
+            int nwrite)
+{
+    HeapTuple ntup;
+    PageHeader ph;
+    int tupsize;
+    int hoff;
+    Offset lower;
+    Offset upper;
+    ItemId itemId;
+    OffsetNumber off;
+    OffsetNumber limit;
+    char *attptr;
+    
+    /* compute tuple size -- no nulls */
+    hoff = sizeof(HeapTupleData) - sizeof(ntup->t_bits);
+
+    /* add in olastbyte, varlena.vl_len, varlena.vl_dat */
+    tupsize = hoff + (2 * sizeof(int32)) + nwrite;
+    tupsize = LONGALIGN(tupsize);
+
+    /*
+     *  Allocate the tuple on the page, violating the page abstraction.
+     *  This code was swiped from PageAddItem().
+     */
+
+    ph = (PageHeader) page;
+    limit = OffsetNumberNext(PageGetMaxOffsetNumber(page));
+
+    /* look for "recyclable" (unused & deallocated) ItemId */
+    for (off = FirstOffsetNumber; off < limit; off = OffsetNumberNext(off)) {
+       itemId = &ph->pd_linp[off - 1];
+       if ((((*itemId).lp_flags & LP_USED) == 0) && 
+           ((*itemId).lp_len == 0)) 
+           break;
+    }
+
+    if (off > limit)
+       lower = (Offset) (((char *) (&ph->pd_linp[off])) - ((char *) page));
+    else if (off == limit)
+       lower = ph->pd_lower + sizeof (ItemIdData);
+    else
+       lower = ph->pd_lower;
+
+    upper = ph->pd_upper - tupsize;
+    
+    itemId = &ph->pd_linp[off - 1];
+    (*itemId).lp_off = upper;
+    (*itemId).lp_len = tupsize;
+    (*itemId).lp_flags = LP_USED;
+    ph->pd_lower = lower;
+    ph->pd_upper = upper;
+
+    ntup = (HeapTuple) ((char *) page + upper);
+
+    /*
+     *  Tuple is now allocated on the page.  Next, fill in the tuple
+     *  header.  This block of code violates the tuple abstraction.
+     */
+
+    ntup->t_len = tupsize;
+    ItemPointerSet(&(ntup->t_ctid), BufferGetBlockNumber(buffer), off);
+    ItemPointerSetInvalid(&(ntup->t_chain));
+    LastOidProcessed = ntup->t_oid = newoid();
+    TransactionIdStore(GetCurrentTransactionId(), &(ntup->t_xmin));
+    ntup->t_cmin = GetCurrentCommandId();
+    StoreInvalidTransactionId(&(ntup->t_xmax));
+    ntup->t_cmax = 0;
+    ntup->t_tmin = INVALID_ABSTIME;
+    ntup->t_tmax = CURRENT_ABSTIME;
+    ntup->t_natts = 2;
+    ntup->t_hoff = hoff;
+    ntup->t_vtype = 0;
+    ntup->t_infomask = 0x0;
+
+    /* if a NULL is passed in, avoid the calculations below */
+    if (dbuf == NULL)
+       return ntup;
+
+    /*
+     *  Finally, copy the user's data buffer into the tuple.  This violates
+     *  the tuple and class abstractions.
+     */
+
+    attptr = ((char *) ntup) + hoff;
+    *((int32 *) attptr) = obj_desc->offset + nwrite - 1;
+    attptr += sizeof(int32);
+
+    /*
+    ** mer fixed disk layout of varlenas to get rid of the need for this.
+    **
+    ** *((int32 *) attptr) = nwrite + sizeof(int32);
+    ** attptr += sizeof(int32);
+    */
+
+    *((int32 *) attptr) = nwrite + sizeof(int32);
+    attptr += sizeof(int32);
+
+    /*
+     *  If a data buffer was passed in, then copy the data from the buffer
+     *  to the tuple.  Some callers (eg, inv_wrold()) may not pass in a
+     *  buffer, since they have to copy part of the old tuple data and
+     *  part of the user's new data into the new tuple.
+     */
+
+    if (dbuf != (char *) NULL)
+       memmove(attptr, dbuf, nwrite);
+
+    /* keep track of boundary of current tuple */
+    obj_desc->lowbyte = obj_desc->offset;
+    obj_desc->highbyte = obj_desc->offset + nwrite - 1;
+
+    /* new tuple is filled -- return it */
+    return (ntup);
+}
+
+static void
+inv_indextup(LargeObjectDesc *obj_desc, HeapTuple htup)
+{
+    IndexTuple itup;
+    InsertIndexResult res;
+    Datum v[1];
+    char n[1];
+
+    n[0] = ' ';
+    v[0] = Int32GetDatum(obj_desc->highbyte);
+    itup = index_formtuple(obj_desc->idesc, &v[0], &n[0]);
+    memmove((char *)&(itup->t_tid),
+           (char *)&(htup->t_ctid),
+           sizeof(ItemPointerData)); 
+    res = index_insert(obj_desc->index_r, itup);
+
+    if (res)
+       pfree(res);
+
+    pfree(itup);
+}
+
+/*
+static void
+DumpPage(Page page, int blkno)
+{
+       ItemId          lp;
+       HeapTuple       tup;
+       int             flags, i, nline;
+       ItemPointerData pointerData;
+
+       printf("\t[subblock=%d]:lower=%d:upper=%d:special=%d\n", 0,
+               ((PageHeader)page)->pd_lower, ((PageHeader)page)->pd_upper,
+               ((PageHeader)page)->pd_special);
+
+       printf("\t:MaxOffsetNumber=%d\n",
+              (int16) PageGetMaxOffsetNumber(page));
+       
+       nline = (int16) PageGetMaxOffsetNumber(page);
+
+{
+       int     i;
+       char    *cp;
+
+       i = PageGetSpecialSize(page);
+       cp = PageGetSpecialPointer(page);
+
+       printf("\t:SpecialData=");
+
+       while (i > 0) {
+               printf(" 0x%02x", *cp);
+               cp += 1;
+               i -= 1;
+       }
+       printf("\n");
+}
+       for (i = 0; i < nline; i++) {
+               lp = ((PageHeader)page)->pd_linp + i;
+               flags = (*lp).lp_flags;
+               ItemPointerSet(&pointerData, blkno, 1 + i);
+               printf("%s:off=%d:flags=0x%x:len=%d",
+                       ItemPointerFormExternal(&pointerData), (*lp).lp_off,
+                       flags, (*lp).lp_len);
+
+               if (flags & LP_USED) {
+                       HeapTupleData   htdata;
+
+                       printf(":USED");
+
+                       memmove((char *) &htdata,
+                               (char *) &((char *)page)[(*lp).lp_off],
+                               sizeof(htdata));
+
+                       tup = &htdata;
+
+                       printf("\n\t:ctid=%s:oid=%d",
+                               ItemPointerFormExternal(&tup->t_ctid),
+                               tup->t_oid);
+                       printf(":natts=%d:thoff=%d:vtype=`%c' (0x%02x):",
+                               tup->t_natts,
+                               tup->t_hoff, tup->t_vtype, tup->t_vtype);
+
+                       printf("\n\t:tmin=%d:cmin=%u:",
+                               tup->t_tmin, tup->t_cmin);
+
+                       printf("xmin=%u:", tup->t_xmin);
+
+                       printf("\n\t:tmax=%d:cmax=%u:",
+                               tup->t_tmax, tup->t_cmax);
+
+                       printf("xmax=%u:", tup->t_xmax);
+
+                       printf("\n\t:chain=%s:\n",
+                               ItemPointerFormExternal(&tup->t_chain));
+               } else
+                       putchar('\n');
+       }
+}
+
+static char*
+ItemPointerFormExternal(ItemPointer pointer)
+{
+       static char     itemPointerString[32];
+
+       if (!ItemPointerIsValid(pointer)) {
+           memmove(itemPointerString, "<-,-,->", sizeof "<-,-,->");
+       } else {
+           sprintf(itemPointerString, "<%u,%u>",
+                   ItemPointerGetBlockNumber(pointer),
+                   ItemPointerGetOffsetNumber(pointer));
+       }
+
+       return (itemPointerString);
+}
+*/
+
+static int
+_inv_getsize(Relation hreln, TupleDesc hdesc, Relation ireln)
+{
+    IndexScanDesc iscan;
+    RetrieveIndexResult res;
+    Buffer buf;
+    HeapTuple htup;
+    Datum d;
+    long size;
+    bool isNull;
+
+    /* scan backwards from end */
+    iscan = index_beginscan(ireln, (bool) 1, 0, (ScanKey) NULL);
+
+    buf = InvalidBuffer;
+
+    do {
+       res = index_getnext(iscan, BackwardScanDirection);
+
+       /*
+        *  If there are no more index tuples, then the relation is empty,
+        *  so the file's size is zero.
+        */
+
+       if (res == (RetrieveIndexResult) NULL) {
+           index_endscan(iscan);
+           return (0);
+       }
+
+       /*
+        *  For time travel, we need to use the actual time qual here,
+        *  rather that NowTimeQual.  We currently have no way to pass
+        *  a time qual in.
+        */
+
+       if (buf != InvalidBuffer)
+           (void) ReleaseBuffer(buf);
+
+       htup = heap_fetch(hreln, NowTimeQual, &(res->heap_iptr), &buf);
+
+    } while (!HeapTupleIsValid(htup));
+
+    /* don't need the index scan anymore */
+    index_endscan(iscan);
+
+    /* get olastbyte attribute */
+    d = (Datum) heap_getattr(htup, buf, 1, hdesc, &isNull);
+    size = DatumGetInt32(d) + 1;
+
+    /* wei hates it if you forget to do this */
+    ReleaseBuffer(buf);
+
+    return (size);
+}
diff --git a/src/backend/storage/lmgr.h b/src/backend/storage/lmgr.h
new file mode 100644 (file)
index 0000000..0f7c9ac
--- /dev/null
@@ -0,0 +1,84 @@
+/*-------------------------------------------------------------------------
+ *
+ * lmgr.h--
+ *    POSTGRES lock manager definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        LMGR_H
+#define LMGR_H
+
+#include "postgres.h"
+
+#include "storage/itemptr.h"
+#include "storage/lock.h"
+#include "utils/rel.h"
+
+/* 
+ * This was moved from pladt.h for the new lock manager.  Want to obsolete
+ * all of the old code.
+ */
+typedef struct LRelId {
+    Oid         relId;     /* a relation identifier */
+    Oid     dbId;      /* a database identifier */
+} LRelId;
+
+typedef struct LockInfoData  {
+    bool                    initialized;
+    LRelId                  lRelId;
+    TransactionId           transactionIdData;
+    uint16                  flags;
+} LockInfoData;
+typedef LockInfoData    *LockInfo;
+
+#define LockInfoIsValid(linfo) \
+       ((PointerIsValid(linfo)) &&  ((LockInfo) linfo)->initialized)
+
+
+extern LRelId RelationGetLRelId(Relation relation);
+extern Oid LRelIdGetDatabaseId(LRelId lRelId);
+extern Oid LRelIdGetRelationId(LRelId lRelId);
+extern bool DatabaseIdIsMyDatabaseId(Oid databaseId);
+extern bool LRelIdContainsMyDatabaseId(LRelId lRelId);
+extern void RelationInitLockInfo(Relation relation);
+extern void RelationDiscardLockInfo(Relation relation);
+extern void RelationSetLockForDescriptorOpen(Relation relation);
+extern void RelationSetLockForRead(Relation relation);
+extern void RelationUnsetLockForRead(Relation relation);
+extern void RelationSetLockForWrite(Relation relation);
+extern void RelationUnsetLockForWrite(Relation relation);
+extern void RelationSetLockForTupleRead(Relation relation,
+                                       ItemPointer itemPointer);
+
+/* used in vaccum.c */
+extern void RelationSetLockForWritePage(Relation relation,
+                      ItemPointer itemPointer);
+
+/* used in nbtpage.c, hashpage.c */
+extern void RelationSetSingleWLockPage(Relation relation,
+                      ItemPointer itemPointer);
+extern void RelationUnsetSingleWLockPage(Relation relation,
+                      ItemPointer itemPointer);
+extern void RelationSetSingleRLockPage(Relation relation,
+                      ItemPointer itemPointer);
+extern void RelationUnsetSingleRLockPage(Relation relation,
+                      ItemPointer itemPointer);
+extern void RelationSetRIntentLock(Relation relation);
+extern void RelationUnsetRIntentLock(Relation relation);
+extern void RelationSetWIntentLock(Relation relation);
+extern void RelationUnsetWIntentLock(Relation relation);
+extern void RelationSetLockForExtend(Relation relation);
+extern void RelationUnsetLockForExtend(Relation relation);
+extern void LRelIdAssign(LRelId *lRelId, Oid dbId, Oid relId);
+
+/* single.c */
+extern bool SingleLockReln(LockInfo linfo, LOCKT lockt, int action);
+extern bool SingleLockPage(LockInfo linfo, ItemPointer tidPtr,
+                          LOCKT lockt, int action);
+
+#endif /* LMGR_H */
diff --git a/src/backend/storage/lmgr/Makefile.inc b/src/backend/storage/lmgr/Makefile.inc
new file mode 100644 (file)
index 0000000..07a7ac0
--- /dev/null
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for storage/lmgr
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= lmgr.c lock.c multi.c proc.c single.c
diff --git a/src/backend/storage/lmgr/README b/src/backend/storage/lmgr/README
new file mode 100644 (file)
index 0000000..3775f18
--- /dev/null
@@ -0,0 +1,93 @@
+$Header$
+
+This file is an attempt to save me (and future code maintainers) some
+time and a lot of headaches.  The existing lock manager code at the time
+of this writing (June 16 1992) can best be described as confusing.  The
+complexity seems inherent in lock manager functionality, but variable
+names chosen in the current implementation really confuse me everytime
+I have to track down a bug.  Also, what gets done where and by whom isn't
+always clear....
+
+Starting with the data structures the lock manager relies upon...
+
+(NOTE - these will undoubtedly change over time and it is likely
+that this file won't always be updated along with the structs.)
+
+The lock manager's LOCK:
+
+tag -
+    The key fields that are used for hashing locks in the shared memory
+    lock hash table.  This is kept as a separate struct to ensure that we
+    always zero out the correct number of bytes.  This is a problem as
+    part of the tag is an itempointer which is 6 bytes and causes 2
+    additional bytes to be added as padding.
+
+    tag.relId -
+       Uniquely identifies the relation that the lock corresponds to.
+    
+    tag.dbId -
+       Uniquely identifies the database in which the relation lives.  If
+       this is a shared system relation (e.g. pg_user) the dbId should be
+       set to 0.
+
+    tag.tupleId -
+       Uniquely identifies the block/page within the relation and the
+       tuple within the block.  If we are setting a table level lock
+       both the blockId and tupleId (in an item pointer this is called
+       the position) are set to invalid, if it is a page level lock the
+       blockId is valid, while the tuleId is still invalid.  Finally if
+       this is a tuple level lock (we currently never do this) then both
+       the blockId and tupleId are set to valid specifications.  This is
+       how we get the appearance of a multi-level lock table while using
+       only a single table (see Gray's paper on 2 phase locking if
+       you are puzzled about how multi-level lock tables work).
+
+mask -
+    This field indicates what types of locks are currently held in the
+    given lock.  It is used (against the lock table's conflict table)
+    to determine if the new lock request will conflict with existing
+    lock types held.  Conficts are determined by bitwise AND operations
+    between the mask and the conflict table entry for the given lock type
+    to be set.  The current representation is that each bit (1 through 5)
+    is set when that lock type (WRITE, READ, WRITE INTENT, READ INTENT, EXTEND)
+    has been acquired for the lock.
+
+waitProcs -
+    This is a shared memory queue of all process structures corresponding to
+    a backend that is waiting (sleeping) until another backend releases this
+    lock.  The process structure holds the information needed to determine
+    if it should be woken up when this lock is released.  If, for example,
+    we are releasing a read lock and the process is sleeping trying to acquire
+    a read lock then there is no point in waking it since the lock being
+    released isn't what caused it to sleep in the first place.  There will
+    be more on this below (when I get to releasing locks and waking sleeping
+    process routines).
+
+nHolding -
+    Keeps a count of how many times this lock has been attempted to be
+    acquired.  The count includes attempts by processes which were put
+    to sleep due to conflicts.  It also counts the same backend twice
+    if, for example, a backend process first acquires a read and then
+    acquires a write.
+
+holders -
+    Keeps a count of how many locks of each type have been attempted.  Only
+    elements 1 through MAX_LOCK_TYPES are used as they correspond to the lock
+    type defined constants (WRITE through EXTEND).  Summing the values of
+    holders should come out equal to nHolding.
+
+nActive -
+    Keeps a count of how many times this lock has been succesfully acquired.
+    This count does not include attempts that were rejected due to conflicts,
+    but can count the same backend twice (e.g. a read then a write -- since
+    its the same transaction this won't cause a conflict)
+
+activeHolders -
+    Keeps a count of how locks of each type are currently held.  Once again
+    only elements 1 through MAX_LOCK_TYPES are used (0 is not).  Also, like
+    holders, summing the values of activeHolders should total to the value
+    of nActive.
+
+
+This is all I had the stomach for right now..... I will get back to this
+someday.       -mer 17 June 1992 12:00 am
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c
new file mode 100644 (file)
index 0000000..16b3032
--- /dev/null
@@ -0,0 +1,933 @@
+/*-------------------------------------------------------------------------
+ *
+ * lmgr.c--
+ *    POSTGRES lock manager code
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/* #define LOCKDEBUGALL        1 */
+/* #define LOCKDEBUG   1 */
+
+#ifdef LOCKDEBUGALL
+#define LOCKDEBUG      1
+#endif /*  LOCKDEBUGALL */
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "utils/tqual.h"
+#include "access/xact.h"
+
+#include "storage/block.h"
+#include "storage/buf.h"
+#include "storage/itemptr.h"
+#include "storage/bufpage.h"
+#include "storage/multilev.h"
+#include "storage/lmgr.h"
+
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+
+#include "catalog/catname.h"
+#include "catalog/catalog.h"
+#include "catalog/pg_class.h"
+
+#include "nodes/memnodes.h"
+#include "storage/bufmgr.h"
+#include "access/transam.h"    /* for AmiTransactionId */
+
+/* ----------------
+ *     
+ * ----------------
+ */
+#define MaxRetries     4       /* XXX about 1/4 minute--a hack */
+
+#define IntentReadRelationLock 0x0100
+#define ReadRelationLock       0x0200
+#define IntentWriteRelationLock        0x0400
+#define WriteRelationLock      0x0800
+#define IntentReadPageLock     0x1000
+#define ReadTupleLock          0x2000
+
+#define TupleLevelLockCountMask        0x000f
+
+#define TupleLevelLockLimit    10
+
+extern Oid     MyDatabaseId;
+
+static LRelId  VariableRelationLRelId = {
+    RelOid_pg_variable,
+    InvalidOid
+};
+
+/* ----------------
+ *     RelationGetLRelId
+ * ----------------
+ */
+#ifdef LOCKDEBUG
+#define LOCKDEBUG_10 \
+elog(NOTICE, "RelationGetLRelId(%s) invalid lockInfo", \
+     RelationGetRelationName(relation));
+#else
+#define LOCKDEBUG_10
+#endif /* LOCKDEBUG */
+     
+/*
+ * RelationGetLRelId --
+ *     Returns "lock" relation identifier for a relation.
+ */
+LRelId
+RelationGetLRelId(Relation relation)
+{
+    LockInfo   linfo;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(RelationIsValid(relation));
+    linfo = (LockInfo) relation->lockInfo;
+    
+    /* ----------------
+     * initialize lock info if necessary
+     * ----------------
+     */
+    if (! LockInfoIsValid(linfo)) {
+       LOCKDEBUG_10;
+       RelationInitLockInfo(relation);
+       linfo = (LockInfo) relation->lockInfo;
+    }
+    
+    /* ----------------
+     * XXX hack to prevent problems during
+     * VARIABLE relation initialization
+     * ----------------
+     */
+    if (strcmp(RelationGetRelationName(relation)->data,
+              VariableRelationName) == 0) {
+       return (VariableRelationLRelId);
+    }
+    
+    return (linfo->lRelId);
+}
+
+/*
+ * LRelIdGetDatabaseId --
+ *     Returns database identifier for a "lock" relation identifier.
+ */
+/* ----------------
+ *     LRelIdGetDatabaseId
+ *
+ * Note: The argument may not be correct, if it is not used soon
+ *      after it is created.
+ * ----------------
+ */
+Oid
+LRelIdGetDatabaseId(LRelId lRelId)
+{
+    return (lRelId.dbId);
+}
+
+
+/*
+ * LRelIdGetRelationId --
+ *     Returns relation identifier for a "lock" relation identifier.
+ */
+Oid 
+LRelIdGetRelationId(LRelId lRelId)
+{
+    return (lRelId.relId);
+}
+
+/*
+ * DatabaseIdIsMyDatabaseId --
+ *     True iff database object identifier is valid in my present database.
+ */
+bool
+DatabaseIdIsMyDatabaseId(Oid databaseId)
+{
+    return (bool)
+       (!OidIsValid(databaseId) || databaseId == MyDatabaseId);
+}
+
+/*
+ * LRelIdContainsMyDatabaseId --
+ *     True iff "lock" relation identifier is valid in my present database.
+ */
+bool
+LRelIdContainsMyDatabaseId(LRelId lRelId)
+{
+    return (bool)
+       (!OidIsValid(lRelId.dbId) || lRelId.dbId == MyDatabaseId);
+}
+
+/*
+ * RelationInitLockInfo --
+ *     Initializes the lock information in a relation descriptor.
+ */
+/* ----------------
+ *     RelationInitLockInfo
+ *
+ *     XXX processingVariable is a hack to prevent problems during
+ *     VARIABLE relation initialization.
+ * ----------------
+ */
+void
+RelationInitLockInfo(Relation relation)
+{
+    LockInfo           info;
+    char               *relname;
+    Oid                relationid;
+    bool               processingVariable;
+    extern Oid MyDatabaseId;           /* XXX use include */
+    extern GlobalMemory CacheCxt;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(RelationIsValid(relation));
+    Assert(OidIsValid(RelationGetRelationId(relation)));
+    
+    /* ----------------
+     * get information from relation descriptor
+     * ----------------
+     */
+    info = (LockInfo) relation->lockInfo;
+    relname = (char *) RelationGetRelationName(relation);
+    relationid = RelationGetRelationId(relation);
+    processingVariable = (strcmp(relname, VariableRelationName) == 0);
+    
+    /* ----------------
+     * create a new lockinfo if not already done
+     * ----------------
+     */
+    if (! PointerIsValid(info)) 
+       {
+           MemoryContext oldcxt;
+           
+           oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+           info = (LockInfo)palloc(sizeof(LockInfoData));
+           MemoryContextSwitchTo(oldcxt);
+       }
+    else if (processingVariable) {
+       if (IsTransactionState()) {
+           TransactionIdStore(GetCurrentTransactionId(),
+                              &info->transactionIdData);
+       }
+       info->flags = 0x0;
+       return;         /* prevent an infinite loop--still true? */
+    }
+    else if (info->initialized)
+       {
+           /* ------------
+            *  If we've already initialized we're done.
+            * ------------
+            */
+           return;
+       }
+    
+    /* ----------------
+     * initialize lockinfo.dbId and .relId appropriately
+     * ----------------
+     */
+    if (IsSharedSystemRelationName(relname))
+       LRelIdAssign(&info->lRelId, InvalidOid, relationid);
+    else
+       LRelIdAssign(&info->lRelId, MyDatabaseId, relationid);
+    
+    /* ----------------
+     * store the transaction id in the lockInfo field
+     * ----------------
+     */
+    if (processingVariable)
+       TransactionIdStore(AmiTransactionId,
+                          &info->transactionIdData);
+    else if (IsTransactionState()) 
+       TransactionIdStore(GetCurrentTransactionId(),
+                          &info->transactionIdData);
+    else
+       StoreInvalidTransactionId(&(info->transactionIdData));
+    
+    /* ----------------
+     * initialize rest of lockinfo
+     * ----------------
+     */
+    info->flags = 0x0;
+    info->initialized =        (bool)true;
+    relation->lockInfo = (Pointer) info;
+}
+
+/* ----------------
+ *     RelationDiscardLockInfo
+ * ----------------
+ */
+#ifdef LOCKDEBUG
+#define LOCKDEBUG_20 \
+elog(DEBUG, "DiscardLockInfo: NULL relation->lockInfo")
+#else
+#define LOCKDEBUG_20
+#endif /* LOCKDEBUG */
+     
+/*
+ * RelationDiscardLockInfo --
+ *     Discards the lock information in a relation descriptor.
+ */
+void
+RelationDiscardLockInfo(Relation relation)
+{
+    if (! LockInfoIsValid(relation->lockInfo)) {
+       LOCKDEBUG_20;
+       return;
+    }
+    
+    pfree(relation->lockInfo);
+    relation->lockInfo = NULL;
+}
+
+/*
+ * RelationSetLockForDescriptorOpen --
+ *     Sets read locks for a relation descriptor.
+ */
+#ifdef LOCKDEBUGALL
+#define LOCKDEBUGALL_30 \
+elog(DEBUG, "RelationSetLockForDescriptorOpen(%s[%d,%d]) called", \
+     RelationGetRelationName(relation), lRelId.dbId, lRelId.relId)
+#else
+#define LOCKDEBUGALL_30
+#endif /* LOCKDEBUGALL*/
+     
+void
+RelationSetLockForDescriptorOpen(Relation relation)
+{
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(RelationIsValid(relation));
+    if (LockingDisabled())
+       return;
+    
+    LOCKDEBUGALL_30;
+    
+    /* ----------------
+     * read lock catalog tuples which compose the relation descriptor
+     * XXX race condition? XXX For now, do nothing.
+     * ----------------
+     */
+}
+
+/* ----------------
+ *     RelationSetLockForRead
+ * ----------------
+ */
+#ifdef LOCKDEBUG
+#define LOCKDEBUG_40 \
+elog(DEBUG, "RelationSetLockForRead(%s[%d,%d]) called", \
+     RelationGetRelationName(relation), lRelId.dbId, lRelId.relId)
+#else
+#define LOCKDEBUG_40
+#endif /* LOCKDEBUG*/
+     
+/*
+ * RelationSetLockForRead --
+ *     Sets relation level read lock.
+ */
+void
+RelationSetLockForRead(Relation relation)
+{
+    LockInfo   linfo;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(RelationIsValid(relation));
+    if (LockingDisabled())
+       return;
+    
+    LOCKDEBUG_40;
+    
+    /* ----------------
+     * If we don't have lock info on the reln just go ahead and
+     * lock it without trying to short circuit the lock manager.
+     * ----------------
+     */
+    if (!LockInfoIsValid(relation->lockInfo))
+       {
+           RelationInitLockInfo(relation);
+           linfo = (LockInfo) relation->lockInfo;
+           linfo->flags |= ReadRelationLock;
+           MultiLockReln(linfo, READ_LOCK);
+           return;
+       }
+    else
+        linfo = (LockInfo) relation->lockInfo;
+    
+    MultiLockReln(linfo, READ_LOCK);
+}
+
+/* ----------------
+ *     RelationUnsetLockForRead
+ * ----------------
+ */
+#ifdef LOCKDEBUG
+#define LOCKDEBUG_50 \
+elog(DEBUG, "RelationUnsetLockForRead(%s[%d,%d]) called", \
+     RelationGetRelationName(relation), lRelId.dbId, lRelId.relId)
+#else
+#define LOCKDEBUG_50
+#endif /* LOCKDEBUG*/
+     
+/*
+ * RelationUnsetLockForRead --
+ *     Unsets relation level read lock.
+ */
+void
+RelationUnsetLockForRead(Relation relation)
+{
+    LockInfo   linfo;
+    
+    /* ----------------
+     * sanity check
+     * ----------------
+     */
+    Assert(RelationIsValid(relation));
+    if (LockingDisabled())
+       return;
+    
+    linfo = (LockInfo) relation->lockInfo;
+    
+    /* ----------------
+     * If we don't have lock info on the reln just go ahead and
+     * release it.
+     * ----------------
+     */
+    if (!LockInfoIsValid(linfo))
+       {
+           elog(WARN, 
+                "Releasing a lock on %s with invalid lock information",
+                RelationGetRelationName(relation));
+       }
+    
+    MultiReleaseReln(linfo, READ_LOCK);
+}
+
+/* ----------------
+ *     RelationSetLockForWrite(relation)
+ * ----------------
+ */
+#ifdef LOCKDEBUG
+#define LOCKDEBUG_60 \
+elog(DEBUG, "RelationSetLockForWrite(%s[%d,%d]) called", \
+     RelationGetRelationName(relation), lRelId.dbId, lRelId.relId)
+#else
+#define LOCKDEBUG_60
+#endif /* LOCKDEBUG*/
+     
+/*
+ * RelationSetLockForWrite --
+ *     Sets relation level write lock.
+ */
+void
+RelationSetLockForWrite(Relation relation)
+{
+    LockInfo   linfo;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(RelationIsValid(relation));
+    if (LockingDisabled())
+       return;
+    
+    LOCKDEBUG_60;
+    
+    /* ----------------
+     * If we don't have lock info on the reln just go ahead and
+     * lock it without trying to short circuit the lock manager.
+     * ----------------
+     */
+    if (!LockInfoIsValid(relation->lockInfo))
+       {
+           RelationInitLockInfo(relation);
+           linfo = (LockInfo) relation->lockInfo;
+           linfo->flags |= WriteRelationLock;
+           MultiLockReln(linfo, WRITE_LOCK);
+           return;
+       }
+    else
+        linfo = (LockInfo) relation->lockInfo;
+    
+    MultiLockReln(linfo, WRITE_LOCK);
+}
+
+/* ----------------
+ *     RelationUnsetLockForWrite
+ * ----------------
+ */
+#ifdef LOCKDEBUG
+#define LOCKDEBUG_70 \
+elog(DEBUG, "RelationUnsetLockForWrite(%s[%d,%d]) called", \
+     RelationGetRelationName(relation), lRelId.dbId, lRelId.relId);
+#else
+#define LOCKDEBUG_70
+#endif /* LOCKDEBUG */
+     
+/*
+ * RelationUnsetLockForWrite --
+ *     Unsets relation level write lock.
+ */
+void
+RelationUnsetLockForWrite(Relation relation)
+{
+    LockInfo   linfo;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(RelationIsValid(relation));
+    if (LockingDisabled()) {
+       return;
+    }
+    
+    linfo = (LockInfo) relation->lockInfo;
+    
+    if (!LockInfoIsValid(linfo))
+       {
+           elog(WARN, 
+                "Releasing a lock on %s with invalid lock information",
+                RelationGetRelationName(relation));
+       }
+    
+    MultiReleaseReln(linfo, WRITE_LOCK);
+}
+
+/* ----------------
+ *     RelationSetLockForTupleRead
+ * ----------------
+ */
+#ifdef LOCKDEBUG
+#define LOCKDEBUG_80 \
+elog(DEBUG, "RelationSetLockForTupleRead(%s[%d,%d], 0x%x) called", \
+     RelationGetRelationName(relation), lRelId.dbId, lRelId.relId, \
+     itemPointer)
+#define LOCKDEBUG_81 \
+     elog(DEBUG, "RelationSetLockForTupleRead() escalating");
+#else
+#define LOCKDEBUG_80
+#define LOCKDEBUG_81
+#endif /* LOCKDEBUG */
+     
+/*
+ * RelationSetLockForTupleRead --
+ *     Sets tuple level read lock.
+ */
+void
+RelationSetLockForTupleRead(Relation relation, ItemPointer itemPointer)
+{
+    LockInfo   linfo;
+    TransactionId curXact;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(RelationIsValid(relation));
+    if (LockingDisabled())
+       return;
+    
+    LOCKDEBUG_80;
+    
+    /* ---------------------
+     * If our lock info is invalid don't bother trying to short circuit
+     * the lock manager.
+     * ---------------------
+     */
+    if (!LockInfoIsValid(relation->lockInfo))
+       {
+           RelationInitLockInfo(relation);
+           linfo = (LockInfo) relation->lockInfo;
+           linfo->flags |=
+                IntentReadRelationLock |
+                   IntentReadPageLock |
+                       ReadTupleLock;
+           MultiLockTuple(linfo, itemPointer, READ_LOCK);
+           return;
+       }
+    else
+        linfo = (LockInfo) relation->lockInfo;
+    
+    /* ----------------
+     * no need to set a lower granularity lock
+     * ----------------
+     */
+    curXact = GetCurrentTransactionId();
+    if ((linfo->flags & ReadRelationLock) &&
+       TransactionIdEquals(curXact, linfo->transactionIdData))
+       {
+           return;
+       }
+    
+    /* ----------------
+     * If we don't already have a tuple lock this transaction
+     * ----------------
+     */
+    if (!( (linfo->flags & ReadTupleLock) &&
+         TransactionIdEquals(curXact, linfo->transactionIdData) )) {
+       
+       linfo->flags |=
+           IntentReadRelationLock |
+               IntentReadPageLock |
+                   ReadTupleLock;
+       
+       /* clear count */
+       linfo->flags &= ~TupleLevelLockCountMask;
+       
+    } else {
+       if (TupleLevelLockLimit == (TupleLevelLockCountMask &
+                                   linfo->flags)) {
+           LOCKDEBUG_81;
+           
+           /* escalate */
+           MultiLockReln(linfo, READ_LOCK);
+           
+           /* clear count */
+           linfo->flags &= ~TupleLevelLockCountMask;
+           return;
+       }
+       
+       /* increment count */
+       linfo->flags =
+           (linfo->flags & ~TupleLevelLockCountMask) |
+               (1 + (TupleLevelLockCountMask & linfo->flags));
+    }
+    
+    TransactionIdStore(curXact, &linfo->transactionIdData);
+    
+    /* ----------------
+     * Lock the tuple.
+     * ----------------
+     */
+    MultiLockTuple(linfo, itemPointer, READ_LOCK);
+}
+
+/* ----------------
+ *     RelationSetLockForReadPage
+ * ----------------
+ */
+#ifdef LOCKDEBUG
+#define LOCKDEBUG_90 \
+elog(DEBUG, "RelationSetLockForReadPage(%s[%d,%d], @%d) called", \
+     RelationGetRelationName(relation), lRelId.dbId, lRelId.relId, page);
+#else
+#define LOCKDEBUG_90
+#endif /* LOCKDEBUG*/
+     
+/* ----------------
+ *     RelationSetLockForWritePage
+ * ----------------
+ */
+#ifdef LOCKDEBUG
+#define LOCKDEBUG_100 \
+elog(DEBUG, "RelationSetLockForWritePage(%s[%d,%d], @%d) called", \
+     RelationGetRelationName(relation), lRelId.dbId, lRelId.relId, page);
+#else
+#define LOCKDEBUG_100
+#endif /* LOCKDEBUG */
+     
+/*
+ * RelationSetLockForWritePage --
+ *     Sets write lock on a page.
+ */
+void 
+RelationSetLockForWritePage(Relation relation,
+                           ItemPointer itemPointer)
+{
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(RelationIsValid(relation));
+    if (LockingDisabled())
+       return;
+    
+    /* ---------------
+     * Make sure linfo is initialized
+     * ---------------
+     */
+    if (!LockInfoIsValid(relation->lockInfo))
+       RelationInitLockInfo(relation);
+    
+    /* ----------------
+     * attempt to set lock
+     * ----------------
+     */
+    MultiLockPage((LockInfo) relation->lockInfo, itemPointer, WRITE_LOCK);
+}
+
+/* ----------------
+ *     RelationUnsetLockForReadPage
+ * ----------------
+ */
+#ifdef LOCKDEBUG
+#define LOCKDEBUG_110 \
+elog(DEBUG, "RelationUnsetLockForReadPage(%s[%d,%d], @%d) called", \
+     RelationGetRelationName(relation), lRelId.dbId, lRelId.relId, page)
+#else
+#define LOCKDEBUG_110
+#endif /* LOCKDEBUG */
+     
+/* ----------------
+ *     RelationUnsetLockForWritePage
+ * ----------------
+ */
+#ifdef LOCKDEBUG
+#define LOCKDEBUG_120 \
+elog(DEBUG, "RelationUnsetLockForWritePage(%s[%d,%d], @%d) called", \
+     RelationGetRelationName(relation), lRelId.dbId, lRelId.relId, page)
+#else
+#define LOCKDEBUG_120
+#endif /* LOCKDEBUG */
+     
+/*
+ * Set a single level write page lock.  Assumes that you already
+ * have a write intent lock on the relation.
+ */
+void
+RelationSetSingleWLockPage(Relation relation,
+                          ItemPointer itemPointer)
+{
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(RelationIsValid(relation));
+    if (LockingDisabled())
+       return;
+    
+    if (!LockInfoIsValid(relation->lockInfo))
+       RelationInitLockInfo(relation);
+    
+    SingleLockPage((LockInfo)relation->lockInfo, itemPointer, WRITE_LOCK, !UNLOCK);
+}
+
+/*
+ * Unset a single level write page lock
+ */
+void
+RelationUnsetSingleWLockPage(Relation relation,
+                            ItemPointer itemPointer)
+{
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(RelationIsValid(relation));
+    if (LockingDisabled())
+       return;
+    
+    if (!LockInfoIsValid(relation->lockInfo))
+        elog(WARN, 
+            "Releasing a lock on %s with invalid lock information",
+            RelationGetRelationName(relation));
+    
+    SingleLockPage((LockInfo)relation->lockInfo, itemPointer, WRITE_LOCK, UNLOCK);
+}
+
+/*
+ * Set a single level read page lock.  Assumes you already have a read
+ * intent lock set on the relation.
+ */
+void
+RelationSetSingleRLockPage(Relation relation,
+                          ItemPointer itemPointer)
+{
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(RelationIsValid(relation));
+    if (LockingDisabled())
+       return;
+    
+    if (!LockInfoIsValid(relation->lockInfo))
+       RelationInitLockInfo(relation);
+    
+    SingleLockPage((LockInfo)relation->lockInfo, itemPointer, READ_LOCK, !UNLOCK);
+}
+
+/* 
+ * Unset a single level read page lock.
+ */
+void
+RelationUnsetSingleRLockPage(Relation relation,
+                            ItemPointer itemPointer)
+{
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(RelationIsValid(relation));
+    if (LockingDisabled())
+       return;
+    
+    if (!LockInfoIsValid(relation->lockInfo))
+        elog(WARN, 
+            "Releasing a lock on %s with invalid lock information",
+            RelationGetRelationName(relation));
+    
+    SingleLockPage((LockInfo)relation->lockInfo, itemPointer, READ_LOCK, UNLOCK);
+}
+
+/*
+ * Set a read intent lock on a relation.
+ *
+ * Usually these are set in a multi-level table when you acquiring a
+ * page level lock.  i.e. To acquire a lock on a page you first acquire
+ * an intent lock on the entire relation.  Acquiring an intent lock along
+ * allows one to use the single level locking routines later.  Good for
+ * index scans that do a lot of page level locking.
+ */
+void
+RelationSetRIntentLock(Relation relation)
+{
+    /* -----------------
+     * Sanity check
+     * -----------------
+     */
+    Assert(RelationIsValid(relation));
+    if (LockingDisabled())
+       return;
+    
+    if (!LockInfoIsValid(relation->lockInfo))
+       RelationInitLockInfo(relation);
+    
+    SingleLockReln((LockInfo)relation->lockInfo, READ_LOCK+INTENT, !UNLOCK);
+}
+
+/*
+ * Unset a read intent lock on a relation
+ */
+void
+RelationUnsetRIntentLock(Relation relation)
+{
+    /* -----------------
+     * Sanity check
+     * -----------------
+     */
+    Assert(RelationIsValid(relation));
+    if (LockingDisabled())
+       return;
+    
+    if (!LockInfoIsValid(relation->lockInfo))
+       RelationInitLockInfo(relation);
+    
+    SingleLockReln((LockInfo)relation->lockInfo, READ_LOCK+INTENT, UNLOCK);
+}
+
+/*
+ * Set a write intent lock on a relation. For a more complete explanation
+ * see RelationSetRIntentLock()
+ */
+void
+RelationSetWIntentLock(Relation relation)
+{
+    /* -----------------
+     * Sanity check
+     * -----------------
+     */
+    Assert(RelationIsValid(relation));
+    if (LockingDisabled())
+       return;
+    
+    if (!LockInfoIsValid(relation->lockInfo))
+       RelationInitLockInfo(relation);
+    
+    SingleLockReln((LockInfo)relation->lockInfo, WRITE_LOCK+INTENT, !UNLOCK);
+}
+
+/*
+ * Unset a write intent lock.
+ */
+void
+RelationUnsetWIntentLock(Relation relation)
+{
+    /* -----------------
+     * Sanity check
+     * -----------------
+     */
+    Assert(RelationIsValid(relation));
+    if (LockingDisabled())
+       return;
+    
+    if (!LockInfoIsValid(relation->lockInfo))
+       RelationInitLockInfo(relation);
+    
+    SingleLockReln((LockInfo)relation->lockInfo, WRITE_LOCK+INTENT, UNLOCK);
+}
+
+/*
+ * Extend locks are used primarily in tertiary storage devices such as
+ * a WORM disk jukebox.  Sometimes need exclusive access to extend a 
+ * file by a block.
+ */
+void
+RelationSetLockForExtend(Relation relation)
+{
+    /* -----------------
+     * Sanity check
+     * -----------------
+     */
+    Assert(RelationIsValid(relation));
+    if (LockingDisabled())
+       return;
+    
+    if (!LockInfoIsValid(relation->lockInfo))
+       RelationInitLockInfo(relation);
+    
+    MultiLockReln((LockInfo) relation->lockInfo, EXTEND_LOCK);
+}
+
+void
+RelationUnsetLockForExtend(Relation relation)
+{
+    /* -----------------
+     * Sanity check
+     * -----------------
+     */
+    Assert(RelationIsValid(relation));
+    if (LockingDisabled())
+       return;
+    
+    if (!LockInfoIsValid(relation->lockInfo))
+       RelationInitLockInfo(relation);
+    
+    MultiReleaseReln((LockInfo) relation->lockInfo, EXTEND_LOCK);
+}
+
+/* 
+ * Create an LRelid --- Why not just pass in a pointer to the storage?
+ */
+void
+LRelIdAssign(LRelId *lRelId, Oid dbId, Oid relId)
+{   
+    lRelId->dbId = dbId;
+    lRelId->relId = relId;
+}
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
new file mode 100644 (file)
index 0000000..d32fda3
--- /dev/null
@@ -0,0 +1,1020 @@
+/*-------------------------------------------------------------------------
+ *
+ * lock.c--
+ *    simple lock acquisition
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    Outside modules can create a lock table and acquire/release
+ *    locks.  A lock table is a shared memory hash table.  When
+ *    a process tries to acquire a lock of a type that conflicts
+ *    with existing locks, it is put to sleep using the routines
+ *    in storage/lmgr/proc.c.
+ *
+ *  Interface:
+ *
+ *  LockAcquire(), LockRelease(), LockTabInit().
+ *
+ *  LockReplace() is called only within this module and by the
+ *     lkchain module.  It releases a lock without looking
+ *     the lock up in the lock table.
+ *
+ *  NOTE: This module is used to define new lock tables.  The
+ *     multi-level lock table (multi.c) used by the heap
+ *     access methods calls these routines.  See multi.c for
+ *     examples showing how to use this interface.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>             /* for sprintf() */
+#include "storage/shmem.h"
+#include "storage/spin.h"
+#include "storage/proc.h"
+#include "storage/lock.h"
+#include "utils/hsearch.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "access/xact.h"
+
+/*#define LOCK_MGR_DEBUG*/
+
+#ifndef LOCK_MGR_DEBUG
+
+#define LOCK_PRINT(where,tag,type)
+#define LOCK_DUMP(where,lock,type)
+#define XID_PRINT(where,xidentP)
+
+#else /* LOCK_MGR_DEBUG */
+
+#define LOCK_PRINT(where,tag,type)\
+  elog(NOTICE, "%s: rel (%d) dbid (%d) tid (%d,%d) type (%d)\n",where, \
+        tag->relId, tag->dbId, \
+        ( (tag->tupleId.ip_blkid.data[0] >= 0) ? \
+               BlockIdGetBlockNumber(&tag->tupleId.ip_blkid) : -1 ), \
+        tag->tupleId.ip_posid, \
+        type);
+
+#define LOCK_DUMP(where,lock,type)\
+  elog(NOTICE, "%s: rel (%d) dbid (%d) tid (%d,%d) nHolding (%d) holders (%d,%d,%d,%d,%d) type (%d)\n",where, \
+       lock->tag.relId, lock->tag.dbId, \
+       ((lock->tag.tupleId.ip_blkid.data[0] >= 0) ? \
+       BlockIdGetBlockNumber(&lock->tag.tupleId.ip_blkid) : -1 ), \
+       lock->tag.tupleId.ip_posid, \
+       lock->nHolding,\
+       lock->holders[1],\
+       lock->holders[2],\
+       lock->holders[3],\
+       lock->holders[4],\
+       lock->holders[5],\
+       type);
+
+#define XID_PRINT(where,xidentP)\
+  elog(NOTICE,\
+       "%s:xid (%d) pid (%d) lock (%x) nHolding (%d) holders (%d,%d,%d,%d,%d)",\
+       where,\
+       xidentP->tag.xid,\
+       xidentP->tag.pid,\
+       xidentP->tag.lock,\
+       xidentP->nHolding,\
+       xidentP->holders[1],\
+       xidentP->holders[2],\
+       xidentP->holders[3],\
+       xidentP->holders[4],\
+       xidentP->holders[5]);
+
+#endif /* LOCK_MGR_DEBUG */
+
+SPINLOCK LockMgrLock;          /* in Shmem or created in CreateSpinlocks() */
+
+/* This is to simplify/speed up some bit arithmetic */
+
+static MASK    BITS_OFF[MAX_LOCKTYPES];
+static MASK    BITS_ON[MAX_LOCKTYPES];
+
+/* -----------------
+ * XXX Want to move this to this file
+ * -----------------
+ */
+static bool LockingIsDisabled;
+
+/* ------------------
+ * from storage/ipc/shmem.c
+ * ------------------
+ */
+extern HTAB *ShmemInitHash();
+
+/* -------------------
+ * map from tableId to the lock table structure
+ * -------------------
+ */
+static LOCKTAB *AllTables[MAX_TABLES];
+
+/* -------------------
+ * no zero-th table
+ * -------------------
+ */
+static int     NumTables = 1;
+
+/* -------------------
+ * InitLocks -- Init the lock module.  Create a private data
+ *     structure for constructing conflict masks.
+ * -------------------
+ */
+void
+InitLocks()
+{
+    int i;
+    int bit;
+    
+    bit = 1;
+    /* -------------------
+     * remember 0th locktype is invalid
+     * -------------------
+     */
+    for (i=0;i<MAX_LOCKTYPES;i++,bit <<= 1)
+       {
+           BITS_ON[i] = bit;
+           BITS_OFF[i] = ~bit;
+       }
+}
+
+/* -------------------
+ * LockDisable -- sets LockingIsDisabled flag to TRUE or FALSE.
+ * ------------------
+ */
+void
+LockDisable(int status)
+{
+    LockingIsDisabled = status;
+}
+
+
+/*
+ * LockTypeInit -- initialize the lock table's lock type
+ *     structures
+ *
+ * Notes: just copying.  Should only be called once.
+ */
+static void
+LockTypeInit(LOCKTAB *ltable,
+            MASK *conflictsP,
+            int *prioP,
+            int ntypes)
+{
+    int        i;
+    
+    ltable->ctl->nLockTypes = ntypes;
+    ntypes++;
+    for (i=0;i<ntypes;i++,prioP++,conflictsP++)
+       {
+           ltable->ctl->conflictTab[i] = *conflictsP;
+           ltable->ctl->prio[i] = *prioP;
+       }
+}
+
+/*
+ * LockTabInit -- initialize a lock table structure
+ *
+ * Notes:
+ *     (a) a lock table has four separate entries in the binding
+ *     table.  This is because every shared hash table and spinlock
+ *     has its name stored in the binding table at its creation.  It
+ *     is wasteful, in this case, but not much space is involved.
+ *
+ */
+LockTableId
+LockTabInit(char *tabName,
+           MASK *conflictsP,
+           int *prioP,
+           int ntypes)
+{
+    LOCKTAB *ltable;
+    char *shmemName;
+    HASHCTL info;
+    int hash_flags;
+    bool       found;
+    int status = TRUE;
+    
+    if (ntypes > MAX_LOCKTYPES)
+       {
+           elog(NOTICE,"LockTabInit: too many lock types %d greater than %d",
+                ntypes,MAX_LOCKTYPES);
+           return(INVALID_TABLEID);
+       }
+    
+    if (NumTables > MAX_TABLES)
+       {
+           elog(NOTICE,
+                "LockTabInit: system limit of MAX_TABLES (%d) lock tables",
+                MAX_TABLES);
+           return(INVALID_TABLEID);
+       }
+    
+    /* allocate a string for the binding table lookup */
+    shmemName = (char *) palloc((unsigned)(strlen(tabName)+32));
+    if (! shmemName)
+       {
+           elog(NOTICE,"LockTabInit: couldn't malloc string %s \n",tabName);
+           return(INVALID_TABLEID);
+       }
+    
+    /* each lock table has a non-shared header */
+    ltable = (LOCKTAB *) palloc((unsigned) sizeof(LOCKTAB));
+    if (! ltable)
+       {
+           elog(NOTICE,"LockTabInit: couldn't malloc lock table %s\n",tabName);
+           (void) pfree (shmemName);
+           return(INVALID_TABLEID);
+       }
+    
+    /* ------------------------
+     * find/acquire the spinlock for the table
+     * ------------------------
+     */
+    SpinAcquire(LockMgrLock);
+    
+    
+    /* -----------------------
+     * allocate a control structure from shared memory or attach to it
+     * if it already exists.
+     * -----------------------
+     */
+    sprintf(shmemName,"%s (ctl)",tabName);
+    ltable->ctl = (LOCKCTL *)
+       ShmemInitStruct(shmemName,(unsigned)sizeof(LOCKCTL),&found);
+    
+    if (! ltable->ctl)
+       {
+           elog(FATAL,"LockTabInit: couldn't initialize %s",tabName);
+           status = FALSE;
+       }
+    
+    /* ----------------
+     * we're first - initialize
+     * ----------------
+     */
+    if (! found)
+       {
+           memset(ltable->ctl, 0, sizeof(LOCKCTL)); 
+           ltable->ctl->masterLock = LockMgrLock;
+           ltable->ctl->tableId = NumTables;
+       }
+    
+    /* --------------------
+     * other modules refer to the lock table by a tableId
+     * --------------------
+     */
+    AllTables[NumTables] = ltable;
+    NumTables++;
+    Assert(NumTables <= MAX_TABLES);
+    
+    /* ----------------------
+     * allocate a hash table for the lock tags.  This is used
+     * to find the different locks.
+     * ----------------------
+     */
+    info.keysize =  sizeof(LOCKTAG);
+    info.datasize = sizeof(LOCK);
+    info.hash = tag_hash;
+    hash_flags = (HASH_ELEM | HASH_FUNCTION);
+    
+    sprintf(shmemName,"%s (lock hash)",tabName);
+    ltable->lockHash = (HTAB *) ShmemInitHash(shmemName,
+                                             INIT_TABLE_SIZE,MAX_TABLE_SIZE,
+                                             &info,hash_flags);
+    
+    Assert( ltable->lockHash->hash == tag_hash);
+    if (! ltable->lockHash)
+       {
+           elog(FATAL,"LockTabInit: couldn't initialize %s",tabName);
+           status = FALSE;
+       }
+    
+    /* -------------------------
+     * allocate an xid table.  When different transactions hold
+     * the same lock, additional information must be saved (locks per tx).
+     * -------------------------
+     */
+    info.keysize = XID_TAGSIZE;
+    info.datasize = sizeof(XIDLookupEnt);
+    info.hash = tag_hash;
+    hash_flags = (HASH_ELEM | HASH_FUNCTION);
+    
+    sprintf(shmemName,"%s (xid hash)",tabName);
+    ltable->xidHash = (HTAB *) ShmemInitHash(shmemName,
+                                            INIT_TABLE_SIZE,MAX_TABLE_SIZE,
+                                            &info,hash_flags);
+    
+    if (! ltable->xidHash)
+       {
+           elog(FATAL,"LockTabInit: couldn't initialize %s",tabName);
+           status = FALSE;
+       }
+    
+    /* init ctl data structures */
+    LockTypeInit(ltable, conflictsP, prioP, ntypes);
+    
+    SpinRelease(LockMgrLock);
+    
+    (void) pfree (shmemName);
+    
+    if (status)
+       return(ltable->ctl->tableId);
+    else
+       return(INVALID_TABLEID);
+}
+
+/*
+ * LockTabRename -- allocate another tableId to the same
+ *     lock table.
+ *
+ * NOTES: Both the lock module and the lock chain (lchain.c)
+ *     module use table id's to distinguish between different
+ *     kinds of locks.  Short term and long term locks look
+ *     the same to the lock table, but are handled differently
+ *     by the lock chain manager.  This function allows the
+ *     client to use different tableIds when acquiring/releasing
+ *     short term and long term locks.
+ */
+LockTableId
+LockTabRename(LockTableId tableId)
+{
+    LockTableId        newTableId;
+    
+    if (NumTables >= MAX_TABLES)
+       {
+           return(INVALID_TABLEID);
+       }
+    if (AllTables[tableId] == INVALID_TABLEID)
+       {
+           return(INVALID_TABLEID);
+       }
+    
+    /* other modules refer to the lock table by a tableId */
+    newTableId = NumTables;
+    NumTables++;
+    
+    AllTables[newTableId] = AllTables[tableId];
+    return(newTableId);
+}
+
+/*
+ * LockAcquire -- Check for lock conflicts, sleep if conflict found,
+ *     set lock if/when no conflicts.
+ *
+ * Returns: TRUE if parameters are correct, FALSE otherwise.
+ *
+ * Side Effects: The lock is always acquired.  No way to abort
+ *     a lock acquisition other than aborting the transaction.
+ *     Lock is recorded in the lkchain.
+ */
+bool
+LockAcquire(LockTableId tableId, LOCKTAG *lockName, LOCKT lockt)
+{
+    XIDLookupEnt       *result,item;
+    HTAB               *xidTable;
+    bool       found;
+    LOCK               *lock = NULL;
+    SPINLOCK   masterLock;
+    LOCKTAB    *ltable;
+    int                status;
+    TransactionId      myXid;
+    
+    Assert (tableId < NumTables);
+    ltable = AllTables[tableId];
+    if (!ltable)
+       {
+           elog(NOTICE,"LockAcquire: bad lock table %d",tableId);
+           return  (FALSE);
+       }
+    
+    if (LockingIsDisabled)
+       {
+           return(TRUE);
+       }
+    
+    LOCK_PRINT("Acquire",lockName,lockt);
+    masterLock = ltable->ctl->masterLock;
+    
+    SpinAcquire(masterLock);
+    
+    Assert( ltable->lockHash->hash == tag_hash);
+    lock = (LOCK *)hash_search(ltable->lockHash,(Pointer)lockName,HASH_ENTER,&found);
+    
+    if (! lock)
+       {
+           SpinRelease(masterLock);
+           elog(FATAL,"LockAcquire: lock table %d is corrupted",tableId);
+           return(FALSE);
+       }
+    
+    /* --------------------
+     * if there was nothing else there, complete initialization
+     * --------------------
+     */
+    if  (! found)
+       {
+           lock->mask = 0;
+           ProcQueueInit(&(lock->waitProcs));
+           memset((char *)lock->holders, 0, sizeof(int)*MAX_LOCKTYPES);
+           memset((char *)lock->activeHolders, 0, sizeof(int)*MAX_LOCKTYPES);
+           lock->nHolding = 0;
+           lock->nActive = 0;
+           
+           Assert(BlockIdEquals(&(lock->tag.tupleId.ip_blkid),
+                                &(lockName->tupleId.ip_blkid)));
+           
+       }
+    
+    /* ------------------
+     * add an element to the lock queue so that we can clear the
+     * locks at end of transaction.
+     * ------------------
+     */
+    xidTable = ltable->xidHash;
+    myXid = GetCurrentTransactionId();
+    
+    /* ------------------
+     * Zero out all of the tag bytes (this clears the padding bytes for long
+     * word alignment and ensures hashing consistency).
+     * ------------------
+     */
+    memset(&item, 0, XID_TAGSIZE); 
+    TransactionIdStore(myXid, &item.tag.xid);
+    item.tag.lock = MAKE_OFFSET(lock);
+#if 0
+    item.tag.pid = MyPid;
+#endif
+    
+    result = (XIDLookupEnt *)hash_search(xidTable, (Pointer)&item, HASH_ENTER, &found);
+    if (!result)
+       {
+           elog(NOTICE,"LockAcquire: xid table corrupted");
+           return(STATUS_ERROR);
+       }
+    if (!found)
+       {
+           XID_PRINT("queueing XidEnt LockAcquire:", result);
+           ProcAddLock(&result->queue);
+           result->nHolding = 0;
+           memset((char *)result->holders, 0, sizeof(int)*MAX_LOCKTYPES);
+       }
+    
+    /* ----------------
+     * lock->nholding tells us how many processes have _tried_ to
+     * acquire this lock,  Regardless of whether they succeeded or
+     * failed in doing so.
+     * ----------------
+     */
+    lock->nHolding++;
+    lock->holders[lockt]++;
+    
+    /* --------------------
+     * If I'm the only one holding a lock, then there
+     * cannot be a conflict.  Need to subtract one from the
+     * lock's count since we just bumped the count up by 1 
+     * above.
+     * --------------------
+     */
+    if (result->nHolding == lock->nActive)
+       {
+           result->holders[lockt]++;
+           result->nHolding++;
+           GrantLock(lock, lockt);
+           SpinRelease(masterLock);
+           return(TRUE);
+       }
+    
+    Assert(result->nHolding <= lock->nActive);
+    
+    status = LockResolveConflicts(ltable, lock, lockt, myXid);
+    
+    if (status == STATUS_OK)
+       {
+           GrantLock(lock, lockt);
+       }
+    else if (status == STATUS_FOUND)
+       {
+           status = WaitOnLock(ltable, tableId, lock, lockt);
+           XID_PRINT("Someone granted me the lock", result);
+       }
+    
+    SpinRelease(masterLock);
+    
+    return(status == STATUS_OK);
+}
+
+/* ----------------------------
+ * LockResolveConflicts -- test for lock conflicts
+ *
+ * NOTES:
+ *     Here's what makes this complicated: one transaction's
+ * locks don't conflict with one another.  When many processes
+ * hold locks, each has to subtract off the other's locks when
+ * determining whether or not any new lock acquired conflicts with
+ * the old ones.
+ *
+ *  For example, if I am already holding a WRITE_INTENT lock,
+ *  there will not be a conflict with my own READ_LOCK.  If I
+ *  don't consider the intent lock when checking for conflicts,
+ *  I find no conflict.
+ * ----------------------------
+ */
+int
+LockResolveConflicts(LOCKTAB *ltable,
+                    LOCK *lock,
+                    LOCKT lockt,
+                    TransactionId xid)
+{
+    XIDLookupEnt       *result,item;
+    int                *myHolders;
+    int                nLockTypes;
+    HTAB               *xidTable;
+    bool       found;
+    int                bitmask;
+    int                i,tmpMask;
+    
+    nLockTypes = ltable->ctl->nLockTypes;
+    xidTable = ltable->xidHash;
+    
+    /* ---------------------
+     * read my own statistics from the xid table.  If there
+     * isn't an entry, then we'll just add one.
+     *
+     * Zero out the tag, this clears the padding bytes for long
+     * word alignment and ensures hashing consistency.
+     * ------------------
+     */
+    memset(&item, 0, XID_TAGSIZE);
+    TransactionIdStore(xid, &item.tag.xid);
+    item.tag.lock = MAKE_OFFSET(lock);
+#if 0
+    item.tag.pid = pid;
+#endif
+    
+    if (! (result = (XIDLookupEnt *)
+          hash_search(xidTable, (Pointer)&item, HASH_ENTER, &found)))
+       {
+           elog(NOTICE,"LockResolveConflicts: xid table corrupted");
+           return(STATUS_ERROR);
+       }
+    myHolders = result->holders;
+    
+    if (! found)
+       {
+           /* ---------------
+            * we're not holding any type of lock yet.  Clear
+            * the lock stats.
+            * ---------------
+            */
+           memset(result->holders, 0, nLockTypes * sizeof(*(lock->holders))); 
+           result->nHolding = 0;
+       }
+    
+    /* ----------------------------
+     * first check for global conflicts: If no locks conflict
+     * with mine, then I get the lock.
+     *
+     * Checking for conflict: lock->mask represents the types of
+     * currently held locks.  conflictTable[lockt] has a bit
+     * set for each type of lock that conflicts with mine.  Bitwise
+     * compare tells if there is a conflict.
+     * ----------------------------
+     */
+    if (! (ltable->ctl->conflictTab[lockt] & lock->mask))
+       {
+           
+           result->holders[lockt]++;
+           result->nHolding++;
+           
+           XID_PRINT("Conflict Resolved: updated xid entry stats", result);
+           
+           return(STATUS_OK);
+       }
+    
+    /* ------------------------
+     * Rats.  Something conflicts. But it could still be my own
+     * lock.  We have to construct a conflict mask
+     * that does not reflect our own locks.
+     * ------------------------
+     */
+    bitmask = 0;
+    tmpMask = 2;
+    for (i=1;i<=nLockTypes;i++, tmpMask <<= 1)
+       {
+           if (lock->activeHolders[i] - myHolders[i])
+               {
+                   bitmask |= tmpMask;
+               }
+       }
+    
+    /* ------------------------
+     * now check again for conflicts.  'bitmask' describes the types
+     * of locks held by other processes.  If one of these
+     * conflicts with the kind of lock that I want, there is a
+     * conflict and I have to sleep.
+     * ------------------------
+     */
+    if (! (ltable->ctl->conflictTab[lockt] & bitmask))
+       {
+           
+           /* no conflict. Get the lock and go on */
+           
+           result->holders[lockt]++;
+           result->nHolding++;
+           
+           XID_PRINT("Conflict Resolved: updated xid entry stats", result);
+           
+           return(STATUS_OK);
+           
+       }
+    
+    return(STATUS_FOUND);
+}
+
+int
+WaitOnLock(LOCKTAB *ltable, LockTableId tableId, LOCK *lock, LOCKT lockt)
+{
+    PROC_QUEUE *waitQueue = &(lock->waitProcs);
+    
+    int prio = ltable->ctl->prio[lockt];
+    
+    /* the waitqueue is ordered by priority. I insert myself
+     * according to the priority of the lock I am acquiring.
+     *
+     * SYNC NOTE: I am assuming that the lock table spinlock
+     * is sufficient synchronization for this queue.  That
+     * will not be true if/when people can be deleted from
+     * the queue by a SIGINT or something.
+     */
+    LOCK_DUMP("WaitOnLock: sleeping on lock", lock, lockt);
+    if (ProcSleep(waitQueue,
+                 ltable->ctl->masterLock,
+                 lockt,
+                 prio,
+                 lock) != NO_ERROR)
+       {
+           /* -------------------
+            * This could have happend as a result of a deadlock, see HandleDeadLock()
+            * Decrement the lock nHolding and holders fields as we are no longer 
+            * waiting on this lock.
+            * -------------------
+            */
+           lock->nHolding--;
+           lock->holders[lockt]--;
+           LOCK_DUMP("WaitOnLock: aborting on lock", lock, lockt);
+           SpinRelease(ltable->ctl->masterLock);
+           elog(WARN,"WaitOnLock: error on wakeup - Aborting this transaction");
+       }
+    
+    return(STATUS_OK);
+}
+
+/*
+ * LockRelease -- look up 'lockName' in lock table 'tableId' and
+ *     release it.
+ *
+ * Side Effects: if the lock no longer conflicts with the highest
+ *     priority waiting process, that process is granted the lock
+ *     and awoken. (We have to grant the lock here to avoid a
+ *     race between the waking process and any new process to
+ *     come along and request the lock).
+ */
+bool
+LockRelease(LockTableId tableId, LOCKTAG *lockName, LOCKT lockt)
+{
+    LOCK               *lock = NULL;
+    SPINLOCK   masterLock;
+    bool       found;
+    LOCKTAB    *ltable;
+    XIDLookupEnt       *result,item;
+    HTAB               *xidTable;
+    bool               wakeupNeeded = true;
+    
+    Assert (tableId < NumTables);
+    ltable = AllTables[tableId];
+    if (!ltable) {
+       elog(NOTICE, "ltable is null in LockRelease");
+       return (FALSE);
+    }
+    
+    if (LockingIsDisabled)
+       {
+           return(TRUE);
+       }
+    
+    LOCK_PRINT("Release",lockName,lockt);
+    
+    masterLock = ltable->ctl->masterLock;
+    xidTable = ltable->xidHash;
+    
+    SpinAcquire(masterLock);
+    
+    Assert( ltable->lockHash->hash == tag_hash);
+    lock = (LOCK *)
+       hash_search(ltable->lockHash,(Pointer)lockName,HASH_FIND_SAVE,&found);
+    
+    /* let the caller print its own error message, too.
+     * Do not elog(WARN).
+     */
+    if (! lock)
+       {
+           SpinRelease(masterLock);
+           elog(NOTICE,"LockRelease: locktable corrupted");
+           return(FALSE);
+       }
+    
+    if (! found)
+       {
+           SpinRelease(masterLock);
+           elog(NOTICE,"LockRelease: locktable lookup failed, no lock");
+           return(FALSE);
+       }
+    
+    Assert(lock->nHolding > 0);
+    
+    /*
+     * fix the general lock stats
+     */
+    lock->nHolding--;
+    lock->holders[lockt]--;
+    lock->nActive--;
+    lock->activeHolders[lockt]--;
+    
+    Assert(lock->nActive >= 0);
+    
+    if (! lock->nHolding)
+       {
+           /* ------------------
+            * if there's no one waiting in the queue,
+            * we just released the last lock.
+            * Delete it from the lock table.
+            * ------------------
+            */
+           Assert( ltable->lockHash->hash == tag_hash);
+           lock = (LOCK *) hash_search(ltable->lockHash,
+                                       (Pointer) &(lock->tag),
+                                       HASH_REMOVE_SAVED,
+                                       &found);
+           Assert(lock && found);
+           wakeupNeeded = false;
+       }
+    
+    /* ------------------
+     * Zero out all of the tag bytes (this clears the padding bytes for long
+     * word alignment and ensures hashing consistency).
+     * ------------------
+     */
+    memset(&item, 0, XID_TAGSIZE);
+    
+    TransactionIdStore(GetCurrentTransactionId(), &item.tag.xid);
+    item.tag.lock = MAKE_OFFSET(lock);
+#if 0
+    item.tag.pid = MyPid;
+#endif
+    
+    if (! ( result = (XIDLookupEnt *) hash_search(xidTable,
+                                                 (Pointer)&item,
+                                                 HASH_FIND_SAVE,
+                                                 &found) )
+       || !found)
+       {
+           SpinRelease(masterLock);
+           elog(NOTICE,"LockReplace: xid table corrupted");
+           return(FALSE);
+       }
+    /*
+     * now check to see if I have any private locks.  If I do,
+     * decrement the counts associated with them.
+     */
+    result->holders[lockt]--;
+    result->nHolding--;
+    
+    XID_PRINT("LockRelease updated xid stats", result);
+    
+    /*
+     * If this was my last hold on this lock, delete my entry
+     * in the XID table.
+     */
+    if (! result->nHolding)
+       {
+           if (result->queue.next != INVALID_OFFSET)
+               SHMQueueDelete(&result->queue);
+           if (! (result = (XIDLookupEnt *)
+                  hash_search(xidTable, (Pointer)&item, HASH_REMOVE_SAVED, &found)) ||
+               ! found)
+               {
+                   SpinRelease(masterLock);
+                   elog(NOTICE,"LockReplace: xid table corrupted");
+                   return(FALSE);
+               }
+       }
+    
+    /* --------------------------
+     * If there are still active locks of the type I just released, no one
+     * should be woken up.  Whoever is asleep will still conflict
+     * with the remaining locks.
+     * --------------------------
+     */
+    if (! (lock->activeHolders[lockt]))
+       {
+           /* change the conflict mask.  No more of this lock type. */
+           lock->mask &= BITS_OFF[lockt];
+       }
+    
+    if (wakeupNeeded)
+       {
+           /* --------------------------
+            * Wake the first waiting process and grant him the lock if it
+            * doesn't conflict.  The woken process must record the lock
+            * himself.
+            * --------------------------
+            */
+           (void) ProcLockWakeup(&(lock->waitProcs), (char *) ltable, (char *) lock);
+       }
+    
+    SpinRelease(masterLock);
+    return(TRUE);
+}
+
+/*
+ * GrantLock -- udpate the lock data structure to show
+ *     the new lock holder.
+ */
+void
+GrantLock(LOCK *lock, LOCKT lockt)
+{
+    lock->nActive++;
+    lock->activeHolders[lockt]++;
+    lock->mask |= BITS_ON[lockt];
+}
+
+bool
+LockReleaseAll(LockTableId tableId, SHM_QUEUE *lockQueue)
+{
+    PROC_QUEUE         *waitQueue;
+    int                done;
+    XIDLookupEnt       *xidLook = NULL;
+    XIDLookupEnt       *tmp = NULL;
+    SHMEM_OFFSET       end = MAKE_OFFSET(lockQueue);
+    SPINLOCK   masterLock;
+    LOCKTAB    *ltable;
+    int                i,nLockTypes;
+    LOCK               *lock;
+    bool       found;
+    
+    Assert (tableId < NumTables);
+    ltable = AllTables[tableId];
+    if (!ltable)
+       return (FALSE);
+    
+    nLockTypes = ltable->ctl->nLockTypes;
+    masterLock = ltable->ctl->masterLock;
+    
+    if (SHMQueueEmpty(lockQueue))
+       return TRUE;
+    
+    SHMQueueFirst(lockQueue,(Pointer*)&xidLook,&xidLook->queue);
+    
+    XID_PRINT("LockReleaseAll:", xidLook);
+    
+    SpinAcquire(masterLock);
+    for (;;)
+       {
+           /* ---------------------------
+            * XXX Here we assume the shared memory queue is circular and
+            * that we know its internal structure.  Should have some sort of
+            * macros to allow one to walk it.  mer 20 July 1991
+            * ---------------------------
+            */
+           done = (xidLook->queue.next == end);
+           lock = (LOCK *) MAKE_PTR(xidLook->tag.lock);
+           
+           LOCK_PRINT("ReleaseAll",(&lock->tag),0);
+           
+           /* ------------------
+            * fix the general lock stats
+            * ------------------
+            */
+           if (lock->nHolding != xidLook->nHolding)
+               {
+                   lock->nHolding -= xidLook->nHolding;
+                   lock->nActive -= xidLook->nHolding;
+                   Assert(lock->nActive >= 0);
+                   for (i=1; i<=nLockTypes; i++)
+                       {
+                           lock->holders[i] -= xidLook->holders[i];
+                           lock->activeHolders[i] -= xidLook->holders[i];
+                           if (! lock->activeHolders[i])
+                               lock->mask &= BITS_OFF[i];
+                       }
+               }
+           else
+               {
+                   /* --------------
+                    * set nHolding to zero so that we can garbage collect the lock
+                    * down below...
+                    * --------------
+                    */
+                   lock->nHolding = 0;
+               }
+           /* ----------------
+            * always remove the xidLookup entry, we're done with it now
+            * ----------------
+            */
+           if ((! hash_search(ltable->xidHash, (Pointer)xidLook, HASH_REMOVE, &found))
+               || !found)
+               {
+                   SpinRelease(masterLock);
+                   elog(NOTICE,"LockReplace: xid table corrupted");
+                   return(FALSE);
+               }
+           
+           if (! lock->nHolding)
+               {
+                   /* --------------------
+                    * if there's no one waiting in the queue, we've just released
+                    * the last lock.
+                    * --------------------
+                    */
+                   
+                   Assert( ltable->lockHash->hash == tag_hash);
+                   lock = (LOCK *)
+                       hash_search(ltable->lockHash,(Pointer)&(lock->tag),HASH_REMOVE, &found);
+                   if ((! lock) || (!found))
+                       {
+                           SpinRelease(masterLock);
+                           elog(NOTICE,"LockReplace: cannot remove lock from HTAB");
+                           return(FALSE);
+                       }
+               }
+           else
+               {
+                   /* --------------------
+                    * Wake the first waiting process and grant him the lock if it
+                    * doesn't conflict.  The woken process must record the lock
+                    * him/herself.
+                    * --------------------
+                    */
+                   waitQueue = &(lock->waitProcs);
+                   (void) ProcLockWakeup(waitQueue, (char *) ltable, (char *) lock);
+               }
+           
+           if (done)
+               break;
+           SHMQueueFirst(&xidLook->queue,(Pointer*)&tmp,&tmp->queue);
+           xidLook = tmp;
+       }
+    SpinRelease(masterLock);
+    SHMQueueInit(lockQueue);
+    return TRUE;
+}
+
+int
+LockShmemSize()
+{
+    int size = 0;
+    int nLockBuckets, nLockSegs;
+    int nXidBuckets, nXidSegs;
+    
+    nLockBuckets = 1 << (int)my_log2((NLOCKENTS - 1) / DEF_FFACTOR + 1);
+    nLockSegs = 1 << (int)my_log2((nLockBuckets - 1) / DEF_SEGSIZE + 1);
+    
+    nXidBuckets = 1 << (int)my_log2((NLOCKS_PER_XACT-1) / DEF_FFACTOR + 1);
+    nXidSegs = 1 << (int)my_log2((nLockBuckets - 1) / DEF_SEGSIZE + 1);
+    
+    size += MAXALIGN(NBACKENDS * sizeof(PROC));        /* each MyProc */
+    size += MAXALIGN(NBACKENDS * sizeof(LOCKCTL));     /* each ltable->ctl */
+    size += MAXALIGN(sizeof(PROC_HDR));                /* ProcGlobal */
+    
+    size += MAXALIGN(my_log2(NLOCKENTS) * sizeof(void *));
+    size += MAXALIGN(sizeof(HHDR));
+    size += nLockSegs * MAXALIGN(DEF_SEGSIZE * sizeof(SEGMENT));
+    size += NLOCKENTS * /* XXX not multiple of BUCKET_ALLOC_INCR? */
+       (MAXALIGN(sizeof(BUCKET_INDEX)) +
+        MAXALIGN(sizeof(LOCK))); /* contains hash key */
+    
+    size += MAXALIGN(my_log2(NBACKENDS) * sizeof(void *));
+    size += MAXALIGN(sizeof(HHDR));
+    size += nXidSegs * MAXALIGN(DEF_SEGSIZE * sizeof(SEGMENT));
+    size += NBACKENDS * /* XXX not multiple of BUCKET_ALLOC_INCR? */
+       (MAXALIGN(sizeof(BUCKET_INDEX)) +
+        MAXALIGN(sizeof(XIDLookupEnt))); /* contains hash key */
+    
+    return size;
+}
+
+/* -----------------
+ * Boolean function to determine current locking status
+ * -----------------
+ */
+bool
+LockingDisabled()
+{
+    return LockingIsDisabled;
+}
diff --git a/src/backend/storage/lmgr/multi.c b/src/backend/storage/lmgr/multi.c
new file mode 100644 (file)
index 0000000..09bab87
--- /dev/null
@@ -0,0 +1,415 @@
+/*-------------------------------------------------------------------------
+ *
+ * multi.c--
+ *    multi level lock table manager
+ *
+ *    Standard multi-level lock manager as per the Gray paper
+ *    (at least, that is what it is supposed to be).  We implement
+ *    three levels -- RELN, PAGE, TUPLE.  Tuple is actually TID
+ *    a physical record pointer.  It isn't an object id.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES:
+ *   (1) The lock.c module assumes that the caller here is doing
+ *       two phase locking.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <string.h>
+#include "storage/lmgr.h"
+#include "storage/multilev.h"
+
+#include "utils/rel.h"
+#include "utils/elog.h"
+#include "miscadmin.h"         /* MyDatabaseId */
+
+
+/*
+ * INTENT indicates to higher level that a lower level lock has been
+ * set.  For example, a write lock on a tuple conflicts with a write 
+ * lock on a relation.  This conflict is detected as a WRITE_INTENT/
+ * WRITE conflict between the tuple's intent lock and the relation's
+ * write lock.
+ */
+static int MultiConflicts[] = {
+    (int)NULL, 
+    /* All reads and writes at any level conflict with a write lock */
+    (1 << WRITE_LOCK)|(1 << WRITE_INTENT)|(1 << READ_LOCK)|(1 << READ_INTENT),
+    /* read locks conflict with write locks at curr and lower levels */
+    (1 << WRITE_LOCK)| (1 << WRITE_INTENT),  
+    /* write intent locks */
+    (1 << READ_LOCK) | (1 << WRITE_LOCK),
+    /* read intent locks*/
+    (1 << WRITE_LOCK),
+    /* extend locks for archive storage manager conflict only w/extend locks */
+    (1 << EXTEND_LOCK)
+};
+
+/*
+ * write locks have higher priority than read locks and extend locks.  May
+ * want to treat INTENT locks differently.
+ */
+static int MultiPrios[] = {
+    (int)NULL,
+    2,
+    1,
+    2,
+    1,
+    1
+};
+
+/* 
+ * Lock table identifier for this lock table.  The multi-level
+ * lock table is ONE lock table, not three.
+ */
+LockTableId MultiTableId = (LockTableId)NULL;
+LockTableId ShortTermTableId = (LockTableId)NULL;
+
+/*
+ * Create the lock table described by MultiConflicts and Multiprio.
+ */
+LockTableId
+InitMultiLevelLockm()
+{
+    int tableId;
+    
+    /* -----------------------
+     * If we're already initialized just return the table id.
+     * -----------------------
+     */
+    if (MultiTableId)
+       return MultiTableId;
+    
+    tableId = LockTabInit("LockTable", MultiConflicts, MultiPrios, 5);
+    MultiTableId = tableId;
+    if (! (MultiTableId)) {
+       elog(WARN,"InitMultiLockm: couldnt initialize lock table");
+    }
+    /* -----------------------
+     * No short term lock table for now.  -Jeff 15 July 1991
+     * 
+     * ShortTermTableId = LockTabRename(tableId);
+     * if (! (ShortTermTableId)) {
+     *   elog(WARN,"InitMultiLockm: couldnt rename lock table");
+     * }
+     * -----------------------
+     */
+    return MultiTableId;
+}
+
+/*
+ * MultiLockReln -- lock a relation
+ *
+ * Returns: TRUE if the lock can be set, FALSE otherwise.
+ */
+bool
+MultiLockReln(LockInfo linfo, LOCKT lockt)
+{
+    LOCKTAG    tag;
+    
+    /* LOCKTAG has two bytes of padding, unfortunately.  The
+     * hash function will return miss if the padding bytes aren't
+     * zero'd.
+     */
+    memset(&tag,0,sizeof(tag));
+    tag.relId = linfo->lRelId.relId;
+    tag.dbId = linfo->lRelId.dbId;
+    return(MultiAcquire(MultiTableId, &tag, lockt, RELN_LEVEL));
+}
+
+/*
+ * MultiLockTuple -- Lock the TID associated with a tuple
+ *
+ * Returns: TRUE if lock is set, FALSE otherwise.
+ *
+ * Side Effects: causes intention level locks to be set
+ *     at the page and relation level.
+ */
+bool
+MultiLockTuple(LockInfo linfo, ItemPointer tidPtr, LOCKT lockt)
+{
+    LOCKTAG    tag;
+    
+    /* LOCKTAG has two bytes of padding, unfortunately.  The
+     * hash function will return miss if the padding bytes aren't
+     * zero'd.
+     */
+    memset(&tag,0,sizeof(tag));
+    
+    tag.relId = linfo->lRelId.relId;
+    tag.dbId = linfo->lRelId.dbId;
+    
+    /* not locking any valid Tuple, just the page */
+    tag.tupleId = *tidPtr;
+    return(MultiAcquire(MultiTableId, &tag, lockt, TUPLE_LEVEL));
+}
+
+/*
+ * same as above at page level
+ */
+bool
+MultiLockPage(LockInfo linfo, ItemPointer tidPtr, LOCKT lockt)
+{
+    LOCKTAG    tag;
+    
+    /* LOCKTAG has two bytes of padding, unfortunately.  The
+     * hash function will return miss if the padding bytes aren't
+     * zero'd.
+     */
+    memset(&tag,0,sizeof(tag));
+    
+    
+    /* ----------------------------
+     * Now we want to set the page offset to be invalid 
+     * and lock the block.  There is some confusion here as to what
+     * a page is.  In Postgres a page is an 8k block, however this
+     * block may be partitioned into many subpages which are sometimes
+     * also called pages.  The term is overloaded, so don't be fooled
+     * when we say lock the page we mean the 8k block. -Jeff 16 July 1991
+     * ----------------------------
+     */
+    tag.relId = linfo->lRelId.relId;
+    tag.dbId = linfo->lRelId.dbId;
+    BlockIdCopy(&(tag.tupleId.ip_blkid), &(tidPtr->ip_blkid));
+    return(MultiAcquire(MultiTableId, &tag, lockt, PAGE_LEVEL));
+}
+
+/*
+ * MultiAcquire -- acquire multi level lock at requested level
+ *
+ * Returns: TRUE if lock is set, FALSE if not
+ * Side Effects:
+ */
+bool
+MultiAcquire(LockTableId tableId,
+            LOCKTAG *tag,
+            LOCKT lockt,
+            LOCK_LEVEL level)
+{
+    LOCKT locks[N_LEVELS];
+    int        i,status;
+    LOCKTAG    xxTag, *tmpTag = &xxTag;
+    int        retStatus = TRUE;
+    
+    /*
+     * Three levels implemented.  If we set a low level (e.g. Tuple)
+     * lock, we must set INTENT locks on the higher levels.  The 
+     * intent lock detects conflicts between the low level lock
+     * and an existing high level lock.  For example, setting a
+     * write lock on a tuple in a relation is disallowed if there
+     * is an existing read lock on the entire relation.  The
+     * write lock would set a WRITE + INTENT lock on the relation
+     * and that lock would conflict with the read.
+     */
+    switch (level) {
+    case RELN_LEVEL:
+       locks[0] = lockt;
+       locks[1] = NO_LOCK;
+       locks[2] = NO_LOCK;
+       break;
+    case PAGE_LEVEL:
+       locks[0] = lockt + INTENT;
+       locks[1] = lockt;
+       locks[2] = NO_LOCK;
+       break;
+    case TUPLE_LEVEL:
+       locks[0] = lockt + INTENT;
+       locks[1] = lockt + INTENT;
+       locks[2] = lockt;
+       break;
+    default:
+       elog(WARN,"MultiAcquire: bad lock level");
+       return(FALSE);
+    }
+    
+    /*
+     * construct a new tag as we go. Always loop through all levels,
+     * but if we arent' seting a low level lock, locks[i] is set to
+     * NO_LOCK for the lower levels.  Always start from the highest
+     * level and go to the lowest level. 
+     */
+    memset(tmpTag,0,sizeof(*tmpTag));
+    tmpTag->relId = tag->relId;
+    tmpTag->dbId = tag->dbId;
+    
+    for (i=0;i<N_LEVELS;i++) {
+       if (locks[i] != NO_LOCK) {
+           switch (i) {
+           case RELN_LEVEL:
+               /* -------------
+                * Set the block # and offset to invalid
+                * -------------
+                */
+               BlockIdSet(&(tmpTag->tupleId.ip_blkid), InvalidBlockNumber);
+               tmpTag->tupleId.ip_posid = InvalidOffsetNumber;
+               break;
+           case PAGE_LEVEL:
+               /* -------------
+                * Copy the block #, set the offset to invalid
+                * -------------
+                */
+               BlockIdCopy(&(tmpTag->tupleId.ip_blkid),
+                           &(tag->tupleId.ip_blkid));
+               tmpTag->tupleId.ip_posid = InvalidOffsetNumber;
+               break;
+           case TUPLE_LEVEL:
+               /* --------------
+                * Copy the entire tuple id.
+                * --------------
+                */
+               ItemPointerCopy(&tmpTag->tupleId, &tag->tupleId);
+               break;
+           }
+           
+           status = LockAcquire(tableId, tmpTag, locks[i]);
+           if (! status) {
+               /* failed for some reason. Before returning we have
+                * to release all of the locks we just acquired.
+                * MultiRelease(xx,xx,xx, i) means release starting from
+                * the last level lock we successfully acquired
+                */
+               retStatus = FALSE;
+               (void) MultiRelease(tableId, tag, lockt, i);
+               /* now leave the loop.  Don't try for any more locks */
+               break;
+           }
+       }
+    }
+    return(retStatus);
+}
+
+/* ------------------
+ * Release a page in the multi-level lock table
+ * ------------------
+ */
+bool
+MultiReleasePage(LockInfo linfo, ItemPointer tidPtr, LOCKT     lockt)
+{
+    LOCKTAG tag;
+    
+    /* ------------------
+     * LOCKTAG has two bytes of padding, unfortunately.  The
+     * hash function will return miss if the padding bytes aren't
+     * zero'd.
+     * ------------------
+     */
+    memset(&tag, 0,sizeof(LOCKTAG));
+    
+    tag.relId = linfo->lRelId.relId;
+    tag.dbId = linfo->lRelId.dbId;
+    BlockIdCopy(&(tag.tupleId.ip_blkid), &(tidPtr->ip_blkid));
+    
+    return (MultiRelease(MultiTableId, &tag, lockt, PAGE_LEVEL));
+}
+
+/* ------------------
+ * Release a relation in the multi-level lock table
+ * ------------------
+ */
+bool
+MultiReleaseReln(LockInfo linfo, LOCKT lockt)          
+{
+    LOCKTAG tag;
+    
+    /* ------------------
+     * LOCKTAG has two bytes of padding, unfortunately.  The
+     * hash function will return miss if the padding bytes aren't
+     * zero'd.
+     * ------------------
+     */
+    memset(&tag, 0, sizeof(LOCKTAG));
+    tag.relId = linfo->lRelId.relId;
+    tag.dbId = linfo->lRelId.dbId;
+    
+    return (MultiRelease(MultiTableId, &tag, lockt, RELN_LEVEL));
+}
+
+/*
+ * MultiRelease -- release a multi-level lock
+ *
+ * Returns: TRUE if successful, FALSE otherwise.
+ */
+bool
+MultiRelease(LockTableId tableId,
+            LOCKTAG *tag,
+            LOCKT      lockt,
+            LOCK_LEVEL level)
+{
+    LOCKT      locks[N_LEVELS];
+    int                i,status;
+    LOCKTAG    xxTag, *tmpTag = &xxTag;
+    
+    /* 
+     * same level scheme as MultiAcquire().
+     */
+    switch (level) {
+    case RELN_LEVEL:
+       locks[0] = lockt;
+       locks[1] = NO_LOCK;
+       locks[2] = NO_LOCK;
+       break;
+    case PAGE_LEVEL:
+       locks[0] = lockt + INTENT;
+       locks[1] = lockt;
+       locks[2] = NO_LOCK;
+       break;
+    case TUPLE_LEVEL:
+       locks[0] = lockt + INTENT;
+       locks[1] = lockt + INTENT;
+       locks[2] = lockt;
+       break;
+    default:
+       elog(WARN,"MultiRelease: bad lockt");
+    }
+    
+    /*
+     * again, construct the tag on the fly.  This time, however,
+     * we release the locks in the REVERSE order -- from lowest
+     * level to highest level.  
+     *
+     * Must zero out the tag to set padding byes to zero and ensure
+     * hashing consistency.
+     */
+    memset(tmpTag, 0, sizeof(*tmpTag));
+    tmpTag->relId = tag->relId;
+    tmpTag->dbId =  tag->dbId;
+    
+    for (i=(N_LEVELS-1); i>=0; i--) {
+       if (locks[i] != NO_LOCK) {
+           switch (i) {
+           case RELN_LEVEL:
+               /* -------------
+                * Set the block # and offset to invalid
+                * -------------
+                */
+               BlockIdSet(&(tmpTag->tupleId.ip_blkid), InvalidBlockNumber);
+               tmpTag->tupleId.ip_posid = InvalidOffsetNumber;
+               break;
+           case PAGE_LEVEL:
+               /* -------------
+                * Copy the block #, set the offset to invalid
+                * -------------
+                */
+               BlockIdCopy(&(tmpTag->tupleId.ip_blkid),
+                           &(tag->tupleId.ip_blkid));
+               tmpTag->tupleId.ip_posid = InvalidOffsetNumber;
+               break;
+           case TUPLE_LEVEL:
+               ItemPointerCopy(&tmpTag->tupleId, &tag->tupleId);
+               break;
+           }
+           status = LockRelease(tableId, tmpTag, locks[i]);
+           if (! status) {
+               elog(WARN,"MultiRelease: couldn't release after error");
+           }
+       }
+    }
+    /* shouldn't reach here */
+    return false;
+}
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
new file mode 100644 (file)
index 0000000..8bfe1b9
--- /dev/null
@@ -0,0 +1,826 @@
+/*-------------------------------------------------------------------------
+ *
+ * proc.c--
+ *    routines to manage per-process shared memory data structure
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ *  Each postgres backend gets one of these.  We'll use it to
+ *  clean up after the process should the process suddenly die.
+ *
+ *
+ * Interface (a):
+ *     ProcSleep(), ProcWakeup(), ProcWakeupNext(),
+ *     ProcQueueAlloc() -- create a shm queue for sleeping processes
+ *     ProcQueueInit() -- create a queue without allocing memory
+ *
+ * Locking and waiting for buffers can cause the backend to be
+ * put to sleep.  Whoever releases the lock, etc. wakes the
+ * process up again (and gives it an error code so it knows
+ * whether it was awoken on an error condition).
+ *
+ * Interface (b):
+ *
+ * ProcReleaseLocks -- frees the locks associated with this process,
+ * ProcKill -- destroys the shared memory state (and locks)
+ *     associated with the process.
+ *
+ * 5/15/91 -- removed the buffer pool based lock chain in favor
+ *     of a shared memory lock chain.  The write-protection is
+ *     more expensive if the lock chain is in the buffer pool.
+ *     The only reason I kept the lock chain in the buffer pool
+ *     in the first place was to allow the lock table to grow larger
+ *     than available shared memory and that isn't going to work
+ *     without a lot of unimplemented support anyway.
+ *
+ * 4/7/95 -- instead of allocating a set of 1 semaphore per process, we
+ *      allocate a semaphore from a set of PROC_NSEMS_PER_SET semaphores
+ *      shared among backends (we keep a few sets of semaphores around).
+ *      This is so that we can support more backends. (system-wide semaphore
+ *      sets run out pretty fast.)                -ay 4/95
+ *
+ * $Header$
+ */
+#include <sys/time.h>
+#ifndef WIN32
+#include <unistd.h>
+#endif /* WIN32 */
+#include <string.h>
+#include <sys/types.h>
+#include "libpq/pqsignal.h"    /* substitute for <signal.h> */
+
+#if defined(PORTNAME_bsdi)
+/* hacka, hacka, hacka (XXX) */
+union semun {
+       int val; /* value for SETVAL */
+       struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */
+       ushort *array; /* array for GETALL & SETALL */
+};
+#endif
+
+#include "access/xact.h"
+#include "utils/hsearch.h"
+#include "utils/elog.h"
+
+#include "storage/buf.h"       
+#include "storage/lock.h"
+#include "storage/shmem.h"
+#include "storage/spin.h"
+#include "storage/proc.h"
+
+/*
+ * timeout (in seconds) for resolving possible deadlock
+ */
+#ifndef DEADLOCK_TIMEOUT
+#define DEADLOCK_TIMEOUT       60
+#endif
+
+/* --------------------
+ * Spin lock for manipulating the shared process data structure:
+ * ProcGlobal.... Adding an extra spin lock seemed like the smallest
+ * hack to get around reading and updating this structure in shared
+ * memory. -mer 17 July 1991
+ * --------------------
+ */
+SPINLOCK ProcStructLock;
+
+/*
+ * For cleanup routines.  Don't cleanup if the initialization
+ * has not happened.
+ */
+static bool    ProcInitialized = FALSE;
+
+static PROC_HDR *ProcGlobal = NULL;
+
+PROC   *MyProc = NULL;
+
+static void ProcKill(int exitStatus, int pid);
+static void ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum);
+static void ProcFreeSem(IpcSemaphoreKey semKey, int semNum);
+#if defined(PORTNAME_linux)
+extern int HandleDeadLock(int);
+#else
+extern int HandleDeadLock(void);
+#endif
+/*
+ * InitProcGlobal -
+ *    initializes the global process table. We put it here so that
+ *    the postmaster can do this initialization. (ProcFreeAllSem needs
+ *    to read this table on exiting the postmaster. If we have the first
+ *    backend do this, starting up and killing the postmaster without
+ *    starting any backends will be a problem.)
+ */
+void
+InitProcGlobal(IPCKey key)
+{
+    bool found = false;
+
+    /* attach to the free list */
+    ProcGlobal = (PROC_HDR *)
+       ShmemInitStruct("Proc Header",(unsigned)sizeof(PROC_HDR),&found);
+
+    /* --------------------
+     * We're the first - initialize.
+     * --------------------
+     */
+    if (! found)
+       {
+           int i;
+
+           ProcGlobal->numProcs = 0;
+           ProcGlobal->freeProcs = INVALID_OFFSET;
+           ProcGlobal->currKey = IPCGetProcessSemaphoreInitKey(key);
+           for (i=0; i < MAX_PROC_SEMS/PROC_NSEMS_PER_SET; i++)
+               ProcGlobal->freeSemMap[i] = 0;
+       }
+}
+
+/* ------------------------
+ * InitProc -- create a per-process data structure for this process
+ * used by the lock manager on semaphore queues.
+ * ------------------------
+ */
+void
+InitProcess(IPCKey key)
+{
+    bool found = false;
+    int pid;
+    int semstat;
+    unsigned long location, myOffset;
+    
+    /* ------------------
+     * Routine called if deadlock timer goes off. See ProcSleep()
+     * ------------------
+     */
+#ifndef WIN32
+    signal(SIGALRM, HandleDeadLock);
+#endif /* WIN32 we'll have to figure out how to handle this later */
+
+    SpinAcquire(ProcStructLock);
+    
+    /* attach to the free list */
+    ProcGlobal = (PROC_HDR *)
+       ShmemInitStruct("Proc Header",(unsigned)sizeof(PROC_HDR),&found);
+    if (!found) {
+       /* this should not happen. InitProcGlobal() is called before this. */
+       elog(WARN, "InitProcess: Proc Header uninitialized");
+    }
+    
+    if (MyProc != NULL)
+       {
+           SpinRelease(ProcStructLock);
+           elog(WARN,"ProcInit: you already exist");
+           return;
+       }
+    
+    /* try to get a proc from the free list first */
+    
+    myOffset = ProcGlobal->freeProcs;
+    
+    if (myOffset != INVALID_OFFSET)
+       {
+           MyProc = (PROC *) MAKE_PTR(myOffset);
+           ProcGlobal->freeProcs = MyProc->links.next;
+       }
+    else
+       {
+           /* have to allocate one.  We can't use the normal binding
+            * table mechanism because the proc structure is stored
+            * by PID instead of by a global name (need to look it
+            * up by PID when we cleanup dead processes).
+            */
+           
+           MyProc = (PROC *) ShmemAlloc((unsigned)sizeof(PROC));
+           if (! MyProc)
+               {
+                   SpinRelease(ProcStructLock);
+                   elog (FATAL,"cannot create new proc: out of memory");
+               }
+           
+           /* this cannot be initialized until after the buffer pool */
+           SHMQueueInit(&(MyProc->lockQueue));
+           MyProc->procId = ProcGlobal->numProcs;
+           ProcGlobal->numProcs++;
+       }
+    
+    /*
+     * zero out the spin lock counts and set the sLocks field for
+     * ProcStructLock to 1 as we have acquired this spinlock above but 
+     * didn't record it since we didn't have MyProc until now.
+     */
+    memset(MyProc->sLocks, 0, sizeof(MyProc->sLocks));
+    MyProc->sLocks[ProcStructLock] = 1;
+
+
+    if (IsUnderPostmaster) {
+       IPCKey semKey;
+       int semNum;
+       int semId;
+       union semun semun;
+
+       ProcGetNewSemKeyAndNum(&semKey, &semNum);
+       
+       semId = IpcSemaphoreCreate(semKey,
+                                  PROC_NSEMS_PER_SET,
+                                  IPCProtection,
+                                  IpcSemaphoreDefaultStartValue,
+                                  0,
+                                  &semstat);
+       /*
+        * we might be reusing a semaphore that belongs to a dead
+        * backend. So be careful and reinitialize its value here.
+        */
+       semun.val = IpcSemaphoreDefaultStartValue;
+       semctl(semId, semNum, SETVAL, semun);
+
+       IpcSemaphoreLock(semId, semNum, IpcExclusiveLock);
+       MyProc->sem.semId = semId;
+       MyProc->sem.semNum = semNum;
+       MyProc->sem.semKey = semKey;
+    } else {
+       MyProc->sem.semId = -1;
+    }
+    
+    /* ----------------------
+     * Release the lock.
+     * ----------------------
+     */
+    SpinRelease(ProcStructLock);
+    
+    MyProc->pid = 0;
+#if 0
+    MyProc->pid = MyPid;
+#endif
+    
+    /* ----------------
+     * Start keeping spin lock stats from here on.  Any botch before
+     * this initialization is forever botched
+     * ----------------
+     */
+    memset(MyProc->sLocks, 0, MAX_SPINS*sizeof(*MyProc->sLocks));
+    
+    /* -------------------------
+     * Install ourselves in the binding table.  The name to
+     * use is determined by the OS-assigned process id.  That
+     * allows the cleanup process to find us after any untimely
+     * exit.
+     * -------------------------
+     */
+    pid = getpid();
+    location = MAKE_OFFSET(MyProc);
+    if ((! ShmemPIDLookup(pid,&location)) || (location != MAKE_OFFSET(MyProc)))
+       {
+           elog(FATAL,"InitProc: ShmemPID table broken");
+       }
+    
+    MyProc->errType = NO_ERROR;
+    SHMQueueElemInit(&(MyProc->links));
+    
+    on_exitpg(ProcKill, (caddr_t)pid);
+    
+    ProcInitialized = TRUE;
+}
+
+/*
+ * ProcReleaseLocks() -- release all locks associated with this process
+ *
+ */
+void
+ProcReleaseLocks()
+{
+    if (!MyProc)
+       return;
+    LockReleaseAll(1,&MyProc->lockQueue);
+}
+
+/*
+ * ProcRemove -
+ *    used by the postmaster to clean up the global tables. This also frees
+ *    up the semaphore used for the lmgr of the process. (We have to do
+ *    this is the postmaster instead of doing a IpcSemaphoreKill on exiting
+ *    the process because the semaphore set is shared among backends and
+ *    we don't want to remove other's semaphores on exit.)
+ */
+bool
+ProcRemove(int pid)
+{
+    SHMEM_OFFSET  location;
+    PROC *proc;
+    
+    location = INVALID_OFFSET;
+    
+    location = ShmemPIDDestroy(pid);
+    if (location == INVALID_OFFSET)
+       return(FALSE);
+    proc = (PROC *) MAKE_PTR(location);
+
+    SpinAcquire(ProcStructLock);
+    
+    ProcFreeSem(proc->sem.semKey, proc->sem.semNum);
+
+    proc->links.next =  ProcGlobal->freeProcs;
+    ProcGlobal->freeProcs = MAKE_OFFSET(proc);
+    
+    SpinRelease(ProcStructLock);
+
+    return(TRUE);
+}
+
+/*
+ * ProcKill() -- Destroy the per-proc data structure for
+ *     this process. Release any of its held spin locks.
+ */
+static void
+ProcKill(int exitStatus, int pid)
+{
+    PROC               *proc;
+    SHMEM_OFFSET       location;
+    
+    /* -------------------- 
+     * If this is a FATAL exit the postmaster will have to kill all the
+     * existing backends and reinitialize shared memory.  So all we don't 
+     * need to do anything here.
+     * --------------------
+     */
+    if (exitStatus != 0)
+       return;
+    
+    if (! pid)
+       {
+           pid = getpid();
+       }
+    
+    ShmemPIDLookup(pid,&location);
+    if (location == INVALID_OFFSET)
+       return;
+    
+    proc = (PROC *) MAKE_PTR(location);
+    
+    if (proc != MyProc) {
+       Assert( pid != getpid() );
+    } else
+       MyProc = NULL;
+    
+    /* ---------------
+     * Assume one lock table.
+     * ---------------
+     */
+    ProcReleaseSpins(proc);
+    LockReleaseAll(1,&proc->lockQueue);
+    
+    /* ----------------
+     * get off the wait queue
+     * ----------------
+     */
+    LockLockTable();
+    if (proc->links.next != INVALID_OFFSET) {
+       Assert(proc->waitLock->waitProcs.size > 0);
+       SHMQueueDelete(&(proc->links));
+       --proc->waitLock->waitProcs.size;
+    }
+    SHMQueueElemInit(&(proc->links));
+    UnlockLockTable();
+    
+    return;
+}
+
+/*
+ * ProcQueue package: routines for putting processes to sleep
+ *     and  waking them up
+ */
+
+/*
+ * ProcQueueAlloc -- alloc/attach to a shared memory process queue
+ *
+ * Returns: a pointer to the queue or NULL
+ * Side Effects: Initializes the queue if we allocated one
+ */
+PROC_QUEUE *
+ProcQueueAlloc(char *name)
+{
+    bool       found;
+    PROC_QUEUE *queue = (PROC_QUEUE *)
+       ShmemInitStruct(name,(unsigned)sizeof(PROC_QUEUE),&found);
+    
+    if (! queue)
+       {
+           return(NULL);
+       }
+    if (! found)
+       {
+           ProcQueueInit(queue);
+       }
+    return(queue);
+}
+
+/*
+ * ProcQueueInit -- initialize a shared memory process queue
+ */
+void
+ProcQueueInit(PROC_QUEUE *queue)
+{
+    SHMQueueInit(&(queue->links));
+    queue->size = 0;
+}
+
+
+
+/*
+ * ProcSleep -- put a process to sleep
+ *
+ * P() on the semaphore should put us to sleep.  The process
+ * semaphore is cleared by default, so the first time we try
+ * to acquire it, we sleep.
+ *
+ * ASSUME: that no one will fiddle with the queue until after
+ *     we release the spin lock.
+ *
+ * NOTES: The process queue is now a priority queue for locking.
+ */
+int
+ProcSleep(PROC_QUEUE *queue,
+         SPINLOCK spinlock,
+         int token,
+         int prio,
+         LOCK *lock)
+{
+    int        i;
+    PROC       *proc;
+#ifndef WIN32 /* figure this out later */
+    struct itimerval timeval, dummy;
+#endif /* WIN32 */
+    
+    proc = (PROC *) MAKE_PTR(queue->links.prev);
+    for (i=0;i<queue->size;i++)
+       {
+           if (proc->prio < prio)
+               proc = (PROC *) MAKE_PTR(proc->links.prev);
+           else
+               break;
+       }
+    
+    MyProc->token = token;
+    MyProc->waitLock = lock;
+    
+    /* -------------------
+     * currently, we only need this for the ProcWakeup routines
+     * -------------------
+     */
+    TransactionIdStore((TransactionId) GetCurrentTransactionId(), &MyProc->xid);
+    
+    /* -------------------
+     * assume that these two operations are atomic (because
+     * of the spinlock).
+     * -------------------
+     */
+    SHMQueueInsertTL(&(proc->links),&(MyProc->links));
+    queue->size++;
+    
+    SpinRelease(spinlock);
+    
+    /* --------------
+     * Postgres does not have any deadlock detection code and for this 
+     * reason we must set a timer to wake up the process in the event of
+     * a deadlock.  For now the timer is set for 1 minute and we assume that
+     * any process which sleeps for this amount of time is deadlocked and will 
+     * receive a SIGALRM signal.  The handler should release the processes
+     * semaphore and abort the current transaction.
+     *
+     * Need to zero out struct to set the interval and the micro seconds fields
+     * to 0.
+     * --------------
+     */
+#ifndef WIN32
+    memset(&timeval, 0, sizeof(struct itimerval));
+    timeval.it_value.tv_sec = DEADLOCK_TIMEOUT;
+    
+    if (setitimer(ITIMER_REAL, &timeval, &dummy))
+       elog(FATAL, "ProcSleep: Unable to set timer for process wakeup");
+#endif /* WIN32 */
+    
+    /* --------------
+     * if someone wakes us between SpinRelease and IpcSemaphoreLock,
+     * IpcSemaphoreLock will not block.  The wakeup is "saved" by
+     * the semaphore implementation.
+     * --------------
+     */
+    IpcSemaphoreLock(MyProc->sem.semId, MyProc->sem.semNum, IpcExclusiveLock);
+    
+    /* ---------------
+     * We were awoken before a timeout - now disable the timer
+     * ---------------
+     */
+#ifndef WIN32
+    timeval.it_value.tv_sec = 0;
+    
+    
+    if (setitimer(ITIMER_REAL, &timeval, &dummy))
+       elog(FATAL, "ProcSleep: Unable to diable timer for process wakeup");
+#endif /* WIN32 */
+    
+    /* ----------------
+     * We were assumed to be in a critical section when we went
+     * to sleep.
+     * ----------------
+     */
+    SpinAcquire(spinlock);
+    
+    return(MyProc->errType);
+}
+
+
+/*
+ * ProcWakeup -- wake up a process by releasing its private semaphore.
+ *
+ *   remove the process from the wait queue and set its links invalid.
+ *   RETURN: the next process in the wait queue.
+ */
+PROC *
+ProcWakeup(PROC *proc, int errType)
+{
+    PROC *retProc;
+    /* assume that spinlock has been acquired */
+    
+    if (proc->links.prev == INVALID_OFFSET ||
+       proc->links.next == INVALID_OFFSET)
+       return((PROC *) NULL);
+    
+    retProc = (PROC *) MAKE_PTR(proc->links.prev);
+    
+    /* you have to update waitLock->waitProcs.size yourself */
+    SHMQueueDelete(&(proc->links));
+    SHMQueueElemInit(&(proc->links));
+    
+    proc->errType = errType;
+    
+    IpcSemaphoreUnlock(proc->sem.semId, proc->sem.semNum, IpcExclusiveLock);
+    
+    return retProc;
+}
+
+
+/*
+ * ProcGetId --
+ */
+int
+ProcGetId()
+{
+    return( MyProc->procId );
+}
+
+/*
+ * ProcLockWakeup -- routine for waking up processes when a lock is
+ *     released.
+ */
+int
+ProcLockWakeup(PROC_QUEUE *queue, char *ltable, char *lock)
+{
+    PROC       *proc;
+    int        count;
+    
+    if (! queue->size)
+       return(STATUS_NOT_FOUND);
+    
+    proc = (PROC *) MAKE_PTR(queue->links.prev);
+    count = 0;
+    while ((LockResolveConflicts ((LOCKTAB *) ltable,
+                                 (LOCK *) lock,
+                                 proc->token,
+                                 proc->xid) == STATUS_OK))
+       {
+           /* there was a waiting process, grant it the lock before waking it
+            * up.  This will prevent another process from seizing the lock
+            * between the time we release the lock master (spinlock) and
+            * the time that the awoken process begins executing again.
+            */
+           GrantLock((LOCK *) lock, proc->token);
+           queue->size--;
+           
+           /*
+            * ProcWakeup removes proc from the lock waiting process queue and
+            * returns the next proc in chain.  If a writer just dropped
+            * its lock and there are several waiting readers, wake them all up.
+            */
+           proc = ProcWakeup(proc, NO_ERROR);
+           
+           count++;
+           if (!proc || queue->size == 0)
+               break;
+       }
+    
+    if (count)
+       return(STATUS_OK);
+    else
+       /* Something is still blocking us.  May have deadlocked. */
+       return(STATUS_NOT_FOUND);
+}
+
+void
+ProcAddLock(SHM_QUEUE *elem)
+{
+    SHMQueueInsertTL(&MyProc->lockQueue,elem);
+}
+
+/* --------------------
+ * We only get to this routine if we got SIGALRM after DEADLOCK_TIMEOUT
+ * while waiting for a lock to be released by some other process.  After
+ * the one minute deadline we assume we have a deadlock and must abort
+ * this transaction.  We must also indicate that I'm no longer waiting
+ * on a lock so that other processes don't try to wake me up and screw 
+ * up my semaphore.
+ * --------------------
+ */
+int
+#if defined(PORTNAME_linux)
+HandleDeadLock(int i)
+#else
+HandleDeadLock()
+#endif
+{
+    LOCK *lock;
+    int size;
+    
+    LockLockTable();
+    
+    /* ---------------------
+     * Check to see if we've been awoken by anyone in the interim.
+     *
+     * If we have we can return and resume our transaction -- happy day.
+     * Before we are awoken the process releasing the lock grants it to
+     * us so we know that we don't have to wait anymore.
+     * 
+     * Damn these names are LONG! -mer
+     * ---------------------
+     */
+    if (IpcSemaphoreGetCount(MyProc->sem.semId, MyProc->sem.semNum) == 
+       IpcSemaphoreDefaultStartValue) {
+       UnlockLockTable();
+       return 1;
+    }
+    
+    /*
+     * you would think this would be unnecessary, but...
+     *
+     * this also means we've been removed already.  in some ports
+     * (e.g., sparc and aix) the semop(2) implementation is such that
+     * we can actually end up in this handler after someone has removed
+     * us from the queue and bopped the semaphore *but the test above
+     * fails to detect the semaphore update* (presumably something weird
+     * having to do with the order in which the semaphore wakeup signal
+     * and SIGALRM get handled).
+     */
+    if (MyProc->links.prev == INVALID_OFFSET ||
+       MyProc->links.next == INVALID_OFFSET) {
+       UnlockLockTable();
+       return(1);
+    }
+    
+    lock = MyProc->waitLock;
+    size = lock->waitProcs.size; /* so we can look at this in the core */
+    
+    /* ------------------------
+     * Get this process off the lock's wait queue
+     * ------------------------
+     */
+    Assert(lock->waitProcs.size > 0);
+    --lock->waitProcs.size;
+    SHMQueueDelete(&(MyProc->links));
+    SHMQueueElemInit(&(MyProc->links));
+    
+    /* ------------------
+     * Unlock my semaphore so that the count is right for next time.
+     * I was awoken by a signal, not by someone unlocking my semaphore.
+     * ------------------
+     */
+    IpcSemaphoreUnlock(MyProc->sem.semId, MyProc->sem.semNum, IpcExclusiveLock);
+    
+    /* -------------
+     * Set MyProc->errType to STATUS_ERROR so that we abort after
+     * returning from this handler.
+     * -------------
+     */
+    MyProc->errType = STATUS_ERROR;
+    
+    /*
+     * if this doesn't follow the IpcSemaphoreUnlock then we get lock
+     * table corruption ("LockReplace: xid table corrupted") due to
+     * race conditions.  i don't claim to understand this...
+     */
+    UnlockLockTable();
+    
+    elog(NOTICE, "Timeout -- possible deadlock");
+    return 0;
+}
+
+void
+ProcReleaseSpins(PROC *proc)
+{
+    int i;
+    
+    if (!proc)
+       proc = MyProc;
+    
+    if (!proc)
+       return;
+    for (i=0; i < (int)MAX_SPINS; i++)
+       {
+           if (proc->sLocks[i])
+               {
+                   Assert(proc->sLocks[i] == 1);
+                   SpinRelease(i);
+               }
+       }
+}
+
+/*****************************************************************************
+ * 
+ *****************************************************************************/
+
+/*
+ * ProcGetNewSemKeyAndNum -
+ *    scan the free semaphore bitmap and allocate a single semaphore from
+ *    a semaphore set. (If the semaphore set doesn't exist yet,
+ *    IpcSemaphoreCreate will create it. Otherwise, we use the existing
+ *    semaphore set.)
+ */
+static void
+ProcGetNewSemKeyAndNum(IPCKey *key, int *semNum)
+{
+    int i;
+    int32 *freeSemMap = ProcGlobal->freeSemMap;
+    unsigned int fullmask;
+
+    /*
+     * we hold ProcStructLock when entering this routine. We scan through
+     * the bitmap to look for a free semaphore.
+     */
+    fullmask = ~0 >> (32 - PROC_NSEMS_PER_SET);
+    for(i=0; i < MAX_PROC_SEMS/PROC_NSEMS_PER_SET; i++) {
+       int mask = 1;
+       int j;
+
+       if (freeSemMap[i] == fullmask)
+           continue; /* none free for this set */
+
+       for(j = 0; j < PROC_NSEMS_PER_SET; j++) {
+           if ((freeSemMap[i] & mask) == 0) {
+               /*
+                * a free semaphore found. Mark it as allocated.
+                */
+               freeSemMap[i] |= mask;
+
+               *key = ProcGlobal->currKey + i;
+               *semNum = j;
+               return;
+           }
+           mask <<= 1;
+       }
+    }
+
+    /* if we reach here, all the semaphores are in use. */
+    elog(WARN, "InitProc: cannot allocate a free semaphore");
+}
+
+/*
+ * ProcFreeSem -
+ *    free up our semaphore in the semaphore set. If we're the last one
+ *    in the set, also remove the semaphore set.
+ */
+static void
+ProcFreeSem(IpcSemaphoreKey semKey, int semNum)
+{
+    int mask;
+    int i;
+    int32 *freeSemMap = ProcGlobal->freeSemMap;
+
+    i = semKey - ProcGlobal->currKey;
+    mask = ~(1 << semNum);
+    freeSemMap[i] &= mask;
+
+    if (freeSemMap[i]==0)
+       IpcSemaphoreKill(semKey);
+}
+
+/*
+ * ProcFreeAllSemaphores -
+ *    on exiting the postmaster, we free up all the semaphores allocated
+ *    to the lmgrs of the backends.
+ */
+void
+ProcFreeAllSemaphores()
+{
+    int i;
+    int32 *freeSemMap = ProcGlobal->freeSemMap;
+
+    for(i=0; i < MAX_PROC_SEMS/PROC_NSEMS_PER_SET; i++) {
+       if (freeSemMap[i]!=0)
+           IpcSemaphoreKill(ProcGlobal->currKey + i);
+    }
+}
diff --git a/src/backend/storage/lmgr/single.c b/src/backend/storage/lmgr/single.c
new file mode 100644 (file)
index 0000000..56a6132
--- /dev/null
@@ -0,0 +1,86 @@
+/*-------------------------------------------------------------------------
+ *
+ * single.c--
+ *    set single locks in the multi-level lock hierarchy
+ *
+ *    Sometimes we don't want to set all levels of the multi-level
+ *     lock hierarchy at once.  This allows us to set and release
+ *     one level at a time.  It's useful in index scans when
+ *     you can set an intent lock at the beginning and thereafter
+ *     only set page locks.  Tends to speed things up.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "storage/lmgr.h"      /* where the declarations go */
+#include "storage/lock.h"
+#include "storage/multilev.h"
+#include "utils/rel.h"
+
+/*
+ * SingleLockReln -- lock a relation
+ *
+ * Returns: TRUE if the lock can be set, FALSE otherwise.
+ */
+bool
+SingleLockReln(LockInfo linfo, LOCKT lockt, int action)
+{
+    LOCKTAG    tag;
+    
+    /* 
+     * LOCKTAG has two bytes of padding, unfortunately.  The
+     * hash function will return miss if the padding bytes aren't
+     * zero'd.
+     */
+    memset(&tag,0,sizeof(tag));
+    tag.relId = linfo->lRelId.relId;
+    tag.dbId = linfo->lRelId.dbId;
+    BlockIdSet(&(tag.tupleId.ip_blkid), InvalidBlockNumber);
+    tag.tupleId.ip_posid = InvalidOffsetNumber;
+    
+    if (action == UNLOCK)
+       return(LockRelease(MultiTableId, &tag, lockt));
+    else
+       return(LockAcquire(MultiTableId, &tag, lockt));
+}
+
+/*
+ * SingleLockPage -- use multi-level lock table, but lock
+ *     only at the page level.
+ *
+ * Assumes that an INTENT lock has already been set in the
+ * multi-level lock table.
+ *
+ */
+bool
+SingleLockPage(LockInfo linfo,
+              ItemPointer tidPtr,
+              LOCKT lockt,
+              int action)
+{
+    LOCKTAG    tag;
+    
+    /* 
+     * LOCKTAG has two bytes of padding, unfortunately.  The
+     * hash function will return miss if the padding bytes aren't
+     * zero'd.
+     */
+    memset(&tag,0,sizeof(tag));
+    tag.relId = linfo->lRelId.relId;
+    tag.dbId = linfo->lRelId.dbId;
+    BlockIdCopy(&(tag.tupleId.ip_blkid), &(tidPtr->ip_blkid));
+    tag.tupleId.ip_posid = InvalidOffsetNumber;
+    
+    
+    if (action == UNLOCK)
+       return(LockRelease(MultiTableId, &tag, lockt));
+    else
+       return(LockAcquire(MultiTableId, &tag, lockt));
+}
+
diff --git a/src/backend/storage/lock.h b/src/backend/storage/lock.h
new file mode 100644 (file)
index 0000000..fe563cb
--- /dev/null
@@ -0,0 +1,218 @@
+/*-------------------------------------------------------------------------
+ *
+ * lock.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef LOCK_H_
+#define LOCK_H_
+
+#include "postgres.h"
+#include "storage/itemptr.h"
+#include "storage/shmem.h"
+#include "storage/spin.h"
+#include "storage/backendid.h"
+#include "utils/hsearch.h"
+
+extern SPINLOCK LockMgrLock;
+typedef int MASK;
+
+#define INIT_TABLE_SIZE                100
+#define MAX_TABLE_SIZE                 1000
+
+
+/* ----------------------
+ * The following defines are used to estimate how much shared
+ * memory the lock manager is going to require.  
+ * 
+ * NBACKENDS - The number of concurrently running backends
+ * NLOCKS_PER_XACT - The number of unique locks acquired in a transaction
+ * NLOCKENTS - The maximum number of lock entries in the lock table.
+ * ----------------------
+ */
+#define NBACKENDS 50
+#define NLOCKS_PER_XACT 40
+#define NLOCKENTS NLOCKS_PER_XACT*NBACKENDS
+
+typedef int LOCK_TYPE;
+typedef int LOCKT;
+typedef int LockTableId;
+
+/* MAX_LOCKTYPES cannot be larger than the bits in MASK */
+#define MAX_LOCKTYPES 6
+
+/*
+ * MAX_TABLES corresponds to the number of spin locks allocated in
+ * CreateSpinLocks() or the number of shared memory locations allocated
+ * for lock table spin locks in the case of machines with TAS instructions.
+ */
+#define MAX_TABLES 2
+
+#define INVALID_TABLEID 0
+
+/*typedef struct LOCK LOCK; */
+
+
+typedef struct ltag {
+    Oid                        relId;
+    Oid                        dbId;
+    ItemPointerData    tupleId;
+} LOCKTAG;
+
+#define TAGSIZE (sizeof(LOCKTAG))
+
+/* This is the control structure for a lock table.  It
+ * lives in shared memory:
+ *
+ * tableID -- the handle used by the lock table's clients to
+ *     refer to the table.
+ *
+ * nLockTypes -- number of lock types (READ,WRITE,etc) that
+ *     are defined on this lock table
+ *
+ * conflictTab -- this is an array of bitmasks showing lock
+ *     type conflicts. conflictTab[i] is a mask with the j-th bit
+ *     turned on if lock types i and j conflict.
+ *
+ * prio -- each locktype has a priority, so, for example, waiting
+ *     writers can be given priority over readers (to avoid
+ *     starvation).
+ *
+ * masterlock -- synchronizes access to the table
+ *
+ */
+typedef struct lockctl {
+  LockTableId  tableId;
+  int          nLockTypes;
+  int          conflictTab[MAX_LOCKTYPES];
+  int          prio[MAX_LOCKTYPES];
+  SPINLOCK     masterLock;
+} LOCKCTL;
+
+/*
+ * lockHash -- hash table on lock Ids,
+ * xidHash -- hash on xid and lockId in case
+ *     multiple processes are holding the lock
+ * ctl - control structure described above.
+ */
+typedef struct ltable {
+    HTAB       *lockHash;
+    HTAB       *xidHash;
+    LOCKCTL    *ctl;
+} LOCKTAB;
+
+/* -----------------------
+ * A transaction never conflicts with its own locks.  Hence, if
+ * multiple transactions hold non-conflicting locks on the same
+ * data, private per-transaction information must be stored in the
+ * XID table.  The tag is XID + shared memory lock address so that
+ * all locks can use the same XID table.  The private information
+ * we store is the number of locks of each type (holders) and the
+ * total number of locks (nHolding) held by the transaction.
+ *
+ * NOTE: --
+ * There were some problems with the fact that currently TransactionIdData
+ * is a 5 byte entity and compilers long word aligning of structure fields.
+ * If the 3 byte padding is put in front of the actual xid data then the
+ * hash function (which uses XID_TAGSIZE when deciding how many bytes of a
+ * struct to look at for the key) might only see the last two bytes of the xid.
+ *
+ * Clearly this is not good since its likely that these bytes will be the
+ * same for many transactions and hence they will share the same entry in
+ * hash table causing the entry to be corrupted.  For this long-winded
+ * reason I have put the tag in a struct of its own to ensure that the
+ * XID_TAGSIZE is computed correctly.  It used to be sizeof (SHMEM_OFFSET) +
+ * sizeof(TransactionIdData) which != sizeof(XIDTAG).
+ *
+ * Finally since the hash function will now look at all 12 bytes of the tag
+ * the padding bytes MUST be zero'd before use in hash_search() as they
+ * will have random values otherwise.  Jeff 22 July 1991.
+ * -----------------------
+ */
+
+typedef struct XIDTAG {
+    SHMEM_OFFSET       lock;
+    int                        pid;
+    TransactionId      xid;
+} XIDTAG;
+
+typedef struct XIDLookupEnt {
+    /* tag */
+    XIDTAG tag;
+
+    /* data */
+    int                        holders[MAX_LOCKTYPES];
+    int                        nHolding;
+    SHM_QUEUE          queue;
+} XIDLookupEnt;
+
+#define XID_TAGSIZE (sizeof(XIDTAG))
+
+/* originally in procq.h */
+typedef struct procQueue {
+    SHM_QUEUE  links;
+    int                size;
+} PROC_QUEUE;
+
+
+/*
+ * lock information:
+ *
+ * tag -- uniquely identifies the object being locked
+ * mask -- union of the conflict masks of all lock types
+ *     currently held on this object.
+ * waitProcs -- queue of processes waiting for this lock
+ * holders -- count of each lock type currently held on the
+ *     lock.
+ * nHolding -- total locks of all types.
+ */
+typedef struct Lock {
+    /* hash key */
+    LOCKTAG            tag;
+
+    /* data */
+    int                        mask;
+    PROC_QUEUE         waitProcs;
+    int                        holders[MAX_LOCKTYPES];
+    int                        nHolding;
+    int                        activeHolders[MAX_LOCKTYPES];
+    int                        nActive;
+} LOCK;
+
+#define LockGetLock_nHolders(l) l->nHolders
+
+#define LockDecrWaitHolders(lock, lockt) \
+  lock->nHolding--; \
+  lock->holders[lockt]--
+
+#define LockLockTable() SpinAcquire(LockMgrLock);
+#define UnlockLockTable() SpinRelease(LockMgrLock);
+
+extern SPINLOCK LockMgrLock;
+
+/*
+ * function prototypes
+ */
+extern void InitLocks(void);
+extern void LockDisable(int status);
+extern LockTableId LockTabInit(char *tabName, MASK *conflictsP, int *prioP,
+                              int ntypes);
+extern LockTableId LockTabRename(LockTableId tableId);
+extern bool LockAcquire(LockTableId tableId, LOCKTAG *lockName, LOCKT lockt);
+extern int LockResolveConflicts(LOCKTAB *ltable, LOCK *lock, LOCKT lockt,
+                           TransactionId xid);
+extern int WaitOnLock(LOCKTAB *ltable, LockTableId tableId, LOCK *lock,
+                     LOCKT lockt);
+extern bool LockRelease(LockTableId tableId, LOCKTAG *lockName, LOCKT lockt);
+extern void GrantLock(LOCK *lock, LOCKT lockt);
+extern bool LockReleaseAll(LockTableId tableId, SHM_QUEUE *lockQueue);
+extern int LockShmemSize(void);
+extern bool LockingDisabled(void);
+
+#endif /* LOCK_H */
diff --git a/src/backend/storage/multilev.h b/src/backend/storage/multilev.h
new file mode 100644 (file)
index 0000000..106602a
--- /dev/null
@@ -0,0 +1,64 @@
+/*-------------------------------------------------------------------------
+ *
+ * multilev.h--
+ *    multi level lock table consts/defs for single.c and multi.c and their
+ *    clients
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTILEV_H
+#define MULTILEV_H
+
+#include "storage/lock.h"
+#include "storage/lmgr.h"
+
+#define READ_LOCK      2
+#define WRITE_LOCK     1
+
+/* any time a small granularity READ/WRITE lock is set.  
+ * Higher granularity READ_INTENT/WRITE_INTENT locks must
+ * also be set.  A read intent lock is has value READ+INTENT.
+ * in this implementation.
+ */
+#define NO_LOCK                0
+#define INTENT         2
+#define READ_INTENT    (READ_LOCK+INTENT)
+#define WRITE_INTENT   (WRITE_LOCK+INTENT)
+
+#define EXTEND_LOCK    5
+
+#define SHORT_TERM     1
+#define LONG_TERM      2
+#define UNLOCK         0
+
+#define N_LEVELS 3
+#define RELN_LEVEL 0
+#define PAGE_LEVEL 1
+#define TUPLE_LEVEL 2
+typedef int LOCK_LEVEL;
+
+/* multi.c */
+
+extern LockTableId MultiTableId;
+extern LockTableId ShortTermTableId;
+
+/*
+ * function prototypes
+ */
+extern LockTableId InitMultiLevelLockm(void);
+extern bool MultiLockReln(LockInfo linfo, LOCKT lockt);
+extern bool MultiLockTuple(LockInfo linfo, ItemPointer tidPtr, LOCKT lockt);
+extern bool MultiLockPage(LockInfo linfo, ItemPointer tidPtr, LOCKT lockt);
+extern bool MultiAcquire(LockTableId tableId, LOCKTAG *tag, LOCKT lockt,
+                        LOCK_LEVEL level);
+extern bool MultiReleasePage(LockInfo linfo, ItemPointer tidPtr, LOCKT lockt);
+extern bool MultiReleaseReln(LockInfo linfo, LOCKT lockt);
+extern bool MultiRelease(LockTableId tableId, LOCKTAG *tag, LOCKT lockt,
+                        LOCK_LEVEL level);
+
+#endif /* MULTILEV_H */
diff --git a/src/backend/storage/off.h b/src/backend/storage/off.h
new file mode 100644 (file)
index 0000000..e887234
--- /dev/null
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+ *
+ * off.h--
+ *    POSTGRES disk "offset" definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        OFF_H
+#define OFF_H
+
+#include "c.h"
+#include "machine.h"           /* for BLCKSZ */
+#include "storage/itemid.h"
+
+/*
+ * OffsetNumber:
+ *
+ * this is a 1-based index into the linp (ItemIdData) array in the
+ * header of each disk page.
+ */
+typedef uint16                 OffsetNumber;
+
+#define InvalidOffsetNumber    ((OffsetNumber) 0)
+#define FirstOffsetNumber      ((OffsetNumber) 1)
+#define        MaxOffsetNumber         ((OffsetNumber) (BLCKSZ / sizeof(ItemIdData)))
+#define        OffsetNumberMask        (0xffff)                /* valid uint16 bits */
+
+/* ----------------
+ *     support macros
+ * ----------------
+ */
+
+/*
+ * OffsetNumberIsValid --
+ *     True iff the offset number is valid.
+ */
+#define OffsetNumberIsValid(offsetNumber) \
+    ((bool) ((offsetNumber != InvalidOffsetNumber) && \
+            (offsetNumber <= MaxOffsetNumber)))
+
+/*
+ * OffsetNumberNext --
+ * OffsetNumberPrev --
+ *     Increments/decrements the argument.  These macros look pointless
+ *     but they help us disambiguate the different manipulations on
+ *     OffsetNumbers (e.g., sometimes we substract one from an
+ *     OffsetNumber to move back, and sometimes we do so to form a
+ *     real C array index).
+ */
+#define OffsetNumberNext(offsetNumber) \
+    ((OffsetNumber) (1 + (offsetNumber)))
+#define OffsetNumberPrev(offsetNumber) \
+    ((OffsetNumber) (-1 + (offsetNumber)))
+
+#endif /* OFF_H */
diff --git a/src/backend/storage/page.h b/src/backend/storage/page.h
new file mode 100644 (file)
index 0000000..98f4e8e
--- /dev/null
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * page.h--
+ *    POSTGRES buffer page abstraction definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        PAGE_H
+#define PAGE_H
+
+#include "c.h"
+
+typedef Pointer        Page;
+
+/*
+ * PageIsValid --
+ *     True iff page is valid.
+ */
+#define        PageIsValid(page) PointerIsValid(page)
+
+#endif /* PAGE_H */
diff --git a/src/backend/storage/page/Makefile.inc b/src/backend/storage/page/Makefile.inc
new file mode 100644 (file)
index 0000000..653b5ad
--- /dev/null
@@ -0,0 +1,16 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for storage/page
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= bufpage.c itemptr.c
+
+
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
new file mode 100644 (file)
index 0000000..fecc639
--- /dev/null
@@ -0,0 +1,519 @@
+/*-------------------------------------------------------------------------
+ *
+ * bufpage.c--
+ *    POSTGRES standard buffer page code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <sys/types.h>
+#include <sys/file.h>
+
+#include "c.h"
+
+#include "storage/item.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/memutils.h"
+#include "storage/bufpage.h"
+
+#include "lib/qsort.h"
+
+static bool PageManagerShuffle = true; /* default is shuffle mode */
+
+/* ----------------------------------------------------------------
+ *                     Buffer support functions
+ * ----------------------------------------------------------------
+ */
+/*
+ * BufferGetPageSize --
+ *     Returns the page size within a buffer.
+ *
+ * Notes:
+ *     Assumes buffer is valid.
+ *
+ *     The buffer can be a raw disk block and need not contain a valid
+ *     (formatted) disk page.
+ */
+Size
+BufferGetPageSize(Buffer buffer)
+{
+    Size       pageSize;
+    
+    Assert(BufferIsValid(buffer));
+    pageSize = BLCKSZ; /* XXX dig out of buffer descriptor */
+    
+    Assert(PageSizeIsValid(pageSize));
+    return (pageSize);
+}
+
+/*
+ * BufferGetPage --
+ *     Returns the page associated with a buffer.
+ */
+Page
+BufferGetPage(Buffer buffer)
+{
+    return (Page) BufferGetBlock(buffer);
+}
+
+
+/* ----------------------------------------------------------------
+ *                     Page support functions
+ * ----------------------------------------------------------------
+ */
+
+/*
+ * PageInit --
+ *     Initializes the contents of a page.
+ */
+void
+PageInit(Page page, Size pageSize, Size specialSize)
+{
+    PageHeader p = (PageHeader) page;
+
+    Assert(pageSize == BLCKSZ);
+    Assert(pageSize >
+          specialSize + sizeof(PageHeaderData) - sizeof(ItemIdData));
+    
+    specialSize = DOUBLEALIGN(specialSize);
+
+    p->pd_lower = sizeof(PageHeaderData) - sizeof(ItemIdData);
+    p->pd_upper = pageSize - specialSize;
+    p->pd_special = pageSize - specialSize;
+    PageSetPageSize(page, pageSize);
+}
+
+/*
+ * PageGetItem --
+ *     Retrieves an item on the given page.
+ *
+ * Note:
+ *     This does change the status of any of the resources passed.
+ *     The semantics may change in the future.
+ */
+Item
+PageGetItem(Page page, ItemId itemId)
+{
+    Item       item;
+    
+    Assert(PageIsValid(page));
+    Assert((*itemId).lp_flags & LP_USED);
+    
+    item = (Item)(((char *)page) + (*itemId).lp_off);
+    
+    return (item);
+}
+
+/*
+ * PageAddItem --
+ *     Adds item to the given page.
+ *
+ * Note:
+ *     This does not assume that the item resides on a single page.
+ *     It is the responsiblity of the caller to act appropriately
+ *     depending on this fact.  The "pskip" routines provide a
+ *     friendlier interface, in this case.
+ *     
+ *     This does change the status of any of the resources passed.
+ *     The semantics may change in the future.
+ *
+ *     This routine should probably be combined with others?
+ */
+/* ----------------
+ *     PageAddItem
+ *
+ *     add an item to a page.
+ *
+ *   Notes on interface:
+ *     If offsetNumber is valid, shuffle ItemId's down to make room
+ *     to use it, if PageManagerShuffle is true.  If PageManagerShuffle is
+ *     false, then overwrite the specified ItemId.  (PageManagerShuffle is
+ *     true by default, and is modified by calling PageManagerModeSet.)
+ *     If offsetNumber is not valid, then assign one by finding the first 
+ *     one that is both unused and deallocated.
+ *
+ *   NOTE: If offsetNumber is valid, and PageManagerShuffle is true, it
+ *     is assumed that there is room on the page to shuffle the ItemId's
+ *     down by one.
+ * ----------------
+ */
+OffsetNumber
+PageAddItem(Page page,
+           Item item,
+           Size size,
+           OffsetNumber offsetNumber,
+           ItemIdFlags flags)
+{
+    register           i;
+    Size               alignedSize;
+    Offset             lower;
+    Offset             upper;
+    ItemId             itemId;
+    ItemId             fromitemId, toitemId;
+    OffsetNumber       limit;
+    
+    bool shuffled = false;
+    
+    /*
+     *  Find first unallocated offsetNumber
+     */
+    limit = OffsetNumberNext(PageGetMaxOffsetNumber(page));
+    
+    /* was offsetNumber passed in? */
+    if (OffsetNumberIsValid(offsetNumber)) {
+       if (PageManagerShuffle == true) {
+           /* shuffle ItemId's (Do the PageManager Shuffle...) */
+           for (i = (limit - 1); i >= offsetNumber; i--) {
+               fromitemId = &((PageHeader)page)->pd_linp[i - 1];
+               toitemId = &((PageHeader)page)->pd_linp[i];
+               *toitemId = *fromitemId;
+           }
+           shuffled = true;    /* need to increase "lower" */
+       } else { /* overwrite mode */
+           itemId = &((PageHeader)page)->pd_linp[offsetNumber - 1];
+           if (((*itemId).lp_flags & LP_USED)  || 
+               ((*itemId).lp_len != 0)) {
+               elog(WARN, "PageAddItem: tried overwrite of used ItemId");
+               return (InvalidOffsetNumber);
+           }
+       }
+    } else {   /* offsetNumber was not passed in, so find one */
+       /* look for "recyclable" (unused & deallocated) ItemId */
+       for (offsetNumber = 1; offsetNumber < limit; offsetNumber++) {
+           itemId = &((PageHeader)page)->pd_linp[offsetNumber - 1];
+           if ((((*itemId).lp_flags & LP_USED) == 0) && 
+               ((*itemId).lp_len == 0)) 
+               break;
+       }
+    }
+    if (offsetNumber > limit)
+       lower = (Offset) (((char *) (&((PageHeader)page)->pd_linp[offsetNumber])) - ((char *) page));
+    else if (offsetNumber == limit || shuffled == true)
+       lower = ((PageHeader)page)->pd_lower + sizeof (ItemIdData);
+    else
+       lower = ((PageHeader)page)->pd_lower;
+    
+    alignedSize = DOUBLEALIGN(size);
+    
+    upper = ((PageHeader)page)->pd_upper - alignedSize;
+    
+    if (lower > upper) {
+       return (InvalidOffsetNumber);
+    }
+    
+    itemId = &((PageHeader)page)->pd_linp[offsetNumber - 1];
+    (*itemId).lp_off = upper;
+    (*itemId).lp_len = size;
+    (*itemId).lp_flags = flags;
+    memmove((char *)page + upper, item, size);
+    ((PageHeader)page)->pd_lower = lower;
+    ((PageHeader)page)->pd_upper = upper;
+    
+    return (offsetNumber);
+}
+
+/*
+ * PageGetTempPage --
+ *     Get a temporary page in local memory for special processing
+ */
+Page
+PageGetTempPage(Page page, Size specialSize)
+{
+    Size       pageSize;
+    Size       size;
+    Page       temp;
+    PageHeader thdr;
+    
+    pageSize = PageGetPageSize(page);
+    
+    if ((temp = (Page) palloc(pageSize)) == (Page) NULL)
+       elog(FATAL, "Cannot allocate %d bytes for temp page.", pageSize);
+    thdr = (PageHeader) temp;
+    
+    /* copy old page in */
+    memmove(temp, page, pageSize);
+    
+    /* clear out the middle */
+    size = (pageSize - sizeof(PageHeaderData)) + sizeof(ItemIdData);
+    size -= DOUBLEALIGN(specialSize);
+    memset((char *) &(thdr->pd_linp[0]), 0, size);
+    
+    /* set high, low water marks */
+    thdr->pd_lower = sizeof (PageHeaderData) - sizeof (ItemIdData);
+    thdr->pd_upper = pageSize - DOUBLEALIGN(specialSize);
+    
+    return (temp);
+}
+
+/*
+ * PageRestoreTempPage --
+ *     Copy temporary page back to permanent page after special processing
+ *     and release the temporary page.
+ */
+void
+PageRestoreTempPage(Page tempPage, Page oldPage)
+{
+    Size       pageSize;
+    
+    pageSize = PageGetPageSize(tempPage);
+    memmove((char *) oldPage, (char *) tempPage, pageSize);
+    
+    pfree(tempPage);
+}
+
+/*
+ * PageGetMaxOffsetNumber --
+ *     Returns the maximum offset number used by the given page.
+ *
+ *     NOTE: The offset is invalid if the page is non-empty.
+ *     Test whether PageIsEmpty before calling this routine
+ *     and/or using its return value.
+ */
+OffsetNumber
+PageGetMaxOffsetNumber(Page page)
+{
+    LocationIndex      low;
+    OffsetNumber       i;
+    
+    low = ((PageHeader) page)->pd_lower;
+    i = (low - (sizeof(PageHeaderData) - sizeof(ItemIdData)))
+       / sizeof(ItemIdData);
+    
+    return(i);
+}      
+
+/* ----------------
+ *     itemid stuff for PageRepairFragmentation
+ * ----------------
+ */
+struct itemIdSortData {
+    int                offsetindex;    /* linp array index */
+    ItemIdData  itemiddata;
+};
+
+static int
+itemidcompare(struct itemIdSortData *itemidp1, struct itemIdSortData *itemidp2)
+{
+    if (itemidp1->itemiddata.lp_off == itemidp2->itemiddata.lp_off)
+       return(0);
+    else if (itemidp1->itemiddata.lp_off < itemidp2->itemiddata.lp_off)
+       return(1);
+    else
+       return(-1);
+}
+
+/*
+ * PageRepairFragmentation --
+ *     Frees fragmented space on a page.
+ */
+void
+PageRepairFragmentation(Page page)
+{
+    int                i;
+    struct itemIdSortData      *itemidbase, *itemidptr;
+    ItemId             lp;
+    int                nline, nused;
+    int                itemidcompare();
+    Offset             upper;
+    Size               alignedSize;
+    
+    nline = (int16) PageGetMaxOffsetNumber(page);
+    nused = 0;
+    for (i=0; i<nline; i++) {
+       lp = ((PageHeader)page)->pd_linp + i;
+       if ((*lp).lp_flags & LP_USED)
+           nused++;
+    }
+    
+    if (nused == 0) {
+       for (i=0; i<nline; i++) {
+           lp = ((PageHeader)page)->pd_linp + i;
+           if ((*lp).lp_len > 0)       /* unused, but allocated */
+               (*lp).lp_len = 0;       /* indicate unused & deallocated */
+       }
+       
+       ((PageHeader)page)->pd_upper = ((PageHeader)page)->pd_special;
+    } else {   /* nused != 0 */
+       itemidbase = (struct itemIdSortData *) 
+           palloc(sizeof(struct itemIdSortData) * nused);
+       memset((char *) itemidbase, 0, sizeof(struct itemIdSortData) * nused);
+       itemidptr = itemidbase;
+       for (i=0; i<nline; i++) {
+           lp = ((PageHeader)page)->pd_linp + i;
+           if ((*lp).lp_flags & LP_USED) {
+               itemidptr->offsetindex = i;
+               itemidptr->itemiddata = *lp;
+               itemidptr++;
+           } else {
+               if ((*lp).lp_len > 0)   /* unused, but allocated */
+                   (*lp).lp_len = 0;   /* indicate unused & deallocated */
+           }
+       }
+       
+       /* sort itemIdSortData array...*/
+       pg_qsort((char *) itemidbase, nused, sizeof(struct itemIdSortData),
+                (void*) itemidcompare);
+       
+       /* compactify page */
+       ((PageHeader)page)->pd_upper = ((PageHeader)page)->pd_special;
+       
+       for (i=0, itemidptr = itemidbase; i<nused; i++, itemidptr++) {
+           lp = ((PageHeader)page)->pd_linp + itemidptr->offsetindex;
+           alignedSize = DOUBLEALIGN((*lp).lp_len);
+           upper = ((PageHeader)page)->pd_upper - alignedSize;
+           memmove((char *) page + upper,
+                   (char *)page + (*lp).lp_off, 
+                   (*lp).lp_len);
+           (*lp).lp_off = upper;
+           ((PageHeader)page)->pd_upper = upper;
+       }
+       
+       pfree(itemidbase);
+    }
+}
+
+/*
+ * PageGetFreeSpace --
+ *     Returns the size of the free (allocatable) space on a page.
+ */
+Size
+PageGetFreeSpace(Page page)
+{
+    Size       space;
+    
+    
+    space = ((PageHeader)page)->pd_upper - ((PageHeader)page)->pd_lower;
+    
+    if (space < sizeof (ItemIdData)) {
+       return (0);
+    }
+    space -= sizeof (ItemIdData);              /* XXX not always true */
+    
+    return (space);
+}
+
+/*
+ * PageManagerModeSet --
+ *
+ *   Sets mode to either: ShufflePageManagerMode (the default) or
+ *   OverwritePageManagerMode.  For use by access methods code
+ *   for determining semantics of PageAddItem when the offsetNumber
+ *   argument is passed in.
+ */
+void
+PageManagerModeSet(PageManagerMode mode)
+{
+    if (mode == ShufflePageManagerMode)
+       PageManagerShuffle = true;
+    else if (mode == OverwritePageManagerMode)
+       PageManagerShuffle = false;
+}
+
+/*
+ *----------------------------------------------------------------
+ * PageIndexTupleDelete
+ *----------------------------------------------------------------
+ *
+ *     This routine does the work of removing a tuple from an index page.
+ */
+void
+PageIndexTupleDelete(Page page, OffsetNumber offnum)
+{
+    PageHeader         phdr;
+    char       *addr;
+    ItemId     tup;
+    Size       size;
+    char       *locn;
+    int        nbytes;
+    int                offidx;
+    
+    phdr = (PageHeader) page;
+    
+    /* change offset number to offset index */
+    offidx = offnum - 1;
+    
+    tup = PageGetItemId(page, offnum);
+    size = ItemIdGetLength(tup);
+    size = DOUBLEALIGN(size);
+    
+    /* location of deleted tuple data */
+    locn = (char *) (page + ItemIdGetOffset(tup));
+    
+    /*
+     * First, we want to get rid of the pd_linp entry for the index
+     * tuple.  We copy all subsequent linp's back one slot in the
+     * array.
+     */
+    
+    nbytes = phdr->pd_lower -
+       ((char *)&phdr->pd_linp[offidx + 1] - (char *) phdr);
+    memmove((char *) &(phdr->pd_linp[offidx]),
+           (char *) &(phdr->pd_linp[offidx + 1]),
+           nbytes);
+    
+    /*
+     * Now move everything between the old upper bound (beginning of tuple
+     * space) and the beginning of the deleted tuple forward, so that
+     * space in the middle of the page is left free.  If we've just deleted
+     * the tuple at the beginning of tuple space, then there's no need
+     * to do the copy (and bcopy on some architectures SEGV's if asked
+     * to move zero bytes).
+     */
+    
+    /* beginning of tuple space */
+    addr = (char *) (page + phdr->pd_upper);
+    
+    if (locn != addr)
+       memmove(addr + size, addr, (int) (locn - addr));
+    
+    /* adjust free space boundary pointers */
+    phdr->pd_upper += size;
+    phdr->pd_lower -= sizeof (ItemIdData);
+    
+    /* finally, we need to adjust the linp entries that remain */
+    if (!PageIsEmpty(page))
+       PageIndexTupleDeleteAdjustLinePointers(phdr, locn, size);
+}
+
+/*
+ *----------------------------------------------------------------
+ * PageIndexTupleDeleteAdjustLinePointers
+ *----------------------------------------------------------------
+ *
+ *     Once the line pointers and tuple data have been shifted around
+ *     on the page, we need to go down the line pointer vector and
+ *     adjust pointers to reflect new locations.  Anything that used
+ *     to be before the deleted tuple's data was moved forward by the
+ *     size of the deleted tuple.
+ *
+ *     This routine does the work of adjusting the line pointers.
+ *     Location is where the tuple data used to lie; size is how
+ *     much space it occupied.  We assume that size has been aligned
+ *     as required by the time we get here.
+ *
+ *     This routine should never be called on an empty page.
+ */
+void
+PageIndexTupleDeleteAdjustLinePointers(PageHeader phdr,
+                                      char *location,
+                                      Size size)
+{
+    int i;
+    
+    /* location is an index into the page... */
+    location -= (int) phdr;
+    
+    for (i = PageGetMaxOffsetNumber((Page) phdr) - 1; i >= 0; i--) {
+       if (phdr->pd_linp[i].lp_off <= (unsigned) location) {
+           phdr->pd_linp[i].lp_off += size;
+       }
+    }
+}
diff --git a/src/backend/storage/page/itemptr.c b/src/backend/storage/page/itemptr.c
new file mode 100644 (file)
index 0000000..14e4a35
--- /dev/null
@@ -0,0 +1,40 @@
+/*-------------------------------------------------------------------------
+ *
+ * itemptr.c--
+ *    POSTGRES disk item pointer code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+
+#include "storage/block.h"
+#include "storage/off.h"
+#include "storage/itemptr.h"
+#include "storage/bufpage.h"
+
+/*
+ * ItemPointerEquals --
+ *  Returns true if both item pointers point to the same item, 
+ *   otherwise returns false.
+ *
+ * Note:
+ *  Assumes that the disk item pointers are not NULL.
+ */
+bool
+ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
+{
+    if (ItemPointerGetBlockNumber(pointer1) ==
+        ItemPointerGetBlockNumber(pointer2) &&
+        ItemPointerGetOffsetNumber(pointer1) ==
+        ItemPointerGetOffsetNumber(pointer2))
+       return(true);
+    else
+        return(false);
+}
+
diff --git a/src/backend/storage/pagenum.h b/src/backend/storage/pagenum.h
new file mode 100644 (file)
index 0000000..86f2a77
--- /dev/null
@@ -0,0 +1,33 @@
+/*-------------------------------------------------------------------------
+ *
+ * pagenum.h--
+ *    POSTGRES page number definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        PAGENUM_H
+#define PAGENUM_H
+
+#include "c.h"
+#include "storage/page.h"
+
+typedef uint16 PageNumber;
+
+typedef uint32 LogicalPageNumber;
+
+#define InvalidLogicalPageNumber       0
+
+/*
+ * LogicalPageNumberIsValid --
+ *     True iff the logical page number is valid.
+ */
+#define LogicalPageNumberIsValid(pageNumber) \
+    ((bool)((pageNumber) != InvalidLogicalPageNumber))
+
+
+#endif /* PAGENUM_H */
diff --git a/src/backend/storage/pos.h b/src/backend/storage/pos.h
new file mode 100644 (file)
index 0000000..6b88c22
--- /dev/null
@@ -0,0 +1,64 @@
+/*-------------------------------------------------------------------------
+ *
+ * pos.h--
+ *    POSTGRES "position" definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        POS_H
+#define POS_H
+
+#include "c.h"
+
+/*
+ * a 'position' used to be <pagenumber, offset> in postgres.  this has
+ * been changed to just <offset> as the notion of having multiple pages
+ * within a block has been removed.
+ *
+ * the 'offset' abstraction is somewhat confusing.  it is NOT a byte
+ * offset within the page; instead, it is an offset into the line
+ * pointer array contained on every page that store (heap or index)
+ * tuples.
+ */
+typedef bits16         PositionIdData;
+typedef PositionIdData *PositionId;
+
+/* ----------------
+ *     support macros
+ * ----------------
+ */
+
+/*
+ * PositionIdIsValid --
+ *     True iff the position identifier is valid.
+ */
+#define PositionIdIsValid(positionId) \
+    PointerIsValid(positionId)
+
+/*
+ * PositionIdSetInvalid --
+ *      Make an invalid position.
+ */
+#define PositionIdSetInvalid(positionId) \
+    *(positionId) = (bits16) 0
+
+/*
+ * PositionIdSet --
+ *     Sets a position identifier to the specified value.
+ */
+#define PositionIdSet(positionId, offsetNumber) \
+    *(positionId) = (offsetNumber)
+
+/*
+ * PositionIdGetOffsetNumber --
+ *     Retrieve the offset number from a position identifier.
+ */
+#define PositionIdGetOffsetNumber(positionId) \
+    ((OffsetNumber) *(positionId))
+
+#endif /*  POS_H */
diff --git a/src/backend/storage/proc.h b/src/backend/storage/proc.h
new file mode 100644 (file)
index 0000000..c8bc40e
--- /dev/null
@@ -0,0 +1,127 @@
+/*-------------------------------------------------------------------------
+ *
+ * proc.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _PROC_H_
+#define _PROC_H_
+
+#include "storage/ipc.h"
+#include "storage/lock.h"
+#ifndef WIN32
+#include <sys/sem.h>
+#else
+/* This is because WIN32 already defines PROC */
+#define PROC   PGL_PROC
+#endif /* WIN32 */
+#include "storage/shmem.h"
+
+
+typedef struct {
+  int                  sleeplock;
+  int                  semNum;
+  IpcSemaphoreId       semId;
+  IpcSemaphoreKey      semKey;
+} SEMA;
+
+/*
+ * Each backend has:
+ */
+typedef struct proc {
+
+  /* proc->links MUST BE THE FIRST ELEMENT OF STRUCT (see ProcWakeup()) */
+
+  SHM_QUEUE         links;     /* proc can be waiting for one event(lock) */
+  SEMA              sem;       /* ONE semaphore to sleep on */
+  int               errType;   /* error code tells why we woke up */
+
+  int               procId;    /* unique number for this structure
+                                * NOT unique per backend, these things
+                                * are reused after the backend dies.
+                                */
+
+  int               critSects; /* If critSects > 0, we are in sensitive
+                                * routines that cannot be recovered when
+                                * the process fails.
+                                */
+
+  int               prio;      /* priority for sleep queue */
+
+  TransactionId     xid;       /* transaction currently being executed
+                                * by this proc
+                                */
+
+  LOCK *            waitLock;  /* Lock we're sleeping on */
+  int               token;     /* info for proc wakeup routines */     
+  int              pid;        /* This procs process id */
+  short                    sLocks[MAX_SPINS];  /* Spin lock stats */
+  SHM_QUEUE        lockQueue;  /* locks associated with current transaction */
+} PROC;
+
+
+/*
+ * MAX_PROC_SEMS is the maximum number of per-process semaphores (those used
+ * by the lock mgr) we can keep track of. PROC_NSEMS_PER_SET is the number
+ * of semaphores in each (sys-V) semaphore set allocated. (Be careful not
+ * to set it to greater 32. Otherwise, the bitmap will overflow.)
+ */
+#define  MAX_PROC_SEMS         128
+#define  PROC_NSEMS_PER_SET    16
+
+typedef struct procglobal {
+    SHMEM_OFFSET       freeProcs;
+    int                        numProcs;
+    IPCKey             currKey;
+    int32              freeSemMap[MAX_PROC_SEMS/PROC_NSEMS_PER_SET];
+} PROC_HDR;
+
+extern PROC *MyProc;
+
+#define PROC_INCR_SLOCK(lock) if (MyProc) (MyProc->sLocks[(lock)])++
+#define PROC_DECR_SLOCK(lock) if (MyProc) (MyProc->sLocks[(lock)])--
+
+/*
+ * flags explaining why process woke up
+ */
+#define NO_ERROR       0
+#define ERR_TIMEOUT    1
+#define ERR_BUFFER_IO  2
+
+#define MAX_PRIO       50
+#define MIN_PRIO       (-1)
+
+extern SPINLOCK ProcStructLock;
+
+/*
+ * Function Prototypes
+ */
+extern void InitProcess(IPCKey key);
+extern void ProcReleaseLocks(void);
+extern bool ProcRemove(int pid);
+/* extern bool ProcKill(int exitStatus, int pid); */
+/* make static in storage/lmgr/proc.c -- jolly */
+
+extern PROC_QUEUE *ProcQueueAlloc(char *name);
+extern void ProcQueueInit(PROC_QUEUE *queue);
+extern int ProcSleep(PROC_QUEUE *queue, SPINLOCK spinlock, int token, 
+             int prio, LOCK *lock);
+extern PROC *ProcWakeup(PROC *proc, int errType);
+extern int ProcGetId(void);
+extern int ProcLockWakeup(PROC_QUEUE *queue, char * ltable, char * lock);
+extern void ProcAddLock(SHM_QUEUE *elem);
+#if defined(PORTNAME_linux)
+extern int HandleDeadLock(int);
+#else
+extern int HandleDeadLock(void);
+#endif
+extern void ProcReleaseSpins(PROC *proc);
+extern void ProcFreeAllSemaphores(void);
+
+#endif /* PROC_H */
diff --git a/src/backend/storage/shmem.h b/src/backend/storage/shmem.h
new file mode 100644 (file)
index 0000000..d89b9a4
--- /dev/null
@@ -0,0 +1,104 @@
+/*-------------------------------------------------------------------------
+ *
+ * shmem.h--
+ *    shared memory management structures
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        SHMEM_H
+#define SHMEM_H
+
+#include "storage/spin.h"              /* for SPINLOCK */
+#include "utils/hsearch.h"             /* for HTAB */
+
+/* The shared memory region can start at a different address
+ * in every process.  Shared memory "pointers" are actually
+ * offsets relative to the start of the shared memory region(s).
+ */
+typedef unsigned long SHMEM_OFFSET;
+#define INVALID_OFFSET (-1)
+#define BAD_LOCATION (-1)
+
+/* start of the lowest shared memory region.  For now, assume that
+ * there is only one shared memory region 
+ */
+extern SHMEM_OFFSET ShmemBase;
+
+
+/* coerce an offset into a pointer in this process's address space */
+#define MAKE_PTR(xx_offs)\
+  (ShmemBase+((unsigned long)(xx_offs)))
+
+/* coerce a pointer into a shmem offset */
+#define MAKE_OFFSET(xx_ptr)\
+  (SHMEM_OFFSET) (((unsigned long)(xx_ptr))-ShmemBase)
+
+#define SHM_PTR_VALID(xx_ptr)\
+  (((unsigned long)xx_ptr) > ShmemBase)
+
+/* cannot have an offset to ShmemFreeStart (offset 0) */
+#define SHM_OFFSET_VALID(xx_offs)\
+  ((xx_offs != 0) && (xx_offs != INVALID_OFFSET))
+
+
+extern SPINLOCK ShmemLock;
+extern SPINLOCK BindingLock;
+
+/* shmemqueue.c */
+typedef struct SHM_QUEUE {
+    SHMEM_OFFSET       prev;
+    SHMEM_OFFSET       next;
+} SHM_QUEUE;
+
+/* shmem.c */
+extern void ShmemBindingTabReset();
+extern void ShmemCreate(unsigned int key, unsigned int size);
+extern int InitShmem(unsigned int key, unsigned int size);
+extern long *ShmemAlloc(unsigned long size);
+extern int ShmemIsValid(unsigned long addr);
+extern HTAB *ShmemInitHash(char *name, long init_size, long max_size,
+                          HASHCTL *infoP, int hash_flags);
+extern bool ShmemPIDLookup(int pid, SHMEM_OFFSET* locationPtr);
+extern SHMEM_OFFSET ShmemPIDDestroy(int pid);
+extern long *ShmemInitStruct(char *name, unsigned long size,
+                            bool *foundPtr);
+
+
+typedef int TableID;
+
+/* size constants for the binding table */
+        /* max size of data structure string name */
+#define BTABLE_KEYSIZE  (50)
+        /* data in binding table hash bucket */
+#define BTABLE_DATASIZE (sizeof(BindingEnt) - BTABLE_KEYSIZE)
+        /* maximum size of the binding table */
+#define BTABLE_SIZE      (100)
+
+/* this is a hash bucket in the binding table */
+typedef struct {
+    char          key[BTABLE_KEYSIZE]; /* string name */
+    unsigned long  location;           /* location in shared mem */
+    unsigned long  size;               /* numbytes allocated for the
+                                        * structure
+                                        */
+} BindingEnt;
+
+/*
+ * prototypes for functions in shmqueue.c
+ */
+extern void SHMQueueInit(SHM_QUEUE *queue);
+extern bool SHMQueueIsDetached(SHM_QUEUE *queue);
+extern void SHMQueueElemInit(SHM_QUEUE *queue);
+extern void SHMQueueDelete(SHM_QUEUE *queue);
+extern void SHMQueueInsertHD(SHM_QUEUE *queue, SHM_QUEUE *elem);
+extern void SHMQueueInsertTL(SHM_QUEUE *queue, SHM_QUEUE *elem);
+extern void SHMQueueFirst(SHM_QUEUE *queue, Pointer *nextPtrPtr,
+                         SHM_QUEUE *nextQueue);
+extern bool SHMQueueEmpty(SHM_QUEUE *queue);
+
+#endif /* SHMEM_H */
diff --git a/src/backend/storage/sinval.h b/src/backend/storage/sinval.h
new file mode 100644 (file)
index 0000000..fd63a78
--- /dev/null
@@ -0,0 +1,33 @@
+/*-------------------------------------------------------------------------
+ *
+ * sinval.h--
+ *    POSTGRES shared cache invalidation communication definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        SINVAL_H
+#define SINVAL_H
+
+#include "c.h"
+#include "storage/spin.h"
+#include "storage/ipc.h"
+#include "storage/itemptr.h"
+#include "storage/backendid.h"
+
+extern SPINLOCK SInvalLock;
+
+extern void CreateSharedInvalidationState(IPCKey key);
+extern void AttachSharedInvalidationState(IPCKey key);
+extern void InitSharedInvalidationState();
+extern void RegisterSharedInvalid(int cacheId, Index hashIndex,
+                                 ItemPointer pointer);
+extern void InvalidateSharedInvalid(void (*invalFunction)(),
+                                   void (*resetFunction)());
+
+
+#endif /* SINVAL_H */
diff --git a/src/backend/storage/sinvaladt.h b/src/backend/storage/sinvaladt.h
new file mode 100644 (file)
index 0000000..b4813d0
--- /dev/null
@@ -0,0 +1,126 @@
+/*-------------------------------------------------------------------------
+ *
+ * sinvaladt.h--
+ *    POSTGRES shared cache invalidation segment definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SINVALADT_H
+#define SINVALADT_H
+
+#include "postgres.h"  /* XXX */
+
+#include "storage/ipc.h"
+#include "storage/itemptr.h"
+#include "storage/sinval.h"
+/*
+ * The structure of the shared cache invaidation segment
+ *
+ */
+/*
+A------------- Header info --------------
+    criticalSectionSemaphoreId
+    generalSemaphoreId
+    startEntrySection   (offset a)
+    endEntrySection     (offset a + b)
+    startFreeSpace      (offset relative to B)
+    startEntryChain     (offset relatiev to B)
+    endEntryChain       (offset relative to B)
+    numEntries
+    maxNumEntries
+    procState[MaxBackendId] --> limit
+                               resetState (bool)
+a                              tag (POSTID)
+B------------- Start entry section -------
+    SISegEntry  --> entryData --> ... (see  SharedInvalidData!)
+                    isfree  (bool)
+                    next  (offset to next entry in chain )
+b     .... (dynamically growing down)
+C----------------End shared segment -------  
+
+*/
+
+/* Parameters (configurable)  *******************************************/
+#define MaxBackendId 32            /* maximum number of backends       */
+#define MAXNUMMESSAGES 1000        /* maximum number of messages in seg*/
+
+
+#define        InvalidOffset   1000000000  /* a invalid offset  (End of chain) */
+
+typedef struct ProcState {
+    int        limit;          /* the number of read messages          */
+    bool       resetState;     /* true, if backend has to reset its state */
+    int                tag;            /* special tag, recieved from the postmaster */
+} ProcState;
+
+
+typedef struct SISeg {
+    IpcSemaphoreId     criticalSectionSemaphoreId; /* semaphore id     */
+    IpcSemaphoreId     generalSemaphoreId;         /* semaphore id     */
+    Offset      startEntrySection;     /* (offset a)                   */
+    Offset      endEntrySection;       /* (offset a + b)               */
+    Offset      startFreeSpace;                /* (offset relative to B)       */
+    Offset      startEntryChain;       /* (offset relative to B)       */
+    Offset      endEntryChain;          /* (offset relative to B)      */
+    int         numEntries;
+    int         maxNumEntries;
+    ProcState   procState[MaxBackendId]; /* reflects the invalidation state */
+    /* here starts the entry section, controlled by offsets */
+} SISeg;
+#define SizeSISeg     sizeof(SISeg)
+
+typedef struct SharedInvalidData {
+    int                        cacheId;    /* XXX */
+    Index              hashIndex;
+    ItemPointerData    pointerData;
+} SharedInvalidData;
+
+typedef SharedInvalidData   *SharedInvalid;
+
+
+typedef struct SISegEntry {
+    SharedInvalidData  entryData;                  /* the message data */
+    bool                isfree;                            /* entry free? */
+    Offset             next;                       /* offset to next entry*/
+} SISegEntry;
+
+#define SizeOfOneSISegEntry   sizeof(SISegEntry)
+    
+typedef struct SISegOffsets {
+    Offset  startSegment;              /* always 0 (for now) */
+    Offset  offsetToFirstEntry;         /* A + a = B */
+    Offset  offsetToEndOfSegemnt;       /* A + a + b */
+} SISegOffsets;
+
+
+/****************************************************************************/
+/* synchronization of the shared buffer access                             */
+/*    access to the buffer is synchronized by the lock manager !!          */
+/****************************************************************************/
+
+#define SI_LockStartValue  255
+#define SI_SharedLock     (-1)
+#define SI_ExclusiveLock  (-255)
+
+extern SISeg *shmInvalBuffer;  
+
+/*
+ * prototypes for functions in sinvaladt.c
+ */
+extern int SIBackendInit(SISeg *segInOutP);
+extern int SISegmentInit(bool killExistingSegment, IPCKey key);
+
+extern bool SISetDataEntry(SISeg *segP, SharedInvalidData  *data);
+extern void SISetProcStateInvalid(SISeg *segP);
+extern bool SIDelDataEntry(SISeg *segP);
+extern void SIReadEntryData(SISeg *segP, int backendId,
+               void (*invalFunction)(), void (*resetFunction)());
+extern void SIDelExpiredDataEntries(SISeg *segP);
+
+#endif /* SINVALADT_H */
diff --git a/src/backend/storage/smgr.h b/src/backend/storage/smgr.h
new file mode 100644 (file)
index 0000000..f7f7042
--- /dev/null
@@ -0,0 +1,84 @@
+/*-------------------------------------------------------------------------
+ *
+ * smgr.h--
+ *    storage manager switch public interface declarations.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SMGR_H
+#define SMGR_H
+
+#include "utils/rel.h"
+#include "storage/spin.h"      /* for SPINLOCK */
+
+#define SM_FAIL                0
+#define        SM_SUCCESS      1
+
+#define        DEFAULT_SMGR    0
+
+extern int smgrinit(void);
+extern void smgrshutdown(int dummy);
+extern int smgrcreate(int16 which, Relation reln);
+extern int smgrunlink(int16 which, Relation reln);
+extern int smgrextend(int16 which, Relation reln, char *buffer);
+extern int smgropen(int16 which, Relation reln);
+extern int smgrclose(int16 which, Relation reln);
+extern int smgrread(int16 which, Relation reln, BlockNumber blocknum,
+                   char *buffer);
+extern int smgrwrite(int16 which, Relation reln, BlockNumber blocknum,
+                    char *buffer);
+extern int smgrflush(int16 which, Relation reln, BlockNumber blocknum,
+                    char *buffer);
+extern int smgrblindwrt(int16 which, char *dbname, char *relname, Oid dbid,
+                       Oid relid, BlockNumber blkno, char *buffer);
+extern int smgrnblocks(int16 which, Relation reln);
+extern int smgrcommit(void);
+extern int smgrabort(void);
+extern bool smgriswo(int16 smgrno);
+
+
+
+/* internals: move me elsewhere -- ay 7/94 */
+
+/* in md.c */
+extern int mdinit(void);
+extern int mdcreate(Relation reln);
+extern int mdunlink(Relation reln);
+extern int mdextend(Relation reln, char *buffer);
+extern int mdopen(Relation reln);
+extern int mdclose(Relation reln);
+extern int mdread(Relation reln, BlockNumber blocknum, char *buffer);
+extern int mdwrite(Relation reln, BlockNumber blocknum, char *buffer);
+extern int mdflush(Relation reln, BlockNumber blocknum, char *buffer);
+extern int mdblindwrt(char *dbstr, char *relstr, Oid dbid, Oid relid,
+                     BlockNumber blkno, char *buffer);
+extern int mdnblocks(Relation reln);
+extern int mdcommit(void);
+extern int mdabort(void);
+
+/* mm.c */
+extern SPINLOCK MMCacheLock;
+
+extern int mminit(void);
+extern int mmshutdown(void);
+extern int mmcreate(Relation reln);
+extern int mmunlink(Relation reln);
+extern int mmextend(Relation reln, char *buffer);
+extern int mmopen(Relation reln);
+extern int mmclose(Relation reln);
+extern int mmread(Relation reln, BlockNumber blocknum, char *buffer);
+extern int mmwrite(Relation reln, BlockNumber blocknum, char *buffer);
+extern int mmflush(Relation reln, BlockNumber blocknum, char *buffer);
+extern int mmblindwrt(char *dbstr, char *relstr, Oid dbid, Oid relid,
+                     BlockNumber blkno, char *buffer);
+extern int mmnblocks(Relation reln);
+extern int mmcommit(void);
+extern int mmabort(void);
+extern int MMShmemSize(void);
+
+#endif /* SMGR_H */
diff --git a/src/backend/storage/smgr/Makefile.inc b/src/backend/storage/smgr/Makefile.inc
new file mode 100644 (file)
index 0000000..f22b8e4
--- /dev/null
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for storage/smgr
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= md.c mm.c smgr.c smgrtype.c
diff --git a/src/backend/storage/smgr/README b/src/backend/storage/smgr/README
new file mode 100644 (file)
index 0000000..418cbe1
--- /dev/null
@@ -0,0 +1,40 @@
+# $Header$
+
+This directory contains the code that supports the Postgres storage manager
+switch and all of the installed storage managers.  In released systems,
+the only supported storage manager is the magnetic disk manager.  At UC
+Berkeley, the Sony WORM optical disk jukebox and persistent main memory are
+also supported.
+
+As of Postgres Release 3.0, every relation in the system is tagged with the
+storage manager on which it resides.  The storage manager switch code turns
+what used to by filesystem operations into operations on the correct store,
+for any given relation.
+
+The files in this directory, and their contents, are
+
+    smgrtype.c Storage manager type -- maps string names to storage manager
+               IDs and provides simple comparison operators.  This is the
+               regproc support for type 'smgr' in the system catalogs.
+
+    smgr.c     The storage manager switch dispatch code.  The routines in
+               this file call the appropriate storage manager to do hardware
+               accesses requested by the backend.
+
+    md.c       The magnetic disk storage manager.
+
+    mm.c       The persistent main memory storage manager (#undef'ed in
+               tmp/c.h for all distributed systems).
+
+    sj.c       The sony jukebox storage manager and cache management code
+               (#undef'ed in tmp/c.h for all distributed systems).  The
+               routines in this file allocate extents, maintain block
+               maps, and guarantee the persistence and coherency of a cache
+               of jukebox blocks on magnetic disk.
+
+    pgjb.c     The postgres jukebox interface routines.  The routines here
+               handle exclusion on the physical device and translate requests
+               from the storage manager code (sj.c) into jbaccess calls.
+
+    jbaccess.c Access code for the physical Sony jukebox device.  This code
+               was swiped from Andy McFadden's jblib.a code at UC Berkeley.
diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
new file mode 100644 (file)
index 0000000..cd37f5c
--- /dev/null
@@ -0,0 +1,697 @@
+/*-------------------------------------------------------------------------
+ *
+ * md.c--
+ *    This code manages relations that reside on magnetic disk.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>             /* for sprintf() */
+#include <sys/file.h>
+
+#include "postgres.h"
+#include "miscadmin.h"  /* for DataDir */
+
+#include "machine.h"
+#include "storage/smgr.h"      /* where the declarations go */
+#include "storage/block.h"
+#include "storage/fd.h"
+#include "utils/mcxt.h"
+#include "utils/rel.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "catalog/catalog.h"
+
+#undef DIAGNOSTIC
+
+/*
+ *  The magnetic disk storage manager keeps track of open file descriptors
+ *  in its own descriptor pool.  This happens for two reasons.  First, at
+ *  transaction boundaries, we walk the list of descriptors and flush
+ *  anything that we've dirtied in the current transaction.  Second, we
+ *  have to support relations of > 4GBytes.  In order to do this, we break
+ *  relations up into chunks of < 2GBytes and store one chunk in each of
+ *  several files that represent the relation.
+ */
+
+typedef struct _MdfdVec {
+    int                        mdfd_vfd; /* fd number in vfd pool */
+    uint16             mdfd_flags; /* clean, dirty */
+    int                        mdfd_lstbcnt; /* most recent block count */
+    struct _MdfdVec    *mdfd_chain; /* for large relations */
+} MdfdVec;
+
+static int     Nfds = 100;
+static MdfdVec *Md_fdvec = (MdfdVec *) NULL;
+static int     CurFd = 0;
+static MemoryContext   MdCxt;
+
+#define MDFD_DIRTY     (uint16) 0x01
+
+#define        RELSEG_SIZE     262144          /* (2 ** 31) / 8192 -- 2GB file */
+
+/* routines declared here */
+static MdfdVec *_mdfd_openseg(Relation reln, int segno, int oflags);
+static MdfdVec *_mdfd_getseg(Relation reln, int blkno, int oflag);
+static int _fdvec_ext(void);
+static BlockNumber _mdnblocks(File file, Size blcksz);
+
+/*
+ *  mdinit() -- Initialize private state for magnetic disk storage manager.
+ *
+ *     We keep a private table of all file descriptors.  Whenever we do
+ *     a write to one, we mark it dirty in our table.  Whenever we force
+ *     changes to disk, we mark the file descriptor clean.  At transaction
+ *     commit, we force changes to disk for all dirty file descriptors.
+ *     This routine allocates and initializes the table.
+ *
+ *     Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
+ */
+int
+mdinit()
+{
+    MemoryContext oldcxt;
+
+    MdCxt = (MemoryContext) CreateGlobalMemory("MdSmgr");
+    if (MdCxt == (MemoryContext) NULL)
+       return (SM_FAIL);
+
+    oldcxt = MemoryContextSwitchTo(MdCxt);
+    Md_fdvec = (MdfdVec *) palloc(Nfds * sizeof(MdfdVec));
+    (void) MemoryContextSwitchTo(oldcxt);
+
+    if (Md_fdvec == (MdfdVec *) NULL)
+       return (SM_FAIL);
+
+    memset(Md_fdvec, 0, Nfds * sizeof(MdfdVec)); 
+
+    return (SM_SUCCESS);
+}
+
+int
+mdcreate(Relation reln)
+{
+    int fd, vfd;
+    int tmp;
+    char *path;
+    extern bool IsBootstrapProcessingMode();
+
+    path = relpath(&(reln->rd_rel->relname.data[0]));
+    fd = FileNameOpenFile(path, O_RDWR|O_CREAT|O_EXCL, 0600);
+
+    /*
+     *  If the file already exists and is empty, we pretend that the
+     *  create succeeded.  During bootstrap processing, we skip that check,
+     *  because pg_time, pg_variable, and pg_log get created before their
+     *  .bki file entries are processed.
+     */
+
+    if (fd < 0) {
+       if ((fd = FileNameOpenFile(path, O_RDWR, 0600)) >= 0) {
+           if (!IsBootstrapProcessingMode() &&
+               FileRead(fd, (char *) &tmp, sizeof(tmp)) != 0) {
+               FileClose(fd);
+               return (-1);
+           }
+       }
+    }
+
+    if (CurFd >= Nfds) {
+       if (_fdvec_ext() == SM_FAIL)
+           return (-1);
+    }
+
+    Md_fdvec[CurFd].mdfd_vfd = fd;
+    Md_fdvec[CurFd].mdfd_flags = (uint16) 0;
+    Md_fdvec[CurFd].mdfd_chain = (MdfdVec *) NULL;
+    Md_fdvec[CurFd].mdfd_lstbcnt = 0;
+
+    vfd = CurFd++;
+
+    return (vfd);
+}
+
+/*
+ *  mdunlink() -- Unlink a relation.
+ */
+int
+mdunlink(Relation reln)
+{
+    int fd;
+    int i;
+    MdfdVec *v, *ov;
+    MemoryContext oldcxt;
+    char fname[20];    /* XXX should have NAMESIZE defined */
+    char tname[20];
+
+ /* On Windows NT you can't unlink a file if it is open so we have
+ ** to do this.
+ */
+#ifdef WIN32
+    (void) mdclose(reln);
+#endif /* WIN32 */
+
+    memset(fname,0,20); 
+    strncpy(fname, RelationGetRelationName(reln)->data, 16);
+
+    if (FileNameUnlink(fname) < 0)
+       return (SM_FAIL);
+
+    /* unlink all the overflow files for large relations */
+    for (i = 1; ; i++) {
+#ifdef WIN32
+       (void) mdclose(reln);
+#endif /* WIN32 */
+       sprintf(tname, "%s.%d", fname, i);
+       if (FileNameUnlink(tname) < 0)
+           break;
+    }
+
+    /* finally, clean out the mdfd vector */
+    fd = RelationGetFile(reln);
+    Md_fdvec[fd].mdfd_flags = (uint16) 0;
+
+    oldcxt = MemoryContextSwitchTo(MdCxt);
+    for (v = &Md_fdvec[fd]; v != (MdfdVec *) NULL; ) {
+       ov = v;
+       v = v->mdfd_chain;
+       if (ov != &Md_fdvec[fd])
+           pfree(ov);
+    }
+    Md_fdvec[fd].mdfd_chain = (MdfdVec *) NULL;
+    (void) MemoryContextSwitchTo(oldcxt);
+
+    return (SM_SUCCESS);
+}
+
+/*
+ *  mdextend() -- Add a block to the specified relation.
+ *
+ *     This routine returns SM_FAIL or SM_SUCCESS, with errno set as
+ *     appropriate.
+ */
+int
+mdextend(Relation reln, char *buffer)
+{
+    long pos;
+    int nblocks;
+    MdfdVec *v;
+
+    nblocks = mdnblocks(reln);
+    v = _mdfd_getseg(reln, nblocks, O_CREAT);
+
+    if ((pos = FileSeek(v->mdfd_vfd, 0L, SEEK_END)) < 0)
+       return (SM_FAIL);
+
+    if (FileWrite(v->mdfd_vfd, buffer, BLCKSZ) != BLCKSZ)
+       return (SM_FAIL);
+
+    /* remember that we did a write, so we can sync at xact commit */
+    v->mdfd_flags |= MDFD_DIRTY;
+
+    /* try to keep the last block count current, though it's just a hint */
+    if ((v->mdfd_lstbcnt = (++nblocks % RELSEG_SIZE)) == 0)
+       v->mdfd_lstbcnt = RELSEG_SIZE;
+
+#ifdef DIAGNOSTIC
+    if (_mdnblocks(v->mdfd_vfd, BLCKSZ) > RELSEG_SIZE
+       || v->mdfd_lstbcnt > RELSEG_SIZE)
+       elog(FATAL, "segment too big!");
+#endif
+
+    return (SM_SUCCESS);
+}
+
+/*
+ *  mdopen() -- Open the specified relation.
+ */
+int
+mdopen(Relation reln)
+{
+    char *path;
+    int fd;
+    int vfd;
+
+    if (CurFd >= Nfds) {
+       if (_fdvec_ext() == SM_FAIL)
+           return (-1);
+    }
+
+    path = relpath(&(reln->rd_rel->relname.data[0]));
+
+    fd = FileNameOpenFile(path, O_RDWR, 0600);
+
+    /* this should only happen during bootstrap processing */
+    if (fd < 0)
+       fd = FileNameOpenFile(path, O_RDWR|O_CREAT|O_EXCL, 0600);
+
+    Md_fdvec[CurFd].mdfd_vfd = fd;
+    Md_fdvec[CurFd].mdfd_flags = (uint16) 0;
+    Md_fdvec[CurFd].mdfd_chain = (MdfdVec *) NULL;
+    Md_fdvec[CurFd].mdfd_lstbcnt = _mdnblocks(fd, BLCKSZ);
+
+#ifdef DIAGNOSTIC
+    if (Md_fdvec[CurFd].mdfd_lstbcnt > RELSEG_SIZE)
+       elog(FATAL, "segment too big on relopen!");
+#endif
+
+    vfd = CurFd++;
+
+    return (vfd);
+}
+
+/*
+ *  mdclose() -- Close the specified relation.
+ *
+ *     Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
+ */
+int
+mdclose(Relation reln)
+{
+    int fd;
+    MdfdVec *v;
+
+    fd = RelationGetFile(reln);
+
+    for (v = &Md_fdvec[fd]; v != (MdfdVec *) NULL; v = v->mdfd_chain) {
+
+       /* may be closed already */
+       if (v->mdfd_vfd < 0)
+           continue;
+
+       /*
+        *  We sync the file descriptor so that we don't need to reopen it at
+        *  transaction commit to force changes to disk.
+        */
+
+       FileSync(v->mdfd_vfd);
+       FileClose(v->mdfd_vfd);
+
+       /* mark this file descriptor as clean in our private table */
+       v->mdfd_flags &= ~MDFD_DIRTY;
+    }
+
+    return (SM_SUCCESS);
+}
+
+/*
+ *  mdread() -- Read the specified block from a relation.
+ *
+ *     Returns SM_SUCCESS or SM_FAIL.
+ */
+int
+mdread(Relation reln, BlockNumber blocknum, char *buffer)
+{
+    int status;
+    long seekpos;
+    int nbytes;
+    MdfdVec *v;
+
+    v = _mdfd_getseg(reln, blocknum, 0);
+
+    seekpos = (long) (BLCKSZ * (blocknum % RELSEG_SIZE));
+
+#ifdef DIAGNOSTIC
+    if (seekpos >= BLCKSZ * RELSEG_SIZE)
+       elog(FATAL, "seekpos too big!");
+#endif
+
+    if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos) {
+       return (SM_FAIL);
+    }
+
+    status = SM_SUCCESS;
+    if ((nbytes = FileRead(v->mdfd_vfd, buffer, BLCKSZ)) != BLCKSZ) {
+       if (nbytes == 0) {
+         memset(buffer, 0, BLCKSZ); 
+       } else {
+           status = SM_FAIL;
+       }
+    }
+
+    return (status);
+}
+
+/*
+ *  mdwrite() -- Write the supplied block at the appropriate location.
+ *
+ *     Returns SM_SUCCESS or SM_FAIL.
+ */
+int
+mdwrite(Relation reln, BlockNumber blocknum, char *buffer)
+{
+    int status;
+    long seekpos;
+    MdfdVec *v;
+
+    v = _mdfd_getseg(reln, blocknum, 0);
+
+    seekpos = (long) (BLCKSZ * (blocknum % RELSEG_SIZE));
+#ifdef DIAGNOSTIC
+    if (seekpos >= BLCKSZ * RELSEG_SIZE)
+       elog(FATAL, "seekpos too big!");
+#endif
+
+    if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos) {
+       return (SM_FAIL);
+    }
+
+    status = SM_SUCCESS;
+    if (FileWrite(v->mdfd_vfd, buffer, BLCKSZ) != BLCKSZ)
+       status = SM_FAIL;
+
+    v->mdfd_flags |= MDFD_DIRTY;
+
+    return (status);
+}
+
+/*
+ *  mdflush() -- Synchronously write a block to disk.
+ *
+ *     This is exactly like mdwrite(), but doesn't return until the file
+ *     system buffer cache has been flushed.
+ */
+int
+mdflush(Relation reln, BlockNumber blocknum, char *buffer)
+{
+    int status;
+    long seekpos;
+    MdfdVec *v;
+
+    v = _mdfd_getseg(reln, blocknum, 0);
+
+    seekpos = (long) (BLCKSZ * (blocknum % RELSEG_SIZE));
+#ifdef DIAGNOSTIC
+    if (seekpos >= BLCKSZ * RELSEG_SIZE)
+       elog(FATAL, "seekpos too big!");
+#endif
+
+    if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos) {
+       return (SM_FAIL);
+    }
+
+    /* write and sync the block */
+    status = SM_SUCCESS;
+    if (FileWrite(v->mdfd_vfd, buffer, BLCKSZ) != BLCKSZ
+       || FileSync(v->mdfd_vfd) < 0)
+       status = SM_FAIL;
+
+    /*
+     *  By here, the block is written and changes have been forced to stable
+     *  storage.  Mark the descriptor as clean until the next write, so we
+     *  don't sync it again unnecessarily at transaction commit.
+     */
+
+    v->mdfd_flags &= ~MDFD_DIRTY;
+
+    return (status);
+}
+
+/*
+ *  mdblindwrt() -- Write a block to disk blind.
+ *
+ *     We have to be able to do this using only the name and OID of
+ *     the database and relation in which the block belongs.  This
+ *     is a synchronous write.
+ */
+int
+mdblindwrt(char *dbstr,
+          char *relstr,
+          Oid dbid,
+          Oid relid,
+          BlockNumber blkno,
+          char *buffer)
+{
+    int fd;
+    int segno;
+    long seekpos;
+    int status;
+    char *path;
+    int nchars;
+
+    /* be sure we have enough space for the '.segno', if any */
+    segno = blkno / RELSEG_SIZE;
+    if (segno > 0)
+       nchars = 10;
+    else
+       nchars = 0;
+
+    /* construct the path to the file and open it */
+    if (dbid == (Oid) 0) {
+       path = (char *) palloc(strlen(DataDir) + sizeof(NameData) + 2 + nchars);
+       if (segno == 0)
+           sprintf(path, "%s/%.*s", DataDir, NAMEDATALEN, relstr);
+       else
+           sprintf(path, "%s/%.*s.%d", DataDir, NAMEDATALEN, relstr, segno);
+    } else {
+       path = (char *) palloc(strlen(DataDir) + strlen("/base/") + 2 * sizeof(NameData) + 2 + nchars);
+       if (segno == 0)
+           sprintf(path, "%s/base/%.*s/%.*s", DataDir, NAMEDATALEN, 
+                       dbstr, NAMEDATALEN, relstr);
+       else
+           sprintf(path, "%s/base/%.*s/%.*s.%d", DataDir, NAMEDATALEN, dbstr,
+                       NAMEDATALEN, relstr, segno);
+    }
+
+    if ((fd = open(path, O_RDWR, 0600)) < 0)
+       return (SM_FAIL);
+
+    /* seek to the right spot */
+    seekpos = (long) (BLCKSZ * (blkno % RELSEG_SIZE));
+    if (lseek(fd, seekpos, SEEK_SET) != seekpos) {
+       (void) close(fd);
+       return (SM_FAIL);
+    }
+
+    status = SM_SUCCESS;
+
+    /* write and sync the block */
+    if (write(fd, buffer, BLCKSZ) != BLCKSZ || fsync(fd) < 0)
+       status = SM_FAIL;
+
+    if (close(fd) < 0)
+       status = SM_FAIL;
+
+    pfree(path);
+
+    return (status);
+}
+
+/*
+ *  mdnblocks() -- Get the number of blocks stored in a relation.
+ *
+ *     Returns # of blocks or -1 on error.
+ */
+int
+mdnblocks(Relation reln)
+{
+    int fd;
+    MdfdVec *v;
+    int nblocks;
+    int segno;
+
+    fd = RelationGetFile(reln);
+    v = &Md_fdvec[fd];
+
+#ifdef DIAGNOSTIC
+    if (_mdnblocks(v->mdfd_vfd, BLCKSZ) > RELSEG_SIZE)
+       elog(FATAL, "segment too big in getseg!");
+#endif
+
+    segno = 0;
+    for (;;) {
+       if (v->mdfd_lstbcnt == RELSEG_SIZE
+           || (nblocks = _mdnblocks(v->mdfd_vfd, BLCKSZ)) == RELSEG_SIZE) {
+
+           v->mdfd_lstbcnt = RELSEG_SIZE;
+           segno++;
+
+           if (v->mdfd_chain == (MdfdVec *) NULL) {
+               v->mdfd_chain = _mdfd_openseg(reln, segno, O_CREAT);
+               if (v->mdfd_chain == (MdfdVec *) NULL)
+                   elog(WARN, "cannot count blocks for %.16s -- open failed",
+                               RelationGetRelationName(reln));
+           }
+
+           v = v->mdfd_chain;
+       } else {
+           return ((segno * RELSEG_SIZE) + nblocks);
+       }
+    }
+}
+
+/*
+ *  mdcommit() -- Commit a transaction.
+ *
+ *     All changes to magnetic disk relations must be forced to stable
+ *     storage.  This routine makes a pass over the private table of
+ *     file descriptors.  Any descriptors to which we have done writes,
+ *     but not synced, are synced here.
+ *
+ *     Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
+ */
+int
+mdcommit()
+{
+    int i;
+    MdfdVec *v;
+
+    for (i = 0; i < CurFd; i++) {
+       for (v = &Md_fdvec[i]; v != (MdfdVec *) NULL; v = v->mdfd_chain) {
+           if (v->mdfd_flags & MDFD_DIRTY) {
+               if (FileSync(v->mdfd_vfd) < 0)
+                   return (SM_FAIL);
+
+               v->mdfd_flags &= ~MDFD_DIRTY;
+           }
+       }
+    }
+
+    return (SM_SUCCESS);
+}
+
+/*
+ *  mdabort() -- Abort a transaction.
+ *
+ *     Changes need not be forced to disk at transaction abort.  We mark
+ *     all file descriptors as clean here.  Always returns SM_SUCCESS.
+ */
+int
+mdabort()
+{
+    int i;
+    MdfdVec *v;
+
+    for (i = 0; i < CurFd; i++) {
+       for (v = &Md_fdvec[i]; v != (MdfdVec *) NULL; v = v->mdfd_chain) {
+           v->mdfd_flags &= ~MDFD_DIRTY;
+       }
+    }
+
+    return (SM_SUCCESS);
+}
+
+/*
+ *  _fdvec_ext() -- Extend the md file descriptor vector.
+ *
+ *     The file descriptor vector must be large enough to hold at least
+ *     'fd' entries.
+ */
+static
+int _fdvec_ext()
+{
+    MdfdVec *nvec;
+    MemoryContext oldcxt;
+
+    Nfds *= 2;
+
+    oldcxt = MemoryContextSwitchTo(MdCxt);
+
+    nvec = (MdfdVec *) palloc(Nfds * sizeof(MdfdVec));
+    memset(nvec, 0, Nfds * sizeof(MdfdVec)); 
+    memmove(nvec, (char *) Md_fdvec, (Nfds / 2) * sizeof(MdfdVec)); 
+    pfree(Md_fdvec);
+
+    (void) MemoryContextSwitchTo(oldcxt);
+
+    Md_fdvec = nvec;
+
+    return (SM_SUCCESS);
+}
+
+static MdfdVec *
+_mdfd_openseg(Relation reln, int segno, int oflags)
+{
+    MemoryContext oldcxt;
+    MdfdVec *v;
+    int fd;
+    bool dofree;
+    char *path, *fullpath;
+
+    /* be sure we have enough space for the '.segno', if any */
+    path = relpath(RelationGetRelationName(reln)->data);
+
+    dofree = false;
+    if (segno > 0) {
+       dofree = true;
+       fullpath = (char *) palloc(strlen(path) + 12);
+       sprintf(fullpath, "%s.%d", path, segno);
+    } else
+       fullpath = path;
+
+    /* open the file */
+    fd = PathNameOpenFile(fullpath, O_RDWR|oflags, 0600);
+
+    if (dofree)
+       pfree(fullpath);
+
+    if (fd < 0)
+       return ((MdfdVec *) NULL);
+
+    /* allocate an mdfdvec entry for it */
+    oldcxt = MemoryContextSwitchTo(MdCxt);
+    v = (MdfdVec *) palloc(sizeof(MdfdVec));
+    (void) MemoryContextSwitchTo(oldcxt);
+
+    /* fill the entry */
+    v->mdfd_vfd = fd;
+    v->mdfd_flags = (uint16) 0;
+    v->mdfd_chain = (MdfdVec *) NULL;
+    v->mdfd_lstbcnt = _mdnblocks(fd, BLCKSZ);
+
+#ifdef DIAGNOSTIC
+    if (v->mdfd_lstbcnt > RELSEG_SIZE)
+       elog(FATAL, "segment too big on open!");
+#endif
+
+    /* all done */
+    return (v);
+}
+
+static MdfdVec *
+_mdfd_getseg(Relation reln, int blkno, int oflag)
+{
+    MdfdVec *v;
+    int segno;
+    int fd;
+    int i;
+
+    fd = RelationGetFile(reln);
+    if (fd < 0) {
+       if ((fd = mdopen(reln)) < 0)
+           elog(WARN, "cannot open relation %.16s",
+                       RelationGetRelationName(reln));
+       reln->rd_fd = fd;
+    }
+
+    for (v = &Md_fdvec[fd], segno = blkno / RELSEG_SIZE, i = 1;
+        segno > 0;
+        i++, segno--) {
+
+       if (v->mdfd_chain == (MdfdVec *) NULL) {
+           v->mdfd_chain = _mdfd_openseg(reln, i, oflag);
+
+           if (v->mdfd_chain == (MdfdVec *) NULL)
+               elog(WARN, "cannot open segment %d of relation %.16s",
+                           i, RelationGetRelationName(reln));
+       }
+       v = v->mdfd_chain;
+    }
+
+    return (v);
+}
+
+static BlockNumber
+_mdnblocks(File file, Size blcksz)
+{
+    long len;
+    
+    len = FileSeek(file, 0L, SEEK_END) - 1;
+    return((BlockNumber)((len < 0) ? 0 : 1 + len / blcksz));
+}
diff --git a/src/backend/storage/smgr/mm.c b/src/backend/storage/smgr/mm.c
new file mode 100644 (file)
index 0000000..1a49b00
--- /dev/null
@@ -0,0 +1,586 @@
+/*-------------------------------------------------------------------------
+ *
+ * mm.c--
+ *    main memory storage manager
+ *
+ *    This code manages relations that reside in (presumably stable)
+ *    main memory.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#ifdef MAIN_MEMORY
+
+#include <math.h>
+#include "machine.h"
+#include "storage/ipc.h"
+#include "storage/smgr.h"      /* where the declarations go */
+#include "storage/block.h"
+#include "storage/shmem.h"
+#include "storage/spin.h"
+
+#include "utils/hsearch.h"
+#include "utils/rel.h"
+#include "utils/elog.h"
+#include "utils/memutils.h"
+
+/*
+ *  MMCacheTag -- Unique triplet for blocks stored by the main memory
+ *               storage manager.
+ */
+
+typedef struct MMCacheTag {
+    Oid                        mmct_dbid;
+    Oid                        mmct_relid;
+    BlockNumber                mmct_blkno;
+} MMCacheTag;
+
+/*
+ *  Shared-memory hash table for main memory relations contains
+ *  entries of this form.
+ */
+
+typedef struct MMHashEntry {
+    MMCacheTag         mmhe_tag;
+    int                        mmhe_bufno;
+} MMHashEntry;
+
+/*
+ * MMRelTag -- Unique identifier for each relation that is stored in the
+ *                 main-memory storage manager.
+ */
+
+typedef struct MMRelTag {
+    Oid                mmrt_dbid;
+    Oid                mmrt_relid;
+} MMRelTag;
+
+/*
+ *  Shared-memory hash table for # blocks in main memory relations contains
+ *  entries of this form.
+ */
+
+typedef struct MMRelHashEntry {
+    MMRelTag           mmrhe_tag;
+    int                        mmrhe_nblocks;
+} MMRelHashEntry;
+
+#define MMNBUFFERS     10
+#define MMNRELATIONS   2
+
+SPINLOCK       MMCacheLock;
+extern bool    IsPostmaster;
+extern Oid     MyDatabaseId;
+
+static int             *MMCurTop;
+static int             *MMCurRelno;
+static MMCacheTag      *MMBlockTags;
+static char            *MMBlockCache;
+static HTAB            *MMCacheHT;
+static HTAB            *MMRelCacheHT;
+
+int
+mminit()
+{
+    char *mmcacheblk;
+    int mmsize = 0;
+    bool found;
+    HASHCTL info;
+
+    SpinAcquire(MMCacheLock);
+
+    mmsize += MAXALIGN(BLCKSZ * MMNBUFFERS);
+    mmsize += MAXALIGN(sizeof(*MMCurTop));
+    mmsize += MAXALIGN(sizeof(*MMCurRelno));
+    mmsize += MAXALIGN((MMNBUFFERS * sizeof(MMCacheTag)));
+    mmcacheblk = (char *) ShmemInitStruct("Main memory smgr", mmsize, &found);
+
+    if (mmcacheblk == (char *) NULL) {
+       SpinRelease(MMCacheLock);
+       return (SM_FAIL);
+    }
+
+    info.keysize = sizeof(MMCacheTag);
+    info.datasize = sizeof(int);
+    info.hash = tag_hash;
+
+    MMCacheHT = (HTAB *) ShmemInitHash("Main memory store HT",
+                                       MMNBUFFERS, MMNBUFFERS,
+                                       &info, (HASH_ELEM|HASH_FUNCTION));
+
+    if (MMCacheHT == (HTAB *) NULL) {
+       SpinRelease(MMCacheLock);
+       return (SM_FAIL);
+    }
+
+    info.keysize = sizeof(MMRelTag);
+    info.datasize = sizeof(int);
+    info.hash = tag_hash;
+
+    MMRelCacheHT = (HTAB *) ShmemInitHash("Main memory rel HT",
+                                         MMNRELATIONS, MMNRELATIONS,
+                                         &info, (HASH_ELEM|HASH_FUNCTION));
+
+    if (MMRelCacheHT == (HTAB *) NULL) {
+       SpinRelease(MMCacheLock);
+       return (SM_FAIL);
+    }
+
+    if (IsPostmaster) {
+       memset(mmcacheblk, 0, mmsize);
+       SpinRelease(MMCacheLock);
+       return (SM_SUCCESS);
+    }
+
+    SpinRelease(MMCacheLock);
+
+    MMCurTop = (int *) mmcacheblk;
+    mmcacheblk += sizeof(int);
+    MMCurRelno = (int *) mmcacheblk;
+    mmcacheblk += sizeof(int);
+    MMBlockTags = (MMCacheTag *) mmcacheblk;
+    mmcacheblk += (MMNBUFFERS * sizeof(MMCacheTag));
+    MMBlockCache = mmcacheblk;
+
+    return (SM_SUCCESS);
+}
+
+int
+mmshutdown()
+{
+    return (SM_SUCCESS);
+}
+
+int
+mmcreate(Relation reln)
+{
+    MMRelHashEntry *entry;
+    bool found;
+    MMRelTag tag;
+
+    SpinAcquire(MMCacheLock);
+
+    if (*MMCurRelno == MMNRELATIONS) {
+       SpinRelease(MMCacheLock);
+       return (SM_FAIL);
+    }
+
+    (*MMCurRelno)++;
+
+    tag.mmrt_relid = reln->rd_id;
+    if (reln->rd_rel->relisshared)
+       tag.mmrt_dbid = (Oid) 0;
+    else
+       tag.mmrt_dbid = MyDatabaseId;
+
+    entry = (MMRelHashEntry *) hash_search(MMRelCacheHT,
+                                          (char *) &tag, HASH_ENTER, &found);
+
+    if (entry == (MMRelHashEntry *) NULL) {
+       SpinRelease(MMCacheLock);
+       elog(FATAL, "main memory storage mgr rel cache hash table corrupt");
+    }
+
+    if (found) {
+       /* already exists */
+       SpinRelease(MMCacheLock);
+       return (SM_FAIL);
+    }
+
+    entry->mmrhe_nblocks = 0;
+
+    SpinRelease(MMCacheLock);
+
+    return (SM_SUCCESS);
+}
+
+/*
+ *  mmunlink() -- Unlink a relation.
+ */
+int
+mmunlink(Relation reln)
+{
+    int i;
+    Oid reldbid;
+    MMHashEntry *entry;
+    MMRelHashEntry *rentry;
+    bool found;
+    MMRelTag rtag;
+
+    if (reln->rd_rel->relisshared)
+       reldbid = (Oid) 0;
+    else
+       reldbid = MyDatabaseId;
+
+    SpinAcquire(MMCacheLock);
+
+    for (i = 0; i < MMNBUFFERS; i++) {
+       if (MMBlockTags[i].mmct_dbid == reldbid
+           && MMBlockTags[i].mmct_relid == reln->rd_id) {
+           entry = (MMHashEntry *) hash_search(MMCacheHT,
+                                               (char *) &MMBlockTags[i],
+                                                HASH_REMOVE, &found);
+           if (entry == (MMHashEntry *) NULL || !found) {
+               SpinRelease(MMCacheLock);
+               elog(FATAL, "mmunlink: cache hash table corrupted");
+           }
+           MMBlockTags[i].mmct_dbid = (Oid) 0;
+           MMBlockTags[i].mmct_relid = (Oid) 0;
+           MMBlockTags[i].mmct_blkno = (BlockNumber) 0;
+       }
+    }
+    rtag.mmrt_dbid = reldbid;
+    rtag.mmrt_relid = reln->rd_id;
+
+    rentry = (MMRelHashEntry *) hash_search(MMRelCacheHT, (char *) &rtag,
+                                           HASH_REMOVE, &found);
+
+    if (rentry == (MMRelHashEntry *) NULL || !found) {
+       SpinRelease(MMCacheLock);
+       elog(FATAL, "mmunlink: rel cache hash table corrupted");
+    }
+
+    (*MMCurRelno)--;
+
+    SpinRelease(MMCacheLock);
+    return 1;
+}
+
+/*
+ *  mmextend() -- Add a block to the specified relation.
+ *
+ *     This routine returns SM_FAIL or SM_SUCCESS, with errno set as
+ *     appropriate.
+ */
+int
+mmextend(Relation reln, char *buffer)
+{
+    MMRelHashEntry *rentry;
+    MMHashEntry *entry;
+    int i;
+    Oid reldbid;
+    int offset;
+    bool found;
+    MMRelTag rtag;
+    MMCacheTag tag;
+
+    if (reln->rd_rel->relisshared)
+       reldbid = (Oid) 0;
+    else
+       reldbid = MyDatabaseId;
+
+    tag.mmct_dbid = rtag.mmrt_dbid = reldbid;
+    tag.mmct_relid = rtag.mmrt_relid = reln->rd_id;
+
+    SpinAcquire(MMCacheLock);
+
+    if (*MMCurTop == MMNBUFFERS) {
+       for (i = 0; i < MMNBUFFERS; i++) {
+           if (MMBlockTags[i].mmct_dbid == 0 &&
+               MMBlockTags[i].mmct_relid == 0)
+               break;
+       }
+       if (i == MMNBUFFERS) {
+           SpinRelease(MMCacheLock);
+           return (SM_FAIL);
+       }
+    } else {
+       i = *MMCurTop;
+       (*MMCurTop)++;
+    }
+
+    rentry = (MMRelHashEntry *) hash_search(MMRelCacheHT, (char *) &rtag,
+                                           HASH_FIND, &found);
+    if (rentry == (MMRelHashEntry *) NULL || !found) {
+       SpinRelease(MMCacheLock);
+       elog(FATAL, "mmextend: rel cache hash table corrupt");
+    }
+
+    tag.mmct_blkno = rentry->mmrhe_nblocks;
+
+    entry = (MMHashEntry *) hash_search(MMCacheHT, (char *) &tag,
+                                       HASH_ENTER, &found);
+    if (entry == (MMHashEntry *) NULL || found) {
+       SpinRelease(MMCacheLock);
+       elog(FATAL, "mmextend: cache hash table corrupt");
+    }
+
+    entry->mmhe_bufno = i;
+    MMBlockTags[i].mmct_dbid = reldbid;
+    MMBlockTags[i].mmct_relid = reln->rd_id;
+    MMBlockTags[i].mmct_blkno = rentry->mmrhe_nblocks;
+
+    /* page numbers are zero-based, so we increment this at the end */
+    (rentry->mmrhe_nblocks)++;
+
+    /* write the extended page */
+    offset = (i * BLCKSZ);
+    memmove(&(MMBlockCache[offset]), buffer, BLCKSZ);
+
+    SpinRelease(MMCacheLock);
+
+    return (SM_SUCCESS);
+}
+
+/*
+ *  mmopen() -- Open the specified relation.
+ */
+int
+mmopen(Relation reln)
+{
+    /* automatically successful */
+    return (0);
+}
+
+/*
+ *  mmclose() -- Close the specified relation.
+ *
+ *     Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
+ */
+int
+mmclose(Relation reln)
+{
+    /* automatically successful */
+    return (SM_SUCCESS);
+}
+
+/*
+ *  mmread() -- Read the specified block from a relation.
+ *
+ *     Returns SM_SUCCESS or SM_FAIL.
+ */
+int
+mmread(Relation reln, BlockNumber blocknum, char *buffer)
+{
+    MMHashEntry *entry;
+    bool found;
+    int offset;
+    MMCacheTag tag;
+
+    if (reln->rd_rel->relisshared)
+       tag.mmct_dbid = (Oid) 0;
+    else
+       tag.mmct_dbid = MyDatabaseId;
+
+    tag.mmct_relid = reln->rd_id;
+    tag.mmct_blkno = blocknum;
+
+    SpinAcquire(MMCacheLock);
+    entry = (MMHashEntry *) hash_search(MMCacheHT, (char *) &tag,
+                                       HASH_FIND, &found);
+
+    if (entry == (MMHashEntry *) NULL) {
+       SpinRelease(MMCacheLock);
+       elog(FATAL, "mmread: hash table corrupt");
+    }
+
+    if (!found) {
+       /* reading nonexistent pages is defined to fill them with zeroes */
+       SpinRelease(MMCacheLock);
+       memset(buffer, 0, BLCKSZ);
+       return (SM_SUCCESS);
+    }
+
+    offset = (entry->mmhe_bufno * BLCKSZ);
+    memmove(buffer, &MMBlockCache[offset], BLCKSZ);
+
+    SpinRelease(MMCacheLock);
+
+    return (SM_SUCCESS);
+}
+
+/*
+ *  mmwrite() -- Write the supplied block at the appropriate location.
+ *
+ *     Returns SM_SUCCESS or SM_FAIL.
+ */
+int
+mmwrite(Relation reln, BlockNumber blocknum, char *buffer)
+{
+    MMHashEntry *entry;
+    bool found;
+    int offset;
+    MMCacheTag tag;
+
+    if (reln->rd_rel->relisshared)
+       tag.mmct_dbid = (Oid) 0;
+    else
+       tag.mmct_dbid = MyDatabaseId;
+
+    tag.mmct_relid = reln->rd_id;
+    tag.mmct_blkno = blocknum;
+
+    SpinAcquire(MMCacheLock);
+    entry = (MMHashEntry *) hash_search(MMCacheHT, (char *) &tag,
+                                       HASH_FIND, &found);
+
+    if (entry == (MMHashEntry *) NULL) {
+       SpinRelease(MMCacheLock);
+       elog(FATAL, "mmread: hash table corrupt");
+    }
+
+    if (!found) {
+       SpinRelease(MMCacheLock);
+       elog(FATAL, "mmwrite: hash table missing requested page");
+    }
+
+    offset = (entry->mmhe_bufno * BLCKSZ);
+    memmove(&MMBlockCache[offset], buffer, BLCKSZ);
+
+    SpinRelease(MMCacheLock);
+
+    return (SM_SUCCESS);
+}
+
+/*
+ *  mmflush() -- Synchronously write a block to stable storage.
+ *
+ *     For main-memory relations, this is exactly equivalent to mmwrite().
+ */
+int
+mmflush(Relation reln, BlockNumber blocknum, char *buffer)
+{
+    return (mmwrite(reln, blocknum, buffer));
+}
+
+/*
+ *  mmblindwrt() -- Write a block to stable storage blind.
+ *
+ *     We have to be able to do this using only the name and OID of
+ *     the database and relation in which the block belongs.
+ */
+int
+mmblindwrt(char *dbstr,
+          char *relstr,
+          Oid dbid,
+          Oid relid,
+          BlockNumber blkno,
+          char *buffer)
+{
+    return (SM_FAIL);
+}
+
+/*
+ *  mmnblocks() -- Get the number of blocks stored in a relation.
+ *
+ *     Returns # of blocks or -1 on error.
+ */
+int
+mmnblocks(Relation reln)
+{
+    MMRelTag rtag;
+    MMRelHashEntry *rentry;
+    bool found;
+    int nblocks;
+
+    if (reln->rd_rel->relisshared)
+       rtag.mmrt_dbid = (Oid) 0;
+    else
+       rtag.mmrt_dbid = MyDatabaseId;
+
+    rtag.mmrt_relid = reln->rd_id;
+
+    SpinAcquire(MMCacheLock);
+
+    rentry = (MMRelHashEntry *) hash_search(MMRelCacheHT, (char *) &rtag,
+                                           HASH_FIND, &found);
+
+    if (rentry == (MMRelHashEntry *) NULL) {
+       SpinRelease(MMCacheLock);
+       elog(FATAL, "mmnblocks: rel cache hash table corrupt");
+    }
+
+    if (found)
+       nblocks = rentry->mmrhe_nblocks;
+    else
+       nblocks = -1;
+
+    SpinRelease(MMCacheLock);
+
+    return (nblocks);
+}
+
+/*
+ *  mmcommit() -- Commit a transaction.
+ *
+ *     Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
+ */
+int
+mmcommit()
+{
+    return (SM_SUCCESS);
+}
+
+/*
+ *  mmabort() -- Abort a transaction.
+ */
+
+int
+mmabort()
+{
+    return (SM_SUCCESS);
+}
+
+/*
+ *  MMShmemSize() -- Declare amount of shared memory we require.
+ *
+ *     The shared memory initialization code creates a block of shared
+ *     memory exactly big enough to hold all the structures it needs to.
+ *     This routine declares how much space the main memory storage
+ *     manager will use.
+ */
+int
+MMShmemSize()
+{
+    int size = 0;
+    int nbuckets;
+    int nsegs;
+    int tmp;
+
+    /*
+     *  first compute space occupied by the (dbid,relid,blkno) hash table
+     */
+
+    nbuckets = 1 << (int)my_log2((MMNBUFFERS - 1) / DEF_FFACTOR + 1);
+    nsegs = 1 << (int)my_log2((nbuckets - 1) / DEF_SEGSIZE + 1);
+    
+    size += MAXALIGN(my_log2(MMNBUFFERS) * sizeof(void *));
+    size += MAXALIGN(sizeof(HHDR));
+    size += nsegs * MAXALIGN(DEF_SEGSIZE * sizeof(SEGMENT));
+    tmp = (int)ceil((double)MMNBUFFERS/BUCKET_ALLOC_INCR);
+    size += tmp * BUCKET_ALLOC_INCR *
+       (MAXALIGN(sizeof(BUCKET_INDEX)) +
+        MAXALIGN(sizeof(MMHashEntry)));        /* contains hash key */
+
+    /*
+     *  now do the same for the rel hash table
+     */
+
+    size += MAXALIGN(my_log2(MMNRELATIONS) * sizeof(void *));
+    size += MAXALIGN(sizeof(HHDR));
+    size += nsegs * MAXALIGN(DEF_SEGSIZE * sizeof(SEGMENT));
+    tmp = (int)ceil((double)MMNRELATIONS/BUCKET_ALLOC_INCR);
+    size += tmp * BUCKET_ALLOC_INCR *
+       (MAXALIGN(sizeof(BUCKET_INDEX)) +
+        MAXALIGN(sizeof(MMRelHashEntry)));     /* contains hash key */
+
+    /*
+     *  finally, add in the memory block we use directly
+     */
+
+    size += MAXALIGN(BLCKSZ * MMNBUFFERS);
+    size += MAXALIGN(sizeof(*MMCurTop));
+    size += MAXALIGN(sizeof(*MMCurRelno));
+    size += MAXALIGN(MMNBUFFERS * sizeof(MMCacheTag));
+
+    return (size);
+}
+
+#endif /* MAIN_MEMORY */
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
new file mode 100644 (file)
index 0000000..68c074a
--- /dev/null
@@ -0,0 +1,371 @@
+/*-------------------------------------------------------------------------
+ *
+ * smgr.c--
+ *    public interface routines to storage manager switch.
+ *
+ *    All file system operations in POSTGRES dispatch through these
+ *    routines.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+
+#include "machine.h"
+#include "storage/ipc.h"
+#include "storage/smgr.h"
+#include "storage/block.h"
+#include "utils/rel.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+typedef struct f_smgr {
+    int                (*smgr_init)();         /* may be NULL */
+    int                (*smgr_shutdown)();     /* may be NULL */
+    int                (*smgr_create)();
+    int                (*smgr_unlink)();
+    int                (*smgr_extend)();
+    int                (*smgr_open)();
+    int                (*smgr_close)();
+    int                (*smgr_read)();
+    int                (*smgr_write)();
+    int                (*smgr_flush)();
+    int                (*smgr_blindwrt)();
+    int                (*smgr_nblocks)();
+    int                (*smgr_commit)();       /* may be NULL */
+    int                (*smgr_abort)();        /* may be NULL */
+} f_smgr;
+
+/*
+ *  The weird placement of commas in this init block is to keep the compiler
+ *  happy, regardless of what storage managers we have (or don't have).
+ */
+
+static f_smgr smgrsw[] = {
+
+    /* magnetic disk */
+    { mdinit, NULL, mdcreate, mdunlink, mdextend, mdopen, mdclose,
+      mdread, mdwrite, mdflush, mdblindwrt, mdnblocks, mdcommit, mdabort },
+
+#ifdef MAIN_MEMORY
+    /* main memory */
+    { mminit, mmshutdown, mmcreate, mmunlink, mmextend, mmopen, mmclose,
+      mmread, mmwrite, mmflush, mmblindwrt, mmnblocks, mmcommit, mmabort },
+
+#endif /* MAIN_MEMORY */
+};
+
+/*
+ *  This array records which storage managers are write-once, and which
+ *  support overwrite.  A 'true' entry means that the storage manager is
+ *  write-once.  In the best of all possible worlds, there would be no
+ *  write-once storage managers.
+ */
+
+static bool smgrwo[] = {
+    false,             /* magnetic disk */
+#ifdef MAIN_MEMORY
+    false,             /* main memory*/
+#endif /* MAIN_MEMORY */
+};
+static int NSmgr = lengthof(smgrsw);
+
+/*
+ *  smgrinit(), smgrshutdown() -- Initialize or shut down all storage
+ *                               managers.
+ *
+ */
+int
+smgrinit()
+{
+    int i;
+    extern char *smgrout();
+
+    for (i = 0; i < NSmgr; i++) {
+       if (smgrsw[i].smgr_init) {
+           if ((*(smgrsw[i].smgr_init))() == SM_FAIL)
+               elog(FATAL, "initialization failed on %s", smgrout(i));
+       }
+    }
+
+    /* register the shutdown proc */
+    on_exitpg(smgrshutdown, 0);
+
+    return (SM_SUCCESS);
+}
+
+void
+smgrshutdown(int dummy)
+{
+    int i;
+    extern char *smgrout();
+
+    for (i = 0; i < NSmgr; i++) {
+       if (smgrsw[i].smgr_shutdown) {
+           if ((*(smgrsw[i].smgr_shutdown))() == SM_FAIL)
+               elog(FATAL, "shutdown failed on %s", smgrout(i));
+       }
+    }
+}
+
+/*
+ *  smgrcreate() -- Create a new relation.
+ *
+ *     This routine takes a reldesc, creates the relation on the appropriate
+ *     device, and returns a file descriptor for it.
+ */
+int
+smgrcreate(int16 which, Relation reln)
+{
+    int fd;
+
+    if ((fd = (*(smgrsw[which].smgr_create))(reln)) < 0)
+       elog(WARN, "cannot open %.*s",
+            NAMEDATALEN, &(reln->rd_rel->relname.data[0]));
+
+    return (fd);
+}
+
+/*
+ *  smgrunlink() -- Unlink a relation.
+ *
+ *     The relation is removed from the store.
+ */
+int
+smgrunlink(int16 which, Relation reln)
+{
+    int status;
+
+    if ((status = (*(smgrsw[which].smgr_unlink))(reln)) == SM_FAIL)
+       elog(WARN, "cannot unlink %.*s",
+            NAMEDATALEN, &(reln->rd_rel->relname.data[0]));
+
+    return (status);
+}
+
+/*
+ *  smgrextend() -- Add a new block to a file.
+ *
+ *     Returns SM_SUCCESS on success; aborts the current transaction on
+ *     failure.
+ */
+int
+smgrextend(int16 which, Relation reln, char *buffer)
+{
+    int status;
+
+    status = (*(smgrsw[which].smgr_extend))(reln, buffer);
+
+    if (status == SM_FAIL)
+       elog(WARN, "%.*s: cannot extend",
+            NAMEDATALEN, &(reln->rd_rel->relname.data[0]));
+
+    return (status);
+}
+
+/*
+ *  smgropen() -- Open a relation using a particular storage manager.
+ *
+ *     Returns the fd for the open relation on success, aborts the
+ *     transaction on failure.
+ */
+int
+smgropen(int16 which, Relation reln)
+{
+    int fd;
+
+    if ((fd = (*(smgrsw[which].smgr_open))(reln)) < 0)
+       elog(WARN, "cannot open %.*s",
+            NAMEDATALEN, &(reln->rd_rel->relname.data[0]));
+
+    return (fd);
+}
+
+/*
+ *  smgrclose() -- Close a relation.
+ *
+ *     Returns SM_SUCCESS on success, aborts on failure.
+ */
+int
+smgrclose(int16 which, Relation reln)
+{
+    if ((*(smgrsw[which].smgr_close))(reln) == SM_FAIL)
+       elog(WARN, "cannot close %.*s",
+            NAMEDATALEN, &(reln->rd_rel->relname.data[0]));
+
+    return (SM_SUCCESS);
+}
+
+/*
+ *  smgrread() -- read a particular block from a relation into the supplied
+ *               buffer.
+ *
+ *     This routine is called from the buffer manager in order to
+ *     instantiate pages in the shared buffer cache.  All storage managers
+ *     return pages in the format that POSTGRES expects.  This routine
+ *     dispatches the read.  On success, it returns SM_SUCCESS.  On failure,
+ *     the current transaction is aborted.
+ */
+int
+smgrread(int16 which, Relation reln, BlockNumber blocknum, char *buffer)
+{
+    int status;
+
+    status = (*(smgrsw[which].smgr_read))(reln, blocknum, buffer);
+
+    if (status == SM_FAIL)
+       elog(WARN, "cannot read block %d of %.*s",
+            blocknum, NAMEDATALEN, &(reln->rd_rel->relname.data[0]));
+
+    return (status);
+}
+
+/*
+ *  smgrwrite() -- Write the supplied buffer out.
+ *
+ *     This is not a synchronous write -- the interface for that is
+ *     smgrflush().  The buffer is written out via the appropriate
+ *     storage manager.  This routine returns SM_SUCCESS or aborts
+ *     the current transaction.
+ */
+int
+smgrwrite(int16 which, Relation reln, BlockNumber blocknum, char *buffer)
+{
+    int status;
+
+    status = (*(smgrsw[which].smgr_write))(reln, blocknum, buffer);
+
+    if (status == SM_FAIL)
+       elog(WARN, "cannot write block %d of %.*s",
+            blocknum, NAMEDATALEN, &(reln->rd_rel->relname.data[0]));
+
+    return (status);
+}
+
+/*
+ *  smgrflush() -- A synchronous smgrwrite().
+ */
+int
+smgrflush(int16 which, Relation reln, BlockNumber blocknum, char *buffer)
+{
+    int status;
+
+    status = (*(smgrsw[which].smgr_flush))(reln, blocknum, buffer);
+
+    if (status == SM_FAIL)
+       elog(WARN, "cannot flush block %d of %.*s to stable store",
+            blocknum, NAMEDATALEN, &(reln->rd_rel->relname.data[0]));
+
+    return (status);
+}
+
+/*
+ *  smgrblindwrt() -- Write a page out blind.
+ *
+ *     In some cases, we may find a page in the buffer cache that we
+ *     can't make a reldesc for.  This happens, for example, when we
+ *     want to reuse a dirty page that was written by a transaction
+ *     that has not yet committed, which created a new relation.  In
+ *     this case, the buffer manager will call smgrblindwrt() with
+ *     the name and OID of the database and the relation to which the
+ *     buffer belongs.  Every storage manager must be able to force
+ *     this page down to stable storage in this circumstance.
+ */
+int
+smgrblindwrt(int16 which,
+            char *dbname,
+            char *relname,
+            Oid dbid,
+            Oid relid,
+            BlockNumber blkno,
+            char *buffer)
+{
+    char *dbstr;
+    char *relstr;
+    int status;
+
+    dbstr = pstrdup(dbname);
+    relstr = pstrdup(relname);
+
+    status = (*(smgrsw[which].smgr_blindwrt))(dbstr, relstr, dbid, relid,
+                                             blkno, buffer);
+
+    if (status == SM_FAIL)
+       elog(WARN, "cannot write block %d of %s [%s] blind",
+            blkno, relstr, dbstr);
+
+    pfree(dbstr);
+    pfree(relstr);
+
+    return (status);
+}
+
+/*
+ *  smgrnblocks() -- Calculate the number of POSTGRES blocks in the
+ *                  supplied relation.
+ *
+ *     Returns the number of blocks on success, aborts the current
+ *     transaction on failure.
+ */
+int
+smgrnblocks(int16 which, Relation reln)
+{
+    int nblocks;
+
+    if ((nblocks = (*(smgrsw[which].smgr_nblocks))(reln)) < 0)
+       elog(WARN, "cannot count blocks for %.*s",
+            NAMEDATALEN, &(reln->rd_rel->relname.data[0]));
+
+    return (nblocks);
+}
+
+/*
+ *  smgrcommit(), smgrabort() -- Commit or abort changes made during the
+ *                              current transaction.
+ */
+int
+smgrcommit()
+{
+    int i;
+    extern char *smgrout();
+
+    for (i = 0; i < NSmgr; i++) {
+       if (smgrsw[i].smgr_commit) {
+           if ((*(smgrsw[i].smgr_commit))() == SM_FAIL)
+               elog(FATAL, "transaction commit failed on %s", smgrout(i));
+       }
+    }
+
+    return (SM_SUCCESS);
+}
+
+int
+smgrabort()
+{
+    int i;
+    extern char *smgrout();
+
+    for (i = 0; i < NSmgr; i++) {
+       if (smgrsw[i].smgr_abort) {
+           if ((*(smgrsw[i].smgr_abort))() == SM_FAIL)
+               elog(FATAL, "transaction abort failed on %s", smgrout(i));
+       }
+    }
+
+    return (SM_SUCCESS);
+}
+
+bool
+smgriswo(int16 smgrno)
+{
+    if (smgrno < 0 || smgrno >= NSmgr)
+       elog(WARN, "illegal storage manager number %d", smgrno);
+
+    return (smgrwo[smgrno]);
+}
diff --git a/src/backend/storage/smgr/smgrtype.c b/src/backend/storage/smgr/smgrtype.c
new file mode 100644 (file)
index 0000000..2ded5a0
--- /dev/null
@@ -0,0 +1,82 @@
+/*-------------------------------------------------------------------------
+ *
+ * smgrtype.c--
+ *    storage manager type
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+
+#include "utils/builtins.h"    /* where the declarations go */
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "storage/smgr.h"
+
+typedef struct smgrid {
+    char *smgr_name;
+} smgrid;
+
+/*
+ *  StorageManager[] -- List of defined storage managers.
+ *
+ *     The weird comma placement is to keep compilers happy no matter
+ *     which of these is (or is not) defined.
+ */
+
+static smgrid StorageManager[] = {
+       {"magnetic disk"},
+#ifdef MAIN_MEMORY
+       {"main memory"}
+#endif /* MAIN_MEMORY */
+};
+
+static int NStorageManagers = lengthof(StorageManager);
+
+int2
+smgrin(char *s)
+{
+    int i;
+
+    for (i = 0; i < NStorageManagers; i++) {
+       if (strcmp(s, StorageManager[i].smgr_name) == 0)
+           return((int2) i);
+    }
+    elog(WARN, "smgrin: illegal storage manager name %s", s);
+    return 0;
+}
+
+char *
+smgrout(int2 i)
+{
+    char *s;
+
+    if (i >= NStorageManagers || i < 0)
+       elog(WARN, "Illegal storage manager id %d", i);
+
+    s = (char *) palloc(strlen(StorageManager[i].smgr_name) + 1);
+    strcpy(s, StorageManager[i].smgr_name);
+    return (s);
+}
+
+bool
+smgreq(int2 a, int2 b)
+{
+    if (a == b)
+       return (true);
+    return (false);
+}
+
+bool
+smgrne(int2 a, int2 b)
+{
+    if (a == b)
+       return (false);
+    return (true);
+}
diff --git a/src/backend/storage/spin.h b/src/backend/storage/spin.h
new file mode 100644 (file)
index 0000000..8c1ea2f
--- /dev/null
@@ -0,0 +1,38 @@
+/*-------------------------------------------------------------------------
+ *
+ * spin.h--
+ *    synchronization routines
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        SPIN_H
+#define SPIN_H
+
+#include "ipc.h"
+
+/* 
+ * two implementations of spin locks
+ *
+ * sequent, sparc, sun3: real spin locks. uses a TAS instruction; see
+ * src/storage/ipc/s_lock.c for details.
+ *
+ * default: fake spin locks using semaphores.  see spin.c
+ *
+ */
+
+typedef int SPINLOCK;
+
+extern bool CreateSpinlocks(IPCKey key);
+extern bool AttachSpinLocks(IPCKey key);
+extern bool InitSpinLocks(int init, IPCKey key);
+
+extern void SpinAcquire(SPINLOCK lock);
+extern void SpinRelease(SPINLOCK lock);
+extern bool SpinIsLocked(SPINLOCK lock);
+
+#endif /* SPIN_H */
diff --git a/src/backend/tcop/Makefile.inc b/src/backend/tcop/Makefile.inc
new file mode 100644 (file)
index 0000000..d940469
--- /dev/null
@@ -0,0 +1,18 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the traffic cop module
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+VPATH:= $(VPATH):$(CURDIR)/tcop
+
+SRCS_TCOP= aclchk.c dest.c fastpath.c postgres.c pquery.c utility.c
+
+HEADERS+= dest.h fastpath.h pquery.h tcopdebug.h tcopprot.h utility.h
diff --git a/src/backend/tcop/aclchk.c b/src/backend/tcop/aclchk.c
new file mode 100644 (file)
index 0000000..fb53b94
--- /dev/null
@@ -0,0 +1,555 @@
+/*-------------------------------------------------------------------------
+ *
+ * aclchk.c--
+ *    Routines to check access control permissions.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    See acl.h.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "utils/acl.h"         /* where declarations for this file goes */
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/tupmacs.h"
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "catalog/indexing.h"
+#include "catalog/catalog.h"
+#include "catalog/catname.h"
+#include "catalog/pg_group.h"
+#include "catalog/pg_user.h"
+#include "utils/syscache.h"
+#include "parser/catalog_utils.h"
+#include "fmgr.h"
+
+/*
+ * Enable use of user relations in place of real system catalogs.
+ */
+/*#define ACLDEBUG*/
+
+#ifdef ACLDEBUG
+/*
+ * Fool the code below into thinking that "pgacls" is pg_class.
+ * relname and relowner are in the same place, happily.
+ */
+#undef Anum_pg_class_relacl
+#define        Anum_pg_class_relacl            3
+#undef Natts_pg_class
+#define        Natts_pg_class                  3
+#undef Name_pg_class
+#define        Name_pg_class                   "pgacls"
+#undef Name_pg_group
+#define        Name_pg_group                   "pggroup"
+#endif
+
+#ifdef ACLDEBUG_TRACE
+static
+dumpacl(Acl *acl)
+{
+    register unsigned i;
+    AclItem *aip;
+       
+    elog(DEBUG, "acl size = %d, # acls = %d",
+        ACL_SIZE(acl), ACL_NUM(acl));
+    aip = (AclItem *) ACL_DAT(acl);
+    for (i = 0; i < ACL_NUM(acl); ++i)
+       elog(DEBUG, "   acl[%d]: %s", i, aclitemout(aip + i));
+}
+#endif
+
+/*
+ * 
+ */
+void
+ChangeAcl(char *relname,       
+         AclItem *mod_aip,
+         unsigned modechg)
+{
+    register unsigned i;
+    Acl *old_acl = (Acl *) NULL, *new_acl;
+    Relation relation;
+    static ScanKeyData relkey[1] = {
+       { 0, Anum_pg_class_relname, NameEqualRegProcedure }
+    };
+    HeapScanDesc hsdp;
+    HeapTuple htp;
+    Buffer buffer;
+    Datum values[Natts_pg_class];
+    char nulls[Natts_pg_class];
+    char replaces[Natts_pg_class];
+    ItemPointerData tmp_ipd;
+    Relation idescs[Num_pg_class_indices];
+    int free_old_acl = 0;
+
+    /*
+     * Find the pg_class tuple matching 'relname' and extract the ACL.
+     * If there's no ACL, create a default using the pg_class.relowner
+     * field.
+     *
+     * We can't use the syscache here, since we need to do a heap_replace
+     * on the tuple we find.  Feh.
+     */
+    relation = heap_openr(RelationRelationName);
+    if (!RelationIsValid(relation))
+       elog(WARN, "ChangeAcl: could not open '%s'??",
+            RelationRelationName);
+    fmgr_info(NameEqualRegProcedure, &relkey[0].sk_func, &relkey[0].sk_nargs);
+    relkey[0].sk_argument = NameGetDatum(relname);
+    hsdp = heap_beginscan(relation,
+                         0,
+                         NowTimeQual,
+                         (unsigned) 1,
+                         relkey);
+    htp = heap_getnext(hsdp, 0, &buffer);
+    if (!HeapTupleIsValid(htp)) {
+       heap_endscan(hsdp);
+       heap_close(relation);
+       elog(WARN, "ChangeAcl: class \"%s\" not found",
+            relname);
+       return;
+    }
+    if (!heap_attisnull(htp, Anum_pg_class_relacl))
+       old_acl = (Acl *) heap_getattr(htp, buffer,
+                                      Anum_pg_class_relacl,
+                                      RelationGetTupleDescriptor(relation),
+                                      (bool *) NULL);
+    if (!old_acl || ACL_NUM(old_acl) < 1) {
+#ifdef ACLDEBUG_TRACE
+       elog(DEBUG, "ChangeAcl: using default ACL");
+#endif
+/*     old_acl = acldefault(((Form_pg_class) GETSTRUCT(htp))->relowner); */
+       old_acl = acldefault();
+       free_old_acl = 1;
+    }
+
+#ifdef ACLDEBUG_TRACE
+    dumpacl(old_acl);
+#endif
+    new_acl = aclinsert3(old_acl, mod_aip, modechg);
+#ifdef ACLDEBUG_TRACE
+    dumpacl(new_acl);
+#endif
+
+    for (i = 0; i < Natts_pg_class; ++i) {
+       replaces[i] = ' ';
+       nulls[i] = ' ';         /* ignored if replaces[i] == ' ' anyway */
+       values[i] = (Datum)NULL;/* ignored if replaces[i] == ' ' anyway */
+    }
+    replaces[Anum_pg_class_relacl - 1] = 'r';
+    values[Anum_pg_class_relacl - 1] = (Datum)new_acl;
+    htp = heap_modifytuple(htp, buffer, relation, values, nulls, replaces);
+    /* XXX is this necessary? */
+    ItemPointerCopy(&htp->t_ctid, &tmp_ipd);
+    /* XXX handle index on pg_class? */
+    setheapoverride(true);
+    (void) heap_replace(relation, &tmp_ipd, htp);
+    setheapoverride(false);
+    heap_endscan(hsdp);
+
+    /* keep the catalog indices up to date */
+    CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices,
+                      idescs);
+    CatalogIndexInsert(idescs, Num_pg_class_indices, relation, htp);
+    CatalogCloseIndices(Num_pg_class_indices, idescs);
+
+    heap_close(relation);
+    if (free_old_acl)
+       pfree(old_acl);
+    pfree(new_acl);
+}
+
+AclId
+get_grosysid(char *groname)
+{
+    HeapTuple htp;
+    AclId id = 0;
+
+    htp = SearchSysCacheTuple(GRONAME, PointerGetDatum(groname), 
+                             0,0,0);
+    if (HeapTupleIsValid(htp)) {
+       id = ((Form_pg_group) GETSTRUCT(htp))->grosysid;
+    } else {
+       elog(WARN, "non-existent group \"%s\"", groname);
+    }
+    return(id);
+}
+
+char*
+get_groname(AclId grosysid)
+{
+    HeapTuple htp;
+    char *name;
+
+    htp = SearchSysCacheTuple(GROSYSID, PointerGetDatum(grosysid),
+                             0,0,0);
+    if (HeapTupleIsValid(htp)) {
+       name = (((Form_pg_group) GETSTRUCT(htp))->groname).data;
+    } else {
+       elog(NOTICE, "get_groname: group %d not found", grosysid);
+    }
+    return(name);
+}
+
+static int32
+in_group(AclId uid, AclId gid)
+{
+       Relation relation;
+       HeapTuple htp;
+       Acl *tmp;
+       unsigned i, num;
+       AclId *aidp;
+       int32 found = 0;
+
+       relation = heap_openr(GroupRelationName);
+       if (!RelationIsValid(relation)) {
+               elog(NOTICE, "in_group: could not open \"%s\"??",
+                    GroupRelationName);
+               return(0);
+       }
+       htp = SearchSysCacheTuple(GROSYSID, ObjectIdGetDatum(gid),
+                                 0,0,0);
+       if (HeapTupleIsValid(htp) &&
+           !heap_attisnull(htp, Anum_pg_group_grolist)) {
+               tmp = (IdList *) heap_getattr(htp, InvalidBuffer,
+                                             Anum_pg_group_grolist,
+                                     RelationGetTupleDescriptor(relation),
+                                             (bool *) NULL);
+               /* XXX make me a function */
+               num = IDLIST_NUM(tmp);
+               aidp = IDLIST_DAT(tmp);
+               for (i = 0; i < num; ++i)
+                       if (aidp[i] == uid) {
+                               found = 1;
+                               break;
+                       }
+       } else {
+               elog(NOTICE, "in_group: group %d not found", gid);
+       }
+       heap_close(relation);
+       return(found);
+}
+
+/*
+ * aclcheck
+ * Returns 1 if the 'id' of type 'idtype' has ACL entries in 'acl' to satisfy
+ * any one of the requirements of 'mode'.  Returns 0 otherwise.
+ */
+int32
+aclcheck(Acl *acl, AclId id, AclIdType idtype, AclMode mode)
+{
+    register unsigned i;
+    register AclItem *aip, *aidat;
+    unsigned num, found_group;
+
+    /* if no acl is found, use world default */
+    if (!acl) {
+       acl = acldefault();
+    }
+
+    num = ACL_NUM(acl);
+    aidat = ACL_DAT(acl);
+
+    /*
+     * We'll treat the empty ACL like that, too, although this is more
+     * like an error (i.e., you manually blew away your ACL array) --
+     * the system never creates an empty ACL.
+     */
+    if (num < 1) {
+#ifdef ACLDEBUG_TRACE
+       elog(DEBUG, "aclcheck: zero-length ACL, returning 1");
+#endif
+       return(1);
+    }
+
+    switch (idtype) {
+    case ACL_IDTYPE_UID:
+       for (i = 1, aip = aidat + 1; /* skip world entry */
+            i < num && aip->ai_idtype == ACL_IDTYPE_UID;
+            ++i, ++aip) {
+           if (aip->ai_id == id) {
+#ifdef ACLDEBUG_TRACE
+               elog(DEBUG, "aclcheck: found %d/%d",
+                    aip->ai_id, aip->ai_mode);
+#endif
+               return((aip->ai_mode & mode) ? 1 : 0);
+           }
+       }
+       for (found_group = 0;
+            i < num && aip->ai_idtype == ACL_IDTYPE_GID;
+            ++i, ++aip) {
+           if (in_group(id, aip->ai_id)) {
+               if (aip->ai_mode & mode)
+                   ++found_group;
+               else {
+#ifdef ACLDEBUG_TRACE
+                   elog(DEBUG, "aclcheck: found %d/%d",
+                        aip->ai_id, aip->ai_mode);
+#endif
+                   return(0);
+               }
+           }
+       }
+       if (found_group) {
+#ifdef ACLDEBUG_TRACE
+           elog(DEBUG,"aclcheck: all groups ok");
+#endif
+           return(1);
+       }
+       break;
+    case ACL_IDTYPE_GID:
+       for (i = 1, aip = aidat + 1; /* skip world entry and UIDs */
+            i < num && aip->ai_idtype == ACL_IDTYPE_UID;
+            ++i, ++aip)
+           ;
+       for (;
+            i < num && aip->ai_idtype == ACL_IDTYPE_GID;
+            ++i, ++aip) {
+           if (aip->ai_id == id) {
+#ifdef ACLDEBUG_TRACE
+               elog(DEBUG, "aclcheck: found %d/%d",
+                    aip->ai_id, aip->ai_mode);
+#endif
+               return((aip->ai_mode & mode) ? 1 : 0);
+           }
+       }
+       break;
+    case ACL_IDTYPE_WORLD:
+       break;
+    default:
+       elog(WARN, "aclcheck: bogus ACL id type: %d", idtype);
+       break;
+    }
+       
+#ifdef ACLDEBUG_TRACE
+    elog(DEBUG, "aclcheck: using world=%d", aidat->ai_mode);
+#endif
+    return((aidat->ai_mode & mode) ? 1 : 0);
+}
+
+int32
+pg_aclcheck(char *relname, char *usename, AclMode mode)
+{
+    HeapTuple htp;
+    AclId id;
+    Acl *acl = (Acl *) NULL, *tmp;
+    int32 result;
+    Relation relation;
+
+    htp = SearchSysCacheTuple(USENAME, PointerGetDatum(usename), 
+                             0,0,0);
+    if (!HeapTupleIsValid(htp))
+       elog(WARN, "pg_aclcheck: user \"%-.*s\" not found",
+            NAMEDATALEN, usename);
+    id = (AclId) ((Form_pg_user) GETSTRUCT(htp))->usesysid;
+
+    /* for the 'pg_database' relation, check the usecreatedb
+       field before checking normal permissions */
+    if ( strcmp(DatabaseRelationName, relname) == 0 &&
+       (((Form_pg_user) GETSTRUCT(htp))->usecreatedb)) {
+       /* note that even though the user can now append to the
+          pg_database table, there is still additional permissions checking
+          in dbcommands.c */
+       if (mode & ACL_AP)
+           return (1);
+    }
+
+    /*
+     * Deny anyone permission to update a system catalog unless
+     * pg_user.usecatupd is set.  (This is to let superusers protect
+     * themselves from themselves.)
+     */
+    if (((mode & ACL_WR) || (mode & ACL_AP)) &&
+       IsSystemRelationName(relname) &&
+       !((Form_pg_user) GETSTRUCT(htp))->usecatupd) {
+       elog(DEBUG, "pg_aclcheck: catalog update to \"%-.*s\": permission denied",
+            NAMEDATALEN, relname);
+       return(0);
+    }
+       
+    /*
+     * Otherwise, superusers bypass all permission-checking.
+     */
+    if (((Form_pg_user) GETSTRUCT(htp))->usesuper) {
+#ifdef ACLDEBUG_TRACE
+       elog(DEBUG, "pg_aclcheck: \"%-.*s\" is superuser",
+            NAMEDATALEN, usename);
+#endif
+       return(1);
+    }
+       
+#ifndef ACLDEBUG
+    htp = SearchSysCacheTuple(RELNAME, PointerGetDatum(relname), 
+                             0,0,0);
+    if (!HeapTupleIsValid(htp)) {
+       elog(WARN, "pg_aclcheck: class \"%-.*s\" not found",
+            NAMEDATALEN, relname);
+       return(1);
+    }
+    if (!heap_attisnull(htp, Anum_pg_class_relacl)) {
+       relation = heap_openr(RelationRelationName);
+       tmp = (Acl *) heap_getattr(htp, InvalidBuffer,
+                                  Anum_pg_class_relacl,
+                                  RelationGetTupleDescriptor(relation),
+                                  (bool *) NULL);
+       acl = makeacl(ACL_NUM(tmp));
+       memmove((char *) acl, (char *) tmp, ACL_SIZE(tmp));
+       heap_close(relation);
+    } else {
+       /* if the acl is null, by default the owner can do whatever
+          he wants to with it */
+       Oid ownerId;
+       relation = heap_openr(RelationRelationName);
+       ownerId = (Oid)heap_getattr(htp, InvalidBuffer,
+                                   Anum_pg_class_relowner,
+                                   RelationGetTupleDescriptor(relation),
+                                   (bool*) NULL);
+       acl = aclownerdefault(ownerId);
+    }
+#else
+    {                          /* This is why the syscache is great... */
+       static ScanKeyData relkey[1] = {
+           { 0, Anum_pg_class_relname, NameEqualRegProcedure }
+       };
+       HeapScanDesc hsdp;
+
+       relation = heap_openr(RelationRelationName);
+       if (!RelationIsValid(relation)) {
+           elog(NOTICE, "pg_checkacl: could not open \"%-.*s\"??",
+                RelationRelationName);
+           return(1);
+       }
+       fmgr_info(NameEqualRegProcedure,
+                 &relkey[0].sk_func,
+                 &relkey[0].sk_nargs);
+       relkey[0].sk_argument = NameGetDatum(relname);
+       hsdp = heap_beginscan(relation, 0, NowTimeQual, 1, relkey);
+       htp = heap_getnext(hsdp, 0, (Buffer *) 0);
+       if (HeapTupleIsValid(htp) &&
+           !heap_attisnull(htp, Anum_pg_class_relacl)) {
+           tmp = (Acl *) heap_getattr(htp, InvalidBuffer,
+                                      Anum_pg_class_relacl,
+                                      RelationGetTupleDescriptor(relation),
+                                      (bool *) NULL);
+           acl = makeacl(ACL_NUM(tmp));
+           memmove((char *) acl, (char *) tmp, ACL_SIZE(tmp));
+       }
+       heap_endscan(hsdp);
+       heap_close(relation);
+    }
+#endif
+    result = aclcheck(acl, id, (AclIdType) ACL_IDTYPE_UID, mode);
+    if (acl)
+       pfree(acl);
+    return(result);
+}
+
+int32
+pg_ownercheck(char *usename,
+             char *value,
+             int cacheid)
+{
+    HeapTuple htp;
+    AclId user_id, owner_id;
+
+    htp = SearchSysCacheTuple(USENAME, PointerGetDatum(usename), 
+                             0,0,0);
+    if (!HeapTupleIsValid(htp))
+       elog(WARN, "pg_ownercheck: user \"%-.*s\" not found",
+            NAMEDATALEN, usename);
+    user_id = (AclId) ((Form_pg_user) GETSTRUCT(htp))->usesysid;
+
+    /*
+     * Superusers bypass all permission-checking.
+     */
+    if (((Form_pg_user) GETSTRUCT(htp))->usesuper) {
+#ifdef ACLDEBUG_TRACE
+       elog(DEBUG, "pg_ownercheck: user \"%-.*s\" is superuser",
+            NAMEDATALEN, usename);
+#endif
+       return(1);
+    }
+
+    htp = SearchSysCacheTuple(cacheid, PointerGetDatum(value), 
+                             0,0,0);
+    switch (cacheid) {
+    case OPROID:
+       if (!HeapTupleIsValid(htp))
+           elog(WARN, "pg_ownercheck: operator %d not found",
+                (int) value);
+       owner_id = ((OperatorTupleForm) GETSTRUCT(htp))->oprowner;
+       break;
+    case PRONAME:
+       if (!HeapTupleIsValid(htp))
+           elog(WARN, "pg_ownercheck: function \"%-.*s\" not found",
+                NAMEDATALEN, value);
+       owner_id = ((Form_pg_proc) GETSTRUCT(htp))->proowner;
+       break;
+    case RELNAME:
+       if (!HeapTupleIsValid(htp))
+           elog(WARN, "pg_ownercheck: class \"%-.*s\" not found",
+                NAMEDATALEN, value);
+       owner_id = ((Form_pg_class) GETSTRUCT(htp))->relowner;
+       break;
+    case TYPNAME:
+       if (!HeapTupleIsValid(htp))
+           elog(WARN, "pg_ownercheck: type \"%-.*s\" not found",
+                NAMEDATALEN, value);
+       owner_id = ((TypeTupleForm) GETSTRUCT(htp))->typowner;
+       break;
+    default:
+       elog(WARN, "pg_ownercheck: invalid cache id: %d",
+            cacheid);
+       break;
+    }
+       
+    return(user_id == owner_id);
+}
+
+int32
+pg_func_ownercheck(char *usename, 
+                  char *funcname,
+                  int nargs,
+                  Oid *arglist)
+{
+    HeapTuple htp;
+    AclId user_id, owner_id;
+
+    htp = SearchSysCacheTuple(USENAME, PointerGetDatum(usename), 
+                             0,0,0);
+    if (!HeapTupleIsValid(htp))
+       elog(WARN, "pg_func_ownercheck: user \"%-.*s\" not found",
+            NAMEDATALEN, usename);
+    user_id = (AclId) ((Form_pg_user) GETSTRUCT(htp))->usesysid;
+
+    /*
+     * Superusers bypass all permission-checking.
+     */
+    if (((Form_pg_user) GETSTRUCT(htp))->usesuper) {
+#ifdef ACLDEBUG_TRACE
+       elog(DEBUG, "pg_ownercheck: user \"%-.*s\" is superuser",
+            NAMEDATALEN, usename);
+#endif
+       return(1);
+    }
+
+    htp = SearchSysCacheTuple(PRONAME, 
+                             PointerGetDatum(funcname),
+                             PointerGetDatum(nargs),
+                             PointerGetDatum(arglist),
+                             0);
+    if (!HeapTupleIsValid(htp))
+       func_error("pg_func_ownercheck", funcname, nargs, (int*)arglist);
+
+    owner_id = ((Form_pg_proc) GETSTRUCT(htp))->proowner;
+       
+    return(user_id == owner_id);
+}
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
new file mode 100644 (file)
index 0000000..8d4263f
--- /dev/null
@@ -0,0 +1,354 @@
+/*-------------------------------------------------------------------------
+ *
+ * dest.c--
+ *    support for various communication destinations - see lib/H/tcop/dest.h
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ *   INTERFACE ROUTINES
+ *     BeginCommand - prepare destination for tuples of the given type
+ *     EndCommand - tell destination that no more tuples will arrive
+ *     NullCommand - tell dest that the last of a query sequence was processed
+ *     
+ *   NOTES
+ *     These routines do the appropriate work before and after
+ *     tuples are returned by a query to keep the backend and the
+ *     "destination" portals synchronized.
+ *
+ */
+#include <stdio.h>             /* for sprintf() */
+#include "postgres.h"
+
+#include "access/htup.h"
+#include "libpq/libpq-be.h"
+#include "access/printtup.h"
+#include "utils/portal.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+#include "executor/executor.h"
+
+#include "tcop/dest.h"
+
+#include "catalog/pg_type.h"
+#include "utils/mcxt.h"
+
+#include "commands/async.h"
+
+/* ----------------
+ *     output functions
+ * ----------------
+ */
+void
+donothing(List *tuple, List *attrdesc)
+{
+}
+
+void (*DestToFunction(CommandDest dest))()
+{
+    switch (dest) {
+    case RemoteInternal:
+       return printtup_internal;
+       break;
+       
+    case Remote:
+       return printtup;
+       break;
+       
+    case Local:
+       return be_printtup;
+       break;
+       
+    case Debug:
+       return debugtup;
+       break;
+       
+    case None:
+    default:
+       return donothing;
+       break;
+    }  
+    
+    /*
+     * never gets here, but DECstation lint appears to be stupid...
+     */
+    
+    return donothing;
+}
+
+#define IS_INSERT_TAG(tag) (*tag == 'I' && *(tag+1) == 'N')
+
+/* ----------------
+ *     EndCommand - tell destination that no more tuples will arrive
+ * ----------------
+ */
+void
+EndCommand(char *commandTag, CommandDest dest)
+{
+    char buf[64];
+    
+    switch (dest) {
+    case RemoteInternal:
+    case Remote:
+       /* ----------------
+        *      tell the fe that the query is over
+        * ----------------
+        */
+       pq_putnchar("C", 1);
+/*     pq_putint(0, 4); */
+       if (IS_INSERT_TAG(commandTag))
+           {
+               sprintf(buf, "%s %d", commandTag, GetAppendOid());
+               pq_putstr(buf);
+           }
+       else
+           pq_putstr(commandTag);
+       pq_flush();
+       break;
+       
+    case Local:
+    case Debug:
+       break;
+    case CopyEnd:
+       pq_putnchar("Z", 1);
+       pq_flush();
+       break;
+    case None:
+    default:
+       break;
+    }
+}
+
+/*
+ * These are necessary to sync communications between fe/be processes doing
+ * COPY rel TO stdout
+ * 
+ * or 
+ *
+ * COPY rel FROM stdin
+ *
+ */
+void
+SendCopyBegin()
+{
+    pq_putnchar("B", 1);
+/*    pq_putint(0, 4); */
+    pq_flush();
+}
+
+void
+ReceiveCopyBegin()
+{
+    pq_putnchar("D", 1);
+/*    pq_putint(0, 4); */
+    pq_flush();
+}
+
+/* ----------------
+ *     NullCommand - tell dest that the last of a query sequence was processed
+ * 
+ *     Necessary to implement the hacky FE/BE interface to handle
+ *     multiple-return queries.
+ * ----------------
+ */
+void
+NullCommand(CommandDest dest)
+{
+    switch (dest) {
+    case RemoteInternal:
+    case Remote: {
+#if 0
+       /* Do any asynchronous notification.  If front end wants to poll,
+          it can send null queries to call this function.
+          */
+       PQNotifyList *nPtr;
+       MemoryContext orig;
+       
+       if (notifyContext == NULL) {
+           notifyContext = CreateGlobalMemory("notify");
+       }
+       orig = MemoryContextSwitchTo((MemoryContext)notifyContext);
+       
+       for (nPtr = PQnotifies() ;
+            nPtr != NULL;
+            nPtr = (PQNotifyList *)SLGetSucc(&nPtr->Node)) {
+           pq_putnchar("A",1);
+           pq_putint(0, 4);
+           pq_putstr(nPtr->relname);
+           pq_putint(nPtr->be_pid,4);
+           PQremoveNotify(nPtr);
+       }
+       pq_flush();
+       PQcleanNotify();        /* garbage collect */
+       (void) MemoryContextSwitchTo(orig);
+#endif
+       /* ----------------
+        *      tell the fe that the last of the queries has finished
+        * ----------------
+        */
+/*     pq_putnchar("I", 1);  */
+       pq_putstr("I");  
+       /* pq_putint(0, 4);*/
+       pq_flush();
+    }
+       break;
+       
+    case Local:
+    case Debug:
+    case None:
+    default:
+       break;
+    }  
+}
+
+/* ----------------
+ *     BeginCommand - prepare destination for tuples of the given type
+ * ----------------
+ */
+void
+BeginCommand(char *pname,
+            int operation,
+            TupleDesc tupdesc,
+            bool isIntoRel,
+            bool isIntoPortal,
+            char *tag,
+            CommandDest dest)
+{
+    PortalEntry        *entry;
+    AttributeTupleForm *attrs = tupdesc->attrs;
+    int    natts = tupdesc->natts;
+    int    i;
+    char   *p;
+
+    switch (dest) {
+    case RemoteInternal:
+    case Remote:
+       /* ----------------
+        *      if this is a "retrieve portal" query, just return
+        *      because nothing needs to be sent to the fe.
+        * ----------------
+        */
+        ResetAppendOid();
+       if (isIntoPortal)
+           return;
+       
+       /* ----------------
+        *      if portal name not specified for remote query,
+        *      use the "blank" portal.
+        * ----------------
+        */
+       if (pname == NULL)
+           pname = "blank";
+       
+       /* ----------------
+        *      send fe info on tuples we're about to send
+        * ----------------
+        */
+       pq_flush();
+       pq_putnchar("P", 1);    /* new portal.. */
+       pq_putstr(pname);       /* portal name */
+       
+       /* ----------------
+        *      if this is a retrieve, then we send back the tuple
+        *      descriptor of the tuples.  "retrieve into" is an
+        *      exception because no tuples are returned in that case.
+        * ----------------
+        */
+       if (operation == CMD_SELECT && !isIntoRel) {
+           pq_putnchar("T", 1);        /* type info to follow.. */
+           pq_putint(natts, 2);        /* number of attributes in tuples */
+           
+           for (i = 0; i < natts; ++i) {
+               pq_putstr(attrs[i]->attname.data);/* if 16 char name oops.. */
+               pq_putint((int) attrs[i]->atttypid, 4);
+               pq_putint(attrs[i]->attlen, 2);
+           }
+       }
+       pq_flush();
+       break;
+       
+    case Local:
+       /* ----------------
+        *      prepare local portal buffer for query results
+        *      and setup result for PQexec()
+        * ----------------
+        */
+       entry = be_currentportal();
+       if (pname != NULL)
+           pbuf_setportalinfo(entry, pname);
+       
+       if (operation == CMD_SELECT && !isIntoRel) {
+           be_typeinit(entry, tupdesc, natts);
+           p = (char *) palloc(strlen(entry->name)+2);
+           p[0] = 'P';
+           strcpy(p+1,entry->name);
+       } else {
+           p = (char *) palloc(strlen(tag)+2);
+           p[0] = 'C';
+           strcpy(p+1,tag);
+       }
+       entry->result = p;
+       break;
+       
+    case Debug:
+       /* ----------------
+        *      show the return type of the tuples
+        * ----------------
+        */
+       if (pname == NULL)
+           pname = "blank";
+       
+       showatts(pname, tupdesc);
+       break;
+       
+    case None:
+    default:
+       break;
+    }
+}
+
+static Oid AppendOid;
+
+void
+ResetAppendOid()
+{
+    AppendOid = InvalidOid;
+}
+
+#define MULTI_TUPLE_APPEND -1
+
+void
+UpdateAppendOid(Oid newoid)
+{
+    /*
+     * First update after AppendOid was reset (at command beginning).
+     */
+    if (AppendOid == InvalidOid)
+       AppendOid = newoid;
+    /*
+     * Already detected a multiple tuple append, return a void oid ;)
+     */
+    else if (AppendOid == MULTI_TUPLE_APPEND)
+       return;
+    /*
+     * Oid has been assigned once before, tag this as a multiple tuple
+     * append.
+     */
+    else
+       AppendOid = MULTI_TUPLE_APPEND;
+}
+
+Oid
+GetAppendOid()
+{
+    if (AppendOid == MULTI_TUPLE_APPEND)
+       return InvalidOid;
+    return AppendOid;
+}
diff --git a/src/backend/tcop/dest.h b/src/backend/tcop/dest.h
new file mode 100644 (file)
index 0000000..47005d3
--- /dev/null
@@ -0,0 +1,78 @@
+/*-------------------------------------------------------------------------
+ *
+ * dest.h--
+ *     Whenever the backend is submitted a query, the results
+ *     have to go someplace - either to the standard output,
+ *     to a local portal buffer or to a remote portal buffer.
+ *
+ *    -        stdout is the destination only when we are running a
+ *     backend without a postmaster and are returning results
+ *     back to the user.
+ *
+ *    -        a local portal buffer is the destination when a backend
+ *     executes a user-defined function which calls PQexec() or
+ *     PQfn().  In this case, the results are collected into a
+ *     PortalBuffer which the user's function may diddle with.
+ *
+ *    -        a remote portal buffer is the destination when we are
+ *     running a backend with a frontend and the frontend executes
+ *     PQexec() or PQfn().  In this case, the results are sent
+ *     to the frontend via the pq_ functions.
+ *
+ *    - None is the destination when the system executes
+ *     a query internally.  This is not used now but it may be
+ *     useful for the parallel optimiser/executor.
+ *     
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef DEST_H
+#define DEST_H
+
+#include "catalog/pg_attribute.h"
+#include "access/tupdesc.h"
+
+/* ----------------
+ *     CommandDest is used to allow the results of calling
+ *     pg_eval() to go to the right place.
+ * ----------------
+ */
+typedef enum {
+    None,              /* results are discarded */
+    Debug,             /* results go to debugging output */
+    Local,             /* results go in local portal buffer */
+    Remote,            /* results sent to frontend process */
+    CopyBegin,         /* results sent to frontend process but are strings */
+    CopyEnd,           /* results sent to frontend process but are strings */
+    RemoteInternal      /* results sent to frontend process in internal
+                          (binary) form */
+} CommandDest;
+
+
+/* AttrInfo* replaced with TupleDesc, now that TupleDesc also has within it
+   the number of attributes
+
+typedef struct AttrInfo {
+    int                        numAttr;
+    AttributeTupleForm *attrs;
+} AttrInfo;
+*/
+
+extern void donothing(List *tuple, List *attrdesc);
+extern void (*DestToFunction(CommandDest dest))();
+extern void EndCommand(char *commandTag, CommandDest dest);
+extern void SendCopyBegin();
+extern void ReceiveCopyBegin();
+extern void NullCommand(CommandDest dest);
+extern void BeginCommand(char *pname, int operation, TupleDesc attinfo,
+                        bool isIntoRel, bool isIntoPortal, char *tag,
+                        CommandDest dest);
+extern void ResetAppendOid();
+extern void UpdateAppendOid(Oid newoid);
+extern Oid GetAppendOid();
+
+#endif  /* DEST_H */
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
new file mode 100644 (file)
index 0000000..17acc57
--- /dev/null
@@ -0,0 +1,353 @@
+/*-------------------------------------------------------------------------
+ *
+ * fastpath.c--
+ *    routines to handle function requests from the frontend
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    This cruft is the server side of PQfn.
+ *
+ *    - jolly 07/11/95:
+ *
+ *    no longer rely on return sizes provided by the frontend.  Always
+ *    use the true lengths for the catalogs.  Assume that the frontend 
+ *    has allocated enough space to handle the result value returned.
+ *    
+ *    trust that the user knows what he is doing with the args.  If the 
+ *    sys catalog says it is a varlena, assume that the user is only sending
+ *    down VARDATA and that the argsize is the VARSIZE.  If the arg is
+ *    fixed len, assume that the argsize given by the user is correct.
+ *  
+ *    if the function returns by value, then only send 4 bytes value 
+ *    back to the frontend.  If the return returns by reference, 
+ *    send down only the data portion and set the return size appropriately.
+ * 
+ *   OLD COMMENTS FOLLOW
+ *
+ *    The VAR_LENGTH_{ARGS,RESULT} stuff is limited to MAX_STRING_LENGTH
+ *    (see src/backend/tmp/fastpath.h) for no obvious reason.  Since its
+ *    primary use (for us) is for Inversion path names, it should probably
+ *    be increased to 256 (MAXPATHLEN for Inversion, hidden in pg_type
+ *    as well as utils/adt/filename.c).
+ *
+ *    Quoth PMA on 08/15/93:
+ *
+ *    This code has been almost completely rewritten with an eye to
+ *    keeping it as compatible as possible with the previous (broken)
+ *    implementation.
+ *
+ *    The previous implementation would assume (1) that any value of
+ *    length <= 4 bytes was passed-by-value, and that any other value
+ *    was a struct varlena (by-reference).  There was NO way to pass a
+ *    fixed-length by-reference argument (like char16) or a struct
+ *    varlena of size <= 4 bytes.
+ *     
+ *    The new implementation checks the catalogs to determine whether
+ *    a value is by-value (type "0" is null-delimited character string,
+ *    as it is for, e.g., the parser).  The only other item obtained
+ *    from the catalogs is whether or not the value should be placed in
+ *    a struct varlena or not.  Otherwise, the size given by the
+ *    frontend is assumed to be correct (probably a bad decision, but
+ *    we do strange things in the name of compatibility).
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "tcop/tcopdebug.h"
+
+#include "utils/palloc.h"
+#include "fmgr.h"
+#include "utils/elog.h"
+#include "utils/builtins.h"    /* for oideq */
+#include "tcop/fastpath.h"
+#include "libpq/libpq.h"
+
+#include "access/xact.h"       /* for TransactionId/CommandId protos */
+
+#include "utils/syscache.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+
+
+/* ----------------
+ *     SendFunctionResult
+ * ----------------
+ */
+static void
+SendFunctionResult(Oid fid,    /* function id */
+                  char *retval, /* actual return value */
+                  bool retbyval,
+                  int retlen   /* the length according to the catalogs */
+                  )
+{
+    pq_putnchar("V", 1);
+    
+    if (retlen != 0) { 
+       pq_putnchar("G", 1);
+       if (retbyval) {         /* by-value */
+           pq_putint(retlen, 4);
+           pq_putint((int)retval, retlen); 
+       } else {                /* by-reference ... */
+           if (retlen < 0) {           /* ... varlena */
+               pq_putint(VARSIZE(retval) - VARHDRSZ, 4);
+               pq_putnchar(VARDATA(retval), VARSIZE(retval) - VARHDRSZ);
+           } else {                    /* ... fixed */
+               pq_putint(retlen, 4);
+               pq_putnchar(retval, retlen);
+           }
+       }
+    }
+
+    pq_putnchar("0", 1);
+    pq_flush();
+}
+
+/*
+ * This structure saves enough state so that one can avoid having to
+ * do catalog lookups over and over again.  (Each RPC can require up
+ * to MAXFMGRARGS+2 lookups, which is quite tedious.)
+ *
+ * The previous incarnation of this code just assumed that any argument
+ * of size <= 4 was by value; this is not correct.  There is no cheap
+ * way to determine function argument length etc.; one must simply pay
+ * the price of catalog lookups.
+ */
+struct fp_info {
+    Oid                funcid;
+    int                        nargs;
+    bool               argbyval[MAXFMGRARGS];
+    int32              arglen[MAXFMGRARGS];    /* signed (for varlena) */
+    bool               retbyval;
+    int32              retlen;                 /* signed (for varlena) */
+    TransactionId      xid;
+    CommandId          cid;
+};
+
+/*
+ * We implement one-back caching here.  If we need to do more, we can.
+ * Most routines in tight loops (like PQfswrite -> F_LOWRITE) will do
+ * the same thing repeatedly.
+ */
+static struct fp_info last_fp = { InvalidOid };
+
+/*
+ * valid_fp_info
+ *
+ * RETURNS:
+ *     1 if the state in 'fip' is valid
+ *     0 otherwise
+ *
+ * "valid" means:
+ * The saved state was either uninitialized, for another function,
+ * or from a previous command.  (Commands can do updates, which
+ * may invalidate catalog entries for subsequent commands.  This
+ * is overly pessimistic but since there is no smarter invalidation
+ * scheme...).
+ */
+static int
+valid_fp_info(Oid func_id, struct fp_info *fip)
+{
+    Assert(OidIsValid(func_id));
+    Assert(fip != (struct fp_info *) NULL);
+    
+    return(OidIsValid(fip->funcid) &&
+          oideq(func_id, fip->funcid) &&
+          TransactionIdIsCurrentTransactionId(fip->xid) &&
+          CommandIdIsCurrentCommandId(fip->cid));
+}
+
+/*
+ * update_fp_info
+ *
+ * Performs catalog lookups to load a struct fp_info 'fip' for the
+ * function 'func_id'.
+ *
+ * RETURNS:
+ *     The correct information in 'fip'.  Sets 'fip->funcid' to
+ *     InvalidOid if an exception occurs.
+ */
+static void
+update_fp_info(Oid func_id, struct fp_info *fip)
+{
+    Oid                *argtypes;      /* an oid8 */
+    Oid                rettype;
+    HeapTuple          func_htp, type_htp;
+    TypeTupleForm      tp;
+    Form_pg_proc       pp;
+    int                        i;
+    
+    Assert(OidIsValid(func_id));
+    Assert(fip != (struct fp_info *) NULL);
+
+    /*
+     * Since the validity of this structure is determined by whether
+     * the funcid is OK, we clear the funcid here.  It must not be
+     * set to the correct value until we are about to return with
+     * a good struct fp_info, since we can be interrupted (i.e., with
+     * an elog(WARN, ...)) at any time.
+     */
+    memset((char *) fip, 0, (int) sizeof(struct fp_info));
+    fip->funcid = InvalidOid;
+
+    func_htp = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(func_id),
+                                  0,0,0);
+    if (!HeapTupleIsValid(func_htp)) {
+       elog(WARN, "update_fp_info: cache lookup for function %d failed",
+            func_id);
+    }
+    pp = (Form_pg_proc) GETSTRUCT(func_htp);
+    fip->nargs = pp->pronargs;
+    rettype = pp->prorettype;
+    argtypes = pp->proargtypes;
+
+    for (i = 0; i < fip->nargs; ++i) {
+       if (OidIsValid(argtypes[i])) {
+           type_htp = SearchSysCacheTuple(TYPOID, 
+                                          ObjectIdGetDatum(argtypes[i]),
+                                          0,0,0);
+           if (!HeapTupleIsValid(type_htp)) {
+               elog(WARN, "update_fp_info: bad argument type %d for %d",
+                    argtypes[i], func_id);
+           }
+           tp = (TypeTupleForm) GETSTRUCT(type_htp);
+           fip->argbyval[i] = tp->typbyval;
+           fip->arglen[i] = tp->typlen;
+       } /* else it had better be VAR_LENGTH_ARG */
+    }
+
+    if (OidIsValid(rettype)) {
+       type_htp = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(rettype),
+                                      0,0,0);
+       if (!HeapTupleIsValid(type_htp)) {
+           elog(WARN, "update_fp_info: bad return type %d for %d",
+                rettype, func_id);
+       }
+       tp = (TypeTupleForm) GETSTRUCT(type_htp);
+       fip->retbyval = tp->typbyval;
+       fip->retlen = tp->typlen;
+    } /* else it had better by VAR_LENGTH_RESULT */
+
+    fip->xid = GetCurrentTransactionId();
+    fip->cid = GetCurrentCommandId();
+
+    /*
+     * This must be last!
+     */
+    fip->funcid = func_id;
+}
+       
+
+/*
+ * HandleFunctionRequest
+ *
+ * Server side of PQfn (fastpath function calls from the frontend).
+ * This corresponds to the libpq protocol symbol "F".
+ *
+ * RETURNS:
+ *     nothing of significance.
+ *     All errors result in elog(WARN,...).
+ */
+int
+HandleFunctionRequest()
+{
+    Oid                fid;
+    int                argsize;
+    int                nargs;
+    char       *arg[8];
+    char       *retval;
+    int                        i;
+    uint32             palloced;
+    char               *p;
+    struct fp_info     *fip;
+
+    fid = (Oid) pq_getint(4);  /* function oid */
+    nargs = pq_getint(4);      /* # of arguments */
+
+    /*
+     * This is where the one-back caching is done.
+     * If you want to save more state, make this a loop around an array.
+     */
+    fip = &last_fp;
+    if (!valid_fp_info(fid, fip)) {
+       update_fp_info(fid, fip);
+    }
+
+    if (fip->nargs != nargs) {
+       elog(WARN, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
+            nargs, fip->nargs);
+    }
+
+    /*
+     *  Copy arguments into arg vector.  If we palloc() an argument, we need
+     *  to remember, so that we pfree() it after the call.
+     */
+    palloced = 0x0;
+    for (i = 0; i < 8; ++i) {
+       if (i >= nargs) {
+           arg[i] = (char *) NULL;
+       } else {
+           argsize = pq_getint(4);
+           
+           Assert(argsize > 0);
+           if (fip->argbyval[i]) {             /* by-value */
+               Assert(argsize <= 4);
+               arg[i] = (char *) pq_getint(argsize);
+           } else {                    /* by-reference ... */
+               if (fip->arglen[i] < 0) {               /* ... varlena */
+                   if (!(p = palloc(argsize + VARHDRSZ))) {
+                       elog(WARN, "HandleFunctionRequest: palloc failed");
+                   }
+                   VARSIZE(p) = argsize + VARHDRSZ;
+                   pq_getnchar(VARDATA(p), 0, argsize);
+               } else {                                /* ... fixed */
+                       /* XXX cross our fingers and trust "argsize" */
+                       if (!(p = palloc(argsize))) {
+                           elog(WARN, "HandleFunctionRequest: palloc failed");
+                       }
+                       pq_getnchar(p, 0, argsize);
+                   }
+               palloced |= (1 << i);
+               arg[i] = p;
+           }
+       }
+    }
+
+#ifndef NO_FASTPATH
+    retval = fmgr(fid,
+                 arg[0], arg[1], arg[2], arg[3],
+                 arg[4], arg[5], arg[6], arg[7]);
+
+#else
+    retval = NULL;
+#endif /* NO_FASTPATH */
+
+    /* free palloc'ed arguments */
+    for (i = 0; i < nargs; ++i) {
+       if (palloced & (1 << i))
+           pfree(arg[i]);
+    }
+
+    /*
+     *  If this is an ordinary query (not a retrieve portal p ...), then
+     *  we return the data to the user.  If the return value was palloc'ed,
+     *  then it must also be freed.
+     */
+#ifndef NO_FASTPATH
+    SendFunctionResult(fid, retval, fip->retbyval, fip->retlen);
+#else
+    SendFunctionResult(fid, retval, fip->retbyval, 0);
+#endif /* NO_FASTPATH */
+
+    if (!fip->retbyval)
+       pfree(retval);
+
+    
+    
+    return(0);
+}
diff --git a/src/backend/tcop/fastpath.h b/src/backend/tcop/fastpath.h
new file mode 100644 (file)
index 0000000..135f6de
--- /dev/null
@@ -0,0 +1,31 @@
+/*-------------------------------------------------------------------------
+ *
+ * fastpath.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    This information pulled out of tcop/fastpath.c and put
+ *    here so that the PQfn() in be-pqexec.c could access it.
+ *     -cim 2/26/91
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FASTPATH_H
+#define FASTPATH_H
+
+/* ----------------
+ *     fastpath #defines
+ * ----------------
+ */
+#define VAR_LENGTH_RESULT      (-1)
+#define VAR_LENGTH_ARG                 (-5)
+#define MAX_STRING_LENGTH      256
+
+extern int HandleFunctionRequest(void);
+
+#endif /* FASTPATH_H */
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
new file mode 100644 (file)
index 0000000..7e89206
--- /dev/null
@@ -0,0 +1,1500 @@
+/*-------------------------------------------------------------------------
+ *
+ * postgres.c--
+ *    POSTGRES C Backend Interface
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    this is the "main" module of the postgres backend and
+ *    hence the main module of the "traffic cop".
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "libpq/pqsignal.h"    /* substitute for <signal.h> */
+#if defined(PORTNAME_linux)
+#ifndef __USE_POSIX
+#define __USE_POSIX
+#endif
+#endif /* defined(PORTNAME_linux) */
+#include <setjmp.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/param.h>         /* for MAXHOSTNAMELEN on most */
+#ifndef WIN32
+#include <netdb.h>             /* for MAXHOSTNAMELEN on some */
+#endif /* WIN32 */
+#include <errno.h>
+#ifdef PORTNAME_aix
+#include <sys/select.h>
+#endif /* PORTNAME_aix */
+
+
+#include "postgres.h"
+#include "miscadmin.h"
+#include "catalog/catname.h"
+#include "access/xact.h"
+
+#include "lib/dllist.h"
+
+#include "parser/catalog_utils.h"
+#include "parser/parse_query.h"            /* for MakeTimeRange() */
+#include "commands/async.h"
+#include "tcop/tcopprot.h"         /* where declarations for this file go */
+#include "optimizer/planner.h"
+
+#include "tcop/tcopdebug.h"
+
+#include "executor/execdebug.h"
+#include "executor/executor.h"
+#include "nodes/relation.h"
+
+#include "optimizer/cost.h"
+#include "optimizer/planner.h"
+#if 0
+#include "optimizer/xfunc.h"
+#endif
+#include "optimizer/prep.h"
+#include "nodes/plannodes.h"
+
+#include "storage/bufmgr.h"
+#include "fmgr.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/rel.h"
+
+#include "nodes/pg_list.h"
+#include "tcop/dest.h"
+#include "nodes/memnodes.h"
+#include "utils/mcxt.h"
+#include "tcop/pquery.h"
+#include "tcop/utility.h"
+#include "tcop/fastpath.h"
+
+#include "libpq/libpq.h"
+#include "rewrite/rewriteHandler.h" /* for QueryRewrite() */
+
+/* ----------------
+ *     global variables
+ * ----------------
+ */
+static bool    DebugPrintPlan = false;
+static bool    DebugPrintParse = false;
+static bool    DebugPrintRewrittenParsetree = false;
+/*static bool  EnableRewrite = true; , never changes why have it*/
+CommandDest whereToSendOutput;
+
+extern int     lockingOff;
+extern int     NBuffers;
+
+int    dontExecute = 0;
+static int     ShowStats;
+static bool    IsEmptyQuery = false;
+
+Relation       reldesc;                /* current relation descritor */
+char           relname[80];            /* current relation name */
+
+#if defined(WIN32) || defined(PORTNAME_next)
+jmp_buf    Warn_restart;
+#define sigsetjmp(x,y)  setjmp(x)
+#define siglongjmp longjmp
+#else
+sigjmp_buf Warn_restart;
+#endif /*defined(WIN32) || defined(PORTNAME_next) */
+
+extern int     NBuffers;
+
+static int     EchoQuery = 0;          /* default don't echo */
+time_t         tim;
+char           pg_pathname[256];
+static int     ShowParserStats;
+static int     ShowPlannerStats;
+int            ShowExecutorStats;
+FILE           *StatFp;
+
+typedef struct frontend {
+  bool  fn_connected;
+  Port  fn_port;
+  FILE  *fn_Pfin;  /* the input fd */
+  FILE  *fn_Pfout; /* the output fd */
+  bool  fn_done; /* set after the frontend closes its connection */
+} FrontEnd;
+
+static Dllist* frontendList;
+
+/* ----------------
+ *     people who want to use EOF should #define DONTUSENEWLINE in
+ *     tcop/tcopdebug.h
+ * ----------------
+ */
+#ifndef TCOP_DONTUSENEWLINE
+int UseNewLine = 1;  /* Use newlines query delimiters (the default) */
+#else
+int UseNewLine = 0;  /* Use EOF as query delimiters */
+#endif /* TCOP_DONTUSENEWLINE */
+
+/* ----------------
+ *     bushy tree plan flag: if true planner will generate bushy-tree
+ *     plans
+ * ----------------
+ */
+int BushyPlanFlag = 0; /* default to false -- consider only left-deep trees */
+
+/*
+** Flags for expensive function optimization -- JMH 3/9/92
+*/
+int XfuncMode = 0;
+
+/*
+ * ----------------
+ *   Note: _exec_repeat_ defaults to 1 but may be changed
+ *        by a DEBUG command.   If you set this to a large
+ *        number N, run a single query, and then set it
+ *        back to 1 and run N queries, you can get an idea
+ *        of how much time is being spent in the parser and
+ *        planner b/c in the first case this overhead only
+ *        happens once.  -cim 6/9/91
+ * ----------------
+*/
+int _exec_repeat_ = 1;
+
+/* ----------------------------------------------------------------
+ *     decls for routines only used in this file
+ * ----------------------------------------------------------------
+ */
+static char InteractiveBackend(char *inBuf);
+static char SocketBackend(char *inBuf, int multiplexedBackend);
+static char ReadCommand(char *inBuf, int multiplexedBackend);
+
+
+/* ----------------------------------------------------------------
+ *     routines to obtain user input
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *  InteractiveBackend() is called for user interactive connections
+ *  the string entered by the user is placed in its parameter inBuf.
+ * ----------------
+ */
+
+static char
+InteractiveBackend(char *inBuf)
+{
+    char *stuff = inBuf;               /* current place in input buffer */
+    int c;                             /* character read from getc() */
+    bool end = false;                  /* end-of-input flag */
+    bool backslashSeen = false;                /* have we seen a \ ? */
+    
+    /* ----------------
+     * display a prompt and obtain input from the user
+     * ----------------
+     */
+    printf("> ");
+    
+    for (;;) {
+       if (UseNewLine) {
+           /* ----------------
+            *  if we are using \n as a delimiter, then read
+            *  characters until the \n.
+            * ----------------
+            */
+           while ( (c = getc(stdin)) != EOF) {
+               if (c == '\n') {
+                   if (backslashSeen) {
+                       stuff--;
+                       continue;
+                   } else {
+                       /* keep the newline character */
+                       *stuff++ = '\n';
+                       *stuff++ = '\0';
+                       break;
+                   }
+               } else if (c == '\\')
+                   backslashSeen = true;
+               else
+                   backslashSeen = false;
+               
+               *stuff++ = (char)c;
+           }
+           
+           if (c == EOF)
+               end = true;
+       } else {
+           /* ----------------
+            *  otherwise read characters until EOF.
+            * ----------------
+            */
+           while ( (c = getc(stdin)) != EOF )
+               *stuff++ = (char)c;
+           
+           if ( stuff == inBuf )
+               end = true;
+       }
+       
+       if (end) {
+           if (!Quiet) puts("EOF");
+           IsEmptyQuery = true;
+           exitpg(0);
+       }
+       
+       /* ----------------
+        *  otherwise we have a user query so process it.
+        * ----------------
+        */
+       break;
+    }
+    
+    /* ----------------
+     * if the query echo flag was given, print the query..
+     * ----------------
+     */
+    if (EchoQuery)
+       printf("query is: %s\n", inBuf);
+    
+    return('Q');
+}
+
+/* ----------------
+ *  SocketBackend()    Is called for frontend-backend connections
+ *
+ *  If the input is a query (case 'Q') then the string entered by
+ *  the user is placed in its parameter inBuf.
+ *
+ *  If the input is a fastpath function call (case 'F') then
+ *  the function call is processed in HandleFunctionRequest().
+ *  (now called from PostgresMain())
+ * ----------------
+ */
+
+static char
+SocketBackend(char *inBuf, int multiplexedBackend)
+{
+    char qtype[2];
+    char result;
+    
+    /* ----------------
+     * get input from the frontend
+     * ----------------
+     */
+    (void) strcpy(qtype, "?");
+    if (pq_getnchar(qtype,0,1) == EOF) {
+       /* ------------
+        *  when front-end applications quits/dies
+        * ------------
+        */
+       if (multiplexedBackend) {
+           return 'X';
+       }
+       else
+           exitpg(0);
+    }
+    
+    switch(*qtype) {
+       /* ----------------
+        *  'Q': user entered a query
+        * ----------------
+        */
+    case 'Q':
+       pq_getstr(inBuf, MAX_PARSE_BUFFER);
+       result = 'Q';
+       break;
+       
+       /* ----------------
+        *  'F':  calling user/system functions
+        * ----------------
+        */
+    case 'F':  
+       pq_getstr(inBuf, MAX_PARSE_BUFFER);/* ignore the rest of the line */
+        result = 'F';
+        break;
+       
+       /* ----------------
+        *  'X':  frontend is exiting
+        * ----------------
+        */
+    case 'X':
+       result = 'X';
+       break;
+       
+       /* ----------------
+        *  otherwise we got garbage from the frontend.
+        *
+        *  XXX are we certain that we want to do an elog(FATAL) here?
+        *      -cim 1/24/90
+        * ----------------
+        */
+    default:
+       elog(FATAL, "Socket command type %c unknown\n", *qtype);
+       break;
+    }
+    return result;
+}
+
+/* ----------------
+ *     ReadCommand reads a command from either the frontend or
+ *     standard input, places it in inBuf, and returns a char
+ *     representing whether the string is a 'Q'uery or a 'F'astpath
+ *     call.
+ * ----------------
+ */
+static char
+ReadCommand(char *inBuf, int multiplexedBackend)
+{
+    if (IsUnderPostmaster || multiplexedBackend)
+       return SocketBackend(inBuf, multiplexedBackend);
+    else
+       return InteractiveBackend(inBuf);
+}
+
+List *
+pg_plan(char *query_string,    /* string to execute */
+       Oid *typev,             /* argument types */
+       int nargs,              /* number of arguments */
+       QueryTreeList **queryListP,  /* pointer to the parse trees */
+       CommandDest dest)       /* where results should go */
+{
+    QueryTreeList *querytree_list;
+    int i;
+    List *plan_list = NIL;
+    Plan *plan;
+    int j;
+    QueryTreeList *new_list; 
+    List *rewritten = NIL;
+    Query* querytree;
+
+    /* ----------------
+     * (1) parse the request string into a list of parse trees
+     * ----------------
+     */
+    if (ShowParserStats)
+       ResetUsage();
+    
+    querytree_list = parser(query_string, typev, nargs);
+    
+    if (ShowParserStats) {
+       fprintf(stderr, "! Parser Stats:\n");
+       ShowUsage();
+    }
+
+    /* new_list holds the rewritten queries */
+    new_list = (QueryTreeList*)malloc(sizeof(QueryTreeList));
+    new_list->len = querytree_list->len;
+    new_list->qtrees = (Query**)malloc(new_list->len * sizeof(Query*));
+
+    /* ----------------
+     * (2) rewrite the queries, as necessary     
+     * ----------------
+     */
+    j = 0; /* counter for the new_list, new_list can be longer than
+             old list as a result of rewrites */
+    for (i=0;i<querytree_list->len;i++) {
+        querytree = querytree_list->qtrees[i];
+       
+
+       /* don't rewrite utilites */
+       if (querytree->commandType == CMD_UTILITY) {
+           new_list->qtrees[j++] = querytree;
+           continue;
+       }
+       
+       if ( DebugPrintParse == true ) {
+           printf("\ninput string is \"%s\"\n",query_string);
+           printf("\n---- \tparser outputs :\n");
+           nodeDisplay(querytree);
+           printf("\n");
+       }
+       
+       /* rewrite queries (retrieve, append, delete, replace) */
+       rewritten = QueryRewrite(querytree);
+       if (rewritten != NULL) {
+         int len, k;
+         len = length(rewritten);
+         if (len == 1)
+           new_list->qtrees[j++] = (Query*)lfirst(rewritten); 
+         else {
+           /* rewritten queries are longer than original query */
+           /* grow the new_list to accommodate */
+           new_list->len += len - 1; /* - 1 because originally we 
+                                        allocated one space for the query */
+           new_list->qtrees = realloc(new_list->qtrees, 
+                                      new_list->len * sizeof(Query*));
+           for (k=0;k<len;k++)
+             new_list->qtrees[j++] = (Query*)nth(k, rewritten);
+         }
+       }
+    }
+    
+    /* we're done with the original lists, free it */
+    free(querytree_list->qtrees);
+    free(querytree_list);
+
+    querytree_list = new_list;
+
+    /* ----------------
+     * Fix time range quals
+     * this _must_ go here, because it must take place after rewrites
+     * ( if they take place ) so that time quals are usable by the executor
+     *
+     * Also, need to frob the range table entries here to plan union
+     * queries for archived relations.
+     * ----------------
+     */
+    for (i=0;i<querytree_list->len;i++) {
+       List *l;
+       List *rt = NULL;
+
+       querytree = querytree_list->qtrees[i];
+
+       /* ----------------
+        *  utilities don't have time ranges
+        * ----------------
+        */
+       if (querytree->commandType == CMD_UTILITY)
+           continue;
+       
+       rt = querytree->rtable;
+       
+       foreach (l, rt) {
+           RangeTblEntry *rte = lfirst(l);
+           TimeRange *timequal = rte->timeRange;
+
+           if (timequal) {
+               int timecode = (rte->timeRange->endDate == NULL)? 0 : 1;
+
+               rte->timeQual = makeTimeRange(rte->timeRange->startDate,
+                                             rte->timeRange->endDate,
+                                             timecode);
+           }else {
+               rte->timeQual = NULL;
+           }
+       }
+       
+       /* check for archived relations */
+       plan_archive(rt);
+    }
+    
+    if (DebugPrintRewrittenParsetree == true) {
+       printf("\n=================\n");
+       printf("  After Rewriting\n");
+       printf("=================\n");
+
+       for (i=0; i<querytree_list->len; i++) {
+           print(querytree_list->qtrees[i]);
+           printf("\n");
+       }
+    }
+    
+    for (i=0; i<querytree_list->len;i++) {
+        querytree = querytree_list->qtrees[i];
+       
+       /*
+        *  For each query that isn't a utility invocation,
+        *  generate a plan.
+        */
+       
+       if (querytree->commandType != CMD_UTILITY) {
+           
+           if (IsAbortedTransactionBlockState()) {
+               /* ----------------
+                *   the EndCommand() stuff is to tell the frontend
+                *   that the command ended. -cim 6/1/90
+                * ----------------
+                */
+               char *tag = "*ABORT STATE*";
+               EndCommand(tag, dest);
+               
+               elog(NOTICE, "(transaction aborted): %s",
+                    "queries ignored until END");
+               
+               *queryListP = (QueryTreeList*)NULL;
+               return (List*)NULL;
+           }
+           
+           if (ShowPlannerStats) ResetUsage();
+           plan = planner(querytree);
+           if (ShowPlannerStats) {
+               fprintf(stderr, "! Planner Stats:\n");
+               ShowUsage();
+           }
+           plan_list = lappend(plan_list, plan);
+       }
+    }
+    
+    if (queryListP)
+       *queryListP = querytree_list;
+    
+    return (plan_list);
+}
+
+/* ----------------------------------------------------------------
+ *     pg_eval()
+ *     
+ *     Takes a querystring, runs the parser/utilities or
+ *     parser/planner/executor over it as necessary
+ *     Begin Transaction Should have been called before this
+ *     and CommitTransaction After this is called
+ *     This is strictly because we do not allow for nested xactions.
+ *
+ *     NON-OBVIOUS-RESTRICTIONS
+ *     this function _MUST_ allocate a new "parsetree" each time, 
+ *     since it may be stored in a named portal and should not 
+ *     change its value.
+ *
+ * ----------------------------------------------------------------
+ */
+
+void
+pg_eval(char *query_string, char *argv[], Oid *typev, int nargs)
+{
+    pg_eval_dest(query_string, argv, typev, nargs, whereToSendOutput);
+}
+
+void
+pg_eval_dest(char *query_string, /* string to execute */
+            char *argv[],      /* arguments */
+            Oid *typev,        /* argument types */
+            int nargs,         /* number of arguments */
+            CommandDest dest)  /* where results should go */
+{
+    List *plan_list; 
+    Plan *plan;
+    Query *querytree;
+    int i,j;
+    QueryTreeList *querytree_list;
+    
+    /* plan the queries */
+    plan_list = pg_plan(query_string, typev, nargs, &querytree_list, dest);
+    
+    /* pg_plan could have failed */
+    if (querytree_list == NULL)
+       return;
+
+    for (i=0;i<querytree_list->len;i++) {
+       querytree = querytree_list->qtrees[i];
+       
+       if (querytree->commandType == CMD_UTILITY) {
+           /* ----------------
+            *   process utility functions (create, destroy, etc..)
+            *
+            *   Note: we do not check for the transaction aborted state
+            *   because that is done in ProcessUtility.
+            * ----------------
+            */
+           if (! Quiet) {
+               time(&tim);
+               printf("\tProcessUtility() at %s\n", ctime(&tim));
+           }
+           
+           ProcessUtility(querytree->utilityStmt, dest);
+           
+       } else {
+           plan = (Plan *) lfirst(plan_list);
+           plan_list = lnext(plan_list);
+           
+           /* ----------------
+            *  print plan if debugging
+            * ----------------
+            */
+           if ( DebugPrintPlan == true ) {
+               printf("\nPlan is :\n");
+               nodeDisplay(plan);
+               printf("\n");
+           }
+           
+           /* ----------------
+            *   execute the plan
+            *
+            */
+           if (ShowExecutorStats)
+               ResetUsage();
+           
+           for (j = 0; j < _exec_repeat_; j++) {
+               if (! Quiet) {
+                   time(&tim);
+                   printf("\tProcessQuery() at %s\n", ctime(&tim));
+               }
+               ProcessQuery(querytree, plan, argv, typev, nargs, dest);
+           }
+           
+           if (ShowExecutorStats) {
+               fprintf(stderr, "! Executor Stats:\n");
+               ShowUsage();
+           }
+       }
+       /*
+        *  In a query block, we want to increment the command counter
+        *  between queries so that the effects of early queries are
+        *  visible to subsequent ones.
+        */
+       
+       if (querytree_list)
+           CommandCounterIncrement();
+    }
+
+    free(querytree_list->qtrees);
+    free(querytree_list);
+}
+
+/* --------------------------------
+ *     signal handler routines used in PostgresMain()
+ *
+ *     handle_warn() is used to catch kill(getpid(),1) which
+ *     occurs when elog(WARN) is called.
+ *
+ *      quickdie() occurs when signalled by the postmaster, some backend
+ *      has bought the farm we need to stop what we're doing and exit.
+ *
+ *     die() preforms an orderly cleanup via ExitPostgres()
+ * --------------------------------
+ */
+
+void
+handle_warn()
+{
+    siglongjmp(Warn_restart, 1);
+}
+
+void
+quickdie()
+{
+    elog(NOTICE, "I have been signalled by the postmaster.");
+    elog(NOTICE, "Some backend process has died unexpectedly and possibly");
+    elog(NOTICE, "corrupted shared memory.  The current transaction was");
+    elog(NOTICE, "aborted, and I am going to exit.  Please resend the");
+    elog(NOTICE, "last query. -- The postgres backend");
+    
+    /*
+     *  DO NOT ExitPostgres(0) -- we're here because shared memory may be
+     *  corrupted, so we don't want to flush any shared state to stable
+     *  storage.  Just nail the windows shut and get out of town.
+     */
+    
+    exit (0);
+}
+
+void
+die()
+{
+    ExitPostgres(0);
+}
+
+/* signal handler for floating point exception */
+void
+FloatExceptionHandler()
+{
+   elog(WARN, "floating point exception! the last floating point operation eit\
+her exceeded legal ranges or was a divide by zero");
+}
+
+
+static void usage(char* progname)
+{
+    fprintf(stderr, 
+           "Usage: %s [-B nbufs] [-d lvl] ] [-f plantype] \t[-m portno] [\t -o filename]\n",
+           progname);
+    fprintf(stderr,"\t[-P portno] [-t tracetype] [-x opttype] [-bCEiLNopQSs] [dbname]\n");
+    fprintf(stderr, "    b: consider bushy plan trees during optimization\n");
+    fprintf(stderr, "    B: set number of buffers in buffer pool\n");
+    fprintf(stderr, "    C: supress version info\n");
+    fprintf(stderr, "    d: set debug level\n");
+    fprintf(stderr, "    E: echo query before execution\n");
+    fprintf(stderr, "    f: forbid plantype generation\n");
+    fprintf(stderr, "    i: don't execute the query, just show the plan tree\n");
+    fprintf(stderr, "    L: turn off locking\n");
+    fprintf(stderr, "    m: set up a listening backend at portno to support multiple front-ends\n");
+    fprintf(stderr, "    M: start as postmaster\n");
+    fprintf(stderr, "    N: don't use newline as query delimiter\n");
+    fprintf(stderr, "    o: send stdout and stderr to given filename \n");
+    fprintf(stderr, "    p: backend started by postmaster\n");
+    fprintf(stderr, "    P: set port file descriptor\n");
+    fprintf(stderr, "    Q: suppress informational messages\n");
+    fprintf(stderr, "    S: assume stable main memory\n");
+    fprintf(stderr, "    s: show stats after each query\n");
+    fprintf(stderr, "    t: trace component execution times\n");
+    fprintf(stderr, "    T: execute all possible plans for each query\n");
+    fprintf(stderr, "    x: control expensive function optimization\n");
+}
+
+/* ----------------------------------------------------------------
+ *     PostgresMain
+ *       postgres main loop
+ *      all backends, interactive or otherwise start here
+ * ----------------------------------------------------------------
+ */
+int
+PostgresMain(int argc, char *argv[])
+{
+    int    flagC;
+    int           flagQ;
+    int           flagS;
+    int           flagE;
+    int           flag;
+    
+    char   *DBName; 
+    int    errs = 0;
+    
+    char   firstchar;
+    char   parser_input[MAX_PARSE_BUFFER];
+    char *userName;
+    
+    int    multiplexedBackend = 0;
+    char*  hostName;                /* the host name of the backend server */
+    char   hostbuf[MAXHOSTNAMELEN];
+    int    serverSock;
+    int    serverPortnum;
+    int    nSelected; /* number of descriptors ready from select(); */
+    int    maxFd; /* max file descriptor + 1 */
+    fd_set rmask, basemask;
+    FrontEnd *newFE, *currentFE;
+    int    numFE = 0; /* keep track of number of active frontends */
+    Port   *newPort;
+    int    newFd;
+    Dlelem *curr;
+    int    status;
+
+#ifdef WIN32
+    WSADATA WSAData;
+#endif /* WIN32 */
+
+    extern int   optind;
+    extern char          *optarg;
+    extern short  DebugLvl;
+    
+    /* ----------------
+     *         register signal handlers.
+     * ----------------
+     */
+    signal(SIGINT, die);
+
+#ifndef WIN32
+    signal(SIGHUP, die);
+    signal(SIGTERM, die);
+    signal(SIGPIPE, die);
+    signal(SIGUSR1, quickdie);
+    signal(SIGUSR2, Async_NotifyHandler);
+    signal(SIGFPE, FloatExceptionHandler);
+#endif /* WIN32 */
+    
+    /* --------------------
+     * initialize globals 
+     * -------------------
+     */
+    
+    InitGlobals();
+
+    /* ----------------
+     * parse command line arguments
+     * ----------------
+     */
+    flagC = flagQ = flagS = flagE = ShowStats = 0;
+    ShowParserStats = ShowPlannerStats = ShowExecutorStats = 0;
+    
+    /* get hostname is either the environment variable PGHOST
+       or 'localhost' */
+    if (!(hostName = getenv("PGHOST"))) {
+       if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0)
+           (void) strcpy(hostbuf, "localhost");
+       hostName = hostbuf;
+    }
+
+    while ((flag = getopt(argc, argv, "B:bCd:Ef:iLm:MNo:P:pQSst:x:")) != EOF)
+       switch (flag) {
+           
+       case 'b':
+           /* ----------------
+            *  set BushyPlanFlag to true.
+            * ----------------
+            */
+           BushyPlanFlag = 1;
+           break;
+       case 'B':
+           /* ----------------
+            *  specify the size of buffer pool
+            * ----------------
+            */
+           NBuffers = atoi(optarg);
+           break;
+           
+       case 'C':
+           /* ----------------
+            *  don't print version string (don't know why this is 'C' --mao)
+            * ----------------
+            */
+           flagC = 1;
+           break;
+           
+           /* ----------------
+            *  -debug mode
+            * ----------------
+            */
+       case 'd':
+           /* DebugMode = true;  */
+           flagQ = 0;
+           DebugPrintPlan = true;
+           DebugPrintParse = true;
+           DebugPrintRewrittenParsetree = true;
+           DebugLvl = (short)atoi(optarg);
+           break;
+           
+       case 'E':
+           /* ----------------
+            *  E - echo the query the user entered
+            * ----------------
+            */
+           flagE = 1;
+           break;
+           
+       case 'f':
+           /* -----------------
+            *    f - forbid generation of certain plans
+            * -----------------
+            */
+           switch (optarg[0]) {
+           case 's': /* seqscan */
+                _enable_seqscan_ = false;
+                break;
+           case 'i': /* indexscan */
+                _enable_indexscan_ = false;
+                break;
+           case 'n': /* nestloop */
+                _enable_nestloop_ = false;
+                break;
+           case 'm': /* mergejoin */
+                _enable_mergesort_ = false;
+                break;
+           case 'h': /* hashjoin */
+                _enable_hashjoin_ = false;
+                break;
+           default:
+                errs++;
+           }
+           break;
+
+       case 'i':
+           dontExecute = 1;
+           break;
+           
+       case 'L':
+           /* --------------------
+            *  turn off locking
+            * --------------------
+            */
+           lockingOff = 1;
+           break;
+           
+       case 'm':
+           /* start up a listening backend that can respond to 
+              multiple front-ends.  (Note:  all the front-end connections
+              are still connected to a single-threaded backend.  Requests
+              are FCFS.  Everything is in one transaction 
+              */
+           multiplexedBackend = 1;
+           serverPortnum = atoi(optarg);
+#ifdef WIN32
+           /* There was no postmaster started so the shared memory
+           ** for the shared memory table hasn't been allocated so
+           ** do it now.
+           */
+           _nt_init();
+#endif /* WIN32 */
+           break;
+       case 'M':
+           exit(PostmasterMain(argc, argv));
+           break;
+       case 'N':
+           /* ----------------
+            *  N - Don't use newline as a query delimiter
+            * ----------------
+            */
+           UseNewLine = 0;
+           break;
+           
+       case 'o':
+           /* ----------------
+            *  o - send output (stdout and stderr) to the given file
+            * ----------------
+            */
+           (void) strncpy(OutputFileName, optarg, MAXPGPATH);
+           break;
+           
+       case 'p':       /* started by postmaster */
+           /* ----------------
+            *  p - special flag passed if backend was forked
+            *      by a postmaster.
+            * ----------------
+            */
+           IsUnderPostmaster = true;
+           break;
+           
+       case 'P':
+           /* ----------------
+            *  P - Use the passed file descriptor number as the port
+            *    on which to communicate with the user.  This is ONLY
+            *    useful for debugging when fired up by the postmaster.
+            * ----------------
+            */
+           Portfd = atoi(optarg);
+           break;
+           
+       case 'Q':
+           /* ----------------
+            *  Q - set Quiet mode (reduce debugging output)
+            * ----------------
+            */
+           flagQ = 1;
+           break;
+           
+       case 'S':
+           /* ----------------
+            *  S - assume stable main memory
+            *      (don't flush all pages at end transaction)
+            * ----------------
+            */
+           flagS = 1;
+           SetTransactionFlushEnabled(false);
+           break;
+           
+       case 's':
+           /* ----------------
+            *    s - report usage statistics (timings) after each query
+            * ----------------
+            */
+           ShowStats = 1;
+           StatFp = stderr;
+           break;
+           
+       case 't':
+           /* ----------------
+            *  tell postgres to report usage statistics (timings) for
+            *  each query
+            *
+            *  -tpa[rser] = print stats for parser time of each query
+            *  -tpl[anner] = print stats for planner time of each query
+            *  -te[xecutor] = print stats for executor time of each query
+            *  caution: -s can not be used together with -t.
+            * ----------------
+            */
+           StatFp = stderr;
+           switch (optarg[0]) {
+           case 'p':  if (optarg[1] == 'a')
+               ShowParserStats = 1;
+           else if (optarg[1] == 'l')
+               ShowPlannerStats = 1;
+           else
+               errs++;
+               break;
+           case 'e':  ShowExecutorStats = 1;   break;
+           default:   errs++; break;
+           } 
+           break;
+           
+       case 'x':
+#if 0 /* planner/xfunc.h */
+           /* control joey hellerstein's expensive function optimization */
+           if (XfuncMode != 0)
+               {
+                   fprintf(stderr, "only one -x flag is allowed\n");
+                   errs++;
+                   break;
+               }
+           if (strcmp(optarg, "off") == 0)
+               XfuncMode = XFUNC_OFF;
+           else if (strcmp(optarg, "nor") == 0)
+               XfuncMode = XFUNC_NOR;
+           else if (strcmp(optarg, "nopull") == 0)
+               XfuncMode = XFUNC_NOPULL;
+           else if (strcmp(optarg, "nopm") == 0)
+               XfuncMode = XFUNC_NOPM;
+           else if (strcmp(optarg, "pullall") == 0)
+               XfuncMode = XFUNC_PULLALL;
+           else if (strcmp(optarg, "wait") == 0)
+               XfuncMode = XFUNC_WAIT;
+           else {
+               fprintf(stderr, "use -x {off,nor,nopull,nopm,pullall,wait}\n");
+               errs++;
+           }
+#endif
+           break;
+           
+       default:
+           /* ----------------
+            *  default: bad command line option
+            * ----------------
+            */
+           errs++;
+       }
+    
+    /* ----------------
+     * get user name and pathname and check command line validity
+     * ----------------
+     */
+    SetPgUserName();
+    userName = GetPgUserName();
+    
+    if (FindBackend(pg_pathname, argv[0]) < 0)
+       elog(FATAL, "%s: could not locate executable, bailing out...",
+            argv[0]);
+    
+    if (errs || argc - optind > 1) {
+       usage (argv[0]);
+       exitpg(1);
+    } else if (argc - optind == 1) {
+       DBName = argv[optind];
+    } else if ((DBName = userName) == NULL) {
+       fprintf(stderr, "%s: USER undefined and no database specified\n",
+               argv[0]);
+       exitpg(1);
+    }
+    
+    if (ShowStats && 
+       (ShowParserStats || ShowPlannerStats || ShowExecutorStats)) {
+       fprintf(stderr, "-s can not be used together with -t.\n");
+       exitpg(1);
+    }
+    
+    Noversion = flagC;
+    Quiet = flagQ;
+    EchoQuery = flagE;
+    
+    /* ----------------
+     *         print flags
+     * ----------------
+     */
+    if (! Quiet) {
+       puts("\t---debug info---");
+       printf("\tQuiet =        %c\n", Quiet     ? 't' : 'f');
+       printf("\tNoversion =    %c\n", Noversion ? 't' : 'f');
+       printf("\tstable    =    %c\n", flagS     ? 't' : 'f');
+       printf("\ttimings   =    %c\n", ShowStats ? 't' : 'f');
+       printf("\tbufsize   =    %d\n", NBuffers);
+       
+       printf("\tquery echo =   %c\n", EchoQuery ? 't' : 'f');
+       printf("\tmultiplexed backend? =  %c\n", multiplexedBackend ? 't' : 'f');
+       printf("\tDatabaseName = [%s]\n", DBName);
+       puts("\t----------------\n");
+    }
+    
+    /* ----------------
+     * initialize portal file descriptors
+     * ----------------
+     */
+    if (IsUnderPostmaster == true) {
+       if (Portfd < 0) {
+           fprintf(stderr,
+                   "Postmaster flag set: no port number specified, use /dev/null\n");
+           Portfd = open(NULL_DEV, O_RDWR, 0666);
+       }
+       pq_init(Portfd);
+    }
+
+#ifdef WIN32
+    if ((status = WSAStartup(MAKEWORD(1,1), &WSAData)) == 0)
+       (void) printf("%s\nInitializing WinSock: %s\n", WSAData.szDescription, WSAData.szSystemStatus);
+    else {
+       fprintf(stderr, "Error initializing WinSock: %d is the err", status);
+       exit(1);
+    }
+#endif /* WIN32 */
+    
+    if (multiplexedBackend) {
+      if (StreamServerPort(hostName, serverPortnum, &serverSock) != STATUS_OK)
+       {
+         fprintf(stderr, "Postgres: cannot create stream port %d\n", serverPortnum);
+         exit(1);
+       }
+/*
+{
+    char buf[100];
+    sprintf(buf, "stream port %d created, socket = %d\n", serverPortnum, serverSock);
+    puts(buf);
+}
+*/
+      FD_ZERO(&rmask);
+      FD_ZERO(&basemask);
+      FD_SET(serverSock, &basemask);  
+
+      frontendList = DLNewList();
+      /* add the original FrontEnd to the list */
+      if (IsUnderPostmaster == true) {
+       FrontEnd *fe = malloc(sizeof(FrontEnd));
+
+       FD_SET(Portfd, &basemask);
+       maxFd = Max(serverSock,Portfd) + 1;
+
+       fe->fn_connected = true;
+       fe->fn_Pfin = Pfin;
+       fe->fn_Pfout = Pfout;
+       fe->fn_done = false;
+       (fe->fn_port).sock = Portfd;
+       DLAddHead(frontendList, DLNewElem(fe));
+       numFE++;
+      } else {
+         numFE = 1;
+         maxFd = serverSock + 1;
+      }
+    }
+
+    if (IsUnderPostmaster || multiplexedBackend)
+       whereToSendOutput = Remote;
+    else 
+       whereToSendOutput = Debug;
+    
+    SetProcessingMode(InitProcessing);
+    
+    /* initialize */
+    if (! Quiet) {
+       puts("\tInitPostgres()..");
+    }
+#if WIN32
+     _nt_attach();
+#endif /* WIN32 */
+
+    InitPostgres(DBName);
+
+    /* ----------------
+     * if an exception is encountered, processing resumes here
+     *  so we abort the current transaction and start a new one.
+     *  This must be done after we initialize the slave backends
+     *  so that the slaves signal the master to abort the transaction
+     *  rather than calling AbortCurrentTransaction() themselves.
+     *
+     *  Note:  elog(WARN) causes a kill(getpid(),1) to occur sending
+     *         us back here.
+     * ----------------
+     */
+
+#ifndef WIN32    
+    signal(SIGHUP, handle_warn);
+
+    if (sigsetjmp(Warn_restart, 1) != 0) {
+#else
+    if (setjmp(Warn_restart) != 0) {
+#endif /* WIN32 */
+
+       time(&tim);
+       
+       if (! Quiet)
+           printf("\tAbortCurrentTransaction() at %s\n", ctime(&tim));
+
+       memset(parser_input, 0, MAX_PARSE_BUFFER);
+       
+       AbortCurrentTransaction();
+    }
+    
+    /* ----------------
+     * POSTGRES main processing loop begins here
+     * ----------------
+     */
+    if (IsUnderPostmaster == false) {
+       puts("\nPOSTGRES backend interactive interface");
+       puts("$Revision: 1.1.1.1 $ $Date: 1996/07/09 06:22:00 $");
+    }
+    
+    /* ----------------
+     * if stable main memory is assumed (-S flag is set), it is necessary
+     * to flush all dirty shared buffers before exit
+     * plai 8/7/90
+     * ----------------
+     */
+    if (!TransactionFlushEnabled())
+        on_exitpg(FlushBufferPool, (caddr_t) 0);
+    
+    for (;;) {
+      
+      if (multiplexedBackend) {
+       if (numFE == 0) 
+         break;
+
+       memmove((char *) &rmask, (char *) &basemask, sizeof(fd_set));
+       nSelected = select(maxFd, &rmask,0,0,0);
+
+       if (nSelected < 0) {
+
+         if (errno == EINTR) continue;
+         fprintf(stderr,"postgres: multiplexed backend select failed\n");
+         exitpg(1);
+       }
+       if (FD_ISSET(serverSock, &rmask)) {
+       /* new connection pending on our well-known port's socket */
+         newFE = (FrontEnd*) malloc (sizeof(FrontEnd));
+         memset(newFE, sizeof(FrontEnd),0);
+         newFE->fn_connected = false;
+         newFE->fn_done = false;
+         newPort = &(newFE->fn_port);
+         if (StreamConnection(serverSock,newPort) != STATUS_OK) {
+           StreamClose(newPort->sock);
+           newFd = -1;
+         }
+         else {
+           DLAddHead(frontendList, DLNewElem(newFE));
+           numFE++;
+           newFd = newPort->sock;
+           if (newFd >= maxFd) maxFd = newFd + 1;
+           FD_SET(newFd, &rmask);
+           FD_SET(newFd, &basemask);
+           --nSelected;
+           FD_CLR(serverSock, &rmask);
+         }
+         continue;
+       } /* if FD_ISSET(serverSock) */
+
+        /* if we get here, it means that the serverSocket was not the one
+          selected.  Instead, one of the front ends was selected.
+          find which one */
+       curr = DLGetHead(frontendList);
+       while (curr) {
+         FrontEnd *fe = (FrontEnd*)DLE_VAL(curr);
+         Port *port = &(fe->fn_port);
+
+         /* this is lifted from postmaster.c */
+         if (FD_ISSET(port->sock, &rmask)) {
+           if (fe->fn_connected == false) {
+               /* we have a message from a new frontEnd */
+               status = PacketReceive(port, &port->buf, NON_BLOCKING);
+               if (status == STATUS_OK) {
+                 fe->fn_connected = true;
+                 pq_init(port->sock);
+                 fe->fn_Pfin = Pfin;
+                 fe->fn_Pfout = Pfout;
+               }
+               else
+                 fprintf(stderr,"Multiplexed backend: error in reading packets from %d\n", port->sock);
+               }
+           else  /* we have a query from an existing,  active FrontEnd */
+             {
+               Pfin = fe->fn_Pfin;
+               Pfout = fe->fn_Pfout;
+               currentFE = fe;
+              }
+           if (fe->fn_done)
+               {
+                   Dlelem *c = curr;
+                   curr = DLGetSucc(curr);
+                   DLRemove(c);
+               }
+             break;
+             }
+         else
+           curr = DLGetSucc(curr);
+       }
+    }
+       /* ----------------
+        *   (1) read a command. 
+        * ----------------
+        */
+       memset(parser_input, 0, MAX_PARSE_BUFFER);
+
+       firstchar = ReadCommand(parser_input, multiplexedBackend);
+       /* process the command */
+       switch (firstchar) {
+           /* ----------------
+            *  'F' indicates a fastpath call.
+            *      XXX HandleFunctionRequest
+            * ----------------
+            */
+       case 'F':
+           IsEmptyQuery = false;
+           
+           /* start an xact for this function invocation */
+           if (! Quiet) {
+               time(&tim);
+               printf("\tStartTransactionCommand() at %s\n", ctime(&tim));
+           }
+           
+           StartTransactionCommand();
+           HandleFunctionRequest();
+           break;
+           
+           /* ----------------
+            *  'Q' indicates a user query
+            * ----------------
+            */
+       case 'Q':
+           fflush(stdout);
+           
+           if ( parser_input[0] ==  ' ' && parser_input[1] == '\0' ) {
+               /* ----------------
+                *  if there is nothing in the input buffer, don't bother
+                *  trying to parse and execute anything..
+                * ----------------
+                */
+               IsEmptyQuery = true;
+           } else {
+               /* ----------------
+                *  otherwise, process the input string.
+                * ----------------
+                */
+               IsEmptyQuery = false;
+               if (ShowStats)
+                   ResetUsage();
+               
+               /* start an xact for this query */
+               if (! Quiet) {
+                   time(&tim);
+                   printf("\tStartTransactionCommand() at %s\n", ctime(&tim));
+               }
+               StartTransactionCommand();
+               
+               pg_eval(parser_input, (char **) NULL, (Oid *) NULL, 0);
+               
+               if (ShowStats)
+                   ShowUsage();
+           }
+           break;
+           
+           /* ----------------
+            *  'X' means that the frontend is closing down the socket
+            * ----------------
+            */
+       case 'X':
+           IsEmptyQuery = true;
+            if (multiplexedBackend) {
+               FD_CLR(currentFE->fn_port.sock, &basemask);
+              currentFE->fn_done = true;
+               numFE--;
+            }
+           pq_close();
+           break;
+           
+       default:
+           elog(WARN,"unknown frontend message was recieved");
+       }
+       
+       /* ----------------
+        *   (3) commit the current transaction
+        *
+        *   Note: if we had an empty input buffer, then we didn't
+        *   call pg_eval, so we don't bother to commit this transaction.
+        * ----------------
+        */
+       if (! IsEmptyQuery) {
+           if (! Quiet) {
+               time(&tim);
+               printf("\tCommitTransactionCommand() at %s\n", ctime(&tim));
+           }
+           CommitTransactionCommand();
+           
+       } else {
+           if (IsUnderPostmaster || multiplexedBackend)
+               NullCommand(Remote);
+       }
+       
+} /* infinite for-loop */
+  exitpg(0);
+  return 1;
+}
+
+#ifndef WIN32
+#ifdef NEED_RUSAGE
+#include "rusagestub.h"
+#else /* NEED_RUSAGE */
+#include <sys/resource.h>
+#endif /* NEED_RUSAGE */
+
+struct rusage Save_r;
+struct timeval Save_t;
+
+void
+ResetUsage()
+{
+    struct timezone tz;
+    getrusage(RUSAGE_SELF, &Save_r);
+    gettimeofday(&Save_t, &tz);
+    ResetBufferUsage();
+/*    ResetTupleCount(); */
+}
+
+void
+ShowUsage()
+{
+    struct timeval user, sys;
+    struct timeval elapse_t;
+    struct timezone tz;
+    struct rusage r;
+    
+    getrusage(RUSAGE_SELF, &r);
+    gettimeofday(&elapse_t, &tz);
+    memmove((char *)&user, (char *)&r.ru_utime, sizeof(user)); 
+    memmove((char *)&sys, (char *)&r.ru_stime,sizeof(sys)); 
+    if (elapse_t.tv_usec < Save_t.tv_usec) {
+       elapse_t.tv_sec--;
+       elapse_t.tv_usec += 1000000;
+    }
+    if (r.ru_utime.tv_usec < Save_r.ru_utime.tv_usec) {
+       r.ru_utime.tv_sec--;
+       r.ru_utime.tv_usec += 1000000;
+    }
+    if (r.ru_stime.tv_usec < Save_r.ru_stime.tv_usec) {
+       r.ru_stime.tv_sec--;
+       r.ru_stime.tv_usec += 1000000;
+    }
+    
+    /*
+     *  the only stats we don't show here are for memory usage -- i can't
+     *  figure out how to interpret the relevant fields in the rusage
+     *  struct, and they change names across o/s platforms, anyway.
+     *  if you can figure out what the entries mean, you can somehow
+     *  extract resident set size, shared text size, and unshared data
+     *  and stack sizes.
+     */
+    
+    fprintf(StatFp, "! system usage stats:\n");
+    fprintf(StatFp, 
+           "!\t%d.%06d elapsed %d.%06d user %d.%06d system sec\n",
+           elapse_t.tv_sec - Save_t.tv_sec,
+           elapse_t.tv_usec - Save_t.tv_usec,
+           r.ru_utime.tv_sec - Save_r.ru_utime.tv_sec,
+           r.ru_utime.tv_usec - Save_r.ru_utime.tv_usec,
+           r.ru_stime.tv_sec - Save_r.ru_stime.tv_sec,
+           r.ru_stime.tv_usec - Save_r.ru_stime.tv_usec);
+    fprintf(StatFp,
+           "!\t[%d.%06d user %d.%06d sys total]\n",
+           user.tv_sec, user.tv_usec, sys.tv_sec, sys.tv_usec);
+#ifndef NEED_RUSAGE
+    fprintf(StatFp, 
+           "!\t%ld/%ld [%ld/%ld] filesystem blocks in/out\n",
+           r.ru_inblock - Save_r.ru_inblock,
+           /* they only drink coffee at dec */
+           r.ru_oublock - Save_r.ru_oublock,
+           r.ru_inblock, r.ru_oublock);
+    fprintf(StatFp, 
+           "!\t%ld/%ld [%ld/%ld] page faults/reclaims, %ld [%ld] swaps\n",
+           r.ru_majflt - Save_r.ru_majflt,
+           r.ru_minflt - Save_r.ru_minflt,
+           r.ru_majflt, r.ru_minflt,
+           r.ru_nswap - Save_r.ru_nswap,
+           r.ru_nswap);
+    fprintf(StatFp, 
+           "!\t%ld [%ld] signals rcvd, %ld/%ld [%ld/%ld] messages rcvd/sent\n",
+           r.ru_nsignals - Save_r.ru_nsignals,
+           r.ru_nsignals,
+           r.ru_msgrcv - Save_r.ru_msgrcv,
+           r.ru_msgsnd - Save_r.ru_msgsnd,
+           r.ru_msgrcv, r.ru_msgsnd);
+    fprintf(StatFp, 
+           "!\t%ld/%ld [%ld/%ld] voluntary/involuntary context switches\n",
+           r.ru_nvcsw - Save_r.ru_nvcsw,
+           r.ru_nivcsw - Save_r.ru_nivcsw,
+           r.ru_nvcsw, r.ru_nivcsw);
+#endif /* NEED_RUSAGE */
+    fprintf(StatFp, "! postgres usage stats:\n");
+    PrintBufferUsage(StatFp);
+/*     DisplayTupleCount(StatFp); */
+}
+#else
+void
+ShowUsage()
+{}
+
+void
+ResetUsage()
+{}
+#endif /* WIN32 */
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
new file mode 100644 (file)
index 0000000..e93c690
--- /dev/null
@@ -0,0 +1,362 @@
+/*-------------------------------------------------------------------------
+ *
+ * pquery.c--
+ *    POSTGRES process query command code
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "tcop/tcopdebug.h"
+
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/mcxt.h"
+#include "miscadmin.h"
+#include "utils/portal.h"
+
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
+#include "nodes/memnodes.h"
+
+#include "tcop/dest.h"
+
+#include "executor/execdefs.h"
+#include "executor/execdesc.h"
+#include "executor/executor.h"
+#include "tcop/pquery.h"
+
+#include "commands/command.h"
+
+static char* CreateOperationTag(int operationType);
+
+/* ----------------------------------------------------------------
+ *     CreateQueryDesc
+ * ----------------------------------------------------------------
+ */
+QueryDesc *
+CreateQueryDesc(Query *parsetree,
+               Plan *plantree,
+               CommandDest dest)
+{
+    QueryDesc *qd = (QueryDesc *)palloc(sizeof(QueryDesc));
+    
+    qd->operation = parsetree->commandType;    /* operation */
+    qd->parsetree = parsetree;                 /* parse tree */
+    qd->plantree  = plantree;                  /* plan */
+    qd->dest      = dest;                      /* output dest */
+    return qd;
+}
+
+/* ----------------------------------------------------------------
+ *     CreateExecutorState
+ *
+ *     Note: this may someday take parameters -cim 9/18/89
+ * ----------------------------------------------------------------
+ */
+EState *
+CreateExecutorState()
+{
+    EState             *state;
+    extern int         NBuffers;
+    long               *refcount;
+    
+    /* ----------------
+     * create a new executor state
+     * ----------------
+     */
+    state = makeNode(EState);
+    
+    /* ----------------
+     * initialize the Executor State structure
+     * ----------------
+     */
+    state->es_direction = EXEC_FRWD;
+    state->es_range_table = NIL;
+
+    state->es_into_relation_descriptor = NULL;
+    state->es_result_relation_info = NULL;
+    
+    state->es_param_list_info = NULL;
+    
+    state->es_BaseId = 0;
+    state->es_tupleTable = NULL;
+    
+    state->es_junkFilter = NULL;
+    
+    refcount = (long *) palloc(NBuffers * sizeof(long));
+    memset((char *) refcount, 0, NBuffers * sizeof(long));
+    state->es_refcount = (int *) refcount;
+    
+    /* ----------------
+     * return the executor state structure
+     * ----------------
+     */
+    return state;
+}
+
+/* ----------------------------------------------------------------
+ *     CreateOperationTag
+ *
+ *     utility to get a string representation of the
+ *     query operation.
+ * ----------------------------------------------------------------
+ */
+static char*
+CreateOperationTag(int operationType)
+{
+    char* tag;
+    
+    switch (operationType) {
+    case CMD_SELECT:
+       tag = "SELECT";
+       break;
+    case CMD_INSERT:
+       tag = "INSERT";
+       break;
+    case CMD_DELETE:
+       tag = "DELETE";
+       break;
+    case CMD_UPDATE:
+       tag = "UPDATE";
+       break;
+    default:
+       elog(DEBUG, "CreateOperationTag: unknown operation type %d", 
+            operationType);
+       tag = NULL;
+       break;
+    }
+    
+    return tag;
+}
+
+/* ----------------
+ *     ProcessPortal
+ * ----------------
+ */
+
+void
+ProcessPortal(char* portalName,
+             Query *parseTree,
+             Plan *plan,
+             EState *state,
+             TupleDesc attinfo, 
+             CommandDest dest)
+{
+    Portal             portal;
+    MemoryContext      portalContext;
+    
+    /* ----------------
+     *   convert the current blank portal into the user-specified
+     *   portal and initialize the state and query descriptor.
+     * ----------------
+     */
+    
+    if (PortalNameIsSpecial(portalName))
+       elog(WARN,
+            "The portal name %s is reserved for internal use",
+            portalName);
+    
+    portal = BlankPortalAssignName(portalName);
+    
+    PortalSetQuery(portal,
+                  CreateQueryDesc(parseTree, plan, dest),
+                  attinfo,
+                  state,
+                  PortalCleanup);
+    
+    /* ----------------
+     * now create a new blank portal and switch to it. 
+     * Otherwise, the new named portal will be cleaned.
+     *
+     *  Note: portals will only be supported within a BEGIN...END
+     *  block in the near future.  Later, someone will fix it to
+     *  do what is possible across transaction boundries. -hirohama
+     * ----------------
+     */
+    portalContext = (MemoryContext)
+       PortalGetHeapMemory(GetPortalByName(NULL));
+    
+    MemoryContextSwitchTo(portalContext);
+    
+    StartPortalAllocMode(DefaultAllocMode, 0);
+}
+
+
+/* ----------------------------------------------------------------
+ *     ProcessQueryDesc
+ *
+ *     Read the comments for ProcessQuery() below...
+ * ----------------------------------------------------------------
+ */
+void
+ProcessQueryDesc(QueryDesc *queryDesc)
+{
+    Query      *parseTree;
+    Plan       *plan;
+    int                operation;
+    char*      tag;
+    EState     *state;
+    TupleDesc   attinfo;
+    
+    bool       isRetrieveIntoPortal;
+    bool       isRetrieveIntoRelation;
+    char*      intoName;
+    CommandDest dest;
+    
+    /* ----------------
+     * get info from the query desc
+     * ----------------
+     */
+    parseTree = queryDesc->parsetree;
+    plan =     queryDesc->plantree;
+    
+    operation = queryDesc->operation;
+    tag =      CreateOperationTag(operation);
+    dest =     queryDesc->dest;
+    
+    /* ----------------
+     * initialize portal/into relation status
+     * ----------------
+     */
+    isRetrieveIntoPortal =   false;
+    isRetrieveIntoRelation = false;
+    
+    if (operation == CMD_SELECT) {
+       if (parseTree->isPortal) {
+           isRetrieveIntoPortal = true;
+           intoName = parseTree->into;
+           if (parseTree->isBinary) {
+               /*
+                * For internal format portals, we change Remote
+                * (externalized form) to RemoteInternal (internalized
+                * form)
+                */
+               dest = queryDesc->dest = RemoteInternal;
+           }
+       } else if (parseTree->into != NULL) {
+           /* select into table */
+           isRetrieveIntoRelation = true;
+       }
+
+    }
+    
+    /* ----------------
+     * when performing a retrieve into, we override the normal
+     *  communication destination during the processing of the
+     *  the query.  This only affects the tuple-output function
+     *  - the correct destination will still see BeginCommand()
+     *  and EndCommand() messages.
+     * ----------------
+     */
+    if (isRetrieveIntoRelation)
+       queryDesc->dest = (int) None;
+    
+    /* ----------------
+     * create a default executor state.. 
+     * ----------------
+     */
+    state = CreateExecutorState();
+    
+    /* ----------------
+     * call ExecStart to prepare the plan for execution
+     * ----------------
+     */
+    attinfo = ExecutorStart(queryDesc, state);
+    
+    /* ----------------
+     *   report the query's result type information
+     *   back to the front end or to whatever destination
+     *   we're dealing with.
+     * ----------------
+     */
+    BeginCommand(NULL,
+                operation,
+                attinfo,
+                isRetrieveIntoRelation,
+                isRetrieveIntoPortal,
+                tag,
+                dest);
+    
+    /* ----------------
+     *  Named portals do not do a "fetch all" initially, so now
+     *  we return since ExecMain has been called with EXEC_START
+     *  to initialize the query plan.  
+     *
+     *  Note: ProcessPortal transforms the current "blank" portal
+     *        into a named portal and creates a new blank portal so
+     *       everything we allocated in the current "blank" memory
+     *       context will be preserved across queries.  -cim 2/22/91
+     * ----------------
+     */
+    if (isRetrieveIntoPortal) {
+       PortalExecutorHeapMemory = NULL;
+       
+       ProcessPortal(intoName,
+                     parseTree,
+                     plan,
+                     state,
+                     attinfo,
+                     dest);
+       
+       EndCommand(tag, dest);
+       return;
+    }
+    
+    /* ----------------
+     *   Now we get to the important call to ExecutorRun() where we
+     *   actually run the plan..
+     * ----------------
+     */
+    ExecutorRun(queryDesc, state, EXEC_RUN, 0);
+    
+    /* ----------------
+     *   now, we close down all the scans and free allocated resources...
+     * with ExecutorEnd()
+     * ----------------
+     */
+    ExecutorEnd(queryDesc, state);
+    
+    /* ----------------
+     *  Notify the destination of end of processing.
+     * ----------------
+     */
+    EndCommand(tag, dest);
+}
+
+/* ----------------------------------------------------------------
+ *     ProcessQuery
+ *
+ *     Execute a plan, the non-parallel version
+ * ----------------------------------------------------------------
+ */
+
+void
+ProcessQuery(Query *parsetree,
+            Plan *plan,
+            char *argv[],
+            Oid *typev,
+            int nargs,
+            CommandDest dest)
+{
+    QueryDesc *queryDesc;
+    extern int dontExecute; /* from postgres.c */
+    extern void print_plan (Plan* p, Query* parsetree); /* from print.c */
+
+    queryDesc = CreateQueryDesc(parsetree, plan, dest);
+
+    if (dontExecute) {
+       /* don't execute it, just show the query plan */
+       print_plan(plan, parsetree);
+    } else
+       ProcessQueryDesc(queryDesc);
+}
+
diff --git a/src/backend/tcop/pquery.h b/src/backend/tcop/pquery.h
new file mode 100644 (file)
index 0000000..a2ac8b9
--- /dev/null
@@ -0,0 +1,36 @@
+/*-------------------------------------------------------------------------
+ *
+ * pquery.h--
+ *    prototypes for pquery.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PQUERY_H
+#define PQUERY_H
+
+#include "executor/execdesc.h"
+#include "tcop/dest.h"
+
+/* moved to execdesc.h 
+extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree,
+                                 CommandDest dest);
+
+*/
+extern EState *CreateExecutorState();
+
+
+extern void ProcessPortal(char *portalName, Query *parseTree,
+                         Plan *plan, EState *state, TupleDesc attinfo, 
+                         CommandDest dest);
+
+extern void ProcessQueryDesc(QueryDesc *queryDesc);
+
+extern void ProcessQuery(Query *parsetree, Plan *plan, char *argv[], 
+                        Oid *typev, int nargs, CommandDest dest);
+
+#endif /* pqueryIncluded */
diff --git a/src/backend/tcop/tcopdebug.h b/src/backend/tcop/tcopdebug.h
new file mode 100644 (file)
index 0000000..a57a824
--- /dev/null
@@ -0,0 +1,43 @@
+/*-------------------------------------------------------------------------
+ *
+ * tcopdebug.h--
+ *    #defines governing debugging behaviour in the traffic cop
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef TCOPDEBUG_H
+#define TCOPDEBUG_H
+
+/* ----------------------------------------------------------------
+ *     debugging defines.
+ *
+ *     If you want certain debugging behaviour, then #define
+ *     the variable to 1, else #undef it. -cim 10/26/89
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *     TCOP_SHOWSTATS controls whether or not buffer and
+ *     access method statistics are shown for each query.  -cim 2/9/89
+ * ----------------
+ */
+#undef TCOP_SHOWSTATS
+
+/* ----------------
+ *     TCOP_DONTUSENEWLINE controls the default setting of
+ *     the UseNewLine variable in postgres.c
+ * ----------------
+ */
+#undef TCOP_DONTUSENEWLINE
+
+/* ----------------------------------------------------------------
+ *     #defines controlled by above definitions
+ * ----------------------------------------------------------------
+ */
+
+#endif  /* TCOPDEBUG_H */
diff --git a/src/backend/tcop/tcopprot.h b/src/backend/tcop/tcopprot.h
new file mode 100644 (file)
index 0000000..199f2ee
--- /dev/null
@@ -0,0 +1,40 @@
+/*-------------------------------------------------------------------------
+ *
+ * tcopprot.h--
+ *    prototypes for postgres.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * OLD COMMENTS
+ *    This file was created so that other c files could get the two
+ *    function prototypes without having to include tcop.h which single
+ *    handedly includes the whole f*cking tree -- mer 5 Nov. 1991
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef TCOPPROT_H
+#define TCOPPROT_H
+
+#include "tcop/dest.h"
+#include "nodes/pg_list.h"
+#include "parser/parse_query.h"
+
+#ifndef BOOTSTRAP_INCLUDE
+extern List *pg_plan(char *query_string, Oid *typev, int nargs,
+                    QueryTreeList **queryListP, CommandDest dest);
+extern void pg_eval(char *query_string, char *argv[], Oid *typev, int nargs);
+extern void pg_eval_dest(char *query_string, char *argv[], Oid *typev,
+                        int nargs, CommandDest dest);
+#endif /* BOOTSTRAP_HEADER */
+
+extern void handle_warn();
+extern void quickdie();
+extern void die();
+extern int PostgresMain(int argc, char *argv[]);
+extern void ResetUsage();
+extern void ShowUsage();
+
+#endif /* tcopprotIncluded */
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
new file mode 100644 (file)
index 0000000..55d3cfd
--- /dev/null
@@ -0,0 +1,646 @@
+/*-------------------------------------------------------------------------
+ *
+ * utility.c--
+ *    Contains functions which control the execution of the POSTGRES utility
+ *    commands.  At one time acted as an interface between the Lisp and C
+ *    systems.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "parser/dbcommands.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "catalog/pg_type.h"
+
+#include "commands/async.h"
+#include "commands/cluster.h"
+#include "commands/command.h"
+#include "commands/copy.h"
+#include "commands/creatinh.h"
+#include "commands/defrem.h"
+#include "commands/purge.h"
+#include "commands/rename.h"
+#include "commands/view.h"
+#include "commands/version.h"
+#include "commands/vacuum.h"
+#include "commands/recipe.h"
+#include "commands/explain.h"
+
+#include "nodes/parsenodes.h"
+#include "parse.h"
+#include "utils/elog.h"
+#include "utils/builtins.h"
+#include "utils/acl.h"
+#include "utils/palloc.h"
+#include "rewrite/rewriteRemove.h"
+#include "rewrite/rewriteDefine.h"
+#include "tcop/tcopdebug.h"
+#include "tcop/dest.h"
+
+#ifndef NO_SECURITY
+#include "miscadmin.h"
+#include "utils/acl.h"
+#include "utils/syscache.h"
+#endif
+
+
+/* ----------------
+ *     CHECK_IF_ABORTED() is used to avoid doing unnecessary
+ *     processing within an aborted transaction block.
+ * ----------------
+ */
+#define CHECK_IF_ABORTED() \
+    if (IsAbortedTransactionBlockState()) { \
+       elog(NOTICE, "(transaction aborted): %s", \
+            "queries ignored until END"); \
+       commandTag = "*ABORT STATE*"; \
+       break; \
+    } \
+    
+/* ----------------
+ *     general utility function invoker
+ * ----------------
+ */
+void
+ProcessUtility(Node *parsetree,
+              CommandDest dest)
+{
+    char *commandTag = NULL;
+    char *relname;
+    char *relationName;
+    char *userName;
+    
+    userName = GetPgUserName();
+    
+    switch (nodeTag(parsetree)) {
+       /* ********************************
+        *      transactions
+        * ********************************
+        */
+    case T_TransactionStmt:
+       {
+           TransactionStmt *stmt = (TransactionStmt *)parsetree;
+           switch (stmt->command) {
+           case BEGIN_TRANS:
+               commandTag = "BEGIN";
+               CHECK_IF_ABORTED();
+               BeginTransactionBlock();
+               break;
+               
+           case END_TRANS:
+               commandTag = "END";
+               EndTransactionBlock();
+               break;
+               
+           case ABORT_TRANS:
+               commandTag = "ABORT";
+               UserAbortTransactionBlock();
+               break;
+           }
+       }
+       break;
+      
+       /* ********************************
+        *      portal manipulation
+        * ********************************
+        */
+    case T_ClosePortalStmt:
+       {
+           ClosePortalStmt *stmt = (ClosePortalStmt *)parsetree;
+
+           commandTag = "CLOSE";
+           CHECK_IF_ABORTED();
+       
+           PerformPortalClose(stmt->portalname, dest);
+       }
+       break;
+      
+    case T_FetchStmt:
+       {
+           FetchStmt *stmt = (FetchStmt *)parsetree;
+           char *portalName = stmt->portalname;
+           bool forward;
+           int count;
+
+           commandTag = "FETCH";
+           CHECK_IF_ABORTED();
+
+           forward = (bool)(stmt->direction == FORWARD);
+
+           /* parser ensures that count is >= 0 and 
+              'fetch ALL' -> 0 */
+              
+           count = stmt->howMany;
+           PerformPortalFetch(portalName, forward, count, commandTag, dest);
+       }
+       break;
+      
+       /* ********************************
+        *      relation and attribute manipulation
+        * ********************************
+        */
+    case T_CreateStmt:
+       commandTag = "CREATE";
+       CHECK_IF_ABORTED();
+      
+       DefineRelation((CreateStmt *)parsetree);
+       break;
+      
+    case T_DestroyStmt:
+       {
+           DestroyStmt *stmt = (DestroyStmt *)parsetree;
+           List *arg;
+           List *args = stmt->relNames;
+
+           commandTag = "DROP";
+           CHECK_IF_ABORTED();
+
+           foreach (arg, args) {
+               relname = strVal(lfirst(arg));
+               if (IsSystemRelationName(relname))
+                   elog(WARN, "class \"%-.*s\" is a system catalog",
+                        NAMEDATALEN, relname);
+#ifndef NO_SECURITY
+               if (!pg_ownercheck(userName, relname, RELNAME))
+                   elog(WARN, "you do not own class \"%-.*s\"",
+                        NAMEDATALEN, relname);
+#endif
+           }
+           foreach (arg, args) {
+               relname = strVal(lfirst(arg));
+               RemoveRelation(relname);
+           }
+       }
+       break;
+      
+    case T_PurgeStmt:
+       {
+           PurgeStmt *stmt = (PurgeStmt *)parsetree;
+
+           commandTag = "PURGE";
+           CHECK_IF_ABORTED();
+           
+           RelationPurge(stmt->relname,
+                         stmt->beforeDate, /* absolute time string */
+                         stmt->afterDate); /* relative time string */
+       }
+       break;
+      
+    case T_CopyStmt:
+       {
+           CopyStmt *stmt = (CopyStmt *)parsetree;
+           char *filename;
+           char *delim;
+           bool        isBinary;
+           bool        isFrom;
+           bool        pipe = false;
+
+           commandTag = "COPY";
+           CHECK_IF_ABORTED();
+           
+           relname = stmt->relname;
+           isBinary = stmt->binary;
+           
+           isFrom = (bool)(stmt->direction == FROM);
+           filename = stmt->filename;
+           delim = stmt->delimiter;
+
+#ifndef NO_SECURITY
+           if (isFrom) {
+               if (!pg_aclcheck(relname, userName, ACL_RD))
+                   elog(WARN, "%s %s", relname, ACL_NO_PRIV_WARNING);
+           } else {
+               if (!pg_aclcheck(relname, userName, ACL_WR))
+                   elog(WARN, "%s %s", relname, ACL_NO_PRIV_WARNING);
+           }
+#endif
+           
+           /* Free up file descriptors - going to do a read... */
+           
+           closeOneVfd();
+
+           /*
+            * use stdin/stdout if filename is null.
+            */
+           if (filename == NULL)
+               pipe = true;
+           
+           if (pipe && IsUnderPostmaster) dest = CopyEnd;
+           
+           DoCopy(relname, isBinary, isFrom, pipe, filename, delim);
+       }
+       break;
+      
+    case T_AddAttrStmt:
+       {
+           AddAttrStmt *stmt = (AddAttrStmt *)parsetree;
+
+           commandTag = "ADD";
+           CHECK_IF_ABORTED();
+       
+           /* owner checking done in PerformAddAttribute (now recursive) */
+           PerformAddAttribute(stmt->relname,
+                               userName,
+                               stmt->inh,
+                               stmt->colDef);
+       }
+       break;
+      
+       /*
+        * schema
+        */
+    case T_RenameStmt:
+       {
+           RenameStmt *stmt = (RenameStmt *)parsetree;
+
+           commandTag = "RENAME";
+           CHECK_IF_ABORTED();
+       
+           relname = stmt->relname;
+           if (IsSystemRelationName(relname))
+               elog(WARN, "class \"%s\" is a system catalog",
+                    relname);
+#ifndef NO_SECURITY
+           if (!pg_ownercheck(userName, relname, RELNAME))
+               elog(WARN, "you do not own class \"%s\"",
+                    relname);
+#endif
+           
+           /* ----------------
+            *  XXX using len == 3 to tell the difference
+            *      between "rename rel to newrel" and
+            *      "rename att in rel to newatt" will not
+            *      work soon because "rename type/operator/rule"
+            *      stuff is being added. - cim 10/24/90
+            * ----------------
+            * [another piece of amuzing but useless anecdote -- ay]
+            */
+           if (stmt->column == NULL) {
+               /* ----------------
+                *      rename relation
+                *
+                *      Note: we also rename the "type" tuple
+                *      corresponding to the relation.
+                * ----------------
+                */
+               renamerel(relname, /* old name */
+                         stmt->newname); /* new name */
+               TypeRename(relname, /* old name */
+                          stmt->newname); /* new name */
+           } else {
+               /* ----------------
+                *      rename attribute
+                * ----------------
+                */
+               renameatt(relname, /* relname */
+                         stmt->column, /* old att name */
+                         stmt->newname, /* new att name */
+                         userName,
+                         stmt->inh); /* recursive? */
+           }
+       }
+       break;
+      
+    case T_ChangeACLStmt:
+       {
+           ChangeACLStmt *stmt = (ChangeACLStmt *)parsetree;
+           List *i;
+           AclItem *aip;
+           unsigned modechg;
+
+           commandTag = "CHANGE";
+           CHECK_IF_ABORTED();
+           
+           aip = stmt->aclitem;
+           modechg = stmt->modechg;
+#ifndef NO_SECURITY
+           foreach (i, stmt->relNames) {
+               relname = strVal(lfirst(i));
+               if (!pg_ownercheck(userName, relname, RELNAME))
+                   elog(WARN, "you do not own class \"%-.*s\"",
+                        NAMEDATALEN, relname);
+           }
+#endif
+           foreach (i, stmt->relNames) {
+               relname = strVal(lfirst(i));
+               ChangeAcl(relname, aip, modechg);
+           }
+
+       }
+       break;
+      
+       /* ********************************
+        *      object creation / destruction
+        * ********************************
+        */
+    case T_DefineStmt:
+       {
+           DefineStmt *stmt = (DefineStmt *)parsetree;
+
+           commandTag = "CREATE";
+           CHECK_IF_ABORTED();
+
+           switch(stmt->defType) {
+           case OPERATOR:
+               DefineOperator(stmt->defname, /* operator name */
+                              stmt->definition); /* rest */
+               break;
+           case P_TYPE:
+               {
+                   DefineType (stmt->defname, stmt->definition);
+               }
+               break;
+           case AGGREGATE:
+               DefineAggregate(stmt->defname, /*aggregate name */
+                               stmt->definition); /* rest */
+               break;
+           }
+       }
+       break;
+      
+    case T_ViewStmt:           /* VIEW */
+       {
+           ViewStmt *stmt = (ViewStmt *)parsetree;
+
+           commandTag = "CREATE";
+           CHECK_IF_ABORTED();
+           DefineView (stmt->viewname, stmt->query); /* retrieve parsetree */
+       }
+       break;
+      
+    case T_ProcedureStmt:      /* FUNCTION */
+       commandTag = "CREATE";
+       CHECK_IF_ABORTED();
+       DefineFunction((ProcedureStmt *)parsetree, dest); /* everything */
+       break;
+      
+    case T_IndexStmt:
+       {
+           IndexStmt *stmt = (IndexStmt *)parsetree;
+
+           commandTag = "CREATE";
+           CHECK_IF_ABORTED();
+           /* XXX no support for ARCHIVE indices, yet */
+           DefineIndex(stmt->relname, /* relation name */
+                       stmt->idxname, /* index name */
+                       stmt->accessMethod, /* am name */
+                       stmt->indexParams, /* parameters */
+                       stmt->withClause,
+                       (Expr*)stmt->whereClause,
+                       stmt->rangetable);
+       }
+       break;
+      
+    case T_RuleStmt:
+       {
+           RuleStmt *stmt = (RuleStmt *)parsetree;
+#ifndef NO_SECURITY
+           relname = stmt->object->relname;
+           if (!pg_aclcheck(relname, userName, ACL_RU))
+               elog(WARN, "%s %s", relname, ACL_NO_PRIV_WARNING);      
+#endif
+           commandTag = "CREATE";
+           CHECK_IF_ABORTED();
+           DefineQueryRewrite(stmt);
+       }
+       break;
+      
+    case T_ExtendStmt:
+       {
+           ExtendStmt *stmt = (ExtendStmt *)parsetree;
+
+           commandTag = "EXTEND";
+           CHECK_IF_ABORTED();
+       
+           ExtendIndex(stmt->idxname, /* index name */
+                       (Expr*)stmt->whereClause, /* where */
+                       stmt->rangetable);
+       }
+       break;
+      
+    case T_RemoveStmt:
+       {
+           RemoveStmt *stmt = (RemoveStmt *)parsetree;
+
+           commandTag = "DROP";
+           CHECK_IF_ABORTED();
+       
+           switch(stmt->removeType) {
+           case AGGREGATE:
+               RemoveAggregate(stmt->name);
+               break;
+           case INDEX:
+               relname = stmt->name;
+               if (IsSystemRelationName(relname))
+                   elog(WARN, "class \"%s\" is a system catalog index",
+                        relname);
+#ifndef NO_SECURITY
+               if (!pg_ownercheck(userName, relname, RELNAME))
+                   elog(WARN, "you do not own class \"%s\"",
+                        relname);
+#endif
+               RemoveIndex(relname);
+               break;
+           case RULE:
+               {
+                   char *rulename = stmt->name;
+#ifndef NO_SECURITY
+               
+                   relationName = RewriteGetRuleEventRel(rulename);
+                   if (!pg_aclcheck(relationName, userName, ACL_RU))
+                       elog(WARN, "%s %s", relationName, ACL_NO_PRIV_WARNING);
+#endif
+                   RemoveRewriteRule(rulename);
+               }
+               break;
+           case P_TYPE:
+#ifndef NO_SECURITY
+               /* XXX moved to remove.c */
+#endif
+               RemoveType(stmt->name);
+               break;
+           case VIEW:
+               {
+                   char *viewName = stmt->name;
+                   char *ruleName;
+                   extern char *RewriteGetRuleEventRel();
+
+#ifndef NO_SECURITY
+               
+                   ruleName = MakeRetrieveViewRuleName(viewName);
+                   relationName = RewriteGetRuleEventRel(ruleName);
+                   if (!pg_ownercheck(userName, relationName, RELNAME))
+                       elog(WARN, "%s %s", relationName, ACL_NO_PRIV_WARNING);
+                   pfree(ruleName);
+#endif
+                   RemoveView(viewName);
+               }
+               break;
+           }
+           break;
+       }
+       break;
+    case T_RemoveFuncStmt:
+       {
+           RemoveFuncStmt *stmt = (RemoveFuncStmt *)parsetree;
+           commandTag = "DROP";
+           CHECK_IF_ABORTED();
+           RemoveFunction(stmt->funcname,
+                          length(stmt->args),
+                          stmt->args);
+       }
+       break;
+      
+    case T_RemoveOperStmt:
+       {
+           RemoveOperStmt *stmt = (RemoveOperStmt *)parsetree;
+           char* type1 = (char*) NULL; 
+           char *type2 = (char*) NULL;
+               
+           commandTag = "DROP";
+           CHECK_IF_ABORTED();
+
+           if (lfirst(stmt->args)!=NULL)
+               type1 = strVal(lfirst(stmt->args));
+           if (lsecond(stmt->args)!=NULL)
+               type2 = strVal(lsecond(stmt->args));
+           RemoveOperator(stmt->opname, type1, type2);
+       }
+       break;
+      
+    case T_VersionStmt:
+       {
+           elog(WARN, "CREATE VERSION is not currently implemented");
+       }
+       break;
+      
+    case T_CreatedbStmt:
+       {
+           CreatedbStmt *stmt = (CreatedbStmt *)parsetree;
+
+           commandTag = "CREATEDB";
+           CHECK_IF_ABORTED();
+           createdb(stmt->dbname);
+       }
+       break;
+      
+    case T_DestroydbStmt:
+       {
+           DestroydbStmt *stmt = (DestroydbStmt *)parsetree;
+
+           commandTag = "DESTROYDB";
+           CHECK_IF_ABORTED();
+           destroydb(stmt->dbname);
+       }
+       break;
+      
+       /* Query-level asynchronous notification */
+    case T_NotifyStmt:
+       {
+           NotifyStmt *stmt = (NotifyStmt *)parsetree;
+
+           commandTag = "NOTIFY";
+           CHECK_IF_ABORTED();
+
+           Async_Notify(stmt->relname);
+       }
+       break;
+      
+    case T_ListenStmt:
+       {
+           ListenStmt *stmt = (ListenStmt *)parsetree;
+
+           commandTag = "LISTEN";
+           CHECK_IF_ABORTED();
+
+           Async_Listen(stmt->relname,MasterPid);
+       }
+       break;
+      
+       /* ********************************
+        *      dynamic loader
+        * ********************************
+        */
+    case T_LoadStmt:
+       {
+           LoadStmt *stmt = (LoadStmt *)parsetree;
+           FILE *fp, *fopen();
+           char *filename;
+
+           commandTag = "LOAD";
+           CHECK_IF_ABORTED();
+
+           filename = stmt->filename;
+           closeAllVfds();
+           if ((fp = fopen(filename, "r")) == NULL)
+               elog(WARN, "LOAD: could not open file %s", filename);
+           fclose(fp);
+           load_file(filename);
+       }
+       break;
+
+    case T_ClusterStmt:
+       {
+           ClusterStmt *stmt = (ClusterStmt *)parsetree;
+
+           commandTag = "CLUSTER";
+           CHECK_IF_ABORTED();
+
+           cluster(stmt->relname, stmt->indexname);
+       }
+       break;
+       
+    case T_VacuumStmt:
+       commandTag = "VACUUM";
+       CHECK_IF_ABORTED();
+       vacuum(((VacuumStmt *) parsetree)->vacrel);
+       break;
+
+    case T_ExplainStmt:
+       {
+           ExplainStmt *stmt = (ExplainStmt *)parsetree;
+
+           commandTag = "EXPLAIN";
+           CHECK_IF_ABORTED();
+
+           ExplainQuery(stmt->query, stmt->options, dest);
+       }
+       break;
+       
+       /* ********************************
+          Tioga-related statements
+          *********************************/
+    case T_RecipeStmt:
+       {
+           RecipeStmt* stmt = (RecipeStmt*)parsetree;
+           commandTag="EXECUTE RECIPE";
+           CHECK_IF_ABORTED();
+           beginRecipe(stmt);
+       }
+       break;
+      
+      
+       /* ********************************
+        *      default
+        * ********************************
+        */
+    default:
+       elog(WARN, "ProcessUtility: command #%d unsupported",
+            nodeTag(parsetree));
+       break;
+    }
+    
+    /* ----------------
+     * tell fe/be or whatever that we're done.
+     * ----------------
+     */
+    EndCommand(commandTag, dest);
+}
+
diff --git a/src/backend/tcop/utility.h b/src/backend/tcop/utility.h
new file mode 100644 (file)
index 0000000..ea9d4f7
--- /dev/null
@@ -0,0 +1,18 @@
+/*-------------------------------------------------------------------------
+ *
+ * utility.h--
+ *    prototypes for utility.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef UTILITY_H
+#define UTILITY_H
+
+extern void ProcessUtility(Node *parsetree, CommandDest dest);
+
+#endif /* UTILITY_H */
diff --git a/src/backend/tioga/Arr_TgRecipe.h b/src/backend/tioga/Arr_TgRecipe.h
new file mode 100644 (file)
index 0000000..365f464
--- /dev/null
@@ -0,0 +1,120 @@
+#include "tioga/Varray.h"
+
+/* Modify the following size macros to suit your need. */
+
+#ifndef Arr_TgString_INITIAL_SIZE
+#define Arr_TgString_INITIAL_SIZE 32
+#endif
+
+#ifndef Arr_TgElementPtr_INITIAL_SIZE
+#define Arr_TgElementPtr_INITIAL_SIZE 32
+#endif
+
+#ifndef Arr_TgNodePtr_INITIAL_SIZE
+#define Arr_TgNodePtr_INITIAL_SIZE 32
+#endif
+/***************************************************************/
+/*            Do not modify anything below this line.          */
+/***************************************************************/
+
+/* -- Defining types and function for Arr_TgString type -- */
+/* -- the following must be supplied by the user:
+
+   void copyTgString(TgString* from, TgString* to); - make a copy of TgString.
+*/
+
+#ifndef _ARR_TgString_
+#define _ARR_TgString_
+
+#ifndef ARR_TgString_INITIAL_SIZE
+#define ARR_TgString_INITIAL_SIZE 32   /* change this size to suit your need */
+#endif /* ARR_TgString_INITIAL_SIZE */
+
+typedef struct Arr_TgString {
+  size_t num;
+  size_t size;
+  size_t valSize;
+  TgString *val;
+} Arr_TgString;
+
+#define newArr_TgString() \
+  (Arr_TgString *) NewVarray(ARR_TgString_INITIAL_SIZE, sizeof(TgString))
+
+#define enlargeArr_TgString(A, I) \
+  (A)->size += (I); \
+  (A)->val = (TgString *) realloc((A)->val, (A)->valSize * (A)->size)
+
+#define addArr_TgString(A, V) \
+  AppendVarray((Varray *) (A), (void *) (V), (CopyingFunct) copyTgString)
+
+#define deleteArr_TgString(A) FreeVarray(A)
+
+#endif /* _ARR_TgString_ */
+
+/* -- Defining types and function for Arr_TgElementPtr type -- */
+/* -- the following must be supplied by the user:
+
+   void copyTgElementPtr(TgElementPtr* from, TgElementPtr* to); - make a copy of TgElementPtr.
+*/
+
+#ifndef _ARR_TgElementPtr_
+#define _ARR_TgElementPtr_
+
+#ifndef ARR_TgElementPtr_INITIAL_SIZE
+#define ARR_TgElementPtr_INITIAL_SIZE 32   /* change this size to suit your need */
+#endif /* ARR_TgElementPtr_INITIAL_SIZE */
+
+typedef struct Arr_TgElementPtr {
+  size_t num;
+  size_t size;
+  size_t valSize;
+  TgElementPtr *val;
+} Arr_TgElementPtr;
+
+#define newArr_TgElementPtr() \
+  (Arr_TgElementPtr *) NewVarray(ARR_TgElementPtr_INITIAL_SIZE, sizeof(TgElementPtr))
+
+#define enlargeArr_TgElementPtr(A, I) \
+  (A)->size += (I); \
+  (A)->val = (TgElementPtr *) realloc((A)->val, (A)->valSize * (A)->size)
+
+#define addArr_TgElementPtr(A, V) \
+  AppendVarray((Varray *) (A), (void *) (V), (CopyingFunct) copyTgElementPtr)
+
+#define deleteArr_TgElementPtr(A) FreeVarray(A)
+
+#endif /* _ARR_TgElementPtr_ */
+
+/* -- Defining types and function for Arr_TgNodePtr type -- */
+/* -- the following must be supplied by the user:
+
+   void copyTgNodePtr(TgNodePtr* from, TgNodePtr* to); - make a copy of TgNodePtr.
+*/
+
+#ifndef _ARR_TgNodePtr_
+#define _ARR_TgNodePtr_
+
+#ifndef ARR_TgNodePtr_INITIAL_SIZE
+#define ARR_TgNodePtr_INITIAL_SIZE 32   /* change this size to suit your need */
+#endif /* ARR_TgNodePtr_INITIAL_SIZE */
+
+typedef struct Arr_TgNodePtr {
+  size_t num;
+  size_t size;
+  size_t valSize;
+  TgNodePtr *val;
+} Arr_TgNodePtr;
+
+#define newArr_TgNodePtr() \
+  (Arr_TgNodePtr *) NewVarray(ARR_TgNodePtr_INITIAL_SIZE, sizeof(TgNodePtr))
+
+#define enlargeArr_TgNodePtr(A, I) \
+  (A)->size += (I); \
+  (A)->val = (TgNodePtr *) realloc((A)->val, (A)->valSize * (A)->size)
+
+#define addArr_TgNodePtr(A, V) \
+  AppendVarray((Varray *) (A), (void *) (V), (CopyingFunct) copyTgNodePtr)
+
+#define deleteArr_TgNodePtr(A) FreeVarray(A)
+
+#endif /* _ARR_TgNodePtr_ */
diff --git a/src/backend/tioga/Makefile.inc b/src/backend/tioga/Makefile.inc
new file mode 100644 (file)
index 0000000..b62727d
--- /dev/null
@@ -0,0 +1,21 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the Tioga module
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+VPATH:= $(VPATH):$(CURDIR)/tioga
+
+SRCS_TIOGA= tgRecipe.c Varray.c
+
+HEADERS+= tgRecipe.h Arr_TgRecipe.h Varray.h
+
+
+
diff --git a/src/backend/tioga/Varray.c b/src/backend/tioga/Varray.c
new file mode 100644 (file)
index 0000000..3ed45c6
--- /dev/null
@@ -0,0 +1,48 @@
+/* ************************************************************************
+ *
+ * Varray.c --
+ *
+ *    routines to provide a generic set of functions to handle variable sized
+ * arrays.    originally by Jiang Wu
+ * ************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "Varray.h"
+
+Varray *NewVarray(size_t nobj, size_t size)
+/*
+ * NewVarray -- allocate a Varray to contain an array of val each of which
+ *              is size valSize.  Returns the Varray if successful,
+ *              returns NULL otherwise.
+ */
+{
+  Varray *result;
+
+  if (nobj == 0)
+    nobj = VARRAY_INITIAL_SIZE;
+  result = (Varray *) malloc(sizeof(Varray));
+  result->val = (void *) calloc(nobj, size);
+  if (result == NULL)
+    return NULL;
+  result->size = size;
+  result->nobj = 0;
+  result->maxObj = nobj;
+  return result;
+}
+
+int AppendVarray(Varray *array, void *value, CopyingFunct copy)
+/*
+ * AppendVarray -- append value to the end of array.  This function
+ *                 returns the size of the array after the addition of
+ *                 the new element.
+ */
+{
+  copy(value, VARRAY_NTH(array->val, array->size, array->nobj));
+  array->nobj++;
+  if (array->nobj >= array->maxObj) {
+    ENLARGE_VARRAY(array, array->maxObj / 2);
+  }
+  return array->nobj;
+}
+
diff --git a/src/backend/tioga/Varray.h b/src/backend/tioga/Varray.h
new file mode 100644 (file)
index 0000000..f6d01c2
--- /dev/null
@@ -0,0 +1,45 @@
+/* ********************************************************************
+ *
+ * Varray.h -- header file for varray.c which provides a generic
+ *             set of functions to handle variable sized arrays.
+ *
+ *             originally by Jiang Wu
+ * ********************************************************************/
+
+#ifndef _VARRAY_H_
+#define _VARRAY_H_
+
+typedef struct _varray {
+  size_t nobj;                 /* number of objects in this array */
+  size_t maxObj;               /* max. number of objects in this array */
+  size_t size;                 /* size of each element in the array */
+  void   *val;                 /* array of elements */
+} Varray;
+
+/* type for custom copying function */
+typedef void (*CopyingFunct) (void *from, void *to);
+
+#define VARRAY_INITIAL_SIZE 32
+
+#define ENLARGE_VARRAY(ARRAY, INC) \
+  (ARRAY)->maxObj += (INC); \
+  (ARRAY)->val = (void *) realloc((ARRAY)->val, \
+                                 (ARRAY)->size * (ARRAY)->maxObj)
+
+#define VARRAY_NTH(VAL, SIZE, N) (((char *) (VAL)) + (SIZE) * (N))
+
+#define FreeVarray(ARRAY) \
+  if ((ARRAY) != NULL) { free((ARRAY)->val); free((ARRAY)); (ARRAY) = NULL ; }
+                      
+#define ModifyVarray(ARRAY, N, NEW, COPY) \
+  if ((N) < (ARRAY)->nobj) \
+    (COPY)(VARRAY_NTH((ARRAY)->val, (ARRAY)->size, (N)), (NEW))
+
+#define GetVarray(ARRAY, N) \
+  ((N) < (ARRAY)->nobj ? VARRAY_NTH((ARRAY)->val, (ARRAY)->size, (N)) \
+                       : NULL)
+
+extern Varray *NewVarray(size_t nobj, size_t size);
+extern int AppendVarray(Varray *array, void *value, CopyingFunct copy);
+
+#endif /* _VARRAY_H_ */
diff --git a/src/backend/tioga/tgRecipe.c b/src/backend/tioga/tgRecipe.c
new file mode 100644 (file)
index 0000000..874e26e
--- /dev/null
@@ -0,0 +1,694 @@
+/*-------------------------------------------------------------------------
+ *
+ * tgRecipe.c--
+ *      Tioga recipe-related definitions
+ *      these functions can be used in both the frontend and the 
+ *      backend
+ *    
+ *      this file must be kept current with recipe-schema.sql
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <stdlib.h>
+#include "tioga/tgRecipe.h"
+
+#include "catalog/catalog.h" /*for newoid() */
+
+static Arr_TgString *TextArray2ArrTgString(char *str);
+
+#define ARRAY_LEFT_DELIM '{'
+#define ARRAY_RIGHT_DELIM '}'
+#define ARRAY_ELEM_LEFT '"'
+#define ARRAY_ELEM_RIGHT '"'
+#define ARRAY_ELEM_SEPARATOR ','
+
+/* maximum length of query string */
+#define MAX_QBUF_LENGTH 2048 
+
+/**** the queries being used ********/
+#define Q_RETRIEVE_RECIPE_BYNAME \
+      "select * from Recipes where Recipes.elemName = '%s';"
+#define Q_RETRIEVE_ELEMENTS_IN_RECIPE \
+      "select e.* from Element e, Node n where n.belongsTo = '%s' and n.nodeElem = e.elemName;"
+#define Q_RETRIEVE_NODES_IN_RECIPE \
+      "select * from Node n where n.belongsTo = '%s'"
+#define Q_LOOKUP_EDGES_IN_RECIPE \
+      "select * from Edge e where e.belongsTo = '%s'"
+
+/* static functions only used here */
+static void fillTgElement(TgElement *elem,  PortalBuffer *pbuf, int tupno);
+static void fillTgNode(TgRecipe *r, TgNode *node,  PortalBuffer *pbuf, int tupno);
+static TgRecipe* fillTgRecipe(PortalBuffer* pbuf, int tupno);
+static void lookupEdges(TgRecipe *r, char* name);
+static void fillAllNodes(TgRecipe *r, char* name);
+static void fillAllElements(TgRecipe *r, char* name);
+static TgNode* connectTee(TgRecipe *r, TgNodePtr fromNode, TgNodePtr toNode,
+                      int fromPort, int toPort);
+
+/*
+ * TextArray2ArrTgString --  take a string of the form:
+ *                      {"fooo", "bar", "xxxxx"} (for postgres)
+ *                      and parse it into a Array of TgString's
+ *
+ * always returns a valid Arr_TgString.  It could be a newly initialized one with
+ * zero elements
+ */
+Arr_TgString*
+TextArray2ArrTgString(char *str)
+{
+  Arr_TgString *result;
+  
+  char* beginQuote;
+  char* endQuote;
+  int nextlen;
+  char* word;
+  
+  result = newArr_TgString();
+  
+  if ((str == NULL) || (str[0] == '\0'))
+    return result;
+  
+  if (*str != ARRAY_LEFT_DELIM) {
+    elog(NOTICE,"TextArray2ArrTgString: badly formed string, must have %c as \
+first character\n", ARRAY_LEFT_DELIM);
+    return result;
+  }
+  
+  str++; /* skip the first { */
+  while  ( *str != '}' ) {
+    if (*str == '\0') {
+      elog(NOTICE,"TextArray2ArrTgString: text string ended prematurely\n");
+      return result;
+    }
+
+    if ((beginQuote = index(str, ARRAY_ELEM_LEFT)) == NULL) {
+      elog(NOTICE,"textArray2ArrTgString:  missing a begin quote\n");
+      return result;
+    }
+    if  ( (endQuote = index(beginQuote+1,'"')) == NULL) {
+      elog(NOTICE,"textArray2ArrTgString:  missing an end quote\n");
+      return result;
+    }
+    nextlen = endQuote - beginQuote; /* don't subtract one here because we
+                                     need the extra character for \0 anyway */
+    word = (char*) malloc(nextlen);
+    strncpy(word, beginQuote+1, nextlen-1);
+    word[nextlen-1] ='\0';
+    addArr_TgString(result, (TgString*)&word);
+    free (word);
+    str = endQuote + 1;
+  }
+  return result;
+}
+
+/* -------------------------------------
+findElemInRecipe()
+   given an element name, find that element in the TgRecipe structure and return it.
+
+   XXX Currently, this is done by linear search.  Change to using a hash table.
+-------------------------------------- */
+
+TgElement* 
+findElemInRecipe(TgRecipe *r, char* elemName)
+{
+  int i;
+  Arr_TgElementPtr* arr = r->elements;
+  TgElement* e;
+
+  for (i=0;i<arr->num;i++) {
+    e = (TgElement*)arr->val[i];
+    if (strcmp(e->elemName, elemName) == 0)
+      return e;
+  }
+  elog (NOTICE, "Element named %s not found in recipe named %s", elemName, r->elmValue.elemName);
+  return NULL;
+}
+
+/* -------------------------------------
+findNodeInRecipe()
+   given an node name, find that node in the TgRecipe structure and return it.
+
+   XXX Currently, this is done by linear search.  Change to using a hash table.
+-------------------------------------- */
+
+TgNode* 
+findNodeInRecipe(TgRecipe *r, char* nodeName)
+{
+  int i;
+  Arr_TgNodePtr* arr = r->allNodes;
+  TgNode *n;
+
+  for (i=0;i<arr->num;i++) {
+    n = (TgNode*)arr->val[i];
+    if (strcmp(n->nodeName, nodeName) == 0)
+      return n;
+  }
+  elog (NOTICE, "Node named %s not found in recipe named %s", nodeName, r->elmValue.elemName);
+  return NULL;
+}
+
+
+/* -------------------------------------
+fillTgNode
+    takes a query result in the PortalBuffer containing a Node
+    and converts it to a C Node strcture.
+    The Node structure passed in is 'filled' appropriately
+
+-------------------------------------- */
+
+void
+fillTgNode(TgRecipe* r, TgNode *node,  PortalBuffer *pbuf, int tupno)
+{
+  char* nodeType;
+  char* nodeElem;
+  char* locString;  /* ascii string rep of the point */
+  static int attnums_initialized = 0;
+  static int nodeName_attnum;
+  static int nodeElem_attnum;
+  static int nodeType_attnum;
+  static int loc_attnum;
+  TgNodePtr BlankNodePtr;
+  int i;
+
+  if (!attnums_initialized) {
+    /* the first time fillTgNode is called,
+       we find out all the relevant attribute numbers in a TgNode
+       so subsequent calls are speeded up,
+       the assumption is that the schema won't change between calls*/
+    nodeName_attnum = PQfnumber(pbuf, tupno, "nodeName");
+    nodeElem_attnum = PQfnumber(pbuf, tupno, "nodeElem");
+    nodeType_attnum = PQfnumber(pbuf, tupno, "nodeType");
+    loc_attnum = PQfnumber(pbuf, tupno, "loc");
+    attnums_initialized = 1;
+  }
+  node->nodeName = PQgetAttr(pbuf, tupno, nodeName_attnum);
+  locString = PQgetvalue(pbuf, tupno, loc_attnum);
+  if (locString == NULL || locString[0] == '\0') {
+     node->loc.x = 0; node->loc.y = 0; /* assign to zero for default */
+   }
+  else
+      {
+         float x,y;
+         sscanf(locString, "(%f, %f)", &x, &y);
+         node->loc.x = x;
+         node->loc.y = y;
+      }
+  nodeElem = PQgetvalue(pbuf, tupno, nodeElem_attnum);
+  node->nodeElem = findElemInRecipe(r,nodeElem);
+  node->inNodes = newArr_TgNodePtr();
+  node->outNodes = newArr_TgNodePtr();
+
+  /* fill the inNodes array with as many NULL's are there are inPorts in
+   * the underlying element */
+  BlankNodePtr = (TgNodePtr)NULL;
+  for (i = 0 ; i < node->nodeElem->inPorts->num ; i++)
+    addArr_TgNodePtr(node->inNodes, &BlankNodePtr);
+
+  /* fill the outNodes array with as many NULL's are there are inPorts in
+   * the underlying element */
+  for (i = 0 ; i < node->nodeElem->outPorts->num ; i++)
+    addArr_TgNodePtr(node->outNodes, &BlankNodePtr);
+
+  nodeType = PQgetvalue(pbuf, tupno, nodeType_attnum);
+
+  if (strcmp(nodeType, "Ingred") == 0)
+    node->nodeType = TG_INGRED_NODE;
+  else if (strcmp(nodeType, "Eye") == 0)
+    node->nodeType = TG_EYE_NODE;
+  else if (strcmp(nodeType, "Recipe") == 0)
+    node->nodeType = TG_RECIPE_NODE;
+  else
+    elog(NOTICE, "fillTgNode: unknown nodeType field value : %s\n", nodeType);
+  
+}
+
+/* -------------------------------------
+fillTgElement
+    takes a query result in the PortalBuffer containing a Element 
+    and converts it to a C TgElement strcture.
+    The TgElement structure passed in is 'filled' appropriately
+  ------------------------------------ */
+
+void
+fillTgElement(TgElement *elem,  PortalBuffer *pbuf, int tupno)
+{
+  char* srcLang, *elemType;
+  static int attnums_initialized = 0;
+  static int elemName_attnum; 
+  static int elemType_attnum; 
+  static int inPorts_attnum;
+  static int inTypes_attnum;
+  static int outPorts_attnum;
+  static int outTypes_attnum;
+  static int doc_attnum;
+  static int keywords_attnum;
+  static int icon_attnum;
+  static int srcLang_attnum;
+  static int src_attnum;
+  static int owner_attnum;
+
+  if (!attnums_initialized) {
+    /* the first time fillTgElement is called,
+       we find out all the relevant attribute numbers in a TgElement
+       so subsequent calls are speeded up,
+       the assumption is that the schema won't change between calls*/
+    elemName_attnum = PQfnumber(pbuf, tupno, "elemName");
+    elemType_attnum = PQfnumber(pbuf, tupno, "elemType");
+    inPorts_attnum = PQfnumber(pbuf, tupno, "inPorts");
+    inTypes_attnum = PQfnumber(pbuf, tupno, "inTypes");
+    outPorts_attnum = PQfnumber(pbuf, tupno, "outPorts");
+    outTypes_attnum = PQfnumber(pbuf, tupno, "outTypes");
+    doc_attnum = PQfnumber(pbuf, tupno, "doc");
+    keywords_attnum = PQfnumber(pbuf, tupno, "keywords");
+    icon_attnum = PQfnumber(pbuf, tupno, "icon");
+    srcLang_attnum = PQfnumber(pbuf, tupno, "srcLang");
+    src_attnum = PQfnumber(pbuf, tupno, "src");
+    attnums_initialized = 1;
+  }
+
+  elem->elemName = PQgetAttr(pbuf, tupno, elemName_attnum);
+  elem->inPorts = TextArray2ArrTgString(PQgetvalue(pbuf, tupno, inPorts_attnum));
+  elem->inTypes = TextArray2ArrTgString(PQgetvalue(pbuf, tupno, inTypes_attnum));
+  elem->outPorts = TextArray2ArrTgString(PQgetvalue(pbuf, tupno, outPorts_attnum));
+  elem->outTypes = TextArray2ArrTgString(PQgetvalue(pbuf, tupno, outTypes_attnum));
+  elem->doc = PQgetAttr(pbuf, tupno, doc_attnum);
+  elem->keywords = TextArray2ArrTgString(PQgetvalue(pbuf, tupno, keywords_attnum));
+  elem->icon = PQgetAttr(pbuf,tupno, icon_attnum);
+  elem->src = PQgetAttr(pbuf,tupno, src_attnum);
+  elem->owner = PQgetAttr(pbuf,tupno, owner_attnum);
+
+  /* we don't need to keep the value returned so use PQgetvalue()
+     instead of PQgetAttr() */
+  srcLang = PQgetvalue(pbuf,tupno, srcLang_attnum); 
+
+  if (strcmp(srcLang, "SQL") == 0) 
+    elem->srcLang = TG_SQL;
+  else
+    if (strcmp(srcLang, "C") == 0)
+      elem->srcLang = TG_C;
+  else
+    if (strcmp(srcLang, "RecipeGraph") == 0)
+      elem->srcLang = TG_RECIPE_GRAPH;
+  else
+    if (strcmp(srcLang, "Compiled") == 0)
+      elem->srcLang = TG_COMPILED;
+  else
+    elog(NOTICE, "fillTgElement(): unknown srcLang field value : %s\n", srcLang);
+
+  elemType = PQgetvalue(pbuf, tupno, elemType_attnum);
+  if (strcmp(elemType, "Ingred") == 0)
+    elem->elemType = TG_INGRED;
+  else if (strcmp(elemType, "Eye") == 0)
+    elem->elemType = TG_EYE;
+  else if (strcmp(elemType, "Recipe") == 0)
+    elem->elemType = TG_RECIPE;
+  else
+    elog(NOTICE, "fillTgElement(): unknown elemType field value : %s\n", elemType);
+
+
+}
+/* -------------------------------------
+lookupEdges - 
+    look up the edges of a recipe and fill in the inNodes 
+    and outNodes of each node.
+    In the process of connecting edges, we detect tee's and create
+    teeNodes.  We add the teeNodes to the allNodes field of r as well
+------------------------------------ */
+void 
+lookupEdges(TgRecipe *r, char* name)
+{
+  char qbuf[MAX_QBUF_LENGTH];
+  int i;
+  char *pqres;
+  char *pbufname;
+  PortalBuffer *pbuf;
+  int ntups;
+  int fromNode_attnum;
+  int fromPort_attnum;
+  int toPort_attnum;
+  int toNode_attnum;
+  char *toNode, *fromNode;
+  char *toPortStr, *fromPortStr;
+  int toPort, fromPort;
+
+  TgNodePtr fromNodePtr, toNodePtr;
+
+  sprintf(qbuf, Q_LOOKUP_EDGES_IN_RECIPE, name);
+  pqres = PQexec(qbuf);
+  pqres = PQexec(qbuf);
+  if (*pqres == 'R' || *pqres == 'E') {
+    elog(NOTICE, "lookupEdges(): Error while executing query : %s\n", qbuf);
+    elog(NOTICE, "result = %s, error is %s\n", pqres, PQerrormsg);
+    return;
+  }
+  pbufname = ++pqres;
+  pbuf = PQparray(pbufname);
+  ntups = PQntuplesGroup(pbuf,0);
+
+  if (ntups == 0) { return; }
+
+  fromNode_attnum = PQfnumber(pbuf, 0, "fromNode");
+  fromPort_attnum = PQfnumber(pbuf, 0, "fromPort");
+  toNode_attnum = PQfnumber(pbuf, 0, "toNode");
+  toPort_attnum = PQfnumber(pbuf, 0, "toPort");
+
+  for (i=0;i<ntups;i++) {
+
+    fromNode = PQgetvalue(pbuf, i, fromNode_attnum);
+    toNode = PQgetvalue(pbuf, i, toNode_attnum);
+    fromPortStr = PQgetvalue(pbuf, i, fromPort_attnum);
+    toPortStr = PQgetvalue(pbuf, i, toPort_attnum);
+
+    if (!fromPortStr || fromPortStr[0] == '\0')  {
+      elog(NOTICE, "lookupEdges():  SANITY CHECK failed.  Edge with invalid fromPort value!");
+      return;
+    }
+    if (!toPortStr || toPortStr[0] == '\0') {
+      elog(NOTICE, "lookupEdges():  SANITY CHECK failed.  Edge with invalid toPort value!!");
+      return;
+    }
+    fromPort = atoi(fromPortStr);
+    toPort = atoi(toPortStr);
+
+    fromNodePtr = findNodeInRecipe(r, fromNode);
+    if (!fromNodePtr) {
+      elog(NOTICE, "lookupEdges():  SANITY CHECK failed.  Edge with bad fromNode value!");
+      return;
+    }
+    toNodePtr = findNodeInRecipe(r, toNode);
+    if (!toNodePtr) {
+      elog(NOTICE, "lookupEdges():  SANITY CHECK failed.  Edge with bad toNode value!");
+      return;
+    }
+
+    /* check to see if the from port is already connected.
+       if it is, then this means we should construct a Tee node 
+    */
+    if (fromNodePtr->outNodes->val[fromPort-1] != NULL) {
+       TgNodePtr tn;
+
+       tn = connectTee(r,fromNodePtr, toNodePtr, fromPort, toPort);
+       addArr_TgNodePtr(r->allNodes,&tn);
+    } else {
+       fromNodePtr->outNodes->val[fromPort-1] = toNodePtr;
+       toNodePtr->inNodes->val[toPort-1] = fromNodePtr;
+    }
+  }
+
+  PQclear(pbufname);
+}
+
+/*
+   handle tee connections here
+   Everytime an output port is connected multiply,
+   we explicitly insert TgTeeNode
+
+   returns the teeNode created
+*/
+static TgNode*
+connectTee(TgRecipe *r, TgNodePtr fromNode, TgNodePtr toNode,
+          int fromPort, int toPort)
+{
+    TgNodePtr origToNode;
+    TgNodePtr tn;
+    TgNodePtr BlankNodePtr;
+    int origToPort;
+    int i;
+
+    /* the toNode formerly pointed to */
+    origToNode = fromNode->outNodes->val[fromPort-1];
+
+    if (origToNode == NULL) {
+       elog(NOTICE,"Internal Error: connectTee() called with a null origToNode");
+       return;
+    }
+
+    for (i=0;i<origToNode->inNodes->num;i++) {
+       if (origToNode->inNodes->val[i] == fromNode)
+           break;
+    }
+
+    /* the inport of the former toNode  */
+    /* ports start with 1, array indices start from 0 */
+    origToPort = i + 1;
+
+    /* add a tee node now. */
+    tn = malloc(sizeof(TgNode));
+    /* generate a name for the tee node table */
+    tn->nodeName = malloc(50);
+    sprintf(tn->nodeName, "tee_%d", newoid());
+/*    tn->nodeName = NULL; */
+    
+    tn->nodeType = TG_TEE_NODE;
+    tn->nodeElem = NULL;
+    tn->inNodes = newArr_TgNodePtr();
+    tn->outNodes = newArr_TgNodePtr();
+
+    BlankNodePtr = (TgNodePtr)NULL;
+    /* each TgTeeNode has one input and two outputs, NULL them initiallly */
+    addArr_TgNodePtr(tn->inNodes, &BlankNodePtr);
+    addArr_TgNodePtr(tn->outNodes, &BlankNodePtr);
+    addArr_TgNodePtr(tn->outNodes, &BlankNodePtr);
+
+    /* make the old toNode the left parent of the tee node
+       add the new toNode as the right parent of the tee node */
+    tn->outNodes->val[0] = origToNode;
+    origToNode->inNodes->val[origToPort-1] = tn;
+
+    tn->outNodes->val[1] = toNode;
+    toNode->inNodes->val[toPort-1] = tn;
+
+    /* connect the fromNode to the new tee node */
+    fromNode->outNodes->val[fromPort-1] = tn;
+    tn->inNodes->val[0] = fromNode;
+
+    return tn;
+}
+
+/* -------------------------------------
+fillAllNodes
+    fill out the nodes of a recipe
+  ------------------------------------ */
+void 
+fillAllNodes(TgRecipe *r, char* name)
+{
+  char qbuf[MAX_QBUF_LENGTH];
+  int i;
+  char *pqres;
+  char *pbufname;
+  PortalBuffer *pbuf;
+  int ntups;
+  TgElement *elem;
+  TgNode *node;
+
+  /* 1) fill out the elements that are in the recipe */
+  sprintf(qbuf, Q_RETRIEVE_ELEMENTS_IN_RECIPE, name);
+  pqres = PQexec(qbuf);
+  if (*pqres == 'R' || *pqres == 'E') {
+    elog(NOTICE, "fillAllNodes(): Error while executing query : %s\n", qbuf);
+    elog(NOTICE, "result = %s, error is %s\n", pqres, PQerrormsg);
+    return;
+  }
+  pbufname = ++pqres;
+  pbuf = PQparray(pbufname);
+  ntups = PQntuplesGroup(pbuf,0);
+  for (i=0;i<ntups;i++) {
+    elem = malloc(sizeof(TgElement));
+    fillTgElement(elem, pbuf, i);
+    addArr_TgElementPtr(r->elements, &elem);
+  }
+  PQclear(pbufname);
+  
+  sprintf(qbuf, Q_RETRIEVE_NODES_IN_RECIPE, name);
+  pqres = PQexec(qbuf);
+  if (*pqres == 'R' || *pqres == 'E') {
+    elog(NOTICE, "fillAllNodes(): Error while executing query : %s\n", qbuf);
+    elog(NOTICE, "result = %s, error is %s\n", pqres, PQerrormsg);
+    return;
+  }
+  pbufname = ++pqres;
+  pbuf = PQparray(pbufname);
+  ntups = PQntuplesGroup(pbuf,0);
+  for (i=0;i<ntups;i++) {
+    node = malloc(sizeof(TgNode));
+    fillTgNode(r, node, pbuf, i);
+    addArr_TgNodePtr(r->allNodes, &node);
+  }
+  PQclear(pbufname);
+
+}
+
+
+/* -------------------------------------
+fillAllElements
+    fill out the elements of a recipe
+  ------------------------------------ */
+void 
+fillAllElements(TgRecipe *r, char* name)
+{
+  char qbuf[MAX_QBUF_LENGTH];
+  int i;
+  char *pqres;
+  char *pbufname;
+  PortalBuffer *pbuf;
+  int ntups;
+  TgElement *elem;
+
+  sprintf(qbuf, Q_RETRIEVE_ELEMENTS_IN_RECIPE, name);
+  pqres = PQexec(qbuf);
+  if (*pqres == 'R' || *pqres == 'E') {
+    elog(NOTICE, "fillAllElements(): Error while executing query : %s\n", qbuf);
+    elog(NOTICE, "result = %s, error is %s\n", pqres, PQerrormsg);
+    return;
+  }
+  pbufname = ++pqres;
+  pbuf = PQparray(pbufname);
+  ntups = PQntuplesGroup(pbuf,0);
+  for (i=0;i<ntups;i++) {
+    elem = malloc(sizeof(TgElement));
+    fillTgElement(elem, pbuf, i);
+    addArr_TgElementPtr(r->elements, &elem);
+  }
+  PQclear(pbufname);
+  
+}
+
+
+/* -------------------------------------
+fillTgRecipe
+    takes a query result in the PortalBuffer containing a Recipe
+    and converts it to a C TgRecipe strcture
+  ------------------------------------ */
+TgRecipe* 
+fillTgRecipe(PortalBuffer* pbuf, int tupno)
+{
+  TgRecipe* r;
+  int i,j;
+
+  /* 1) set up the recipe structure */
+  r = (TgRecipe*)malloc(sizeof(TgRecipe));
+  fillTgElement(&r->elmValue, pbuf, 0); 
+  r->elmValue.elemType = TG_RECIPE;
+  r->allNodes = newArr_TgNodePtr();
+  r->rootNodes = newArr_TgNodePtr();
+  r->eyes = newArr_TgNodePtr();
+  r->tees = newArr_TgNodePtr();
+  r->elements = newArr_TgElementPtr();
+
+  /* 2) find all the elements. There may be less elements than nodes
+        because you can have multiple instantiations of an element
+       in a recipe*/
+  fillAllElements(r, r->elmValue.elemName);
+
+  /* 3) find all the nodes in the recipe*/
+  fillAllNodes(r, r->elmValue.elemName);
+  
+  /* 4) find all the edges, and connect the nodes,
+        may also add tee nodes to the allNodes field*/
+  lookupEdges(r, r->elmValue.elemName);
+
+  /* 5) find all the rootNodes in the recipe */
+  /*   root nodes are nodes with no incoming nodes or
+       whose incoming nodes are all null */
+  /* 6) find all the eyes in the recipe */
+  /* eye nodes are nodes with the node type TG_EYE_NODE */
+  /* 7) find all the tee nodes in the recipe */
+  /* tee nodes are nodes with the node type TG_TEE_NODE */
+  for (i=0;i<r->allNodes->num;i++) {
+    TgNode* nptr = r->allNodes->val[i];
+
+    if (nptr->nodeType == TG_EYE_NODE) 
+       addArr_TgNodePtr(r->eyes, &nptr);
+    else
+       if (nptr->nodeType == TG_TEE_NODE)
+           addArr_TgNodePtr(r->tees, &nptr);
+
+    if (nptr->inNodes->num == 0) 
+       addArr_TgNodePtr(r->rootNodes, &nptr);
+    else {
+       for (j=0;
+          j<nptr->inNodes->num && (nptr->inNodes->val[j] == NULL);
+          j++);
+      if (j == nptr->inNodes->num)
+       addArr_TgNodePtr(r->rootNodes, &nptr);
+    }
+  }
+
+  return r;
+
+}
+
+
+/* -------------------------------------
+retrieveRecipe
+   find the recipe with the given name
+  ------------------------------------ */
+TgRecipe*
+retrieveRecipe(char* name)
+{
+  char qbuf[MAX_QBUF_LENGTH];
+  TgRecipe* recipe;
+  char *pqres;
+  char *pbufname;
+  PortalBuffer *pbuf;
+  int ntups;
+
+  sprintf(qbuf, Q_RETRIEVE_RECIPE_BYNAME, name);
+
+  pqres = PQexec(qbuf);
+  if (*pqres == 'R' || *pqres == 'E') {
+    elog(NOTICE, "retrieveRecipe: Error while executing query : %s\n", qbuf);
+    elog(NOTICE, "result = %s, error is %s\n", pqres, PQerrormsg);
+    return NULL;
+  }
+  pbufname = ++pqres;
+  pbuf = PQparray(pbufname);
+  ntups = PQntuplesGroup(pbuf,0);
+  if (ntups == 0) {
+    elog(NOTICE, "retrieveRecipe():  No recipe named %s exists\n", name);
+    return NULL;
+  }
+  if (ntups != 1) {
+    elog(NOTICE, "retrieveRecipe():  Multiple (%d) recipes named %s exists\n", ntups, name);
+    return NULL;
+  }
+
+  recipe = fillTgRecipe(pbuf,0);
+
+  PQclear(pbufname);
+  return recipe;
+
+}
+
+/* -------------------- copyXXX functions ----------------------- */
+void copyTgElementPtr(TgElementPtr* from, TgElementPtr* to)
+{
+  *to = *from;
+}
+
+void copyTgNodePtr(TgNodePtr* from, TgNodePtr* to)
+{
+  *to = *from;
+}
+
+void copyTgRecipePtr(TgRecipePtr* from, TgRecipePtr* to)
+{
+  *to = *from;
+}
+
+void copyTgString(TgString* from, TgString* to)
+{
+  TgString fromTgString = *from;
+  TgString toTgString;
+  toTgString = (TgString)malloc(strlen(fromTgString)+1);
+  strcpy(toTgString, fromTgString);
+  *to = toTgString;
+}
+
diff --git a/src/backend/tioga/tgRecipe.h b/src/backend/tioga/tgRecipe.h
new file mode 100644 (file)
index 0000000..1545494
--- /dev/null
@@ -0,0 +1,121 @@
+/*-------------------------------------------------------------------------
+ *
+ * tgRecipe.h--
+ *      Tioga recipe-related definitions and declarations
+ *      these functions can be used in both the frontend and the 
+ *      backend 
+ *    
+ *     to use this header, you must also include
+ *          "utils/geo-decls.h"
+ *      and "libpq/libpq.h"
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "libpq/libpq.h"
+#ifndef TIOGA_FRONTEND
+#include "libpq/libpq-be.h"
+#include "utils/elog.h"
+#include "utils/geo-decls.h"
+#else
+#include "libpq-fe.h"
+typedef struct {
+       double  x, y;
+} Point; /* this should match whatever is in geo-decls.h*/
+#endif /* TIOGA_FRONTEND */
+
+typedef enum {TG_INGRED,
+             TG_EYE, 
+              TG_RECIPE} TgElemType;
+
+typedef enum {  TG_SQL, 
+               TG_C, 
+               TG_RECIPE_GRAPH, 
+               TG_COMPILED
+             } TgSrcLangType;
+
+typedef enum {  TG_INGRED_NODE,
+               TG_EYE_NODE, 
+               TG_RECIPE_NODE,
+               TG_TEE_NODE /* tee nodes are not stored in the db
+                              we create them when we read the recipe
+                              back */
+           } TgNodeType;
+
+/* -- type definition for setting up in memory Tioga recipe structure -- */
+/* -- see 'recipe-schema.sql' for their corresponding database types  -- */
+
+typedef char *TgString;
+
+typedef struct _tgelement *TgElementPtr;
+typedef struct _tgnode    *TgNodePtr;
+typedef struct _tgrecipe  *TgRecipePtr;
+
+/* auto-generated header containing Arr_TgString, Arr_TgElementPtr,
+   and Arr_TgNodePtr */
+#include "tioga/Arr_TgRecipe.h" 
+
+/* C structure representation of a Tioga Element */
+typedef struct _tgelement {    
+  char          *elemName;     /* name of function this element represent */
+  TgElemType    elemType;      /* type of this element */
+  Arr_TgString  *inPorts;      /* names of inputs */
+  Arr_TgString  *inTypes;       /* name of input types */
+  Arr_TgString  *outPorts;     /* type of output */
+  Arr_TgString  *outTypes;      /* name of output types */
+  char          *doc;          /* description  of this element */
+  Arr_TgString  *keywords;      /* keywords used to search for this element */
+  char          *icon;         /* iconic representation */
+  char          *src;          /* source code for this element */
+  TgSrcLangType srcLang;        /* source language */
+  char          *owner;                /* owner recipe name */
+} TgElement;
+
+
+/* C structure representation of a Tioga Node */
+typedef struct _tgnode { 
+  char          *nodeName;     /* name of this node */
+  TgNodeType    nodeType;       /* type of this node */
+  Point       loc;             /* screen location of the node. */
+  TgElement     *nodeElem;     /* the underlying element of this node */
+  Arr_TgNodePtr *inNodes;      /* variable array of in node pointers 
+                                 * a NULL TgNodePtr indicates a run-time
+                                * parameter*/
+  Arr_TgNodePtr *outNodes;     /* variable array of out node pointers. */
+} TgNode;
+
+/* C structure representation of a Tioga Recipe */
+typedef struct _tgrecipe {
+  TgElement        elmValue;   /* "inherits" TgElement attributes. */
+  Arr_TgNodePtr    *allNodes;  /* array of all nodes for this recipe. */
+  Arr_TgNodePtr    *rootNodes; /* array of root nodes for this recipe. --
+                                  root nodes are nodes with no parents */
+  Arr_TgNodePtr    *eyes;      /* array of pointers for the browser nodes
+                                * recipe, execution of recipe starts
+                                * by traversing the recipe C structure
+                                * from the eye nodes pointed by these
+                                * pointers. */
+  Arr_TgNodePtr    *tees;       /* array of pointers of all the tee nodes */
+  Arr_TgElementPtr *elements;  /* array of all the elements in this recipe,
+                                * elements may be shared by multiple nodes */
+
+} TgRecipe;
+
+/* functions defined in tgRecipe.c */
+extern TgRecipe* retrieveRecipe(char* name);
+extern TgElement* findElemInRecipe(TgRecipe *r, char* elemName);
+extern TgNode* findNodeInRecipe(TgRecipe *r, char* nodeName);
+
+/* ---- copyXXX functions ---- */
+extern void copyTgElementPtr(TgElementPtr *, TgElementPtr *);
+extern void copyTgNodePtr(TgNodePtr *, TgNodePtr *);
+extern void copyTgRecipePtr(TgRecipePtr *, TgRecipePtr *);
+extern void copyTgString(TgString *, TgString *);
+
+
+
+
diff --git a/src/backend/utils/Gen_fmgrtab.sh b/src/backend/utils/Gen_fmgrtab.sh
new file mode 100644 (file)
index 0000000..136aa63
--- /dev/null
@@ -0,0 +1,265 @@
+#!/bin/sh
+#-------------------------------------------------------------------------
+#
+# Gen_fmgrtab.sh--
+#    shell script to generate fmgr.h and fmgrtab.c from pg_proc.h
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+# NOTES
+#    Passes any -D options on to cpp prior to generating the list
+#    of internal functions.  These come from BKIOPTS.
+#
+#-------------------------------------------------------------------------
+
+# cpp is usually in one of these places...
+PATH=/usr/lib:/lib:/usr/ccs/lib:$PATH
+
+BKIOPTS=''
+if [ $? != 0 ]
+then
+       echo `basename $0`: Bad option
+       exit 1
+fi
+
+#
+# Pass on any -D declarations, throwing away any other command
+# line switches.
+#
+for opt in $*
+do
+       case $opt in
+       -D) BKIOPTS="$BKIOPTS -D$2"; shift; shift;;
+       -D*) BKIOPTS="$BKIOPTS $1";shift;;
+       --) shift; break;;
+       -*) shift;;
+       esac
+done
+
+INFILE=$1
+RAWFILE=fmgr.raw
+HFILE=fmgr.h
+TABCFILE=fmgrtab.c
+
+#
+# Generate the file containing raw pg_proc tuple data
+# (but only for "internal" language procedures...).
+#
+# Unlike genbki.sh, which can run through cpp last, we have to
+# deal with preprocessor statements first (before we sort the
+# function table by oid).
+#
+awk '
+BEGIN          { raw = 0; }
+/^DATA/                { print; next; }
+/^BKI_BEGIN/   { raw = 1; next; }
+/^BKI_END/     { raw = 0; next; }
+raw == 1       { print; next; }' $INFILE | \
+sed    -e 's/^.*OID[^=]*=[^0-9]*//' \
+       -e 's/(//g' \
+       -e 's/[         ]*).*$//' | \
+awk '
+/^#/           { print; next; }
+$4 == "11"     { print; next; }' | \
+cpp $BKIOPTS | \
+egrep '^[0-9]' | \
+sort -n > $RAWFILE
+
+#
+# Generate fmgr.h
+#
+cat > $HFILE <<FuNkYfMgRsTuFf
+/*-------------------------------------------------------------------------
+ *
+ * $HFILE--
+ *    Definitions for using internal procedures.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *     ******************************
+ *     *** DO NOT EDIT THIS FILE! ***
+ *     ******************************
+ *
+ *     It has been GENERATED by $0
+ *     from $1
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        FMGR_H
+#define FMGR_H
+
+#include "postgres.h"                  /* for some prototype typedefs */
+
+/*
+ *     Maximum number of arguments for a built-in function.
+ *
+ *     XXX note that you cannot call a function with more than 8 
+ *         arguments from the user level since the catalogs only 
+ *         store 8 argument type values for type-checking ...
+ */
+#define        MAXFMGRARGS     9
+
+typedef struct {
+    char *data[MAXFMGRARGS];
+} FmgrValues;
+
+/*
+ * defined in fmgr.c
+ */
+extern char *fmgr_c(func_ptr user_fn, Oid func_id, int n_arguments,
+       FmgrValues *values, bool *isNull);
+extern void fmgr_info(Oid procedureId, func_ptr *function, int *nargs);
+extern char *fmgr(Oid procedureId, ... );
+extern char *fmgr_ptr(func_ptr user_fn, Oid func_id, ... );
+extern char *fmgr_array_args(Oid procedureId, int nargs, 
+                            char *args[], bool *isNull);
+
+/*
+ * defined in dfmgr.c
+ */
+extern func_ptr fmgr_dynamic(Oid procedureId, int *pronargs);
+extern void load_file(char *filename);
+
+
+/*
+ *     For performance reasons, we often want to simply jump through a
+ *     a function pointer (if it's valid, that is).  These calls have
+ *     been macroized so we can run them through a routine that does
+ *     sanity-checking (and so we can track them down more easily when
+ *     we must).
+ */
+#ifdef TRACE_FMGR_PTR
+#define        FMGR_PTR2(FP, FID, ARG1, ARG2) \
+       fmgr_ptr(FP, FID, 2, ARG1, ARG2)
+#else
+#define        FMGR_PTR2(FP, FID, ARG1, ARG2) \
+       ((FP) ? (*((func_ptr)(FP)))(ARG1, ARG2) : fmgr(FID, ARG1, ARG2))
+#endif
+
+/*
+ *     Flags for the builtin oprrest selectivity routines.
+ */
+#define        SEL_CONSTANT    1       /* constant does not vary (not a parameter) */
+#define        SEL_RIGHT       2       /* constant appears to right of operator */
+
+FuNkYfMgRsTuFf
+awk '{ print $2, $1; }' $RAWFILE | \
+tr '[a-z]' '[A-Z]' | \
+sed -e 's/^/#define F_/' >> $HFILE
+cat >> $HFILE <<FuNkYfMgRsTuFf
+
+#endif /* FMGR_H */
+FuNkYfMgRsTuFf
+
+#
+# Generate fmgr function table file.
+#
+# Print out the bogus function declarations, then the table that
+# refers to them.
+#
+cat > $TABCFILE <<FuNkYfMgRtAbStUfF
+/*-------------------------------------------------------------------------
+ *
+ * $TABCFILE--
+ *    The function manager's table of internal functions.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *
+ *     ******************************
+ *     *** DO NOT EDIT THIS FILE! ***
+ *     ******************************
+ *
+ *     It has been GENERATED by $0
+ *     from $1
+ *
+ *     We lie here to cc about the return type and arguments of the
+ *     builtin functions; all ld cares about is the fact that it
+ *     will need to resolve an external function reference.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifdef WIN32
+#include <limits.h>
+#else
+# if defined(PORTNAME_BSD44_derived) || defined(PORTNAME_bsdi)
+# include <machine/limits.h>
+# define MAXINT        INT_MAX
+# else
+# include <values.h>           /* for MAXINT */
+# endif /* PORTNAME_BSD44_derived || PORTNAME_bsdi */
+#endif /* WIN32 */
+
+#include "utils/fmgrtab.h"
+
+FuNkYfMgRtAbStUfF
+awk '{ print "extern char *" $2 "();"; }' $RAWFILE >> $TABCFILE
+cat >> $TABCFILE <<FuNkYfMgRtAbStUfF
+
+static FmgrCall fmgr_builtins[] = {
+FuNkYfMgRtAbStUfF
+awk '{ printf ("  {%d , %d , %s, \"%s\" },\n"), $1, $8, $2, $2 }' $RAWFILE >> $TABCFILE
+cat >> $TABCFILE <<FuNkYfMgRtAbStUfF
+       /* guardian value */
+#ifndef WIN32
+      { MAXINT, 0, (func_ptr) NULL }
+#else
+      { INT_MAX, 0, (func_ptr) NULL }
+#endif /* WIN32 */
+};
+
+static int fmgr_nbuiltins = (sizeof(fmgr_builtins) / sizeof(FmgrCall)) - 1;
+
+FmgrCall *fmgr_isbuiltin(Oid id)
+{
+    register int i;
+    int        low = 0;
+    int        high = fmgr_nbuiltins;
+
+    low = 0;
+    high = fmgr_nbuiltins;
+    while (low <= high) {
+       i = low + (high - low) / 2;
+       if (id == fmgr_builtins[i].proid)
+           break;
+       else if (id > fmgr_builtins[i].proid)
+           low = i + 1;
+       else
+           high = i - 1;
+    }
+    if (id == fmgr_builtins[i].proid)
+       return(&fmgr_builtins[i]);
+    return((FmgrCall *) NULL);
+}
+
+func_ptr fmgr_lookupByName(char *name) 
+{
+    int i;
+    for (i=0;i<fmgr_nbuiltins;i++) {
+       if (strcmp(name,fmgr_builtins[i].funcName) == 0)
+               return(fmgr_builtins[i].func);
+    }
+    return((func_ptr) NULL);
+}
+
+FuNkYfMgRtAbStUfF
+
+rm -f $RAWFILE
+
+# ----------------
+#      all done
+# ----------------
+exit 0
diff --git a/src/backend/utils/Makefile.inc b/src/backend/utils/Makefile.inc
new file mode 100644 (file)
index 0000000..183d253
--- /dev/null
@@ -0,0 +1,62 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for the utilities modules
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+utilsdir= $(CURDIR)/utils
+VPATH:= $(VPATH):$(utilsdir):\
+       $(utilsdir)/adt:$(utilsdir)/cache:$(utilsdir)/error:$(utilsdir)/fmgr:\
+       $(utilsdir)/hash:$(utilsdir)/init:$(utilsdir)/mmgr:$(utilsdir)/sort:\
+       $(utilsdir)/time
+
+SUBSRCS=
+include $(utilsdir)/adt/Makefile.inc
+include $(utilsdir)/cache/Makefile.inc
+include $(utilsdir)/error/Makefile.inc
+include $(utilsdir)/fmgr/Makefile.inc
+include $(utilsdir)/hash/Makefile.inc
+include $(utilsdir)/init/Makefile.inc
+include $(utilsdir)/mmgr/Makefile.inc
+include $(utilsdir)/sort/Makefile.inc
+include $(utilsdir)/time/Makefile.inc
+SRCS_UTILS:= $(SUBSRCS) fmgrtab.c
+
+GENFMGRTAB= $(utilsdir)/Gen_fmgrtab.sh
+#GENFMGRTABFILES= fmgr.h fmgrtab.c
+GENFMGRTABFILES= fmgrtab.c
+
+#
+# BKIOPTS is set in ../catalog/Makefile.inc and sets the -D flags for
+# the DATA(...); statements.  Hence, ../catalog/Makefile.inc had better
+# get slurped in prior to this Makefile.inc, or BKIOPTS should be set
+# in a higher directory level.
+#
+$(GENFMGRTABFILES): $(GENFMGRTAB) $(catdir)/pg_proc.h
+       cd $(objdir); \
+       sh $(SHOPTS) $(GENFMGRTAB) $(BKIOPTS) $(catdir)/pg_proc.h
+
+$(objdir)/fmgrtab.o:  fmgrtab.c
+       $(cc_inobjdir)
+
+POSTGRES_DEPEND+= ${GENFMGRTABFILES}
+
+#
+#${PROG}: ${GENFMGRTABFILES}
+#
+
+CLEANFILES+= $(GENFMGRTABFILES)
+
+HEADERS+= acl.h array.h bit.h builtins.h catcache.h datum.h \
+       dynamic_loader.h elog.h exc.h excid.h fcache.h fmgrtab.h \
+       geo-decls.h hsearch.h inval.h lselect.h lsyscache.h mcxt.h \
+       memutils.h module.h nabstime.h oidcompos.h palloc.h  \
+       portal.h psort.h rel.h rel2.h relcache.h sets.h \
+       syscache.h tqual.h
diff --git a/src/backend/utils/acl.h b/src/backend/utils/acl.h
new file mode 100644 (file)
index 0000000..86b7fa4
--- /dev/null
@@ -0,0 +1,163 @@
+/*-------------------------------------------------------------------------
+ *
+ * acl.h--
+ *    Definition of (and support for) access control list data structures.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    For backward-compatability purposes we have to allow there
+ *    to be a null ACL in a pg_class tuple.  This will be defined as
+ *    meaning "no protection" (i.e., old catalogs get old semantics).
+ *
+ *    The AclItems in an ACL array are currently kept in sorted order.
+ *    Things will break hard if you change that without changing the
+ *    code wherever this is included.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef ACL_H
+#define ACL_H
+
+#include "postgres.h"
+#include "utils/array.h"
+#include "nodes/parsenodes.h" /* for ChangeACLStmt */
+
+/*
+ * AclId       system identifier for the user, group, etc.
+ *             XXX currently UNIX uid for users...
+ */
+typedef uint32 AclId;
+#define        ACL_ID_WORLD    0       /* XXX only idtype should be checked */
+
+/*
+ * AclIdType   tag that describes if the AclId is a user, group, etc.
+ */
+typedef uint8 AclIdType;
+#define        ACL_IDTYPE_WORLD        0x00
+#define        ACL_IDTYPE_UID          0x01    /* user id - from pg_user */
+#define        ACL_IDTYPE_GID          0x02    /* group id - from pg_group */
+
+/*
+ * AclMode     the actual permissions
+ *             XXX should probably use bit.h routines.
+ *             XXX should probably also stuff the modechg cruft in the
+ *                 high bits, too.
+ */
+typedef uint8 AclMode;
+#define        ACL_NO          0       /* no permissions */
+#define        ACL_AP          (1<<0)  /* append */
+#define        ACL_RD          (1<<1)  /* read */
+#define        ACL_WR          (1<<2)  /* write (append/delete/replace) */
+#define        ACL_RU          (1<<3)  /* place rules */
+#define        N_ACL_MODES     4
+
+#define        ACL_MODECHG_ADD         1
+#define        ACL_MODECHG_DEL         2
+#define        ACL_MODECHG_EQL         3
+
+/* change this line if you want to set the default acl permission  */
+#define        ACL_WORLD_DEFAULT       (ACL_RD)
+/* #define     ACL_WORLD_DEFAULT       (ACL_RD|ACL_WR|ACL_AP|ACL_RU) */
+#define        ACL_OWNER_DEFAULT       (ACL_RD|ACL_WR|ACL_AP|ACL_RU)
+
+/*
+ * AclItem
+ */
+typedef struct AclItem {
+    AclId      ai_id;
+    AclIdType  ai_idtype;
+    AclMode    ai_mode;
+} AclItem;
+/* Note: if the size of AclItem changes, 
+   change the aclitem typlen in pg_type.h */
+
+/*
+ * The value of the first dimension-array element.  Since these arrays
+ * always have a lower-bound of 0, this is the same as the number of
+ * elements in the array.
+ */
+#define        ARR_DIM0(a) (((unsigned *) (((char *) a) + sizeof(ArrayType)))[0])
+
+/*
+ * Acl         a one-dimensional POSTGRES array of AclItem
+ */
+typedef ArrayType Acl;
+#define        ACL_NUM(ACL)            ARR_DIM0(ACL)
+#define        ACL_DAT(ACL)            ((AclItem *) ARR_DATA_PTR(ACL))
+#define        ACL_N_SIZE(N) \
+       ((unsigned) (ARR_OVERHEAD(1) + ((N) * sizeof(AclItem))))
+#define        ACL_SIZE(ACL)           ARR_SIZE(ACL)
+
+/*
+ * IdList      a one-dimensional POSTGRES array of AclId
+ */
+typedef ArrayType IdList;
+#define        IDLIST_NUM(IDL)         ARR_DIM0(IDL)
+#define        IDLIST_DAT(IDL)         ((AclId *) ARR_DATA_PTR(IDL))
+#define        IDLIST_N_SIZE(N) \
+       ((unsigned) (ARR_OVERHEAD(1) + ((N) * sizeof(AclId))))
+#define        IDLIST_SIZE(IDL)        ARR_SIZE(IDL)
+
+#define        ACL_MODECHG_STR         "+-="   /* list of valid characters */
+#define        ACL_MODECHG_ADD_CHR     '+'
+#define        ACL_MODECHG_DEL_CHR     '-'
+#define        ACL_MODECHG_EQL_CHR     '='
+#define        ACL_MODE_STR            "arwR"  /* list of valid characters */
+#define        ACL_MODE_AP_CHR         'a'
+#define        ACL_MODE_RD_CHR         'r'
+#define        ACL_MODE_WR_CHR         'w'
+#define        ACL_MODE_RU_CHR         'R'
+
+/* we use this warning string both for non-existent tables and
+   insufficient privilege so non-privileged users cannot ascertain whether 
+   the class exists or not */
+#define ACL_NO_PRIV_WARNING "Either no such class or insufficient privilege"
+
+/*
+ * Enable ACL execution tracing and table dumps
+ */
+/*#define ACLDEBUG_TRACE*/
+
+/*
+ * routines used internally (parser, etc.) 
+ */
+extern char *aclparse(char *s, AclItem *aip, unsigned *modechg);
+extern Acl *aclownerdefault(AclId ownerid);
+extern Acl *acldefault();
+extern Acl *aclinsert3(Acl *old_acl, AclItem *mod_aip, unsigned modechg);
+
+extern char* aclmakepriv(char* old_privlist, char new_priv);
+extern char* aclmakeuser(char* user_type, char* user);
+extern ChangeACLStmt* makeAclStmt(char* privs, List* rel_list, char* grantee,
+                                 char grant_or_revoke);
+
+/*
+ * exported routines (from acl.c)
+ */
+extern Acl *makeacl(int n);
+extern AclItem *aclitemin(char *s);
+extern char *aclitemout(AclItem *aip);
+extern Acl *aclinsert(Acl *old_acl, AclItem *mod_aip);
+extern Acl *aclremove(Acl *old_acl, AclItem *mod_aip);
+extern int32 aclcontains(Acl *acl, AclItem *aip);
+
+/*
+ * prototypes for functions in aclchk.c
+ */
+extern void ChangeAcl(char *relname, AclItem *mod_aip, unsigned modechg);
+extern AclId get_grosysid(char *groname);
+extern char *get_groname(AclId grosysid);
+extern int32 aclcheck(Acl *acl, AclId id, AclIdType idtype, AclMode mode);
+
+/* XXX move these elsewhere -pma */
+extern int32 pg_aclcheck(char *relname, char *usename, AclMode mode);
+extern int32 pg_ownercheck(char *usename, char *value, int cacheid);
+extern int32 pg_func_ownercheck(char *usename, char *funcname,
+                        int nargs, Oid *arglist);
+
+#endif /* ACL_H */
+
diff --git a/src/backend/utils/adt/Makefile.inc b/src/backend/utils/adt/Makefile.inc
new file mode 100644 (file)
index 0000000..df25b0d
--- /dev/null
@@ -0,0 +1,20 @@
+
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for utils/adt
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= acl.c arrayfuncs.c arrayutils.c bool.c char.c chunk.c date.c \
+       datum.c dt.c filename.c float.c geo-ops.c geo-selfuncs.c int.c \
+       misc.c nabstime.c name.c not_in.c numutils.c oid.c \
+       oidname.c oidint2.c oidint4.c regexp.c regproc.c selfuncs.c \
+       tid.c varchar.c varlena.c sets.c datetimes.c like.c
+
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
new file mode 100644 (file)
index 0000000..946ecc0
--- /dev/null
@@ -0,0 +1,618 @@
+/*-------------------------------------------------------------------------
+ *
+ * acl.c--
+ *    Basic access control list data structures manipulation routines.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <ctype.h>
+#include <string.h>
+#include "postgres.h"
+#include "c.h"
+#include "utils/acl.h"
+#include "access/htup.h"
+#include "catalog/pg_user.h"
+#include "utils/syscache.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+static char *getid(char *s, char *n);
+static int32 aclitemeq(AclItem *a1, AclItem *a2);
+static int32 aclitemgt(AclItem *a1, AclItem *a2);
+
+#define        ACL_IDTYPE_GID_KEYWORD  "group"
+#define        ACL_IDTYPE_UID_KEYWORD  "user"
+
+
+/*
+ * getid
+ *     Consumes the first alphanumeric string (identifier) found in string
+ *     's', ignoring any leading white space.
+ *
+ * RETURNS:
+ *     the string position in 's' that points to the next non-space character
+ *      in 's'.  Also:
+ *     - loads the identifier into 'name'.  (If no identifier is found, 'name'
+ *       contains an empty string).
+ */
+static char *
+getid(char *s, char *n)
+{
+    unsigned len;
+    char *id;
+    
+    Assert(s && n);
+    
+    while (isspace(*s))
+       ++s;
+    for (id = s, len = 0; isalnum(*s); ++len, ++s)
+       ;
+    if (len > sizeof(NameData))
+       elog(WARN, "getid: identifier cannot be >%d characters",
+            sizeof(NameData));
+    if (len > 0)
+       memmove(n, id, len);
+    n[len] = '\0';
+    while (isspace(*s))
+       ++s;
+    return(s);
+}
+
+/*
+ * aclparse
+ *     Consumes and parses an ACL specification of the form:
+ *             [group|user] [A-Za-z0-9]*[+-=][rwaR]*
+ *     from string 's', ignoring any leading white space or white space
+ *     between the optional id type keyword (group|user) and the actual
+ *     ACL specification.
+ *
+ *     This routine is called by the parser as well as aclitemin(), hence
+ *     the added generality.
+ *
+ * RETURNS:
+ *     the string position in 's' immediately following the ACL
+ *     specification.  Also:
+ *     - loads the structure pointed to by 'aip' with the appropriate
+ *       UID/GID, id type identifier and mode type values.
+ *     - loads 'modechg' with the mode change flag.
+ */
+char *
+aclparse(char *s, AclItem *aip, unsigned *modechg)
+{
+    HeapTuple htp;
+    char name[NAMEDATALEN+1];
+    extern AclId get_grosysid();
+    
+    Assert(s && aip && modechg);
+    
+    aip->ai_idtype = ACL_IDTYPE_UID;
+    s = getid(s, name);
+    if (*s != ACL_MODECHG_ADD_CHR &&
+       *s != ACL_MODECHG_DEL_CHR &&
+        *s != ACL_MODECHG_EQL_CHR)
+       {       /* we just read a keyword, not a name */
+       if (!strcmp(name, ACL_IDTYPE_GID_KEYWORD)) {
+           aip->ai_idtype = ACL_IDTYPE_GID;
+       } else if (strcmp(name, ACL_IDTYPE_UID_KEYWORD)) {
+           elog(WARN, "aclparse: bad keyword, must be [group|user]");
+       }
+       s = getid(s, name); /* move s to the name beyond the keyword */
+       if (name[0] == '\0')
+           elog(WARN, "aclparse: a name must follow the [group|user] keyword");
+    }
+    if (name[0] == '\0')
+       aip->ai_idtype = ACL_IDTYPE_WORLD;
+    
+    switch (*s) {
+    case ACL_MODECHG_ADD_CHR: *modechg = ACL_MODECHG_ADD; break;
+    case ACL_MODECHG_DEL_CHR: *modechg = ACL_MODECHG_DEL; break;
+    case ACL_MODECHG_EQL_CHR: *modechg = ACL_MODECHG_EQL; break;
+    default:  elog(WARN, "aclparse: mode change flag must use \"%s\"",
+                  ACL_MODECHG_STR);
+    }
+    
+    aip->ai_mode = ACL_NO;
+    while (isalpha(*++s)) {
+       switch (*s) {
+       case ACL_MODE_AP_CHR: aip->ai_mode |= ACL_AP; break;
+       case ACL_MODE_RD_CHR: aip->ai_mode |= ACL_RD; break;
+       case ACL_MODE_WR_CHR: aip->ai_mode |= ACL_WR; break;
+       case ACL_MODE_RU_CHR: aip->ai_mode |= ACL_RU; break;
+       default:  elog(WARN, "aclparse: mode flags must use \"%s\"",
+                      ACL_MODE_STR);
+       }
+    }
+    
+    switch (aip->ai_idtype) {
+    case ACL_IDTYPE_UID:
+       htp = SearchSysCacheTuple(USENAME, PointerGetDatum(name),
+                                 0,0,0);
+       if (!HeapTupleIsValid(htp))
+           elog(WARN, "aclparse: non-existent user \"%s\"", name);
+       aip->ai_id = ((Form_pg_user) GETSTRUCT(htp))->usesysid;
+       break;
+    case ACL_IDTYPE_GID:
+       aip->ai_id = get_grosysid(name);
+       break;
+    case ACL_IDTYPE_WORLD:
+       aip->ai_id = ACL_ID_WORLD;
+       break;
+    }
+    
+#ifdef ACLDEBUG_TRACE
+    elog(DEBUG, "aclparse: correctly read [%x %d %x], modechg=%x",
+        aip->ai_idtype, aip->ai_id, aip->ai_mode, *modechg);
+#endif
+    return(s);
+}
+
+/*
+ * makeacl
+ *     Allocates storage for a new Acl with 'n' entries.
+ *
+ * RETURNS:
+ *     the new Acl
+ */
+Acl *
+makeacl(int n)
+{
+    Acl *new_acl;
+    Size size;
+    
+    if (n < 0)
+       elog(WARN, "makeacl: invalid size: %d\n", n);
+    size = ACL_N_SIZE(n);
+    if (!(new_acl = (Acl *) palloc(size)))
+       elog(WARN, "makeacl: palloc failed on %d\n", size);
+    memset((char *) new_acl, 0, size);
+    new_acl->size = size;
+    new_acl->ndim = 1;
+    new_acl->flags = 0;
+    ARR_LBOUND(new_acl)[0] = 0;
+    ARR_DIMS(new_acl)[0] = n;
+    return(new_acl);
+}
+
+/*
+ * aclitemin
+ *     Allocates storage for, and fills in, a new AclItem given a string
+ *     's' that contains an ACL specification.  See aclparse for details.
+ *
+ * RETURNS:
+ *     the new AclItem
+ */
+AclItem *
+aclitemin(char *s)
+{
+    unsigned modechg;
+    AclItem *aip;
+    
+    if (!s)
+       elog(WARN, "aclitemin: null string");
+    
+    aip = (AclItem *) palloc(sizeof(AclItem));
+    if (!aip)
+       elog(WARN, "aclitemin: palloc failed");
+    s = aclparse(s, aip, &modechg);
+    if (modechg != ACL_MODECHG_EQL)
+       elog(WARN, "aclitemin: cannot accept anything but = ACLs");
+    while (isspace(*s))
+       ++s;
+    if (*s)
+       elog(WARN, "aclitemin: extra garbage at end of specification");
+    return(aip);
+}
+
+/*
+ * aclitemout
+ *     Allocates storage for, and fills in, a new null-delimited string
+ *     containing a formatted ACL specification.  See aclparse for details.
+ *
+ * RETURNS:
+ *     the new string
+ */
+char *
+aclitemout(AclItem *aip)
+{
+    register char *p;
+    char *out;
+    HeapTuple htp;
+    unsigned i;
+    static AclItem default_aclitem = { ACL_ID_WORLD,
+                                          ACL_IDTYPE_WORLD,
+                                          ACL_WORLD_DEFAULT };
+    extern char *int2out();
+    char *tmpname;
+    
+    if (!aip)
+       aip = &default_aclitem;
+    
+    p = out = palloc(strlen("group =arwR ") + 1 + NAMEDATALEN);
+    if (!out)
+       elog(WARN, "aclitemout: palloc failed");
+    *p = '\0';
+    
+    switch (aip->ai_idtype) {
+    case ACL_IDTYPE_UID:
+       htp = SearchSysCacheTuple(USESYSID, ObjectIdGetDatum(aip->ai_id),
+                                 0,0,0);
+       if (!HeapTupleIsValid(htp)) {
+           char *tmp = int2out(aip->ai_id);
+           
+           elog(NOTICE, "aclitemout: usesysid %d not found",
+                aip->ai_id);
+           (void) strcat(p, tmp);
+           pfree(tmp);
+       } else
+           (void) strncat(p, (char *) &((Form_pg_user)
+                                        GETSTRUCT(htp))->usename,
+                          sizeof(NameData));
+       break;
+    case ACL_IDTYPE_GID:
+       (void) strcat(p, "group ");
+       tmpname = get_groname(aip->ai_id);
+       (void) strncat(p, tmpname, NAMEDATALEN);
+       pfree(tmpname);
+       break;
+    case ACL_IDTYPE_WORLD:
+       break;
+    default:
+       elog(WARN, "aclitemout: bad ai_idtype: %d", aip->ai_idtype);
+       break;
+    }
+    while (*p)
+       ++p;
+    *p++ = '=';
+    for (i = 0; i < N_ACL_MODES; ++i)
+       if ((aip->ai_mode >> i) & 01)
+           *p++ = ACL_MODE_STR[i];
+    *p = '\0';
+    
+    return(out);
+}
+
+/*
+ * aclitemeq
+ * aclitemgt
+ *     AclItem equality and greater-than comparison routines.
+ *     Two AclItems are equal iff they are both NULL or they have the
+ *     same identifier (and identifier type).
+ *
+ * RETURNS:
+ *     a boolean value indicating = or >
+ */
+static int32
+aclitemeq(AclItem *a1, AclItem *a2)
+{
+    if (!a1 && !a2)
+       return(1);
+    if (!a1 || !a2)
+       return(0);
+    return(a1->ai_idtype == a2->ai_idtype && a1->ai_id == a2->ai_id);
+}
+
+static int32
+aclitemgt(AclItem *a1, AclItem *a2)
+{
+    if (a1 && !a2)
+       return(1);
+    if (!a1 || !a2)
+       return(0);
+    return((a1->ai_idtype > a2->ai_idtype) ||
+          (a1->ai_idtype == a2->ai_idtype && a1->ai_id > a2->ai_id));
+}
+
+Acl *
+aclownerdefault(AclId ownerid)
+{
+    Acl *acl;
+    AclItem *aip;
+    
+    acl = makeacl(2);
+    aip = ACL_DAT(acl);
+    aip[0].ai_idtype = ACL_IDTYPE_WORLD;
+    aip[0].ai_id = ACL_ID_WORLD;
+    aip[0].ai_mode = ACL_WORLD_DEFAULT;
+    aip[1].ai_idtype = ACL_IDTYPE_UID;
+    aip[1].ai_id = ownerid;
+    aip[1].ai_mode = ACL_OWNER_DEFAULT;
+    return(acl);
+}
+
+Acl *
+acldefault()
+{
+    Acl *acl;
+    AclItem *aip;
+    
+    acl = makeacl(1);
+    aip = ACL_DAT(acl);
+    aip[0].ai_idtype = ACL_IDTYPE_WORLD;
+    aip[0].ai_id = ACL_ID_WORLD;
+    aip[0].ai_mode = ACL_WORLD_DEFAULT;
+    return(acl);
+}
+
+Acl *
+aclinsert3(Acl *old_acl, AclItem *mod_aip, unsigned modechg)
+{
+    Acl *new_acl;
+    AclItem *old_aip, *new_aip;
+    unsigned src, dst, num;
+    
+    if (!old_acl || ACL_NUM(old_acl) < 1) {
+       new_acl = makeacl(0);
+       return(new_acl);
+    }
+    if (!mod_aip) {
+       new_acl = makeacl(ACL_NUM(old_acl));
+       memmove((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
+       return(new_acl);
+    }
+    
+    num = ACL_NUM(old_acl);
+    old_aip = ACL_DAT(old_acl);
+    
+    /*
+     * Search the ACL for an existing entry for 'id'.  If one exists,
+     * just modify the entry in-place (well, in the same position, since
+     * we actually return a copy); otherwise, insert the new entry in
+     * sort-order.
+     */
+    /* find the first element not less than the element to be inserted */
+    for (dst = 0; dst < num && aclitemgt(mod_aip, old_aip+dst); ++dst)
+       ;
+    if (dst < num && aclitemeq(mod_aip, old_aip+dst)) {
+       /* modify in-place */
+       new_acl = makeacl(ACL_NUM(old_acl));
+       memmove((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
+       new_aip = ACL_DAT(new_acl);
+       src = dst;
+    } else {
+      new_acl = makeacl(num + 1);
+       new_aip = ACL_DAT(new_acl);
+       if (dst == 0) {                         /* start */
+           elog(WARN, "aclinsert3: insertion before world ACL??");
+       } else if (dst >= num) {                /* end */
+           memmove((char *) new_aip,
+                   (char *) old_aip,
+                   num * sizeof(AclItem));
+       } else {                                /* middle */
+           memmove((char *) new_aip,
+                   (char *) old_aip,
+                   dst * sizeof(AclItem));
+           memmove((char *) (new_aip+dst+1),
+                   (char *) (old_aip+dst),
+                   (num - dst) * sizeof(AclItem));
+       }
+       new_aip[dst].ai_id = mod_aip->ai_id;
+       new_aip[dst].ai_idtype = mod_aip->ai_idtype;
+      num++;          /* set num to the size of new_acl */
+       src = 0;        /* world entry */
+    }
+    switch (modechg) {
+    case ACL_MODECHG_ADD: new_aip[dst].ai_mode =
+       old_aip[src].ai_mode | mod_aip->ai_mode;
+       break;
+    case ACL_MODECHG_DEL: new_aip[dst].ai_mode =
+       old_aip[src].ai_mode & ~mod_aip->ai_mode;
+       break;
+    case ACL_MODECHG_EQL: new_aip[dst].ai_mode =
+       mod_aip->ai_mode;
+       break;
+    }
+    /* if the newly added entry has no permissions, delete it from
+       the list.  For example, this helps in removing entries for users who
+       no longer exists...*/
+    for (dst = 1; dst < num; dst++) {
+       if (new_aip[dst].ai_mode == 0) {
+           int i;
+           for (i=dst+1; i<num; i++) {
+               new_aip[i-1].ai_id = new_aip[i].ai_id;
+               new_aip[i-1].ai_idtype = new_aip[i].ai_idtype;
+               new_aip[i-1].ai_mode = new_aip[i].ai_mode;
+           }
+           ARR_DIMS(new_acl)[0] = num -1 ;
+           break;
+       }
+    }
+
+    return(new_acl);
+}
+
+/*
+ * aclinsert
+ *
+ */
+Acl *
+aclinsert(Acl *old_acl, AclItem *mod_aip)
+{
+    return(aclinsert3(old_acl, mod_aip, ACL_MODECHG_EQL));
+}
+
+Acl *
+aclremove(Acl *old_acl, AclItem *mod_aip)
+{
+    Acl *new_acl;
+    AclItem *old_aip, *new_aip;
+    unsigned dst, old_num, new_num;
+    
+    if (!old_acl || ACL_NUM(old_acl) < 1) {
+       new_acl = makeacl(0);
+       return(new_acl);
+    }
+    if (!mod_aip) {
+       new_acl = makeacl(ACL_NUM(old_acl));
+       memmove((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
+       return(new_acl);
+    }
+    
+    old_num = ACL_NUM(old_acl);
+    old_aip = ACL_DAT(old_acl);
+    
+    for (dst = 0; dst < old_num && !aclitemeq(mod_aip, old_aip+dst); ++dst)
+       ;
+    if (dst >= old_num) {                      /* not found or empty */
+       new_acl = makeacl(ACL_NUM(old_acl));
+       memmove((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
+    } else {
+       new_num = old_num - 1;
+       new_acl = makeacl(ACL_NUM(old_acl) - 1);
+       new_aip = ACL_DAT(new_acl);
+       if (dst == 0) {                 /* start */
+           elog(WARN, "aclremove: removal of the world ACL??");
+       } else if (dst == old_num - 1) {/* end */
+           memmove((char *) new_aip,
+                   (char *) old_aip,
+                   new_num * sizeof(AclItem));
+       } else {                        /* middle */
+           memmove((char *) new_aip,
+                   (char *) old_aip,
+                   dst * sizeof(AclItem));
+           memmove((char *) (new_aip+dst),
+                   (char *) (old_aip+dst+1),
+                   (new_num - dst) * sizeof(AclItem));
+       }
+    }
+    return(new_acl);
+}
+
+int32
+aclcontains(Acl *acl, AclItem *aip)
+{
+    unsigned i, num;
+    AclItem *aidat;
+    
+    if (!acl || !aip || ((num = ACL_NUM(acl)) < 1))
+       return(0);
+    aidat = ACL_DAT(acl);
+    for (i = 0; i < num; ++i)
+       if (aclitemeq(aip, aidat+i))
+           return(1);
+    return(0);
+}
+
+/* parser support routines */
+
+/*
+ * aclmakepriv
+ *    make a acl privilege string out of an existing privilege string
+ * and a new privilege
+ *
+ * does not add duplicate privileges
+ *  
+ * the CALLER is reponsible for free'ing the string returned
+ */
+
+char* 
+aclmakepriv(char* old_privlist, char new_priv)
+{
+    char* priv;
+    int i;
+    int l;
+
+    Assert(strlen(old_privlist)<5);
+    priv = malloc(5); /* at most "rwaR" */;
+
+    if (old_privlist == NULL || old_privlist[0] == '\0') {
+       priv[0] = new_priv;
+       priv[1] = '\0';
+       return priv;
+    }
+
+    strcpy(priv,old_privlist);
+    
+    l = strlen(old_privlist);
+
+    if (l == 4) { /* can't add any more privileges */
+       return priv;
+    }
+
+    /* check to see if the new privilege is already in the old string */
+    for (i=0;i<l;i++) {
+       if (priv[i] == new_priv)
+           break;
+    }
+    if (i == l)  { /* we really have a new privilege*/
+       priv[l] = new_priv;
+       priv[l+1] = '\0';
+    }
+       
+    return priv;
+}
+
+/*
+ * aclmakeuser
+ *    user_type must be "A"  - all users
+ *                      "G"  - group
+ *                      "U"  - user
+ *
+ * concatentates the two strings together with a space in between
+ * 
+ * this routine is used in the parser
+ * 
+ * the CALLER is responsible for freeing the memory allocated
+ */
+
+char*
+aclmakeuser(char* user_type, char* user)
+{
+    char* user_list;
+    
+    user_list = malloc(strlen(user) + 3);
+    sprintf(user_list, "%s %s", user_type, user);
+    return user_list;
+}
+
+
+/*
+ * makeAclStmt:
+ *    this is a helper routine called by the parser 
+ * create a ChangeAclStmt
+ *    we take in the privilegs, relation_name_list, and grantee
+ * as well as a single character '+' or '-' to indicate grant or revoke
+ *
+ * returns a new ChangeACLStmt*
+ *
+ * this routines works by creating a old-style changle acl string and
+ * then calling aclparse;
+ */
+
+ChangeACLStmt* 
+makeAclStmt(char* privileges, List* rel_list, char* grantee, 
+           char grant_or_revoke)
+{
+    ChangeACLStmt *n = makeNode(ChangeACLStmt);
+    char str[MAX_PARSE_BUFFER];
+     
+    n->aclitem = (AclItem*)palloc(sizeof(AclItem));
+  /* the grantee string is "G <group_name>", "U  <user_name>", or "ALL" */
+    if (grantee[0] == 'G') /* group permissions */
+       {
+           sprintf(str,"%s %s%c%s",
+                   ACL_IDTYPE_GID_KEYWORD,
+                   grantee+2, grant_or_revoke,privileges);
+       } 
+    else if (grantee[0] == 'U')  /* user permission */
+       {
+           sprintf(str,"%s %s%c%s",
+                   ACL_IDTYPE_UID_KEYWORD,
+                   grantee+2, grant_or_revoke,privileges);
+       }
+    else  /* all permission */
+       {
+           sprintf(str,"%c%s",
+                   grant_or_revoke,privileges);
+       }
+    n->relNames = rel_list;
+    aclparse(str, n->aclitem, (unsigned*)&n->modechg);
+    return n;
+}
+
+
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
new file mode 100644 (file)
index 0000000..01a4af0
--- /dev/null
@@ -0,0 +1,1375 @@
+/*-------------------------------------------------------------------------
+ *
+ * arrayfuncs.c--
+ *    Special functions for arrays.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <ctype.h>
+#include <stdio.h>
+
+#include "postgres.h"
+
+#include "catalog/catalog.h"
+#include "catalog/pg_type.h"
+
+#include "utils/syscache.h"
+#include "utils/palloc.h"
+#include "utils/memutils.h"
+#include "storage/fd.h"                /* for SEEK_ */
+#include "fmgr.h"
+#include "utils/elog.h"
+#include "utils/array.h"
+
+#include "libpq/libpq-fs.h"
+#include "libpq/be-fsstubs.h"
+
+#define ASSGN    "="
+
+/* An array has the following internal structure:
+ *    <nbytes>            - total number of bytes 
+ *     <ndim>                - number of dimensions of the array 
+ *    <flags>                - bit mask of flags
+ *    <dim>                - size of each array axis 
+ *    <dim_lower>            - lower boundary of each dimension 
+ *    <actual data>        - whatever is the stored data
+*/
+
+/*-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-*/
+static int _ArrayCount(char *str, int dim[], int typdelim);
+static char *_ReadArrayStr(char *arrayStr, int nitems, int ndim, int dim[],
+                          func_ptr inputproc, Oid typelem, char typdelim,
+                          int typlen, bool typbyval, char typalign,
+                          int *nbytes);
+static char *_ReadLOArray(char *str, int *nbytes, int *fd, bool *chunkFlag,
+                         int ndim, int dim[], int baseSize);
+static void _CopyArrayEls(char **values, char *p, int nitems, int typlen,
+                         char typalign, bool typbyval);
+static void system_cache_lookup(Oid element_type, bool input, int *typlen,
+               bool *typbyval, char *typdelim, Oid *typelem, Oid *proc,
+               char *typalign);
+static Datum _ArrayCast(char *value, bool byval, int len);
+static char *_AdvanceBy1word(char *str, char **word);
+static void _ArrayRange(int st[], int endp[], int bsize, char *destPtr,
+                       ArrayType *array, int from);
+static int _ArrayClipCount(int stI[], int endpI[], ArrayType *array);
+static void _LOArrayRange(int st[], int endp[], int bsize, int srcfd,
+         int destfd, ArrayType *array, int isSrcLO, bool *isNull);
+static void _ReadArray (int st[], int endp[], int bsize, int srcfd, int destfd,
+                      ArrayType *array, int isDestLO, bool *isNull);
+static char *_array_set(ArrayType *array, struct varlena *indx_str,
+                       struct varlena *dataPtr);
+static ArrayCastAndSet(char *src, bool typbyval, int typlen, char *dest);
+
+
+/*---------------------------------------------------------------------
+ * array_in : 
+ *        converts an array from the external format in "string" to
+ *        it internal format.
+ * return value :
+ *        the internal representation of the input array
+ *--------------------------------------------------------------------
+ */
+char *
+array_in(char *string,         /* input array in external form */
+        Oid element_type)      /* type OID of an array element */
+{
+    int typlen;
+    bool typbyval, done;
+    char typdelim;
+    Oid typinput;
+    Oid typelem;
+    char *string_save, *p, *q, *r;
+    func_ptr inputproc;
+    int i, nitems, dummy;
+    int32 nbytes;
+    char *dataPtr;
+    ArrayType *retval;
+    int ndim, dim[MAXDIM], lBound[MAXDIM];
+    char typalign;
+    
+    system_cache_lookup(element_type, true, &typlen, &typbyval, &typdelim, 
+                       &typelem, &typinput, &typalign);
+    
+    fmgr_info(typinput, &inputproc, &dummy);
+    
+    string_save = (char *) palloc(strlen(string) + 3);
+    strcpy(string_save, string);
+    
+    /* --- read array dimensions  ---------- */
+    p = q = string_save; done = false;
+    for ( ndim = 0; !done; ) {
+        while (isspace(*p)) p++;
+        if (*p == '[' ) {
+           p++;
+           if ((r = (char *)strchr(p, ':')) == (char *)NULL)
+               lBound[ndim] = 1;
+           else { 
+               *r = '\0'; 
+               lBound[ndim] = atoi(p); 
+               p = r + 1;
+           }
+           for (q = p; isdigit(*q); q++);
+           if (*q != ']')
+               elog(WARN, "array_in: missing ']' in array declaration");
+           *q = '\0';
+           dim[ndim] = atoi(p);
+           if ((dim[ndim] < 0) || (lBound[ndim] < 0))
+               elog(WARN,"array_in: array dimensions need to be positive");
+           dim[ndim] = dim[ndim] - lBound[ndim] + 1;
+           if (dim[ndim] < 0)
+               elog(WARN, "array_in: upper_bound cannot be < lower_bound");
+           p = q + 1; ndim++;
+        } else {
+            done = true;
+        }
+    }
+    
+    if (ndim == 0) {
+        if (*p == '{') {
+            ndim = _ArrayCount(p, dim, typdelim);
+            for (i = 0; i < ndim; lBound[i++] = 1);
+        } else { 
+           elog(WARN,"array_in: Need to specify dimension");
+        }
+    } else {
+        while (isspace(*p)) p++;
+        if (strncmp(p, ASSGN, strlen(ASSGN)))
+            elog(WARN, "array_in: missing assignment operator");
+        p+= strlen(ASSGN);
+        while (isspace(*p)) p++;
+    }        
+    
+    nitems = getNitems( ndim, dim);
+    if (nitems == 0) {
+        char *emptyArray = palloc(sizeof(ArrayType));
+        memset(emptyArray, 0, sizeof(ArrayType));
+        * (int32 *) emptyArray = sizeof(ArrayType);
+        return emptyArray;
+    }
+    
+    if (*p == '{') {
+        /* array not a large object */
+        dataPtr =
+           (char *) _ReadArrayStr(p, nitems,  ndim, dim, inputproc, typelem,
+                                  typdelim, typlen, typbyval, typalign,
+                                  &nbytes );
+        nbytes += ARR_OVERHEAD(ndim);
+        retval = (ArrayType *) palloc(nbytes);
+       memset(retval,0, nbytes);
+        memmove(retval, (char *)&nbytes, sizeof(int)); 
+        memmove((char*)ARR_NDIM_PTR(retval), (char *)&ndim, sizeof(int)); 
+        SET_LO_FLAG (false, retval);
+        memmove((char *)ARR_DIMS(retval), (char *)dim, ndim*sizeof(int)); 
+        memmove((char *)ARR_LBOUND(retval), (char *)lBound, 
+               ndim*sizeof(int));
+       /* dataPtr is an array of arbitraystuff even though its type is char*
+          cast to char** to pass to _CopyArrayEls for now  - jolly */
+        _CopyArrayEls((char**)dataPtr,
+                     ARR_DATA_PTR(retval), nitems,
+                     typlen, typalign, typbyval);
+    } else  {
+#ifdef LOARRAY
+        int dummy, bytes;
+        bool chunked = false;
+       
+        dataPtr = _ReadLOArray(p, &bytes, &dummy, &chunked, ndim, 
+                              dim, typlen );
+        nbytes = bytes + ARR_OVERHEAD(ndim);
+        retval = (ArrayType *) palloc(nbytes);
+        memset(retval, 0,nbytes); 
+        memmove(retval, (char *)&nbytes, sizeof(int));
+        memmove((char *)ARR_NDIM_PTR(retval), (char *)&ndim, sizeof(int));
+        SET_LO_FLAG (true, retval);
+        SET_CHUNK_FLAG (chunked, retval);
+        memmove((char *)ARR_DIMS(retval), (char *)dim, ndim*sizeof(int));
+        memmove((char *)ARR_LBOUND(retval),(char *)lBound, ndim*sizeof(int));
+        memmove(ARR_DATA_PTR(retval), dataPtr, bytes);
+#endif
+       elog(WARN, "large object arrays not supported");
+    }
+    pfree(string_save);
+    return((char *)retval);
+}
+
+/*-----------------------------------------------------------------------------
+ * _ArrayCount --
+ *   Counts the number of dimensions and the dim[] array for an array string. 
+ *      The syntax for array input is C-like nested curly braces 
+ *-----------------------------------------------------------------------------
+ */
+static int
+_ArrayCount(char *str, int dim[], int typdelim)
+{
+    int nest_level = 0, i; 
+    int ndim = 0, temp[MAXDIM];
+    bool scanning_string = false;
+    bool eoArray = false;
+    char *q;
+    
+    for (i = 0; i < MAXDIM; ++i) {
+       temp[i] = dim[i] = 0;
+    }
+    
+    if (strncmp (str, "{}", 2) == 0) return(0); 
+    
+    q = str;
+    while (eoArray != true) {
+        bool done = false;
+        while (!done) {
+            switch (*q) {
+           case '\"':
+               scanning_string = ! scanning_string;
+               break;
+           case '{':
+               if (!scanning_string)  { 
+                   temp[nest_level] = 0;
+                   nest_level++;
+               }
+               break;
+           case '}':
+               if (!scanning_string) {
+                   if (!ndim) ndim = nest_level;
+                   nest_level--;
+                   if (nest_level) temp[nest_level-1]++;
+                   if (nest_level == 0) eoArray = done = true;
+               }
+               break;
+           default:
+               if (!ndim) ndim = nest_level;
+               if (*q == typdelim && !scanning_string )
+                   done = true;
+               break;
+            }
+            if (!done) q++;
+        }
+        temp[ndim-1]++;
+        q++;
+        if (!eoArray) 
+            while (isspace(*q)) q++;
+    }
+    for (i = 0; i < ndim; ++i) {
+       dim[i] = temp[i];
+    }
+    
+    return(ndim);
+}
+
+/*---------------------------------------------------------------------------
+ * _ReadArrayStr :
+ *   parses the array string pointed by "arrayStr" and converts it in the 
+ *   internal format. The external format expected is like C array
+ *   declaration. Unspecified elements are initialized to zero for fixed length
+ *   base types and to empty varlena structures for variable length base
+ *   types.
+ * result :
+ *   returns the internal representation of the array elements
+ *   nbytes is set to the size of the array in its internal representation.
+ *---------------------------------------------------------------------------
+ */
+static char *
+_ReadArrayStr(char *arrayStr,
+             int nitems,
+             int ndim,
+             int dim[],
+             func_ptr inputproc, /* function used for the conversion */
+             Oid typelem,
+             char typdelim,
+             int typlen,
+             bool typbyval,
+             char typalign,
+             int *nbytes)
+{
+    int i, nest_level = 0;
+    char *p, *q, *r, **values;
+    bool scanning_string = false;
+    int indx[MAXDIM], prod[MAXDIM];
+    bool eoArray = false;
+    
+    mda_get_prod(ndim, dim, prod);
+    for (i = 0; i < ndim; indx[i++] = 0);
+    /* read array enclosed within {} */    
+    values = (char **) palloc(nitems * sizeof(char *));
+    memset(values, 0, nitems * sizeof(char *));
+    q = p = arrayStr;
+    
+    while ( ! eoArray ) {
+        bool done = false;
+        int i = -1;
+       
+        while (!done) {
+            switch (*q) {
+           case '\\':
+               /* Crunch the string on top of the backslash. */
+               for (r = q; *r != '\0'; r++) *r = *(r+1);
+               break;
+           case '\"':
+               if (!scanning_string ) {
+                   while (p != q) p++;
+                   p++; /* get p past first doublequote */
+               } else 
+                   *q = '\0';
+               scanning_string = ! scanning_string;
+               break;
+           case '{':
+               if (!scanning_string) { 
+                   p++; 
+                   nest_level++;
+                   if (nest_level > ndim)
+                       elog(WARN, "array_in: illformed array constant");
+                   indx[nest_level - 1] = 0;
+                   indx[ndim - 1] = 0;
+               }
+               break;
+           case '}':
+               if (!scanning_string) {
+                   if (i == -1)
+                       i = tuple2linear(ndim, indx, prod); 
+                   nest_level--;
+                   if (nest_level == 0)
+                       eoArray = done = true;
+                   else { 
+                       *q = '\0';
+                       indx[nest_level - 1]++;
+                   }
+               }
+               break;
+           default:
+               if (*q == typdelim && !scanning_string ) {
+                   if (i == -1) 
+                       i = tuple2linear(ndim, indx, prod); 
+                   done = true;
+                   indx[ndim - 1]++;
+               }
+               break;
+            }
+            if (!done) 
+                q++;
+        }
+        *q = '\0';                    
+        if (i >= nitems)
+            elog(WARN, "array_in: illformed array constant");
+        values[i] = (*inputproc) (p, typelem);
+        p = ++q;
+        if (!eoArray)    
+            /* 
+             * if not at the end of the array skip white space 
+             */
+            while (isspace(*q)) {
+                p++;
+                q++;
+            }
+    }
+    if (typlen > 0) {
+        *nbytes = nitems * typlen;
+        if (!typbyval)
+            for (i = 0; i < nitems; i++)
+                if (!values[i]) {
+                    values[i] = palloc(typlen);
+                    memset(values[i], 0, typlen);
+                }
+    } else {
+        for (i = 0, *nbytes = 0; i < nitems; i++) {
+            if (values[i]) {
+               if (typalign=='d') {
+                   *nbytes += DOUBLEALIGN(* (int32 *) values[i]);
+               } else {
+                   *nbytes += INTALIGN(* (int32 *) values[i]);
+               }
+           } else {
+                *nbytes += sizeof(int32);
+                values[i] = palloc(sizeof(int32));
+                *(int32 *)values[i] = sizeof(int32); 
+            }
+        }
+    }
+    return((char *)values);
+}
+
+
+/*----------------------------------------------------------------------------
+ * Read data about an array to be stored as a large object                    
+ *----------------------------------------------------------------------------
+ */
+static char *
+_ReadLOArray(char *str,
+            int *nbytes,
+            int *fd,
+            bool *chunkFlag,
+            int ndim,
+            int dim[],
+            int baseSize)
+{
+    char *inputfile, *accessfile = NULL, *chunkfile = NULL;
+    char *retStr, *_AdvanceBy1word();
+    Oid lobjId;
+    
+    str = _AdvanceBy1word(str, &inputfile); 
+    
+    while (str != NULL) {
+        char *word;
+        
+        str = _AdvanceBy1word(str, &word); 
+       
+        if (!strcmp (word, "-chunk")) {
+            if (str == NULL)
+                elog(WARN, "array_in: access pattern file required");
+            str = _AdvanceBy1word(str, &accessfile);
+        }
+        else if (!strcmp (word, "-noreorg"))  {
+            if (str == NULL)
+                elog(WARN, "array_in: chunk file required");
+            str = _AdvanceBy1word(str, &chunkfile);
+        } else {
+            elog(WARN, "usage: <input file> -chunk DEFAULT/<access pattern file> -invert/-native [-noreorg <chunk file>]");
+       }
+    }
+    
+    if (inputfile == NULL) 
+        elog(WARN, "array_in: missing file name");
+    lobjId = lo_creat(0);
+    *fd = lo_open(lobjId, INV_READ);
+    if ( *fd < 0 )
+       elog(WARN, "Large object create failed");
+    retStr = inputfile;
+    *nbytes = strlen(retStr) + 2;
+    
+    if ( accessfile ) {
+        FILE *afd;
+        if ((afd = fopen (accessfile, "r")) == NULL)
+            elog(WARN, "unable to open access pattern file");
+        *chunkFlag = true;
+        retStr = _ChunkArray(*fd, afd, ndim, dim, baseSize, nbytes,
+                            chunkfile);
+    }    
+    return(retStr);
+}
+
+static void
+_CopyArrayEls(char **values, 
+             char *p, 
+             int nitems, 
+             int typlen,
+             char typalign,
+             bool typbyval)
+{
+    int i;
+    
+    for (i = 0; i < nitems; i++) {
+        int inc;
+        inc = ArrayCastAndSet(values[i], typbyval, typlen, p);
+        p += inc;
+        if (!typbyval) 
+            pfree(values[i]);
+    }
+    pfree(values);
+}
+
+/*-------------------------------------------------------------------------
+ * array_out : 
+ *         takes the internal representation of an array and returns a string
+ *        containing the array in its external format.
+ *-------------------------------------------------------------------------
+ */
+char *
+array_out(ArrayType *v, Oid element_type)
+{
+    int typlen;
+    bool typbyval;
+    char typdelim;
+    Oid typoutput, typelem;
+    func_ptr outputproc;
+    char typalign;
+    
+    char *p, *retval, **values, delim[2];
+    int nitems, overall_length, i, j, k, indx[MAXDIM];
+    bool dummy_bool;
+    int dummy_int;
+    int ndim, *dim;
+    
+    if (v == (ArrayType *) NULL)
+        return ((char *) NULL);
+    
+    if (ARR_IS_LO(v) == true)  {
+        char *p, *save_p;
+        int nbytes;
+       
+        /* get a wide string to print to */
+        p = array_dims(v, &dummy_bool);
+        nbytes = strlen(ARR_DATA_PTR(v)) + 4 + *(int *)p;
+       
+        save_p = (char *) palloc(nbytes);
+       
+        strcpy(save_p, p + sizeof(int));
+        strcat(save_p, ASSGN);
+        strcat(save_p, ARR_DATA_PTR(v));
+        pfree(p);
+        return (save_p);
+    }
+    
+    system_cache_lookup(element_type, false, &typlen, &typbyval,
+                        &typdelim, &typelem, &typoutput, &typalign);
+    fmgr_info(typoutput, & outputproc, &dummy_int);
+    sprintf(delim, "%c", typdelim);
+    ndim = ARR_NDIM(v);
+    dim = ARR_DIMS(v);
+    nitems = getNitems(ndim, dim);
+    
+    if (nitems == 0) {
+        char *emptyArray = palloc(3);
+        emptyArray[0] = '{';
+        emptyArray[1] = '}';
+        emptyArray[2] = '\0';
+        return emptyArray;
+    }
+    
+    p = ARR_DATA_PTR(v);
+    overall_length = 1; /* [TRH] don't forget to count \0 at end. */
+    values = (char **) palloc(nitems * sizeof (char *));
+    for (i = 0; i < nitems; i++) {
+        if (typbyval) {
+            switch(typlen) {
+           case 1:
+               values[i] = (*outputproc) (*p, typelem);
+               break;
+           case 2:
+               values[i] = (*outputproc) (* (int16 *) p, typelem);
+               break;
+           case 3:
+           case 4:
+               values[i] = (*outputproc) (* (int32 *) p, typelem);
+               break;
+            }
+            p += typlen;
+        } else {
+            values[i] = (*outputproc) (p, typelem);
+            if (typlen > 0)
+                p += typlen;
+            else
+                p += INTALIGN(* (int32 *) p);
+            /*
+            * For the pair of double quotes
+            */
+            overall_length += 2;
+        }
+        overall_length += (strlen(values[i]) + 1);
+    }
+    
+    /* 
+     * count total number of curly braces in output string 
+     */
+    for (i = j = 0, k = 1; i < ndim; k *= dim[i++], j += k);
+    
+    p = (char *) palloc(overall_length + 2*j);        
+    retval = p;
+    
+    strcpy(p, "{");
+    for (i = 0; i < ndim; indx[i++] = 0);
+    j = 0; k = 0;
+    do {
+        for (i = j; i < ndim - 1; i++)
+            strcat(p, "{");
+        /*
+        * Surround anything that is not passed by value in double quotes.
+        * See above for more details.
+        */
+        if (!typbyval) {
+           strcat(p, "\"");
+           strcat(p, values[k]);
+           strcat(p, "\"");
+        } else
+           strcat(p, values[k]);
+        pfree(values[k++]); 
+       
+        for (i = ndim - 1; i >= 0; i--) {
+            indx[i] = (indx[i] + 1)%dim[i];
+            if (indx[i]) {
+                strcat (p, delim);
+                break;
+            } else  
+                strcat (p, "}");
+        }
+        j = i;
+    } while (j  != -1);
+    
+    pfree(values);
+    return(retval);
+}
+
+/*-----------------------------------------------------------------------------
+ * array_dims :
+ *        returns the dimension of the array pointed to by "v"
+ *---------------------------------------------------------------------------- 
+ */
+char *
+array_dims(ArrayType *v, bool *isNull)
+{
+    char *p, *save_p;
+    int nbytes, i;
+    int *dimv, *lb;
+    
+    if (v == (ArrayType *) NULL) RETURN_NULL;
+    nbytes = ARR_NDIM(v)*33;    
+    /* 
+     * 33 since we assume 15 digits per number + ':' +'[]' 
+     */         
+    save_p = p =  (char *) palloc(nbytes + 4);
+    memset(save_p, 0, nbytes + 4);
+    dimv = ARR_DIMS(v); lb = ARR_LBOUND(v);
+    p += 4;
+    for (i = 0; i < ARR_NDIM(v); i++) {
+        sprintf(p, "[%d:%d]", lb[i], dimv[i]+lb[i]-1);
+        p += strlen(p);
+    }
+    nbytes = strlen(save_p + 4) + 4;
+    memmove(save_p, &nbytes,4); 
+    return (save_p);
+} 
+
+/*---------------------------------------------------------------------------
+ * array_ref :
+ *    This routing takes an array pointer and an index array and returns
+ *    a pointer to the referred element if element is passed by 
+ *    reference otherwise returns the value of the referred element.
+ *---------------------------------------------------------------------------
+ */
+Datum
+array_ref(ArrayType *array,
+         int n,
+         int indx[],
+         int reftype,
+         int elmlen,
+         int arraylen,
+         bool *isNull)
+{
+    int i, ndim, *dim, *lb, offset, nbytes;
+    struct varlena *v;
+    char *retval;
+    
+    if (array == (ArrayType *) NULL) RETURN_NULL;
+    if (arraylen > 0) {
+        /*
+         * fixed length arrays -- these are assumed to be 1-d
+         */
+        if (indx[0]*elmlen > arraylen) 
+            elog(WARN, "array_ref: array bound exceeded");
+        retval = (char *)array + indx[0]*elmlen;
+        return _ArrayCast(retval, reftype, elmlen);
+    }
+    dim = ARR_DIMS(array);
+    lb = ARR_LBOUND(array);
+    ndim = ARR_NDIM(array);
+    nbytes = (* (int32 *) array) - ARR_OVERHEAD(ndim);
+    
+    if (!SanityCheckInput(ndim, n,  dim, lb, indx))
+        RETURN_NULL;
+    
+    offset = GetOffset(n, dim, lb, indx);
+    
+    if (ARR_IS_LO(array)) {
+        char * lo_name;
+        int fd;
+       
+        /* We are assuming fixed element lengths here */
+        offset *= elmlen;
+        lo_name = (char *)ARR_DATA_PTR(array);
+#ifdef LOARRAY
+        if ((fd = LOopen(lo_name, ARR_IS_INV(array)?INV_READ:O_RDONLY)) < 0)
+            RETURN_NULL;
+#endif 
+        if (ARR_IS_CHUNKED(array))
+            v = _ReadChunkArray1El(indx, elmlen, fd, array, isNull);
+        else {
+            if (lo_lseek(fd, offset, SEEK_SET) < 0)
+                RETURN_NULL;
+#ifdef LOARRAY
+            v = (struct varlena *) LOread(fd, elmlen);
+#endif
+        }
+        if (*isNull) RETURN_NULL;
+        if (VARSIZE(v) - 4 < elmlen)
+            RETURN_NULL;
+        (void) lo_close(fd);
+        retval  = (char *)_ArrayCast((char *)VARDATA(v), reftype, elmlen);
+       if ( reftype == 0) { /* not by value */
+           char * tempdata = palloc (elmlen);
+           memmove(tempdata, retval, elmlen);
+           retval = tempdata;
+       }
+       pfree(v);
+        return (Datum) retval;
+    }
+    
+    if (elmlen >  0) {
+        offset = offset * elmlen;
+        /*  off the end of the array */
+        if (nbytes - offset < 1) RETURN_NULL;
+        retval = ARR_DATA_PTR (array) + offset;
+        return _ArrayCast(retval, reftype, elmlen);
+    } else {
+        bool done = false;
+        char *temp;
+        int bytes = nbytes;
+        temp = ARR_DATA_PTR (array);
+        i = 0;
+        while (bytes > 0 && !done) {
+            if (i == offset) {
+                retval = temp;
+                done = true;
+            }
+            bytes -= INTALIGN(* (int32 *) temp);
+            temp += INTALIGN(* (int32 *) temp);
+            i++;
+        }
+        if (! done) 
+            RETURN_NULL;
+        return (Datum) retval;
+    }
+}
+
+/*-----------------------------------------------------------------------------
+ * array_clip :
+ *        This routine takes an array and a range of indices (upperIndex and 
+ *         lowerIndx), creates a new array structure for the referred elements 
+ *         and returns a pointer to it.
+ *-----------------------------------------------------------------------------
+ */
+Datum
+array_clip(ArrayType *array,
+          int n,
+          int upperIndx[],
+          int lowerIndx[],
+          int reftype,
+          int len,
+          bool *isNull)
+{
+    int i, ndim, *dim, *lb, nbytes;
+    ArrayType *newArr; 
+    int bytes, span[MAXDIM];
+    
+    /* timer_start(); */
+    if (array == (ArrayType *) NULL) 
+        RETURN_NULL;
+    dim = ARR_DIMS(array);
+    lb = ARR_LBOUND(array);
+    ndim = ARR_NDIM(array);
+    nbytes = (* (int32 *) array) - ARR_OVERHEAD(ndim);
+    
+    if (!SanityCheckInput(ndim, n,  dim, lb, upperIndx))
+        RETURN_NULL;
+    
+    if (!SanityCheckInput(ndim, n, dim, lb, lowerIndx))
+        RETURN_NULL;
+    
+    for (i = 0; i < n; i++)
+        if (lowerIndx[i] > upperIndx[i])
+            elog(WARN, "lowerIndex cannot be larger than upperIndx"); 
+    mda_get_range(n, span, lowerIndx, upperIndx);
+    
+    if (ARR_IS_LO(array)) {
+        char * lo_name, * newname;
+        int fd, newfd, isDestLO = true, rsize;
+       
+        if (len < 0) 
+            elog(WARN, "array_clip: array of variable length objects not supported");  
+#ifdef LOARRAY
+        lo_name = (char *)ARR_DATA_PTR(array);
+        if ((fd = LOopen(lo_name, ARR_IS_INV(array)?INV_READ:O_RDONLY)) < 0)
+            RETURN_NULL;
+        newname = _array_newLO( &newfd, Unix );
+#endif
+        bytes = strlen(newname) + 1 + ARR_OVERHEAD(n);
+        newArr = (ArrayType *) palloc(bytes);
+        memmove(newArr, array, sizeof(ArrayType));
+        memmove(newArr, &bytes, sizeof(int));
+        memmove(ARR_DIMS(newArr), span, n*sizeof(int));
+        memmove(ARR_LBOUND(newArr), lowerIndx, n*sizeof(int));
+        strcpy(ARR_DATA_PTR(newArr), newname);
+       
+        rsize = compute_size (lowerIndx, upperIndx, n, len);
+        if (rsize < MAX_BUFF_SIZE) { 
+            char *buff;
+            rsize += 4;
+            buff = palloc(rsize);
+            if ( buff ) 
+                isDestLO = false;
+            if (ARR_IS_CHUNKED(array)) {
+               _ReadChunkArray(lowerIndx, upperIndx, len, fd, &(buff[4]), 
+                               array,0,isNull);
+            } else { 
+               _ReadArray(lowerIndx, upperIndx, len, fd, (int)&(buff[4]),
+                          array, 
+                          0,isNull);
+            }
+            memmove(buff, &rsize, 4);
+#ifdef LOARRAY
+            if (! *isNull)
+                bytes = LOwrite(newfd, (struct varlena *)buff);
+#endif
+            pfree (buff);
+        }
+        if (isDestLO)   
+            if (ARR_IS_CHUNKED(array)) {
+               _ReadChunkArray(lowerIndx, upperIndx, len, fd, (char*)newfd, array,
+                               1,isNull);
+            } else {
+               _ReadArray(lowerIndx, upperIndx, len, fd, newfd, array, 1,isNull);
+            }
+#ifdef LOARRAY
+        (void) LOclose(fd);
+        (void) LOclose(newfd);
+#endif
+        if (*isNull) { 
+            pfree(newArr); 
+            newArr = NULL;
+        }
+        /* timer_end(); */
+        return ((Datum) newArr);
+    }
+    
+    if (len >  0) {
+        bytes = getNitems(n, span);
+        bytes = bytes*len + ARR_OVERHEAD(n);
+    } else {
+        bytes = _ArrayClipCount(lowerIndx, upperIndx, array);
+        bytes += ARR_OVERHEAD(n);
+    }
+    newArr = (ArrayType *) palloc(bytes);
+    memmove(newArr, array, sizeof(ArrayType));
+    memmove(newArr, &bytes, sizeof(int));
+    memmove(ARR_DIMS(newArr), span, n*sizeof(int));
+    memmove(ARR_LBOUND(newArr), lowerIndx, n*sizeof(int));
+    _ArrayRange(lowerIndx, upperIndx, len, ARR_DATA_PTR(newArr), array, 1);
+    return (Datum) newArr;
+}
+
+/*-----------------------------------------------------------------------------
+ * array_set :
+ *        This routine sets the value of an array location (specified by an index array)
+ *        to a new value specified by "dataPtr".
+ * result :
+ *        returns a pointer to the modified array.
+ *-----------------------------------------------------------------------------
+ */
+char *
+array_set(ArrayType *array,
+         int n,
+         int indx[],
+         char *dataPtr,
+         int reftype,
+         int elmlen,
+         int arraylen,
+         bool *isNull)
+{
+    int ndim, *dim, *lb, offset, nbytes;
+    char *pos;
+    
+    if (array == (ArrayType *) NULL) 
+        RETURN_NULL;
+    if (arraylen > 0) {
+        /*
+         * fixed length arrays -- these are assumed to be 1-d
+         */
+        if (indx[0]*elmlen > arraylen) 
+            elog(WARN, "array_ref: array bound exceeded");
+        pos = (char *)array + indx[0]*elmlen;
+        ArrayCastAndSet(dataPtr, (bool) reftype, elmlen, pos);
+        return((char *)array);
+    }
+    dim = ARR_DIMS(array);
+    lb = ARR_LBOUND(array);
+    ndim = ARR_NDIM(array);
+    nbytes = (* (int32 *) array) - ARR_OVERHEAD(ndim);
+    
+    if (!SanityCheckInput(ndim, n,  dim, lb, indx)) 
+        return((char *)array);
+    offset = GetOffset( n, dim, lb, indx);
+    
+    if (ARR_IS_LO(array)) {
+        int fd;
+        char * lo_name;
+        struct varlena *v;
+       
+        /* We are assuming fixed element lengths here */
+        offset *= elmlen;
+#ifdef LOARRAY
+        lo_name = ARR_DATA_PTR(array);
+        if ((fd = LOopen(lo_name, ARR_IS_INV(array)?INV_WRITE:O_WRONLY)) < 0)
+            return((char *)array);
+#endif 
+        if (lo_lseek(fd, offset, SEEK_SET) < 0)
+            return((char *)array);
+        v = (struct varlena *) palloc(elmlen + 4);
+        VARSIZE (v) = elmlen + 4;
+        ArrayCastAndSet(dataPtr, (bool) reftype, elmlen, VARDATA(v));
+#ifdef LOARRAY
+        n =  LOwrite(fd, v);
+#endif
+        /* if (n < VARSIZE(v) - 4) 
+          RETURN_NULL;
+          */
+        pfree(v);
+        (void) lo_close(fd);
+        return((char *)array);
+    }
+    if (elmlen >  0) {
+        offset = offset * elmlen;
+        /*  off the end of the array */
+        if (nbytes - offset < 1) return((char *)array);
+        pos = ARR_DATA_PTR (array) + offset;
+    } else {
+        elog(WARN, "array_set: update of variable length fields not supported");
+    } 
+    ArrayCastAndSet(dataPtr, (bool) reftype, elmlen, pos);
+    return((char *)array);
+}
+
+/*----------------------------------------------------------------------------
+ * array_assgn :
+ *        This routine sets the value of a range of array locations (specified
+ *        by upper and lower index values ) to new values passed as 
+ *        another array
+ * result :
+ *        returns a pointer to the modified array.
+ *----------------------------------------------------------------------------
+ */
+char *
+array_assgn(ArrayType *array,
+           int n,
+           int upperIndx[],
+           int lowerIndx[],
+           ArrayType *newArr,
+           int reftype,
+           int len,
+           bool *isNull)
+{
+    int i, ndim, *dim, *lb;
+    
+    if (array == (ArrayType *) NULL) 
+        RETURN_NULL;
+    if (len < 0) 
+        elog(WARN,"array_assgn:updates on arrays of variable length elements not allowed");
+    
+    dim = ARR_DIMS(array);
+    lb = ARR_LBOUND(array);
+    ndim = ARR_NDIM(array);
+    
+    if (!SanityCheckInput(ndim, n,  dim, lb, upperIndx) || 
+       !SanityCheckInput(ndim, n, dim, lb, lowerIndx)) {
+        return((char *)array);
+    }
+    
+    for (i = 0; i < n; i++)
+        if (lowerIndx[i] > upperIndx[i])
+            elog(WARN, "lowerIndex larger than upperIndx"); 
+    
+    if (ARR_IS_LO(array)) {
+        char * lo_name;
+        int fd, newfd;
+       
+#ifdef LOARRAY
+        lo_name = (char *)ARR_DATA_PTR(array);
+        if ((fd = LOopen(lo_name,  ARR_IS_INV(array)?INV_WRITE:O_WRONLY)) < 0)
+            return((char *)array);
+#endif
+        if (ARR_IS_LO(newArr)) {
+#ifdef LOARRAY
+            lo_name = (char *)ARR_DATA_PTR(newArr);
+            if ((newfd = LOopen(lo_name, ARR_IS_INV(newArr)?INV_READ:O_RDONLY)) < 0)
+                return((char *)array);
+#endif
+            _LOArrayRange(lowerIndx, upperIndx, len, fd, newfd, array, 1, isNull);
+            (void) lo_close(newfd);
+        } else {
+            _LOArrayRange(lowerIndx, upperIndx, len, fd, (int)ARR_DATA_PTR(newArr), 
+                         array, 0, isNull);
+        }
+        (void) lo_close(fd);
+        return ((char *) array);
+    }
+    _ArrayRange(lowerIndx, upperIndx, len, ARR_DATA_PTR(newArr), array, 0);
+    return (char *) array;
+}
+
+/*-----------------------------------------------------------------------------
+ * array_eq :
+ *        compares two arrays for equality    
+ * result :
+ *        returns 1 if the arrays are equal, 0 otherwise.
+ *-----------------------------------------------------------------------------
+ */
+int
+array_eq (ArrayType *array1, ArrayType *array2)
+{
+    if ((array1 == NULL) || (array2 == NULL))
+       return(0);
+    if (*(int *)array1 != *(int *)array2)
+       return (0);
+    if (memcmp(array1, array2, *(int *)array1))
+       return(0);
+    return(1);
+}
+
+/***************************************************************************/
+/******************|          Support  Routines           |*****************/
+/***************************************************************************/
+static void
+system_cache_lookup(Oid element_type,
+                   bool input,
+                   int *typlen,
+                   bool *typbyval,
+                   char *typdelim,
+                   Oid *typelem,
+                   Oid *proc,
+                   char *typalign)
+{
+    HeapTuple typeTuple;
+    TypeTupleForm typeStruct;
+    
+    typeTuple = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(element_type),
+                                   0,0,0);
+    
+    if (!HeapTupleIsValid(typeTuple)) {
+        elog(WARN, "array_out: Cache lookup failed for type %d\n",
+             element_type);
+        return;
+    }
+    typeStruct = (TypeTupleForm) GETSTRUCT(typeTuple);
+    *typlen    = typeStruct->typlen;
+    *typbyval  = typeStruct->typbyval;
+    *typdelim  = typeStruct->typdelim;
+    *typelem   = typeStruct->typelem;
+    *typalign  = typeStruct->typalign;
+    if (input) {
+        *proc = typeStruct->typinput;
+    } else {
+        *proc = typeStruct->typoutput;
+    }
+}
+
+static Datum
+_ArrayCast(char *value, bool byval, int len)
+{
+    if (byval) {
+        switch (len) {
+       case 1:
+           return((Datum) * value);
+       case 2:
+           return((Datum) * (int16 *) value);
+       case 3:
+       case 4:
+           return((Datum) * (int32 *) value);
+       default:
+           elog(WARN, "array_ref: byval and elt len > 4!");
+           break;
+        }
+    } else {
+        return (Datum) value;
+    }
+ return 0;
+}
+
+
+static int
+ArrayCastAndSet(char *src,
+               bool typbyval,
+               int typlen,
+               char *dest)
+{
+    int inc;
+    
+    if (typlen > 0) {
+       if (typbyval) {
+            switch(typlen) {
+           case 1: 
+               *dest = DatumGetChar(src);
+               break;
+           case 2: 
+               * (int16 *) dest = DatumGetInt16(src);
+               break;
+           case 4:
+               * (int32 *) dest = (int32)src;
+               break;
+            }
+        } else {
+            memmove(dest, src, typlen);
+        }
+        inc = typlen;
+    } else {
+        memmove(dest, src, *(int32 *)src);
+        inc = (INTALIGN(* (int32 *) src));
+    }
+    return(inc);
+} 
+
+static char *
+_AdvanceBy1word(char *str, char **word)
+{
+    char *retstr, *space;
+    
+    *word = NULL;
+    if (str == NULL) return str;
+    while (isspace(*str)) str++;
+    *word = str;
+    if ((space = (char *)strchr(str, ' ')) != (char *) NULL) {
+        retstr = space + 1;
+        *space = '\0';
+    }
+    else 
+        retstr = NULL;
+    return retstr;
+}
+
+int
+SanityCheckInput(int ndim, int n, int dim[], int lb[], int indx[])
+{
+    int i;
+    /* Do Sanity check on input */
+    if (n != ndim) return 0;
+    for (i = 0; i < ndim; i++)
+        if ((lb[i] > indx[i]) || (indx[i] >= (dim[i] + lb[i])))
+            return 0;
+    return 1;
+}
+
+static void
+_ArrayRange(int st[],
+           int endp[],
+           int bsize,
+           char *destPtr,
+           ArrayType *array,
+           int from)
+{
+    int n, *dim, *lb, st_pos, prod[MAXDIM];
+    int span[MAXDIM], dist[MAXDIM], indx[MAXDIM];
+    int i, j, inc;
+    char *srcPtr, *array_seek();
+    
+    n = ARR_NDIM(array); dim = ARR_DIMS(array);
+    lb = ARR_LBOUND(array); srcPtr = ARR_DATA_PTR(array);
+    for (i = 0; i < n; st[i] -= lb[i], endp[i] -= lb[i], i++); 
+    mda_get_prod(n, dim, prod);
+    st_pos = tuple2linear(n, st, prod);
+    srcPtr = array_seek(srcPtr, bsize, st_pos);
+    mda_get_range(n, span, st, endp);
+    mda_get_offset_values(n, dist, prod, span);
+    for (i=0; i < n; indx[i++]=0);
+    i = j = n-1; inc = bsize;
+    do {
+        srcPtr = array_seek(srcPtr, bsize,  dist[j]); 
+        if (from) 
+            inc = array_read(destPtr, bsize, 1, srcPtr);
+        else 
+            inc = array_read(srcPtr, bsize, 1, destPtr);
+        destPtr += inc; srcPtr += inc;
+    } while ((j = next_tuple(i+1, indx, span)) != -1);
+}
+
+static int
+_ArrayClipCount(int stI[], int endpI[], ArrayType *array)
+{
+    int n, *dim, *lb, st_pos, prod[MAXDIM];
+    int span[MAXDIM], dist[MAXDIM], indx[MAXDIM];
+    int i, j, inc, st[MAXDIM], endp[MAXDIM];
+    int count = 0;
+    char *ptr, *array_seek();
+    
+    n = ARR_NDIM(array); dim = ARR_DIMS(array);
+    lb = ARR_LBOUND(array); ptr = ARR_DATA_PTR(array);
+    for (i = 0; i < n; st[i] = stI[i]-lb[i], endp[i]=endpI[i]-lb[i], i++);
+    mda_get_prod(n, dim, prod);
+    st_pos = tuple2linear(n, st, prod);
+    ptr = array_seek(ptr, -1, st_pos);
+    mda_get_range(n, span, st, endp);
+    mda_get_offset_values(n, dist, prod, span);
+    for (i=0; i < n; indx[i++]=0);
+    i = j = n-1;
+    do {
+        ptr = array_seek(ptr, -1,  dist[j]);
+        inc =  INTALIGN(* (int32 *) ptr);
+        ptr += inc; count += inc;
+    } while ((j = next_tuple(i+1, indx, span)) != -1);
+    return count;
+}
+
+char *
+array_seek(char *ptr, int eltsize, int nitems)
+{
+    int i;
+    
+    if (eltsize > 0) 
+        return(ptr + eltsize*nitems);
+    for (i = 0; i < nitems; i++) 
+       ptr += INTALIGN(* (int32 *) ptr);
+    return(ptr);
+}
+
+int
+array_read(char *destptr, int eltsize, int nitems, char *srcptr)
+{
+    int i, inc, tmp;
+    
+    if (eltsize > 0)  {
+        memmove(destptr, srcptr, eltsize*nitems);
+        return(eltsize*nitems);
+    }
+    for (i = inc = 0; i < nitems; i++) {
+       tmp = (INTALIGN(* (int32 *) srcptr));
+       memmove(destptr, srcptr, tmp);
+       srcptr += tmp;
+       destptr += tmp;
+       inc += tmp;
+    }
+    return(inc);
+}
+
+static void
+_LOArrayRange(int st[],
+             int endp[],
+             int bsize,
+             int srcfd,
+             int destfd,
+             ArrayType *array,
+             int isSrcLO,
+             bool *isNull)
+{
+    int n, *dim, st_pos, prod[MAXDIM];
+    int span[MAXDIM], dist[MAXDIM], indx[MAXDIM];
+    int i, j, inc, tmp, *lb, offset;
+    
+    n = ARR_NDIM(array); dim = ARR_DIMS(array);
+    lb = ARR_LBOUND(array);
+    for (i = 0; i < n; st[i] -= lb[i], endp[i] -= lb[i], i++);
+    
+    mda_get_prod(n, dim, prod);
+    st_pos = tuple2linear(n, st, prod);
+    offset = st_pos*bsize;
+    if (lo_lseek(srcfd, offset, SEEK_SET) < 0) 
+        return;
+    mda_get_range(n, span, st, endp);
+    mda_get_offset_values(n, dist, prod, span);
+    for (i=0; i < n; indx[i++]=0);
+    for (i = n-1, inc = bsize; i >= 0; inc *= span[i--])
+        if (dist[i]) 
+            break;
+    j = n-1;
+    do {
+        offset += (dist[j]*bsize);
+        if (lo_lseek(srcfd,  offset, SEEK_SET) < 0) 
+            return;
+        tmp = _LOtransfer((char**)&srcfd, inc, 1, (char**)&destfd, isSrcLO, 1);
+        if ( tmp < inc )
+           return;
+        offset += inc;
+    } while ((j = next_tuple(i+1, indx, span)) != -1);
+}
+
+
+static void
+_ReadArray (int st[],
+           int endp[],
+           int bsize,
+           int srcfd,
+           int destfd,
+           ArrayType *array,
+           int isDestLO,
+           bool *isNull)
+{
+    int n, *dim, st_pos, prod[MAXDIM];
+    int span[MAXDIM], dist[MAXDIM], indx[MAXDIM];
+    int i, j, inc, tmp, *lb, offset;
+    
+    n = ARR_NDIM(array); dim = ARR_DIMS(array);
+    lb = ARR_LBOUND(array);
+    for (i = 0; i < n; st[i] -= lb[i], endp[i] -= lb[i], i++);
+    
+    mda_get_prod(n, dim, prod);
+    st_pos = tuple2linear(n, st, prod);
+    offset = st_pos*bsize;
+    if (lo_lseek(srcfd, offset, SEEK_SET) < 0)
+       return;
+    mda_get_range(n, span, st, endp);
+    mda_get_offset_values(n, dist, prod, span);
+    for (i=0; i < n; indx[i++]=0);
+    for (i = n-1, inc = bsize; i >= 0; inc *= span[i--])
+        if (dist[i]) 
+            break;
+    j = n-1;
+    do {
+        offset += (dist[j]*bsize);
+        if (lo_lseek(srcfd,  offset, SEEK_SET) < 0) 
+            return;
+        tmp = _LOtransfer((char**)&destfd, inc, 1, (char**)&srcfd, 1, isDestLO);
+        if ( tmp < inc ) 
+           return;
+        offset += inc;
+    } while ((j = next_tuple(i+1, indx, span)) != -1);
+}
+
+
+int
+_LOtransfer(char **destfd,
+           int size,
+           int nitems,
+           char **srcfd,
+           int isSrcLO,
+           int isDestLO)
+{
+#define MAX_READ (512 * 1024)
+#define min(a, b) (a < b ? a : b)
+    struct varlena *v;
+    int tmp, inc, resid;
+    
+    inc = nitems*size; 
+    if (isSrcLO && isDestLO && inc > 0)
+       for (tmp = 0, resid = inc;
+            resid > 0 && (inc = min(resid, MAX_READ)) > 0; resid -= inc) { 
+#ifdef LOARRAY
+           v = (struct varlena *) LOread((int) *srcfd, inc);
+           if (VARSIZE(v) - 4 < inc) 
+               {pfree(v); return(-1);}
+           tmp += LOwrite((int) *destfd, v);    
+#endif
+           pfree(v);
+           
+       } 
+    else if (!isSrcLO && isDestLO) {
+        tmp = lo_write((int) *destfd, *srcfd, inc);
+        *srcfd = *srcfd + tmp;
+    } 
+    else if (isSrcLO && !isDestLO) {
+        tmp = lo_read((int) *srcfd, *destfd, inc);
+        *destfd = *destfd + tmp;
+    } 
+    else {
+        memmove(*destfd, *srcfd, inc);
+        tmp = inc;
+        *srcfd += inc;
+        *destfd += inc;
+    }
+    return(tmp);
+#undef MAX_READ
+}
+
+char *
+_array_newLO(int *fd, int flag)
+{
+    char *p;
+    char saveName[NAME_LEN];
+    
+    p = (char *) palloc(NAME_LEN);
+    sprintf(p, "/Arry.%d", newoid());
+    strcpy (saveName, p);
+#ifdef LOARRAY
+    if ( (*fd = LOcreat (saveName, 0600, flag)) < 0)
+       elog(WARN, "Large object create failed");
+#endif
+    return (p);
+}
+
diff --git a/src/backend/utils/adt/arrayutils.c b/src/backend/utils/adt/arrayutils.c
new file mode 100644 (file)
index 0000000..ca7e19b
--- /dev/null
@@ -0,0 +1,111 @@
+/*-------------------------------------------------------------------------
+ *
+ * arrayutils.c--
+ *    This file contains some support routines required for array functions.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#define WEAK_C_OPTIMIZER
+
+#include "c.h"
+
+int
+GetOffset(int n, int dim[], int lb[], int indx[])
+{                                              
+    int i, scale, offset;                                     
+    for (i = n-1, scale = 1, offset = 0; i >= 0; scale*=dim[i--]) 
+        offset += (indx[i] - lb[i])*scale;
+    return offset ;
+}
+
+int
+getNitems(int n, int a[])
+{    
+    int i, ret;      
+    for (i = 0, ret = 1; i < n; ret *= a[i++]);   
+    if (n == 0) ret = 0;     
+    return ret;
+}
+
+int
+compute_size(int st[], int endp[], int n, int base)
+{
+    int i, ret;
+    for (i = 0, ret = base; i < n; i++)
+        ret *= (endp[i] - st[i] + 1);
+    return ret;
+}
+
+void
+mda_get_offset_values(int n, int dist[], int PC[], int span[])
+{                                                                     
+    int i, j;                                                         
+    for (j = n-2, dist[n-1]=0; j  >= 0; j--)                         
+       for (i = j+1, dist[j] = PC[j]-1; i < n;                             
+            dist[j] -= (span[i] - 1)*PC[i], i++);                         
+}                                                                 
+
+void
+mda_get_range(int n, int span[], int st[], int endp[])
+{                                                                     
+    int i;                                                         
+    for (i= 0; i < n; i++)                             
+        span[i] = endp[i] - st[i] + 1;                 
+} 
+
+void
+mda_get_prod(int n, int range[], int P[])
+{                                                                     
+    int i;                                                         
+    for (i= n-2, P[n-1] = 1; i >= 0; i--)             
+        P[i] = P[i+1] * range[i + 1];                 
+} 
+
+int
+tuple2linear(int n, int tup[], int scale[])
+{
+    int i, lin;
+    for (i= lin = 0; i < n; i++)
+        lin += tup[i]*scale[i];
+    return lin;
+} 
+
+void
+array2chunk_coord(int n, int C[], int a_coord[], int c_coord[])
+{
+    int i;
+    for (i= 0; i < n; i++)
+       c_coord[i] = a_coord[i]/C[i];
+}
+
+/*-----------------------------------------------------------------------------
+  generates the tuple that is lexicographically one greater than the current
+  n-tuple in "curr", with the restriction that the i-th element of "curr" is
+  less than the i-th element of "span".
+  RETURNS   0   if no next tuple exists
+  1   otherwise
+  -----------------------------------------------------------------------------*/
+int
+next_tuple(int n, int curr[], int span[])
+{
+    int i;
+    
+    if (!n) return(-1);
+    curr[n-1] = (curr[n-1]+1)%span[n-1];
+    for (i = n-1; i*(!curr[i]); i--)
+        curr[i-1] = (curr[i-1]+1)%span[i-1];
+    
+    if (i) 
+        return(i);
+    if (curr[0]) 
+        return(0);
+    return(-1);
+}
+
diff --git a/src/backend/utils/adt/bool.c b/src/backend/utils/adt/bool.c
new file mode 100644 (file)
index 0000000..129f59f
--- /dev/null
@@ -0,0 +1,65 @@
+/*-------------------------------------------------------------------------
+ *
+ * bool.c--
+ *    Functions for the built-in type "bool".
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "utils/builtins.h"    /* where the declarations go */
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+/***************************************************************************** 
+ *   USER I/O ROUTINES                                                       *
+ *****************************************************************************/
+
+/*
+ *     boolin          - converts "t" or "f" to 1 or 0
+ */
+int32
+boolin(char *b)
+{
+    if (b == NULL)
+       elog(WARN, "Bad input string for type bool");
+    return((int32) (*b == 't') || (*b == 'T'));
+}
+
+/*
+ *     boolout         - converts 1 or 0 to "t" or "f"
+ */
+char *
+boolout(long b)
+{
+    char       *result = (char *) palloc(2);
+    
+    *result = (b) ? 't' : 'f';
+    result[1] = '\0';
+    return(result);
+}
+
+/***************************************************************************** 
+ *   PUBLIC ROUTINES                                                         *
+ *****************************************************************************/
+
+int32
+booleq(int8 arg1, int8 arg2)   
+{ 
+    return(arg1 == arg2); 
+}
+
+int32
+boolne(int8 arg1, int8 arg2)   
+{
+    return(arg1 != arg2); 
+}
+
+
+
+
+
diff --git a/src/backend/utils/adt/char.c b/src/backend/utils/adt/char.c
new file mode 100644 (file)
index 0000000..69ca708
--- /dev/null
@@ -0,0 +1,392 @@
+/*-------------------------------------------------------------------------
+ *
+ * char.c--
+ *    Functions for the built-in type "char".
+ *    Functions for the built-in type "cid".
+ *    Functions for the built-in type "char2".
+ *    Functions for the built-in type "char4".
+ *    Functions for the built-in type "char8".
+ *    Functions for the built-in type "char16".
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>             /* for sprintf() */
+#include <string.h>
+#include "postgres.h"
+#include "utils/palloc.h"
+#include "utils/builtins.h"            /* where the declarations go */
+
+/***************************************************************************** 
+ *   USER I/O ROUTINES                                                       *
+ *****************************************************************************/
+
+/*
+ *     charin          - converts "x" to 'x'
+ */
+int32 charin(char *ch)
+{
+    if (ch == NULL)
+       return((int32) NULL);
+    return((int32) *ch);
+}
+
+/*
+ *     charout         - converts 'x' to "x"
+ */
+char *charout(int32 ch)
+{
+    char       *result = (char *) palloc(2);
+    
+    result[0] = (char) ch;
+    result[1] = '\0';
+    return(result);
+}
+
+/*
+ *     cidin   - converts "..." to internal representation.
+ *
+ *     NOTE: we must not use 'charin' because cid might be a non
+ *     printable character...
+ */
+int32 cidin(char *s)
+{
+    CommandId c;
+    
+    if (s==NULL)
+       c = 0;
+    else
+       c = atoi(s);
+    
+    return((int32)c);
+}
+
+/*
+ *     cidout  - converts a cid to "..."
+ *
+ *     NOTE: we must no use 'charout' because cid might be a non
+ *     printable character...
+ */
+char *cidout(int32 c)
+{
+    char *result;
+    CommandId c2;
+    
+    /*
+     * cid is a number between 0 .. 2^16-1, therefore we need at most
+     * 6 chars for the string (5 digits + '\0')
+     * NOTE: print it as an UNSIGNED int!
+     */
+    result = palloc(6);
+    c2 = (CommandId)c;
+    sprintf(result, "%u", (unsigned)(c2));
+    return(result);
+}
+
+/*
+ *     char16in        - converts "..." to internal reprsentation
+ *
+ *     Note:
+ *             Currently if strlen(s) < 14, the extra chars are nulls
+ */
+char *char16in(char *s)
+{
+    char       *result;
+
+    if (s == NULL)
+       return(NULL);
+    result = (char *) palloc(16);
+    memset(result, 0, 16);
+    (void) strncpy(result, s, 16);
+    return(result);
+}
+
+/*
+ *     char16out       - converts internal reprsentation to "..."
+ */
+char *char16out(char *s)
+{
+    char       *result = (char *) palloc(17);
+    
+    memset(result, 0, 17);
+    if (s == NULL) {
+       result[0] = '-';
+    } else {
+       strncpy(result, s, 16);
+    }
+    return(result);
+}
+
+
+/***************************************************************************** 
+ *   PUBLIC ROUTINES                                                         *
+ *****************************************************************************/
+
+int32 chareq(int8 arg1, int8 arg2)     { return(arg1 == arg2); }
+int32 charne(int8 arg1, int8 arg2)     { return(arg1 != arg2); }
+int32 charlt(int8 arg1, int8 arg2)     { return(arg1 < arg2); }
+int32 charle(int8 arg1, int8 arg2)     { return(arg1 <= arg2); }
+int32 chargt(int8 arg1, int8 arg2)     { return(arg1 > arg2); }
+int32 charge(int8 arg1, int8 arg2)     { return(arg1 >= arg2); }
+int8 charpl(int8 arg1, int8 arg2)       { return(arg1 + arg2); }
+int8 charmi(int8 arg1, int8 arg2)      { return(arg1 - arg2); }
+int8 charmul(int8 arg1, int8 arg2)     { return(arg1 * arg2); }
+int8 chardiv(int8 arg1, int8 arg2)     { return(arg1 / arg2); }
+
+int32 cideq(int8 arg1, int8 arg2)      { return(arg1 == arg2); }
+
+/*
+ *     char16eq        - returns 1 iff arguments are equal
+ *     char16ne        - returns 1 iff arguments are not equal
+ *
+ *     BUGS:
+ *             Assumes that "xy\0\0a" should be equal to "xy\0b".
+ *             If not, can do the comparison backwards for efficiency.
+ *
+ *     char16lt        - returns 1 iff a < b
+ *     char16le        - returns 1 iff a <= b
+ *     char16gt        - returns 1 iff a < b
+ *     char16ge        - returns 1 iff a <= b
+ *
+ */
+int32 char16eq(char *arg1, char *arg2)
+{
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    return(strncmp(arg1, arg2, 16) == 0);
+}
+
+int32 char16ne(char *arg1, char *arg2)
+{
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    return(strncmp(arg1, arg2, 16) != 0);
+}
+
+int32 char16lt(char *arg1, char *arg2)
+{
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    return((int32) (strncmp(arg1, arg2, 16) < 0));
+}
+
+int32 char16le(char *arg1, char *arg2)
+{
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    return((int32) (strncmp(arg1, arg2, 16) <= 0));
+}
+
+int32 char16gt(char *arg1, char *arg2)
+{
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    
+    return((int32) (strncmp(arg1, arg2, 16) > 0));
+}
+
+int32 char16ge(char *arg1, char *arg2)
+{
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    
+    return((int32) (strncmp(arg1, arg2, 16) >= 0));
+}
+
+
+/* ============================== char2 ============================== */
+uint16 char2in(char *s)
+{
+    uint16     res;
+    
+    if (s == NULL)
+       return(0);
+    
+    memset((char *) &res, 0, sizeof(res));
+    (void) strncpy((char *) &res, s, 2);
+    return(res);
+}
+
+char *char2out(uint16 s)
+{
+    char       *result = (char *) palloc(3);
+    
+    memset(result, 0, 3);
+    (void) strncpy(result, (char *) &s, 2);
+    
+    return(result);
+}
+
+int32 char2eq(uint16 a, uint16 b)
+{
+    return(strncmp((char *) &a, (char *) &b, 2) == 0);
+}
+
+int32 char2ne(uint16 a, uint16 b)
+{
+    return(strncmp((char *) &a, (char *) &b, 2) != 0);
+}
+
+int32 char2lt(uint16 a, uint16 b)
+{
+    return(strncmp((char *) &a, (char *) &b, 2) < 0);
+}
+
+int32 char2le(uint16 a, uint16 b)
+{
+    return(strncmp((char *) &a, (char *) &b, 2) <= 0);
+}
+
+int32 char2gt(uint16 a, uint16 b)
+{
+    return(strncmp((char *) &a, (char *) &b, 2) > 0);
+}
+
+int32 char2ge(uint16 a, uint16 b)
+{
+    return(strncmp((char *) &a, (char *) &b, 2) >= 0);
+}
+
+int32 char2cmp(uint16 a, uint16 b)
+{
+    return (strncmp((char *) &a, (char *) &b, 2));
+}
+
+/* ============================== char4 ============================== */
+uint32 char4in(char *s)
+{
+    uint32     res;
+    
+    if (s == NULL)
+       return(0);
+    
+    memset((char *) &res, 0, sizeof(res));
+    (void) strncpy((char *) &res, s, 4);
+    
+    return(res);
+}
+
+char *char4out(s)
+     uint32 s;
+{
+    char       *result = (char *) palloc(5);
+    
+    memset(result, 0, 5);
+    (void) strncpy(result, (char *) &s, 4);
+    
+    return(result);
+}
+
+int32 char4eq(uint32 a, uint32 b)
+{
+    return(strncmp((char *) &a, (char *) &b, 4) == 0);
+}
+
+int32 char4ne(uint32 a, uint32 b)
+{
+    return(strncmp((char *) &a, (char *) &b, 4) != 0);
+}
+
+int32 char4lt(uint32 a, uint32 b)
+{
+    return(strncmp((char *) &a, (char *) &b, 4) < 0);
+}
+
+int32 char4le(uint32 a, uint32 b)
+{
+    return(strncmp((char *) &a, (char *) &b, 4) <= 0);
+}
+
+int32 char4gt(uint32 a, uint32 b)
+{
+    return(strncmp((char *) &a, (char *) &b, 4) > 0);
+}
+
+int32 char4ge(uint32 a, uint32 b)
+{
+    return(strncmp((char *) &a, (char *) &b, 4) >= 0);
+}
+
+int32 char4cmp(uint32 a, uint32 b)
+{
+    return(strncmp((char *) &a, (char *) &b, 4));
+}
+
+/* ============================== char8 ============================== */
+char *char8in(char *s)
+{
+    char       *result;
+    
+    if (s == NULL)
+       return((char *) NULL);
+    
+    result = (char *) palloc(8);
+    memset(result, 0, 8);
+    (void) strncpy(result, s, 8);
+    return(result);
+}
+
+char *char8out(char *s)
+{
+    char       *result = (char *) palloc(9);
+    
+    memset(result, 0, 9);
+    if (s == NULL) {
+       result[0] = '-';
+    } else {
+       strncpy(result, s, 8);
+    }
+    return(result);
+}
+
+int32 char8eq(char *arg1, char *arg2)
+{
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    return(strncmp(arg1, arg2, 8) == 0);
+}
+
+int32 char8ne(char *arg1, char *arg2)
+{
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    return(strncmp(arg1, arg2, 8) != 0);
+}
+
+int32 char8lt(char *arg1, char *arg2)
+{
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    return(strncmp(arg1, arg2, 8) < 0);
+}
+
+int32 char8le(char *arg1, char *arg2)
+{
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    return(strncmp(arg1, arg2, 8) <= 0);
+}
+
+int32 char8gt(char *arg1, char *arg2)
+{
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    return(strncmp(arg1, arg2, 8) > 0);
+}
+
+int32 char8ge(char *arg1, char *arg2)
+{
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    return(strncmp(arg1, arg2, 8) >= 0);
+}
+
+int32 char8cmp(char *arg1, char *arg2)
+{
+    return(strncmp(arg1, arg2, 8));
+}
diff --git a/src/backend/utils/adt/chunk.c b/src/backend/utils/adt/chunk.c
new file mode 100644 (file)
index 0000000..1cd3504
--- /dev/null
@@ -0,0 +1,587 @@
+/*-------------------------------------------------------------------------
+ *
+ * chunk.c--
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <ctype.h>
+#include "postgres.h"
+#include "utils/memutils.h"
+#include "libpq/libpq-fs.h"
+
+#include "storage/fd.h"                /* for SEEK_ */
+
+#include "catalog/pg_type.h"
+
+#include "utils/palloc.h"
+#include "fmgr.h"
+#include "utils/elog.h"
+#include "utils/array.h"
+
+#include "optimizer/internal.h"
+
+#define INFTY 500000000
+#define MANY  10000
+#define MAXPAT 20
+#define quot_ceil(x,y)  (((x)+(y)-1)/(y))
+#define min(x,y)        (((x) < (y))? (x) : (y))
+#define max(x,y)        (((x) > (y))? (x) : (y))
+
+static CHUNK_INFO cInfo;
+
+/* non-export function prototypes */
+static int _FindBestChunk(int size, int dmax[], int dbest[], int dim,
+                         int A[MAXPAT][MAXDIM+1], int N);
+static int get_next(int d[], int k, int C, int dmax[]);
+static void initialize_info(CHUNK_INFO *A, int ndim, int dim[], int chunk[]);
+static void _ConvertToChunkFile(int n, int baseSize, int dim[],        int C[],
+                               int srcfd, int destfd);
+static void read_chunk(int chunk_no[], int C[], char a_chunk[], int srcfd,
+                      int n, int baseSize, int PX[], int dist[]);
+static int write_chunk(struct varlena * a_chunk, int ofile);
+static int seek_and_read(int pos, int size, char buff[], int fp, int from);
+
+/*------------------------------------------------------------------------
+ * _ChunkArray ---
+ *     converts an input array to chunked format using the information 
+ *     provided by the access pattern.
+ * Results:
+ *     creates a new file that stores the chunked array and returns 
+ *     information about the chunked file
+ *-----------------------------------------------------------------------
+ */
+char *
+_ChunkArray(int fd,
+           FILE *afd,
+           int ndim,
+           int dim[],
+           int baseSize,
+           int *nbytes,
+           char *chunkfile)
+{
+    int cfd;
+    int chunk[MAXDIM], csize;
+    bool reorgFlag;
+    
+    if (chunkfile == NULL) 
+       reorgFlag = true;
+    else
+       reorgFlag = false;
+    
+#ifdef LOARRAY
+    if (reorgFlag) 
+       /* create new LO for chunked file */
+       chunkfile = _array_newLO( &cfd, fileFlag );
+    else 
+       cfd = LOopen(chunkfile, O_RDONLY); 
+#endif
+    if (cfd < 0)
+       elog(WARN, "Enable to open chunk file");
+    strcpy (cInfo.lo_name, chunkfile);
+    
+    /* find chunk size */
+    csize = GetChunkSize(afd, ndim, dim, baseSize, chunk);
+    
+    if (reorgFlag)
+       /* copy data from input file to chunked file */
+       _ConvertToChunkFile(ndim, baseSize, dim, chunk, fd, cfd);
+    
+    initialize_info(&cInfo, ndim, dim, chunk);
+    *nbytes = sizeof(CHUNK_INFO);
+    return (char *) &cInfo ;
+}
+
+/*--------------------------------------------------------------------------
+ * GetChunkSize --
+ *        given an access pattern and array dimensionality etc, this program
+ *      returns the dimensions of the chunk in "d"
+ *-----------------------------------------------------------------------
+ */
+int
+GetChunkSize(FILE *fd, 
+            int ndim,
+            int dim[MAXDIM], 
+            int baseSize, 
+            int d[MAXDIM])
+{
+    int N, i, j, csize;
+    int A[MAXPAT][MAXDIM+1], dmax[MAXDIM];
+    
+    /* 
+     * ----------- read input ------------ 
+     */
+    fscanf(fd, "%d", &N);
+    if ( N > MAXPAT )
+        elog(WARN, "array_in: too many access pattern elements");
+    for (i = 0; i < N; i++)
+        for (j = 0; j < ndim+1; j++) 
+            if (fscanf(fd, "%d ", &(A[i][j])) == EOF)
+                elog (WARN, "array_in: bad access pattern input");
+    
+    /* 
+     * estimate chunk size 
+     */
+    for (i = 0; i < ndim; i++)
+        for (j = 0, dmax[i] = 1; j < N; j++)
+           if (dmax[i] < A[j][i])
+               dmax[i] = A[j][i];
+    csize = _PAGE_SIZE_/baseSize;
+    
+    _FindBestChunk (csize, dmax, d, ndim, A, N);
+    
+    return csize;    
+}
+
+/*-------------------------------------------------------------------------
+ * _FindBestChunk --
+ *        This routine does most of the number crunching to compute the 
+ *        optimal chunk shape.
+ * Called by GetChunkSize
+ *------------------------------------------------------------------------
+ */
+static int
+_FindBestChunk(int size,
+              int dmax[],
+              int dbest[],
+              int dim,
+              int A[MAXPAT][MAXDIM+1],
+              int N)
+{
+    int d[MAXDIM];
+    int tc, mintc = INFTY;
+    
+    d[0] = 0;
+    mintc = INFTY;
+    while (get_next(d,dim,size, dmax)) {
+       /*
+        * compute the number of page fetches for a given
+        * chunk size (d[]) and access pattern (A[][])
+        */
+       register int i,j, nc;
+       for (i = 0, tc = 0; i < N; i++){
+           for (j = 0, nc = 1; j < dim; j++)
+               nc *= quot_ceil(A[i][j], d[j]);
+           nc *= A[i][dim];
+           tc += nc;
+       }
+       /*
+        * tc holds the total number of page fetches
+        */
+        if (mintc >= tc) {
+           mintc = tc;
+           for (j = 0; j < dim; dbest[j] = d[j], j++)
+               ;
+        }
+    }
+    return(mintc);
+}
+
+/*----------------------------------------------------------------------
+ * get_next --
+ *   Called by _GetBestChunk to get the next tuple in the lexicographic order
+ *---------------------------------------------------------------------
+ */
+static int
+get_next(int d[], int k, int C, int dmax[])
+{
+    register int i,j, temp;
+    
+    if (!d[0]) {
+        temp = C;
+        for (j = k-1; j >= 0; j--){
+            d[j] = min(temp, dmax[j]);
+            temp = max(1, temp/d[j]);
+        }
+        return(1);
+    }
+    
+    for (j = 0, temp = 1; j < k; j++)
+        temp *= d[j];
+    
+    for (i=k-1; i >= 0; i--){
+        temp = temp/d[i];
+        if (((temp*(d[i]+1)) < C) && (d[i]+1 <= dmax[i]))
+           break;
+    }
+    if (i < 0)
+       return(0);
+    
+    d[i]++;
+    j = C/temp;
+    d[i] = min(dmax[i], j/(j/d[i]));
+    temp = temp*d[i];
+    temp = C/temp;
+    
+    for (j = k-1; j > i; j--){
+       d[j] = min(temp, dmax[j]);
+       temp = max(1, temp/d[j]);
+    }
+    return(1);
+}
+
+static char a_chunk[_PAGE_SIZE_ + 4];   /* 4 since a_chunk is in 
+                                           varlena format */
+
+static void
+initialize_info(CHUNK_INFO *A, int ndim, int dim[], int chunk[])
+{
+    int i;
+    
+    for ( i = 0; i < ndim; i++)
+        A->C[i] = chunk[i];
+}
+
+/*--------------------------------------------------------------------------
+ * Procedure reorganize_data():
+ *    This procedure reads the input multidimensional array that is organised
+ *    in the order specified by array "X" and breaks it up into chunks of
+ *    dimensions specified in "C". 
+ *
+ *    This is a very slow process, since reading and writing of LARGE files
+ *    may be involved.
+ *
+ *-------------------------------------------------------------------------
+ */
+static void
+_ConvertToChunkFile(int n,
+                   int baseSize,
+                   int dim[],
+                   int C[],
+                   int srcfd,
+                   int destfd)
+{
+    int max_chunks[MAXDIM], chunk_no[MAXDIM];
+    int PX[MAXDIM], dist[MAXDIM];
+    int csize = 1, i, temp;
+    
+    for (i = 0; i < n; chunk_no[i++] = 0) {
+        max_chunks[i] = dim[i]/C[i];
+        csize *= C[i];
+    }
+    csize *= baseSize;
+    temp = csize + 4;
+    memmove(a_chunk, &temp, 4); 
+    
+    mda_get_prod(n, dim, PX);
+    mda_get_offset_values(n, dist, PX, C);
+    for (i = 0; i < n; dist[i] *= baseSize, i++)
+       ;
+    do {
+        read_chunk(chunk_no, C, &(a_chunk[4]), srcfd, n, baseSize, PX, dist); 
+        write_chunk((struct varlena*)a_chunk, destfd);
+    }   while (next_tuple(n, chunk_no, max_chunks) != -1);
+}
+
+/*--------------------------------------------------------------------------
+ * read_chunk
+ *    reads a chunk from the input files into a_chunk, the position of the
+ *    chunk is specified by chunk_no  
+ *--------------------------------------------------------------------------
+ */
+static void
+read_chunk(int chunk_no[],
+          int C[],
+          char a_chunk[],
+          int srcfd,
+          int n,
+          int baseSize,
+          int PX[],
+          int dist[])
+{
+    int i, j, cp, unit_transfer;
+    int start_pos, pos[MAXDIM];
+    int indx[MAXDIM];
+    int fpOff;
+    
+    for ( i = start_pos = 0; i < n; i++) {
+        pos[i] = chunk_no[i] * C[i];
+        start_pos += pos[i]*PX[i];
+    }
+    start_pos *= baseSize;
+    
+    /* Read a block of dimesion C starting at co-ordinates pos */
+    unit_transfer = C[n-1] * baseSize;
+    
+    for (i = 0; i < n; indx[i++] = 0)
+       ;
+    fpOff = start_pos;
+    seek_and_read(fpOff, unit_transfer, a_chunk, srcfd, SEEK_SET);
+    fpOff += unit_transfer;
+    cp = unit_transfer;
+    
+    while ((j = next_tuple(n-1, indx, C)) != -1) {
+        fpOff += dist[j];
+        seek_and_read(fpOff, unit_transfer, &(a_chunk[cp]), srcfd, SEEK_SET);
+        cp += unit_transfer;
+        fpOff += unit_transfer;
+    }
+}
+
+/*--------------------------------------------------------------------------
+ * write_chunk()
+ *    writes a chunk of size csize into the output file
+ *--------------------------------------------------------------------------
+ */
+static int
+write_chunk(struct varlena * a_chunk, int ofile)
+{
+    int     got_n;
+#ifdef LOARRAY
+    got_n = LOwrite (ofile, a_chunk);
+#endif
+    return(got_n);
+}
+
+/*--------------------------------------------------------------------------
+ * seek_and_read()
+ *    seeks to the asked location in the input file and reads the 
+ *    appropriate number of blocks
+ *   Called By: read_chunk()
+ *--------------------------------------------------------------------------
+ */
+static int
+seek_and_read(int pos, int size, char buff[], int fp, int from)
+{
+    struct varlena *v;
+    
+    /* Assuming only one file */
+    if ( lo_lseek(fp, pos, from ) < 0)
+       elog(WARN, "File seek error");
+#ifdef LOARRAY
+    v = (struct varlena *) LOread(fp, size);
+#endif
+    if (VARSIZE(v) - 4 < size)
+       elog(WARN, "File read error");
+    memmove(buff, VARDATA(v), size);
+    pfree(v);
+    return(1);
+    
+}
+
+/*----------------------------------------------------------------------------
+ * _ReadChunkArray --
+ *        returns the subarray specified bu the range indices "st" and "endp"
+ *        from the chunked array stored in file "fp"
+ *---------------------------------------------------------------------------
+ */
+int
+_ReadChunkArray(int st[],
+               int endp[],
+               int bsize,
+               int fp,
+               char *destfp,
+               ArrayType *array,
+               int isDestLO,
+               bool *isNull)
+{
+    int i,j,jj;
+    int n, temp, words_read;
+    int chunk_span[MAXDIM], chunk_off[MAXDIM]; 
+    int chunk_st[MAXDIM], chunk_end[MAXDIM];
+    int block_seek;
+    
+    int  bptr, *C, csize, *dim, *lb;
+    int range_st[MAXDIM], range_end[MAXDIM], 
+    range[MAXDIM], array_span[MAXDIM];
+    int PA[MAXDIM], PCHUNK[MAXDIM], PC[MAXDIM];
+    int  to_read;
+    int cdist[MAXDIM], adist[MAXDIM]; 
+    int dist[MAXDIM], temp_seek;
+    
+    int srcOff;        /* Needed since LO don't understand SEEK_CUR*/
+    char *baseDestFp = (char *)destfp;
+    
+    CHUNK_INFO *A = (CHUNK_INFO *) ARR_DATA_PTR(array);
+    n = ARR_NDIM(array); 
+    dim = ARR_DIMS(array);
+    lb = ARR_LBOUND(array); 
+    C = A->C;
+    
+    csize = C[n-1];
+    PC[n-1] = 1;
+    temp = dim[n - 1]/C[n-1];
+    for (i = n-2; i >= 0; i--){
+        PC[i] = PC[i+1] * temp;
+        temp = dim[i] / C[i];
+        csize *= C[i];
+    }
+    
+    for (i = 0; i < n; st[i] -= lb[i], endp[i] -= lb[i], i++)
+       ;
+    mda_get_prod(n, C, PCHUNK);
+    mda_get_range(n, array_span, st, endp);
+    mda_get_prod(n, array_span, PA);
+    
+    array2chunk_coord(n, C, st, chunk_st);
+    array2chunk_coord(n, C, endp, chunk_end);
+    mda_get_range(n, chunk_span, chunk_st, chunk_end);
+    mda_get_offset_values(n, dist, PC, chunk_span);
+    
+    for (i = 0; i < n; i++) {
+        range_st[i] = st[i];
+        range_end[i] = min(chunk_st[i]*C[i]+C[i]-1, endp[i]);
+    }
+    
+    for (i = j = 0; i < n; i++)
+        j+= chunk_st[i]*PC[i];
+    temp_seek = srcOff = j * csize * bsize;
+    if (lo_lseek(fp, srcOff, SEEK_SET) < 0) RETURN_NULL;
+    
+    jj = n-1;
+    for (i = 0; i < n; chunk_off[i++] = 0)
+       ;
+    words_read = 0; temp_seek = 0;
+    do {
+        /* Write chunk (chunk_st) to output buffer */
+        mda_get_range(n, array_span,  range_st, range_end);
+        mda_get_offset_values(n, adist, PA, array_span);
+        mda_get_offset_values(n, cdist, PCHUNK, array_span);
+        for (i=0; i < n; range[i] = range_st[i]-st[i], i++);
+        bptr = tuple2linear(n, range, PA);
+        for (i = 0; i < n; range[i++] = 0);
+        j = n-1; bptr *= bsize;
+        if (isDestLO) { 
+            if (lo_lseek(destfp, bptr, SEEK_SET) < 0)
+               RETURN_NULL;
+        }
+        else 
+            destfp = baseDestFp + bptr; 
+        for(i = 0, block_seek = 0; i < n; i++)
+            block_seek += (range_st[i]-(chunk_st[i] + chunk_off[i])
+                          *C[i])*PCHUNK[i];
+        if (dist[jj] + block_seek + temp_seek) {
+            temp = (dist[jj]*csize+block_seek+temp_seek)*bsize;
+            srcOff += temp;
+            if (lo_lseek(fp, srcOff, SEEK_SET) < 0)
+               RETURN_NULL;
+        }
+        for (i = n-1, to_read = bsize; i >= 0; 
+            to_read *= min(C[i], array_span[i]), i--)
+            if (cdist[i] || adist[i])
+               break;
+        do {
+            if (cdist[j]) {
+                srcOff += (cdist[j]*bsize);
+                if (lo_lseek(fp, srcOff, SEEK_SET) < 0)
+                   RETURN_NULL;
+            }
+            block_seek += cdist[j];
+            bptr += adist[j]*bsize;
+            if (isDestLO) { 
+                if (lo_lseek(destfp, bptr, SEEK_SET) < 0)
+                   RETURN_NULL;
+            }
+            else 
+                destfp = baseDestFp + bptr;
+            temp = _LOtransfer ((char**)&destfp, to_read, 1, (char**)&fp, 1, isDestLO);
+            if (temp < to_read)
+               RETURN_NULL;
+            srcOff += to_read;
+            words_read+=to_read;
+            bptr += to_read;
+            block_seek += (to_read/bsize);
+            /* 
+             * compute next tuple in range[]
+             */
+            {
+                int x;
+                if (!(i+1)) 
+                    j = -1;
+                else {
+                    range[i] = (range[i]+1)%array_span[i];
+                    for (x = i; x*(!range[x]); x--) 
+                        range[x-1] = (range[x-1]+1)%array_span[x-1];
+                    if (x) 
+                        j = x; 
+                    else { 
+                        if (range[0]) 
+                            j = 0; 
+                        else 
+                            j = -1;
+                    }
+                }
+            }
+           /* 
+            * end of compute next tuple -- 
+            * j is set to -1 if tuple generation is over
+            */
+        } while (j != -1);    
+       
+       block_seek = csize - block_seek;    
+        temp_seek = block_seek;
+        jj = next_tuple(n, chunk_off, chunk_span);
+        if (jj == -1)
+           break;
+        range_st[jj] = (chunk_st[jj]+chunk_off[jj])*C[jj];
+        range_end[jj] = min(range_st[jj] + C[jj]-1, endp[jj]);
+        
+        for (i = jj+1; i < n; i++) {
+           range_st[i] = st[i];
+           range_end[i] = min((chunk_st[i]+chunk_off[i])*C[i]+C[i]-1, endp[i]);
+        }
+    } while (jj != -1);
+    return(words_read);
+}
+
+/*------------------------------------------------------------------------
+ * _ReadChunkArray1El --
+ *       returns one element of the chunked array as specified by the index "st"
+ *       the chunked file descriptor is "fp"
+ *-------------------------------------------------------------------------
+ */
+struct varlena *
+_ReadChunkArray1El(int st[],
+                  int bsize,
+                  int fp,
+                  ArrayType *array,
+                  bool *isNull)
+{
+    int i, j, n, temp, srcOff;
+    int chunk_st[MAXDIM];
+    
+    int  *C, csize, *dim, *lb;
+    int PCHUNK[MAXDIM], PC[MAXDIM];
+    
+    CHUNK_INFO *A = (CHUNK_INFO *) ARR_DATA_PTR(array);
+    
+    n = ARR_NDIM(array); 
+    lb = ARR_LBOUND(array); 
+    C = A->C;
+    dim = ARR_DIMS(array);
+    
+    csize = C[n-1];
+    PC[n-1] = 1;
+    temp = dim[n - 1]/C[n-1];
+    for (i = n-2; i >= 0; i--){
+        PC[i] = PC[i+1] * temp;
+        temp = dim[i] / C[i];
+        csize *= C[i];
+    }
+    
+    for (i = 0; i < n; st[i] -= lb[i], i++);
+    mda_get_prod(n, C, PCHUNK);
+    
+    array2chunk_coord(n, C, st, chunk_st);
+    
+    for (i = j = 0; i < n; i++)
+        j+= chunk_st[i]*PC[i];
+    srcOff = j * csize;
+    
+    for(i = 0; i < n; i++)
+        srcOff += (st[i]-chunk_st[i]*C[i])*PCHUNK[i];
+    
+    srcOff *= bsize;
+    if (lo_lseek(fp, srcOff, SEEK_SET) < 0)
+       RETURN_NULL;
+#ifdef LOARRAY
+    return (struct varlena *) LOread(fp, bsize);
+#endif
+    return (struct varlena *) 0;
+}
+
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
new file mode 100644 (file)
index 0000000..536b06d
--- /dev/null
@@ -0,0 +1,891 @@
+/*-------------------------------------------------------------------------
+ *
+ * date.c--
+ *    Functions for the built-in type "AbsoluteTime".
+ *    Functions for the built-in type "RelativeTime".
+ *    Functions for the built-in type "TimeInterval".
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *   This code is actually (almost) unused.
+ *   It needs to be integrated with Time and struct trange.
+ *
+ * XXX This code needs to be rewritten to work with the "new" definitions
+ * XXX in h/tim.h.  Look for int32's, int, long, etc. in the code.  The
+ * XXX definitions in h/tim.h may need to be rethought also.
+ *
+ * XXX  This code has been cleaned up some - avi 07/07/93
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <ctype.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <string.h>
+
+#include "postgres.h"
+#include "miscadmin.h"
+#include "access/xact.h"
+#include "utils/builtins.h"    /* where function declarations go */
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/nabstime.h"
+
+#define        TM_YEAR_BASE    1900            /* compatible to UNIX time */
+#define        EPOCH_YEAR      1970            /* compatible to UNIX time */
+#define        YEAR_MAX        2038            /* otherwise overflow */
+#define        YEAR_MIN        1902            /* otherwise overflow */
+#define        DAYS_PER_LYEAR  366
+#define        DAYS_PER_NYEAR  365
+#define        HOURS_PER_DAY   24
+#define        MINS_PER_HOUR   60
+#define        SECS_PER_MIN    60
+#define        MAX_LONG        2147483647      /* 2^31 */
+
+/* absolute time definitions */
+#define        TIME_NOW_STR            "now"      /* represents time now */
+#define        TIME_EPOCH_STR          "epoch"    /* Jan 1 00:00:00 1970 GMT */
+#define TIME_EPOCH_STR_LEN     (sizeof(TIME_EPOCH_STR)-1)
+
+#define        INVALID_ABSTIME_STR     "Undefined AbsTime"
+#define        INVALID_ABSTIME_STR_LEN (sizeof(INVALID_ABSTIME_STR)-1)
+
+#define        INVALID_RELTIME_STR     "Undefined RelTime"
+#define        INVALID_RELTIME_STR_LEN (sizeof(INVALID_RELTIME_STR)-1)
+#define        RELTIME_LABEL           '@'
+#define        RELTIME_PAST            "ago"
+#define        DIRMAXLEN               (sizeof(RELTIME_PAST)-1)
+
+/*
+ *  Unix epoch is Jan  1 00:00:00 1970.  Postgres knows about times
+ *  sixty-eight years on either side of that.
+ */ 
+
+#define        IsCharDigit(C)          isdigit(C)
+#define        IsCharA_Z(C)            isalpha(C)              
+#define        IsSpace(C)              ((C) == ' ')
+#define        IsNull(C)               ((C) == NULL)
+
+#define        T_INTERVAL_INVAL   0    /* data represents no valid interval */
+#define        T_INTERVAL_VALID   1    /* data represents a valid interval */
+/*
+ * ['Mon May 10 23:59:12 1943 PST' 'Sun Jan 14 03:14:21 1973 PST']
+ * 0        1         2         3         4         5         6
+ * 1234567890123456789012345678901234567890123456789012345678901234
+ *
+ * we allocate some extra -- timezones are usually 3 characters but
+ * this is not in the POSIX standard...
+ */
+#define        T_INTERVAL_LEN                  80
+#define        INVALID_INTERVAL_STR            "Undefined Range"
+#define        INVALID_INTERVAL_STR_LEN        (sizeof(INVALID_INTERVAL_STR)-1)
+
+#define ABSTIMEMIN(t1, t2) abstimele((t1),(t2)) ? (t1) : (t2)
+#define ABSTIMEMAX(t1, t2) abstimelt((t1),(t2)) ? (t2) : (t1)
+
+static char    *month_name[] = {
+       "Jan","Feb","Mar","Apr","May","Jun","Jul",
+       "Aug","Sep","Oct","Nov","Dec" };
+
+static char    *unit_tab[] = {
+       "second", "seconds", "minute", "minutes",
+       "hour", "hours", "day", "days", "week", "weeks",
+       "month", "months", "year", "years"};
+#define UNITMAXLEN 7   /* max length of a unit name */
+#define NUNITS 14      /* number of different units */
+
+/* table of seconds per unit (month = 30 days, year = 365 days)  */
+static int     sec_tab[] = { 
+       1,1, 60, 60,
+       3600, 3600, 86400, 86400, 604800,  604800,
+       2592000,  2592000,  31536000,  31536000 };
+
+/* maximal values (in seconds) per unit which can be represented */
+static int     unit_max_quantity[] = {
+       2144448000, 2144448000, 35740800, 35740800,
+       595680, 595680, 24820, 24820, 3545, 3545,
+       827, 827, 68, 68 };
+
+
+struct timeb *TimeDifferenceFromGMT = NULL;
+static bool TimeDiffIsInited = false;
+static char *timezonename = NULL;
+
+/*
+ * Function prototypes -- internal to this file only
+ */
+static int correct_unit(char unit[], int *unptr);
+static int correct_dir(char direction[], int *signptr);
+static int istinterval(char *i_string, 
+                      AbsoluteTime *i_start, 
+                      AbsoluteTime *i_end);
+
+/***************************************************************************** 
+ *   USER I/O ROUTINES                                                       *
+ *****************************************************************************/
+
+/*
+ *     reltimein       - converts a reltime string in an internal format
+ */
+int32  /* RelativeTime */
+reltimein(char *timestring)
+{
+    int                error;
+    int32 /* RelativeTime */   timeinsec;
+    int                sign, unitnr;
+    long               quantity;
+    
+    error = isreltime(timestring, &sign, &quantity, &unitnr);
+    
+#ifdef DATEDEBUG
+    elog(DEBUG, "reltimein: isreltime(%s) returns error=%d, %d, %d, %d",
+        timestring, error, sign, quantity, unitnr);
+#endif /* !DATEDEBUG */
+    
+    if (error != 1) {
+       timeinsec = INVALID_RELTIME;  /*invalid time representation */
+    } else {
+       /* this check is necessary, while no control on overflow */
+       if (quantity > unit_max_quantity[unitnr] || quantity < 0) {
+#ifdef DATEDEBUG
+           elog(DEBUG, "reltimein: illegal quantity %d (< %d)",
+                quantity, unit_max_quantity[unitnr]);
+#endif /* DATEDEBUG */
+           timeinsec = INVALID_RELTIME; /* illegal quantity */
+       } else {
+           timeinsec = sign * quantity * sec_tab[unitnr];
+#ifdef DATEDEBUG
+           elog(DEBUG, "reltimein: computed timeinsec %d",
+                timeinsec);
+#endif /* DATEDEBUG */
+       }
+    }
+    return(timeinsec);
+}
+
+
+/*
+ *     reltimeout      - converts the internal format to a reltime string
+ */
+char *reltimeout(int32 timevalue)
+{
+    char               *timestring;
+    long               quantity;
+    register int       i;
+    int                unitnr;
+       
+    timestring = (char *) palloc(Max(strlen(INVALID_RELTIME_STR),
+                                    UNITMAXLEN) + 1);
+    if (timevalue == INVALID_RELTIME) {
+       (void) strcpy(timestring,INVALID_RELTIME_STR);
+       return(timestring);
+    }
+    if (timevalue == 0)
+       i = 1;                  /* unit = 'seconds' */
+    else
+       for (i = 12; i >= 0; i = i-2)
+           if ((timevalue % sec_tab[i]) == 0)
+               break;          /* appropriate unit found */
+    unitnr = i;
+    quantity = (timevalue / sec_tab[unitnr]);
+    if (quantity > 1 || quantity < -1)
+       unitnr++;               /* adjust index for PLURAL of unit */
+    if (quantity >= 0)
+       (void) sprintf( timestring, "%c %lu %s", RELTIME_LABEL,
+                      quantity, unit_tab[unitnr]);
+    else
+       (void) sprintf( timestring, "%c %lu %s %s", RELTIME_LABEL,
+                      (quantity * -1), unit_tab[unitnr], RELTIME_PAST);
+    return(timestring);
+}
+
+
+/*
+ *     tintervalin     - converts an interval string to an internal format
+ */
+TimeInterval tintervalin(char *intervalstr)
+{      
+    int                error;
+    AbsoluteTime       i_start, i_end, t1, t2;
+    TimeInterval       interval;
+    
+    interval = (TimeInterval) palloc(sizeof(TimeIntervalData));
+    error = istinterval(intervalstr, &t1, &t2);
+    if (error == 0)
+       interval->status = T_INTERVAL_INVAL;
+    if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
+       interval->status = T_INTERVAL_INVAL;  /* undefined  */
+    else {
+       i_start = ABSTIMEMIN(t1, t2);
+       i_end = ABSTIMEMAX(t1, t2);
+       interval->data[0] = i_start;
+       interval->data[1] = i_end;
+       interval->status = T_INTERVAL_VALID;
+    }
+    return(interval);
+}
+
+
+/*
+ *     tintervalout    - converts an internal interval format to a string
+ *
+ */
+char *tintervalout(TimeInterval interval)
+{
+    char       *i_str, *p;
+    
+    i_str = (char      *) palloc( T_INTERVAL_LEN );  /* ['...' '...'] */
+    (void) strcpy(i_str,"['");
+    if (interval->status == T_INTERVAL_INVAL)
+       (void) strcat(i_str,INVALID_INTERVAL_STR);
+    else {
+       p = nabstimeout(interval->data[0]);
+       (void) strcat(i_str,p);
+       pfree(p);
+       (void) strcat(i_str,"' '");
+       p = nabstimeout(interval->data[1]);
+       (void) strcat(i_str,p);
+       pfree(p);
+    }
+    (void) strcat(i_str,"']\0");
+    return(i_str);
+}
+
+
+/***************************************************************************** 
+ *   PUBLIC ROUTINES                                                         *
+ *****************************************************************************/
+
+/*
+ *     mktinterval     - creates a time interval with endpoints t1 and t2
+ */
+TimeInterval mktinterval(AbsoluteTime t1, AbsoluteTime t2)
+{
+    AbsoluteTime       tstart = ABSTIMEMIN(t1, t2), tend = ABSTIMEMAX(t1, t2);
+    TimeInterval interval;
+    
+    interval = (TimeInterval) palloc(sizeof(TimeIntervalData));
+    if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
+       interval->status = T_INTERVAL_INVAL;
+    else {
+       interval->status = T_INTERVAL_VALID;
+       interval->data[0] = tstart;
+       interval->data[1] = tend;
+    }
+    
+    return interval;
+}
+
+/*
+ *     timepl, timemi and abstimemi use the formula
+ *             abstime + reltime = abstime
+ *     so      abstime - reltime = abstime
+ *     and     abstime - abstime = reltime
+ */
+
+/*
+ *     timepl          - returns the value of (abstime t1 + relime t2)
+ */
+AbsoluteTime timepl(AbsoluteTime t1, RelativeTime t2)
+{
+    if (t1 == CURRENT_ABSTIME)
+       t1 = GetCurrentTransactionStartTime();
+    
+    if (AbsoluteTimeIsReal(t1) &&
+       RelativeTimeIsValid(t2) &&
+       ((t2 > 0) ? (t1 < NOEND_ABSTIME - t2)
+        : (t1 > NOSTART_ABSTIME - t2)))        /* prevent overflow */
+       return (t1 + t2);
+    
+    return(INVALID_ABSTIME);
+}
+
+
+/*
+ *     timemi          - returns the value of (abstime t1 - reltime t2)
+ */
+AbsoluteTime timemi(AbsoluteTime t1, RelativeTime t2)
+{
+    if (t1 == CURRENT_ABSTIME)
+       t1 = GetCurrentTransactionStartTime();
+    
+    if (AbsoluteTimeIsReal(t1) &&
+       RelativeTimeIsValid(t2) &&
+       ((t2 > 0) ? (t1 > NOSTART_ABSTIME + t2)
+        : (t1 < NOEND_ABSTIME + t2)))  /* prevent overflow */
+       return (t1 - t2);
+    
+    return(INVALID_ABSTIME);
+}
+
+
+/*
+ *     abstimemi       - returns the value of (abstime t1 - abstime t2)
+ */
+static RelativeTime abstimemi(AbsoluteTime t1, AbsoluteTime t2)
+{
+    if (t1 == CURRENT_ABSTIME)
+       t1 = GetCurrentTransactionStartTime();
+    if (t2 == CURRENT_ABSTIME)
+       t2 = GetCurrentTransactionStartTime();
+    
+    if (AbsoluteTimeIsReal(t1) &&
+       AbsoluteTimeIsReal(t2))
+       return (t1 - t2);
+    
+    return(INVALID_RELTIME);
+}
+
+
+/*
+ *     ininterval      - returns 1, iff absolute date is in the interval
+ */
+int ininterval(AbsoluteTime t, TimeInterval interval)
+{
+    if (interval->status == T_INTERVAL_VALID && t != INVALID_ABSTIME)
+       return (abstimege(t, interval->data[0]) &&
+               abstimele(t, interval->data[1]));
+    return(0);
+}
+
+/*
+ *     intervalrel     - returns  relative time corresponding to interval
+ */
+RelativeTime intervalrel(TimeInterval interval)
+{
+    if (interval->status == T_INTERVAL_VALID)
+       return(abstimemi(interval->data[1], interval->data[0]));
+    else
+       return(INVALID_RELTIME);
+}
+
+/*
+ *     timenow         - returns  time "now", internal format
+ *
+ *      Now AbsoluteTime is time since Jan 1 1970 -mer 7 Feb 1992
+ */
+AbsoluteTime timenow()
+{
+    time_t sec;
+    if (time(&sec) < 0)
+       return(INVALID_ABSTIME);
+    return((AbsoluteTime) sec);
+}
+
+/*
+ *     reltimeeq       - returns 1, iff arguments are equal
+ *     reltimene       - returns 1, iff arguments are not equal
+ *     reltimelt       - returns 1, iff t1 less than t2
+ *     reltimegt       - returns 1, iff t1 greater than t2
+ *     reltimele       - returns 1, iff t1 less than or equal to t2
+ *     reltimege       - returns 1, iff t1 greater than or equal to t2
+ */
+int32 reltimeeq(RelativeTime t1, RelativeTime t2)
+{
+    if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
+       return 0;
+    return(t1 == t2);
+}
+
+int32 reltimene(RelativeTime t1, RelativeTime t2)
+{
+    if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
+       return 0;
+    return(t1 != t2);
+}
+
+int32 reltimelt(RelativeTime t1, RelativeTime t2)
+{
+    if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
+       return 0;
+    return(t1 < t2);
+}
+
+int32 reltimegt(RelativeTime t1, RelativeTime t2)
+{
+    if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
+       return 0;
+    return(t1 > t2);
+}
+
+int32 reltimele(RelativeTime t1, RelativeTime t2)
+{
+    if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
+       return 0;
+    return(t1 <= t2);
+}
+
+int32 reltimege(RelativeTime t1, RelativeTime t2)
+{
+    if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
+       return 0;
+    return(t1 >= t2);
+}
+
+
+/*
+ *     intervaleq      - returns 1, iff interval i1 is equal to interval i2
+ */
+int32 intervaleq(TimeInterval i1, TimeInterval i2)
+{
+    if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
+       return(0);      /* invalid interval */
+    return(abstimeeq(i1->data[0], i2->data[0]) &&
+          abstimeeq(i1->data[1], i2->data[1]));
+}
+
+/*
+ *     intervalleneq   - returns 1, iff length of interval i is equal to
+ *                             reltime t
+ */
+int32 intervalleneq(TimeInterval i, RelativeTime t)
+{
+    RelativeTime rt;
+    
+    if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
+       return(0);
+    rt = intervalrel(i);
+    return (rt != INVALID_RELTIME && rt == t);
+}
+
+/*
+ *     intervallenne   - returns 1, iff length of interval i is not equal
+ *                             to reltime t
+ */
+int32 intervallenne(TimeInterval i, RelativeTime t)
+{
+    RelativeTime rt;
+    
+    if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
+       return(0);
+    rt = intervalrel(i);
+    return (rt != INVALID_RELTIME && rt != t);
+}
+
+/*
+ *     intervallenlt   - returns 1, iff length of interval i is less than
+ *                             reltime t
+ */
+int32 intervallenlt(TimeInterval i, RelativeTime t)
+{
+    RelativeTime rt;
+    
+    if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
+       return(0);
+    rt = intervalrel(i);
+    return (rt != INVALID_RELTIME && rt < t);
+}
+
+/*
+ *     intervallengt   - returns 1, iff length of interval i is greater than
+ *                             reltime t
+ */
+int32 intervallengt(TimeInterval i, RelativeTime t)
+{
+    RelativeTime rt;
+    
+    if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
+       return(0);
+    rt = intervalrel(i);
+    return (rt != INVALID_RELTIME && rt > t);
+}
+
+/*
+ *     intervallenle   - returns 1, iff length of interval i is less or equal
+ *                                 than reltime t
+ */
+int32 intervallenle(TimeInterval i, RelativeTime t)
+{      
+    RelativeTime rt;
+    
+    if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
+       return(0);
+    rt = intervalrel(i);
+    return (rt != INVALID_RELTIME && rt <= t);
+}
+
+/*
+ *     intervallenge   - returns 1, iff length of interval i is greater or
+ *                             equal than reltime t
+ */
+int32 intervallenge(TimeInterval i, RelativeTime t)
+{
+    RelativeTime rt;
+    
+    if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME))
+       return(0);
+    rt = intervalrel(i);
+    return (rt != INVALID_RELTIME && rt >= t);
+}
+
+/*
+ *     intervalct      - returns 1, iff interval i1 contains interval i2
+ */
+int32 intervalct(TimeInterval i1, TimeInterval i2)
+{
+    if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
+       return(0);
+    return(abstimele(i1->data[0], i2->data[0]) &&
+          abstimege(i1->data[1], i2->data[1]));
+}
+
+/*
+ *     intervalov      - returns 1, iff interval i1 (partially) overlaps i2
+ */
+int32 intervalov(TimeInterval i1, TimeInterval i2)
+{
+    if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
+       return(0);
+    return(! (abstimelt(i1->data[1], i2->data[0]) ||
+             abstimegt(i1->data[0], i2->data[1])));
+}
+
+/*
+ *     intervalstart   - returns  the start of interval i
+ */
+AbsoluteTime intervalstart(TimeInterval i)
+{
+    if (i->status == T_INTERVAL_INVAL)
+       return INVALID_ABSTIME;
+    return(i->data[0]);
+}
+
+/*
+ *     intervalend     - returns  the end of interval i
+ */
+AbsoluteTime intervalend(TimeInterval i)
+{
+    if (i->status == T_INTERVAL_INVAL)
+       return INVALID_ABSTIME;
+    return(i->data[1]);
+}
+
+
+/***************************************************************************** 
+ *   PRIVATE ROUTINES                                                        *
+ *****************************************************************************/
+
+/*
+ *     isreltime       - returns 1, iff datestring is of type reltime
+ *                               2, iff datestring is 'invalid time' identifier
+ *                               0, iff datestring contains a syntax error
+ *
+ *     output parameter:
+ *             sign = -1, iff direction is 'ago'
+ *                            else sign = 1.
+ *             quantity   : quantity of unit
+ *             unitnr     :    0 or 1 ... sec
+ *                             2 or 3 ... min
+ *                             4 or 5 ... hour
+ *                             6 or 7 ... day
+ *                             8 or 9 ... week
+ *                            10 or 11... month
+ *                            12 or 13... year
+ *                       
+ *
+ *     Relative time:
+ *
+ *     `@' ` ' Quantity ` ' Unit [ ` ' Direction]
+ *
+ *     OR  `Undefined RelTime'         (see also INVALID_RELTIME_STR)
+ *
+ *     where
+ *     Quantity is     `1', `2', ...
+ *     Unit is         `second', `minute', `hour', `day', `week',
+ *                     `month' (30-days), or `year' (365-days),
+ *                             or PLURAL of these units.
+ *     Direction is    `ago'
+ *
+ *     VALID time  less or equal   `@ 68 years' 
+ *
+ */
+int isreltime(char *timestring, int *sign, long *quantity, int *unitnr)
+{
+    register char      *p;
+    register char      c;
+    int                i;
+    char               unit[UNITMAXLEN] ;
+    char               direction[DIRMAXLEN];
+    int                localSign;
+    int                localUnitNumber;
+    long               localQuantity;
+    
+    if (!PointerIsValid(sign)) {
+       sign = &localSign;
+    }
+    if (!PointerIsValid(unitnr)) {
+       unitnr = &localUnitNumber;
+    }
+    if (!PointerIsValid(quantity)) {
+       quantity = &localQuantity;
+    }
+    unit[0] = '\0';
+    direction[0] = '\0';
+    p = timestring;
+    /* skip leading blanks */
+    while ((c = *p) != '\0') {
+       if (c != ' ')
+           break;
+       p++;
+    }
+    /* Test whether 'invalid time' identifier or not */
+    if (!strncmp(INVALID_RELTIME_STR,p,strlen(INVALID_RELTIME_STR) + 1))
+       return(2);      /* correct 'invalid time' identifier found */
+    
+    /* handle label of relative time */
+    if (c != RELTIME_LABEL)
+       return(0);      /*syntax error*/
+    c = *++p;
+    if (c != ' ')      return(0);      /*syntax error*/
+    p++;
+    /* handle the quantity */
+    *quantity = 0;
+    for (;;) {
+       c = *p;
+       if (isdigit(c)) {
+           *quantity = *quantity * 10 + (c -'0');
+           p++;
+       } else {
+           if (c == ' ' )
+               break;          /* correct quantity found */
+           else
+               return(0);      /* syntax error */
+       }
+    }
+    /* handle unit */
+    p++;
+    i = 0;
+    for (;;) {
+       c = *p;
+       if (c >= 'a' && c <= 'z' && i <= (UNITMAXLEN - 1)) {
+           unit[i] = c;
+           p++;
+           i++;
+       } else {
+           if ((c == ' ' || c == '\0')
+               && correct_unit(unit, unitnr))
+               break;          /* correct unit found */
+           else
+               return(0);      /* syntax error */
+       }
+    }
+    /* handle optional direction */
+    if (c == ' ')
+       p++;
+    i = 0;
+    *sign = 1;
+    for (;;) {
+       c = *p;
+       if (c >= 'a' && c <= 'z' && i <= (DIRMAXLEN - 1)) {
+           direction[i] = c;
+           p++;
+           i++;
+       } else {
+           if ((c == ' ' || c == '\0') && i == 0) {
+               *sign = 1;
+               break;  /* no direction specified */
+           }
+           if ((c == ' ' || c == '\0') && i != 0)
+               {
+                   direction[i] = '\0';
+                   correct_dir(direction, sign);
+                   break;        /* correct direction found */
+               }
+           else
+               return(0);      /* syntax error*/
+       }
+    }
+    return(1);
+}
+
+/*
+ *     correct_unit    - returns 1, iff unit is a correct unit description
+ *
+ *     output parameter:
+ *             unptr: points to an integer which is the appropriate unit number
+ *                    (see function isreltime())
+ */
+static int correct_unit(char unit[], int *unptr)
+{
+    int        j = 0;
+    
+    while (j < NUNITS) {
+       if (strncmp(unit, unit_tab[j], strlen(unit_tab[j])) == 0) {
+           *unptr = j;
+           return(1);
+       }
+       j++;
+    }
+    return (0); /* invalid unit descriptor */
+}
+
+/*
+ *     correct_dir     - returns 1, iff direction is a correct identifier
+ *
+ *     output parameter:
+ *             signptr: points to -1 if dir corresponds to past tense
+ *                      else  to 1
+ */
+static int correct_dir(char direction[], int *signptr)
+{      
+    *signptr = 1;
+    if (strncmp(RELTIME_PAST, direction, strlen(RELTIME_PAST)+1) == 0)
+       {
+           *signptr = -1;
+           return(1);
+       } else
+           return (0);  /* invalid direction descriptor */
+}
+
+
+/*
+ *     istinterval     - returns 1, iff i_string is a valid interval descr.
+ *                               0, iff i_string is NOT a valid interval desc.
+ *                               2, iff any time is INVALID_ABSTIME
+ *
+ *     output parameter:
+ *             i_start, i_end: interval margins
+ *
+ *     Time interval:
+ *     `[' {` '} `'' <AbsTime> `'' {` '} `'' <AbsTime> `'' {` '} `]'
+ *
+ *     OR  `Undefined Range'   (see also INVALID_INTERVAL_STR)
+ *
+ *     where <AbsTime> satisfies the syntax of absolute time.
+ *
+ *     e.g.  [  '  Jan 18 1902'   'Jan 1 00:00:00 1970']
+ */
+static int istinterval(char *i_string, 
+                      AbsoluteTime *i_start, 
+                      AbsoluteTime *i_end)
+{
+    register char              *p,*p1;
+    register char              c;
+    
+    p = i_string;
+    /* skip leading blanks up to '[' */
+    while ((c = *p) != '\0') {
+       if ( IsSpace(c))
+           p++;
+       else if (c != '[')
+           return(0); /* syntax error */
+       else
+           break;
+    }
+    p++;
+    /* skip leading blanks up to "'" */
+    while ((c = *p) != '\0') {
+       if (IsSpace(c))
+           p++;
+       else if (c != '"')
+           return (0); /* syntax error */
+       else
+           break;
+    }
+    p++;
+    if (strncmp(INVALID_INTERVAL_STR,p,strlen(INVALID_INTERVAL_STR)) == 0)
+       return(0);      /* undefined range, handled like a syntax err.*/
+    /* search for the end of the first date and change it to a NULL*/
+    p1 = p;
+    while ((c = *p1) != '\0') {
+       if ( c == '"') {
+           *p1 = '\0';
+           break;
+       }
+       p1++;
+    }
+    /* get the first date */
+    *i_start = nabstimein(p);  /* first absolute date */
+    /* rechange NULL at the end of the first date to a "'" */
+    *p1 = '"';
+    p = ++p1;
+    /* skip blanks up to "'", beginning of second date*/
+    while ((c = *p) != '\0') {
+       if (IsSpace(c))
+           p++;
+       else if (c != '"')
+           return (0); /* syntax error */
+       else
+           break;
+    }  
+    p++;
+    /* search for the end of the second date and change it to a NULL*/
+    p1 = p;
+    while ((c = *p1) != '\0') {
+       if ( c == '"') {
+           *p1 = '\0';
+           break;
+       }
+       p1++;
+    }
+    /* get the second date */
+    *i_end = nabstimein(p);            /* second absolute date */
+    /* rechange NULL at the end of the first date to a ''' */
+    *p1 = '"';
+    p = ++p1;
+    /* skip blanks up to ']'*/
+    while ((c = *p) != '\0') {
+       if ( IsSpace(c))
+           p++;
+       else if (c != ']')
+           return(0); /*syntax error */
+       else
+           break;
+    }
+    p++;
+    c = *p;
+    if ( c != '\0' )
+       return (0);     /* syntax error */
+    /* it seems to be a valid interval */
+    return(1); 
+}
+
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+/*
+ * timeofday -
+ *     returns the current time as a text. similar to timenow() but returns
+ *     seconds with more precision (up to microsecs). (I need this to compare
+ *     the Wisconsin benchmark with Illustra whose TimeNow() shows current 
+ *     time with precision up to microsecs.)              - ay 3/95
+ */
+text *
+timeofday()
+{
+
+#ifndef WIN32
+    struct timeval tp;
+    struct timezone tpz;
+#endif /* WIN32 */
+    char templ[500];
+    char buf[500];
+    text *tm;
+    int len = 0;
+
+#ifndef WIN32
+    gettimeofday(&tp, &tpz);
+    (void) strftime(templ, sizeof(templ), "%a %b %d %H:%M:%S.%%d %Y %Z",
+                   localtime((time_t *) &tp.tv_sec));
+    sprintf(buf, templ, tp.tv_usec);
+
+    len = VARHDRSZ + strlen(buf);
+    tm = (text *)palloc(len);
+    VARSIZE(tm) = len;
+    strncpy(VARDATA(tm), buf, strlen(buf));
+    return tm;
+#else
+    len = len / len;
+    return tm;
+#endif /* WIN32 */
+
+}
diff --git a/src/backend/utils/adt/datetimes.c b/src/backend/utils/adt/datetimes.c
new file mode 100644 (file)
index 0000000..b40339b
--- /dev/null
@@ -0,0 +1,350 @@
+/*-------------------------------------------------------------------------
+ *
+ * datetimes.c--
+ *    implements DATE and TIME data types specified in SQL-92 standard
+ *
+ * Copyright (c) 1994-5, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>             /* for sprintf() */
+#include <string.h>
+#include "postgres.h"
+#include "utils/palloc.h"
+#include "utils/elog.h"
+
+/* these things look like structs, but we pass them by value so be careful
+   For example, passing an int -> DateADT is not portable! */
+typedef struct DateADT {
+    char       day;
+    char       month;
+    short      year;
+} DateADT;
+
+typedef struct TimeADT {
+    short      hr;
+    short      min;
+    float      sec;
+} TimeADT;
+
+#ifndef EUROPEAN_STYLE
+#define AMERICAN_STYLE
+#endif
+
+static int     day_tab[2][12] = {
+       {31,28,31,30,31,30,31,31,30,31,30,31},
+       {31,29,31,30,31,30,31,31,30,31,30,31}  };
+
+static int
+isleap(int year)
+{
+    return
+       (((year % 4) == 0 && (year % 100) != 0) || (year % 400) == 0);
+}
+
+/*****************************************************************************
+ *   Date ADT
+ *****************************************************************************/
+
+int4
+date_in(char *datestr)
+{
+    int d, m, y;
+    int4 result;
+    DateADT *date = (DateADT*)&result;
+
+#ifdef USE_SHORT_YEAR
+#define CHECK_DATE_LEN(datestr) (strlen(datestr) >= 8)
+#else
+#define CHECK_DATE_LEN(datestr) (strlen(datestr) == 10)
+#endif /* USE_SHORT_YEAR */
+
+#ifdef AMERICAN_STYLE
+    if (!CHECK_DATE_LEN(datestr) ||
+       sscanf(datestr, "%d%*c%d%*c%d", &m, &d, &y) != 3) {
+       elog(WARN, "date_in: date \"%s\" not of the form mm-dd-yyyy",
+            datestr);
+    }
+#else
+    if (!CHECK_DATE_LEN(datestr) ||
+       sscanf(datestr, "%d%*c%d%*c%d", &d, &m, &y) != 3) {
+       elog(WARN, "date_in: date \"%s\" not of the form dd-mm-yyyy",
+            datestr);
+    }
+#endif
+    if (m < 1 || m > 12)
+       elog(WARN, "date_in: month must be limited to values 1 through 12 in \"%s\"", datestr);
+    if (d < 1 || d > day_tab[isleap(y)][m-1])
+       elog(WARN, "date_in: day must be limited to values 1 through %d in \"%s\"",
+            day_tab[isleap(y)][m-1], datestr);
+
+#ifdef USE_SHORT_YEAR
+    if (y < 100) 
+       y += 1900; /* hack! */
+#endif /* USE_SHORT_YEAR */
+    date->day = d;
+    date->month = m;
+    date->year = y;
+    return result;
+}
+
+char *
+date_out(int4 dateVal)
+{
+    char *datestr = palloc(11);
+    int4 dateStore;
+    DateADT *date;
+
+       /* DateADT is a structure that happens to be four bytes long,
+          trust me on this.... */
+    date = (DateADT*)&dateStore;
+    dateStore = dateVal;
+
+#ifdef AMERICAN_STYLE
+    sprintf(datestr, "%02d-%02d-%04d",
+            (int)date->month, (int)date->day, (int)date->year);
+#else
+    sprintf(datestr, "%02d-%02d-%04d",
+            (int)date->day, (int)date->month, (int)date->year);
+#endif
+
+    return datestr;
+}
+
+
+int
+date_eq(int4 dateVal1, int4 dateVal2)
+{
+    int4 dateStore1 = dateVal1;
+    int4 dateStore2 = dateVal2;
+    DateADT *date1, *date2;
+    
+    date1 = (DateADT*)&dateStore1;
+    date2 = (DateADT*)&dateStore2;
+
+    return (date1->day==date2->day && 
+           date1->month==date2->month &&
+           date1->year==date2->year);
+}
+
+int
+date_ne(int4 dateVal1, int4 dateVal2)
+{
+    int4 dateStore1 = dateVal1;
+    int4 dateStore2 = dateVal2;
+    DateADT *date1, *date2;
+    
+    date1 = (DateADT*)&dateStore1;
+    date2 = (DateADT*)&dateStore2;
+
+    return (date1->day!=date2->day || date1->month!=date2->month ||
+           date1->year!=date2->year);
+}
+
+int
+date_lt(int4 dateVal1, int4 dateVal2)
+{
+    int4 dateStore1 = dateVal1;
+    int4 dateStore2 = dateVal2;
+    DateADT *date1, *date2;
+    
+    date1 = (DateADT*)&dateStore1;
+    date2 = (DateADT*)&dateStore2;
+
+    if (date1->year!=date2->year)
+       return (date1->year<date2->year);
+    if (date1->month!=date2->month)
+       return (date1->month<date2->month);
+    return (date1->day<date2->day);
+}
+
+int
+date_le(int4 dateVal1, int4 dateVal2)
+{
+
+    int4 dateStore1 = dateVal1;
+    int4 dateStore2 = dateVal2;
+    DateADT *date1, *date2;
+    
+    date1 = (DateADT*)&dateStore1;
+    date2 = (DateADT*)&dateStore2;
+
+    if (date1->year!=date2->year)
+       return (date1->year<=date2->year);
+    if (date1->month!=date2->month)
+       return (date1->month<=date2->month);
+    return (date1->day<=date2->day);
+}
+
+int
+date_gt(int4 dateVal1, int4 dateVal2)
+{
+    int4 dateStore1 = dateVal1;
+    int4 dateStore2 = dateVal2;
+    DateADT *date1, *date2;
+    
+    date1 = (DateADT*)&dateStore1;
+    date2 = (DateADT*)&dateStore2;
+
+
+    if (date1->year!=date2->year)
+       return (date1->year>date2->year);
+    if (date1->month!=date2->month)
+       return (date1->month>date2->month);
+    return (date1->day>date2->day);
+}
+
+int
+date_ge(int4 dateVal1, int4 dateVal2)
+{
+    int4 dateStore1 = dateVal1;
+    int4 dateStore2 = dateVal2;
+    DateADT *date1, *date2;
+    
+    date1 = (DateADT*)&dateStore1;
+    date2 = (DateADT*)&dateStore2;
+
+    if (date1->year!=date2->year)
+       return (date1->year>=date2->year);
+    if (date1->month!=date2->month)
+       return (date1->month>=date2->month);
+    return (date1->day>=date2->day);
+}
+
+int
+date_cmp(int4 dateVal1, int4 dateVal2)
+{
+    int4 dateStore1 = dateVal1;
+    int4 dateStore2 = dateVal2;
+    DateADT *date1, *date2;
+    
+    date1 = (DateADT*)&dateStore1;
+    date2 = (DateADT*)&dateStore2;
+
+    if (date1->year!=date2->year)
+       return ((date1->year<date2->year) ? -1 : 1);
+    if (date1->month!=date2->month)
+       return ((date1->month<date2->month) ? -1 : 1);
+    if (date1->day!=date2->day)
+       return ((date1->day<date2->day) ? -1 : 1);
+    return 0;
+}
+
+/*****************************************************************************
+ *   Time ADT
+ *****************************************************************************/
+
+char *
+time_in(char *timestr)
+{
+    int h, m;
+    float sec;
+    TimeADT *time;
+
+    if (sscanf(timestr, "%d%*c%d%*c%f", &h, &m, &sec) != 3) {
+       elog(WARN, "time_in: time \"%s\" not of the form hh:mm:ss",
+            timestr);
+    }
+
+    if (h < 0 || h > 23)
+       elog(WARN, "time_in: hour must be limited to values 0 through 23 in \"%s\"", timestr);
+    if (m < 0 || m > 59)
+       elog(WARN, "time_in: minute must be limited to values 0 through 59 in \"%s\"", timestr);
+    if (sec < 0 || sec >= 62.0)
+       elog(WARN, "time_in: second must be limited to values 0 through 61.99 in \"%s\"", timestr);
+
+    time = (TimeADT*)palloc(sizeof(TimeADT));
+    time->hr = h;
+    time->min = m;
+    time->sec = sec;
+    return (char*)time;
+}
+
+char *
+time_out(TimeADT *time)
+{
+    char *timestr = palloc(16);
+    
+    sprintf(timestr, "%02d:%02d:%09.6f",
+           (int)time->hr, (int)time->min, time->sec);
+
+    return timestr;
+}
+
+
+int
+time_eq(TimeADT *time1, TimeADT *time2)
+{
+    return (time1->sec==time2->sec && time1->min==time2->min &&
+           time1->hr==time2->hr);
+}
+
+int
+time_ne(TimeADT *time1, TimeADT *time2)
+{
+    return (time1->sec!=time2->sec || time1->min!=time2->min ||
+           time1->hr!=time2->hr);
+}
+
+int
+time_lt(TimeADT *time1, TimeADT *time2)
+{
+    if (time1->hr!=time2->hr)
+       return (time1->hr<time2->hr);
+    if (time1->min!=time2->min)
+       return (time1->min<time2->min);
+    return (time1->sec<time2->sec);
+}
+
+int
+time_le(TimeADT *time1, TimeADT *time2)
+{
+    if (time1->hr!=time2->hr)
+       return (time1->hr<=time2->hr);
+    if (time1->min!=time2->min)
+       return (time1->min<=time2->min);
+    return (time1->sec<=time2->sec);
+}
+
+int
+time_gt(TimeADT *time1, TimeADT *time2)
+{
+    if (time1->hr!=time2->hr)
+       return (time1->hr>time2->hr);
+    if (time1->min!=time2->min)
+       return (time1->min>time2->min);
+    return (time1->sec>time2->sec);
+}
+
+int
+time_ge(TimeADT *time1, TimeADT *time2)
+{
+    if (time1->hr!=time2->hr)
+       return (time1->hr>=time2->hr);
+    if (time1->min!=time2->min)
+       return (time1->min>=time2->min);
+    return (time1->sec>=time2->sec);
+}
+
+int
+time_cmp(TimeADT *time1, TimeADT *time2)
+{
+    if (time1->hr!=time2->hr)
+       return ((time1->hr<time2->hr) ? -1 : 1);
+    if (time1->min!=time2->min)
+       return ((time1->min<time2->min) ? -1 : 1);
+    if (time1->sec!=time2->sec)
+       return ((time1->sec<time2->sec) ? -1 : 1);
+    return 0;
+}
+
+int32   /* RelativeTime */
+int42reltime(int32 timevalue)
+{
+    return(timevalue);
+}
diff --git a/src/backend/utils/adt/datum.c b/src/backend/utils/adt/datum.c
new file mode 100644 (file)
index 0000000..5445ae4
--- /dev/null
@@ -0,0 +1,201 @@
+/*-------------------------------------------------------------------------
+ *
+ * datum.c--
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * In the implementation of the next routines we assume the following:
+ *
+ * A) if a type is "byVal" then all the information is stored in the
+ * Datum itself (i.e. no pointers involved!). In this case the
+ * length of the type is always greater than zero and less than
+ * "sizeof(Datum)"
+ * B) if a type is not "byVal" and it has a fixed length, then
+ * the "Datum" always contain a pointer to a stream of bytes.
+ * The number of significant bytes are always equal to the length of the
+ * type.
+ * C) if a type is not "byVal" and is of variable length (i.e. it has
+ * length == -1) then "Datum" always points to a "struct varlena".
+ * This varlena structure has information about the actual length of this
+ * particular instance of the type and about its value.
+ *
+ */
+#include <string.h>
+#include "postgres.h"
+#include "utils/datum.h"
+#include "catalog/pg_type.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+/*-------------------------------------------------------------------------
+ * datumGetSize
+ *
+ * Find the "real" size of a datum, given the datum value,
+ * its type, whether it is a "by value", and its length.
+ *
+ * To cut a long story short, usually the real size is equal to the
+ * type length, with the exception of variable length types which have
+ * a length equal to -1. In this case, we have to look at the value of
+ * the datum itself (which is a pointer to a 'varlena' struct) to find
+ * its size.
+ *-------------------------------------------------------------------------
+ */
+Size
+datumGetSize(Datum value, Oid type, bool byVal, Size len)
+{
+    
+    struct varlena *s;
+    Size size;
+    
+    if (byVal) {
+       if (len >= 0 && len <= sizeof(Datum)) {
+           size = len;
+       } else {
+           elog(WARN,
+                "datumGetSize: Error: type=%ld, byVaL with len=%d",
+                (long) type, len);
+       }
+    } else { /*  not byValue */
+       if (len == -1) {
+           /*
+            * variable length type
+            * Look at the varlena struct for its real length...
+            */
+           s = (struct varlena *) DatumGetPointer(value);
+           if (!PointerIsValid(s)) {
+               elog(WARN,
+                    "datumGetSize: Invalid Datum Pointer");
+           }
+           size = (Size) VARSIZE(s);
+       } else {
+           /*
+            * fixed length type
+            */
+           size = len;
+       }
+    }
+    
+    return(size);
+}
+
+/*-------------------------------------------------------------------------
+ * datumCopy
+ *
+ * make a copy of a datum
+ *
+ * If the type of the datum is not passed by value (i.e. "byVal=false")
+ * then we assume that the datum contains a pointer and we copy all the
+ * bytes pointed by this pointer
+ *-------------------------------------------------------------------------
+ */
+Datum
+datumCopy(Datum value, Oid type, bool byVal, Size len)
+{
+    
+    Size realSize;
+    Datum res;
+    char *s;
+    
+    
+    if (byVal) {
+       res = value;
+    } else {
+       if (value == 0) return((Datum)NULL);
+       realSize = datumGetSize(value, type, byVal, len);
+       /*
+        * the value is a pointer. Allocate enough space
+        * and copy the pointed data.
+        */
+       s = (char *) palloc(realSize);
+       if (s == NULL) {
+           elog(WARN,"datumCopy: out of memory\n");
+       }
+       memmove(s, DatumGetPointer(value), realSize);
+       res = (Datum)s;
+    }
+    return(res);
+}
+
+/*-------------------------------------------------------------------------
+ * datumFree
+ *
+ * Free the space occupied by a datum CREATED BY "datumCopy"
+ *
+ * NOTE: DO NOT USE THIS ROUTINE with datums returned by amgetattr() etc.
+ * ONLY datums created by "datumCopy" can be freed!
+ *-------------------------------------------------------------------------
+ */
+void
+datumFree(Datum value, Oid type, bool byVal, Size len)
+{
+    
+    Size realSize;
+    Pointer s;
+    
+    realSize = datumGetSize(value, type, byVal, len);
+    
+    if (!byVal) {
+       /*
+        * free the space palloced by "datumCopy()"
+        */
+       s = DatumGetPointer(value);
+       pfree(s);
+    }
+}
+
+/*-------------------------------------------------------------------------
+ * datumIsEqual
+ *
+ * Return true if two datums are equal, false otherwise
+ *
+ * NOTE: XXX!
+ * We just compare the bytes of the two values, one by one.
+ * This routine will return false if there are 2 different
+ * representations of the same value (something along the lines
+ * of say the representation of zero in one's complement arithmetic).
+ *
+ *-------------------------------------------------------------------------
+ */
+bool
+datumIsEqual(Datum value1, Datum value2, Oid type, bool byVal, Size len)
+{
+    Size size1, size2;
+    char *s1, *s2;
+    
+    if (byVal) {
+       /*
+        * just compare the two datums.
+        * NOTE: just comparing "len" bytes will not do the
+        * work, because we do not know how these bytes
+        * are aligned inside the "Datum".
+        */
+       if (value1 == value2)
+           return(true);
+       else
+           return(false);
+    } else {
+       /*
+        * byVal = false
+        * Compare the bytes pointed by the pointers stored in the
+        * datums.
+        */
+       size1 = datumGetSize(value1, type, byVal, len);
+       size2 = datumGetSize(value2, type, byVal, len);
+       if (size1 != size2)
+           return(false);
+       s1 = (char *) DatumGetPointer(value1);
+       s2 = (char *) DatumGetPointer(value2);
+       if (!memcmp(s1, s2, size1))
+           return(true);
+       else
+           return(false);
+    }
+}
+
diff --git a/src/backend/utils/adt/dt.c b/src/backend/utils/adt/dt.c
new file mode 100644 (file)
index 0000000..0522b93
--- /dev/null
@@ -0,0 +1,58 @@
+/*-------------------------------------------------------------------------
+ *
+ * dt.c--
+ *    Functions for the built-in type "dt".
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+#include "utils/palloc.h"
+#include "utils/builtins.h"            /* where function declarations go */
+
+
+/***************************************************************************** 
+ *   USER I/O ROUTINES                                                       *
+ *****************************************************************************/
+
+/*
+ *     dtin            - converts "nseconds" to internal representation
+ *
+ *     XXX Currently, just creates an integer.
+ */
+int32 dtin(char *datetime)
+{
+    if (datetime == NULL)
+       return((int32) 0);
+    return((int32) atol(datetime));
+}
+
+/*
+ *     dtout           - converts internal form to "..."
+ *
+ *     XXX assumes sign, 10 digits max, '\0'
+ */
+char *dtout(int32 datetime)
+{
+    char               *result;
+    
+    result = (char *) palloc(12);
+    Assert(result);
+    ltoa(datetime, result);
+    return(result);
+}
+
+/***************************************************************************** 
+ *   PUBLIC ROUTINES                                                         *
+ *****************************************************************************/
+/* (see int.c for comparison/operation routines) */
+
+/***************************************************************************** 
+ *   PRIVATE ROUTINES                                                        *
+ *****************************************************************************/
+/* (none) */
diff --git a/src/backend/utils/adt/filename.c b/src/backend/utils/adt/filename.c
new file mode 100644 (file)
index 0000000..9472fa9
--- /dev/null
@@ -0,0 +1,120 @@
+/*-------------------------------------------------------------------------
+ *
+ * filename.c--
+ *    
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include <stdio.h>
+#ifndef WIN32
+#include <pwd.h>
+#endif /* WIN32 */
+
+#include <sys/param.h>
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/builtins.h"    /* where function declarations go */
+
+char *
+filename_in(char *file)
+{
+    char *str, *getenv();
+    int ind;
+    
+    /*
+     * XXX - HACK CITY --- REDO
+     *   should let the shell do expansions (shexpand)
+     */
+
+#ifndef WIN32    
+    str = (char *) palloc(MAXPATHLEN * sizeof(*str));
+    str[0] = '\0';
+    if (file[0] == '~') {
+       if (file[1] == '\0' || file[1] == '/') {
+           /* Home directory */
+           
+           char *userName;
+           struct passwd *pw;
+           
+           userName = GetPgUserName();
+           
+           if ((pw = getpwnam(userName)) == NULL) {
+               elog(WARN, "User %s is not a Unix user on the db server.",
+                    userName);
+           }
+           
+           strcpy(str, pw->pw_dir);
+           
+           ind = 1;
+       } else {
+           /* Someone else's directory */
+           char name[16], *p;
+           struct passwd *pw;
+           int len;
+           
+           if ((p = (char *) strchr(file, '/')) == NULL) {
+               strcpy(name, file+1);
+               len = strlen(name);
+           } else {
+               len = (p - file) - 1;
+               strncpy(name, file+1, len);
+               name[len] = '\0';
+           }
+           /*printf("name: %s\n");*/
+           if ((pw = getpwnam(name)) == NULL) {
+               elog(WARN, "No such user: %s\n", name);
+               ind = 0;
+           } else {
+               strcpy(str, pw->pw_dir);
+               ind = len + 1;
+           }
+       }
+    } else if (file[0] == '$') {  /* $POSTGRESHOME, etc.  expand it. */
+       char environment[80], *envirp, *p;
+       int len;
+       
+       if ((p = (char *) strchr(file, '/')) == NULL) {
+           strcpy(environment, file+1);
+           len = strlen(environment);
+       } else {
+           len = (p - file) - 1;
+           strncpy(environment, file+1, len);
+           environment[len] = '\0';
+       }
+       envirp = getenv(environment);
+       if (envirp) {
+           strcpy(str, envirp);
+           ind = len + 1;
+       }
+       else {
+           elog(WARN,"Couldn't find %s in your environment", environment);
+       }
+    } else {
+       ind = 0;
+    }
+    strcat(str, file+ind);
+    return(str);
+#else
+    return(NULL);
+#endif /* WIN32 */
+}
+
+char *
+filename_out(char *s)
+{
+    char *ret;
+    
+    if (!s)
+       return((char *) NULL);
+    ret = (char *) palloc(strlen(s) + 1);
+    if (!ret)
+       elog(WARN, "filename_out: palloc failed");
+    return(strcpy(ret, s));
+}
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
new file mode 100644 (file)
index 0000000..acfbcb2
--- /dev/null
@@ -0,0 +1,1320 @@
+/*-------------------------------------------------------------------------
+ *
+ * float.c--
+ *    Functions for the built-in floating-point types.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * OLD COMMENTS
+ *     Basic float4 ops:
+ *      float4in, float4out, float4abs, float4um
+ *     Basic float8 ops:
+ *      float8in, float8inAd, float8out, float8outAd, float8abs, float8um
+ *     Arithmetic operators:
+ *      float4pl, float4mi, float4mul, float4div
+ *      float8pl, float8mi, float8mul, float8div
+ *     Comparison operators:
+ *      float4eq, float4ne, float4lt, float4le, float4gt, float4ge
+ *      float8eq, float8ne, float8lt, float8le, float8gt, float8ge
+ *     Conversion routines:
+ *      ftod, dtof
+ *
+ *     Random float8 ops:
+ *      dround, dtrunc, dsqrt, dcbrt, dpow, dexp, dlog1
+ *     Arithmetic operators:
+ *      float48pl, float48mi, float48mul, float48div
+ *      float84pl, float84mi, float84mul, float84div
+ *     Comparison operators:
+ *      float48eq, float48ne, float48lt, float48le, float48gt, float48ge
+ *      float84eq, float84ne, float84lt, float84le, float84gt, float84ge
+ *
+ *     (You can do the arithmetic and comparison stuff using conversion
+ *      routines, but then you pay the overhead of converting...)
+ *
+ * XXX GLUESOME STUFF. FIX IT! -AY '94
+ */
+#include <stdio.h>             /* for sprintf() */
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <float.h>             /* faked on sunos4 */
+
+#include <math.h>
+
+#include "postgres.h"
+#include "fmgr.h"
+#include "utils/builtins.h"    /* for ftod() prototype */
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+
+#define FORMAT                 'g'     /* use "g" output format as standard format */
+/* not sure what the following should be, but better to make it over-sufficient */
+#define        MAXFLOATWIDTH   64
+#define MAXDOUBLEWIDTH 128
+
+#if !(NeXT && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_2)
+       /* NS3.3 has conflicting declarations of these in <math.h> */
+
+#ifndef atof
+extern double  atof(const char *p);
+#endif
+
+#ifdef NEED_CBRT
+#define cbrt my_cbrt
+static double   cbrt(double x);
+#else /* NEED_CBRT */
+extern double   cbrt(double x);
+#endif /* NEED_CBRT */
+
+#ifdef NEED_RINT
+#define rint my_rint
+static double   rint(double x);
+#else /* NEED_RINT */
+extern double   rint(double x);
+#endif /* NEED_RINT */
+
+#ifdef NEED_ISINF
+#define isinf my_isinf
+static int     isinf(double x);
+#else /* NEED_ISINF */
+extern int     isinf(double x);
+#endif /* NEED_ISINF */
+
+#endif 
+/* ========== USER I/O ROUTINES ========== */
+     
+     
+#define FLOAT4_MAX       FLT_MAX
+#define FLOAT4_MIN       FLT_MIN
+#define FLOAT8_MAX       DBL_MAX
+#define FLOAT8_MIN       DBL_MIN
+
+/*
+   check to see if a float4 val is outside of
+   the FLOAT4_MIN, FLOAT4_MAX bounds.
+   
+   raise an elog warning if it is
+*/
+static void CheckFloat4Val(double val)
+{
+  /* defining unsafe floats's will make float4 and float8 ops faster
+     at the cost of safety, of course! */
+#ifdef UNSAFE_FLOATS
+  return;
+#else
+  if (fabs(val) > FLOAT4_MAX)
+    elog(WARN,"\tBad float4 input format -- overflow\n");
+  if (val != 0.0 && fabs(val) < FLOAT4_MIN)
+    elog(WARN,"\tBad float4 input format -- underflow\n");
+  return;
+#endif /* UNSAFE_FLOATS */
+}
+
+/*
+   check to see if a float8 val is outside of
+   the FLOAT8_MIN, FLOAT8_MAX bounds.
+   
+   raise an elog warning if it is
+*/
+static void CheckFloat8Val(double val)
+{
+  /* defining unsafe floats's will make float4 and float8 ops faster
+     at the cost of safety, of course! */
+#ifdef UNSAFE_FLOATS
+  return;
+#else
+  if (fabs(val) > FLOAT8_MAX)
+    elog(WARN,"\tBad float8 input format -- overflow\n");
+  if (val != 0.0 && fabs(val) < FLOAT8_MIN)
+    elog(WARN,"\tBad float8 input format -- underflow\n");
+  return;
+#endif /* UNSAFE_FLOATS */
+}
+
+/*
+ *     float4in        - converts "num" to float
+ *                       restricted syntax:
+ *                       {<sp>} [+|-] {digit} [.{digit}] [<exp>]
+ *                       where <sp> is a space, digit is 0-9,
+ *                       <exp> is "e" or "E" followed by an integer.
+ */
+float32 float4in(char *num)
+{
+    float32    result = (float32) palloc(sizeof(float32data));
+    double val;
+    char* endptr;
+    
+    errno = 0;
+    val = strtod(num,&endptr);
+    if (*endptr != '\0' || errno == ERANGE)
+       elog(WARN,"\tBad float4 input format\n");
+    
+    /* if we get here, we have a legal double, still need to check to see
+       if it's a legal float */
+    
+    CheckFloat4Val(val);
+
+    *result = val;
+    return result;
+}
+
+/*
+ *     float4out       - converts a float4 number to a string
+ *                       using a standard output format
+ */
+char *float4out(float32        num)
+{
+    char       *ascii = (char *)palloc(MAXFLOATWIDTH+1);       
+    
+    if (!num)
+       return strcpy(ascii, "(null)");
+    
+    sprintf(ascii, "%.*g", FLT_DIG, *num);
+    return(ascii);
+}
+
+
+/*
+ *     float8in        - converts "num" to float8
+ *                       restricted syntax:
+ *                       {<sp>} [+|-] {digit} [.{digit}] [<exp>]
+ *                       where <sp> is a space, digit is 0-9,
+ *                       <exp> is "e" or "E" followed by an integer.
+ */
+float64 float8in(char *num)
+{
+    float64    result = (float64) palloc(sizeof(float64data));
+    double val;
+    char* endptr;
+    
+    errno = 0;
+    val = strtod(num,&endptr);
+    if (*endptr != '\0' || errno == ERANGE)
+       elog(WARN,"\tBad float8 input format\n");
+    
+    CheckFloat8Val(val);
+    *result = val;
+    return(result);
+}
+
+
+/*
+ *     float8out       - converts float8 number to a string
+ *                       using a standard output format
+ */
+char *float8out(float64        num)
+{
+    char       *ascii = (char *)palloc(MAXDOUBLEWIDTH+1);
+    
+    if (!num)
+       return strcpy(ascii, "(null)");
+
+#ifndef WIN32
+    if (isnan(*num))
+       return strcpy(ascii, "NaN");
+    if (isinf(*num))
+       return strcpy(ascii, "Infinity");
+#else
+    if (_isnan(*num))
+       return strcpy(ascii, "NaN");
+    if (!_finite(*num))
+       return strcpy(ascii, "Infinity");
+#endif    
+
+    sprintf(ascii, "%.*g", DBL_DIG, *num);
+    return(ascii);
+}
+
+/* ========== PUBLIC ROUTINES ========== */
+
+
+/*
+ *     ======================
+ *     FLOAT4 BASE OPERATIONS
+ *     ======================
+ */
+
+/*
+ *     float4abs       - returns a pointer to |arg1| (absolute value)
+ */
+float32 float4abs(float32 arg1)
+{
+    float32    result;
+    double val;
+    
+    if (!arg1)
+       return (float32)NULL;
+    
+    val = fabs(*arg1);
+
+    CheckFloat4Val(val);
+
+    result = (float32) palloc(sizeof(float32data));
+    *result = val;
+    return(result);
+}
+
+/*
+ *     float4um        - returns a pointer to -arg1 (unary minus)
+ */
+float32 float4um(float32 arg1)
+{
+    float32    result;
+    double      val;
+    
+    if (!arg1)
+       return (float32)NULL;
+    
+    val = -(*arg1);
+    CheckFloat4Val(val);
+
+    result = (float32) palloc(sizeof(float32data));
+    *result = val;
+    return(result);
+}
+
+float32 float4larger(float32 arg1, float32 arg2)
+{
+    float32    result;
+    
+    if (!arg1 || !arg2)
+       return (float32)NULL;
+    
+    result = (float32) palloc(sizeof(float32data));
+    
+    *result = ((*arg1 > *arg2) ? *arg1 : *arg2);
+    return result;
+}
+
+float32 float4smaller(float32 arg1, float32 arg2)
+{
+    float32    result;
+    
+    if (!arg1 || !arg2)
+       return (float32)NULL;
+    
+    result = (float32) palloc(sizeof(float32data));
+    
+    *result = ((*arg1 > *arg2) ? *arg2 : *arg1);
+    return result;
+}
+
+/*
+ *     ======================
+ *     FLOAT8 BASE OPERATIONS
+ *     ======================
+ */
+
+/*
+ *     float8abs       - returns a pointer to |arg1| (absolute value)
+ */
+float64 float8abs(float64 arg1)
+{
+    float64    result;
+    double      val;
+    
+    if (!arg1)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    val = fabs(*arg1);
+    CheckFloat8Val(val);
+    *result = val;
+    return(result);
+}
+
+
+/*
+ *     float8um        - returns a pointer to -arg1 (unary minus)
+ */
+float64 float8um(float64 arg1)
+{
+    float64    result;
+    double      val;
+
+    if (!arg1)
+       return (float64)NULL;
+    
+    val = -(*arg1);
+    
+    CheckFloat8Val(val);
+    result = (float64) palloc(sizeof(float64data));
+    *result = val;
+    return(result);
+}
+
+float64 float8larger(float64 arg1, float64 arg2)
+{
+    float64    result;
+    
+    if (!arg1 || !arg2)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    *result = ((*arg1 > *arg2) ? *arg1 : *arg2);
+    return result;
+}
+
+float64 float8smaller(float64 arg1, float64 arg2)
+{
+    float64    result;
+    
+    if (!arg1 || !arg2)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    *result = ((*arg1 > *arg2) ? *arg2 : *arg1);
+    return result;
+}
+
+
+/*
+ *     ====================
+ *     ARITHMETIC OPERATORS
+ *     ====================
+ */
+
+/*
+ *     float4pl        - returns a pointer to arg1 + arg2
+ *     float4mi        - returns a pointer to arg1 - arg2
+ *     float4mul       - returns a pointer to arg1 * arg2
+ *     float4div       - returns a pointer to arg1 / arg2
+ *     float4inc       - returns a poniter to arg1 + 1.0
+ */
+float32 float4pl(float32 arg1, float32 arg2)
+{
+    float32    result;
+    double val;
+    
+    if (!arg1 || !arg2)
+       return (float32)NULL;
+    
+    val = *arg1 + *arg2;
+    CheckFloat4Val(val);
+
+    result = (float32) palloc(sizeof(float32data));
+    *result = val;
+
+    return(result);
+}
+
+float32 float4mi(float32 arg1, float32 arg2)
+{
+    float32    result;
+    double val;
+    
+    if (!arg1 || !arg2)
+       return (float32)NULL;
+    
+    val = *arg1 - *arg2;
+
+    CheckFloat4Val(val);
+    result = (float32) palloc(sizeof(float32data));
+    *result = val;
+    return(result);
+}
+
+float32 float4mul(float32 arg1, float32 arg2)
+{
+    float32    result;
+    double val;
+    
+    if (!arg1 || !arg2)
+       return (float32)NULL;
+    
+    val = *arg1 * *arg2;
+
+    CheckFloat4Val(val);
+    result = (float32) palloc(sizeof(float32data));
+    *result = val;
+    return(result);
+}
+
+float32 float4div(float32 arg1, float32 arg2)
+{
+    float32    result;
+    double val;
+    
+    if (!arg1 || !arg2)
+       return (float32)NULL;
+    
+    if (*arg2 == 0.0)
+      elog(WARN,"float4div:  divide by 0.0 error");
+
+    val = *arg1 / *arg2;
+    
+    CheckFloat4Val(val);
+    result = (float32) palloc(sizeof(float32data));
+    *result = *arg1 / *arg2;
+    return(result);
+}
+
+float32 float4inc(float32 arg1)
+{
+  double val;
+
+  if (!arg1)
+    return (float32)NULL;
+  
+  val = *arg1 + (float32data)1.0;
+  CheckFloat4Val(val);
+  *arg1 = val;
+  return arg1;
+}
+
+/*
+ *     float8pl        - returns a pointer to arg1 + arg2
+ *     float8mi        - returns a pointer to arg1 - arg2
+ *     float8mul       - returns a pointer to arg1 * arg2
+ *     float8div       - returns a pointer to arg1 / arg2
+ *     float8inc       - returns a pointer to arg1 + 1.0
+ */
+float64 float8pl(float64 arg1, float64 arg2)
+{
+    float64    result;
+    double         val;
+    
+    if (!arg1 || !arg2)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    val = *arg1 + *arg2;
+    CheckFloat8Val(val);
+    *result = val;
+    return(result);
+}
+
+float64 float8mi(float64 arg1, float64 arg2)
+{
+    float64    result;
+    double      val;
+    
+    if (!arg1 || !arg2)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    val = *arg1 - *arg2;
+    CheckFloat8Val(val);
+    *result = val;
+    return(result);
+}
+
+float64 float8mul(float64 arg1, float64 arg2)
+{
+    float64    result;
+    double      val;
+    
+    if (!arg1 || !arg2)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    val = *arg1 * *arg2;
+    CheckFloat8Val(val);
+    *result = val;
+    return(result);
+}
+
+float64 float8div(float64 arg1, float64 arg2)
+{
+    float64    result;
+    double      val;
+    
+    if (!arg1 || !arg2)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    if (*arg2 == 0.0)
+      elog(WARN,"float8div:  divide by 0.0 error");
+
+    val = *arg1 / *arg2;
+    CheckFloat8Val(val);
+    *result = val;
+    return(result);
+}
+
+float64 float8inc(float64 arg1)
+{
+    double val;
+    if (!arg1)
+       return (float64)NULL;
+    
+    val = *arg1 + (float64data)1.0;
+    CheckFloat8Val(val);
+    *arg1 = val;
+    return(arg1);
+}
+
+
+/*
+ *     ====================
+ *     COMPARISON OPERATORS
+ *     ====================
+ */
+
+/*
+ *     float4{eq,ne,lt,le,gt,ge}       - float4/float4 comparison operations
+ */
+long float4eq(float32 arg1, float32 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 == *arg2);
+}
+
+long float4ne(float32 arg1, float32 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 != *arg2);
+}
+
+long float4lt(float32 arg1, float32 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 < *arg2);
+}
+
+long float4le(float32 arg1, float32 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 <= *arg2);
+}
+
+long float4gt(float32 arg1, float32 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 > *arg2);
+}
+
+long float4ge(float32 arg1, float32 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 >= *arg2);
+}
+
+/*
+ *     float8{eq,ne,lt,le,gt,ge}       - float8/float8 comparison operations
+ */
+long float8eq(float64 arg1, float64 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 == *arg2);
+}
+
+long float8ne(float64 arg1, float64 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 != *arg2);
+}
+
+long float8lt(float64 arg1, float64 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 < *arg2);
+}
+
+long float8le(float64 arg1, float64 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 <= *arg2);
+}
+
+long float8gt(float64 arg1, float64 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 > *arg2);
+}
+
+long float8ge(float64 arg1, float64 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 >= *arg2);
+}
+
+
+/*
+ *     ===================
+ *     CONVERSION ROUTINES
+ *     ===================
+ */
+
+/*
+ *     ftod            - converts a float4 number to a float8 number
+ */
+float64 ftod(float32 num)
+{
+    float64    result;
+    
+    if (!num)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    *result = *num;
+    return(result);
+}
+
+
+/*
+ *     dtof            - converts a float8 number to a float4 number
+ */
+float32 dtof(float64 num)
+{
+    float32    result;
+    
+    if (!num)
+       return (float32)NULL;
+    
+    result = (float32) palloc(sizeof(float32data));
+    
+    *result = *num;
+    return(result);
+}
+
+
+/*
+ *     =======================
+ *     RANDOM FLOAT8 OPERATORS
+ *     =======================
+ */
+
+/*
+ *     dround          - returns a pointer to  ROUND(arg1)
+ */
+float64 dround(float64 arg1)
+{
+    float64    result;
+    double tmp;
+    
+    if (!arg1)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    tmp = *arg1;
+    *result = (float64data) rint(tmp);
+    return(result);
+}
+
+
+/*
+ *     dtrunc          - returns a pointer to  truncation of arg1,
+ *                       arg1 >= 0 ... the greatest integer as float8 less 
+ *                                     than or equal to arg1
+ *                       arg1 < 0  ... the greatest integer as float8 greater
+ *                                     than or equal to arg1
+ */
+float64 dtrunc(float64 arg1)
+{
+    float64    result;
+    double tmp;
+    
+    if (!arg1)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    tmp = *arg1;
+    if (*arg1 >= 0)
+       *result = (float64data) floor(tmp);
+    else
+       *result = (float64data) -(floor(-tmp));
+    return(result);
+}
+
+
+/*     
+ *     dsqrt           - returns a pointer to square root of arg1
+ */
+float64 dsqrt(float64 arg1)
+{
+    float64    result;
+    double tmp;
+    
+    if (!arg1)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    tmp = *arg1;
+    *result = (float64data) sqrt(tmp);
+    return (result);
+}
+
+
+/*
+ *     dcbrt           - returns a pointer to cube root of arg1        
+ */
+float64 dcbrt(float64 arg1)
+{
+    float64    result;
+    double tmp;
+    
+    if (!arg1)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    tmp = *arg1;
+    *result = (float64data) cbrt(tmp);
+    return(result);
+}
+
+
+/*
+ *     dpow            - returns a pointer to pow(arg1,arg2)
+ */
+float64 dpow(float64 arg1, float64 arg2)
+{
+    float64    result;
+    double tmp1, tmp2;
+    
+    if (!arg1 || !arg2)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    tmp1 = *arg1;
+    tmp2 = *arg2;
+    errno = 0;
+    *result = (float64data) pow(tmp1, tmp2);
+    if (errno == ERANGE)
+      elog(WARN, "pow() returned a floating point out of the range\n");
+
+    CheckFloat8Val(*result);
+    return(result);
+}
+
+
+/*
+ *     dexp            - returns a pointer to the exponential function of arg1
+ */
+float64 dexp(float64 arg1)
+{
+    float64    result;
+    double tmp;
+    
+    if (!arg1)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    tmp = *arg1;
+    errno = 0;
+    *result = (float64data) exp(tmp);
+    if (errno == ERANGE)
+      elog(WARN, "exp() returned a floating point out of range\n");
+
+    CheckFloat8Val(*result);
+    return(result);
+}
+
+
+/*
+ *     dlog1           - returns a pointer to the natural logarithm of arg1
+ *                       ("dlog" is already a logging routine...)
+ */
+float64 dlog1(float64 arg1)
+{
+    float64    result;
+    double tmp;
+    
+    if (!arg1)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    tmp = *arg1;
+    if (tmp == 0.0)
+      elog(WARN, "can't take log of 0!");
+    if (tmp < 0)
+      elog(WARN, "can't take log of a negative number");
+    *result = (float64data) log(tmp);
+
+    CheckFloat8Val(*result);
+    return(result);
+}
+
+
+/*
+ *     ====================
+ *     ARITHMETIC OPERATORS
+ *     ====================
+ */
+
+/*
+ *     float48pl       - returns a pointer to arg1 + arg2
+ *     float48mi       - returns a pointer to arg1 - arg2
+ *     float48mul      - returns a pointer to arg1 * arg2
+ *     float48div      - returns a pointer to arg1 / arg2
+ */
+float64 float48pl(float32 arg1, float64 arg2)
+{
+    float64    result;
+    
+    if (!arg1 || !arg2)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    *result = *arg1 + *arg2;
+    CheckFloat8Val(*result);
+    return(result);
+}
+
+float64 float48mi(float32 arg1, float64 arg2)
+{
+    float64    result;
+    
+    if (!arg1 || !arg2)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    *result = *arg1 - *arg2;
+    CheckFloat8Val(*result);
+    return(result);
+}
+
+float64 float48mul(float32 arg1, float64 arg2)
+{
+    float64    result;
+    
+    if (!arg1 || !arg2)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    *result = *arg1 * *arg2;
+    CheckFloat8Val(*result);
+    return(result);
+}
+
+float64 float48div(float32 arg1, float64 arg2)
+{
+    float64    result;
+    
+    if (!arg1 || !arg2)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    if (*arg2 == 0.0)
+      elog(WARN, "float48div:  divide by 0.0 error!");
+
+    *result = *arg1 / *arg2;
+    CheckFloat8Val(*result);
+    return(result);
+}
+
+/*
+ *     float84pl       - returns a pointer to arg1 + arg2
+ *     float84mi       - returns a pointer to arg1 - arg2
+ *     float84mul      - returns a pointer to arg1 * arg2
+ *     float84div      - returns a pointer to arg1 / arg2
+ */
+float64 float84pl(float64 arg1, float32 arg2)
+{
+    float64    result;
+    
+    if (!arg1 || !arg2)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    *result = *arg1 + *arg2;
+    CheckFloat8Val(*result);
+    return(result);
+}
+
+float64 float84mi(float64 arg1, float32 arg2)
+{
+    float64    result;
+    
+    if (!arg1 || !arg2)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    *result = *arg1 - *arg2;
+    CheckFloat8Val(*result);
+    return(result);
+}
+
+float64 float84mul(float64 arg1, float32 arg2)
+{
+    
+    float64    result;
+    
+    if (!arg1 || !arg2)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    *result = *arg1 * *arg2;
+    CheckFloat8Val(*result);
+    return(result);
+}
+
+float64 float84div(float64 arg1, float32 arg2)
+{
+    float64    result;
+    
+    if (!arg1 || !arg2)
+       return (float64)NULL;
+    
+    result = (float64) palloc(sizeof(float64data));
+    
+    if (*arg2 == 0.0)
+      elog(WARN, "float48div:  divide by 0.0 error!");
+
+    *result = *arg1 / *arg2;
+    CheckFloat8Val(*result);
+    return(result);
+}
+
+/*
+ *     ====================
+ *     COMPARISON OPERATORS
+ *     ====================
+ */
+
+/*
+ *     float48{eq,ne,lt,le,gt,ge}      - float4/float8 comparison operations
+ */
+long float48eq(float32 arg1, float64 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 == (float)*arg2);
+}
+
+long float48ne(float32 arg1, float64 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 != (float)*arg2);
+}
+
+long float48lt(float32 arg1, float64 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 < (float)*arg2);
+}
+
+long float48le(float32 arg1, float64 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 <= (float)*arg2);
+}
+
+long float48gt(float32 arg1, float64 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 > (float)*arg2);
+}
+
+long float48ge(float32 arg1, float64 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return(*arg1 >= (float)*arg2);
+}
+
+/*
+ *     float84{eq,ne,lt,le,gt,ge}      - float4/float8 comparison operations
+ */
+long float84eq(float64 arg1, float32 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return((float)*arg1 == *arg2);
+}
+
+long float84ne(float64 arg1, float32 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return((float)*arg1 != *arg2);
+}
+
+long float84lt(float64 arg1, float32 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return((float)*arg1 < *arg2);
+}
+
+long float84le(float64 arg1, float32 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return((float)*arg1 <= *arg2);
+}
+
+long float84gt(float64 arg1, float32 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return((float)*arg1 > *arg2);
+}
+
+long float84ge(float64 arg1, float32 arg2)
+{
+    if (!arg1 || !arg2)
+       return (long)NULL;
+    
+    return((float)*arg1 >= *arg2);
+}
+
+/* ========== PRIVATE ROUTINES ========== */
+
+/* From "fdlibm" @ netlib.att.com */
+
+#ifdef NEED_RINT
+
+/* @(#)s_rint.c 5.1 93/09/24 */
+/*
+ * ====================================================
+ * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ *
+ * Developed at SunPro, a Sun Microsystems, Inc. business.
+ * Permission to use, copy, modify, and distribute this
+ * software is freely granted, provided that this notice 
+ * is preserved.
+ * ====================================================
+ */
+
+/*
+ * rint(x)
+ * Return x rounded to integral value according to the prevailing
+ * rounding mode.
+ * Method:
+ *     Using floating addition.
+ * Exception:
+ *     Inexact flag raised if x not equal to rint(x).
+ */
+
+#ifdef __STDC__
+static const double
+#else
+    static double 
+#endif
+    one = 1.0,
+    TWO52[2]={
+       4.50359962737049600000e+15, /* 0x43300000, 0x00000000 */
+       -4.50359962737049600000e+15, /* 0xC3300000, 0x00000000 */
+    };
+
+#ifdef __STDC__
+static double rint(double x)
+#else
+     static double rint(x)
+     double x;
+#endif
+{
+    int i0,n0,j0,sx;
+    unsigned i,i1;
+    double w,t;
+    n0 = (*((int *)&one)>>29)^1;
+    i0 =  *(n0+(int*)&x);
+    sx = (i0>>31)&1;
+    i1 =  *(1-n0+(int*)&x);
+    j0 = ((i0>>20)&0x7ff)-0x3ff;
+    if(j0<20) {
+       if(j0<0) {      
+           if(((i0&0x7fffffff)|i1)==0) return x;
+           i1 |= (i0&0x0fffff);
+           i0 &= 0xfffe0000;
+           i0 |= ((i1|-i1)>>12)&0x80000;
+           *(n0+(int*)&x)=i0;
+           w = TWO52[sx]+x;
+           t =  w-TWO52[sx];
+           i0 = *(n0+(int*)&t);
+           *(n0+(int*)&t) = (i0&0x7fffffff)|(sx<<31);
+           return t;
+       } else {
+           i = (0x000fffff)>>j0;
+           if(((i0&i)|i1)==0) return x; /* x is integral */
+           i>>=1;
+           if(((i0&i)|i1)!=0) {
+               if(j0==19) i1 = 0x40000000; else
+                   i0 = (i0&(~i))|((0x20000)>>j0);
+           }
+       }
+    } else if (j0>51) {
+       if(j0==0x400) return x+x;       /* inf or NaN */
+       else return x;          /* x is integral */
+    } else {
+       i = ((unsigned)(0xffffffff))>>(j0-20);
+       if((i1&i)==0) return x; /* x is integral */
+       i>>=1;
+       if((i1&i)!=0) i1 = (i1&(~i))|((0x40000000)>>(j0-20));
+    }
+    *(n0+(int*)&x) = i0;
+    *(1-n0+(int*)&x) = i1;
+    w = TWO52[sx]+x;
+    return w-TWO52[sx];
+}
+
+#endif /* NEED_RINT */
+
+#ifdef NEED_CBRT
+
+static
+    double
+    cbrt(x)
+double x;
+{
+    int isneg = (x < 0.0);
+    double tmpres = pow(fabs(x), (double) 1.0 / (double) 3.0);
+    
+    return(isneg ? -tmpres : tmpres);
+}
+
+#endif /* NEED_CBRT */
+
+#ifdef NEED_ISINF
+
+#if defined(PORTNAME_aix)
+#ifdef CLASS_CONFLICT
+/* we want the math symbol */
+#undef class
+#endif /* CLASS_CONFICT */
+
+static int isinf(x)
+     double x;
+{
+    int fpclass = class(x);
+    if (fpclass == FP_PLUS_INF)
+       return(1);
+    if (fpclass == FP_MINUS_INF)
+       return(-1);
+    return(0);
+}
+#endif /* PORTNAME_aix */
+
+#if defined(PORTNAME_ultrix4)
+#include <fp_class.h>
+static int isinf(x)
+     double x;
+{
+    int fpclass = fp_class_d(x);
+    if (fpclass == FP_POS_INF)
+       return(1);
+    if (fpclass == FP_NEG_INF)
+       return(-1);
+    return(0);
+}
+#endif /* PORTNAME_ultrix4 */
+
+#if defined(PORTNAME_alpha)
+#include <fp_class.h>
+static int isinf(x)
+     double x;
+{
+    int fpclass = fp_class(x);
+    if (fpclass == FP_POS_INF)
+       return(1);
+    if (fpclass == FP_NEG_INF)
+       return(-1);
+    return(0);
+}
+#endif /* PORTNAME_alpha */
+
+#if defined(PORTNAME_sparc_solaris)
+#include <ieeefp.h>
+static int
+    isinf(d)
+double d;
+{
+    fpclass_t  type = fpclass(d);
+    switch (type) {
+    case FP_SNAN:
+    case FP_QNAN:
+    case FP_NINF:
+    case FP_PINF:
+       return (1);
+    default:
+       break;
+    }
+    
+    return (0);
+}
+#endif /* PORTNAME_sparc_solaris */
+
+#if defined(PORTNAME_irix5)
+#include <ieeefp.h>
+static int
+    isinf(d)
+double d;
+{
+    fpclass_t  type = fpclass(d);
+    switch (type) {
+    case FP_SNAN:
+    case FP_QNAN:
+    case FP_NINF:
+    case FP_PINF:
+       return (1);
+    default:
+       break;
+    }
+    
+    return (0);
+}
+#endif /* PORTNAME_irix5 */
+
+#endif /* NEED_ISINF */
diff --git a/src/backend/utils/adt/geo-ops.c b/src/backend/utils/adt/geo-ops.c
new file mode 100644 (file)
index 0000000..f31b8d4
--- /dev/null
@@ -0,0 +1,1947 @@
+/*-------------------------------------------------------------------------
+ *
+ * geo-ops.c--
+ *    2D geometric operations
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <math.h>
+#include <float.h>     /* faked on sunos */
+#include <stdio.h>     /* for sprintf proto, etc. */
+#include <string.h>
+
+#include "utils/geo-decls.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+#define LDELIM         '('
+#define RDELIM         ')'
+#define        DELIM           ','
+#define BOXNARGS       4
+#define        LSEGNARGS       4
+#define        POINTNARGS      2
+
+/***********************************************************************
+ **
+ **    Routines for two-dimensional boxes.
+ **
+ ***********************************************************************/
+
+/*----------------------------------------------------------
+ * Formatting and conversion routines.
+ *---------------------------------------------------------*/
+
+/*     box_in  -       convert a string to internal form.
+ *
+ *     str:    input string "(f8, f8, f8, f8)"
+ */
+BOX *box_in(char *str)
+{
+    double     tmp;
+    char       *p, *coord[BOXNARGS];
+    int        i;
+    BOX        *result;
+    
+    if (str == NULL)
+       elog (WARN," Bad (null) box external representation");
+    
+    if ((p = (char *)strchr(str, LDELIM)) == (char *)NULL)
+       elog (WARN, "Bad box external representation '%s'",str);
+    for (i = 0, p = str; *p && i < BOXNARGS && *p != RDELIM; p++)
+       if (*p == DELIM || (*p == LDELIM && !i))
+           coord[i++] = p + 1;
+    if (i < BOXNARGS - 1)
+       elog (WARN, "Bad box external representation '%s'", str);
+    result = PALLOCTYPE(BOX);
+    result->xh = atof(coord[0]);
+    result->yh = atof(coord[1]);
+    result->xl = atof(coord[2]);
+    result->yl = atof(coord[3]);
+    if (result->xh < result->xl) {
+       tmp = result->xh;
+       result->xh = result->xl;
+       result->xl = tmp;
+    }
+    if (result->yh < result->yl) {
+       tmp = result->yh;
+       result->yh = result->yl;
+       result->yl = tmp;
+    }
+    
+    return(result);
+}
+
+/*     box_out -       convert a box to external form.
+ */
+char *box_out(BOX *box)
+{
+    char       *result;
+    
+    if (box == NULL)
+       return(NULL);
+    result = (char *)PALLOC(80);
+    (void) sprintf(result, "(%G,%G,%G,%G)",
+                  box->xh, box->yh, box->xl, box->yl);
+    
+    return(result);
+}
+
+
+/*     box_construct   -       fill in a new box.
+ */
+BOX *box_construct(double x1, double x2, double y1, double y2)
+{
+    BOX        *result;
+    
+    result = PALLOCTYPE(BOX);
+    return( box_fill(result, x1, x2, y1, y2) );
+}
+
+
+/*     box_fill        -       fill in a static box
+ */
+BOX *box_fill(BOX *result, double x1, double x2, double y1, double y2)
+{
+    double     tmp;
+    
+    result->xh = x1;
+    result->xl = x2;
+    result->yh = y1;
+    result->yl = y2;
+    if (result->xh < result->xl) {
+       tmp = result->xh;
+       result->xh = result->xl;
+       result->xl = tmp;
+    }
+    if (result->yh < result->yl) {
+       tmp = result->yh;
+       result->yh = result->yl;
+       result->yl = tmp;
+    }
+    
+    return(result);
+}
+
+
+/*     box_copy        -       copy a box
+ */
+BOX *box_copy(BOX *box)
+{
+    BOX        *result;
+    
+    result = PALLOCTYPE(BOX);
+    memmove((char *) result, (char *) box, sizeof(BOX));
+    
+    return(result);
+}
+
+
+/*----------------------------------------------------------
+ *  Relational operators for BOXes.
+ *     <, >, <=, >=, and == are based on box area.
+ *---------------------------------------------------------*/
+
+/*     box_same        -       are two boxes identical?
+ */
+long box_same(BOX *box1, BOX *box2)
+{
+    return((box1->xh == box2->xh && box1->xl == box2->xl) &&
+          (box1->yh == box2->yh && box1->yl == box2->yl));
+}
+
+/*     box_overlap     -       does box1 overlap box2?
+ */
+long box_overlap(BOX *box1, BOX *box2)
+{
+    return(((box1->xh >= box2->xh && box1->xl <= box2->xh) ||
+           (box2->xh >= box1->xh && box2->xl <= box1->xh)) &&
+          ((box1->yh >= box2->yh && box1->yl <= box2->yh) ||
+           (box2->yh >= box1->yh && box2->yl <= box1->yh)) );
+}
+
+/*     box_overleft    -       is the right edge of box1 to the left of
+ *                             the right edge of box2?
+ *
+ *     This is "less than or equal" for the end of a time range,
+ *     when time ranges are stored as rectangles.
+ */
+long box_overleft(BOX *box1, BOX *box2)
+{
+    return(box1->xh <= box2->xh);
+}
+
+/*     box_left        -       is box1 strictly left of box2?
+ */
+long box_left(BOX *box1, BOX *box2)
+{
+    return(box1->xh < box2->xl);
+}
+
+/*     box_right       -       is box1 strictly right of box2?
+ */
+long box_right(BOX *box1, BOX *box2)
+{
+    return(box1->xl > box2->xh);
+}
+
+/*     box_overright   -       is the left edge of box1 to the right of
+ *                             the left edge of box2?
+ *
+ *     This is "greater than or equal" for time ranges, when time ranges
+ *     are stored as rectangles.
+ */
+long box_overright(BOX *box1, BOX *box2)
+{
+    return(box1->xl >= box2->xl);
+}
+
+/*     box_contained   -       is box1 contained by box2?
+ */
+long box_contained(BOX *box1, BOX *box2)
+{
+    return((box1->xh <= box2->xh && box1->xl >= box2->xl &&
+           box1->yh <= box2->yh && box1->yl >= box2->yl));
+}
+
+/*     box_contain     -       does box1 contain box2?
+ */
+long box_contain(BOX *box1, BOX *box2)
+{
+    return((box1->xh >= box2->xh && box1->xl <= box2->xl &&
+           box1->yh >= box2->yh && box1->yl <= box2->yl));
+}
+
+
+/*     box_positionop  -
+ *             is box1 entirely {above, below } box2?
+ */
+long box_below(BOX *box1, BOX *box2)
+{
+    return( box1->yh <= box2->yl );
+}
+
+long box_above(BOX *box1, BOX *box2)
+{
+    return( box1->yl >= box2->yh );
+}
+
+
+/*     box_relop       -       is area(box1) relop area(box2), within
+ *                             our accuracy constraint?
+ */
+long box_lt(BOX *box1, BOX *box2)
+{
+    return( FPlt(box_ar(box1), box_ar(box2)) );
+}
+
+long box_gt(BOX *box1, BOX *box2)
+{
+    return( FPgt(box_ar(box1), box_ar(box2)) );
+}
+
+long box_eq(BOX *box1, BOX *box2)
+{
+    return( FPeq(box_ar(box1), box_ar(box2)) );
+}
+
+long box_le(BOX        *box1, BOX *box2)
+{
+    return( FPle(box_ar(box1), box_ar(box2)) );
+}
+
+long box_ge(BOX        *box1, BOX *box2)
+{
+    return( FPge(box_ar(box1), box_ar(box2)) );
+}
+
+
+/*----------------------------------------------------------
+ *  "Arithmetic" operators on boxes.
+ *     box_foo returns foo as an object (pointer) that
+ can be passed between languages.
+ *     box_xx  is an internal routine which returns the
+ *             actual value (and cannot be handed back to
+ *             LISP).
+ *---------------------------------------------------------*/
+
+/*     box_area        -       returns the area of the box.
+ */
+double *box_area(BOX *box)
+{
+    double *result;
+    
+    result = PALLOCTYPE(double);
+    *result = box_ln(box) * box_ht(box);
+    
+    return(result);
+}
+
+
+/*     box_length      -       returns the length of the box 
+ *                               (horizontal magnitude).
+ */
+double *box_length(BOX *box)
+{
+    double     *result;
+    
+    result = PALLOCTYPE(double);
+    *result = box->xh - box->xl;
+    
+    return(result);
+}
+
+
+/*     box_height      -       returns the height of the box 
+ *                               (vertical magnitude).
+ */
+double *box_height(BOX *box)
+{
+    double     *result;
+    
+    result = PALLOCTYPE(double);
+    *result = box->yh - box->yl;
+    
+    return(result);
+}
+
+
+/*     box_distance    -       returns the distance between the
+ *                               center points of two boxes.
+ */
+double *box_distance(BOX *box1, BOX *box2)
+{
+    double     *result;
+    Point      *box_center(), *a, *b;
+    
+    result = PALLOCTYPE(double);
+    a = box_center(box1);
+    b = box_center(box2);
+    *result = HYPOT(a->x - b->x, a->y - b->y);
+    
+    PFREE(a);
+    PFREE(b);
+    return(result);
+}
+
+
+/*     box_center      -       returns the center point of the box.
+ */
+Point *box_center(BOX *box)
+{
+    Point      *result;
+    
+    result = PALLOCTYPE(Point);
+    result->x = (box->xh + box->xl) / 2.0;
+    result->y = (box->yh + box->yl) / 2.0;
+    
+    return(result);
+}
+
+
+/*     box_ar  -       returns the area of the box.
+ */
+double box_ar(BOX *box)
+{
+    return( box_ln(box) * box_ht(box) );
+}
+
+
+/*     box_ln  -       returns the length of the box 
+ *                               (horizontal magnitude).
+ */
+double box_ln(BOX *box)
+{
+    return( box->xh - box->xl );
+}
+
+
+/*     box_ht  -       returns the height of the box 
+ *                               (vertical magnitude).
+ */
+double box_ht(BOX *box)
+{
+    return( box->yh - box->yl );
+}
+
+
+/*     box_dt  -       returns the distance between the
+ *                       center points of two boxes.
+ */
+double box_dt(BOX *box1, BOX *box2)
+{
+    double     result;
+    Point      *box_center(),
+    *a, *b;
+    
+    a = box_center(box1);
+    b = box_center(box2);
+    result = HYPOT(a->x - b->x, a->y - b->y);
+    
+    PFREE(a);
+    PFREE(b);
+    return(result);
+}
+
+/*----------------------------------------------------------
+ *  Funky operations.
+ *---------------------------------------------------------*/
+
+/*     box_intersect   -
+ *             returns the overlapping portion of two boxes,
+ *               or NULL if they do not intersect.
+ */
+BOX *box_intersect(BOX *box1, BOX *box2)
+{
+    BOX        *result;
+    long       box_overlap();
+    
+    if (! box_overlap(box1,box2))
+       return(NULL);
+    result = PALLOCTYPE(BOX);
+    result->xh = Min(box1->xh, box2->xh);
+    result->xl = Max(box1->xl, box2->xl);
+    result->yh = Min(box1->yh, box2->yh);
+    result->yl = Max(box1->yl, box2->yl);
+    
+    return(result);
+}
+
+
+/*     box_diagonal    -       
+ *             returns a line segment which happens to be the
+ *               positive-slope diagonal of "box".
+ *             provided, of course, we have LSEGs.
+ */
+LSEG *box_diagonal(BOX *box)
+{
+    Point      p1, p2;
+    
+    p1.x = box->xh;
+    p1.y = box->yh;
+    p2.x = box->xl;
+    p2.y = box->yl;
+    return( lseg_construct( &p1, &p2 ) );
+    
+}
+
+/***********************************************************************
+ **
+ **    Routines for 2D lines.
+ **            Lines are not intended to be used as ADTs per se,
+ **            but their ops are useful tools for other ADT ops.  Thus,
+ **            there are few relops.
+ **
+ ***********************************************************************/
+
+/*----------------------------------------------------------
+ *  Conversion routines from one line formula to internal.
+ *     Internal form:  Ax+By+C=0
+ *---------------------------------------------------------*/
+
+LINE *                         /* point-slope */
+line_construct_pm(Point *pt, double m)
+{
+    LINE       *result;
+    
+    result = PALLOCTYPE(LINE);
+    /* use "mx - y + yinter = 0" */
+    result->A = m;
+    result->B = -1.0;
+    result->C = pt->y - m * pt->x;
+    return(result);
+}
+
+
+LINE *                         /* two points */
+line_construct_pp(Point *pt1, Point *pt2)
+{
+    LINE       *result;
+    
+    result = PALLOCTYPE(LINE);
+    if (FPeq(pt1->x, pt2->x)) {                /* vertical */
+       /* use "x = C" */
+       result->m = 0.0;
+       result->A = -1.0;
+       result->B = 0.0;
+       result->C = pt1->x;
+    } else {
+       /* use "mx - y + yinter = 0" */
+       result->m = (pt1->y - pt2->y) / (pt1->x - pt2->x);
+       result->A = result->m;
+       result->B = -1.0;
+       result->C = pt1->y - result->m * pt1->x;
+    }
+    return(result);
+}
+
+
+/*----------------------------------------------------------
+ *  Relative position routines.
+ *---------------------------------------------------------*/
+
+long line_intersect(LINE *l1, LINE *l2)
+{
+    return( ! line_parallel(l1, l2) );
+}
+
+long line_parallel(LINE *l1, LINE *l2)
+{
+    return( FPeq(l1->m, l2->m) );
+}
+
+long line_perp(LINE *l1, LINE *l2)
+{
+    if (l1->m)
+       return( FPeq(l2->m / l1->m, -1.0) );
+    else if (l2->m)
+       return( FPeq(l1->m / l2->m, -1.0) );
+    return(1); /* both 0.0 */
+}
+
+long line_vertical(LINE *line)
+{
+    return( FPeq(line->A, -1.0) && FPzero(line->B) );
+}
+
+long line_horizontal(LINE *line)
+{
+    return( FPzero(line->m) );
+}
+
+
+long line_eq(LINE *l1, LINE *l2)
+{
+    double     k;
+    
+    if (! FPzero(l2->A))
+       k = l1->A / l2->A;
+    else if (! FPzero(l2->B))
+       k = l1->B / l2->B;
+    else if (! FPzero(l2->C))
+       k = l1->C / l2->C;
+    else
+       k = 1.0;
+    return( FPeq(l1->A, k * l2->A) &&
+          FPeq(l1->B, k * l2->B) &&
+          FPeq(l1->C, k * l2->C) );
+}
+
+
+/*----------------------------------------------------------
+ *  Line arithmetic routines.
+ *---------------------------------------------------------*/
+
+double *               /* distance between l1, l2 */
+line_distance(LINE *l1, LINE *l2)
+{
+    double     *result;
+    Point      *tmp;
+    
+    result = PALLOCTYPE(double);
+    if (line_intersect(l1, l2)) {
+       *result = 0.0;
+       return(result);
+    }
+    if (line_vertical(l1))
+       *result = fabs(l1->C - l2->C);
+    else {
+       tmp = point_construct(0.0, l1->C);
+       result = dist_pl(tmp, l2);
+       PFREE(tmp);
+    }
+    return(result);
+}
+
+Point *                        /* point where l1, l2 intersect (if any) */
+line_interpt(LINE *l1, LINE *l2)
+{
+    Point      *result;
+    double     x;
+    
+    if (line_parallel(l1, l2))
+       return(NULL);
+    if (line_vertical(l1))
+       result = point_construct(l2->m * l1->C + l2->C, l1->C);
+    else if (line_vertical(l2))
+       result = point_construct(l1->m * l2->C + l1->C, l2->C);
+    else {
+       x = (l1->C - l2->C) / (l2->A - l1->A);
+       result = point_construct(x, l1->m * x + l1->C);
+    }
+    return(result);
+}
+
+/***********************************************************************
+ **
+ **    Routines for 2D paths (sequences of line segments, also
+ **            called `polylines').
+ **
+ **            This is not a general package for geometric paths, 
+ **            which of course include polygons; the emphasis here
+ **            is on (for example) usefulness in wire layout.
+ **
+ ***********************************************************************/
+
+#define        PATHALLOCSIZE(N) \
+    (long) ((unsigned) (sizeof(PATH) + \
+                       (((N)-1) > 0 ? ((N)-1) : 0) \
+                       * sizeof(Point)))
+
+/*----------------------------------------------------------
+ *  String to path / path to string conversion.
+ *     External format: 
+ *             "(closed, npts, xcoord, ycoord,... )"
+ *---------------------------------------------------------*/
+
+PATH *path_in(char *str)
+{
+    double     coord;
+    long       field[2];
+    char       *s;
+    int        ct, i;
+    PATH       *result;
+    long       pathsize;
+    
+    if (str == NULL)
+       elog(WARN, "Bad (null) path external representation");
+    
+    /* read the path header information */
+    for (i = 0, s = str; *s && i < 2 && *s != RDELIM; ++s)
+       if (*s == DELIM || (*s == LDELIM && !i))
+           field[i++] = atol(s + 1);
+    if (i < 1)
+       elog(WARN, "Bad path external representation '%s'", str);
+    pathsize = PATHALLOCSIZE(field[1]);
+    result = (PATH *)palloc(pathsize);
+    result->length = pathsize;
+    result->closed = field[0];
+    result->npts =  field[1];
+    
+    /* read the path points */
+    
+    ct = result->npts * 2;     /* two coords for every point */
+    for (i = 0;
+        *s && i < ct && *s != RDELIM; 
+        ++s) {
+       if (*s == ',') {
+           coord = atof(s + 1);
+           if (i % 2)
+               (result->p[i/2]).y = coord;
+           else
+               (result->p[i/2]).x = coord;
+           ++i;
+       }
+    }
+    if (i % 2 || i < --ct) {
+       PFREE(result);
+       elog(WARN, "Bad path external representation '%s'", str);
+    } 
+    
+    return(result);
+}
+
+
+char *path_out(PATH *path)
+{
+    char               buf[BUFSIZ + 20000], *result, *s;
+    int                i;
+    char       tmp[64];
+    
+    if (path == NULL)
+       return(NULL);
+    (void) sprintf(buf,"%c%d,%d", LDELIM, 
+                  path->closed, path->npts);
+    s = buf + strlen(buf);
+    for (i = 0; i < path->npts; ++i) {
+       (void) sprintf(tmp, ",%G,%G", 
+                      path->p[i].x, path->p[i].y);
+       (void) strcpy(s, tmp);
+       s += strlen(tmp);
+    }
+    *s++ = RDELIM;
+    *s = '\0';
+    result = (char *)PALLOC(strlen(buf) + 1);
+    (void) strcpy(result, buf);
+    
+    return(result);
+}
+
+
+/*----------------------------------------------------------
+ *  Relational operators.
+ *     These are based on the path cardinality, 
+ *     as stupid as that sounds.
+ *
+ *     Better relops and access methods coming soon.
+ *---------------------------------------------------------*/
+
+long path_n_lt(PATH *p1, PATH *p2)
+{
+    return( (p1->npts < p2->npts ) );
+}
+
+long path_n_gt(PATH *p1, PATH *p2)
+{
+    return( (p1->npts > p2->npts ) );
+}
+
+long path_n_eq(PATH *p1, PATH *p2)
+{
+    return( (p1->npts == p2->npts) );
+}
+
+long path_n_le(PATH *p1, PATH *p2)
+{
+    return( (p1->npts <= p2->npts ) );
+}
+
+long path_n_ge(PATH *p1, PATH *p2)
+{
+    return( (p1->npts >= p2->npts ) );
+}
+
+/* path_inter -
+ *     Does p1 intersect p2 at any point?
+ *     Use bounding boxes for a quick (O(n)) check, then do a 
+ *     O(n^2) iterative edge check.
+ */
+long path_inter(PATH *p1, PATH *p2)
+{
+    BOX        b1, b2;
+    int        i, j;
+    LSEG seg1, seg2;
+    
+    b1.xh = b1.yh = b2.xh = b2.yh = DBL_MAX;
+    b1.xl = b1.yl = b2.xl = b2.yl = -DBL_MAX;
+    for (i = 0; i < p1->npts; ++i) {
+       b1.xh = Max(p1->p[i].x, b1.xh);
+       b1.yh = Max(p1->p[i].y, b1.yh);
+       b1.xl = Min(p1->p[i].x, b1.xl);
+       b1.yl = Min(p1->p[i].y, b1.yl);
+    }
+    for (i = 0; i < p2->npts; ++i) {
+       b2.xh = Max(p2->p[i].x, b2.xh);
+       b2.yh = Max(p2->p[i].y, b2.yh);
+       b2.xl = Min(p2->p[i].x, b2.xl);
+       b2.yl = Min(p2->p[i].y, b2.yl);
+    }
+    if (! box_overlap(&b1, &b2))
+       return(0);
+    
+    /*  pairwise check lseg intersections */
+    for (i = 0; i < p1->npts - 1; i++) {
+       for (j = 0; j < p2->npts - 1; j++) {
+           statlseg_construct(&seg1, &p1->p[i], &p1->p[i+1]);
+           statlseg_construct(&seg2, &p2->p[j], &p2->p[j+1]);
+           if (lseg_intersect(&seg1, &seg2))
+               return(1);
+       }
+    }
+    
+    /* if we dropped through, no two segs intersected */
+    return(0);
+}
+
+/* this essentially does a cartesian product of the lsegs in the
+   two paths, and finds the min distance between any two lsegs */
+double *path_distance(PATH *p1, PATH *p2)
+{
+    double *min, *tmp;
+    int i,j;
+    LSEG seg1, seg2;
+    
+    statlseg_construct(&seg1, &p1->p[0], &p1->p[1]);
+    statlseg_construct(&seg2, &p2->p[0], &p2->p[1]);
+    min = lseg_distance(&seg1, &seg2);
+    
+    for (i = 0; i < p1->npts - 1; i++)
+       for (j = 0; j < p2->npts - 1; j++)
+           {
+               statlseg_construct(&seg1, &p1->p[i], &p1->p[i+1]);
+               statlseg_construct(&seg2, &p2->p[j], &p2->p[j+1]);
+               
+               if (*min < *(tmp = lseg_distance(&seg1, &seg2)))
+                   *min = *tmp;
+               PFREE(tmp);
+           }
+    
+    return(min);
+}
+
+
+/*----------------------------------------------------------
+ *  "Arithmetic" operations.
+ *---------------------------------------------------------*/
+
+double *path_length(PATH *path)
+{
+    double     *result;
+    int        ct, i;
+    
+    result = PALLOCTYPE(double);
+    ct = path->npts - 1;
+    for (i = 0; i < ct; ++i)
+       *result += point_dt(&path->p[i], &path->p[i+1]);
+    
+    return(result);
+}
+
+
+
+double path_ln(PATH *path)
+{
+    double     result;
+    int        ct, i;
+    
+    ct = path->npts - 1;
+    for (result = i = 0; i < ct; ++i)
+       result += point_dt(&path->p[i], &path->p[i+1]);
+    
+    return(result);
+}
+/***********************************************************************
+ **
+ **    Routines for 2D points.
+ **
+ ***********************************************************************/
+
+/*----------------------------------------------------------
+ *  String to point, point to string conversion.
+ *     External form:  "(x, y)"
+ *---------------------------------------------------------*/
+
+Point *point_in(char *str)
+{
+    char       *coord[POINTNARGS], *p, *r;
+    int        i;
+    Point      *result;
+    
+    if (str == NULL)
+       elog(WARN, "Bad (null) point external representation");
+    
+    if ((p = (char *)strchr(str, LDELIM)) == (char *)NULL)
+       elog (WARN, "Bad point external representation '%s'",str);
+    for (i = 0, p++; *p && i < POINTNARGS-1 && *p != RDELIM; p = r+1)
+       if ((r = (char *)strchr(p, DELIM)) == (char *)NULL)
+           elog (WARN, "Bad point external representation '%s'",str);
+       else    
+           coord[i++] = p;
+    if ((r = (char *)strchr(p, RDELIM)) == (char *)NULL)
+       elog (WARN, "Bad point external representation '%s'",str);
+    coord[i++] = p;
+    
+    if (i < POINTNARGS - 1)
+       elog(WARN, "Bad point external representation '%s'",str);
+    result = PALLOCTYPE(Point);
+    result->x = atof(coord[0]);
+    result->y = atof(coord[1]);
+    return(result);
+}
+
+char *point_out(Point *pt)
+{
+    char       *result;
+    
+    if (pt == NULL)
+       return(NULL);
+    result = (char *)PALLOC(40);
+    (void) sprintf(result, "(%G,%G)", pt->x, pt->y);
+    return(result);
+}
+
+
+Point *point_construct(double x, double y)
+{
+    Point      *result;
+    
+    result = PALLOCTYPE(Point);
+    result->x = x;
+    result->y = y;
+    return(result);
+}
+
+
+Point *point_copy(Point *pt)
+{
+    Point      *result;
+    
+    result = PALLOCTYPE(Point);
+    result->x = pt->x;
+    result->y = pt->y;
+    return(result);
+}
+
+
+/*----------------------------------------------------------
+ *  Relational operators for Points.
+ *     Since we do have a sense of coordinates being
+ *     "equal" to a given accuracy (point_vert, point_horiz), 
+ *     the other ops must preserve that sense.  This means
+ *     that results may, strictly speaking, be a lie (unless
+ *     EPSILON = 0.0).
+ *---------------------------------------------------------*/
+
+long point_left(Point *pt1, Point *pt2)
+{
+    return( FPlt(pt1->x, pt2->x) );
+}
+
+long point_right(Point *pt1, Point *pt2)
+{
+    return( FPgt(pt1->x, pt2->x) );
+}
+
+long point_above(Point *pt1, Point *pt2)
+{
+    return( FPgt(pt1->y, pt2->y) );
+}
+
+long point_below(Point *pt1, Point *pt2)
+{
+    return( FPlt(pt1->y, pt2->y) );
+}
+
+long point_vert(Point *pt1, Point *pt2)
+{
+    return( FPeq( pt1->x, pt2->x ) );
+}
+
+long point_horiz(Point *pt1, Point *pt2)
+{
+    return( FPeq( pt1->y, pt2->y ) );
+}
+
+long point_eq(Point *pt1, Point *pt2)
+{
+    return( point_horiz(pt1, pt2) && point_vert(pt1, pt2) );
+}
+
+/*----------------------------------------------------------
+ *  "Arithmetic" operators on points.
+ *---------------------------------------------------------*/
+
+long pointdist(Point *p1, Point *p2)
+{
+    long result;
+    
+    result = point_dt(p1, p2);
+    return(result);
+}
+
+double *point_distance(Point *pt1, Point *pt2)
+{
+    double     *result;
+    
+    result = PALLOCTYPE(double);
+    *result = HYPOT( pt1->x - pt2->x, pt1->y - pt2->y );
+    return(result);
+}
+
+
+double point_dt(Point *pt1, Point *pt2)
+{
+    return( HYPOT( pt1->x - pt2->x, pt1->y - pt2->y ) );
+}
+
+double *point_slope(Point *pt1, Point *pt2)
+{
+    double     *result;
+    
+    result = PALLOCTYPE(double);
+    if (point_vert(pt1, pt2))
+       *result = DBL_MAX;
+    else
+       *result = (pt1->y - pt2->y) / (pt1->x - pt1->x);
+    return(result);
+}
+
+
+double point_sl(Point *pt1, Point *pt2)
+{
+    return(    point_vert(pt1, pt2)
+          ? DBL_MAX
+          : (pt1->y - pt2->y) / (pt1->x - pt2->x) );
+}
+
+/***********************************************************************
+ **
+ **    Routines for 2D line segments.
+ **
+ ***********************************************************************/
+
+/*----------------------------------------------------------
+ *  String to lseg, lseg to string conversion.
+ *     External form:  "(id, info, x1, y1, x2, y2)"
+ *---------------------------------------------------------*/
+
+LSEG *lseg_in(char *str)
+{
+    char       *coord[LSEGNARGS], *p;
+    int        i;
+    LSEG       *result;
+    
+    if (str == NULL)
+       elog (WARN," Bad (null) box external representation");
+    
+    if ((p = (char *)strchr(str, LDELIM)) == (char *)NULL)
+       elog (WARN, "Bad lseg external representation '%s'",str);
+    for (i = 0, p = str; *p && i < LSEGNARGS && *p != RDELIM; p++)
+       if (*p == DELIM || (*p == LDELIM && !i))
+           coord[i++] = p + 1;
+    if (i < LSEGNARGS - 1)
+       elog (WARN, "Bad lseg external representation '%s'", str);
+    result = PALLOCTYPE(LSEG);
+    result->p[0].x = atof(coord[0]);
+    result->p[0].y = atof(coord[1]);
+    result->p[1].x = atof(coord[2]);
+    result->p[1].y = atof(coord[3]);
+    result->m = point_sl(&result->p[0], &result->p[1]);
+    
+    return(result);
+}
+
+
+char *lseg_out(LSEG *ls)
+{
+    char       *result;
+    
+    if (ls == NULL)
+       return(NULL);
+    result = (char *)PALLOC(80);
+    (void) sprintf(result, "(%G,%G,%G,%G)",
+                  ls->p[0].x, ls->p[0].y, ls->p[1].x, ls->p[1].y);
+    return(result);
+}
+
+
+/* lseg_construct -
+ *     form a LSEG from two Points.
+ */
+LSEG *lseg_construct(Point *pt1, Point *pt2)
+{
+    LSEG       *result;
+    
+    result = PALLOCTYPE(LSEG);
+    result->p[0].x = pt1->x;
+    result->p[0].y = pt1->y;
+    result->p[1].x = pt2->x;
+    result->p[1].y = pt2->y;
+    result->m = point_sl(pt1, pt2);
+    
+    return(result);
+}
+
+/* like lseg_construct, but assume space already allocated */
+void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2)
+{
+    lseg->p[0].x = pt1->x;
+    lseg->p[0].y = pt1->y;
+    lseg->p[1].x = pt2->x;
+    lseg->p[1].y = pt2->y;
+    lseg->m = point_sl(pt1, pt2);
+}
+
+/*----------------------------------------------------------
+ *  Relative position routines.
+ *---------------------------------------------------------*/
+
+/*
+ **  find intersection of the two lines, and see if it falls on 
+ **  both segments.
+ */
+long lseg_intersect(LSEG *l1, LSEG *l2)
+{
+    LINE *ln;
+    Point *interpt;
+    long retval;
+    
+    ln = line_construct_pp(&l2->p[0], &l2->p[1]);
+    interpt = interpt_sl(l1, ln);
+    
+    if (interpt != NULL && on_ps(interpt, l2)) /* interpt on l1 and l2 */
+       retval = 1;
+    else retval = 0;
+    if (interpt != NULL) PFREE(interpt);
+    PFREE(ln);
+    return(retval);
+}
+
+long lseg_parallel(LSEG *l1, LSEG *l2)
+{
+    return( FPeq(l1->m, l2->m) );
+}
+
+long lseg_perp(LSEG *l1, LSEG *l2)
+{
+    if (! FPzero(l1->m))
+       return( FPeq(l2->m / l1->m, -1.0) );
+    else if (! FPzero(l2->m))
+       return( FPeq(l1->m / l2->m, -1.0) );
+    return(0); /* both 0.0 */
+}
+
+long lseg_vertical(LSEG *lseg)
+{
+    return( FPeq(lseg->p[0].x, lseg->p[1].x) );
+}
+
+long lseg_horizontal(LSEG *lseg)
+{
+    return( FPeq(lseg->p[0].y, lseg->p[1].y) );
+}
+
+
+long lseg_eq(LSEG *l1, LSEG *l2)
+{
+    return( FPeq(l1->p[0].x, l2->p[0].x) &&
+          FPeq(l1->p[1].y, l2->p[1].y) &&
+          FPeq(l1->p[0].x, l2->p[0].x) &&
+          FPeq(l1->p[1].y, l2->p[1].y) );
+}
+
+
+/*----------------------------------------------------------
+ *  Line arithmetic routines.
+ *---------------------------------------------------------*/
+
+/* lseg_distance -
+ *     If two segments don't intersect, then the closest
+ *     point will be from one of the endpoints to the other
+ *     segment.
+ */
+double *lseg_distance(LSEG *l1, LSEG *l2)
+{
+    double     *d, *result;
+    
+    result = PALLOCTYPE(double);
+    if (lseg_intersect(l1, l2)) {
+       *result = 0.0;
+       return(result);
+    }
+    *result = DBL_MAX;
+    d = dist_ps(&l1->p[0], l2);
+    *result = Min(*result, *d);
+    PFREE(d);
+    d = dist_ps(&l1->p[1], l2);
+    *result = Min(*result, *d);
+    PFREE(d);
+    d = dist_ps(&l2->p[0], l1);
+    *result = Min(*result, *d);
+    PFREE(d);
+    d = dist_ps(&l2->p[1], l1);
+    *result = Min(*result, *d);
+    PFREE(d);
+    
+    return(result);
+}
+
+/* distance between l1, l2 */
+double lseg_dt(LSEG *l1, LSEG *l2)
+{
+    double     *d, result;
+    
+    if (lseg_intersect(l1, l2))
+       return(0.0);
+    result = DBL_MAX;
+    d = dist_ps(&l1->p[0], l2);
+    result = Min(result, *d);
+    PFREE(d);
+    d = dist_ps(&l1->p[1], l2);
+    result = Min(result, *d);
+    PFREE(d);
+    d = dist_ps(&l2->p[0], l1);
+    result = Min(result, *d);
+    PFREE(d);
+    d = dist_ps(&l2->p[1], l1);
+    result = Min(result, *d);
+    PFREE(d);
+    
+    return(result);
+}
+
+
+/* lseg_interpt -
+ *     Find the intersection point of two segments (if any).
+ *     Find the intersection of the appropriate lines; if the 
+ *     point is not on a given segment, there is no valid segment
+ *     intersection point at all.
+ */
+Point *lseg_interpt(LSEG *l1, LSEG *l2)
+{
+    Point      *result;
+    LINE       *tmp1, *tmp2;
+    
+    tmp1 = line_construct_pp(&l1->p[0], &l1->p[1]);
+    tmp2 = line_construct_pp(&l2->p[0], &l2->p[1]);
+    result = line_interpt(tmp1, tmp2);
+    if (result)
+       if (! on_ps(result, l1)) {
+           PFREE(result);
+           result = NULL;
+       }
+    
+    PFREE(tmp1);
+    PFREE(tmp2);
+    return(result);
+}
+
+/***********************************************************************
+ **
+ **    Routines for position comparisons of differently-typed
+ **            2D objects.
+ **
+ ***********************************************************************/
+
+#define        ABOVE   1
+#define        BELOW   0
+#define        UNDEF   -1
+
+
+/*---------------------------------------------------------------------
+ *     dist_
+ *             Minimum distance from one object to another.
+ *-------------------------------------------------------------------*/
+
+double *dist_pl(Point *pt, LINE *line)
+{
+    double     *result;
+    
+    result = PALLOCTYPE(double);
+    *result = (line->A * pt->x + line->B * pt->y + line->C) /
+       HYPOT(line->A, line->B);
+    
+    return(result);
+}
+
+double *dist_ps(Point *pt, LSEG *lseg)
+{
+    double m;                       /* slope of perp. */
+    LINE *ln;
+    double *result, *tmpdist;
+    Point *ip;
+    
+    /* construct a line that's perpendicular.  See if the intersection of
+       the two lines is on the line segment. */
+    if (lseg->p[1].x == lseg->p[0].x)
+       m = 0;
+    else if (lseg->p[1].y == lseg->p[0].y) /* slope is infinite */
+       m = DBL_MAX;
+    else m = (-1) * (lseg->p[1].y - lseg->p[0].y) / 
+       (lseg->p[1].x - lseg->p[0].x);
+    ln = line_construct_pm(pt, m);
+    
+    if ((ip = interpt_sl(lseg, ln)) != NULL)
+       result = point_distance(pt, ip);
+    else  /* intersection is not on line segment, so distance is min
+            of distance from point to an endpoint */
+       {
+           result = point_distance(pt, &lseg->p[0]);
+           tmpdist = point_distance(pt, &lseg->p[1]);
+           if (*tmpdist < *result) *result = *tmpdist;
+           PFREE (tmpdist);
+       }
+    
+    if (ip != NULL) PFREE(ip);
+    PFREE(ln);
+    return (result);
+}
+
+
+/*
+ ** Distance from a point to a path 
+ */
+double *dist_ppth(Point *pt, PATH *path)
+{
+    double *result;
+    double *tmp;
+    int i;
+    LSEG lseg;
+    
+    switch (path->npts) {
+    case 0:
+       result = PALLOCTYPE(double);
+       *result = Abs((double) DBL_MAX);        /* +infinity */
+       break;
+    case 1:
+       result = point_distance(pt, &path->p[0]);
+       break;
+    default:
+       /*
+        * the distance from a point to a path is the smallest distance
+        * from the point to any of its constituent segments.
+        */
+       Assert(path->npts > 1);
+       result = PALLOCTYPE(double);
+       for (i = 0; i < path->npts - 1; ++i) {
+           statlseg_construct(&lseg, &path->p[i], &path->p[i+1]);
+           tmp = dist_ps(pt, &lseg);
+           if (i == 0 || *tmp < *result)
+               *result = *tmp;
+           PFREE(tmp);
+       }
+       break;
+    }
+    return(result);
+}
+
+double *dist_pb(Point *pt, BOX *box)
+{
+    Point      *tmp;
+    double     *result;
+    
+    tmp = close_pb(pt, box);
+    result = point_distance(tmp, pt);
+    
+    PFREE(tmp);
+    return(result);
+}
+
+
+double *dist_sl(LSEG *lseg, LINE *line)
+{
+    double     *result;
+    
+    if (inter_sl(lseg, line)) {
+       result = PALLOCTYPE(double);
+       *result = 0.0;
+    } else     /* parallel */
+       result = dist_pl(&lseg->p[0], line);
+    
+    return(result);
+}
+
+
+double *dist_sb(LSEG *lseg, BOX *box)
+{
+    Point      *tmp;
+    double     *result;
+    
+    tmp = close_sb(lseg, box);
+    if (tmp == NULL) {
+       result = PALLOCTYPE(double);
+       *result = 0.0;
+    } else {
+       result = dist_pb(tmp, box);
+       PFREE(tmp);
+    }
+    
+    return(result);
+}
+
+
+double *dist_lb(LINE *line, BOX *box)
+{
+    Point      *tmp;
+    double     *result;
+    
+    tmp = close_lb(line, box);
+    if (tmp == NULL) {
+       result = PALLOCTYPE(double);
+       *result = 0.0;
+    } else {
+       result = dist_pb(tmp, box);
+       PFREE(tmp);
+    }
+    
+    return(result);
+}
+
+
+/*---------------------------------------------------------------------
+ *     interpt_
+ *             Intersection point of objects.
+ *             We choose to ignore the "point" of intersection between 
+ *               lines and boxes, since there are typically two.
+ *-------------------------------------------------------------------*/
+
+Point *interpt_sl(LSEG *lseg, LINE *line)
+{
+    LINE       *tmp;
+    Point      *p;
+    
+    tmp = line_construct_pp(&lseg->p[0], &lseg->p[1]);
+    p = line_interpt(tmp, line);
+    if (p)
+       if (! on_ps(p, lseg)) {
+           PFREE(p);
+           p = NULL;
+       }
+    
+    PFREE(tmp);
+    return(p);
+}
+
+
+/*---------------------------------------------------------------------
+ *     close_
+ *             Point of closest proximity between objects.
+ *-------------------------------------------------------------------*/
+
+/* close_pl - 
+ *     The intersection point of a perpendicular of the line 
+ *     through the point.
+ */
+Point *close_pl(Point *pt, LINE *line)
+{
+    Point      *result;
+    LINE       *tmp;
+    double     invm;
+    
+    result = PALLOCTYPE(Point);
+    if (FPeq(line->A, -1.0) && FPzero(line->B)) {      /* vertical */
+       result->x = line->C;
+       result->y = pt->y;
+       return(result);
+    } else if (FPzero(line->m)) {                      /* horizontal */
+       result->x = pt->x;
+       result->y = line->C;
+       return(result);
+    }
+    /* drop a perpendicular and find the intersection point */
+    invm = -1.0 / line->m;
+    tmp = line_construct_pm(pt, invm);
+    result = line_interpt(tmp, line);
+    return(result);
+}
+
+
+/* close_ps - 
+ *     Take the closest endpoint if the point is left, right, 
+ *     above, or below the segment, otherwise find the intersection
+ *     point of the segment and its perpendicular through the point.
+ */
+Point *close_ps(Point *pt, LSEG *lseg)
+{
+    Point      *result;
+    LINE       *tmp;
+    double     invm;
+    int        xh, yh;
+    
+    result = NULL;
+    xh = lseg->p[0].x < lseg->p[1].x;
+    yh = lseg->p[0].y < lseg->p[1].y;
+    if (pt->x < lseg->p[!xh].x)
+       result = point_copy(&lseg->p[!xh]);
+    else if (pt->x > lseg->p[xh].x)
+       result = point_copy(&lseg->p[xh]);
+    else if (pt->y < lseg->p[!yh].y)
+       result = point_copy(&lseg->p[!yh]);
+    else if (pt->y > lseg->p[yh].y)
+       result = point_copy(&lseg->p[yh]);
+    if (result)
+       return(result);
+    if (FPeq(lseg->p[0].x, lseg->p[1].x)) {    /* vertical */
+       result->x = lseg->p[0].x;
+       result->y = pt->y;
+       return(result);
+    } else if (FPzero(lseg->m)) {                      /* horizontal */
+       result->x = pt->x;
+       result->y = lseg->p[0].y;
+       return(result);
+    }
+    
+    invm = -1.0 / lseg->m;
+    tmp = line_construct_pm(pt, invm);
+    result = interpt_sl(lseg, tmp);
+    return(result);
+}
+
+Point *close_pb(Point *pt, BOX *box)
+{
+    /* think about this one for a while */
+    
+    return(NULL);
+}
+
+Point *close_sl(LSEG *lseg, LINE *line)
+{
+    Point      *result;
+    double     *d1, *d2;
+
+    result = interpt_sl(lseg, line);
+    if (result)
+       return(result);
+    d1 = dist_pl(&lseg->p[0], line);
+    d2 = dist_pl(&lseg->p[1], line);
+    if (d1 < d2)
+       result = point_copy(&lseg->p[0]);
+    else
+       result = point_copy(&lseg->p[1]);
+    
+    PFREE(d1);
+    PFREE(d2);
+    return(result);
+}
+
+Point *close_sb(LSEG *lseg, BOX *box)
+{
+    /* think about this one for a while */
+    
+    return(NULL);
+}
+
+Point *close_lb(LINE *line, BOX *box)
+{
+    /* think about this one for a while */
+    
+    return(NULL);
+}
+
+/*---------------------------------------------------------------------
+ *     on_
+ *             Whether one object lies completely within another.
+ *-------------------------------------------------------------------*/
+
+/* on_pl -
+ *     Does the point satisfy the equation? 
+ */
+long on_pl(Point *pt, LINE *line)
+{
+    return( FPzero(line->A * pt->x + line->B * pt->y + line->C) );
+}
+
+
+/* on_ps -
+ *     Determine colinearity by detecting a triangle inequality.
+ */
+long on_ps(Point *pt, LSEG *lseg)
+{
+    return( point_dt(pt, &lseg->p[0]) + point_dt(pt, &lseg->p[1])
+          == point_dt(&lseg->p[0], &lseg->p[1]) );
+}
+
+long on_pb(Point *pt, BOX *box)
+{
+    return( pt->x <= box->xh && pt->x >= box->xl &&
+          pt->y <= box->yh && pt->y >= box->yl );
+}
+
+/* on_ppath - 
+ *     Whether a point lies within (on) a polyline.
+ *     If open, we have to (groan) check each segment.
+ *     If closed, we use the old O(n) ray method for point-in-polygon.
+ *             The ray is horizontal, from pt out to the right.
+ *             Each segment that crosses the ray counts as an 
+ *             intersection; note that an endpoint or edge may touch 
+ *             but not cross.
+ *             (we can do p-in-p in lg(n), but it takes preprocessing)
+ */
+#define NEXT(A)        ((A+1) % path->npts)    /* cyclic "i+1" */
+
+long on_ppath(Point *pt, PATH *path)
+{
+    int        above, next,    /* is the seg above the ray? */
+    inter,             /* # of times path crosses ray */
+    hi,                /* index inc of higher seg (0,1) */
+    i, n;
+    double a, b, x, 
+    yh, yl, xh, xl;
+    
+    if (! path->closed) {              /*-- OPEN --*/
+       n = path->npts - 1;
+       a = point_dt(pt, &path->p[0]);
+       for (i = 0; i < n; i++) {
+           b = point_dt(pt, &path->p[i+1]);
+           if (FPeq(a+b,
+                    point_dt(&path->p[i], &path->p[i+1])))
+               return(1);
+           a = b;
+       }
+       return(0);
+    }
+    
+    inter = 0;                 /*-- CLOSED --*/
+    above = FPgt(path->p[0].y, pt->y) ? ABOVE : 
+       FPlt(path->p[0].y, pt->y) ? BELOW : UNDEF;
+    
+    for (i = 0; i < path->npts; i++) {
+       hi = path->p[i].y < path->p[NEXT(i)].y;
+       /* must take care of wrap around to original vertex for closed paths */
+       yh = (i+hi < path->npts) ? path->p[i+hi].y : path->p[0].y;
+       yl = (i+!hi < path->npts) ? path->p[i+!hi].y : path->p[0].y;
+       hi = path->p[i].x < path->p[NEXT(i)].x;
+       xh = (i+hi < path->npts) ? path->p[i+hi].x : path->p[0].x;
+       xl = (i+!hi < path->npts) ? path->p[i+!hi].x : path->p[0].x;
+       /* skip seg if it doesn't touch the ray */
+       
+       if (FPeq(yh, yl))       /* horizontal seg? */
+           if (FPge(pt->x, xl) && FPle(pt->x, xh) &&
+               FPeq(pt->y, yh))
+               return(1);      /* pt lies on seg */
+           else
+               continue;       /* skip other hz segs */
+       if (FPlt(yh, pt->y) ||  /* pt is strictly below seg */
+           FPgt(yl, pt->y))    /* strictly above */
+           continue;
+       
+       /* seg touches the ray, find out where */
+       
+       x = FPeq(xh, xl)        /* vertical seg? */
+           ? path->p[i].x      
+               : (pt->y - path->p[i].y) / 
+                   point_sl(&path->p[i],
+                            &path->p[NEXT(i)]) +
+                                path->p[i].x;
+       if (FPeq(x, pt->x))     /* pt lies on this seg */
+           return(1);
+       
+       /* does the seg actually cross the ray? */
+       
+       next = FPgt(path->p[NEXT(i)].y, pt->y) ? ABOVE : 
+           FPlt(path->p[NEXT(i)].y, pt->y) ? BELOW : above;
+       inter += FPge(x, pt->x) && next != above;
+       above = next;
+    }
+    return(    above == UNDEF ||       /* path is horizontal */
+          inter % 2);          /* odd # of intersections */
+}
+
+
+long on_sl(LSEG *lseg, LINE *line)
+{
+    return( on_pl(&lseg->p[0], line) && on_pl(&lseg->p[1], line) );
+}
+
+long on_sb(LSEG *lseg, BOX *box)
+{
+    return( on_pb(&lseg->p[0], box) && on_pb(&lseg->p[1], box) );
+}
+
+/*---------------------------------------------------------------------
+ *     inter_
+ *             Whether one object intersects another.
+ *-------------------------------------------------------------------*/
+
+long inter_sl(LSEG *lseg, LINE *line)
+{
+    Point      *tmp;
+
+    tmp = interpt_sl(lseg, line);
+    if (tmp) {
+       PFREE(tmp);
+       return(1);
+    }
+    return(0);
+}
+
+long inter_sb(LSEG *lseg, BOX *box)
+{
+    return(0);
+}
+
+long inter_lb(LINE *line, BOX *box)
+{
+    return(0);
+}
+
+/*------------------------------------------------------------------
+ * The following routines define a data type and operator class for
+ * POLYGONS .... Part of which (the polygon's bounding box is built on 
+ * top of the BOX data type.
+ *
+ * make_bound_box - create the bounding box for the input polygon
+ *------------------------------------------------------------------*/
+
+/* Maximum number of output digits printed */
+#define P_MAXDIG 12
+
+/*---------------------------------------------------------------------
+ * Make the smallest bounding box for the given polygon.
+ *---------------------------------------------------------------------*/
+void make_bound_box(POLYGON *poly)
+{
+    double x1,y1,x2,y2;
+    int npts = poly->npts;
+    
+    if (npts > 0) {
+       x1 = poly_min((double *)poly->pts, npts);
+       x2 = poly_max((double *)poly->pts, npts);
+       y1 = poly_min(((double *)poly->pts)+npts, npts),
+       y2 = poly_max(((double *)poly->pts)+npts, npts);
+       box_fill(&(poly->boundbox), x1, x2, y1, y2); 
+    }
+}
+
+/*------------------------------------------------------------------
+ * polygon_in - read in the polygon from a string specification
+ *              the string is of the form "(f8,f8,f8,f8,...,f8)"
+ *------------------------------------------------------------------*/
+POLYGON *poly_in(char *s)
+{
+    POLYGON *poly;
+    long points;
+    double *xp, *yp, strtod();
+    int i, size;
+    
+    if((points = poly_pt_count(s, ',')) < 0)
+       elog(WARN, "Bad polygon external representation '%s'", s);
+    
+    size = offsetof(POLYGON, pts[0]) + 2 * sizeof(double) * points;
+    poly = (POLYGON *) PALLOC(size);
+    memset((char *) poly, 0, size);    /* zero any holes */
+    
+    if (!PointerIsValid(poly))
+       elog(WARN, "Memory allocation failed, can't input polygon");
+    
+    poly->npts = points;
+    poly->size = size;
+    
+    /* Store all x coords followed by all y coords */
+    xp = (double *) &(poly->pts[0]);
+    yp = (double *) (poly->pts + points*sizeof(double));
+    
+    s++;                               /* skip LDELIM */
+    
+    for (i=0; i<points; i++,xp++,yp++)
+       {
+           *xp = strtod(s, &s);
+           s++;                                        /* skip delimiter */
+           *yp = strtod(s, &s);
+           s++;                                        /* skip delimiter */
+       }
+    make_bound_box(poly);
+    return (poly);
+}
+
+/*-------------------------------------------------------------
+ * poly_pt_count - count the number of points specified in the
+ *                 polygon.
+ *-------------------------------------------------------------*/
+long poly_pt_count(char *s, char delim)
+{
+    long total = 0;
+    
+    if (*s++ != LDELIM)                /* no left delimeter */
+       return (long) -1;
+    
+    while (*s && (*s != RDELIM))
+       {
+           while (*s && (*s != delim))
+               s++;
+           total++;    /* found one */
+           if (*s)
+               s++;    /* bump s past the delimiter */
+       }
+    
+    /* if there was no right delimiter OR an odd number of points */
+    
+    if ((*(s-1) != RDELIM) || ((total%2) != 0))
+       return (long) -1;
+    
+    return (total/2);
+}
+
+/*---------------------------------------------------------------
+ * poly_out - convert internal POLYGON representation to the 
+ *            character string format "(f8,f8,f8,f8,...f8)"
+ *---------------------------------------------------------------*/
+char *poly_out(POLYGON *poly)
+{
+    int i;
+    double *xp, *yp;
+    char *output, *outptr;
+    
+    /*-----------------------------------------------------
+     * Get enough space for "(f8,f8,f8,f8,...,f8)"
+     * which P_MAXDIG+1 for each coordinate plus 2
+     * for parens and 1 for the null
+     *-----------------------------------------------------*/
+    output = (char *)PALLOC(2*(P_MAXDIG+1)*poly->npts + 3);
+    outptr = output;
+    
+    if (!output)
+       elog(WARN, "Memory allocation failed, can't output polygon");
+    
+    *outptr++ = LDELIM;
+    
+    xp = (double *) poly->pts;
+    yp = (double *) (poly->pts + (poly->npts * sizeof(double)));
+    
+    sprintf(outptr, "%*g,%*g", P_MAXDIG, *xp++, P_MAXDIG, *yp++);
+    outptr += (2*P_MAXDIG + 1);
+    
+    for (i=1; i<poly->npts; i++,xp++,yp++)
+       {
+           sprintf(outptr, ",%*g,%*g", P_MAXDIG, *xp, P_MAXDIG, *yp);
+           outptr += 2*(P_MAXDIG + 1);
+       }
+    *outptr++ = RDELIM;
+    *outptr = '\0';
+    return (output);
+}
+
+/*-------------------------------------------------------
+ * Find the largest coordinate out of n coordinates
+ *-------------------------------------------------------*/
+double poly_max(double *coords, int ncoords)
+{
+    double max;
+    
+    max = *coords++;
+    ncoords--;
+    while (ncoords--)
+       {
+           if (*coords > max)
+               max = *coords;
+           coords++;
+       }
+    return max;
+}
+
+/*-------------------------------------------------------
+ * Find the smallest coordinate out of n coordinates
+ *-------------------------------------------------------*/
+double poly_min(double *coords, int ncoords)
+{
+    double min;
+    
+    min = *coords++;
+    ncoords--;
+    while (ncoords--)
+       {
+           if (*coords < min)
+               min = *coords;
+           coords++;
+       }
+    return min;
+}
+
+/*-------------------------------------------------------
+ * Is polygon A strictly left of polygon B? i.e. is
+ * the right most point of A left of the left most point
+ * of B?
+ *-------------------------------------------------------*/
+long poly_left(POLYGON *polya, POLYGON *polyb)
+{
+    double right, left;
+    
+    if (polya->npts > 0)
+       right = poly_max((double *)polya->pts, polya->npts);
+    else
+       right = polya->boundbox.xh;
+    if (polyb->npts > 0)
+       left = poly_min((double *)polyb->pts, polyb->npts);
+    else
+       left = polyb->boundbox.xl;
+    
+    return (right < left);
+}
+
+/*-------------------------------------------------------
+ * Is polygon A overlapping or left of polygon B? i.e. is
+ * the left most point of A left of the right most point
+ * of B?
+ *-------------------------------------------------------*/
+long poly_overleft(POLYGON *polya, POLYGON *polyb)
+{
+    double left, right;
+    
+    if (polya->npts > 0)
+       left = poly_min((double *)polya->pts, polya->npts);
+    else
+       left = polya->boundbox.xl;
+    if (polyb->npts > 0)
+       right = poly_max((double *)polyb->pts, polyb->npts);
+    else
+       right = polyb->boundbox.xh;
+    
+    return (left <= right);
+}
+
+/*-------------------------------------------------------
+ * Is polygon A strictly right of polygon B? i.e. is
+ * the left most point of A right of the right most point
+ * of B?
+ *-------------------------------------------------------*/
+long poly_right(POLYGON *polya, POLYGON *polyb)
+{
+    double right, left;
+    
+    if (polya->npts > 0)
+       left = poly_min((double *)polya->pts, polya->npts);
+    else
+       left = polya->boundbox.xl;
+    if (polyb->npts > 0)
+       right = poly_max((double *)polyb->pts, polyb->npts);
+    else
+       right = polyb->boundbox.xh;
+    
+    return (left > right);
+}
+
+/*-------------------------------------------------------
+ * Is polygon A overlapping or right of polygon B? i.e. is
+ * the right most point of A right of the left most point
+ * of B?
+ *-------------------------------------------------------*/
+long poly_overright(POLYGON *polya, POLYGON *polyb)
+{
+    double right, left;
+    
+    if (polya->npts > 0)
+       right = poly_max((double *)polya->pts, polya->npts);
+    else
+       right = polya->boundbox.xh;
+    if (polyb->npts > 0)
+       left = poly_min((double *)polyb->pts, polyb->npts);
+    else
+       left = polyb->boundbox.xl;
+    
+    return (right > left);
+}
+
+/*-------------------------------------------------------
+ * Is polygon A the same as polygon B? i.e. are all the
+ * points the same?
+ *-------------------------------------------------------*/
+long poly_same(POLYGON *polya, POLYGON *polyb)
+{
+    int i;
+    double *axp, *bxp; /* point to x coordinates for a and b */
+    
+    if (polya->npts != polyb->npts)
+       return 0;
+    
+    axp = (double *)polya->pts;
+    bxp = (double *)polyb->pts;
+    
+    for (i=0; i<polya->npts; axp++, bxp++, i++)
+       {
+           if (*axp != *bxp)
+               return 0;
+       }
+    return 1;
+}
+
+/*-----------------------------------------------------------------
+ * Determine if polygon A overlaps polygon B by determining if
+ * their bounding boxes overlap.
+ *-----------------------------------------------------------------*/
+long poly_overlap(POLYGON *polya, POLYGON *polyb)
+{
+    return box_overlap(&(polya->boundbox), &(polyb->boundbox));
+}
+
+/*-----------------------------------------------------------------
+ * Determine if polygon A contains polygon B by determining if A's
+ * bounding box contains B's bounding box.
+ *-----------------------------------------------------------------*/
+long poly_contain(POLYGON *polya, POLYGON *polyb)
+{
+    return box_contain(&(polya->boundbox), &(polyb->boundbox));
+}
+
+/*-----------------------------------------------------------------
+ * Determine if polygon A is contained by polygon B by determining 
+ * if A's bounding box is contained by B's bounding box.
+ *-----------------------------------------------------------------*/
+long poly_contained(POLYGON *polya, POLYGON *polyb)
+{
+    return box_contained(&(polya->boundbox), &(polyb->boundbox));
+}
diff --git a/src/backend/utils/adt/geo-selfuncs.c b/src/backend/utils/adt/geo-selfuncs.c
new file mode 100644 (file)
index 0000000..854b8c5
--- /dev/null
@@ -0,0 +1,124 @@
+/*-------------------------------------------------------------------------
+ *
+ * geo-selfuncs.c--
+ *    Selectivity routines registered in the operator catalog in the
+ *    "oprrest" and "oprjoin" attributes.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *     XXX These are totally bogus.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/attnum.h"
+#include "utils/geo-decls.h"   /* where function declarations go */
+#include "utils/palloc.h"
+
+float64
+areasel(Oid opid, 
+       Oid relid, 
+       AttrNumber attno, 
+       char *value,
+       int32 flag)
+{
+    float64    result;
+    
+    result = (float64) palloc(sizeof(float64data));
+    *result = 1.0 / 4.0;
+    return(result);
+}
+
+float64
+areajoinsel(Oid opid,
+           Oid relid,
+           AttrNumber attno,
+           char *value,
+           int32 flag)
+{
+    float64    result;
+    
+    result = (float64) palloc(sizeof(float64data));
+    *result = 1.0 / 4.0;
+    return(result);
+}
+
+/*
+ *  Selectivity functions for rtrees.  These are bogus -- unless we know
+ *  the actual key distribution in the index, we can't make a good prediction
+ *  of the selectivity of these operators.
+ *
+ *  In general, rtrees need to search multiple subtrees in order to guarantee
+ *  that all occurrences of the same key have been found.  Because of this,
+ *  the heuristic selectivity functions we return are higher than they would
+ *  otherwise be.
+ */
+
+/*
+ *  left_sel -- How likely is a box to be strictly left of (right of, above,
+ *             below) a given box?
+ */
+
+float64
+leftsel(Oid opid,
+       Oid relid,
+       AttrNumber attno,
+       char *value,
+       int32 flag)
+{
+    float64    result;
+    
+    result = (float64) palloc(sizeof(float64data));
+    *result = 1.0 / 6.0;
+    return(result);
+}
+
+float64
+leftjoinsel(Oid opid,
+           Oid relid,
+           AttrNumber attno,
+           char *value,
+           int32 flag)
+{
+    float64    result;
+    
+    result = (float64) palloc(sizeof(float64data));
+    *result = 1.0 / 6.0;
+    return(result);
+}
+
+/*
+ *  contsel -- How likely is a box to contain (be contained by) a given box?
+ */
+float64
+contsel(Oid opid,
+       Oid relid,
+       AttrNumber attno,
+       char *value,
+       int32 flag)
+{
+    float64    result;
+    
+    result = (float64) palloc(sizeof(float64data));
+    *result = 1.0 / 10.0;
+    return(result);
+}
+
+float64
+contjoinsel(Oid opid,
+           Oid relid,
+           AttrNumber attno,
+           char *value,
+           int32 flag)
+{
+    float64    result;
+    
+    result = (float64) palloc(sizeof(float64data));
+    *result = 1.0 / 10.0;
+    return(result);
+}
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
new file mode 100644 (file)
index 0000000..94a5e08
--- /dev/null
@@ -0,0 +1,343 @@
+/*-------------------------------------------------------------------------
+ *
+ * int.c--
+ *    Functions for the built-in integer types.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * OLD COMMENTS
+ *     I/O routines:
+ *      int2in, int2out, int28in, int28out, int4in, int4out
+ *     Conversion routines:
+ *      itoi
+ *     Boolean operators:
+ *      inteq, intne, intlt, intle, intgt, intge
+ *     Arithmetic operators:
+ *      intpl, intmi, int4mul, intdiv
+ *
+ *     Arithmetic operators:
+ *      intmod, int4fac
+ *
+ * XXX makes massive and possibly unwarranted type promotion assumptions.
+ * fix me when we figure out what we want to do about ANSIfication...
+ */
+
+#include "postgres.h"
+#include "fmgr.h"
+#include "utils/builtins.h"    /* where the declarations go */
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+/***************************************************************************** 
+ *   USER I/O ROUTINES                                                       *
+ *****************************************************************************/
+
+/*
+ *     int2in          - converts "num" to short
+ */
+int32 int2in(char *num)
+{
+    return((int32) pg_atoi(num, sizeof(int16), '\0'));
+}
+
+/*
+ *     int2out         - converts short to "num"
+ */
+char *int2out(int16 sh)
+{
+    char               *result;
+    
+    result = (char *)palloc(7);        /* assumes sign, 5 digits, '\0' */
+    itoa((int) sh, result);
+    return(result);
+}
+
+/*
+ *     int28in         - converts "num num ..." to internal form
+ *
+ *     Note:
+ *             Fills any nonexistent digits with NULLs.
+ */
+int16 *int28in(char *shs)
+{
+    register int16     (*result)[];
+    int                nums;
+    
+    if (shs == NULL)
+       return(NULL);
+    result = (int16 (*)[]) palloc(sizeof(int16 [8]));
+    if ((nums = sscanf(shs, "%hd%hd%hd%hd%hd%hd%hd%hd",
+                      *result,
+                      *result + 1,
+                      *result + 2,
+                      *result + 3,
+                      *result + 4,
+                      *result + 5,
+                      *result + 6,
+                      *result + 7)) != 8) {
+       do
+           (*result)[nums++] = 0;
+       while (nums < 8);
+    }
+    return((int16 *) result);
+}
+
+/*
+ *     int28out        - converts internal form to "num num ..."
+ */
+char *int28out(int16 (*shs)[])
+{
+    register int       num;
+    register int16     *sp;
+    register char      *rp;
+    char               *result;
+    
+    if (shs == NULL) {
+       result = (char *)palloc(2);
+       result[0] = '-';
+       result[1] = '\0';
+       return(result);
+    }
+    rp = result = (char *)palloc(8 * 7); /* assumes sign, 5 digits, ' ' */
+    sp = *shs;
+    for (num = 8; num != 0; num--) {
+       itoa(*sp++, rp);
+       while (*++rp != '\0')
+           ;
+       *rp++ = ' ';
+    }
+    *--rp = '\0';
+    return(result);
+}
+
+/*
+ *     int28in         - converts "num num ..." to internal form
+ *
+ *     Note:
+ *             Fills any nonexistent digits with NULLs.
+ */
+int32 *int44in(char *input_string)
+{
+    int32 *foo = (int32 *)palloc(4*sizeof(int32));
+    register int i = 0;
+    
+    i = sscanf(input_string,
+              "%d, %d, %d, %d",
+              &foo[0],
+              &foo[1],
+              &foo[2],
+              &foo[3]);
+    while (i < 4)
+       foo[i++] = 0;
+    
+    return(foo);
+}
+
+/*
+ *     int28out        - converts internal form to "num num ..."
+ */
+char *int44out(int32 an_array[])
+{
+    int temp = 4;
+    char *output_string = NULL;
+    int i;
+    
+    if ( temp > 0 ) {
+       char *walk;
+       output_string = (char *)palloc(16*temp); /* assume 15 digits + sign */
+       walk = output_string;
+       for ( i = 0 ; i < temp ; i++ ) {
+           itoa(an_array[i],walk);
+           while (*++walk != '\0')
+               ;
+           *walk++ = ' ';
+       }
+       *--walk = '\0';
+    }
+    return(output_string);
+}
+
+
+/***************************************************************************** 
+ *   PUBLIC ROUTINES                                                         *
+ *****************************************************************************/
+
+/*
+ *     int4in          - converts "num" to int4
+ */
+int32 int4in(char *num)
+{
+    return(pg_atoi(num, sizeof(int32), '\0'));
+}
+
+/*
+ *     int4out         - converts int4 to "num"
+ */
+char *int4out(int32 l)
+{
+    char               *result;
+    
+    result = (char *)palloc(12);       /* assumes sign, 10 digits, '\0' */
+    ltoa(l, result);
+    return(result);
+}
+
+
+/*
+ *     ===================
+ *     CONVERSION ROUTINES
+ *     ===================
+ */
+
+int32 i2toi4(int16 arg1)
+{
+    return((int32) arg1);
+}
+
+int16 i4toi2(int32 arg1)
+{
+    if (arg1< -0x8000)
+       elog(NOTICE, "i4toi2: \"%d\" causes int2 underflow", arg1);
+    if (arg1 > 0x7FFF)
+       elog(NOTICE, "i4toi2: \"%d\" causes int2 overflow", arg1);
+    
+    return((int16) arg1);
+}
+
+
+/*
+ *     =========================
+ *     BOOLEAN OPERATOR ROUTINES
+ *     =========================
+ */
+
+/*
+ *     inteq           - returns 1 iff arg1 == arg2
+ *     intne           - returns 1 iff arg1 != arg2
+ *     intlt           - returns 1 iff arg1 < arg2
+ *     intle           - returns 1 iff arg1 <= arg2
+ *     intgt           - returns 1 iff arg1 > arg2
+ *     intge           - returns 1 iff arg1 >= arg2
+ */
+int32 int4eq(int32 arg1, int32 arg2) { return(arg1 == arg2); }
+int32 int4ne(int32 arg1, int32 arg2) { return(arg1 != arg2); }
+int32 int4lt(int32 arg1, int32 arg2) { return(arg1 < arg2); }
+int32 int4le(int32 arg1, int32 arg2) { return(arg1 <= arg2); }
+int32 int4gt(int32 arg1, int32 arg2) { return(arg1 > arg2); } 
+int32 int4ge(int32 arg1, int32 arg2) { return(arg1 >= arg2); }
+
+int32 int2eq(int16 arg1, int16 arg2) { return(arg1 == arg2); }
+int32 int2ne(int16 arg1, int16 arg2) { return(arg1 != arg2); }
+int32 int2lt(int16 arg1, int16 arg2) { return(arg1 < arg2); }
+int32 int2le(int16 arg1, int16 arg2) { return(arg1 <= arg2); }
+int32 int2gt(int16 arg1, int16 arg2) { return(arg1 > arg2); }
+int32 int2ge(int16 arg1, int16 arg2) { return(arg1 >= arg2); }
+
+int32 int24eq(int32 arg1, int32 arg2) { return(arg1 == arg2); }
+int32 int24ne(int32 arg1, int32 arg2) { return(arg1 != arg2); }
+int32 int24lt(int32 arg1, int32 arg2) { return(arg1 < arg2); }
+int32 int24le(int32 arg1, int32 arg2) { return(arg1 <= arg2); }
+int32 int24gt(int32 arg1, int32 arg2) { return(arg1 > arg2); }
+int32 int24ge(int32 arg1, int32 arg2) { return(arg1 >= arg2); }
+
+int32 int42eq(int32 arg1, int32 arg2) { return(arg1 == arg2); }
+int32 int42ne(int32 arg1, int32 arg2) { return(arg1 != arg2); }
+int32 int42lt(int32 arg1, int32 arg2) { return(arg1 < arg2); }
+int32 int42le(int32 arg1, int32 arg2) { return(arg1 <= arg2); }
+int32 int42gt(int32 arg1, int32 arg2) { return(arg1 > arg2); } 
+int32 int42ge(int32 arg1, int32 arg2) { return(arg1 >= arg2); }
+
+
+int32 keyfirsteq(int16 *arg1, int16 arg2) { return(*arg1 == arg2); }
+
+/*
+ *     int[24]pl       - returns arg1 + arg2
+ *     int[24]mi       - returns arg1 - arg2
+ *     int[24]mul      - returns arg1 * arg2
+ *     int[24]div      - returns arg1 / arg2
+ */
+int32 int4um(int32 arg)        { return(-arg); }
+int32 int4pl(int32 arg1, int32 arg2)  { return(arg1 + arg2); }
+int32 int4mi(int32 arg1, int32 arg2)  { return(arg1 - arg2); }
+int32 int4mul(int32 arg1, int32 arg2) { return(arg1 * arg2); }
+int32 int4div(int32 arg1, int32 arg2) { return(arg1 / arg2); }
+int32 int4inc(int32 arg) { return(arg + (int32)1); }
+     
+int16 int2um(int16 arg) { return(-arg); }
+int16 int2pl(int16 arg1, int16 arg2)   { return(arg1 + arg2); }
+int16 int2mi(int16 arg1, int16 arg2)   { return(arg1 - arg2); }
+int16 int2mul(int16 arg1, int16 arg2)  { return(arg1 * arg2); }
+int16 int2div(int16 arg1, int16 arg2)  { return(arg1 / arg2); }
+int16 int2inc(int16 arg)               { return(arg + (int16)1); }
+     
+int32 int24pl(int32 arg1, int32 arg2)  { return(arg1 + arg2); }
+int32 int24mi(int32 arg1, int32 arg2)  { return(arg1 - arg2); }
+int32 int24mul(int32 arg1, int32 arg2) { return(arg1 * arg2); }
+int32 int24div(int32 arg1, int32 arg2) { return(arg1 / arg2); }
+
+int32 int42pl(int32 arg1, int32 arg2)  { return(arg1 + arg2); }
+int32 int42mi(int32 arg1, int32 arg2)  { return(arg1 - arg2); }
+int32 int42mul(int32 arg1, int32 arg2) { return(arg1 * arg2); }
+int32 int42div(int32 arg1, int32 arg2) { return(arg1 / arg2); }
+     
+/*
+ *     int[24]mod      - returns arg1 mod arg2
+ */
+int32 int4mod(int32 arg1, int32 arg2)  { return(arg1 % arg2); }
+int32 int2mod(int16 arg1, int16 arg2)  { return(arg1 % arg2); }
+int32 int24mod(int32 arg1, int32 arg2) { return(arg1 % arg2); }
+int32 int42mod(int32 arg1, int32 arg2) { return(arg1 % arg2); }
+     
+/*
+ *     int[24]fac      - returns arg1!
+ */
+int32 int4fac(int32 arg1)
+{
+    int32      result;
+    
+    if (arg1 < 1)
+       result = 0;
+    else 
+       for (result = 1; arg1 > 0; --arg1)
+           result *= arg1;
+    return(result);
+}
+
+int32 int2fac(int16 arg1)
+{
+    int16      result;
+    
+    if (arg1 < 1)
+       result = 0;
+    else 
+       for (result = 1; arg1 > 0; --arg1)
+           result *= arg1;
+    return(result);
+}
+
+int16 int2larger(int16 arg1, int16 arg2)
+{
+    return ((arg1 > arg2) ? arg1 : arg2);
+}
+
+int16 int2smaller(int16 arg1, int16 arg2)
+{
+    return ((arg1 < arg2) ? arg1 : arg2);
+}
+
+int32 int4larger(int32 arg1, int32 arg2)
+{
+    return ((arg1 > arg2) ? arg1 : arg2);
+}
+
+int32 int4smaller(int32 arg1, int32 arg2)
+{
+    return ((arg1 < arg2) ? arg1 : arg2);
+}
diff --git a/src/backend/utils/adt/like.c b/src/backend/utils/adt/like.c
new file mode 100644 (file)
index 0000000..e33e66e
--- /dev/null
@@ -0,0 +1,225 @@
+/*-------------------------------------------------------------------------
+ *
+ * like.c--
+ *    like expression handling code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    /usr/local/devel/pglite/cvs/src/backend/utils/adt/like.c,v 1.1 1995/07/30 23:55:36 emkxp01 Exp
+ *
+ *
+ *   NOTES
+ *     A big hack of the regexp.c code!! Contributed by
+ *     Keith Parks <[email protected]> (7/95).
+ *
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"          /* postgres system include file */
+#include "utils/elog.h"                /* for logging postgres errors */
+#include "utils/palloc.h"
+#include "utils/builtins.h"    /* where the function declarations go */
+
+int like(char *text, char *p);
+
+/*
+ *  interface routines called by the function manager
+ */
+
+/*
+   fixedlen_like:
+
+   a generic fixed length like routine
+         s      - the string to match against  (not necessarily null-terminated)
+        p         - the pattern
+        charlen   - the length of the string
+*/
+static bool 
+fixedlen_like(char *s, struct varlena* p, int charlen)
+{
+    char *sterm, *pterm;
+    int result;
+
+    if (!s || !p)
+       return FALSE;
+    
+    /* be sure sterm is null-terminated */
+    sterm = (char *) palloc(charlen + 1);
+    memset(sterm, 0, charlen + 1);
+    strncpy(sterm, s, charlen);
+    
+    /* p is a text = varlena, not a string so we have to make 
+     * a string from the vl_data field of the struct. */
+    
+    /* palloc the length of the text + the null character */
+    pterm = (char *) palloc(VARSIZE(p) - VARHDRSZ + 1);
+    memmove(pterm, VARDATA(p), VARSIZE(p) - VARHDRSZ);
+    *(pterm + VARSIZE(p) - VARHDRSZ) = (char)NULL;
+    
+    /* do the regexp matching */
+    result = like(sterm, pterm);
+    
+    pfree(sterm);
+    pfree(pterm);
+    
+    return ((bool) result);
+}
+
+bool 
+char2like(uint16 arg1, struct varlena *p)
+{
+    char *s = (char *) &arg1;
+    return (fixedlen_like(s, p, 2));
+}    
+
+bool 
+char2nlike(uint16 arg1, struct varlena *p)
+{
+    return (!char2like(arg1, p));
+}
+
+bool 
+char4like(uint32 arg1, struct varlena *p)
+{
+    char *s = (char *) &arg1;
+    return (fixedlen_like(s, p, 4));
+}
+
+bool 
+char4nlike(uint32 arg1, struct varlena *p)
+{
+    return (!char4like(arg1, p));
+}
+
+bool 
+char8like(char *s, struct varlena *p)
+{
+    return (fixedlen_like(s, p, 8));
+}
+
+bool 
+char8nlike(char *s, struct varlena *p)
+{
+    return (!char8like(s, p));
+}
+
+bool 
+char16like(char *s, struct varlena *p)
+{
+    return (fixedlen_like(s, p, 16));
+}
+bool 
+char16nlike(char *s, struct varlena *p)
+{
+    return (!char16like(s, p));
+}
+
+bool 
+namelike(NameData *n, struct varlena *p)
+{
+    return (fixedlen_like(n->data, p, NAMEDATALEN));
+}
+
+bool 
+namenlike(NameData *s, struct varlena *p)
+{
+    return (!namelike(s, p));
+}
+
+bool 
+textlike(struct varlena *s, struct varlena *p)
+{
+    return (fixedlen_like(VARDATA(s), p, VARSIZE(s) - VARHDRSZ));
+}
+
+bool textnlike(struct varlena *s, struct varlena *p)
+{
+    return (!textlike(s, p));
+}
+
+
+/*  $Revision: 1.1.1.1 $
+**  "like.c" A first attempt at a LIKE operator for Postgres95.
+**
+**  Originally written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
+**  Rich $alz is now <[email protected]>.
+**  Special thanks to Lars Mathiesen <[email protected]> for the LABORT code.
+** 
+**  This code was shamelessly stolen from the "pql" code by myself and
+**  slightly modified :)
+** 
+**  All references to the word "star" were replaced by "percent"
+**  All references to the word "wild" were replaced by "like"
+** 
+**  All the nice shell RE matching stuff was replaced by just "_" and "%"
+** 
+**  As I don't have a copy of the SQL standard handy I wasn't sure whether
+**  to leave in the '\' escape character handling. (I suspect the standard
+**  handles "%%" as a single literal percent)
+**
+**  Keith Parks. <[email protected]>
+**
+**  [SQL92 lets you specify the escape character by saying
+**   LIKE <pattern> ESCAPE <escape character>. We are a small operation
+**   so we force you to use '\'. - ay 7/95]
+**
+*/
+
+#define LIKE_TRUE                      1
+#define LIKE_FALSE                     0
+#define LIKE_ABORT                     -1
+
+/*
+**  Match text and p, return LIKE_TRUE, LIKE_FALSE, or LIKE_ABORT.
+*/
+static int
+DoMatch(register char *text, register char *p)
+{
+    register int       matched;
+
+    for ( ; *p; text++, p++) {
+       if (*text == '\0' && *p != '%')
+           return LIKE_ABORT;
+       switch (*p) {
+       case '\\':
+           /* Literal match with following character. */
+           p++;
+           /* FALLTHROUGH */
+       default:
+           if (*text != *p)
+               return LIKE_FALSE;
+           continue;
+       case '_':
+           /* Match anything. */
+           continue;
+       case '%':
+           while (*++p == '%')
+               /* Consecutive percents act just like one. */
+               continue;
+           if (*p == '\0')
+               /* Trailing percent matches everything. */
+               return LIKE_TRUE;
+           while (*text)
+               if ((matched = DoMatch(text++, p)) != LIKE_FALSE)
+                   return matched;
+           return LIKE_ABORT;
+       }
+    }
+
+    return *text == '\0';
+}
+
+
+/*
+**  User-level routine.  Returns TRUE or FALSE.
+*/
+int
+like(char *text, char *p)
+{
+    if (p[0] == '%' && p[1] == '\0')
+       return TRUE;
+    return (DoMatch(text, p) == LIKE_TRUE);
+}
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
new file mode 100644 (file)
index 0000000..f6ef056
--- /dev/null
@@ -0,0 +1,96 @@
+/*-------------------------------------------------------------------------
+ *
+ * misc.c--
+ *    
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <sys/file.h>
+#include "postgres.h"
+#include "utils/datum.h"
+#include "catalog/pg_type.h"
+#include "utils/builtins.h"
+
+#if !defined(PORTNAME_linux) && !defined(PORTNAME_BSD44_derived) && \
+    !defined(PORTNAME_irix5) && !defined(PORTNAME_bsdi) && !defined(PORTNAME_aix)
+extern int random();
+extern int srandom(unsigned);
+#endif
+
+
+/*-------------------------------------------------------------------------
+ * Check if data is Null 
+ */
+bool
+NullValue(Datum value, bool *isNull)
+{
+    if (*isNull) {
+       *isNull = false;
+       return(true);
+    }
+    return(false);
+    
+}
+
+/*----------------------------------------------------------------------*
+ *     check if data is not Null                                        *
+ *--------------------------------------------------------------------- */
+bool
+NonNullValue(Datum value, bool *isNull)
+{
+    if (*isNull) {
+       *isNull = false;
+       return(false);
+    }
+    return(true);
+    
+}
+
+/*
+ * oidrand (oid o, int4 X)-
+ *    takes in an oid and a int4 X, and will return 'true'
+ *  about 1/X of the time.
+ *    Useful for doing random sampling or subsetting.
+ *  if X == 0, this will always return true;
+ *
+ * Example use:
+ *     select * from TEMP where oidrand(TEMP.oid, 10)
+ * will return about 1/10 of the tuples in TEMP
+ *
+ */
+bool 
+oidrand(Oid o, int32 X)
+{
+    bool result;
+
+    if (X == 0) return true;
+
+    result = (random() % X == 0);
+    return result;
+}
+
+/*
+   oidsrand(int32 X) -
+      seeds the random number generator
+      always return true
+*/ 
+bool
+oidsrand(int32 X)
+{
+    srand(X);
+    return true;
+}
+
+
+
+int32
+userfntest(int i)
+{
+    return (i);
+}
diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c
new file mode 100644 (file)
index 0000000..4d670d9
--- /dev/null
@@ -0,0 +1,866 @@
+/*-------------------------------------------------------------------------
+ *
+ * nabstime.c--
+ *    parse almost any absolute date getdate(3) can (& some it can't)
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include "postgres.h"
+#include "access/xact.h"
+#include "utils/nabstime.h"
+#include "utils/palloc.h"
+
+#define MAXDATEFIELDS 25
+
+#define ISSPACE(c) ((c) == ' ' || (c) == '\n' || (c) == '\t')
+
+/* this is fast but dirty.  note the return's in the middle. */
+#define GOBBLE_NUM(cp, c, x, ip) \
+       (c) = *(cp)++; \
+       if ((c) < '0' || (c) > '9') \
+               return -1;              /* missing digit */ \
+       (x) = (c) - '0'; \
+       (c) = *(cp)++; \
+       if ((c) >= '0' && (c) <= '9') { \
+               (x) = 10*(x) + (c) - '0'; \
+               (c) = *(cp)++; \
+       } \
+       if ((c) != ':' && (c) != '\0' && !ISSPACE(c)) \
+               return -1;              /* missing colon */ \
+       *(ip) = (x)                     /* N.B.: no semi-colon here */
+
+#define EPOCH 1970
+#define DAYS_PER_400YRS        (time_t)146097
+#define DAYS_PER_4YRS  (time_t)1461
+#define SECS_PER_DAY   86400
+#define SECS_PER_HOUR  3600
+#define DIVBY4(n) ((n) >> 2)
+#define YRNUM(c, y) (DIVBY4(DAYS_PER_400YRS*(c)) + DIVBY4(DAYS_PER_4YRS*(y)))
+#define DAYNUM(c,y,mon,d)      (YRNUM((c), (y)) + mdays[mon] + (d))
+#define EPOCH_DAYNUM   DAYNUM(19, 69, 10, 1)   /* really January 1, 1970 */
+#define MIN_DAYNUM -24856                      /* December 13, 1901 */
+#define MAX_DAYNUM 24854                       /* January 18, 2038 */
+
+/* definitions for squeezing values into "value" */
+#define ABS_SIGNBIT 0200
+#define VALMASK 0177
+#define NEG(n)         ((n)|ABS_SIGNBIT)
+#define SIGNEDCHAR(c)  ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
+#define FROMVAL(tp)    (-SIGNEDCHAR((tp)->value) * 10) /* uncompress */
+#define TOVAL(tp, v)   ((tp)->value = ((v) < 0? NEG((-(v))/10): (v)/10))
+#define IsLeapYear(yr) ((yr%4) == 0)
+
+char nmdays[] = {
+       0, 31, 28, 31,  30, 31, 30,  31, 31, 30,  31, 30, 31
+};
+/* days since start of year. mdays[0] is March, mdays[11] is February */
+static short mdays[] = {
+       0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337
+};
+
+/* exports */
+static int dtok_numparsed;
+
+/*
+ * to keep this table reasonably small, we divide the lexval for TZ and DTZ
+ * entries by 10 and truncate the text field at MAXTOKLEN characters.
+ * the text field is not guaranteed to be NUL-terminated.
+ */
+static datetkn datetktbl[] = {
+/*     text            token   lexval */
+{      "acsst",        DTZ,    63},            /* Cent. Australia */
+{      "acst",         TZ,     57},            /* Cent. Australia */
+{      "adt",          DTZ,    NEG(18)},       /* Atlantic Daylight Time */
+{      "aesst",        DTZ,    66},            /* E. Australia */
+{      "aest",         TZ,     60},            /* Australia Eastern Std Time */
+{      "ahst",         TZ,     60},            /* Alaska-Hawaii Std Time */
+{      "am",           AMPM,   AM},
+{      "apr",          MONTH,  4},
+{      "april",        MONTH,  4},
+{      "ast",          TZ,     NEG(24)},       /* Atlantic Std Time (Canada) */
+{      "at",           PG_IGNORE,      0},             /* "at" (throwaway) */
+{      "aug",          MONTH,  8},
+{      "august",       MONTH,  8},
+{      "awsst",        DTZ,    54},            /* W. Australia */
+{      "awst",         TZ,     48},            /* W. Australia */
+{      "bst",          TZ,     6},             /* British Summer Time */
+{      "bt",           TZ,     18},            /* Baghdad Time */
+{      "cadt",         DTZ,    63},            /* Central Australian DST */
+{      "cast",         TZ,     57},            /* Central Australian ST */
+{      "cat",          TZ,     NEG(60)},       /* Central Alaska Time */
+{      "cct",          TZ,     48},            /* China Coast */
+{      "cdt",          DTZ,    NEG(30)},       /* Central Daylight Time */
+{      "cet",          TZ,     6},             /* Central European Time */
+{      "cetdst",       DTZ,    12},            /* Central European Dayl.Time */
+{      "cst",          TZ,     NEG(36)},       /* Central Standard Time */
+{      "dec",          MONTH,  12},
+{      "decemb",       MONTH,  12},
+{      "dnt",          TZ,     6},             /* Dansk Normal Tid */
+{      "dst",          PG_IGNORE,      0},
+{      "east",         TZ,     NEG(60)},       /* East Australian Std Time */
+{      "edt",          DTZ,    NEG(24)},       /* Eastern Daylight Time */
+{      "eet",          TZ,     12},            /* East. Europe, USSR Zone 1 */
+{      "eetdst",       DTZ,    18},            /* Eastern Europe */
+{      "est",          TZ,     NEG(30)},       /* Eastern Standard Time */
+{      "feb",          MONTH,  2},
+{      "februa",       MONTH,  2},
+{      "fri",          PG_IGNORE,      5},
+{      "friday",       PG_IGNORE,      5},
+{      "fst",          TZ,     6},             /* French Summer Time */
+{      "fwt",          DTZ,    12},            /* French Winter Time  */
+{      "gmt",          TZ,     0},             /* Greenwish Mean Time */
+{      "gst",          TZ,     60},            /* Guam Std Time, USSR Zone 9 */
+{      "hdt",          DTZ,    NEG(54)},       /* Hawaii/Alaska */
+{      "hmt",          DTZ,    18},            /* Hellas ? ? */
+{      "hst",          TZ,     NEG(60)},       /* Hawaii Std Time */
+{      "idle",         TZ,     72},            /* Intl. Date Line, East */
+{      "idlw",         TZ,     NEG(72)},       /* Intl. Date Line, West */
+{      "ist",          TZ,     12},            /* Israel */
+{      "it",           TZ,     22},            /* Iran Time */
+{      "jan",          MONTH,  1},
+{      "januar",       MONTH,  1},
+{      "jst",          TZ,     54},            /* Japan Std Time,USSR Zone 8 */
+{      "jt",           TZ,     45},            /* Java Time */
+{      "jul",          MONTH,  7},
+{      "july",         MONTH,  7},
+{      "jun",          MONTH,  6},
+{      "june",         MONTH,  6},
+{      "kst",          TZ,     54},            /* Korea Standard Time */
+{      "ligt",         TZ,     60},            /* From Melbourne, Australia */
+{      "mar",          MONTH,  3},
+{      "march",        MONTH,  3},
+{      "may",          MONTH,  5},
+{      "mdt",          DTZ,    NEG(36)},       /* Mountain Daylight Time */
+{      "mest",         DTZ,    12},            /* Middle Europe Summer Time */
+{      "met",          TZ,     6},             /* Middle Europe Time */
+{      "metdst",       DTZ,    12},            /* Middle Europe Daylight Time*/
+{      "mewt",         TZ,     6},             /* Middle Europe Winter Time */
+{      "mez",          TZ,     6},             /* Middle Europe Zone */
+{      "mon",          PG_IGNORE,      1},
+{      "monday",       PG_IGNORE,      1},
+{      "mst",          TZ,     NEG(42)},       /* Mountain Standard Time */
+{      "mt",           TZ,     51},            /* Moluccas Time */
+{      "ndt",          DTZ,    NEG(15)},       /* Nfld. Daylight Time */
+{      "nft",          TZ,     NEG(21)},       /* Newfoundland Standard Time */
+{      "nor",          TZ,     6},             /* Norway Standard Time */
+{      "nov",          MONTH,  11},
+{      "novemb",       MONTH,  11},
+{      "nst",          TZ,     NEG(21)},       /* Nfld. Standard Time */
+{      "nt",           TZ,     NEG(66)},       /* Nome Time */
+{      "nzdt",         DTZ,    78},            /* New Zealand Daylight Time */
+{      "nzst",         TZ,     72},            /* New Zealand Standard Time */
+{      "nzt",          TZ,     72},            /* New Zealand Time */
+{      "oct",          MONTH,  10},
+{      "octobe",       MONTH,  10},
+{      "on",           PG_IGNORE,      0},             /* "on" (throwaway) */
+{      "pdt",          DTZ,    NEG(42)},       /* Pacific Daylight Time */
+{      "pm",           AMPM,   PM},
+{      "pst",          TZ,     NEG(48)},       /* Pacific Standard Time */
+{      "sadt",         DTZ,    63},            /* S. Australian Dayl. Time */
+{      "sast",         TZ,     57},            /* South Australian Std Time */
+{      "sat",          PG_IGNORE,      6},
+{      "saturd",       PG_IGNORE,      6},
+{      "sep",          MONTH,  9},
+{      "sept",         MONTH,  9},
+{      "septem",       MONTH,  9},
+{      "set",          TZ,     NEG(6)},        /* Seychelles Time ?? */
+{      "sst",          DTZ,    12},            /* Swedish Summer Time */
+{      "sun",          PG_IGNORE,      0},
+{      "sunday",       PG_IGNORE,      0},
+{      "swt",          TZ,     6},             /* Swedish Winter Time  */
+{      "thu",          PG_IGNORE,      4},
+{      "thur",         PG_IGNORE,      4},
+{      "thurs",        PG_IGNORE,      4},
+{      "thursd",       PG_IGNORE,      4},
+{      "tue",          PG_IGNORE,      2},
+{      "tues",         PG_IGNORE,      2},
+{      "tuesda",       PG_IGNORE,      2},
+{      "ut",           TZ,     0},
+{      "utc",          TZ,     0},
+{      "wadt",         DTZ,    48},            /* West Australian DST */
+{      "wast",         TZ,     42},            /* West Australian Std Time */
+{      "wat",          TZ,     NEG(6)},        /* West Africa Time */
+{      "wdt",          DTZ,    54},            /* West Australian DST */
+{      "wed",          PG_IGNORE,      3},
+{      "wednes",       PG_IGNORE,      3},
+{      "weds",         PG_IGNORE,      3},
+{      "wet",          TZ,     0},             /* Western Europe */
+{      "wetdst",       DTZ,    6},             /* Western Europe */
+{      "wst",          TZ,     48},            /* West Australian Std Time */
+{      "ydt",          DTZ,    NEG(48)},       /* Yukon Daylight Time */
+{      "yst",          TZ,     NEG(54)},       /* Yukon Standard Time */
+{      "zp4",          TZ,     NEG(24)},       /* GMT +4  hours. */
+{      "zp5",          TZ,     NEG(30)},       /* GMT +5  hours. */
+{      "zp6",          TZ,     NEG(36)},       /* GMT +6  hours. */
+};
+
+static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
+
+/*
+ * parse and convert absolute date in timestr (the normal interface)
+ *
+ * Returns the number of seconds since epoch (January 1 1970 GMT)
+ */
+AbsoluteTime
+nabstimein(char* timestr)
+{
+    int tz = 0;
+    struct tm date;
+    
+    if (!timestr)
+       return INVALID_ABSTIME;
+    while (ISSPACE(*timestr))
+       ++timestr;
+    
+    if (!strcasecmp(timestr, "epoch"))
+       return EPOCH_ABSTIME;
+    if (!strcasecmp(timestr, "now"))
+       return GetCurrentTransactionStartTime();
+    if (!strcasecmp(timestr, "current"))
+       return CURRENT_ABSTIME;
+    if (!strcasecmp(timestr, "infinity"))
+       return NOEND_ABSTIME;
+    if (!strcasecmp(timestr, "-infinity"))
+       return NOSTART_ABSTIME;
+    if (prsabsdate(timestr, &date, &tz) < 0)
+       return INVALID_ABSTIME;
+    return dateconv(&date, tz);
+}
+
+/*
+ * just parse the absolute date in timestr and get back a broken-out date.
+ */
+int
+prsabsdate(char *timestr,
+          struct tm *tm,
+          int *tzp)            /* - minutes west */
+{
+    register int nf;
+    char *fields[MAXDATEFIELDS];
+    static char delims[] = "- \t\n/,";
+    
+    nf = split(timestr, fields, MAXDATEFIELDS, delims+1);
+    if (nf > MAXDATEFIELDS)
+       return -1;
+    if (tryabsdate(fields, nf, tm, tzp) < 0) {
+       register char *p = timestr;
+       
+       /*
+        * could be a DEC-date; glue it all back together, split it
+        * with dash as a delimiter and try again.  Yes, this is a
+        * hack, but so are DEC-dates.
+        */
+       while (--nf > 0) {
+           while (*p++ != '\0')
+               ;
+           p[-1] = ' ';
+       }
+       nf = split(timestr, fields, MAXDATEFIELDS, delims);
+       if (nf > MAXDATEFIELDS)
+           return -1;
+       if (tryabsdate(fields, nf, tm, tzp) < 0)
+           return -1;
+    }
+    return 0;
+}
+
+/*
+ * try to parse pre-split timestr as an absolute date
+ */
+int
+tryabsdate(char *fields[], int nf, struct tm *tm, int *tzp)
+{
+    register int i;
+    register datetkn *tp;
+    register long flg = 0, ty;
+    int mer = HR24, bigval = -1;
+#ifndef USE_POSIX_TIME
+    struct timeb now;          /* the old V7-ism */
+    
+    (void) ftime(&now);
+    *tzp = now.timezone;
+#else /* USE_POSIX_TIME */
+#if defined(PORTNAME_hpux) || \
+    defined(PORTNAME_aix) || \
+    defined(PORTNAME_irix5) || \
+    defined(WIN32) || \
+       defined(PORTNAME_sparc_solaris)
+           tzset();
+#ifndef WIN32
+    *tzp = timezone / 60;              /* this is an X/Open-ism */
+#else
+    *tzp = _timezone / 60;            /* this is an X/Open-ism */
+#endif /* WIN32 */
+#else /* PORTNAME_hpux || PORTNAME_aix || PORTNAME_sparc_solaris || PORTNAME_irix5 */
+    time_t now = time((time_t *) NULL);
+    struct tm *tmnow = localtime(&now);
+    
+    *tzp = - tmnow->tm_gmtoff / 60;    /* tm_gmtoff is Sun/DEC-ism */
+#endif /* PORTNAME_hpux || PORTNAME_aix */
+#endif /* USE_POSIX_TIME */
+    
+    tm->tm_mday = tm->tm_mon = tm->tm_year = -1;       /* mandatory */
+    tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
+    tm->tm_isdst = -1;             /* assume we don't know. */
+    dtok_numparsed = 0;
+    
+    for (i = 0; i < nf; i++) {
+       if (fields[i][0] == '\0')
+           continue;
+       tp = datetoktype(fields[i], &bigval);
+       ty = (1L << tp->type) & ~(1L << PG_IGNORE);
+       if (flg&ty)
+           return -1;          /* repeated type */
+       flg |= ty;
+       switch (tp->type) {
+       case YEAR:
+           tm->tm_year = bigval;
+           break;
+       case DAY:
+           tm->tm_mday = bigval;
+           break;
+       case MONTH:
+           tm->tm_mon = tp->value;
+           break;
+       case TIME:
+           if (parsetime(fields[i], tm) < 0)
+               return -1;
+           break;
+       case DTZ:
+           tm->tm_isdst++;
+           /* FALLTHROUGH */
+       case TZ:
+           *tzp = FROMVAL(tp);
+           break;
+       case PG_IGNORE:
+           break;
+       case AMPM:
+           mer = tp->value;
+           break;
+       default:
+           return -1;  /* bad token type: CANTHAPPEN */
+       }
+    }
+    if (tm->tm_year == -1 || tm->tm_mon == -1 || tm->tm_mday == -1)
+       return -1;              /* missing component */
+    if (mer == PM)
+       tm->tm_hour += 12;
+    return 0;
+}
+
+
+/* return -1 on failure */
+int
+parsetime(char *time, struct tm *tm)
+{
+    register char c;
+    register int x;
+    
+    tm->tm_sec = 0;
+    GOBBLE_NUM(time, c, x, &tm->tm_hour);
+    if (c != ':')
+       return -1;              /* only hour; too short */
+    GOBBLE_NUM(time, c, x, &tm->tm_min);
+    if (c != ':')
+       return 0;               /* no seconds; okay */
+    GOBBLE_NUM(time, c, x, &tm->tm_sec);
+    /* this may be considered too strict.  garbage at end of time? */
+    return (c == '\0' || ISSPACE(c)? 0: -1);
+}
+
+
+/*
+ * split - divide a string into fields, like awk split()
+ */
+int                    /* number of fields, including overflow */
+split(char *string,
+      char *fields[],  /* list is not NULL-terminated */
+      int nfields,     /* number of entries available in fields[] */
+      char *sep)       /* "" white, "c" single char, "ab" [ab]+ */
+{
+    register char *p = string;
+    register char c;                   /* latest character */
+    register char sepc = sep[0];
+    register char sepc2;
+    register int fn;
+    register char **fp = fields;
+    register char *sepp;
+    register int trimtrail;
+    
+    /* white space */
+    if (sepc == '\0') {
+       while ((c = *p++) == ' ' || c == '\t')
+           continue;
+       p--;
+       trimtrail = 1;
+       sep = " \t";    /* note, code below knows this is 2 long */
+       sepc = ' ';
+    } else
+       trimtrail = 0;
+    sepc2 = sep[1];            /* now we can safely pick this up */
+    
+    /* catch empties */
+    if (*p == '\0')
+       return(0);
+    
+    /* single separator */
+    if (sepc2 == '\0') {
+       fn = nfields;
+       for (;;) {
+           *fp++ = p;
+           fn--;
+           if (fn == 0)
+               break;
+           while ((c = *p++) != sepc)
+               if (c == '\0')
+                   return(nfields - fn);
+           *(p-1) = '\0';
+       }
+       /* we have overflowed the fields vector -- just count them */
+       fn = nfields;
+       for (;;) {
+           while ((c = *p++) != sepc)
+               if (c == '\0')
+                   return(fn);
+           fn++;
+       }
+       /* not reached */
+    }
+    
+    /* two separators */
+    if (sep[2] == '\0') {
+       fn = nfields;
+       for (;;) {
+           *fp++ = p;
+           fn--;
+           while ((c = *p++) != sepc && c != sepc2)
+               if (c == '\0') {
+                   if (trimtrail && **(fp-1) == '\0')
+                       fn++;
+                   return(nfields - fn);
+               }
+           if (fn == 0)
+               break;
+           *(p-1) = '\0';
+           while ((c = *p++) == sepc || c == sepc2)
+               continue;
+           p--;
+       }
+       /* we have overflowed the fields vector -- just count them */
+       fn = nfields;
+       while (c != '\0') {
+           while ((c = *p++) == sepc || c == sepc2)
+               continue;
+           p--;
+           fn++;
+           while ((c = *p++) != '\0' && c != sepc && c != sepc2)
+               continue;
+       }
+       /* might have to trim trailing white space */
+       if (trimtrail) {
+           p--;
+           while ((c = *--p) == sepc || c == sepc2)
+               continue;
+           p++;
+           if (*p != '\0') {
+               if (fn == nfields+1)
+                   *p = '\0';
+               fn--;
+           }
+       }
+       return(fn);
+    }
+    
+    /* n separators */
+    fn = 0;
+    for (;;) {
+       if (fn < nfields)
+           *fp++ = p;
+       fn++;
+       for (;;) {
+           c = *p++;
+           if (c == '\0')
+               return(fn);
+           sepp = sep;
+           while ((sepc = *sepp++) != '\0' && sepc != c)
+               continue;
+           if (sepc != '\0')   /* it was a separator */
+               break;
+       }
+       if (fn < nfields)
+           *(p-1) = '\0';
+       for (;;) {
+           c = *p++;
+           sepp = sep;
+           while ((sepc = *sepp++) != '\0' && sepc != c)
+               continue;
+           if (sepc == '\0')   /* it wasn't a separator */
+               break;
+       }
+       p--;
+    }
+    
+    /* not reached */
+}
+
+/*
+ * Given an AbsoluteTime return the English text version of the date
+ */
+char *
+nabstimeout(AbsoluteTime time)
+{
+    /*
+     * Fri Jan 28 23:05:29 1994 PST
+     * 0        1         2
+     * 12345678901234567890123456789
+     *
+     * we allocate some extra -- timezones are usually 3 characters but
+     * this is not in the POSIX standard...
+     */
+    char buf[40];
+    char* result;
+
+    switch (time) {
+    case EPOCH_ABSTIME:          (void) strcpy(buf, "epoch");                  break;
+    case INVALID_ABSTIME: (void) strcpy(buf, "Invalid Abstime");       break;
+    case CURRENT_ABSTIME: (void) strcpy(buf, "current");               break;
+    case NOEND_ABSTIME:   (void) strcpy(buf, "infinity");              break;
+    case NOSTART_ABSTIME: (void) strcpy(buf, "-infinity");             break;
+    default:
+       /* hack -- localtime happens to work for negative times */
+       (void) strftime(buf, sizeof(buf), "%a %b %d %H:%M:%S %Y %Z",
+                       localtime((time_t *) &time));
+       break;
+    }
+    result = (char*)palloc(strlen(buf) + 1);
+    strcpy(result, buf);
+    return result;
+}
+
+/* turn a (struct tm) and a few variables into a time_t, with range checking */
+AbsoluteTime
+dateconv(register struct tm *tm, int zone)
+{
+    tm->tm_wday = tm->tm_yday = 0;
+    
+    /* validate, before going out of range on some members */
+    if (tm->tm_year < 0 || tm->tm_mon < 1 || tm->tm_mon > 12 ||
+       tm->tm_mday < 1 || tm->tm_hour < 0 || tm->tm_hour >= 24 ||
+       tm->tm_min < 0 || tm->tm_min > 59 ||
+       tm->tm_sec < 0 || tm->tm_sec > 59)
+       return -1;
+    
+    /*
+     * zone should really be -zone, and tz should be set to tp->value, not
+     * -tp->value.  Or the table could be fixed.
+     */
+    tm->tm_min += zone;                /* mktime lets it be out of range */
+    
+    /* convert to seconds */
+    return qmktime(tm);
+}
+
+
+/*
+ * near-ANSI qmktime suitable for use by dateconv; not necessarily as paranoid
+ * as ANSI requires, and it may not canonicalise the struct tm.  Ignores tm_wday
+ * and tm_yday.
+ */
+time_t
+qmktime(struct tm *tp)
+{
+    register int mon = tp->tm_mon;
+    register int day = tp->tm_mday, year = tp->tm_year;
+    register time_t daynum;
+    time_t secondnum;
+    register int century;
+    
+    /* If it was a 2 digit year */
+    if (year < 100)
+       year += 1900;
+    
+    /*
+     * validate day against days-per-month table, with leap-year
+     * correction
+     */
+    if (day > nmdays[mon])
+       if (mon != 2 || year % 4 == 0 &&
+           (year % 100 != 0 || year % 400 == 0) && day > 29)
+           return -1;  /* day too large for month */
+    
+    /* split year into century and year-of-century */
+    century = year / 100;
+    year %= 100;
+    /*
+     * We calculate the day number exactly, assuming the calendar has
+     * always had the current leap year rules.  (The leap year rules are
+     * to compensate for the fact that the Earth's revolution around the
+     * Sun takes 365.2425 days).  We first need to rotate months so March
+     * is 0, since we want the last month to have the reduced number of
+     * days.
+     */
+    if (mon > 2)
+       mon -= 3;
+    else {
+       mon += 9;
+       if (year == 0) {
+           century--;
+           year = 99;
+       } else
+           --year;
+    }
+    daynum = -EPOCH_DAYNUM + DAYNUM(century, year, mon, day);
+    
+    /* check for time out of range */
+    if (daynum < MIN_DAYNUM || daynum > MAX_DAYNUM)
+       return INVALID_ABSTIME;
+    
+    /* convert to seconds */
+    secondnum =
+       tp->tm_sec + (tp->tm_min +(daynum*24 + tp->tm_hour)*60)*60;
+    
+    /* check for overflow */
+    if ((daynum == MAX_DAYNUM && secondnum < 0) ||
+       (daynum == MIN_DAYNUM && secondnum > 0))
+       return INVALID_ABSTIME;
+    
+    /* check for "current", "infinity", "-infinity" */
+    if (!AbsoluteTimeIsReal(secondnum))
+       return INVALID_ABSTIME;
+    
+    /* daylight correction */
+    if (tp->tm_isdst < 0)              /* unknown; find out */
+     {
+       struct tm *result;
+       /* NT returns NULL for any time before 1/1/70 */
+       result = localtime(&secondnum);
+       if (result == NULL)
+           return INVALID_ABSTIME;
+       else
+           tp->tm_isdst = result->tm_isdst;
+     }
+    if (tp->tm_isdst > 0)
+       secondnum -= 60*60;
+    
+    return secondnum;
+}
+
+datetkn *
+datetoktype(char *s, int *bigvalp)
+{
+    register char *cp = s;
+    register char c = *cp;
+    static datetkn t;
+    register datetkn *tp = &t;
+    
+    if (isascii(c) && isdigit(c)) {
+       register int len = strlen(cp);
+       
+       if (len > 3 && (cp[1] == ':' || cp[2] == ':'))
+           tp->type = TIME;
+       else {
+           if (bigvalp != NULL)
+               /* won't fit in tp->value */
+               *bigvalp = atoi(cp);
+           if (len == 4)
+               tp->type = YEAR;
+           else if (++dtok_numparsed == 1)
+               tp->type = DAY;
+           else
+               tp->type = YEAR;
+       }
+    } else if (c == '-' || c == '+') {
+       register int val = atoi(cp + 1);
+       register int hr =  val / 100;
+       register int min = val % 100;
+       
+       val = hr*60 + min;
+       if (c == '-')
+           val = -val;
+       tp->type = TZ;
+       TOVAL(tp, val);
+    } else {
+       char lowtoken[TOKMAXLEN+1];
+       register char *ltp = lowtoken, *endltp = lowtoken+TOKMAXLEN;
+       
+       /* copy to lowtoken to avoid modifying s */
+       while ((c = *cp++) != '\0' && ltp < endltp)
+           *ltp++ = (isascii(c) && isupper(c)? tolower(c): c);
+       *ltp = '\0';
+       tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
+       if (tp == NULL) {
+           tp = &t;
+           tp->type = PG_IGNORE;
+       }
+    }
+    return tp;
+}
+
+/*
+ * Binary search -- from Knuth (6.2.1) Algorithm B.  Special case like this
+ * is WAY faster than the generic bsearch().
+ */
+datetkn *
+datebsearch(char *key, datetkn *base, unsigned int nel)
+{
+    register datetkn *last = base + nel - 1, *position;
+    register int result;
+    
+    while (last >= base) {
+       position = base + ((last - base) >> 1);
+       result = key[0] - position->token[0];
+       if (result == 0) {
+           result = strncmp(key, position->token, TOKMAXLEN);
+           if (result == 0)
+               return position;
+       }
+       if (result < 0)
+           last = position - 1;
+       else
+           base = position + 1;
+    }
+    return 0;
+}
+
+
+/*
+ *  AbsoluteTimeIsBefore -- true iff time1 is before time2.
+ */
+
+bool
+AbsoluteTimeIsBefore(AbsoluteTime time1, AbsoluteTime time2)
+{
+    AbsoluteTime tm = GetCurrentTransactionStartTime();
+    
+    Assert(AbsoluteTimeIsValid(time1));
+    Assert(AbsoluteTimeIsValid(time2));
+    
+    if ((time1 == CURRENT_ABSTIME) || (time2 == CURRENT_ABSTIME))
+       return false;
+    if (time1 == CURRENT_ABSTIME)
+       return (tm < time2);
+    if (time2 == CURRENT_ABSTIME)
+       return (time1 < tm);
+    
+    return (time1 < time2);
+}
+
+bool
+AbsoluteTimeIsAfter(AbsoluteTime time1, AbsoluteTime time2)
+{
+    AbsoluteTime tm = GetCurrentTransactionStartTime();
+    
+    Assert(AbsoluteTimeIsValid(time1));
+    Assert(AbsoluteTimeIsValid(time2));
+    
+    if ((time1 == CURRENT_ABSTIME) || (time2 == CURRENT_ABSTIME))
+       return false;
+    if (time1 == CURRENT_ABSTIME)
+       return (tm > time2);
+    if (time2 == CURRENT_ABSTIME)
+       return (time1 > tm);
+    
+    return (time1 > time2);
+}
+
+
+/*
+ *     abstimeeq       - returns 1, iff arguments are equal
+ *     abstimene       - returns 1, iff arguments are not equal
+ *     abstimelt       - returns 1, iff t1 less than t2
+ *     abstimegt       - returns 1, iff t1 greater than t2
+ *     abstimele       - returns 1, iff t1 less than or equal to t2
+ *     abstimege       - returns 1, iff t1 greater than or equal to t2
+ *
+ */
+int32
+abstimeeq(AbsoluteTime t1, AbsoluteTime t2)
+{
+    if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
+       return 0;
+    if (t1 == CURRENT_ABSTIME)
+       t1 = GetCurrentTransactionStartTime();
+    if (t2 == CURRENT_ABSTIME)
+       t2 = GetCurrentTransactionStartTime();
+    
+    return(t1 == t2);
+}
+
+int32
+abstimene(AbsoluteTime t1, AbsoluteTime t2)
+{ 
+    if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
+       return 0;
+    if (t1 == CURRENT_ABSTIME)
+       t1 = GetCurrentTransactionStartTime();
+    if (t2 == CURRENT_ABSTIME)
+       t2 = GetCurrentTransactionStartTime();
+    
+    return(t1 != t2);
+}
+
+int32
+abstimelt(AbsoluteTime t1, AbsoluteTime t2)
+{ 
+    if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
+       return 0;
+    if (t1 == CURRENT_ABSTIME)
+       t1 = GetCurrentTransactionStartTime();
+    if (t2 == CURRENT_ABSTIME)
+       t2 = GetCurrentTransactionStartTime();
+    
+    return(t1 < t2);
+}
+
+int32
+abstimegt(AbsoluteTime t1, AbsoluteTime t2)
+{ 
+    if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
+       return 0;
+    if (t1 == CURRENT_ABSTIME)
+       t1 = GetCurrentTransactionStartTime();
+    if (t2 == CURRENT_ABSTIME)
+       t2 = GetCurrentTransactionStartTime();
+    
+    return(t1 > t2);
+}
+
+int32
+abstimele(AbsoluteTime t1, AbsoluteTime t2)
+{ 
+    if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
+       return 0;
+    if (t1 == CURRENT_ABSTIME)
+       t1 = GetCurrentTransactionStartTime();
+    if (t2 == CURRENT_ABSTIME)
+       t2 = GetCurrentTransactionStartTime();
+    
+    return(t1 <= t2);
+}
+
+int32
+abstimege(AbsoluteTime t1, AbsoluteTime t2)
+{ 
+    if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
+       return 0;
+    if (t1 == CURRENT_ABSTIME)
+       t1 = GetCurrentTransactionStartTime();
+    if (t2 == CURRENT_ABSTIME)
+       t2 = GetCurrentTransactionStartTime();
+    
+    return(t1 >= t2);
+}
+
+
diff --git a/src/backend/utils/adt/name.c b/src/backend/utils/adt/name.c
new file mode 100644 (file)
index 0000000..adfb319
--- /dev/null
@@ -0,0 +1,198 @@
+/*-------------------------------------------------------------------------
+ *
+ * name.c--
+ *    Functions for the built-in type "name".
+ * name replaces char16 and is carefully implemented so that it
+ * is a string of length NAMEDATALEN.  DO NOT use hard-coded constants anywhere
+ * always use NAMEDATALEN as the symbolic constant!   - jolly 8/21/95
+ * 
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+#include "utils/builtins.h"    /* where the declarations go */
+#include "utils/palloc.h"      /* where the declarations go */
+
+/***************************************************************************** 
+ *   USER I/O ROUTINES (none)                                                *
+ *****************************************************************************/
+
+
+/*
+ *     namein  - converts "..." to internal representation
+ *
+ *     Note:
+ *             Currently if strlen(s) < NAMEDATALEN, the extra chars are nulls
+ */
+NameData *namein(char *s)
+{
+    NameData *result;
+
+    if (s == NULL)
+       return(NULL);
+    result = (NameData*) palloc(NAMEDATALEN);
+    /* always keep it null-padded */
+    memset(result->data, 0, NAMEDATALEN); 
+    (void) strncpy(result->data, s, NAMEDATALEN-1);
+    return(result);
+}
+
+/*
+ *     nameout - converts internal reprsentation to "..."
+ */
+char *nameout(NameData *s)
+{
+    if (s == NULL)
+       return "-";
+    else
+       return pstrdup(s->data);
+}
+
+
+/***************************************************************************** 
+ *   PUBLIC ROUTINES                                                         *
+ *****************************************************************************/
+
+/*
+ *     nameeq  - returns 1 iff arguments are equal
+ *     namene  - returns 1 iff arguments are not equal
+ *
+ *     BUGS:
+ *             Assumes that "xy\0\0a" should be equal to "xy\0b".
+ *             If not, can do the comparison backwards for efficiency.
+ *
+ *     namelt  - returns 1 iff a < b
+ *     namele  - returns 1 iff a <= b
+ *     namegt  - returns 1 iff a < b
+ *     namege  - returns 1 iff a <= b
+ *
+ */
+int32 nameeq(NameData *arg1, NameData *arg2)
+{
+    if (!arg1  || !arg2)
+       return 0;
+    else
+       return (strncmp(arg1->data, arg2->data, NAMEDATALEN) == 0);
+}
+
+int32 namene(NameData *arg1, NameData *arg2)
+{
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    return(strncmp(arg1->data, arg2->data, NAMEDATALEN) != 0);
+}
+
+int32 namelt(NameData *arg1, NameData *arg2)
+{
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    return((int32) (strncmp(arg1->data, arg2->data, NAMEDATALEN) < 0));
+}
+
+int32 namele(NameData *arg1, NameData *arg2)
+{
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    return((int32) (strncmp(arg1->data, arg2->data, NAMEDATALEN) <= 0));
+}
+
+int32 namegt(NameData *arg1, NameData *arg2)
+{
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    
+    return((int32) (strncmp(arg1->data, arg2->data, NAMEDATALEN) > 0));
+}
+
+int32 namege(NameData *arg1, NameData *arg2)
+{
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    
+    return((int32) (strncmp(arg1->data, arg2->data, NAMEDATALEN) >= 0));
+}
+
+
+/* (see char.c for comparison/operation routines) */
+
+int namecpy(Name n1, Name n2)
+{
+    if (!n1 || !n2)
+       return(-1);
+    (void) strncpy(n1->data, n2->data, NAMEDATALEN);
+    return(0);
+}
+
+int namecat(Name n1, Name n2)
+{
+    return(namestrcat(n1, n2->data)); /* n2 can't be any longer than n1 */
+}
+
+int namecmp(Name n1, Name n2)
+{
+    return(strncmp(n1->data, n2->data, NAMEDATALEN));
+}
+
+int 
+namestrcpy(Name name, char *str)
+{
+    if (!name || !str)
+       return(-1);
+    memset(name->data, 0, sizeof(NameData));
+    (void) strncpy(name->data, str, NAMEDATALEN);
+    return(0);
+}
+
+int namestrcat(Name name, char *str)
+{
+    int i;
+    char *p, *q;
+    
+    if (!name || !str)
+       return(-1);
+    for (i = 0, p = name->data; i < NAMEDATALEN && *p; ++i, ++p)
+       ;
+    for (q = str; i < NAMEDATALEN; ++i, ++p, ++q) {
+       *p = *q;
+       if (!*q)
+           break;
+    }
+    return(0);
+}
+
+int 
+namestrcmp(Name name, char *str)
+{
+    if (!name && !str)
+       return(0);
+    if (!name)
+       return(-1);     /* NULL < anything */
+    if (!str)
+       return(1);      /* NULL < anything */
+    return(strncmp(name->data, str, NAMEDATALEN));
+}
+
+/***************************************************************************** 
+ *   PRIVATE ROUTINES                                                        *
+ *****************************************************************************/
+
+uint32 
+NameComputeLength(Name name)
+{
+    char       *charP;
+    int        length;
+    
+    for (length = 0, charP = name->data;
+        length < NAMEDATALEN && *charP != '\0';
+        length++, charP++) {
+       ;
+    }
+    return (uint32)length;
+}
diff --git a/src/backend/utils/adt/not_in.c b/src/backend/utils/adt/not_in.c
new file mode 100644 (file)
index 0000000..70b5700
--- /dev/null
@@ -0,0 +1,124 @@
+/*-------------------------------------------------------------------------
+ *
+ * not_in.c--
+ *    Executes the "not_in" operator for any data type
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ *
+ * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ * X HACK WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! X
+ * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ *
+ * This code is the OLD not-in code that is HACKED
+ * into place until operators that can have arguments as
+ * columns are ******REALLY****** implemented!!!!!!!!!!!
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include "postgres.h"
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/relscan.h"
+#include "utils/rel.h"
+#include "utils/elog.h"
+#include "utils/builtins.h"    /* where function decls go */
+
+/* ----------------------------------------------------------------
+ *     
+ * ----------------------------------------------------------------
+ */
+bool
+int4notin(int16 not_in_arg, char *relation_and_attr)
+{
+    Relation relation_to_scan;
+    int left_side_argument, integer_value;
+    HeapTuple current_tuple;
+    HeapScanDesc scan_descriptor;
+    bool dummy, retval;
+    int attrid;
+    char *relation, *attribute;
+    char my_copy[32];
+    Datum value;
+    NameData relNameData;
+    ScanKeyData skeyData;
+    
+    strcpy(my_copy, relation_and_attr);
+    
+    relation = (char *) strtok(my_copy, ".");
+    attribute = (char *) strtok(NULL, ".");
+    
+    
+    /* fetch tuple OID */
+    
+    left_side_argument = not_in_arg;
+    
+    /* Open the relation and get a relation descriptor */
+    
+    namestrcpy(&relNameData,relation);
+    relation_to_scan = heap_openr(relNameData.data);
+    attrid  = my_varattno(relation_to_scan, attribute);
+
+    /* the last argument should be a ScanKey, not an integer! - jolly*/
+    /* it looks like the arguments are out of order, too */
+    /* but skeyData is never initialized! does this work?? - ay 2/95 */
+    scan_descriptor = heap_beginscan(relation_to_scan, false, NULL, 0,
+                                    &skeyData); 
+
+    retval = true;
+    
+    /* do a scan of the relation, and do the check */
+    for (current_tuple = heap_getnext(scan_descriptor, 0, NULL);
+         current_tuple != NULL && retval;
+         current_tuple = heap_getnext(scan_descriptor, 0, NULL))
+       {
+           value = PointerGetDatum(heap_getattr(current_tuple,
+                                                InvalidBuffer,
+                                                (AttrNumber) attrid,
+                                RelationGetTupleDescriptor(relation_to_scan),
+                                                &dummy));
+           
+           integer_value = DatumGetInt16(value);
+           if (left_side_argument == integer_value)
+               {
+                   retval = false;
+               }
+       }
+    
+    /* close the relation */
+    heap_close(relation_to_scan);
+    return(retval);
+}
+
+bool oidnotin(Oid the_oid, char *compare)
+{
+    if (the_oid == InvalidOid)
+       return false;
+    return(int4notin(the_oid, compare));
+}
+
+/*
+ * XXX
+ * If varattno (in parser/catalog_utils.h) ever is added to
+ * cinterface.a, this routine should go away
+ */
+int my_varattno(Relation rd, char *a)
+{
+    int i;
+    
+    for (i = 0; i < rd->rd_rel->relnatts; i++) {
+       if (!namestrcmp(&rd->rd_att->attrs[i]->attname, a)) {
+           return(i+1);
+       }
+    }
+    return(-1);
+}
+
diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c
new file mode 100644 (file)
index 0000000..c80d8d2
--- /dev/null
@@ -0,0 +1,401 @@
+/*-------------------------------------------------------------------------
+ *
+ * numutils.c--
+ *    utility functions for I/O of built-in numeric types.
+ *
+ *     integer:                itoa, ltoa
+ *     floating point:         ftoa, atof1
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>             /* for sprintf() */
+#include <errno.h>
+#include <math.h>
+#include "postgres.h"
+#include "utils/builtins.h"            /* where the declarations go */
+#include "utils/elog.h"
+
+int32
+pg_atoi(char *s, int size, int c)
+{
+    long l;
+    char *badp = (char *) NULL;
+    
+    Assert(s);
+    
+    errno = 0;
+    l = strtol(s, &badp, 10);
+    if (errno)         /* strtol must set ERANGE */
+       elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
+    if (badp && *badp && (*badp != c))
+       elog(WARN, "pg_atoi: error in \"%s\": can\'t parse \"%s\"", s, badp);
+    
+    switch (size) {
+    case sizeof(int32):
+#ifdef HAS_LONG_LONG
+       /* won't get ERANGE on these with 64-bit longs... */
+       if (l < -0x80000000L) {
+           errno = ERANGE;
+           elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
+       }
+       if (l > 0x7fffffffL) {
+           errno = ERANGE;
+           elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
+       }
+#endif /* HAS_LONG_LONG */
+       break;
+    case sizeof(int16):
+       if (l < -0x8000) {
+           errno = ERANGE;
+           elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
+       }
+       if (l > 0x7fff) {
+           errno = ERANGE;
+           elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
+       }
+       break;
+    case sizeof(int8):
+       if (l < -0x80) {
+           errno = ERANGE;
+           elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
+       }
+       if (l > 0x7f) {
+           errno = ERANGE;
+           elog(WARN, "pg_atoi: error reading \"%s\": %m", s);
+       }
+       break;
+    default:
+       elog(WARN, "pg_atoi: invalid result size: %d", size);
+    }
+    return((int32) l);
+}
+
+/*
+ *     itoa            - converts a short int to its string represention
+ *
+ *     Note:
+ *             previously based on ~ingres/source/gutil/atoi.c
+ *             now uses vendor's sprintf conversion
+ */
+void
+itoa(int i, char *a)
+{
+    sprintf(a, "%hd", (short)i);
+}
+
+/*
+ *     ltoa            - converts a long int to its string represention
+ *
+ *     Note:
+ *             previously based on ~ingres/source/gutil/atoi.c
+ *             now uses vendor's sprintf conversion
+ */
+void
+ltoa(int32 l, char *a)
+{
+    sprintf(a, "%d", l);
+}
+
+/*
+ **  ftoa      - FLOATING POINT TO ASCII CONVERSION
+ **
+ **    CODE derived from ingres, ~ingres/source/gutil/ftoa.c
+ **
+ **    'Value' is converted to an ascii character string and stored
+ **    into 'ascii'.  Ascii should have room for at least 'width' + 1
+ **    characters.  'Width' is the width of the output field (max).
+ **    'Prec' is the number of characters to put after the decimal
+ **    point.  The format of the output string is controlled by
+ **    'format'.
+ **
+ **    'Format' can be:
+ **            e or E: "E" format output
+ **            f or F:  "F" format output
+ **            g or G:  "F" format output if it will fit, otherwise
+ **                    use "E" format.
+ **            n or N:  same as G, but decimal points will not always
+ **                    be aligned.
+ **
+ **    If 'format' is upper case, the "E" comes out in upper case;
+ **    otherwise it comes out in lower case.
+ **
+ **    When the field width is not big enough, it fills the field with
+ **    stars ("*****") and returns zero.  Normal return is the width
+ **    of the output field (sometimes shorter than 'width').
+ */
+int
+ftoa(double value, char *ascii, int width, int prec1, char format)
+{
+#if defined(PORTNAME_BSD44_derived) || defined(PORTNAME_bsdi)
+       char    out[256];
+       char    fmt[256];
+       int     ret;
+
+       (void) sprintf(fmt, "%%%d.%d%c", width, prec1, format);
+       (void) sprintf(out, fmt, value);
+       if ((ret = strlen(out)) > width) {
+               memset(ascii, '*', width - 2);
+               ascii[width] = 0;
+               return(0);
+       }
+       (void) strcpy(ascii, out);
+       return(ret);
+#else
+    auto int   expon;
+    auto int   sign;
+    register int       avail;
+    register char      *a;
+    register char      *p;
+    char               mode;
+    int                lowercase;
+    int                prec;
+/*    extern char      *ecvt(), *fcvt();*/
+    
+    prec = prec1;
+    mode = format;
+    lowercase = 'a' - 'A';
+    if (mode >= 'a')
+       mode -= 'a' - 'A';
+    else
+       lowercase = 0;
+    
+    if (mode != 'E') {
+       /* try 'F' style output */
+       p = fcvt(value, prec, &expon, &sign);
+       avail = width;
+       a = ascii;
+       
+       /* output sign */
+       if (sign) {
+           avail--;
+           *a++ = '-';
+       }
+       
+       /* output '0' before the decimal point */
+       if (expon <= 0) {
+           *a++ = '0';
+           avail--;
+       }
+       
+       /* compute space length left after dec pt and fraction */
+       avail -= prec + 1;
+       if (mode == 'G')
+           avail -= 4;
+       
+       if (avail >= expon) {
+           
+           /* it fits.  output */
+           while (expon > 0) {
+               /* output left of dp */
+               expon--;
+               if (*p) {
+                   *a++ = *p++;
+               } else
+                   *a++ = '0';
+           }
+           
+           /* output fraction (right of dec pt) */
+           avail = expon;
+           goto frac_out;
+       }
+       /* won't fit; let's hope for G format */
+    }
+    
+    if (mode != 'F') {
+       /* try to do E style output */
+       p = ecvt(value, prec + 1, &expon, &sign);
+       avail = width - 5;
+       a = ascii;
+       
+       /* output the sign */
+       if (sign) {
+           *a++ = '-';
+           avail--;
+       }
+    }
+    
+    /* check for field too small */
+    if (mode == 'F' || avail < prec) {
+       /* sorry joker, you lose */
+       a = ascii;
+       for (avail = width; avail > 0; avail--)
+           *a++ = '*';
+       *a = 0;
+       return (0);
+    }
+    
+    /* it fits; output the number */
+    mode = 'E';
+    
+    /* output the LHS single digit */
+    *a++ = *p++;
+    expon--;
+    
+    /* output the rhs */
+    avail = 1;
+    
+ frac_out:
+    *a++ = '.';
+    while (prec > 0) {
+       prec--;
+       if (avail < 0) {
+           avail++;
+           *a++ = '0';
+       } else {
+           if (*p)
+               *a++ = *p++;
+           else
+               *a++ = '0';
+       }
+    }
+    
+    /* output the exponent */
+    if (mode == 'E') {
+       *a++ = 'E' + lowercase;
+       if (expon < 0) {
+           *a++ = '-';
+           expon = -expon;
+       } else
+           *a++ = '+';
+       *a++ = (expon / 10) % 10 + '0';
+       *a++ = expon % 10 + '0';
+    }
+    
+    /* output spaces on the end in G format */
+    if (mode == 'G') {
+       *a++ = ' ';
+       *a++ = ' ';
+       *a++ = ' ';
+       *a++ = ' ';
+    }
+    
+    /* finally, we can return */
+    *a = 0;
+    avail = a - ascii;
+    return (avail);
+#endif /* !PORTNAME_BSD44_derived */
+}
+
+/*
+ **   atof1    - ASCII TO FLOATING CONVERSION
+ **
+ **    CODE derived from ~ingres/source/gutil/atof.c
+ **
+ **    Converts the string 'str' to floating point and stores the
+ **    result into the cell pointed to by 'val'.
+ **
+ **    The syntax which it accepts is pretty much what you would
+ **    expect.  Basically, it is:
+ **            {<sp>} [+|-] {<sp>} {<digit>} [.{digit}] {<sp>} [<exp>]
+ **    where <exp> is "e" or "E" followed by an integer, <sp> is a
+ **    space character, <digit> is zero through nine, [] is zero or
+ **    one, and {} is zero or more.
+ **
+ **    Parameters:
+ **            str -- string to convert.
+ **            val -- pointer to place to put the result (which
+ **                    must be type double).
+ **
+ **    Returns:
+ **            zero -- ok.
+ **            -1 -- syntax error.
+ **            +1 -- overflow (not implemented).
+ **
+ **    Side Effects:
+ **            clobbers *val.
+ */
+int
+atof1(char *str, double *val)
+{
+    register char      *p;
+    double             v;
+    double             fact;
+    int                minus;
+    register char      c;
+    int                expon;
+    register int       gotmant;
+    
+    v = 0.0;
+    p = str;
+    minus = 0;
+    
+    /* skip leading blanks */
+    while ((c = *p) != '\0') {
+       if (c != ' ')
+           break;
+       p++;
+    }
+    
+    /* handle possible sign */
+    switch (c) {
+    case '-':
+       minus++;
+       
+    case '+':
+       p++;
+    }
+    
+    /* skip blanks after sign */
+    while ((c = *p) != '\0') {
+       if (c != ' ')
+           break;
+       p++;
+    }
+    
+    /* start collecting the number to the decimal point */
+    gotmant = 0;
+    for (;;) {
+       c = *p;
+       if (c < '0' || c > '9')
+           break;
+       v = v * 10.0 + (c - '0');
+       gotmant++;
+       p++;
+    }
+    
+    /* check for fractional part */
+    if (c == '.') {
+       fact = 1.0;
+       for (;;) {
+           c = *++p;
+           if (c < '0' || c > '9')
+               break;
+           fact *= 0.1;
+           v += (c - '0') * fact;
+           gotmant++;
+       }
+    }
+    
+    /* skip blanks before possible exponent */
+    while ((c = *p) != '\0') {
+       if (c != ' ')
+           break;
+       p++;
+    }
+    
+    /* test for exponent */
+    if (c == 'e' || c == 'E') {
+       p++;
+       expon = pg_atoi(p, sizeof(expon), '\0');
+       if (!gotmant)
+           v = 1.0;
+       fact = expon;
+       v *= pow(10.0, fact);
+    } else {
+       /* if no exponent, then nothing */
+       if (c != 0)
+           return (-1);
+    }
+    
+    /* store the result and exit */
+    if (minus)
+       v = -v;
+    *val = v;
+    return (0);
+}
diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c
new file mode 100644 (file)
index 0000000..ff7989a
--- /dev/null
@@ -0,0 +1,127 @@
+/*-------------------------------------------------------------------------
+ *
+ * oid.c--
+ *    Functions for the built-in type Oid.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "utils/palloc.h"
+#include "utils/builtins.h"    /* where function declarations go */
+#include "utils/elog.h"
+
+/***************************************************************************** 
+ *   USER I/O ROUTINES                                                       *
+ *****************************************************************************/
+
+/*
+ *     oid8in          - converts "num num ..." to internal form
+ *
+ *     Note:
+ *             Fills any nonexistent digits with NULL oids.
+ */
+Oid *oid8in(char *oidString)
+{
+    register Oid       (*result)[];
+    int                        nums;
+    
+    if (oidString == NULL)
+       return(NULL);
+    result = (Oid (*)[]) palloc(sizeof(Oid [8]));
+    if ((nums = sscanf(oidString, "%d%d%d%d%d%d%d%d",
+                      *result,
+                      *result + 1,
+                      *result + 2,
+                      *result + 3,
+                      *result + 4,
+                      *result + 5,
+                      *result + 6,
+                      *result + 7)) != 8) {
+       do
+           (*result)[nums++] = 0;
+       while (nums < 8);
+    }
+    return((Oid *) result);
+}
+
+/*
+ *     oid8out - converts internal form to "num num ..."
+ */
+char *oid8out(Oid      (*oidArray)[])
+{
+    register int               num;
+    register Oid       *sp;
+    register char              *rp;
+    char                       *result;
+    
+    if (oidArray == NULL) {
+       result = (char *) palloc(2);
+       result[0] = '-';
+       result[1] = '\0';
+       return(result);
+    }
+    
+    /* assumes sign, 10 digits, ' ' */
+    rp = result = (char *) palloc(8 * 12);
+    sp = *oidArray;
+    for (num = 8; num != 0; num--) {
+       ltoa(*sp++, rp);
+       while (*++rp != '\0')
+           ;
+       *rp++ = ' ';
+    }
+    *--rp = '\0';
+    return(result);
+}
+
+Oid oidin(char *s)
+{
+    extern int32 int4in();
+    
+    return(int4in(s));
+}
+
+char *oidout(Oid o)
+{
+    extern char *int4out();
+    
+    return(int4out(o));
+}
+
+/***************************************************************************** 
+ *   PUBLIC ROUTINES                                                         *
+ *****************************************************************************/
+
+int32 oideq(Oid arg1, Oid arg2)
+{
+    return(arg1 == arg2);
+}
+
+int32 oidne(Oid arg1, Oid arg2)
+{
+    return(arg1 != arg2);
+}
+
+int32 oid8eq(Oid arg1[], Oid arg2[])
+{
+    return (int32)(memcmp(arg1, arg2, 8 * sizeof(Oid)) == 0);
+}
+
+bool oideqint4(Oid arg1, int32 arg2)
+{
+/* oid is unsigned, but int4 is signed */
+    return (arg2 >= 0 && arg1 == arg2);
+}
+
+bool int4eqoid(int32 arg1, Oid arg2)
+{
+/* oid is unsigned, but int4 is signed */
+    return (arg1 >= 0 && arg1 == arg2);
+}
+
diff --git a/src/backend/utils/adt/oidint2.c b/src/backend/utils/adt/oidint2.c
new file mode 100644 (file)
index 0000000..d733f66
--- /dev/null
@@ -0,0 +1,120 @@
+/*-------------------------------------------------------------------------
+ *
+ * oidint2.c--
+ *    Functions for the built-in type "oidint2".
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include "postgres.h"
+#include "utils/palloc.h"
+#include "utils/builtins.h"    /* for pg_atoi() */
+#include "utils/oidcompos.h"   /* where function declarations go */
+
+
+OidInt2
+oidint2in(char *o)
+{
+    OidInt2 oi;
+    char *p;
+    
+    oi = (OidInt2) palloc(sizeof(OidInt2Data));
+    
+    for (p = o; *p != '\0' && *p != '/'; p++)
+       continue;
+    
+    oi->oi_oid = (Oid) pg_atoi(o, sizeof(Oid), '/');
+    if (*p == '\0') {
+       oi->oi_int2 = 0;
+    } else {
+       oi->oi_int2 = (int16) pg_atoi(++p, sizeof(int2), '\0');
+    }
+    
+    return (oi);
+}
+
+char *
+oidint2out(OidInt2 o)
+{
+    char *r;
+    
+    /*
+     * -2147483647/-32767
+     * 0        1
+     * 1234567890123456789
+     */
+    r = (char *) palloc(19);
+    sprintf(r, "%d/%d", o->oi_oid, o->oi_int2);
+    
+    return (r);
+}
+
+bool
+oidint2lt(OidInt2 o1, OidInt2 o2)
+{
+    return
+       ((bool) (o1->oi_oid < o2->oi_oid ||
+                (o1->oi_oid == o2->oi_oid && o1->oi_int2 < o2->oi_int2)));
+}
+
+bool
+oidint2le(OidInt2 o1, OidInt2 o2)
+{
+    return ((bool) (o1->oi_oid < o2->oi_oid ||
+                   (o1->oi_oid == o2->oi_oid && o1->oi_int2 <= o2->oi_int2)));
+}
+
+bool
+oidint2eq(OidInt2 o1, OidInt2 o2)
+{
+    return ((bool) (o1->oi_oid == o2->oi_oid && o1->oi_int2 == o2->oi_int2));
+}
+
+bool
+oidint2ge(OidInt2 o1, OidInt2 o2)
+{
+    return ((bool) (o1->oi_oid > o2->oi_oid ||
+                   (o1->oi_oid == o2->oi_oid && o1->oi_int2 >= o2->oi_int2)));
+}
+
+bool
+oidint2gt(OidInt2 o1, OidInt2 o2)
+{
+    return ((bool) (o1->oi_oid > o2->oi_oid ||
+                   (o1->oi_oid == o2->oi_oid && o1->oi_int2 > o2->oi_int2)));
+}
+
+bool
+oidint2ne(OidInt2 o1, OidInt2 o2)
+{
+    return ((bool) (o1->oi_oid != o2->oi_oid || o1->oi_int2 != o2->oi_int2));
+}
+
+int
+oidint2cmp(OidInt2 o1, OidInt2 o2)
+{
+    if (oidint2lt(o1, o2))
+       return (-1);
+    else if (oidint2eq(o1, o2))
+       return (0);
+    else
+       return (1);
+}
+
+OidInt2
+mkoidint2(Oid v_oid, uint16 v_int2)
+{
+    OidInt2 o;
+    
+    o = (OidInt2) palloc(sizeof(OidInt2Data));
+    o->oi_oid = v_oid;
+    o->oi_int2 = v_int2;
+    return (o);
+}
+
diff --git a/src/backend/utils/adt/oidint4.c b/src/backend/utils/adt/oidint4.c
new file mode 100644 (file)
index 0000000..812522b
--- /dev/null
@@ -0,0 +1,111 @@
+/*-------------------------------------------------------------------------
+ *
+ * oidint4.c--
+ *    Functions for the built-in type "oidint4".
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>             /* for sprintf() */
+#include "postgres.h"
+#include "utils/palloc.h"
+#include "utils/builtins.h"
+#include "utils/oidcompos.h"   /* where function declarations go */
+
+OidInt4 oidint4in(char *o)
+{
+    OidInt4 oi;
+    char *p;
+    
+    oi = (OidInt4) palloc(sizeof(OidInt4Data));
+    
+    for (p = o; *p != '\0' && *p != '/'; p++)
+       continue;
+    
+    oi->oi_oid = (Oid) pg_atoi(o, sizeof(Oid), '/');
+    if (*p == '\0') {
+       oi->oi_int4 = 0;
+    } else {
+       oi->oi_int4 = pg_atoi(++p, sizeof(int4), '\0');
+    }
+    
+    return (oi);
+}
+
+char *oidint4out(OidInt4 o)
+{
+    char *r;
+    
+    /*
+     * -2147483647/-2147483647 
+     * 0        1         2
+     * 123456789012345678901234
+     */
+    r = (char *) palloc(24);
+    sprintf(r, "%d/%d", o->oi_oid, o->oi_int4);
+    
+    return (r);
+}
+
+bool oidint4lt(OidInt4 o1, OidInt4 o2)
+{
+    return
+       ((bool) (o1->oi_oid < o2->oi_oid ||
+                (o1->oi_oid == o2->oi_oid && o1->oi_int4 < o2->oi_int4)));
+}
+
+bool oidint4le(OidInt4 o1, OidInt4 o2)
+{
+    return ((bool) (o1->oi_oid < o2->oi_oid ||
+                   (o1->oi_oid == o2->oi_oid && o1->oi_int4 <= o2->oi_int4)));
+}
+
+bool oidint4eq(OidInt4 o1, OidInt4 o2)
+{
+    return ((bool) (o1->oi_oid == o2->oi_oid && o1->oi_int4 == o2->oi_int4));
+}
+
+bool oidint4ge(OidInt4 o1, OidInt4 o2)
+{
+    return ((bool) (o1->oi_oid > o2->oi_oid ||
+                   (o1->oi_oid == o2->oi_oid && o1->oi_int4 >= o2->oi_int4)));
+}
+
+bool oidint4gt(OidInt4 o1, OidInt4 o2)
+{
+    return ((bool) (o1->oi_oid > o2->oi_oid ||
+                   (o1->oi_oid == o2->oi_oid && o1->oi_int4 > o2->oi_int4)));
+}
+
+bool oidint4ne(OidInt4 o1, OidInt4 o2)
+{
+    return ((bool) (o1->oi_oid != o2->oi_oid || o1->oi_int4 != o2->oi_int4));
+}
+
+int oidint4cmp(OidInt4 o1, OidInt4 o2)
+{
+    if (oidint4lt(o1, o2))
+       return (-1);
+    else if (oidint4eq(o1, o2))
+       return (0);
+    else
+       return (1);
+}
+
+OidInt4 mkoidint4(Oid v_oid, uint32 v_int4)
+{
+    OidInt4 o;
+    
+    o = (OidInt4) palloc(sizeof(OidInt4Data));
+    o->oi_oid = v_oid;
+    o->oi_int4 = v_int4;
+    return (o);
+}
+
+
+
diff --git a/src/backend/utils/adt/oidname.c b/src/backend/utils/adt/oidname.c
new file mode 100644 (file)
index 0000000..18f5136
--- /dev/null
@@ -0,0 +1,123 @@
+/*-------------------------------------------------------------------------
+ *
+ * oidname.c--
+ *    adt for multiple key indices involving oid and name.  Used for cache
+ *    index scans (could also be used in the general case with name).
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <string.h>
+
+#include "postgres.h"
+#include "utils/oidcompos.h"   /* where function declarations go */
+#include "utils/builtins.h"    /* for pg_atoi() */
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+OidName
+oidnamein(char *inStr)
+{
+    OidName oc;
+    char *inptr;
+    
+    oc = (OidName) palloc(sizeof(OidNameData));
+    
+    memset(oc, 0, sizeof(OidNameData)); 
+    for (inptr = inStr; *inptr && *inptr != ','; inptr++)
+       ;
+    
+    if (*inptr) {
+       oc->id = (Oid) pg_atoi(inStr, sizeof(Oid), ',');
+       /* copy one less to ensure null-padding */
+       strncpy(oc->name.data,++inptr,NAMEDATALEN-1);
+       /* namestrcpy(&oc->name, ++inptr); */
+    }else
+       elog(WARN, "Bad input data for type oidname");
+    
+    return oc;
+}
+
+char *
+oidnameout(OidName oidname)
+{
+    char buf[30+NAMEDATALEN];  /* oidname length + oid length + some safety */
+    char *res;
+    
+    sprintf(buf, "%d,%s", oidname->id, oidname->name.data);
+    res = pstrdup(buf);
+    return(res);
+}
+
+bool
+oidnamelt(OidName o1, OidName o2)
+{
+    return (bool)
+       (o1->id < o2->id ||
+        (o1->id == o2->id && namecmp(&o1->name, &o2->name) < 0));
+}
+
+bool
+oidnamele(OidName o1, OidName o2)
+{
+    return (bool)
+       (o1->id < o2->id ||
+        (o1->id == o2->id && namecmp(&o1->name,&o2->name) <= 0));
+}
+
+bool
+oidnameeq(OidName o1, OidName o2)
+{
+    return (bool)
+       (o1->id == o2->id &&
+        (namecmp(&o1->name, &o2->name) == 0));
+}
+
+bool
+oidnamene(OidName o1, OidName o2)
+{
+    return (bool)
+       (o1->id != o2->id ||
+        (namecmp(&o1->name,&o2->name) != 0));
+}
+
+bool
+oidnamege(OidName o1, OidName o2)
+{
+    return (bool) (o1->id > o2->id || (o1->id == o2->id && 
+                                      namecmp(&o1->name, &o2->name) >= 0));
+}
+
+bool
+oidnamegt(OidName o1, OidName o2)
+{
+    return (bool) (o1->id > o2->id ||  (o1->id == o2->id && 
+                                       namecmp(&o1->name, &o2->name) > 0)); 
+}
+
+int
+oidnamecmp(OidName o1, OidName o2)
+{
+    if (o1->id == o2->id)
+       return (namecmp(&o1->name,&o2->name));
+    
+    return (o1->id < o2->id) ? -1 : 1;
+}
+
+OidName
+mkoidname(Oid id, char *name)
+{
+    OidName oidname;
+    
+    oidname = (OidName) palloc(sizeof(Oid)+NAMEDATALEN);
+    
+    oidname->id = id;
+    namestrcpy(&oidname->name,name);
+    return oidname;
+}
diff --git a/src/backend/utils/adt/regexp.c b/src/backend/utils/adt/regexp.c
new file mode 100644 (file)
index 0000000..92ed5f0
--- /dev/null
@@ -0,0 +1,343 @@
+/*-------------------------------------------------------------------------
+ *
+ * regexp.c--
+ *    regular expression handling code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *      Alistair Crooks added the code for the regex caching
+ *     agc - cached the regular expressions used - there's a good chance
+ *     that we'll get a hit, so this saves a compile step for every
+ *     attempted match. I haven't actually measured the speed improvement,
+ *     but it `looks' a lot quicker visually when watching regression
+ *     test output.
+ *
+ *     agc - incorporated Keith Bostic's Berkeley regex code into
+ *     the tree for all ports. To distinguish this regex code from any that
+ *     is existent on a platform, I've prepended the string "pg95_" to
+ *     the functions regcomp, regerror, regexec and regfree.
+ *     Fixed a bug that was originally a typo by me, where `i' was used
+ *     instead of `oldest' when compiling regular expressions - benign
+ *     results mostly, although occasionally it bit you...
+ *
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"          /* postgres system include file */
+#include "utils/elog.h"                /* for logging postgres errors */
+#include "utils/palloc.h"
+#include "utils/builtins.h"    /* where the function declarations go */
+
+#if defined(DISABLE_XOPEN_NLS)
+#undef _XOPEN_SOURCE
+#endif /* DISABLE_XOPEN_NLS */
+
+#ifndef WIN32
+
+#include <sys/types.h>
+#include <regex.h>
+
+#endif /* WIN32 why is this necessary? */
+
+/* this is the number of cached regular expressions held. */
+#ifndef MAX_CACHED_RES
+#define MAX_CACHED_RES 32
+#endif
+
+/* this structure describes a cached regular expression */
+struct cached_re_str {
+        struct varlena  *cre_text;      /* pattern as a text* */
+       char            *cre_s;         /* pattern as null-terminated string */
+       int             cre_type;       /* compiled-type: extended,icase etc */
+       regex_t         cre_re;         /* the compiled regular expression */
+       unsigned long   cre_lru;        /* lru tag */
+};
+
+static int                     rec = 0;                /* # of cached re's */
+static struct cached_re_str    rev[MAX_CACHED_RES];    /* cached re's */
+static unsigned long           lru;                    /* system lru tag */
+
+/* attempt to compile `re' as an re, then match it against text */
+/* cflags - flag to regcomp indicates case sensitivity */
+static int
+RE_compile_and_execute(struct varlena *text_re, char *text, int cflags)
+{
+       int     oldest;
+       int     n;
+       int     i;
+       char    *re;
+       int regcomp_result;
+
+       re = textout(text_re);
+       /* find a previously compiled regular expression */
+       for (i = 0 ; i < rec ; i++) {
+           if (rev[i].cre_s) {
+               if (strcmp(rev[i].cre_s, re) == 0) {
+                   if (rev[i].cre_type == cflags) {
+                       rev[i].cre_lru = ++lru;
+                       pfree(re);
+                       return(pg95_regexec(&rev[i].cre_re,
+                                           text, 0,
+                                           (regmatch_t *) NULL, 0) == 0);
+                   }
+               }
+           }
+       }
+
+
+       /* we didn't find it - make room in the cache for it */
+       if (rec == MAX_CACHED_RES) {
+               /* cache is full - find the oldest entry */
+               for (oldest = 0, i = 1 ; i < rec ; i++) {
+                       if (rev[i].cre_lru < rev[oldest].cre_lru) {
+                               oldest = i;
+                       }
+               }
+       } else {
+               oldest = rec++;
+       }
+
+       /* if there was an old re, then de-allocate the space it used */
+       if (rev[oldest].cre_s != (char *) NULL) {
+               for (lru = i = 0 ; i < rec ; i++) {
+                       rev[i].cre_lru =
+                               (rev[i].cre_lru - rev[oldest].cre_lru) / 2;
+                       if (rev[i].cre_lru > lru) {
+                               lru = rev[i].cre_lru;
+                       }
+               }
+               pg95_regfree(&rev[oldest].cre_re);
+               /* use malloc/free for the cre_s field because the storage
+                  has to persist across transactions */
+               free(rev[oldest].cre_s); 
+       }
+
+       /* compile the re */
+       regcomp_result = pg95_regcomp(&rev[oldest].cre_re, re, cflags);
+       if ( regcomp_result == 0) {
+               n = strlen(re);
+               /* use malloc/free for the cre_s field because the storage
+                  has to persist across transactions */
+               rev[oldest].cre_s = (char *) malloc(n + 1);
+               (void) memmove(rev[oldest].cre_s, re, n);
+               rev[oldest].cre_s[n] = 0;
+               rev[oldest].cre_text = text_re;
+               rev[oldest].cre_lru = ++lru;
+               rev[oldest].cre_type = cflags;
+               pfree(re);
+               /* agc - fixed an old typo here */
+               return(pg95_regexec(&rev[oldest].cre_re, text, 0,
+                                               (regmatch_t *) NULL, 0) == 0);
+       } else {
+           char errMsg[1000];
+           /* re didn't compile */
+           rev[oldest].cre_s = (char *) NULL;
+           pg95_regerror(regcomp_result, &rev[oldest].cre_re, errMsg,
+                         sizeof(errMsg));
+           elog(WARN,"regcomp failed with error %s",errMsg);
+       }
+
+       /* not reached */
+       return(0);
+}
+
+
+
+/*
+ *  interface routines called by the function manager
+ */
+
+/*
+   fixedlen_regexeq:
+
+   a generic fixed length regexp routine
+         s      - the string to match against (not necessarily null-terminated)
+        p      - the pattern
+        charlen   - the length of the string
+*/
+static bool 
+fixedlen_regexeq(char *s, struct varlena* p, int charlen, int cflags)
+{
+    char *sterm;
+    int result;
+    
+    if (!s || !p)
+       return FALSE;
+    
+    /* be sure sterm is null-terminated */
+    sterm = (char *) palloc(charlen + 1);
+    memset(sterm, 0, charlen + 1);
+    strncpy(sterm, s, charlen);
+    
+    result = RE_compile_and_execute(p, sterm, cflags);
+
+    pfree(sterm);
+    
+    return ((bool) result);
+
+}
+
+
+/*
+ *  routines that use the regexp stuff
+ */
+bool 
+char2regexeq(uint16 arg1, struct varlena *p)
+{
+    char *s = (char *) &arg1;
+    return (fixedlen_regexeq(s, p, 2, REG_EXTENDED));
+}
+
+bool 
+char2regexne(uint16 arg1, struct varlena *p)
+{
+    return (!char2regexeq(arg1, p));
+}
+
+bool 
+char4regexeq(uint32 arg1, struct varlena *p)
+{
+    char *s = (char *) &arg1;
+    return (fixedlen_regexeq(s, p, 4, REG_EXTENDED));
+}
+
+bool 
+char4regexne(uint32 arg1, struct varlena *p)
+{
+    return (!char4regexeq(arg1, p));
+}
+
+bool 
+char8regexeq(char *s, struct varlena *p)
+{
+    return (fixedlen_regexeq(s, p, 8, REG_EXTENDED));
+}
+
+bool 
+char8regexne(char *s, struct varlena *p)
+{
+    return (!char8regexeq(s, p));
+}
+
+bool 
+char16regexeq(char *s, struct varlena *p)
+{
+    return (fixedlen_regexeq(s, p, 16, REG_EXTENDED));
+}
+
+bool 
+char16regexne(char *s, struct varlena *p)
+{
+    return (!char16regexeq(s, p));
+}
+
+bool 
+nameregexeq(NameData *n, struct varlena *p)
+{
+    return (fixedlen_regexeq(n->data, p, NAMEDATALEN, REG_EXTENDED));
+}
+bool 
+nameregexne(NameData *s, struct varlena *p)
+{
+    return (!nameregexeq(s, p));
+}
+
+bool 
+textregexeq(struct varlena *s, struct varlena *p)
+{
+    return (fixedlen_regexeq(VARDATA(s), p, VARSIZE(s) - VARHDRSZ, REG_EXTENDED));
+}
+
+bool 
+textregexne(struct varlena *s, struct varlena *p)
+{
+    return (!textregexeq(s, p));
+}
+
+
+/*
+*  routines that use the regexp stuff, but ignore the case.
+ *  for this, we use the REG_ICASE flag to pg95_regcomp
+ */
+bool 
+char2icregexeq(uint16 arg1, struct varlena *p)
+{
+    char *s = (char *) &arg1;
+    return (fixedlen_regexeq(s, p, 2, REG_ICASE | REG_EXTENDED));
+}
+
+  
+bool 
+char2icregexne(uint16 arg1, struct varlena *p)
+{
+    return (!char2icregexeq(arg1, p));
+}
+
+bool 
+char4icregexeq(uint32 arg1, struct varlena *p)
+{
+    char *s = (char *) &arg1;
+    return (fixedlen_regexeq(s, p, 4, REG_ICASE | REG_EXTENDED ));
+}
+
+bool 
+char4icregexne(uint32 arg1, struct varlena *p)
+{
+    return (!char4icregexeq(arg1, p));
+}
+bool 
+char8icregexeq(char *s, struct varlena *p)
+{
+    return (fixedlen_regexeq(s, p, 8, REG_ICASE | REG_EXTENDED));
+}
+
+bool 
+char8icregexne(char *s, struct varlena *p)
+{
+    return (!char8icregexeq(s, p));
+}
+
+bool 
+char16icregexeq(char *s, struct varlena *p)
+{
+    return (fixedlen_regexeq(s, p, 16, REG_ICASE | REG_EXTENDED));
+}
+
+bool 
+char16icregexne(char *s, struct varlena *p)
+{
+    return (!char16icregexeq(s, p));
+}
+
+bool 
+texticregexeq(struct varlena *s, struct varlena *p)
+{
+    return (fixedlen_regexeq(VARDATA(s), p, VARSIZE(s) - VARHDRSZ, 
+                            REG_ICASE | REG_EXTENDED));
+}
+
+bool 
+texticregexne(struct varlena *s, struct varlena *p)
+{
+    return (!texticregexeq(s, p));
+}
+
+bool 
+nameicregexeq(NameData *n, struct varlena *p)
+{
+    return (fixedlen_regexeq(n->data, p, NAMEDATALEN, 
+                            REG_ICASE | REG_EXTENDED));
+}
+bool 
+nameicregexne(NameData *s, struct varlena *p)
+{
+    return (!nameicregexeq(s, p));
+}
+
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
new file mode 100644 (file)
index 0000000..840be6b
--- /dev/null
@@ -0,0 +1,159 @@
+/*-------------------------------------------------------------------------
+ *
+ * regproc.c--
+ *    Functions for the built-in type "RegProcedure".
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+#include "access/heapam.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "utils/tqual.h"       /* for NowTimeQual */
+#include "fmgr.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+#include "catalog/catname.h"
+#include "utils/builtins.h"    /* where function declarations go */
+
+/***************************************************************************** 
+ *   USER I/O ROUTINES                                                       *
+ *****************************************************************************/
+
+/*
+ *     regprocin       - converts "proname" to proid
+ *
+ *     proid of NULL signifies unknown
+ */
+int32 regprocin(char *proname)
+{
+    Relation   proc;
+    HeapScanDesc       procscan;
+    HeapTuple  proctup;
+    ScanKeyData               key;             
+    RegProcedure       result;
+    bool               isnull;
+    
+    if (proname == NULL)
+       return(0);
+    proc = heap_openr(ProcedureRelationName);
+    if (!RelationIsValid(proc)) {
+       elog(WARN, "regprocin: could not open %s",
+            ProcedureRelationName);
+       return(0);
+    }
+    ScanKeyEntryInitialize(&key,
+                          (bits16)0, 
+                          (AttrNumber)1, 
+                          (RegProcedure)F_CHAR16EQ,
+                          (Datum)proname);
+    
+    procscan = heap_beginscan(proc, 0, NowTimeQual, 1, &key);
+    if (!HeapScanIsValid(procscan)) {
+       heap_close(proc);
+       elog(WARN, "regprocin: could not being scan of %s",
+            ProcedureRelationName);
+       return(0);
+    }
+    proctup = heap_getnext(procscan, 0, (Buffer *) NULL);
+    switch (HeapTupleIsValid(proctup)) {
+    case 1:
+       result = (RegProcedure) heap_getattr(proctup,
+                                         InvalidBuffer,
+                                         ObjectIdAttributeNumber,
+                                         RelationGetTupleDescriptor(proc),
+                                         &isnull);
+       if (isnull) {
+           elog(FATAL, "regprocin: null procedure %s", proname);
+       }
+       break;
+    case 0:
+       result = (RegProcedure) 0;
+#ifdef EBUG
+       elog(DEBUG, "regprocin: no such procedure %s", proname);
+#endif /* defined(EBUG) */
+    }
+    heap_endscan(procscan);
+    heap_close(proc);
+    return((int32) result);
+}
+
+/*
+ *     regprocout      - converts proid to "proname"
+ */
+char *regprocout(RegProcedure proid)
+{
+    Relation   proc;
+    HeapScanDesc       procscan;
+    HeapTuple  proctup;
+    char               *result;
+    ScanKeyData        key;
+    
+    result = (char *)palloc(NAMEDATALEN);
+    proc = heap_openr(ProcedureRelationName);
+    if (!RelationIsValid(proc)) {
+       elog(WARN, "regprocout: could not open %s",
+            ProcedureRelationName);
+       return(0);
+    }
+    ScanKeyEntryInitialize(&key,
+                          (bits16)0,
+                          (AttrNumber)ObjectIdAttributeNumber,
+                          (RegProcedure)F_INT4EQ,
+                          (Datum)proid);
+    
+    procscan = heap_beginscan(proc, 0, NowTimeQual, 1, &key);
+    if (!HeapScanIsValid(procscan)) {
+       heap_close(proc);
+       elog(WARN, "regprocin: could not being scan of %s",
+            ProcedureRelationName);
+       return(0);
+    }
+    proctup = heap_getnext(procscan, 0, (Buffer *)NULL);
+    switch (HeapTupleIsValid(proctup)) {
+       char    *s;
+       bool    isnull;
+    case 1:
+       s = (char *) heap_getattr(proctup, InvalidBuffer, 1,
+                                 RelationGetTupleDescriptor(proc), &isnull);
+       if (!isnull) {
+           strncpy(result, s, 16);
+           break;
+       }
+       elog(FATAL, "regprocout: null procedure %d", proid);
+       /*FALLTHROUGH*/
+    case 0:
+       memset(result, 0, 16);
+       result[0] = '-';
+#ifdef EBUG
+       elog(DEBUG, "regprocout: no such procedure %d", proid);
+#endif /* defined(EBUG) */
+    }
+    heap_endscan(procscan);
+    heap_close(proc);
+    return(result);
+}
+
+
+/***************************************************************************** 
+ *   PUBLIC ROUTINES                                                         *
+ *****************************************************************************/
+
+Oid RegprocToOid(RegProcedure rp)
+{
+    return (Oid)rp;
+}
+
+/* (see int.c for comparison/operation routines) */
+
+
+/* ========== PRIVATE ROUTINES ========== */
+
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
new file mode 100644 (file)
index 0000000..5bacda4
--- /dev/null
@@ -0,0 +1,585 @@
+/*-------------------------------------------------------------------------
+ *
+ * selfuncs.c--
+ *    Selectivity functions for system catalogs and builtin types
+ *
+ *    These routines are registered in the operator catalog in the
+ *    "oprrest" and "oprjoin" attributes.
+ *
+ *    XXX check all the functions--I suspect them to be 1-based.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <string.h>
+
+#include "access/heapam.h"
+#include "utils/tqual.h"       /* for NowTimeQual */
+#include "fmgr.h"
+#include "utils/builtins.h"    /* for textout() prototype 
+                                 and where the declarations go */
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+#include "catalog/catname.h"
+#include "utils/syscache.h"
+#include "utils/lsyscache.h"   /* for get_oprrest() */
+#include "catalog/pg_statistic.h"
+
+
+/* N is not a valid var/constant or relation id */
+#define        NONVALUE(N)     ((N) == -1)
+
+/* 
+ * generalize the test for functional index selectivity request
+ */
+#define FunctionalSelectivity(nIndKeys,attNum) (attNum==InvalidAttrNumber)
+
+static int32 getattnvals(Oid relid, AttrNumber attnum);
+static void gethilokey(Oid relid, AttrNumber attnum, Oid opid,
+                      char **high, char **low);
+
+
+/*
+ *     eqsel           - Selectivity of "=" for any data type.
+ */
+float64
+eqsel(Oid opid,
+      Oid relid,
+      AttrNumber attno,
+      char *value,
+      int32 flag)
+{
+    int32              nvals;
+    float64            result;
+    
+    result = (float64) palloc(sizeof(float64data));
+    if (NONVALUE(attno) || NONVALUE(relid))
+       *result = 0.1;
+    else {
+       nvals = getattnvals(relid, (int) attno);
+       if (nvals == 0)
+           *result = 0.0;
+       else
+           *result = 1.0 / nvals;
+    }
+    return(result);
+}
+
+/*
+ *     neqsel          - Selectivity of "!=" for any data type.
+ */
+float64
+neqsel(Oid opid,
+       Oid relid,
+       AttrNumber attno,
+       char *value,
+       int32 flag)
+{
+    float64            result;
+    
+    result = eqsel(opid, relid, attno, value, flag);
+    *result = 1.0 - *result;
+    return(result);
+}
+
+/*
+ *     intltsel        - Selectivity of "<" for integers.
+ *                       Should work for both longs and shorts.
+ */
+float64
+intltsel(Oid opid,
+        Oid relid,
+        AttrNumber attno,
+        int32 value,
+        int32 flag)
+{
+    float64    result;
+    char               *highchar, *lowchar;
+    long               val, high, low, top, bottom;
+    
+    result = (float64) palloc(sizeof(float64data));
+    if (NONVALUE(attno) || NONVALUE(relid))
+       *result = 1.0 / 3;
+    else {
+       /* XXX          val = atol(value);*/
+       val = value;
+       gethilokey(relid, (int) attno, opid, &highchar, &lowchar);
+       if (*highchar == 'n' || *lowchar == 'n') {
+           *result = 1.0/3.0;
+           return (result);
+       }
+       high = atol(highchar);
+       low = atol(lowchar);
+       if ((flag & SEL_RIGHT && val < low) ||
+           (!(flag & SEL_RIGHT) && val > high)) {
+           int nvals;
+           nvals = getattnvals(relid, (int) attno);
+           if (nvals == 0)
+               *result = 1.0 / 3.0;
+           else
+               *result = 3.0 / nvals;
+       }else {
+           bottom = high - low;
+           if (bottom == 0)
+               ++bottom;
+           if (flag & SEL_RIGHT)
+               top = val - low;
+           else
+               top = high - val;
+           if (top > bottom)
+               *result = 1.0;
+           else {
+               if (top == 0)
+                   ++top;
+               *result = ((1.0 * top) / bottom);
+           }
+       }
+    }
+    return(result);
+}
+
+/*
+ *     intgtsel        - Selectivity of ">" for integers.
+ *                       Should work for both longs and shorts.
+ */
+float64
+intgtsel(Oid opid,
+        Oid relid,
+        AttrNumber attno,
+        int32 value,
+        int32 flag)
+{
+    float64            result;
+    int                notflag;
+    
+    if (flag & 0)
+       notflag = flag & ~SEL_RIGHT;
+    else
+       notflag = flag | SEL_RIGHT;
+    result = intltsel(opid, relid, attno, value, (int32) notflag);
+    return(result);
+}
+
+/*
+ *     eqjoinsel       - Join selectivity of "="
+ */
+float64
+eqjoinsel(Oid opid,
+         Oid relid1,
+         AttrNumber attno1,
+         Oid relid2,
+         AttrNumber attno2)
+{
+    float64            result;
+    int32              num1, num2, max;
+    
+    result = (float64) palloc(sizeof(float64data));
+    if (NONVALUE(attno1) || NONVALUE(relid1) ||
+       NONVALUE(attno2) || NONVALUE(relid2))
+       *result = 0.1;
+    else {
+       num1 = getattnvals(relid1, (int) attno1);
+       num2 = getattnvals(relid2, (int) attno2);
+       max = (num1 > num2) ? num1 : num2;
+       if (max == 0)
+           *result = 1.0;
+       else
+           *result = 1.0 / max;
+    }
+    return(result);
+}
+
+/*
+ *     neqjoinsel      - Join selectivity of "!="
+ */
+float64
+neqjoinsel(Oid opid,
+          Oid relid1,
+          AttrNumber attno1,
+          Oid relid2,
+          AttrNumber attno2)
+{
+    float64            result;
+    
+    result = eqjoinsel(opid, relid1, attno1, relid2, attno2);
+    *result = 1.0 - *result;
+    return(result);
+}
+
+/*
+ *     intltjoinsel    - Join selectivity of "<"
+ */
+float64
+intltjoinsel(Oid opid,
+            Oid relid1,
+            AttrNumber attno1,
+            Oid relid2,
+            AttrNumber attno2)
+{
+    float64    result;
+    
+    result = (float64) palloc(sizeof(float64data));
+    *result = 1.0 / 3.0;
+    return(result);
+}
+
+/*
+ *     intgtjoinsel    - Join selectivity of ">"
+ */
+float64
+intgtjoinsel(Oid opid,
+            Oid relid1,
+            AttrNumber attno1,
+            Oid relid2,
+            AttrNumber attno2)
+{
+    float64    result;
+    
+    result = (float64) palloc(sizeof(float64data));
+    *result = 1.0 / 3.0;
+    return(result);
+}
+
+/*
+ *     getattnvals     - Retrieves the number of values within an attribute.
+ *
+ *     Note:
+ *             getattnvals and gethilokey both currently use keyed
+ *             relation scans and amgetattr.  Alternatively,
+ *             the relation scan could be non-keyed and the tuple
+ *             returned could be cast (struct X *) tuple + tuple->t_hoff.
+ *             The first method is good for testing the implementation,
+ *             but the second may ultimately be faster?!?  In any case,
+ *             using the cast instead of amgetattr would be
+ *             more efficient.  However, the cast will not work
+ *             for gethilokey which accesses stahikey in struct statistic.
+ */
+static int32
+getattnvals(Oid relid, AttrNumber attnum)
+{
+    HeapTuple  atp;
+    int                nvals;
+    
+    atp = SearchSysCacheTuple(ATTNUM, 
+                             ObjectIdGetDatum(relid), 
+                             Int16GetDatum(attnum),
+                             0,0);
+    if (!HeapTupleIsValid(atp)) {
+       elog(WARN, "getattnvals: no attribute tuple %d %d",
+            relid, attnum);
+       return(0);
+    }
+    nvals = ((AttributeTupleForm ) GETSTRUCT(atp))->attnvals;
+    if (nvals > 0) return(nvals);
+    
+    atp = SearchSysCacheTuple(RELOID, ObjectIdGetDatum(relid),
+                             0,0,0);
+    /* XXX -- use number of tuples as number of distinctive values
+       just for now, in case number of distinctive values is
+       not cached */
+    if (!HeapTupleIsValid(atp)) {
+       elog(WARN, "getattnvals: no relation tuple %d", relid);
+       return(0);
+    }
+    nvals = ((Form_pg_class) GETSTRUCT(atp))->reltuples;
+    return(nvals);
+}
+
+/*
+ *     gethilokey      - Returns a pointer to strings containing
+ *                       the high and low keys within an attribute.
+ *
+ *     Currently returns "0", and "0" in high and low if the statistic
+ *     catalog does not contain the proper tuple.  Eventually, the
+ *     statistic demon should have the tuple maintained, and it should
+ *     elog() if the tuple is missing.
+ *
+ *     XXX Question: is this worth sticking in the catalog caches,
+ *         or will this get invalidated too often?
+ */
+static void
+gethilokey(Oid relid,
+          AttrNumber attnum,
+          Oid opid,
+          char **high,
+          char **low)
+{
+    register Relation  rdesc;
+    register HeapScanDesc      sdesc;
+    static ScanKeyData key[3] = {
+       { 0, Anum_pg_statistic_starelid, F_OIDEQ },
+       { 0, Anum_pg_statistic_staattnum, F_INT2EQ },
+       { 0, Anum_pg_statistic_staop, F_OIDEQ }
+    };
+    bool               isnull;
+    HeapTuple          tuple;
+    
+    rdesc = heap_openr(StatisticRelationName);
+
+    key[0].sk_argument = ObjectIdGetDatum(relid);
+    key[1].sk_argument = Int16GetDatum((int16) attnum);
+    key[2].sk_argument = ObjectIdGetDatum(opid);
+    sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 3, key);
+    tuple = heap_getnext(sdesc, 0, (Buffer *) NULL);
+    if (!HeapTupleIsValid(tuple)) {
+       *high = "n";
+       *low = "n";
+       /* XXX          elog(WARN, "gethilokey: statistic tuple not found");*/
+       return;
+    }
+    *high = textout((struct varlena *)
+                   heap_getattr(tuple,
+                                InvalidBuffer,
+                                Anum_pg_statistic_stahikey,
+                                RelationGetTupleDescriptor(rdesc),
+                                &isnull));
+    if (isnull)
+       elog(DEBUG, "gethilokey: high key is null");
+    *low = textout((struct varlena *)
+                  heap_getattr(tuple,
+                               InvalidBuffer,
+                               Anum_pg_statistic_stalokey,
+                               RelationGetTupleDescriptor(rdesc),
+                               &isnull));
+    if (isnull)
+       elog(DEBUG, "gethilokey: low key is null");
+    heap_endscan(sdesc);
+    heap_close(rdesc);
+}
+
+float64
+btreesel(Oid operatorObjectId,
+        Oid indrelid,
+        AttrNumber attributeNumber,
+        char *constValue,
+        int32 constFlag,
+        int32 nIndexKeys,
+        Oid indexrelid)
+{
+    float64 result;
+    float64data resultData;
+    
+    if (FunctionalSelectivity(nIndexKeys, attributeNumber)) {
+       /*
+        * Need to call the functions selectivity
+        * function here.  For now simply assume it's
+        * 1/3 since functions don't currently
+        * have selectivity functions
+        */
+       resultData = 1.0 / 3.0;
+       result = &resultData;
+    }
+    else {
+       result = (float64)fmgr(get_oprrest (operatorObjectId),
+                              (char*)operatorObjectId,
+                              (char*)indrelid,
+                              (char*)attributeNumber,
+                              (char*)constValue,
+                              (char*)constFlag,
+                              NULL);
+    }
+    
+    if (!PointerIsValid(result))
+       elog(WARN, "Btree Selectivity: bad pointer");
+    if (*result < 0.0 || *result > 1.0)
+       elog(WARN, "Btree Selectivity: bad value %lf", *result);
+    
+    return(result);
+}
+
+float64
+btreenpage(Oid operatorObjectId,
+          Oid indrelid,
+          AttrNumber attributeNumber,
+          char *constValue,
+          int32 constFlag,
+          int32 nIndexKeys,
+          Oid indexrelid)
+{
+    float64 temp, result;
+    float64data tempData;
+    HeapTuple atp;
+    int npage;
+    
+    if (FunctionalSelectivity(nIndexKeys, attributeNumber)) {
+       /*
+        * Need to call the functions selectivity
+        * function here.  For now simply assume it's
+        * 1/3 since functions don't currently
+        * have selectivity functions
+        */
+       tempData = 1.0 / 3.0;
+       temp = &tempData;
+    }
+    else {
+       temp = (float64)fmgr(get_oprrest (operatorObjectId),
+                            (char*)operatorObjectId,
+                            (char*)indrelid,
+                            (char*)attributeNumber,
+                            (char*)constValue,
+                            (char*)constFlag,
+                            NULL);
+    }
+    atp = SearchSysCacheTuple(RELOID, 
+                             ObjectIdGetDatum(indexrelid),
+                             0,0,0);
+    if (!HeapTupleIsValid(atp)) {
+       elog(WARN, "btreenpage: no index tuple %d", indexrelid);
+       return(0);
+    }
+    
+    npage = ((Form_pg_class) GETSTRUCT(atp))->relpages;
+    result = (float64)palloc(sizeof(float64data));
+    *result = *temp * npage;
+    return(result);
+}
+
+float64
+hashsel(Oid operatorObjectId,
+       Oid indrelid,
+       AttrNumber attributeNumber,
+       char *constValue,
+       int32 constFlag,
+       int32 nIndexKeys,
+       Oid indexrelid)
+{
+    
+    float64 result;
+    float64data resultData;
+    HeapTuple atp;
+    int ntuples;
+    
+    if (FunctionalSelectivity(nIndexKeys, attributeNumber)) {
+       /*
+        * Need to call the functions selectivity
+        * function here.  For now simply use 1/Number of Tuples
+        * since functions don't currently
+        * have selectivity functions
+        */
+       
+       atp = SearchSysCacheTuple(RELOID, ObjectIdGetDatum(indexrelid),
+                                 0,0,0);
+       if (!HeapTupleIsValid(atp)) {
+           elog(WARN, "hashsel: no index tuple %d", indexrelid);
+           return(0);
+       }
+       ntuples = ((Form_pg_class) GETSTRUCT(atp))->reltuples;
+       if (ntuples > 0) {
+           resultData =  1.0 / (float64data) ntuples;
+       }
+       else {
+           resultData = (float64data) (1.0 / 100.0);
+       }
+       result = &resultData;
+       
+    }
+    else {
+       result = (float64)fmgr(get_oprrest (operatorObjectId),
+                              (char*)operatorObjectId,
+                              (char*)indrelid,
+                              (char*)attributeNumber,
+                              (char*)constValue,
+                              (char*)constFlag,
+                              NULL);
+    }
+    
+    if (!PointerIsValid(result))
+       elog(WARN, "Hash Table Selectivity: bad pointer");
+    if (*result < 0.0 || *result > 1.0)
+       elog(WARN, "Hash Table Selectivity: bad value %lf", *result);
+    
+    return(result);
+    
+    
+}
+
+float64
+hashnpage(Oid operatorObjectId,
+         Oid indrelid,
+         AttrNumber attributeNumber,
+         char *constValue,
+         int32 constFlag,
+         int32 nIndexKeys,
+         Oid indexrelid)
+{
+    float64 temp, result;
+    float64data tempData;
+    HeapTuple atp;
+    int npage;
+    int ntuples;
+    
+    atp = SearchSysCacheTuple(RELOID, ObjectIdGetDatum(indexrelid),
+                             0,0,0);
+    if (!HeapTupleIsValid(atp)) {
+       elog(WARN, "hashsel: no index tuple %d", indexrelid);
+       return(0);
+    }
+    
+    
+    if (FunctionalSelectivity(nIndexKeys, attributeNumber)) {
+       /*
+        * Need to call the functions selectivity
+        * function here.  For now, use 1/Number of Tuples
+        * since functions don't currently
+        * have selectivity functions
+        */
+       
+       ntuples = ((Form_pg_class) GETSTRUCT(atp))->reltuples;
+       if (ntuples > 0) {
+           tempData =  1.0 / (float64data) ntuples;
+       }
+       else {
+           tempData = (float64data) (1.0 / 100.0);
+       }
+       temp = &tempData;
+       
+    }
+    else {
+       temp = (float64)fmgr(get_oprrest (operatorObjectId),
+                            (char*)operatorObjectId,
+                            (char*)indrelid,
+                            (char*)attributeNumber,
+                            (char*)constValue,
+                            (char*)constFlag,
+                            NULL);
+    }
+    
+    npage = ((Form_pg_class) GETSTRUCT(atp))->relpages;
+    result = (float64)palloc(sizeof(float64data));
+    *result = *temp * npage;
+    return(result);
+}
+
+
+float64
+rtsel(Oid operatorObjectId,
+      Oid indrelid,
+      AttrNumber attributeNumber,
+      char *constValue,
+      int32 constFlag,
+      int32 nIndexKeys,
+      Oid indexrelid)
+{
+    return (btreesel(operatorObjectId, indrelid, attributeNumber,
+                    constValue, constFlag, nIndexKeys, indexrelid));
+}
+
+float64
+rtnpage(Oid operatorObjectId,
+       Oid indrelid,
+       AttrNumber attributeNumber,
+       char *constValue,
+       int32 constFlag,
+       int32 nIndexKeys,
+       Oid indexrelid)
+{
+    return (btreenpage(operatorObjectId, indrelid, attributeNumber,
+                      constValue, constFlag, nIndexKeys, indexrelid));
+}
diff --git a/src/backend/utils/adt/sets.c b/src/backend/utils/adt/sets.c
new file mode 100644 (file)
index 0000000..58686bb
--- /dev/null
@@ -0,0 +1,164 @@
+/*-------------------------------------------------------------------------
+ *
+ * sets.c--
+ *    Functions for sets, which are defined by queries.
+ *    Example:   a set is defined as being the result of the query
+ *          retrieve (X.all)
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>             /* for sprintf() */
+#include "postgres.h"
+#include "utils/elog.h"
+#include "nodes/pg_list.h"          /* for LispValue and List */
+#include "access/htup.h"            /* for HeapTuple */
+#include "access/heapam.h"
+#include "access/relscan.h"
+#include "access/xact.h"
+#include "catalog/pg_proc.h"        /* for Form_pg_proc */
+#include "utils/syscache.h"         /* for PROOID */
+#include "catalog/catname.h"        /* for ProcedureRelationName */
+#include "catalog/indexing.h"       /* for Num_pg_proc_indices */
+#include "storage/lmgr.h"
+#include "utils/sets.h"             /* for GENERICSETNAME      */
+#include "tcop/dest.h"
+#include "fmgr.h"
+
+extern CommandDest whereToSendOutput;  /* defined in tcop/postgres.c */
+
+
+/*
+ *    SetDefine        - converts query string defining set to an oid
+ *
+ *    The query string is used to store the set as a function in 
+ *    pg_proc.  The name of the function is then changed to use the
+ *    OID of its tuple in pg_proc.
+ */
+Oid
+SetDefine(char *querystr, char *typename)
+{
+    Oid setoid;
+    char *procname = GENERICSETNAME;
+    char *fileName = "-";
+    char realprocname[16];
+    HeapTuple tup, newtup;
+    Form_pg_proc proc;
+    Relation procrel;
+    int i;
+    Datum replValue[Natts_pg_proc];
+    char replNull[Natts_pg_proc];
+    char repl[Natts_pg_proc];
+    HeapScanDesc pg_proc_scan;
+    Buffer buffer;
+    ItemPointerData ipdata;
+
+    static ScanKeyData oidKey[1] = {
+       { 0, ObjectIdAttributeNumber, ObjectIdEqualRegProcedure }};
+    
+
+    setoid = ProcedureCreate(procname, /* changed below, after oid known */
+                            true,               /* returnsSet */
+                            typename,   /* returnTypeName */
+                            "sql",         /* languageName */
+                            querystr,           /* sourceCode */
+                            fileName,           /* fileName */
+                            false,              /* canCache */
+                            true,               /* trusted */
+                            100,                /* byte_pct */ 
+                            0,                  /* perbyte_cpu */
+                            0,                  /* percall_cpu */
+                            100,                /* outin_ratio */
+                            NIL,                /* argList */
+                            whereToSendOutput);
+    /* Since we're still inside this command of the transaction, we can't
+     * see the results of the procedure definition unless we pretend
+     * we've started the next command.  (Postgres's solution to the
+     * Halloween problem is to not allow you to see the results of your
+     * command until you start the next command.)
+     */
+    CommandCounterIncrement();
+    tup = SearchSysCacheTuple(PROOID,
+                             ObjectIdGetDatum(setoid),
+                             0,0,0);
+    if (!HeapTupleIsValid(tup))
+       elog(WARN, "setin: unable to define set %s", querystr);
+    
+    /* We can tell whether the set was already defined by checking
+     * the name.   If it's GENERICSETNAME, the set is new.  If it's
+     * "set<some oid>" it's already defined.
+     */
+    proc = (Form_pg_proc)GETSTRUCT(tup);
+    if (!strcmp((char*)procname, (char*)&(proc->proname))) {
+       /* make the real proc name */
+       sprintf(realprocname, "set%u", setoid);
+       
+       /* set up the attributes to be modified or kept the same */
+       repl[0] = 'r';
+       for (i = 1; i < Natts_pg_proc; i++) repl[i] = ' ';
+       replValue[0] = (Datum)realprocname;
+       for (i = 1; i < Natts_pg_proc; i++) replValue[i] = (Datum)0;
+       for (i = 0; i < Natts_pg_proc; i++) replNull[i] = ' ';
+       
+       /* change the pg_proc tuple */
+       procrel = heap_openr(ProcedureRelationName);
+       RelationSetLockForWrite(procrel);
+       fmgr_info(ObjectIdEqualRegProcedure, 
+                 &oidKey[0].sk_func, 
+                 &oidKey[0].sk_nargs);
+       oidKey[0].sk_argument = ObjectIdGetDatum(setoid);
+       pg_proc_scan = heap_beginscan(procrel,
+                                     0,
+                                     SelfTimeQual,
+                                     1,
+                                     oidKey);
+       tup = heap_getnext(pg_proc_scan, 0, &buffer);
+       if (HeapTupleIsValid(tup)) {
+           newtup = heap_modifytuple(tup, 
+                                     buffer, 
+                                     procrel, 
+                                     replValue, 
+                                     replNull, 
+                                     repl);
+           
+           /* XXX may not be necessary */
+           ItemPointerCopy(&tup->t_ctid, &ipdata);
+           
+           setheapoverride(true);
+           (void) heap_replace(procrel, &ipdata, newtup);
+           setheapoverride(false);
+           
+           setoid = newtup->t_oid;
+       } else
+           elog(WARN, "setin: could not find new set oid tuple");
+       heap_endscan(pg_proc_scan);
+       
+       if (RelationGetRelationTupleForm(procrel)->relhasindex)
+           {
+               Relation idescs[Num_pg_proc_indices];
+               
+               CatalogOpenIndices(Num_pg_proc_indices, Name_pg_proc_indices, idescs);
+               CatalogIndexInsert(idescs, Num_pg_proc_indices, procrel, newtup);
+               CatalogCloseIndices(Num_pg_proc_indices, idescs);
+           }
+       RelationUnsetLockForWrite(procrel);
+       heap_close(procrel);
+    }
+    return setoid;
+}
+
+/* This function is a placeholder.  The parser uses the OID of this
+ * function to fill in the :funcid field  of a set.  This routine is
+ * never executed.  At runtime, the OID of the actual set is substituted
+ * into the :funcid.
+ */
+int
+seteval(Oid funcoid)
+{
+    return 17;
+}
diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c
new file mode 100644 (file)
index 0000000..f68b97d
--- /dev/null
@@ -0,0 +1,92 @@
+/*-------------------------------------------------------------------------
+ *
+ * tid.c--
+ *    Functions for the built-in type tuple id
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    input routine largely stolen from boxin().
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>             /* for sprintf() */
+#include <string.h>
+#include "postgres.h"
+#include "storage/block.h"
+#include "storage/off.h"
+#include "storage/itemptr.h"
+#include "storage/bufpage.h"
+
+#include "utils/palloc.h"
+#include "utils/builtins.h"    /* where function declarations go */
+
+
+#define LDELIM         '('
+#define RDELIM         ')'
+#define        DELIM           ','
+#define NTIDARGS       2
+
+/* ----------------------------------------------------------------
+ *     tidin
+ * ----------------------------------------------------------------
+ */
+ItemPointer
+tidin(char *str)
+{
+    char               *p, *coord[NTIDARGS];
+    int                        i;
+    ItemPointer                result;
+    
+    BlockNumber        blockNumber;
+    OffsetNumber       offsetNumber;
+    
+    if (str == NULL)
+       return NULL;
+    
+    for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++)
+       if (*p == DELIM || (*p == LDELIM && !i))
+           coord[i++] = p + 1;
+    
+    if (i < NTIDARGS - 1)
+       return NULL;
+    
+    blockNumber =      (BlockNumber)   atoi(coord[0]);
+    offsetNumber =     (OffsetNumber)  atoi(coord[1]);
+    
+    result = (ItemPointer) palloc(sizeof(ItemPointerData));
+    
+    ItemPointerSet(result, blockNumber, offsetNumber);
+    
+    return result;
+}
+
+/* ----------------------------------------------------------------
+ *     tidout
+ * ----------------------------------------------------------------
+ */
+char *
+tidout(ItemPointer itemPtr)
+{
+    BlockNumber        blockNumber;
+    OffsetNumber       offsetNumber;
+    BlockId            blockId;
+    char               buf[32];
+    char               *str;
+    
+    blockId =   &(itemPtr->ip_blkid);
+    
+    blockNumber =  BlockIdGetBlockNumber(blockId);
+    offsetNumber = itemPtr->ip_posid;
+    
+    sprintf(buf, "(%d,%d)", blockNumber, offsetNumber);
+    
+    str = (char *) palloc(strlen(buf)+1);
+    strcpy(str, buf);
+    
+    return str;
+}
diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
new file mode 100644 (file)
index 0000000..5c0c163
--- /dev/null
@@ -0,0 +1,496 @@
+/*-------------------------------------------------------------------------
+ *
+ * char.c--
+ *    Functions for the built-in type char() and varchar().
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>             /* for sprintf() */
+#include <string.h>
+#include "postgres.h"
+#include "utils/palloc.h"
+#include "utils/elog.h"
+
+/*
+ * CHAR() and VARCHAR() types are part of the ANSI SQL standard. CHAR()
+ * is for blank-padded string whose length is specified in CREATE TABLE.
+ * VARCHAR is for storing string whose length is at most the length specified
+ * at CREATE TABLE time.
+ *
+ * It's hard to implement these types because we cannot figure out what
+ * the length of the type from the type itself. I change (hopefully all) the
+ * fmgr calls that invoke input functions of a data type to supply the
+ * length also. (eg. in INSERTs, we have the tupleDescriptor which contains
+ * the length of the attributes and hence the exact length of the char() or
+ * varchar(). We pass this to bpcharin() or varcharin().) In the case where
+ * we cannot determine the length, we pass in -1 instead and the input string
+ * must be null-terminated.
+ *
+ * We actually implement this as a varlena so that we don't have to pass in
+ * the length for the comparison functions. (The difference between "text"
+ * is that we truncate and possibly blank-pad the string at insertion time.)
+ *
+ *                                                            - ay 6/95
+ */
+
+
+/***************************************************************************** 
+ *   bpchar - char()                                                         *
+ *****************************************************************************/
+
+/*
+ * bpcharin -
+ *    converts a string of char() type to the internal representation.
+ *    len is the length specified in () plus 4 bytes. (XXX dummy is here
+ *    because we pass typelem as the second argument for array_in.)
+ */
+char *
+bpcharin(char *s, int dummy, int typlen)
+{
+    char *result, *r;
+    int        len = typlen - 4;
+    int i;
+    
+    if (s == NULL)
+       return((char *) NULL);
+
+    if (typlen == -1) {
+       /*
+        * this is here because some functions can't supply the typlen
+        */
+       len = strlen(s);
+       typlen = len + 4;
+    }
+    
+    if (len < 1 || len > 4096)
+       elog(WARN, "bpcharin: length of char() must be between 1 and 4096");
+    
+    result = (char *) palloc(typlen);
+    *(int32*)result = typlen;
+    r = result + 4;
+    for(i=0; i < len; i++, r++, s++) {
+       *r = *s;
+       if (*r == '\0')
+           break;
+    }
+    /* blank pad the string if necessary */
+    for(; i < len; i++) {
+       *r++ = ' ';
+    }
+    return(result);
+}
+
+char *
+bpcharout(char *s)
+{
+    char *result;
+    int len;
+
+    if (s == NULL) {
+       result = (char *) palloc(2);
+       result[0] = '-';
+       result[1] = '\0';
+    } else {
+       len = *(int32*)s - 4;
+       result = (char *) palloc(len+1);
+       strncpy(result, s+4, len);      /* these are blank-padded */
+       result[len] = '\0';
+    }
+    return(result);
+}
+
+/***************************************************************************** 
+ *   varchar - varchar()                                                     *
+ *****************************************************************************/
+
+/*
+ * vcharin -
+ *    converts a string of varchar() type to the internal representation.
+ *    len is the length specified in () plus 4 bytes. (XXX dummy is here
+ *    because we pass typelem as the second argument for array_in.)
+ */
+char *
+varcharin(char *s, int dummy, int typlen)
+{
+    char *result;
+    int        len = typlen - 4;
+    
+    if (s == NULL)
+       return((char *) NULL);
+
+    if (typlen == -1) {
+       /*
+        * this is here because some functions can't supply the typlen
+        */
+       len = strlen(s);
+       typlen = len + 4;
+    }
+    
+    if (len < 1 || len > 4096)
+       elog(WARN, "bpcharin: length of char() must be between 1 and 4096");
+    
+    result = (char *) palloc(typlen);
+    *(int32*)result = typlen;
+    memset(result+4, 0, len);
+    (void) strncpy(result+4, s, len);
+
+    return(result);
+}
+
+char *
+varcharout(char *s)
+{
+    char *result;
+    int len;
+
+    if (s == NULL) {
+       result = (char *) palloc(2);
+       result[0] = '-';
+       result[1] = '\0';
+    } else {
+       len = *(int32*)s - 4;
+       result = (char *) palloc(len+1);
+       memset(result, 0, len+1);
+       strncpy(result, s+4, len);
+    }
+    return(result);
+}
+
+/*****************************************************************************
+ *  Comparison Functions used for bpchar 
+ *****************************************************************************/
+
+static int
+bcTruelen(char *arg)
+{
+    char *s = arg + 4;
+    int i;
+    int len;
+
+    len = *(int32*)arg - 4;
+    for(i=len-1; i >= 0; i--) {
+       if (s[i] != ' ')
+           break;
+    }
+    return (i+1);
+}
+
+int32
+bpchareq(char *arg1, char *arg2)
+{
+    int len1, len2;
+
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    len1 = bcTruelen(arg1);
+    len2 = bcTruelen(arg2);
+
+    if (len1!=len2)
+       return 0;
+    
+    return(strncmp(arg1+4, arg2+4, len1) == 0);
+}
+
+int32
+bpcharne(char *arg1, char *arg2)
+{
+    int len1, len2;
+
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    len1 = bcTruelen(arg1);
+    len2 = bcTruelen(arg2);
+
+    if (len1!=len2)
+       return 1;
+
+    return(strncmp(arg1+4, arg2+4, len1) != 0);
+}
+
+int32
+bpcharlt(char *arg1, char *arg2)
+{
+    int len1, len2;
+    int cmp;
+    
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    len1 = bcTruelen(arg1);
+    len2 = bcTruelen(arg2);
+
+    cmp = strncmp(arg1+4, arg2+4, Min(len1,len2));
+    if (cmp == 0)
+       return (len1<len2);
+    else 
+       return (cmp < 0);
+}
+
+int32
+bpcharle(char *arg1, char *arg2)
+{
+    int len1, len2;
+
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    len1 = bcTruelen(arg1);
+    len2 = bcTruelen(arg2);
+
+    return(strncmp(arg1+4, arg2+4, Min(len1,len2)) <= 0);
+}
+
+int32
+bpchargt(char *arg1, char *arg2)
+{
+    int len1, len2;
+    int cmp;
+
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    len1 = bcTruelen(arg1);
+    len2 = bcTruelen(arg2);
+
+    cmp = strncmp(arg1+4, arg2+4, Min(len1,len2));
+    if (cmp == 0)
+       return (len1 > len2);
+    else
+       return (cmp > 0);
+}
+
+int32
+bpcharge(char *arg1, char *arg2)
+{
+    int len1, len2;
+
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    len1 = bcTruelen(arg1);
+    len2 = bcTruelen(arg2);
+
+    return(strncmp(arg1+4, arg2+4, Min(len1,len2)) >= 0);
+}
+
+int32
+bpcharcmp(char *arg1, char *arg2)
+{
+    int len1, len2;
+
+    len1 = bcTruelen(arg1);
+    len2 = bcTruelen(arg2);
+
+    return(strncmp(arg1+4, arg2+4, Min(len1,len2)));
+}
+
+/*****************************************************************************
+ *  Comparison Functions used for varchar 
+ *****************************************************************************/
+
+static int
+vcTruelen(char *arg)
+{
+    char *s = arg + 4;
+    int i;
+    int len;
+
+    len = *(int32*)arg - 4;
+    for(i=0; i < len; i++) {
+       if (*s++ == '\0')
+           break;
+    }
+    return i;
+}
+
+int32
+varchareq(char *arg1, char *arg2)
+{
+    int len1, len2;
+
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    len1 = vcTruelen(arg1);
+    len2 = vcTruelen(arg2);
+
+    if (len1!=len2)
+       return 0;
+    
+    return(strncmp(arg1+4, arg2+4, len1) == 0);
+}
+
+int32
+varcharne(char *arg1, char *arg2)
+{
+    int len1, len2;
+
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    len1 = vcTruelen(arg1);
+    len2 = vcTruelen(arg2);
+
+    if (len1!=len2)
+       return 1;
+
+    return(strncmp(arg1+4, arg2+4, len1) != 0);
+}
+
+int32
+varcharlt(char *arg1, char *arg2)
+{
+    int len1, len2;
+    int cmp;
+    
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    len1 = vcTruelen(arg1);
+    len2 = vcTruelen(arg2);
+
+    cmp = strncmp(arg1+4, arg2+4, Min(len1,len2));
+    if (cmp == 0)
+       return (len1<len2);
+    else 
+       return (cmp < 0);
+}
+
+int32
+varcharle(char *arg1, char *arg2)
+{
+    int len1, len2;
+
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    len1 = vcTruelen(arg1);
+    len2 = vcTruelen(arg2);
+
+    return(strncmp(arg1+4, arg2+4, Min(len1,len2)) <= 0);
+}
+
+int32
+varchargt(char *arg1, char *arg2)
+{
+    int len1, len2;
+    int cmp;
+
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    len1 = vcTruelen(arg1);
+    len2 = vcTruelen(arg2);
+
+    cmp = strncmp(arg1+4, arg2+4, Min(len1,len2));
+    if (cmp == 0)
+       return (len1 > len2);
+    else
+       return (cmp > 0);
+}
+
+int32
+varcharge(char *arg1, char *arg2)
+{
+    int len1, len2;
+
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    len1 = vcTruelen(arg1);
+    len2 = vcTruelen(arg2);
+
+    return(strncmp(arg1+4, arg2+4, Min(len1,len2)) >= 0);
+}
+
+int32
+varcharcmp(char *arg1, char *arg2)
+{
+    int len1, len2;
+
+    len1 = vcTruelen(arg1);
+    len2 = vcTruelen(arg2);
+
+    return(strncmp(arg1+4, arg2+4, Min(len1,len2)));
+}
+
+/*****************************************************************************
+ * Hash functions (modified from hashtext in access/hash/hashfunc.c)
+ *****************************************************************************/
+
+uint32 hashbpchar(struct varlena *key)
+{
+    int keylen;
+    char *keydata;
+    uint32 n;
+    int loop;
+
+    keydata = VARDATA(key);
+    keylen = bcTruelen((char*)key);
+
+#define HASHC   n = *keydata++ + 65599 * n
+
+    n = 0;
+    if (keylen > 0) {
+       loop = (keylen + 8 - 1) >> 3;
+       
+       switch (keylen & (8 - 1)) {
+       case 0:
+           do {        /* All fall throughs */
+               HASHC;
+           case 7:
+               HASHC;
+           case 6:
+               HASHC;
+           case 5:
+               HASHC;
+           case 4:
+               HASHC;
+           case 3:
+               HASHC;
+           case 2:
+               HASHC;
+           case 1:
+               HASHC;
+           } while (--loop);
+       }
+    }
+    return (n);
+}      
+
+uint32 hashvarchar(struct varlena *key)
+{
+    int keylen;
+    char *keydata;
+    uint32 n;
+    int loop;
+
+    keydata = VARDATA(key);
+    keylen = vcTruelen((char*)key);
+
+#define HASHC   n = *keydata++ + 65599 * n
+
+    n = 0;
+    if (keylen > 0) {
+       loop = (keylen + 8 - 1) >> 3;
+       
+       switch (keylen & (8 - 1)) {
+       case 0:
+           do {        /* All fall throughs */
+               HASHC;
+           case 7:
+               HASHC;
+           case 6:
+               HASHC;
+           case 5:
+               HASHC;
+           case 4:
+               HASHC;
+           case 3:
+               HASHC;
+           case 2:
+               HASHC;
+           case 1:
+               HASHC;
+           } while (--loop);
+       }
+    }
+    return (n);
+}      
+
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
new file mode 100644 (file)
index 0000000..b72274a
--- /dev/null
@@ -0,0 +1,488 @@
+/*-------------------------------------------------------------------------
+ *
+ * varlena.c--
+ *    Functions for the variable-length built-in types.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <ctype.h>
+#include <string.h>
+
+#include "postgres.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/builtins.h"    /* where function declarations go */
+
+/***************************************************************************** 
+ *   USER I/O ROUTINES                                                       *
+ *****************************************************************************/
+
+
+#define        VAL(CH)         ((CH) - '0')
+#define        DIG(VAL)        ((VAL) + '0')
+
+/*
+ *     byteain         - converts from printable representation of byte array
+ *
+ *     Non-printable characters must be passed as '\nnn' (octal) and are
+ *     converted to internal form.  '\' must be passed as '\\'.
+ *     elog(WARN, ...) if bad form.
+ *
+ *     BUGS:
+ *             The input is scaned twice.
+ *             The error checking of input is minimal.
+ */
+struct varlena *
+byteain(char *inputText)
+{
+    register char      *tp;
+    register char      *rp;
+    register int       byte;
+    struct varlena     *result;
+    
+    if (inputText == NULL)
+       elog(WARN, "Bad input string for type bytea");
+    
+    for (byte = 0, tp = inputText; *tp != '\0'; byte++)
+       if (*tp++ == '\\')
+           {
+               if (*tp == '\\')
+                   tp++;
+               else if (!isdigit(*tp++) ||
+                        !isdigit(*tp++) ||
+                        !isdigit(*tp++))
+                   elog(WARN, "Bad input string for type bytea");
+           }
+    tp = inputText;
+    byte += sizeof(int32);                                     /* varlena? */
+    result = (struct varlena *) palloc(byte);
+    result->vl_len = byte;                                     /* varlena? */
+    rp = result->vl_dat;
+    while (*tp != '\0')
+       if (*tp != '\\' || *++tp == '\\')
+           *rp++ = *tp++;
+       else {
+           byte = VAL(*tp++);
+           byte <<= 3;
+           byte += VAL(*tp++);
+           byte <<= 3;
+           *rp++ = byte + VAL(*tp++);
+       }
+    return(result);
+}
+
+/*
+ * Shoves a bunch of memory pointed at by bytes into varlena.
+ * BUGS:  Extremely unportable as things shoved can be string
+ * representations of structs, etc.
+ */
+struct varlena *
+shove_bytes(unsigned char *stuff, int len)
+{
+    struct varlena *result;
+    
+    result = (struct varlena *) palloc(len + sizeof(int32));
+    result->vl_len = len;
+    memmove(result->vl_dat, 
+           stuff + sizeof(int32),
+           len - sizeof(int32)); 
+    return(result);
+}
+
+
+
+/*
+ *     byteaout        - converts to printable representation of byte array
+ *
+ *     Non-printable characters are inserted as '\nnn' (octal) and '\' as
+ *     '\\'.
+ *
+ *     NULL vlena should be an error--returning string with NULL for now.
+ */
+char *
+byteaout(struct varlena        *vlena)
+{
+    register char      *vp;
+    register char      *rp;
+    register int       val;            /* holds unprintable chars */
+    int                i;
+    int                len;
+    static     char    *result;
+    
+    if (vlena == NULL) {
+       result = (char *) palloc(2);
+       result[0] = '-';
+       result[1] = '\0';
+       return(result);
+    }
+    vp = vlena->vl_dat;
+    len = 1;           /* empty string has 1 char */
+    for (i = vlena->vl_len - sizeof(int32); i != 0; i--, vp++) /* varlena? */
+       if (*vp == '\\')
+           len += 2;
+       else if (isascii(*vp) && isprint(*vp))
+           len++;
+       else
+           len += 4;
+    rp = result = (char *) palloc(len);
+    vp = vlena->vl_dat;
+    for (i = vlena->vl_len - sizeof(int32); i != 0; i--)       /* varlena? */
+       if (*vp == '\\') {
+           *vp++;
+           *rp++ = '\\';
+           *rp++ = '\\';
+       } else if (isascii(*vp) && isprint(*vp))
+           *rp++ = *vp++;
+       else {
+           val = *vp++;
+           *rp = '\\';
+           rp += 3;
+           *rp-- = DIG(val & 07);
+           val >>= 3;
+           *rp-- = DIG(val & 07);
+           val >>= 3;
+           *rp = DIG(val & 03);
+           rp += 3;
+       }
+    *rp = '\0';
+    return(result);
+}
+
+
+/*
+ *     textin          - converts "..." to internal representation
+ */
+struct varlena *
+textin(char *inputText)
+{
+    struct varlena     *result;
+    int                len;
+    
+    if (inputText == NULL)
+       return(NULL);
+    len = strlen(inputText) + VARHDRSZ;                
+    result = (struct varlena *) palloc(len);
+    VARSIZE(result) = len;
+    memmove(VARDATA(result), inputText, len - VARHDRSZ);       
+    return(result);
+}
+
+/*
+ *     textout         - converts internal representation to "..."
+ */
+char *
+textout(struct varlena *vlena)
+{
+    int        len;
+    char *result;
+    
+    if (vlena == NULL) {
+       result = (char *) palloc(2);
+       result[0] = '-';
+       result[1] = '\0';
+       return(result);
+    }
+    len = VARSIZE(vlena) - VARHDRSZ;
+    result = (char *) palloc(len + 1);
+    memmove(result, VARDATA(vlena), len);
+    result[len] = '\0';
+    return(result);
+}
+
+
+/* ========== PUBLIC ROUTINES ========== */
+
+/*
+ * textcat -
+ *    takes two text* and returns a text* that is the concatentation of 
+ *  the two
+ */
+text* 
+textcat(text* t1, text* t2)
+{
+    int newlen;
+    char *str1, *str2;
+    text* result;
+
+    if (t1 == NULL) return t2;
+    if (t2 == NULL) return t1;
+
+    /* since t1, and t2 are non-null, str1 and str2 must also be non-null */
+    str1 = textout(t1);
+    str2 = textout(t2);
+    /* we use strlen here to calculate the length because the size fields
+       of t1, t2 may be longer than necessary to hold the string */
+    newlen = strlen(str1) + strlen(str2) + VARHDRSZ;
+    result = (text*)palloc(newlen);
+    strcpy(VARDATA(result), str1);
+    strncat(VARDATA(result), str2, newlen - VARHDRSZ);
+    /* [TRH] Was:
+      strcat(VARDATA(result), str2);
+      which may corrupt the malloc arena due to writing trailing \0. */
+
+    pfree(str1);
+    pfree(str2);
+    return result;
+}
+
+/*
+ *     texteq          - returns 1 iff arguments are equal
+ *     textne          - returns 1 iff arguments are not equal
+ */
+int32
+texteq(struct varlena *arg1, struct varlena *arg2)
+{
+    register int       len;
+    register char      *a1p, *a2p;
+    
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) NULL);
+    if ((len = arg1->vl_len) != arg2->vl_len)
+       return((int32) 0);
+    a1p = arg1->vl_dat;
+    a2p = arg2->vl_dat;
+    /*
+     * Varlenas are stored as the total size (data + size variable)
+     * followed by the data.  The size variable is an int32 so the
+     * length of the data is the total length less sizeof(int32)
+     */
+    len -= sizeof(int32);
+    while (len-- != 0)
+       if (*a1p++ != *a2p++)
+           return((int32) 0);
+    return((int32) 1);
+}
+
+int32
+textne(struct varlena *arg1, struct varlena *arg2) 
+{
+    return((int32) !texteq(arg1, arg2));
+}
+
+int32
+text_lt(struct varlena *arg1, struct varlena *arg2) 
+{
+    int len;
+    char *a1p, *a2p;
+    
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    
+    a1p = VARDATA(arg1);
+    a2p = VARDATA(arg2);
+    
+    if ((len = arg1->vl_len) > arg2->vl_len)
+       len = arg2->vl_len;
+    len -= sizeof(int32);
+    
+    while (len != 0 && *a1p == *a2p)
+       {
+           a1p++;
+           a2p++;
+           len--;
+       }
+    if (len)
+       return (int32) (*a1p < *a2p);
+    else
+       return (int32) (arg1->vl_len < arg2->vl_len);
+}
+
+int32
+text_le(struct varlena *arg1, struct varlena *arg2) 
+{
+    int len;
+    char *a1p, *a2p;
+    
+    if (arg1 == NULL || arg2 == NULL)
+       return((int32) 0);
+    
+    a1p = VARDATA(arg1);
+    a2p = VARDATA(arg2);
+    
+    if ((len = arg1->vl_len) > arg2->vl_len)
+       len = arg2->vl_len;
+    len -= sizeof(int32);                                      /* varlena! */
+    
+    while (len != 0 && *a1p == *a2p)
+       {
+           a1p++;
+           a2p++;
+           len--;
+       }
+    if (len)
+       return (int32) (*a1p < *a2p);
+    else
+       return ((int32) VARSIZE(arg1) <= VARSIZE(arg2));
+}
+
+int32
+text_gt(struct varlena *arg1, struct varlena *arg2) 
+{
+    return ((int32) !text_le(arg1, arg2));
+}
+
+int32
+text_ge(struct varlena *arg1, struct varlena *arg2) 
+{
+    return ((int32) !text_lt(arg1, arg2));
+}
+
+/*-------------------------------------------------------------
+ * byteaGetSize
+ *
+ * get the number of bytes contained in an instance of type 'bytea'
+ *-------------------------------------------------------------
+ */
+int32
+byteaGetSize(struct varlena *v)
+{
+    register int len;
+    
+    len = v->vl_len - sizeof(v->vl_len);
+    
+    return(len);
+}
+
+/*-------------------------------------------------------------
+ * byteaGetByte
+ *
+ * this routine treats "bytea" as an array of bytes.
+ * It returns the Nth byte (a number between 0 and 255) or
+ * it dies if the length of this array is less than n.
+ *-------------------------------------------------------------
+ */
+int32
+byteaGetByte(struct varlena *v, int32 n)
+{
+    int len;
+    int byte;
+    
+    len = byteaGetSize(v);
+    
+    if (n>=len) {
+       elog(WARN, "byteaGetByte: index (=%d) out of range [0..%d]",
+            n,len-1);
+    }
+    
+    byte = (unsigned char) (v->vl_dat[n]);
+    
+    return((int32) byte);
+}
+
+/*-------------------------------------------------------------
+ * byteaGetBit
+ *
+ * This routine treats a "bytea" type like an array of bits.
+ * It returns the value of the Nth bit (0 or 1).
+ * If 'n' is out of range, it dies!
+ *
+ *-------------------------------------------------------------
+ */
+int32
+byteaGetBit(struct varlena *v, int32 n)
+{
+    int byteNo, bitNo;
+    int byte;
+    
+    byteNo = n/8;
+    bitNo = n%8;
+    
+    byte = byteaGetByte(v, byteNo);
+    
+    if (byte & (1<<bitNo)) {
+       return((int32)1);
+    } else {
+       return((int32)0);
+    }
+}
+/*-------------------------------------------------------------
+ * byteaSetByte
+ *
+ * Given an instance of type 'bytea' creates a new one with
+ * the Nth byte set to the given value.
+ *
+ *-------------------------------------------------------------
+ */
+struct varlena *
+byteaSetByte(struct varlena *v, int32 n, int32 newByte)
+{
+    int len;
+    struct varlena *res;
+    
+    len = byteaGetSize(v);
+    
+    if (n>=len) {
+       elog(WARN,
+            "byteaSetByte: index (=%d) out of range [0..%d]",
+            n, len-1);
+    }
+    
+    /*
+     * Make a copy of the original varlena.
+     */
+    res = (struct varlena *) palloc(VARSIZE(v));
+    if (res==NULL) {
+       elog(WARN, "byteaSetByte: Out of memory (%d bytes requested)",
+            VARSIZE(v));
+    }
+    memmove((char *)res, (char *)v, VARSIZE(v));
+    
+    /*
+     *  Now set the byte.
+     */
+    res->vl_dat[n] = newByte;
+    
+    return(res);
+}
+
+/*-------------------------------------------------------------
+ * byteaSetBit
+ *
+ * Given an instance of type 'bytea' creates a new one with
+ * the Nth bit set to the given value.
+ *
+ *-------------------------------------------------------------
+ */
+struct varlena *
+byteaSetBit(struct varlena *v, int32 n, int32 newBit)
+{
+    struct varlena *res;
+    int oldByte, newByte;
+    int byteNo, bitNo;
+    
+    /*
+     * sanity check!
+     */
+    if (newBit != 0 && newBit != 1) {
+       elog(WARN, "byteaSetByte: new bit must be 0 or 1");
+    }
+    
+    /*
+     * get the byte where the bit we want is stored.
+     */
+    byteNo = n / 8;
+    bitNo = n % 8;
+    oldByte = byteaGetByte(v, byteNo);
+    
+    /*
+     * calculate the new value for that byte
+     */
+    if (newBit == 0) {
+       newByte = oldByte & (~(1<<bitNo));
+    } else {
+       newByte = oldByte | (1<<bitNo);
+    }
+    
+    /*
+     * NOTE: 'byteaSetByte' creates a copy of 'v' & sets the byte.
+     */
+    res = byteaSetByte(v, byteNo, newByte);
+    
+    return(res);
+}
diff --git a/src/backend/utils/array.h b/src/backend/utils/array.h
new file mode 100644 (file)
index 0000000..8916145
--- /dev/null
@@ -0,0 +1,166 @@
+/*-------------------------------------------------------------------------
+ *
+ * array.h--
+ *    Utilities for the new array code. Contain prototypes from the
+ *    following files:
+ *             utils/adt/arrayfuncs.c
+ *             utils/adt/arrayutils.c
+ *             utils/adt/chunk.c
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    XXX the data array should be LONGALIGN'd -- notice that the array
+ *    allocation code does not allocate the extra space required for this,
+ *    even though the array-packing code does the LONGALIGNs.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef ARRAY_H
+#define ARRAY_H
+
+#include <stdio.h>     /* for FILE (XXX should use File) */
+#include "utils/memutils.h"
+
+typedef struct {
+    int        size;           /* total array size (in bytes) */ 
+    int        ndim;           /* # of dimensions */
+    int        flags;          /* implementation flags */
+} ArrayType;
+
+/*
+ * bitmask of ArrayType flags field:
+ * 1st bit - large object flag
+ * 2nd bit - chunk flag (array is chunked if set)
+ * 3rd,4th,&5th bit - large object type (used only if bit 1 is set)
+ */
+#define        ARR_LOB_FLAG    (0x1)
+#define        ARR_CHK_FLAG    (0x2)
+#define        ARR_OBJ_MASK    (0x1c)
+
+#define ARR_FLAGS(a)           ((ArrayType *) a)->flags
+#define        ARR_SIZE(a)             (((ArrayType *) a)->size)
+
+#define ARR_NDIM(a)            (((ArrayType *) a)->ndim)
+#define ARR_NDIM_PTR(a)                (&(((ArrayType *) a)->ndim))
+
+#define ARR_IS_LO(a) \
+       (((ArrayType *) a)->flags & ARR_LOB_FLAG)
+#define SET_LO_FLAG(f,a) \
+       (((ArrayType *) a)->flags |= ((f) ? ARR_LOB_FLAG : 0x0))
+
+#define ARR_IS_CHUNKED(a) \
+       (((ArrayType *) a)->flags & ARR_CHK_FLAG)
+#define SET_CHUNK_FLAG(f,a) \
+       (((ArrayType *) a)->flags |= ((f) ? ARR_CHK_FLAG : 0x0))
+
+#define ARR_OBJ_TYPE(a) \
+       ((ARR_FLAGS(a) & ARR_OBJ_MASK) >> 2)
+#define SET_OBJ_TYPE(f,a) \
+       ((ARR_FLAGS(a)&= ~ARR_OBJ_MASK), (ARR_FLAGS(a)|=((f<<2)&ARR_OBJ_MASK)))
+
+/*
+ * ARR_DIMS returns a pointer to an array of array dimensions (number of
+ * elements along the various array axes).
+ *
+ * ARR_LBOUND returns a pointer to an array of array lower bounds.
+ *
+ * That is: if the third axis of an array has elements 5 through 10, then
+ * ARR_DIMS(a)[2] == 6 and ARR_LBOUND[2] == 5.
+ *
+ * Unlike C, the default lower bound is 1.
+ */
+#define ARR_DIMS(a) \
+       ((int *) (((char *) a) + sizeof(ArrayType)))
+#define ARR_LBOUND(a) \
+       ((int *) (((char *) a) + sizeof(ArrayType) + \
+                 (sizeof(int) * (((ArrayType *) a)->ndim))))
+
+/*
+ * Returns a pointer to the actual array data.
+ */
+#define ARR_DATA_PTR(a) \
+       (((char *) a) + \
+        DOUBLEALIGN(sizeof(ArrayType) + 2 * (sizeof(int) * (a)->ndim)))
+
+/*
+ * The total array header size for an array of dimension n (in bytes).
+ */
+#define ARR_OVERHEAD(n)        \
+       (DOUBLEALIGN(sizeof(ArrayType) + 2 * (n) * sizeof(int)))
+
+/*------------------------------------------------------------------------
+ * Miscellaneous helper definitions and routines for arrayfuncs.c
+ *------------------------------------------------------------------------
+ */
+
+/* #if defined(PORTNAME_irix5) */
+/* #define RETURN_NULL {*isNull = true; return(0); }*/
+/* #else*/ /* PORTNAME_irix5 */
+#define RETURN_NULL {*isNull = true; return(0); }
+/* #endif */ /* PORTNAME_irix5 */ 
+#define NAME_LEN    30
+#define MAX_BUFF_SIZE (1 << 13)
+
+typedef struct {
+    char  lo_name[NAME_LEN];
+    int   C[MAXDIM];
+} CHUNK_INFO;
+
+/*
+ * prototypes for functions defined in arrayfuncs.c
+ */
+extern char *array_in(char *string, Oid element_type);
+extern char *array_out(ArrayType *v, Oid element_type);
+extern char *array_dims(ArrayType *v, bool *isNull);
+extern Datum array_ref(ArrayType *array, int n, int indx[], int reftype,
+                      int elmlen, int arraylen, bool *isNull);
+extern Datum array_clip(ArrayType *array, int n, int upperIndx[],
+                       int lowerIndx[], int reftype, int len, bool *isNull);
+extern char *array_set(ArrayType *array, int n, int indx[], char *dataPtr,
+                      int reftype, int elmlen, int arraylen, bool *isNull);
+extern char *array_assgn(ArrayType *array, int n, int upperIndx[],
+                 int lowerIndx[], ArrayType *newArr, int reftype,
+                 int len, bool *isNull);
+extern int array_eq (ArrayType *array1, ArrayType *array2);
+extern SanityCheckInput(int ndim, int n, int dim[], int lb[], int indx[]);
+extern char *array_seek(char *ptr, int eltsize, int nitems);
+extern int array_read(char *destptr, int eltsize, int nitems, char *srcptr);
+extern int _LOtransfer(char **destfd, int size,        int nitems, char **srcfd,
+               int isSrcLO, int isDestLO);
+
+extern char * _array_newLO(int *fd, int flag);
+
+
+/*
+ * prototypes for functions defined in arrayutils.c
+ * [these names seem to be too generic. Add prefix for arrays? -- AY]
+ */
+
+extern int GetOffset(int n, int dim[], int lb[], int indx[]);
+extern int getNitems(int n, int a[]);
+extern int compute_size(int st[], int endp[], int n, int base);
+extern void mda_get_offset_values(int n, int dist[], int PC[], int span[]);
+extern void mda_get_range(int n, int span[], int st[], int endp[]);
+extern void mda_get_prod(int n, int range[], int P[]);
+extern int tuple2linear(int n, int tup[], int scale[]);
+extern void array2chunk_coord(int n, int C[], int a_coord[], int c_coord[]);
+extern int next_tuple(int n, int curr[], int span[]);
+
+/*
+ * prototypes for functions defined in chunk.c
+ */
+extern char * _ChunkArray(int fd, FILE *afd, int ndim, int dim[], int baseSize,
+                         int *nbytes, char *chunkfile);
+extern int GetChunkSize(FILE *fd, int ndim, int dim[MAXDIM], int baseSize, 
+                       int d[MAXDIM]);
+extern int _ReadChunkArray(int st[], int endp[], int bsize, int fp,
+               char *destfp, ArrayType *array, int isDestLO, bool *isNull);
+extern struct varlena *_ReadChunkArray1El(int st[], int bsize, int fp,
+                                         ArrayType *array, bool *isNull);
+
+
+#endif /*  ARRAY_H */
diff --git a/src/backend/utils/bit.h b/src/backend/utils/bit.h
new file mode 100644 (file)
index 0000000..8ea701a
--- /dev/null
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * bit.h--
+ *    Standard bit array definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        BIT_H
+#define BIT_H
+
+typedef bits8  *BitArray;
+typedef uint32 BitIndex;
+
+#define BitsPerByte    8
+
+/*
+ * BitArraySetBit --
+ *     Sets (to 1) the value of a bit in a bit array.
+ */
+extern void BitArraySetBit(BitArray bitArray, BitIndex bitIndex);
+
+/*
+ * BitArrayClearBit --
+ *     Clears (to 0) the value of a bit in a bit array.
+ */
+extern void BitArrayClearBit(BitArray bitArray, BitIndex bitIndex);
+
+/*
+ * BitArrayBitIsSet --
+ *     True iff the bit is set (1) in a bit array.
+ */
+extern bool BitArrayBitIsSet(BitArray bitArray, BitIndex bitIndex);
+
+#endif /* BIT_H */
diff --git a/src/backend/utils/builtins.h b/src/backend/utils/builtins.h
new file mode 100644 (file)
index 0000000..eca26df
--- /dev/null
@@ -0,0 +1,433 @@
+/*-------------------------------------------------------------------------
+ *
+ * builtins.h--
+ *    Declarations for operations on built-in types.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    This should normally only be included by fmgr.h.
+ *    Under no circumstances should it ever be included before 
+ *    including fmgr.h!
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef BUILTINS_H
+#define BUILTINS_H
+
+#include "postgres.h"
+
+#include "storage/itemptr.h"
+
+#include "storage/large_object.h"
+
+#include "utils/geo-decls.h"
+
+/*
+ *     Defined in adt/
+ */
+/* bool.c */
+extern int32 boolin(char *b);
+extern char *boolout(long b);
+extern int32 booleq(int8 arg1, int8 arg2);
+extern int32 boolne(int8 arg1, int8 arg2);
+
+/* char.c */
+extern int32 charin(char *ch);
+extern char *charout(int32 ch);
+extern int32 cidin(char *s);
+extern char *cidout(int32 c);
+extern char *char16in(char *s);
+extern char *char16out(char *s);
+extern int32 chareq(int8 arg1, int8 arg2);
+extern int32 charne(int8 arg1, int8 arg2);
+extern int32 charlt(int8 arg1, int8 arg2);
+extern int32 charle(int8 arg1, int8 arg2);
+extern int32 chargt(int8 arg1, int8 arg2);
+extern int32 charge(int8 arg1, int8 arg2);
+extern int8 charpl(int8 arg1, int8 arg2);
+extern int8 charmi(int8 arg1, int8 arg2);
+extern int8 charmul(int8 arg1, int8 arg2);
+extern int8 chardiv(int8 arg1, int8 arg2);
+extern int32 cideq(int8 arg1, int8 arg2);
+extern int32 char16eq(char *arg1, char *arg2);
+extern int32 char16ne(char *arg1, char *arg2);
+extern int32 char16lt(char *arg1, char *arg2);
+extern int32 char16le(char *arg1, char *arg2);
+extern int32 char16gt(char *arg1, char *arg2);
+extern int32 char16ge(char *arg1, char *arg2);
+extern uint16 char2in(char *s);
+extern char *char2out(uint16 s);
+extern int32 char2eq(uint16 a, uint16 b);
+extern int32 char2ne(uint16 a, uint16 b);
+extern int32 char2lt(uint16 a, uint16 b);
+extern int32 char2le(uint16 a, uint16 b);
+extern int32 char2gt(uint16 a, uint16 b);
+extern int32 char2ge(uint16 a, uint16 b);
+extern int32 char2cmp(uint16 a, uint16 b);
+extern uint32 char4in(char *s);
+extern char *char4out(uint32 s);
+extern int32 char4eq(uint32 a, uint32 b);
+extern int32 char4ne(uint32 a, uint32 b);
+extern int32 char4lt(uint32 a, uint32 b);
+extern int32 char4le(uint32 a, uint32 b);
+extern int32 char4gt(uint32 a, uint32 b);
+extern int32 char4ge(uint32 a, uint32 b);
+extern int32 char4cmp(uint32 a, uint32 b);
+extern char *char8in(char *s);
+extern char *char8out(char *s);
+extern int32 char8eq(char *arg1, char *arg2);
+extern int32 char8ne(char *arg1, char *arg2);
+extern int32 char8lt(char *arg1, char *arg2);
+extern int32 char8le(char *arg1, char *arg2);
+extern int32 char8gt(char *arg1, char *arg2);
+extern int32 char8ge(char *arg1, char *arg2);
+extern int32 char8cmp(char *arg1, char *arg2);
+
+/* int.c */
+extern int32 int2in(char *num);
+extern char *int2out(int16 sh);
+extern int16 *int28in(char *shs);
+extern char *int28out(int16 (*shs)[]);
+extern int32 *int44in(char *input_string);
+extern char *int44out(int32 an_array[]);
+extern int32 int4in(char *num);
+extern char *int4out(int32 l);
+extern int32 i2toi4(int16 arg1);
+extern int16 i4toi2(int32 arg1);
+extern int32 int4eq(int32 arg1, int32 arg2);
+extern int32 int4ne(int32 arg1, int32 arg2);
+extern int32 int4lt(int32 arg1, int32 arg2);
+extern int32 int4le(int32 arg1, int32 arg2);
+extern int32 int4gt(int32 arg1, int32 arg2);
+extern int32 int4ge(int32 arg1, int32 arg2);
+extern int32 int2eq(int16 arg1, int16 arg2);
+extern int32 int2ne(int16 arg1, int16 arg2);
+extern int32 int2lt(int16 arg1, int16 arg2);
+extern int32 int2le(int16 arg1, int16 arg2);
+extern int32 int2gt(int16 arg1, int16 arg2);
+extern int32 int2ge(int16 arg1, int16 arg2);
+extern int32 int24eq(int32 arg1, int32 arg2);
+extern int32 int24ne(int32 arg1, int32 arg2);
+extern int32 int24lt(int32 arg1, int32 arg2);
+extern int32 int24le(int32 arg1, int32 arg2);
+extern int32 int24gt(int32 arg1, int32 arg2);
+extern int32 int24ge(int32 arg1, int32 arg2);
+extern int32 int42eq(int32 arg1, int32 arg2);
+extern int32 int42ne(int32 arg1, int32 arg2);
+extern int32 int42lt(int32 arg1, int32 arg2);
+extern int32 int42le(int32 arg1, int32 arg2);
+extern int32 int42gt(int32 arg1, int32 arg2);
+extern int32 int42ge(int32 arg1, int32 arg2);
+extern int32 keyfirsteq(int16 *arg1, int16 arg2);
+extern int32 int4um(int32 arg);
+extern int32 int4pl(int32 arg1, int32 arg2);
+extern int32 int4mi(int32 arg1, int32 arg2);
+extern int32 int4mul(int32 arg1, int32 arg2);
+extern int32 int4div(int32 arg1, int32 arg2);
+extern int32 int4inc(int32 arg);
+extern int16 int2um(int16 arg);
+extern int16 int2pl(int16 arg1, int16 arg2);
+extern int16 int2mi(int16 arg1, int16 arg2);
+extern int16 int2mul(int16 arg1, int16 arg2);
+extern int16 int2div(int16 arg1, int16 arg2);
+extern int16 int2inc(int16 arg);
+extern int32 int24pl(int32 arg1, int32 arg2);
+extern int32 int24mi(int32 arg1, int32 arg2);
+extern int32 int24mul(int32 arg1, int32 arg2);
+extern int32 int24div(int32 arg1, int32 arg2);
+extern int32 int42pl(int32 arg1, int32 arg2);
+extern int32 int42mi(int32 arg1, int32 arg2);
+extern int32 int42mul(int32 arg1, int32 arg2);
+extern int32 int42div(int32 arg1, int32 arg2);
+extern int32 int4mod(int32 arg1, int32 arg2);
+extern int32 int2mod(int16 arg1, int16 arg2);
+extern int32 int24mod(int32 arg1, int32 arg2);
+extern int32 int42mod(int32 arg1, int32 arg2);
+extern int32 int4fac(int32 arg1);
+extern int32 int2fac(int16 arg1);
+extern int16 int2larger(int16 arg1, int16 arg2);
+extern int16 int2smaller(int16 arg1, int16 arg2);
+extern int32 int4larger(int32 arg1, int32 arg2);
+extern int32 int4smaller(int32 arg1, int32 arg2);
+
+/* name.c */
+extern NameData *namein(char *s);
+extern char *nameout(NameData *s);
+extern int32 nameeq(NameData *arg1, NameData *arg2);
+extern int32 namene(NameData *arg1, NameData *arg2);
+extern int32 namelt(NameData *arg1, NameData *arg2);
+extern int32 namele(NameData *arg1, NameData *arg2);
+extern int32 namegt(NameData *arg1, NameData *arg2);
+extern int32 namege(NameData *arg1, NameData *arg2);
+extern int namecmp(Name n1, Name n2);
+extern int namecpy(Name n1, Name n2);
+extern int namecat(Name n1, Name n2);
+extern int namestrcpy(Name name, char *str);
+extern int namestrcat(Name name, char *str);
+extern int namestrcmp(Name name, char *str);
+extern uint32 NameComputeLength(Name name);
+
+/* numutils.c */
+/* XXX hack.  HP-UX has a ltoa (with different arguments) already. */
+#ifdef PORTNAME_hpux
+#define ltoa pg_ltoa
+#endif /* PORTNAME_hpux */
+extern int32 pg_atoi(char *s, int size, int c);
+extern void itoa(int i, char *a);
+extern void ltoa(int32 l, char *a);
+extern int ftoa(double value, char *ascii, int width, int prec1, char format);
+extern int atof1(char *str, double *val);
+
+/*
+ *     Per-opclass comparison functions for new btrees.  These are
+ *     stored in pg_amproc and defined in nbtree/
+ */
+extern int32           btint2cmp();
+extern int32           btint4cmp();
+extern int32           btint24cmp();
+extern int32           btint42cmp();
+extern int32           btfloat4cmp();
+extern int32           btfloat8cmp();
+extern int32           btoidcmp();
+extern int32           btabstimecmp();
+extern int32           btcharcmp();
+extern int32           btchar16cmp();
+extern int32           bttextcmp();
+
+/*
+ *     RTree code.
+ *     Defined in access/index-rtree/
+ */
+extern char            *rtinsert();
+extern char            *rtdelete();
+extern char            *rtgettuple();
+extern char            *rtbeginscan();
+extern void            rtendscan();
+extern void            rtreebuild();
+extern void            rtmarkpos();
+extern void            rtrestrpos();
+extern void            rtrescan();
+extern void            rtbuild();
+
+/* support routines for the rtree access method, by opclass */
+extern BOX             *rt_box_union();
+extern BOX             *rt_box_inter();
+extern float           *rt_box_size();
+extern float           *rt_bigbox_size();
+extern float           *rt_poly_size();
+extern POLYGON *rt_poly_union();
+extern POLYGON *rt_poly_inter();
+
+/* projection utilities */
+/* extern char *GetAttributeByName(); 
+   extern char *GetAttributeByNum(); ,
+ in executor/executor.h*/
+
+
+extern int32 pqtest();
+
+/* arrayfuncs.c */
+#include "utils/array.h"
+
+/* date.c */
+extern int32 reltimein(char *timestring);
+extern char *reltimeout(int32 timevalue);
+extern TimeInterval tintervalin(char *intervalstr);
+extern char *tintervalout(TimeInterval interval);
+extern TimeInterval mktinterval(AbsoluteTime t1, AbsoluteTime t2);
+extern AbsoluteTime timepl(AbsoluteTime t1, RelativeTime t2);
+extern AbsoluteTime timemi(AbsoluteTime t1, RelativeTime t2);
+/* extern RelativeTime abstimemi(AbsoluteTime t1, AbsoluteTime t2);  static*/
+extern int ininterval(AbsoluteTime t, TimeInterval interval);
+extern RelativeTime intervalrel(TimeInterval interval);
+extern AbsoluteTime timenow(void);
+extern int32 reltimeeq(RelativeTime t1, RelativeTime t2);
+extern int32 reltimene(RelativeTime t1, RelativeTime t2);
+extern int32 reltimelt(RelativeTime t1, RelativeTime t2);
+extern int32 reltimegt(RelativeTime t1, RelativeTime t2);
+extern int32 reltimele(RelativeTime t1, RelativeTime t2);
+extern int32 reltimege(RelativeTime t1, RelativeTime t2);
+extern int32 intervaleq(TimeInterval i1, TimeInterval i2);
+extern int32 intervalleneq(TimeInterval i, RelativeTime t);
+extern int32 intervallenne(TimeInterval i, RelativeTime t);
+extern int32 intervallenlt(TimeInterval i, RelativeTime t);
+extern int32 intervallengt(TimeInterval i, RelativeTime t);
+extern int32 intervallenle(TimeInterval i, RelativeTime t);
+extern int32 intervallenge(TimeInterval i, RelativeTime t);
+extern int32 intervalct(TimeInterval i1, TimeInterval i2);
+extern int32 intervalov(TimeInterval i1, TimeInterval i2);
+extern AbsoluteTime intervalstart(TimeInterval i);
+extern AbsoluteTime intervalend(TimeInterval i);
+extern int isreltime(char *timestring, int *sign, long *quantity, int *unitnr);
+
+/* dt.c */
+extern int32 dtin(char *datetime);
+extern char *dtout(int32 datetime);
+
+/* filename.c */
+extern char *filename_in(char *file);
+extern char *filename_out(char *s);
+
+/* float.c */
+extern float32 float4in(char *num);
+extern char *float4out(float32 num);
+extern float64 float8in(char *num);
+extern char *float8out(float64 num);
+extern float32 float4abs(float32 arg1);
+extern float32 float4um(float32 arg1);
+extern float32 float4larger(float32 arg1, float32 arg2);
+extern float32 float4smaller(float32 arg1, float32 arg2);
+extern float64 float8abs(float64 arg1);
+extern float64 float8um(float64 arg1);
+extern float64 float8larger(float64 arg1, float64 arg2);
+extern float64 float8smaller(float64 arg1, float64 arg2);
+extern float32 float4pl(float32 arg1, float32 arg2);
+extern float32 float4mi(float32 arg1, float32 arg2);
+extern float32 float4mul(float32 arg1, float32 arg2);
+extern float32 float4div(float32 arg1, float32 arg2);
+extern float32 float4inc(float32 arg1);
+extern float64 float8pl(float64 arg1, float64 arg2);
+extern float64 float8mi(float64 arg1, float64 arg2);
+extern float64 float8mul(float64 arg1, float64 arg2);
+extern float64 float8div(float64 arg1, float64 arg2);
+extern float64 float8inc(float64 arg1);
+extern long float4eq(float32 arg1, float32 arg2);
+extern long float4ne(float32 arg1, float32 arg2);
+extern long float4lt(float32 arg1, float32 arg2);
+extern long float4le(float32 arg1, float32 arg2);
+extern long float4gt(float32 arg1, float32 arg2);
+extern long float4ge(float32 arg1, float32 arg2);
+extern long float8eq(float64 arg1, float64 arg2);
+extern long float8ne(float64 arg1, float64 arg2);
+extern long float8lt(float64 arg1, float64 arg2);
+extern long float8le(float64 arg1, float64 arg2);
+extern long float8gt(float64 arg1, float64 arg2);
+extern long float8ge(float64 arg1, float64 arg2);
+extern float64 ftod(float32 num);
+extern float32 dtof(float64 num);
+extern float64 dround(float64 arg1);
+extern float64 dtrunc(float64 arg1);
+extern float64 dsqrt(float64 arg1);
+extern float64 dcbrt(float64 arg1);
+extern float64 dpow(float64 arg1, float64 arg2);
+extern float64 dexp(float64 arg1);
+extern float64 dlog1(float64 arg1);
+extern float64 float48pl(float32 arg1, float64 arg2);
+extern float64 float48mi(float32 arg1, float64 arg2);
+extern float64 float48mul(float32 arg1, float64 arg2);
+extern float64 float48div(float32 arg1, float64 arg2);
+extern float64 float84pl(float64 arg1, float32 arg2);
+extern float64 float84mi(float64 arg1, float32 arg2);
+extern float64 float84mul(float64 arg1, float32 arg2);
+extern float64 float84div(float64 arg1, float32 arg2);
+extern long float48eq(float32 arg1, float64 arg2);
+extern long float48ne(float32 arg1, float64 arg2);
+extern long float48lt(float32 arg1, float64 arg2);
+extern long float48le(float32 arg1, float64 arg2);
+extern long float48gt(float32 arg1, float64 arg2);
+extern long float48ge(float32 arg1, float64 arg2);
+extern long float84eq(float64 arg1, float32 arg2);
+extern long float84ne(float64 arg1, float32 arg2);
+extern long float84lt(float64 arg1, float32 arg2);
+extern long float84le(float64 arg1, float32 arg2);
+extern long float84gt(float64 arg1, float32 arg2);
+extern long float84ge(float64 arg1, float32 arg2);
+
+/* geo-ops.c, geo-selfuncs.c */
+#include "utils/geo-decls.h"
+
+/* misc.c */
+extern bool NullValue(Datum value, bool *isNull);
+extern bool NonNullValue(Datum value, bool *isNull);
+extern int32 userfntest(int i);
+
+/* not_in.c */
+extern bool int4notin(int16 not_in_arg, char *relation_and_attr);
+extern bool oidnotin(Oid the_oid, char *compare);
+extern int my_varattno(Relation rd, char *a);
+
+/* oid.c */
+extern Oid *oid8in(char *oidString);
+extern char *oid8out(Oid (*oidArray)[]);
+extern Oid oidin(char *s);
+extern char *oidout(Oid o);
+extern int32 oideq(Oid arg1, Oid arg2);
+extern int32 oidne(Oid arg1, Oid arg2);
+extern int32 oid8eq(Oid arg1[], Oid arg2[]);
+
+/* regexp.c */
+extern bool char2regexeq(uint16 arg1, struct varlena *p);
+extern bool char2regexne(uint16 arg1, struct varlena *p);
+extern bool char4regexeq(uint32 arg1, struct varlena *p);
+extern bool char4regexne(uint32 arg1, struct varlena *p);
+extern bool char8regexeq(char *s, struct varlena *p);
+extern bool char8regexne(char *s, struct varlena *p);
+extern bool char16regexeq(char *s, struct varlena *p);
+extern bool char16regexne(char *s, struct varlena *p);
+extern bool textregexeq(struct varlena *s, struct varlena *p);
+extern bool textregexne(struct varlena *s, struct varlena *p);
+extern bool char2icregexeq(uint16 arg1, struct varlena *p);
+extern bool char2icregexne(uint16 arg1, struct varlena *p);
+extern bool char4icregexeq(uint32 arg1, struct varlena *p);
+extern bool char4icregexne(uint32 arg1, struct varlena *p);
+extern bool char8icregexeq(char *s, struct varlena *p);
+extern bool char8icregexne(char *s, struct varlena *p);
+extern bool char16icregexeq(char *s, struct varlena *p);
+extern bool char16icregexne(char *s, struct varlena *p);
+extern bool nameicregexeq(NameData *s, struct varlena *p);
+extern bool nameicregexne(NameData *s, struct varlena *p);
+extern bool texticregexeq(struct varlena *s, struct varlena *p);
+extern bool texticregexne(struct varlena *s, struct varlena *p);
+
+
+/* regproc.c */
+extern int32 regprocin(char *proname);
+extern char *regprocout(RegProcedure proid);
+extern Oid RegprocToOid(RegProcedure rp);
+
+/* selfuncs.c */
+extern float64 eqsel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag);
+extern float64 neqsel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag);
+extern float64 intltsel(Oid opid, Oid relid, AttrNumber attno, int32 value, int32 flag);
+extern float64 intgtsel(Oid opid, Oid relid, AttrNumber attno, int32 value, int32 flag);
+extern float64 eqjoinsel(Oid opid, Oid relid1, AttrNumber attno1, Oid relid2, AttrNumber attno2);
+extern float64 neqjoinsel(Oid opid, Oid relid1, AttrNumber attno1, Oid relid2, AttrNumber attno2);
+extern float64 intltjoinsel(Oid opid, Oid relid1, AttrNumber attno1, Oid relid2, AttrNumber attno2);
+extern float64 intgtjoinsel(Oid opid, Oid relid1, AttrNumber attno1, Oid relid2, AttrNumber attno2);
+extern float64 btreesel(Oid operatorOid, Oid indrelid, AttrNumber attributeNumber, char *constValue, int32 constFlag, int32 nIndexKeys, Oid indexrelid);
+extern float64 btreenpage(Oid operatorOid, Oid indrelid, AttrNumber attributeNumber, char *constValue, int32 constFlag, int32 nIndexKeys, Oid indexrelid);
+extern float64 hashsel(Oid operatorOid, Oid indrelid, AttrNumber attributeNumber, char *constValue, int32 constFlag, int32 nIndexKeys, Oid indexrelid);
+extern float64 hashnpage(Oid operatorOid, Oid indrelid, AttrNumber attributeNumber, char *constValue, int32 constFlag, int32 nIndexKeys, Oid indexrelid);
+extern float64 rtsel(Oid operatorOid, Oid indrelid, AttrNumber attributeNumber, char *constValue, int32 constFlag, int32 nIndexKeys, Oid indexrelid);
+extern float64 rtnpage(Oid operatorOid, Oid indrelid, AttrNumber attributeNumber, char *constValue, int32 constFlag, int32 nIndexKeys, Oid indexrelid);
+
+/* tid.c */
+extern ItemPointer tidin(char *str);
+extern char *tidout(ItemPointer itemPtr);
+
+/* varlena.c */
+extern struct varlena *byteain(char *inputText);
+extern struct varlena *shove_bytes(unsigned char *stuff, int len);
+extern char *byteaout(struct varlena *vlena);
+extern struct varlena *textin(char *inputText);
+extern char *textout(struct varlena *vlena);
+extern int32 texteq(struct varlena *arg1, struct varlena *arg2);
+extern int32 textne(struct varlena *arg1, struct varlena *arg2);
+extern int32 text_lt(struct varlena *arg1, struct varlena *arg2);
+extern int32 text_le(struct varlena *arg1, struct varlena *arg2);
+extern int32 text_gt(struct varlena *arg1, struct varlena *arg2);
+extern int32 text_ge(struct varlena *arg1, struct varlena *arg2);
+extern int32 byteaGetSize(struct varlena *v);
+extern int32 byteaGetByte(struct varlena *v, int32 n);
+extern int32 byteaGetBit(struct varlena *v, int32 n);
+extern struct varlena *byteaSetByte(struct varlena *v, int32 n, int32 newByte);
+extern struct varlena *byteaSetBit(struct varlena *v, int32 n, int32 newBit);
+
+/* acl.c */
+#include "utils/acl.h"
+
+#endif /* BUILTINS_H */
diff --git a/src/backend/utils/cache/Makefile.inc b/src/backend/utils/cache/Makefile.inc
new file mode 100644 (file)
index 0000000..0782bce
--- /dev/null
@@ -0,0 +1,15 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for utils/cache
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= catcache.c inval.c rel.c relcache.c syscache.c lsyscache.c fcache.c
+
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
new file mode 100644 (file)
index 0000000..bd062a8
--- /dev/null
@@ -0,0 +1,1023 @@
+/*-------------------------------------------------------------------------
+ *
+ * catcache.c--
+ *    System catalog cache for tuples matching a key.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * Notes:
+ *     XXX This needs to use exception.h to handle recovery when
+ *             an abort occurs during DisableCache.
+ *     
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "utils/builtins.h"
+#include "utils/tqual.h"
+#include "storage/bufpage.h"
+#include "access/valid.h"
+#include "miscadmin.h"
+#include "utils/portal.h"
+#include "utils/catcache.h"
+#include "fmgr.h"              /* for F_BOOLEQ, etc.  DANGER */
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/mcxt.h"
+#include "utils/rel.h"
+#include "catalog/pg_type.h"   /* for OID of int28 type */
+#include "lib/dllist.h"
+
+/* ----------------
+ *     variables, macros and other stuff
+ *
+ *  note CCSIZE allocates 51 buckets .. one was already allocated in
+ *  the catcache structure.
+ * ----------------
+ */
+
+#ifdef CACHEDEBUG
+#define CACHE1_elog(a,b)               elog(a,b)
+#define CACHE2_elog(a,b,c)             elog(a,b,c)
+#define CACHE3_elog(a,b,c,d)           elog(a,b,c,d)
+#define CACHE4_elog(a,b,c,d,e)         elog(a,b,c,d,e)
+#define CACHE5_elog(a,b,c,d,e,f)       elog(a,b,c,d,e,f)
+#define CACHE6_elog(a,b,c,d,e,f,g)     elog(a,b,c,d,e,f,g)
+#else
+#define CACHE1_elog(a,b)
+#define CACHE2_elog(a,b,c)
+#define CACHE3_elog(a,b,c,d)
+#define CACHE4_elog(a,b,c,d,e)
+#define CACHE5_elog(a,b,c,d,e,f)
+#define CACHE6_elog(a,b,c,d,e,f,g)
+#endif
+
+CatCache       *Caches = NULL;
+GlobalMemory   CacheCxt;
+
+static int     DisableCache;
+
+/* ----------------
+ *     EQPROC is used in CatalogCacheInitializeCache
+ *     XXX this should be replaced by catalog lookups soon
+ * ----------------
+ */
+static long   eqproc[] = {
+    F_BOOLEQ, 0l, F_CHAREQ, F_CHAR16EQ, 0l,
+    F_INT2EQ, F_KEYFIRSTEQ, F_INT4EQ, 0l, F_TEXTEQ,
+    F_OIDEQ, 0l, 0l, 0l, F_OID8EQ
+};
+
+#define        EQPROC(SYSTEMTYPEOID)   eqproc[(SYSTEMTYPEOID)-16]
+
+/* ----------------------------------------------------------------
+ *                 internal support functions
+ * ----------------------------------------------------------------
+ */
+/* --------------------------------
+ *     CatalogCacheInitializeCache
+ * --------------------------------
+ */
+#ifdef CACHEDEBUG
+#define CatalogCacheInitializeCache_DEBUG1 \
+    elog(DEBUG, "CatalogCacheInitializeCache: cache @%08lx", cache); \
+    if (relation) \
+        elog(DEBUG, "CatalogCacheInitializeCache: called w/relation(inval)"); \
+    else \
+        elog(DEBUG, "CatalogCacheInitializeCache: called w/relname %s", \
+           cache->cc_relname)
+#define CatalogCacheInitializeCache_DEBUG2 \
+        if (cache->cc_key[i] > 0) { \
+            elog(DEBUG, "CatalogCacheInitializeCache: load %d/%d w/%d, %d", \
+               i+1, cache->cc_nkeys, cache->cc_key[i], \
+               relation->rd_att->attrs[cache->cc_key[i] - 1]->attlen); \
+       } else { \
+            elog(DEBUG, "CatalogCacheInitializeCache: load %d/%d w/%d", \
+               i+1, cache->cc_nkeys, cache->cc_key[i]); \
+       }
+#else
+#define CatalogCacheInitializeCache_DEBUG1
+#define CatalogCacheInitializeCache_DEBUG2
+#endif
+
+void
+CatalogCacheInitializeCache(struct catcache *cache,
+                           Relation relation)
+{
+    MemoryContext      oldcxt;
+    short              didopen = 0;
+    short              i;
+    TupleDesc          tupdesc;
+    
+    CatalogCacheInitializeCache_DEBUG1;
+    
+    /* ----------------
+     * first switch to the cache context so our allocations
+     *  do not vanish at the end of a transaction
+     * ----------------
+     */
+    if (!CacheCxt)
+       CacheCxt = CreateGlobalMemory("Cache");
+    oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+    
+    /* ----------------
+     *  If no relation was passed we must open it to get access to 
+     *  its fields.  If one of the other caches has already opened
+     *  it we use heap_open() instead of heap_openr()
+     * ----------------
+     */
+    if (! RelationIsValid(relation)) {
+       struct catcache *cp;
+       /* ----------------
+        *  scan the caches to see if any other cache has opened the relation
+        * ----------------
+        */
+        for (cp = Caches; cp; cp = cp->cc_next) {
+           if (strncmp(cp->cc_relname, cache->cc_relname, NAMEDATALEN) == 0) {
+               if (cp->relationId != InvalidOid)
+                   break;
+           }
+       }
+       
+       /* ----------------
+        *  open the relation by name or by id
+        * ----------------
+        */
+       if (cp)
+           relation = heap_open(cp->relationId);
+       else
+         {
+           relation = heap_openr(cache->cc_relname);
+          }    
+
+       didopen = 1;
+    }
+    
+    /* ----------------
+     * initialize the cache's relation id
+     * ----------------
+     */
+    Assert(RelationIsValid(relation));
+    cache->relationId = RelationGetRelationId(relation);
+    tupdesc = cache->cc_tupdesc = RelationGetTupleDescriptor(relation);
+    
+    CACHE3_elog(DEBUG, "CatalogCacheInitializeCache: relid %d, %d keys", 
+               cache->relationId, cache->cc_nkeys);
+    
+    /* ----------------
+     * initialize cache's key information
+     * ----------------
+     */
+    for (i = 0; i < cache->cc_nkeys; ++i) {
+       CatalogCacheInitializeCache_DEBUG2;
+       
+       if (cache->cc_key[i] > 0) {
+           
+           /*
+            *  Yoiks.  The implementation of the hashing code and the
+            *  implementation of int28's are at loggerheads.  The right
+            *  thing to do is to throw out the implementation of int28's
+            *  altogether; until that happens, we do the right thing here
+            *  to guarantee that the hash key generator doesn't try to
+            *  dereference an int2 by mistake.
+            */
+           
+           if (tupdesc->attrs[cache->cc_key[i]-1]->atttypid == INT28OID)
+               cache->cc_klen[i] = sizeof (short);
+           else
+               cache->cc_klen[i] = tupdesc->attrs[cache->cc_key[i]-1]->attlen;
+           
+           cache->cc_skey[i].sk_procedure =
+               EQPROC(tupdesc->attrs[cache->cc_key[i]-1]->atttypid);
+           
+           fmgr_info(cache->cc_skey[i].sk_procedure, 
+                     (func_ptr *) &cache->cc_skey[i].sk_func,
+                     (int *) &cache->cc_skey[i].sk_nargs);
+           
+           CACHE5_elog(DEBUG, "CatalogCacheInit %16s %d %d %x",
+                       &relation->rd_rel->relname,
+                       i,
+                       tupdesc->attrs[ cache->cc_key[i]-1 ]->attlen,
+                       cache);
+       }
+    }
+    
+    /* ----------------
+     * close the relation if we opened it
+     * ----------------
+     */
+    if (didopen)
+        heap_close(relation);
+    
+    /* ----------------
+     * initialize index information for the cache.  this
+     *  should only be done once per cache.
+     * ----------------
+     */
+    if (cache->cc_indname != NULL && cache->indexId == InvalidOid)
+       {
+           if (RelationGetRelationTupleForm(relation)->relhasindex)
+               {
+                   /*
+                    * If the index doesn't exist we are in trouble.
+                    */
+                   relation = index_openr( cache->cc_indname);
+                   Assert(relation);
+                   cache->indexId = RelationGetRelationId(relation);
+                   index_close(relation);
+               }
+           else
+               cache->cc_indname = NULL;
+       }
+    
+    /* ----------------
+     * return to the proper memory context
+     * ----------------
+     */
+    MemoryContextSwitchTo(oldcxt);
+}
+
+/* --------------------------------
+ *     CatalogCacheSetId
+ *
+ *     XXX temporary function
+ * --------------------------------
+ */
+void
+CatalogCacheSetId(CatCache *cacheInOutP, int id)
+{
+    Assert(id == InvalidCatalogCacheId || id >= 0);
+    cacheInOutP->id = id;
+}
+
+/* ----------------
+ * comphash --
+ *     Compute a hash value, somehow.
+ *
+ * XXX explain algorithm here.
+ *
+ * l is length of the attribute value, v
+ * v is the attribute value ("Datum")
+ * ----------------
+ */
+long
+comphash(long l, register char *v)
+{
+    long  i;
+    NameData n;
+
+    CACHE3_elog(DEBUG, "comphash (%d,%x)", l, v);
+    
+    switch (l) {
+    case 1:
+    case 2:
+    case 4:
+       return((long) v);
+    }
+    
+    if (l == NAMEDATALEN)  {
+       /* if it's a name, make sure that the values
+          are null-padded.
+
+          Note that this other fixed-length types can also have 
+          the same typelen so this may break them     - XXX
+          */
+       namestrcpy(&n,v);
+       v = n.data;
+    } else
+       if (l < 0)
+           l = VARSIZE(v);
+    
+    i = 0;
+    while (l--) {
+       i += *v++;
+    }
+    return(i);
+}
+
+/* --------------------------------
+ *     CatalogCacheComputeHashIndex
+ * --------------------------------
+ */
+Index
+CatalogCacheComputeHashIndex(struct catcache *cacheInP)
+{
+    Index      hashIndex;
+    hashIndex = 0x0;
+    CACHE6_elog(DEBUG,"CatalogCacheComputeHashIndex %s %d %d %d %x",
+               cacheInP->cc_relname,
+               cacheInP->cc_nkeys,
+               cacheInP->cc_klen[0],
+               cacheInP->cc_klen[1],
+               cacheInP);
+    
+    switch (cacheInP->cc_nkeys) {
+    case 4:
+       hashIndex ^= comphash(cacheInP->cc_klen[3],
+                             (char*)cacheInP->cc_skey[3].sk_argument) << 9;
+       /* FALLTHROUGH */
+    case 3:
+       hashIndex ^= comphash(cacheInP->cc_klen[2],
+                             (char*)cacheInP->cc_skey[2].sk_argument) << 6;
+       /* FALLTHROUGH */
+    case 2:
+       hashIndex ^= comphash(cacheInP->cc_klen[1],
+                             (char*)cacheInP->cc_skey[1].sk_argument) << 3;
+       /* FALLTHROUGH */
+    case 1:
+       hashIndex ^= comphash(cacheInP->cc_klen[0],
+                             (char*)cacheInP->cc_skey[0].sk_argument);
+       break;
+    default:
+       elog(FATAL, "CCComputeHashIndex: %d cc_nkeys", cacheInP->cc_nkeys);
+       break;
+    }
+    hashIndex %= cacheInP->cc_size;
+    return (hashIndex);
+}
+
+/* --------------------------------
+ *     CatalogCacheComputeTupleHashIndex
+ * --------------------------------
+ */
+Index
+CatalogCacheComputeTupleHashIndex(struct catcache      *cacheInOutP,
+                                 Relation relation,
+                                 HeapTuple tuple)
+{
+    bool               isNull = '\0';
+    if (cacheInOutP->relationId == InvalidOid)
+       CatalogCacheInitializeCache(cacheInOutP, relation);
+    switch (cacheInOutP->cc_nkeys) {
+    case 4:
+       cacheInOutP->cc_skey[3].sk_argument =
+           (cacheInOutP->cc_key[3] == ObjectIdAttributeNumber)
+               ? (Datum)tuple->t_oid
+                   : (Datum)fastgetattr(tuple,
+                                        cacheInOutP->cc_key[3],
+                                RelationGetTupleDescriptor(relation),
+                                        &isNull);
+       Assert(!isNull);
+       /* FALLTHROUGH */
+    case 3:
+       cacheInOutP->cc_skey[2].sk_argument =
+           (cacheInOutP->cc_key[2] == ObjectIdAttributeNumber)
+               ? (Datum)tuple->t_oid
+                   : (Datum)fastgetattr(tuple,
+                                        cacheInOutP->cc_key[2],
+                                RelationGetTupleDescriptor(relation),
+                                        &isNull);
+       Assert(!isNull);
+       /* FALLTHROUGH */
+    case 2:
+       cacheInOutP->cc_skey[1].sk_argument =
+           (cacheInOutP->cc_key[1] == ObjectIdAttributeNumber)
+               ? (Datum)tuple->t_oid
+                   : (Datum)fastgetattr(tuple,
+                                        cacheInOutP->cc_key[1],
+                               RelationGetTupleDescriptor(relation),
+                                        &isNull);
+       Assert(!isNull);
+       /* FALLTHROUGH */
+    case 1:
+       cacheInOutP->cc_skey[0].sk_argument =
+           (cacheInOutP->cc_key[0] == ObjectIdAttributeNumber)
+               ? (Datum)tuple->t_oid
+                   : (Datum)fastgetattr(tuple,
+                                        cacheInOutP->cc_key[0],
+                                        RelationGetTupleDescriptor(relation),
+                                        &isNull);
+       Assert(!isNull);
+       break;
+    default:
+       elog(FATAL, "CCComputeTupleHashIndex: %d cc_nkeys",
+            cacheInOutP->cc_nkeys
+            );
+       break;
+    }
+    
+    return
+       CatalogCacheComputeHashIndex(cacheInOutP);
+}
+
+/* --------------------------------
+ *     CatCacheRemoveCTup
+ * --------------------------------
+ */
+void
+CatCacheRemoveCTup(CatCache *cache, Dlelem *elt)
+{
+    CatCTup *ct;
+    CatCTup *other_ct; 
+    Dlelem *other_elt;
+
+    if (elt)
+      ct = (CatCTup*) DLE_VAL(elt);
+    else
+      return;
+
+    other_elt = ct->ct_node;
+    other_ct = (CatCTup*)DLE_VAL(other_elt);
+    DLRemove(other_elt);
+    DLFreeElem(other_elt);
+    free(other_ct);
+    DLRemove(elt);
+    DLFreeElem(elt);
+    free(ct);
+    --cache->cc_ntup;
+}
+
+/* --------------------------------
+ *  CatalogCacheIdInvalidate()
+ *
+ *  Invalidate a tuple given a cache id.  In this case the id should always
+ *  be found (whether the cache has opened its relation or not).  Of course,
+ *  if the cache has yet to open its relation, there will be no tuples so
+ *  no problem.
+ * --------------------------------
+ */
+void
+CatalogCacheIdInvalidate(int cacheId, /* XXX */
+                        Index hashIndex,
+                        ItemPointer pointer)
+{
+    CatCache   *ccp;
+    CatCTup    *ct;
+    Dlelem      *elt;
+    MemoryContext      oldcxt;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(hashIndex < NCCBUCK);
+    Assert(ItemPointerIsValid(pointer));
+    CACHE1_elog(DEBUG, "CatalogCacheIdInvalidate: called");
+    
+    /* ----------------
+     * switch to the cache context for our memory allocations
+     * ----------------
+     */
+    if (!CacheCxt)
+       CacheCxt = CreateGlobalMemory("Cache");
+    oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+    
+    /* ----------------
+     * inspect every cache that could contain the tuple
+     * ----------------
+     */
+    for (ccp = Caches; ccp; ccp = ccp->cc_next) {
+       if (cacheId != ccp->id) 
+           continue;
+       /* ----------------
+        *  inspect the hash bucket until we find a match or exhaust
+        * ----------------
+        */
+       for (elt = DLGetHead(ccp->cc_cache[hashIndex]);
+            elt;
+            elt = DLGetSucc(elt))
+           {
+             ct = (CatCTup*) DLE_VAL(elt);
+             if (ItemPointerEquals(pointer, &ct->ct_tup->t_ctid)) 
+               break;
+           }
+       
+       /* ----------------
+        *  if we found a matching tuple, invalidate it.
+        * ----------------
+        */
+
+       if (elt) {
+           CatCacheRemoveCTup(ccp, elt);
+
+           CACHE1_elog(DEBUG, "CatalogCacheIdInvalidate: invalidated");
+       }
+       
+       if (cacheId != InvalidCatalogCacheId) 
+           break;
+    }
+    
+    /* ----------------
+     * return to the proper memory context
+     * ----------------
+     */
+    MemoryContextSwitchTo(oldcxt);
+    /* sendpm('I', "Invalidated tuple"); */
+}
+
+/* ----------------------------------------------------------------
+ *                    public functions
+ *
+ *     ResetSystemCache
+ *     InitIndexedSysCache
+ *     InitSysCache
+ *     SearchSysCache
+ *     RelationInvalidateCatalogCacheTuple
+ * ----------------------------------------------------------------
+ */
+/* --------------------------------
+ *     ResetSystemCache
+ * --------------------------------
+ */
+void
+ResetSystemCache()
+{
+    MemoryContext      oldcxt;
+    struct catcache    *cache;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    CACHE1_elog(DEBUG, "ResetSystemCache called");
+    if (DisableCache) {
+       elog(WARN, "ResetSystemCache: Called while cache disabled");
+       return;
+    }
+    
+    /* ----------------
+     * first switch to the cache context so our allocations
+     *  do not vanish at the end of a transaction
+     * ----------------
+     */
+    if (!CacheCxt)
+       CacheCxt = CreateGlobalMemory("Cache");
+    
+    oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+    
+    /* ----------------
+     *  here we purge the contents of all the caches
+     *
+     * for each system cache
+     *     for each hash bucket
+     *         for each tuple in hash bucket
+     *            remove the tuple
+     * ----------------
+     */
+    for (cache = Caches; PointerIsValid(cache); cache = cache->cc_next) {
+        int hash;
+       for (hash = 0; hash < NCCBUCK; hash += 1) {
+           Dlelem *elt, *nextelt;
+           for (elt = DLGetHead(cache->cc_cache[hash]); elt; elt = nextelt) {
+               nextelt = DLGetSucc(elt);
+               CatCacheRemoveCTup(cache, elt);
+               if (cache->cc_ntup == -1) 
+                   elog(WARN, "ResetSystemCache: cc_ntup<0 (software error)");
+           }
+       }
+       cache->cc_ntup = 0; /* in case of WARN error above */
+    }
+    
+    CACHE1_elog(DEBUG, "end of ResetSystemCache call");
+    
+    /* ----------------
+     * back to the old context before we return...
+     * ----------------
+     */
+    MemoryContextSwitchTo(oldcxt);
+}
+
+/* --------------------------------
+ *     InitIndexedSysCache
+ *
+ *  This allocates and initializes a cache for a system catalog relation.
+ *  Actually, the cache is only partially initialized to avoid opening the
+ *  relation.  The relation will be opened and the rest of the cache
+ *  structure initialized on the first access.
+ * --------------------------------
+ */
+#ifdef CACHEDEBUG
+#define InitSysCache_DEBUG1 \
+elog(DEBUG, "InitSysCache: rid=%d id=%d nkeys=%d size=%d\n", \
+     cp->relationId, cp->id, cp->cc_nkeys, cp->cc_size); \
+    for (i = 0; i < nkeys; i += 1) { \
+                                        elog(DEBUG, "InitSysCache: key=%d len=%d skey=[%d %d %d %d]\n", \
+                                             cp->cc_key[i], cp->cc_klen[i], \
+                                             cp->cc_skey[i].sk_flags, \
+                                             cp->cc_skey[i].sk_attno, \
+                                             cp->cc_skey[i].sk_procedure, \
+                                             cp->cc_skey[i].sk_argument); \
+                                             }
+#else
+#define InitSysCache_DEBUG1
+#endif
+
+CatCache*
+InitSysCache(char *relname,
+            char *iname,
+            int id,
+            int nkeys,
+            int key[], 
+            HeapTuple (*iScanfuncP)())
+{
+    CatCache     *cp;
+    register int        i;
+    MemoryContext      oldcxt;
+    
+    char *indname;
+
+    indname = (iname) ? iname : NULL;
+
+    /* ----------------
+     * first switch to the cache context so our allocations
+     *  do not vanish at the end of a transaction
+     * ----------------
+     */
+    if (!CacheCxt)
+       CacheCxt = CreateGlobalMemory("Cache");
+    
+    oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+    
+    /* ----------------
+     *  allocate a new cache structure
+     * ----------------
+     */
+    cp = (CatCache *)palloc(sizeof(CatCache));
+    memset((char*)cp, 0, sizeof(CatCache));
+
+    /* ----------------
+     * initialize the cache buckets (each bucket is a list header)
+     *  and the LRU tuple list
+     * ----------------
+     */
+    for (i = 0; i <= NCCBUCK; ++i) {
+      cp->cc_cache[i] = DLNewList();
+    }
+    
+    cp->cc_lrulist = DLNewList();
+    
+    /* ----------------
+     *  Caches is the pointer to the head of the list of all the
+     *  system caches.  here we add the new cache to the top of the list.
+     * ----------------
+     */
+    cp->cc_next = Caches;  /* list of caches (single link) */
+    Caches = cp;
+    
+    /* ----------------
+     * initialize the cache's relation information for the relation
+     *  corresponding to this cache and initialize some of the the new
+     *  cache's other internal fields.
+     * ----------------
+     */
+    cp->relationId =   InvalidOid;
+    cp->indexId =      InvalidOid;
+    cp->cc_relname =   relname;
+    cp->cc_indname =   indname;
+    cp->cc_tupdesc =   (TupleDesc) NULL;
+    cp->id  = id;
+    cp->cc_maxtup =    MAXTUP;
+    cp->cc_size =      NCCBUCK;
+    cp->cc_nkeys =     nkeys;
+    cp->cc_iscanfunc =         iScanfuncP;
+    
+    /* ----------------
+     * initialize the cache's key information
+     * ----------------
+     */
+    for (i = 0; i < nkeys; ++i) {
+       cp->cc_key[i] = key[i];
+       if (!key[i]) {
+           elog(FATAL, "InitSysCache: called with 0 key[%d]", i);
+       }
+       if (key[i] < 0) {
+           if (key[i] != ObjectIdAttributeNumber) {
+               elog(FATAL, "InitSysCache: called with %d key[%d]", key[i], i);
+           } else {
+               cp->cc_klen[i] = sizeof(Oid);
+               /*
+                * ScanKeyEntryData and struct skey are equivalent. It looks
+                * like a move was made to obsolete struct skey, but it
+                * didn't reach this file.  Someday we should clean up this
+                * code and consolidate to ScanKeyEntry - mer 10 Nov 1991
+                */
+               ScanKeyEntryInitialize(&cp->cc_skey[i], 
+                                      (bits16)0,
+                                      (AttrNumber)key[i],
+                                      (RegProcedure)F_OIDEQ,
+                                      (Datum)0);
+               continue;
+           }
+       }
+       
+       cp->cc_skey[i].sk_attno = key[i];
+    }
+    
+    /* ----------------
+     * all done.  new cache is initialized.  print some debugging
+     *  information, if appropriate.
+     * ----------------
+     */
+    InitSysCache_DEBUG1;
+    
+    /* ----------------
+     * back to the old context before we return...
+     * ----------------
+     */
+    MemoryContextSwitchTo(oldcxt);
+    return(cp);
+}
+
+
+/* --------------------------------
+ *     SearchSysCache
+ *
+ *     This call searches a system cache for a tuple, opening the relation
+ *     if necessary (the first access to a particular cache).
+ * --------------------------------
+ */
+HeapTuple
+SearchSysCache(struct catcache *cache,
+              Datum v1,
+              Datum v2,
+              Datum v3,
+              Datum v4)
+{
+    unsigned   hash;
+    CatCTup    *ct; 
+    CatCTup    *nct; 
+    CatCTup    *nct2; 
+    Dlelem      *elt;
+    HeapTuple          ntp;
+    Buffer             buffer;
+    
+    Relation           relation;
+    MemoryContext      oldcxt;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    if (cache->relationId == InvalidOid)
+       CatalogCacheInitializeCache(cache, NULL);
+    
+    /* ----------------
+     * initialize the search key information
+     * ----------------
+     */
+    cache->cc_skey[0].sk_argument = v1;
+    cache->cc_skey[1].sk_argument = v2;
+    cache->cc_skey[2].sk_argument = v3;
+    cache->cc_skey[3].sk_argument = v4;
+    
+    /* ----------------
+     * find the hash bucket in which to look for the tuple
+     * ----------------
+     */
+    hash = CatalogCacheComputeHashIndex(cache);
+    
+    /* ----------------
+     * scan the hash bucket until we find a match or exhaust our tuples
+     * ----------------
+     */
+    for (elt = DLGetHead(cache->cc_cache[hash]); 
+        elt;
+        elt = DLGetSucc(elt))
+       {
+         ct = (CatCTup*)DLE_VAL(elt);
+           /* ----------------
+            *  see if the cached tuple matches our key.
+            *  (should we be worried about time ranges? -cim 10/2/90)
+            * ----------------
+            */
+           if (heap_keytest(ct->ct_tup,
+                            cache->cc_tupdesc,
+                            cache->cc_nkeys,
+                            cache->cc_skey))
+               break;
+       }
+    
+    /* ----------------
+     * if we found a tuple in the cache, move it to the top of the
+     *  lru list, and return it.
+     * ----------------
+     */
+    if (elt) {
+        Dlelem* old_lru_elt;
+       old_lru_elt = ((CatCTup*)DLE_VAL(elt))->ct_node;
+       DLRemove(old_lru_elt);
+       DLAddHead(cache->cc_lrulist, old_lru_elt);
+
+#ifdef CACHEDEBUG
+       relation = heap_open(cache->relationId);
+       CACHE3_elog(DEBUG, "SearchSysCache(%s): found in bucket %d",
+                   RelationGetRelationName(relation), hash);
+       heap_close(relation);
+#endif /* CACHEDEBUG */
+       
+       return (ct->ct_tup); 
+    }
+    
+    /* ----------------
+     * Tuple was not found in cache, so we have to try and
+     *  retrieve it directly from the relation.  If it's found,
+     *  we add it to the cache.  We must avoid recursion here,
+     *  so we disable cache operations.  If operations are
+     *  currently disabled and we couldn't find the requested item
+     *  in the cache, then this may be a recursive request, and we
+     *  abort with an error.
+     * ----------------
+     */
+    
+    if (DisableCache) {
+       elog(WARN, "SearchSysCache: Called while cache disabled");
+       return((HeapTuple) NULL);
+    }
+    
+    /* ----------------
+     * open the relation associated with the cache
+     * ----------------
+     */
+    relation = heap_open(cache->relationId);
+    CACHE2_elog(DEBUG, "SearchSysCache(%s)",
+               RelationGetRelationName(relation));
+    
+    /* ----------------
+     *  DisableCache and then switch to the cache memory context.
+     * ----------------
+     */
+    DisableCache = 1;
+    
+    if (!CacheCxt)
+       CacheCxt = CreateGlobalMemory("Cache");
+    
+    oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+    
+    /* ----------------
+     *  Scan the relation to find the tuple.  If there's an index, and
+     *  if this isn't bootstrap (initdb) time, use the index.
+     * ----------------
+     */
+    CACHE2_elog(DEBUG, "SearchSysCache: performing scan (override==%d)",
+               heapisoverride());
+    
+    if ((RelationGetRelationTupleForm(relation))->relhasindex
+       && !IsBootstrapProcessingMode())
+       {
+           Assert(cache->cc_iscanfunc);
+           switch(cache->cc_nkeys)
+               {
+               case 4: ntp = cache->cc_iscanfunc(relation,v1,v2,v3,v4); break;
+               case 3: ntp = cache->cc_iscanfunc(relation,v1,v2,v3); break;
+               case 2: ntp = cache->cc_iscanfunc(relation,v1,v2); break;
+               case 1: ntp = cache->cc_iscanfunc(relation,v1); break;
+               }
+       }
+    else
+       {
+           HeapScanDesc        sd;
+           
+           sd =  heap_beginscan(relation, 0, NowTimeQual,
+                                cache->cc_nkeys, cache->cc_skey);
+           
+           ntp = heap_getnext(sd, 0, &buffer);
+           
+           if (HeapTupleIsValid(ntp)) {
+               CACHE1_elog(DEBUG, "SearchSysCache: found tuple");
+               ntp = heap_copytuple(ntp);
+           }
+           
+           heap_endscan(sd);
+       }
+    
+    DisableCache = 0;
+    
+    /* ----------------
+     * scan is complete.  if tup is valid, we copy it and add the copy to
+     *  the cache.
+     * ----------------
+     */
+    if (HeapTupleIsValid(ntp)) {
+       /* ----------------
+        *  allocate a new cache tuple holder, store the pointer
+        *  to the heap tuple there and initialize the list pointers.
+        * ----------------
+        */
+        Dlelem *lru_elt;
+
+       /* this is a little cumbersome here because we want the Dlelem's
+          in both doubly linked lists to point to one another.
+          That makes it easier to remove something from both the cache bucket
+          and the lru list at the same time */
+       nct = (CatCTup*) malloc(sizeof(CatCTup));
+       nct->ct_tup = ntp;
+       elt = DLNewElem(nct);
+       nct2 = (CatCTup*) malloc(sizeof(CatCTup));
+       nct2->ct_tup = ntp;
+       lru_elt = DLNewElem(nct2);
+       nct2->ct_node = elt;
+       nct->ct_node = lru_elt;
+       
+       DLAddHead(cache->cc_lrulist, lru_elt);
+       DLAddHead(cache->cc_cache[hash], elt);
+
+       /* ----------------
+        *  deal with hash bucket overflow
+        * ----------------
+        */
+       if (++cache->cc_ntup > cache->cc_maxtup) {
+           CatCTup *ct;
+           elt = DLGetTail(cache->cc_lrulist);
+           ct = (CatCTup *) DLE_VAL(elt);
+           
+           if (ct != nct) {
+               CACHE2_elog(DEBUG, "SearchSysCache(%s): Overflow, LRU removal",
+                           RelationGetRelationName(relation));
+               
+               CatCacheRemoveCTup(cache, elt); 
+
+           }
+       } 
+       
+       CACHE4_elog(DEBUG, "SearchSysCache(%s): Contains %d/%d tuples",
+                   RelationGetRelationName(relation),
+                   cache->cc_ntup, cache->cc_maxtup);
+       CACHE3_elog(DEBUG, "SearchSysCache(%s): put in bucket %d",
+                   RelationGetRelationName(relation), hash);
+    }
+    
+    /* ----------------
+     * close the relation, switch back to the original memory context
+     *  and return the tuple we found (or NULL)
+     * ----------------
+     */
+    heap_close(relation);
+    
+    MemoryContextSwitchTo(oldcxt);
+    return ntp;
+}
+
+/* --------------------------------
+ *  RelationInvalidateCatalogCacheTuple()
+ *
+ *  Invalidate a tuple from a specific relation.  This call determines the
+ *  cache in question and calls CatalogCacheIdInvalidate().  It is -ok-
+ *  if the relation cannot be found, it simply means this backend has yet
+ *  to open it.
+ * --------------------------------
+ */
+void
+RelationInvalidateCatalogCacheTuple(Relation relation,
+                                   HeapTuple tuple,
+                                   void (*function)())
+{
+    struct catcache    *ccp;
+    MemoryContext      oldcxt;
+    Oid                relationId;
+    
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(RelationIsValid(relation));
+    Assert(HeapTupleIsValid(tuple));
+    CACHE1_elog(DEBUG, "RelationInvalidateCatalogCacheTuple: called");
+    
+    /* ----------------
+     * switch to the cache memory context
+     * ----------------
+     */
+    if (!CacheCxt)
+       CacheCxt = CreateGlobalMemory("Cache");
+    oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+    
+    /* ----------------
+     * for each cache
+     *     if the cache contains tuples from the specified relation
+     *        call the invalidation function on the tuples
+     *        in the proper hash bucket
+     * ----------------
+     */
+    relationId = RelationGetRelationId(relation);
+    
+    for (ccp = Caches; ccp; ccp = ccp->cc_next) {
+        if (relationId != ccp->relationId) 
+           continue;
+       
+       /* OPT inline simplification of CatalogCacheIdInvalidate */
+       if (!PointerIsValid(function)) {
+           function = CatalogCacheIdInvalidate;
+       }
+       
+       (*function)(ccp->id,
+                   CatalogCacheComputeTupleHashIndex(ccp, relation, tuple),
+                   &tuple->t_ctid);
+       
+       heap_close(relation);
+    }
+    
+    /* ----------------
+     * return to the proper memory context
+     * ----------------
+     */
+    MemoryContextSwitchTo(oldcxt);
+    
+    /* sendpm('I', "Invalidated tuple"); */
+}
+
diff --git a/src/backend/utils/cache/fcache.c b/src/backend/utils/cache/fcache.c
new file mode 100644 (file)
index 0000000..8a5de92
--- /dev/null
@@ -0,0 +1,297 @@
+/*-------------------------------------------------------------------------
+ *
+ * fcache.c--
+ *    Code for the 'function cache' used in Oper and Func nodes....
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+#include "access/htup.h"
+#include "utils/catcache.h"
+#include "utils/syscache.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_language.h"
+#include "catalog/pg_class.h"
+#include "parser/parsetree.h"          /* for getrelname() */
+#include "utils/builtins.h"
+#include "utils/fcache.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "nodes/primnodes.h"
+#include "nodes/execnodes.h"
+
+static Oid GetDynamicFuncArgType(Var *arg, ExprContext *econtext);
+static FunctionCachePtr init_fcache(Oid foid,
+                            bool use_syscache,
+                            List *argList,
+                            ExprContext *econtext);
+
+/*-----------------------------------------------------------------
+ *
+ * Initialize the 'FunctionCache' given the PG_PROC oid.
+ *
+ *
+ * NOTE:  This function can be called when the system cache is being
+ *        initialized.  Therefore, use_syscache should ONLY be true
+ *        when the function return type is interesting (ie: set_fcache).
+ *-----------------------------------------------------------------
+ */
+#define FuncArgTypeIsDynamic(arg) \
+    (IsA(arg,Var) && ((Var*)arg)->varattno == InvalidAttrNumber)
+
+static Oid
+GetDynamicFuncArgType(Var *arg, ExprContext *econtext)
+{
+    char *relname;
+    int rtid;
+    HeapTuple tup;
+    
+    Assert(IsA(arg,Var));
+    
+    rtid = ((Var*)arg)->varno;
+    relname = (char*)getrelname(rtid, econtext->ecxt_range_table);
+
+    
+    tup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(relname),
+                             0,0,0);
+    if (!tup)
+       elog(WARN, "Lookup failed on type tuple for class %s",
+            relname);
+    
+    return tup->t_oid;
+}
+
+static FunctionCachePtr
+init_fcache(Oid foid,
+           bool use_syscache,
+           List *argList,
+           ExprContext *econtext)
+{
+    HeapTuple        procedureTuple;
+    HeapTuple        typeTuple;
+    Form_pg_proc     procedureStruct;
+    TypeTupleForm     typeStruct;
+    FunctionCachePtr retval;
+    text            *tmp;
+    int              nargs;
+    
+    /* ----------------
+     *   get the procedure tuple corresponding to the given
+     *   functionOid.  If this fails, returnValue has been
+     *   pre-initialized to "null" so we just return it.
+     * ----------------
+     */
+    retval = (FunctionCachePtr) palloc(sizeof(FunctionCache));
+    
+    if (!use_syscache)
+       elog(WARN, "what the ????, init the fcache without the catalogs?");
+    
+    procedureTuple = SearchSysCacheTuple(PROOID,
+                                        ObjectIdGetDatum(foid),
+                                        0,0,0);
+    
+    if (!HeapTupleIsValid(procedureTuple))
+       elog(WARN,
+            "init_fcache: %s %d",
+            "Cache lookup failed for procedure", foid);
+    
+    /* ----------------
+     *   get the return type from the procedure tuple
+     * ----------------
+     */
+    procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
+    
+    /* ----------------
+     *   get the type tuple corresponding to the return type
+     *   If this fails, returnValue has been pre-initialized
+     *   to "null" so we just return it.
+     * ----------------
+     */
+    typeTuple = SearchSysCacheTuple(TYPOID,
+                                   ObjectIdGetDatum(procedureStruct->prorettype),
+                                   0,0,0);
+    
+    if (!HeapTupleIsValid(typeTuple))
+       elog(WARN,
+            "init_fcache: %s %d",
+            "Cache lookup failed for type",
+            (procedureStruct)->prorettype);
+    
+    /* ----------------
+     *   get the type length and by-value from the type tuple and
+     *   save the information in our one element cache.
+     * ----------------
+     */
+    typeStruct = (TypeTupleForm) GETSTRUCT(typeTuple);
+    
+    retval->typlen = (typeStruct)->typlen;
+    if ((typeStruct)->typrelid == InvalidOid) {
+       /* The return type is not a relation, so just use byval */
+       retval->typbyval = (typeStruct)->typbyval ? true : false ;
+    } else {
+       /* This is a hack.  We assume here that any function returning
+        * a relation returns it by reference.  This needs to be
+        * fixed.
+        */
+       retval->typbyval = false;
+    }
+    retval->foid = foid;
+    retval->language = procedureStruct->prolang;
+    retval->func_state = (char *)NULL;
+    retval->setArg     = NULL;
+    retval->hasSetArg  = false;
+    retval->oneResult  = ! procedureStruct->proretset;
+    retval->istrusted  = procedureStruct->proistrusted;
+    
+    /*
+     * If we are returning exactly one result then we have to copy
+     * tuples and by reference results because we have to end the execution
+     * before we return the results.  When you do this everything allocated
+     * by the executor (i.e. slots and tuples) is freed.
+     */
+    if ((retval->language == SQLlanguageId) &&
+       (retval->oneResult) &&
+       !(retval->typbyval))
+       {
+           Form_pg_class relationStruct;
+           HeapTuple relationTuple;
+           TupleDesc td;
+           TupleTableSlot *slot;
+
+           slot = makeNode(TupleTableSlot);
+           slot->ttc_shouldFree = true;
+           slot->ttc_descIsNew = true;
+           slot->ttc_tupleDescriptor = (TupleDesc) NULL;
+           slot->ttc_buffer = InvalidBuffer;
+           slot->ttc_whichplan = -1;
+           retval->funcSlot = (Pointer)slot;
+
+           relationTuple = (HeapTuple)
+               SearchSysCacheTuple(RELNAME,
+                                   PointerGetDatum(&typeStruct->typname),
+                                   0,0,0);
+           
+           if (relationTuple)
+               {
+                   relationStruct = (Form_pg_class)GETSTRUCT(relationTuple);
+                   td = CreateTemplateTupleDesc(relationStruct->relnatts);
+               }
+           else
+               td = CreateTemplateTupleDesc(1);
+           
+           ((TupleTableSlot*)retval->funcSlot)->ttc_tupleDescriptor = td;
+       }
+    else
+       retval->funcSlot = (char *)NULL;
+    
+    nargs = procedureStruct->pronargs;
+    retval->nargs = nargs;
+    
+    if (nargs > 0)
+       {
+           Oid *argTypes;
+           
+           retval->nullVect = (bool *)palloc((retval->nargs)*sizeof(bool));
+           
+           if (retval->language == SQLlanguageId)
+               {
+                   int  i;
+                   List *oneArg;
+                   
+                   retval->argOidVect =
+                       (Oid *)palloc(retval->nargs*sizeof(Oid));
+                   argTypes = procedureStruct->proargtypes;
+                   memmove(retval->argOidVect,
+                           argTypes,
+                           (retval->nargs)*sizeof(Oid));
+                   
+                   for (i=0;
+                        argList;
+                        i++, argList = lnext(argList))
+                       {
+                           oneArg = lfirst(argList);
+                           if (FuncArgTypeIsDynamic(oneArg))
+               retval->argOidVect[i] = GetDynamicFuncArgType((Var*)oneArg,
+                                                             econtext);
+                       }
+               }
+           else
+               retval->argOidVect = (Oid *)NULL;
+       }
+    else
+       {
+           retval->argOidVect = (Oid *)NULL;
+           retval->nullVect = (BoolPtr)NULL;
+       }
+    
+    /*
+     * XXX this is the first varlena in the struct.  If the order
+     *     changes for some reason this will fail.
+     */
+    if (procedureStruct->prolang == SQLlanguageId)
+       {
+           retval->src = textout(&(procedureStruct->prosrc));
+           retval->bin = (char *) NULL;
+       }
+    else 
+       {
+           
+           /*
+            * I'm not sure that we even need to do this at all.
+            */
+           
+           /*
+            * We do for untrusted functions.
+            */
+           
+           if (procedureStruct->proistrusted)
+               retval->bin = (char *) NULL;
+           else {
+               tmp = (text *)
+                   SearchSysCacheGetAttribute(PROOID,
+                                              Anum_pg_proc_probin,
+                                              ObjectIdGetDatum(foid),
+                                              0,0,0);
+               retval->bin = textout(tmp);
+           }
+           retval->src = (char *) NULL;
+       }
+    
+    
+    
+    
+    if (retval->language != SQLlanguageId)
+       fmgr_info(foid, &(retval->func), &(retval->nargs));
+    else
+       retval->func = (func_ptr)NULL;
+    
+    
+    return(retval);
+}
+
+void
+setFcache(Node *node, Oid foid, List *argList, ExprContext *econtext)
+{
+    Func *fnode;
+    Oper *onode;
+    FunctionCachePtr fcache;
+    
+    fcache = init_fcache(foid, true, argList, econtext);
+    
+    if (IsA(node,Oper)) {
+       onode = (Oper*) node;
+       onode->op_fcache = fcache;
+    }else if (IsA(node,Func)) {
+       fnode = (Func*) node;
+       fnode->func_fcache = fcache;
+    }else {
+       elog(WARN, "init_fcache: node must be Oper or Func!");
+    }
+}
diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c
new file mode 100644 (file)
index 0000000..2a24df8
--- /dev/null
@@ -0,0 +1,612 @@
+/*-------------------------------------------------------------------------
+ *
+ * inval.c--
+ *    POSTGRES cache invalidation dispatcher code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * Note - this code is real crufty...
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "access/heapam.h"     /* XXX to support hacks below */
+#include "access/htup.h"
+#include "catalog/catalog.h"
+#include "storage/bufpage.h"
+#include "storage/buf.h"       /* XXX for InvalidBuffer */
+#include "storage/ipc.h"
+#include "storage/sinval.h"
+#include "utils/catcache.h"
+#include "utils/inval.h"
+#include "utils/elog.h"
+#include "utils/rel.h"
+#include "utils/relcache.h"
+#include "catalog/catname.h"   /* XXX to support hacks below */
+#include "utils/syscache.h"    /* XXX to support the hacks below */
+
+/* ----------------
+ *     private invalidation structures
+ * ----------------
+ */
+typedef struct CatalogInvalidationData {
+    Index              cacheId;
+    Index              hashIndex;
+    ItemPointerData    pointerData;
+} CatalogInvalidationData;
+
+typedef struct RelationInvalidationData {
+    Oid        relationId;
+    Oid        objectId;
+} RelationInvalidationData;
+
+typedef union AnyInvalidation {
+    CatalogInvalidationData    catalog;
+    RelationInvalidationData   relation;
+} AnyInvalidation;
+
+typedef struct InvalidationMessageData {
+    char               kind;
+    AnyInvalidation    any;
+} InvalidationMessageData;
+
+typedef InvalidationMessageData        *InvalidationMessage;
+
+/* ----------------
+ *     variables and macros
+ * ----------------
+ */
+static LocalInvalid Invalid = EmptyLocalInvalid;       /* XXX global */
+static bool RefreshWhenInvalidate = false;
+
+Oid MyRelationRelationId = InvalidOid;
+Oid MyAttributeRelationId = InvalidOid;
+Oid MyAMRelationId =   InvalidOid;
+Oid MyAMOPRelationId =         InvalidOid;
+
+#define ValidateHacks() \
+    if (!OidIsValid(MyRelationRelationId)) getmyrelids()
+
+/* ----------------------------------------------------------------
+ *             "local" invalidation support functions
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     InvalidationEntryAllocate--
+ *             Allocates an invalidation entry.
+ * --------------------------------
+ */
+InvalidationEntry
+InvalidationEntryAllocate(uint16 size)
+{
+    InvalidationEntryData      *entryDataP;
+    entryDataP = (InvalidationEntryData *)
+       malloc(sizeof (char *) + size); /* XXX alignment */
+    entryDataP->nextP = NULL;
+    return ((Pointer) &entryDataP->userData);
+}
+
+/* --------------------------------
+ *     LocalInvalidRegister --
+ *        Returns a new local cache invalidation state containing a new entry.
+ * --------------------------------
+ */
+LocalInvalid
+LocalInvalidRegister(LocalInvalid invalid,
+                    InvalidationEntry entry)
+{
+    Assert(PointerIsValid(entry));
+    
+    ((InvalidationUserData *)entry)->dataP[-1] =
+       (InvalidationUserData *)invalid;
+    
+    return (entry);
+}
+
+/* --------------------------------
+ *     LocalInvalidInvalidate--
+ *             Processes, then frees all entries in a local cache 
+ *             invalidation state.
+ * --------------------------------
+ */
+void
+LocalInvalidInvalidate(LocalInvalid invalid, void (*function)())
+{
+    InvalidationEntryData      *entryDataP;
+    
+    while (PointerIsValid(invalid)) {
+       entryDataP = (InvalidationEntryData *)
+           &((InvalidationUserData *)invalid)->dataP[-1];
+       
+       if (PointerIsValid(function)) {
+           (*function)((Pointer) &entryDataP->userData);
+       }
+       
+       invalid = (Pointer) entryDataP->nextP;
+       
+       /* help catch errors */
+       entryDataP->nextP = (InvalidationUserData *) NULL;
+       
+       free((Pointer)entryDataP);
+    }
+}
+
+/* ----------------------------------------------------------------
+ *                   private support functions
+ * ----------------------------------------------------------------
+ */
+/* --------------------------------
+ *     CacheIdRegisterLocalInvalid
+ * --------------------------------
+ */
+#ifdef INVALIDDEBUG
+#define CacheIdRegisterLocalInvalid_DEBUG1 \
+elog(DEBUG, "CacheIdRegisterLocalInvalid(%d, %d, [%d, %d])", \
+     cacheId, hashIndex, ItemPointerGetBlockNumber(pointer), \
+     ItemPointerGetOffsetNumber(pointer))
+#else
+#define CacheIdRegisterLocalInvalid_DEBUG1
+#endif /* INVALIDDEBUG */
+    
+static void
+CacheIdRegisterLocalInvalid(Index cacheId,
+                           Index hashIndex,
+                           ItemPointer pointer)
+{
+    InvalidationMessage        message;
+    
+    /* ----------------
+     * debugging stuff
+     * ----------------
+     */
+    CacheIdRegisterLocalInvalid_DEBUG1;
+    
+    /* ----------------
+     * create a message describing the system catalog tuple
+     *  we wish to invalidate.
+     * ----------------
+     */
+    message = (InvalidationMessage)
+       InvalidationEntryAllocate(sizeof (InvalidationMessageData));
+    
+    message->kind = 'c';
+    message->any.catalog.cacheId = cacheId;
+    message->any.catalog.hashIndex = hashIndex;
+    
+    ItemPointerCopy(pointer, &message->any.catalog.pointerData);
+    
+    /* ----------------
+     * Note: Invalid is a global variable
+     * ----------------
+     */
+    Invalid = LocalInvalidRegister(Invalid, (InvalidationEntry)message);
+}
+
+/* --------------------------------
+ *     RelationIdRegisterLocalInvalid
+ * --------------------------------
+ */
+static void
+RelationIdRegisterLocalInvalid(Oid relationId, Oid objectId)
+{
+    InvalidationMessage        message;
+    
+    /* ----------------
+     * debugging stuff
+     * ----------------
+     */
+#ifdef INVALIDDEBUG
+    elog(DEBUG, "RelationRegisterLocalInvalid(%d, %d)", relationId,
+        objectId);
+#endif /* defined(INVALIDDEBUG) */
+    
+    /* ----------------
+     * create a message describing the relation descriptor
+     *  we wish to invalidate.
+     * ----------------
+     */
+    message = (InvalidationMessage)
+       InvalidationEntryAllocate(sizeof (InvalidationMessageData));
+    
+    message->kind = 'r';
+    message->any.relation.relationId = relationId;
+    message->any.relation.objectId = objectId;
+    
+    /* ----------------
+     * Note: Invalid is a global variable
+     * ----------------
+     */
+    Invalid = LocalInvalidRegister(Invalid, (InvalidationEntry)message);
+}
+
+/* --------------------------------
+ *     getmyrelids
+ * --------------------------------
+ */
+void
+getmyrelids()
+{
+    HeapTuple  tuple;
+    
+    tuple = SearchSysCacheTuple(RELNAME,
+                               PointerGetDatum(RelationRelationName),
+                               0,0,0);
+    Assert(HeapTupleIsValid(tuple));
+    MyRelationRelationId = tuple->t_oid;
+    
+    tuple = SearchSysCacheTuple(RELNAME,
+                               PointerGetDatum(AttributeRelationName),
+                               0,0,0);
+    Assert(HeapTupleIsValid(tuple));
+    MyAttributeRelationId = tuple->t_oid;
+    
+    tuple = SearchSysCacheTuple(RELNAME,
+                               PointerGetDatum(AccessMethodRelationName),
+                               0,0,0);
+    Assert(HeapTupleIsValid(tuple));
+    MyAMRelationId = tuple->t_oid;
+    
+    tuple = SearchSysCacheTuple(RELNAME,
+                               PointerGetDatum(AccessMethodOperatorRelationName),
+                               0,0,0);
+    Assert(HeapTupleIsValid(tuple));
+    MyAMOPRelationId = tuple->t_oid;
+}
+
+/* --------------------------------
+ *     CacheIdInvalidate
+ *
+ *     This routine can invalidate an tuple in a system catalog cache
+ *     or a cached relation descriptor.  You pay your money and you
+ *     take your chances...
+ * --------------------------------
+ */
+#ifdef INVALIDDEBUG
+#define CacheIdInvalidate_DEBUG1 \
+elog(DEBUG, "CacheIdInvalidate(%d, %d, 0x%x[%d])", cacheId, hashIndex,\
+     pointer, ItemPointerIsValid(pointer))
+#else
+#define CacheIdInvalidate_DEBUG1
+#endif /* defined(INVALIDDEBUG) */
+     
+static void
+CacheIdInvalidate(Index cacheId,
+                 Index hashIndex,
+                 ItemPointer pointer)
+{
+    /* ----------------
+     * assume that if the item pointer is valid, then we are
+     *  invalidating an item in the specified system catalog cache.
+     * ----------------
+     */
+    if (ItemPointerIsValid(pointer)) {
+       CatalogCacheIdInvalidate(cacheId, hashIndex, pointer);
+       return;
+    }
+    
+    CacheIdInvalidate_DEBUG1;
+    
+    ValidateHacks();   /* XXX */
+    
+    /* ----------------
+     * if the cacheId is the oid of any of the tuples in the
+     *  following system relations, then assume we are invalidating
+     *  a relation descriptor
+     * ----------------
+     */
+    if (cacheId == MyRelationRelationId) {
+       RelationIdInvalidateRelationCacheByRelationId(hashIndex);
+       return;
+    }
+    
+    if (cacheId == MyAttributeRelationId) {
+       RelationIdInvalidateRelationCacheByRelationId(hashIndex);
+       return;
+    }
+    
+    if (cacheId == MyAMRelationId) {
+       RelationIdInvalidateRelationCacheByAccessMethodId(hashIndex);
+       return;
+    }
+    
+    if (cacheId == MyAMOPRelationId) {
+       RelationIdInvalidateRelationCacheByAccessMethodId(InvalidOid);
+       return;
+    }
+    
+    /* ----------------
+     * Yow! the caller asked us to invalidate something else.
+     * ----------------
+     */
+    elog(FATAL, "CacheIdInvalidate: cacheId=%d relation id?", cacheId);
+}
+
+/* --------------------------------
+ *     ResetSystemCaches
+ *
+ *     this blows away all tuples in the system catalog caches and
+ *     all the cached relation descriptors (and closes the files too).
+ * --------------------------------
+ */
+static void
+ResetSystemCaches()
+{
+    ResetSystemCache();
+    RelationCacheInvalidate(false); 
+}
+
+/* --------------------------------
+ *     InvalidationMessageRegisterSharedInvalid
+ * --------------------------------
+ */
+#ifdef INVALIDDEBUG
+#define InvalidationMessageRegisterSharedInvalid_DEBUG1 \
+elog(DEBUG,\
+     "InvalidationMessageRegisterSharedInvalid(c, %d, %d, [%d, %d])",\
+     message->any.catalog.cacheId,\
+     message->any.catalog.hashIndex,\
+     ItemPointerGetBlockNumber(&message->any.catalog.pointerData),\
+     ItemPointerGetOffsetNumber(&message->any.catalog.pointerData))
+#define InvalidationMessageRegisterSharedInvalid_DEBUG2 \
+     elog(DEBUG, \
+         "InvalidationMessageRegisterSharedInvalid(r, %d, %d)", \
+         message->any.relation.relationId, \
+         message->any.relation.objectId)
+#else    
+#define InvalidationMessageRegisterSharedInvalid_DEBUG1
+#define InvalidationMessageRegisterSharedInvalid_DEBUG2
+#endif /* INVALIDDEBUG */
+     
+static void
+InvalidationMessageRegisterSharedInvalid(InvalidationMessage message)
+{
+    Assert(PointerIsValid(message));
+    
+    switch (message->kind) {
+    case 'c':  /* cached system catalog tuple */
+       InvalidationMessageRegisterSharedInvalid_DEBUG1;
+       
+       RegisterSharedInvalid(message->any.catalog.cacheId,
+                             message->any.catalog.hashIndex,
+                             &message->any.catalog.pointerData);
+       break;
+       
+    case 'r':   /* cached relation descriptor */
+       InvalidationMessageRegisterSharedInvalid_DEBUG2;
+       
+       RegisterSharedInvalid(message->any.relation.relationId,
+                             message->any.relation.objectId,
+                             (ItemPointer) NULL);
+       break;
+       
+    default:
+       elog(FATAL,
+            "InvalidationMessageRegisterSharedInvalid: `%c' kind",
+            message->kind);
+    }
+}
+
+/* --------------------------------
+ *     InvalidationMessageCacheInvalidate
+ * --------------------------------
+ */
+#ifdef INVALIDDEBUG
+#define InvalidationMessageCacheInvalidate_DEBUG1 \
+elog(DEBUG, "InvalidationMessageCacheInvalidate(c, %d, %d, [%d, %d])",\
+     message->any.catalog.cacheId,\
+     message->any.catalog.hashIndex,\
+     ItemPointerGetBlockNumber(&message->any.catalog.pointerData),\
+     ItemPointerGetOffsetNumber(&message->any.catalog.pointerData))
+#define InvalidationMessageCacheInvalidate_DEBUG2 \
+     elog(DEBUG, "InvalidationMessageCacheInvalidate(r, %d, %d)", \
+         message->any.relation.relationId, \
+         message->any.relation.objectId)
+#else
+#define InvalidationMessageCacheInvalidate_DEBUG1
+#define InvalidationMessageCacheInvalidate_DEBUG2
+#endif /* defined(INVALIDDEBUG) */
+     
+static void
+InvalidationMessageCacheInvalidate(InvalidationMessage message)
+{
+    Assert(PointerIsValid(message));
+    
+    switch (message->kind) {
+    case 'c':  /* cached system catalog tuple */
+       InvalidationMessageCacheInvalidate_DEBUG1;
+       
+       CatalogCacheIdInvalidate(message->any.catalog.cacheId,
+                                message->any.catalog.hashIndex,
+                                &message->any.catalog.pointerData);
+       break;
+       
+    case 'r':  /* cached relation descriptor */
+       InvalidationMessageCacheInvalidate_DEBUG2;
+       
+       /* XXX ignore this--is this correct ??? */
+       break;
+       
+    default:
+       elog(FATAL, "InvalidationMessageCacheInvalidate: `%c' kind",
+            message->kind);
+    }
+}
+
+/* --------------------------------
+ *     RelationInvalidateRelationCache
+ * --------------------------------
+ */
+static void
+RelationInvalidateRelationCache(Relation relation,
+                               HeapTuple tuple,
+                               void (*function)())
+{
+    Oid        relationId;
+    Oid        objectId;
+    
+    /* ----------------
+     * get the relation object id
+     * ----------------
+     */
+    ValidateHacks();   /* XXX */
+    relationId = RelationGetRelationId(relation);
+    
+    /* ----------------
+     * 
+     * ----------------
+     */
+    if (relationId == MyRelationRelationId) {
+       objectId = tuple->t_oid;
+    } else if (relationId == MyAttributeRelationId) {
+       objectId = ((AttributeTupleForm)GETSTRUCT(tuple))->attrelid;
+    } else if (relationId == MyAMRelationId) {
+       objectId = tuple->t_oid;
+    } else if (relationId == MyAMOPRelationId) {
+       ; /* objectId is unused */
+    } else 
+       return;
+    
+    /* ----------------
+     *   can't handle immediate relation descriptor invalidation
+     * ----------------
+     */
+    Assert(PointerIsValid(function));
+    
+    (*function)(relationId, objectId);
+}
+
+/*
+ * DiscardInvalid --
+ *     Causes the invalidated cache state to be discarded.
+ *
+ * Note:
+ *     This should be called as the first step in processing a transaction.
+ *     This should be called while waiting for a query from the front end
+ *     when other backends are active.
+ */
+void
+DiscardInvalid()
+{
+    /* ----------------
+     * debugging stuff
+     * ----------------
+     */
+#ifdef INVALIDDEBUG
+    elog(DEBUG, "DiscardInvalid called");
+#endif /* defined(INVALIDDEBUG) */
+    
+    InvalidateSharedInvalid(CacheIdInvalidate, ResetSystemCaches);
+}
+
+/*
+ * RegisterInvalid --
+ *     Causes registration of invalidated state with other backends iff true.
+ *
+ * Note:
+ *     This should be called as the last step in processing a transaction.
+ */
+void
+RegisterInvalid(bool send)
+{
+    /* ----------------
+     * debugging stuff
+     * ----------------
+     */
+#ifdef INVALIDDEBUG
+    elog(DEBUG, "RegisterInvalid(%d) called", send);
+#endif /* defined(INVALIDDEBUG) */
+    
+    /* ----------------
+     * Note: Invalid is a global variable
+     * ----------------
+     */
+    if (send)
+       LocalInvalidInvalidate(Invalid,
+                              InvalidationMessageRegisterSharedInvalid);
+    else
+       LocalInvalidInvalidate(Invalid,
+                              InvalidationMessageCacheInvalidate);
+    
+    Invalid = EmptyLocalInvalid;
+}
+
+/*
+ * SetRefreshWhenInvalidate --
+ *     Causes the local caches to be immediately refreshed iff true.
+ */
+void
+SetRefreshWhenInvalidate(bool on)
+{
+#ifdef INVALIDDEBUG
+    elog(DEBUG, "RefreshWhenInvalidate(%d) called", on);
+#endif /* defined(INVALIDDEBUG) */
+    
+    RefreshWhenInvalidate = on;
+}
+
+/*
+ * RelationIdInvalidateHeapTuple --
+ *     Causes the given tuple in a relation to be invalidated.
+ *
+ * Note:
+ *     Assumes object id is valid.
+ *     Assumes tuple is valid.
+ */
+#ifdef INVALIDDEBUG
+#define RelationInvalidateHeapTuple_DEBUG1 \
+elog(DEBUG, "RelationInvalidateHeapTuple(%.16s, [%d,%d])", \
+     RelationGetRelationName(relation), \
+     ItemPointerGetBlockNumber(&tuple->t_ctid), \
+     ItemPointerGetOffsetNumber(&tuple->t_ctid))
+#else
+#define RelationInvalidateHeapTuple_DEBUG1
+#endif /* defined(INVALIDDEBUG) */
+     
+void
+RelationInvalidateHeapTuple(Relation relation, HeapTuple tuple)
+{
+    /* ----------------
+     * sanity checks
+     * ----------------
+     */
+    Assert(RelationIsValid(relation));
+    Assert(HeapTupleIsValid(tuple));
+    
+    if (IsBootstrapProcessingMode())
+       return;
+    /* ----------------
+     * this only works for system relations now
+     * ----------------
+     */
+    if (! IsSystemRelationName(RelationGetRelationTupleForm(relation)->relname.data))
+       return;
+    
+    /* ----------------
+     * debugging stuff
+     * ----------------
+     */
+    RelationInvalidateHeapTuple_DEBUG1;
+    
+    /* ----------------
+     * 
+     * ----------------
+     */
+    RelationInvalidateCatalogCacheTuple(relation,
+                                       tuple,
+                                       CacheIdRegisterLocalInvalid);
+    
+    RelationInvalidateRelationCache(relation,
+                                   tuple,
+                                   RelationIdRegisterLocalInvalid);
+    
+    if (RefreshWhenInvalidate)
+       RelationInvalidateCatalogCacheTuple(relation,
+                                           tuple,
+                                           (void (*)()) NULL);
+}
+
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
new file mode 100644 (file)
index 0000000..ac2d1fd
--- /dev/null
@@ -0,0 +1,484 @@
+/*-------------------------------------------------------------------------
+ *
+ * lsyscache.c--
+ *    Routines to access information within system caches      
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    Eventually, the index information should go through here, too.
+ *    
+ *    Most of these routines call SearchSysCacheStruct() and thus simply
+ *    (1) allocate some space for the return struct and (2) call it.
+ *    
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "postgres.h"
+
+#include "nodes/pg_list.h"
+#include "utils/syscache.h"
+#include "utils/lsyscache.h"
+#include "access/tupmacs.h"
+#include "utils/rel.h"
+#include "utils/palloc.h"
+#include "utils/elog.h"
+#include "access/attnum.h"
+#include "access/heapam.h"
+
+#include "catalog/pg_amop.h"
+#include "catalog/pg_type.h"
+
+/*             ---------- AMOP CACHES ----------                        */
+
+/*    
+ * op_class -
+ *    
+ *     Return t iff operator 'opno' is in operator class 'opclass'.
+ *    
+ */
+bool
+op_class(Oid opno, int32 opclass, Oid amopid)
+{
+    FormData_pg_amop amoptup;
+
+    if (SearchSysCacheStruct(AMOPOPID, 
+                            (char *) &amoptup,
+                            ObjectIdGetDatum(opclass),
+                            ObjectIdGetDatum(opno),
+                            ObjectIdGetDatum(amopid),
+                            0))
+       return(true);
+    else
+       return(false);
+}
+
+/*             ---------- ATTRIBUTE CACHES ----------                   */
+
+/*    
+ * get_attname -
+ *    
+ *     Given the relation id and the attribute number,
+ *     return the "attname" field from the attribute relation.
+ *    
+ */
+char*
+get_attname(Oid relid, AttrNumber attnum)
+{
+    FormData_pg_attribute att_tup;
+    char *retval;
+
+    if (SearchSysCacheStruct(ATTNUM, 
+                            (char*)&att_tup,
+                            ObjectIdGetDatum(relid),
+                            UInt16GetDatum(attnum),
+                            0,0)) {
+       retval = pstrdup(att_tup.attname.data);
+
+       return(retval);
+    }
+    else
+      return(NULL);
+}
+
+/*    
+ * get_attnum -
+ *    
+ *     Given the relation id and the attribute name,
+ *     return the "attnum" field from the attribute relation.
+ *    
+ */
+AttrNumber
+get_attnum(Oid relid, char *attname)
+{
+    FormData_pg_attribute att_tup;
+
+    if (SearchSysCacheStruct(ATTNAME, (char *) &att_tup,
+                            ObjectIdGetDatum(relid),
+                            PointerGetDatum(attname),
+                            0,0))
+       return(att_tup.attnum);
+    else
+       return(InvalidAttrNumber);
+}
+
+/*    
+ * get_atttype -
+ *    
+ *     Given the relation OID and the attribute number with the relation,
+ *     return the attribute type OID.
+ *    
+ */
+Oid
+get_atttype(Oid relid, AttrNumber attnum)
+{
+    AttributeTupleForm att_tup = (AttributeTupleForm)palloc(sizeof(*att_tup));
+
+    if (SearchSysCacheStruct(ATTNUM, 
+                            (char *) att_tup,
+                            ObjectIdGetDatum(relid),
+                            UInt16GetDatum(attnum),
+                            0,0))
+       return(att_tup->atttypid);
+    else
+       return((Oid)NULL);
+}
+
+/* This routine uses the attname instead of the attnum because it
+ * replaces the routine find_atttype, which is called sometimes when
+ * only the attname, not the attno, is available.
+ */
+bool
+get_attisset(Oid relid, char *attname)
+{
+    HeapTuple htup;
+    AttrNumber attno;
+    AttributeTupleForm att_tup;
+
+    attno = get_attnum(relid, attname);
+     
+    htup = SearchSysCacheTuple(ATTNAME, 
+                              ObjectIdGetDatum(relid),
+                              PointerGetDatum(attname), 
+                              0,0);
+    if (!HeapTupleIsValid(htup))
+       elog(WARN, "get_attisset: no attribute %.16s in relation %d",
+            attname, relid);
+    if (heap_attisnull(htup, attno))
+       return(false);
+    else {
+       att_tup = (AttributeTupleForm)GETSTRUCT(htup);
+       return(att_tup->attisset);
+    }
+}
+
+/*             ---------- INDEX CACHE ----------                        */
+
+/*     watch this space...
+ */
+
+/*             ---------- OPERATOR CACHE ----------                     */
+
+/*    
+ * get_opcode -
+ *    
+ *     Returns the regproc id of the routine used to implement an
+ *     operator given the operator uid.
+ *    
+ */
+RegProcedure
+get_opcode(Oid opno)
+{
+    FormData_pg_operator optup;
+
+    if (SearchSysCacheStruct(OPROID, (char *) &optup,
+                            ObjectIdGetDatum(opno),
+                            0,0,0))
+       return(optup.oprcode);
+    else
+       return((RegProcedure)NULL);
+}
+
+/*
+ * get_opname -
+ *    returns the name of the operator with the given opno
+ *
+ * Note: return the struct so that it gets copied.
+ */
+char*
+get_opname(Oid opno)
+{
+    FormData_pg_operator optup;
+
+    if (SearchSysCacheStruct(OPROID, (char *) &optup,
+                            ObjectIdGetDatum(opno),
+                            0,0,0))
+       return (pstrdup(optup.oprname.data));
+    else {
+       elog(WARN, "can't look up operator %d\n", opno);
+       return NULL;
+    }
+}
+
+/*    
+ * op_mergesortable -
+ *    
+ *     Returns the left and right sort operators and types corresponding to a
+ *     mergesortable operator, or nil if the operator is not mergesortable.
+ *    
+ */
+bool
+op_mergesortable(Oid opno, Oid ltype, Oid rtype, Oid *leftOp, Oid *rightOp) 
+{
+    FormData_pg_operator optup;
+
+    if (SearchSysCacheStruct(OPROID, (char *) &optup,
+                            ObjectIdGetDatum(opno),
+                            0,0,0) &&
+       optup.oprlsortop &&
+       optup.oprrsortop && 
+       optup.oprleft == ltype &&
+       optup.oprright == rtype) {
+
+       *leftOp = ObjectIdGetDatum(optup.oprlsortop);
+       *rightOp = ObjectIdGetDatum(optup.oprrsortop);
+       return TRUE;
+    } else {
+       return FALSE;
+    }
+}
+
+/*    
+ * op_hashjoinable--
+ *    
+ * Returns the hash operator corresponding to a hashjoinable operator, 
+ * or nil if the operator is not hashjoinable.
+ *    
+ */
+Oid
+op_hashjoinable(Oid opno, Oid ltype, Oid rtype)
+{
+    FormData_pg_operator optup;
+
+    if (SearchSysCacheStruct(OPROID, (char *) &optup,
+                            ObjectIdGetDatum(opno),
+                            0,0,0) &&
+       optup.oprcanhash  &&
+       optup.oprleft == ltype &&
+       optup.oprright == rtype) 
+       return(opno);
+    else
+       return(InvalidOid);
+}
+
+/*    
+ * get_commutator -
+ *    
+ *     Returns the corresponding commutator of an operator.
+ *    
+ */
+Oid
+get_commutator(Oid opno)
+{
+    FormData_pg_operator optup;
+
+    if (SearchSysCacheStruct(OPROID, (char *) &optup,
+                            ObjectIdGetDatum(opno),
+                            0,0,0))
+       return(optup.oprcom);
+    else
+       return((Oid)NULL);
+}
+
+HeapTuple
+get_operator_tuple(Oid opno)
+{
+    HeapTuple optup;
+
+    if ((optup = SearchSysCacheTuple(OPROID, 
+                                    ObjectIdGetDatum(opno),
+                                    0,0,0)))
+       return(optup);
+    else
+       return((HeapTuple)NULL);
+}
+
+/*    
+ * get_negator -
+ *    
+ *     Returns the corresponding negator of an operator.
+ *    
+ */
+Oid
+get_negator(Oid opno)
+{
+    FormData_pg_operator optup;
+
+    if (SearchSysCacheStruct(OPROID, (char *) &optup,
+                            ObjectIdGetDatum(opno),
+                            0,0,0))
+       return(optup.oprnegate);
+    else
+       return((Oid)NULL);
+}
+
+/*    
+ * get_oprrest -
+ *    
+ *     Returns procedure id for computing selectivity of an operator.
+ *    
+ */
+RegProcedure
+get_oprrest(Oid opno)
+{
+    FormData_pg_operator optup;
+
+    if (SearchSysCacheStruct(OPROID, (char *) &optup,
+                            ObjectIdGetDatum(opno),
+                            0,0,0))
+       return(optup.oprrest );
+    else
+       return((RegProcedure) NULL);
+}
+
+/*    
+ * get_oprjoin -
+ *    
+ *     Returns procedure id for computing selectivity of a join.
+ *    
+ */
+RegProcedure
+get_oprjoin(Oid opno)
+{
+    FormData_pg_operator optup;
+
+    if (SearchSysCacheStruct(OPROID, (char *) &optup,
+                            ObjectIdGetDatum(opno),
+                            0,0,0))
+       return(optup.oprjoin);
+    else
+       return((RegProcedure)NULL);
+}
+
+/*             ---------- RELATION CACHE ----------                     */
+
+/*    
+ * get_relnatts -
+ *    
+ *     Returns the number of attributes for a given relation.
+ *    
+ */
+int
+get_relnatts(Oid relid)
+{
+    FormData_pg_class reltup;
+
+    if (SearchSysCacheStruct(RELOID, (char *) &reltup,
+                            ObjectIdGetDatum(relid),
+                            0,0,0))
+       return(reltup.relnatts);
+    else
+       return(InvalidAttrNumber);
+}
+
+/*    
+ * get_rel_name -
+ *    
+ *     Returns the name of a given relation.
+ *    
+ */
+char*
+get_rel_name(Oid relid)
+{
+    FormData_pg_class reltup;
+
+    if ((SearchSysCacheStruct(RELOID, 
+                             (char*)&reltup,
+                             ObjectIdGetDatum(relid),
+                             0,0,0))) {
+       return (pstrdup(reltup.relname.data));
+    } else
+       return(NULL);
+}
+
+/*             ---------- TYPE CACHE ----------                         */
+
+/*    
+ * get_typlen -
+ *    
+ *     Given the type OID, return the length of the type.
+ *    
+ */
+int16
+get_typlen(Oid typid)
+{
+    TypeTupleFormData typtup;
+
+    if (SearchSysCacheStruct(TYPOID, (char *) &typtup,
+                            ObjectIdGetDatum(typid),
+                            0,0,0))
+       return(typtup.typlen);
+    else
+       return((int16)NULL);
+}
+
+/*    
+ * get_typbyval -
+ *    
+ *     Given the type OID, determine whether the type is returned by value or
+ *     not.  Returns 1 if by value, 0 if by reference.
+ *    
+ */
+bool
+get_typbyval(Oid typid)
+{
+    TypeTupleFormData typtup;
+
+    if (SearchSysCacheStruct(TYPOID, (char *) &typtup,
+                            ObjectIdGetDatum(typid),
+                            0,0,0))
+       return((bool)typtup.typbyval);
+    else
+       return(false);
+}
+
+/*    
+ * get_typbyval -
+ *    
+ *     Given the type OID, determine whether the type is returned by value or
+ *     not.  Returns 1 if by value, 0 if by reference.
+ *    
+ */
+char
+get_typalign(Oid typid)
+{
+    TypeTupleFormData typtup;
+
+    if (SearchSysCacheStruct(TYPOID, (char *) &typtup,
+                            ObjectIdGetDatum(typid),
+                            0,0,0))
+       return(typtup.typalign);
+    else
+       return ('i');
+}
+
+/*    
+ * get_typdefault - 
+ *    
+ *     Given the type OID, return the default value of the ADT.
+ *    
+ */
+struct varlena *
+get_typdefault(Oid typid)
+{
+    struct varlena *typdefault = 
+       (struct varlena *)TypeDefaultRetrieve (typid);
+    return(typdefault);
+}
+
+/*    
+ * get_typtype -
+ *    
+ *     Given the type OID, find if it is a basic type, a named relation
+ *     or the generic type 'relation'.
+ *     It returns the null char if the cache lookup fails...
+ *    
+ */
+char
+get_typtype(Oid typid)
+{
+    TypeTupleFormData typtup;
+
+    if (SearchSysCacheStruct(TYPOID, (char *) &typtup,
+                            ObjectIdGetDatum(typid),
+                            0,0,0)) {
+       return(typtup.typtype);
+    } else {
+       return('\0');
+    }
+}
+
diff --git a/src/backend/utils/cache/rel.c b/src/backend/utils/cache/rel.c
new file mode 100644 (file)
index 0000000..a794825
--- /dev/null
@@ -0,0 +1,77 @@
+/*-------------------------------------------------------------------------
+ *
+ * rel.c--
+ *    POSTGRES relation descriptor code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/* #define RELREFDEBUG 1 */
+
+#include "postgres.h"
+#include "miscadmin.h"
+#include "access/istrat.h"
+#include "access/tupdesc.h"
+#include "utils/rel.h"
+#include "storage/fd.h"
+
+
+/* 
+ *     RelationIsValid is now a macro in rel.h -cim 4/27/91
+ *
+ *      Many of the RelationGet...() functions are now macros in rel.h
+ *             -mer 3/2/92
+ */
+
+/*
+ * RelationGetIndexStrategy --
+ *     Returns index strategy for a relation.
+ *
+ * Note:
+ *     Assumes relation descriptor is valid.
+ *     Assumes relation descriptor is for an index relation.
+ */
+IndexStrategy
+RelationGetIndexStrategy(Relation relation)
+{
+  return relation->rd_istrat;
+}
+
+/*
+ * RelationSetIndexSupport --
+ *     Sets index strategy and support info for a relation.
+ *
+ * Note:
+ *     Assumes relation descriptor is a valid pointer to sufficient space.
+ *     Assumes index strategy is valid.  Assumes support is valid if non-
+ *     NULL.
+ */
+/* ----------------
+ *     RelationSetIndexSupport
+ *
+ *     This routine saves two pointers -- one to the IndexStrategy, and
+ *     one to the RegProcs that support the indexed access method.  These
+ *     pointers are stored in the space following the attribute data in the
+ *     reldesc.
+ *
+ *   NEW:  the index strategy and support are now stored in real fields
+ *         at the end of the structure                    - jolly
+ * ----------------
+ */
+void
+RelationSetIndexSupport(Relation relation,
+                       IndexStrategy strategy,
+                       RegProcedure *support)
+{
+    Assert(PointerIsValid(relation));
+    Assert(IndexStrategyIsValid(strategy));
+    
+    relation->rd_istrat = strategy;
+    relation->rd_support = support;
+}
+
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
new file mode 100644 (file)
index 0000000..885a5f7
--- /dev/null
@@ -0,0 +1,1795 @@
+/*-------------------------------------------------------------------------
+ *
+ * relcache.c--
+ *    POSTGRES relation descriptor cache code
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *     RelationInitialize              - initialize relcache
+ *     RelationIdCacheGetRelation      - get a reldesc from the cache (id)
+ *     RelationNameCacheGetRelation    - get a reldesc from the cache (name)
+ *     RelationIdGetRelation           - get a reldesc by relation id
+ *     RelationNameGetRelation         - get a reldesc by relation name
+ *     RelationClose                   - close an open relation
+ *     RelationFlushRelation           - flush relation information
+ *
+ * NOTES
+ *     This file is in the process of being cleaned up
+ *     before I add system attribute indexing.  -cim 1/13/91
+ *
+ *     The following code contains many undocumented hacks.  Please be
+ *     careful....
+ *
+ */
+#include <stdio.h>             /* for sprintf() */
+#include <errno.h>
+#include <sys/file.h>
+#include <string.h>
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "access/attnum.h"
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/istrat.h"
+#include "access/itup.h"
+#include "access/skey.h"
+#include "utils/builtins.h"
+#include "utils/tqual.h"       /* for NowTimeQual */
+#include "access/tupdesc.h"
+#include "access/tupmacs.h"
+#include "access/xact.h"
+#include "storage/buf.h"
+#include "storage/fd.h"                /* for SEEK_ */
+#include "storage/lmgr.h"
+#include "storage/bufmgr.h"
+
+#include "lib/hasht.h"
+#include "utils/memutils.h"
+#include "utils/elog.h"
+#include "utils/mcxt.h"
+#include "utils/rel.h"
+#include "utils/relcache.h"
+#include "utils/hsearch.h"
+#include "utils/palloc.h"
+#include "utils/relcache.h"
+#include "catalog/catname.h"
+#include "catalog/catalog.h"
+#include "utils/syscache.h"
+
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_aggregate.h"
+#include "catalog/pg_index.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_rewrite.h"
+#include "catalog/pg_type.h"
+
+#include "catalog/pg_variable.h"
+#include "catalog/pg_log.h"
+#include "catalog/pg_time.h"
+#include "catalog/indexing.h"
+#include "catalog/index.h"
+#include "fmgr.h"
+
+/* ----------------
+ *     defines
+ * ----------------
+ */
+#define private static
+#define INIT_FILENAME  "pg_internal.init"
+
+/* ----------------
+ *     externs
+ * ----------------
+ */
+extern bool    AMI_OVERRIDE;   /* XXX style */
+extern GlobalMemory CacheCxt;  /* from utils/cache/catcache.c */
+
+/* ----------------
+ *     hardcoded tuple descriptors.  see lib/backend/catalog/pg_attribute.h
+ * ----------------
+ */
+FormData_pg_attribute Desc_pg_class[Natts_pg_class] = { Schema_pg_class };
+FormData_pg_attribute Desc_pg_attribute[Natts_pg_attribute] = { Schema_pg_attribute };
+FormData_pg_attribute Desc_pg_proc[Natts_pg_proc] = { Schema_pg_proc };
+FormData_pg_attribute Desc_pg_type[Natts_pg_type] = { Schema_pg_type };
+FormData_pg_attribute Desc_pg_variable[Natts_pg_variable] = { Schema_pg_variable };
+FormData_pg_attribute Desc_pg_log[Natts_pg_log] = { Schema_pg_log };
+FormData_pg_attribute Desc_pg_time[Natts_pg_time] = { Schema_pg_time };
+
+/* ----------------
+ *     global variables
+ *
+ *     Relations are cached two ways, by name and by id,
+ *     thus there are two hash tables for referencing them. 
+ * ----------------
+ */
+HTAB   *RelationNameCache;
+HTAB   *RelationIdCache;
+
+/* ----------------
+ *     RelationBuildDescInfo exists so code can be shared
+ *      between RelationIdGetRelation() and RelationNameGetRelation()
+ * ----------------
+ */
+typedef struct RelationBuildDescInfo {
+    int infotype;              /* lookup by id or by name */
+#define INFO_RELID 1
+#define INFO_RELNAME 2
+    union {
+       Oid info_id;    /* relation object id */
+       char *info_name;        /* relation name */
+    } i;
+} RelationBuildDescInfo;
+
+typedef struct relidcacheent {
+    Oid reloid;
+    Relation reldesc;
+} RelIdCacheEnt;
+
+typedef struct relnamecacheent {
+    NameData relname;
+    Relation reldesc;
+} RelNameCacheEnt;
+
+/* -----------------
+ *     macros to manipulate name cache and id cache
+ * -----------------
+ */
+#define RelationCacheInsert(RELATION)  \
+    {   RelIdCacheEnt *idhentry; RelNameCacheEnt *namehentry; \
+       char *relname; Oid reloid; bool found; \
+       relname = (RELATION->rd_rel->relname).data; \
+       namehentry = (RelNameCacheEnt*)hash_search(RelationNameCache, \
+                                                  relname, \
+                                                  HASH_ENTER, \
+                                                  &found); \
+       if (namehentry == NULL) { \
+           elog(FATAL, "can't insert into relation descriptor cache"); \
+         } \
+       if (found && !IsBootstrapProcessingMode()) { \
+           /* used to give notice -- now just keep quiet */ ; \
+         } \
+       namehentry->reldesc = RELATION; \
+       reloid = RELATION->rd_id; \
+       idhentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \
+                                              (char *)&reloid, \
+                                              HASH_ENTER, \
+                                              &found); \
+       if (idhentry == NULL) { \
+           elog(FATAL, "can't insert into relation descriptor cache"); \
+         } \
+       if (found && !IsBootstrapProcessingMode()) { \
+           /* used to give notice -- now just keep quiet */ ; \
+         } \
+       idhentry->reldesc = RELATION; \
+    }
+#define RelationNameCacheLookup(NAME, RELATION)        \
+    {   RelNameCacheEnt *hentry; bool found; \
+       hentry = (RelNameCacheEnt*)hash_search(RelationNameCache, \
+                                              (char *)NAME,HASH_FIND,&found); \
+       if (hentry == NULL) { \
+           elog(FATAL, "error in CACHE"); \
+         } \
+       if (found) { \
+           RELATION = hentry->reldesc; \
+         } \
+       else { \
+           RELATION = NULL; \
+         } \
+    }
+#define RelationIdCacheLookup(ID, RELATION)    \
+    {   RelIdCacheEnt *hentry; bool found; \
+       hentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \
+                                            (char *)&(ID),HASH_FIND, &found); \
+       if (hentry == NULL) { \
+           elog(FATAL, "error in CACHE"); \
+         } \
+       if (found) { \
+           RELATION = hentry->reldesc; \
+         } \
+       else { \
+           RELATION = NULL; \
+         } \
+    }
+#define RelationCacheDelete(RELATION)  \
+    {   RelNameCacheEnt *namehentry; RelIdCacheEnt *idhentry; \
+       char *relname; Oid reloid; bool found; \
+       relname = (RELATION->rd_rel->relname).data; \
+       namehentry = (RelNameCacheEnt*)hash_search(RelationNameCache, \
+                                                  relname, \
+                                                  HASH_REMOVE, \
+                                                  &found); \
+       if (namehentry == NULL) { \
+           elog(FATAL, "can't delete from relation descriptor cache"); \
+         } \
+       if (!found) { \
+           elog(NOTICE, "trying to delete a reldesc that does not exist."); \
+         } \
+       reloid = RELATION->rd_id; \
+       idhentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \
+                                              (char *)&reloid, \
+                                              HASH_REMOVE, &found); \
+       if (idhentry == NULL) { \
+           elog(FATAL, "can't delete from relation descriptor cache"); \
+         } \
+       if (!found) { \
+           elog(NOTICE, "trying to delete a reldesc that does not exist."); \
+         } \
+    }
+
+/* non-export function prototypes */
+static void formrdesc(char *relationName, u_int natts,
+                     FormData_pg_attribute att[]);
+
+static void RelationFlushIndexes(Relation *r, Oid accessMethodId);
+
+static char *BuildDescInfoError(RelationBuildDescInfo buildinfo);
+static HeapTuple ScanPgRelation(RelationBuildDescInfo buildinfo);
+static HeapTuple scan_pg_rel_seq(RelationBuildDescInfo buildinfo);
+static HeapTuple scan_pg_rel_ind(RelationBuildDescInfo buildinfo);
+static Relation AllocateRelationDesc(u_int natts, Form_pg_class relp);
+static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,    
+               Relation relation, AttributeTupleForm attp, u_int natts);
+static void build_tupdesc_seq(RelationBuildDescInfo buildinfo,
+               Relation relation, AttributeTupleForm attp, u_int natts);
+static void build_tupdesc_ind(RelationBuildDescInfo buildinfo,
+               Relation relation, AttributeTupleForm attp, u_int natts);
+static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo);
+static void IndexedAccessMethodInitialize(Relation relation);
+
+/* ----------------------------------------------------------------
+ *     RelationIdGetRelation() and RelationNameGetRelation()
+ *                     support functions
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     BuildDescInfoError returns a string appropriate to
+ *     the buildinfo passed to it
+ * --------------------------------
+ */
+static char *
+BuildDescInfoError(RelationBuildDescInfo buildinfo)
+{
+    static char errBuf[64];
+    
+    memset(errBuf, 0, (int) sizeof(errBuf));
+    switch(buildinfo.infotype) {
+    case INFO_RELID:
+       sprintf(errBuf, "(relation id %d)", buildinfo.i.info_id);
+       break;
+    case INFO_RELNAME:
+       sprintf(errBuf, "(relation name %.*s)", NAMEDATALEN, buildinfo.i.info_name);
+       break;
+    }
+    
+    return errBuf;
+}
+
+/* --------------------------------
+ *     ScanPgRelation
+ *
+ *     this is used by RelationBuildDesc to find a pg_class
+ *     tuple matching either a relation name or a relation id
+ *     as specified in buildinfo.
+ * --------------------------------
+ */
+static HeapTuple
+ScanPgRelation(RelationBuildDescInfo buildinfo)
+{
+    /*
+     *  If this is bootstrap time (initdb), then we can't use the system
+     *  catalog indices, because they may not exist yet.  Otherwise, we
+     *  can, and do.
+     */
+    
+    if (IsBootstrapProcessingMode())
+       return (scan_pg_rel_seq(buildinfo));
+    else
+       return (scan_pg_rel_ind(buildinfo));
+}
+
+static HeapTuple
+scan_pg_rel_seq(RelationBuildDescInfo buildinfo)
+{
+    HeapTuple   pg_class_tuple;
+    HeapTuple   return_tuple;
+    Relation    pg_class_desc;
+    HeapScanDesc pg_class_scan;
+    ScanKeyData         key;
+    Buffer      buf;
+    
+    /* ----------------
+     * form a scan key
+     * ----------------
+     */
+    switch (buildinfo.infotype) {
+    case INFO_RELID:
+       ScanKeyEntryInitialize(&key, 0,
+                              ObjectIdAttributeNumber,
+                              ObjectIdEqualRegProcedure,
+                              ObjectIdGetDatum(buildinfo.i.info_id));
+       break;
+       
+    case INFO_RELNAME:
+       ScanKeyEntryInitialize(&key, 0,
+                              Anum_pg_class_relname,
+                              Character16EqualRegProcedure,
+                              NameGetDatum(buildinfo.i.info_name));
+       break;
+       
+    default:
+       elog(WARN, "ScanPgRelation: bad buildinfo");
+       return NULL;
+    }
+    
+    /* ----------------
+     * open pg_class and fetch a tuple
+     * ----------------
+     */
+    pg_class_desc =  heap_openr(RelationRelationName);
+    if (!IsInitProcessingMode())
+       RelationSetLockForRead(pg_class_desc);
+    pg_class_scan =
+       heap_beginscan(pg_class_desc, 0, NowTimeQual, 1, &key);
+    pg_class_tuple = heap_getnext(pg_class_scan, 0, &buf);
+    
+    /* ----------------
+     * get set to return tuple
+     * ----------------
+     */
+    if (! HeapTupleIsValid(pg_class_tuple)) {
+       return_tuple = pg_class_tuple;
+    } else {
+       /* ------------------
+        *  a satanic bug used to live here: pg_class_tuple used to be
+        *  returned here without having the corresponding buffer pinned.
+        *  so when the buffer gets replaced, all hell breaks loose.
+        *  this bug is discovered and killed by wei on 9/27/91.
+        * -------------------
+        */
+       return_tuple = (HeapTuple) palloc((Size) pg_class_tuple->t_len);
+       memmove((char *) return_tuple,
+               (char *) pg_class_tuple, 
+               (int) pg_class_tuple->t_len);
+       ReleaseBuffer(buf);
+    }
+    
+    /* all done */
+    heap_endscan(pg_class_scan);
+    if (!IsInitProcessingMode())
+       RelationUnsetLockForRead(pg_class_desc);
+    heap_close(pg_class_desc);
+    
+    return return_tuple;
+}
+
+static HeapTuple
+scan_pg_rel_ind(RelationBuildDescInfo buildinfo)
+{
+    Relation pg_class_desc;
+    HeapTuple return_tuple;
+    
+    pg_class_desc = heap_openr(RelationRelationName);
+    if (!IsInitProcessingMode())
+       RelationSetLockForRead(pg_class_desc);
+    
+    switch (buildinfo.infotype) {
+    case INFO_RELID:
+       return_tuple = ClassOidIndexScan(pg_class_desc, buildinfo.i.info_id);
+       break;
+       
+    case INFO_RELNAME:
+       return_tuple = ClassNameIndexScan(pg_class_desc, 
+                                         buildinfo.i.info_name);
+       break;
+       
+    default:
+       elog(WARN, "ScanPgRelation: bad buildinfo");
+    }
+    
+    /* all done */
+    if (!IsInitProcessingMode())
+       RelationUnsetLockForRead(pg_class_desc);
+    heap_close(pg_class_desc);
+    
+    return return_tuple;
+}
+
+/* ----------------
+ *     AllocateRelationDesc
+ *
+ *     This is used to allocate memory for a new relation descriptor
+ *     and initialize the rd_rel field.
+ * ----------------
+ */
+static Relation
+AllocateRelationDesc(u_int natts, Form_pg_class relp)
+{
+    Relation           relation;
+    Size               len;
+    Form_pg_class      relationTupleForm;
+    
+    /* ----------------
+     *  allocate space for the relation tuple form
+     * ----------------
+     */
+    relationTupleForm = (Form_pg_class)
+       palloc((Size) (sizeof(FormData_pg_class)));
+    
+    memmove((char *) relationTupleForm, (char *) relp, CLASS_TUPLE_SIZE);
+    
+    /* ----------------
+     * allocate space for new relation descriptor
+     */
+    len = sizeof(RelationData) + 10;   /* + 10 is voodoo XXX mao */
+    
+    relation = (Relation) palloc(len);
+
+    /* ----------------
+     * clear new reldesc 
+     * ----------------
+     */
+     memset((char *) relation, 0, len); 
+
+    /* initialize attribute tuple form */
+    relation->rd_att = CreateTemplateTupleDesc(natts);
+
+    /*and initialize relation tuple form */
+    relation->rd_rel = relationTupleForm;
+    
+    return relation;
+}
+
+/* --------------------------------
+ *     RelationBuildTupleDesc
+ *
+ *     Form the relation's tuple descriptor from information in
+ *     the pg_attribute system catalog.
+ * --------------------------------
+ */
+static void
+RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,    
+                      Relation relation,
+                      AttributeTupleForm attp,
+                      u_int natts)
+{
+    /*
+     *  If this is bootstrap time (initdb), then we can't use the system
+     *  catalog indices, because they may not exist yet.  Otherwise, we
+     *  can, and do.
+     */
+    
+    if (IsBootstrapProcessingMode())
+       build_tupdesc_seq(buildinfo, relation, attp, natts);
+    else
+       build_tupdesc_ind(buildinfo, relation, attp, natts);
+}
+
+static void
+build_tupdesc_seq(RelationBuildDescInfo buildinfo,
+                 Relation relation,
+                 AttributeTupleForm attp,
+                 u_int natts)
+{
+    HeapTuple    pg_attribute_tuple;
+    Relation    pg_attribute_desc;
+    HeapScanDesc pg_attribute_scan;
+    ScanKeyData         key;
+    int                 need;
+    
+    /* ----------------
+     * form a scan key
+     * ----------------
+     */
+    ScanKeyEntryInitialize(&key, 0, 
+                           Anum_pg_attribute_attrelid,
+                           ObjectIdEqualRegProcedure,
+                           ObjectIdGetDatum(relation->rd_id));
+    
+    /* ----------------
+     * open pg_attribute and begin a scan
+     * ----------------
+     */
+    pg_attribute_desc = heap_openr(AttributeRelationName);
+    pg_attribute_scan =
+       heap_beginscan(pg_attribute_desc, 0, NowTimeQual, 1, &key);
+    
+    /* ----------------
+     * add attribute data to relation->rd_att
+     * ----------------
+     */
+    need = natts;
+    pg_attribute_tuple = heap_getnext(pg_attribute_scan, 0, (Buffer *) NULL);
+    while (HeapTupleIsValid(pg_attribute_tuple) && need > 0) {
+       attp = (AttributeTupleForm) GETSTRUCT(pg_attribute_tuple);
+       
+       if (attp->attnum > 0) {
+           relation->rd_att->attrs[attp->attnum - 1] = 
+               (AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE);
+           
+           memmove((char *) (relation->rd_att->attrs[attp->attnum - 1]),
+                   (char *) attp,
+                   ATTRIBUTE_TUPLE_SIZE);
+           need--;
+       }
+       pg_attribute_tuple = heap_getnext(pg_attribute_scan,
+                                         0, (Buffer *) NULL);
+    }
+    
+    if (need > 0)
+       elog(WARN, "catalog is missing %d attribute%s for relid %d",
+            need, (need == 1 ? "" : "s"), relation->rd_id);
+    
+    /* ----------------
+     * end the scan and close the attribute relation
+     * ----------------
+     */
+    heap_endscan(pg_attribute_scan);
+    heap_close(pg_attribute_desc);
+}
+
+static void
+build_tupdesc_ind(RelationBuildDescInfo buildinfo,
+                 Relation      relation,
+                 AttributeTupleForm attp,
+                 u_int natts)
+{
+    Relation attrel;
+    HeapTuple atttup;
+    int i;
+     
+    attrel = heap_openr(AttributeRelationName);
+    
+    for (i = 1; i <= relation->rd_rel->relnatts; i++) {
+       
+       atttup = (HeapTuple) AttributeNumIndexScan(attrel, relation->rd_id, i);
+       
+       if (!HeapTupleIsValid(atttup))
+           elog(WARN, "cannot find attribute %d of relation %.16s", i,
+                &(relation->rd_rel->relname.data[0]));
+       attp = (AttributeTupleForm) GETSTRUCT(atttup);
+       
+       relation->rd_att->attrs[i - 1] = 
+           (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
+       
+       memmove((char *) (relation->rd_att->attrs[i - 1]),
+               (char *) attp,
+               ATTRIBUTE_TUPLE_SIZE);
+    }
+    
+    heap_close(attrel);
+}
+
+/* --------------------------------
+ *     RelationBuildRuleLock
+ *
+ *     Form the relation's rewrite rules from information in
+ *     the pg_rewrite system catalog.
+ * --------------------------------
+ */
+static void
+RelationBuildRuleLock(Relation relation)
+{
+    HeapTuple    pg_rewrite_tuple;
+    Relation    pg_rewrite_desc;
+    TupleDesc pg_rewrite_tupdesc;
+    HeapScanDesc pg_rewrite_scan;
+    ScanKeyData         key;
+    RuleLock   *rulelock;
+    int         numlocks;
+    RewriteRule **rules;
+    int         maxlocks;
+    
+    /* ----------------
+     * form an array to hold the rewrite rules (the array is extended if
+     *  necessary)
+     * ----------------
+     */
+    maxlocks = 4;
+    rules = (RewriteRule **)palloc(sizeof(RewriteRule*)*maxlocks);
+    numlocks = 0;
+
+    /* ----------------
+     * form a scan key
+     * ----------------
+     */
+    ScanKeyEntryInitialize(&key, 0, 
+                           Anum_pg_rewrite_ev_class,
+                           ObjectIdEqualRegProcedure,
+                           ObjectIdGetDatum(relation->rd_id));
+    
+    /* ----------------
+     * open pg_attribute and begin a scan
+     * ----------------
+     */
+    pg_rewrite_desc = heap_openr(RewriteRelationName);
+    pg_rewrite_scan =
+       heap_beginscan(pg_rewrite_desc, 0, NowTimeQual, 1, &key);
+    pg_rewrite_tupdesc =
+       RelationGetTupleDescriptor(pg_rewrite_desc);
+    
+    /* ----------------
+     * add attribute data to relation->rd_att
+     * ----------------
+     */
+    while ((pg_rewrite_tuple = heap_getnext(pg_rewrite_scan, 0,
+                                           (Buffer *) NULL)) != NULL) {
+       bool isnull;
+       char *ruleaction = NULL;
+       char *rule_evqual_string;
+       RewriteRule *rule;
+
+       rule = (RewriteRule *)palloc(sizeof(RewriteRule));
+
+       rule->ruleId = pg_rewrite_tuple->t_oid;
+
+       /* XXX too lazy to fix the type cast problem
+        *      (see rewriteDefine.c:121)
+        */
+       rule->event =
+           (CmdType)((char)heap_getattr(pg_rewrite_tuple, InvalidBuffer,
+                                 Anum_pg_rewrite_ev_type, pg_rewrite_tupdesc,
+                                 &isnull) - 48);
+       rule->attrno = 
+           (AttrNumber)heap_getattr(pg_rewrite_tuple, InvalidBuffer,
+                                 Anum_pg_rewrite_ev_attr, pg_rewrite_tupdesc,
+                                 &isnull);
+       rule->isInstead = 
+           (bool)heap_getattr(pg_rewrite_tuple, InvalidBuffer,
+                              Anum_pg_rewrite_is_instead, pg_rewrite_tupdesc,
+                              &isnull);
+
+       ruleaction =
+           heap_getattr(pg_rewrite_tuple, InvalidBuffer,
+                        Anum_pg_rewrite_action, pg_rewrite_tupdesc,
+                        &isnull);
+       rule_evqual_string =
+           heap_getattr(pg_rewrite_tuple, InvalidBuffer,
+                        Anum_pg_rewrite_ev_qual, pg_rewrite_tupdesc,
+                        &isnull);
+
+       ruleaction = textout((struct varlena *)ruleaction);
+       rule_evqual_string = textout((struct varlena *)rule_evqual_string);
+
+       rule->actions = (List*)stringToNode(ruleaction);
+       rule->qual = (Node*)stringToNode(rule_evqual_string);
+
+       rules[numlocks++] = rule;
+       if (numlocks==maxlocks) {
+           maxlocks *= 2;
+           rules =
+               (RewriteRule **)repalloc(rules, sizeof(RewriteRule*)*maxlocks);
+       }
+    }
+
+    /* ----------------
+     * end the scan and close the attribute relation
+     * ----------------
+     */
+    heap_endscan(pg_rewrite_scan);
+    heap_close(pg_rewrite_desc);
+
+    /* ----------------
+     * form a RuleLock and insert into relation
+     * ----------------
+     */
+    rulelock = (RuleLock *)palloc(sizeof(RuleLock));
+    rulelock->numLocks = numlocks;
+    rulelock->rules = rules;
+
+    relation->rd_rules = rulelock;
+    return;
+}
+
+
+/* --------------------------------
+ *     RelationBuildDesc
+ *     
+ *     To build a relation descriptor, we have to allocate space,
+ *     open the underlying unix file and initialize the following
+ *     fields:
+ *
+ *  File                  rd_fd;        open file descriptor
+ *  int                    rd_nblocks;   number of blocks in rel 
+ *                                      it will be set in ambeginscan()
+ *  uint16                rd_refcnt;    reference count
+ *  Form_pg_am            rd_am;        AM tuple
+ *  Form_pg_class         rd_rel;       RELATION tuple
+ *  Oid                           rd_id;        relations's object id 
+ *  Pointer               lockInfo;     ptr. to misc. info.
+ *  TupleDesc              rd_att;      tuple desciptor
+ *
+ *     Note: rd_ismem (rel is in-memory only) is currently unused
+ *      by any part of the system.  someday this will indicate that
+ *     the relation lives only in the main-memory buffer pool
+ *     -cim 2/4/91
+ * --------------------------------
+ */
+static Relation
+RelationBuildDesc(RelationBuildDescInfo buildinfo)
+{
+    File               fd;
+    Relation           relation;
+    u_int              natts;
+    Oid                        relid;
+    Oid                        relam;
+    Form_pg_class      relp;
+    AttributeTupleForm attp = NULL;
+    
+    MemoryContext      oldcxt;
+    
+    HeapTuple          pg_class_tuple;
+    
+    oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+    
+    /* ----------------
+     * find the tuple in pg_class corresponding to the given relation id
+     * ----------------
+     */
+    pg_class_tuple = ScanPgRelation(buildinfo);
+    
+    /* ----------------
+     * if no such tuple exists, return NULL
+     * ----------------
+     */
+    if (! HeapTupleIsValid(pg_class_tuple)) {
+       
+       MemoryContextSwitchTo(oldcxt); 
+       
+       return NULL;
+    }
+    
+    /* ----------------
+     * get information from the pg_class_tuple
+     * ----------------
+     */
+    relid = pg_class_tuple->t_oid;
+    relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
+    natts = relp->relnatts;
+    
+    /* ----------------
+     * allocate storage for the relation descriptor,
+     *  initialize relation->rd_rel and get the access method id.
+     * ----------------
+     */
+    relation = AllocateRelationDesc(natts, relp);
+    relam = relation->rd_rel->relam;
+    
+    /* ----------------
+     * initialize the relation's relation id (relation->rd_id)
+     * ----------------
+     */
+    relation->rd_id = relid;
+    
+    /* ----------------
+     * initialize relation->rd_refcnt
+     * ----------------
+     */
+    RelationSetReferenceCount(relation, 1);
+    
+    /* ----------------
+     *   normal relations are not nailed into the cache
+     * ----------------
+     */
+    relation->rd_isnailed = false;
+    
+    /* ----------------
+     * initialize the access method information (relation->rd_am)
+     * ----------------
+     */
+    if (OidIsValid(relam)) {
+       relation->rd_am = (Form_pg_am)
+           AccessMethodObjectIdGetAccessMethodTupleForm(relam);
+    }
+    
+    /* ----------------
+     * initialize the tuple descriptor (relation->rd_att).
+     *  remember, rd_att is an array of attribute pointers that lives
+     *  off the end of the relation descriptor structure so space was
+     *  already allocated for it by AllocateRelationDesc.
+     * ----------------
+     */
+    RelationBuildTupleDesc(buildinfo, relation, attp, natts);
+
+    /* ----------------
+     *  initialize rules that affect this relation
+     * ----------------
+     */
+    if (relp->relhasrules) {
+       RelationBuildRuleLock(relation);
+    } else {
+       relation->rd_rules = NULL;
+    }
+    
+    /* ----------------
+     * initialize index strategy and support information for this relation
+     * ----------------
+     */
+    if (OidIsValid(relam)) {
+       IndexedAccessMethodInitialize(relation);
+    }
+    
+    /* ----------------
+     * initialize the relation lock manager information
+     * ----------------
+     */
+    RelationInitLockInfo(relation); /* see lmgr.c */
+    
+    /* ----------------
+     * open the relation and assign the file descriptor returned
+     *  by the storage manager code to rd_fd.
+     * ----------------
+     */
+    fd = smgropen(relp->relsmgr, relation);
+    
+    Assert (fd >= -1);
+    if (fd == -1)
+       elog(NOTICE, "RelationIdBuildRelation: smgropen(%s): %m",
+            &relp->relname);
+    
+    relation->rd_fd = fd;
+    
+    /* ----------------
+     * insert newly created relation into proper relcaches,
+     *  restore memory context and return the new reldesc.
+     * ----------------
+     */
+    
+    RelationCacheInsert(relation);
+    
+    /* -------------------
+     *  free the memory allocated for pg_class_tuple
+     *  and for lock data pointed to by pg_class_tuple
+     * -------------------
+     */
+    pfree(pg_class_tuple);
+    
+    MemoryContextSwitchTo(oldcxt);
+    
+    return relation;
+}
+
+static void
+IndexedAccessMethodInitialize(Relation relation)
+{
+    IndexStrategy      strategy;
+    RegProcedure       *support;
+    int                        natts;
+    Size               stratSize;
+    Size               supportSize;
+    uint16             relamstrategies;
+    uint16             relamsupport;
+    
+    natts = relation->rd_rel->relnatts;
+    relamstrategies = relation->rd_am->amstrategies;
+    stratSize = AttributeNumberGetIndexStrategySize(natts, relamstrategies);
+    strategy = (IndexStrategy) palloc(stratSize);
+    relamsupport = relation->rd_am->amsupport;
+    
+    if (relamsupport > 0) {
+       supportSize = natts * (relamsupport * sizeof (RegProcedure));
+       support = (RegProcedure *) palloc(supportSize);
+    } else {
+       support = (RegProcedure *) NULL;
+    }
+    
+    IndexSupportInitialize(strategy, support,
+                          relation->rd_att->attrs[0]->attrelid,
+                          relation->rd_rel->relam,
+                          relamstrategies, relamsupport, natts);
+    
+    RelationSetIndexSupport(relation, strategy, support);
+}
+
+/* --------------------------------
+ *     formrdesc
+ *
+ *     This is a special version of RelationBuildDesc()
+ *     used by RelationInitialize() in initializing the
+ *     relcache.  The system relation descriptors built
+ *     here are all nailed in the descriptor caches, for
+ *     bootstrapping.
+ * --------------------------------
+ */
+static void
+formrdesc(char *relationName,
+         u_int natts,
+         FormData_pg_attribute att[])
+{
+    Relation   relation;
+    Size       len;
+    int                i;
+    
+    /* ----------------
+     * allocate new relation desc
+     * ----------------
+     */
+    len = sizeof (RelationData);
+    relation = (Relation) palloc(len);
+    memset((char *)relation, 0,len); 
+
+    /* ----------------
+     * don't open the unix file yet..
+     * ----------------
+     */
+    relation->rd_fd = -1;
+    
+    /* ----------------
+     * initialize reference count
+     * ----------------
+     */
+    RelationSetReferenceCount(relation, 1);
+    
+    /* ----------------
+     * initialize relation tuple form
+     * ----------------
+     */
+    relation->rd_rel = (Form_pg_class)
+       palloc((Size) (sizeof(*relation->rd_rel)));
+    memset(relation->rd_rel, 0, sizeof(FormData_pg_class)); 
+    namestrcpy(&relation->rd_rel->relname, relationName);
+    
+    /* ----------------
+       initialize attribute tuple form
+    */
+    relation->rd_att = CreateTemplateTupleDesc(natts);
+    
+    /*
+     *  For debugging purposes, it's important to distinguish between
+     *  shared and non-shared relations, even at bootstrap time.  There's
+     *  code in the buffer manager that traces allocations that has to
+     *  know about this.
+     */
+    
+    if (IsSystemRelationName(relationName)) {
+       relation->rd_rel->relowner = 6;                 /* XXX use sym const */
+       relation->rd_rel->relisshared =
+           IsSharedSystemRelationName(relationName);
+    } else {
+       relation->rd_rel->relowner = InvalidOid;        /* XXX incorrect*/
+       relation->rd_rel->relisshared = false;
+    }
+    
+    relation->rd_rel->relpages = 1;                    /* XXX */
+    relation->rd_rel->reltuples = 1;                   /* XXX */
+    relation->rd_rel->relkind = RELKIND_RELATION;
+    relation->rd_rel->relarch = 'n';
+    relation->rd_rel->relnatts = (uint16) natts;
+    relation->rd_isnailed = true;
+    
+    /* ----------------
+     * initialize tuple desc info
+     * ----------------
+     */
+    for (i = 0; i < natts; i++) {
+       relation->rd_att->attrs[i] = 
+           (AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE);
+       
+       memset((char *)relation->rd_att->attrs[i], 0,
+              ATTRIBUTE_TUPLE_SIZE);
+       memmove((char *)relation->rd_att->attrs[i],
+               (char *)&att[i],
+               ATTRIBUTE_TUPLE_SIZE);
+    }
+    
+    /* ----------------
+     * initialize relation id
+     * ----------------
+     */
+    relation->rd_id = relation->rd_att->attrs[0]->attrelid;
+    
+    /* ----------------
+     * add new reldesc to relcache
+     * ----------------
+     */
+    RelationCacheInsert(relation);
+    /*
+     * Determining this requires a scan on pg_class, but to do the
+     * scan the rdesc for pg_class must already exist.  Therefore
+     * we must do the check (and possible set) after cache insertion.
+     */
+    relation->rd_rel->relhasindex =
+       CatalogHasIndex(relationName, relation->rd_id);
+}
+
+
+/* ----------------------------------------------------------------
+ *              Relation Descriptor Lookup Interface
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     RelationIdCacheGetRelation
+ *
+ *     only try to get the reldesc by looking up the cache
+ *     do not go to the disk.  this is used by BlockPrepareFile()
+ *     and RelationIdGetRelation below.
+ * --------------------------------
+ */
+Relation
+RelationIdCacheGetRelation(Oid relationId)
+{
+    Relation   rd;
+    
+    RelationIdCacheLookup(relationId, rd);
+    
+    if (RelationIsValid(rd)) {
+       if (rd->rd_fd == -1) {
+           rd->rd_fd = smgropen(rd->rd_rel->relsmgr, rd);
+           Assert(rd->rd_fd != -1);
+       }
+       
+       RelationIncrementReferenceCount(rd);
+       RelationSetLockForDescriptorOpen(rd);
+       
+    }
+    
+    return(rd);
+}
+
+/* --------------------------------
+ *     RelationNameCacheGetRelation
+ * --------------------------------
+ */
+Relation
+RelationNameCacheGetRelation(char *relationName)
+{
+    Relation   rd;
+    NameData   name;
+    
+    /* make sure that the name key used for hash lookup is properly
+       null-padded */
+    memset(&name,0, NAMEDATALEN); 
+    namestrcpy(&name, relationName);
+    RelationNameCacheLookup(name.data, rd);
+    
+    if (RelationIsValid(rd)) {
+       if (rd->rd_fd == -1) {
+           rd->rd_fd = smgropen(rd->rd_rel->relsmgr, rd);
+           Assert(rd->rd_fd != -1);
+       }
+       
+       RelationIncrementReferenceCount(rd);
+       RelationSetLockForDescriptorOpen(rd);
+       
+    }
+    
+    return(rd);
+}
+
+/* --------------------------------
+ *     RelationIdGetRelation
+ *
+ *     return a relation descriptor based on its id.
+ *     return a cached value if possible
+ * --------------------------------
+ */
+Relation
+RelationIdGetRelation(Oid relationId)
+{
+    Relation             rd;
+    RelationBuildDescInfo buildinfo;
+    
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_RelationIdGetRelation);
+    IncrHeapAccessStat(global_RelationIdGetRelation);
+    
+    /* ----------------
+     * first try and get a reldesc from the cache
+     * ----------------
+     */
+    rd = RelationIdCacheGetRelation(relationId);
+    if (RelationIsValid(rd))
+       return rd;
+    
+    /* ----------------
+     * no reldesc in the cache, so have RelationBuildDesc()
+     *  build one and add it.
+     * ----------------
+     */
+    buildinfo.infotype =  INFO_RELID;
+    buildinfo.i.info_id = relationId;
+    
+    rd = RelationBuildDesc(buildinfo);
+    return
+       rd;
+}
+
+/* --------------------------------
+ *     RelationNameGetRelation
+ *
+ *     return a relation descriptor based on its name.
+ *     return a cached value if possible
+ * --------------------------------
+ */
+Relation
+RelationNameGetRelation(char *relationName)
+{
+    Relation             rd;
+    RelationBuildDescInfo buildinfo;
+    
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_RelationNameGetRelation);
+    IncrHeapAccessStat(global_RelationNameGetRelation);
+    
+    /* ----------------
+     * first try and get a reldesc from the cache
+     * ----------------
+     */
+    rd = RelationNameCacheGetRelation(relationName);
+    if (RelationIsValid(rd))
+       return rd;
+    
+    /* ----------------
+     * no reldesc in the cache, so have RelationBuildDesc()
+     *  build one and add it.
+     * ----------------
+     */
+    buildinfo.infotype =    INFO_RELNAME;
+    buildinfo.i.info_name = relationName;
+    
+    rd = RelationBuildDesc(buildinfo);
+    return rd;
+}
+
+/* ----------------
+ *     old "getreldesc" interface.
+ * ----------------
+ */
+Relation
+getreldesc(char *relationName)
+{
+    /* ----------------
+     * increment access statistics
+     * ----------------
+     */
+    IncrHeapAccessStat(local_getreldesc);
+    IncrHeapAccessStat(global_getreldesc);
+    
+    return RelationNameGetRelation(relationName);
+}
+
+/* ----------------------------------------------------------------
+ *             cache invalidation support routines
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *     RelationClose - close an open relation
+ * --------------------------------
+ */
+void
+RelationClose(Relation relation)
+{
+    /* Note: no locking manipulations needed */
+    RelationDecrementReferenceCount(relation);
+}
+
+/* --------------------------------
+ * RelationFlushRelation
+ *
+ *   Actually blows away a relation... RelationFree doesn't do 
+ *   anything anymore.
+ * --------------------------------
+ */
+void
+RelationFlushRelation(Relation *relationPtr,
+                     bool onlyFlushReferenceCountZero)
+{
+    int                        i;
+    AttributeTupleForm *p;
+    MemoryContext      oldcxt;
+    Relation           relation = *relationPtr;
+    
+    if (relation->rd_isnailed) {
+       /* this is a nailed special relation for bootstraping */
+       return;
+    }
+    
+    if (!onlyFlushReferenceCountZero || 
+       RelationHasReferenceCountZero(relation)) {
+       
+       oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+       
+       RelationCacheDelete(relation);
+       
+       FileInvalidate(RelationGetSystemPort(relation));
+       
+       i = relation->rd_rel->relnatts - 1;
+       p = &relation->rd_att->attrs[i];
+       while ((i -= 1) >= 0) {
+           pfree(*p--);
+       }
+
+#if 0
+       if (relation->rd_rules) {
+           int j;
+           for(j=0; j < relation->rd_rules->numLocks; j++) {
+               pfree(relation->rd_rules->rules[j]);
+           }
+           pfree(relation->rd_rules->rules);
+           pfree(relation->rd_rules);
+       }
+#endif
+       
+       pfree(RelationGetLockInfo(relation));
+       pfree(RelationGetRelationTupleForm(relation));
+       pfree(relation);
+       
+       MemoryContextSwitchTo(oldcxt);
+    }
+}
+
+/* --------------------------------
+ *     RelationIdInvalidateRelationCacheByRelationId
+ * --------------------------------
+ */
+void
+RelationIdInvalidateRelationCacheByRelationId(Oid relationId)
+{
+    Relation   relation;
+    
+    RelationIdCacheLookup(relationId, relation);
+
+    /*
+     * "local" relations are invalidated by RelationPurgeLocalRelation.
+     * (This is to make LocalBufferSync's life easier: want the descriptor
+     * to hang around for a while. In fact, won't we want this for
+     * BufferSync also? But I'll leave it for now since I don't want to
+     * break anything.)        - ay 3/95
+     */
+    if (PointerIsValid(relation) && !relation->rd_islocal) {
+       /*
+        * The boolean onlyFlushReferenceCountZero in RelationFlushReln()
+        * should be set to true when we are incrementing the command
+        * counter and to false when we are starting a new xaction.  This
+        * can be determined by checking the current xaction status.
+        */
+       RelationFlushRelation(&relation, CurrentXactInProgress());
+    }
+}
+
+/* --------------------------------
+ *     RelationIdInvalidateRelationCacheByAccessMethodId
+ *
+ *     RelationFlushIndexes is needed for use with HashTableWalk..
+ * --------------------------------
+ */
+static void
+RelationFlushIndexes(Relation *r, 
+                    Oid accessMethodId)
+{
+    Relation relation = *r;
+    
+    if (!RelationIsValid(relation)) {
+       elog(NOTICE, "inval call to RFI");
+       return;
+    }
+    
+    if (relation->rd_rel->relkind == RELKIND_INDEX &&  /* XXX style */
+       (!OidIsValid(accessMethodId) ||
+        relation->rd_rel->relam == accessMethodId))
+       {
+           RelationFlushRelation(&relation, false);
+       }
+}
+
+void
+RelationIdInvalidateRelationCacheByAccessMethodId(Oid accessMethodId)
+{
+# if 0
+    /*
+     *  25 aug 1992:  mao commented out the ht walk below.  it should be
+     *  doing the right thing, in theory, but flushing reldescs for index
+     *  relations apparently doesn't work.  we want to cut 4.0.1, and i
+     *  don't want to introduce new bugs.  this code never executed before,
+     *  so i'm turning it off for now.  after the release is cut, i'll
+     *  fix this up.
+     */
+    
+    HashTableWalk(RelationNameCache, (HashtFunc) RelationFlushIndexes,
+                 accessMethodId);
+# else
+    return;
+# endif
+}
+
+/*
+ * RelationCacheInvalidate
+ *
+ *   Will blow away either all the cached relation descriptors or
+ *   those that have a zero reference count.
+ *
+ */
+void
+RelationCacheInvalidate(bool onlyFlushReferenceCountZero)
+{
+    HashTableWalk(RelationNameCache, (HashtFunc) RelationFlushRelation,
+                 onlyFlushReferenceCountZero);
+    
+    /*
+     * nailed-in reldescs will still be in the cache...
+     * 7 hardwired heaps + 3 hardwired indices == 10 total.
+     */
+    if (!onlyFlushReferenceCountZero) {
+       Assert(RelationNameCache->hctl->nkeys == 10);
+       Assert(RelationIdCache->hctl->nkeys == 10);
+    }
+}
+
+
+/*
+ * newlyCreatedRelns -
+ *    relations created during this transaction. We need to keep track of
+ *    these
+ */
+static List *newlyCreatedRelns = NULL;
+                                          
+
+/* --------------------------------
+ *     RelationRegisterRelation -
+ *        register the Relation descriptor of a newly created relation
+ *        with the relation descriptor Cache.
+ * --------------------------------
+ */
+void
+RelationRegisterRelation(Relation relation)
+{
+    MemoryContext      oldcxt;
+    
+    oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+    
+    if (oldcxt != (MemoryContext)CacheCxt) 
+       elog(NOIND,"RelationRegisterRelation: WARNING: Context != CacheCxt");
+    
+    RelationCacheInsert(relation);
+    
+    RelationInitLockInfo(relation);
+
+    /*
+     * we've just created the relation. It is invisible to anyone else
+     * before the transaction is committed. Setting rd_islocal allows us
+     * to use the local buffer manager for select/insert/etc before the end
+     * of transaction. (We also need to keep track of relations
+     * created during a transaction and does the necessary clean up at
+     * the end of the transaction.)            - ay 3/95
+     */
+    relation->rd_islocal = TRUE;
+    newlyCreatedRelns = lcons(relation, newlyCreatedRelns);
+    
+    MemoryContextSwitchTo(oldcxt);
+}
+
+/*
+ * RelationPurgeLocalRelation -
+ *    find all the Relation descriptors marked rd_islocal and reset them.
+ *    This should be called at the end of a transaction (commit/abort) when
+ *    the "local" relations will become visible to others and the multi-user
+ *    buffer pool should be used.
+ */
+void
+RelationPurgeLocalRelation(bool xactCommitted)
+{
+    MemoryContext      oldcxt;
+
+    if (newlyCreatedRelns==NULL)
+       return;         
+    
+    oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+    
+    while (newlyCreatedRelns) {
+       List *l = newlyCreatedRelns;
+       Relation reln = lfirst(l);
+
+       Assert(reln!=NULL && reln->rd_islocal);
+
+       if (!xactCommitted) {
+           /*
+            * remove the file if we abort. This is so that files for 
+            * tables created inside a transaction block get removed.
+            */
+           smgrunlink(reln->rd_rel->relsmgr, reln);
+       }
+       
+       reln->rd_islocal = FALSE;
+
+       if (!IsBootstrapProcessingMode())
+           RelationFlushRelation(&reln, FALSE);
+       
+       newlyCreatedRelns = lnext(newlyCreatedRelns);
+       pfree(l);
+    }
+
+    MemoryContextSwitchTo(oldcxt);
+}
+
+/* --------------------------------
+ *     RelationInitialize
+ *
+ *     This initializes the relation descriptor cache.
+ * --------------------------------
+ */
+
+#define INITRELCACHESIZE       400
+
+void
+RelationInitialize()
+{
+    MemoryContext              oldcxt;
+    HASHCTL                    ctl;
+    
+    /* ----------------
+     * switch to cache memory context
+     * ----------------
+     */
+    if (!CacheCxt)
+       CacheCxt = CreateGlobalMemory("Cache");
+    
+    oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt);
+    
+    /* ----------------
+     * create global caches
+     * ----------------
+     */
+    memset(&ctl,0, (int) sizeof(ctl)); 
+    ctl.keysize = sizeof(NameData);
+    ctl.datasize = sizeof(Relation); 
+    RelationNameCache = hash_create(INITRELCACHESIZE, &ctl, HASH_ELEM);
+    
+    ctl.keysize = sizeof(Oid);
+    ctl.hash = tag_hash;
+    RelationIdCache = hash_create(INITRELCACHESIZE, &ctl, 
+                                 HASH_ELEM | HASH_FUNCTION);
+    
+    /* ----------------
+     * initialize the cache with pre-made relation descriptors
+     *  for some of the more important system relations.  These
+     *  relations should always be in the cache.
+     * ----------------
+     */
+    formrdesc(RelationRelationName, Natts_pg_class, Desc_pg_class);
+    formrdesc(AttributeRelationName, Natts_pg_attribute, Desc_pg_attribute);
+    formrdesc(ProcedureRelationName, Natts_pg_proc, Desc_pg_proc);
+    formrdesc(TypeRelationName, Natts_pg_type, Desc_pg_type);
+    formrdesc(VariableRelationName, Natts_pg_variable, Desc_pg_variable);
+    formrdesc(LogRelationName, Natts_pg_log, Desc_pg_log);
+    formrdesc(TimeRelationName, Natts_pg_time, Desc_pg_time);
+
+    /*
+     *  If this isn't initdb time, then we want to initialize some index
+     *  relation descriptors, as well.  The descriptors are for pg_attnumind
+     *  (to make building relation descriptors fast) and possibly others,
+     *  as they're added.
+     */
+    
+    if (!IsBootstrapProcessingMode())
+       init_irels();
+    
+    MemoryContextSwitchTo(oldcxt);
+}
+
+/*
+ *  init_irels(), write_irels() -- handle special-case initialization of
+ *                                index relation descriptors.
+ *
+ *     In late 1992, we started regularly having databases with more than
+ *     a thousand classes in them.  With this number of classes, it became
+ *     critical to do indexed lookups on the system catalogs.
+ *
+ *     Bootstrapping these lookups is very hard.  We want to be able to
+ *     use an index on pg_attribute, for example, but in order to do so,
+ *     we must have read pg_attribute for the attributes in the index,
+ *     which implies that we need to use the index.
+ *
+ *     In order to get around the problem, we do the following:
+ *
+ *        +  When the database system is initialized (at initdb time), we
+ *           don't use indices on pg_attribute.  We do sequential scans.
+ *
+ *        +  When the backend is started up in normal mode, we load an image
+ *           of the appropriate relation descriptors, in internal format,
+ *           from an initialization file in the data/base/... directory.
+ *
+ *        +  If the initialization file isn't there, then we create the
+ *           relation descriptor using sequential scans and write it to
+ *           the initialization file for use by subsequent backends.
+ *
+ *     This is complicated and interferes with system changes, but
+ *     performance is so bad that we're willing to pay the tax.
+ */
+
+/* pg_attnumind, pg_classnameind, pg_classoidind */
+#define Num_indices_bootstrap  3
+
+void
+init_irels()
+{
+    Size len;
+    int nread;
+    File fd;
+    Relation irel[Num_indices_bootstrap];
+    Relation ird;
+    Form_pg_am am;
+    Form_pg_class relform;
+    IndexStrategy strat;
+    RegProcedure *support;
+    int i;
+    int relno;
+    
+    if ((fd = FileNameOpenFile(INIT_FILENAME, O_RDONLY, 0600)) < 0) {
+       write_irels();
+       return;
+    }
+    
+    (void) FileSeek(fd, 0L, SEEK_SET);
+    
+    for (relno = 0; relno < Num_indices_bootstrap; relno++) {
+       /* first read the relation descriptor length*/
+       if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) {
+           write_irels();
+           return;
+       }
+       
+       ird = irel[relno] = (Relation) palloc(len);
+       memset(ird, 0, len); 
+
+       /* then, read the Relation structure */
+       if ((nread = FileRead(fd, (char*)ird, len)) != len) {
+           write_irels();
+           return;
+       }
+       
+       /* the file descriptor is not yet opened */
+       ird->rd_fd = -1;
+       
+       /* lock info is not initialized */
+       ird->lockInfo = (char *) NULL;
+       
+       /* next, read the access method tuple form */
+       if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) {
+           write_irels();
+           return;
+       }
+       
+       am = (Form_pg_am) palloc(len);
+       if ((nread = FileRead(fd, (char*)am, len)) != len) {
+           write_irels();
+           return;
+       }
+       
+       ird->rd_am = am;
+       
+       /* next read the relation tuple form */
+       if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) {
+           write_irels();
+           return;
+       }
+       
+       relform = (Form_pg_class) palloc(len);
+       if ((nread = FileRead(fd, (char*)relform, len)) != len) {
+           write_irels();
+           return;
+       }
+       
+       ird->rd_rel = relform;
+       
+       /* initialize attribute tuple forms */
+       ird->rd_att = CreateTemplateTupleDesc(relform->relnatts);
+
+       /* next read all the attribute tuple form data entries */
+       len = ATTRIBUTE_TUPLE_SIZE;
+       for (i = 0; i < relform->relnatts; i++) {
+           if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) {
+               write_irels();
+               return;
+           }
+           
+           ird->rd_att->attrs[i] = (AttributeTupleForm) palloc(len);
+           
+           if ((nread = FileRead(fd, (char*)ird->rd_att->attrs[i], len)) != len) {
+               write_irels();
+               return;
+           }
+       }
+       
+       /* next, read the index strategy map */
+       if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) {
+           write_irels();
+           return;
+       }
+       
+       strat = (IndexStrategy) palloc(len);
+       if ((nread = FileRead(fd, (char*)strat, len)) != len) {
+           write_irels();
+           return;
+       }
+       
+       /* oh, for god's sake... */
+#define SMD(i) strat[0].strategyMapData[i].entry[0]
+       
+       /* have to reinit the function pointers in the strategy maps */
+       for (i = 0; i < am->amstrategies; i++)
+           fmgr_info(SMD(i).sk_procedure,
+                     &(SMD(i).sk_func), &(SMD(i).sk_nargs));
+       
+       
+       /* use a real field called rd_istrat instead of the 
+          bogosity of hanging invisible fields off the end of a structure
+          - jolly */
+       ird->rd_istrat = strat;
+
+       /* finally, read the vector of support procedures */
+       if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) {
+           write_irels();
+           return;
+       }
+       
+       support = (RegProcedure *) palloc(len);
+       if ((nread = FileRead(fd, (char*)support, len)) != len) {
+           write_irels();
+           return;
+       }
+       
+       /*
+       p += sizeof(IndexStrategy);
+       *((RegProcedure **) p) = support;
+       */
+
+       ird->rd_support = support;
+       
+       RelationCacheInsert(ird);
+    }
+}
+
+void
+write_irels()
+{
+    int len;
+    int nwritten;
+    File fd;
+    Relation irel[Num_indices_bootstrap];
+    Relation ird;
+    Form_pg_am am;
+    Form_pg_class relform;
+    IndexStrategy strat;
+    RegProcedure *support;
+    ProcessingMode oldmode;
+    int i;
+    int relno;
+    RelationBuildDescInfo bi;
+    
+    fd = FileNameOpenFile(INIT_FILENAME, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+    if (fd < 0)
+       elog(FATAL, "cannot create init file %s", INIT_FILENAME);
+    
+    (void) FileSeek(fd, 0L, SEEK_SET);
+    
+    /*
+     *  Build a relation descriptor for pg_attnumind without resort to the
+     *  descriptor cache.  In order to do this, we set ProcessingMode
+     *  to Bootstrap.  The effect of this is to disable indexed relation
+     *  searches -- a necessary step, since we're trying to instantiate
+     *  the index relation descriptors here.
+     */
+    
+    oldmode = GetProcessingMode();
+    SetProcessingMode(BootstrapProcessing);
+    
+    bi.infotype = INFO_RELNAME;
+    bi.i.info_name = AttributeNumIndex;
+    irel[0] = RelationBuildDesc(bi);
+    irel[0]->rd_isnailed = true;
+    
+    bi.i.info_name = ClassNameIndex;
+    irel[1] = RelationBuildDesc(bi);
+    irel[1]->rd_isnailed = true;
+    
+    bi.i.info_name = ClassOidIndex;
+    irel[2] = RelationBuildDesc(bi);
+    irel[2]->rd_isnailed = true;
+    
+    SetProcessingMode(oldmode);
+    
+    /* nail the descriptor in the cache */
+    for (relno = 0; relno < Num_indices_bootstrap; relno++) {
+       ird = irel[relno];
+       
+       /* save the volatile fields in the relation descriptor */
+       am = ird->rd_am;
+       ird->rd_am = (Form_pg_am) NULL;
+       relform = ird->rd_rel;
+       ird->rd_rel = (Form_pg_class) NULL;
+       strat = ird->rd_istrat;
+       support = ird->rd_support;
+       
+       /* first write the relation descriptor , excluding strategy and support */
+       len = sizeof(RelationData);
+       
+       /* first, write the relation descriptor length */
+       if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int)))
+           != sizeof(int))
+           elog(FATAL, "cannot write init file -- descriptor length");
+       
+       /* next, write out the Relation structure */
+       if ((nwritten = FileWrite(fd, (char*) ird, len)) != len)
+           elog(FATAL, "cannot write init file -- reldesc");
+       
+       /* next, write the access method tuple form */
+       len = sizeof(FormData_pg_am);
+       if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int)))
+           != sizeof(int))
+           elog(FATAL, "cannot write init file -- am tuple form length");
+       
+       if ((nwritten = FileWrite(fd, (char*) am, len)) != len)
+           elog(FATAL, "cannot write init file -- am tuple form");
+       
+       /* next write the relation tuple form */
+       len = sizeof(FormData_pg_class);
+       if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int)))
+           != sizeof(int))
+           elog(FATAL, "cannot write init file -- relation tuple form length");
+       
+       if ((nwritten = FileWrite(fd, (char*) relform, len)) != len)
+           elog(FATAL, "cannot write init file -- relation tuple form");
+       
+       /* next, do all the attribute tuple form data entries */
+       len = ATTRIBUTE_TUPLE_SIZE;
+       for (i = 0; i < relform->relnatts; i++) {
+           if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int)))
+               != sizeof(int))
+               elog(FATAL, "cannot write init file -- length of attdesc %d", i);
+           if ((nwritten = FileWrite(fd, (char*) ird->rd_att->attrs[i], len))
+               != len)
+               elog(FATAL, "cannot write init file -- attdesc %d", i);
+       }
+       
+       /* next, write the index strategy map */
+       len = AttributeNumberGetIndexStrategySize(relform->relnatts,
+                                                 am->amstrategies);
+       if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int)))
+           != sizeof(int))
+           elog(FATAL, "cannot write init file -- strategy map length");
+       
+       if ((nwritten = FileWrite(fd, (char*) strat, len)) != len)
+           elog(FATAL, "cannot write init file -- strategy map");
+       
+       /* finally, write the vector of support procedures */
+       len = relform->relnatts * (am->amsupport * sizeof(RegProcedure));
+       if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int)))
+           != sizeof(int))
+           elog(FATAL, "cannot write init file -- support vector length");
+       
+       if ((nwritten = FileWrite(fd, (char*) support, len)) != len)
+           elog(FATAL, "cannot write init file -- support vector");
+       
+       /* restore volatile fields */
+       ird->rd_am = am;
+       ird->rd_rel = relform;
+    }
+    
+    (void) FileClose(fd);
+}
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
new file mode 100644 (file)
index 0000000..321437c
--- /dev/null
@@ -0,0 +1,630 @@
+/*-------------------------------------------------------------------------
+ *
+ * syscache.c--
+ *    System cache management routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    These routines allow the parser/planner/executor to perform
+ *    rapid lookups on the contents of the system catalogs.
+ *
+ *    see catalog/syscache.h for a list of the cache id's
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "catalog/catname.h"
+#include "utils/catcache.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "nodes/pg_list.h"
+/* ----------------
+ *     hardwired attribute information comes from system catalog files.
+ * ----------------
+ */
+#include "catalog/pg_am.h"
+#include "catalog/pg_amop.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_group.h"
+#include "catalog/pg_index.h"
+#include "catalog/pg_inherits.h"
+#include "catalog/pg_language.h"
+#include "catalog/pg_opclass.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_rewrite.h"
+#include "catalog/pg_aggregate.h"
+#include "catalog/pg_user.h"
+#include "storage/large_object.h"
+#include "catalog/pg_listener.h"
+extern bool    AMI_OVERRIDE;   /* XXX style */
+#include "utils/syscache.h"
+#include "catalog/indexing.h"
+    
+typedef HeapTuple (*ScanFunc)();
+
+/* ----------------
+ *     Warning:  cacheinfo[] below is changed, then be sure and
+ *     update the magic constants in syscache.h!
+ * ----------------
+ */
+static struct cachedesc cacheinfo[] = {
+    { AccessMethodOperatorRelationName,        /* AMOPOPID */
+         3,
+         { Anum_pg_amop_amopclaid,
+               Anum_pg_amop_amopopr,
+               Anum_pg_amop_amopid,
+               0 },
+         sizeof(FormData_pg_amop),
+      NULL,
+      (ScanFunc) NULL  },
+    { AccessMethodOperatorRelationName,        /* AMOPSTRATEGY */
+         3,
+         { Anum_pg_amop_amopid,
+               Anum_pg_amop_amopclaid,
+               Anum_pg_amop_amopstrategy,
+               0 },
+         sizeof(FormData_pg_amop),
+      NULL,
+      (ScanFunc) NULL  },
+    { AttributeRelationName,                   /* ATTNAME */
+         2,
+         { Anum_pg_attribute_attrelid,
+               Anum_pg_attribute_attname,
+               0,
+               0 },
+         ATTRIBUTE_TUPLE_SIZE,
+      AttributeNameIndex,
+      (ScanFunc) AttributeNameIndexScan  },
+    { AttributeRelationName,                   /* ATTNUM */
+         2,
+         { Anum_pg_attribute_attrelid,
+               Anum_pg_attribute_attnum,
+               0,
+               0 },
+         ATTRIBUTE_TUPLE_SIZE,
+      AttributeNumIndex,
+      (ScanFunc) AttributeNumIndexScan  },
+    { IndexRelationName,                       /* INDEXRELID */
+         1,
+         { Anum_pg_index_indexrelid,
+               0,
+               0,
+               0 },
+         offsetof(FormData_pg_index, indpred),
+      NULL,
+      NULL  },
+    { LanguageRelationName,                    /* LANNAME */
+         1,
+         { Anum_pg_language_lanname,
+               0,
+               0,
+               0 },
+         offsetof(FormData_pg_language, lancompiler),
+      NULL,
+      NULL  },
+    { OperatorRelationName,                    /* OPRNAME */
+         4,
+         { Anum_pg_operator_oprname,
+               Anum_pg_operator_oprleft,
+               Anum_pg_operator_oprright,
+               Anum_pg_operator_oprkind },
+         sizeof(FormData_pg_operator),
+      NULL,
+      NULL  },
+    { OperatorRelationName,                    /* OPROID */
+         1,
+         { ObjectIdAttributeNumber,
+               0,
+               0,
+               0 },
+         sizeof(FormData_pg_operator),
+      NULL,
+      (ScanFunc) NULL  },
+    { ProcedureRelationName,                   /* PRONAME */
+         3,
+         { Anum_pg_proc_proname,
+           Anum_pg_proc_pronargs,
+           Anum_pg_proc_proargtypes,
+               0 },
+         offsetof(FormData_pg_proc, prosrc),
+      ProcedureNameIndex,
+      (ScanFunc) ProcedureNameIndexScan  },
+    { ProcedureRelationName,                   /* PROOID */
+         1,
+         { ObjectIdAttributeNumber,
+               0,
+               0,
+               0 },
+         offsetof(FormData_pg_proc, prosrc),
+      ProcedureOidIndex,
+      (ScanFunc) ProcedureOidIndexScan  },
+    { RelationRelationName,                    /* RELNAME */
+         1,
+         { Anum_pg_class_relname,
+               0,
+               0,
+               0 },
+         CLASS_TUPLE_SIZE,
+      ClassNameIndex,
+      (ScanFunc) ClassNameIndexScan  },
+    { RelationRelationName,                    /* RELOID */
+         1,
+         { ObjectIdAttributeNumber,
+               0,
+               0,
+               0 },
+         CLASS_TUPLE_SIZE,
+      ClassOidIndex,
+      (ScanFunc) ClassOidIndexScan  },
+    { TypeRelationName,                        /* TYPNAME */
+         1,
+         { Anum_pg_type_typname,
+               0,
+               0,
+               0 },
+         offsetof(TypeTupleFormData,typalign)+sizeof(char),
+      TypeNameIndex,
+      TypeNameIndexScan  },
+    { TypeRelationName,                        /* TYPOID */
+         1,
+         { ObjectIdAttributeNumber,
+               0,
+               0,
+               0},
+         offsetof(TypeTupleFormData,typalign)+sizeof(char),
+      TypeOidIndex,
+      TypeOidIndexScan  },
+    { AccessMethodRelationName,                /* AMNAME */
+         1,
+         { Anum_pg_am_amname,
+               0,
+               0,
+               0},
+         sizeof(FormData_pg_am),
+      NULL,
+      NULL  },
+    { OperatorClassRelationName,               /* CLANAME */
+         1,
+         { Anum_pg_opclass_opcname,
+               0,
+               0,
+               0},
+         sizeof(FormData_pg_opclass),
+      NULL,
+      NULL  },
+    { IndexRelationName,                       /* INDRELIDKEY */
+         2,
+         { Anum_pg_index_indrelid,
+               Anum_pg_index_indkey,
+               0,
+               0},
+         offsetof(FormData_pg_index, indpred),
+      NULL,
+      (ScanFunc) NULL  },
+    { InheritsRelationName,                    /* INHRELID */
+         2,
+         { Anum_pg_inherits_inhrel,
+               Anum_pg_inherits_inhseqno,
+               0,
+               0},
+         sizeof(FormData_pg_inherits),
+      NULL,
+      (ScanFunc) NULL  },
+    { RewriteRelationName,                     /* RULOID */
+         1,
+         { ObjectIdAttributeNumber,
+               0,
+               0,
+               0 },
+         offsetof(FormData_pg_rewrite, ev_qual),
+      NULL,
+      (ScanFunc) NULL  },
+    { AggregateRelationName,                   /*AGGNAME*/
+         2,
+         { Anum_pg_aggregate_aggname,
+           Anum_pg_aggregate_aggbasetype,
+               0,
+               0 },
+          offsetof(FormData_pg_aggregate, agginitval1),
+       NULL,
+       (ScanFunc) NULL  },    
+    { ListenerRelationName,                  /* LISTENREL */
+       2,
+       {  Anum_pg_listener_relname,
+            Anum_pg_listener_pid,
+            0,
+            0 },
+       sizeof(FormData_pg_listener),
+       NULL,
+       (ScanFunc) NULL  },
+    { UserRelationName,                        /* USENAME */
+             1,
+             { Anum_pg_user_usename,
+                       0,
+                       0,
+                       0 },
+             sizeof(FormData_pg_user),
+             NULL,
+             (ScanFunc) NULL  },
+    { UserRelationName,                        /* USESYSID */
+             1,
+             { Anum_pg_user_usesysid,
+                       0,
+                       0,
+                       0 },
+             sizeof(FormData_pg_user),
+             NULL,
+             (ScanFunc) NULL  },
+    { GroupRelationName,                       /* GRONAME */
+             1,
+             { Anum_pg_group_groname,
+                       0,
+                       0,
+                       0 },
+             offsetof(FormData_pg_group, grolist[0]),
+             NULL,
+             (ScanFunc) NULL  },
+    { GroupRelationName,                       /* GROSYSID */
+             1,
+             { Anum_pg_group_grosysid,
+                       0,
+                       0,
+                       0 },
+             offsetof(FormData_pg_group, grolist[0]),
+             NULL,
+             (ScanFunc) NULL  },
+    { RewriteRelationName,                     /* REWRITENAME */
+             1,
+             { Anum_pg_rewrite_rulename,
+                       0,
+                       0,
+                       0 },
+             offsetof(FormData_pg_rewrite, ev_qual),
+             NULL,
+             (ScanFunc) NULL  },
+    { ProcedureRelationName,                   /* PROSRC */
+          1,
+           { Anum_pg_proc_prosrc,
+                 0,
+                 0,
+                 0 },
+          offsetof(FormData_pg_proc, prosrc),
+      ProcedureSrcIndex,
+      (ScanFunc) ProcedureSrcIndexScan  }
+};
+static struct catcache *SysCache[lengthof(cacheinfo)];
+static int32           SysCacheSize = lengthof(cacheinfo);
+    
+    
+/*
+ * zerocaches--
+ *
+ *    Make sure the SysCache structure is zero'd.
+ */
+void
+zerocaches()
+{
+    memset((char *) SysCache, 0, SysCacheSize * sizeof(struct catcache *));
+}
+
+/*
+ * Note:
+ *     This function was written because the initialized catalog caches
+ *     are used to determine which caches may contain tuples which need
+ *     to be invalidated in other backends.
+ */
+void
+InitCatalogCache()
+{
+    int        cacheId;        /* XXX type */
+    
+    if (!AMI_OVERRIDE) {
+       for (cacheId = 0; cacheId < SysCacheSize; cacheId += 1) {
+           
+           Assert(!PointerIsValid((Pointer)SysCache[cacheId]));
+           
+           SysCache[cacheId] =
+               InitSysCache(cacheinfo[cacheId].name,
+                            cacheinfo[cacheId].indname,
+                            cacheId,
+                            cacheinfo[cacheId].nkeys,
+                            cacheinfo[cacheId].key,
+                            cacheinfo[cacheId].iScanFunc);
+           if (!PointerIsValid((char *)SysCache[cacheId])) {
+               elog(WARN,
+                    "InitCatalogCache: Can't init cache %.16s(%d)",
+                    cacheinfo[cacheId].name,
+                    cacheId);
+           }
+           
+       }
+    }
+}
+
+/*
+ * SearchSysCacheTuple--
+ *
+ *    A layer on top of SearchSysCache that does the initialization and
+ *    key-setting for you.
+ *
+ * Returns the tuple if one is found, NULL if not.
+ *
+ * XXX The tuple that is returned is NOT supposed to be pfree'd!
+ */
+HeapTuple
+SearchSysCacheTuple(int cacheId,       /* cache selection code */
+                   Datum key1,
+                   Datum key2,
+                   Datum key3,
+                   Datum key4)
+{
+    register HeapTuple tp;
+    
+    if (cacheId < 0 || cacheId >= SysCacheSize) {
+       elog(WARN, "SearchSysCacheTuple: Bad cache id %d", cacheId);
+       return((HeapTuple) NULL);
+    }
+    
+    if (!AMI_OVERRIDE) {
+       Assert(PointerIsValid(SysCache[cacheId]));
+    } else {
+       if (!PointerIsValid(SysCache[cacheId])) {
+           SysCache[cacheId] =
+               InitSysCache(cacheinfo[cacheId].name,
+                            cacheinfo[cacheId].indname,
+                            cacheId,
+                            cacheinfo[cacheId].nkeys,
+                            cacheinfo[cacheId].key,
+                            cacheinfo[cacheId].iScanFunc);
+           if (!PointerIsValid(SysCache[cacheId])) {
+               elog(WARN,
+                    "InitCatalogCache: Can't init cache %.16s(%d)",
+                    cacheinfo[cacheId].name,
+                    cacheId);
+           }
+           
+       }
+    }
+    
+    tp = SearchSysCache(SysCache[cacheId], key1, key2, key3, key4);
+    if (!HeapTupleIsValid(tp)) {
+#ifdef CACHEDEBUG
+       elog(DEBUG,
+            "SearchSysCacheTuple: Search %s(%d) %d %d %d %d failed", 
+            (*cacheinfo[cacheId].name)->data,
+            cacheId, key1, key2, key3, key4);
+#endif
+       return((HeapTuple) NULL);
+    }
+    return(tp);
+}
+
+/*
+ * SearchSysCacheStruct--
+ *    Fills 's' with the information retrieved by calling SearchSysCache()
+ *    with arguments key1...key4.  Retrieves only the portion of the tuple
+ *    which is not variable-length.
+ *
+ * NOTE: we are assuming that non-variable-length fields in the system
+ *      catalogs will always be defined!
+ *
+ * Returns 1L if a tuple was found, 0L if not.
+ */
+int32
+SearchSysCacheStruct(int cacheId,              /* cache selection code */
+                    char *returnStruct,        /* (preallocated!) */
+                    Datum key1,
+                    Datum key2,
+                    Datum key3,
+                    Datum key4)
+{
+    HeapTuple  tp;
+    
+    if (!PointerIsValid(returnStruct)) {
+       elog(WARN, "SearchSysCacheStruct: No receiving struct");
+       return(0);
+    }
+    tp = SearchSysCacheTuple(cacheId, key1, key2, key3, key4);
+    if (!HeapTupleIsValid(tp))
+       return(0);
+    memmove(returnStruct, (char *) GETSTRUCT(tp), cacheinfo[cacheId].size);
+    return(1);
+}
+
+
+/*
+ * SearchSysCacheGetAttribute--
+ *    Returns the attribute corresponding to 'attributeNumber' for
+ *    a given cached tuple.
+ *
+ * XXX This re-opens a relation, so this is slower.
+ *
+ * [callers all assume this returns a (struct varlena *). -ay 10/94]
+ */
+void *
+SearchSysCacheGetAttribute(int cacheId,
+                          AttrNumber attributeNumber,
+                          Datum key1,
+                          Datum key2,
+                          Datum key3,
+                          Datum key4)
+{
+    HeapTuple  tp;
+    char *cacheName;
+    Relation   relation;
+    int32      attributeLength, attributeByValue;
+    bool       isNull;
+    char       *attributeValue;
+    void       *returnValue;
+    
+    tp = SearchSysCacheTuple(cacheId, key1, key2, key3, key4);
+     cacheName = cacheinfo[cacheId].name;
+    
+    if (!HeapTupleIsValid(tp)) {
+#ifdef CACHEDEBUG
+       elog(DEBUG,
+            "SearchSysCacheGetAttribute: Lookup in %s(%d) failed",
+            cacheName, cacheId);
+#endif /* defined(CACHEDEBUG) */
+       return(NULL);
+    }
+    
+    relation = heap_openr(cacheName);
+    
+    if (attributeNumber < 0 &&
+       attributeNumber > FirstLowInvalidHeapAttributeNumber) {
+       attributeLength = heap_sysattrlen(attributeNumber);
+       attributeByValue = heap_sysattrbyval(attributeNumber);
+    } else if (attributeNumber > 0 &&
+              attributeNumber <= relation->rd_rel->relnatts) {
+       attributeLength =
+           relation->rd_att->attrs[attributeNumber-1]->attlen;
+       attributeByValue =
+           relation->rd_att->attrs[attributeNumber-1]->attbyval;
+    } else {
+       elog(WARN, 
+            "SearchSysCacheGetAttribute: Bad attr # %d in %s(%d)",
+            attributeNumber, cacheName, cacheId);
+       return(NULL);
+    }
+    
+    attributeValue = heap_getattr(tp,
+                                 (Buffer) 0,
+                                 attributeNumber,
+                                 RelationGetTupleDescriptor(relation),
+                                 &isNull);
+    
+    if (isNull) {
+       /*
+        * Used to be an elog(DEBUG, ...) here and a claim that it should
+        * be a FATAL error, I don't think either is warranted -mer 6/9/92
+        */
+       return(NULL);
+    }
+    
+    if (attributeByValue) {
+       returnValue = (void *)attributeValue;
+    } else {
+       char    *tmp;
+       int size = (attributeLength < 0)
+           ? VARSIZE((struct varlena *) attributeValue) /* variable length */
+               : attributeLength;                       /* fixed length */
+       
+       tmp = (char *) palloc(size);
+       memmove(tmp, attributeValue, size);
+       returnValue = (void *)tmp;
+    }
+    
+    heap_close(relation);
+    return(returnValue);
+}
+
+/*
+ * TypeDefaultRetrieve--
+ *
+ *    Given a type OID, return the typdefault field associated with that
+ *    type.  The typdefault is returned as the car of a dotted pair which
+ *    is passed to TypeDefaultRetrieve by the calling routine.
+ *
+ * Returns a fixnum for types which are passed by value and a ppreserve'd
+ * vectori for types which are not.
+ *
+ * [identical to get_typdefault, expecting a (struct varlena *) as ret val.
+ *  some day, either of the functions should be removed      -ay 10/94]
+ */
+void *
+TypeDefaultRetrieve(Oid typId)
+{
+    HeapTuple          typeTuple;
+    TypeTupleForm      type;
+    int32              typByVal, typLen;
+    struct varlena     *typDefault;
+    int32              dataSize;
+    void               *returnValue;
+    
+    typeTuple = SearchSysCacheTuple(TYPOID,
+                                   ObjectIdGetDatum(typId),
+                                   0,0,0);
+    
+    if (!HeapTupleIsValid(typeTuple)) {
+#ifdef CACHEDEBUG
+       elog(DEBUG, "TypeDefaultRetrieve: Lookup in %s(%d) failed",
+            (*cacheinfo[TYPOID].name)->data, TYPOID);
+#endif /* defined(CACHEDEBUG) */
+       return(NULL);
+    }
+    
+    type = (TypeTupleForm) GETSTRUCT(typeTuple);
+    typByVal = type->typbyval;
+    typLen = type->typlen;
+    
+    typDefault = (struct varlena *)
+       SearchSysCacheGetAttribute(TYPOID, 
+                                  Anum_pg_type_typdefault,
+                                  ObjectIdGetDatum(typId),
+                                  0,0,0);
+    
+    if (typDefault == (struct varlena *)NULL) {
+#ifdef CACHEDEBUG
+       elog(DEBUG, "TypeDefaultRetrieve: No extractable typdefault",
+            (*cacheinfo[TYPOID].name)->data, TYPOID);
+#endif /* defined(CACHEDEBUG) */
+       return (NULL);
+       
+    }
+    
+    dataSize = VARSIZE(typDefault) - VARHDRSZ;
+    
+    if (typByVal) {
+       int8 i8;
+       int16 i16;
+       int32 i32;
+       
+       if (dataSize == typLen) {
+           switch (typLen) {
+           case sizeof(int8):
+               memmove((char *) &i8, VARDATA(typDefault), sizeof(int8));
+               i32 = i8;
+               break;
+           case sizeof(int16):
+               memmove((char *) &i16, VARDATA(typDefault), sizeof(int16));
+               i32 = i16;
+               break;
+           case sizeof(int32):
+               memmove((char *) &i32, VARDATA(typDefault), sizeof(int32));
+               break;
+           }
+           returnValue = (void *)i32;
+       } else {
+           returnValue = NULL;
+       }
+    } else {
+       if ((typLen < 0 && dataSize < 0) || dataSize != typLen)
+           returnValue = NULL;
+       else {
+           returnValue = (void *)palloc(VARSIZE(typDefault));
+           memmove((char *) returnValue,
+                   (char *) typDefault,
+                   (int) VARSIZE(typDefault));
+       }
+    }
+    
+    return(returnValue);
+}
+
+
diff --git a/src/backend/utils/catcache.h b/src/backend/utils/catcache.h
new file mode 100644 (file)
index 0000000..5316642
--- /dev/null
@@ -0,0 +1,85 @@
+/*-------------------------------------------------------------------------
+ *
+ * catcache.h--
+ *    Low-level catalog cache definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        CATCACHE_H
+#define CATCACHE_H
+
+/* #define     CACHEDEBUG       turns DEBUG elogs on */
+
+#include "postgres.h"
+    
+#include "access/skey.h"
+#include "access/htup.h"
+#include "utils/rel.h"
+#include "nodes/memnodes.h"
+#include "lib/dllist.h"
+
+/*
+ *     struct catctup:         tuples in the cache.
+ *     struct catcache:        information for managing a cache.
+ */
+
+typedef struct catctup {
+    HeapTuple  ct_tup;         /* A pointer to a tuple         */
+    Dlelem     *ct_node; /* points to LRU list is the CatCTup is in the cache,
+                           else, points to the cache if the CatCTup is in 
+                           LRU list */
+} CatCTup;
+
+/* voodoo constants */
+#define        NCCBUCK 500     /* CatCache buckets*/
+#define MAXTUP 300     /* Maximum # of tuples cached per cache */
+
+typedef struct catcache {
+    Oid                relationId;
+    Oid                indexId;
+    char       *cc_relname;    /* relation name for defered open */
+    char       *cc_indname;    /* index name for defered open */
+    HeapTuple  (*cc_iscanfunc)(); /* index scanfunction */
+    TupleDesc  cc_tupdesc;     /* tuple descriptor from reldesc */
+    int                id;             /* XXX could be improved -hirohama */
+    short      cc_ntup;        /* # of tuples in this cache    */
+    short      cc_maxtup;      /* max # of tuples allowed (LRU)*/
+    short      cc_nkeys;
+    short      cc_size;
+    short      cc_key[4];
+    short      cc_klen[4];
+    ScanKeyData        cc_skey[4];
+    struct catcache *cc_next;
+    Dllist  *cc_lrulist;          /* LRU list, most recent first */
+    Dllist  *cc_cache[NCCBUCK+1];
+} CatCache;
+
+#define        InvalidCatalogCacheId   (-1)
+
+extern struct catcache *Caches;
+extern GlobalMemory    CacheCxt;
+
+extern void CatalogCacheInitializeCache(struct catcache *cache, 
+                                       Relation relation);
+extern void CatalogCacheSetId(CatCache *cacheInOutP, int id);
+extern long comphash(long l, char *v);
+extern Index CatalogCacheComputeHashIndex(struct catcache *cacheInP);
+extern Index CatalogCacheComputeTupleHashIndex(struct catcache *cacheInOutP,
+                                      Relation relation, HeapTuple tuple);
+extern void CatCacheRemoveCTup(CatCache *cache, Dlelem *e);  
+extern void CatalogCacheIdInvalidate(int cacheId, Index hashIndex, 
+                                    ItemPointer pointer);
+extern void ResetSystemCache(void);
+extern CatCache *InitSysCache(char *relname, char *indname, int id, int nkeys, 
+                             int key[], HeapTuple (*iScanfuncP)());
+extern HeapTuple SearchSysCache(struct catcache *cache, Datum v1, Datum v2,
+                               Datum v3, Datum v4);
+extern void RelationInvalidateCatalogCacheTuple(Relation relation, 
+                               HeapTuple tuple, void (*function)());
+
+#endif /* CATCACHE_H */
diff --git a/src/backend/utils/datum.h b/src/backend/utils/datum.h
new file mode 100644 (file)
index 0000000..c1bf19c
--- /dev/null
@@ -0,0 +1,64 @@
+/*-------------------------------------------------------------------------
+ *
+ * datum.h--
+ *    POSTGRES abstract data type datum representation definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        DATUM_H
+#define        DATUM_H
+
+#include "postgres.h"
+
+/*--------------------------------------------------------
+ * SOME NOT VERY PORTABLE ROUTINES ???
+ *--------------------------------------------------------
+ *
+ * In the implementation of the next routines we assume the following:
+ *
+ * A) if a type is "byVal" then all the information is stored in the
+ * Datum itself (i.e. no pointers involved!). In this case the
+ * length of the type is always greater than zero and less than
+ * "sizeof(Datum)"
+ * B) if a type is not "byVal" and it has a fixed length, then
+ * the "Datum" always contain a pointer to a stream of bytes.
+ * The number of significant bytes are always equal to the length of thr
+ * type.
+ * C) if a type is not "byVal" and is of variable length (i.e. it has
+ * length == -1) then "Datum" always points to a "struct varlena".
+ * This varlena structure has information about the actual length of this
+ * particular instance of the type and about its value.
+ */
+
+/*---------------
+ * datumGetSize
+ * find the "real" length of a datum
+ */
+extern Size datumGetSize(Datum value, Oid type, bool byVal, Size len);
+
+/*---------------
+ * datumCopy
+ * make a copy of a datum.
+ */
+extern Datum datumCopy(Datum value, Oid type, bool byVal, Size len);
+
+/*---------------
+ * datumFree
+ * free space that *might* have been palloced by "datumCopy"
+ */
+extern void datumFree(Datum value, Oid type, bool byVal, Size len);
+
+/*---------------
+ * datumIsEqual
+ * return true if thwo datums are equal, false otherwise.
+ * XXX : See comments in the code for restrictions!
+ */
+extern bool datumIsEqual(Datum value1, Datum value2, Oid type, 
+                        bool byVal, Size len);
+
+#endif /* DATUM_H */
diff --git a/src/backend/utils/dynamic_loader.h b/src/backend/utils/dynamic_loader.h
new file mode 100644 (file)
index 0000000..92c92bb
--- /dev/null
@@ -0,0 +1,53 @@
+/*-------------------------------------------------------------------------
+ *
+ * dynamic_loader.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef DYNAMIC_LOADER_H
+#define DYNAMIC_LOADER_H
+
+#ifdef MIN
+#undef MIN
+#undef MAX
+#endif /* MIN */
+
+#ifdef WIN32
+#define MAXPATHLEN    250
+#endif
+
+#include <sys/param.h>                 /* for MAXPATHLEN */
+#include <sys/types.h>                 /* for dev_t, ino_t, etc. */
+#ifdef WIN32
+#include <wchar.h>
+#endif
+
+/*
+ * List of dynamically loaded files.
+ */
+
+typedef struct df_files {
+    char filename[MAXPATHLEN];         /* Full pathname of file */
+#ifdef WIN32
+    _dev_t device;                     /* Device file is on */
+    _ino_t inode;                      /* Inode number of file */
+#else
+    dev_t device;                      /* Device file is on */
+    ino_t inode;                       /* Inode number of file */
+#endif /* WIN32 */
+    void *handle;                      /* a handle for pg_dl* functions */
+    struct df_files *next;
+} DynamicFileList;
+
+extern void *pg_dlopen(char *filename);
+extern func_ptr pg_dlsym(void *handle, char *funcname);
+extern void pg_dlclose(void *handle);
+extern char *pg_dlerror(void);
+
+#endif /* DYNAMIC_LOADER_H */
diff --git a/src/backend/utils/elog.h b/src/backend/utils/elog.h
new file mode 100644 (file)
index 0000000..a812bca
--- /dev/null
@@ -0,0 +1,38 @@
+/*-------------------------------------------------------------------------
+ *
+ * elog.h--
+ *    POSTGRES error logging definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        ELOG_H
+#define ELOG_H
+
+#define NOTICE 0       /* random info - no special action */
+#define WARN   -1      /* Warning error - return to known state */
+#define FATAL  1       /* Fatal error - abort process */
+#define DEBUG  -2      /* debug message */
+#define NOIND  -3      /* debug message, don't indent as far */
+
+#define PTIME  0x100   /* prepend time to message */
+#define POS    0x200   /* prepend source position to message */
+#define USERMSG        0x400   /* send message to user */
+#define TERM   0x800   /* send message to terminal */
+#define DBLOG  0x1000  /* put message in per db log */
+#define SLOG   0x2000  /* put message in system log */
+#define ABORT  0x4000  /* abort process after logging */
+
+#define ELOG_MAXLEN 4096
+
+
+/* uncomment the following if you want your elog's to be timestamped */
+/* #define ELOG_TIMESTAMPS */
+
+extern void elog(int lev, char *fmt, ...);
+
+#endif /* ELOG_H */
diff --git a/src/backend/utils/error/Makefile.inc b/src/backend/utils/error/Makefile.inc
new file mode 100644 (file)
index 0000000..5087057
--- /dev/null
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for utils/error
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= assert.c elog.c exc.c excabort.c excid.c format.c
diff --git a/src/backend/utils/error/assert.c b/src/backend/utils/error/assert.c
new file mode 100644 (file)
index 0000000..eadf915
--- /dev/null
@@ -0,0 +1,64 @@
+/*-------------------------------------------------------------------------
+ *
+ * assert.c--
+ *    Assert code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTE
+ *    This should eventually work with elog(), dlog(), etc.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+
+#include "c.h"                 /* where the declaration goes */
+#include "utils/module.h"
+
+#include "utils/exc.h"
+
+int
+ExceptionalCondition(char* conditionName,
+                    Exception *exceptionP,
+                    char* detail,
+                    char* fileName,
+                    int lineNumber)
+{
+    extern char* ExcFileName;  /* XXX */
+    extern Index ExcLineNumber;        /* XXX */
+    
+    ExcFileName = fileName;
+    ExcLineNumber = lineNumber;
+    
+    if (!PointerIsValid(conditionName)
+       || !PointerIsValid(fileName)
+       || !PointerIsValid(exceptionP)) {
+       fprintf(stderr, "ExceptionalCondition: bad arguments\n");
+       
+       ExcAbort(exceptionP, 
+                (ExcDetail)detail,
+                (ExcData)NULL,
+                (ExcMessage)NULL);
+    } else {
+       fprintf(stderr,
+               "%s(\"%s:%s\", File: \"%s\", Line: %d)\n",
+               exceptionP->message, conditionName, detail,
+               fileName, lineNumber);
+    }
+    
+    /*
+     * XXX Depending on the Exception and tracing conditions, you will
+     * XXX want to stop here immediately and maybe dump core.
+     * XXX This may be especially true for Assert(), etc.
+     */
+    
+    /* TraceDump();    dump the trace stack */
+    
+    /* XXX FIXME: detail is lost */
+    ExcRaise(exceptionP, (ExcDetail)0, (ExcData)NULL, conditionName);
+    return(0);
+}
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
new file mode 100644 (file)
index 0000000..ba8279e
--- /dev/null
@@ -0,0 +1,237 @@
+/*-------------------------------------------------------------------------
+ *
+ * elog.c--
+ *    error logger
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#ifndef O_RDONLY
+#include <sys/file.h>
+#endif /* O_RDONLY */
+#include <sys/types.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include "postgres.h"
+#include "miscadmin.h"
+#include "utils/elog.h"
+#include "libpq/libpq.h"
+#include "storage/proc.h"
+
+static int     Debugfile = -1;
+static int     Err_file = -1;
+static int     ElogDebugIndentLevel = 0;
+
+extern char    OutputFileName[];
+#ifdef WIN32
+extern jmp_buf  Warn_restart;
+#endif
+
+/*
+ * elog --
+ *     Old error logging function.
+ */
+void
+elog(int lev, char *fmt, ... )
+{
+    va_list ap;
+    char               buf[ELOG_MAXLEN], line[ELOG_MAXLEN];
+    register char      *bp, *cp;
+    extern     int     errno, sys_nerr;
+#if !defined(PORTNAME_BSD44_derived) && !defined(PORTNAME_bsdi)
+    extern     char    *sys_errlist[];
+#endif /* !PORTNAME_BSD44_derived */
+#ifndef PG_STANDALONE
+    extern     FILE    *Pfout;
+#endif /* !PG_STANDALONE */
+    time_t             tim, time();
+    int                len;
+    int                i = 0;
+    
+    va_start(ap, fmt);
+    if (lev == DEBUG && Debugfile < 0) {
+       return;
+    }
+    switch (lev) {
+    case NOIND:
+       i = ElogDebugIndentLevel-1;
+       if (i < 0) i = 0;
+       if (i > 30) i = i%30;
+       cp = "DEBUG:";
+       break;
+    case DEBUG:
+       i = ElogDebugIndentLevel;
+       if (i < 0) i = 0;
+       if (i > 30) i = i%30;
+       cp = "DEBUG:";
+       break;
+    case NOTICE:
+       cp = "NOTICE:";
+       break;
+    case WARN:
+       cp = "WARN:";
+       break;
+    default:
+       sprintf(line, "FATAL %d:", lev);
+       cp = line;
+    }
+#ifdef ELOG_TIMESTAMPS
+    time(&tim);
+    strcat(strcpy(buf, cp), ctime(&tim)+4);
+    bp = buf+strlen(buf)-6;
+    *bp++ = ':';
+#else
+    strcpy(buf,cp);
+    bp = buf+strlen(buf);
+#endif
+    while (i-- >0) *bp++ = ' ';
+    for (cp = fmt; *cp; cp++)
+       if (*cp == '%' && *(cp+1) == 'm') {
+           if (errno < sys_nerr && errno >= 0)
+               strcpy(bp, sys_errlist[errno]);
+           else
+               sprintf(bp, "error %d", errno);
+           bp += strlen(bp);
+           cp++;
+       } else
+           *bp++ = *cp;
+    *bp = '\0';
+    vsprintf(line, buf, ap);
+    va_end(ap);
+    len = strlen(strcat(line, "\n"));
+    if (Debugfile > -1)
+       write(Debugfile, line, len);
+    if (lev == DEBUG || lev == NOIND)
+       return;
+    
+    /*
+     *  If there's an error log file other than our channel to the
+     *  front-end program, write to it first.  This is important
+     *  because there's a bug in the socket code on ultrix.  If the
+     *  front end has gone away (so the channel to it has been closed
+     *  at the other end), then writing here can cause this backend
+     *  to exit without warning -- that is, write() does an exit().
+     *  In this case, our only hope of finding out what's going on
+     *  is if Err_file was set to some disk log.  This is a major pain.
+     */
+    
+    if (Err_file > -1 && Debugfile != Err_file) {
+       if (write(Err_file, line, len) < 0) {
+           write(open("/dev/console", O_WRONLY, 0666), line, len);
+           fflush(stdout);
+           fflush(stderr);
+           exitpg(lev);
+       }
+       fsync(Err_file);
+    }
+    
+#ifndef PG_STANDALONE
+    /* Send IPC message to the front-end program */
+    if (Pfout != NULL && lev > DEBUG) {
+       /* notices are not exactly errors, handle it differently */
+       if (lev == NOTICE) 
+           pq_putnchar("N", 1);
+       else
+           pq_putnchar("E", 1);
+       /* pq_putint(-101, 4);*/        /* should be query id */ 
+       pq_putstr(line);
+       pq_flush();
+    }
+#endif /* !PG_STANDALONE */
+    
+    if (lev == WARN) {
+       ProcReleaseSpins(NULL); /* get rid of spinlocks we hold */
+#ifndef WIN32
+       kill(getpid(), 1);      /* abort to traffic cop */
+       pause();
+#else
+       longjmp(Warn_restart, 1);
+#endif /* WIN32 */
+       /*
+        * The pause(3) is just to avoid race conditions where the
+        * thread of control on an MP system gets past here (i.e.,
+        * the signal is not received instantaneously).
+        */
+    }
+    
+    if (lev == FATAL) {
+       /*
+        * Assume that if we have detected the failure we can
+        * exit with a normal exit status.  This will prevent
+        * the postmaster from cleaning up when it's not needed.
+        */
+       fflush(stdout);
+       fflush(stderr);
+       ProcReleaseSpins(NULL); /* get rid of spinlocks we hold */
+       ProcReleaseLocks();     /* get rid of real locks we hold */
+       exitpg(0);
+    }
+    
+    if (lev > FATAL) {
+       fflush(stdout);
+       fflush(stderr);
+       exitpg(lev);
+    }
+}
+
+#ifndef PG_STANDALONE
+int
+DebugFileOpen()
+{
+    int fd, istty;
+    
+    Err_file = Debugfile = -1;
+    ElogDebugIndentLevel = 0;
+    
+    if (OutputFileName[0]) {
+       OutputFileName[MAXPGPATH-1] = '\0';
+       if ((fd = open(OutputFileName, O_CREAT|O_APPEND|O_WRONLY,
+                      0666)) < 0)
+           elog(FATAL, "DebugFileOpen: open of %s: %m",
+                OutputFileName);
+       istty = isatty(fd);
+       (void) close(fd);
+       /* If the file is a tty and we're running under the
+        * postmaster, try to send stdout there as well (if it
+        * isn't a tty then stderr will block out stdout, so we
+        * may as well let stdout go wherever it was going before).
+        */
+       if (istty &&
+           IsUnderPostmaster &&
+           !freopen(OutputFileName, "a", stdout))
+           elog(FATAL, "DebugFileOpen: %s reopen as stdout: %m",
+                OutputFileName);
+       if (!freopen(OutputFileName, "a", stderr))
+           elog(FATAL, "DebugFileOpen: %s reopen as stderr: %m",
+                OutputFileName);
+       Err_file = Debugfile = fileno(stderr);
+       return(Debugfile);
+    }
+#ifndef WIN32    
+    /* If no filename was specified, send debugging output to stderr.
+     * If stderr has been hosed, try to open a file.
+     */
+    fd = fileno(stderr);
+    if (fcntl(fd, F_GETFD, 0) < 0) {
+       sprintf(OutputFileName, "%s/pg.errors.%d",
+               GetPGData(), getpid());
+       fd = open(OutputFileName, O_CREAT|O_APPEND|O_WRONLY, 0666);
+    }
+#endif /* WIN32 */    
+    if (fd < 0)
+       elog(FATAL, "DebugFileOpen: could not open debugging file");
+    
+    Err_file = Debugfile = fd;
+    return(Debugfile);
+}
+#endif
diff --git a/src/backend/utils/error/exc.c b/src/backend/utils/error/exc.c
new file mode 100644 (file)
index 0000000..6d9ec15
--- /dev/null
@@ -0,0 +1,183 @@
+/*-------------------------------------------------------------------------
+ *
+ * exc.c--
+ *    POSTGRES exception handling code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTE
+ *    XXX this code needs improvement--check for state violations and
+ *    XXX reset after handling an exception.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>     /* XXX use own I/O routines */
+#include <errno.h>
+#include "utils/exc.h"
+#include "storage/ipc.h"
+
+/*
+ * Global Variables
+ */
+static bool ExceptionHandlingEnabled = false;
+
+char*                  ExcFileName = NULL;
+Index                  ExcLineNumber = 0;
+
+ExcFrame               *ExcCurFrameP = NULL;
+
+static ExcProc         *ExcUnCaughtP = NULL;
+
+extern char*   ProgramName;
+
+/*
+ * Exported Functions
+ */
+
+/*
+ * EnableExceptionHandling --
+ *     Enables/disables the exception handling system.
+ *
+ * Note:
+ *     This must be called before any exceptions occur.  I.e., call this first!
+ *     This routine will not return if an error is detected.
+ *     This does not follow the usual Enable... protocol.
+ *     This should be merged more closely with the error logging and tracing
+ *     packages.
+ *
+ * Exceptions:
+ *     none
+ */
+/*
+ * Excection handling should be supported by the language, thus there should
+ * be no need to explicitly enable exception processing.
+ *
+ * This function should probably not be called, ever.  Currently it does
+ * almost nothing.  If there is a need for this intialization and checking.
+ * then this function should be converted to the new-style Enable code and
+ * called by all the other module Enable functions.
+ */
+void
+EnableExceptionHandling(bool on)
+{
+    if (on == ExceptionHandlingEnabled) {
+       /* XXX add logging of failed state */
+       exitpg(255);
+       /* ExitPostgres(FatalExitStatus); */
+    }
+    
+    if (on) {  /* initialize */
+       ;
+    } else {   /* cleanup */
+       ExcFileName = NULL;
+       ExcLineNumber = 0;
+       ExcCurFrameP = NULL;
+       ExcUnCaughtP = NULL;
+    }
+    
+    ExceptionHandlingEnabled = on;
+}
+
+void
+ExcPrint(Exception *excP,
+        ExcDetail detail,
+        ExcData data,
+        ExcMessage message)
+{
+    extern     int     errno;
+    extern     int     sys_nerr;
+#if !defined(PORTNAME_BSD44_derived) && !defined(PORTNAME_bsdi)
+    extern     char    *sys_errlist[];
+#endif /* !PORTNAME_BSD44_derived */
+    
+#ifdef lint
+    data = data;
+#endif
+    
+    (void) fflush(stdout);     /* In case stderr is buffered */
+    
+#if    0
+    if (ProgramName != NULL && *ProgramName != '\0')
+       (void) fprintf(stderr, "%s: ", ProgramName);
+#endif
+    
+    if (message != NULL)
+       (void) fprintf(stderr, "%s", message);
+    else if (excP->message != NULL)
+       (void) fprintf(stderr, "%s", excP->message);
+    else
+#ifdef lint
+       (void) fprintf(stderr, "UNNAMED EXCEPTION 0x%lx", excP);
+#else
+    (void) fprintf(stderr, "UNNAMED EXCEPTION 0x%lx", (long)excP);
+#endif
+    
+    (void) fprintf(stderr, " (%ld)", detail);
+    
+    if (errno > 0 && errno < sys_nerr &&
+       sys_errlist[errno] != NULL && sys_errlist[errno][0] != '\0')
+       (void) fprintf(stderr, " [%s]", sys_errlist[errno]);
+    else if (errno != 0)
+       (void) fprintf(stderr, " [Error %d]", errno);
+    
+    (void) fprintf(stderr, "\n");
+    
+    (void) fflush(stderr);
+}
+
+ExcProc *
+ExcGetUnCaught()
+{
+    return (ExcUnCaughtP);
+}
+
+ExcProc *
+ExcSetUnCaught(ExcProc *newP)
+{
+    ExcProc    *oldP = ExcUnCaughtP;
+    
+    ExcUnCaughtP = newP;
+    
+    return (oldP);
+}
+
+void
+ExcUnCaught(Exception *excP,
+           ExcDetail detail,
+           ExcData data,
+           ExcMessage message)
+{
+    ExcPrint(excP, detail, data, message);
+    
+    ExcAbort(excP, detail, data, message);
+}
+
+void
+ExcRaise(Exception *excP,
+        ExcDetail detail,
+        ExcData        data,
+        ExcMessage message)
+{
+    register ExcFrame  *efp;
+    
+    efp = ExcCurFrameP;
+    if (efp == NULL) {
+       if (ExcUnCaughtP != NULL)
+           (*ExcUnCaughtP)(excP, detail, data, message);
+       
+       ExcUnCaught(excP, detail, data, message);
+    } else {
+       efp->id         = excP;
+       efp->detail     = detail;
+       efp->data       = data;
+       efp->message    = message;
+       
+       ExcCurFrameP = efp->link;
+       
+       longjmp(efp->context, 1);
+    }
+}
diff --git a/src/backend/utils/error/excabort.c b/src/backend/utils/error/excabort.c
new file mode 100644 (file)
index 0000000..495166d
--- /dev/null
@@ -0,0 +1,28 @@
+/*-------------------------------------------------------------------------
+ *
+ * excabort.c--
+ *    Default exception abort code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "utils/exc.h"         /* where function declarations go */
+
+void
+ExcAbort(const Exception *excP, 
+        ExcDetail detail, 
+        ExcData data,
+        ExcMessage message)
+{
+#ifdef __SABER__
+    saber_stop();
+#else
+    /* dump core */
+    abort();
+#endif
+}
diff --git a/src/backend/utils/error/excid.c b/src/backend/utils/error/excid.c
new file mode 100644 (file)
index 0000000..04e90ee
--- /dev/null
@@ -0,0 +1,64 @@
+/*-------------------------------------------------------------------------
+ *
+ * excid.c--
+ *    POSTGRES known exception identifier code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "utils/excid.h"
+
+/*****************************************************************************
+ *   Generic Recoverable Exceptions                                          *
+ *****************************************************************************/
+
+
+/*
+ * FailedAssertion --
+ *     Indicates an Assert(...) failed.
+ */
+Exception FailedAssertion = { "Failed Assertion" };
+
+/*
+ * BadState --
+ *     Indicates a function call request is inconsistent with module state.
+ */
+Exception BadState = { "Bad State for Function Call" };
+
+/*
+ * BadArg --
+ *     Indicates a function call argument or arguments is out-of-bounds.
+ */
+Exception BadArg = { "Bad Argument to Function Call" };
+
+/*****************************************************************************
+ *   Specific Recoverable Exceptions                                         *
+ *****************************************************************************/
+
+/*
+ * BadAllocSize --
+ *     Indicates that an allocation request is of unreasonable size.
+ */
+Exception BadAllocSize = { "Too Large Allocation Request" };
+
+/*
+ * ExhaustedMemory --
+ *     Indicates an dynamic memory allocation failed.
+ */
+Exception ExhaustedMemory = { "Memory Allocation Failed" };
+
+/*
+ * Unimplemented --
+ *     Indicates a function call request requires unimplemented code.
+ */
+Exception Unimplemented = { "Unimplemented Functionality" };
+
+Exception CatalogFailure = {"Catalog failure"};        /* XXX inconsistent */
+Exception InternalError = {"Internal Error"};  /* XXX inconsistent */
+Exception SemanticError = {"Semantic Error"};  /* XXX inconsistent */
+Exception SystemError = {"System Error"};      /* XXX inconsistent */
diff --git a/src/backend/utils/error/format.c b/src/backend/utils/error/format.c
new file mode 100644 (file)
index 0000000..0201b2d
--- /dev/null
@@ -0,0 +1,40 @@
+/*-------------------------------------------------------------------------
+ *
+ * format.c--
+ *    a wrapper around code that does what vsprintf does.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <stdarg.h>
+#include "c.h"
+
+#define FormMaxSize    1024
+#define FormMinSize    (FormMaxSize / 8)
+
+static char    FormBuf[FormMaxSize];
+
+
+/* ----------------
+ *     form
+ * ----------------
+ */
+char *
+form(char *fmt, ... )
+{
+    va_list    args;
+
+    va_start(args, fmt);
+    
+    (void) vsprintf(FormBuf, fmt, args);
+
+    va_end(args);
+    
+    return (FormBuf);
+}
diff --git a/src/backend/utils/exc.h b/src/backend/utils/exc.h
new file mode 100644 (file)
index 0000000..1192c74
--- /dev/null
@@ -0,0 +1,101 @@
+/*-------------------------------------------------------------------------
+ *
+ * exc.h--
+ *    POSTGRES exception handling definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        EXC_H
+#define EXC_H
+
+#include "c.h"         /* for Exception, etc. */
+#include <setjmp.h>
+
+extern char *ExcFileName;
+extern Index ExcLineNumber;
+
+/*
+ * ExcMessage and Exception are now defined in c.h
+ */
+
+#if defined(PORTNAME_linux) \
+|| defined(PORTNAME_hpux) \
+|| defined(PORTNAME_next)\
+|| defined(WIN32)
+typedef jmp_buf                ExcContext;
+#else
+typedef sigjmp_buf     ExcContext;
+#endif
+
+typedef Exception*     ExcId;
+typedef long           ExcDetail;
+typedef char*          ExcData;
+
+typedef struct ExcFrame {
+    struct ExcFrame    *link;
+    ExcContext         context;
+    ExcId              id;
+    ExcDetail          detail;
+    ExcData            data;
+    ExcMessage         message;
+} ExcFrame;
+
+extern ExcFrame*       ExcCurFrameP;
+
+#define        ExcBegin()                                                      \
+       {                                                               \
+               ExcFrame        exception;                              \
+                                                                       \
+               exception.link = ExcCurFrameP;                          \
+               if (sigsetjmp(exception.context, 1) == 0) {             \
+                       ExcCurFrameP = &exception;                      \
+                       {
+#define        ExcExcept()                                                     \
+                       }                                               \
+                       ExcCurFrameP = exception.link;                  \
+               } else {                                                \
+                       {
+#define        ExcEnd()                                                        \
+                       }                                               \
+               }                                                       \
+       }
+
+#define raise4(x, t, d, message) \
+       ExcRaise(&(x), (ExcDetail)(t), (ExcData)(d), (ExcMessage)(message))
+
+#define        reraise() \
+       raise4(*exception.id,exception.detail,exception.data,exception.message)
+
+typedef        void ExcProc(Exception*, ExcDetail, ExcData, ExcMessage);
+
+
+/*
+ * prototypes for functions in exc.c
+ */
+extern void EnableExceptionHandling(bool on);
+extern void ExcPrint(Exception *excP, ExcDetail detail, ExcData data,
+                    ExcMessage message);
+extern ExcProc *ExcGetUnCaught();
+extern ExcProc *ExcSetUnCaught(ExcProc *newP);
+extern void ExcUnCaught(Exception *excP, ExcDetail detail, ExcData data,
+                       ExcMessage message);
+extern void ExcUnCaught(Exception *excP, ExcDetail detail, ExcData data,
+                       ExcMessage message);
+extern void ExcRaise(Exception *excP,
+                    ExcDetail detail,
+                    ExcData    data,
+                    ExcMessage message);
+
+
+/*
+ * prototypes for functions in excabort.c
+ */
+extern void ExcAbort(const Exception *excP, ExcDetail detail, ExcData data,
+                    ExcMessage message);
+
+#endif /* EXC_H */
diff --git a/src/backend/utils/excid.h b/src/backend/utils/excid.h
new file mode 100644 (file)
index 0000000..0fd7356
--- /dev/null
@@ -0,0 +1,31 @@
+/*-------------------------------------------------------------------------
+ *
+ * excid.h--
+ *    POSTGRES known exception identifier definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        EXCID_H
+#define EXCID_H
+
+#include "c.h"
+#include "utils/exc.h" /* for Exception */
+
+extern Exception FailedAssertion;
+extern Exception BadState;
+extern Exception BadArg;
+extern Exception BadAllocSize;
+extern Exception ExhaustedMemory;
+extern Exception Unimplemented;
+
+extern Exception CatalogFailure;       /* XXX inconsistent naming style */
+extern Exception InternalError;                /* XXX inconsistent naming style */
+extern Exception SemanticError;                /* XXX inconsistent naming style */
+extern Exception SystemError;          /* XXX inconsistent naming style */
+
+#endif /* EXCID_H */
diff --git a/src/backend/utils/fcache.h b/src/backend/utils/fcache.h
new file mode 100644 (file)
index 0000000..4f9a64f
--- /dev/null
@@ -0,0 +1,55 @@
+/*-------------------------------------------------------------------------
+ *
+ * fcache.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        FCACHE_H
+#define        FCACHE_H
+
+#include "fmgr.h"
+
+typedef struct
+{
+    int      typlen;           /* length of the return type */
+    int      typbyval;         /* true if return type is pass by value */
+    func_ptr func;             /* address of function to call (for c funcs) */
+    Oid             foid;              /* oid of the function in pg_proc */
+    Oid      language;         /* oid of the language in pg_language */
+    int      nargs;            /* number of arguments */
+
+    /* Might want to make these two arrays of size MAXFUNCARGS */
+
+    Oid *argOidVect;   /* oids of all the arguments */
+    bool     *nullVect;                /* keep track of null arguments */
+
+    char     *src;             /* source code of the function */
+    char     *bin;             /* binary object code ?? */
+    char     *func_state;      /* fuction_state struct for execution */
+
+    bool     oneResult;                /* true we only want 1 result from the 
+                                * function
+                                */
+    bool     hasSetArg;                /* true if func is part of a nested dot expr
+                                * whose argument is func returning a set ugh!
+                                */
+
+    Pointer  funcSlot;         /* if one result we need to copy it before we
+                                * end execution of the function and free stuff
+                                */
+
+    char     *setArg;          /* current argument for nested dot execution
+                                * Nested dot expressions mean we have funcs
+                                * whose argument is a set of tuples
+                                */
+
+    bool     istrusted;                /* trusted fn? */
+} FunctionCache, *FunctionCachePtr;
+
+#endif /* FCACHE_H */
diff --git a/src/backend/utils/fcache2.h b/src/backend/utils/fcache2.h
new file mode 100644 (file)
index 0000000..6aa8dc1
--- /dev/null
@@ -0,0 +1,19 @@
+/*-------------------------------------------------------------------------
+ *
+ * fcache2.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FCACHE2_H
+#define FCACHE2_H
+
+extern void
+setFcache(Node *node, Oid foid, List *argList, ExprContext *econtext);
+
+#endif /* FCACHE2_H */
diff --git a/src/backend/utils/fmgr/Makefile.inc b/src/backend/utils/fmgr/Makefile.inc
new file mode 100644 (file)
index 0000000..283bba5
--- /dev/null
@@ -0,0 +1,15 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for utils/fmgr
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= dfmgr.c fmgr.c 
+
diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c
new file mode 100644 (file)
index 0000000..d2a1c2e
--- /dev/null
@@ -0,0 +1,269 @@
+/*-------------------------------------------------------------------------
+ *
+ * dfmgr.c--
+ *    Dynamic function manager code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "postgres.h"
+
+#include "fmgr.h"              /* generated by Gen_fmgrtab.sh */
+#include "utils/dynamic_loader.h"
+#include "utils/elog.h"
+#include "utils/builtins.h"
+#include "access/heapam.h"
+#include "nodes/pg_list.h"
+
+#include "port-protos.h"       /* system specific function prototypes */
+
+#include "catalog/catname.h"
+#include "utils/syscache.h"
+#include "catalog/pg_proc.h"
+
+static DynamicFileList *file_list = (DynamicFileList *) NULL;
+static DynamicFileList *file_tail = (DynamicFileList *) NULL;
+
+#define NOT_EQUAL(A, B) (((A).st_ino != (B).inode) \
+                      || ((A).st_dev != (B).device))
+
+static Oid procedureId_save = -1;
+static int pronargs_save;
+static func_ptr user_fn_save = (func_ptr) NULL;
+static func_ptr handle_load(char *filename, char *funcname);
+
+func_ptr
+fmgr_dynamic(Oid procedureId, int *pronargs)
+{
+    HeapTuple   procedureTuple;
+    Form_pg_proc procedureStruct;
+    char        *proname;
+    char        *probinattr, *probinstring;
+    func_ptr    user_fn, handle_load();
+    Relation    rdesc;
+    bool     isnull;
+    
+    if (procedureId == procedureId_save) {
+        *pronargs = pronargs_save;
+        return(user_fn_save);
+    }
+    
+    /*
+     * The procedure isn't a builtin, so we'll have to do a catalog
+     * lookup to find its pg_proc entry.
+     */
+    procedureTuple = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(procedureId),
+                                        0,0,0);
+    if (!HeapTupleIsValid(procedureTuple)) {
+        elog(WARN, "fmgr: Cache lookup failed for procedure %d\n",
+             procedureId);
+        return((func_ptr) NULL);
+    }
+    
+    procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
+    proname = procedureStruct->proname.data;
+    pronargs_save = *pronargs = procedureStruct->pronargs;
+    
+    /*
+     * Extract the procedure info from the pg_proc tuple.
+     * Since probin is varlena, do a amgetattr() on the procedure
+     * tuple.  To do that, we need the rdesc for the procedure
+     * relation, so...
+     */
+    
+    /* open pg_procedure */
+    
+    rdesc = heap_openr(ProcedureRelationName);
+    if (!RelationIsValid(rdesc)) {
+        elog(WARN, "fmgr: Could not open relation %s",
+             ProcedureRelationName);
+        return((func_ptr) NULL);
+    }
+    probinattr = heap_getattr(procedureTuple, (Buffer) 0,
+                             Anum_pg_proc_probin,
+                             RelationGetTupleDescriptor(rdesc), &isnull);
+    if (!PointerIsValid(probinattr) /*|| isnull*/) {
+        heap_close(rdesc);
+        elog(WARN, "fmgr: Could not extract probin for %d from %s",
+             procedureId, ProcedureRelationName);
+        return((func_ptr) NULL);
+    }
+    probinstring = textout((struct varlena *) probinattr);
+    
+    user_fn = handle_load(probinstring, proname);
+    
+    procedureId_save = procedureId;
+    user_fn_save = user_fn;
+    
+    return(user_fn);
+}
+
+static func_ptr
+handle_load(char *filename, char *funcname)
+{
+    DynamicFileList     *file_scanner = (DynamicFileList *) NULL;
+    func_ptr            retval = (func_ptr) NULL;
+    char                *load_error;
+#ifdef WIN32
+    struct _stat        stat_buf;
+#else
+    struct stat        stat_buf;
+#endif     /* WIN32 */
+
+    /*
+     * Do this because loading files may screw up the dynamic function
+     * manager otherwise.
+     */
+    procedureId_save = -1;
+
+    /*
+     * Scan the list of loaded FILES to see if the function
+     * has been loaded.  
+     */
+    
+    if (filename != (char *) NULL) {
+        for (file_scanner = file_list;
+             file_scanner != (DynamicFileList *) NULL
+             && file_scanner->filename != (char *) NULL
+             && strcmp(filename, file_scanner->filename) != 0;
+             file_scanner = file_scanner->next)
+           ;
+        if (file_scanner == (DynamicFileList *) NULL) {
+            if (stat(filename, &stat_buf) == -1) {
+                elog(WARN, "stat failed on file %s", filename);
+            }
+           
+            for (file_scanner = file_list;
+                 file_scanner != (DynamicFileList *) NULL
+                 && (NOT_EQUAL(stat_buf, *file_scanner));
+                 file_scanner = file_scanner->next)
+               ;
+            /*
+             * Same files - different paths (ie, symlink or link)
+             */
+            if (file_scanner != (DynamicFileList *) NULL)
+               (void) strcpy(file_scanner->filename, filename);
+           
+        }
+    } else {
+        file_scanner = (DynamicFileList *) NULL;
+    }
+    
+    /*
+     * File not loaded yet.
+     */
+    
+    if (file_scanner == (DynamicFileList *) NULL) {
+        if (file_list == (DynamicFileList *) NULL) {
+            file_list = (DynamicFileList *)
+               malloc(sizeof(DynamicFileList));
+            file_scanner = file_list;
+        } else {
+            file_tail->next = (DynamicFileList *)
+               malloc(sizeof(DynamicFileList));
+            file_scanner = file_tail->next;
+        }
+       memset((char *) file_scanner, 0, sizeof(DynamicFileList));
+       
+        (void) strcpy(file_scanner->filename, filename);
+#ifndef WIN32
+        file_scanner->device = stat_buf.st_dev;
+        file_scanner->inode = stat_buf.st_ino;
+#endif /* WIN32 */
+        file_scanner->next = (DynamicFileList *) NULL;
+       
+       file_scanner->handle = pg_dlopen(filename);
+        if (file_scanner->handle == (void *)NULL) {
+           load_error = pg_dlerror();
+            if (file_scanner == file_list) {
+                file_list = (DynamicFileList *) NULL;
+            } else {
+                file_tail->next = (DynamicFileList *) NULL;
+            }
+           
+            free((char *) file_scanner);
+            elog(WARN, "Load of file %s failed: %s", filename, load_error);
+        }
+       
+       /*
+        * Just load the file - we are done with that so return.
+        */
+        file_tail = file_scanner;
+       
+        if (funcname == (char *) NULL)
+           return((func_ptr) NULL);
+    }
+    
+    retval = pg_dlsym(file_scanner->handle, funcname);
+    
+    if (retval == (func_ptr) NULL) {
+       elog(WARN, "Can't find function %s in file %s", funcname, filename);
+    }
+    
+    return(retval);
+}
+
+/*
+ * This function loads files by the following:
+ *
+ * If the file is already loaded:
+ * o  Zero out that file's loaded space (so it doesn't screw up linking)
+ * o  Free all space associated with that file 
+ * o  Free that file's descriptor.
+ *
+ * Now load the file by calling handle_load with a NULL argument as the
+ * function.
+ */
+void
+load_file(char *filename)
+{
+    DynamicFileList *file_scanner, *p;
+#ifdef WIN32
+    struct _stat stat_buf;
+#else
+     struct stat stat_buf;
+#endif /* WIN32 */
+
+    int done = 0;
+    
+    if (stat(filename, &stat_buf) == -1) {
+        elog(WARN, "stat failed on file %s", filename);
+    }
+    
+    if (file_list != (DynamicFileList *) NULL
+       && !NOT_EQUAL(stat_buf, *file_list)) {
+        file_scanner = file_list;
+        file_list = file_list->next;
+       pg_dlclose(file_scanner->handle);
+        free((char *) file_scanner);
+    } else if (file_list != (DynamicFileList *) NULL) {
+        file_scanner = file_list;
+        while (!done) {
+            if (file_scanner->next == (DynamicFileList *) NULL) {
+                done = 1;
+            } else if (!NOT_EQUAL(stat_buf, *(file_scanner->next))) {
+                done = 1;
+            } else {
+                file_scanner = file_scanner->next;
+            }
+        }
+       
+        if (file_scanner->next != (DynamicFileList *) NULL) {
+            p = file_scanner->next;
+            file_scanner->next = file_scanner->next->next;
+           pg_dlclose(file_scanner->handle);
+            free((char *)p);
+        }
+    }
+    handle_load(filename, (char *) NULL);
+}
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
new file mode 100644 (file)
index 0000000..604e3af
--- /dev/null
@@ -0,0 +1,254 @@
+/*-------------------------------------------------------------------------
+ *
+ * fmgr.c--
+ *    Interface routines for the table-driven function manager.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "postgres.h"
+
+/* these 2 files are generated by Gen_fmgrtab.sh; contain the declarations */
+#include "fmgr.h"              
+#include "utils/fmgrtab.h"
+
+#include "nodes/pg_list.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_language.h"
+#include "utils/syscache.h"
+#include "nodes/params.h"
+
+#include "utils/elog.h"
+
+
+char *
+fmgr_c(func_ptr user_fn,
+       Oid func_id,
+       int n_arguments,
+       FmgrValues *values,
+       bool *isNull)           
+{
+    char       *returnValue = (char *) NULL;
+
+    
+    if (user_fn == (func_ptr) NULL) {
+       /*
+        * a NULL func_ptr denotes untrusted function (in postgres 4.2).
+        * Untrusted functions have very limited use and is clumsy. We
+        * just get rid of it.
+        */
+       elog(WARN, "internal error: untrusted function not supported.");
+    }
+    
+    switch (n_arguments) {
+    case 0:
+       returnValue = (*user_fn)();
+       break;
+    case 1:
+       /* NullValue() uses isNull to check if args[0] is NULL */
+       returnValue = (*user_fn)(values->data[0], isNull);
+       break;
+    case 2:
+       returnValue = (*user_fn)(values->data[0], values->data[1]);
+       break;
+    case 3:
+       returnValue = (*user_fn)(values->data[0], values->data[1],
+                                values->data[2]);
+       break;
+    case 4:
+       returnValue = (*user_fn)(values->data[0], values->data[1],
+                                values->data[2], values->data[3]);
+       break;
+    case 5:
+       returnValue = (*user_fn)(values->data[0], values->data[1],
+                                values->data[2], values->data[3],
+                                values->data[4]);
+       break;
+    case 6:
+       returnValue = (*user_fn)(values->data[0], values->data[1],
+                                values->data[2], values->data[3],
+                                values->data[4], values->data[5]);
+       break;
+    case 7:
+       returnValue = (*user_fn)(values->data[0], values->data[1],
+                                values->data[2], values->data[3],
+                                values->data[4], values->data[5],
+                                values->data[6]);
+       break;
+    case 8:
+       returnValue = (*user_fn)(values->data[0], values->data[1],
+                                values->data[2], values->data[3],
+                                values->data[4], values->data[5],
+                                values->data[6], values->data[7]);
+       break;
+    case 9:
+       /*
+        * XXX Note that functions with >8 arguments can only be
+        * called from inside the system, not from the user level,
+        * since the catalogs only store 8 argument types for user
+        * type-checking!
+        */
+       returnValue = (*user_fn)(values->data[0], values->data[1],
+                                values->data[2], values->data[3],
+                                values->data[4], values->data[5],
+                                values->data[6], values->data[7],
+                                values->data[8]);
+       break;
+    default:
+       elog(WARN, "fmgr_c: function %d: too many arguments (%d > %d)",
+            func_id, n_arguments, MAXFMGRARGS);
+       break;
+    }
+    return(returnValue);
+}
+
+void
+fmgr_info(Oid procedureId, func_ptr *function, int *nargs)
+{
+    func_ptr           user_fn;
+    FmgrCall           *fcp;
+    HeapTuple          procedureTuple;
+    FormData_pg_proc   *procedureStruct;
+    Oid                language;
+    
+    if (!(fcp = fmgr_isbuiltin(procedureId))) {
+       procedureTuple = SearchSysCacheTuple(PROOID,
+                                            ObjectIdGetDatum(procedureId),
+                                            0,0,0);
+       if (!HeapTupleIsValid(procedureTuple)) {
+           elog(WARN, "fmgr_info: function %d: cache lookup failed\n",
+                procedureId);
+       }
+       procedureStruct = (FormData_pg_proc *)
+           GETSTRUCT(procedureTuple);
+       if (!procedureStruct->proistrusted) {
+           *function = (func_ptr) NULL;
+           *nargs = procedureStruct->pronargs;
+           return;
+       }
+       language = procedureStruct->prolang;
+       switch (language) {
+       case INTERNALlanguageId:
+           user_fn = fmgr_lookupByName(procedureStruct->proname.data);
+           if (!user_fn)
+               elog(WARN, "fmgr_info: function %s: not in internal table",
+                    procedureStruct->proname.data);
+           break;
+       case ClanguageId:
+           user_fn = fmgr_dynamic(procedureId, nargs);
+           break;
+       case SQLlanguageId:
+           user_fn = (func_ptr) NULL;
+           *nargs = procedureStruct->pronargs;
+           break;
+       default:
+           elog(WARN, "fmgr_info: function %d: unknown language %d",
+                procedureId, language);
+       }
+    } else {
+       user_fn = fcp->func;
+       *nargs = fcp->nargs;
+    }
+    *function = user_fn;
+}
+
+/*
+ *     fmgr            - return the value of a function call
+ *
+ *     If the function is a system routine, it's compiled in, so call
+ *     it directly.
+ *
+ *      Otherwise pass it to the the appropriate 'language' function caller.
+ *
+ *     Returns the return value of the invoked function if succesful,
+ *     0 if unsuccessful.
+ */
+char *
+fmgr(Oid procedureId, ... )
+{
+    va_list            pvar;
+    register   i;
+    int                pronargs;
+    FmgrValues values;
+    func_ptr   user_fn;
+    bool               isNull = false;
+    
+    va_start(pvar, procedureId);
+    
+    fmgr_info(procedureId, &user_fn, &pronargs);
+    
+    if (pronargs > MAXFMGRARGS) {
+       elog(WARN, "fmgr: function %d: too many arguments (%d > %d)",
+            procedureId, pronargs, MAXFMGRARGS); 
+    }
+    for (i = 0; i < pronargs; ++i)
+       values.data[i] = va_arg(pvar, char *);
+    va_end(pvar);
+    
+    /* XXX see WAY_COOL_ORTHOGONAL_FUNCTIONS */
+    return(fmgr_c(user_fn, procedureId, pronargs, &values,
+                 &isNull));
+}
+
+/*
+ * This is just a version of fmgr() in which the hacker can prepend a C
+ * function pointer.  This routine is not normally called; generally,
+ * if you have all of this information you're likely to just jump through
+ * the pointer, but it's available for use with macros in fmgr.h if you
+ * want this routine to do sanity-checking for you.
+ * 
+ * func_ptr, func_id, n_arguments, args...
+ */
+char *
+fmgr_ptr(func_ptr user_fn, Oid func_id, ...)
+{
+    va_list            pvar;
+    register   i;
+    int                n_arguments;
+    FmgrValues values;
+    bool               isNull = false;
+    
+    va_start(pvar, func_id);
+    n_arguments = va_arg(pvar, int);
+    if (n_arguments > MAXFMGRARGS) {
+       elog(WARN, "fmgr_ptr: function %d: too many arguments (%d > %d)",
+            func_id, n_arguments, MAXFMGRARGS); 
+    }
+    for (i = 0; i < n_arguments; ++i)
+       values.data[i] = va_arg(pvar, char *);
+    va_end(pvar);
+    
+    /* XXX see WAY_COOL_ORTHOGONAL_FUNCTIONS */
+    return(fmgr_c(user_fn, func_id, n_arguments, &values,
+                 &isNull));
+}
+
+/*
+ * This routine is not well thought out.  When I get around to adding a
+ * function pointer field to FuncIndexInfo, it will be replace by calls
+ * to fmgr_c().
+ */
+char *
+fmgr_array_args(Oid procedureId, int nargs, char *args[], bool *isNull)
+{
+    func_ptr   user_fn;
+    int                true_arguments;
+    
+    fmgr_info(procedureId, &user_fn, &true_arguments);
+    
+    /* XXX see WAY_COOL_ORTHOGONAL_FUNCTIONS */
+    return
+       (fmgr_c(user_fn,
+               procedureId,
+               true_arguments,
+               (FmgrValues*)args,
+               isNull));
+}
diff --git a/src/backend/utils/fmgrtab.h b/src/backend/utils/fmgrtab.h
new file mode 100644 (file)
index 0000000..9c80a02
--- /dev/null
@@ -0,0 +1,29 @@
+/*-------------------------------------------------------------------------
+ *
+ * fmgrtab.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FMGRTAB_H
+#define FMGRTAB_H
+
+#include "postgres.h"          /* for ObjectId */
+#include "fmgr.h"              /* genearated by Gen_fmgrtab.sh */
+
+typedef struct {
+    Oid                proid;
+    uint16     nargs;
+    func_ptr   func;
+    char*       funcName;
+} FmgrCall;
+
+extern FmgrCall        *fmgr_isbuiltin(Oid id);
+extern func_ptr fmgr_lookupByName(char* name);
+
+#endif /* FMGRTAB_H */
diff --git a/src/backend/utils/geo-decls.h b/src/backend/utils/geo-decls.h
new file mode 100644 (file)
index 0000000..54000e2
--- /dev/null
@@ -0,0 +1,248 @@
+/*-------------------------------------------------------------------------
+ *
+ * geo-decls.h--
+ *    Declarations for various 2D constructs.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTE
+ *    These routines do *not* use the float types from adt/.
+ *
+ *    XXX These routines were not written by a numerical analyst.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        GEO_DECLS_H
+#define        GEO_DECLS_H
+
+/*#ifndef FmgrIncluded -- seems like always included. (it's FMgrIncluded) AY */
+
+/*--------------------------------------------------------------------
+ *     Useful floating point utilities and constants.
+ *-------------------------------------------------------------------*/
+
+#include <math.h>
+#include "c.h"
+
+#define        EPSILON                 1.0E-06
+
+#define        FPzero(A)               (fabs(A) <= EPSILON)
+#define        FPeq(A,B)               (fabs((A) - (B)) <= EPSILON)
+#define        FPlt(A,B)               ((B) - (A) > EPSILON)
+#define        FPle(A,B)               ((A) - (B) <= EPSILON)
+#define        FPgt(A,B)               ((A) - (B) > EPSILON)
+#define        FPge(A,B)               ((B) - (A) <= EPSILON)
+
+#define        HYPOT(A, B)             sqrt((A) * (A) + (B) * (B))
+
+/*--------------------------------------------------------------------
+ *     Memory management.
+ *-------------------------------------------------------------------*/
+
+#define        PALLOC(SIZE)            palloc(SIZE)
+#define        PFREE(P)                pfree(P)
+#define        PALLOCTYPE(TYPE)        (TYPE *) PALLOC(sizeof(TYPE))
+
+/*#endif !FmgrIncluded */
+
+/*---------------------------------------------------------------------
+ *     Point   -       (x,y)
+ *-------------------------------------------------------------------*/
+typedef struct {
+       double  x, y;
+} Point;
+
+
+/*---------------------------------------------------------------------
+ *     LSEG    -       A straight line, specified by endpoints.
+ *-------------------------------------------------------------------*/
+typedef        struct {
+       Point   p[2];
+
+       double  m;      /* precomputed to save time, not in tuple */
+} LSEG;
+
+
+/*---------------------------------------------------------------------
+ *     PATH    -       Specified by vertex points.
+ *-------------------------------------------------------------------*/
+typedef        struct {
+       int32   length; /* XXX varlena */
+       int32   npts;
+       int32   closed; /* is this a closed polygon? */
+       int32   dummy;  /* padding to make it double align */
+       Point   p[1];   /* variable length array of POINTs */
+} PATH;
+
+
+/*---------------------------------------------------------------------
+ *     LINE    -       Specified by its general equation (Ax+By+C=0).
+ *                     If there is a y-intercept, it is C, which
+ *                      incidentally gives a freebie point on the line
+ *                      (if B=0, then C is the x-intercept).
+ *                     Slope m is precalculated to save time; if
+ *                      the line is not vertical, m == A.
+ *-------------------------------------------------------------------*/
+typedef struct {
+       double  A, B, C;
+       double  m;
+} LINE;
+
+
+/*---------------------------------------------------------------------
+ *     BOX     -       Specified by two corner points, which are
+ *                      sorted to save calculation time later.
+ *-------------------------------------------------------------------*/
+typedef struct {
+       double  xh, yh, xl, yl;         /* high and low coords */
+} BOX;
+
+/*---------------------------------------------------------------------
+ *  POLYGON - Specified by an array of doubles defining the points, 
+ *                       keeping the number of points and the bounding box for 
+ *                       speed purposes.
+ *-------------------------------------------------------------------*/
+typedef struct {
+       int32 size;     /* XXX varlena */
+       int32 npts;
+       BOX boundbox;
+       char pts[1];
+} POLYGON;
+
+
+/* 
+ * in geo-ops.h
+ */
+extern BOX *box_in(char *str);
+extern char *box_out(BOX *box);
+extern BOX *box_construct(double x1, double x2, double y1, double y2);
+extern BOX *box_fill(BOX *result, double x1, double x2, double y1, double y2);
+extern BOX *box_copy(BOX *box);
+extern long box_same(BOX *box1, BOX *box2);
+extern long box_overlap(BOX *box1, BOX *box2);
+extern long box_overleft(BOX *box1, BOX *box2);
+extern long box_left(BOX *box1, BOX *box2);
+extern long box_right(BOX *box1, BOX *box2);
+extern long box_overright(BOX *box1, BOX *box2);
+extern long box_contained(BOX *box1, BOX *box2);
+extern long box_contain(BOX *box1, BOX *box2);
+extern long box_below(BOX *box1, BOX *box2);
+extern long box_above(BOX *box1, BOX *box2);
+extern long box_lt(BOX *box1, BOX *box2);
+extern long box_gt(BOX *box1, BOX *box2);
+extern long box_eq(BOX *box1, BOX *box2);
+extern long box_le(BOX *box1, BOX *box2);
+extern long box_ge(BOX *box1, BOX *box2);
+extern double *box_area(BOX *box);
+extern double *box_length(BOX *box);
+extern double *box_height(BOX *box);
+extern double *box_distance(BOX *box1, BOX *box2);
+extern Point *box_center(BOX *box);
+extern double box_ar(BOX *box);
+extern double box_ln(BOX *box);
+extern double box_ht(BOX *box);
+extern double box_dt(BOX *box1, BOX *box2);
+extern BOX *box_intersect(BOX *box1, BOX *box2);
+extern LSEG *box_diagonal(BOX *box);
+extern LINE *line_construct_pm(Point *pt, double m);
+extern LINE *line_construct_pp(Point *pt1, Point *pt2);
+extern long line_intersect(LINE *l1, LINE *l2);
+extern long line_parallel(LINE *l1, LINE *l2);
+extern long line_perp(LINE *l1, LINE *l2);
+extern long line_vertical(LINE *line);
+extern long line_horizontal(LINE *line);
+extern long line_eq(LINE *l1, LINE *l2);
+extern double *line_distance(LINE *l1, LINE *l2);
+extern Point *line_interpt(LINE *l1, LINE *l2);
+extern PATH *path_in(char *str);
+extern char *path_out(PATH *path);
+extern long path_n_lt(PATH *p1, PATH *p2);
+extern long path_n_gt(PATH *p1, PATH *p2);
+extern long path_n_eq(PATH *p1, PATH *p2);
+extern long path_n_le(PATH *p1, PATH *p2);
+extern long path_n_ge(PATH *p1, PATH *p2);
+extern long path_inter(PATH *p1, PATH *p2);
+extern double *path_distance(PATH *p1, PATH *p2);
+extern double *path_length(PATH *path);
+extern double path_ln(PATH *path);
+extern Point *point_in(char *str);
+extern char *point_out(Point *pt);
+extern Point *point_construct(double x, double y);
+extern Point *point_copy(Point *pt);
+extern long point_left(Point *pt1, Point *pt2);
+extern long point_right(Point *pt1, Point *pt2);
+extern long point_above(Point *pt1, Point *pt2);
+extern long point_below(Point *pt1, Point *pt2);
+extern long point_vert(Point *pt1, Point *pt2);
+extern long point_horiz(Point *pt1, Point *pt2);
+extern long point_eq(Point *pt1, Point *pt2);
+extern long pointdist(Point *p1, Point *p2);
+extern double *point_distance(Point *pt1, Point *pt2);
+extern double point_dt(Point *pt1, Point *pt2);
+extern double *point_slope(Point *pt1, Point *pt2);
+extern double point_sl(Point *pt1, Point *pt2);
+extern LSEG *lseg_in(char *str);
+extern char *lseg_out(LSEG *ls);
+extern LSEG *lseg_construct(Point *pt1, Point *pt2);
+extern void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2);
+extern long lseg_intersect(LSEG *l1, LSEG *l2);
+extern long lseg_parallel(LSEG *l1, LSEG *l2);
+extern long lseg_perp(LSEG *l1, LSEG *l2);
+extern long lseg_vertical(LSEG *lseg);
+extern long lseg_horizontal(LSEG *lseg);
+extern long lseg_eq(LSEG *l1, LSEG *l2);
+extern double *lseg_distance(LSEG *l1, LSEG *l2);
+extern double lseg_dt(LSEG *l1, LSEG *l2);
+extern Point *lseg_interpt(LSEG *l1, LSEG *l2);
+extern double *dist_pl(Point *pt, LINE *line);
+extern double *dist_ps(Point *pt, LSEG *lseg);
+extern double *dist_ppth(Point *pt, PATH *path);
+extern double *dist_pb(Point *pt, BOX *box);
+extern double *dist_sl(LSEG *lseg, LINE *line);
+extern double *dist_sb(LSEG *lseg, BOX *box);
+extern double *dist_lb(LINE *line, BOX *box);
+extern Point *interpt_sl(LSEG *lseg, LINE *line);
+extern Point *close_pl(Point *pt, LINE *line);
+extern Point *close_ps(Point *pt, LSEG *lseg);
+extern Point *close_pb(Point *pt, BOX *box);
+extern Point *close_sl(LSEG *lseg, LINE *line);
+extern Point *close_sb(LSEG *lseg, BOX *box);
+extern Point *close_lb(LINE *line, BOX *box);
+extern long on_pl(Point *pt, LINE *line);
+extern long on_ps(Point *pt, LSEG *lseg);
+extern long on_pb(Point *pt, BOX *box);
+extern long on_ppath(Point *pt, PATH *path);
+extern long on_sl(LSEG *lseg, LINE *line);
+extern long on_sb(LSEG *lseg, BOX *box);
+extern long inter_sl(LSEG *lseg, LINE *line);
+extern long inter_sb(LSEG *lseg, BOX *box);
+extern long inter_lb(LINE *line, BOX *box);
+extern void make_bound_box(POLYGON *poly);
+extern POLYGON *poly_in(char *s);
+extern long poly_pt_count(char *s, char delim);
+extern char *poly_out(POLYGON *poly);
+extern double poly_max(double *coords, int ncoords);
+extern double poly_min(double *coords, int ncoords);
+extern long poly_left(POLYGON *polya, POLYGON *polyb);
+extern long poly_overleft(POLYGON *polya, POLYGON *polyb);
+extern long poly_right(POLYGON *polya, POLYGON *polyb);
+extern long poly_overright(POLYGON *polya, POLYGON *polyb);
+extern long poly_same(POLYGON *polya, POLYGON *polyb);
+extern long poly_overlap(POLYGON *polya, POLYGON *polyb);
+extern long poly_contain(POLYGON *polya, POLYGON *polyb);
+extern long poly_contained(POLYGON *polya, POLYGON *polyb);
+
+/* geo-selfuncs.c */
+#if 0  /* FIX ME! */
+extern float64 areasel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag);
+extern float64 areajoinsel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag);
+extern float64 leftsel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag);
+extern float64 leftjoinsel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag);
+extern float64 contsel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag);
+extern float64 contjoinsel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag);
+#endif
+
+#endif /* GEO_DECLS_H */
diff --git a/src/backend/utils/hash/Makefile.inc b/src/backend/utils/hash/Makefile.inc
new file mode 100644 (file)
index 0000000..32bb3ef
--- /dev/null
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for utils/hash
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= dynahash.c hashfn.c
diff --git a/src/backend/utils/hash/dynahash.c b/src/backend/utils/hash/dynahash.c
new file mode 100644 (file)
index 0000000..d8598ea
--- /dev/null
@@ -0,0 +1,868 @@
+/*-------------------------------------------------------------------------
+ *
+ * dynahash.c--
+ *    dynamic hashing
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * 
+ * Dynamic hashing, after CACM April 1988 pp 446-457, by Per-Ake Larson.
+ * Coded into C, with minor code improvements, and with hsearch(3) interface,
+ * by [email protected], Jul 26, 1988: 13:16;
+ * also, hcreate/hdestroy routines added to simulate hsearch(3).
+ *
+ * These routines simulate hsearch(3) and family, with the important
+ * difference that the hash table is dynamic - can grow indefinitely
+ * beyond its original size (as supplied to hcreate()).
+ *
+ * Performance appears to be comparable to that of hsearch(3).
+ * The 'source-code' options referred to in hsearch(3)'s 'man' page
+ * are not implemented; otherwise functionality is identical.
+ *
+ * Compilation controls:
+ * DEBUG controls some informative traces, mainly for debugging.
+ * HASH_STATISTICS causes HashAccesses and HashCollisions to be maintained;
+ * when combined with HASH_DEBUG, these are displayed by hdestroy().
+ *
+ * Problems & fixes to [email protected]. WARNING: relies on pre-processor
+ * concatenation property, in probably unnecessary code 'optimisation'.
+ *
+ * Modified [email protected] February 1990
+ *     added multiple table interface
+ * Modified by [email protected] April 1990
+ *      changed ctl structure for shared memory
+ */
+# include      <stdio.h>
+# include      <sys/types.h>
+# include      <string.h>
+# include      "postgres.h"
+# include      "utils/hsearch.h"
+#ifndef FRONTEND
+# include      "nodes/memnodes.h"
+# include      "utils/mcxt.h"
+#endif /* !FRONTEND */
+# include      "utils/palloc.h"
+# include      "utils/elog.h"
+
+/*
+ * Fast arithmetic, relying on powers of 2,
+ * and on pre-processor concatenation property
+ */
+
+# define MOD(x,y)              ((x) & ((y)-1))
+
+/*
+ * external routines
+ */
+
+/*
+ * Private function prototypes
+ */
+static long *DynaHashAlloc(unsigned int size);
+static void DynaHashFree(Pointer ptr);
+static int hash_clear(HTAB *hashp);
+static uint32 call_hash(HTAB *hashp, char *k, int len);
+static SEG_OFFSET seg_alloc(HTAB *hashp);
+static int bucket_alloc(HTAB *hashp);
+static int dir_realloc(HTAB *hashp);
+
+typedef long * ((*dhalloc_ptr)());
+
+#ifndef FRONTEND
+/* ----------------
+ * memory allocation routines
+ *
+ * for postgres: all hash elements have to be in
+ * the global cache context.  Otherwise the postgres
+ * garbage collector is going to corrupt them. -wei
+ *
+ * ??? the "cache" memory context is intended to store only
+ *     system cache information.  The user of the hashing
+ *     routines should specify which context to use or we
+ *     should create a separate memory context for these
+ *     hash routines.  For now I have modified this code to
+ *     do the latter -cim 1/19/91
+ * ----------------
+ */
+GlobalMemory DynaHashCxt = (GlobalMemory) NULL;
+
+static long *
+DynaHashAlloc(unsigned int size)
+{
+    if (! DynaHashCxt)
+       DynaHashCxt = CreateGlobalMemory("DynaHash");
+
+    return (long  *)
+       MemoryContextAlloc((MemoryContext)DynaHashCxt, size);
+}
+
+static void
+DynaHashFree(Pointer ptr)
+{
+    MemoryContextFree((MemoryContext)DynaHashCxt, ptr);
+}
+
+#define MEM_ALLOC      DynaHashAlloc
+#define MEM_FREE       DynaHashFree
+
+#else /* FRONTEND */
+
+#define        MEM_ALLOC       palloc
+#define        MEM_FREE        pfree
+
+#endif /* FRONTEND */
+
+/* ----------------
+ * Internal routines
+ * ----------------
+ */
+
+static int expand_table();
+static int hdefault();
+static int init_htab();
+
+
+/*
+ * pointer access macros.  Shared memory implementation cannot
+ * store pointers in the hash table data structures because 
+ * pointer values will be different in different address spaces.
+ * these macros convert offsets to pointers and pointers to offsets.
+ * Shared memory need not be contiguous, but all addresses must be
+ * calculated relative to some offset (segbase).
+ */
+
+#define GET_SEG(hp,seg_num)\
+  (SEGMENT) (((unsigned long) (hp)->segbase) + (hp)->dir[seg_num])
+
+#define GET_BUCKET(hp,bucket_offs)\
+  (ELEMENT *) (((unsigned long) (hp)->segbase) + bucket_offs)
+
+#define MAKE_HASHOFFSET(hp,ptr)\
+  ( ((unsigned long) ptr) - ((unsigned long) (hp)->segbase) )
+
+# if HASH_STATISTICS
+static long hash_accesses, hash_collisions, hash_expansions;
+# endif
+
+/************************** CREATE ROUTINES **********************/
+
+HTAB *
+hash_create(int nelem, HASHCTL *info, int flags)
+{
+    register HHDR *    hctl;
+    HTAB *             hashp;
+    
+    
+    hashp = (HTAB *) MEM_ALLOC((unsigned long) sizeof(HTAB));
+    memset(hashp, 0, sizeof(HTAB)); 
+    
+    if ( flags & HASH_FUNCTION ) {
+       hashp->hash    = info->hash;
+    } else {
+       /* default */
+       hashp->hash      = string_hash;
+    }
+    
+    if ( flags & HASH_SHARED_MEM )  {
+       /* ctl structure is preallocated for shared memory tables */
+       
+       hashp->hctl     = (HHDR *) info->hctl;
+       hashp->segbase  = (char *) info->segbase; 
+       hashp->alloc    = info->alloc;
+       hashp->dir        = (SEG_OFFSET *)info->dir;
+       
+       /* hash table already exists, we're just attaching to it */
+       if (flags & HASH_ATTACH) {
+           return(hashp);
+       }
+       
+    } else {
+       /* setup hash table defaults */
+       
+       hashp->alloc      = (dhalloc_ptr) MEM_ALLOC;
+       hashp->dir        = NULL;
+       hashp->segbase  = NULL;
+       
+    }
+    
+    if (! hashp->hctl) {
+       hashp->hctl = (HHDR *) hashp->alloc((unsigned long)sizeof(HHDR));
+       if (! hashp->hctl) {
+           return(0);
+       }
+    }
+    
+    if ( !hdefault(hashp) ) return(0);
+    hctl = hashp->hctl;
+#ifdef HASH_STATISTICS
+    hctl->accesses = hctl->collisions = 0;
+#endif
+    
+    if ( flags & HASH_BUCKET )   {
+       hctl->bsize   = info->bsize;
+       hctl->bshift  = my_log2(info->bsize);
+    }
+    if ( flags & HASH_SEGMENT )  {
+       hctl->ssize   = info->ssize;
+       hctl->sshift  = my_log2(info->ssize);
+    }
+    if ( flags & HASH_FFACTOR )  {
+       hctl->ffactor = info->ffactor;
+    }
+    
+    /*
+     * SHM hash tables have fixed maximum size (allocate
+     * a maximal sized directory).
+     */
+    if ( flags & HASH_DIRSIZE )  {
+       hctl->max_dsize = my_log2(info->max_size);
+       hctl->dsize     = my_log2(info->dsize);
+    }
+    /* hash table now allocates space for key and data
+     * but you have to say how much space to allocate 
+     */
+    if ( flags & HASH_ELEM ) {
+       hctl->keysize    = info->keysize; 
+       hctl->datasize   = info->datasize;
+    }
+    if ( flags & HASH_ALLOC )  {
+       hashp->alloc = info->alloc;
+    }
+    
+    if ( init_htab (hashp, nelem ) ) {
+       hash_destroy(hashp);
+       return(0);
+    }
+    return(hashp);
+}
+
+/*
+  Allocate and initialize an HTAB structure 
+  */
+static int
+hdefault(HTAB *hashp)
+{
+    HHDR       *hctl;
+    
+    memset(hashp->hctl, 0, sizeof(HHDR)); 
+    
+    hctl = hashp->hctl;
+    hctl->bsize    = DEF_BUCKET_SIZE;
+    hctl->bshift   = DEF_BUCKET_SHIFT;
+    hctl->ssize    = DEF_SEGSIZE;
+    hctl->sshift   = DEF_SEGSIZE_SHIFT;
+    hctl->dsize    = DEF_DIRSIZE;
+    hctl->ffactor  = DEF_FFACTOR;
+    hctl->nkeys    = 0;
+    hctl->nsegs    = 0;
+    
+    /* I added these MS. */
+    
+    /* default memory allocation for hash buckets */
+    hctl->keysize       = sizeof(char *);
+    hctl->datasize = sizeof(char *);
+    
+    /* table has no fixed maximum size */
+    hctl->max_dsize = NO_MAX_DSIZE;
+    
+    /* garbage collection for HASH_REMOVE */
+    hctl->freeBucketIndex = INVALID_INDEX;
+    
+    return(1);
+}
+
+
+static int
+init_htab (HTAB *hashp, int nelem)
+{
+    register SEG_OFFSET        *segp;
+    register int nbuckets;
+    register int nsegs;
+    int        l2;
+    HHDR       *hctl;
+    
+    hctl = hashp->hctl;
+    /*
+     * Divide number of elements by the fill factor and determine a desired
+     * number of buckets.  Allocate space for the next greater power of
+     * two number of buckets
+     */
+    nelem = (nelem - 1) / hctl->ffactor + 1;
+    
+    l2 = my_log2(nelem);
+    nbuckets = 1 << l2;
+    
+    hctl->max_bucket = hctl->low_mask = nbuckets - 1;
+    hctl->high_mask = (nbuckets << 1) - 1;
+    
+    nsegs = (nbuckets - 1) / hctl->ssize + 1;
+    nsegs = 1 << my_log2(nsegs);
+    
+    if ( nsegs > hctl->dsize ) {
+       hctl->dsize  = nsegs;
+    }
+    
+    /* Use two low order bits of points ???? */
+    /*
+      if ( !(hctl->mem = bit_alloc ( nbuckets )) ) return(-1);
+      if ( !(hctl->mod = bit_alloc ( nbuckets )) ) return(-1);
+      */
+    
+    /* allocate a directory */
+    if (!(hashp->dir)) {
+       hashp->dir = 
+           (SEG_OFFSET *)hashp->alloc(hctl->dsize * sizeof(SEG_OFFSET));
+       if (! hashp->dir)
+           return(-1);
+    }
+    
+    /* Allocate initial segments */
+    for (segp = hashp->dir; hctl->nsegs < nsegs; hctl->nsegs++, segp++ ) {
+       *segp = seg_alloc(hashp);
+       if ( *segp == (SEG_OFFSET)0 ) {
+           hash_destroy(hashp);
+           return (0);
+       }
+    }
+    
+# if HASH_DEBUG
+    fprintf(stderr, "%s\n%s%x\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%x\n%s%x\n%s%d\n%s%d\n",
+           "init_htab:",
+           "TABLE POINTER   ", hashp,
+           "BUCKET SIZE     ", hctl->bsize,
+           "BUCKET SHIFT    ", hctl->bshift,
+           "DIRECTORY SIZE  ", hctl->dsize,
+           "SEGMENT SIZE    ", hctl->ssize,
+           "SEGMENT SHIFT   ", hctl->sshift,
+           "FILL FACTOR     ", hctl->ffactor,
+           "MAX BUCKET      ", hctl->max_bucket,
+           "HIGH MASK       ", hctl->high_mask,
+           "LOW  MASK       ", hctl->low_mask,
+           "NSEGS           ", hctl->nsegs,
+           "NKEYS           ", hctl->nkeys );
+# endif
+    return (0);
+}
+
+/********************** DESTROY ROUTINES ************************/
+
+static int
+hash_clear(HTAB *hashp)
+{
+    elog(NOTICE,"hash_clear not implemented\n");
+    return 0;
+}
+
+
+void
+hash_destroy (HTAB     *hashp)
+{
+    /* cannot destroy a shared memory hash table */
+    Assert(! hashp->segbase);
+    
+    if (hashp != NULL) {
+       register SEG_OFFSET     segNum;
+       SEGMENT         segp;
+       int                     nsegs = hashp->hctl->nsegs;
+       int             j;
+       BUCKET_INDEX    *elp,p,q;
+       ELEMENT         *curr;
+       
+       for (segNum =  0;  nsegs > 0; nsegs--, segNum++) {
+           
+           segp = GET_SEG(hashp,segNum);
+           for (j = 0, elp = segp; j < hashp->hctl->ssize; j++, elp++) {
+               for ( p = *elp; p != INVALID_INDEX; p = q ){
+                   curr = GET_BUCKET(hashp,p);
+                   q = curr->next;
+                   MEM_FREE((char *) curr);
+               }
+           }
+           free((char *)segp);
+       }
+       (void) MEM_FREE( (char *) hashp->dir);
+       (void) MEM_FREE( (char *) hashp->hctl);
+       hash_stats("destroy",hashp);
+       (void) MEM_FREE( (char *) hashp);
+    }
+}
+
+void
+hash_stats(char *where, HTAB *hashp)
+{
+# if HASH_STATISTICS
+    
+    fprintf(stderr,"%s: this HTAB -- accesses %ld collisions %ld\n",
+           where,hashp->hctl->accesses,hashp->hctl->collisions);
+    
+    fprintf(stderr,"hash_stats: keys %ld keysize %ld maxp %d segmentcount %d\n",
+           hashp->hctl->nkeys, hashp->hctl->keysize,
+           hashp->hctl->max_bucket, hashp->hctl->nsegs);
+    fprintf(stderr,"%s: total accesses %ld total collisions %ld\n",
+           where, hash_accesses, hash_collisions);
+    fprintf(stderr,"hash_stats: total expansions %ld\n",
+           hash_expansions);
+    
+# endif
+    
+}
+
+/*******************************SEARCH ROUTINES *****************************/
+
+static uint32
+call_hash(HTAB *hashp, char *k, int len)
+{
+    long     hash_val, bucket;
+    HHDR       *hctl;
+    
+    hctl = hashp->hctl;
+    hash_val = hashp->hash(k, len);
+    
+    bucket = hash_val & hctl->high_mask;
+    if ( bucket > hctl->max_bucket ) {
+       bucket = bucket & hctl->low_mask;
+    }
+    
+    return(bucket);
+}
+
+/*
+ * hash_search -- look up key in table and perform action
+ *
+ * action is one of HASH_FIND/HASH_ENTER/HASH_REMOVE
+ *
+ * RETURNS: NULL if table is corrupted, a pointer to the element
+ *     found/removed/entered if applicable, TRUE otherwise.
+ *     foundPtr is TRUE if we found an element in the table 
+ *     (FALSE if we entered one).
+ */
+long *
+hash_search(HTAB *hashp,
+           char *keyPtr, 
+           HASHACTION action,  /*
+                                * HASH_FIND / HASH_ENTER / HASH_REMOVE
+                                * HASH_FIND_SAVE / HASH_REMOVE_SAVED
+                                */
+           bool *foundPtr)
+{
+    uint32 bucket;
+    long segment_num;
+    long segment_ndx;
+    SEGMENT segp;
+    register ELEMENT *curr;
+    HHDR       *hctl;
+    BUCKET_INDEX       currIndex;
+    BUCKET_INDEX       *prevIndexPtr;
+    char *             destAddr;
+    static struct State {
+       ELEMENT      *currElem;
+       BUCKET_INDEX currIndex;
+       BUCKET_INDEX *prevIndex;
+    } saveState;
+    
+    Assert((hashp && keyPtr));
+    Assert((action == HASH_FIND) || (action == HASH_REMOVE) || (action == HASH_ENTER) || (action == HASH_FIND_SAVE) || (action == HASH_REMOVE_SAVED));
+    
+    hctl = hashp->hctl;
+    
+# if HASH_STATISTICS
+    hash_accesses++;
+    hashp->hctl->accesses++;
+# endif
+    if (action == HASH_REMOVE_SAVED)
+       {
+           curr = saveState.currElem;
+           currIndex = saveState.currIndex;
+           prevIndexPtr = saveState.prevIndex;
+           /*
+            * Try to catch subsequent errors
+            */
+           Assert(saveState.currElem && !(saveState.currElem = 0));
+       }
+    else
+       {
+           bucket = call_hash(hashp, keyPtr, hctl->keysize);
+           segment_num = bucket >> hctl->sshift;
+           segment_ndx = bucket & ( hctl->ssize - 1 );
+           
+           segp = GET_SEG(hashp,segment_num);
+           
+           Assert(segp);
+           
+           prevIndexPtr = &segp[segment_ndx];
+           currIndex = *prevIndexPtr;
+           /*
+            * Follow collision chain
+            */
+           for (curr = NULL;currIndex != INVALID_INDEX;) {
+               /* coerce bucket index into a pointer */
+               curr = GET_BUCKET(hashp,currIndex);
+               
+               if (! memcmp((char *)&(curr->key), keyPtr, hctl->keysize)) {
+                   break;
+               } 
+               prevIndexPtr = &(curr->next);
+               currIndex = *prevIndexPtr;
+# if HASH_STATISTICS
+               hash_collisions++;
+               hashp->hctl->collisions++;
+# endif
+           }
+       }
+    
+    /*
+     *  if we found an entry or if we weren't trying
+     * to insert, we're done now.
+     */
+    *foundPtr = (bool) (currIndex != INVALID_INDEX);
+    switch (action) {
+    case HASH_ENTER:
+       if (currIndex != INVALID_INDEX)
+           return(&(curr->key));
+       break;
+    case HASH_REMOVE:
+    case HASH_REMOVE_SAVED:
+       if (currIndex != INVALID_INDEX) {
+           Assert(hctl->nkeys > 0);
+           hctl->nkeys--;
+           
+           /* add the bucket to the freelist for this table.  */
+           *prevIndexPtr = curr->next;
+           curr->next = hctl->freeBucketIndex;
+           hctl->freeBucketIndex = currIndex;
+           
+           /* better hope the caller is synchronizing access to
+            * this element, because someone else is going to reuse
+            * it the next time something is added to the table
+            */
+           return (&(curr->key));
+       }
+       return((long *) TRUE);
+    case HASH_FIND:
+       if (currIndex != INVALID_INDEX)
+           return(&(curr->key));
+       return((long *)TRUE);
+    case HASH_FIND_SAVE:
+       if (currIndex != INVALID_INDEX)
+           {
+               saveState.currElem = curr;
+               saveState.prevIndex = prevIndexPtr;
+               saveState.currIndex = currIndex;
+               return(&(curr->key));
+           }
+       return((long *)TRUE);
+    default:
+       /* can't get here */
+       return (NULL);
+    }
+    
+    /* 
+      If we got here, then we didn't find the element and
+      we have to insert it into the hash table
+      */
+    Assert(currIndex == INVALID_INDEX);
+    
+    /* get the next free bucket */
+    currIndex = hctl->freeBucketIndex;
+    if (currIndex == INVALID_INDEX) {
+       
+       /* no free elements.  allocate another chunk of buckets */
+       if (! bucket_alloc(hashp)) {
+           return(NULL);
+       }
+       currIndex = hctl->freeBucketIndex;
+    }
+    Assert(currIndex != INVALID_INDEX);
+    
+    curr = GET_BUCKET(hashp,currIndex);
+    hctl->freeBucketIndex = curr->next;
+    
+    /* link into chain */
+    *prevIndexPtr = currIndex; 
+    
+    /* copy key and data */
+    destAddr = (char *) &(curr->key);
+    memmove(destAddr,keyPtr,hctl->keysize);
+    curr->next = INVALID_INDEX;
+    
+    /* let the caller initialize the data field after 
+     * hash_search returns.
+     */
+    /* memmove(destAddr,keyPtr,hctl->keysize+hctl->datasize);*/
+    
+    /*
+     * Check if it is time to split the segment
+     */
+    if (++hctl->nkeys / (hctl->max_bucket+1) > hctl->ffactor) {
+       /*
+         fprintf(stderr,"expanding on '%s'\n",keyPtr);
+         hash_stats("expanded table",hashp);
+         */
+       if (! expand_table(hashp))
+           return(NULL);
+    }
+    return (&(curr->key));
+}
+
+/*
+ * hash_seq -- sequentially search through hash table and return
+ *             all the elements one by one, return NULL on error and
+ *            return TRUE in the end.
+ *
+ */
+long *
+hash_seq(HTAB *hashp)
+{
+    static uint32 curBucket = 0;
+    static BUCKET_INDEX curIndex;
+    ELEMENT *curElem;
+    long segment_num;
+    long segment_ndx;
+    SEGMENT segp;
+    HHDR *hctl;
+    
+    if (hashp == NULL)
+       {
+           /*
+            * reset static state
+            */
+           curBucket = 0;
+           curIndex = INVALID_INDEX;
+           return((long *) NULL);
+       }
+    
+    hctl = hashp->hctl;
+    while (curBucket <= hctl->max_bucket) {
+       if (curIndex != INVALID_INDEX) {
+           curElem = GET_BUCKET(hashp, curIndex);
+           curIndex = curElem->next;
+           if (curIndex == INVALID_INDEX)      /* end of this bucket */
+               ++curBucket;
+           return(&(curElem->key));
+       }
+       
+       /*
+        * initialize the search within this bucket.
+        */
+       segment_num = curBucket >> hctl->sshift;
+       segment_ndx = curBucket & ( hctl->ssize - 1 );
+       
+       /*
+        * first find the right segment in the table directory.
+        */
+       segp = GET_SEG(hashp, segment_num);
+       if (segp == NULL)
+           /* this is probably an error */
+           return((long *) NULL);
+       
+       /*
+        * now find the right index into the segment for the first
+        * item in this bucket's chain.  if the bucket is not empty
+        * (its entry in the dir is valid), we know this must
+        * correspond to a valid element and not a freed element
+        * because it came out of the directory of valid stuff.  if
+        * there are elements in the bucket chains that point to the
+        * freelist we're in big trouble.
+        */
+       curIndex = segp[segment_ndx];
+       
+       if (curIndex == INVALID_INDEX)          /* empty bucket */
+           ++curBucket;
+    }
+    
+    return((long *) TRUE);                     /* out of buckets */
+}
+
+
+/********************************* UTILITIES ************************/
+static int
+expand_table(HTAB *hashp)
+{
+    HHDR       *hctl;
+    SEGMENT    old_seg,new_seg;
+    long       old_bucket, new_bucket;
+    long       new_segnum, new_segndx;
+    long       old_segnum, old_segndx;
+    ELEMENT    *chain;
+    BUCKET_INDEX *old,*newbi;
+    register BUCKET_INDEX chainIndex,nextIndex;
+    
+#ifdef HASH_STATISTICS
+    hash_expansions++;
+#endif
+    
+    hctl = hashp->hctl;
+    new_bucket = ++hctl->max_bucket;
+    old_bucket = (hctl->max_bucket & hctl->low_mask);
+    
+    new_segnum = new_bucket >> hctl->sshift;
+    new_segndx = MOD ( new_bucket, hctl->ssize );
+    
+    if ( new_segnum >= hctl->nsegs ) {
+       
+       /* Allocate new segment if necessary */
+       if (new_segnum >= hctl->dsize) {
+           dir_realloc(hashp);
+       }
+       if (! (hashp->dir[new_segnum] = seg_alloc(hashp))) {
+           return (0);
+       }
+       hctl->nsegs++;
+    }
+    
+    
+    if ( new_bucket > hctl->high_mask ) {
+       /* Starting a new doubling */
+       hctl->low_mask = hctl->high_mask;
+       hctl->high_mask = new_bucket | hctl->low_mask;
+    }
+    
+    /*
+     * Relocate records to the new bucket
+     */
+    old_segnum = old_bucket >> hctl->sshift;
+    old_segndx = MOD(old_bucket, hctl->ssize);
+    
+    old_seg = GET_SEG(hashp,old_segnum);
+    new_seg = GET_SEG(hashp,new_segnum);
+    
+    old = &old_seg[old_segndx];
+    newbi = &new_seg[new_segndx];
+    for (chainIndex = *old; 
+        chainIndex != INVALID_INDEX;
+        chainIndex = nextIndex){
+       
+       chain = GET_BUCKET(hashp,chainIndex);
+       nextIndex = chain->next;
+       if ( call_hash(hashp,
+                      (char *)&(chain->key),
+                      hctl->keysize) == old_bucket ) {
+           *old = chainIndex;
+           old = &chain->next;
+       } else {
+           *newbi = chainIndex;
+           newbi = &chain->next;
+       }
+       chain->next = INVALID_INDEX;
+    }
+    return (1);
+}
+
+
+static int
+dir_realloc(HTAB *hashp)
+{
+    register char      *p;
+    char       **p_ptr;
+    long       old_dirsize;
+    long       new_dirsize;
+    
+    
+    if (hashp->hctl->max_dsize != NO_MAX_DSIZE) 
+       return (0);
+    
+    /* Reallocate directory */
+    old_dirsize = hashp->hctl->dsize * sizeof ( SEGMENT * );
+    new_dirsize = old_dirsize << 1;
+    
+    p_ptr = (char **) hashp->dir;
+    p = (char *) hashp->alloc((unsigned long) new_dirsize );
+    if (p != NULL) {
+       memmove(p, *p_ptr, old_dirsize );
+       memset ( *p_ptr + old_dirsize, 0, new_dirsize-old_dirsize );
+       (void) free( (char *)*p_ptr);
+       *p_ptr = p;
+       hashp->hctl->dsize = new_dirsize;
+       return(1);
+    } 
+    return (0);
+    
+}
+
+
+static SEG_OFFSET
+seg_alloc(HTAB * hashp)
+{
+    SEGMENT segp;
+    SEG_OFFSET segOffset;
+    
+    
+    segp = (SEGMENT) hashp->alloc((unsigned long) 
+                                 sizeof(SEGMENT)*hashp->hctl->ssize);
+    
+    if (! segp) {
+       return(0);
+    }
+    
+    memset((char *)segp, 0,
+         (long) sizeof(SEGMENT)*hashp->hctl->ssize);
+    
+    segOffset = MAKE_HASHOFFSET(hashp,segp);
+    return(segOffset);
+}
+
+/*
+ * allocate some new buckets and link them into the free list
+ */
+static int
+bucket_alloc(HTAB *hashp)
+{
+    int i;
+    ELEMENT *tmpBucket;
+    long bucketSize;
+    BUCKET_INDEX tmpIndex,lastIndex;
+    
+    bucketSize = 
+       sizeof(BUCKET_INDEX) + hashp->hctl->keysize + hashp->hctl->datasize;
+    
+    /* make sure its aligned correctly */
+    bucketSize += sizeof(long *) - (bucketSize % sizeof(long *));
+    
+    /* tmpIndex is the shmem offset into the first bucket of
+     * the array.
+     */
+    tmpBucket = (ELEMENT *)
+       hashp->alloc((unsigned long) BUCKET_ALLOC_INCR*bucketSize);
+    
+    if (! tmpBucket) {
+       return(0);
+    }
+    
+    tmpIndex = MAKE_HASHOFFSET(hashp,tmpBucket);
+    
+    /* set the freebucket list to point to the first bucket */
+    lastIndex = hashp->hctl->freeBucketIndex;
+    hashp->hctl->freeBucketIndex = tmpIndex;
+    
+    /* initialize each bucket to point to the one behind it */
+    for (i=0;i<(BUCKET_ALLOC_INCR-1);i++) {
+       tmpBucket = GET_BUCKET(hashp,tmpIndex);
+       tmpIndex += bucketSize;
+       tmpBucket->next = tmpIndex;
+    }
+    
+    /* the last bucket points to the old freelist head (which is
+     * probably invalid or we wouldnt be here) 
+     */
+    tmpBucket->next = lastIndex;
+    
+    return(1);
+}
+
+/* calculate the log base 2 of num */
+int
+my_log2(long num)
+{
+    int                i = 1;
+    int                limit;
+    
+    for ( i = 0, limit = 1; limit < num; limit = 2 * limit, i++ );
+    return (i);
+}
diff --git a/src/backend/utils/hash/hashfn.c b/src/backend/utils/hash/hashfn.c
new file mode 100644 (file)
index 0000000..76efb82
--- /dev/null
@@ -0,0 +1,156 @@
+/*-------------------------------------------------------------------------
+ *
+ * hashfn.c--
+ *    
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include "utils/hsearch.h"
+
+/*
+ * Assume that we've already split the bucket to which this
+ * key hashes, calculate that bucket, and check that in fact
+ * we did already split it.
+ */
+long
+string_hash(char *key, int keysize)
+{
+    int h;
+    register unsigned char *k = (unsigned char *) key;
+    
+    h = 0;
+    /*
+     * Convert string to integer
+     */
+    while (*k)
+       h = h * PRIME1 ^ (*k++ - ' ');
+    h %= PRIME2;
+    
+    return (h);
+}
+
+
+long
+tag_hash(int *key, int keysize)
+{
+    register long h = 0;
+    
+    /*
+     * Convert tag to integer;  Use four byte chunks in a "jump table"
+     * to go a little faster.  Currently the maximum keysize is 16
+     * (mar 17 1992) I have put in cases for up to 24.  Bigger than
+     * this will resort to the old behavior of the for loop. (see the
+     * default case).
+     */
+    switch (keysize)
+       {
+       case 6*sizeof(int):
+           h = h * PRIME1 ^ (*key);
+           key++;
+           /* fall through */
+           
+       case 5*sizeof(int):
+           h = h * PRIME1 ^ (*key);
+           key++;
+           /* fall through */
+           
+       case 4*sizeof(int):
+           h = h * PRIME1 ^ (*key);
+           key++;
+           /* fall through */
+           
+       case 3*sizeof(int):
+           h = h * PRIME1 ^ (*key);
+           key++;
+           /* fall through */
+           
+       case 2*sizeof(int):
+           h = h * PRIME1 ^ (*key);
+           key++;
+           /* fall through */
+           
+       case sizeof(int):
+           h = h * PRIME1 ^ (*key);
+           key++;
+           break;
+           
+       default:
+           for(; keysize > (sizeof(int)-1); keysize -= sizeof(int), key++)
+               h = h * PRIME1 ^ (*key);
+           /*
+            * now let's grab the last few bytes of the tag if the tag
+            * has (size % 4) != 0 (which it sometimes will on a sun3).
+            */
+           if (keysize)
+               {
+                   char *keytmp = (char *)key;
+                   
+                   switch (keysize)
+                       {
+                       case 3:
+                           h = h * PRIME1 ^ (*keytmp);
+                           keytmp++;
+                           /* fall through */
+                       case 2:
+                           h = h * PRIME1 ^ (*keytmp);
+                           keytmp++;
+                           /* fall through */
+                       case 1:
+                           h = h * PRIME1 ^ (*keytmp);
+                           break;
+                       }
+               }
+           break;
+       }
+    
+    h %= PRIME2;
+    return (h);
+}
+
+/*
+ * This is INCREDIBLY ugly, but fast.
+ * We break the string up into 8 byte units.  On the first time
+ * through the loop we get the "leftover bytes" (strlen % 8).
+ * On every other iteration, we perform 8 HASHC's so we handle
+ * all 8 bytes.  Essentially, this saves us 7 cmp & branch
+ * instructions.  If this routine is heavily used enough, it's
+ * worth the ugly coding
+ */
+long
+disk_hash(char *key)
+{
+    register int n = 0;
+    register char *str = key;
+    register int len = strlen(key);
+    register int loop;
+    
+#define HASHC   n = *str++ + 65599 * n
+    
+    if (len > 0) {
+       loop = (len + 8 - 1) >> 3;
+       
+       switch(len & (8 - 1)) {
+       case 0: do {            /* All fall throughs */
+           HASHC;  
+       case 7: HASHC;
+       case 6: HASHC;  
+       case 5: HASHC;
+       case 4: HASHC;  
+       case 3: HASHC;
+       case 2: HASHC;  
+       case 1: HASHC;
+       } while (--loop);
+       }
+       
+    }
+    return(n);
+}
+
+
diff --git a/src/backend/utils/hsearch.h b/src/backend/utils/hsearch.h
new file mode 100644 (file)
index 0000000..12f6611
--- /dev/null
@@ -0,0 +1,141 @@
+/*-------------------------------------------------------------------------
+ *
+ * hsearch.h--
+ *    for hashing in the new buffer manager
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef HSEARCH_H
+#define HSEARCH_H
+
+#include "postgres.h"
+
+/*
+ * Constants
+ */
+# define DEF_BUCKET_SIZE       256
+# define DEF_BUCKET_SHIFT      8       /* log2(BUCKET) */
+# define DEF_SEGSIZE           256
+# define DEF_SEGSIZE_SHIFT             8      /* log2(SEGSIZE)  */
+# define DEF_DIRSIZE           256
+# define PRIME1                        37
+# define PRIME2                        1048583
+# define DEF_FFACTOR           1
+# define SPLTMAX               8
+
+
+/*
+ * Hash bucket is actually bigger than this.  Key field can have
+ * variable length and a variable length data field follows it.
+ */
+typedef struct element {
+    unsigned long next;                /* secret from user      */
+    long key;
+} ELEMENT;
+
+typedef unsigned long BUCKET_INDEX;
+/* segment is an array of bucket pointers  */
+typedef BUCKET_INDEX *SEGMENT;
+typedef unsigned long SEG_OFFSET;
+
+typedef struct hashhdr {
+    long bsize;                        /* Bucket/Page Size */
+    long bshift;               /* Bucket shift */
+    long dsize;                        /* Directory Size */
+    long ssize;                        /* Segment Size */
+    long sshift;               /* Segment shift */
+    long max_bucket;           /* ID of Maximum bucket in use */
+    long high_mask;            /* Mask to modulo into entire table */
+    long low_mask;             /* Mask to modulo into lower half of table */
+    long ffactor;              /* Fill factor */
+    long nkeys;                        /* Number of keys in hash table */
+    long nsegs;                        /* Number of allocated segments */
+    long keysize;              /* hash key length in bytes */
+    long datasize;             /* elem data length in bytes */
+    long max_dsize;            /* 'dsize' limit if directory is fixed size */ 
+    BUCKET_INDEX freeBucketIndex;
+    /* index of first free bucket */
+#ifdef HASH_STATISTICS
+    long accesses;
+    long collisions;
+#endif
+} HHDR;
+
+typedef struct htab {
+    HHDR       *hctl;          /* shared control information */
+    long       (*hash)();      /* Hash Function */
+    char       *segbase;       /* segment base address for 
+                                * calculating pointer values 
+                                */
+    SEG_OFFSET *dir;           /* 'directory' of segm starts */
+    long       *(*alloc)();    /* memory allocator 
+                                * (long * for alignment reasons)
+                                */
+
+} HTAB;
+
+typedef struct hashctl {
+    long bsize;                /* Bucket Size */
+    long ssize;                /* Segment Size */
+    long dsize;                /* Dirsize Size */
+    long ffactor;      /* Fill factor */
+    long (*hash)();    /* Hash Function */
+    long keysize;      /* hash key length in bytes */
+    long datasize;     /* elem data length in bytes */
+    long max_size;     /* limit to dsize if directory size is limited */
+    long *segbase;     /* base for calculating bucket + seg ptrs */
+    long * (*alloc)(); /* memory allocation function */
+    long *dir;         /* directory if allocated already */
+    long *hctl;                /* location of header information in shd mem */
+} HASHCTL;
+
+/* Flags to indicate action for hctl */
+#define HASH_BUCKET    0x001   /* Setting bucket size */
+#define HASH_SEGMENT   0x002   /* Setting segment size */
+#define HASH_DIRSIZE   0x004   /* Setting directory size */
+#define HASH_FFACTOR   0x008   /* Setting fill factor */
+#define HASH_FUNCTION  0x010   /* Set user defined hash function */
+#define HASH_ELEM      0x020   /* Setting key/data size */
+#define HASH_SHARED_MEM 0x040   /* Setting shared mem const */
+#define HASH_ATTACH    0x080   /* Do not initialize hctl */
+#define HASH_ALLOC     0x100   /* Setting memory allocator */ 
+
+
+/* seg_alloc assumes that INVALID_INDEX is 0*/
+#define INVALID_INDEX          (0)
+#define NO_MAX_DSIZE           (-1)
+/* number of hash buckets allocated at once */
+#define BUCKET_ALLOC_INCR      (30)
+
+/* hash_search operations */
+typedef enum { 
+    HASH_FIND, 
+    HASH_ENTER, 
+    HASH_REMOVE, 
+    HASH_FIND_SAVE, 
+    HASH_REMOVE_SAVED 
+} HASHACTION;
+
+/* 
+ * prototypes from functions in dynahash.c
+ */
+extern HTAB *hash_create(int nelem, HASHCTL *info, int flags);
+extern void hash_destroy(HTAB *hashp);
+extern void hash_stats(char *where, HTAB *hashp);
+extern long *hash_search(HTAB *hashp, char *keyPtr, HASHACTION action,
+                        bool *foundPtr);
+extern long *hash_seq(HTAB *hashp);
+
+/* 
+ * prototypes from functions in hashfn.c
+ */
+extern long string_hash(char *key, int keysize);
+extern long tag_hash(int *key, int keysize);
+extern long disk_hash(char *key);
+
+#endif /* HSEARCH_H */
diff --git a/src/backend/utils/init/Makefile.inc b/src/backend/utils/init/Makefile.inc
new file mode 100644 (file)
index 0000000..22d9cdc
--- /dev/null
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for utils/init
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= enbl.c findbe.c globals.c magic.c miscinit.c postinit.c
diff --git a/src/backend/utils/init/enbl.c b/src/backend/utils/init/enbl.c
new file mode 100644 (file)
index 0000000..4acbe73
--- /dev/null
@@ -0,0 +1,45 @@
+/*-------------------------------------------------------------------------
+ *
+ * enbl.c--
+ *    POSTGRES module enable and disable support code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+#include "utils/module.h"              /* where the declarations go */
+
+/*
+ * BypassEnable --
+ *     False iff enable/disable processing is required given on and "*countP."
+ *
+ * Note:
+ *     As a side-effect, *countP is modified.  It should be 0 initially.
+ *
+ * Exceptions:
+ *     BadState if called with pointer to value 0 and false.
+ *     BadArg if "countP" is invalid pointer.
+ *     BadArg if on is invalid.
+ */
+bool
+BypassEnable(int *enableCountInOutP, bool on)
+{
+       AssertArg(PointerIsValid(enableCountInOutP));
+       AssertArg(BoolIsValid(on));
+
+       if (on) {
+               *enableCountInOutP += 1;
+               return ((bool)(*enableCountInOutP >= 2));
+       }
+
+       AssertState(*enableCountInOutP >= 1);
+
+       *enableCountInOutP -= 1;
+
+       return ((bool)(*enableCountInOutP >= 1));
+}
diff --git a/src/backend/utils/init/findbe.c b/src/backend/utils/init/findbe.c
new file mode 100644 (file)
index 0000000..c2a1ede
--- /dev/null
@@ -0,0 +1,251 @@
+/*-------------------------------------------------------------------------
+ *
+ * findbe.c --
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#ifndef WIN32
+#include <grp.h>
+#else
+#include <windows.h>
+#endif /* WIN32 */
+#include <pwd.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "c.h"
+#include "miscadmin.h" /* for DebugLvl */
+
+#ifndef S_IRUSR                        /* XXX [TRH] should be in a header */
+#   define S_IRUSR     S_IREAD
+#   define S_IWUSR     S_IWRITE
+#   define S_IXUSR     S_IEXEC
+#   define S_IRGRP     ((S_IRUSR)>>3)
+#   define S_IWGRP     ((S_IWUSR)>>3)
+#   define S_IXGRP     ((S_IXUSR)>>3)
+#   define S_IROTH     ((S_IRUSR)>>6)
+#   define S_IWOTH     ((S_IWUSR)>>6)
+#   define S_IXOTH     ((S_IXUSR)>>6)
+#endif
+
+/*
+ * ValidateBackend -- validate "path" as a POSTGRES executable file
+ *
+ * returns 0 if the file is found and no error is encountered.
+ *       -1 if the regular file "path" does not exist or cannot be executed.
+ *       -2 if the file is otherwise valid but cannot be read.
+ */
+int
+ValidateBackend(char *path)
+{
+#ifndef WIN32
+    struct stat                buf;
+    uid_t              euid;
+    struct group       *gp;
+    struct passwd      *pwp;
+    int                        i;
+    int                        is_r = 0;
+    int                        is_x = 0;
+    int                        in_grp = 0;
+#else
+    DWORD file_attributes;
+#endif /* WIN32 */
+    
+    /*
+     * Ensure that the file exists and is a regular file.
+     *
+     * XXX if you have a broken system where stat() looks at the symlink
+     *     instead of the underlying file, you lose.
+     */
+    if (strlen(path) >= MAXPGPATH) {
+       if (DebugLvl > 1)
+           fprintf(stderr, "ValidateBackend: pathname \"%s\" is too long\n",
+                   path);
+       return(-1);
+    }
+
+#ifndef WIN32
+    if (stat(path, &buf) < 0) {
+       if (DebugLvl > 1)
+           fprintf(stderr, "ValidateBackend: can't stat \"%s\"\n",
+                   path);
+       return(-1);
+    }
+    if (!(buf.st_mode & S_IFREG)) {
+       if (DebugLvl > 1)
+           fprintf(stderr, "ValidateBackend: \"%s\" is not a regular file\n",
+                   path);
+       return(-1);
+    }
+    
+    /*
+     * Ensure that we are using an authorized backend. 
+     *
+     * XXX I'm open to suggestions here.  I would like to enforce ownership
+     *     of backends by user "postgres" but people seem to like to run
+     *     as users other than "postgres"...
+     */
+    
+    /*
+     * Ensure that the file is both executable and readable (required for
+     * dynamic loading).
+     *
+     * We use the effective uid here because the backend will not have
+     * executed setuid() by the time it calls this routine.
+     */
+    euid = geteuid();
+    if (euid == buf.st_uid) {
+       is_r = buf.st_mode & S_IRUSR;
+       is_x = buf.st_mode & S_IXUSR;
+       if (DebugLvl > 1 && !(is_r && is_x))
+           fprintf(stderr, "ValidateBackend: \"%s\" is not user read/execute\n",
+                   path);
+       return(is_x ? (is_r ? 0 : -2) : -1);
+    }
+    pwp = getpwuid(euid);
+    if (pwp) {
+       if (pwp->pw_gid == buf.st_gid) {
+           ++in_grp;
+       } else if (pwp->pw_name &&
+                  (gp = getgrgid(buf.st_gid))) {
+           for (i = 0; gp->gr_mem[i]; ++i) {
+               if (!strcmp(gp->gr_mem[i], pwp->pw_name)) {
+                   ++in_grp;
+                   break;
+               }
+           }
+       }
+       if (in_grp) {
+           is_r = buf.st_mode & S_IRGRP;
+           is_x = buf.st_mode & S_IXGRP;
+           if (DebugLvl > 1 && !(is_r && is_x))
+               fprintf(stderr, "ValidateBackend: \"%s\" is not group read/execute\n",
+                       path);
+           return(is_x ? (is_r ? 0 : -2) : -1);
+       }
+    }
+    is_r = buf.st_mode & S_IROTH;
+    is_x = buf.st_mode & S_IXOTH;
+    if (DebugLvl > 1 && !(is_r && is_x))
+       fprintf(stderr, "ValidateBackend: \"%s\" is not other read/execute\n",
+               path);
+    return(is_x ? (is_r ? 0 : -2) : -1);
+#else
+    file_attributes = GetFileAttributes(path);
+    if(file_attributes != 0xFFFFFFFF)
+      return(0);
+    else
+      return(-1);
+#endif /* WIN32 */
+}
+
+/*
+ * FindBackend -- find an absolute path to a valid backend executable
+ *
+ * The reason we have to work so hard to find an absolute path is that
+ * we need to feed the backend server the location of its actual 
+ * executable file -- otherwise, we can't do dynamic loading.
+ */
+int
+FindBackend(char *backend, char *argv0)
+{
+    char       buf[MAXPGPATH + 2];
+    char       *p;
+    char       *path, *startp, *endp;
+    int                pathlen;
+    
+#ifdef WIN32
+    strcpy(backend, argv0);
+    return(0);
+#endif /* WIN32 */
+    
+    /*
+     * for the postmaster:
+     * First try: use the backend that's located in the same directory
+     * as the postmaster, if it was invoked with an explicit path.
+     * Presumably the user used an explicit path because it wasn't in
+     * PATH, and we don't want to use incompatible executables.
+     *
+     * This has the neat property that it works for installed binaries,
+     * old source trees (obj/support/post{master,gres}) and new marc
+     * source trees (obj/post{master,gres}) because they all put the 
+     * two binaries in the same place.
+     *
+     * for the backend server:
+     * First try: if we're given some kind of path, use it (making sure
+     * that a relative path is made absolute before returning it).
+     */
+    if (argv0 && (p = strrchr(argv0, '/')) && *++p) {
+       if (*argv0 == '/' || !getcwd(buf, MAXPGPATH))
+           buf[0] = '\0';
+       else
+           (void) strcat(buf, "/");
+       (void) strcat(buf, argv0);
+       p = strrchr(buf, '/');
+       (void) strcpy(++p, "postgres");
+       if (!ValidateBackend(buf)) {
+           (void) strncpy(backend, buf, MAXPGPATH);
+           if (DebugLvl)
+               fprintf(stderr, "FindBackend: found \"%s\" using argv[0]\n",
+                       backend);
+           return(0);
+       }
+       fprintf(stderr, "FindBackend: invalid backend \"%s\"\n",
+               buf);
+       return(-1);
+    }
+    
+    /*
+     * Second try: since no explicit path was supplied, the user must
+     * have been relying on PATH.  We'll use the same PATH.
+     */
+    if ((p = getenv("PATH")) && *p) {
+       if (DebugLvl)
+           fprintf(stderr, "FindBackend: searching PATH ...\n");
+       pathlen = strlen(p);
+       path = malloc(pathlen + 1);
+       (void) strcpy(path, p);
+       for (startp = path, endp = strchr(path, ':');
+            startp && *startp;
+            startp = endp + 1, endp = strchr(startp, ':')) {
+           if (startp == endp) /* it's a "::" */
+               continue;
+           if (endp)
+               *endp = '\0';
+           if (*startp == '/' || !getcwd(buf, MAXPGPATH))
+               buf[0] = '\0';
+           (void) strcat(buf, startp);
+           (void) strcat(buf, "/postgres");
+           switch (ValidateBackend(buf)) {
+           case 0:             /* found ok */
+               (void) strncpy(backend, buf, MAXPGPATH);
+               if (DebugLvl)
+                   fprintf(stderr, "FindBackend: found \"%s\" using PATH\n",
+                           backend);
+               free(path);
+               return(0);
+           case -1:            /* wasn't even a candidate, keep looking */
+               break;
+           case -2:            /* found but disqualified */
+               fprintf(stderr, "FindBackend: could not read backend \"%s\"\n",
+                       buf);
+               free(path);
+               return(-1);
+           }
+           if (!endp)          /* last one */
+               break;
+       }
+       free(path);
+    }
+
+    fprintf(stderr, "FindBackend: could not find a backend to execute...\n");
+    return(-1);
+}
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
new file mode 100644 (file)
index 0000000..800f1b5
--- /dev/null
@@ -0,0 +1,108 @@
+/*-------------------------------------------------------------------------
+ *
+ * globals.c--
+ *    global variable declarations
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *    Globals used all over the place should be declared here and not
+ *    in other modules.  
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <math.h>
+
+#include "postgres.h"
+#include "miscadmin.h"         /* where the declarations go */
+
+#include "access/heapam.h"
+#include "utils/tqual.h"
+#include "storage/sinval.h"
+#include "storage/sinvaladt.h"
+#include "storage/lmgr.h"
+#include "utils/elog.h"
+
+#include "catalog/catname.h"
+
+int            Portfd = -1;
+int            Noversion = 0;
+int             Quiet = 1;
+
+int            MasterPid;
+char*           DataDir;
+
+char           OutputFileName[MAXPGPATH] = "";
+
+BackendId      MyBackendId;
+BackendTag     MyBackendTag;
+
+char            *UserName = NULL;
+char            *DatabaseName = NULL;
+char           *DatabasePath = NULL;
+
+bool           MyDatabaseIdIsInitialized = false;
+Oid            MyDatabaseId = InvalidOid;
+bool           TransactionInitWasProcessed = false;
+
+bool           IsUnderPostmaster = false;
+bool           IsPostmaster = false;
+
+short          DebugLvl = 0;
+
+char *IndexedCatalogNames[] = {
+    AttributeRelationName,
+    ProcedureRelationName,
+    TypeRelationName,
+    RelationRelationName,
+    0
+};
+
+
+/* ----------------
+ * we just do a linear search now so there's no requirement that the list
+ * be ordered.  The list is so small it shouldn't make much difference.
+ * make sure the list is null-terminated
+ *              - jolly 8/19/95
+ *                                  
+ * OLD COMMENT
+ *     WARNING  WARNING  WARNING  WARNING  WARNING  WARNING
+ *
+ *     keep SharedSystemRelationNames[] in SORTED order!  A binary search
+ *     is done on it in catalog.c!
+ *
+ *     XXX this is a serious hack which should be fixed -cim 1/26/90
+ * ----------------
+ */
+char *SharedSystemRelationNames[] = {
+    DatabaseRelationName, 
+    DefaultsRelationName,
+    DemonRelationName,
+    GroupRelationName,
+    HostsRelationName,
+    LogRelationName,
+    MagicRelationName,
+    ServerRelationName,
+    TimeRelationName,
+    UserRelationName,
+    VariableRelationName,
+    0
+};
+
+/* set up global variables, pointers, etc. */
+void InitGlobals()
+{
+    MasterPid = getpid();
+    DataDir = GetPGData();
+}
+
+
diff --git a/src/backend/utils/init/magic.c b/src/backend/utils/init/magic.c
new file mode 100644 (file)
index 0000000..fb0a80a
--- /dev/null
@@ -0,0 +1,167 @@
+/*-------------------------------------------------------------------------
+ *
+ * magic.c--
+ *    magic number management routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *     XXX eventually, should be able to handle version identifiers
+ *     of length != 4.
+ *
+ *  STANDALONE CODE - do not use error routines as this code is linked with
+ *  stuff that does not cinterface.a
+ *-------------------------------------------------------------------------
+ */
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "postgres.h"
+
+#include "utils/elog.h"
+#include "miscadmin.h"         /* for global decls */
+
+#include "storage/fd.h"                /* for O_ */
+
+static char    Pg_verfile[] = PG_VERFILE;
+
+
+/*
+ * private function prototypes
+ */
+static void PathSetVersionFilePath(char path[], char filepathbuf[]);
+
+/*
+ * DatabaseMetaGunkIsConsistent
+ *
+ * Returns 1 iff all version numbers and ownerships are consistent.
+ *
+ * Note that we have to go through the whole rigmarole of generating the path
+ * and checking the existence of the database whether Noversion is set or not.
+ */
+int
+DatabaseMetaGunkIsConsistent(char *database, char *path)
+{
+    int                isValid;
+#ifndef WIN32
+    struct stat        statbuf;
+#else    
+    struct _stat statbuf;
+#endif
+
+    /* XXX We haven't changed PG_VERSION since 1.1! */
+#ifndef WIN32    
+    isValid = ValidPgVersion(DataDir);
+    sprintf(path, "%s/base/%s", DataDir, database);
+    isValid = ValidPgVersion(path) || isValid;
+#endif /* WIN32 */
+    
+    if (stat(path, &statbuf) < 0)
+       elog(FATAL, "database %s does not exist, bailing out...",
+            database);
+    
+    return(isValid);
+}
+
+
+/*
+ * ValidPgVersion      - verifies the consistency of the database
+ *
+ *     Returns 1 iff the catalog version number (from the version number file
+ *     in the directory specified in "path") is consistent with the backend
+ *     version number.
+ */
+int
+ValidPgVersion(char *path)
+{
+    int                fd;
+    char               version[4], buf[MAXPGPATH+1];
+#ifndef WIN32
+    struct stat        statbuf;
+#else    
+    struct _stat statbuf;
+#endif    
+    u_short            my_euid = geteuid();
+    
+    PathSetVersionFilePath(path, buf);
+    
+    if (stat(buf, &statbuf) >= 0) {
+       if (statbuf.st_uid != my_euid && my_euid != 0)
+           elog(FATAL,
+                "process userid (%d) != database owner (%d)",
+                my_euid, statbuf.st_uid);
+    } else
+       return(0);
+    
+    if ((fd = open(buf, O_RDONLY, 0)) < 0) {
+       if (!Noversion)
+           elog(DEBUG, "ValidPgVersion: %s: %m", buf);
+       return(0);
+    }
+    
+    if (read(fd, version, 4) < 4 ||
+       !isascii(version[0]) || !isdigit(version[0]) ||
+       version[1] != '.' ||
+       !isascii(version[2]) || !isdigit(version[2]) ||
+       version[3] != '\n')
+       elog(FATAL, "ValidPgVersion: %s: bad format", buf);
+    if (version[2] != '0' + PG_VERSION ||
+       version[0] != '0' + PG_RELEASE) {
+       if (!Noversion)
+           elog(DEBUG,
+                "ValidPgVersion: should be %d.%d not %c.%c",
+                PG_RELEASE, PG_VERSION, version[0], version[2]);
+       close(fd);
+       return(0);
+    }
+    close(fd);
+    return(1);
+}
+
+
+/*
+ *     SetPgVersion    - writes the version to a database directory
+ */
+void
+SetPgVersion(char *path)
+{
+    int        fd;
+    char       version[4], buf[MAXPGPATH+1];
+    
+    PathSetVersionFilePath(path, buf);
+    
+    if ((fd = open(buf, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0)
+       elog(FATAL, "SetPgVersion: %s: %m", buf);
+    
+    version[0] = '0' + PG_RELEASE;
+    version[1] = '.';
+    version[2] = '0' + PG_VERSION;
+    version[3] = '\n';
+    if (write(fd, version, 4) != 4)
+       elog(WARN, "SetPgVersion: %s: %m", buf);
+    
+    close(fd);
+}
+
+
+/*
+ * PathSetVersionFilePath
+ *
+ * Destructively change "filepathbuf" to contain the concatenation of "path"
+ * and the name of the version file name.
+ */
+static void
+PathSetVersionFilePath(char *path, char *filepathbuf)
+{
+    if (strlen(path) > (MAXPGPATH - sizeof(Pg_verfile) - 1))
+       elog(FATAL, "PathSetVersionFilePath: %s: path too long");
+    (void) sprintf(filepathbuf, "%s%c%s", path, SEP_CHAR, Pg_verfile);
+}
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
new file mode 100644 (file)
index 0000000..bcd9c35
--- /dev/null
@@ -0,0 +1,378 @@
+/*-------------------------------------------------------------------------
+ *
+ * miscinit.c--
+ *    miscellanious initialization support stuff
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include <sys/param.h>         /* for MAXPATHLEN */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <stdio.h>
+#ifndef WIN32
+#include <grp.h>               /* for getgrgid */
+#include <pwd.h>               /* for getpwuid */
+#endif /* WIN32 */
+
+#include "postgres.h"
+
+#include "utils/portal.h"      /* for EnablePortalManager, etc. */
+#include "utils/exc.h"         /* for EnableExceptionHandling, etc. */
+#include "utils/mcxt.h"                /* for EnableMemoryContext, etc. */
+#include "utils/elog.h"
+#include "utils/builtins.h"
+
+#include "miscadmin.h"         /* where the declarations go */
+
+#include "catalog/catname.h"
+#include "catalog/pg_user.h"
+#include "catalog/pg_proc.h"
+#include "utils/syscache.h"
+
+#include "storage/fd.h"                /* for O_ */
+
+/*
+ * EnableAbortEnvVarName --
+ *     Enables system abort iff set to a non-empty string in environment.
+ */
+#define EnableAbortEnvVarName  "POSTGRESABORT"
+
+extern char *getenv(const char *name); /* XXX STDLIB */
+
+/*  from globals.c */
+extern char *DatabaseName;
+extern char *UserName;
+extern char *DatabasePath;
+
+
+/*
+ * Define USE_ENVIRONMENT to get PGDATA, etc. from environment variables.
+ * This is the default on UNIX platforms.
+ */
+#ifndef WIN32
+#define USE_ENVIRONMENT
+#endif
+
+/* ----------------------------------------------------------------
+ *             some of the 19 ways to leave postgres
+ * ----------------------------------------------------------------
+ */
+
+/*
+ * ExitPostgres --
+ *     Exit POSTGRES with a status code.
+ *
+ * Note:
+ *     This function never returns.
+ *     ...
+ *
+ * Side effects:
+ *     ...
+ *
+ * Exceptions:
+ *     none
+ */
+void
+ExitPostgres(ExitStatus status)
+{
+#ifdef __SABER__
+    saber_stop();
+#endif
+    exitpg(status);
+}
+
+/*
+ * AbortPostgres --
+ *     Abort POSTGRES dumping core.
+ *
+ * Note:
+ *     This function never returns.
+ *     ...
+ *
+ * Side effects:
+ *     Core is dumped iff EnableAbortEnvVarName is set to a non-empty string.
+ *     ...
+ *
+ * Exceptions:
+ *     none
+ */
+void
+AbortPostgres()
+{
+    char *abortValue = getenv(EnableAbortEnvVarName);
+    
+#ifdef __SABER__
+    saber_stop();
+#endif
+    
+    if (PointerIsValid(abortValue) && abortValue[0] != '\0')
+       abort();
+    else
+       exitpg(FatalExitStatus);
+}
+
+/* ----------------
+ *     StatusBackendExit
+ * ----------------
+ */
+void
+StatusBackendExit(int status)
+{
+    /* someday, do some real cleanup and then call the LISP exit */
+    /* someday, call StatusPostmasterExit if running without postmaster */
+    exitpg(status);
+}
+
+/* ----------------
+ *     StatusPostmasterExit
+ * ----------------
+ */
+void
+StatusPostmasterExit(int status)
+{
+    /* someday, do some real cleanup and then call the LISP exit */
+    exitpg(status);
+}
+
+/* ----------------------------------------------------------------
+ *     processing mode support stuff (used to be in pmod.c)
+ * ----------------------------------------------------------------
+ */
+static ProcessingMode  Mode = NoProcessing;
+
+/*
+ * IsNoProcessingMode --
+ *     True iff processing mode is NoProcessing.
+ */
+bool
+IsNoProcessingMode()
+{
+    return ((bool)(Mode == NoProcessing));
+}
+
+/*
+ * IsBootstrapProcessingMode --
+ *     True iff processing mode is BootstrapProcessing.
+ */
+bool
+IsBootstrapProcessingMode()
+{
+    return ((bool)(Mode == BootstrapProcessing));
+}
+
+/*
+ * IsInitProcessingMode --
+ *     True iff processing mode is InitProcessing.
+ */
+bool
+IsInitProcessingMode()
+{
+    return ((bool)(Mode == InitProcessing));
+}
+
+/*
+ * IsNormalProcessingMode --
+ *     True iff processing mode is NormalProcessing.
+ */
+bool
+IsNormalProcessingMode()
+{
+    return ((bool)(Mode == NormalProcessing));
+}
+
+/*
+ * SetProcessingMode --
+ *     Sets mode of processing as specified.
+ *
+ * Exceptions:
+ *     BadArg if called with invalid mode.
+ *
+ * Note:
+ *     Mode is NoProcessing before the first time this is called.
+ */
+void
+SetProcessingMode(ProcessingMode mode)
+{
+    AssertArg(mode == NoProcessing || mode == BootstrapProcessing ||
+             mode == InitProcessing || mode == NormalProcessing);
+    
+    Mode = mode;
+}
+
+ProcessingMode
+GetProcessingMode()
+{
+    return (Mode);
+}
+
+/* ----------------------------------------------------------------
+ *             database path / name support stuff
+ * ----------------------------------------------------------------
+ */
+
+/*
+ * GetDatabasePath --
+ *     Returns path to database.
+ *
+ */
+char*
+GetDatabasePath()
+{
+    return DatabasePath;
+}
+
+/*
+ * GetDatabaseName --
+ *     Returns name of database.
+ */
+char*
+GetDatabaseName()
+{
+    return DatabaseName;
+}
+
+void
+SetDatabasePath(char *path)
+{
+    /* use malloc since this is done before memory contexts are set up */
+    if (DatabasePath)
+       free(DatabasePath);
+    DatabasePath = malloc(strlen(path)+1);
+    strcpy(DatabasePath, path);
+}
+
+void
+SetDatabaseName(char *name)
+{
+    if (DatabaseName)
+       free (DatabaseName);
+    DatabaseName = malloc(strlen(name)+1);
+    strcpy(DatabaseName, name);
+}
+
+/* ----------------
+ *     GetPgUserName and SetPgUserName
+ *
+ *     SetPgUserName must be called before InitPostgres, since the setuid()
+ *     is done there.
+ * ----------------
+ */
+char*
+GetPgUserName()
+{
+    return UserName;
+}
+
+void
+SetPgUserName()
+{
+#ifndef NO_SECURITY
+    char *p;
+    struct passwd *pw;
+    
+    if (IsUnderPostmaster) {
+       /* use the (possibly) authenticated name that's provided */
+       if (!(p = getenv("PG_USER")))
+           elog(FATAL, "SetPgUserName: PG_USER environment variable unset");
+    } else {
+       /* setuid() has not yet been done, see above comment */
+       if (!(pw = getpwuid(geteuid())))
+           elog(FATAL, "SetPgUserName: no entry in passwd file");
+       p = pw->pw_name;
+    }
+    if (UserName)
+       free(UserName);
+    UserName = malloc(strlen(p)+1);
+    strcpy(UserName, p);
+#endif /* NO_SECURITY */
+    
+#ifdef WIN32
+    /* XXX We'll figure out how to get the user name later */
+    if (UserName)
+       free(UserName);
+    UserName = malloc(strlen(p)+1);
+    strcpy(UserName, "postgres");
+#endif /* WIN32 */   
+
+}
+
+/* ----------------------------------------------------------------
+ *     GetUserId and SetUserId
+ * ----------------------------------------------------------------
+ */
+static Oid     UserId = InvalidOid;
+
+Oid
+GetUserId()
+{
+    Assert(OidIsValid(UserId));
+    return(UserId);
+}
+
+void
+SetUserId()
+{
+    HeapTuple  userTup;
+    char *userName;
+    
+    Assert(!OidIsValid(UserId));       /* only once */
+    
+    /*
+     * Don't do scans if we're bootstrapping, none of the system
+     * catalogs exist yet, and they should be owned by postgres
+     * anyway.
+     */
+    if (IsBootstrapProcessingMode()) {
+       UserId = geteuid();
+       return;
+    }
+    
+    userName = GetPgUserName();
+    userTup = SearchSysCacheTuple(USENAME, PointerGetDatum(userName),
+                                 0,0,0);
+    if (!HeapTupleIsValid(userTup))
+       elog(FATAL, "SetUserId: user \"%s\" is not in \"%s\"",
+            userName, 
+            UserRelationName);
+    UserId = (Oid) ((Form_pg_user) GETSTRUCT(userTup))->usesysid;
+}
+
+/* ----------------
+ *     GetPGHome
+ *
+ *  Get POSTGRESHOME from environment, or return default.
+ * ----------------
+ */
+char *
+GetPGHome()
+{
+#ifdef USE_ENVIRONMENT
+    char *h;
+    
+    if ((h = getenv("POSTGRESHOME")) != (char *) NULL)
+       return (h);
+#endif /* USE_ENVIRONMENT */
+    return (POSTGRESDIR);    
+
+}
+
+char *
+GetPGData()
+{
+#ifdef USE_ENVIRONMENT
+    char *p;
+    
+    if ((p = getenv("PGDATA")) != (char *) NULL) {
+        return (p);
+    }
+#endif /* USE_ENVIRONMENT */    
+    return (PGDATADIR);
+}
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
new file mode 100644 (file)
index 0000000..12f798d
--- /dev/null
@@ -0,0 +1,648 @@
+/*-------------------------------------------------------------------------
+ *
+ * postinit.c--
+ *    postgres initialization utilities
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *     InitPostgres() is the function called from PostgresMain
+ *     which does all non-trival initialization, mainly by calling
+ *     all the other initialization functions.  InitPostgres()
+ *     is only used within the "postgres" backend and so that routine
+ *     is in tcop/postgres.c  InitPostgres() is needed in cinterface.a
+ *     because things like the bootstrap backend program need it. Hence
+ *     you find that in this file...
+ *
+ *     If you feel the need to add more initialization code, it should be
+ *     done in InitPostgres() or someplace lower.  Do not start
+ *     putting stuff in PostgresMain - if you do then someone
+ *     will have to clean it up later, and it's not going to be me!
+ *     -cim 10/3/90
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <math.h>
+
+#include "postgres.h"
+
+#include "machine.h"           /* for BLCKSZ, for InitMyDatabaseId() 
+                                * and where the decarations for this file go
+                                */
+#include "access/heapam.h"
+#include "access/xact.h"
+#include "storage/bufmgr.h"
+#include "access/transam.h"    /* XXX dependency problem */
+#include "utils/tqual.h"
+#include "utils/syscache.h"
+#include "storage/bufpage.h"   /* for page layout, for InitMyDatabaseId() */
+#include "storage/sinval.h"
+#include "storage/sinvaladt.h"
+#include "storage/lmgr.h"
+
+#include "miscadmin.h"         /* for global decls */
+#include "utils/portal.h"      /* for EnablePortalManager, etc. */
+
+#include "utils/exc.h"         /* for EnableExceptionHandling, etc. */
+#include "fmgr.h"              /* for EnableDynamicFunctionManager, etc. */
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/mcxt.h"                /* for EnableMemoryContext, etc. */
+
+#include "catalog/catname.h"
+#include "catalog/pg_database.h"
+
+#include "port-protos.h"
+#include "libpq/libpq-be.h"
+
+
+static IPCKey          PostgresIpcKey;
+
+
+#ifndef        private
+#ifndef        EBUG
+#define        private static
+#else  /* !defined(EBUG) */
+#define private
+#endif /* !defined(EBUG) */
+#endif /* !defined(private) */
+
+/* ----------------------------------------------------------------
+ *                     InitPostgres support
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ *  InitMyDatabaseId() -- Find and record the OID of the database we are
+ *                       to open.
+ *
+ *     The database's oid forms half of the unique key for the system
+ *     caches and lock tables.  We therefore want it initialized before
+ *     we open any relations, since opening relations puts things in the
+ *     cache.  To get around this problem, this code opens and scans the
+ *     pg_database relation by hand.
+ *
+ *     This algorithm relies on the fact that first attribute in the
+ *     pg_database relation schema is the database name.  It also knows
+ *     about the internal format of tuples on disk and the length of
+ *     the datname attribute.  It knows the location of the pg_database
+ *     file.
+ *
+ *     This code is called from InitDatabase(), after we chdir() to the
+ *     database directory but before we open any relations.
+ * --------------------------------
+ */
+void
+InitMyDatabaseId()
+{
+    int                dbfd;
+    int                fileflags;
+    int                nbytes;
+    int                max, i;
+    HeapTuple  tup;
+    Page       pg;
+    PageHeader ph;
+    char       *dbfname;
+    Form_pg_database   tup_db;
+    
+    /*
+     *  At bootstrap time, we don't need to check the oid of the database
+     *  in use, since we're not using shared memory.  This is lucky, since
+     *  the database may not be in the tables yet.
+     */
+    
+    if (IsBootstrapProcessingMode()) {
+       LockDisable(true);
+       return;
+    }
+    
+    dbfname = (char *) palloc(strlen(DataDir) + strlen("pg_database") + 2);
+    sprintf(dbfname, "%s%cpg_database", DataDir, SEP_CHAR);
+    fileflags = O_RDONLY;
+#ifdef WIN32
+    fileflags |= _O_BINARY;
+#endif /* WIN32 */
+    
+    if ((dbfd = open(dbfname, O_RDONLY, 0666)) < 0)
+       elog(FATAL, "Cannot open %s", dbfname);
+    
+    pfree(dbfname);
+    
+    /* ----------------
+     * read and examine every page in pg_database
+     *
+     * Raw I/O! Read those tuples the hard way! Yow!
+     *
+     *  Why don't we use the access methods or move this code
+     *  someplace else?  This is really pg_database schema dependent
+     *  code.  Perhaps it should go in lib/catalog/pg_database?
+     *  -cim 10/3/90
+     *
+     *  mao replies 4 apr 91:  yeah, maybe this should be moved to
+     *  lib/catalog.  however, we CANNOT use the access methods since
+     *  those use the buffer cache, which uses the relation cache, which
+     *  requires that the dbid be set, which is what we're trying to do
+     *  here.
+     * ----------------
+     */
+    pg = (Page) palloc(BLCKSZ);
+    ph = (PageHeader) pg;
+    
+    while ((nbytes = read(dbfd, pg, BLCKSZ)) == BLCKSZ) {
+       max = PageGetMaxOffsetNumber(pg);
+       
+       /* look at each tuple on the page */
+       for (i = 0; i <= max; i++) {
+           int offset;
+           
+           /* if it's a freed tuple, ignore it */
+           if (!(ph->pd_linp[i].lp_flags & LP_USED))
+               continue;
+           
+           /* get a pointer to the tuple itself */
+           offset = (int) ph->pd_linp[i].lp_off;
+           tup = (HeapTuple) (((char *) pg) + offset);
+           
+           /*
+            *  if the tuple has been deleted (the database was destroyed),
+            *  skip this tuple.  XXX warning, will robinson:  violation of
+            *  transaction semantics happens right here.  we should check
+            *  to be sure that the xact that deleted this tuple actually
+            *  committed.  only way to do this at init time is to paw over
+            *  the log relation by hand, too.  let's be optimistic.
+            *
+            *  XXX This is an evil type cast.  tup->t_xmax is char[5] while
+            *  TransactionId is struct * { char data[5] }.  It works but
+            *  if data is ever moved and no longer the first field this 
+            *  will be broken!! -mer 11 Nov 1991.
+            */
+           if (TransactionIdIsValid((TransactionId)tup->t_xmax))
+               continue;
+           
+           /*
+            *  Okay, see if this is the one we want.
+            *  XXX 1 july 91:  mao and mer discover that tuples now squash
+            *                  t_bits.  Why is this?
+            *
+            *     24 july 92:  mer realizes that the t_bits field is only
+            *                  used in the event of null values.  If no
+            *                  fields are null we reduce the header size
+            *                  by doing the squash.  t_hoff tells you exactly
+            *                  how big the header actually is. use the PC
+            *                  means of getting at sys cat attrs.
+            */
+           tup_db = (Form_pg_database)GETSTRUCT(tup);
+           
+           if (strncmp(GetDatabaseName(),
+                       &(tup_db->datname.data[0]),
+                       16) == 0)
+               {
+                   MyDatabaseId = tup->t_oid;
+                   goto done;
+               }
+       }
+    }
+    
+ done:
+    (void) close(dbfd);
+    pfree(pg);
+    
+    if (!OidIsValid(MyDatabaseId))
+       elog(FATAL,
+            "Database %s does not exist in %s",
+            GetDatabaseName(),
+            DatabaseRelationName);
+}
+
+/*
+ * DoChdirAndInitDatabaseNameAndPath --
+ *     Sets current directory appropriately for given path and name.
+ *
+ * Arguments:
+ *     Path and name are invalid if it invalid as a string.
+ *     Path is "badly formated" if it is not a string containing a path
+ *     to a writable directory.
+ *     Name is "badly formated" if it contains more than 16 characters or if
+ *     it is a bad file name (e.g., it contains a '/' or an 8-bit character).
+ *
+ * Side effects:
+ *     Initially, DatabasePath and DatabaseName are invalid.  They are
+ *     set to valid strings before this function returns.
+ *
+ * Exceptions:
+ *     BadState if called more than once.
+ *     BadArg if both path and name are "badly formated" or invalid.
+ *     BadArg if path and name are both "inconsistent" and valid.
+ */
+/* ----------------
+ *     DoChdirAndInitDatabaseNameAndPath
+ *
+ *     this just chdir's to the proper data/base directory
+ *     XXX clean this up more.
+ *
+ * XXX The following code is an incorrect of the semantics
+ * XXX described in the header file.  Handling of defaults
+ * XXX should happen here, too.
+ * ----------------
+ */
+void
+DoChdirAndInitDatabaseNameAndPath(char *name, /* name of database */
+                                 char *path) /* full path to database */
+{
+    /* ----------------
+     * check the path
+     * ----------------
+     */
+    if (path)
+       SetDatabasePath(path);
+    else
+       elog(FATAL, "DoChdirAndInitDatabaseNameAndPath: path:%s is not valid",
+            path);
+    
+    /* ----------------
+     * check the name
+     * ----------------
+     */
+    if (name)
+       SetDatabaseName(name);
+    else 
+       elog(FATAL, "DoChdirAndInitDatabaseNameAndPath: name:%s is not valid",
+            name);
+    
+    /* ----------------
+     * change to the directory, or die trying.
+     *
+     * XXX unless the path hasn't been set because we're bootstrapping.
+     *     HP-UX doesn't like chdir("") so check for that case before
+     *     doing anything drastic.
+     * ----------------
+     */
+    if (*path && (chdir(path) < 0))
+       elog(FATAL, "DoChdirAndInitDatabaseNameAndPath: chdir(\"%s\"): %m",
+            path);
+}
+
+/* --------------------------------
+ *     InitUserid
+ *
+ *     initializes crap associated with the user id.
+ * --------------------------------
+ */
+void
+InitUserid()
+{
+    setuid(geteuid());
+    SetUserId();
+}
+
+/* --------------------------------
+ *     InitCommunication
+ *
+ *     This routine initializes stuff needed for ipc, locking, etc.
+ *     it should be called something more informative.
+ *
+ * Note:
+ *     This does not set MyBackendId.  MyBackendTag is set, however.
+ * --------------------------------
+ */
+void
+InitCommunication()
+{
+    char *getenv();    /* XXX style */
+    char *postid;
+    char *postport;
+    IPCKey     key;
+    
+    /* ----------------
+     * try and get the backend tag from POSTID
+     * ----------------
+     */
+    MyBackendId = -1;
+    
+    postid = getenv("POSTID");
+    if (!PointerIsValid(postid)) {
+       MyBackendTag = -1;
+    } else {
+       MyBackendTag = atoi(postid);
+       Assert(MyBackendTag >= 0);
+    }
+    
+    /* ----------------
+     *  try and get the ipc key from POSTPORT
+     * ----------------
+     */
+    postport = getenv("POSTPORT");
+    
+    if (PointerIsValid(postport)) {
+       SystemPortAddress address = atoi(postport);
+       
+       if (address == 0)
+           elog(FATAL, "InitCommunication: invalid POSTPORT");
+       
+       if (MyBackendTag == -1)
+           elog(FATAL, "InitCommunication: missing POSTID");
+       
+       key = SystemPortAddressCreateIPCKey(address);
+       
+       /*
+        * Enable this if you are trying to force the backend to run as if it 
+        * is running under the postmaster.
+        *
+        * This goto forces Postgres to attach to shared memory instead of 
+        * using malloc'ed memory (which is the normal behavior if run
+        * directly).
+        *
+        * To enable emulation, run the following shell commands (in addition
+        * to enabling this goto)
+        *
+        *     % setenv POSTID 1
+        *     % setenv POSTPORT 4321
+        *     % postmaster &
+        *     % kill -9 %1
+        *
+        * Upon doing this, Postmaster will have allocated the shared memory 
+        * resources that Postgres will attach to if you enable
+        * EMULATE_UNDER_POSTMASTER.
+        *
+        * This comment may well age with time - it is current as of
+        * 8 January 1990
+        * 
+        * Greg
+        */
+       
+#ifdef EMULATE_UNDER_POSTMASTER
+       
+       goto forcesharedmemory;
+       
+#endif
+       
+    } else if (IsUnderPostmaster) {
+       elog(FATAL,
+            "InitCommunication: under postmaster and POSTPORT not set");
+    } else {
+       /* ----------------
+        *  assume we're running a postgres backend by itself with
+        *  no front end or postmaster.
+        * ----------------
+        */
+       if (MyBackendTag == -1) {
+           MyBackendTag = 1;
+       }
+       
+       key = PrivateIPCKey;
+    }
+    
+    /* ----------------
+     *  initialize shared memory and semaphores appropriately.
+     * ----------------
+     */
+#ifdef EMULATE_UNDER_POSTMASTER
+    
+ forcesharedmemory:
+    
+#endif
+    
+    PostgresIpcKey = key;
+    AttachSharedMemoryAndSemaphores(key);
+}
+
+
+/* --------------------------------
+ *     InitStdio
+ *
+ *     this routine consists of a bunch of code fragments
+ *     that used to be randomly scattered through cinit().
+ *     they all seem to do stuff associated with io.
+ * --------------------------------
+ */
+void
+InitStdio()
+{
+    (void) DebugFileOpen();
+}
+
+/* --------------------------------
+ * InitPostgres --
+ *     Initialize POSTGRES.
+ *
+ * Note:
+ *     Be very careful with the order of calls in the InitPostgres function.
+ * --------------------------------
+ */
+bool PostgresIsInitialized = false;
+extern int NBuffers;
+
+/*
+ *  this global is used by wei for testing his code, but must be declared
+ *  here rather than in postgres.c so that it's defined for cinterface.a
+ *  applications.
+ */
+
+/*int  testFlag = 0;*/
+int    lockingOff = 0;
+
+/*
+ */
+void
+InitPostgres(char *name)       /* database name */
+{
+    bool       bootstrap;      /* true if BootstrapProcessing */
+    
+    /* ----------------
+     * see if we're running in BootstrapProcessing mode
+     * ----------------
+     */
+    bootstrap = IsBootstrapProcessingMode();
+    
+    /* ----------------
+     * turn on the exception handler.  Note: we cannot use elog, Assert,
+     *  AssertState, etc. until after exception handling is on.
+     * ----------------
+     */
+    EnableExceptionHandling(true);
+    
+    /* ----------------
+     * A stupid check to make sure we don't call this more than once.
+     *  But things like ReinitPostgres() get around this by just diddling
+     * the PostgresIsInitialized flag.
+     * ----------------
+     */
+    AssertState(!PostgresIsInitialized);
+    
+    /* ----------------
+     * Memory system initialization.
+     *  (we may call palloc after EnableMemoryContext())
+     *
+     *  Note EnableMemoryContext() must happen before EnablePortalManager().
+     * ----------------
+     */
+    EnableMemoryContext(true); /* initializes the "top context" */
+    EnablePortalManager(true); /* memory for portal/transaction stuff */
+    
+    /* ----------------
+     * initialize the backend local portal stack used by
+     *  internal PQ function calls.  see src/lib/libpq/be-dumpdata.c
+     *  This is different from the "portal manager" so this goes here.
+     *  -cim 2/12/91
+     * ----------------
+     */    
+    be_portalinit();
+    
+    /* ----------------
+     *  attach to shared memory and semaphores, and initialize our
+     *   input/output/debugging file descriptors.
+     * ----------------
+     */
+    InitCommunication();
+    InitStdio();
+    
+    /*
+     * initialize the local buffer manager
+     */
+    InitLocalBuffer();
+
+    if (!TransactionFlushEnabled())
+        on_exitpg(FlushBufferPool, (caddr_t) NULL);
+    
+    /* ----------------
+     * check for valid "meta gunk" (??? -cim 10/5/90) and change to
+     *  database directory.
+     *
+     *  Note:  DatabaseName, MyDatabaseName, and DatabasePath are all
+     *  initialized with DatabaseMetaGunkIsConsistent(), strncpy() and
+     *  DoChdirAndInitDatabase() below!  XXX clean this crap up!
+     *  -cim 10/5/90
+     * ----------------
+     */
+    {
+       char  myPath[MAXPGPATH] = ".";  /* DatabasePath points here! */
+       
+       /* ----------------
+        *  DatabaseMetaGunkIsConsistent fills in myPath, but what about
+        *  when bootstrap or Noversion is true?? -cim 10/5/90
+        * ----------------
+        */
+       
+       if (! bootstrap &&
+           ! DatabaseMetaGunkIsConsistent(name, myPath) &&
+           ! Noversion) {
+           elog(NOTICE, "InitPostgres: could not locate valid PG_VERSION\n");
+           elog(NOTICE, "files for %s and %s.", DataDir, name);
+           elog(FATAL,  "Have you run initdb/createdb and set PGDATA properly?");
+       }
+       
+       /* ----------------
+        *  ok, we've figured out myName and myPath, now save these
+        *  and chdir to myPath.
+        * ----------------
+        */
+       DoChdirAndInitDatabaseNameAndPath(name, myPath);
+    }
+    
+    /* ********************************
+     * code after this point assumes we are in the proper directory!
+     * ********************************
+     */
+    
+    /* ----------------
+     * initialize the database id used for system caches and lock tables
+     * ----------------
+     */
+    InitMyDatabaseId();
+    
+    smgrinit();
+    
+    /* ----------------
+     * initialize the transaction system and the relation descriptor
+     *  cache.  Note we have to make certain the lock manager is off while
+     *  we do this.
+     * ----------------
+     */
+    AmiTransactionOverride(IsBootstrapProcessingMode());
+    LockDisable(true);
+    
+    /*
+     * Part of the initialization processing done here sets a read
+     * lock on pg_log.  Since locking is disabled the set doesn't have
+     * intended effect of locking out writers, but this is ok, since
+     * we only lock it to examine AMI transaction status, and this is
+     * never written after initdb is done. -mer 15 June 1992
+     */
+    RelationInitialize();         /* pre-allocated reldescs created here */
+    InitializeTransactionSystem(); /* pg_log,etc init/crash recovery here */
+    
+    LockDisable(false);
+    
+    /* ----------------
+     * anyone knows what this does?  something having to do with
+     *  system catalog cache invalidation in the case of multiple
+     *  backends, I think -cim 10/3/90
+     *  Sets up MyBackendId a unique backend identifier.
+     * ----------------
+     */
+    InitSharedInvalidationState();
+    
+    /* ----------------
+     * Set up a per backend process in shared memory.  Must be done after
+     * InitSharedInvalidationState() as it relies on MyBackendId being
+     * initialized already.  XXX -mer 11 Aug 1991
+     * ----------------
+     */
+    InitProcess(PostgresIpcKey);
+    
+    if (MyBackendId > MaxBackendId || MyBackendId <= 0) {
+       elog(FATAL, "cinit2: bad backend id %d (%d)",
+            MyBackendTag,
+            MyBackendId);
+    }
+    
+    /* ----------------
+     *  initialize the access methods.
+     * ----------------
+     */
+    initam();
+    
+    /* ----------------
+     * initialize all the system catalog caches.
+     * ----------------
+     */
+    zerocaches();
+    InitCatalogCache();
+    
+    /* ----------------
+     *   set ourselves to the proper user id and figure out our postgres
+     *   user id.  If we ever add security so that we check for valid
+     *   postgres users, we might do it here.
+     * ----------------
+     */
+    InitUserid();
+    
+    /* ----------------
+     * ok, all done, now let's make sure we don't do it again.
+     * ----------------
+     */
+    PostgresIsInitialized = true;
+/*    on_exitpg(DestroyLocalRelList, (caddr_t) NULL); */
+    
+    /* ----------------
+     *  Done with "InitPostgres", now change to NormalProcessing unless
+     *  we're in BootstrapProcessing mode.
+     * ----------------
+     */
+    if (!bootstrap)
+       SetProcessingMode(NormalProcessing);
+/*    if (testFlag || lockingOff) */
+    if (lockingOff)
+       LockDisable(true);
+}
+
+
diff --git a/src/backend/utils/inval.h b/src/backend/utils/inval.h
new file mode 100644 (file)
index 0000000..ffc8720
--- /dev/null
@@ -0,0 +1,56 @@
+/*-------------------------------------------------------------------------
+ *
+ * inval.h--
+ *    POSTGRES cache invalidation dispatcher definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        INVAL_H
+#define INVAL_H
+
+#include "postgres.h"
+#include "access/htup.h"
+#include "utils/rel.h"
+
+extern void DiscardInvalid(void);
+
+extern void RegisterInvalid(bool send);
+
+extern void SetRefreshWhenInvalidate(bool on);
+
+extern void RelationInvalidateHeapTuple(Relation relation, HeapTuple tuple);
+
+/*
+ * POSTGRES local cache invalidation definitions. (originates from linval.h)
+ */
+typedef struct InvalidationUserData {
+       struct InvalidationUserData     *dataP[1];      /* VARIABLE LENGTH */
+} InvalidationUserData;        /* VARIABLE LENGTH STRUCTURE */
+
+typedef struct InvalidationEntryData {
+       InvalidationUserData    *nextP;
+       InvalidationUserData    userData;       /* VARIABLE LENGTH ARRAY */
+} InvalidationEntryData;       /* VARIABLE LENGTH STRUCTURE */
+
+typedef Pointer InvalidationEntry;
+
+typedef InvalidationEntry      LocalInvalid;
+
+#define EmptyLocalInvalid      NULL
+
+extern InvalidationEntry InvalidationEntryAllocate(uint16 size);
+
+extern LocalInvalid LocalInvalidRegister(LocalInvalid invalid,
+                                        InvalidationEntry entry);
+
+extern void LocalInvalidInvalidate(LocalInvalid invalid, void (*function)());
+
+extern void getmyrelids(void);
+
+#endif /* INVAL_H */
+
diff --git a/src/backend/utils/lselect.h b/src/backend/utils/lselect.h
new file mode 100644 (file)
index 0000000..16639bb
--- /dev/null
@@ -0,0 +1,40 @@
+/*-------------------------------------------------------------------------
+ *
+ * lselect.h--
+ *    definitions for the replacement selection algorithm.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        LSELECT_H
+#define        LSELECT_H
+
+#include "c.h"
+#include "access/htup.h"
+
+struct leftist {
+    short      lt_dist;        /* distance to leaf/empty node */
+    short      lt_devnum;      /* device number of tuple */
+    HeapTuple  lt_tuple;
+    struct     leftist *lt_left;
+    struct     leftist *lt_right;
+};
+
+extern struct  leftist *Tuples;
+
+extern struct leftist *lmerge(struct leftist *pt, struct leftist *qt);
+extern HeapTuple gettuple(struct leftist **treep, short *devnum);
+extern int puttuple(struct leftist **treep, HeapTuple newtuple, int devnum);
+extern void dumptuples(FILE *file);
+extern int tuplecmp(HeapTuple ltup, HeapTuple rtup);
+
+#ifdef EBUG
+extern void checktree(struct leftist *tree);
+extern int checktreer(struct leftist *tree, int level);
+#endif /* EBUG */
+
+#endif         /* LSELECT_H */
diff --git a/src/backend/utils/lsyscache.h b/src/backend/utils/lsyscache.h
new file mode 100644 (file)
index 0000000..0f1ceb1
--- /dev/null
@@ -0,0 +1,45 @@
+/*-------------------------------------------------------------------------
+ *
+ * lsyscache.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        LSYSCACHE_H
+#define        LSYSCACHE_H
+
+#include "access/htup.h"
+
+extern bool op_class(Oid opid, int32 opclass, Oid amopid);
+extern char *get_attname(Oid relid, AttrNumber attnum);
+extern AttrNumber get_attnum(Oid relid, char *attname);
+extern Oid get_atttype(Oid relid, AttrNumber attnum);
+extern bool get_attisset(Oid relid, char *attname);
+extern RegProcedure get_opcode(Oid opid);
+extern char *get_opname(Oid opid);
+extern bool op_mergesortable(Oid opid, Oid ltype, Oid rtype,
+                            Oid *leftOp, Oid *rightOp);
+extern Oid op_hashjoinable(Oid opid, Oid ltype, Oid rtype);
+extern Oid get_commutator(Oid opid);
+extern HeapTuple get_operator_tuple(Oid opno);
+extern Oid get_negator(Oid opid);
+extern RegProcedure get_oprrest(Oid opid);
+extern RegProcedure get_oprjoin(Oid opid);
+extern int get_relnatts(Oid relid);
+extern char *get_rel_name(Oid relid);
+extern struct varlena * get_relstub(Oid relid, int no, bool *islast);
+extern Oid get_ruleid(char *rulename);
+extern Oid get_eventrelid(Oid ruleid);
+extern int16 get_typlen(Oid typid);
+extern char get_typalign(Oid typid);
+extern bool get_typbyval(Oid typid);
+extern struct varlena *get_typdefault(Oid typid);
+extern char get_typtype(Oid typid);
+
+#endif /* LSYSCACHE_H */
+
diff --git a/src/backend/utils/mcxt.h b/src/backend/utils/mcxt.h
new file mode 100644 (file)
index 0000000..e7b0274
--- /dev/null
@@ -0,0 +1,56 @@
+/*-------------------------------------------------------------------------
+ *
+ * mcxt.h--
+ *    POSTGRES memory context definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        MCXT_H
+#define MCXT_H
+
+#include "c.h"
+
+#include "nodes/memnodes.h"
+#include "nodes/nodes.h"
+
+extern MemoryContext   CurrentMemoryContext;
+extern MemoryContext   TopMemoryContext;
+
+
+/*
+ * MaxAllocSize --
+ *     Arbitrary limit on size of allocations.
+ *
+ * Note:
+ *     There is no guarantee that allocations smaller than MaxAllocSize
+ *     will succeed.  Allocation requests larger than MaxAllocSize will
+ *     be summarily denied.
+ *
+ *     This value should not be referenced except in one place in the code.
+ *
+ * XXX This should be defined in a file of tunable constants.
+ */
+#define MaxAllocSize   (0xfffffff)     /* 16G - 1 */
+
+/*
+ * prototypes for functions in mcxt.c
+ */
+extern void EnableMemoryContext(bool on);
+extern Pointer MemoryContextAlloc(MemoryContext context, Size size);
+extern Pointer MemoryContextRealloc(MemoryContext context,
+                                   Pointer pointer,
+                                   Size size);
+extern void MemoryContextFree(MemoryContext context, Pointer pointer);
+extern char *MemoryContextGetName(MemoryContext context);
+extern Size PointerGetAllocSize(Pointer pointer);
+extern MemoryContext MemoryContextSwitchTo(MemoryContext context);
+extern GlobalMemory CreateGlobalMemory(char *name);
+extern void GlobalMemoryDestroy(GlobalMemory context);
+
+
+#endif /* MCXT_H */
diff --git a/src/backend/utils/memutils.h b/src/backend/utils/memutils.h
new file mode 100644 (file)
index 0000000..3972c9f
--- /dev/null
@@ -0,0 +1,281 @@
+/*-------------------------------------------------------------------------
+ *
+ * memutils.h--
+ *    this file contains general memory alignment, allocation
+ *    and manipulation stuff that used to be spread out
+ *    between the following files:
+ *
+ *     align.h                         alignment macros
+ *     aset.h                          memory allocation set stuff
+ *     oset.h                            (used by aset.h)
+ *     (bit.h                          bit array type / extern)
+ *     clib.h                          mem routines
+ *     limit.h                         max bits/byte, etc.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    some of the information in this file will be moved to
+ *    other files, (like MaxHeapTupleSize and MaxAttributeSize).
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MEMUTILS_H
+#define MEMUTILS_H
+
+#include "c.h"
+
+#if 0
+/*****************************************************************************
+ *     align.h         - alignment macros                                   *
+ ****************************************************************************
+ [TRH] Let the compiler decide what alignment it uses instead of  
+tending
+   we know better.
+   GCC (at least v2.5.8 and up) has an __alignof__ keyword.
+   However, we cannot use it here since on some architectures it reports
+   just a _recommended_ alignment instead of the actual alignment used in
+   padding structures (or at least, this is how I understand gcc's  
+s...)
+   So define a macro that gives us the _actual_ alignment inside a struct.
+   {{note: assumes that alignment size is always a power of 2.}}
+ */
+#define _ALIGNSIZE(TYPE)       offsetof(struct { char __c; TYPE __t;}, __t)
+#define _ALIGN(TYPE, LEN) \
+       (((long)(LEN) + (_ALIGNSIZE(TYPE) - 1)) & ~(_ALIGNSIZE(TYPE) - 1))
+#define SHORTALIGN(LEN)                _ALIGN(short, (LEN))
+#define INTALIGN(LEN)          _ALIGN(int, (LEN))
+#define LONGALIGN(LEN)         _ALIGN(long, (LEN))
+#define DOUBLEALIGN(LEN)       _ALIGN(double, (LEN))
+#define MAXALIGN(LEN)          _ALIGN(double, (LEN))
+
+#endif /* 0 */
+
+/*
+ *     SHORTALIGN(LEN) - length (or address) aligned for shorts
+ */
+#define        SHORTALIGN(LEN)\
+       (((long)(LEN) + (sizeof (short) - 1)) & ~(sizeof (short) - 1))
+
+#define INTALIGN(LEN)\
+       (((long)(LEN) + (sizeof (int) - 1)) & ~(sizeof (int) -1))
+
+/*
+ *     LONGALIGN(LEN)  - length (or address) aligned for longs
+ */
+#if defined(sun) && ! defined(sparc)
+#define        LONGALIGN(LEN)  SHORTALIGN(LEN)
+#elif defined (PORTNAME_alpha)
+#define        LONGALIGN(LEN)\
+       (((long)(LEN) + (sizeof (int) - 1)) & ~(sizeof (int) -1))
+#else
+#define        LONGALIGN(LEN)\
+       (((long)(LEN) + (sizeof (long) - 1)) & ~(sizeof (long) -1))
+#endif
+
+#define DOUBLEALIGN(LEN)\
+       (((long)(LEN) + (sizeof (double) - 1)) & ~(sizeof (double) -1))
+
+#define MAXALIGN(LEN)\
+       (((long)(LEN) + (sizeof (double) - 1)) & ~(sizeof (double) -1))
+
+/*****************************************************************************
+ *    bit.h                                                                  *
+ *****************************************************************************/
+#include "utils/bit.h"
+
+/*****************************************************************************
+ *    oset.h --        Fixed format ordered set definitions.                *
+ *****************************************************************************/
+/* Note:
+ *     Fixed format ordered sets are <EXPLAIN>.
+ *     XXX This is a preliminary version.  Work is needed to explain
+ *     XXX semantics of the external definitions.  Otherwise, the
+ *     XXX functional interface should not change.
+ *
+ * Identification:
+ *     $Header$
+ */
+
+typedef struct OrderedElemData OrderedElemData;
+typedef OrderedElemData* OrderedElem;
+
+typedef struct OrderedSetData OrderedSetData;
+typedef OrderedSetData* OrderedSet;
+
+struct OrderedElemData {
+    OrderedElem        next;   /* Next elem or &this->set->dummy       */
+    OrderedElem        prev;   /* Previous elem or &this->set->head    */
+    OrderedSet set;    /* Parent set                           */
+};
+
+struct OrderedSetData {
+    OrderedElem        head;   /* First elem or &this->dummy           */
+    OrderedElem        dummy;  /* (hack) Terminator == NULL            */
+    OrderedElem        tail;   /* Last elem or &this->head             */
+    Offset     offset; /* Offset from struct base to elem      */
+    /* this could be signed short int! */
+};
+
+extern void OrderedSetInit(OrderedSet set, Offset offset);
+extern bool OrderedSetContains(OrderedSet set, OrderedElem elem);
+extern Pointer OrderedSetGetHead(OrderedSet set);
+extern Pointer OrderedSetGetTail(OrderedSet set);
+extern Pointer OrderedElemGetPredecessor(OrderedElem elem);
+extern Pointer OrderedElemGetSuccessor(OrderedElem elem);
+extern void  OrderedElemPop(OrderedElem elem);
+extern void OrderedElemPushInto(OrderedElem elem, OrderedSet Set);
+
+/*****************************************************************************
+ *    aset.h --                Allocation set definitions.                          *
+ *****************************************************************************/
+/*
+ * Description:
+ *     An allocation set is a set containing allocated elements.  When
+ *     an allocation is requested for a set, memory is allocated and a
+ *     pointer is returned.  Subsequently, this memory may be freed or
+ *     reallocated.  In addition, an allocation set may be reset which
+ *     will cause all allocated memory to be freed.
+ *
+ *     Allocations may occur in four different modes.  The mode of
+ *     allocation does not affect the behavior of allocations except in
+ *     terms of performance.  The allocation mode is set at the time of
+ *     set initialization.  Once the mode is chosen, it cannot be changed
+ *     unless the set is reinitialized.
+ *
+ *     "Dynamic" mode forces all allocations to occur in a heap.  This
+ *     is a good mode to use when small memory segments are allocated
+ *     and freed very frequently.  This is a good choice when allocation
+ *     characteristics are unknown.  This is the default mode.
+ *
+ *     "Static" mode attemts to allocate space as efficiently as possible
+ *     without regard to freeing memory.  This mode should be chosen only
+ *     when it is known that many allocations will occur but that very
+ *     little of the allocated memory will be explicitly freed.
+ *
+ *     "Tunable" mode is a hybrid of dynamic and static modes.  The
+ *     tunable mode will use static mode allocation except when the
+ *     allocation request exceeds a size limit supplied at the time of set
+ *     initialization.  "Big" objects are allocated using dynamic mode.
+ *
+ *     "Bounded" mode attempts to allocate space efficiently given a limit
+ *     on space consumed by the allocation set.  This restriction can be
+ *     considered a "soft" restriction, because memory segments will
+ *     continue to be returned after the limit is exceeded.  The limit is
+ *     specified at the time of set initialization like for tunable mode.
+ *
+ * Note:
+ *     Allocation sets are not automatically reset on a system reset.
+ *     Higher level code is responsible for cleaning up.
+ *
+ *     There may other modes in the future.
+ */
+
+/*
+ * AllocPointer --
+ *     Aligned pointer which may be a member of an allocation set.
+ */
+typedef Pointer AllocPointer;
+
+/*
+ * AllocMode --
+ *     Mode of allocation for an allocation set.
+ *
+ * Note:
+ *     See above for a description of the various nodes.
+ */
+typedef enum AllocMode {
+    DynamicAllocMode,  /* always dynamically allocate */
+    StaticAllocMode,   /* always "statically" allocate */
+    TunableAllocMode,  /* allocations are "tuned" */
+    BoundedAllocMode   /* allocations bounded to fixed usage */
+} AllocMode;
+
+#define DefaultAllocMode       DynamicAllocMode
+
+/*
+ * AllocSet --
+ *     Allocation set.
+ */
+typedef struct AllocSetData {
+    OrderedSetData     setData;
+       /* Note: this will change in the future to support other modes */
+} AllocSetData;
+
+typedef AllocSetData *AllocSet;
+
+/*
+ * AllocPointerIsValid --
+ *     True iff pointer is valid allocation pointer.
+ */
+#define AllocPointerIsValid(pointer) PointerIsValid(pointer)
+
+/*
+ * AllocSetIsValid --
+ *     True iff set is valid allocation set.
+ */
+#define AllocSetIsValid(set) PointerIsValid(set)    
+
+extern void AllocSetInit(AllocSet set, AllocMode mode, Size limit);
+
+extern void AllocSetReset(AllocSet set);
+
+extern bool AllocSetContains(AllocSet set, AllocPointer pointer);
+extern AllocPointer AllocSetAlloc(AllocSet set, Size size);
+extern void AllocSetFree(AllocSet set, AllocPointer pointer);
+extern AllocPointer AllocSetRealloc(AllocSet set, AllocPointer pointer, 
+                                   Size size);
+
+extern int AllocSetIterate(AllocSet set,
+                            void (*function)(AllocPointer pointer));
+
+extern int AllocSetCount(AllocSet set);
+
+extern void AllocPointerDump(AllocPointer pointer);
+extern void AllocSetDump(AllocSet set);
+
+/*****************************************************************************
+ *    clib.h --                Standard C library definitions                       *
+ *****************************************************************************/
+/*
+ * Note:
+ *     This file is OPERATING SYSTEM dependent!!!
+ *
+ */
+/* #include <memory.h> */
+/* use <string.h> because it's ANSI */
+#include <string.h>
+
+/* 
+ *     LibCCopyLength is only used within this file. -cim 6/12/90
+ * 
+ */
+typedef int    LibCCopyLength;
+
+typedef        CLibCopyLength;
+
+/*
+ * MemoryCopy --
+ *     Copies fixed length block of memory to another.
+ */
+#define MemoryCopy(toBuffer, fromBuffer, length)\
+    memcpy(toBuffer, fromBuffer, length)
+
+/*****************************************************************************
+ *    limit.h --       POSTGRES limit definitions.                          *
+ *****************************************************************************/
+
+#define MaxBitsPerByte 8
+
+typedef uint32 AttributeSize;  /* XXX should be defined elsewhere */
+
+#define MaxHeapTupleSize       0x7fffffff
+#define MaxAttributeSize       0x7fffffff
+
+#define MaxIndexAttributeNumber        7
+
+
+#endif /* MEMUTILS_H */
diff --git a/src/backend/utils/mmgr/Makefile.inc b/src/backend/utils/mmgr/Makefile.inc
new file mode 100644 (file)
index 0000000..578a10a
--- /dev/null
@@ -0,0 +1,15 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for utils/mmgr
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= aset.c mcxt.c palloc.c portalmem.c oset.c
+
diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c
new file mode 100644 (file)
index 0000000..5cdaa97
--- /dev/null
@@ -0,0 +1,381 @@
+/*-------------------------------------------------------------------------
+ *
+ * aset.c--
+ *    Allocation set definitions.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTE
+ *    XXX This is a preliminary implementation which lacks fail-fast
+ *    XXX validity checking of arguments.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include "c.h"
+#include "utils/excid.h"       /* for ExhaustedMemory */
+#include "utils/memutils.h"    /* where funnction declarations go */
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+#undef AllocSetReset
+#undef malloc
+#undef free
+
+/*
+ * Internal type definitions
+ */
+
+/*
+ * AllocElem --
+ *     Allocation element.
+ */
+typedef struct AllocElemData {
+    OrderedElemData    elemData;       /* elem in AllocSet */
+    Size               size;
+} AllocElemData;
+
+typedef AllocElemData *AllocElem;
+
+
+/*
+ * Private method definitions
+ */
+
+/*
+ * AllocPointerGetAllocElem --
+ *     Returns allocation (internal) elem given (external) pointer.
+ */
+#define AllocPointerGetAllocElem(pointer)      (&((AllocElem)(pointer))[-1])
+
+/*
+ * AllocElemGetAllocPointer --
+ *     Returns allocation (external) pointer given (internal) elem.
+ */
+#define AllocElemGetAllocPointer(alloc)        ((AllocPointer)&(alloc)[1])
+
+/*
+ * AllocElemIsValid --
+ *     True iff alloc is valid.
+ */
+#define AllocElemIsValid(alloc)        PointerIsValid(alloc)
+
+/* non-export function prototypes */
+static AllocPointer AllocSetGetFirst(AllocSet set);
+static AllocPointer AllocPointerGetNext(AllocPointer pointer);
+
+/*
+ * Public routines
+ */
+
+/* 
+ *     AllocPointerIsValid(pointer)
+ *     AllocSetIsValid(set)    
+ *
+ *             .. are now macros in aset.h -cim 4/27/91
+ */
+
+/*
+ * AllocSetInit --
+ *     Initializes given allocation set.
+ *
+ * Note:
+ *     The semantics of the mode are explained above.  Limit is ignored
+ *     for dynamic and static modes.
+ *
+ * Exceptions:
+ *     BadArg if set is invalid pointer.
+ *     BadArg if mode is invalid.
+ */
+void
+AllocSetInit(AllocSet set, AllocMode mode, Size limit)
+{
+    AssertArg(PointerIsValid(set));
+    AssertArg((int)DynamicAllocMode <= (int)mode);
+    AssertArg((int)mode <= (int)BoundedAllocMode);
+    
+    /*
+     * XXX mode is currently ignored and treated as DynamicAllocMode.
+     * XXX limit is also ignored.  This affects this whole file.
+     */
+    
+    OrderedSetInit(&set->setData, offsetof(AllocElemData, elemData));
+}
+
+/*
+ * AllocSetReset --
+ *     Frees memory which is allocated in the given set.
+ *
+ * Exceptions:
+ *     BadArg if set is invalid.
+ */
+void
+AllocSetReset(AllocSet set)
+{
+    AllocPointer       pointer;
+    
+    AssertArg(AllocSetIsValid(set));
+    
+    while (AllocPointerIsValid(pointer = AllocSetGetFirst(set))) {
+       AllocSetFree(set, pointer);
+    }
+}
+
+void
+AllocSetReset_debug(char *file, int line, AllocSet set)
+{
+    AllocPointer       pointer;
+    
+    AssertArg(AllocSetIsValid(set));
+    
+    while (AllocPointerIsValid(pointer = AllocSetGetFirst(set))) {
+       AllocSetFree(set, pointer);
+    }
+}
+
+/*
+ * AllocSetContains --
+ *     True iff allocation set contains given allocation element.
+ *
+ * Exceptions:
+ *     BadArg if set is invalid.
+ *     BadArg if pointer is invalid.
+ */
+bool
+AllocSetContains(AllocSet set, AllocPointer pointer)
+{
+    AssertArg(AllocSetIsValid(set));
+    AssertArg(AllocPointerIsValid(pointer));
+    
+    return (OrderedSetContains(&set->setData,
+                              &AllocPointerGetAllocElem(pointer)->elemData));
+}
+
+/*
+ * AllocSetAlloc --
+ *     Returns pointer to allocated memory of given size; memory is added
+ *     to the set.
+ *
+ * Exceptions:
+ *     BadArg if set is invalid.
+ *     MemoryExhausted if allocation fails.
+ */
+AllocPointer
+AllocSetAlloc(AllocSet set, Size size)
+{
+    AllocElem  alloc;
+    
+    AssertArg(AllocSetIsValid(set));
+    
+    /* allocate */
+    alloc = (AllocElem)malloc(sizeof (*alloc) + size);
+    
+    if (!PointerIsValid(alloc)) {
+       elog (FATAL, "palloc failure: memory exhausted");
+    }
+    
+    /* add to allocation list */
+    OrderedElemPushInto(&alloc->elemData, &set->setData);
+    
+    /* set size */
+    alloc->size = size;
+    
+    return (AllocElemGetAllocPointer(alloc));
+}
+
+/*
+ * AllocSetFree --
+ *     Frees allocated memory; memory is removed from the set.
+ *
+ * Exceptions:
+ *     BadArg if set is invalid.
+ *     BadArg if pointer is invalid.
+ *     BadArg if pointer is not member of set.
+ */
+void
+AllocSetFree(AllocSet set, AllocPointer pointer)
+{
+    AllocElem  alloc;
+    
+    /* AssertArg(AllocSetIsValid(set)); */
+    /* AssertArg(AllocPointerIsValid(pointer)); */
+    AssertArg(AllocSetContains(set, pointer));
+    
+    alloc = AllocPointerGetAllocElem(pointer);
+    
+    /* remove from allocation set */
+    OrderedElemPop(&alloc->elemData);
+    
+    /* free storage */
+    free(alloc);
+}
+
+/*
+ * AllocSetRealloc --
+ *     Returns new pointer to allocated memory of given size; this memory
+ *     is added to the set.  Memory associated with given pointer is copied
+ *     into the new memory, and the old memory is freed.
+ *
+ * Exceptions:
+ *     BadArg if set is invalid.
+ *     BadArg if pointer is invalid.
+ *     BadArg if pointer is not member of set.
+ *     MemoryExhausted if allocation fails.
+ */
+AllocPointer
+AllocSetRealloc(AllocSet set, AllocPointer pointer, Size size)
+{
+    AllocPointer       newPointer;
+    AllocElem  alloc;
+    
+    /* AssertArg(AllocSetIsValid(set)); */
+    /* AssertArg(AllocPointerIsValid(pointer)); */
+    AssertArg(AllocSetContains(set, pointer));
+    
+    /*
+     * Calling realloc(3) directly is not be possible (unless we use
+     * our own hacked version of malloc) since we must keep the
+     * allocations in the allocation set.
+     */
+    
+    alloc = AllocPointerGetAllocElem(pointer);
+    
+    /* allocate new pointer */
+    newPointer = AllocSetAlloc(set, size);
+    
+    /* fill new memory */
+    memmove(newPointer, pointer, (alloc->size < size) ? alloc->size : size);
+    
+    /* free old pointer */
+    AllocSetFree(set, pointer);
+    
+    return (newPointer);
+}
+
+/*
+ * AllocSetIterate --
+ *     Returns size of set.  Iterates through set elements calling function
+ *     (if valid) on each.
+ *
+ * Note:
+ *     This was written as an aid to debugging.  It is intended for
+ *     debugging use only.
+ *
+ * Exceptions:
+ *     BadArg if set is invalid.
+ */
+int
+AllocSetIterate(AllocSet set,
+               void (*function)(AllocPointer pointer))
+{
+    int                count = 0;
+    AllocPointer       pointer;
+    
+    AssertArg(AllocSetIsValid(set));
+    
+    for (pointer = AllocSetGetFirst(set);
+        AllocPointerIsValid(pointer);
+        pointer = AllocPointerGetNext(pointer)) {
+       
+       if (PointerIsValid(function)) {
+           (*function)(pointer);
+       }
+       count += 1;
+    }
+    
+    return (count);
+}
+
+int
+AllocSetCount(AllocSet set)
+{
+    int                count = 0;
+    AllocPointer       pointer;
+    
+    AssertArg(AllocSetIsValid(set));
+    
+    for (pointer = AllocSetGetFirst(set);
+        AllocPointerIsValid(pointer);
+        pointer = AllocPointerGetNext(pointer)) {
+       count++;
+    }
+    return count;
+}
+
+/*
+ * Private routines
+ */
+
+/*
+ * AllocSetGetFirst --
+ *     Returns "first" allocation pointer in a set.
+ *
+ * Note:
+ *     Assumes set is valid.
+ */
+static AllocPointer
+AllocSetGetFirst(AllocSet set)
+{
+    AllocElem  alloc;
+    
+    alloc = (AllocElem)OrderedSetGetHead(&set->setData);
+    
+    if (!AllocElemIsValid(alloc)) {
+       return (NULL);
+    }
+    
+    return (AllocElemGetAllocPointer(alloc));
+}
+
+/*
+ * AllocPointerGetNext --
+ *     Returns "successor" allocation pointer.
+ *
+ * Note:
+ *     Assumes pointer is valid.
+ */
+static AllocPointer
+AllocPointerGetNext(AllocPointer pointer)
+{
+    AllocElem  alloc;
+    
+    alloc = (AllocElem)
+       OrderedElemGetSuccessor(&AllocPointerGetAllocElem(pointer)->elemData);
+    
+    if (!AllocElemIsValid(alloc)) {
+       return (NULL);
+    }
+    
+    return (AllocElemGetAllocPointer(alloc));
+}
+
+
+/*
+ * Debugging routines
+ */
+
+/*
+ * XXX AllocPointerDump --
+ *     Displays allocated pointer.
+ */
+void
+AllocPointerDump(AllocPointer pointer)
+{
+    printf("\t%-10d@ %0#x\n", ((long*)pointer)[-1], pointer); /* XXX */
+}
+
+/*
+ * AllocSetDump --
+ *     Displays allocated set.
+ */
+void
+AllocSetDump(AllocSet set)
+{
+    int count;
+    count = AllocSetIterate(set, AllocPointerDump);
+    printf("\ttotal %d allocations\n", count);
+}
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
new file mode 100644 (file)
index 0000000..6e74ac8
--- /dev/null
@@ -0,0 +1,510 @@
+/*-------------------------------------------------------------------------
+ *
+ * mcxt.c--
+ *    POSTGRES memory context code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>     /* XXX for printf debugging */
+
+#include "postgres.h"
+
+#include "utils/memutils.h"
+#include "utils/module.h"
+#include "utils/excid.h"
+
+#include "nodes/memnodes.h"
+#include "nodes/nodes.h"
+
+#include "utils/mcxt.h"
+#include "utils/elog.h"
+
+#include "utils/palloc.h"
+
+#undef MemoryContextAlloc
+#undef MemoryContextFree
+#undef malloc
+#undef free
+
+/*
+ * Global State
+ */
+static int MemoryContextEnableCount = 0;
+#define MemoryContextEnabled   (MemoryContextEnableCount > 0)
+
+static OrderedSetData  ActiveGlobalMemorySetData;      /* uninitialized */
+#define ActiveGlobalMemorySet  (&ActiveGlobalMemorySetData)
+
+/*
+ * description of allocated memory representation goes here
+ */
+
+#define PSIZE(PTR)     (*((int32 *)(PTR) - 1))
+#define PSIZEALL(PTR)  (*((int32 *)(PTR) - 1) + sizeof (int32))
+#define PSIZESKIP(PTR) ((char *)((int32 *)(PTR) + 1))
+#define PSIZEFIND(PTR) ((char *)((int32 *)(PTR) - 1))
+#define PSIZESPACE(LEN)        ((LEN) + sizeof (int32))
+
+/*
+ * AllocSizeIsValid --
+ *     True iff 0 < size and size <= MaxAllocSize.
+ */
+#define        AllocSizeIsValid(size)  (0 < (size) && (size) <= MaxAllocSize)
+
+/*****************************************************************************
+ *    GLOBAL MEMORY                                                          *
+ *****************************************************************************/
+
+/*
+ * CurrentMemoryContext --
+ *     Memory context for general global allocations.
+ */
+MemoryContext  CurrentMemoryContext = NULL;
+
+/*****************************************************************************
+ *    PRIVATE DEFINITIONS                                                    *
+ *****************************************************************************/
+
+static Pointer GlobalMemoryAlloc(GlobalMemory this, Size size);
+static void GlobalMemoryFree(GlobalMemory this,        Pointer pointer);
+static Pointer GlobalMemoryRealloc(GlobalMemory this, Pointer pointer,
+                                  Size size);
+static char *GlobalMemoryGetName(GlobalMemory this);
+static void GlobalMemoryDump(GlobalMemory this);
+static void DumpGlobalMemories(void);
+
+
+/*
+ * Global Memory Methods
+ */
+
+static struct MemoryContextMethodsData GlobalContextMethodsData = {
+    GlobalMemoryAlloc,   /* Pointer (*)(this, uint32)  palloc */
+    GlobalMemoryFree,    /* void (*)(this, Pointer)    pfree */
+    GlobalMemoryRealloc,  /* Pointer (*)(this, Pointer)        repalloc */
+    GlobalMemoryGetName,  /* char* (*)(this)           getName */
+    GlobalMemoryDump     /* void (*)(this)             dump */
+};
+
+/*
+ * Note:
+ *     TopGlobalMemory is handled specially because of bootstrapping.
+ */
+/* extern bool EqualGlobalMemory(); */
+
+static struct GlobalMemory TopGlobalMemoryData = {
+    T_GlobalMemory,            /* NodeTag              tag       */
+    &GlobalContextMethodsData, /* ContextMethods       method    */
+    { { 0 } },                 /* uninitialized
+                                 * OrderedSetData allocSetD
+                                */
+    "TopGlobal",               /* char* name      */
+    { 0 }                      /* uninitialized OrderedElemData elemD */
+};
+
+/*
+ * TopMemoryContext --
+ *     Memory context for general global allocations.
+ *
+ * Note:
+ *     Don't use this memory context for random allocations.  If you
+ *     allocate something here, you are expected to clean it up when
+ *     appropriate.
+ */
+MemoryContext  TopMemoryContext =  (MemoryContext)&TopGlobalMemoryData;
+
+
+
+
+/*
+ * Module State
+ */
+
+/*
+ * EnableMemoryContext --
+ *     Enables/disables memory management and global contexts.
+ *
+ * Note:
+ *     This must be called before creating contexts or allocating memory.
+ *     This must be called before other contexts are created.
+ *
+ * Exceptions:
+ *     BadArg if on is invalid.
+ *     BadState if on is false when disabled.
+ */
+void
+EnableMemoryContext(bool on)
+{
+    static bool        processing = false;
+    
+    AssertState(!processing);
+    AssertArg(BoolIsValid(on));
+    
+    if (BypassEnable(&MemoryContextEnableCount, on)) {
+       return;
+    }
+    
+    processing = true;
+    
+    if (on) {  /* initialize */
+       /* initialize TopGlobalMemoryData.setData */
+       AllocSetInit(&TopGlobalMemoryData.setData, DynamicAllocMode,
+                    (Size)0);
+       
+       /* make TopGlobalMemoryData member of ActiveGlobalMemorySet */
+       OrderedSetInit(ActiveGlobalMemorySet,
+                      offsetof(struct GlobalMemory, elemData));
+       OrderedElemPushInto(&TopGlobalMemoryData.elemData,
+                           ActiveGlobalMemorySet);
+       
+       /* initialize CurrentMemoryContext */
+       CurrentMemoryContext = TopMemoryContext;
+       
+    } else {   /* cleanup */
+       GlobalMemory    context;
+       
+       /* walk the list of allocations */
+       while (PointerIsValid(context = (GlobalMemory)
+                             OrderedSetGetHead(ActiveGlobalMemorySet))) {
+           
+           if (context == &TopGlobalMemoryData) {
+               /* don't free it and clean it last */
+               OrderedElemPop(&TopGlobalMemoryData.elemData);
+           } else {
+               GlobalMemoryDestroy(context);
+           }
+           /* what is needed for the top? */
+       }
+       
+       /*
+        * Freeing memory here should be safe as this is called
+        * only after all modules which allocate in TopMemoryContext
+        * have been disabled.
+        */
+       
+       /* step through remaining allocations and log */
+       /* AllocSetStep(...); */
+       
+       /* deallocate whatever is left */
+       AllocSetReset(&TopGlobalMemoryData.setData);
+    }
+    
+    processing = false;
+}
+
+/*
+ * MemoryContextAlloc --
+ *     Returns pointer to aligned allocated memory in the given context.
+ *
+ * Note:
+ *     none
+ *
+ * Exceptions:
+ *     BadState if called before InitMemoryManager.
+ *     BadArg if context is invalid or if size is 0.
+ *     BadAllocSize if size is larger than MaxAllocSize.
+ */
+Pointer
+MemoryContextAlloc(MemoryContext context, Size size)
+{
+    AssertState(MemoryContextEnabled);
+    AssertArg(MemoryContextIsValid(context));
+    
+    LogTrap(!AllocSizeIsValid(size), BadAllocSize,
+           ("size=%d [0x%x]", size, size));
+    
+    return (context->method->alloc(context, size));
+}
+
+/*
+ * MemoryContextFree --
+ *     Frees allocated memory referenced by pointer in the given context.
+ *
+ * Note:
+ *     none
+ *
+ * Exceptions:
+ *     ???
+ *     BadArgumentsErr if firstTime is true for subsequent calls.
+ */
+void
+MemoryContextFree(MemoryContext context, Pointer pointer)
+{
+    AssertState(MemoryContextEnabled);
+    AssertArg(MemoryContextIsValid(context));
+    AssertArg(PointerIsValid(pointer));
+    
+    context->method->free_p(context, pointer);
+}
+
+/*
+ * MemoryContextRelloc --
+ *     Returns pointer to aligned allocated memory in the given context.
+ *
+ * Note:
+ *     none
+ *
+ * Exceptions:
+ *     ???
+ *     BadArgumentsErr if firstTime is true for subsequent calls.
+ */
+Pointer
+MemoryContextRealloc(MemoryContext context,
+                    Pointer    pointer,
+                    Size size)
+{
+    AssertState(MemoryContextEnabled);
+    AssertArg(MemoryContextIsValid(context));
+    AssertArg(PointerIsValid(pointer));
+    
+    LogTrap(!AllocSizeIsValid(size), BadAllocSize,
+           ("size=%d [0x%x]", size, size));
+    
+    return (context->method->realloc(context, pointer, size));
+}
+
+/*
+ * MemoryContextGetName --
+ *     Returns pointer to aligned allocated memory in the given context.
+ *
+ * Note:
+ *     none
+ *
+ * Exceptions:
+ *     ???
+ *     BadArgumentsErr if firstTime is true for subsequent calls.
+ */
+char*
+MemoryContextGetName(MemoryContext context)
+{
+    AssertState(MemoryContextEnabled);
+    AssertArg(MemoryContextIsValid(context));
+    
+    return (context->method->getName(context));
+}
+
+/*
+ * PointerGetAllocSize --
+ *     Returns size of aligned allocated memory given pointer to it.
+ *
+ * Note:
+ *     none
+ *
+ * Exceptions:
+ *     ???
+ *     BadArgumentsErr if firstTime is true for subsequent calls.
+ */
+Size
+PointerGetAllocSize(Pointer pointer)
+{
+    AssertState(MemoryContextEnabled);
+    AssertArg(PointerIsValid(pointer));
+    
+    return (PSIZE(pointer));
+}
+
+/*
+ * MemoryContextSwitchTo --
+ *     Returns the current context; installs the given context.
+ *
+ * Note:
+ *     none
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadArg if context is invalid.
+ */
+MemoryContext
+MemoryContextSwitchTo(MemoryContext context)
+{
+    MemoryContext      old;
+    
+    AssertState(MemoryContextEnabled);
+    AssertArg(MemoryContextIsValid(context));
+    
+    old = CurrentMemoryContext;
+    CurrentMemoryContext = context;
+    return (old);
+}
+
+/*
+ * External Functions
+ */
+/*
+ * CreateGlobalMemory --
+ *     Returns new global memory context.
+ *
+ * Note:
+ *     Assumes name is static.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadState if called outside TopMemoryContext (TopGlobalMemory).
+ *     BadArg if name is invalid.
+ */
+GlobalMemory
+CreateGlobalMemory(char  *name)        /* XXX MemoryContextName */
+{
+    GlobalMemory       context;
+    MemoryContext      savecxt;
+    
+    AssertState(MemoryContextEnabled);
+    
+    savecxt = MemoryContextSwitchTo(TopMemoryContext);
+    
+    context = (GlobalMemory)newNode(sizeof(struct GlobalMemory), T_GlobalMemory);
+    context->method = &GlobalContextMethodsData;
+    context->name = name;              /* assumes name is static */
+    AllocSetInit(&context->setData, DynamicAllocMode, (Size)0);
+    
+    /* link the context */
+    OrderedElemPushInto(&context->elemData, ActiveGlobalMemorySet);
+    
+    MemoryContextSwitchTo(savecxt);
+    return (context);
+}
+
+/*
+ * GlobalMemoryDestroy --
+ *     Destroys given global memory context.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadState if called outside TopMemoryContext (TopGlobalMemory).
+ *     BadArg if context is invalid GlobalMemory.
+ *     BadArg if context is TopMemoryContext (TopGlobalMemory).
+ */
+void
+GlobalMemoryDestroy(GlobalMemory context)
+{
+    AssertState(MemoryContextEnabled);
+    AssertArg(IsA(context,GlobalMemory));
+    AssertArg(context != &TopGlobalMemoryData);
+    
+    AllocSetReset(&context->setData);
+    
+    /* unlink and delete the context */
+    OrderedElemPop(&context->elemData);
+    MemoryContextFree(TopMemoryContext, (Pointer)context);
+}
+
+/*****************************************************************************
+ *    PRIVATE                                                                *
+ *****************************************************************************/
+
+/*
+ * GlobalMemoryAlloc --
+ *     Returns pointer to aligned space in the global context.
+ *
+ * Exceptions:
+ *     ExhaustedMemory if allocation fails.
+ */
+static Pointer
+GlobalMemoryAlloc(GlobalMemory this, Size size)
+{
+    return (AllocSetAlloc(&this->setData, size));
+}
+
+/*
+ * GlobalMemoryFree --
+ *     Frees allocated memory in the global context.
+ *
+ * Exceptions:
+ *     BadContextErr if current context is not the global context.
+ *     BadArgumentsErr if pointer is invalid.
+ */
+static void
+GlobalMemoryFree(GlobalMemory this,
+                Pointer pointer)
+{
+    AllocSetFree(&this->setData, pointer);
+}
+
+/*
+ * GlobalMemoryRealloc --
+ *     Returns pointer to aligned space in the global context.
+ *
+ * Note:
+ *     Memory associated with the pointer is freed before return.
+ *
+ * Exceptions:
+ *     BadContextErr if current context is not the global context.
+ *     BadArgumentsErr if pointer is invalid.
+ *     NoMoreMemoryErr if allocation fails.
+ */
+static Pointer
+GlobalMemoryRealloc(GlobalMemory this,
+                   Pointer pointer,
+                   Size size)
+{
+    return (AllocSetRealloc(&this->setData, pointer, size));
+}
+
+/*
+ * GlobalMemoryGetName --
+ *     Returns name string for context.
+ *
+ * Exceptions:
+ *     ???
+ */
+static char*
+GlobalMemoryGetName(GlobalMemory this)
+{
+    return (this->name);
+}
+
+/*
+ * GlobalMemoryDump --
+ *     Dumps global memory context for debugging.
+ *
+ * Exceptions:
+ *     ???
+ */
+static void
+GlobalMemoryDump(GlobalMemory this)
+{
+    GlobalMemory       context;
+    
+    printf("--\n%s:\n", GlobalMemoryGetName(this));
+    
+    context = (GlobalMemory)OrderedElemGetPredecessor(&this->elemData);
+    if (PointerIsValid(context)) {
+       printf("\tpredecessor=%s\n", GlobalMemoryGetName(context));
+    }
+    
+    context = (GlobalMemory)OrderedElemGetSuccessor(&this->elemData);
+    if (PointerIsValid(context)) {
+       printf("\tsucessor=%s\n", GlobalMemoryGetName(context));
+    }
+    
+    AllocSetDump(&this->setData);      /* XXX is this right interface */
+}
+
+/*
+ * DumpGlobalMemories --
+ *     Dumps all global memory contexts for debugging.
+ *
+ * Exceptions:
+ *     ???
+ */
+static void
+DumpGlobalMemories()
+{
+    GlobalMemory       context;
+    
+    context = (GlobalMemory)OrderedSetGetHead(&ActiveGlobalMemorySetData);
+    
+    while (PointerIsValid(context)) {
+       GlobalMemoryDump(context);
+       
+       context = (GlobalMemory)OrderedElemGetSuccessor(
+                                                       &context->elemData);
+    }
+}
+
diff --git a/src/backend/utils/mmgr/oset.c b/src/backend/utils/mmgr/oset.c
new file mode 100644 (file)
index 0000000..4e37585
--- /dev/null
@@ -0,0 +1,173 @@
+/*-------------------------------------------------------------------------
+ *
+ * oset.c--
+ *    Fixed format ordered set definitions.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTE
+ *    XXX This is a preliminary implementation which lacks fail-fast
+ *    XXX validity checking of arguments.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+
+#include "utils/memutils.h"    /* where declarations of this file goes */
+
+static Pointer OrderedElemGetBase(OrderedElem elem);
+static void OrderedElemInit(OrderedElem elem, OrderedSet set);
+static void OrderedElemPush(OrderedElem elem);
+static void OrderedElemPushHead(OrderedElem elem);
+
+/*
+ * OrderedElemGetBase --
+ *     Returns base of enclosing structure.
+ */
+static Pointer
+OrderedElemGetBase(OrderedElem elem)
+{
+    if (elem == (OrderedElem) NULL)
+       return (Pointer) NULL;
+    
+    return ((Pointer)((char*)(elem) - (elem)->set->offset));
+}
+
+/*
+ * OrderedSetInit --
+ */
+void
+OrderedSetInit(OrderedSet set, Offset offset)
+{
+    set->head = (OrderedElem)&set->dummy;
+    set->dummy = NULL;
+    set->tail = (OrderedElem)&set->head;
+    set->offset = offset;
+}
+
+/*
+ * OrderedElemInit --
+ */
+static void
+OrderedElemInit(OrderedElem elem, OrderedSet set)
+{
+    elem->set = set;
+    /* mark as unattached */
+    elem->next = NULL;
+    elem->prev = NULL;
+}
+
+/*
+ * OrderedSetContains --
+ *     True iff ordered set contains given element.
+ */
+bool
+OrderedSetContains(OrderedSet set, OrderedElem elem)
+{
+    return ((bool)(elem->set == set && (elem->next || elem->prev)));
+}
+
+/*
+ * OrderedSetGetHead --
+ */
+Pointer
+OrderedSetGetHead(OrderedSet set)
+{
+    register OrderedElem       elem;
+    
+    elem = set->head;
+    if (elem->next) {
+       return (OrderedElemGetBase(elem));
+    }
+    return (NULL);
+}
+
+/*
+ * OrderedSetGetTail --
+ */
+Pointer
+OrderedSetGetTail(OrderedSet set)
+{
+    register OrderedElem       elem;
+    
+    elem = set->tail;
+    if (elem->prev) {
+       return (OrderedElemGetBase(elem));
+    }
+    return (NULL);
+}
+
+/*
+ * OrderedElemGetPredecessor --
+ */
+Pointer
+OrderedElemGetPredecessor(OrderedElem elem)
+{
+    elem = elem->prev;
+    if (elem->prev) {
+       return (OrderedElemGetBase(elem));
+    }
+    return (NULL);
+}
+
+/*
+ * OrderedElemGetSuccessor --
+ */
+Pointer
+OrderedElemGetSuccessor(OrderedElem elem)
+{
+    elem = elem->next;
+    if (elem->next) {
+       return (OrderedElemGetBase(elem));
+    }
+    return (NULL);
+}
+
+/*
+ * OrderedElemPop --
+ */
+void 
+OrderedElemPop(OrderedElem elem)
+{
+    elem->next->prev = elem->prev;
+    elem->prev->next = elem->next;
+    /* assignments used only for error detection */
+    elem->next = NULL;
+    elem->prev = NULL;
+}
+
+/*
+ * OrderedElemPushInto --
+ */
+void
+OrderedElemPushInto(OrderedElem elem, OrderedSet set)
+{
+    OrderedElemInit(elem, set);
+    OrderedElemPush(elem);
+}
+
+/*
+ * OrderedElemPush --
+ */
+static void
+OrderedElemPush(OrderedElem elem)
+{
+    OrderedElemPushHead(elem);
+}
+
+/*
+ * OrderedElemPushHead --
+ */
+static void
+OrderedElemPushHead(OrderedElem elem)
+{
+    elem->next = elem->set->head;
+    elem->prev = (OrderedElem)&elem->set->head;
+    elem->next->prev = elem;
+    elem->prev->next = elem;
+}
+
diff --git a/src/backend/utils/mmgr/palloc.c b/src/backend/utils/mmgr/palloc.c
new file mode 100644 (file)
index 0000000..d095846
--- /dev/null
@@ -0,0 +1,117 @@
+/*-------------------------------------------------------------------------
+ *
+ * palloc.c--
+ *    POSTGRES memory allocator code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+
+#include "utils/mcxt.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+
+#include "nodes/memnodes.h"
+
+#include "utils/palloc.h"
+
+/* ----------------------------------------------------------------
+ *     User library functions
+ * ----------------------------------------------------------------
+ */
+
+#undef palloc
+#undef pfree
+#undef MemoryContextAlloc
+#undef MemoryContextFree
+#undef malloc
+#undef free
+
+/* define PALLOC_IS_MALLOC if you want palloc to go straight to the 
+   raw malloc, without concern for the extra bookkeeping needed to
+   ensure garbage is collected at the end of transactions - jolly 1/12/94 */
+
+
+/*
+ * palloc --
+ *     Returns pointer to aligned memory of specified size.
+ *
+ * Exceptions:
+ *     BadArgument if size < 1 or size >= MaxAllocSize.
+ *     ExhaustedMemory if allocation fails.
+ *     NonallocatedPointer if pointer was not returned by palloc or repalloc
+ *             or may have been freed already.
+ *
+ * pfree --
+ *     Frees memory associated with pointer returned from palloc or repalloc.
+ *
+ * Exceptions:
+ *     BadArgument if pointer is invalid.
+ *     FreeInWrongContext if pointer was allocated in a different "context."
+ *     NonallocatedPointer if pointer was not returned by palloc or repalloc
+ *             or may have been subsequently freed.
+ */
+void*
+palloc(Size size)
+{
+#ifdef PALLOC_IS_MALLOC
+   return malloc(size); 
+#else
+   return (MemoryContextAlloc(CurrentMemoryContext, size));  
+#endif /* PALLOC_IS_MALLOC */
+}
+
+void
+pfree(void *pointer)
+{      
+#ifdef PALLOC_IS_MALLOC
+  free(pointer); 
+#else
+  MemoryContextFree(CurrentMemoryContext, pointer); 
+#endif /* PALLOC_IS_MALLOC */
+}
+
+/*
+ * repalloc --
+ *     Returns pointer to aligned memory of specified size.
+ *
+ * Side effects:
+ *     The returned memory is first filled with the contents of *pointer
+ *     up to the minimum of size and psize(pointer).  Pointer is freed.
+ *
+ * Exceptions:
+ *     BadArgument if pointer is invalid or size < 1 or size >= MaxAllocSize.
+ *     ExhaustedMemory if allocation fails.
+ *     NonallocatedPointer if pointer was not returned by palloc or repalloc
+ *             or may have been freed already.
+ */
+void *
+repalloc(void *pointer, Size size)
+{
+#ifdef PALLOC_IS_MALLOC
+  return realloc(pointer, size); 
+#else
+  return (MemoryContextRealloc(CurrentMemoryContext, pointer, size)); 
+#endif
+}
+
+/* pstrdup 
+    allocates space for and copies a string
+    just like strdup except it uses palloc instead of malloc */
+char*
+pstrdup(char* string)
+{
+    char *nstr;
+
+    nstr = strcpy((char *)palloc(strlen(string)+1), string);
+    return nstr;
+}
+
+
+
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
new file mode 100644 (file)
index 0000000..d784f71
--- /dev/null
@@ -0,0 +1,980 @@
+/*-------------------------------------------------------------------------
+ *
+ * portalmem.c--
+ *    backend portal memory context management stuff
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * NOTES
+ *     Do not confuse "Portal" with "PortalEntry" (or "PortalBuffer").
+ *     When a PQexec() routine is run, the resulting tuples
+ *     find their way into a "PortalEntry".  The contents of the resulting
+ *     "PortalEntry" can then be inspected by other PQxxx functions.
+ *
+ *     A "Portal" is a structure used to keep track of queries of the
+ *     form:
+ *             retrieve portal FOO ( blah... ) where blah...
+ *
+ *     When the backend sees a "retrieve portal" query, it allocates
+ *     a "PortalD" structure, plans the query and then stores the query
+ *     in the portal without executing it.  Later, when the backend
+ *     sees a
+ *             fetch 1 into FOO
+ *
+ *     the system looks up the portal named "FOO" in the portal table,
+ *     gets the planned query and then calls the executor with a feature of
+ *     '(EXEC_FOR 1).  The executor then runs the query and returns a single
+ *     tuple.  The problem is that we have to hold onto the state of the
+ *     portal query until we see a "close p".  This means we have to be
+ *     careful about memory management.
+ *
+ *     Having said all that, here is what a PortalD currently looks like:
+ *
+ * struct PortalD {
+ *     char*                           name;
+ *     classObj(PortalVariableMemory)  variable;
+ *     classObj(PortalHeapMemory)      heap;
+ *     List                            queryDesc;
+ *     EState                          state;
+ *     void                            (*cleanup) ARGS((Portal portal));
+ * };
+ *
+ *     I hope this makes things clearer to whoever reads this -cim 2/22/91
+ *
+ *     Here is an old comment taken from nodes/memnodes.h
+ *
+ * MemoryContext --
+ *     A logical context in which memory allocations occur.
+ *
+ * The types of memory contexts can be thought of as members of the
+ * following inheritance hierarchy with properties summarized below.
+ *
+ *                     Node
+ *                     |
+ *             MemoryContext___
+ *             /               \
+ *     GlobalMemory    PortalMemoryContext
+ *                     /               \
+ *     PortalVariableMemory    PortalHeapMemory
+ *
+ *                     Flushed at      Flushed at      Checkpoints
+ *                     Transaction     Portal
+ *                     Commit          Close
+ *
+ * GlobalMemory                        n               n               n
+ * PortalVariableMemory                n               y               n
+ * PortalHeapMemory            y               y               y *     
+ *
+ */
+#include <stdio.h>             /* for sprintf() */
+#include <string.h>    /* for strlen, strncpy */
+
+#include "c.h"
+
+#include "lib/hasht.h"
+#include "utils/module.h"
+#include "utils/excid.h"       /* for Unimplemented */
+#include "utils/elog.h"
+#include "utils/mcxt.h"
+#include "utils/hsearch.h"
+
+#include "nodes/memnodes.h"
+#include "nodes/nodes.h"
+#include "nodes/pg_list.h"
+#include "nodes/execnodes.h"   /* for EState */
+
+#include "utils/portal.h"
+
+/* ----------------
+ *     ALLOCFREE_ERROR_ABORT
+ *     define this if you want a core dump when you try to
+ *     free memory already freed -cim 2/9/91
+ * ----------------
+ */
+#undef ALLOCFREE_ERROR_ABORT
+
+/* ----------------
+ *     Global state
+ * ----------------
+ */
+
+static int PortalManagerEnableCount = 0;
+#define MAX_PORTALNAME_LEN     64      /* XXX LONGALIGNable value */
+
+typedef struct portalhashent {
+    char portalname[MAX_PORTALNAME_LEN];
+    Portal portal;
+} PortalHashEnt;
+
+#define PortalManagerEnabled   (PortalManagerEnableCount >= 1)
+
+static HTAB            *PortalHashTable = NULL;
+#define PortalHashTableLookup(NAME, PORTAL) \
+    {   PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \
+       memset(key, 0, MAX_PORTALNAME_LEN); \
+       sprintf(key, "%s", NAME); \
+       hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
+                                            key, HASH_FIND, &found); \
+       if (hentry == NULL) \
+           elog(FATAL, "error in PortalHashTable"); \
+       if (found) \
+           PORTAL = hentry->portal; \
+       else \
+           PORTAL = NULL; \
+    }
+#define PortalHashTableInsert(PORTAL) \
+    {   PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \
+       memset(key, 0, MAX_PORTALNAME_LEN); \
+       sprintf(key, "%s", PORTAL->name); \
+       hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
+                                            key, HASH_ENTER, &found); \
+       if (hentry == NULL) \
+           elog(FATAL, "error in PortalHashTable"); \
+       if (found) \
+           elog(NOTICE, "trying to insert a portal name that exists."); \
+       hentry->portal = PORTAL; \
+    }
+#define PortalHashTableDelete(PORTAL) \
+    {   PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \
+       memset(key, 0, MAX_PORTALNAME_LEN); \
+       sprintf(key, "%s", PORTAL->name); \
+       hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
+                                            key, HASH_REMOVE, &found); \
+       if (hentry == NULL) \
+           elog(FATAL, "error in PortalHashTable"); \
+       if (!found) \
+           elog(NOTICE, "trying to delete portal name that does not exist."); \
+    }
+
+static GlobalMemory    PortalMemory = NULL;
+static char            PortalMemoryName[] = "Portal";
+
+static Portal          BlankPortal = NULL;
+
+/* ----------------
+ *     Internal class definitions
+ * ----------------
+ */
+typedef struct HeapMemoryBlockData {
+    AllocSetData       setData;
+    FixedItemData      itemData;
+} HeapMemoryBlockData;
+
+typedef HeapMemoryBlockData    *HeapMemoryBlock;
+
+#define HEAPMEMBLOCK(context) \
+    ((HeapMemoryBlock)(context)->block)
+
+/* ----------------------------------------------------------------
+ *               Variable and heap memory methods
+ * ----------------------------------------------------------------
+ */
+/* ----------------
+ *     PortalVariableMemoryAlloc
+ * ----------------
+ */
+static Pointer
+PortalVariableMemoryAlloc(PortalVariableMemory this,
+                         Size size)
+{
+    return (AllocSetAlloc(&this->setData, size));
+}
+
+/* ----------------
+ *     PortalVariableMemoryFree
+ * ----------------
+ */
+static void
+PortalVariableMemoryFree(PortalVariableMemory this,
+                        Pointer        pointer)
+{
+    AllocSetFree(&this->setData, pointer);
+}
+
+/* ----------------
+ *     PortalVariableMemoryRealloc
+ * ----------------
+ */
+static Pointer
+PortalVariableMemoryRealloc(PortalVariableMemory this,
+                           Pointer pointer,
+                           Size size)
+{
+    return (AllocSetRealloc(&this->setData, pointer, size));
+}
+
+/* ----------------
+ *     PortalVariableMemoryGetName
+ * ----------------
+ */
+static char*
+PortalVariableMemoryGetName(PortalVariableMemory this)
+{
+    return (form("%s-var", PortalVariableMemoryGetPortal(this)->name));
+}
+
+/* ----------------
+ *     PortalVariableMemoryDump
+ * ----------------
+ */
+static void
+PortalVariableMemoryDump(PortalVariableMemory this)
+{
+    printf("--\n%s:\n", PortalVariableMemoryGetName(this));
+    
+    AllocSetDump(&this->setData);      /* XXX is this the right interface */
+}
+
+/* ----------------
+ *     PortalHeapMemoryAlloc
+ * ----------------
+ */
+static Pointer
+PortalHeapMemoryAlloc(PortalHeapMemory this,
+                     Size size)
+{
+    HeapMemoryBlock    block = HEAPMEMBLOCK(this);
+    
+    AssertState(PointerIsValid(block));
+    
+    return (AllocSetAlloc(&block->setData, size));
+}
+
+/* ----------------
+ *     PortalHeapMemoryFree
+ * ----------------
+ */
+static void
+PortalHeapMemoryFree(PortalHeapMemory this,
+                    Pointer pointer)
+{
+    HeapMemoryBlock    block = HEAPMEMBLOCK(this);
+    
+    AssertState(PointerIsValid(block));
+    
+    if (AllocSetContains(&block->setData, pointer))
+       AllocSetFree(&block->setData, pointer);
+    else {
+       elog(NOTICE,
+            "PortalHeapMemoryFree: 0x%x not in alloc set!",
+            pointer);
+#ifdef ALLOCFREE_ERROR_ABORT
+       Assert(AllocSetContains(&block->setData, pointer));
+#endif /* ALLOCFREE_ERROR_ABORT*/
+    }
+}
+
+/* ----------------
+ *     PortalHeapMemoryRealloc
+ * ----------------
+ */
+static Pointer
+PortalHeapMemoryRealloc(PortalHeapMemory this,
+                       Pointer pointer,
+                       Size size)
+{
+    HeapMemoryBlock    block = HEAPMEMBLOCK(this);
+    
+    AssertState(PointerIsValid(block));
+    
+    return (AllocSetRealloc(&block->setData, pointer, size));
+}
+
+/* ----------------
+ *     PortalHeapMemoryGetName
+ * ----------------
+ */
+static char*
+PortalHeapMemoryGetName(PortalHeapMemory this)
+{
+    return (form("%s-heap", PortalHeapMemoryGetPortal(this)->name));
+}
+
+/* ----------------
+ *     PortalHeapMemoryDump
+ * ----------------
+ */
+static void
+PortalHeapMemoryDump(PortalHeapMemory this)
+{
+    HeapMemoryBlock    block;
+    
+    printf("--\n%s:\n", PortalHeapMemoryGetName(this));
+    
+    /* XXX is this the right interface */
+    if (PointerIsValid(this->block))
+       AllocSetDump(&HEAPMEMBLOCK(this)->setData);
+    
+    /* dump the stack too */
+    for (block = (HeapMemoryBlock)FixedStackGetTop(&this->stackData);
+        PointerIsValid(block);
+        block = (HeapMemoryBlock)
+        FixedStackGetNext(&this->stackData, (Pointer)block)) {
+       
+       printf("--\n");
+       AllocSetDump(&block->setData);
+    }
+}
+
+/* ----------------------------------------------------------------
+ *             variable / heap context method tables
+ * ----------------------------------------------------------------
+ */
+static struct MemoryContextMethodsData PortalVariableContextMethodsData = {
+    PortalVariableMemoryAlloc, /* Pointer (*)(this, uint32)    palloc */
+    PortalVariableMemoryFree,  /* void (*)(this, Pointer)      pfree */
+    PortalVariableMemoryRealloc,/* Pointer (*)(this, Pointer)  repalloc */
+    PortalVariableMemoryGetName,/* char* (*)(this)             getName */
+    PortalVariableMemoryDump   /* void (*)(this)               dump */
+};
+
+static struct MemoryContextMethodsData PortalHeapContextMethodsData = {
+    PortalHeapMemoryAlloc,     /* Pointer (*)(this, uint32)    palloc */
+    PortalHeapMemoryFree,      /* void (*)(this, Pointer)      pfree */
+    PortalHeapMemoryRealloc,    /* Pointer (*)(this, Pointer)  repalloc */
+    PortalHeapMemoryGetName,    /* char* (*)(this)             getName */
+    PortalHeapMemoryDump       /* void (*)(this)               dump */
+};
+
+
+/* ----------------------------------------------------------------
+ *               private internal support routines
+ * ----------------------------------------------------------------
+ */
+/* ----------------
+ *     CreateNewBlankPortal
+ * ----------------
+ */
+static void
+CreateNewBlankPortal()
+{
+    Portal     portal;
+    
+    AssertState(!PortalIsValid(BlankPortal));
+    
+    /*
+     * make new portal structure
+     */
+    portal = (Portal)
+       MemoryContextAlloc((MemoryContext)PortalMemory, sizeof *portal);
+    
+    /*
+     * initialize portal variable context
+     */
+    NodeSetTag((Node*)&portal->variable, T_PortalVariableMemory);
+    AllocSetInit(&portal->variable.setData, DynamicAllocMode, (Size)0);
+    portal->variable.method = &PortalVariableContextMethodsData;
+    
+    /*
+     * initialize portal heap context
+     */
+    NodeSetTag((Node*)&portal->heap, T_PortalHeapMemory);
+    portal->heap.block = NULL;
+    FixedStackInit(&portal->heap.stackData,
+                  offsetof (HeapMemoryBlockData, itemData));
+    portal->heap.method = &PortalHeapContextMethodsData;
+    
+    /*
+     * set bogus portal name
+     */
+    portal->name = "** Blank Portal **";
+    
+    /* initialize portal query */
+    portal->queryDesc = NULL;
+    portal->attinfo = NULL;
+    portal->state = NULL;
+    portal->cleanup = NULL;
+    
+    /*
+     * install blank portal
+     */
+    BlankPortal = portal;
+}
+
+bool
+PortalNameIsSpecial(char *pname)
+{
+    if (strcmp(pname, VACPNAME) == 0)
+       return true;
+    return false;
+}
+
+/*
+ * This routine is used to collect all portals created in this xaction
+ * and then destroy them.  There is a little trickiness required as a
+ * result of the dynamic hashing interface to getting every hash entry
+ * sequentially.  Its use of static variables requires that we get every
+ * entry *before* we destroy anything (destroying updates the hashtable
+ * and screws up the sequential walk of the table). -mer 17 Aug 1992
+ */
+void
+CollectNamedPortals(Portal *portalP, int destroy)
+{
+    static Portal *portalList = (Portal *)NULL;
+    static int    listIndex = 0;
+    static int    maxIndex = 9;
+    
+    if (portalList == (Portal *)NULL)
+       portalList = (Portal *)malloc(10*sizeof(Portal));
+    
+    if (destroy != 0)
+       {
+           int i;
+           
+           for (i = 0; i < listIndex; i++)
+               PortalDestroy(&portalList[i]);
+           listIndex = 0;
+       }
+    else
+       {
+           Assert(portalP);
+           Assert(*portalP);
+           
+           /*
+            * Don't delete special portals, up to portal creator to do this
+            */
+           if (PortalNameIsSpecial((*portalP)->name))
+               return;
+           
+           portalList[listIndex] = *portalP;
+           listIndex++;
+           if (listIndex == maxIndex)
+               {
+                   portalList = (Portal *)
+                       realloc(portalList, (maxIndex+11)*sizeof(Portal));
+                   maxIndex += 10;
+               }
+       }
+    return;
+}
+
+void
+AtEOXact_portals()
+{
+    HashTableWalk(PortalHashTable, CollectNamedPortals, 0);
+    CollectNamedPortals(NULL, 1);
+}
+
+/* ----------------
+ *     PortalDump
+ * ----------------
+ */
+static void
+PortalDump(Portal *thisP)
+{
+    /* XXX state/argument checking here */
+    
+    PortalVariableMemoryDump(PortalGetVariableMemory(*thisP));
+    PortalHeapMemoryDump(PortalGetHeapMemory(*thisP));
+}
+
+/* ----------------
+ *     DumpPortals
+ * ----------------
+ */
+static void
+DumpPortals()
+{
+    /* XXX state checking here */
+    
+    HashTableWalk(PortalHashTable, PortalDump, 0);
+}
+
+/* ----------------------------------------------------------------
+ *                public portal interface functions
+ * ----------------------------------------------------------------
+ */
+/*
+ * EnablePortalManager --
+ *     Enables/disables the portal management module.
+ */
+void
+EnablePortalManager(bool on)
+{
+    static bool        processing = false;
+    HASHCTL ctl;
+    
+    AssertState(!processing);
+    AssertArg(BoolIsValid(on));
+    
+    if (BypassEnable(&PortalManagerEnableCount, on)) 
+       return;
+    
+    processing = true;
+    
+    if (on) {  /* initialize */
+       EnableMemoryContext(true);
+       
+       PortalMemory = CreateGlobalMemory(PortalMemoryName);
+       
+       ctl.keysize = MAX_PORTALNAME_LEN;
+       ctl.datasize = sizeof(Portal);
+
+       /* use PORTALS_PER_USER, defined in utils/portal.h
+        * as a guess of how many hash table entries to create, initially
+        */
+       PortalHashTable = hash_create(PORTALS_PER_USER * 3, &ctl, HASH_ELEM);
+       
+       CreateNewBlankPortal();
+       
+    } else {   /* cleanup */
+       if (PortalIsValid(BlankPortal)) {
+           PortalDestroy(&BlankPortal);
+           MemoryContextFree((MemoryContext)PortalMemory,
+                             (Pointer)BlankPortal);
+           BlankPortal = NULL;
+       }
+       /*
+        * Each portal must free its non-memory resources specially.
+        */
+       HashTableWalk(PortalHashTable, PortalDestroy, 0);
+       hash_destroy(PortalHashTable);
+       PortalHashTable = NULL;
+       
+       GlobalMemoryDestroy(PortalMemory);
+       PortalMemory = NULL;
+       
+       EnableMemoryContext(true);
+    }
+    
+    processing = false;
+}
+
+/*
+ * GetPortalByName --
+ *     Returns a portal given a portal name; returns blank portal given
+ * NULL; returns invalid portal if portal not found.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ */
+Portal
+GetPortalByName(char *name)
+{
+    Portal     portal;
+    
+    AssertState(PortalManagerEnabled);
+    
+    if (PointerIsValid(name)) {
+       PortalHashTableLookup(name, portal);
+    }
+    else {
+       if (!PortalIsValid(BlankPortal))
+           CreateNewBlankPortal();
+       portal = BlankPortal;
+    }
+    
+    return (portal);
+}
+
+/*
+ * BlankPortalAssignName --
+ *     Returns former blank portal as portal with given name.
+ *
+ * Side effect:
+ *     All references to the former blank portal become incorrect.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadState if called without an intervening call to GetPortalByName(NULL).
+ *     BadArg if portal name is invalid.
+ *     "WARN" if portal name is in use.
+ */
+Portal
+BlankPortalAssignName(char *name)      /* XXX PortalName */
+{
+    Portal     portal;
+    uint16     length;
+    
+    AssertState(PortalManagerEnabled);
+    AssertState(PortalIsValid(BlankPortal));
+    AssertArg(PointerIsValid(name));   /* XXX PortalName */
+    
+    portal = GetPortalByName(name);
+    if (PortalIsValid(portal)) {
+       elog(NOTICE, "BlankPortalAssignName: portal %s already exists", name);
+       return (portal);
+    }
+    
+    /*
+     * remove blank portal
+     */
+    portal = BlankPortal;
+    BlankPortal = NULL;
+    
+    /*
+     * initialize portal name
+     */
+    length = 1 + strlen(name);
+    portal->name = (char*)
+       MemoryContextAlloc((MemoryContext)&portal->variable, length);
+    
+    strncpy(portal->name, name, length);
+    
+    /*
+     * put portal in table
+     */
+    PortalHashTableInsert(portal);
+    
+    return (portal);
+}
+
+/*
+ * PortalSetQuery --
+ *     Attaches a "query" to portal.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadArg if portal is invalid.
+ *     BadArg if queryDesc is "invalid."
+ *     BadArg if state is "invalid."
+ */
+void
+PortalSetQuery(Portal portal,
+              QueryDesc *queryDesc,
+              TupleDesc attinfo,
+              EState *state,
+              void (*cleanup)(Portal portal))
+{
+    AssertState(PortalManagerEnabled);
+    AssertArg(PortalIsValid(portal));
+    AssertArg(IsA((Node*)state,EState));
+    
+    portal->queryDesc = queryDesc;
+    portal->state = state;
+    portal->attinfo = attinfo;
+    portal->cleanup = cleanup;
+}
+
+/*
+ * PortalGetQueryDesc --
+ *     Returns query attached to portal.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadArg if portal is invalid.
+ */
+QueryDesc *
+PortalGetQueryDesc(Portal portal)
+{
+    AssertState(PortalManagerEnabled);
+    AssertArg(PortalIsValid(portal));
+    
+    return (portal->queryDesc);
+}
+
+/*
+ * PortalGetState --
+ *     Returns state attached to portal.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadArg if portal is invalid.
+ */
+EState *
+PortalGetState(Portal portal)
+{
+    AssertState(PortalManagerEnabled);
+    AssertArg(PortalIsValid(portal));
+    
+    return (portal->state);
+}
+
+/*
+ * CreatePortal --
+ *     Returns a new portal given a name.
+ *
+ * Note:
+ *     This is expected to be of very limited usability.  See instead,
+ * BlankPortalAssignName.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadArg if portal name is invalid.
+ *     "WARN" if portal name is in use.
+ */
+Portal
+CreatePortal(char *name)       /* XXX PortalName */
+{
+    Portal     portal;
+    uint16     length;
+    
+    AssertState(PortalManagerEnabled);
+    AssertArg(PointerIsValid(name));   /* XXX PortalName */
+    
+    portal = GetPortalByName(name);
+    if (PortalIsValid(portal)) {
+       elog(NOTICE, "CreatePortal: portal %s already exists", name);
+       return (portal);
+    }
+    
+    /* make new portal structure */
+    portal = (Portal)
+       MemoryContextAlloc((MemoryContext)PortalMemory, sizeof *portal);
+    
+    /* initialize portal variable context */
+    NodeSetTag((Node*)&portal->variable, T_PortalVariableMemory);
+    AllocSetInit(&portal->variable.setData, DynamicAllocMode, (Size)0);
+    portal->variable.method = &PortalVariableContextMethodsData;
+    
+    /* initialize portal heap context */
+    NodeSetTag((Node*)&portal->heap, T_PortalHeapMemory);
+    portal->heap.block = NULL;
+    FixedStackInit(&portal->heap.stackData,
+                  offsetof (HeapMemoryBlockData, itemData));
+    portal->heap.method = &PortalHeapContextMethodsData;
+    
+    /* initialize portal name */
+    length = 1 + strlen(name);
+    portal->name = (char*)
+       MemoryContextAlloc((MemoryContext)&portal->variable, length);
+    strncpy(portal->name, name, length);
+    
+    /* initialize portal query */
+    portal->queryDesc = NULL;
+    portal->attinfo = NULL;
+    portal->state = NULL;
+    portal->cleanup = NULL;
+    
+    /* put portal in table */
+    PortalHashTableInsert(portal);
+    
+    /* Trap(PointerIsValid(name), Unimplemented); */
+    return (portal);
+}
+
+/*
+ * PortalDestroy --
+ *     Destroys portal.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadArg if portal is invalid.
+ */
+void
+PortalDestroy(Portal *portalP)
+{
+    Portal portal = *portalP;
+    
+    AssertState(PortalManagerEnabled);
+    AssertArg(PortalIsValid(portal));
+    
+    /* remove portal from table if not blank portal */
+    if (portal != BlankPortal)
+       PortalHashTableDelete(portal);
+    
+    /* reset portal */
+    if (PointerIsValid(portal->cleanup))
+       (*portal->cleanup)(portal);
+    
+    PortalResetHeapMemory(portal);
+    MemoryContextFree((MemoryContext)&portal->variable,
+                     (Pointer)portal->name);
+    AllocSetReset(&portal->variable.setData);  /* XXX log */
+    
+    if (portal != BlankPortal)
+       MemoryContextFree((MemoryContext)PortalMemory, (Pointer)portal);
+}
+
+/* ----------------
+ *     PortalResetHeapMemory --
+ *             Resets portal's heap memory context.
+ *
+ * Someday, Reset, Start, and End can be optimized by keeping a global
+ * portal module stack of free HeapMemoryBlock's.  This will make Start
+ * and End be fast.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadState if called when not in PortalHeapMemory context.
+ *     BadArg if mode is invalid.
+ * ----------------
+ */
+void
+PortalResetHeapMemory(Portal portal)
+{
+    PortalHeapMemory   context;
+    MemoryContext      currentContext;
+    
+    context = PortalGetHeapMemory(portal);
+    
+    if (PointerIsValid(context->block)) {
+       /* save present context */
+       currentContext = MemoryContextSwitchTo((MemoryContext)context);
+       
+       do {
+           EndPortalAllocMode();
+       } while (PointerIsValid(context->block));
+       
+       /* restore context */
+       (void) MemoryContextSwitchTo(currentContext);
+    }
+}
+
+/*
+ * StartPortalAllocMode --
+ *     Starts a new block of portal heap allocation using mode and limit;
+ *     the current block is disabled until EndPortalAllocMode is called.
+ *
+ * Note:
+ *     Note blocks may be stacked and restored arbitarily.
+ *     The semantics of mode and limit are described in aset.h.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadState if called when not in PortalHeapMemory context.
+ *     BadArg if mode is invalid.
+ */
+void
+StartPortalAllocMode(AllocMode mode, Size limit)
+{
+    PortalHeapMemory   context;
+    
+    AssertState(PortalManagerEnabled);
+    AssertState(IsA(CurrentMemoryContext,PortalHeapMemory));
+    /* AssertArg(AllocModeIsValid); */
+    
+    context = (PortalHeapMemory)CurrentMemoryContext;
+    
+    /* stack current mode */
+    if (PointerIsValid(context->block))
+       FixedStackPush(&context->stackData, context->block);
+    
+    /* allocate and initialize new block */
+    context->block =
+       MemoryContextAlloc(
+                          (MemoryContext)PortalHeapMemoryGetVariableMemory(context),
+                          sizeof (HeapMemoryBlockData) );
+    
+    /* XXX careful, context->block has never been stacked => bad state */
+    
+    AllocSetInit(&HEAPMEMBLOCK(context)->setData, mode, limit);
+}
+
+/*
+ * EndPortalAllocMode --
+ *     Ends current block of portal heap allocation; previous block is
+ *     reenabled.
+ *
+ * Note:
+ *     Note blocks may be stacked and restored arbitarily.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadState if called when not in PortalHeapMemory context.
+ */
+void
+EndPortalAllocMode()
+{
+    PortalHeapMemory   context;
+    
+    AssertState(PortalManagerEnabled);
+    AssertState(IsA(CurrentMemoryContext,PortalHeapMemory));
+    
+    context = (PortalHeapMemory)CurrentMemoryContext;
+    AssertState(PointerIsValid(context->block));       /* XXX Trap(...) */
+    
+    /* free current mode */
+    AllocSetReset(&HEAPMEMBLOCK(context)->setData);
+    MemoryContextFree((MemoryContext)PortalHeapMemoryGetVariableMemory(context),
+                     context->block);
+    
+    /* restore previous mode */
+    context->block = FixedStackPop(&context->stackData);
+}
+
+/*
+ * PortalGetVariableMemory --
+ *     Returns variable memory context for a given portal.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadArg if portal is invalid.
+ */
+PortalVariableMemory
+PortalGetVariableMemory(Portal portal)
+{
+    return (&portal->variable);
+}
+
+/*
+ * PortalGetHeapMemory --
+ *     Returns heap memory context for a given portal.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadArg if portal is invalid.
+ */
+PortalHeapMemory
+PortalGetHeapMemory(Portal portal)
+{
+    return (&portal->heap);
+}
+
+/*
+ * PortalVariableMemoryGetPortal --
+ *     Returns portal containing given variable memory context.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadArg if context is invalid.
+ */
+Portal
+PortalVariableMemoryGetPortal(PortalVariableMemory context)
+{
+    return ((Portal)((char *)context - offsetof (PortalD, variable)));
+}
+
+/*
+ * PortalHeapMemoryGetPortal --
+ *     Returns portal containing given heap memory context.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadArg if context is invalid.
+ */
+Portal
+PortalHeapMemoryGetPortal(PortalHeapMemory context)
+{
+    return ((Portal)((char *)context - offsetof (PortalD, heap)));
+}
+
+/*
+ * PortalVariableMemoryGetHeapMemory --
+ *     Returns heap memory context associated with given variable memory.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadArg if context is invalid.
+ */
+PortalHeapMemory
+PortalVariableMemoryGetHeapMemory(PortalVariableMemory context)
+{
+    return ((PortalHeapMemory)((char *)context
+                              - offsetof (PortalD, variable)
+                              + offsetof (PortalD, heap)));
+}
+
+/*
+ * PortalHeapMemoryGetVariableMemory --
+ *     Returns variable memory context associated with given heap memory.
+ *
+ * Exceptions:
+ *     BadState if called when disabled.
+ *     BadArg if context is invalid.
+ */
+PortalVariableMemory
+PortalHeapMemoryGetVariableMemory(PortalHeapMemory context)
+{
+    return ((PortalVariableMemory)((char *)context
+                                  - offsetof (PortalD, heap)
+                                  + offsetof (PortalD, variable)));
+}
+
diff --git a/src/backend/utils/module.h b/src/backend/utils/module.h
new file mode 100644 (file)
index 0000000..f6dadbe
--- /dev/null
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * module.h--
+ *    this file contains general "module" stuff  that used to be
+ *    spread out between the following files:
+ *
+ *     enbl.h                  module enable stuff
+ *     trace.h                 module trace stuff (now gone)
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MODULE_H
+#define MODULE_H
+
+/* 
+ * prototypes for functions in init/enbl.c 
+ */
+extern bool BypassEnable(int *enableCountInOutP, bool on);
+
+#endif /* MODULE_H */
diff --git a/src/backend/utils/nabstime.h b/src/backend/utils/nabstime.h
new file mode 100644 (file)
index 0000000..c43a538
--- /dev/null
@@ -0,0 +1,165 @@
+/*-------------------------------------------------------------------------
+ *
+ * nabstime.h--
+ *    Definitions for the "new" abstime code.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NABSTIME_H
+#define NABSTIME_H
+
+#include <sys/types.h>
+#include <time.h>
+#if !defined(PORTNAME_irix5)
+#include <sys/timeb.h>
+#endif
+#include "miscadmin.h" /* for SystemTime */
+
+/* ----------------------------------------------------------------
+ *             time types + support macros
+ *
+ *
+ * ----------------------------------------------------------------
+ */
+typedef int32  AbsoluteTime;
+typedef int32  RelativeTime;
+
+typedef struct { 
+    int32              status;
+    AbsoluteTime       data[2];
+} TimeIntervalData;
+typedef TimeIntervalData *TimeInterval;
+
+#define EPOCH_ABSTIME  ((AbsoluteTime) 0)
+#define INVALID_ABSTIME ((AbsoluteTime) 2147483647) /* 2^31 - 1 */
+#define CURRENT_ABSTIME ((AbsoluteTime) 2147483646) /* 2^31 - 2 */
+#define NOEND_ABSTIME  ((AbsoluteTime) 2147483645) /* 2^31 - 3 */
+
+
+#if defined(PORTNAME_aix)
+/*
+ * AIX considers 2147483648 == -2147483648 (since they have the same bit
+ * representation) but uses a different sign sense in a comparison to 
+ * these integer constants depending on whether the constant is signed 
+ * or not!
+ */
+#include <values.h>
+/*#define NOSTART_ABSTIME      ((AbsoluteTime) HIBITI) */      /* - 2^31 */
+#define NOSTART_ABSTIME      ((AbsoluteTime) INT_MIN)
+#else
+/*#define NOSTART_ABSTIME ((AbsoluteTime) 2147483648)*/        /* - 2^31 */
+#define NOSTART_ABSTIME ((AbsoluteTime) -2147483647)   /* - 2^31 */
+#endif /* PORTNAME_aix */
+
+#define INVALID_RELTIME ((RelativeTime) 2147483647)    /* 2^31 - 1 */
+
+/* ----------------
+ *     time support macros (from tim.h)
+ * ----------------
+ */
+
+#define AbsoluteTimeIsValid(time) \
+    ((bool) ((time) != INVALID_ABSTIME))
+
+#define AbsoluteTimeIsReal(time) \
+    ((bool) (((AbsoluteTime) time) < NOEND_ABSTIME && \
+            ((AbsoluteTime) time) > NOSTART_ABSTIME))
+
+/* have to include this because EPOCH_ABSTIME used to be invalid - yuk */
+#define AbsoluteTimeIsBackwardCompatiblyValid(time) \
+    ((bool) (((AbsoluteTime) time) != INVALID_ABSTIME && \
+            ((AbsoluteTime) time) > EPOCH_ABSTIME))
+
+#define AbsoluteTimeIsBackwardCompatiblyReal(time) \
+    ((bool) (((AbsoluteTime) time) < NOEND_ABSTIME && \
+            ((AbsoluteTime) time) > NOSTART_ABSTIME && \
+             ((AbsoluteTime) time) > EPOCH_ABSTIME))
+
+#define RelativeTimeIsValid(time) \
+    ((bool) (((RelativeTime) time) != INVALID_RELTIME))
+
+#define GetCurrentAbsoluteTime() \
+    ((AbsoluteTime) getSystemTime())
+
+/*
+ * getSystemTime --
+ *     Returns system time.
+ */
+#define getSystemTime() \
+    ((time_t) (time(0l)))
+
+
+/*
+ *  Meridian:  am, pm, or 24-hour style.
+ */
+#define AM 0
+#define PM 1
+#define HR24 2
+
+/* can't have more of these than there are bits in an unsigned long */
+#define MONTH  1
+#define YEAR   2
+#define DAY    3
+#define TIME   4
+#define TZ     5
+#define DTZ    6
+#define PG_IGNORE      7
+#define AMPM   8
+/* below here are unused so far */
+#define SECONDS        9
+#define MONTHS 10
+#define YEARS  11
+#define NUMBER 12
+/* these are only for relative dates */
+#define ABS_BEFORE     13
+#define ABS_AFTER      14
+#define AGO    15
+
+
+#define SECS(n)                ((time_t)(n))
+#define MINS(n)                ((time_t)(n) * SECS(60))
+#define HOURS(n)       ((time_t)(n) * MINS(60))        /* 3600 secs */
+#define DAYS(n)                ((time_t)(n) * HOURS(24))       /* 86400 secs */
+/* months and years are not constant length, must be specially dealt with */
+
+#define TOKMAXLEN 6    /* only this many chars are stored in datetktbl */
+
+/* keep this struct small; it gets used a lot */
+typedef struct {
+#if defined(PORTNAME_aix)
+    char *token;
+#else
+    char token[TOKMAXLEN];
+#endif /* PORTNAME_aix */
+    char type;
+    char value;                /* this may be unsigned, alas */
+} datetkn;
+
+/*
+ * nabstime.c prototypes 
+ */
+extern AbsoluteTime nabstimein(char *timestr);
+extern int prsabsdate(char *timestr, struct tm *tm, int *tzp);
+extern int tryabsdate(char *fields[], int nf, struct tm *tm, int *tzp);
+extern int parsetime(char *time, struct tm *tm);
+extern int split(char *string, char *fields[], int nfields, char *sep);
+extern char *nabstimeout(AbsoluteTime time);
+extern AbsoluteTime dateconv(struct tm *tm, int zone);
+extern time_t qmktime(struct tm *tp);
+extern datetkn *datetoktype(char *s, int *bigvalp);
+extern datetkn *datebsearch(char *key, datetkn *base, unsigned int nel);
+extern bool AbsoluteTimeIsBefore(AbsoluteTime time1, AbsoluteTime time2);
+extern bool AbsoluteTimeIsAfter(AbsoluteTime time1, AbsoluteTime time2);
+extern int32 abstimeeq(AbsoluteTime t1, AbsoluteTime t2);
+extern int32 abstimene(AbsoluteTime t1, AbsoluteTime t2);
+extern int32 abstimelt(AbsoluteTime t1, AbsoluteTime t2);
+extern int32 abstimegt(AbsoluteTime t1, AbsoluteTime t2);
+extern int32 abstimele(AbsoluteTime t1, AbsoluteTime t2);
+extern int32 abstimege(AbsoluteTime t1, AbsoluteTime t2);
+
+#endif /* NABSTIME_H */
diff --git a/src/backend/utils/oidcompos.h b/src/backend/utils/oidcompos.h
new file mode 100644 (file)
index 0000000..2be98d6
--- /dev/null
@@ -0,0 +1,52 @@
+/*-------------------------------------------------------------------------
+ *
+ * oidcompos.h--
+ *    prototype file for the oid {char16,int4} composite type functions.   
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        OIDCOMPOS_H
+#define OIDCOMPOS_H
+
+/* oidint4.c */
+OidInt4 oidint4in(char *o);
+char *oidint4out(OidInt4 o);
+bool oidint4lt(OidInt4 o1, OidInt4 o2);
+bool oidint4le(OidInt4 o1, OidInt4 o2);
+bool oidint4eq(OidInt4 o1, OidInt4 o2);
+bool oidint4ge(OidInt4 o1, OidInt4 o2);
+bool oidint4gt(OidInt4 o1, OidInt4 o2);
+bool oidint4ne(OidInt4 o1, OidInt4 o2);
+int oidint4cmp(OidInt4 o1, OidInt4 o2);
+OidInt4 mkoidint4(Oid v_oid, uint32 v_int4);
+
+/* oidint2.c */
+OidInt2 oidint2in(char *o);
+char *oidint2out(OidInt2 o);
+bool oidint2lt(OidInt2 o1, OidInt2 o2);
+bool oidint2le(OidInt2 o1, OidInt2 o2);
+bool oidint2eq(OidInt2 o1, OidInt2 o2);
+bool oidint2ge(OidInt2 o1, OidInt2 o2);
+bool oidint2gt(OidInt2 o1, OidInt2 o2);
+bool oidint2ne(OidInt2 o1, OidInt2 o2);
+int oidint2cmp(OidInt2 o1, OidInt2 o2);
+OidInt2 mkoidint2(Oid v_oid, uint16 v_int2);
+
+/* oidname.c */
+OidName oidnamein(char *inStr);
+char *oidnameout(OidName oidname);
+bool oidnamelt(OidName o1, OidName o2);
+bool oidnamele(OidName o1, OidName o2);
+bool oidnameeq(OidName o1, OidName o2);
+bool oidnamene(OidName o1, OidName o2);
+bool oidnamege(OidName o1, OidName o2);
+bool oidnamegt(OidName o1, OidName o2);
+int oidnamecmp(OidName o1, OidName o2);
+OidName mkoidname(Oid id, char *name);
+
+#endif /* OIDCOMPOS_H */
diff --git a/src/backend/utils/palloc.h b/src/backend/utils/palloc.h
new file mode 100644 (file)
index 0000000..0665051
--- /dev/null
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * palloc.h--
+ *    POSTGRES memory allocator definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        PALLOC_H
+#define PALLOC_H
+
+#include "c.h"
+
+extern void*   palloc(Size size);
+extern void    pfree(void *pointer); 
+extern void *repalloc(void *pointer, Size size);
+
+/* like strdup except uses palloc */
+extern char* pstrdup(char* pointer);
+
+#endif /* PALLOC_H */
+
diff --git a/src/backend/utils/portal.h b/src/backend/utils/portal.h
new file mode 100644 (file)
index 0000000..8bd4584
--- /dev/null
@@ -0,0 +1,97 @@
+/*-------------------------------------------------------------------------
+ *
+ * portal.h--
+ *    POSTGRES portal definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * Note:
+ *     A portal is an abstraction which represents the execution state of
+ * a running query (or a fixed sequence of queries).  The "blank portal" is
+ * a portal with an InvalidName.  This blank portal is in existance except
+ * between calls to BlankPortalAssignName and GetPortalByName(NULL).
+ *
+ * Note:
+ *     now that PQ calls can be made from within a backend, a portal
+ *     may also be used to keep track of the tuples resulting
+ *     from the execution of a query.  In this case, entryIndex 
+ */
+#ifndef        PORTAL_H
+#define PORTAL_H
+
+#include "c.h"
+
+#include "nodes/execnodes.h"           /* for EState */
+#include "nodes/memnodes.h"
+#include "nodes/nodes.h"
+#include "nodes/pg_list.h"
+#include "nodes/plannodes.h"           /* for Plan */
+#include "executor/execdesc.h"
+
+typedef struct PortalBlockData {
+    AllocSetData       setData;
+    FixedItemData      itemData;
+} PortalBlockData;
+
+typedef PortalBlockData        *PortalBlock;
+
+typedef struct PortalD PortalD;
+typedef PortalD                *Portal;
+
+struct PortalD {
+    char                               *name;  /* XXX PortalName */
+    struct PortalVariableMemory                variable;
+    struct PortalHeapMemory            heap;
+    QueryDesc                          *queryDesc;
+    TupleDesc                           attinfo;
+    EState                             *state;
+    void                               (*cleanup)();
+};
+
+/*
+ * PortalIsValid --
+ *     True iff portal is valid.
+ */
+#define        PortalIsValid(p) PointerIsValid(p)
+
+/*
+ * Special portals (well, their names anyway)
+ */
+#define        VACPNAME        "<vacuum>"
+
+extern bool PortalNameIsSpecial(char *pname);
+extern void CollectNamedPortals(Portal *portalP, int destroy);
+extern void AtEOXact_portals(void);
+extern void EnablePortalManager(bool on);
+extern Portal GetPortalByName(char *name);
+extern Portal BlankPortalAssignName(char *name);
+extern void PortalSetQuery(Portal portal, QueryDesc *queryDesc, 
+                          TupleDesc attinfo, EState *state,
+                          void (*cleanup)(Portal portal));
+extern QueryDesc *PortalGetQueryDesc(Portal portal);
+extern EState *PortalGetState(Portal portal);
+extern Portal CreatePortal(char *name);
+extern void PortalDestroy(Portal *portalP);
+extern void PortalResetHeapMemory(Portal portal);
+extern void StartPortalAllocMode(AllocMode mode, Size limit);
+extern void EndPortalAllocMode(void);
+extern PortalVariableMemory PortalGetVariableMemory(Portal portal);
+extern PortalHeapMemory PortalGetHeapMemory(Portal portal);
+extern Portal PortalVariableMemoryGetPortal(PortalVariableMemory context);
+extern Portal PortalHeapMemoryGetPortal(PortalHeapMemory context);
+extern PortalHeapMemory PortalVariableMemoryGetHeapMemory(PortalVariableMemory context);
+extern PortalVariableMemory PortalHeapMemoryGetVariableMemory(PortalHeapMemory context);
+
+/* estimate of the maximum number of open portals a user would have,
+ * used in initially sizing the PortalHashTable in  EnablePortalManager() 
+ */
+#define PORTALS_PER_USER       10
+
+
+#endif /* PORTAL_H */
diff --git a/src/backend/utils/psort.h b/src/backend/utils/psort.h
new file mode 100644 (file)
index 0000000..f14847e
--- /dev/null
@@ -0,0 +1,86 @@
+/*-------------------------------------------------------------------------
+ *
+ * psort.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        PSORT_H
+#define        PSORT_H
+
+#define        SORTMEM         (1 << 18)               /* 1/4 M - any static memory */
+#define        MAXTAPES        7                       /* 7--See Fig. 70, p273 */
+#define        TAPEEXT         "pg_psort.XXXXXX"       /* TEMPDIR/TAPEEXT */
+#define        FREE(x)         free((char *) x)
+
+struct tape {
+    int                tp_dummy;       /* (D) */
+    int                tp_fib;         /* (A) */
+    FILE       *tp_file;       /* (TAPE) */
+    struct tape        *tp_prev;
+};
+
+struct cmplist {
+    int                cp_attn;        /* attribute number */
+    int                cp_num;         /* comparison function code */
+    int                cp_rev;         /* invert comparison flag */
+    struct     cmplist         *cp_next; /* next in chain */
+};
+
+extern int             Nkeys;
+extern ScanKey         key;
+extern int             SortMemory;     /* free memory */
+extern Relation        SortRdesc;
+extern struct leftist  *Tuples;
+
+#ifdef EBUG
+#include <stdio.h>
+#include "utils/elog.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+
+#define        PDEBUG(PROC, S1)\
+elog(DEBUG, "%s:%d>> PROC: %s.", __FILE__, __LINE__, S1)
+
+#define        PDEBUG2(PROC, S1, D1)\
+elog(DEBUG, "%s:%d>> PROC: %s %d.", __FILE__, __LINE__, S1, D1)
+
+#define        PDEBUG4(PROC, S1, D1, S2, D2)\
+elog(DEBUG, "%s:%d>> PROC: %s %d, %s %d.", __FILE__, __LINE__, S1, D1, S2, D2)
+
+#define        VDEBUG(VAR, FMT)\
+elog(DEBUG, "%s:%d>> VAR =FMT", __FILE__, __LINE__, VAR)
+
+#define        ASSERT(EXPR, STR)\
+if (!(EXPR)) elog(FATAL, "%s:%d>> %s", __FILE__, __LINE__, STR)
+
+#define        TRACE(VAL, CODE)\
+if (1) CODE; else
+
+#else
+#define        PDEBUG(MSG)
+#define        VDEBUG(VAR, FMT)
+#define        ASSERT(EXPR, MSG)
+#define        TRACE(VAL, CODE)
+#endif
+
+/* psort.c */
+extern void psort(Relation oldrel, Relation newrel, int nkeys, ScanKey key);
+extern void initpsort(void);
+extern void resetpsort(void);
+extern void initialrun(Relation rdesc);
+extern bool createrun(HeapScanDesc sdesc, FILE *file);
+extern HeapTuple tuplecopy(HeapTuple tup, Relation rdesc, Buffer b);
+extern FILE *mergeruns(void);
+extern void merge(struct tape *dest);
+extern void endpsort(Relation rdesc, FILE *file);
+extern FILE *gettape(void);
+extern void resettape(FILE *file);
+extern void destroytape(FILE *file);
+
+#endif /* PSORT_H */
diff --git a/src/backend/utils/rel.h b/src/backend/utils/rel.h
new file mode 100644 (file)
index 0000000..e54fe20
--- /dev/null
@@ -0,0 +1,170 @@
+/*-------------------------------------------------------------------------
+ *
+ * rel.h--
+ *    POSTGRES relation descriptor definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        REL_H
+#define REL_H
+
+#include "postgres.h"
+
+#include "storage/fd.h"
+#include "access/strat.h"      
+#include "access/tupdesc.h"
+
+#include "catalog/pg_am.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_class.h"
+
+#include "rewrite/prs2lock.h"
+
+typedef struct RelationData {
+    File               rd_fd;          /* open file descriptor */
+    int                 rd_nblocks;    /* number of blocks in rel */
+    uint16             rd_refcnt;      /* reference count */
+    bool               rd_islocal;     /* uses the local buffer mgr */
+    bool               rd_isnailed;    /* rel is nailed in cache */
+    Form_pg_am                 rd_am;          /* AM tuple */
+    Form_pg_class      rd_rel;         /* RELATION tuple */
+    Oid                        rd_id;          /* relations's object id */
+    Pointer            lockInfo;       /* ptr. to misc. info. */
+    TupleDesc           rd_att;                /* tuple desciptor */
+    RuleLock           *rd_rules;      /* rewrite rules */
+    IndexStrategy       rd_istrat;    
+    RegProcedure*       rd_support;
+} RelationData;
+
+typedef RelationData   *Relation;
+
+/* ----------------
+ *     RelationPtr is used in the executor to support index scans
+ *     where we have to keep track of several index relations in an
+ *     array.  -cim 9/10/89
+ * ----------------
+ */
+typedef Relation       *RelationPtr;
+
+#define InvalidRelation        ((Relation)NULL)
+
+typedef char   ArchiveMode;
+
+/*
+ * RelationIsValid --
+ *     True iff relation descriptor is valid.
+ */
+#define        RelationIsValid(relation) PointerIsValid(relation)
+
+/*
+ * RelationGetSystemPort --
+ *     Returns system port of a relation.
+ *
+ * Note:
+ *     Assumes relation descriptor is valid.
+ */
+#define RelationGetSystemPort(relation) ((relation)->rd_fd)
+
+/*
+ * RelationGetLockInfo --
+ *      Returns the lock information structure in the reldesc
+ *
+ */
+#define RelationGetLockInfo(relation) ((relation)->lockInfo)
+
+/*
+ * RelationHasReferenceCountZero --
+ *     True iff relation reference count is zero.
+ *
+ * Note:
+ *     Assumes relation descriptor is valid.
+ */
+#define RelationHasReferenceCountZero(relation) \
+       ((bool)((relation)->rd_refcnt == 0))
+
+/*
+ * RelationSetReferenceCount --
+ *     Sets relation reference count.
+ */
+#define RelationSetReferenceCount(relation,count) ((relation)->rd_refcnt = count)
+
+/*
+ * RelationIncrementReferenceCount --
+ *     Increments relation reference count.
+ */
+#define RelationIncrementReferenceCount(relation) ((relation)->rd_refcnt += 1);
+
+/*
+ * RelationDecrementReferenceCount --
+ *     Decrements relation reference count.
+ */
+#define RelationDecrementReferenceCount(relation) ((relation)->rd_refcnt -= 1)
+
+/*
+ * RelationGetAccessMethodTupleForm --
+ *     Returns access method attribute values for a relation.
+ *
+ * Note:
+ *     Assumes relation descriptor is valid.
+ */
+#define RelationGetAccessMethodTupleForm(relation) ((relation)->rd_am)
+
+/*
+ * RelationGetRelationTupleForm --
+ *     Returns relation attribute values for a relation.
+ *
+ * Note:
+ *     Assumes relation descriptor is valid.
+ */
+#define RelationGetRelationTupleForm(relation) ((relation)->rd_rel)
+
+
+/* 
+ * RelationGetRelationId --
+ *
+ *  returns the object id of the relation
+ *
+ */
+#define RelationGetRelationId(relation) ((relation)->rd_id)
+
+/*
+ * RelationGetFile --
+ *
+ *    Returns the open File decscriptor
+ */
+#define RelationGetFile(relation) ((relation)->rd_fd)
+
+
+/*
+ * RelationGetRelationName --
+ *
+ *    Returns a Relation Name
+ */
+#define RelationGetRelationName(relation) (&(relation)->rd_rel->relname)
+
+/*
+ * RelationGetRelationName --
+ *
+ *    Returns a the number of attributes.
+ */
+#define RelationGetNumberOfAttributes(relation) ((relation)->rd_rel->relnatts)
+
+/*
+ * RelationGetTupleDescriptor --
+ *     Returns tuple descriptor for a relation.
+ *
+ * Note:
+ *     Assumes relation descriptor is valid.
+ */
+#define RelationGetTupleDescriptor(relation) ((relation)->rd_att)
+
+extern IndexStrategy RelationGetIndexStrategy(Relation relation);
+
+extern void RelationSetIndexSupport(Relation relation, IndexStrategy strategy,
+                            RegProcedure *support);
+#endif /* REL_H */
diff --git a/src/backend/utils/rel2.h b/src/backend/utils/rel2.h
new file mode 100644 (file)
index 0000000..13c95b9
--- /dev/null
@@ -0,0 +1,23 @@
+/*-------------------------------------------------------------------------
+ *
+ * rel2.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        TMP_REL2_H
+#define        TMP_REL2_H
+
+#include "access/istrat.h"
+
+extern IndexStrategy RelationGetIndexStrategy(Relation relation);
+
+extern void RelationSetIndexSupport(Relation relation, IndexStrategy strategy,
+                            RegProcedure *support);
+
+#endif /* TMP_REL2_H */
diff --git a/src/backend/utils/relcache.h b/src/backend/utils/relcache.h
new file mode 100644 (file)
index 0000000..40941ac
--- /dev/null
@@ -0,0 +1,47 @@
+/*-------------------------------------------------------------------------
+ *
+ * relcache.h--
+ *    Relation descriptor cache definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        RELCACHE_H
+#define RELCACHE_H
+
+#include <sys/types.h>
+
+#include "postgres.h"
+#include "utils/rel.h"
+
+/*
+ * relation lookup routines
+ */
+extern Relation RelationIdCacheGetRelation(Oid relationId);
+extern Relation RelationNameCacheGetRelation(char *relationName);
+extern Relation RelationIdGetRelation(Oid relationId);
+extern Relation RelationNameGetRelation(char *relationName);
+extern Relation getreldesc(char *relationName);
+
+extern void RelationClose(Relation relation);
+extern void RelationFlushRelation(Relation *relationPtr,
+                                 bool  onlyFlushReferenceCountZero);
+extern void RelationIdInvalidateRelationCacheByRelationId(Oid relationId);
+
+extern void 
+RelationIdInvalidateRelationCacheByAccessMethodId(Oid accessMethodId);
+
+extern void RelationCacheInvalidate(bool onlyFlushReferenceCountZero);
+
+extern void RelationRegisterRelation(Relation relation);
+extern void RelationPurgeLocalRelation(bool xactComitted);
+extern void RelationInitialize();
+extern void init_irels();
+extern void write_irels();
+
+
+#endif /* RELCACHE_H */
diff --git a/src/backend/utils/sets.h b/src/backend/utils/sets.h
new file mode 100644 (file)
index 0000000..d8bf171
--- /dev/null
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * sets.h--
+ *    
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SETS_H
+#define SETS_H
+
+/* Temporary name of set, before SetDefine changes it. */
+#define GENERICSETNAME "zyxset"
+
+extern Oid SetDefine(char *querystr, char *typename);
+extern int seteval(Oid funcoid);
+
+#endif /* SETS_H */
diff --git a/src/backend/utils/sort/Makefile.inc b/src/backend/utils/sort/Makefile.inc
new file mode 100644 (file)
index 0000000..47fd069
--- /dev/null
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for utils/sort
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= lselect.c psort.c
diff --git a/src/backend/utils/sort/lselect.c b/src/backend/utils/sort/lselect.c
new file mode 100644 (file)
index 0000000..86a3441
--- /dev/null
@@ -0,0 +1,365 @@
+/*-------------------------------------------------------------------------
+ *
+ * lselect.c--
+ *    leftist tree selection algorithm (linked priority queue--Knuth, Vol.3,
+ *    pp.150-52)
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <string.h>
+#include <stdio.h>
+
+#include "c.h"
+
+#include "storage/buf.h"
+#include "access/skey.h"
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "utils/rel.h"
+
+#include "utils/psort.h"
+#include "utils/lselect.h"
+
+extern Relation        SortRdesc;              /* later static */ 
+
+/*
+ *     PUTTUP          - writes the next tuple
+ *     ENDRUN          - mark end of run
+ *     GETLEN          - reads the length of the next tuple
+ *     ALLOCTUP        - returns space for the new tuple
+ *     SETTUPLEN       - stores the length into the tuple
+ *     GETTUP          - reads the tuple
+ *
+ *     Note:
+ *             LEN field must be a short; FP is a stream
+ */
+
+#define        PUTTUP(TUP, FP) fwrite((char *)TUP, (TUP)->t_len, 1, FP)
+#define        ENDRUN(FP)      fwrite((char *)&shortzero, sizeof (shortzero), 1, FP)
+#define        GETLEN(LEN, FP) fread(&(LEN), sizeof (shortzero), 1, FP)
+#define        ALLOCTUP(LEN)   ((HeapTuple)malloc((unsigned)LEN))
+#define        GETTUP(TUP, LEN, FP)\
+       fread((char *)(TUP) + sizeof (shortzero), 1, (LEN) - sizeof (shortzero), FP)
+#define        SETTUPLEN(TUP, LEN)     (TUP)->t_len = LEN
+
+/*
+ *     USEMEM          - record use of memory
+ *     FREEMEM         - record freeing of memory
+ *     FULLMEM         - 1 iff a tuple will fit
+ */
+
+#define        USEMEM(AMT)     SortMemory -= (AMT)
+#define        FREEMEM(AMT)    SortMemory += (AMT)
+#define        LACKMEM()       (SortMemory <= BLCKSZ)          /* not accurate */
+
+/*
+ *     lmerge          - merges two leftist trees into one
+ *
+ *     Note:
+ *             Enforcing the rule that pt->lt_dist >= qt->lt_dist may
+ *             simplifify much of the code.  Removing recursion will not
+ *             speed up code significantly.
+ */
+struct leftist *
+lmerge(struct leftist *pt, struct leftist *qt)
+{
+    register struct    leftist *root, *majorLeftist, *minorLeftist;
+    int                dist;
+    
+    if (tuplecmp(pt->lt_tuple, qt->lt_tuple)) {
+       root = pt;
+       majorLeftist = qt;
+    } else {
+       root = qt;
+       majorLeftist = pt;
+    }
+    if (root->lt_left == NULL)
+       root->lt_left = majorLeftist;
+    else {
+       if ((minorLeftist = root->lt_right) != NULL)
+           majorLeftist = lmerge(majorLeftist, minorLeftist);
+       if ((dist = root->lt_left->lt_dist) < majorLeftist->lt_dist) {
+           root->lt_dist = 1 + dist;
+           root->lt_right = root->lt_left;
+           root->lt_left = majorLeftist;
+       } else {
+           root->lt_dist = 1 + majorLeftist->lt_dist;
+           root->lt_right = majorLeftist;
+       }
+    }
+    return(root);
+}
+
+static struct leftist *
+linsert(struct leftist *root, struct leftist *new1)
+{
+    register struct    leftist *left, *right;
+    
+    if (! tuplecmp(root->lt_tuple, new1->lt_tuple)) {
+       new1->lt_left = root;
+       return(new1);
+    }
+    left = root->lt_left;
+    right = root->lt_right;
+    if (right == NULL) {
+       if (left == NULL)
+           root->lt_left = new1;
+       else {
+           root->lt_right = new1;
+           root->lt_dist = 2;
+       }
+       return(root);
+    }
+    right = linsert(right, new1);
+    if (right->lt_dist < left->lt_dist) {
+       root->lt_dist = 1 + left->lt_dist;
+       root->lt_left = right;
+       root->lt_right = left;
+    } else {
+       root->lt_dist = 1 + right->lt_dist;
+       root->lt_right = right;
+    }
+    return(root);
+}
+
+/*
+ *     gettuple        - returns tuple at top of tree (Tuples)
+ *
+ *     Returns:
+ *             tuple at top of tree, NULL if failed ALLOC()
+ *             *devnum is set to the devnum of tuple returned
+ *             *treep is set to the new tree
+ *
+ *     Note:
+ *             *treep must not be NULL
+ *             NULL is currently never returned BUG
+ */
+HeapTuple
+gettuple(struct leftist **treep,
+        short *devnum)         /* device from which tuple came */
+{
+    register struct    leftist  *tp;
+    HeapTuple  tup;
+    
+    tp = *treep;
+    tup = tp->lt_tuple;
+    *devnum = tp->lt_devnum;
+    if (tp->lt_dist == 1)                              /* lt_left == NULL */
+       *treep = tp->lt_left;
+    else
+       *treep = lmerge(tp->lt_left, tp->lt_right);
+    
+    FREEMEM(sizeof (struct leftist));
+    FREE(tp);
+    return(tup);
+}
+
+/*
+ *     puttuple        - inserts new tuple into tree
+ *
+ *     Returns:
+ *             NULL iff failed ALLOC()
+ *
+ *     Note:
+ *             Currently never returns NULL BUG
+ */
+int
+puttuple(struct leftist **treep, HeapTuple newtuple, int devnum)
+{
+    register struct    leftist *new1;
+    register struct    leftist *tp;
+    
+    new1 = (struct leftist *) malloc((unsigned) sizeof (struct leftist));
+    USEMEM(sizeof (struct leftist));
+    new1->lt_dist = 1;
+    new1->lt_devnum = devnum;
+    new1->lt_tuple = newtuple;
+    new1->lt_left = NULL;
+    new1->lt_right = NULL;
+    if ((tp = *treep) == NULL)
+       *treep = new1;
+    else
+       *treep = linsert(tp, new1);
+    return(1);
+}
+
+
+/*
+ *     dumptuples      - stores all the tuples in tree into file
+ */
+void
+dumptuples(FILE *file)
+{
+    register struct    leftist *tp;
+    register struct    leftist *newp;
+    HeapTuple  tup;
+    
+    tp = Tuples;
+    while (tp != NULL) {
+       tup = tp->lt_tuple;
+       if (tp->lt_dist == 1)                   /* lt_right == NULL */
+           newp = tp->lt_left;
+       else
+           newp = lmerge(tp->lt_left, tp->lt_right);
+       FREEMEM(sizeof (struct leftist));
+       FREE(tp);
+       PUTTUP(tup, file);
+       FREEMEM(tup->t_len);
+       FREE(tup);
+       tp = newp;
+    }
+    Tuples = NULL;
+}
+
+/*
+ *     tuplecmp        - Compares two tuples with respect CmpList
+ *
+ *     Returns:
+ *             1 if left < right ;0 otherwise
+ *     Assumtions:
+ */
+int
+tuplecmp(HeapTuple ltup, HeapTuple rtup)
+{
+    register char      *lattr, *rattr;
+    int                nkey = 0;
+    extern     int     Nkeys;
+    extern     ScanKey Key;
+    int                result = 0;
+    bool               isnull;
+    
+    if (ltup == (HeapTuple)NULL)
+       return(0);
+    if (rtup == (HeapTuple)NULL)
+       return(1);
+    while (nkey < Nkeys && !result) {
+       lattr = heap_getattr(ltup, InvalidBuffer,
+                            Key[nkey].sk_attno, 
+                            RelationGetTupleDescriptor(SortRdesc),
+                            &isnull);
+       if (isnull)
+           return(0);
+       rattr = heap_getattr(rtup, InvalidBuffer,
+                            Key[nkey].sk_attno, 
+                            RelationGetTupleDescriptor(SortRdesc),
+                            &isnull);
+       if (isnull)
+           return(1);
+       if (Key[nkey].sk_flags & SK_COMMUTE) {
+           if (!(result = (long) (*Key[nkey].sk_func) (rattr, lattr)))
+               result = -(long) (*Key[nkey].sk_func) (lattr, rattr);
+       } else if (!(result = (long) (*Key[nkey].sk_func) (lattr, rattr)))
+           result = -(long) (*Key[nkey].sk_func) (rattr, lattr);
+       nkey++;
+    }
+    return (result == 1);
+}
+
+#ifdef EBUG
+void
+checktree(struct leftist *tree)
+{
+    int                lnodes;
+    int                rnodes;
+    
+    if (tree == NULL) {
+       puts("Null tree.");
+       return;
+    }
+    lnodes = checktreer(tree->lt_left, 1);
+    rnodes = checktreer(tree->lt_right, 1);
+    if (lnodes < 0) {
+       lnodes = -lnodes;
+       puts("0:\tBad left side.");
+    }
+    if (rnodes < 0) {
+       rnodes = -rnodes;
+       puts("0:\tBad right side.");
+    }
+    if (lnodes == 0) {
+       if (rnodes != 0)
+           puts("0:\tLeft and right reversed.");
+       if (tree->lt_dist != 1)
+           puts("0:\tDistance incorrect.");
+    } else if (rnodes == 0) {
+       if (tree->lt_dist != 1)
+           puts("0:\tDistance incorrect.");
+    } else if (tree->lt_left->lt_dist < tree->lt_right->lt_dist) {
+       puts("0:\tLeft and right reversed.");
+       if (tree->lt_dist != 1 + tree->lt_left->lt_dist)
+           puts("0:\tDistance incorrect.");
+    } else if (tree->lt_dist != 1+ tree->lt_right->lt_dist)
+       puts("0:\tDistance incorrect.");
+    if (lnodes > 0)
+       if (tuplecmp(tree->lt_left->lt_tuple, tree->lt_tuple))
+           printf("%d:\tLeft child < parent.\n");
+    if (rnodes > 0)
+       if (tuplecmp(tree->lt_right->lt_tuple, tree->lt_tuple))
+           printf("%d:\tRight child < parent.\n");
+    printf("Tree has %d nodes\n", 1 + lnodes + rnodes);
+}
+
+int
+checktreer(struct leftist *tree, int level)
+{
+    int        lnodes, rnodes;
+    int        error = 0;
+    
+    if (tree == NULL)
+       return(0);
+    lnodes = checktreer(tree->lt_left, level + 1);
+    rnodes = checktreer(tree->lt_right, level + 1);
+    if (lnodes < 0) {
+       error = 1;
+       lnodes = -lnodes;
+       printf("%d:\tBad left side.\n", level);
+    }
+    if (rnodes < 0) {
+       error = 1;
+       rnodes = -rnodes;
+       printf("%d:\tBad right side.\n", level);
+    }
+    if (lnodes == 0) {
+       if (rnodes != 0) {
+           error = 1;
+           printf("%d:\tLeft and right reversed.\n", level);
+       }
+       if (tree->lt_dist != 1) {
+           error = 1;
+           printf("%d:\tDistance incorrect.\n", level);
+       }
+    } else if (rnodes == 0) {
+       if (tree->lt_dist != 1) {
+           error = 1;
+           printf("%d:\tDistance incorrect.\n", level);
+       }                       
+    } else if (tree->lt_left->lt_dist < tree->lt_right->lt_dist) {
+       error = 1;
+       printf("%d:\tLeft and right reversed.\n", level);
+       if (tree->lt_dist != 1 + tree->lt_left->lt_dist)
+           printf("%d:\tDistance incorrect.\n", level);
+    } else if (tree->lt_dist != 1+ tree->lt_right->lt_dist) {
+       error = 1;
+       printf("%d:\tDistance incorrect.\n", level);
+    }
+    if (lnodes > 0)
+       if (tuplecmp(tree->lt_left->lt_tuple, tree->lt_tuple)) {
+           error = 1;
+           printf("%d:\tLeft child < parent.\n");
+       }
+    if (rnodes > 0)
+       if (tuplecmp(tree->lt_right->lt_tuple, tree->lt_tuple)) {
+           error = 1;
+           printf("%d:\tRight child < parent.\n");
+       }
+    if (error)
+       return(-1 + -lnodes + -rnodes);
+    return(1 + lnodes + rnodes);
+}
+#endif
diff --git a/src/backend/utils/sort/psort.c b/src/backend/utils/sort/psort.c
new file mode 100644 (file)
index 0000000..078e7c4
--- /dev/null
@@ -0,0 +1,617 @@
+/*-------------------------------------------------------------------------
+ *
+ * psort.c--
+ *    Polyphase merge sort.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *     Sorts the first relation into the second relation.  The sort may
+ * not be called twice simultaneously.
+ *
+ *    Use the tape-splitting method (Knuth, Vol. III, pp281-86) in the future.
+ *
+ *     Arguments? Variables?
+ *             MAXMERGE, MAXTAPES
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <math.h>
+
+#include "c.h"
+
+#include "executor/execdebug.h"
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/relscan.h"
+#include "access/skey.h"
+#include "utils/tqual.h"       /* for NowTimeQual */
+
+#include "storage/buf.h"
+#include "storage/bufmgr.h"    /* for BLCKSZ */
+#include "utils/portal.h"      /* for {Start,End}PortalAllocMode */
+#include "utils/elog.h"
+#include "utils/rel.h"
+
+#include "utils/psort.h"
+#include "utils/lselect.h"
+
+#include "storage/fd.h"
+
+#define        TEMPDIR "./"
+
+int                    Nkeys;
+ScanKey                        Key;
+int                    SortMemory;
+
+static int             TapeRange;              /* number of tapes - 1 (T) */
+static int             Level;                  /* (l) */
+static int             TotalDummy;             /* summation of tp_dummy */
+static struct  tape    Tape[MAXTAPES];
+static long            shortzero = 0;          /* used to delimit runs */
+static struct  tuple   *LastTuple = NULL;      /* last output */
+
+static  int            BytesRead;              /* to keep track of # of IO */
+static  int            BytesWritten;
+
+Relation               SortRdesc;              /* current tuples in memory */
+struct leftist         *Tuples;                /* current tuples in memory */
+
+/*
+ *     psort           - polyphase merge sort entry point
+ */
+void
+psort(Relation oldrel, Relation newrel, int nkeys, ScanKey key)
+{
+    AssertArg(nkeys >= 1);
+    AssertArg(key[0].sk_attno != 0);
+    AssertArg(key[0].sk_procedure != 0);
+    
+    Nkeys = nkeys;
+    Key = key;
+    SortMemory = 0;
+    SortRdesc = oldrel;
+    BytesRead = 0;
+    BytesWritten = 0;
+    /* 
+     * may not be the best place.  
+     *
+     * Pass 0 for the "limit" as the argument is currently ignored.
+     * Previously, only one arg was passed. -mer 12 Nov. 1991
+     */
+    StartPortalAllocMode(StaticAllocMode, (Size)0);
+    initpsort();
+    initialrun(oldrel);
+    /* call finalrun(newrel, mergerun()) instead */
+    endpsort(newrel, mergeruns());
+    EndPortalAllocMode();
+    NDirectFileRead += (int)ceil((double)BytesRead / BLCKSZ);
+    NDirectFileWrite += (int)ceil((double)BytesWritten / BLCKSZ);
+}
+
+/*
+ *     TAPENO          - number of tape in Tape
+ */
+
+#define        TAPENO(NODE)            (NODE - Tape)
+#define        TUPLENO(TUP)            ((TUP == NULL) ? -1 : (int) TUP->t_iid)
+
+/*
+ *     initpsort       - initializes the tapes
+ *                     - (polyphase merge Alg.D(D1)--Knuth, Vol.3, p.270)
+ *     Returns:
+ *             number of allocated tapes
+ */
+void
+initpsort()
+{
+    register   int                     i;
+    register   struct  tape            *tp;
+    
+    /*
+      ASSERT(ntapes >= 3 && ntapes <= MAXTAPES,
+      "initpsort: Invalid number of tapes to initialize.\n");
+      */
+    
+    tp = Tape;
+    for (i = 0; i < MAXTAPES && (tp->tp_file = gettape()) != NULL; i++) {
+       tp->tp_dummy = 1;
+       tp->tp_fib = 1;
+       tp->tp_prev = tp - 1;
+       tp++;
+    }
+    TapeRange = --tp - Tape;
+    tp->tp_dummy = 0;
+    tp->tp_fib = 0;
+    Tape[0].tp_prev = tp;
+    
+    if (TapeRange <= 1)
+       elog(WARN, "initpsort: Could only allocate %d < 3 tapes\n",
+            TapeRange + 1);
+    
+    Level = 1;
+    TotalDummy = TapeRange;
+    
+    SortMemory = SORTMEM;
+    LastTuple = NULL;
+    Tuples = NULL;
+}
+
+/*
+ *     resetpsort      - resets (frees) malloc'd memory for an aborted Xaction
+ *
+ *     Not implemented yet.
+ */
+void
+resetpsort()
+{
+    ;
+}
+
+/*
+ *     PUTTUP          - writes the next tuple
+ *     ENDRUN          - mark end of run
+ *     GETLEN          - reads the length of the next tuple
+ *     ALLOCTUP        - returns space for the new tuple
+ *     SETTUPLEN       - stores the length into the tuple
+ *     GETTUP          - reads the tuple
+ *
+ *     Note:
+ *             LEN field must be a short; FP is a stream
+ */
+
+#define        PUTTUP(TUP, FP)\
+    BytesWritten += (TUP)->t_len; \
+    fwrite((char *)TUP, (TUP)->t_len, 1, FP)
+#define        ENDRUN(FP)      fwrite((char *)&shortzero, sizeof (shortzero), 1, FP)
+#define        GETLEN(LEN, FP) fread((char *)&(LEN), sizeof (shortzero), 1, FP)
+#define        ALLOCTUP(LEN)   ((HeapTuple)malloc((unsigned)LEN))
+#define        GETTUP(TUP, LEN, FP)\
+    IncrProcessed(); \
+    BytesRead += (LEN) - sizeof (shortzero); \
+    fread((char *)(TUP) + sizeof (shortzero), (LEN) - sizeof (shortzero), 1, FP)
+#define        SETTUPLEN(TUP, LEN)     (TUP)->t_len = LEN
+    
+    /*
+     * USEMEM          - record use of memory
+     * FREEMEM         - record freeing of memory
+     * FULLMEM         - 1 iff a tuple will fit
+     */
+    
+#define        USEMEM(AMT)     SortMemory -= (AMT)
+#define        FREEMEM(AMT)    SortMemory += (AMT)
+#define        LACKMEM()       (SortMemory <= BLCKSZ)          /* not accurate */
+#define        TRACEMEM(FUNC)
+#define        TRACEOUT(FUNC, TUP)
+
+/*
+ *     initialrun      - distributes tuples from the relation
+ *                     - (replacement selection(R2-R3)--Knuth, Vol.3, p.257)
+ *                     - (polyphase merge Alg.D(D2-D4)--Knuth, Vol.3, p.271)
+ *
+ *     Explaination:
+ *             Tuples are distributed to the tapes as in Algorithm D.
+ *             A "tuple" with t_size == 0 is used to mark the end of a run.
+ *
+ *     Note:
+ *             The replacement selection algorithm has been modified
+ *             to go from R1 directly to R3 skipping R2 the first time.
+ *
+ *             Maybe should use closer(rdesc) before return
+ *             Perhaps should adjust the number of tapes if less than n.
+ *             used--v. likely to have problems in mergeruns().
+ *             Must know if should open/close files before each
+ *             call to  psort()?   If should--messy??
+ *
+ *     Possible optimization:
+ *             put the first xxx runs in quickly--problem here since
+ *             I (perhaps prematurely) combined the 2 algorithms.
+ *             Also, perhaps allocate tapes when needed. Split into 2 funcs.
+ */
+void
+initialrun(Relation rdesc)
+{
+    /* register struct tuple   *tup; */
+    register struct    tape    *tp;
+    HeapScanDesc       sdesc;
+    int                baseruns;               /* D:(a) */
+    int                morepasses;             /* EOF */
+    
+    sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0,
+                          (ScanKey)NULL);
+    tp = Tape;
+    
+    if ((bool)createrun(sdesc, tp->tp_file) != false)
+       morepasses = 0;
+    else
+       morepasses = 1 + (Tuples != NULL);      /* (T != N) ? 2 : 1 */
+    
+    for ( ; ; ) {
+       tp->tp_dummy--;
+       TotalDummy--;
+       if (tp->tp_dummy < (tp + 1)->tp_dummy)
+           tp++;
+       else if (tp->tp_dummy != 0)
+           tp = Tape;
+       else {
+           Level++;
+           baseruns = Tape[0].tp_fib;
+           for (tp = Tape; tp - Tape < TapeRange; tp++) {
+               TotalDummy +=
+                   (tp->tp_dummy = baseruns
+                    + (tp + 1)->tp_fib
+                    - tp->tp_fib);
+               tp->tp_fib = baseruns
+                   + (tp + 1)->tp_fib;
+           }
+           tp = Tape;                  /* D4 */
+       }                                       /* D3 */
+       if (morepasses)
+           if (--morepasses) {
+               dumptuples(tp->tp_file);
+               ENDRUN(tp->tp_file);
+               continue;
+           } else
+               break;
+       if ((bool)createrun(sdesc, tp->tp_file) == false)
+           morepasses = 1 + (Tuples != NULL);
+       /* D2 */
+    }
+    for (tp = Tape + TapeRange; tp >= Tape; tp--)
+       rewind(tp->tp_file);                            /* D. */
+    heap_endscan(sdesc);
+}
+
+/*
+ *     createrun       - places the next run on file
+ *
+ *     Uses:
+ *             Tuples, which should contain any tuples for this run
+ *
+ *     Returns:
+ *             FALSE iff process through end of relation
+ *             Tuples contains the tuples for the following run upon exit
+ */
+bool
+createrun(HeapScanDesc sdesc, FILE *file)
+{
+    register HeapTuple lasttuple;
+    register HeapTuple btup, tup;
+    struct     leftist *nextrun;
+    Buffer     b;
+    bool               foundeor;
+    short              junk;
+    
+    lasttuple = NULL;
+    nextrun = NULL;
+    foundeor = false;
+    for ( ; ; ) {
+       while (LACKMEM() && Tuples != NULL) {
+           if (lasttuple != NULL) {
+               FREEMEM(lasttuple->t_len);
+               FREE(lasttuple);
+               TRACEMEM(createrun);
+           }
+           lasttuple = tup = gettuple(&Tuples, &junk);
+           PUTTUP(tup, file);
+           TRACEOUT(createrun, tup);
+       }
+       if (LACKMEM())
+           break;
+       btup = heap_getnext(sdesc, 0, &b);
+       if (!HeapTupleIsValid(btup)) {
+           foundeor = true;
+           break;
+       }
+       IncrProcessed();
+       tup = tuplecopy(btup, sdesc->rs_rd, b);
+       USEMEM(tup->t_len);
+       TRACEMEM(createrun);
+       if (lasttuple != NULL && tuplecmp(tup, lasttuple))
+           puttuple(&nextrun, tup, 0);
+       else
+           puttuple(&Tuples, tup, 0);
+       ReleaseBuffer(b);
+    }
+    if (lasttuple != NULL) {
+       FREEMEM(lasttuple->t_len);
+       FREE(lasttuple);
+       TRACEMEM(createrun);
+    }
+    dumptuples(file);
+    ENDRUN(file);
+    /* delimit the end of the run */
+    Tuples = nextrun;
+    return((bool)! foundeor); /* XXX - works iff bool is {0,1} */
+}
+
+/*
+ *     tuplecopy       - see also tuple.c:palloctup()
+ *
+ *     This should eventually go there under that name?  And this will
+ *     then use malloc directly (see version -r1.2).
+ */
+HeapTuple
+tuplecopy(HeapTuple tup, Relation rdesc, Buffer b)
+{
+    HeapTuple  rettup;
+    
+    if (!HeapTupleIsValid(tup)) {
+       return(NULL);           /* just in case */
+    }
+    rettup = (HeapTuple)malloc(tup->t_len);
+    memmove((char *)rettup, (char *)tup, tup->t_len);  /* XXX */
+    return(rettup);
+}
+
+/*
+ *     mergeruns       - merges all runs from input tapes
+ *                       (polyphase merge Alg.D(D6)--Knuth, Vol.3, p271)
+ *
+ *     Returns:
+ *             file of tuples in order
+ */
+FILE *
+mergeruns()
+{
+    register struct    tape    *tp;
+    
+    tp = Tape + TapeRange;
+    merge(tp);
+    rewind(tp->tp_file);
+    while (--Level != 0) {
+       tp = tp->tp_prev;
+       rewind(tp->tp_file);
+       /*              resettape(tp->tp_file);         -not sufficient */
+       merge(tp);
+       rewind(tp->tp_file);
+    }
+    return(tp->tp_file);
+}
+
+/*
+ *     merge           - handles a single merge of the tape
+ *                       (polyphase merge Alg.D(D5)--Knuth, Vol.3, p271)
+ */
+void
+merge(struct tape *dest)
+{
+    register HeapTuple tup;
+    register struct    tape    *lasttp;        /* (TAPE[P]) */
+    register struct    tape    *tp;
+    struct     leftist *tuples;
+    FILE               *destfile;
+    int                times;          /* runs left to merge */
+    int                outdummy;       /* complete dummy runs */
+    short              fromtape;
+    long               tuplen;
+    
+    lasttp = dest->tp_prev;
+    times = lasttp->tp_fib;
+    for (tp = lasttp ; tp != dest; tp = tp->tp_prev)
+       tp->tp_fib -= times;
+    tp->tp_fib += times;
+    /* Tape[].tp_fib (A[]) is set to proper exit values */
+    
+    if (TotalDummy < TapeRange)                /* no complete dummy runs */
+       outdummy = 0;
+    else {
+       outdummy = TotalDummy;          /* a large positive number */
+       for (tp = lasttp; tp != dest; tp = tp->tp_prev)
+           if (outdummy > tp->tp_dummy)
+               outdummy = tp->tp_dummy;
+       for (tp = lasttp; tp != dest; tp = tp->tp_prev)
+           tp->tp_dummy -= outdummy;
+       tp->tp_dummy += outdummy;
+       TotalDummy -= outdummy * TapeRange;
+       /* do not add the outdummy runs yet */
+       times -= outdummy;
+    }
+    destfile = dest->tp_file;
+    while (times-- != 0) {                     /* merge one run */
+       tuples = NULL;
+       if (TotalDummy == 0)
+           for (tp = dest->tp_prev; tp != dest; tp = tp->tp_prev) {
+               GETLEN(tuplen, tp->tp_file);
+               tup = ALLOCTUP(tuplen);
+               USEMEM(tuplen);
+               TRACEMEM(merge);
+               SETTUPLEN(tup, tuplen);
+               GETTUP(tup, tuplen, tp->tp_file);
+               puttuple(&tuples, tup, TAPENO(tp));
+           }
+       else {
+           for (tp = dest->tp_prev; tp != dest; tp = tp->tp_prev) {
+               if (tp->tp_dummy != 0) {
+                   tp->tp_dummy--;
+                   TotalDummy--;
+               } else {
+                   GETLEN(tuplen, tp->tp_file);
+                   tup = ALLOCTUP(tuplen);
+                   USEMEM(tuplen);
+                   TRACEMEM(merge);
+                   SETTUPLEN(tup, tuplen);
+                   GETTUP(tup, tuplen, tp->tp_file);
+                   puttuple(&tuples, tup, TAPENO(tp));
+               }
+           }
+       }
+       while (tuples != NULL) {
+           /* possible optimization by using count in tuples */
+           tup = gettuple(&tuples, &fromtape);
+           PUTTUP(tup, destfile);
+           FREEMEM(tup->t_len);
+           FREE(tup);
+           TRACEMEM(merge);
+           GETLEN(tuplen, Tape[fromtape].tp_file);
+           if (tuplen == 0)
+               ;
+           else {
+               tup = ALLOCTUP(tuplen);
+               USEMEM(tuplen);
+               TRACEMEM(merge);
+               SETTUPLEN(tup, tuplen);
+               GETTUP(tup, tuplen, Tape[fromtape].tp_file);
+               puttuple(&tuples, tup, fromtape);
+           }
+       }                               
+       ENDRUN(destfile);
+    }
+    TotalDummy += outdummy;
+}
+
+/*
+ *     endpsort        - creates the new relation and unlinks the tape files
+ */
+void
+endpsort(Relation rdesc, FILE *file)
+{
+    register struct    tape    *tp;
+    register HeapTuple tup;
+    long               tuplen;
+    
+    if (! feof(file))
+       while (GETLEN(tuplen, file) && tuplen != 0) {
+           tup = ALLOCTUP(tuplen);
+           SortMemory += tuplen;
+           SETTUPLEN(tup, tuplen);
+           GETTUP(tup, tuplen, file);
+           heap_insert(rdesc, tup);
+           FREE(tup);
+           SortMemory -= tuplen;
+       }
+    for (tp = Tape + TapeRange; tp >= Tape; tp--)
+       destroytape(tp->tp_file);
+}
+
+/*
+ *     gettape         - handles access temporary files in polyphase merging
+ *
+ *     Optimizations:
+ *             If guarenteed that only one sort running/process,
+ *             can simplify the file generation--and need not store the
+ *             name for later unlink.
+ */
+
+struct tapelst {
+    char               *tl_name;
+    int                tl_fd;
+    struct     tapelst *tl_next;
+};
+
+static struct  tapelst *Tapes = NULL;
+static char    Tempfile[MAXPGPATH] = TEMPDIR;
+
+/*
+ *     gettape         - returns an open stream for writing/reading
+ *
+ *     Returns:
+ *             Open stream for writing/reading.
+ *             NULL if unable to open temporary file.
+ */
+FILE *
+gettape()
+{
+    register struct    tapelst *tp;
+    FILE               *file;
+    static     int     tapeinit = 0;
+    char               *mktemp();
+    
+    tp = (struct tapelst *)malloc((unsigned)sizeof (struct tapelst));
+    if (!tapeinit) {
+       Tempfile[sizeof (TEMPDIR) - 1] = '/';
+       memmove(Tempfile + sizeof(TEMPDIR), TAPEEXT, sizeof (TAPEEXT));
+       tapeinit = 1;
+    }
+    tp->tl_name = malloc((unsigned)sizeof(Tempfile));
+    /*
+     * now, copy template with final null into malloc'd space
+     */
+    memmove(tp->tl_name, Tempfile, sizeof (TEMPDIR) + sizeof (TAPEEXT));
+    mktemp(tp->tl_name);
+    
+    AllocateFile();
+    file = fopen(tp->tl_name, "w+");
+    if (file == NULL) {
+       /* XXX this should not happen */
+       FreeFile();
+       FREE(tp->tl_name);
+       FREE(tp);
+       return(NULL);
+    }
+    
+    tp->tl_fd = fileno(file);
+    tp->tl_next = Tapes;
+    Tapes = tp;
+    return(file);
+}
+
+/*
+ *     resettape       - resets the tape to size 0
+ */
+void
+resettape(FILE *file)
+{
+    register   struct  tapelst *tp;
+    register   int             fd;
+    
+    Assert(PointerIsValid(file));
+    
+    fd = fileno(file);
+    for (tp = Tapes; tp != NULL && tp->tl_fd != fd; tp = tp->tl_next)
+       ;
+    if (tp == NULL)
+       elog(WARN, "resettape: tape not found");
+    
+    file = freopen(tp->tl_name, "w+", file);
+    if (file == NULL) {
+       elog(FATAL, "could not freopen temporary file");
+    }
+}
+
+/*
+ *     distroytape     - unlinks the tape
+ *
+ *     Efficiency note:
+ *             More efficient to destroy more recently allocated tapes first.
+ *
+ *     Possible bugs:
+ *             Exits instead of returning status, if given invalid tape.
+ */
+void
+destroytape(FILE *file)
+{
+    register   struct  tapelst         *tp, *tq;
+    register   int                     fd;
+    
+    if ((tp = Tapes) == NULL)
+       elog(FATAL, "destroytape: tape not found");
+    
+    if ((fd = fileno(file)) == tp->tl_fd) {
+       Tapes = tp->tl_next;
+       fclose(file);
+       FreeFile();
+       unlink(tp->tl_name);
+       FREE(tp->tl_name);
+       FREE(tp);
+    } else
+       for ( ; ; ) {
+           if (tp->tl_next == NULL)
+               elog(FATAL, "destroytape: tape not found");
+           if (tp->tl_next->tl_fd == fd) {
+               fclose(file);
+               FreeFile();
+               tq = tp->tl_next;
+               tp->tl_next = tq->tl_next;
+               unlink(tq->tl_name);
+               FREE((tq->tl_name));
+               FREE(tq);
+               break;
+           }
+           tp = tp->tl_next;
+       }
+}
diff --git a/src/backend/utils/syscache.h b/src/backend/utils/syscache.h
new file mode 100644 (file)
index 0000000..f7d58c7
--- /dev/null
@@ -0,0 +1,89 @@
+/*-------------------------------------------------------------------------
+ *
+ * syscache.h--
+ *    System catalog cache definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        SYSCACHE_H
+#define SYSCACHE_H
+
+/*#define CACHEDEBUG*/         /* turns DEBUG elogs on */
+
+#include "postgres.h"
+#include "access/htup.h"
+#include "nodes/pg_list.h"
+
+/*
+ *     Declarations for util/syscache.c.
+ *
+ *     SysCache identifiers.
+ *
+ *      The order of these must match the order
+ *      they are entered into the structure cacheinfo[] in syscache.c 
+ *      The best thing to do is to add yours at the END, because some
+ *      code assumes that certain caches are at certain places in this
+ *      array.
+ */
+
+#define        AMOPOPID        0
+#define        AMOPSTRATEGY    1
+#define        ATTNAME         2
+#define        ATTNUM          3
+#define        INDEXRELID      4
+#define        LANNAME         5
+#define        OPRNAME         6
+#define        OPROID          7
+#define        PRONAME         8
+#define        PROOID          9
+#define        RELNAME         10
+#define        RELOID          11
+#define        TYPNAME         12
+#define        TYPOID          13
+#define        AMNAME          14
+#define        CLANAME         15
+#define INDRELIDKEY    16
+#define INHRELID       17
+#define        RULOID          18
+#define AGGNAME                19
+#define LISTENREL       20
+#define USENAME                21
+#define USESYSID       22
+#define GRONAME                23
+#define GROSYSID       24
+#define        REWRITENAME     25
+#define PROSRC          26
+
+/* ----------------
+ *     struct cachedesc:       information needed for a call to InitSysCache()
+ * ----------------
+ */
+struct cachedesc {
+    char     *name;           /* this is Name * so that we can initialize it */
+    int              nkeys;
+    int              key[4];
+    int              size;            /* sizeof(appropriate struct) */
+    char      *indname;               /* index relation for this cache, if exists */
+    HeapTuple (*iScanFunc)();  /* function to handle index scans */
+};
+
+extern void zerocaches(void);
+extern void InitCatalogCache(void);
+extern HeapTuple SearchSysCacheTuple(int cacheId, Datum key1, Datum key2,
+                                    Datum key3, Datum key4);
+extern int32 SearchSysCacheStruct(int cacheId, char *returnStruct,
+                       Datum key1, Datum key2, Datum key3, Datum key4);
+extern void *SearchSysCacheGetAttribute(int cacheId,
+                                       AttrNumber attributeNumber, 
+                                       Datum key1,
+                                       Datum key2, 
+                                       Datum key3, 
+                                       Datum key4);
+extern void *TypeDefaultRetrieve(Oid typId);
+
+#endif /* SYSCACHE_H */
diff --git a/src/backend/utils/time/Makefile.inc b/src/backend/utils/time/Makefile.inc
new file mode 100644 (file)
index 0000000..873e5a7
--- /dev/null
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for utils/time
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBSRCS+= tqual.c
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
new file mode 100644 (file)
index 0000000..091d922
--- /dev/null
@@ -0,0 +1,815 @@
+/*-------------------------------------------------------------------------
+ *
+ * tqual.c--
+ *    POSTGRES time qualification code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/* #define TQUALDEBUG  1 */
+
+#include "postgres.h"
+
+#include "access/htup.h"
+#include "access/xact.h"
+#include "storage/bufmgr.h"
+#include "access/transam.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/nabstime.h"
+
+#include "utils/tqual.h"
+
+/*
+ * TimeQualMode --
+ *     Mode indicator for treatment of time qualifications.
+ */
+typedef uint16 TimeQualMode;
+
+#define TimeQualAt     0x1
+#define TimeQualNewer  0x2
+#define TimeQualOlder  0x4
+#define TimeQualAll    0x8
+
+#define TimeQualMask   0xf
+
+#define TimeQualEvery  0x0
+#define TimeQualRange  (TimeQualNewer | TimeQualOlder)
+#define TimeQualAllAt  (TimeQualAt | TimeQualAll)
+
+typedef struct TimeQualData {
+    AbsoluteTime       start;
+    AbsoluteTime       end;
+    TimeQualMode       mode;
+} TimeQualData;
+
+typedef TimeQualData   *InternalTimeQual;
+
+static TimeQualData    SelfTimeQualData;
+TimeQual               SelfTimeQual = (Pointer)&SelfTimeQualData;
+
+extern bool            PostgresIsInitialized;
+
+/*
+ * XXX Transaction system override hacks start here
+ */
+#ifndef        GOODAMI
+
+static TransactionId   HeapSpecialTransactionId = InvalidTransactionId;
+static CommandId       HeapSpecialCommandId = FirstCommandId;
+
+void
+setheapoverride(bool on)
+{
+    if (on) {
+       TransactionIdStore(GetCurrentTransactionId(),
+                          &HeapSpecialTransactionId);
+       HeapSpecialCommandId = GetCurrentCommandId();
+    } else {
+       HeapSpecialTransactionId = InvalidTransactionId;
+    }
+}
+
+/* static */
+bool
+heapisoverride()
+{
+    if (!TransactionIdIsValid(HeapSpecialTransactionId)) {
+       return (false);
+    }
+    
+    if (!TransactionIdEquals(GetCurrentTransactionId(),
+                            HeapSpecialTransactionId) ||
+       GetCurrentCommandId() != HeapSpecialCommandId) {
+       HeapSpecialTransactionId = InvalidTransactionId;
+       
+       return (false);
+    }
+    return (true);
+}
+
+#endif /* !defined(GOODAMI) */
+/*
+ * XXX Transaction system override hacks end here
+ */
+
+static bool HeapTupleSatisfiesItself(HeapTuple tuple);
+static bool HeapTupleSatisfiesNow(HeapTuple tuple);
+static bool HeapTupleSatisfiesSnapshotInternalTimeQual(HeapTuple tuple,
+                                          InternalTimeQual qual);
+static bool HeapTupleSatisfiesUpperBoundedInternalTimeQual(HeapTuple tuple,
+                                              InternalTimeQual qual);
+static bool HeapTupleSatisfiesUpperUnboundedInternalTimeQual(HeapTuple tuple,
+                                                InternalTimeQual qual);
+
+
+     
+/*
+ * TimeQualIsValid --
+ *     True iff time qualification is valid.
+ */
+bool
+TimeQualIsValid(TimeQual qual)
+{
+    bool       hasStartTime;
+    
+    if (!PointerIsValid(qual) || qual == SelfTimeQual) {
+       return (true);
+    }
+    
+    if (((InternalTimeQual)qual)->mode & ~TimeQualMask) {
+       return (false);
+    }
+    
+    if (((InternalTimeQual)qual)->mode & TimeQualAt) {
+       return (AbsoluteTimeIsBackwardCompatiblyValid(((InternalTimeQual)qual)->start));
+    }
+    
+    hasStartTime = false;
+    
+    if (((InternalTimeQual)qual)->mode & TimeQualNewer) {
+       if (!AbsoluteTimeIsBackwardCompatiblyValid(((InternalTimeQual)qual)->start)) {
+           return (false);
+       }
+       hasStartTime = true;
+    }
+    
+    if (((InternalTimeQual)qual)->mode & TimeQualOlder) {
+       if (!AbsoluteTimeIsBackwardCompatiblyValid(((InternalTimeQual)qual)->end)) {
+           return (false);
+       }
+       if (hasStartTime) {
+           return ((bool)!AbsoluteTimeIsBefore(
+                                               ((InternalTimeQual)qual)->end,
+                                               ((InternalTimeQual)qual)->start));
+       }
+    }
+    return (true);
+}
+
+/*
+ * TimeQualIsLegal --
+ *     True iff time qualification is legal.
+ *     I.e., true iff time qualification does not intersects the future,
+ *     relative to the transaction start time.
+ *
+ * Note:
+ *     Assumes time qualification is valid.
+ */
+bool
+TimeQualIsLegal(TimeQual qual)
+{
+    Assert(TimeQualIsValid(qual));
+    
+    if (qual == NowTimeQual || qual == SelfTimeQual) {
+       return (true);
+    }
+    
+    /* TimeQualAt */
+    if (((InternalTimeQual)qual)->mode & TimeQualAt) {
+       AbsoluteTime a, b;
+       
+       a = ((InternalTimeQual)qual)->start;
+       b = GetCurrentTransactionStartTime();
+       
+       if (AbsoluteTimeIsAfter(a, b))
+           return (false);
+       else
+           return (true);
+    }
+    
+    /* TimeQualOlder or TimeQualRange */
+    if (((InternalTimeQual)qual)->mode & TimeQualOlder) {
+       AbsoluteTime a, b;
+       
+       a = ((InternalTimeQual)qual)->end;
+       b = GetCurrentTransactionStartTime();
+       
+       if (AbsoluteTimeIsAfter(a, b))
+           return (false);
+       else
+           return (true);
+    }
+    
+    /* TimeQualNewer */
+    if (((InternalTimeQual)qual)->mode & TimeQualNewer) {
+       AbsoluteTime a, b;
+       
+       a = ((InternalTimeQual)qual)->start;
+       b = GetCurrentTransactionStartTime();
+       
+       if (AbsoluteTimeIsAfter(a, b))
+           return (false);
+       else
+           return (true);
+    }
+    
+    /* TimeQualEvery */
+    return (true);
+}
+
+/*
+ * TimeQualIncludesNow --
+ *     True iff time qualification includes "now."
+ *
+ * Note:
+ *     Assumes time qualification is valid.
+ */
+bool
+TimeQualIncludesNow(TimeQual qual)
+{
+    Assert(TimeQualIsValid(qual));
+    
+    if (qual == NowTimeQual || qual == SelfTimeQual) {
+       return (true);
+    }
+    
+    if (((InternalTimeQual)qual)->mode & TimeQualAt) {
+       return (false);
+    }
+    if (((InternalTimeQual)qual)->mode & TimeQualOlder &&
+       !AbsoluteTimeIsAfter(
+                            ((InternalTimeQual)qual)->end,
+                            GetCurrentTransactionStartTime())) {
+       
+       return (false);
+    }
+    return (true);
+}
+
+/*
+ * TimeQualIncludesPast --
+ *     True iff time qualification includes some time in the past.
+ *
+ * Note:
+ *     Assumes time qualification is valid.
+ *     XXX may not be needed?
+ */
+bool
+TimeQualIncludesPast(TimeQual qual)
+{
+    Assert(TimeQualIsValid(qual));
+    
+    if (qual == NowTimeQual || qual == SelfTimeQual) {
+       return (false);
+    }
+    
+    /* otherwise, must check archive (setting locks as appropriate) */
+    return (true);
+}
+
+/*
+ * TimeQualIsSnapshot --
+ *     True iff time qualification is a snapshot qualification.
+ *
+ * Note:
+ *     Assumes time qualification is valid.
+ */
+bool
+TimeQualIsSnapshot(TimeQual qual)
+{
+    Assert(TimeQualIsValid(qual));
+    
+    if (qual == NowTimeQual || qual == SelfTimeQual) {
+       return (false);
+    }
+    
+    return ((bool)!!(((InternalTimeQual)qual)->mode & TimeQualAt));
+}
+
+/*
+ * TimeQualIsRanged --
+ *     True iff time qualification is a ranged qualification.
+ *
+ * Note:
+ *     Assumes time qualification is valid.
+ */
+bool
+TimeQualIsRanged(TimeQual qual)
+{
+    Assert(TimeQualIsValid(qual));
+    
+    if (qual == NowTimeQual || qual == SelfTimeQual) {
+       return (false);
+    }
+    
+    return ((bool)!(((InternalTimeQual)qual)->mode & TimeQualAt));
+}
+
+/*
+ * TimeQualIndicatesDisableValidityChecking --
+ *     True iff time qualification indicates validity checking should be
+ *     disabled.
+ *
+ * Note:
+ *     XXX This should not be implemented since this does not make sense.
+ */
+bool
+TimeQualIndicatesDisableValidityChecking(TimeQual qual)
+{
+    Assert (TimeQualIsValid(qual));
+    
+    if (qual == NowTimeQual || qual == SelfTimeQual) {
+       return (false);
+    }
+    
+    if (((InternalTimeQual)qual)->mode & TimeQualAll) {
+       return (true);
+    }
+    return (false);
+}
+
+/*
+ * TimeQualGetSnapshotTime --
+ *     Returns time for a snapshot time qual.
+ *
+ * Note:
+ *     Assumes time qual is valid snapshot time qual.
+ */
+AbsoluteTime
+TimeQualGetSnapshotTime(TimeQual qual)
+{
+    Assert(TimeQualIsSnapshot(qual));
+    
+    return (((InternalTimeQual)qual)->start);
+}
+
+/*
+ * TimeQualGetStartTime --
+ *     Returns start time for a ranged time qual.
+ *
+ * Note:
+ *     Assumes time qual is valid ranged time qual.
+ */
+AbsoluteTime
+TimeQualGetStartTime(TimeQual qual)
+{
+    Assert(TimeQualIsRanged(qual));
+    
+    return (((InternalTimeQual)qual)->start);
+}
+
+/*
+ * TimeQualGetEndTime --
+ *     Returns end time for a ranged time qual.
+ *
+ * Note:
+ *     Assumes time qual is valid ranged time qual.
+ */
+AbsoluteTime
+TimeQualGetEndTime(TimeQual qual)
+{
+    Assert(TimeQualIsRanged(qual));
+    
+    return (((InternalTimeQual)qual)->end);
+}
+
+/*
+ * TimeFormSnapshotTimeQual --
+ *     Returns snapshot time qual for a time.
+ *
+ * Note:
+ *     Assumes time is valid.
+ */
+TimeQual
+TimeFormSnapshotTimeQual(AbsoluteTime time)
+{
+    InternalTimeQual   qual;
+    
+    Assert(AbsoluteTimeIsBackwardCompatiblyValid(time));
+    
+    qual = (InternalTimeQual)palloc(sizeof *qual);
+    
+    qual->start = time;
+    qual->end = INVALID_ABSTIME;
+    qual->mode = TimeQualAt;
+    
+    return ((TimeQual)qual);
+}
+
+/*
+ * TimeFormRangedTimeQual --
+ *     Returns ranged time qual for a pair of times.
+ *
+ * Note:
+ *     If start time is invalid, it is regarded as the epoch.
+ *     If end time is invalid, it is regarded as "now."
+ *     Assumes start time is before (or the same as) end time.
+ */
+TimeQual
+TimeFormRangedTimeQual(AbsoluteTime startTime,
+                      AbsoluteTime endTime)
+{
+    InternalTimeQual   qual;
+    
+    qual = (InternalTimeQual)palloc(sizeof *qual);
+    
+    qual->start = startTime;
+    qual->end = endTime;
+    qual->mode = TimeQualEvery;
+    
+    if (AbsoluteTimeIsBackwardCompatiblyValid(startTime)) {
+       qual->mode |= TimeQualNewer;
+    }
+    if (AbsoluteTimeIsBackwardCompatiblyValid(endTime)) {
+       qual->mode |= TimeQualOlder;
+    }
+    
+    return ((TimeQual)qual);
+}
+
+/*
+ * HeapTupleSatisfiesTimeQual --
+ *     True iff heap tuple satsifies a time qual.
+ *
+ * Note:
+ *     Assumes heap tuple is valid.
+ *     Assumes time qual is valid.
+ *     XXX Many of the checks may be simplified and still remain correct.
+ *     XXX Partial answers to the checks may be cached in an ItemId.
+ */
+bool
+HeapTupleSatisfiesTimeQual(HeapTuple tuple, TimeQual qual)
+{
+/*    extern TransactionId AmiTransactionId; */
+    
+    Assert(HeapTupleIsValid(tuple));
+    Assert(TimeQualIsValid(qual));
+    
+    if (TransactionIdEquals(tuple->t_xmax, AmiTransactionId))
+       return(false);
+    
+    if (qual == SelfTimeQual || heapisoverride()) {
+       return (HeapTupleSatisfiesItself(tuple));
+    }
+    
+    if (qual == NowTimeQual) {
+       return (HeapTupleSatisfiesNow(tuple));
+    }
+    
+    if (!TimeQualIsLegal(qual)) {
+       elog(WARN, "HeapTupleSatisfiesTimeQual: illegal time qual");
+    }
+    
+    if (TimeQualIndicatesDisableValidityChecking(qual)) {
+       elog(WARN, "HeapTupleSatisfiesTimeQual: no disabled validity checking (yet)");
+    }
+    
+    if (TimeQualIsSnapshot(qual)) {
+       return (HeapTupleSatisfiesSnapshotInternalTimeQual(tuple,
+                                                          (InternalTimeQual)qual));
+    }
+    
+    if (TimeQualIncludesNow(qual)) {
+       return (HeapTupleSatisfiesUpperUnboundedInternalTimeQual(tuple,
+                                                                (InternalTimeQual)qual));
+    }
+    
+    return (HeapTupleSatisfiesUpperBoundedInternalTimeQual(tuple,
+                                                          (InternalTimeQual)qual));
+}
+
+/*
+ * HeapTupleSatisfiesItself --
+ *     True iff heap tuple is valid for "itself."
+ *
+ * Note:
+ *     Assumes heap tuple is valid.
+ */
+/*
+ * The satisfaction of "itself" requires the following:
+ *
+ * ((Xmin == my-transaction && (Xmax is null [|| Xmax != my-transaction)])
+ * ||
+ *
+ * (Xmin is committed &&
+ *     (Xmax is null || (Xmax != my-transaction && Xmax is not committed)))
+ */
+static bool
+HeapTupleSatisfiesItself(HeapTuple tuple)
+{
+    /*
+     * XXX Several evil casts are made in this routine.  Casting XID to be 
+     * TransactionId works only because TransactionId->data is the first
+     * (and only) field of the structure.
+     */
+    if (!AbsoluteTimeIsBackwardCompatiblyValid(tuple->t_tmin)) {
+       if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmin) &&
+           !TransactionIdIsValid((TransactionId)tuple->t_xmax)) {
+           return (true);
+       }
+       
+       if (!TransactionIdDidCommit((TransactionId)tuple->t_xmin)) {
+           return (false);
+       }
+    }
+    /* the tuple was inserted validly */
+    
+    if (AbsoluteTimeIsBackwardCompatiblyReal(tuple->t_tmax)) {
+       return (false);
+    }
+    
+    if (!TransactionIdIsValid((TransactionId)tuple->t_xmax)) {
+       return (true);
+    }
+    
+    if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmax)) {
+       return (false);
+    }
+    
+    return ((bool)!TransactionIdDidCommit((TransactionId)tuple->t_xmax));
+}
+
+/*
+ * HeapTupleSatisfiesNow --
+ *     True iff heap tuple is valid "now."
+ *
+ * Note:
+ *     Assumes heap tuple is valid.
+ */
+/*
+ * The satisfaction of "now" requires the following:
+ *
+ * ((Xmin == my-transaction && Cmin != my-command &&
+ *     (Xmax is null || (Xmax == my-transaction && Cmax != my-command)))
+ * ||
+ *
+ * (Xmin is committed &&
+ *     (Xmax is null || (Xmax == my-transaction && Cmax == my-command) ||
+ *             (Xmax is not committed && Xmax != my-transaction))))
+ *
+ *     mao says 17 march 1993:  the tests in this routine are correct;
+ *     if you think they're not, you're wrong, and you should think
+ *     about it again.  i know, it happened to me.  we don't need to
+ *     check commit time against the start time of this transaction
+ *     because 2ph locking protects us from doing the wrong thing.
+ *     if you mess around here, you'll break serializability.  the only
+ *     problem with this code is that it does the wrong thing for system
+ *     catalog updates, because the catalogs aren't subject to 2ph, so
+ *     the serializability guarantees we provide don't extend to xacts
+ *     that do catalog accesses.  this is unfortunate, but not critical.
+ */
+static bool
+HeapTupleSatisfiesNow(HeapTuple tuple)
+{
+    if (AMI_OVERRIDE)
+       return true;
+    /*
+     *  If the transaction system isn't yet initialized, then we assume
+     *  that transactions committed.  We only look at system catalogs
+     *  during startup, so this is less awful than it seems, but it's
+     *  still pretty awful.
+     */
+    
+    if (!PostgresIsInitialized)
+       return ((bool)(TransactionIdIsValid((TransactionId)tuple->t_xmin) &&
+                      !TransactionIdIsValid((TransactionId)tuple->t_xmax)));
+    
+    /*
+     * XXX Several evil casts are made in this routine.  Casting XID to be 
+     * TransactionId works only because TransactionId->data is the first
+     * (and only) field of the structure.
+     */
+    if (!AbsoluteTimeIsBackwardCompatiblyValid(tuple->t_tmin)) {
+       
+       if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmin)
+           && CommandIdIsCurrentCommandId(tuple->t_cmin)) {
+           
+           return (false);
+       }
+       
+       if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmin)
+           && !CommandIdIsCurrentCommandId(tuple->t_cmin)) {
+           
+           if (!TransactionIdIsValid((TransactionId)tuple->t_xmax)) {
+               return (true);
+           }
+           
+           Assert(TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmax));
+           
+           if (CommandIdIsCurrentCommandId(tuple->t_cmax)) {
+               return (true);
+           }
+       }
+       
+       /*
+        * this call is VERY expensive - requires a log table lookup.
+        */
+       
+       if (!TransactionIdDidCommit((TransactionId)tuple->t_xmin)) {
+           return (false);
+       }
+    }
+    
+    /* by here, the inserting transaction has committed */
+    if (!TransactionIdIsValid((TransactionId)tuple->t_xmax)) {
+       return (true);
+    }
+    
+    if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmax)) {
+       return (false);
+    }
+    
+    if (!TransactionIdDidCommit((TransactionId)tuple->t_xmax)) {
+       return (true);
+    }
+    
+    /* by here, deleting transaction has committed */
+    return (false);
+}
+
+/*
+ * HeapTupleSatisfiesSnapshotInternalTimeQual --
+ *     True iff heap tuple is valid at the snapshot time qualification.
+ *
+ * Note:
+ *     Assumes heap tuple is valid.
+ *     Assumes internal time qualification is valid snapshot qualification.
+ */
+/*
+ * The satisfaction of Rel[T] requires the following:
+ *
+ * (Xmin is committed && Tmin <= T &&
+ *     (Xmax is null || (Xmax is not committed && Xmax != my-transaction) ||
+ *             Tmax >= T))
+ */
+static bool
+HeapTupleSatisfiesSnapshotInternalTimeQual(HeapTuple tuple,
+                                          InternalTimeQual qual)
+{
+    /*
+     * XXX Several evil casts are made in this routine.  Casting XID to be 
+     * TransactionId works only because TransactionId->data is the first
+     * (and only) field of the structure.
+     */
+    if (!AbsoluteTimeIsBackwardCompatiblyValid(tuple->t_tmin)) {
+       
+       if (!TransactionIdDidCommit((TransactionId)tuple->t_xmin)) {
+           return (false);
+       }
+       
+       tuple->t_tmin = TransactionIdGetCommitTime(tuple->t_xmin);
+    }
+    
+    if (AbsoluteTimeIsBefore(TimeQualGetSnapshotTime((TimeQual)qual), tuple->t_tmin)) {
+       return (false);
+    }
+    /* the tuple was inserted validly before the snapshot time */
+    
+    if (!AbsoluteTimeIsBackwardCompatiblyReal(tuple->t_tmax)) {
+       
+       if (!TransactionIdIsValid((TransactionId)tuple->t_xmax) ||
+           !TransactionIdDidCommit((TransactionId)tuple->t_xmax)) {
+           
+           return (true);
+       }
+       
+       tuple->t_tmax = TransactionIdGetCommitTime(tuple->t_xmax);
+    }
+    
+    return ((bool)
+           AbsoluteTimeIsAfter(tuple->t_tmax,
+                               TimeQualGetSnapshotTime((TimeQual)qual)));
+}
+
+/*
+ * HeapTupleSatisfiesUpperBoundedInternalTimeQual --
+ *     True iff heap tuple is valid within a upper bounded time qualification.
+ *
+ * Note:
+ *     Assumes heap tuple is valid.
+ *     Assumes time qualification is valid ranged qualification with fixed
+ *     upper bound.
+ */
+/*
+ * The satisfaction of [T1,T2] requires the following:
+ *
+ * (Xmin is committed && Tmin <= T2 &&
+ *     (Xmax is null || (Xmax is not committed && Xmax != my-transaction) ||
+ *             T1 is null || Tmax >= T1))
+ */
+static bool
+HeapTupleSatisfiesUpperBoundedInternalTimeQual(HeapTuple tuple,
+                                              InternalTimeQual qual)
+{
+    /*
+     * XXX Several evil casts are made in this routine.  Casting XID to be 
+     * TransactionId works only because TransactionId->data is the first
+     * (and only) field of the structure.
+     */
+    if (!AbsoluteTimeIsBackwardCompatiblyValid(tuple->t_tmin)) {
+       
+       if (!TransactionIdDidCommit((TransactionId)tuple->t_xmin)) {
+           return (false);
+       }
+       
+       tuple->t_tmin = TransactionIdGetCommitTime(tuple->t_xmin);
+    }
+    
+    if (AbsoluteTimeIsBefore(TimeQualGetEndTime((TimeQual)qual), tuple->t_tmin)) {
+       return (false);
+    }
+    /* the tuple was inserted validly before the range end */
+    
+    if (!AbsoluteTimeIsBackwardCompatiblyValid(TimeQualGetStartTime((TimeQual)qual))) {
+       return (true);
+    }
+    
+    if (!AbsoluteTimeIsBackwardCompatiblyReal(tuple->t_tmax)) {
+       
+       if (!TransactionIdIsValid((TransactionId)tuple->t_xmax) ||
+           !TransactionIdDidCommit((TransactionId)tuple->t_xmax)) {
+           
+           return (true);
+       }
+       
+       tuple->t_tmax = TransactionIdGetCommitTime(tuple->t_xmax);
+    }
+    
+    return ((bool)AbsoluteTimeIsAfter(tuple->t_tmax,
+                                     TimeQualGetStartTime((TimeQual)qual)));
+}
+
+/*
+ * HeapTupleSatisfiesUpperUnboundedInternalTimeQual --
+ *     True iff heap tuple is valid within a upper bounded time qualification.
+ *
+ * Note:
+ *     Assumes heap tuple is valid.
+ *     Assumes time qualification is valid ranged qualification with no
+ *     upper bound.
+ */
+/*
+ * The satisfaction of [T1,] requires the following:
+ *
+ * ((Xmin == my-transaction && Cmin != my-command &&
+ *     (Xmax is null || (Xmax == my-transaction && Cmax != my-command)))
+ * ||
+ *
+ * (Xmin is committed &&
+ *     (Xmax is null || (Xmax == my-transaction && Cmax == my-command) ||
+ *             (Xmax is not committed && Xmax != my-transaction) ||
+ *             T1 is null || Tmax >= T1)))
+ */
+static bool
+HeapTupleSatisfiesUpperUnboundedInternalTimeQual(HeapTuple tuple,
+                                                InternalTimeQual qual)
+{
+    if (!AbsoluteTimeIsBackwardCompatiblyValid(tuple->t_tmin)) {
+       
+       if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmin) &&
+           CommandIdIsCurrentCommandId(tuple->t_cmin)) {
+           
+           return (false);
+       }
+       
+       if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmin) &&
+           !CommandIdIsCurrentCommandId(tuple->t_cmin)) {
+           
+           if (!TransactionIdIsValid((TransactionId)tuple->t_xmax)) {
+               return (true);
+           }
+           
+           Assert(TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmax));
+           
+           return ((bool) !CommandIdIsCurrentCommandId(tuple->t_cmax));
+       }
+       
+       if (!TransactionIdDidCommit((TransactionId)tuple->t_xmin)) {
+           return (false);
+       }
+       
+       tuple->t_tmin = TransactionIdGetCommitTime(tuple->t_xmin);
+    }
+    /* the tuple was inserted validly */
+    
+    if (!AbsoluteTimeIsBackwardCompatiblyValid(TimeQualGetStartTime((TimeQual)qual))) {
+       return (true);
+    }
+    
+    if (!AbsoluteTimeIsBackwardCompatiblyReal(tuple->t_tmax)) {
+       
+       if (!TransactionIdIsValid((TransactionId)tuple->t_xmax)) {
+           return (true);
+       }
+       
+       if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmax)) {
+           return (CommandIdIsCurrentCommandId(tuple->t_cmin));
+       }
+       
+       if (!TransactionIdDidCommit((TransactionId)tuple->t_xmax)) {
+           return (true);
+       }
+       
+       tuple->t_tmax = TransactionIdGetCommitTime(tuple->t_xmax);
+    }
+    
+    return ((bool)AbsoluteTimeIsAfter(tuple->t_tmax,
+                                     TimeQualGetStartTime((TimeQual)qual)));
+}
diff --git a/src/backend/utils/tqual.h b/src/backend/utils/tqual.h
new file mode 100644 (file)
index 0000000..79d0e80
--- /dev/null
@@ -0,0 +1,55 @@
+/*-------------------------------------------------------------------------
+ *
+ * tqual.h--
+ *    POSTGRES time qualification definitions.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTE
+ *    It may be desirable to allow time qualifications to indicate
+ *    relative times.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef        TQUAL_H
+#define TQUAL_H
+
+#include "postgres.h"
+#include "utils/nabstime.h"
+#include "access/htup.h"
+
+typedef struct TimeQualSpace {
+    char       data[12];
+} TimeQualSpace;
+
+typedef Pointer        TimeQual;
+
+/* Tuples valid as of StartTransactionCommand */
+#define        NowTimeQual     ((TimeQual) NULL)
+
+/* As above, plus updates in this command */
+extern TimeQual        SelfTimeQual;
+
+extern void setheapoverride(bool on);
+extern bool heapisoverride(void);
+
+extern bool TimeQualIsValid(TimeQual qual);
+extern bool TimeQualIsLegal(TimeQual qual);
+extern bool TimeQualIncludesNow(TimeQual qual);
+extern bool TimeQualIncludesPast(TimeQual qual);
+extern bool TimeQualIsSnapshot(TimeQual qual);
+extern bool TimeQualIsRanged(TimeQual qual);
+extern bool TimeQualIndicatesDisableValidityChecking(TimeQual qual);
+extern AbsoluteTime TimeQualGetSnapshotTime(TimeQual qual);
+extern AbsoluteTime TimeQualGetStartTime(TimeQual qual);
+extern AbsoluteTime TimeQualGetEndTime(TimeQual qual);
+extern TimeQual TimeFormSnapshotTimeQual(AbsoluteTime time);
+extern TimeQual TimeFormRangedTimeQual(AbsoluteTime startTime,
+                                      AbsoluteTime endTime);
+extern bool HeapTupleSatisfiesTimeQual(HeapTuple tuple, TimeQual qual);
+
+
+#endif /* TQUAL_H */
diff --git a/src/bin/Makefile b/src/bin/Makefile
new file mode 100644 (file)
index 0000000..3a3bdd6
--- /dev/null
@@ -0,0 +1,30 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for src/bin (utility programs)
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+#
+# C programs
+#
+SUBDIR=  monitor pg_id pg_version psql pg_dump
+
+ifeq ($(USE_TCL), true)
+SUBDIR += pgtclsh
+endif
+
+#
+# Shell scripts
+#
+SUBDIR+= cleardbdir createdb createuser destroydb destroyuser initdb 
+
+
+include ../mk/postgres.subdir.mk
+
diff --git a/src/bin/Makefile.global b/src/bin/Makefile.global
new file mode 100644 (file)
index 0000000..8bd353e
--- /dev/null
@@ -0,0 +1,33 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    global configurations for Makefiles in src/bin
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+CFLAGS+= -I$(HEADERDIR) -I$(srcdir)/backend -I$(srcdir)/backend/include -I$(srcdir)/libpq
+
+#
+# try locating libpq.a in the following places
+# Almost all (all?) the C programs in this directory
+# link with libpq, so we put it here.
+#
+LIBPQ:=   -L$(srcdir)/libpq/$(objdir) -L$(LIBDIR) -lpq
+
+LD_ADD+= $(LIBPQ)
+DPADD+= $(LIBPQ)
+
+
+#
+# And where libpq goes, so goes the authentication stuff...
+#
+ifdef KRBVERS
+LD_ADD+= $(KRBLIBS)
+CFLAGS+= $(KRBFLAGS)
+endif
diff --git a/src/bin/cleardbdir/Makefile b/src/bin/cleardbdir/Makefile
new file mode 100644 (file)
index 0000000..26fe82b
--- /dev/null
@@ -0,0 +1,21 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for bin/cleardbdir
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SHPROG=        cleardbdir
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+include ../Makefile.global
+
+include $(MKDIR)/postgres.shell.mk
+
diff --git a/src/bin/cleardbdir/cleardbdir.sh b/src/bin/cleardbdir/cleardbdir.sh
new file mode 100644 (file)
index 0000000..16a295e
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+#-------------------------------------------------------------------------
+#
+# cleardbdir.sh--
+#    completely clear out the database directory
+#
+#    this program clears out the database directory, but leaves the .bki
+#    files so that initdb(1) can be run again.
+#
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+[ -z "$PGDATA" ] && PGDATA=_fUnKy_DATADIR_sTuFf_
+
+echo "This program completely destroys all the databases in the directory"
+echo "$PGDATA"
+echo _fUnKy_DASH_N_sTuFf_ "Are you sure you want to do this (y/n) [n]? "_fUnKy_BACKSLASH_C_sTuFf_
+read resp || exit
+case $resp in
+       y*)     : ;;
+       *)      exit ;;
+esac
+
+cd $PGDATA || exit
+for i in *
+do
+if [ $i != "files" -a $i != "pg_hba" ]
+then
+       /bin/rm -rf $i
+fi
+done
diff --git a/src/bin/createdb/Makefile b/src/bin/createdb/Makefile
new file mode 100644 (file)
index 0000000..17c5e1f
--- /dev/null
@@ -0,0 +1,21 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for bin/createdb
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SHPROG=        createdb
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+include ../Makefile.global
+
+include $(MKDIR)/postgres.shell.mk
+
diff --git a/src/bin/createdb/createdb.sh b/src/bin/createdb/createdb.sh
new file mode 100644 (file)
index 0000000..fb28599
--- /dev/null
@@ -0,0 +1,66 @@
+#!/bin/sh
+#-------------------------------------------------------------------------
+#
+# createdb.sh--
+#    create a postgres database
+#
+#    this program runs the monitor with the "-c" option to create
+#    the requested database.
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+# ----------------
+#       Set paths from environment or default values.
+#       The _fUnKy_..._sTuFf_ gets set when the script is installed
+#       from the default value for this build.
+#      Currently the only thing we look for from the environment is
+#      PGDATA, PGHOST, and PGPORT
+#
+# ----------------
+[ -z "$PGPORT" ] && PGPORT=5432
+[ -z "$PGHOST" ] && PGHOST=localhost
+BINDIR=_fUnKy_BINDIR_sTuFf_
+PATH=$BINDIR:$PATH
+
+CMDNAME=`basename $0`
+
+if [ -z "$USER" ]; then
+    if [ -z "$LOGNAME" ]; then
+       if [ -z "`whoami`" ]; then
+           echo "$CMDNAME: cannot determine user name"
+           exit 1
+       fi
+    else
+       USER=$LOGNAME
+       export USER
+    fi
+fi
+
+dbname=$USER
+
+while test -n "$1"
+do
+    case $1 in
+       -a) AUTHSYS=$2; shift;;
+        -h) PGHOST=$2; shift;;
+        -p) PGPORT=$2; shift;;
+         *) dbname=$1;;
+    esac
+    shift;
+done
+
+AUTHOPT="-a $AUTHSYS"
+[ -z "$AUTHSYS" ] && AUTHOPT=""
+
+monitor -TN $AUTHOPT -h $PGHOST -p $PGPORT -c "create database $dbname" template1 || {
+    echo "$CMDNAME: database creation failed on $dbname."
+    exit 1
+}
+
+exit 0
diff --git a/src/bin/createuser/Makefile b/src/bin/createuser/Makefile
new file mode 100644 (file)
index 0000000..ae4ab8f
--- /dev/null
@@ -0,0 +1,21 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for bin/createuser
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SHPROG=        createuser
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+include ../Makefile.global
+
+include $(MKDIR)/postgres.shell.mk
+
diff --git a/src/bin/createuser/createuser.sh b/src/bin/createuser/createuser.sh
new file mode 100644 (file)
index 0000000..b9c5414
--- /dev/null
@@ -0,0 +1,225 @@
+#!/bin/sh
+#-------------------------------------------------------------------------
+#
+# createuser.sh--
+#    utility for creating a user in the POSTGRES database
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+# Note - this should NOT be setuid.
+#
+#-------------------------------------------------------------------------
+
+# ----------------
+#       Set paths from environment or default values.
+#       The _fUnKy_..._sTuFf_ gets set when the script is installed
+#       from the default value for this build.
+#       Currently the only thing we look for from the environment is
+#       PGDATA, PGHOST, and PGPORT
+#
+# ----------------
+[ -z "$PGPORT" ] && PGPORT=5432
+[ -z "$PGHOST" ] && PGHOST=localhost
+BINDIR=_fUnKy_BINDIR_sTuFf_
+PATH=$BINDIR:$PATH
+
+CMDNAME=`basename $0`
+
+if [ -z "$USER" ]; then
+    if [ -z "$LOGNAME" ]; then
+       if [ -z "`whoami`" ]; then
+           echo "$CMDNAME: cannot determine user name"
+           exit 1
+       fi
+    else
+       USER=$LOGNAME
+       export USER
+    fi
+fi
+
+while [ -n "$1" ]
+do
+    case $1 in 
+       -a) AUTHSYS=$2; shift;;
+        -h) PGHOST=$2; shift;;
+        -p) PGPORT=$2; shift;;
+         *) NEWUSER=$1;;
+    esac
+    shift;
+done
+
+AUTHOPT="-a $AUTHSYS"
+[ -z "$AUTHSYS" ] && AUTHOPT=""
+
+MARGS="-TN $AUTHOPT -h $PGHOST -p $PGPORT"
+
+#
+# generate the first part of the actual monitor command
+#
+
+MONITOR="monitor $MARGS"
+
+#
+# see if user $USER is allowed to create new users
+#
+
+QUERY="select usesuper from pg_user where usename = '$USER'"
+#echo $QUERY
+
+ADDUSER=`$MONITOR -TN -c "$QUERY" template1`
+
+if [ $? -ne 0 ]
+then
+    echo "$CMDNAME: database access failed." 1>&2
+    exit 1
+fi
+
+if [ -n "$ADDUSER" ]
+then
+
+if [ $ADDUSER != "t" ]
+then
+    echo "$CMDNAME: $USER cannot create users." 1>&2
+    exit 1
+fi
+fi
+
+#
+# get the user name of the new user.  Make sure it doesn't already exist.
+#
+
+if [ -z "$NEWUSER" ]
+then
+    echo _fUnKy_DASH_N_sTuFf_ "Enter name of user to add ---> "_fUnKy_BACKSLASH_C_sTuFf_
+    read NEWUSER
+fi
+
+QUERY="select usesysid from pg_user where usename = '$NEWUSER'"
+
+RES=`$MONITOR -TN -c "$QUERY" template1`
+
+if [ $? -ne 0 ]
+then
+    echo "$CMDNAME: database access failed." 1>&2
+    exit 1
+fi
+
+if [ -n "$RES" ]
+then
+    echo "$CMDNAME: user "\"$NEWUSER\"" already exists" 1>&2
+    exit 1
+fi
+
+done=0
+
+#
+# get the system id of the new user.  Make sure it is unique.
+#
+
+while [ $done -ne 1 ]
+do
+    SYSID=
+    DEFSYSID=`pg_id $NEWUSER 2>/dev/null`
+    if [ $? -eq 0 ]; then
+       DEFMSG=" or RETURN to use unix user ID: $DEFSYSID"
+    else
+       DEFMSG=
+       DEFSYSID=
+    fi
+    while  [ -z "$SYSID" ]
+    do
+       echo _fUnKy_DASH_N_sTuFf_ "Enter user's postgres ID$DEFMSG -> "_fUnKy_BACKSLASH_C_sTuFf_
+       read SYSID
+       [ -z "$SYSID" ] && SYSID=$DEFSYSID;
+       SYSIDISNUM=`echo $SYSID | egrep '^[0-9]+$'`
+       if [ -z "$SYSIDISNUM" ]
+       then
+               echo "$CMDNAME: the postgres ID must be a number"
+               exit 1
+       fi
+       QUERY="select usename from pg_user where usesysid = '$SYSID'::int4"
+       RES=`$MONITOR -TN -c "$QUERY" template1`
+       if [ $? -ne 0 ]
+       then
+               echo "$CMDNAME: database access failed."
+               exit 1
+       fi
+       if [ -n "$RES" ]
+       then
+               echo 
+               echo "$CMDNAME: $SYSID already belongs to $RES, pick another"
+               DEFMSG= DEFSYSID= SYSID=
+       else
+               done=1
+       fi
+    done
+done
+
+#
+# get the rest of the user info...
+#
+
+#
+# can the user create databases?
+#
+
+yn=f
+
+while [ "$yn" != y -a "$yn" != n ]
+do
+    echo _fUnKy_DASH_N_sTuFf_ "Is user \"$NEWUSER\" allowed to create databases (y/n) "_fUnKy_BACKSLASH_C_sTuFf_
+    read yn
+done
+
+if [ "$yn" = y ]
+then
+    CANCREATE=t
+else
+    CANCREATE=f
+fi
+
+#
+# can the user add users?
+#
+
+yn=f
+
+while [ "$yn" != y -a "$yn" != n ]
+do
+    echo _fUnKy_DASH_N_sTuFf_ "Is user \"$NEWUSER\" allowed to add users? (y/n) "_fUnKy_BACKSLASH_C_sTuFf_
+    read yn
+done
+
+if (test "$yn" = y)
+then
+    CANADDUSER=t
+else
+    CANADDUSER=f
+fi
+
+QUERY="insert into pg_user \
+        (usename, usesysid, usecreatedb, usetrace, usesuper, usecatupd) \
+       values \
+         ('$NEWUSER', $SYSID, '$CANCREATE', 't', '$CANADDUSER','t')"
+
+RES=`$MONITOR -TN -c "$QUERY" template1`
+
+#
+# Wrap things up.  If the user was created successfully, AND the user was
+# NOT allowed to create databases, remind the DBA to create one for the user.
+#
+
+if [ $? -ne 0 ]
+then
+    echo "$CMDNAME: $NEWUSER was NOT added successfully"
+else
+    echo "$CMDNAME: $NEWUSER was successfully added"
+    if [ "$CANCREATE" = f ]
+    then
+        echo "don't forget to create a database for $NEWUSER"
+    fi
+fi
diff --git a/src/bin/destroydb/Makefile b/src/bin/destroydb/Makefile
new file mode 100644 (file)
index 0000000..fa9df2e
--- /dev/null
@@ -0,0 +1,21 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for bin/destroydb
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SHPROG=        destroydb
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+include ../Makefile.global
+
+include $(MKDIR)/postgres.shell.mk
+
diff --git a/src/bin/destroydb/destroydb.sh b/src/bin/destroydb/destroydb.sh
new file mode 100644 (file)
index 0000000..1f990a9
--- /dev/null
@@ -0,0 +1,69 @@
+#!/bin/sh
+#-------------------------------------------------------------------------
+#
+# destroydb.sh--
+#    destroy a postgres database
+#
+#    this program runs the monitor with the ? option to destroy
+#    the requested database.
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+# ----------------
+#       Set paths from environment or default values.
+#       The _fUnKy_..._sTuFf_ gets set when the script is installed
+#       from the default value for this build.
+#       Currently the only thing we look for from the environment is
+#       PGDATA, PGHOST, and PGPORT
+#
+# ----------------
+[ -z "$PGPORT" ] && PGPORT=5432
+[ -z "$PGHOST" ] && PGHOST=localhost
+BINDIR=_fUnKy_BINDIR_sTuFf_
+PATH=$BINDIR:$PATH
+
+CMDNAME=`basename $0`
+
+if [ -z "$USER" ]; then
+    if [ -z "$LOGNAME" ]; then
+       if [ -z "`whoami`" ]; then
+           echo "$CMDNAME: cannot determine user name"
+           exit 1
+       fi
+    else
+       USER=$LOGNAME
+       export USER
+    fi
+fi
+
+dbname=$USER
+
+while [ -n "$1" ]
+do
+       case $1 in 
+               -a) AUTHSYS=$2; shift;;
+               -h) PGHOST=$2; shift;;
+               -p) PGPORT=$2; shift;;
+                *) dbname=$1;;
+       esac
+       shift;
+done
+
+AUTHOPT="-a $AUTHSYS"
+[ -z "$AUTHSYS" ] && AUTHOPT=""
+
+monitor -TN -h $PGHOST -p $PGPORT -c "drop database $dbname" template1
+
+if [ $? -ne 0 ]
+then
+       echo "$CMDNAME: database destroy failed on $dbname."
+       exit 1
+fi
+
+exit 0
diff --git a/src/bin/destroyuser/Makefile b/src/bin/destroyuser/Makefile
new file mode 100644 (file)
index 0000000..2ff8e9d
--- /dev/null
@@ -0,0 +1,21 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for bin/destroyuser
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SHPROG=        destroyuser
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+include ../Makefile.global
+
+include $(MKDIR)/postgres.shell.mk
+
diff --git a/src/bin/destroyuser/destroyuser.sh b/src/bin/destroyuser/destroyuser.sh
new file mode 100644 (file)
index 0000000..f7c8999
--- /dev/null
@@ -0,0 +1,192 @@
+#!/bin/sh
+#-------------------------------------------------------------------------
+#
+# destroyuser.sh--
+#    utility for destroying a user from the POSTGRES database.
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+# Note - this should NOT be setuid.
+#
+#-------------------------------------------------------------------------
+
+# ----------------
+#       Set paths from environment or default values.
+#       The _fUnKy_..._sTuFf_ gets set when the script is installed
+#       from the default value for this build.
+#       Currently the only thing we look for from the environment is
+#       PGDATA, PGHOST, and PGPORT
+#
+# ----------------
+[ -z "$PGPORT" ] && PGPORT=5432
+[ -z "$PGHOST" ] && PGHOST=localhost
+BINDIR=_fUnKy_BINDIR_sTuFf_
+PATH=$BINDIR:$PATH
+
+CMDNAME=`basename $0`
+
+if [ -z "$USER" ]; then
+    if [ -z "$LOGNAME" ]; then
+       if [ -z "`whoami`" ]; then
+           echo "$CMDNAME: cannot determine user name"
+           exit 1
+       fi
+    else
+       USER=$LOGNAME
+       export USER
+    fi
+fi
+
+while (test -n "$1")
+do
+    case $1 in 
+       -a) AUTHSYS=$2; shift;;
+        -h) PGHOST=$2; shift;;
+        -p) PGPORT=$2; shift;;
+         *) DELUSER=$1;;
+    esac
+    shift;
+done
+
+AUTHOPT="-a $AUTHSYS"
+[ -z "$AUTHSYS" ] && AUTHOPT=""
+
+MARGS="-TN $AUTHOPT -p $PGPORT -h $PGHOST"
+
+#
+# generate the first part of the actual monitor command
+#
+MONITOR="monitor $MARGS"
+
+#
+# see if user $USER is allowed to create new users.  Only a user who can
+# create users can delete them.
+#
+
+QUERY="select usesuper from pg_user where usename = '$USER'"
+ADDUSER=`$MONITOR -c "$QUERY" template1`
+
+if [ $? -ne 0 ]
+then
+    echo "$CMDNAME: database access failed."
+    exit 1
+fi
+
+if [ $ADDUSER != "t" ]
+then
+    echo "$CMDNAME: $USER cannot delete users."
+fi
+
+#
+# get the user name of the user to delete.  Make sure it exists.
+#
+
+if [ -z "$DELUSER" ]
+then
+    echo _fUnKy_DASH_N_sTuFf_ "Enter name of user to delete ---> "_fUnKy_BACKSLASH_C_sTuFf_
+    read DELUSER
+fi
+
+QUERY="select usesysid from pg_user where usename = '$DELUSER'"
+
+RES=`$MONITOR -c "$QUERY" template1`
+
+if [ $? -ne 0 ]
+then
+    echo "$CMDNAME: database access failed."
+    exit 1
+fi
+
+if [ ! -n "$RES" ]
+then
+    echo "$CMDNAME: user "\"$DELUSER\"" does not exist."
+    exit 1
+fi
+
+SYSID=`echo $RES | sed 's/ //g'`
+
+#
+# destroy the databases owned by the deleted user.  First, use this query
+# to find out what they are.
+#
+
+QUERY="select datname from pg_database where datdba = '$SYSID'::oid"
+       
+
+ALLDBS=`$MONITOR -c "$QUERY" template1`
+
+if [ $? -ne 0 ]
+then
+    echo "$CMDNAME: database access failed - exiting..."
+    exit 1
+fi
+
+
+#
+# don't try to delete template1!
+#
+
+for i in $ALLDBS
+do
+    if [ $i != "template1" ]
+    then
+        DBLIST="$DBLIST $i"
+    fi
+done
+
+if [ -n "$DBLIST" ]
+then
+    echo "User $DELUSER owned the following databases:"
+    echo $DBLIST
+    echo
+
+#
+# Now we warn the DBA that deleting this user will destroy a bunch of databases
+#
+
+    yn=f
+    while [ $yn != y -a $yn != n ]
+    do
+        echo _fUnKy_DASH_N_sTuFf_ "Deleting user $DELUSER will destroy them. Continue (y/n)? "_fUnKy_BACKSLASH_C_sTuFf_
+        read yn
+    done
+
+    if [ $yn = n ]
+    then
+        echo "$CMDNAME: exiting"
+        exit 1
+    fi
+
+    #
+    # now actually destroy the databases
+    #
+
+    for i in $DBLIST
+    do
+        echo "destroying database $i"
+
+        QUERY="drop database $i"
+        $MONITOR -c "$QUERY" template1
+        if [ $? -ne 0 ]
+        then
+            echo "$CMDNAME: drop database on $i failed - exiting"
+            exit 1
+        fi
+    done
+fi
+
+QUERY="delete from pg_user where usename = '$DELUSER'"
+
+$MONITOR -c "$QUERY" template1
+if [ $? -ne 0 ]
+then
+    echo "$CMDNAME: delete of user $DELUSER was UNSUCCESSFUL"
+else
+    echo "$CMDNAME: delete of user $DELUSER was successful."
+fi
+
+exit 0
diff --git a/src/bin/initdb/Makefile b/src/bin/initdb/Makefile
new file mode 100644 (file)
index 0000000..4dfd860
--- /dev/null
@@ -0,0 +1,21 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for bin/initdb
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SHPROG=        initdb
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+include ../Makefile.global
+
+include $(MKDIR)/postgres.shell.mk
+
diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh
new file mode 100644 (file)
index 0000000..13203b4
--- /dev/null
@@ -0,0 +1,222 @@
+#!/bin/sh
+#-------------------------------------------------------------------------
+#
+# initdb.sh--
+#    create a postgres template database
+#
+#    this program feeds the proper input to the ``postgres'' program
+#    to create a postgres database and register it in the
+#    shared ``pg_database'' database.
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+# ----------------
+#       Set paths from environment or default values.
+#       The _fUnKy_..._sTuFf_ gets set when the script is installed
+#       from the default value for this build.
+#       Currently the only thing wee look for from the environment is
+#       PGDATA, PGHOST, and PGPORT
+#
+# ----------------
+[ -z "$PGDATA" ] && { PGDATA=_fUnKy_DATADIR_sTuFf_; export PGDATA; }
+[ -z "$PGPORT" ] && { PGPORT=5432; export PGPORT; }
+[ -z "$PGHOST" ] && { PGHOST=localhost; export PGHOST; }
+POSTGRESDIR=_fUnKy_POSTGRESDIR_sTuFf_
+BINDIR=_fUnKy_BINDIR_sTuFf_
+FILESDIR=$PGDATA/files
+PATH=$BINDIR:$PATH
+export PATH
+
+CMDNAME=`basename $0`
+
+# ----------------
+#      check arguments:
+#          -d indicates debug mode.
+#          -n means don't clean up on error (so your cores don't go away)
+# ----------------
+debug=0
+noclean=0
+verbose=0
+
+for ARG
+do
+       case "$ARG" in
+       -d)     debug=1; echo "$CMDNAME: debug mode on";;
+       -n)     noclean=1; echo "$CMDNAME: noclean mode on";;
+       -v)     verbose=1; echo "$CMDNAME: verbose mode on";;
+       *)      echo "initdb [-d][-n][-v]\n -d : debug mode\n -n : noclean mode, leaves temp files around \n -v : verbose mode";  exit 0;
+       esac
+done
+
+# ----------------
+#      if the debug flag is set, then 
+# ----------------
+if test "$debug" -eq 1
+then
+    BACKENDARGS="-boot -C -d"
+else
+    BACKENDARGS="-boot -C -Q"
+fi
+
+
+TEMPLATE=$FILESDIR/local1_template1.bki
+GLOBAL=$FILESDIR/global1.bki
+if [ ! -f $TEMPLATE -o ! -f $GLOBAL ]
+then
+    echo "$CMDNAME: error: database initialization files not found."
+    echo "$CMDNAME: either gmake install has not been run or you're trying to"
+    echo "$CMDNAME: run this program on a machine that does not store the"
+    echo "$CMDNAME: database (PGHOST doesn't work for this)."
+    exit 1
+fi
+
+if test "$verbose" -eq 1
+then
+    echo "$CMDNAME: using $TEMPLATE"
+    echo "$CMDNAME: using $GLOBAL"
+fi
+
+#
+# Figure out who I am...
+#
+
+PG_UID=`pg_id`
+
+if test $PG_UID -eq 0
+then
+    echo "$CMDNAME: do not install POSTGRES as root"
+    exit 1
+fi
+
+# ----------------
+#      create the template database if necessary
+#      the first we do is create data/base, so we'll check for that.
+# ----------------
+
+if test -d "$PGDATA/base"
+then
+       echo "$CMDNAME: error: it looks like initdb has already been run.  You must"
+       echo "clean out the database directory first with the cleardbdir program"
+       exit 1
+fi
+
+# umask must disallow access to group, other for files and dirs
+umask 077
+
+mkdir $PGDATA/base $PGDATA/base/template1
+
+if test "$verbose" -eq 1
+then
+    echo "$CMDNAME: creating SHARED relations in $PGDATA"
+    echo "$CMDNAME: creating template database in $PGDATA/base/template1"
+    echo "postgres $BACKENDARGS template1 < $TEMPLATE "
+fi
+
+postgres $BACKENDARGS template1 < $TEMPLATE 
+
+
+if test $? -ne 0
+then
+    echo "$CMDNAME: could not create template database"
+    if test $noclean -eq 0
+    then
+           echo "$CMDNAME: cleaning up."
+           cd $PGDATA
+           for i in *
+           do
+               if [ $i != "files" -a $i != "pg_hba" ]
+               then
+                       /bin/rm -rf $i
+               fi
+           done
+        else
+           echo "$CMDNAME: cleanup not done (noclean mode set)."
+    fi
+       exit 1;
+fi
+
+pg_version $PGDATA/base/template1
+
+#
+# Add the template database to pg_database
+#
+
+echo "open pg_database" > /tmp/create.$$
+echo "insert (template1 $PG_UID template1)" >> /tmp/create.$$
+#echo "show" >> /tmp/create.$$
+echo "close pg_database" >> /tmp/create.$$
+
+if test "$verbose" -eq 1
+then
+       echo "postgres $BACKENDARGS template1 < $GLOBAL"
+fi
+
+postgres $BACKENDARGS template1 < $GLOBAL 
+
+if (test $? -ne 0)
+then
+    echo "$CMDNAME: could create shared relations"
+    if (test $noclean -eq 0)
+    then
+           echo "$CMDNAME: cleaning up."
+           cd $PGDATA
+           for i in *
+           do
+               if [ $i != "files" ]
+               then
+                       /bin/rm -rf $i
+               fi
+           done
+    else
+           echo "$CMDNAME: cleanup not done (noclean mode set)."
+    fi
+       exit 1;
+fi
+
+pg_version $PGDATA
+
+if test "$verbose" -eq 1
+then
+       echo "postgres $BACKENDARGS template1 < /tmp/create.$$"
+fi
+
+postgres $BACKENDARGS template1 < /tmp/create.$$ 
+
+if test $? -ne 0
+then
+    echo "$CMDNAME: could not log template database"
+    if (test $noclean -eq 0)
+    then
+           echo "$CMDNAME: cleaning up."
+           cd $PGDATA
+           for i in *
+           do
+               if [ $i != "files" ]
+               then
+                       /bin/rm -rf $i
+               fi
+           done
+    else
+           echo "$CMDNAME: cleanup not done (noclean mode set)."
+    fi
+       exit 1;
+fi
+
+if test $debug -eq 0
+then
+
+if test "$verbose" -eq 1
+then
+    echo "vacuuming template1"
+fi
+
+    echo "vacuum" | postgres -Q template1 > /dev/null
+fi
+
+rm -f /tmp/create.$$
diff --git a/src/bin/ipcclean/Makefile b/src/bin/ipcclean/Makefile
new file mode 100644 (file)
index 0000000..cae1e01
--- /dev/null
@@ -0,0 +1,21 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for bin/initdb
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SHPROG=        ipcclean
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+include ../Makefile.global
+
+include $(MKDIR)/postgres.shell.mk
+
diff --git a/src/bin/ipcclean/ipcclean.sh b/src/bin/ipcclean/ipcclean.sh
new file mode 100644 (file)
index 0000000..803ea19
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# $Header$
+#
+PATH=_fUnKy_IPCCLEANPATH_sTuFf_:$PATH
+export PATH
+ipcs | egrep '^m .*|^s .*' | egrep "`whoami`|postgres" | \
+awk '{printf "ipcrm -%s %s\n", $1, $2}' '-' | sh
diff --git a/src/bin/monitor/Makefile b/src/bin/monitor/Makefile
new file mode 100644 (file)
index 0000000..a59738d
--- /dev/null
@@ -0,0 +1,23 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for bin/monitor
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+PROG=  monitor
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+include ../Makefile.global
+
+SRCS=  monitor.c
+
+include $(MKDIR)/postgres.prog.mk
+
diff --git a/src/bin/monitor/monitor.c b/src/bin/monitor/monitor.c
new file mode 100644 (file)
index 0000000..81c6660
--- /dev/null
@@ -0,0 +1,1058 @@
+/*-------------------------------------------------------------------------
+ *
+ * monitor.c--
+ *    POSTGRES Terminal Monitor
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <errno.h>
+#include "libpq/pqsignal.h"    /* substitute for <signal.h> */
+#include <stdio.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/param.h> /* for MAXHOSTNAMELEN on most */
+#ifndef WIN32
+#include <unistd.h>
+#endif
+#ifdef PORTNAME_sparc_solaris
+#include <netdb.h>     /* for MAXHOSTNAMELEN on some */
+#endif
+#include <sys/types.h>
+/* #include <sys/uio.h> */
+#include <time.h>
+
+#include "libpq-fe.h"
+#include "libpq/libpq-fs.h"
+
+extern char    *getenv();
+
+/* 
+ * monitor.c  -- function prototypes (all private)
+ */
+static void do_input(FILE *ifp);
+static void init_tmon();
+static void welcome();
+static void handle_editor();
+static void handle_shell();
+static void handle_send();
+static int handle_execution(char *query); 
+static void handle_file_insert(FILE *ifp);
+static void handle_print();
+static void handle_exit(int exit_status);
+static void handle_clear();
+static void handle_print_time();
+static int handle_write_to_file();
+static void handle_help();
+static void stuff_buffer(char c);
+static void argsetup(int *argcP, char ***argvP);
+static void handle_copy_out(PGresult *res);
+static void handle_copy_in(PGresult *res);
+
+
+/*      
+ *          Functions which maintain the logical query buffer in
+ *          /tmp/PQxxxxx.  It in general just does a copy from input
+ *          to query buffer, unless it gets a backslash escape character.
+ *          It recognizes the following escapes:
+ *      
+ *          \e -- enter editor
+ *          \g -- "GO": submit query to POSTGRES
+ *          \i -- include (switch input to external file)
+ *          \p -- print query buffer 
+ *          \q -- quit POSTGRES
+ *          \r -- force reset (clear) of query buffer
+ *          \s -- call shell
+ *          \t -- print current time
+ *          \w -- write query buffer to external file
+ *          \h -- print the list of commands
+ *          \? -- print the list of commands
+ *          \\ -- produce a single backslash in query buffer
+ *      
+ */
+
+/*
+ *   Declaration of global variables (but only to the file monitor.c
+ */
+
+#define DEFAULT_EDITOR "/usr/ucb/vi"
+#define COPYBUFSIZ     8192
+static char *user_editor;     /* user's desired editor  */
+static int tmon_temp;         /* file descriptor for temp. buffer file */
+static char *tmon_temp_filename;
+static char query_buffer[8192];  /* Max postgres buffer size */
+static char *RunOneFile = NULL;
+bool RunOneCommand = false;
+bool Debugging;
+bool Verbose;
+bool Silent;
+bool TerseOutput = false;
+bool PrintAttNames = true;
+bool SingleStepMode = false;
+bool SemicolonIsGo = true;
+
+#define COLWIDTH 12
+
+extern char *optarg;
+extern int optind,opterr;
+FILE *debug_port;
+
+/*
+ *  As of release 4, we allow the user to specify options in the environment
+ *  variable PGOPTION.  These are treated as command-line options to the
+ *  terminal monitor, and are parsed before the actual command-line args.
+ *  The arge struct is used to construct an argv we can pass to getopt()
+ *  containing the union of the environment and command line arguments.
+ */
+
+typedef struct arge {
+    char       *a_arg;
+    struct arge        *a_next;
+} arge;
+
+/* the connection to the backend */
+PGconn *conn;
+
+void
+main(int argc, char **argv)
+{
+    int c;
+    int errflag = 0;
+    char *progname;
+    char *debug_file;
+    char *dbname;
+    char *command;
+    int exit_status = 0;
+    char errbuf[ERROR_MSG_LENGTH];
+    char *username, usernamebuf[NAMEDATALEN + 1];
+
+    char *pghost = NULL;
+    char *pgtty = NULL;
+    char *pgoptions = NULL;
+    char *pgport = NULL;
+    int  pgtracep = 0;
+
+    /* 
+     * Processing command line arguments.
+     *
+     * h : sets the hostname.
+     * p : sets the coom. port
+     * t : sets the tty.
+     * o : sets the other options. (see doc/libpq)
+     * d : enable debugging mode.
+     * q : run in quiet mode
+     * Q : run in VERY quiet mode (no output except on errors)
+     * c : monitor will run one POSTQUEL command and exit
+     *
+     * s : step mode (pauses after each command)
+     * S : don't use semi colon as \g
+     *
+     * T : terse mode - no formatting
+     * N : no attribute names - only columns of data
+     *     (these two options are useful in conjunction with the "-c" option
+     *      in scripts.)
+     */
+
+    progname = *argv;
+    Debugging = false;
+    Verbose = true;
+    Silent = false;
+
+    /* prepend PGOPTION, if any */
+    argsetup(&argc, &argv);
+
+    while ((c = getopt(argc, argv, "a:h:f:p:t:d:qsSTNQc:")) != EOF) {
+       switch (c) {
+           case 'a':
+             fe_setauthsvc(optarg, errbuf);
+             break;
+           case 'h' :
+             pghost = optarg;
+             break;
+           case 'f' :
+             RunOneFile = optarg;
+             break;
+           case 'p' :
+             pgport = optarg;
+             break;
+           case 't' :
+             pgtty = optarg;
+             break;
+           case 'T' :
+             TerseOutput = true;
+             break;
+           case 'N' :
+             PrintAttNames = false;
+             break;
+           case 'd' :
+
+             /*
+              *  When debugging is turned on, the debugging messages
+              *  will be sent to the specified debug file, which
+              *  can be a tty ..
+              */
+
+             Debugging = true;
+             debug_file = optarg;
+             debug_port = fopen(debug_file,"w+");
+             if (debug_port == NULL) {
+                 fprintf(stderr,"Unable to open debug file %s \n", debug_file);
+                 exit(1);
+             }
+             pgtracep = 1;
+             break;
+           case 'q' :
+             Verbose = false;
+             break;
+           case 's' :
+             SingleStepMode = true;
+             SemicolonIsGo = true;
+             break;
+           case 'S' :
+             SemicolonIsGo = false;
+             break;
+           case 'Q' :
+             Verbose = false;
+             Silent = true;
+             break;
+           case 'c' :
+             Verbose = false;
+             Silent = true;
+             RunOneCommand = true;
+             command = optarg;
+             break;
+           case '?' :
+           default :
+             errflag++;
+             break;
+       }
+    }
+
+    if (errflag ) {
+      fprintf(stderr, "usage: %s [options...] [dbname]\n", progname);
+      fprintf(stderr, "\t-a authsvc\tset authentication service\n");
+      fprintf(stderr, "\t-c command\t\texecute one command\n");
+      fprintf(stderr, "\t-d debugfile\t\tdebugging output file\n");
+      fprintf(stderr, "\t-h host\t\t\tserver host name\n");
+      fprintf(stderr, "\t-f file\t\t\trun query from file\n");
+      fprintf(stderr, "\t-p port\t\t\tserver port number\n");
+      fprintf(stderr, "\t-q\t\t\tquiet output\n");
+      fprintf(stderr, "\t-t logfile\t\terror-logging tty\n");
+      fprintf(stderr, "\t-N\t\t\toutput without attribute names\n");
+      fprintf(stderr, "\t-Q\t\t\tREALLY quiet output\n");
+      fprintf(stderr, "\t-T\t\t\tterse output\n");
+      exit(2);
+    }
+
+    /* Determine our username (according to the authentication system, if
+     * there is one).
+     */
+    if ((username = fe_getauthname(errbuf)) == (char *) NULL) {
+           fprintf(stderr, "%s: could not find a valid user name\n",
+                   progname);
+           exit(2);
+    }
+    memset(usernamebuf, 0, sizeof(usernamebuf));
+    (void) strncpy(usernamebuf, username, NAMEDATALEN);
+    username = usernamebuf;
+    
+    /* find database */
+    if (!(dbname = argv[optind]) &&
+       !(dbname = getenv("DATABASE")) &&
+       !(dbname = username)) {
+           fprintf(stderr, "%s: no database name specified\n", progname);
+           exit (2);
+    }
+
+    conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbname);
+    if (PQstatus(conn) == CONNECTION_BAD) {
+      fprintf(stderr,"Connection to database '%s' failed.\n", dbname);
+      fprintf(stderr,"%s",PQerrorMessage(conn));
+      exit(1);
+    }
+
+    if (pgtracep)
+      PQtrace(conn,debug_port);
+
+    /* print out welcome message and start up */
+    welcome();
+    init_tmon(); 
+
+    /* parse input */
+    if (RunOneCommand) {
+       exit_status = handle_execution(command);
+    } else if (RunOneFile) {
+       bool oldVerbose;
+       FILE *ifp;
+
+       if ((ifp = fopen(RunOneFile, "r")) == NULL) {
+           fprintf(stderr, "Cannot open %s\n", RunOneFile);
+       }
+       
+       if (SingleStepMode) {
+           oldVerbose = Verbose;
+           Verbose = false;
+       }
+       do_input(ifp);
+       fclose(ifp);
+       if (SingleStepMode)
+           Verbose = oldVerbose;
+    } else {
+       do_input(stdin);
+    }
+
+    handle_exit(exit_status);
+}
+
+static void
+do_input(FILE *ifp)
+{
+    int c;
+    char escape;
+
+    /*
+     *  Processing user input.
+     *  Basically we stuff the user input to a temp. file until
+     *  an escape char. is detected, after which we switch
+     *  to the appropriate routine to handle the escape.
+     */
+
+    if (ifp == stdin) {
+       if (Verbose)
+           fprintf(stdout,"\nGo \n* ");
+       else {
+           if (!Silent)
+               fprintf(stdout, "* ");
+       }
+    }
+    while ((c = getc(ifp)) != EOF ) {
+       if ( c == '\\') {
+           /* handle escapes */
+           escape = getc(ifp);
+           switch( escape ) {
+             case 'e':
+               handle_editor();
+               break;
+             case 'g':
+               handle_send();
+               break;
+             case 'i':
+               {
+                   bool oldVerbose;
+
+                   if (SingleStepMode) {
+                       oldVerbose = Verbose;
+                       Verbose = false;
+                   }
+                   handle_file_insert(ifp);
+                   if (SingleStepMode)
+                       Verbose = oldVerbose;
+               }
+               break;
+             case 'p':
+               handle_print();
+               break;
+             case 'q':
+               handle_exit(0);
+               break;
+             case 'r':
+               handle_clear();
+               break;
+             case 's':
+               handle_shell();
+               break;
+             case 't':
+               handle_print_time();
+               break;
+             case 'w':
+               handle_write_to_file();
+               break;
+             case '?':
+             case 'h':
+               handle_help();
+               break;
+             case '\\':
+               c = escape;
+               stuff_buffer(c); 
+               break;
+             case ';':
+               c = escape;
+               stuff_buffer(c);
+               break;
+             default:
+               fprintf(stderr, "unknown escape given\n");
+               break;
+           } /* end-of-switch */
+           if (ifp == stdin && escape != '\\') {
+               if (Verbose)
+                   fprintf(stdout,"\nGo \n* ");
+               else {
+                   if (!Silent)
+                       fprintf(stdout, "* ");
+               }
+           }
+       } else {
+           stuff_buffer(c);
+           if (c == ';' && SemicolonIsGo) {
+               handle_send();
+               if (Verbose)
+                   fprintf(stdout,"\nGo \n* ");
+               else {
+                   if (!Silent)
+                       fprintf(stdout, "* ");
+               }
+           }
+       }
+    }
+}
+
+/*
+ * init_tmon()
+ *
+ * set the following :
+ *     user_editor, defaults to DEFAULT_EDITOR if env var is not set
+ */
+static void
+init_tmon()
+{
+    if (!RunOneCommand)
+    {
+       char *temp_editor = getenv("EDITOR");
+    
+       if (temp_editor != NULL) 
+           user_editor = temp_editor;
+       else
+           user_editor = DEFAULT_EDITOR;
+
+       tmon_temp_filename = malloc(20);
+       sprintf(tmon_temp_filename, "/tmp/PQ%d", getpid());
+       tmon_temp = open(tmon_temp_filename,O_CREAT | O_RDWR | O_APPEND,0666);
+    }
+
+    /*
+     * Catch signals so we can delete the scratch file GK
+     * but only if we aren't already ignoring them -mer
+     */
+
+#ifndef WIN32
+    if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
+       signal(SIGHUP, handle_exit);
+    if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
+       signal(SIGQUIT, handle_exit);
+#endif /* WIN32 we'll have to figure out how to handle these */
+    if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
+       signal(SIGTERM, handle_exit);
+    if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+       signal(SIGINT, handle_exit);
+}
+
+/*
+ *  welcome simply prints the Postgres welcome mesg.
+ */
+static
+void welcome()
+{
+    if (Verbose) {
+       fprintf(stdout,"Welcome to the POSTGRES95 terminal monitor\n");
+       fprintf(stdout,"  Please read the file COPYRIGHT for copyright terms of POSTGRES95\n");
+    }
+}
+
+
+/*
+ *  handle_editor()
+ *
+ *  puts the user into edit mode using the editor specified
+ *  by the variable "user_editor".
+ */
+static void
+handle_editor()
+{
+    char edit_line[100];
+
+    close(tmon_temp);
+    sprintf(edit_line,"%s %s",user_editor,tmon_temp_filename);
+    system(edit_line);
+    tmon_temp = open(tmon_temp_filename,O_CREAT | O_RDWR | O_APPEND,0666);
+}
+
+static void
+handle_shell()
+{
+    char *user_shell;
+
+    user_shell = getenv("SHELL");
+    if (user_shell != NULL) {
+        system(user_shell);
+    } else {
+        system("/bin/sh");
+    }
+}
+
+/*
+ * handle_send()
+ *
+ * This is the routine that initialises the comm. with the
+ * backend.  After the tuples have been returned and 
+ * displayed, the query_buffer is cleared for the 
+ * next query.
+ *
+ */
+
+#include <ctype.h>
+
+static void
+handle_send()
+{
+    char c  = (char)0;
+    off_t pos;
+    int cc = 0;
+    int i = 0;
+
+    pos = lseek(tmon_temp, (off_t) 0, SEEK_SET);
+
+    if (pos != 0)
+       fprintf(stderr, "Bogus file position\n");
+
+    if (Verbose)
+       printf("\n");
+
+    /* discard leading white space */
+    while ( ( cc = read(tmon_temp,&c,1) ) != 0 && isspace((int)c))
+       continue;
+
+    if ( cc != 0 ) {
+       pos = lseek(tmon_temp, (off_t) -1, SEEK_CUR);
+    }
+
+    if (SingleStepMode) {
+       char buf[1024];
+       fprintf(stdout, "\n*******************************************************************************\n");
+       while ((cc = read(tmon_temp,buf,1024))>0) {
+           buf[cc] = '\0';
+           fprintf(stdout, "%s", buf);
+       }
+       fprintf(stdout, "\n*******************************************************************************\n\n");
+       (void)lseek(tmon_temp, (off_t)pos, SEEK_SET);
+    }
+
+    query_buffer[0] = 0;
+
+    /*
+     *  Stripping out comments (if any) from the query (should really be
+     *  handled in the parser, of course).
+     */
+    while ( ( cc = read(tmon_temp,&c,1) ) != 0) {
+       switch(c) {
+       case '\n':
+           query_buffer[i++] = ' ';
+           break;
+       case '-': {
+           int temp; 
+           char temp_c;
+           if ((temp = read(tmon_temp,&temp_c,1)) > 0) {
+               if (temp_c == '-' ) {
+                   /* read till end of line */
+                   while ((temp = read(tmon_temp,&temp_c,1)) != 0) {
+                       if (temp_c=='\n')
+                           break;
+                   }
+               }else {
+                   query_buffer[i++] = c;
+                   query_buffer[i++] = temp_c;
+               }
+           } else {
+               query_buffer[i++] = c;
+           }
+           break;
+       }
+       case '$': {
+           int temp;
+           char temp_c[4];
+           /*
+            * monitor feature, not POSTGRES SQL. When monitor sees $PWD,
+            * it will substitute in the current directory.
+            */
+           if ((temp = read(tmon_temp,temp_c,3)) > 0) {
+               temp_c[temp] = '\0';
+               if (!strncmp(temp_c, "PWD", 3)) {
+                   int len;
+                   char cwdPath[MAXPATHLEN];
+                   if (getcwd(cwdPath, MAXPATHLEN)==NULL) {
+                       fprintf(stderr,
+                               "cannot get current working directory\n");
+                       break;
+                   }
+                   len = strlen(cwdPath);
+                   query_buffer[i] = '\0';
+                   strcat(query_buffer, cwdPath);
+                   i += len;
+               } else {
+                   int j;
+                   query_buffer[i++] = c;
+                   for(j = 0; j < temp; j++) {
+                       query_buffer[i++] = temp_c[j];
+                   }
+               }
+           } else {
+               query_buffer[i++] = c;
+           }
+           break;
+       }
+       default:
+           query_buffer[i++] = c;
+           break;
+       }
+    }
+
+    if (query_buffer[0] == 0) {
+        query_buffer[0] = ' ';
+        query_buffer[1] = 0;
+    }
+
+    if (Verbose && !SingleStepMode)
+       fprintf(stdout,"Query sent to backend is \"%s\"\n", query_buffer);
+
+    fflush(stderr);
+    fflush(stdout);
+    
+    /*
+     * Repeat commands until done.
+     */
+
+       handle_execution(query_buffer);
+
+    /* clear the query buffer and temp file -- this is very expensive */
+    handle_clear();
+    memset(query_buffer,0,i);
+}
+
+/*
+ * Actually execute the query in *query.
+ *
+ * Returns 0 if the query finished successfully, 1 otherwise.
+ */
+static int
+handle_execution(char *query) 
+{
+    PGresult *result;
+    int retval = 0;
+    
+    result = PQexec(conn, query);
+
+    if (result == NULL) {
+      fprintf(stderr,"%s", PQerrorMessage(conn));
+      return 1;
+    }
+
+    switch (PQresultStatus(result)) {
+    case PGRES_EMPTY_QUERY:
+       break;
+    case PGRES_COMMAND_OK:
+       break;
+    case PGRES_TUPLES_OK:
+/*     PQprintTuples(result,stdout,PrintAttNames,TerseOutput,COLWIDTH); */
+       if (TerseOutput)
+           PQdisplayTuples(result,stdout,1,"",PrintAttNames,TerseOutput);
+       else
+           PQdisplayTuples(result,stdout,1,"|",PrintAttNames,TerseOutput);
+       break;
+    case PGRES_COPY_OUT:
+       handle_copy_out(result);
+       break;
+    case PGRES_COPY_IN:
+       handle_copy_in(result);
+       break;
+    case PGRES_BAD_RESPONSE:
+       retval = 1;
+       break;
+    case PGRES_NONFATAL_ERROR:
+       retval = 1;
+       break;
+    case PGRES_FATAL_ERROR:
+       retval = 1;
+       break;
+    }
+
+    if (SingleStepMode) {
+       fflush(stdin);
+       printf("\npress return to continue ...\n");
+       getc(stdin);    /* assume stdin is not a file! */
+    }
+    return(retval);
+}
+
+/*
+ * handle_file_insert()
+ *
+ * allows the user to insert a query file and execute it.
+ * NOTE: right now the full path name must be specified.
+ */
+static void
+handle_file_insert(FILE *ifp)
+{
+    char user_filename[50];
+    FILE *nifp;
+
+    fscanf(ifp, "%s",user_filename);
+    nifp = fopen(user_filename, "r");
+    if (nifp == (FILE *) NULL) {
+        fprintf(stderr, "Cannot open %s\n", user_filename);
+    } else {
+        do_input(nifp);
+        fclose (nifp);
+    }
+}
+
+/*
+ * handle_print()
+ *
+ * This routine prints out the contents (query) of the temp. file
+ * onto stdout.
+ */
+static void
+handle_print()
+{
+    char c;
+    off_t pos;
+    int cc;
+    
+    pos = lseek(tmon_temp, (off_t) 0, SEEK_SET);
+    
+    if (pos != 0 )
+       fprintf(stderr, "Bogus file position\n");
+    
+    printf("\n");
+    
+    while ( ( cc = read(tmon_temp,&c,1) ) != 0) 
+       putchar(c);
+    
+    printf("\n");
+}
+
+
+/*
+ * handle_exit()
+ *
+ * ends the comm. with the backend and exit the tm.
+ */
+static void
+handle_exit(int exit_status)
+{
+    if (!RunOneCommand) {
+       close(tmon_temp);
+       unlink(tmon_temp_filename);
+    }
+    PQfinish(conn);   
+    exit(exit_status);
+}
+
+/*
+ * handle_clear()
+ *
+ *  This routine clears the temp. file.
+ */
+static void
+handle_clear()
+{
+    /* high cost */
+    close(tmon_temp);
+    tmon_temp = open(tmon_temp_filename,O_TRUNC|O_RDWR|O_CREAT ,0666);
+}
+
+/*
+ * handle_print_time()
+ * prints out the date using the "date" command.
+ */
+static void
+handle_print_time()
+{
+    system("date");
+}
+
+/*
+ * handle_write_to_file()
+ *
+ * writes the contents of the temp. file to the
+ * specified file.
+ */
+static int
+handle_write_to_file()
+{
+    char filename[50];
+    static char command_line[512];
+    int status;
+    
+    status = scanf("%s", filename);
+    if (status < 1 || !filename[0]) {
+       fprintf(stderr, "error: filename is empty\n");
+       return(-1);
+    }
+    
+    /* XXX portable way to check return status?  $%&! ultrix ... */
+    (void) sprintf(command_line, "rm -f %s", filename);
+    (void) system(command_line);
+    (void) sprintf(command_line, "cp %s %s", tmon_temp_filename, filename);
+    (void) system(command_line);
+
+    return(0);
+}
+
+/*
+ *
+ * Prints out a help message.
+ *
+ */
+static void
+handle_help()
+{
+    printf("Available commands include \n\n");
+    printf("\\e -- enter editor\n");
+    printf("\\g -- \"GO\": submit query to POSTGRES\n");
+    printf("\\i -- include (switch input to external file)\n");
+    printf("\\p -- print query buffer\n");
+    printf("\\q -- quit POSTGRES\n");
+    printf("\\r -- force reset (clear) of query buffer\n");
+    printf("\\s -- shell escape \n");
+    printf("\\t -- print current time\n");
+    printf("\\w -- write query buffer to external file\n");
+    printf("\\h -- print the list of commands\n");
+    printf("\\? -- print the list of commands\n");
+    printf("\\\\ -- produce a single backslash in query buffer\n");
+    fflush(stdin);
+}
+
+/*
+ * stuff_buffer()
+ *
+ * writes the user input into the temp. file.
+ */
+static void
+stuff_buffer(char c)
+{
+    int cc;
+
+    cc = write(tmon_temp,&c,1);
+
+    if(cc == -1)
+       fprintf(stderr, "error writing to temp file\n");
+}
+
+static void
+argsetup(int *argcP, char ***argvP)
+{
+    int argc;
+    char **argv, **curarg;
+    char *eopts;
+    char *envopts;
+    int neopts;
+    char *start, *end;
+    arge *head, *tail, *cur;
+
+    /* if no options specified in environment, we're done */
+    if ((envopts = getenv("PGOPTION")) == (char *) NULL)
+       return;
+
+    if ((eopts = (char *) malloc(strlen(envopts) + 1)) == (char *) NULL) {
+       fprintf(stderr, "cannot malloc copy space for PGOPTION\n");
+       fflush(stderr);
+       exit (2);
+    }
+
+    (void) strcpy(eopts, envopts);
+
+    /*
+     *  okay, we have PGOPTION from the environment, and we want to treat
+     *  them as user-specified options.  to do this, we construct a new
+     *  argv that has argv[0] followed by the arguments from the environment
+     *  followed by the arguments on the command line.
+     */
+
+    head = cur = (arge *) NULL;
+    neopts = 0;
+
+    for (;;) {
+       while (isspace(*eopts) && *eopts)
+           eopts++;
+
+       if (*eopts == '\0')
+           break;
+
+       if ((cur = (arge *) malloc(sizeof(arge))) == (arge *) NULL) {
+           fprintf(stderr, "cannot malloc space for arge\n");
+           fflush(stderr);
+           exit (2);
+       }
+
+       end = start = eopts;
+
+       if (*start == '"') {
+           start++;
+           while (*++end != '\0' && *end != '"')
+               continue;
+           if (*end == '\0') {
+               fprintf(stderr, "unterminated string constant in env var PGOPTION\n");
+               fflush(stderr);
+               exit (2);
+           }
+           eopts = end + 1;
+       } else if (*start == '\'') {
+           start++;
+           while (*++end != '\0' && *end != '\'')
+               continue;
+           if (*end == '\0') {
+               fprintf(stderr, "unterminated string constant in env var PGOPTION\n");
+               fflush(stderr);
+               exit (2);
+           }
+           eopts = end + 1;
+       } else {
+           while (!isspace(*end) && *end)
+               end++;
+           if (isspace(*end))
+               eopts = end + 1;
+           else
+               eopts = end;
+       }
+
+       if (head == (arge *) NULL) {
+           head = tail = cur;
+       } else {
+           tail->a_next = cur;
+           tail = cur;
+       }
+
+       cur->a_arg = start;
+       cur->a_next = (arge *) NULL;
+
+       *end = '\0';
+       neopts++;
+    }
+
+    argc = *argcP + neopts;
+
+    if ((argv = (char **) malloc(argc * sizeof(char *))) == (char **) NULL) {
+       fprintf(stderr, "can't malloc space for modified argv\n");
+       fflush(stderr);
+       exit (2);
+    }
+
+    curarg = argv;
+    *curarg++ = *(*argvP)++;
+
+    /* copy env args */
+    while (head != (arge *) NULL) {
+       cur = head;
+       *curarg++ = head->a_arg;
+       head = head->a_next;
+       free(cur);
+    }
+
+    /* copy rest of args from command line */
+    while (--(*argcP))
+       *curarg++ = *(*argvP)++;
+
+    /* all done */
+    *argvP = argv;
+    *argcP = argc;
+}
+
+static void
+handle_copy_out(PGresult *res)
+{
+    bool copydone = false;
+    char copybuf[COPYBUFSIZ];
+    int ret;
+
+    if (!Silent)
+       fprintf(stdout, "Copy command returns...\n");
+    
+    while (!copydone) {
+       ret = PQgetline(res->conn, copybuf, COPYBUFSIZ);
+       
+       if (copybuf[0] == '.' && copybuf[1] =='\0') {
+           copydone = true;    /* don't print this... */
+       } else {
+           fputs(copybuf, stdout);
+           switch (ret) {
+           case EOF:
+               copydone = true;
+               /*FALLTHROUGH*/
+           case 0:
+               fputc('\n', stdout);
+               break;
+           case 1:
+               break;
+           }
+       }
+    }
+    fflush(stdout);
+    PQendcopy(res->conn);
+}
+
+static void
+handle_copy_in(PGresult *res)
+{
+    bool copydone = false;
+    bool firstload;
+    bool linedone;
+    char copybuf[COPYBUFSIZ];
+    char *s;
+    int buflen;
+    int c;
+    
+    if (!Silent) {
+       fputs("Enter info followed by a newline\n", stdout);
+       fputs("End with a dot on a line by itself.\n", stdout);
+    }
+    
+    /*
+     * eat inevitable newline still in input buffer
+     *
+     * XXX the 'inevitable newline' is not always present
+     *     for example `cat file | monitor -c "copy from stdin"'
+     */
+    fflush(stdin);
+    if ((c = getc(stdin)) != '\n' && c != EOF) {
+       (void) ungetc(c, stdin);
+    }
+    
+    while (!copydone) {                        /* for each input line ... */
+       if (!Silent) {
+           fputs(">> ", stdout);
+           fflush(stdout);
+       }
+       firstload = true;
+       linedone = false;
+       while (!linedone) {             /* for each buffer ... */
+           s = copybuf;
+           buflen = COPYBUFSIZ;
+           for (; buflen > 1 &&
+                !(linedone = (c = getc(stdin)) == '\n' || c == EOF);
+                --buflen) {
+               *s++ = c;
+           }
+           if (c == EOF) {
+               /* reading from stdin, but from a file */
+               PQputline(res->conn, ".");
+               copydone = true;
+               break;
+           }
+           *s = '\0';
+           PQputline(res->conn, copybuf);
+           if (firstload) {
+               if (!strcmp(copybuf, ".")) {
+                   copydone = true;
+               }
+               firstload = false;
+           }
+       }
+       PQputline(res->conn, "\n");
+    }
+    PQendcopy(res->conn);
+}
diff --git a/src/bin/pg4_dump/Makefile b/src/bin/pg4_dump/Makefile
new file mode 100644 (file)
index 0000000..286f96e
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# /usr/local/devel/pglite/cvs/src/bin/pg_dump/Makefile.v4r2,v 1.1 1995/05/17 18:57:10 jolly Exp
+#
+
+.include <postgres.global.mk>
+
+CFLAGS+= -I${.CURDIR}/../../backend/tmp
+
+PROG= pg4_dump
+
+SRCS= pg4_dump.c common.c
+
+.include <postgres.prog.mk>
diff --git a/src/bin/pg4_dump/README b/src/bin/pg4_dump/README
new file mode 100644 (file)
index 0000000..4ac5451
--- /dev/null
@@ -0,0 +1,87 @@
+pg4_dump is a utility for dumping out a postgres (version 4, release 2)
+database into a script file containing query commands.  The script
+files are in a ASCII format and can be used to reconstruct the
+database, even on other machines and other architectures.  pg_dump
+will produce the queries necessary to re-generate all user-defined
+types, functions, tables, indices, aggregates, and operators.  In
+addition, all the data is copied out in ASCII format so that it can be
+readily copied in again.
+
+The sources in this directory can be used to build two different
+versions of the program.  The two versions require different
+versions of libpq, and the same binary cannot serve both purposes.
+
+
+       To build:
+
+          % bmake clean install
+
+       This version of the program will read in your postgres v4r2
+database and output the schema and the data tuples in one of two
+formats:  POSTQUEL or SQL.  The POSTQUEL->POSTQUEL dumps are useful
+for moving from one v4r2 installation to another.  The POSTQUEL->SQL
+dumps are useful for migrating from v4r2 to postgres95.  
+
+Use the -o [SQL|POSTQUEL] option to specify output query language.
+
+
+How to use pg4_dump:
+-------------------
+
+The command line options are fairly self explanatory.  Use -help to 
+see the command line options.  I recommend using -v to get more
+verbose descriptions of what pg_dump is doing.  
+
+After running pg4_dump, one should examine the output script file for any 
+warnings, especially in light of the limitations listed below.
+
+A typical use of pg4_dump:
+
+       %  pg4_dump -v -f oldDB.dump  oldDB
+       %  createdb newDB
+       %  monitor newDB < oldDB.dump
+
+
+Caveats and limitations:
+------------------------
+
+pg4_dump has a few limitations.  The limitations mostly stem from
+difficulty in extracting certain meta-information from the system
+catalogs.   
+
+   rules and views:  
+       pg4_dump does not understand user-defined rules and views and
+       will fail to dump them properly.  (This is due to the fact that
+       rules are stored as plans in the catalogs and not textually)
+       
+   partial indices:
+       pg4_dump does not understand partial indices. (The reason is
+       the same as above.  Partial index predicates are stored as plans)
+       
+   source text of POSTQUEL functions:
+       pg4_dump does not convert the source text of a user-defined
+       POSTQUEL function into SQL.  Manual intervention is required.
+
+   large objects:
+       pg4_dump does not handle large objects.  Inversion large
+       objects are ignored and must be dealt with manually.
+
+   oid preservation:
+       pg4_dump does not preserve oid's while dumping.  If you have
+       stored oid's explicitly in tables in user-defined attributes,
+       and are using them as keys, then the output scripts will not
+       regenerate your database correctly. 
+
+pg4_dump has not been tested and will probably not work properly for
+versions of postgres prior to 4.2.
+
+Bug-reporting
+--------------
+
+If you should find a problem with pg4_dump, it is very important that
+you provide a (small) sample database which illustrates the problem.
+Please send bugs, questions, and feedback to the
+       [email protected]
+
+
+
diff --git a/src/bin/pg4_dump/common.c b/src/bin/pg4_dump/common.c
new file mode 100644 (file)
index 0000000..f485c15
--- /dev/null
@@ -0,0 +1,417 @@
+/*-------------------------------------------------------------------------
+ *
+ * common.c--
+ *    common routines between pg_dump and pg4_dump
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    /usr/local/devel/pglite/cvs/src/bin/pg_dump/common.c,v 1.5 1995/06/28 22:32:35 jolly Exp
+ *
+ *-------------------------------------------------------------------------
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/param.h> /* for MAXHOSTNAMELEN on most */
+#ifdef PORTNAME_sparc_solaris
+#include <netdb.h>     /* for MAXHOSTNAMELEN on some */
+#endif
+
+#include "postgres.h"
+#include "libpq-fe.h"
+#include "libpq/auth.h"
+
+#include "pg_dump.h"
+
+/*
+ *  check_conn_and_db checks the connection and the database
+ */
+void 
+check_conn_and_db()
+{
+    char *string= PQexec(" ");
+    switch(*string) {
+    case 'E':
+    case 'R':
+       PQfinish();
+       exit(2);
+       break;
+    }
+}
+
+
+/* dupstr : copies a string, while allocating space for it. 
+   the CALLER is responsible for freeing the space
+   returns NULL if the argument is NULL*/
+char* 
+dupstr(char *s)
+{
+  char* result;
+
+  if (s == NULL)
+    return NULL;
+
+  result = (char*)malloc(strlen(s)+1);
+  strcpy(result, s);
+  return result;
+}
+
+
+/*
+ * findTypeByOid 
+ *    given an oid of a type, return its typename
+ *
+ * if oid is "0", return "opaque" -- this is a special case
+ *
+ * NOTE:  should hash this, but just do linear search for now
+ */
+
+char*
+findTypeByOid(TypeInfo* tinfo, int numTypes, char* oid)
+{
+    int i;
+
+    if (strcmp(oid, "0") == 0) return g_opaque_type;
+
+    for (i=0;i<numTypes;i++) {
+       if (strcmp(tinfo[i].oid, oid) == 0)
+           return tinfo[i].typname;
+    }
+
+    /* should never get here */
+    fprintf(stderr,"failed sanity check,  type with oid %s was not found\n",
+           oid);
+    exit(2);
+}
+
+/*
+ * findOprByOid
+ *    given the oid of an operator, return the name of the operator
+ *
+ *
+ * NOTE:  should hash this, but just do linear search for now
+ * 
+ */
+char*
+findOprByOid(OprInfo *oprinfo, int numOprs, char *oid)
+{
+    int i;
+    for (i=0;i<numOprs;i++) {
+       if (strcmp(oprinfo[i].oid, oid) == 0)
+           return oprinfo[i].oprname;
+    }
+
+    /* should never get here */
+    fprintf(stderr,"failed sanity check,  opr with oid %s was not found\n",
+           oid);
+    exit(2);
+}
+
+
+/*
+ * findParentsByOid --
+ *    given the oid of a class, return the names of its parent classes
+ * and assign the number of parents to the last argument.
+ *
+ *
+ * returns NULL if none
+ */
+
+char** 
+findParentsByOid(TableInfo* tblinfo, int numTables,
+                InhInfo* inhinfo, int numInherits, char *oid,
+                int *numParentsPtr)
+{
+    int i,j;
+    int parentInd;
+    char** result;
+    int numParents;
+
+    numParents = 0;
+    for (i=0;i<numInherits;i++) {
+       if ( strcmp(inhinfo[i].inhrel, oid) == 0) {
+           numParents++;
+       }
+    }
+
+    *numParentsPtr = numParents;
+
+    if (numParents > 0) {
+       result = (char**)malloc(sizeof(char*) * numParents);
+       j = 0;
+       for (i=0;i<numInherits;i++) {
+           if ( strcmp(inhinfo[i].inhrel, oid) == 0) {
+               parentInd = findTableByOid(tblinfo, numTables, 
+                                          inhinfo[i].inhparent);
+               result[j++] = tblinfo[parentInd].relname;
+           }
+       }
+       return result;
+    }
+    else 
+       return NULL;
+}
+
+/*
+ * parseArgTypes
+ *    parse a string of eight numbers delimited by spaces
+ * into a character array
+ */
+
+void 
+parseArgTypes(char **argtypes, char* str)
+{
+    int i, j, argNum;
+    char temp[100];
+    char s;
+
+    argNum = 0;
+    j = 0;
+    while ( (s = *str) != '\0') {
+       if (s == ' ') {
+           temp[j] = '\0';
+           argtypes[argNum] = dupstr(temp);
+           argNum++;
+           j = 0;
+       } else {
+           temp[j] = s;
+           j++;
+       }
+       str++;
+    }
+    if (j != 0)  {
+       temp[j] = '\0';
+        argtypes[argNum] = dupstr(temp);
+    }
+    
+}
+
+
+/*
+ * strInArray:
+ *    takes in a string and a string array and the number of elements in the 
+ * string array.  
+ *    returns the index if the string is somewhere in the array, -1 otherwise
+ *
+ */
+
+int 
+strInArray(char* pattern, char** arr, int arr_size)
+{
+    int i;
+    for (i=0;i<arr_size;i++) {
+       if (strcmp(pattern, arr[i]) == 0) 
+           return i;
+    }
+    return -1;
+}
+
+/*
+ * dumpSchema:
+ *    we have a valid connection, we are now going to dump the schema
+ * into the file
+ *
+ */
+
+TableInfo *
+dumpSchema(FILE* fout, int *numTablesPtr)
+{
+    int numTypes;
+    int numFuncs;
+    int numTables;
+    int numInherits;
+    int numIndices;
+    int numAggregates;
+    int numOperators;
+    TypeInfo *tinfo;
+    FuncInfo *finfo;
+    AggInfo *agginfo;
+    TableInfo *tblinfo;
+    InhInfo *inhinfo;
+    IndInfo *indinfo;
+    OprInfo *oprinfo;
+
+if (g_verbose) fprintf(stderr,"%s reading user-defined types %s\n",
+                      g_comment_start, g_comment_end);
+    tinfo = getTypes(&numTypes);
+
+if (g_verbose) fprintf(stderr,"%s reading user-defined functions %s\n",
+                      g_comment_start, g_comment_end);
+    finfo = getFuncs(&numFuncs);
+
+if (g_verbose) fprintf(stderr,"%s reading user-defined aggregates %s\n",
+                      g_comment_start, g_comment_end);
+    agginfo = getAggregates(&numAggregates);
+
+if (g_verbose) fprintf(stderr,"%s reading user-defined operators %s\n",
+                      g_comment_start, g_comment_end);
+    oprinfo = getOperators(&numOperators);
+
+if (g_verbose) fprintf(stderr,"%s reading user-defined tables %s\n",
+                      g_comment_start, g_comment_end);
+    tblinfo = getTables(&numTables);
+
+if (g_verbose) fprintf(stderr,"%s reading table inheritance information %s\n",
+                      g_comment_start, g_comment_end);
+    inhinfo = getInherits(&numInherits);
+
+if (g_verbose) fprintf(stderr, "%s finding the attribute names and types for each table %s\n",
+                      g_comment_start, g_comment_end);
+    getTableAttrs(tblinfo, numTables);
+
+if (g_verbose) fprintf(stderr, "%s flagging inherited attributes in subtables %s\n",
+                      g_comment_start, g_comment_end);
+    flagInhAttrs(tblinfo, numTables, inhinfo, numInherits);
+
+if (g_verbose) fprintf(stderr,"%s reading indices information %s\n",
+                      g_comment_start, g_comment_end);
+    indinfo = getIndices(&numIndices);
+
+if (g_verbose) fprintf(stderr,"%s dumping out user-defined types %s\n",
+                      g_comment_start, g_comment_end);
+    dumpTypes(fout, finfo, numFuncs, tinfo, numTypes);
+
+if (g_verbose) fprintf(stderr,"%s dumping out tables %s\n",
+                      g_comment_start, g_comment_end);
+    dumpTables(fout, tblinfo, numTables, inhinfo, numInherits,
+              tinfo, numTypes);
+
+if (g_verbose) fprintf(stderr,"%s dumping out user-defined functions %s\n",
+                      g_comment_start, g_comment_end);
+    dumpFuncs(fout, finfo, numFuncs, tinfo, numTypes);
+
+if (g_verbose) fprintf(stderr,"%s dumping out user-defined functions %s\n",
+                      g_comment_start, g_comment_end);
+    dumpAggs(fout, agginfo, numAggregates, tinfo, numTypes);
+
+if (g_verbose) fprintf(stderr,"%s dumping out user-defined operators %s\n",
+                      g_comment_start, g_comment_end);
+    dumpOprs(fout, oprinfo, numOperators, tinfo, numTypes);
+
+if (g_verbose) fprintf(stderr,"%s dumping out indices %s\n",
+                      g_comment_start, g_comment_end);
+    dumpIndices(fout, indinfo, numIndices, tblinfo, numTables);
+
+    *numTablesPtr = numTables;
+    return tblinfo;
+}
+
+
+/* flagInhAttrs -
+ *   for each table in tblinfo, flag its inherited attributes
+ * so when we dump the table out, we don't dump out the inherited attributes
+ *   
+ * initializes the parentRels field of each table
+ *
+ * modifies tblinfo
+ *
+ */
+void
+flagInhAttrs(TableInfo* tblinfo, int numTables,
+            InhInfo* inhinfo, int numInherits)
+{
+    int i,j,k;
+    int parentInd;
+    char *parentRels;
+    int numParents;
+
+    /* we go backwards because the tables in tblinfo are in OID
+       order, meaning the subtables are after the parent tables
+       we flag inherited attributes from child tables first */
+    for (i = numTables-1; i >= 0; i--) {
+       tblinfo[i].parentRels = findParentsByOid(tblinfo, numTables,
+                                               inhinfo, numInherits,
+                                               tblinfo[i].oid,
+                                               &tblinfo[i].numParents);
+       for (k=0;k<tblinfo[i].numParents;k++) {
+           parentInd = findTableByName(tblinfo, numTables, 
+                                       tblinfo[i].parentRels[k]);
+           for (j=0;j<tblinfo[i].numatts;j++) {
+               if (strInArray(tblinfo[i].attnames[j],
+                              tblinfo[parentInd].attnames,
+                              tblinfo[parentInd].numatts) != -1) {
+                   tblinfo[i].inhAttrs[j] = 1;
+               }
+           }
+       }
+    }
+}
+
+
+/*
+ * findTableByName
+ *    finds the index (in tblinfo) of the table with the given relname
+ *  returns -1 if not found
+ *
+ * NOTE:  should hash this, but just do linear search for now
+ */
+
+int
+findTableByName(TableInfo* tblinfo, int numTables, char* relname)
+{
+    int i;
+    for (i=0;i<numTables;i++) {
+       if  (strcmp(tblinfo[i].relname, relname) == 0)
+           return i;
+    }
+    return -1;
+}
+
+/*
+ * findTableByOid
+ *    finds the index (in tblinfo) of the table with the given oid
+ *  returns -1 if not found
+ *
+ * NOTE:  should hash this, but just do linear search for now
+ */
+
+int
+findTableByOid(TableInfo* tblinfo, int numTables, char* oid)
+{
+    int i;
+    for (i=0;i<numTables;i++) {
+       if  (strcmp(tblinfo[i].oid, oid) == 0)
+           return i;
+    }
+    return -1;
+}
+
+
+/*
+ * findFuncByName
+ *    finds the index (in finfo) of the function with the given name
+ *  returns -1 if not found
+ *
+ * NOTE:  should hash this, but just do linear search for now
+ */
+
+int
+findFuncByName(FuncInfo* finfo, int numFuncs, char* name)
+{
+    int i;
+    for (i=0;i<numFuncs;i++) {
+       if  (strcmp(finfo[i].proname, name) == 0)
+           return i;
+    }
+    return -1;
+}
+
+/*
+ * isArchiveName
+ *
+ *   returns true if the relation name is an archive name, false otherwise
+ */ 
+int
+isArchiveName(char* relname)
+{
+    return (strlen(relname) > 1 && relname[1] == ',');
+}
+
+
+
+
+
+
diff --git a/src/bin/pg4_dump/pg4_dump.c b/src/bin/pg4_dump/pg4_dump.c
new file mode 100644 (file)
index 0000000..6e6ee6f
--- /dev/null
@@ -0,0 +1,1602 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg4_dump.c--
+ *    pg4_dump is an utility for dumping out a postgres database
+ * into a script file.
+ *
+ *  pg4_dump will read the system catalogs from a postgresV4r2 database and 
+ *  dump out a script that reproduces the schema of the database in terms of
+ *        user-defined types
+ *        user-defined functions
+ *        tables
+ *        indices
+ *        aggregates
+ *        operators
+ *
+ * the output script is either POSTQUEL or SQL 
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    /usr/local/devel/pglite/cvs/src/bin/pg_dump/pg4_dump.c,v 1.1 1995/05/18 19:23:53 jolly Exp
+ *
+ *-------------------------------------------------------------------------
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/param.h> /* for MAXHOSTNAMELEN on most */
+#ifdef PORTNAME_sparc_solaris
+#include <netdb.h>     /* for MAXHOSTNAMELEN on some */
+#endif
+
+#include "tmp/postgres.h"
+#include "tmp/libpq-fe.h"
+#include "libpq/auth.h"
+
+#include "pg_dump.h"
+
+extern char *optarg;
+extern int optind, opterr;
+
+/* these are used in libpq  */
+extern char    *PQhost;     /* machine on which the backend is running */
+extern char    *PQport;     /* comm. port with the postgres backend. */
+extern char    *PQtty;      /* the tty where postgres msgs are displayed */
+extern char    *PQdatabase; /* the postgres db to access.  */
+
+/* global decls */
+int g_verbose;  /* verbose flag */
+int g_last_builtin_oid; /* value of the last builtin oid */
+FILE *g_fout;     /* the script file */
+
+char g_opaque_type[10]; /* name for the opaque type */
+
+/* placeholders for the delimiters for comments */
+char g_comment_start[10]; 
+char g_comment_end[10]; 
+
+int g_outputSQL; /* if 1, output SQL, otherwise , output Postquel */
+
+static
+usage(char* progname)
+{
+    fprintf(stderr, "usage:  %s [options] [dbname]\n",progname);
+    fprintf(stderr, "\t -f filename \t\t script output filename\n");
+    fprintf(stderr, "\t -H hostname \t\t server host name\n");
+    fprintf(stderr, "\t -o [SQL|POSTQUEL} \t\t output format\n");
+    fprintf(stderr, "\t -p port     \t\t server port number\n");
+    fprintf(stderr, "\t -v          \t\t verbose\n");
+    fprintf(stderr, "\t -S          \t\t dump out only the schema, no data\n");
+    fprintf(stderr, "\n if dbname is not supplied, then the DATABASE environment name is used\n");
+    fprintf(stderr, "\n");
+
+    fprintf(stderr, "\tpg4_dump dumps out postgres databases and produces a script file\n");
+    fprintf(stderr, "\tof query commands to regenerate the schema\n");
+    fprintf(stderr, "\tThe output format is either POSTQUEL or SQL.  The default is SQL\n");
+    exit(1);
+}
+
+void
+main(int argc, char** argv)
+{
+    int c;
+    char* progname;
+    char* filename;
+    char* dbname;
+    char *username, usernamebuf[NAMEDATALEN + 1];
+    char hostbuf[MAXHOSTNAMELEN];
+    int schemaOnly;
+
+    TableInfo *tblinfo;
+    int numTables;
+
+
+    dbname = NULL;
+    filename = NULL;
+    g_verbose = 0;
+    g_outputSQL = 1;
+    schemaOnly = 0;
+
+    progname = *argv;
+
+    while ((c = getopt(argc, argv,"f:H:o:p:vSD")) != EOF) {
+       switch(c) {
+       case 'f': /* output file name */
+           filename = optarg;
+           break;
+       case 'H' : /* server host */
+           PQhost = optarg;
+           break;
+       case 'o': 
+           {
+           char *lang = optarg;
+           if (lang) {
+               if (strcmp(lang,"SQL") != 0)
+                   g_outputSQL = 0;
+           }
+           }
+           break;
+       case 'p' : /* server port */
+           PQport = optarg;
+           break;
+       case 'v': /* verbose */
+           g_verbose = 1;
+           break;
+       case 'S': /* dump schema only */
+           schemaOnly = 1;
+           break;
+       default:
+           usage(progname);
+           break;
+       }
+    }
+
+    /* open the output file */
+    if (filename == NULL) {
+       g_fout = stdout;
+    } else {
+       g_fout = fopen(filename, "w");
+       if (g_fout == NULL) {
+           fprintf(stderr,"%s: could not open output file named %s for writing\n",
+                   progname, filename);
+           exit(2);
+       }
+    }
+
+    /* Determine our username (according to the authentication system, if
+     * there is one).
+     */
+    if ((username = fe_getauthname()) == (char *) NULL) {
+           fprintf(stderr, "%s: could not find a valid user name\n",progname);
+           exit(2);
+    }
+    memset(usernamebuf, 0, sizeof(usernamebuf));
+    (void) strncpy(usernamebuf, username, NAMEDATALEN);
+    username = usernamebuf;
+
+    /*
+     *  Determine the hostname of the database server.  Try to avoid using
+     * "localhost" if at all possible.
+     */
+    if (!PQhost && !(PQhost = getenv("PGHOST")))
+           PQhost = "localhost";
+    if (!strcmp(PQhost, "localhost")) {
+           if (gethostname(hostbuf, MAXHOSTNAMELEN) != -1)
+                   PQhost = hostbuf;
+    }
+
+
+    /* find database */
+    if (!(dbname = argv[optind]) &&
+       !(dbname = getenv("DATABASE")) &&
+       !(dbname = username)) {
+           fprintf(stderr, "%s: no database name specified\n",progname);
+           exit (2);
+    }
+
+    PQsetdb(dbname);
+
+    /* make sure things are ok before giving users a warm welcome! */
+    check_conn_and_db();
+
+    if (g_outputSQL) {
+       strcpy(g_comment_start,"-- ");
+       g_comment_end[0] = '\0';
+       strcpy(g_opaque_type, "opaque");
+    } else {
+       strcpy(g_comment_start,"/* ");
+       strcpy(g_comment_end,"*/ ");
+       strcpy(g_opaque_type, "any");
+    }
+
+    g_last_builtin_oid = findLastBuiltinOid();
+       
+
+if (g_verbose) 
+    fprintf(stderr, "%s last builtin oid is %d %s\n", 
+           g_comment_start, g_last_builtin_oid, g_comment_end);
+
+    tblinfo = dumpSchema(g_fout, &numTables);
+    
+    if (!schemaOnly) {
+
+if (g_verbose) {
+    fprintf(stderr, "%s dumping out the contents of each table %s\n",
+           g_comment_start, g_comment_end );
+    fprintf(stderr, "%s the output language is %s %s\n",
+           g_comment_start,
+           (g_outputSQL) ? "SQL" : "POSTQUEL",
+           g_comment_end);
+}
+
+      dumpClasses(tblinfo, numTables, g_fout); 
+    }     
+
+    fflush(g_fout);
+    fclose(g_fout);
+    exit(0);
+}
+
+/*
+ * getTypes: 
+ *    read all base types in the system catalogs and return them in the 
+ * TypeInfo* structure
+ *
+ *  numTypes is set to the number of types read in 
+ *
+ */
+TypeInfo*
+getTypes(int *numTypes)
+{
+    char* res;
+    PortalBuffer* pbuf;
+    int ntups;
+    int i;
+    char query[MAXQUERYLEN];
+    TypeInfo *tinfo;
+
+    int i_oid;
+    int i_typowner;
+    int i_typname;
+    int i_typlen;
+    int i_typprtlen;
+    int i_typinput;
+    int i_typoutput;
+    int i_typreceive;
+    int i_typsend;
+    int i_typelem;
+    int i_typdelim;
+    int i_typdefault;
+    int i_typrelid;
+    int i_typbyval;
+
+    PQexec("begin");
+    
+   /* find all base types */
+   /* we include even the built-in types 
+      because those may be used as array elements by user-defined types */
+   /* we filter out the built-in types when 
+      we dump out the types */
+
+/*
+    sprintf(query, "SELECT oid, typowner,typname, typlen, typprtlen, typinput, typoutput, typreceive, typsend, typelem, typdelim, typdefault, typrelid,typbyval from pg_type");
+*/
+    sprintf(query, "retrieve (t.oid, t.typowner, t.typname, t.typlen, t.typprtlen, t.typinput, t.typoutput, t.typreceive, t.typsend, t.typelem, t.typdelim, t.typdefault, t.typrelid, t.typbyval) from t in pg_type");
+    
+
+    res = PQexec(query);
+    pbuf = PQparray(res+1);
+    ntups = PQntuplesGroup(pbuf,0);
+    
+    tinfo = (TypeInfo*)malloc(ntups * sizeof(TypeInfo));
+
+    i_oid = PQfnumberGroup(pbuf,0,"oid");
+    i_typowner = PQfnumberGroup(pbuf,0,"typowner");
+    i_typname = PQfnumberGroup(pbuf,0,"typname");
+    i_typlen = PQfnumberGroup(pbuf,0,"typlen");
+    i_typprtlen = PQfnumberGroup(pbuf,0,"typprtlen");
+    i_typinput = PQfnumberGroup(pbuf,0,"typinput");
+    i_typoutput = PQfnumberGroup(pbuf,0,"typoutput");
+    i_typreceive = PQfnumberGroup(pbuf,0,"typreceive");
+    i_typsend = PQfnumberGroup(pbuf,0,"typsend");
+    i_typelem = PQfnumberGroup(pbuf,0,"typelem");
+    i_typdelim = PQfnumberGroup(pbuf,0,"typdelim");
+    i_typdefault = PQfnumberGroup(pbuf,0,"typdefault");
+    i_typrelid = PQfnumberGroup(pbuf,0,"typrelid");
+    i_typbyval = PQfnumberGroup(pbuf,0,"typbyval");
+
+    for (i=0;i<ntups;i++) {
+       tinfo[i].oid = dupstr(PQgetvalue(pbuf,i,i_oid));
+       tinfo[i].typowner = dupstr(PQgetvalue(pbuf,i,i_typowner));
+       tinfo[i].typname = dupstr(PQgetvalue(pbuf,i,i_typname));
+       tinfo[i].typlen = dupstr(PQgetvalue(pbuf,i,i_typlen));
+       tinfo[i].typprtlen = dupstr(PQgetvalue(pbuf,i,i_typprtlen));
+       tinfo[i].typinput = dupstr(PQgetvalue(pbuf,i,i_typinput));
+       tinfo[i].typoutput = dupstr(PQgetvalue(pbuf,i,i_typoutput));
+       tinfo[i].typreceive = dupstr(PQgetvalue(pbuf,i,i_typreceive));
+       tinfo[i].typsend = dupstr(PQgetvalue(pbuf,i,i_typsend));
+       tinfo[i].typelem = dupstr(PQgetvalue(pbuf,i,i_typelem));
+       tinfo[i].typdelim = dupstr(PQgetvalue(pbuf,i,i_typdelim));
+       tinfo[i].typdefault = dupstr(PQgetvalue(pbuf,i,i_typdefault));
+       tinfo[i].typrelid = dupstr(PQgetvalue(pbuf,i,i_typrelid));
+
+       if (strcmp(PQgetvalue(pbuf,i,i_typbyval), "f") == 0)
+           tinfo[i].passedbyvalue = 0;
+       else
+           tinfo[i].passedbyvalue = 1;
+
+       /* check for user-defined array types,
+          omit system generated ones */
+       if ( (strcmp(tinfo[i].typelem, "0") != 0)  &&
+            tinfo[i].typname[0] != '_')
+           tinfo[i].isArray = 1;
+       else
+           tinfo[i].isArray = 0;
+    }
+
+    *numTypes = ntups;
+
+    PQexec("end");
+    PQclear(res+1);
+    return tinfo;
+}
+
+/*
+ * getOperators:
+ *    read all operators in the system catalogs and return them in the 
+ * OprInfo* structure
+ *
+ *  numOprs is set to the number of operators read in 
+ *    
+ *
+ */
+
+OprInfo*
+getOperators(int *numOprs)
+{
+    char *res;
+    PortalBuffer *pbuf;
+    int ntups;
+    int i;
+    char query[MAXQUERYLEN];
+
+    OprInfo* oprinfo;
+
+    int i_oid;
+    int i_oprname;
+    int i_oprkind;
+    int i_oprcode;
+    int i_oprleft;
+    int i_oprright;
+    int i_oprcom;
+    int i_oprnegate;
+    int i_oprrest;
+    int i_oprjoin;
+    int i_oprcanhash;
+    int i_oprlsortop;
+    int i_oprrsortop;
+    
+    /* find all operators, including builtin operators,
+       filter out system-defined operators at dump-out time */
+    PQexec("begin");
+/*
+    sprintf(query, "SELECT oid, oprname, oprkind, oprcode, oprleft, oprright, oprcom, oprnegate, oprrest, oprjoin, oprcanhash, oprlsortop, oprrsortop from pg_operator");
+*/
+    sprintf(query, "retrieve (o.oid, o.oprname, o.oprkind, o.oprcode, o.oprleft, o.oprright, o.oprcom, o.oprnegate, o.oprrest, o.oprjoin, o.oprcanhash, o.oprlsortop, o.oprrsortop) from o in pg_operator");
+
+
+    res = PQexec(query);
+    pbuf = PQparray(res+1);
+    ntups = PQntuplesGroup(pbuf,0);
+    *numOprs = ntups;
+
+    oprinfo = (OprInfo*)malloc(ntups * sizeof(OprInfo));
+
+    i_oid = PQfnumberGroup(pbuf,0,"oid");
+    i_oprname = PQfnumberGroup(pbuf,0,"oprname");
+    i_oprkind = PQfnumberGroup(pbuf,0,"oprkind");
+    i_oprcode = PQfnumberGroup(pbuf,0,"oprcode");
+    i_oprleft = PQfnumberGroup(pbuf,0,"oprleft");
+    i_oprright = PQfnumberGroup(pbuf,0,"oprright");
+    i_oprcom = PQfnumberGroup(pbuf,0,"oprcom");
+    i_oprnegate = PQfnumberGroup(pbuf,0,"oprnegate");
+    i_oprrest = PQfnumberGroup(pbuf,0,"oprrest");
+    i_oprjoin = PQfnumberGroup(pbuf,0,"oprjoin");
+    i_oprcanhash = PQfnumberGroup(pbuf,0,"oprcanhash");
+    i_oprlsortop = PQfnumberGroup(pbuf,0,"oprlsortop");
+    i_oprrsortop = PQfnumberGroup(pbuf,0,"oprrsortop");
+
+    for (i=0;i<ntups;i++) {
+       oprinfo[i].oid = dupstr(PQgetvalue(pbuf,i,i_oid));
+       oprinfo[i].oprname = dupstr(PQgetvalue(pbuf,i,i_oprname));
+       oprinfo[i].oprkind = dupstr(PQgetvalue(pbuf,i,i_oprkind));
+       oprinfo[i].oprcode = dupstr(PQgetvalue(pbuf,i,i_oprcode));
+       oprinfo[i].oprleft = dupstr(PQgetvalue(pbuf,i,i_oprleft));
+       oprinfo[i].oprright = dupstr(PQgetvalue(pbuf,i,i_oprright));
+       oprinfo[i].oprcom = dupstr(PQgetvalue(pbuf,i,i_oprcom));
+       oprinfo[i].oprnegate = dupstr(PQgetvalue(pbuf,i,i_oprnegate));
+       oprinfo[i].oprrest = dupstr(PQgetvalue(pbuf,i,i_oprrest));
+       oprinfo[i].oprjoin = dupstr(PQgetvalue(pbuf,i,i_oprjoin));
+       oprinfo[i].oprcanhash = dupstr(PQgetvalue(pbuf,i,i_oprcanhash));
+       oprinfo[i].oprlsortop = dupstr(PQgetvalue(pbuf,i,i_oprlsortop));
+       oprinfo[i].oprrsortop = dupstr(PQgetvalue(pbuf,i,i_oprrsortop));
+    }
+
+    PQclear(res+1);
+    PQexec("end");
+
+    return oprinfo;
+}
+
+
+/*
+ * getAggregates:
+ *    read all the user-defined aggregates in the system catalogs and
+ * return them in the AggInfo* structure
+ *
+ * numAggs is set to the number of aggregates read in 
+ *    
+ *
+ */
+AggInfo*
+getAggregates(int *numAggs)
+{
+    char* res;
+    PortalBuffer *pbuf;
+    int ntups;
+    int i;
+    char query[MAXQUERYLEN];
+    AggInfo *agginfo;
+
+    int i_oid;
+    int i_aggname;
+    int i_aggtransfn1;
+    int i_aggtransfn2;
+    int i_aggfinalfn;
+    int i_aggtranstype1;
+    int i_aggbasetype;
+    int i_aggtranstype2;
+    int i_agginitval1;
+    int i_agginitval2;
+
+    /* find all user-defined aggregates */
+
+    PQexec("begin");
+/*
+    sprintf(query, 
+           "SELECT oid, aggname, aggtransfn1, aggtransfn2, aggfinalfn, aggtranstype1, aggbasetype, aggtranstype2, agginitval1, agginitval2 from pg_aggregate;");
+*/
+    sprintf(query, 
+           "retrieve (a.oid, a.aggname, a.aggtransfn1, a.aggtransfn2, a.aggfinalfn, a.aggtranstype1, a.aggbasetype, a.aggtranstype2, a.agginitval1, a.agginitval2) from a in pg_aggregate");
+
+    res = PQexec(query);
+    pbuf = PQparray(res+1);
+    ntups = PQntuplesGroup(pbuf,0);
+    *numAggs = ntups;
+
+    agginfo = (AggInfo*)malloc(ntups * sizeof(AggInfo));
+    
+    i_oid = PQfnumberGroup(pbuf,0,"oid");
+    i_aggname = PQfnumberGroup(pbuf,0,"aggname");
+    i_aggtransfn1 = PQfnumberGroup(pbuf,0,"aggtransfn1");
+    i_aggtransfn2 = PQfnumberGroup(pbuf,0,"aggtransfn2");
+    i_aggfinalfn = PQfnumberGroup(pbuf,0,"aggfinalfn");
+    i_aggtranstype1 = PQfnumberGroup(pbuf,0,"aggtranstype1");
+    i_aggbasetype = PQfnumberGroup(pbuf,0,"aggbasetype");
+    i_aggtranstype2 = PQfnumberGroup(pbuf,0,"aggtranstype2");
+    i_agginitval1 = PQfnumberGroup(pbuf,0,"agginitval1");
+    i_agginitval2 = PQfnumberGroup(pbuf,0,"agginitval2");
+
+    for (i=0;i<ntups;i++) {
+       agginfo[i].oid = dupstr(PQgetvalue(pbuf,i,i_oid));
+       agginfo[i].aggname = dupstr(PQgetvalue(pbuf,i,i_aggname));
+       agginfo[i].aggtransfn1 = dupstr(PQgetvalue(pbuf,i,i_aggtransfn1));
+       agginfo[i].aggtransfn2 = dupstr(PQgetvalue(pbuf,i,i_aggtransfn2));
+       agginfo[i].aggfinalfn = dupstr(PQgetvalue(pbuf,i,i_aggfinalfn));
+       agginfo[i].aggtranstype1 = dupstr(PQgetvalue(pbuf,i,i_aggtranstype1));
+       agginfo[i].aggbasetype = dupstr(PQgetvalue(pbuf,i,i_aggbasetype));
+       agginfo[i].aggtranstype2 = dupstr(PQgetvalue(pbuf,i,i_aggtranstype2));
+       agginfo[i].agginitval1 = dupstr(PQgetvalue(pbuf,i,i_agginitval1));
+       agginfo[i].agginitval2 = dupstr(PQgetvalue(pbuf,i,i_agginitval2));
+    }
+
+    PQclear(res+1);
+    PQexec("end");
+
+    return agginfo;
+}
+
+/*
+ * getFuncs:
+ *    read all the user-defined functions in the system catalogs and
+ * return them in the FuncInfo* structure
+ *
+ * numFuncs is set to the number of functions read in 
+ *    
+ *
+ */
+FuncInfo*
+getFuncs(int *numFuncs)
+{
+    char* res;
+    PortalBuffer *pbuf;
+    int ntups;
+    int i, j;
+    char query[MAXQUERYLEN];
+    FuncInfo *finfo;
+    char *proargtypes;
+
+    int i_oid;
+    int i_proname;
+    int i_proowner;
+    int i_prolang;
+    int i_pronargs;
+    int i_proargtypes;
+    int i_prorettype;
+    int i_proretset;
+    int i_prosrc;
+    int i_probin;
+
+   /* find all user-defined funcs */
+
+    PQexec("begin");
+
+/*
+    sprintf(query, 
+           "SELECT oid, proname, proowner, prolang, pronargs, prorettype, proretset, proargtypes, prosrc, probin from pg_proc where oid > '%d'::oid", 
+           g_last_builtin_oid);
+*/
+    sprintf(query, 
+           "retrieve (f.oid, f.proname, f.proowner, f.prolang, f.pronargs, f.prorettype, f.proretset, f.proargtypes, f.prosrc, f.probin) from f in pg_proc where f.oid > \"%d\"::oid", 
+           g_last_builtin_oid);
+
+    res = PQexec(query);
+    pbuf = PQparray(res+1);
+    ntups = PQntuplesGroup(pbuf,0);
+    
+    *numFuncs = ntups;
+
+    finfo = (FuncInfo*)malloc(ntups * sizeof(FuncInfo));
+
+    i_oid = PQfnumberGroup(pbuf,0,"oid");
+    i_proname = PQfnumberGroup(pbuf,0,"proname");
+    i_proowner = PQfnumberGroup(pbuf,0,"proowner");
+    i_prolang = PQfnumberGroup(pbuf,0,"prolang");
+    i_pronargs = PQfnumberGroup(pbuf,0,"pronargs");
+    i_proargtypes = PQfnumberGroup(pbuf,0,"proargtypes");
+    i_prorettype = PQfnumberGroup(pbuf,0,"prorettype");
+    i_proretset = PQfnumberGroup(pbuf,0,"proretset");
+    i_prosrc = PQfnumberGroup(pbuf,0,"prosrc");
+    i_probin = PQfnumberGroup(pbuf,0,"probin");
+    
+    for (i=0;i<ntups;i++) {
+       finfo[i].oid = dupstr(PQgetvalue(pbuf,i,i_oid));
+       finfo[i].proname = dupstr(PQgetvalue(pbuf,i,i_proname));
+       finfo[i].proowner = dupstr(PQgetvalue(pbuf,i,i_proowner));
+
+       finfo[i].prosrc = checkForQuote(PQgetvalue(pbuf,i,i_prosrc));
+       finfo[i].probin = dupstr(PQgetvalue(pbuf,i,i_probin));
+
+       finfo[i].prorettype = dupstr(PQgetvalue(pbuf,i,i_prorettype));
+       finfo[i].retset = (strcmp(PQgetvalue(pbuf,i,i_proretset),"t") == 0);
+       finfo[i].nargs = atoi(PQgetvalue(pbuf,i,i_pronargs));
+       finfo[i].lang = (atoi(PQgetvalue(pbuf,i,i_prolang)) == C_PROLANG_OID);
+
+       parseArgTypes(finfo[i].argtypes, PQgetvalue(pbuf,i,i_proargtypes));
+
+       finfo[i].dumped = 0;
+    }
+
+    PQclear(res+1);
+    PQexec("end");
+
+    return finfo;
+
+}
+
+/*
+ * getTables
+ *    read all the user-defined tables (no indices, no catalogs)
+ * in the system catalogs return them in the TableInfo* structure
+ *
+ * numTables is set to the number of tables read in 
+ *    
+ *
+ */
+TableInfo*
+getTables(int *numTables)
+{
+    char* res;
+    PortalBuffer* pbuf;
+    int ntups;
+    int i, j;
+    char query[MAXQUERYLEN];
+    TableInfo *tblinfo;
+    
+    int i_oid;
+    int i_relname;
+    int i_relarch;
+
+    /* find all the user-defined tables (no indices and no catalogs),
+     ordering by oid is important so that we always process the parent
+     tables before the child tables when traversing the tblinfo* */
+    PQexec("begin");
+/*
+    sprintf(query, 
+           "SELECT oid, relname, relarch from pg_class where relkind = 'r' and relname !~ '^pg_' order by oid;");
+*/
+    sprintf(query, 
+           "retrieve (r.oid, r.relname, r.relarch) from r in pg_class where r.relkind = \"r\" and r.relname !~ \"^pg_\" and r.relname !~ \"^Xinv\" sort by oid");
+
+    res = PQexec(query);
+    pbuf = PQparray(res+1);
+    ntups = PQntuplesGroup(pbuf,0);
+
+    *numTables = ntups;
+
+    tblinfo = (TableInfo*)malloc(ntups * sizeof(TableInfo));
+
+    i_oid = PQfnumberGroup(pbuf,0,"oid");
+    i_relname = PQfnumberGroup(pbuf,0,"relname");
+    i_relarch = PQfnumberGroup(pbuf,0,"relarch");
+
+    for (i=0;i<ntups;i++) {
+       tblinfo[i].oid = dupstr(PQgetvalue(pbuf,i,i_oid));
+       tblinfo[i].relname = dupstr(PQgetvalue(pbuf,i,i_relname));
+       tblinfo[i].relarch = dupstr(PQgetvalue(pbuf,i,i_relarch));
+    }
+
+    PQclear(res+1);
+    PQexec("end");
+
+    return tblinfo;
+
+}
+
+/*
+ * getInherits
+ *    read all the inheritance information
+ * from the system catalogs return them in the InhInfo* structure
+ *
+ * numInherits is set to the number of tables read in 
+ *    
+ *
+ */
+InhInfo*
+getInherits(int *numInherits)
+{
+    char* res;
+    PortalBuffer* pbuf;
+    int ntups;
+    int i;
+    char query[MAXQUERYLEN];
+    InhInfo *inhinfo;
+    
+    int i_inhrel;
+    int i_inhparent;
+
+    /* find all the inheritance information */
+    PQexec("begin");
+/*
+    sprintf(query,  "SELECT inhrel, inhparent from pg_inherits");
+*/
+    sprintf(query,  "retrieve (i.inhrel, i.inhparent) from i in pg_inherits");
+
+    res = PQexec(query);
+    pbuf = PQparray(res+1);
+    ntups = PQntuplesGroup(pbuf,0);
+
+    *numInherits = ntups;
+
+    inhinfo = (InhInfo*)malloc(ntups * sizeof(InhInfo));
+
+    i_inhrel = PQfnumberGroup(pbuf,0,"inhrel");
+    i_inhparent = PQfnumberGroup(pbuf,0,"inhparent");
+
+    for (i=0;i<ntups;i++) {
+       inhinfo[i].inhrel = dupstr(PQgetvalue(pbuf,i,i_inhrel));
+       inhinfo[i].inhparent = dupstr(PQgetvalue(pbuf,i,i_inhparent));
+    }
+
+    PQclear(res+1);
+    PQexec("end");
+    return inhinfo;
+}
+
+/*
+ * getTableAttrs -
+ *    for each table in tblinfo, read its attributes types and names
+ * 
+ * this is implemented in a very inefficient way right now, looping
+ * through the tblinfo and doing a join per table to find the attrs and their 
+ * types
+ *
+ *  modifies tblinfo
+ */
+void
+getTableAttrs(TableInfo* tblinfo, int numTables)
+{
+    int i,j;
+    char q[MAXQUERYLEN];
+    int i_attname;
+    int i_typname;
+    char *res;
+    PortalBuffer *pbuf;
+    int ntups;
+
+    for (i=0;i<numTables;i++)  {
+
+       /* find all the user attributes and their types*/
+       /* we must read the attribute names in attribute number order! */
+       /* because we will use the attnum to index into the attnames array 
+          later */
+/*
+       sprintf(q,"SELECT a.attnum, a.attname, t.typname from pg_attribute a, pg_type t where a.attrelid = '%s' and a.atttypid = t.oid and a.attnum > 0 order by attnum",tblinfo[i].oid);
+*/
+if (g_verbose) 
+    fprintf(stderr,"%s finding the attrs and types for table: %s %s\n",
+           g_comment_start,
+           tblinfo[i].relname,
+           g_comment_end);
+
+
+       sprintf(q,"retrieve (a.attnum, a.attname, t.typname) from a in pg_attribute, t in pg_type where a.attrelid = \"%s\" and a.atttypid = t.oid and a.attnum > 0 sort by attnum",tblinfo[i].oid);
+
+       res = PQexec(q);
+       pbuf = PQparray(res+1);
+       ntups = PQntuplesGroup(pbuf,0);
+
+       i_attname = PQfnumberGroup(pbuf,0,"attname");
+       i_typname = PQfnumberGroup(pbuf,0,"typname");
+
+       tblinfo[i].numatts = ntups;
+       tblinfo[i].attnames = (char**) malloc( ntups * sizeof(char*));
+       tblinfo[i].out_attnames = (char**) malloc( ntups * sizeof(char*));
+       tblinfo[i].typnames = (char**) malloc( ntups * sizeof(char*));
+       tblinfo[i].inhAttrs = (int*) malloc (ntups * sizeof(int));
+       tblinfo[i].parentRels = NULL;
+       tblinfo[i].numParents = 0;
+       for (j=0;j<ntups;j++) {
+           tblinfo[i].attnames[j] = dupstr(PQgetvalue(pbuf,j,i_attname));
+           tblinfo[i].typnames[j] = dupstr(PQgetvalue(pbuf,j,i_typname));
+           tblinfo[i].inhAttrs[j] = 0; /* this flag is set in flagInhAttrs()*/
+       }
+       PQclear(res+1);
+    } 
+}
+
+
+/*
+ * getIndices
+ *    read all the user-defined indices information
+ * from the system catalogs return them in the InhInfo* structure
+ *
+ * numIndices is set to the number of indices read in 
+ *    
+ *
+ */
+IndInfo*
+getIndices(int *numIndices)
+{
+    int i;
+    char query[MAXQUERYLEN];
+    char *res;
+    PortalBuffer *pbuf;
+    int ntups;
+    IndInfo *indinfo;
+
+    int i_indexrelname;
+    int i_indrelname;
+    int i_indamname;
+    int i_indproc;
+    int i_indkey;
+    int i_indclassname;
+    
+    /* find all the user-define indices.
+       We do not handle partial indices.
+       We also assume that only single key indices 
+
+       this is a 5-way join !!
+    */
+       
+    PQexec("begin");
+/*
+    sprintf(query,
+           "SELECT t1.relname as indexrelname, t2.relname as indrelname, i.indproc, i.indkey[0], o.opcname as indclassname, a.amname as indamname from pg_index i, pg_class t1, pg_class t2, pg_opclass o, pg_am a where t1.oid = i.indexrelid and t2.oid = i.indrelid and o.oid = i.indclass[0] and t1.relam = a.oid and i.indexrelid > '%d'::oid and t2.relname !~ '^pg_';",
+           g_last_builtin_oid);
+*/
+
+    sprintf(query,
+           "retrieve (indexrelname = t1.relname, indrelname = t2.relname, i.indproc, i.indkey[0], indclassname = o.opcname, indamname = a.amname) from i in pg_index, t1 in pg_class, t2 in pg_class, o in pg_opclass, a in pg_am where t1.oid = i.indexrelid and t2.oid = i.indrelid and o.oid = i.indclass[0] and t1.relam = a.oid and i.indexrelid > \"%d\"::oid and t2.relname !~ \"^pg_\" and t1.relname !~ \"^Xinx\"",
+           g_last_builtin_oid);
+
+    res = PQexec(query);
+    pbuf = PQparray(res+1);
+    ntups = PQntuplesGroup(pbuf,0);
+
+    *numIndices = ntups;
+
+    indinfo = (IndInfo*)malloc(ntups * sizeof (IndInfo));
+
+    i_indexrelname = PQfnumberGroup(pbuf,0,"indexrelname");
+    i_indrelname = PQfnumberGroup(pbuf,0,"indrelname");
+    i_indamname = PQfnumberGroup(pbuf,0,"indamname");
+    i_indproc = PQfnumberGroup(pbuf,0,"indproc");
+    i_indkey = PQfnumberGroup(pbuf,0,"indkey");
+    i_indclassname = PQfnumberGroup(pbuf,0,"indclassname");
+
+    for (i=0;i<ntups;i++) {
+       indinfo[i].indexrelname = dupstr(PQgetvalue(pbuf,i,i_indexrelname));
+       indinfo[i].indrelname = dupstr(PQgetvalue(pbuf,i,i_indrelname));
+       indinfo[i].indamname = dupstr(PQgetvalue(pbuf,i,i_indamname));
+       indinfo[i].indproc = dupstr(PQgetvalue(pbuf,i,i_indproc));
+       indinfo[i].indkey = dupstr(PQgetvalue(pbuf,i,i_indkey));
+       indinfo[i].indclassname = dupstr(PQgetvalue(pbuf,i,i_indclassname));
+    }
+    PQclear(res+1);
+    PQexec("end");
+
+    return indinfo;
+}
+
+/*
+ * dumpTypes
+ *    writes out to fout queries to recreate all the user-defined types
+ *
+ */
+
+void
+dumpTypes(FILE* fout, FuncInfo* finfo, int numFuncs,
+         TypeInfo* tinfo, int numTypes)
+{
+    int i;
+    char q[MAXQUERYLEN];
+    int funcInd;
+
+    for (i=0;i<numTypes;i++) {
+
+       /* skip all the builtin types */
+       if (atoi(tinfo[i].oid) < g_last_builtin_oid)
+           continue;
+
+       /* skip relation types */
+       if (atoi(tinfo[i].typrelid) != 0)
+           continue;
+
+       /* skip all array types that start w/ underscore */
+       if ( (tinfo[i].typname[0] == '_') &&
+            (strcmp(tinfo[i].typinput, "array_in") == 0))
+           continue;
+
+       /* before we create a type, we need to create the input and
+          output functions for it, if they haven't been created already */
+       funcInd = findFuncByName(finfo, numFuncs, tinfo[i].typinput);
+       if (funcInd !=  -1) 
+           dumpOneFunc(fout,finfo,funcInd,tinfo,numTypes);
+
+       funcInd = findFuncByName(finfo, numFuncs, tinfo[i].typoutput);
+       if (funcInd !=  -1) 
+           dumpOneFunc(fout,finfo,funcInd,tinfo,numTypes);
+
+       if (g_outputSQL) {
+           sprintf(q,
+                   "CREATE TYPE %s ( internallength = %s, externallength = %s, input = %s, output = %s, send = %s, receive = %s, default = '%s'",
+                   tinfo[i].typname,
+                   tinfo[i].typlen,
+                   tinfo[i].typprtlen,
+                   tinfo[i].typinput,
+                   tinfo[i].typoutput,
+                   tinfo[i].typsend,
+                   tinfo[i].typreceive,
+                   tinfo[i].typdefault);
+       } else {
+           sprintf(q,
+                   "define type %s ( internallength = %s, externallength = %s, input = %s, output = %s, send = %s, receive = %s, default = \"%s\"",
+                   tinfo[i].typname,
+                   (strcmp(tinfo[i].typlen, "-1") == 0) ? "variable" : tinfo[i].typlen,
+                   (strcmp(tinfo[i].typprtlen, "-1") == 0) ? "variable " :tinfo[i].typprtlen,
+                   tinfo[i].typinput,
+                   tinfo[i].typoutput,
+                   tinfo[i].typsend,
+                   tinfo[i].typreceive,
+                   tinfo[i].typdefault);
+       }
+
+       if (tinfo[i].isArray) {
+           char* elemType;
+
+           elemType = findTypeByOid(tinfo, numTypes, tinfo[i].typelem);
+           
+           if (g_outputSQL)
+               sprintf(q,"%s, element = %s, delimiter = '%s'",
+                       q, elemType,tinfo[i].typdelim);
+           else
+               sprintf(q,"%s, element = %s, delimiter = \"%s\"",
+                       q, elemType,tinfo[i].typdelim);
+       }
+       if (tinfo[i].passedbyvalue)
+           strcat(q,",passedbyvalue");
+       else
+           strcat(q,")");
+
+       if (g_outputSQL) 
+           strcat(q,";\n");
+       else
+           strcat(q,"\\g\n");
+       
+       fputs(q,fout);
+    }
+    fflush(fout);
+}
+
+/*
+ * dumpFuncs
+ *    writes out to fout the queries to recreate all the user-defined functions
+ *
+ */
+void
+dumpFuncs(FILE* fout, FuncInfo* finfo, int numFuncs, 
+         TypeInfo *tinfo, int numTypes)
+{
+    int i;
+    char q[MAXQUERYLEN];
+    for (i=0;i<numFuncs;i++)  {
+       dumpOneFunc(fout,finfo,i,tinfo,numTypes);
+    }
+}
+
+/*
+ * dumpOneFunc:
+ *    dump out only one function,  the index of which is given in the third
+ *  argument
+ *
+ */
+
+void
+dumpOneFunc(FILE* fout, FuncInfo* finfo, int i,
+           TypeInfo *tinfo, int numTypes)
+{
+    char q[MAXQUERYLEN];
+    int j;
+    
+    if (finfo[i].dumped)
+       return;
+    else
+       finfo[i].dumped = 1;
+
+    if (g_outputSQL) {
+       sprintf(q,"CREATE FUNCTION %s (",finfo[i].proname);
+
+       for (j=0;j<finfo[i].nargs;j++) {
+           char* typname;
+           typname = findTypeByOid(tinfo, numTypes, finfo[i].argtypes[j]);
+           sprintf(q, "%s%s%s",
+                   q,  
+               (j > 0) ? "," : "",
+                   typname);
+       }
+       sprintf(q,"%s ) RETURNS %s%s AS '%s' LANGUAGE '%s';\n",
+               q, 
+               finfo[i].retset ? " SETOF " : "",
+               findTypeByOid(tinfo, numTypes, finfo[i].prorettype),
+               (finfo[i].lang) ? finfo[i].probin : finfo[i].prosrc,
+               (finfo[i].lang) ? "C" : "SQL");
+if (finfo[i].lang != 1) {
+    fprintf(stderr, 
+           "%s WARNING: text of function named %s is in POSTQUEL %s\n",
+           g_comment_start,
+           finfo[i].proname,
+           g_comment_end);
+}
+
+    } else {
+       sprintf(q,"define function %s ( language = \"%s\", returntype = %s%s) arg is (",
+               finfo[i].proname,
+               (finfo[i].lang) ? "c" : "postquel",
+               finfo[i].retset ? " setof " : "",
+               findTypeByOid(tinfo, numTypes, finfo[i].prorettype)
+               );
+
+       for (j=0;j<finfo[i].nargs;j++) {
+           char* typname;
+           typname = findTypeByOid(tinfo, numTypes, finfo[i].argtypes[j]);
+           sprintf(q, "%s%s%s",
+                   q,  
+               (j > 0) ? "," : "",
+                   typname);
+       }
+       sprintf(q,"%s ) as \"%s\"\\g\n",
+               q, 
+               (finfo[i].lang) ? finfo[i].probin : finfo[i].prosrc);
+    }
+    
+    fputs(q,fout);
+    fflush(fout);
+
+}
+
+/*
+ * dumpOprs
+ *    writes out to fout the queries to recreate all the user-defined operators
+ *
+ */
+void 
+dumpOprs(FILE* fout, OprInfo* oprinfo, int numOperators,
+        TypeInfo *tinfo, int numTypes)
+{
+    int i;
+    char q[MAXQUERYLEN];
+    char leftarg[MAXQUERYLEN];
+    char rightarg[MAXQUERYLEN];
+    char commutator[MAXQUERYLEN];
+    char negator[MAXQUERYLEN];
+    char restrict[MAXQUERYLEN];
+    char join[MAXQUERYLEN];
+    char sortop[MAXQUERYLEN];
+    char comma[2];
+
+    for (i=0;i<numOperators;i++) {
+
+       /* skip all the builtin oids */
+       if (atoi(oprinfo[i].oid) < g_last_builtin_oid)
+           continue;
+
+       /* some operator are invalid because they were the result
+          of user defining operators before commutators exist */
+       if (strcmp(oprinfo[i].oprcode, "-") == 0)
+           continue;
+
+       leftarg[0] = '\0';
+       rightarg[0] = '\0';
+       /* right unary means there's a left arg
+          and left unary means there's a right arg */
+       if (strcmp(oprinfo[i].oprkind, "r") == 0 || 
+           strcmp(oprinfo[i].oprkind, "b") == 0 ) {
+           sprintf(leftarg, ", %s = %s ",
+                   (g_outputSQL) ? "LEFTARG" : "arg1",
+                   findTypeByOid(tinfo, numTypes, oprinfo[i].oprleft));
+       } 
+       if (strcmp(oprinfo[i].oprkind, "l") == 0 || 
+           strcmp(oprinfo[i].oprkind, "b") == 0 ) {
+           sprintf(rightarg, ", %s = %s ",
+                   (g_outputSQL) ? "RIGHTARG" : "arg2",
+                   findTypeByOid(tinfo, numTypes, oprinfo[i].oprright));
+       }
+       if (strcmp(oprinfo[i].oprcom, "0") == 0) 
+           commutator[0] = '\0';
+       else
+           sprintf(commutator,", commutator = %s ",
+                   findOprByOid(oprinfo, numOperators, oprinfo[i].oprcom));
+
+       if (strcmp(oprinfo[i].oprnegate, "0") == 0) 
+           negator[0] = '\0';
+       else
+           sprintf(negator,", negator = %s ",
+                   findOprByOid(oprinfo, numOperators, oprinfo[i].oprnegate));
+
+       if (strcmp(oprinfo[i].oprrest, "-") == 0)
+           restrict[0] = '\0';
+       else
+           sprintf(restrict,", restrict = %s ", oprinfo[i].oprrest);
+                   
+       if (strcmp(oprinfo[i].oprjoin,"-") == 0)
+           join[0] = '\0';
+       else
+           sprintf(join,", join = %s ", oprinfo[i].oprjoin);
+                   
+       if (strcmp(oprinfo[i].oprlsortop, "0") == 0) 
+           sortop[0] = '\0';
+       else
+           {
+           sprintf(sortop,", SORT = %s ",
+                   findOprByOid(oprinfo, numOperators,
+                                oprinfo[i].oprlsortop));
+           if (strcmp(oprinfo[i].oprrsortop, "0") != 0)
+               sprintf(sortop, "%s , %s", sortop, 
+                       findOprByOid(oprinfo, numOperators,
+                                    oprinfo[i].oprlsortop));
+       }
+
+       if (g_outputSQL)  {
+           sprintf(q,
+                   "CREATE OPERATOR %s (PROCEDURE = %s %s %s %s %s %s %s %s %s);\n ",
+                   oprinfo[i].oprname,
+                   oprinfo[i].oprcode,
+                   leftarg,
+                   rightarg,
+                   commutator,
+                   negator,
+                   restrict,
+                   (strcmp(oprinfo[i].oprcanhash, "t")) ? ", HASHES" : "",
+                   join,
+                   sortop);
+       } else
+           sprintf(q,
+                   "define operator %s (procedure = %s %s %s %s %s %s %s %s %s)\\g\n ",
+                   oprinfo[i].oprname,
+                   oprinfo[i].oprcode,
+                   leftarg,
+                   rightarg,
+                   commutator,
+                   negator,
+                   restrict,
+                   (strcmp(oprinfo[i].oprcanhash, "t")) ? ", hashes" : "",
+                   join,
+                   sortop);
+
+       fputs(q,fout);
+    }
+    fflush(fout);
+
+}
+
+/*
+ * dumpAggs
+ *    writes out to fout the queries to create all the user-defined aggregates
+ *
+ */
+void
+dumpAggs(FILE* fout, AggInfo* agginfo, int numAggs,
+       TypeInfo *tinfo, int numTypes)
+{
+    int i;
+    char q[MAXQUERYLEN];
+    char sfunc1[MAXQUERYLEN];
+    char sfunc2[MAXQUERYLEN];
+    char finalfunc[MAXQUERYLEN];
+    char *basetype;
+    char *stype1;
+    char *stype2;
+    char comma1[2], comma2[2];
+
+    for (i=0;i<numAggs;i++) {
+       /* skip all the builtin oids */
+       if (atoi(agginfo[i].oid) < g_last_builtin_oid)
+           continue;
+
+       if ( strcmp(agginfo[i].aggtransfn1, "-") == 0) 
+           sfunc1[0] = '\0';
+       else {
+           sprintf(sfunc1, 
+                   "sfunc1 = %s, basetype = %s, stype1 = %s",
+                   agginfo[i].aggtransfn1,
+                   findTypeByOid(tinfo,numTypes,agginfo[i].aggbasetype),
+                   findTypeByOid(tinfo,numTypes,agginfo[i].aggtranstype1));
+           if (agginfo[i].agginitval1) {
+               if (g_outputSQL)
+                   sprintf(sfunc1, "%s ,INITCOND1 = '%s'",
+                           sfunc1, agginfo[i].agginitval1);
+               else
+                   sprintf(sfunc1, "%s ,initcond1 = \"%s\"",
+                           sfunc1, agginfo[i].agginitval1);
+
+           }
+           
+       }
+
+       if ( strcmp(agginfo[i].aggtransfn2, "-") == 0) 
+           sfunc2[0] = '\0';
+       else {
+           sprintf(sfunc2, 
+                   "sfunc2 = %s, stype2 = %s",
+                   agginfo[i].aggtransfn2,
+                   findTypeByOid(tinfo,numTypes,agginfo[i].aggtranstype2));
+           if (agginfo[i].agginitval2) {
+               if (g_outputSQL)
+                   sprintf(sfunc2,"%s ,initcond2 = '%s'",
+                           sfunc2, agginfo[i].agginitval2);
+               else
+                   sprintf(sfunc2,"%s ,initcond2 = \"%s\"",
+                           sfunc2, agginfo[i].agginitval2);
+
+           }
+       }
+       
+       if ( strcmp(agginfo[i].aggfinalfn, "-") == 0)
+           finalfunc[0] = '\0';
+       else {
+           sprintf(finalfunc, "finalfunc = %s", agginfo[i].aggfinalfn);
+       }
+       if (sfunc1[0] != '\0' && sfunc2[0] != '\0') {
+           comma1[0] = ','; comma1[1] = '\0';
+       } else
+           comma1[0] = '\0';
+
+       if (finalfunc[0] != '\0' && (sfunc1[0] != '\0' || sfunc2[0] != '\0')) {
+           comma2[0] = ',';comma2[1] = '\0';
+       } else
+           comma2[0] = '\0';
+
+       if (g_outputSQL) {
+           sprintf(q,"CREATE AGGREGATE %s ( %s %s %s %s %s );\n",
+                   agginfo[i].aggname,
+                   sfunc1,
+                   comma1,
+                   sfunc2,
+                   comma2,
+                   finalfunc);
+       } else {
+           sprintf(q,"define aggregate %s ( %s %s %s %s %s )\\g\n",
+                   agginfo[i].aggname,
+                   sfunc1,
+                   comma1,
+                   sfunc2,
+                   comma2,
+                   finalfunc);
+       }
+       
+       fputs(q,fout);
+    }
+    fflush(fout);
+}
+
+/*
+ * dumpTables:
+ *    write out to fout all the user-define tables
+ */
+
+void
+dumpTables(FILE* fout, TableInfo *tblinfo, int numTables,
+          InhInfo *inhinfo, int numInherits,
+          TypeInfo *tinfo, int numTypes)
+{
+    int i,j,k;
+    char q[MAXQUERYLEN];
+    char **parentRels;  /* list of names of parent relations */
+    int numParents;
+    char *res;
+    PortalBuffer *pbuf;
+    int ntups;
+    int actual_atts; /* number of attrs in this CREATE statment */
+    char *archiveMode;
+
+    for (i=0;i<numTables;i++) {
+       parentRels = tblinfo[i].parentRels;
+       numParents = tblinfo[i].numParents;
+
+       if (g_outputSQL) {
+           sprintf(q, "CREATE TABLE %s (", tblinfo[i].relname);
+       } else {
+           sprintf(q, "create %s (", tblinfo[i].relname);
+       }
+       
+       actual_atts = 0;
+       for (j=0;j<tblinfo[i].numatts;j++) {
+           if (tblinfo[i].inhAttrs[j] == 0) {
+               if (g_outputSQL) {
+                   sprintf(q, "%s%s%s %s",
+                           q,
+                           (actual_atts > 0) ? ", " : "",
+                           tblinfo[i].attnames[j],
+                           tblinfo[i].typnames[j]);
+               }
+               else { 
+                   sprintf(q, "%s%s %s = %s",
+                           q,
+                           (actual_atts > 0) ? ", " : "",
+                           tblinfo[i].attnames[j],
+                           tblinfo[i].typnames[j]);
+
+               }
+               actual_atts++;
+           }
+       }
+
+       strcat(q,")");
+
+       if (numParents > 0) {
+           int oa = 0; /* index for the out_attnames array */
+           int l;
+           int parentInd;
+
+           sprintf(q, "%s inherits ( ",q);
+           for (k=0;k<numParents;k++){
+               sprintf(q, "%s%s%s",
+                       q,
+                       (k>0) ? ", " : "",
+                       parentRels[k]);
+               parentInd = findTableByName(tblinfo,numTables,parentRels[k]);
+
+               /* the out_attnames are in order of the out_attnames
+                  of the parent tables */
+               for (l=0; l<tblinfo[parentInd].numatts;l++)
+                   tblinfo[i].out_attnames[oa++] =
+                       tblinfo[parentInd].out_attnames[l];
+           }
+
+           /* include non-inherited attrs in out_attnames also,
+              oa should never exceed numatts */
+           for (l=0; l < tblinfo[i].numatts && oa < tblinfo[i].numatts ; l++)
+               if (tblinfo[i].inhAttrs[l] == 0) {
+                   tblinfo[i].out_attnames[oa++] = 
+                       tblinfo[i].attnames[l];
+               }
+
+           strcat(q,")");
+       }  else { /* for non-inherited tables, out_attnames 
+                    and attnames are the same  */
+           tblinfo[i].out_attnames = tblinfo[i].attnames;
+       }
+
+       switch(tblinfo[i].relarch[0]) {
+       case 'n':
+           archiveMode = "none";
+           break;
+       case 'h':
+           archiveMode = "heavy";
+           break;
+       case 'l':
+           archiveMode = "light";
+           break;
+       default:
+           fprintf(stderr, "unknown archive mode\n");
+           archiveMode = "none";
+           break;
+       }
+           
+       if (g_outputSQL) {
+           sprintf(q, "%s archive = %s;\n",
+                   q,
+                   archiveMode);
+       } else {
+           sprintf(q, "%s archive = %s\\g\n",
+                   q,
+                   archiveMode);
+       }
+           
+       fputs(q,fout);
+    }
+    fflush(fout);
+}
+
+/*
+ * dumpIndices:
+ *    write out to fout all the user-define indices
+ */
+void 
+dumpIndices(FILE* fout, IndInfo* indinfo, int numIndices,
+           TableInfo* tblinfo, int numTables)
+{
+    int i,j;
+    int tableInd;
+    char *attname;  /* the name of the indexed attribute  */
+    char *funcname; /* the name of the function to compute the index key from*/
+    int indkey;
+
+    char q[MAXQUERYLEN];
+    char *res;
+    PortalBuffer *pbuf;
+
+    for (i=0;i<numIndices;i++) {
+       tableInd = findTableByName(tblinfo, numTables,
+                                  indinfo[i].indrelname);
+       indkey = atoi(indinfo[i].indkey) - 1; 
+       attname = tblinfo[tableInd].attnames[indkey];
+       if (strcmp(indinfo[i].indproc,"0") == 0) {
+           funcname = NULL;
+       } else {
+           /* the funcname is an oid which we use to 
+              find the name of the pg_proc.  We need to do this
+              because getFuncs() only reads in the user-defined funcs
+              not all the funcs.  We might not find what we want
+              by looking in FuncInfo**/
+           sprintf(q,
+                   "retrieve(p.proname) from p in pg_proc where p.oid = \"%s\"::oid",
+                   indinfo[i].indproc);
+           res = PQexec(q);
+           pbuf = PQparray(res+1);
+           funcname = dupstr(PQgetvalue(pbuf,0,
+                                        PQfnumberGroup(pbuf,0,"proname")));
+           PQclear(res+1);
+       }
+       if (g_outputSQL) {
+           sprintf(q,"CREATE INDEX %s on %s using %s (",
+                   indinfo[i].indexrelname,
+                   indinfo[i].indrelname,
+                   indinfo[i].indamname);
+       } else {
+           sprintf(q,"define index %s on %s using %s (",
+                   indinfo[i].indexrelname,
+                   indinfo[i].indrelname,
+                   indinfo[i].indamname);
+
+       }
+       if (funcname) {
+           sprintf(q, "%s %s(%s) %s",
+                   q,funcname, attname, indinfo[i].indclassname);
+           free(funcname); 
+       } else
+           sprintf(q, "%s %s %s",
+                   q,attname,indinfo[i].indclassname);
+
+       if (g_outputSQL) {
+           strcat(q,");\n");
+       } else
+           strcat(q,")\\g\n");
+
+       fputs(q,fout);
+    }
+    fflush(fout);
+}
+
+
+/*
+ * dumpClasses -
+ *    dump the contents of all the classes.
+ */
+void
+dumpClasses(TableInfo *tblinfo, int numTables, FILE *fout)
+{
+    char query[255];
+    char *res;
+    int i,j;
+
+    int *attrmap; /* this is an vector map of how the actual attributes
+                    map to the corresponding output attributes.
+                    This is necessary because of a difference between
+                    SQL and POSTQUEL in the order of inherited attributes */
+
+    for(i = 0; i < numTables; i++) {
+       char *classname = tblinfo[i].relname;
+
+       if (g_outputSQL) 
+           fprintf(fout, "copy %s from stdin;\n", classname);
+       else
+           fprintf(fout, "copy %s from stdin\\g\n", classname);
+
+       sprintf(query, "retrieve (p.all) from p in %s", classname);
+       res = PQexec(query);
+
+       attrmap = (int*)malloc(tblinfo[i].numatts * sizeof(int));
+       if (tblinfo[i].numParents == 0) {
+           /* table with no inheritance use an identity mapping */
+           for (j=0;j<tblinfo[i].numatts;j++)
+               attrmap[j] = j;
+       } else {
+           int n = tblinfo[i].numatts;
+           for (j=0;j < n;j++) {
+               attrmap[j] = strInArray(tblinfo[i].attnames[j],
+                                      tblinfo[i].out_attnames,
+                                      n);
+           }
+       }
+
+/*
+       {
+           int j;
+           for (j=0;j<tblinfo[i].numatts;j++) {
+               fprintf(stderr,":%s\t",tblinfo[i].out_attnames[j]);
+           }
+           fprintf(stderr,"\n");
+       }
+*/
+
+       fflush(stdout);
+       fflush(stderr);
+       switch (*res) {
+       case 'P':
+           dumpTuples(&(res[1]), fout, attrmap);
+           PQclear(&(res[1]));
+           break;
+       case 'E':
+       case 'R':
+           fprintf(stderr, "Error while dumping %s\n", classname);
+           exit(1);
+           break;
+       }
+
+       fprintf(fout, ".\n");
+       free(attrmap);
+    }
+}
+
+/*
+ * dumpTuples --
+ *    prints out the tuples in ASCII representaiton. The output is a valid
+ *    input to COPY FROM stdin.
+ *
+ *    We only need to do this for POSTGRES 4.2 databases since the
+ *    COPY TO statement doesn't escape newlines properly. It's been fixed
+ *    in Postgres95.
+ * 
+ * the attrmap passed in tells how to map the attributes copied in to the
+ * attributes copied out
+ */
+void
+dumpTuples(char *portalname, FILE *fout, int* attrmap)
+{
+    PortalBuffer *pbuf;
+    int i, j, k;
+    int m, n, t;
+    char **outVals = NULL; /* values to copy out */
+
+    /* Now to examine all tuples fetched. */
+    pbuf = PQparray(portalname);
+
+    n = PQntuplesGroup(pbuf,0); /* always assume only one group */
+    m = PQnfieldsGroup(pbuf,0);
+
+    if ( m > 0 ) {
+       /*
+        * Print out the tuples but only print tuples with at least
+        * 1 field.
+        */
+       outVals = (char**)malloc(m * sizeof(char*));
+    
+       for (j = 0; j < n; j++) {
+           for (k = 0; k < m; k++) {
+               outVals[attrmap[k]] = PQgetvalue(pbuf, j, k);
+           }
+           for (k = 0; k < m; k++) {
+               char *pval = outVals[k];
+
+               if (k!=0)
+                   fputc('\t', fout);  /* delimiter for attribute */
+
+               if (pval) {
+                   while (*pval != '\0') {
+                       /* escape tabs, newlines and backslashes */
+                       if (*pval=='\t' || *pval=='\n' || *pval=='\\')
+                           fputc('\\', fout);
+                       fputc(*pval, fout);
+                       pval++;
+                   }
+               }
+           }
+           fputc('\n', fout);  /* delimiter for a tuple */
+       }
+       free (outVals);
+    }
+    
+}
+
+
+
+/*
+ * findLastBuiltInOid -
+ * find the last built in oid 
+ * we do this by looking up the oid of 'template1' in pg_database,
+ * this is probably not foolproof but comes close 
+*/
+
+int
+findLastBuiltinOid()
+{
+       char *res;
+       PortalBuffer* pbuf;
+       int ntups;
+       int last_oid;
+
+       res = PQexec("retrieve (d.oid) from d in pg_database where d.datname = \"template1\"");
+       pbuf = PQparray(res+1);
+       ntups = PQntuplesGroup(pbuf,0);
+       if (ntups != 1) {
+           fprintf(stderr,"pg_dump: couldn't find the template1 database.  You are really hosed\nGiving up\n");
+           exit(2);
+       }
+       return (atoi(PQgetvalue(pbuf,0, PQfnumberGroup(pbuf,0,"oid"))));
+
+}
+
+
+/*
+ * checkForQuote:
+ *    checks a string for quote characters and backslashes them
+ */
+char*
+checkForQuote(char* s)
+{
+    char *r;
+    char c;
+    char *result;
+
+    int j = 0;
+
+    r = malloc(strlen(s)*3 + 1);  /* definitely long enough */
+
+    while ( (c = *s) != '\0') {
+
+       if (c == '\"') {
+           /* backslash the double quotes */
+           if (g_outputSQL) {
+               r[j++] = '\\'; 
+               c = '\'';
+           } else {
+               r[j++] = '\\';
+               r[j++] = '\\';
+           }
+       }
+       r[j++] = c;
+       s++;
+    }
+    r[j] = '\0';
+
+    result = dupstr(r);
+    free(r);
+
+    return result;
+    
+}
diff --git a/src/bin/pg4_dump/pg_dump.h b/src/bin/pg4_dump/pg_dump.h
new file mode 100644 (file)
index 0000000..0708f67
--- /dev/null
@@ -0,0 +1,195 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_dump.h
+ *    header file for the pg_dump utility
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * pg_dump.h,v 1.5 1995/06/28 22:32:36 jolly Exp
+ *
+ *-------------------------------------------------------------------------
+ */
+
+
+/* The *Info data structures run-time C structures used to store
+   system catalog information */
+   
+typedef struct _typeInfo {
+    char* oid;
+    char* typowner;
+    char* typname;
+    char* typlen;
+    char* typprtlen;
+    char* typinput;
+    char* typoutput;
+    char* typreceive;
+    char* typsend;
+    char* typelem;
+    char* typdelim;
+    char* typdefault;
+    char* typrelid;
+    int passedbyvalue;
+    int isArray;
+} TypeInfo;
+
+typedef struct _funcInfo {
+    char* oid;
+    char* proname;
+    char* proowner;
+    int lang;  /* 1 if C, else SQL */
+    int nargs;
+    char* argtypes[8];  /* should be derived from obj/fmgr.h instead of hardwired*/
+    char* prorettype;
+    int retset; /* 1 if the function returns a set, 0 otherwise */
+    char* prosrc;
+    char* probin;
+    int dumped; /* 1 if already dumped */
+} FuncInfo;
+
+typedef struct _tableInfo {
+    char *oid;
+    char *relname;
+    char *relarch;
+    int numatts;            /* number of attributes */
+    int *inhAttrs;          /* an array of flags, one for each attribute
+                             if the value is 1, then this attribute is
+                             an inherited attribute */
+    char **attnames;        /* the attribute names */
+    char **typnames;        /* fill out attributes */
+    int numParents;         /* number of (immediate) parent supertables */
+    char **parentRels;      /* names of parent relations, NULL
+                              if numParents == 0 */
+    char **out_attnames;    /* the attribute names, in the order they would
+                              be in, when the table is created in the
+                              target query language.
+                              this is needed because the SQL tables will
+                              not have the same order of attributes as
+                              the POSTQUEL tables */
+           
+} TableInfo;
+
+typedef struct _inhInfo {
+    char *oid;
+    char *inhrel;
+    char *inhparent;
+} InhInfo;
+
+typedef struct _indInfo {
+    char *indexrelname;  /* name of the secondary index class */
+    char *indrelname;    /* name of the indexed heap class */
+    char *indamname;     /* name of the access method (e.g. btree, rtree, etc.) */
+    char *indproc;       /* oid of the function to compute the index, 0 if none*/
+    char *indkey;        /* attribute number of the key attribute */
+    char *indclassname;  /* name of the opclass of the key */
+} IndInfo;
+
+typedef struct _aggInfo {
+    char *oid;
+    char *aggname;
+    char *aggtransfn1;
+    char *aggtransfn2;
+    char *aggfinalfn;
+    char *aggtranstype1;
+    char *aggbasetype;
+    char *aggtranstype2;
+    char *agginitval1;
+    char *agginitval2;
+} AggInfo;
+
+typedef struct _oprInfo {
+    char *oid;
+    char *oprname;
+    char *oprkind;   /* "b" = binary, "l" = left unary, "r" = right unary */
+    char *oprcode;   /* operator function name */
+    char *oprleft;   /* left operand type */
+    char *oprright;  /* right operand type */
+    char *oprcom;    /* oid of the commutator operator */
+    char *oprnegate; /* oid of the negator operator */
+    char *oprrest;   /* name of the function to calculate operator restriction
+                       selectivity */
+    char *oprjoin;    /* name of the function to calculate operator join
+                        selectivity */
+    char *oprcanhash; /* can we use hash join strategy ? */
+    char *oprlsortop; /* oid's of the left and right sort operators */
+    char *oprrsortop;
+} OprInfo;
+
+
+/* global decls */
+extern int g_verbose;  /* verbose flag */
+extern int g_last_builtin_oid; /* value of the last builtin oid */
+extern FILE *g_fout;     /* the script file */
+
+/* placeholders for comment starting and ending delimiters */
+extern char g_comment_start[10]; 
+extern char g_comment_end[10]; 
+
+extern char g_opaque_type[10]; /* name for the opaque type */
+
+/* pg_dump is really two programs in one
+    one version works with postgres v4r2
+    and the other works with postgres95
+    the common routines are declared here
+
+/*
+ *  common utility functions 
+*/
+
+extern TableInfo* dumpSchema(FILE* fout, int *numTablesPtr);
+
+extern char* findTypeByOid(TypeInfo* tinfo, int numTypes, char* oid);
+extern char* findOprByOid(OprInfo *oprinfo, int numOprs, char *oid);
+extern int findFuncByName(FuncInfo* finfo, int numFuncs, char* name);
+extern char** findParentsByOid(TableInfo* tbinfo, int numTables,
+                             InhInfo* inhinfo, int numInherits,
+                             char *oid, 
+                             int *numParents);
+extern int findTableByName(TableInfo *tbinfo, int numTables, char *relname);
+extern int findTableByOid(TableInfo *tbinfo, int numTables, char *oid);
+extern void flagInhAttrs(TableInfo* tbinfo, int numTables,
+                          InhInfo* inhinfo, int numInherits);
+
+extern void check_conn_and_db();
+extern char* dupstr(char *s);
+extern int strInArray(char* pattern, char** arr, int arr_size);
+extern void parseArgTypes(char **argtypes, char* str);
+extern int isArchiveName(char*);
+
+/*
+ * version specific routines 
+ */
+extern TypeInfo* getTypes(int *numTypes);
+extern FuncInfo* getFuncs(int *numFuncs);
+extern AggInfo* getAggregates(int *numAggregates);
+extern OprInfo* getOperators(int *numOperators);
+extern TableInfo* getTables(int *numTables);
+extern InhInfo* getInherits(int *numInherits);
+extern void getTableAttrs(TableInfo* tbinfo, int numTables);
+extern IndInfo* getIndices(int *numIndices);
+extern void dumpTypes(FILE* fout, FuncInfo* finfo, int numFuncs,
+                     TypeInfo* tinfo, int numTypes);
+extern void dumpFuncs(FILE* fout, FuncInfo* finfo, int numFuncs,
+                     TypeInfo *tinfo, int numTypes);
+extern void dumpAggs(FILE* fout, AggInfo* agginfo, int numAggregates,
+                    TypeInfo *tinfo, int numTypes);
+extern void dumpOprs(FILE* fout, OprInfo* agginfo, int numOperators,
+                    TypeInfo *tinfo, int numTypes);
+extern void dumpOneFunc(FILE* fout, FuncInfo* finfo, int i,
+                       TypeInfo *tinfo, int numTypes);
+extern void dumpTables(FILE* fout, TableInfo* tbinfo, int numTables,
+                      InhInfo *inhinfo, int numInherits,
+                      TypeInfo *tinfo, int numTypes);
+extern void dumpIndices(FILE* fout, IndInfo* indinfo, int numIndices,
+                       TableInfo* tbinfo, int numTables);
+
+extern void dumpClasses(TableInfo *tbinfo, int numTables, FILE *fout);
+extern void dumpTuples(char *portalname, FILE *fout, int *attrmap);
+extern char* checkForQuote(char* s);
+extern int findLastBuiltinOid();
+
+
+/* largest query string size */
+#define MAXQUERYLEN  5000
+
+/* these voodoo constants are from the backend */
+#define C_PROLANG_OID       13
diff --git a/src/bin/pg_dump/Makefile b/src/bin/pg_dump/Makefile
new file mode 100644 (file)
index 0000000..2276a2b
--- /dev/null
@@ -0,0 +1,23 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for bin/pg_dump
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+PROG=  pg_dump
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+include ../Makefile.global
+
+SRCS=  pg_dump.c common.c
+
+include $(MKDIR)/postgres.prog.mk
+
diff --git a/src/bin/pg_dump/README b/src/bin/pg_dump/README
new file mode 100644 (file)
index 0000000..17c433c
--- /dev/null
@@ -0,0 +1,73 @@
+pg_dump is a utility for dumping out a postgres database into a script
+file containing query commands.  The script files are in a ASCII
+format and can be used to reconstruct the database, even on other
+machines and other architectures.  pg_dump will produce the queries
+necessary to re-generate all user-defined types, functions, tables,
+indices, aggregates, and operators.  In addition, all the data is
+copied out in ASCII format so that it can be readily copied in again.
+
+
+To build:
+
+          % gmake clean install
+
+This version of the program will read in your postgres95 database and
+output the schema and the data tuples in SQL.  The dumps are useful
+for moving from one postgres95 installation to another.  
+
+
+How to use pg_dump:
+-------------------
+
+The command line options are fairly self explanatory.  Use -help to
+see the command line options.   recommend using -v to get
+more verbose descriptions of what pg_dump is doing.
+
+After running pg_dump, one should examine the output script file for any 
+warnings, especially in light of the limitations listed below.
+
+A typical use of pg_dump:
+
+       %  pg_dump -v -f oldDB.dump  oldDB
+       %  createdb newDB
+       %  psql newDB < oldDB.dump
+
+
+Caveats and limitations:
+------------------------
+
+pg_dump has a few limitations.  The limitations mostly stem from
+difficulty in extracting certain meta-information from the system
+catalogs.   
+
+   rules and views:  
+       pg_dump does not understand user-defined rules and views and
+       will fail to dump them properly.  (This is due to the fact that
+       rules are stored as plans in the catalogs and not textually)
+       
+   partial indices:
+       pg_dump does not understand partial indices. (The reason is
+       the same as above.  Partial index predicates are stored as plans)
+       
+   large objects:
+       pg_dump does not handle large objects.  Large
+       objects are ignored and must be dealt with manually.
+
+   oid preservation:
+       pg_dump does not preserve oid's while dumping.  If you have
+       stored oid's explicitly in tables in user-defined attributes,
+       and are using them as keys, then the output scripts will not
+       regenerate your database correctly. 
+
+pg_dump requires postgres95 beta0.03 or later.
+
+Bug-reporting
+--------------
+
+If you should find a problem with pg_dump, it is very important that
+you provide a (small) sample database which illustrates the problem.
+Please send bugs, questions, and feedback to the
+       [email protected]
+
+
+
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
new file mode 100644 (file)
index 0000000..8e2e090
--- /dev/null
@@ -0,0 +1,397 @@
+/*-------------------------------------------------------------------------
+ *
+ * common.c--
+ *    common routines between pg_dump and pg4_dump
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/param.h> /* for MAXHOSTNAMELEN on most */
+#ifdef PORTNAME_sparc_solaris
+#include <netdb.h>     /* for MAXHOSTNAMELEN on some */
+#endif
+
+#include "postgres.h"
+#include "libpq-fe.h"
+
+#include "pg_dump.h"
+
+/* dupstr : copies a string, while allocating space for it. 
+   the CALLER is responsible for freeing the space
+   returns NULL if the argument is NULL*/
+char* 
+dupstr(char *s)
+{
+  char* result;
+
+  if (s == NULL)
+    return NULL;
+
+  result = (char*)malloc(strlen(s)+1);
+  strcpy(result, s);
+  return result;
+}
+
+
+/*
+ * findTypeByOid 
+ *    given an oid of a type, return its typename
+ *
+ * if oid is "0", return "opaque" -- this is a special case
+ *
+ * NOTE:  should hash this, but just do linear search for now
+ */
+
+char*
+findTypeByOid(TypeInfo* tinfo, int numTypes, char* oid)
+{
+    int i;
+
+    if (strcmp(oid, "0") == 0) return g_opaque_type;
+
+    for (i=0;i<numTypes;i++) {
+       if (strcmp(tinfo[i].oid, oid) == 0)
+           return tinfo[i].typname;
+    }
+
+    /* should never get here */
+    fprintf(stderr,"failed sanity check,  type with oid %s was not found\n",
+           oid);
+    exit(2);
+}
+
+/*
+ * findOprByOid
+ *    given the oid of an operator, return the name of the operator
+ *
+ *
+ * NOTE:  should hash this, but just do linear search for now
+ * 
+ */
+char*
+findOprByOid(OprInfo *oprinfo, int numOprs, char *oid)
+{
+    int i;
+    for (i=0;i<numOprs;i++) {
+       if (strcmp(oprinfo[i].oid, oid) == 0)
+           return oprinfo[i].oprname;
+    }
+
+    /* should never get here */
+    fprintf(stderr,"failed sanity check,  opr with oid %s was not found\n",
+           oid);
+    exit(2);
+}
+
+
+/*
+ * findParentsByOid --
+ *    given the oid of a class, return the names of its parent classes
+ * and assign the number of parents to the last argument.
+ *
+ *
+ * returns NULL if none
+ */
+
+char** 
+findParentsByOid(TableInfo* tblinfo, int numTables,
+                InhInfo* inhinfo, int numInherits, char *oid,
+                int *numParentsPtr)
+{
+    int i,j;
+    int parentInd;
+    char** result;
+    int numParents;
+
+    numParents = 0;
+    for (i=0;i<numInherits;i++) {
+       if ( strcmp(inhinfo[i].inhrel, oid) == 0) {
+           numParents++;
+       }
+    }
+
+    *numParentsPtr = numParents;
+
+    if (numParents > 0) {
+       result = (char**)malloc(sizeof(char*) * numParents);
+       j = 0;
+       for (i=0;i<numInherits;i++) {
+           if ( strcmp(inhinfo[i].inhrel, oid) == 0) {
+               parentInd = findTableByOid(tblinfo, numTables, 
+                                          inhinfo[i].inhparent);
+               result[j++] = tblinfo[parentInd].relname;
+           }
+       }
+       return result;
+    }
+    else 
+       return NULL;
+}
+
+/*
+ * parseArgTypes
+ *    parse a string of eight numbers delimited by spaces
+ * into a character array
+ */
+
+void 
+parseArgTypes(char **argtypes, char* str)
+{
+    int j, argNum;
+    char temp[100];
+    char s;
+
+    argNum = 0;
+    j = 0;
+    while ( (s = *str) != '\0') {
+       if (s == ' ') {
+           temp[j] = '\0';
+           argtypes[argNum] = dupstr(temp);
+           argNum++;
+           j = 0;
+       } else {
+           temp[j] = s;
+           j++;
+       }
+       str++;
+    }
+    if (j != 0)  {
+       temp[j] = '\0';
+        argtypes[argNum] = dupstr(temp);
+    }
+    
+}
+
+
+/*
+ * strInArray:
+ *    takes in a string and a string array and the number of elements in the 
+ * string array.  
+ *    returns the index if the string is somewhere in the array, -1 otherwise
+ *
+ */
+
+int 
+strInArray(char* pattern, char** arr, int arr_size)
+{
+    int i;
+    for (i=0;i<arr_size;i++) {
+       if (strcmp(pattern, arr[i]) == 0) 
+           return i;
+    }
+    return -1;
+}
+
+/*
+ * dumpSchema:
+ *    we have a valid connection, we are now going to dump the schema
+ * into the file
+ *
+ */
+
+TableInfo *
+dumpSchema(FILE *fout, int *numTablesPtr)
+{
+    int numTypes;
+    int numFuncs;
+    int numTables;
+    int numInherits;
+    int numIndices;
+    int numAggregates;
+    int numOperators;
+    TypeInfo *tinfo;
+    FuncInfo *finfo;
+    AggInfo *agginfo;
+    TableInfo *tblinfo;
+    InhInfo *inhinfo;
+    IndInfo *indinfo;
+    OprInfo *oprinfo;
+
+if (g_verbose) fprintf(stderr,"%s reading user-defined types %s\n",
+                      g_comment_start, g_comment_end);
+    tinfo = getTypes(&numTypes);
+
+if (g_verbose) fprintf(stderr,"%s reading user-defined functions %s\n",
+                      g_comment_start, g_comment_end);
+    finfo = getFuncs(&numFuncs);
+
+if (g_verbose) fprintf(stderr,"%s reading user-defined aggregates %s\n",
+                      g_comment_start, g_comment_end);
+    agginfo = getAggregates(&numAggregates);
+
+if (g_verbose) fprintf(stderr,"%s reading user-defined operators %s\n",
+                      g_comment_start, g_comment_end);
+    oprinfo = getOperators(&numOperators);
+
+if (g_verbose) fprintf(stderr,"%s reading user-defined tables %s\n",
+                      g_comment_start, g_comment_end);
+    tblinfo = getTables(&numTables);
+
+if (g_verbose) fprintf(stderr,"%s reading table inheritance information %s\n",
+                      g_comment_start, g_comment_end);
+    inhinfo = getInherits(&numInherits);
+
+if (g_verbose) fprintf(stderr, "%s finding the attribute names and types for each table %s\n",
+                      g_comment_start, g_comment_end);
+    getTableAttrs(tblinfo, numTables);
+
+if (g_verbose) fprintf(stderr, "%s flagging inherited attributes in subtables %s\n",
+                      g_comment_start, g_comment_end);
+    flagInhAttrs(tblinfo, numTables, inhinfo, numInherits);
+
+if (g_verbose) fprintf(stderr,"%s reading indices information %s\n",
+                      g_comment_start, g_comment_end);
+    indinfo = getIndices(&numIndices);
+
+if (g_verbose) fprintf(stderr,"%s dumping out user-defined types %s\n",
+                      g_comment_start, g_comment_end);
+    dumpTypes(fout, finfo, numFuncs, tinfo, numTypes);
+
+if (g_verbose) fprintf(stderr,"%s dumping out tables %s\n",
+                      g_comment_start, g_comment_end);
+    dumpTables(fout, tblinfo, numTables, inhinfo, numInherits,
+              tinfo, numTypes);
+
+if (g_verbose) fprintf(stderr,"%s dumping out user-defined functions %s\n",
+                      g_comment_start, g_comment_end);
+    dumpFuncs(fout, finfo, numFuncs, tinfo, numTypes);
+
+if (g_verbose) fprintf(stderr,"%s dumping out user-defined functions %s\n",
+                      g_comment_start, g_comment_end);
+    dumpAggs(fout, agginfo, numAggregates, tinfo, numTypes);
+
+if (g_verbose) fprintf(stderr,"%s dumping out user-defined operators %s\n",
+                      g_comment_start, g_comment_end);
+    dumpOprs(fout, oprinfo, numOperators, tinfo, numTypes);
+
+if (g_verbose) fprintf(stderr,"%s dumping out indices %s\n",
+                      g_comment_start, g_comment_end);
+    dumpIndices(fout, indinfo, numIndices, tblinfo, numTables);
+
+    *numTablesPtr = numTables;
+    return tblinfo;
+}
+
+
+/* flagInhAttrs -
+ *   for each table in tblinfo, flag its inherited attributes
+ * so when we dump the table out, we don't dump out the inherited attributes
+ *   
+ * initializes the parentRels field of each table
+ *
+ * modifies tblinfo
+ *
+ */
+void
+flagInhAttrs(TableInfo* tblinfo, int numTables,
+            InhInfo* inhinfo, int numInherits)
+{
+    int i,j,k;
+    int parentInd;
+
+    /* we go backwards because the tables in tblinfo are in OID
+       order, meaning the subtables are after the parent tables
+       we flag inherited attributes from child tables first */
+    for (i = numTables-1; i >= 0; i--) {
+       tblinfo[i].parentRels = findParentsByOid(tblinfo, numTables,
+                                               inhinfo, numInherits,
+                                               tblinfo[i].oid,
+                                               &tblinfo[i].numParents);
+       for (k=0;k<tblinfo[i].numParents;k++) {
+           parentInd = findTableByName(tblinfo, numTables, 
+                                       tblinfo[i].parentRels[k]);
+           for (j=0;j<tblinfo[i].numatts;j++) {
+               if (strInArray(tblinfo[i].attnames[j],
+                              tblinfo[parentInd].attnames,
+                              tblinfo[parentInd].numatts) != -1) {
+                   tblinfo[i].inhAttrs[j] = 1;
+               }
+           }
+       }
+    }
+}
+
+
+/*
+ * findTableByName
+ *    finds the index (in tblinfo) of the table with the given relname
+ *  returns -1 if not found
+ *
+ * NOTE:  should hash this, but just do linear search for now
+ */
+
+int
+findTableByName(TableInfo* tblinfo, int numTables, char* relname)
+{
+    int i;
+    for (i=0;i<numTables;i++) {
+       if  (strcmp(tblinfo[i].relname, relname) == 0)
+           return i;
+    }
+    return -1;
+}
+
+/*
+ * findTableByOid
+ *    finds the index (in tblinfo) of the table with the given oid
+ *  returns -1 if not found
+ *
+ * NOTE:  should hash this, but just do linear search for now
+ */
+
+int
+findTableByOid(TableInfo* tblinfo, int numTables, char* oid)
+{
+    int i;
+    for (i=0;i<numTables;i++) {
+       if  (strcmp(tblinfo[i].oid, oid) == 0)
+           return i;
+    }
+    return -1;
+}
+
+
+/*
+ * findFuncByName
+ *    finds the index (in finfo) of the function with the given name
+ *  returns -1 if not found
+ *
+ * NOTE:  should hash this, but just do linear search for now
+ */
+
+int
+findFuncByName(FuncInfo* finfo, int numFuncs, char* name)
+{
+    int i;
+    for (i=0;i<numFuncs;i++) {
+       if  (strcmp(finfo[i].proname, name) == 0)
+           return i;
+    }
+    return -1;
+}
+
+/*
+ * isArchiveName
+ *
+ *   returns true if the relation name is an archive name, false otherwise
+ */ 
+int
+isArchiveName(char* relname)
+{
+    return (strlen(relname) > 1 && relname[1] == ',');
+}
+
+
+
+
+
+
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
new file mode 100644 (file)
index 0000000..4764a55
--- /dev/null
@@ -0,0 +1,1443 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_dump.c--
+ *    pg_dump is an utility for dumping out a postgres database
+ * into a script file.
+ *
+ *  pg_dump will read the system catalogs in a database and 
+ *  dump out a script that reproduces
+ *  the schema of the database in terms of
+ *        user-defined types
+ *        user-defined functions
+ *        tables
+ *        indices
+ *        aggregates
+ *        operators
+ *
+ * the output script is SQL that is understood by Postgres95
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h> /* for MAXHOSTNAMELEN on most */
+#ifdef PORTNAME_sparc_solaris
+#include <netdb.h>     /* for MAXHOSTNAMELEN on some */
+#endif
+
+#include "postgres.h"
+#include "libpq-fe.h"
+
+#include "pg_dump.h"
+
+extern char *optarg;
+extern int optind, opterr;
+
+/* global decls */
+int g_verbose;  /* verbose flag */
+int g_last_builtin_oid; /* value of the last builtin oid */
+FILE *g_fout;     /* the script file */
+PGconn *g_conn;   /* the database connection */
+
+char g_opaque_type[10]; /* name for the opaque type */
+
+/* placeholders for the delimiters for comments */
+char g_comment_start[10]; 
+char g_comment_end[10]; 
+
+
+static void
+usage(char* progname)
+{
+    fprintf(stderr, "usage:  %s [options] [dbname]\n",progname);
+    fprintf(stderr, "\t -f filename \t\t script output filename\n");
+    fprintf(stderr, "\t -H hostname \t\t server host name\n");
+    fprintf(stderr, "\t -p port     \t\t server port number\n");
+    fprintf(stderr, "\t -v          \t\t verbose\n");
+    fprintf(stderr, "\t -S          \t\t dump out only the schema, no data\n");
+    fprintf(stderr, "\n if dbname is not supplied, then the DATABASE environment name is used\n");
+    fprintf(stderr, "\n");
+
+    fprintf(stderr, "\tpg_dump dumps out postgres databases and produces a script file\n");
+    fprintf(stderr, "\tof SQL commands to regenerate the schema\n");
+    fprintf(stderr, "\tThe SQL output is designed for import into Postgres95\n");
+    exit(1);
+}
+
+void 
+exit_nicely(PGconn* conn)
+{
+  PQfinish(conn);
+  exit(1);
+}
+
+
+void
+main(int argc, char** argv)
+{
+    int c;
+    char* progname;
+    char* filename;
+    char* dbname;
+    int schemaOnly;
+    char *pghost = NULL;
+    char *pgport = NULL;
+
+    TableInfo *tblinfo;
+    int numTables;
+
+    dbname = NULL;
+    filename = NULL;
+    g_verbose = 0;
+
+    strcpy(g_comment_start,"-- ");
+    g_comment_end[0] = '\0';
+    strcpy(g_opaque_type, "opaque");
+
+    schemaOnly = 0;
+
+    progname = *argv;
+
+    while ((c = getopt(argc, argv,"f:H:p:vSD")) != EOF) {
+       switch(c) {
+       case 'f': /* output file name */
+           filename = optarg;
+           break;
+       case 'H' : /* server host */
+           pghost = optarg;
+           break;
+       case 'p' : /* server port */
+           pgport = optarg;
+           break;
+       case 'v': /* verbose */
+           g_verbose = 1;
+           break;
+       case 'S': /* dump schema only */
+           schemaOnly = 1;
+           break;
+       default:
+           usage(progname);
+           break;
+       }
+    }
+
+    /* open the output file */
+    if (filename == NULL) {
+       g_fout = stdout;
+    } else {
+       g_fout = fopen(filename, "w");
+       if (g_fout == NULL) {
+           fprintf(stderr,"%s: could not open output file named %s for writing\n",
+                   progname, filename);
+           exit(2);
+       }
+    }
+
+    /* find database */
+    if (!(dbname = argv[optind]) &&
+       !(dbname = getenv("DATABASE")) ) {
+           fprintf(stderr, "%s: no database name specified\n",progname);
+           exit (2);
+       }
+
+    g_conn = PQsetdb(pghost, pgport, NULL, NULL, dbname);
+    /* check to see that the backend connection was successfully made */
+    if (PQstatus(g_conn) == CONNECTION_BAD) {
+       fprintf(stderr,"Connection to database '%s' failed.\n", dbname);
+       fprintf(stderr,"%s",PQerrorMessage(g_conn));
+       exit_nicely(g_conn);
+    }
+
+    g_last_builtin_oid = findLastBuiltinOid();
+
+if (g_verbose) 
+    fprintf(stderr, "%s last builtin oid is %d %s\n",
+           g_comment_start,  g_last_builtin_oid, g_comment_end);
+
+    tblinfo = dumpSchema(g_fout, &numTables);
+    
+    if (!schemaOnly) {
+
+if (g_verbose) fprintf(stderr,"%s dumping out the contents of each table %s\n",
+                      g_comment_start, g_comment_end);
+
+      dumpClasses(tblinfo, numTables, g_fout); 
+    }     
+
+    fflush(g_fout);
+    fclose(g_fout);
+
+    PQfinish(g_conn);
+    exit(0);
+}
+
+
+/*
+ * getTypes: 
+ *    read all base types in the system catalogs and return them in the 
+ * TypeInfo* structure
+ *
+ *  numTypes is set to the number of types read in 
+ *
+ */
+TypeInfo*
+getTypes(int *numTypes)
+{
+    PGresult *res;
+    int ntups;
+    int i;
+    char query[MAXQUERYLEN];
+    TypeInfo *tinfo;
+
+    int i_oid;
+    int i_typowner;
+    int i_typname;
+    int i_typlen;
+    int i_typprtlen;
+    int i_typinput;
+    int i_typoutput;
+    int i_typreceive;
+    int i_typsend;
+    int i_typelem;
+    int i_typdelim;
+    int i_typdefault;
+    int i_typrelid;
+    int i_typbyval;
+
+    res = PQexec(g_conn, "begin");
+    if (!res || 
+       PQresultStatus(res) != PGRES_COMMAND_OK) {
+       fprintf(stderr,"BEGIN command failed\n");
+       exit_nicely(g_conn);
+    }
+    PQclear(res);
+    
+   /* find all base types */
+   /* we include even the built-in types 
+      because those may be used as array elements by user-defined types */
+   /* we filter out the built-in types when 
+      we dump out the types */
+
+    sprintf(query, "SELECT oid, typowner,typname, typlen, typprtlen, typinput, typoutput, typreceive, typsend, typelem, typdelim, typdefault, typrelid,typbyval from pg_type");
+    
+    res = PQexec(g_conn,query);
+    if (!res || 
+       PQresultStatus(res) != PGRES_TUPLES_OK) {
+       fprintf(stderr,"getTypes(): SELECT failed");
+       exit_nicely(g_conn);
+    }
+    
+    ntups = PQntuples(res);
+    
+    tinfo = (TypeInfo*)malloc(ntups * sizeof(TypeInfo));
+
+    i_oid = PQfnumber(res,"oid");
+    i_typowner = PQfnumber(res,"typowner");
+    i_typname = PQfnumber(res,"typname");
+    i_typlen = PQfnumber(res,"typlen");
+    i_typprtlen = PQfnumber(res,"typprtlen");
+    i_typinput = PQfnumber(res,"typinput");
+    i_typoutput = PQfnumber(res,"typoutput");
+    i_typreceive = PQfnumber(res,"typreceive");
+    i_typsend = PQfnumber(res,"typsend");
+    i_typelem = PQfnumber(res,"typelem");
+    i_typdelim = PQfnumber(res,"typdelim");
+    i_typdefault = PQfnumber(res,"typdefault");
+    i_typrelid = PQfnumber(res,"typrelid");
+    i_typbyval = PQfnumber(res,"typbyval");
+
+    for (i=0;i<ntups;i++) {
+       tinfo[i].oid = dupstr(PQgetvalue(res,i,i_oid));
+       tinfo[i].typowner = dupstr(PQgetvalue(res,i,i_typowner));
+       tinfo[i].typname = dupstr(PQgetvalue(res,i,i_typname));
+       tinfo[i].typlen = dupstr(PQgetvalue(res,i,i_typlen));
+       tinfo[i].typprtlen = dupstr(PQgetvalue(res,i,i_typprtlen));
+       tinfo[i].typinput = dupstr(PQgetvalue(res,i,i_typinput));
+       tinfo[i].typoutput = dupstr(PQgetvalue(res,i,i_typoutput));
+       tinfo[i].typreceive = dupstr(PQgetvalue(res,i,i_typreceive));
+       tinfo[i].typsend = dupstr(PQgetvalue(res,i,i_typsend));
+       tinfo[i].typelem = dupstr(PQgetvalue(res,i,i_typelem));
+       tinfo[i].typdelim = dupstr(PQgetvalue(res,i,i_typdelim));
+       tinfo[i].typdefault = dupstr(PQgetvalue(res,i,i_typdefault));
+       tinfo[i].typrelid = dupstr(PQgetvalue(res,i,i_typrelid));
+
+       if (strcmp(PQgetvalue(res,i,i_typbyval), "f") == 0)
+           tinfo[i].passedbyvalue = 0;
+       else
+           tinfo[i].passedbyvalue = 1;
+
+       /* check for user-defined array types,
+          omit system generated ones */
+       if ( (strcmp(tinfo[i].typelem, "0") != 0)  &&
+            tinfo[i].typname[0] != '_')
+           tinfo[i].isArray = 1;
+       else
+           tinfo[i].isArray = 0;
+    }
+
+    *numTypes = ntups;
+
+    PQclear(res);
+
+    res = PQexec(g_conn,"end");
+    PQclear(res);
+
+    return tinfo;
+}
+
+/*
+ * getOperators:
+ *    read all operators in the system catalogs and return them in the 
+ * OprInfo* structure
+ *
+ *  numOprs is set to the number of operators read in 
+ *    
+ *
+ */
+OprInfo*
+getOperators(int *numOprs)
+{
+    PGresult *res;
+    int ntups;
+    int i;
+    char query[MAXQUERYLEN];
+
+    OprInfo* oprinfo;
+
+    int i_oid;
+    int i_oprname;
+    int i_oprkind;
+    int i_oprcode;
+    int i_oprleft;
+    int i_oprright;
+    int i_oprcom;
+    int i_oprnegate;
+    int i_oprrest;
+    int i_oprjoin;
+    int i_oprcanhash;
+    int i_oprlsortop;
+    int i_oprrsortop;
+    
+    /* find all operators, including builtin operators,
+       filter out system-defined operators at dump-out time */
+    res = PQexec(g_conn, "begin");
+    if (!res || 
+       PQresultStatus(res) != PGRES_COMMAND_OK) {
+       fprintf(stderr,"BEGIN command failed\n");
+       exit_nicely(g_conn);
+    }
+    PQclear(res);
+
+    sprintf(query, "SELECT oid, oprname, oprkind, oprcode, oprleft, oprright, oprcom, oprnegate, oprrest, oprjoin, oprcanhash, oprlsortop, oprrsortop from pg_operator");
+
+    res = PQexec(g_conn, query);
+    if (!res || 
+       PQresultStatus(res) != PGRES_TUPLES_OK) {
+       fprintf(stderr,"getOperators(): SELECT failed");
+       exit_nicely(g_conn);
+    }
+    
+    ntups = PQntuples(res);
+    *numOprs = ntups;
+
+    oprinfo = (OprInfo*)malloc(ntups * sizeof(OprInfo));
+
+    i_oid = PQfnumber(res,"oid");
+    i_oprname = PQfnumber(res,"oprname");
+    i_oprkind = PQfnumber(res,"oprkind");
+    i_oprcode = PQfnumber(res,"oprcode");
+    i_oprleft = PQfnumber(res,"oprleft");
+    i_oprright = PQfnumber(res,"oprright");
+    i_oprcom = PQfnumber(res,"oprcom");
+    i_oprnegate = PQfnumber(res,"oprnegate");
+    i_oprrest = PQfnumber(res,"oprrest");
+    i_oprjoin = PQfnumber(res,"oprjoin");
+    i_oprcanhash = PQfnumber(res,"oprcanhash");
+    i_oprlsortop = PQfnumber(res,"oprlsortop");
+    i_oprrsortop = PQfnumber(res,"oprrsortop");
+
+    for (i=0;i<ntups;i++) {
+       oprinfo[i].oid = dupstr(PQgetvalue(res,i,i_oid));
+       oprinfo[i].oprname = dupstr(PQgetvalue(res,i,i_oprname));
+       oprinfo[i].oprkind = dupstr(PQgetvalue(res,i,i_oprkind));
+       oprinfo[i].oprcode = dupstr(PQgetvalue(res,i,i_oprcode));
+       oprinfo[i].oprleft = dupstr(PQgetvalue(res,i,i_oprleft));
+       oprinfo[i].oprright = dupstr(PQgetvalue(res,i,i_oprright));
+       oprinfo[i].oprcom = dupstr(PQgetvalue(res,i,i_oprcom));
+       oprinfo[i].oprnegate = dupstr(PQgetvalue(res,i,i_oprnegate));
+       oprinfo[i].oprrest = dupstr(PQgetvalue(res,i,i_oprrest));
+       oprinfo[i].oprjoin = dupstr(PQgetvalue(res,i,i_oprjoin));
+       oprinfo[i].oprcanhash = dupstr(PQgetvalue(res,i,i_oprcanhash));
+       oprinfo[i].oprlsortop = dupstr(PQgetvalue(res,i,i_oprlsortop));
+       oprinfo[i].oprrsortop = dupstr(PQgetvalue(res,i,i_oprrsortop));
+    }
+
+    PQclear(res);
+    res = PQexec(g_conn, "end");
+    PQclear(res);
+
+    return oprinfo;
+}
+
+
+/*
+ * getAggregates:
+ *    read all the user-defined aggregates in the system catalogs and
+ * return them in the AggInfo* structure
+ *
+ * numAggs is set to the number of aggregates read in 
+ *    
+ *
+ */
+AggInfo*
+getAggregates(int *numAggs)
+{
+    PGresult* res;
+    int ntups;
+    int i;
+    char query[MAXQUERYLEN];
+    AggInfo *agginfo;
+
+    int i_oid;
+    int i_aggname;
+    int i_aggtransfn1;
+    int i_aggtransfn2;
+    int i_aggfinalfn;
+    int i_aggtranstype1;
+    int i_aggbasetype;
+    int i_aggtranstype2;
+    int i_agginitval1;
+    int i_agginitval2;
+
+    /* find all user-defined aggregates */
+
+    res = PQexec(g_conn, "begin");
+    if (!res || 
+       PQresultStatus(res) != PGRES_COMMAND_OK) {
+       fprintf(stderr,"BEGIN command failed\n");
+       exit_nicely(g_conn);
+    }
+    PQclear(res);
+
+    sprintf(query, 
+           "SELECT oid, aggname, aggtransfn1, aggtransfn2, aggfinalfn, aggtranstype1, aggbasetype, aggtranstype2, agginitval1, agginitval2 from pg_aggregate;");
+
+    res = PQexec(g_conn, query);
+    if (!res || 
+       PQresultStatus(res) != PGRES_TUPLES_OK) {
+       fprintf(stderr,"getAggregates(): SELECT failed");
+       exit_nicely(g_conn);
+    }
+
+    ntups = PQntuples(res);
+    *numAggs = ntups;
+
+    agginfo = (AggInfo*)malloc(ntups * sizeof(AggInfo));
+    
+    i_oid = PQfnumber(res,"oid");
+    i_aggname = PQfnumber(res,"aggname");
+    i_aggtransfn1 = PQfnumber(res,"aggtransfn1");
+    i_aggtransfn2 = PQfnumber(res,"aggtransfn2");
+    i_aggfinalfn = PQfnumber(res,"aggfinalfn");
+    i_aggtranstype1 = PQfnumber(res,"aggtranstype1");
+    i_aggbasetype = PQfnumber(res,"aggbasetype");
+    i_aggtranstype2 = PQfnumber(res,"aggtranstype2");
+    i_agginitval1 = PQfnumber(res,"agginitval1");
+    i_agginitval2 = PQfnumber(res,"agginitval2");
+
+    for (i=0;i<ntups;i++) {
+       agginfo[i].oid = dupstr(PQgetvalue(res,i,i_oid));
+       agginfo[i].aggname = dupstr(PQgetvalue(res,i,i_aggname));
+       agginfo[i].aggtransfn1 = dupstr(PQgetvalue(res,i,i_aggtransfn1));
+       agginfo[i].aggtransfn2 = dupstr(PQgetvalue(res,i,i_aggtransfn2));
+       agginfo[i].aggfinalfn = dupstr(PQgetvalue(res,i,i_aggfinalfn));
+       agginfo[i].aggtranstype1 = dupstr(PQgetvalue(res,i,i_aggtranstype1));
+       agginfo[i].aggbasetype = dupstr(PQgetvalue(res,i,i_aggbasetype));
+       agginfo[i].aggtranstype2 = dupstr(PQgetvalue(res,i,i_aggtranstype2));
+       agginfo[i].agginitval1 = dupstr(PQgetvalue(res,i,i_agginitval1));
+       agginfo[i].agginitval2 = dupstr(PQgetvalue(res,i,i_agginitval2));
+    }
+
+    PQclear(res);
+
+    res = PQexec(g_conn, "end");
+    PQclear(res);
+    return agginfo;
+}
+
+/*
+ * getFuncs:
+ *    read all the user-defined functions in the system catalogs and
+ * return them in the FuncInfo* structure
+ *
+ * numFuncs is set to the number of functions read in 
+ *    
+ *
+ */
+FuncInfo*
+getFuncs(int *numFuncs)
+{
+    PGresult *res;
+    int ntups;
+    int i;
+    char query[MAXQUERYLEN];
+    FuncInfo *finfo;
+
+    int i_oid;
+    int i_proname;
+    int i_proowner;
+    int i_prolang;
+    int i_pronargs;
+    int i_proargtypes;
+    int i_prorettype;
+    int i_proretset;
+    int i_prosrc;
+    int i_probin;
+
+   /* find all user-defined funcs */
+
+    res = PQexec(g_conn, "begin");
+    if (!res || 
+       PQresultStatus(res) != PGRES_COMMAND_OK) {
+       fprintf(stderr,"BEGIN command failed\n");
+       exit_nicely(g_conn);
+    }
+    PQclear(res);
+
+    sprintf(query, 
+           "SELECT oid, proname, proowner, prolang, pronargs, prorettype, proretset, proargtypes, prosrc, probin from pg_proc where oid > '%d'::oid", 
+           g_last_builtin_oid);
+
+    res = PQexec(g_conn, query);
+    if (!res || 
+       PQresultStatus(res) != PGRES_TUPLES_OK) {
+       fprintf(stderr,"getFuncs(): SELECT failed");
+       exit_nicely(g_conn);
+    }
+    
+    ntups = PQntuples(res);
+    
+    *numFuncs = ntups;
+
+    finfo = (FuncInfo*)malloc(ntups * sizeof(FuncInfo));
+
+    i_oid = PQfnumber(res,"oid");
+    i_proname = PQfnumber(res,"proname");
+    i_proowner = PQfnumber(res,"proowner");
+    i_prolang = PQfnumber(res,"prolang");
+    i_pronargs = PQfnumber(res,"pronargs");
+    i_proargtypes = PQfnumber(res,"proargtypes");
+    i_prorettype = PQfnumber(res,"prorettype");
+    i_proretset = PQfnumber(res,"proretset");
+    i_prosrc = PQfnumber(res,"prosrc");
+    i_probin = PQfnumber(res,"probin");
+    
+    for (i=0;i<ntups;i++) {
+       finfo[i].oid = dupstr(PQgetvalue(res,i,i_oid));
+       finfo[i].proname = dupstr(PQgetvalue(res,i,i_proname));
+       finfo[i].proowner = dupstr(PQgetvalue(res,i,i_proowner));
+
+       finfo[i].prosrc = checkForQuote(PQgetvalue(res,i,i_prosrc));
+       finfo[i].probin = dupstr(PQgetvalue(res,i,i_probin));
+
+       finfo[i].prorettype = dupstr(PQgetvalue(res,i,i_prorettype));
+       finfo[i].retset = (strcmp(PQgetvalue(res,i,i_proretset),"t") == 0);
+       finfo[i].nargs = atoi(PQgetvalue(res,i,i_pronargs));
+       finfo[i].lang = (atoi(PQgetvalue(res,i,i_prolang)) == C_PROLANG_OID);
+
+       parseArgTypes(finfo[i].argtypes, PQgetvalue(res,i,i_proargtypes));
+
+       finfo[i].dumped = 0;
+    }
+
+    PQclear(res);
+    res = PQexec(g_conn, "end");
+    PQclear(res);
+
+    return finfo;
+
+}
+
+/*
+ * getTables
+ *    read all the user-defined tables (no indices, no catalogs)
+ * in the system catalogs return them in the TableInfo* structure
+ *
+ * numTables is set to the number of tables read in 
+ *    
+ *
+ */
+TableInfo*
+getTables(int *numTables)
+{
+    PGresult *res;
+    int ntups;
+    int i;
+    char query[MAXQUERYLEN];
+    TableInfo *tblinfo;
+    
+    int i_oid;
+    int i_relname;
+    int i_relarch;
+
+    /* find all the user-defined tables (no indices and no catalogs),
+     ordering by oid is important so that we always process the parent
+     tables before the child tables when traversing the tblinfo* 
+
+      we ignore tables that start with Xinv */
+
+    res = PQexec(g_conn, "begin");
+    if (!res || 
+       PQresultStatus(res) != PGRES_COMMAND_OK) {
+       fprintf(stderr,"BEGIN command failed\n");
+       exit_nicely(g_conn);
+    }
+    PQclear(res);
+
+    sprintf(query, 
+           "SELECT oid, relname, relarch from pg_class where relkind = 'r' and relname !~ '^pg_' and relname !~ '^Xinv' order by oid;");
+
+    res = PQexec(g_conn, query);
+    if (!res || 
+       PQresultStatus(res) != PGRES_TUPLES_OK) {
+       fprintf(stderr,"getTables(): SELECT failed");
+       exit_nicely(g_conn);
+    }
+
+    ntups = PQntuples(res);
+
+    *numTables = ntups;
+
+    tblinfo = (TableInfo*)malloc(ntups * sizeof(TableInfo));
+
+    i_oid = PQfnumber(res,"oid");
+    i_relname = PQfnumber(res,"relname");
+    i_relarch = PQfnumber(res,"relarch");
+
+    for (i=0;i<ntups;i++) {
+       tblinfo[i].oid = dupstr(PQgetvalue(res,i,i_oid));
+       tblinfo[i].relname = dupstr(PQgetvalue(res,i,i_relname));
+       tblinfo[i].relarch = dupstr(PQgetvalue(res,i,i_relarch));
+    }
+
+    PQclear(res);
+    res = PQexec(g_conn, "end");
+    PQclear(res);
+
+    return tblinfo;
+
+}
+
+/*
+ * getInherits
+ *    read all the inheritance information
+ * from the system catalogs return them in the InhInfo* structure
+ *
+ * numInherits is set to the number of tables read in 
+ *    
+ *
+ */
+InhInfo*
+getInherits(int *numInherits)
+{
+    PGresult *res;
+    int ntups;
+    int i;
+    char query[MAXQUERYLEN];
+    InhInfo *inhinfo;
+    
+    int i_inhrel;
+    int i_inhparent;
+
+    /* find all the inheritance information */
+    res = PQexec(g_conn, "begin");
+    if (!res || 
+       PQresultStatus(res) != PGRES_COMMAND_OK) {
+       fprintf(stderr,"BEGIN command failed\n");
+       exit_nicely(g_conn);
+    }
+    PQclear(res);
+
+    sprintf(query, "SELECT inhrel, inhparent from pg_inherits");
+
+    res = PQexec(g_conn, query);
+    if (!res || 
+       PQresultStatus(res) != PGRES_TUPLES_OK) {
+       fprintf(stderr,"getInherits(): SELECT failed");
+       exit_nicely(g_conn);
+    }
+    
+    ntups = PQntuples(res);
+
+    *numInherits = ntups;
+
+    inhinfo = (InhInfo*)malloc(ntups * sizeof(InhInfo));
+
+    i_inhrel = PQfnumber(res,"inhrel");
+    i_inhparent = PQfnumber(res,"inhparent");
+
+    for (i=0;i<ntups;i++) {
+       inhinfo[i].inhrel = dupstr(PQgetvalue(res,i,i_inhrel));
+       inhinfo[i].inhparent = dupstr(PQgetvalue(res,i,i_inhparent));
+    }
+
+    PQclear(res);
+    res = PQexec(g_conn, "end");
+    PQclear(res);
+    return inhinfo;
+}
+
+/*
+ * getTableAttrs -
+ *    for each table in tblinfo, read its attributes types and names
+ * 
+ * this is implemented in a very inefficient way right now, looping
+ * through the tblinfo and doing a join per table to find the attrs and their 
+ * types
+ *
+ *  modifies tblinfo
+ */
+void
+getTableAttrs(TableInfo* tblinfo, int numTables)
+{
+    int i,j;
+    char q[MAXQUERYLEN];
+    int i_attname;
+    int i_typname;
+    PGresult *res;
+    int ntups;
+
+    for (i=0;i<numTables;i++)  {
+
+       /* skip archive tables */
+       if (isArchiveName(tblinfo[i].relname))
+           continue;
+
+       /* find all the user attributes and their types*/
+       /* we must read the attribute names in attribute number order! */
+       /* because we will use the attnum to index into the attnames array 
+          later */
+if (g_verbose) 
+    fprintf(stderr,"%s finding the attrs and types for table: %s %s\n",
+           g_comment_start,
+           tblinfo[i].relname,
+           g_comment_end);
+
+       sprintf(q,"SELECT a.attnum, a.attname, t.typname from pg_attribute a, pg_type t where a.attrelid = '%s'::oid and a.atttypid = t.oid and a.attnum > 0 order by attnum",tblinfo[i].oid);
+       res = PQexec(g_conn, q);
+       if (!res || 
+           PQresultStatus(res) != PGRES_TUPLES_OK) {
+           fprintf(stderr,"getTableAttrs(): SELECT failed");
+           exit_nicely(g_conn);
+       }
+       
+       ntups = PQntuples(res);
+
+       i_attname = PQfnumber(res,"attname");
+       i_typname = PQfnumber(res,"typname");
+
+       tblinfo[i].numatts = ntups;
+       tblinfo[i].attnames = (char**) malloc( ntups * sizeof(char*));
+       tblinfo[i].typnames = (char**) malloc( ntups * sizeof(char*));
+       tblinfo[i].inhAttrs = (int*) malloc (ntups * sizeof(int));
+       tblinfo[i].parentRels = NULL;
+       tblinfo[i].numParents = 0;
+       for (j=0;j<ntups;j++) {
+           tblinfo[i].attnames[j] = dupstr(PQgetvalue(res,j,i_attname));
+           tblinfo[i].typnames[j] = dupstr(PQgetvalue(res,j,i_typname));
+           tblinfo[i].inhAttrs[j] = 0; /* this flag is set in flagInhAttrs()*/
+       }
+       PQclear(res);
+    } 
+}
+
+
+/*
+ * getIndices
+ *    read all the user-defined indices information
+ * from the system catalogs return them in the InhInfo* structure
+ *
+ * numIndices is set to the number of indices read in 
+ *    
+ *
+ */
+IndInfo*
+getIndices(int *numIndices)
+{
+    int i;
+    char query[MAXQUERYLEN];
+    PGresult *res;
+    int ntups;
+    IndInfo *indinfo;
+
+    int i_indexrelname;
+    int i_indrelname;
+    int i_indamname;
+    int i_indproc;
+    int i_indkey;
+    int i_indclassname;
+    
+    /* find all the user-defined indices.
+       We do not handle partial indices.
+       We also assume that only single key indices 
+
+       skip 'Xinx*' - indices on inversion objects
+
+       this is a 5-way join !!
+    */
+       
+    res = PQexec(g_conn, "begin");
+    if (!res || 
+       PQresultStatus(res) != PGRES_COMMAND_OK) {
+       fprintf(stderr,"BEGIN command failed\n");
+       exit_nicely(g_conn);
+    }
+    PQclear(res);
+
+    sprintf(query,
+           "SELECT t1.relname as indexrelname, t2.relname as indrelname, i.indproc, i.indkey[0], o.opcname as indclassname, a.amname as indamname from pg_index i, pg_class t1, pg_class t2, pg_opclass o, pg_am a where t1.oid = i.indexrelid and t2.oid = i.indrelid and o.oid = i.indclass[0] and t1.relam = a.oid and i.indexrelid > '%d'::oid and t2.relname !~ '^pg_' and t1.relname !~ '^Xinx' ;",
+           g_last_builtin_oid);
+
+    res = PQexec(g_conn, query);
+    if (!res || 
+       PQresultStatus(res) != PGRES_TUPLES_OK) {
+       fprintf(stderr,"getIndices(): SELECT failed");
+       exit_nicely(g_conn);
+    }
+
+    ntups = PQntuples(res);
+
+    *numIndices = ntups;
+
+    indinfo = (IndInfo*)malloc(ntups * sizeof (IndInfo));
+
+    i_indexrelname = PQfnumber(res,"indexrelname");
+    i_indrelname = PQfnumber(res,"indrelname");
+    i_indamname = PQfnumber(res,"indamname");
+    i_indproc = PQfnumber(res,"indproc");
+    i_indkey = PQfnumber(res,"indkey");
+    i_indclassname = PQfnumber(res,"indclassname");
+
+    for (i=0;i<ntups;i++) {
+       indinfo[i].indexrelname = dupstr(PQgetvalue(res,i,i_indexrelname));
+       indinfo[i].indrelname = dupstr(PQgetvalue(res,i,i_indrelname));
+       indinfo[i].indamname = dupstr(PQgetvalue(res,i,i_indamname));
+       indinfo[i].indproc = dupstr(PQgetvalue(res,i,i_indproc));
+       indinfo[i].indkey = dupstr(PQgetvalue(res,i,i_indkey));
+       indinfo[i].indclassname = dupstr(PQgetvalue(res,i,i_indclassname));
+    }
+    PQclear(res);
+    res = PQexec(g_conn,"end");
+
+    return indinfo;
+}
+
+/*
+ * dumpTypes
+ *    writes out to fout the queries to recreate all the user-defined types
+ *
+ */
+void
+dumpTypes(FILE* fout, FuncInfo* finfo, int numFuncs,
+         TypeInfo* tinfo, int numTypes)
+{
+    int i;
+    char q[MAXQUERYLEN];
+    int funcInd;
+
+    for (i=0;i<numTypes;i++) {
+
+       /* skip all the builtin types */
+       if (atoi(tinfo[i].oid) < g_last_builtin_oid)
+           continue;
+
+       /* skip relation types */
+       if (atoi(tinfo[i].typrelid) != 0)
+           continue;
+
+       /* skip all array types that start w/ underscore */
+       if ( (tinfo[i].typname[0] == '_') &&
+            (strcmp(tinfo[i].typinput, "array_in") == 0))
+           continue;
+
+       /* before we create a type, we need to create the input and
+          output functions for it, if they haven't been created already */
+       funcInd = findFuncByName(finfo, numFuncs, tinfo[i].typinput);
+       if (funcInd !=  -1) 
+           dumpOneFunc(fout,finfo,funcInd,tinfo,numTypes);
+
+       funcInd = findFuncByName(finfo, numFuncs, tinfo[i].typoutput);
+       if (funcInd !=  -1) 
+           dumpOneFunc(fout,finfo,funcInd,tinfo,numTypes);
+
+       sprintf(q,
+               "CREATE TYPE %s ( internallength = %s, externallength = %s, input = %s, output = %s, send = %s, receive = %s, default = '%s'",
+               tinfo[i].typname,
+               tinfo[i].typlen,
+               tinfo[i].typprtlen,
+               tinfo[i].typinput,
+               tinfo[i].typoutput,
+               tinfo[i].typsend,
+               tinfo[i].typreceive,
+               tinfo[i].typdefault);
+
+       if (tinfo[i].isArray) {
+           char* elemType;
+
+           elemType = findTypeByOid(tinfo, numTypes, tinfo[i].typelem);
+           
+           sprintf(q,"%s, element = %s, delimiter = '%s'",
+                   q, elemType,tinfo[i].typdelim);
+       }
+       if (tinfo[i].passedbyvalue)
+           strcat(q,",passedbyvalue);\n");
+       else
+           strcat(q,");\n");
+           
+       fputs(q,fout);
+    }
+}
+
+/*
+ * dumpFuncs
+ *    writes out to fout the queries to recreate all the user-defined functions
+ *
+ */
+void
+dumpFuncs(FILE* fout, FuncInfo* finfo, int numFuncs, 
+         TypeInfo *tinfo, int numTypes)
+{
+    int i;
+    for (i=0;i<numFuncs;i++)  {
+       dumpOneFunc(fout,finfo,i,tinfo,numTypes);
+    }
+}
+
+/*
+ * dumpOneFunc:
+ *    dump out only one function,  the index of which is given in the third
+ *  argument
+ *
+ */
+
+void
+dumpOneFunc(FILE* fout, FuncInfo* finfo, int i,
+           TypeInfo *tinfo, int numTypes)
+{
+    char q[MAXQUERYLEN];
+    int j;
+    
+    if (finfo[i].dumped)
+       return;
+    else
+       finfo[i].dumped = 1;
+
+    sprintf(q,"CREATE FUNCTION %s (",finfo[i].proname);
+    for (j=0;j<finfo[i].nargs;j++) {
+       char* typname;
+       typname = findTypeByOid(tinfo, numTypes, finfo[i].argtypes[j]);
+       sprintf(q, "%s%s%s",
+               q,  
+               (j > 0) ? "," : "",
+               typname);
+    }
+    sprintf(q,"%s ) RETURNS %s%s AS '%s' LANGUAGE '%s';\n",
+               q, 
+           finfo[i].retset ? " SETOF " : "",
+           findTypeByOid(tinfo, numTypes, finfo[i].prorettype),
+           (finfo[i].lang) ? finfo[i].probin : finfo[i].prosrc,
+           (finfo[i].lang) ? "C" : "SQL");
+    
+    fputs(q,fout);
+
+}
+
+/*
+ * dumpOprs
+ *    writes out to fout the queries to recreate all the user-defined operators
+ *
+ */
+void 
+dumpOprs(FILE* fout, OprInfo* oprinfo, int numOperators,
+        TypeInfo *tinfo, int numTypes)
+{
+    int i;
+    char q[MAXQUERYLEN];
+    char leftarg[MAXQUERYLEN];
+    char rightarg[MAXQUERYLEN];
+    char commutator[MAXQUERYLEN];
+    char negator[MAXQUERYLEN];
+    char restrict[MAXQUERYLEN];
+    char join[MAXQUERYLEN];
+    char sortop[MAXQUERYLEN];
+
+    for (i=0;i<numOperators;i++) {
+
+       /* skip all the builtin oids */
+       if (atoi(oprinfo[i].oid) < g_last_builtin_oid)
+           continue;
+
+       /* some operator are invalid because they were the result
+          of user defining operators before commutators exist */
+       if (strcmp(oprinfo[i].oprcode, "-") == 0)
+           continue;
+
+       leftarg[0] = '\0';
+       rightarg[0] = '\0';
+       /* right unary means there's a left arg
+          and left unary means there's a right arg */
+       if (strcmp(oprinfo[i].oprkind, "r") == 0 || 
+           strcmp(oprinfo[i].oprkind, "b") == 0 ) {
+           sprintf(leftarg, ", LEFTARG = %s ",
+                   findTypeByOid(tinfo, numTypes, oprinfo[i].oprleft));
+       } 
+       if (strcmp(oprinfo[i].oprkind, "l") == 0 || 
+           strcmp(oprinfo[i].oprkind, "b") == 0 ) {
+           sprintf(rightarg, ", RIGHTARG = %s ",
+                   findTypeByOid(tinfo, numTypes, oprinfo[i].oprright));
+       }
+       if (strcmp(oprinfo[i].oprcom, "0") == 0) 
+           commutator[0] = '\0';
+       else
+           sprintf(commutator,", COMMUTATOR = %s ",
+                   findOprByOid(oprinfo, numOperators, oprinfo[i].oprcom));
+
+       if (strcmp(oprinfo[i].oprnegate, "0") == 0) 
+           negator[0] = '\0';
+       else
+           sprintf(negator,", NEGATOR = %s ",
+                   findOprByOid(oprinfo, numOperators, oprinfo[i].oprnegate));
+
+       if (strcmp(oprinfo[i].oprrest, "-") == 0)
+           restrict[0] = '\0';
+       else
+           sprintf(restrict,", RESTRICT = %s ", oprinfo[i].oprrest);
+                   
+       if (strcmp(oprinfo[i].oprjoin,"-") == 0)
+           join[0] = '\0';
+       else
+           sprintf(join,", JOIN = %s ", oprinfo[i].oprjoin);
+                   
+       if (strcmp(oprinfo[i].oprlsortop, "0") == 0) 
+           sortop[0] = '\0';
+       else
+           {
+           sprintf(sortop,", SORT = %s ",
+                   findOprByOid(oprinfo, numOperators,
+                                oprinfo[i].oprlsortop));
+           if (strcmp(oprinfo[i].oprrsortop, "0") != 0)
+               sprintf(sortop, "%s , %s", sortop, 
+                       findOprByOid(oprinfo, numOperators,
+                                    oprinfo[i].oprlsortop));
+       }
+
+       sprintf(q,
+               "CREATE OPERATOR %s (PROCEDURE = %s %s %s %s %s %s %s %s %s);\n ",
+               oprinfo[i].oprname,
+               oprinfo[i].oprcode,
+               leftarg,
+               rightarg,
+               commutator,
+               negator,
+               restrict,
+               (strcmp(oprinfo[i].oprcanhash, "t")) ? ", HASHES" : "",
+                join,
+               sortop);
+
+       fputs(q,fout);
+    }
+}
+
+/*
+ * dumpAggs
+ *    writes out to fout the queries to create all the user-defined aggregates
+ *
+ */
+void
+dumpAggs(FILE* fout, AggInfo* agginfo, int numAggs,
+       TypeInfo *tinfo, int numTypes)
+{
+    int i;
+    char q[MAXQUERYLEN];
+    char sfunc1[MAXQUERYLEN];
+    char sfunc2[MAXQUERYLEN];
+    char finalfunc[MAXQUERYLEN];
+    char comma1[2], comma2[2];
+
+    for (i=0;i<numAggs;i++) {
+       /* skip all the builtin oids */
+       if (atoi(agginfo[i].oid) < g_last_builtin_oid)
+           continue;
+
+       if ( strcmp(agginfo[i].aggtransfn1, "-") == 0) 
+           sfunc1[0] = '\0';
+       else {
+           sprintf(sfunc1, 
+                   "SFUNC1 = %s, BASETYPE = %s, STYPE1 = %s",
+                   agginfo[i].aggtransfn1,
+                   findTypeByOid(tinfo,numTypes,agginfo[i].aggbasetype),
+                   findTypeByOid(tinfo,numTypes,agginfo[i].aggtranstype1));
+           if (agginfo[i].agginitval1)
+               sprintf(sfunc1, "%s ,INITCOND1 = '%s'",
+                       sfunc1, agginfo[i].agginitval1);
+           
+       }
+
+       if ( strcmp(agginfo[i].aggtransfn2, "-") == 0) 
+           sfunc2[0] = '\0';
+       else {
+           sprintf(sfunc2, 
+                   "SFUNC2 = %s, STYPE2 = %s",
+                   agginfo[i].aggtransfn2,
+                   findTypeByOid(tinfo,numTypes,agginfo[i].aggtranstype2));
+           if (agginfo[i].agginitval2)
+               sprintf(sfunc2,"%s ,INITCOND2 = '%s'",
+                       sfunc2, agginfo[i].agginitval2);
+       }
+       
+       if ( strcmp(agginfo[i].aggfinalfn, "-") == 0)
+           finalfunc[0] = '\0';
+       else {
+           sprintf(finalfunc, "FINALFUNC = %s", agginfo[i].aggfinalfn);
+       }
+       if (sfunc1[0] != '\0' && sfunc2[0] != '\0') {
+           comma1[0] = ','; comma1[1] = '\0';
+       } else
+           comma1[0] = '\0';
+
+       if (finalfunc[0] != '\0' && (sfunc1[0] != '\0' || sfunc2[0] != '\0')) {
+           comma2[0] = ',';comma2[1] = '\0';
+       } else
+           comma2[0] = '\0';
+
+       sprintf(q,"CREATE AGGREGATE %s ( %s %s %s %s %s );\n",
+               agginfo[i].aggname,
+               sfunc1,
+               comma1,
+               sfunc2,
+               comma2,
+               finalfunc);
+
+       fputs(q,fout);
+    }
+}
+
+/*
+ * dumpTables:
+ *    write out to fout all the user-define tables
+ */
+void
+dumpTables(FILE* fout, TableInfo *tblinfo, int numTables,
+          InhInfo *inhinfo, int numInherits,
+          TypeInfo *tinfo, int numTypes)
+{
+    int i,j,k;
+    char q[MAXQUERYLEN];
+    char **parentRels;  /* list of names of parent relations */
+    int numParents;
+    int actual_atts; /* number of attrs in this CREATE statment */
+    char *archiveMode;
+
+    for (i=0;i<numTables;i++) {
+
+       /* skip archive names*/
+       if (isArchiveName(tblinfo[i].relname))
+           continue;
+
+       parentRels = tblinfo[i].parentRels;
+       numParents = tblinfo[i].numParents;
+
+       sprintf(q, "CREATE TABLE %s (", tblinfo[i].relname);
+       actual_atts = 0;
+       for (j=0;j<tblinfo[i].numatts;j++) {
+           if (tblinfo[i].inhAttrs[j] == 0) {
+               sprintf(q, "%s%s%s %s",
+                       q,
+                       (actual_atts > 0) ? ", " : "",
+                       tblinfo[i].attnames[j],
+                       tblinfo[i].typnames[j]);
+               actual_atts++;
+           }
+       }
+
+       strcat(q,")");
+
+       if (numParents > 0) {
+           sprintf(q, "%s inherits ( ",q);
+           for (k=0;k<numParents;k++){
+               sprintf(q, "%s%s%s",
+                       q,
+                       (k>0) ? ", " : "",
+                       parentRels[k]);
+           }
+           strcat(q,")");
+       }
+
+       switch(tblinfo[i].relarch[0]) {
+       case 'n':
+           archiveMode = "none";
+           break;
+       case 'h':
+           archiveMode = "heavy";
+           break;
+       case 'l':
+           archiveMode = "light";
+           break;
+       default:
+           fprintf(stderr, "unknown archive mode\n");
+           archiveMode = "none";
+           break;
+       }
+           
+       sprintf(q, "%s archive = %s;\n",
+               q,
+               archiveMode);
+       fputs(q,fout);
+    }
+}
+
+/*
+ * dumpIndices:
+ *    write out to fout all the user-define indices
+ */
+void 
+dumpIndices(FILE* fout, IndInfo* indinfo, int numIndices,
+           TableInfo* tblinfo, int numTables)
+{
+    int i;
+    int tableInd;
+    char *attname;  /* the name of the indexed attribute  */
+    char *funcname; /* the name of the function to comput the index key from*/
+    int indkey;
+
+    char q[MAXQUERYLEN];
+    PGresult *res;
+
+    for (i=0;i<numIndices;i++) {
+       tableInd = findTableByName(tblinfo, numTables,
+                                  indinfo[i].indrelname);
+       indkey = atoi(indinfo[i].indkey) - 1; 
+       attname = tblinfo[tableInd].attnames[indkey];
+       if (strcmp(indinfo[i].indproc,"0") == 0) {
+           funcname = NULL;
+       } else {
+           /* the funcname is an oid which we use to 
+              find the name of the pg_proc.  We need to do this
+              because getFuncs() only reads in the user-defined funcs
+              not all the funcs.  We might not find what we want
+              by looking in FuncInfo**/
+           sprintf(q,
+                   "SELECT proname from pg_proc where pg_proc.oid = '%s'::oid",
+                   indinfo[i].indproc);
+           res = PQexec(g_conn, q);
+           funcname = dupstr(PQgetvalue(res, 0,
+                                        PQfnumber(res,"proname")));
+           PQclear(res);
+       }
+       sprintf(q,"CREATE INDEX %s on %s using %s (",
+               indinfo[i].indexrelname,
+               indinfo[i].indrelname,
+               indinfo[i].indamname);
+       if (funcname) {
+           sprintf(q, "%s %s(%s) %s);\n",
+                   q,funcname, attname, indinfo[i].indclassname);
+           free(funcname); 
+       } else
+           sprintf(q, "%s %s %s);\n",
+                   q,attname,indinfo[i].indclassname);
+
+       fputs(q,fout);
+    }
+
+}
+
+
+
+
+
+/*
+ * DumpClasses -
+ *    dump the contents of all the classes.
+ */
+void
+dumpClasses(TableInfo *tblinfo, int numTables, FILE *fout)
+{
+    char query[255];
+#define COPYBUFSIZ     8192
+    char copybuf[COPYBUFSIZ];
+    PGresult *res;
+    int i;
+    int ret;
+    int copydone;
+
+    for(i = 0; i < numTables; i++) {
+       char *classname = tblinfo[i].relname;
+
+       /* skip archive names*/
+       if (isArchiveName(classname))
+           continue;
+
+       fprintf(fout, "COPY %s from stdin;\n", classname);
+       sprintf(query, "COPY %s to stdout;\n", classname);
+       res = PQexec(g_conn, query);
+       if (!res || 
+           PQresultStatus(res) != PGRES_COPY_OUT) {
+           fprintf(stderr,"dumpClasses(): COPY to stdout failed");
+           exit_nicely(g_conn);
+       }
+       copydone = 0;
+       while (!copydone) {
+           ret = PQgetline(res->conn, copybuf, COPYBUFSIZ);
+       
+           if (copybuf[0] == '.' && copybuf[1] =='\0') {
+               copydone = true;        /* don't print this... */
+           } else {
+               fputs(copybuf, stdout);
+               switch (ret) {
+               case EOF:
+                   copydone = true;
+                   /*FALLTHROUGH*/
+               case 0:
+                   fputc('\n', stdout);
+                   break;
+               case 1:
+                   break;
+               }
+           }
+       }
+       fprintf(fout, ".\n");
+       PQclear(res);
+       PQendcopy(res->conn);
+    }
+    
+}
+
+/*
+ * dumpTuples --
+ *    prints out the tuples in ASCII representation. The output is a valid
+ *    input to COPY FROM stdin.
+ *
+ *    We only need to do this for POSTGRES 4.2 databases since the
+ *    COPY TO statment doesn't escape newlines properly. It's been fixed
+ *    in Postgres95.
+ * 
+ * the attrmap passed in tells how to map the attributes copied in to the
+ * attributes copied out
+ */
+void
+dumpTuples(PGresult *res, FILE *fout, int* attrmap)
+{
+    int j, k;
+    int m, n;
+    char **outVals = NULL; /* values to copy out */
+
+    n = PQntuples(res);
+    m = PQnfields(res);
+    
+    if ( m > 0 ) {
+       /*
+        * Print out the tuples but only print tuples with at least
+        * 1 field.
+        */
+       outVals = (char**)malloc(m * sizeof(char*));
+
+       for (j = 0; j < n; j++) {
+           for (k = 0; k < m; k++) {
+               outVals[attrmap[k]] = PQgetvalue(res, j, k);
+           }
+           for (k = 0; k < m; k++) {
+               char *pval = outVals[k];
+
+               if (k!=0)
+                   fputc('\t', fout);  /* delimiter for attribute */
+
+               if (pval) {
+                   while (*pval != '\0') {
+                       /* escape tabs, newlines and backslashes */
+                       if (*pval=='\t' || *pval=='\n' || *pval=='\\')
+                           fputc('\\', fout);
+                       fputc(*pval, fout);
+                       pval++;
+                   }
+               }
+           }
+           fputc('\n', fout);  /* delimiter for a tuple */
+       }
+       free (outVals);
+    }
+}
+
+
+
+/*
+ * findLastBuiltInOid -
+ * find the last built in oid 
+ * we do this by looking up the oid of 'template1' in pg_database,
+ * this is probably not foolproof but comes close 
+*/
+
+int
+findLastBuiltinOid()
+{
+       PGresult* res;
+       int ntups;
+       int last_oid;
+
+       res = PQexec(g_conn, 
+                    "SELECT oid from pg_database where datname = 'template1';");
+       if (res == NULL ||
+           PQresultStatus(res) != PGRES_TUPLES_OK) {
+           fprintf(stderr,"pg_dump error in finding the template1 database");
+           exit_nicely(g_conn);
+       }
+       ntups = PQntuples(res);
+       if (ntups != 1) {
+           fprintf(stderr,"pg_dump: couldn't find the template1 database.  You are really hosed\nGiving up\n");
+           exit_nicely(g_conn);
+       }
+       last_oid = atoi(PQgetvalue(res, 0, PQfnumber(res, "oid")));
+       PQclear(res);
+       return last_oid;
+}
+
+
+/*
+ * checkForQuote:
+ *    checks a string for quote characters and quotes them
+ */
+char*
+checkForQuote(char* s)
+{
+    char *r;
+    char c;
+    char *result;
+
+    int j = 0;
+
+    r = malloc(strlen(s)*3 + 1);  /* definitely long enough */
+
+    while ( (c = *s) != '\0') {
+
+       if (c == '\'') {
+           r[j++] = '\''; /* quote the single quotes */
+       }
+       r[j++] = c;
+       s++;
+    }
+    r[j] = '\0';
+
+    result = dupstr(r);
+    free(r);
+
+    return result;
+    
+}
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
new file mode 100644 (file)
index 0000000..9be8066
--- /dev/null
@@ -0,0 +1,195 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_dump.h
+ *    header file for the pg_dump utility
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+
+/* The *Info data structures run-time C structures used to store
+   system catalog information */
+   
+typedef struct _typeInfo {
+    char* oid;
+    char* typowner;
+    char* typname;
+    char* typlen;
+    char* typprtlen;
+    char* typinput;
+    char* typoutput;
+    char* typreceive;
+    char* typsend;
+    char* typelem;
+    char* typdelim;
+    char* typdefault;
+    char* typrelid;
+    int passedbyvalue;
+    int isArray;
+} TypeInfo;
+
+typedef struct _funcInfo {
+    char* oid;
+    char* proname;
+    char* proowner;
+    int lang;  /* 1 if C, else SQL */
+    int nargs;
+    char* argtypes[8];  /* should be derived from obj/fmgr.h instead of hardwired*/
+    char* prorettype;
+    int retset; /* 1 if the function returns a set, 0 otherwise */
+    char* prosrc;
+    char* probin;
+    int dumped; /* 1 if already dumped */
+} FuncInfo;
+
+typedef struct _tableInfo {
+    char *oid;
+    char *relname;
+    char *relarch;
+    int numatts;            /* number of attributes */
+    int *inhAttrs;          /* an array of flags, one for each attribute
+                             if the value is 1, then this attribute is
+                             an inherited attribute */
+    char **attnames;        /* the attribute names */
+    char **typnames;        /* fill out attributes */
+    int numParents;         /* number of (immediate) parent supertables */
+    char **parentRels;      /* names of parent relations, NULL
+                              if numParents == 0 */
+    char **out_attnames;    /* the attribute names, in the order they would
+                              be in, when the table is created in the
+                              target query language.
+                              this is needed because the SQL tables will
+                              not have the same order of attributes as
+                              the POSTQUEL tables */
+           
+} TableInfo;
+
+typedef struct _inhInfo {
+    char *oid;
+    char *inhrel;
+    char *inhparent;
+} InhInfo;
+
+typedef struct _indInfo {
+    char *indexrelname;  /* name of the secondary index class */
+    char *indrelname;    /* name of the indexed heap class */
+    char *indamname;     /* name of the access method (e.g. btree, rtree, etc.) */
+    char *indproc;       /* oid of the function to compute the index, 0 if none*/
+    char *indkey;        /* attribute number of the key attribute */
+    char *indclassname;  /* name of the opclass of the key */
+} IndInfo;
+
+typedef struct _aggInfo {
+    char *oid;
+    char *aggname;
+    char *aggtransfn1;
+    char *aggtransfn2;
+    char *aggfinalfn;
+    char *aggtranstype1;
+    char *aggbasetype;
+    char *aggtranstype2;
+    char *agginitval1;
+    char *agginitval2;
+} AggInfo;
+
+typedef struct _oprInfo {
+    char *oid;
+    char *oprname;
+    char *oprkind;   /* "b" = binary, "l" = left unary, "r" = right unary */
+    char *oprcode;   /* operator function name */
+    char *oprleft;   /* left operand type */
+    char *oprright;  /* right operand type */
+    char *oprcom;    /* oid of the commutator operator */
+    char *oprnegate; /* oid of the negator operator */
+    char *oprrest;   /* name of the function to calculate operator restriction
+                       selectivity */
+    char *oprjoin;    /* name of the function to calculate operator join
+                        selectivity */
+    char *oprcanhash; /* can we use hash join strategy ? */
+    char *oprlsortop; /* oid's of the left and right sort operators */
+    char *oprrsortop;
+} OprInfo;
+
+
+/* global decls */
+extern int g_verbose;  /* verbose flag */
+extern int g_last_builtin_oid; /* value of the last builtin oid */
+extern FILE *g_fout;     /* the script file */
+
+/* placeholders for comment starting and ending delimiters */
+extern char g_comment_start[10]; 
+extern char g_comment_end[10]; 
+
+extern char g_opaque_type[10]; /* name for the opaque type */
+
+/* pg_dump is really two programs in one
+    one version works with postgres v4r2
+    and the other works with postgres95
+    the common routines are declared here
+*/
+/*
+ *  common utility functions 
+*/
+
+extern TableInfo* dumpSchema(FILE* fout, int *numTablesPtr);
+
+extern char* findTypeByOid(TypeInfo* tinfo, int numTypes, char* oid);
+extern char* findOprByOid(OprInfo *oprinfo, int numOprs, char *oid);
+extern int findFuncByName(FuncInfo* finfo, int numFuncs, char* name);
+extern char** findParentsByOid(TableInfo* tbinfo, int numTables,
+                             InhInfo* inhinfo, int numInherits,
+                             char *oid, 
+                             int *numParents);
+extern int findTableByName(TableInfo *tbinfo, int numTables, char *relname);
+extern int findTableByOid(TableInfo *tbinfo, int numTables, char *oid);
+extern void flagInhAttrs(TableInfo* tbinfo, int numTables,
+                          InhInfo* inhinfo, int numInherits);
+
+extern void check_conn_and_db();
+extern char* dupstr(char *s);
+extern int strInArray(char* pattern, char** arr, int arr_size);
+extern void parseArgTypes(char **argtypes, char* str);
+extern int isArchiveName(char*);
+
+/*
+ * version specific routines 
+ */
+extern TypeInfo* getTypes(int *numTypes);
+extern FuncInfo* getFuncs(int *numFuncs);
+extern AggInfo* getAggregates(int *numAggregates);
+extern OprInfo* getOperators(int *numOperators);
+extern TableInfo* getTables(int *numTables);
+extern InhInfo* getInherits(int *numInherits);
+extern void getTableAttrs(TableInfo* tbinfo, int numTables);
+extern IndInfo* getIndices(int *numIndices);
+extern void dumpTypes(FILE* fout, FuncInfo* finfo, int numFuncs,
+                     TypeInfo* tinfo, int numTypes);
+extern void dumpFuncs(FILE* fout, FuncInfo* finfo, int numFuncs,
+                     TypeInfo *tinfo, int numTypes);
+extern void dumpAggs(FILE* fout, AggInfo* agginfo, int numAggregates,
+                    TypeInfo *tinfo, int numTypes);
+extern void dumpOprs(FILE* fout, OprInfo* agginfo, int numOperators,
+                    TypeInfo *tinfo, int numTypes);
+extern void dumpOneFunc(FILE* fout, FuncInfo* finfo, int i,
+                       TypeInfo *tinfo, int numTypes);
+extern void dumpTables(FILE* fout, TableInfo* tbinfo, int numTables,
+                      InhInfo *inhinfo, int numInherits,
+                      TypeInfo *tinfo, int numTypes);
+extern void dumpIndices(FILE* fout, IndInfo* indinfo, int numIndices,
+                       TableInfo* tbinfo, int numTables);
+
+extern void dumpClasses(TableInfo *tbinfo, int numTables, FILE *fout);
+extern void dumpTuples(PGresult *res, FILE *fout, int *attrmap);
+extern char* checkForQuote(char* s);
+extern int findLastBuiltinOid();
+
+
+/* largest query string size */
+#define MAXQUERYLEN  5000
+
+/* these voodoo constants are from the backend */
+#define C_PROLANG_OID       13
diff --git a/src/bin/pg_id/Makefile b/src/bin/pg_id/Makefile
new file mode 100644 (file)
index 0000000..c544693
--- /dev/null
@@ -0,0 +1,23 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for bin/pg_id
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+PROG=  pg_id
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+include ../Makefile.global
+
+SRCS=  pg_id.c
+
+include $(MKDIR)/postgres.prog.mk
+
diff --git a/src/bin/pg_id/pg_id.c b/src/bin/pg_id/pg_id.c
new file mode 100644 (file)
index 0000000..0a7eb4f
--- /dev/null
@@ -0,0 +1,52 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_id.c--
+ *    Print the user ID for the login name passed as argument,
+ *    or the real user ID of the caller if no argument.  If the
+ *    login name doesn't exist, print "NOUSER" and exit 1.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <sys/types.h>
+#include <pwd.h>
+#include <stdio.h>
+
+int
+main(int argc, char **argv)
+{
+    struct passwd *pw;
+    int ch;
+    extern int optind;
+
+    while ((ch = getopt(argc, argv, "")) != EOF)
+       switch (ch) {
+       case '?':
+       default:
+           fprintf(stderr, "usage: pg_id [login]\n");
+           exit(1);
+       }
+    argc -= optind;
+    argv += optind;
+
+    if (argc > 0) {
+       if (argc > 1) {
+           fprintf(stderr, "usage: pg_id [login]\n");
+           exit(1);
+       }
+       if ((pw = getpwnam(argv[0])) == NULL) {
+           printf("NOUSER\n");
+           exit(1);
+       }
+       printf("%d\n", pw->pw_uid);
+    } else {
+       printf("%d\n", getuid());
+    }
+
+    exit(0);
+}
diff --git a/src/bin/pg_version/Makefile b/src/bin/pg_version/Makefile
new file mode 100644 (file)
index 0000000..da50874
--- /dev/null
@@ -0,0 +1,26 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for bin/pg_version
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+PROG=  pg_version
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+include ../Makefile.global
+
+VPATH:=$(VPATH):$(srcdir)/backend/utils/init
+SRCS=  pg_version.c magic.c
+
+CFLAGS+= -I$(srcdir)/backend/port/$(PORTNAME)
+
+include $(MKDIR)/postgres.prog.mk
+
diff --git a/src/bin/pg_version/pg_version.c b/src/bin/pg_version/pg_version.c
new file mode 100644 (file)
index 0000000..9fc29c7
--- /dev/null
@@ -0,0 +1,35 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_version.c--
+ *    
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+
+int Noversion = 0;
+char *DataDir = (char *) NULL;
+
+int
+main(int argc, char **argv)
+{
+    if (argc < 2) {
+       fprintf(stderr, "pg_version: missing argument\n");
+       exit(1);
+    }
+    SetPgVersion(argv[1]);
+    exit(0);
+}
+
+elog() {}
+
+GetDataHome()
+{
+       return(NULL);
+}
diff --git a/src/bin/pgtclsh/Makefile b/src/bin/pgtclsh/Makefile
new file mode 100644 (file)
index 0000000..d460b93
--- /dev/null
@@ -0,0 +1,46 @@
+#-------------------------------------------------------------------------
+#
+# Makefile
+#    Makefile for a tclsh workalike with pgtcl commands installed
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+include ../Makefile.global
+
+CFLAGS+=  -I$(TCL_INCDIR) -I$(TK_INCDIR)
+
+# try to find libpgtcl.a in either directory
+LIBPGTCL= -L$(srcdir)/libpgtcl/$(objdir) -L$(LIBDIR) -lpgtcl
+
+pgtclsh: $(objdir)/pgtclAppInit.o
+       $(CC) $(CDEBUG) -o $(objdir)/$(@F) $(objdir)/pgtclAppInit.o\
+       $(LIBPGTCL) $(LIBPQ) -L$(TCL_LIBDIR) $(TCL_LIB) -lm $(LD_ADD)
+
+pgtksh: $(objdir)/pgtkAppInit.o
+       $(CC) $(CDEBUG) -o $(objdir)/$(@F) $(objdir)/pgtkAppInit.o \
+       $(LIBPGTCL) $(LIBPQ) -L$(TCL_LIBDIR) -L$(TK_LIBDIR) \
+       $(TK_LIB) $(TCL_LIB) -lX11 -lm $(LD_ADD)
+
+install::      localobj pgtclsh pgtksh
+       $(INSTALL) $(INSTL_EXE_OPTS) $(objdir)/pgtclsh $(DESTDIR)$(BINDIR)/pgtclsh
+       $(INSTALL) $(INSTL_EXE_OPTS) $(objdir)/pgtksh $(DESTDIR)$(BINDIR)/pgtksh
+
+CLEANFILES = pgtclAppInit.o pgtkAppInit.o pgtclsh pgtksh
+
+PROG=pgtclsh
+
+all:: pgtclsh pgtksh
+
+# don't use the default template for generating executables since we have
+# two executable targets
+# include $(MKDIR)/postgres.prog.mk
+
+
diff --git a/src/bin/pgtclsh/README b/src/bin/pgtclsh/README
new file mode 100644 (file)
index 0000000..bbd89e0
--- /dev/null
@@ -0,0 +1,21 @@
+pgtclsh is an example of a tclsh extended with the new Tcl
+commands provided by the libpgtcl library.  By using pgtclsh, one can
+write front-end applications to Postgres95 in Tcl without having to
+deal with any libpq programming at all.  
+
+The pgtclsh is an enhanced version of tclsh.  Similarly, pgtksh is a
+wish replacement with postgres95 bindings. The Makefile is also set up
+so that you can choose "pgtksh" as a target.   
+
+pgtclsh has been tested with the official releases of
+       Tcl version 7.4 
+and    Tk  version 4.0 
+
+and will probably not work with versions older than those (including
+earlier beta releases). 
+
+For details of the libpgtcl interface, please see the file
+src/doc/libpgtcl.doc. 
+
+If you have any questions or bug reports, please send them to
+Jolly Chen at [email protected].
diff --git a/src/bin/pgtclsh/pgtclAppInit.c b/src/bin/pgtclsh/pgtclAppInit.c
new file mode 100644 (file)
index 0000000..cc38ca3
--- /dev/null
@@ -0,0 +1,114 @@
+/* 
+ * pgtclAppInit.c --
+ *
+ *     a skeletal Tcl_AppInit that provides pgtcl initialization
+ *    to create a tclsh that can talk to pglite backends
+ *
+ * Copyright (c) 1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#) tclAppInit.c 1.11 94/12/17 16:14:03";
+#endif /* not lint */
+
+#include "tcl.h"
+
+#include "libpgtcl.h"
+
+/*
+ * The following variable is a special hack that is needed in order for
+ * Sun shared libraries to be used for Tcl.
+ */
+
+#ifdef NEED_MATHERR
+extern int matherr();
+int *tclDummyMathPtr = (int *) matherr;
+#endif
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * main --
+ *
+ *     This is the main program for the application.
+ *
+ * Results:
+ *     None: Tcl_Main never returns here, so this procedure never
+ *     returns either.
+ *
+ * Side effects:
+ *     Whatever the application does.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+main(argc, argv)
+    int argc;                  /* Number of command-line arguments. */
+    char **argv;               /* Values of command-line arguments. */
+{
+    Tcl_Main(argc, argv, Tcl_AppInit);
+    return 0;                  /* Needed only to prevent compiler warning. */
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_AppInit --
+ *
+ *     This procedure performs application-specific initialization.
+ *     Most applications, especially those that incorporate additional
+ *     packages, will have their own version of this procedure.
+ *
+ * Results:
+ *     Returns a standard Tcl completion code, and leaves an error
+ *     message in interp->result if an error occurs.
+ *
+ * Side effects:
+ *     Depends on the startup script.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_AppInit(interp)
+    Tcl_Interp *interp;                /* Interpreter for application. */
+{
+    if (Tcl_Init(interp) == TCL_ERROR) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Call the init procedures for included packages.  Each call should
+     * look like this:
+     *
+     * if (Mod_Init(interp) == TCL_ERROR) {
+     *     return TCL_ERROR;
+     * }
+     *
+     * where "Mod" is the name of the module.
+     */
+
+    if (Pg_Init(interp) == TCL_ERROR) { 
+      return TCL_ERROR;
+    }
+
+    /*
+     * Call Tcl_CreateCommand for application-specific commands, if
+     * they weren't already created by the init procedures called above.
+     */
+
+    /*
+     * Specify a user-specific startup file to invoke if the application
+     * is run interactively.  Typically the startup file is "~/.apprc"
+     * where "app" is the name of the application.  If this line is deleted
+     * then no user-specific startup file will be run under any conditions.
+     */
+
+    tcl_RcFileName = "~/.tclshrc";
+    return TCL_OK;
+}
diff --git a/src/bin/pgtclsh/pgtclUtils.tcl b/src/bin/pgtclsh/pgtclUtils.tcl
new file mode 100644 (file)
index 0000000..dff87a4
--- /dev/null
@@ -0,0 +1,16 @@
+# getDBs :
+#   get the names of all the databases at a given host and port number
+#   with the defaults being the localhost and port 5432
+#   return them in alphabetical order
+proc getDBs { {host "localhost"} {port "5432"} } {
+    # datnames is the list to be result
+    set conn [pg_connect template1 -host $host -port $port]
+    set res [pg_exec $conn "SELECT datname FROM pg_database ORDER BY datname"]
+    set ntups [pg_result $res -numTuples]
+    for {set i 0} {$i < $ntups} {incr i} {
+       lappend datnames [pg_result $res -getTuple $i]
+    }
+    pg_disconnect $conn
+    return $datnames
+}
+
diff --git a/src/bin/pgtclsh/pgtkAppInit.c b/src/bin/pgtclsh/pgtkAppInit.c
new file mode 100644 (file)
index 0000000..3376345
--- /dev/null
@@ -0,0 +1,117 @@
+/* 
+ * pgtkAppInit.c --
+ *
+ *     a skeletal Tcl_AppInit that provides pgtcl initialization
+ *    to create a tclsh that can talk to pglite backends
+ *
+ * Copyright (c) 1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#) tkAppInit.c 1.12 94/12/17 16:30:56";
+#endif /* not lint */
+
+#include "tk.h"
+#include "libpgtcl.h"
+
+/*
+ * The following variable is a special hack that is needed in order for
+ * Sun shared libraries to be used for Tcl.
+ */
+
+#ifdef NEED_MATHERR
+extern int matherr();
+int *tclDummyMathPtr = (int *) matherr;
+#endif
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * main --
+ *
+ *     This is the main program for the application.
+ *
+ * Results:
+ *     None: Tk_Main never returns here, so this procedure never
+ *     returns either.
+ *
+ * Side effects:
+ *     Whatever the application does.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+main(argc, argv)
+    int argc;                  /* Number of command-line arguments. */
+    char **argv;               /* Values of command-line arguments. */
+{
+    Tk_Main(argc, argv, Tcl_AppInit);
+    return 0;                  /* Needed only to prevent compiler warning. */
+}
+\f
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_AppInit --
+ *
+ *     This procedure performs application-specific initialization.
+ *     Most applications, especially those that incorporate additional
+ *     packages, will have their own version of this procedure.
+ *
+ * Results:
+ *     Returns a standard Tcl completion code, and leaves an error
+ *     message in interp->result if an error occurs.
+ *
+ * Side effects:
+ *     Depends on the startup script.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_AppInit(interp)
+    Tcl_Interp *interp;                /* Interpreter for application. */
+{
+    Tk_Window main;
+
+    if (Tcl_Init(interp) == TCL_ERROR) {
+       return TCL_ERROR;
+    }
+    if (Tk_Init(interp) == TCL_ERROR) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Call the init procedures for included packages.  Each call should
+     * look like this:
+     *
+     * if (Mod_Init(interp) == TCL_ERROR) {
+     *     return TCL_ERROR;
+     * }
+     *
+     * where "Mod" is the name of the module.
+     */
+
+    if (Pg_Init(interp) == TCL_ERROR) { 
+      return TCL_ERROR;
+    }
+    /*
+     * Call Tcl_CreateCommand for application-specific commands, if
+     * they weren't already created by the init procedures called above.
+     */
+
+    /*
+     * Specify a user-specific startup file to invoke if the application
+     * is run interactively.  Typically the startup file is "~/.apprc"
+     * where "app" is the name of the application.  If this line is deleted
+     * then no user-specific startup file will be run under any conditions.
+     */
+
+    tcl_RcFileName = "~/.wishrc";
+    return TCL_OK;
+}
diff --git a/src/bin/pgtclsh/updateStats.tcl b/src/bin/pgtclsh/updateStats.tcl
new file mode 100644 (file)
index 0000000..62c9564
--- /dev/null
@@ -0,0 +1,71 @@
+#
+# updateStats 
+#   updates the statistic of number of distinct attribute values
+#  (this should really be done by the vacuum command)
+#   this is kind of brute force and slow, but it works
+#  since we use SELECT DISTINCT to calculate the number of distinct values
+# and that does a sort, you need to have plenty of disk space for the 
+# intermediate sort files.
+# 
+# - jolly 6/8/95
+
+#
+# update_attnvals
+#   takes in a table and updates the attnvals columns for the attributes
+# of that table
+#
+#  conn is the database connection
+#  rel is the table name 
+proc update_attnvals {conn rel} {
+    
+   # first, get the oid of the rel
+    set res [pg_exec $conn "SELECT oid FROM pg_class where relname = '$rel'"]
+    if { [pg_result $res -numTuples] == "0"} {
+       puts stderr "update_attnvals: Relation named $rel was not found"
+       return
+    }
+    set oid [pg_result $res -getTuple 0]
+    pg_result $res -clear
+
+    # use this query to find the names of the attributes
+    set res [pg_exec $conn "SELECT * FROM $rel WHERE 'f'::bool"]
+    set attrNames [pg_result $res -attributes]
+
+    puts "attrNames = $attrNames"
+    foreach att $attrNames {
+       # find how many distinct values there are for this attribute
+       # this may fail if the user-defined type doesn't have 
+       # comparison operators defined
+       set res2 [pg_exec $conn "SELECT DISTINCT $att FROM $rel"]
+       set NVALS($att) [pg_result $res2 -numTuples]
+       puts "NVALS($att) is $NVALS($att)"
+       pg_result $res2 -clear
+    }
+    pg_result $res -clear
+
+    # now, update the pg_attribute table
+    foreach att $attrNames {
+       # first find the oid of the row to change
+       set res [pg_exec $conn "SELECT oid FROM pg_attribute a WHERE a.attname = '$att' and a.attrelid = '$oid'"]
+       set attoid [pg_result $res -getTuple 0]
+       set res2 [pg_exec $conn "UPDATE pg_attribute SET attnvals = $NVALS($att) where pg_attribute.oid = '$attoid'::oid"]
+    }
+}
+
+# updateStats
+#    takes in a database name
+# and updates the attnval stat for all the user-defined tables
+# in the database
+proc updateStats { dbName } {
+    # datnames is the list to be result
+    set conn [pg_connect $dbName]
+    set res [pg_exec $conn "SELECT relname FROM pg_class WHERE relkind = 'r' and relname !~ '^pg_' and relname !~ '^Xinv'"]
+    set ntups [pg_result $res -numTuples]
+    for {set i 0} {$i < $ntups} {incr i} {
+       set rel [pg_result $res -getTuple $i]
+       puts "updating attnvals stats on table $rel"
+       update_attnvals $conn $rel
+    }
+    pg_disconnect $conn
+}
+
diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile
new file mode 100644 (file)
index 0000000..36b0197
--- /dev/null
@@ -0,0 +1,65 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for bin/psql
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+PROG= psql
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+include ../Makefile.global
+
+#
+#USE_READLINE is set in Makefile.global
+# 
+
+ifeq ($(USE_READLINE), true)
+   CFLAGS += -I$(READLINE_INCDIR) -I$(HISTORY_INCDIR)
+
+# if you are using an older readline that uses #include "readline.h" instead
+# of #include <readline/readline.h>,
+# uncomment this
+# CFLAGS += -DOLD_READLINE
+
+   LIBCURSES=  -lcurses
+   LD_ADD += -L$(READLINE_LIBDIR) -L$(HISTORY_LIBDIR) -lreadline -lhistory $(LIBCURSES)
+# use the following if your readline has no separate history lib
+#   LD_ADD += -L$(READLINE_LIBDIR) -lreadline $(LIBCURSES)
+
+   ifeq ($(PORTNAME), ultrix4)
+   LD_ADD += -ltermcap
+   else
+   ifeq ($(PORTNAME), sparc)
+   LD_ADD += -ltermcap
+   else
+   ifeq ($(PORTNAME), linux)
+   LD_ADD += -ltermcap
+   endif
+   ifeq ($(PORTNAME), next)
+   LD_ADD += -ltermcap
+   endif
+   endif
+   endif
+else
+   CFLAGS += -DNOREADLINE
+endif
+
+SRCS= psql.c stringutils.c 
+
+ifneq ($(USE_READLINE), true)
+SRCS+= rlstubs.c
+endif
+
+include $(MKDIR)/postgres.prog.mk
+
+
+
+
diff --git a/src/bin/psql/psql.c b/src/bin/psql/psql.c
new file mode 100644 (file)
index 0000000..8065f26
--- /dev/null
@@ -0,0 +1,1230 @@
+/*-------------------------------------------------------------------------
+ *
+ * psql.c--
+ *    an interactive front-end to postgres95
+ *
+ * Copyright (c) 1996, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "libpq-fe.h"
+#include "stringutils.h"
+
+#include "psqlHelp.h"
+
+#ifdef NOREADLINE
+extern char *readline(char *); /* in rlstubs.c */
+#else
+/* from the GNU readline library */
+#ifdef OLD_READLINE
+#include "readline.h"
+#include "history.h"
+#else
+#include <readline/readline.h>
+#include <history.h>
+#endif
+#endif
+
+#define MAX_QUERY_BUFFER 20000
+#define MAX_FIELD_SEP_LENGTH 40
+
+#define COPYBUFSIZ     8192
+
+#define DEFAULT_FIELD_SEP " "
+#define DEFAULT_EDITOR  "vi"
+#define DEFAULT_SHELL  "/bin/sh"
+
+typedef struct _psqlSettings {
+    int echoQuery;        /* if 1, echo the query before sending it */
+    int quiet;            /* run quietly, no messages, no promt */
+    int singleStep;       /* if 1, prompt for each query */ 
+    int singleLineMode;   /* if 1, query terminated by newline */
+    int useReadline;      /* use the readline routines or not */
+    int printHeader;      /* print output field headers or not */
+    int fillAlign;        /* fill align the fields */
+    FILE *queryFout;      /* where to send the query results */
+    char fieldSep[MAX_FIELD_SEP_LENGTH];  /* field separator */
+} PsqlSettings;
+
+/* declarations for functions in this file */
+static void usage(char* progname);
+static void slashUsage();
+static void handleCopyOut(PGresult *res, int quiet);
+static void handleCopyIn(PGresult *res, int quiet);
+static int tableList(PGconn* conn, int deep_tablelist);
+static int tableDesc(PGconn* conn, char* table);
+
+char* gets_noreadline(char* prompt, FILE* source);
+char* gets_readline(char* prompt, FILE* source);
+char* gets_fromFile(char* prompt, FILE* source);
+int listAllDbs(PGconn *db, PsqlSettings *settings);
+int SendQuery(PGconn* db, char* query, PsqlSettings *settings);
+int HandleSlashCmds(PGconn** db_ptr,  
+                   char *line,
+                   char** prompt_ptr,
+                   char *query,
+                   PsqlSettings *settings);
+int MainLoop(PGconn** db_ptr, FILE *source, PsqlSettings *settings);
+FILE* setFout(char *fname);
+
+
+/*
+ * usage 
+ *   print out usage for command line arguments 
+ */
+
+static void  
+usage(char* progname)
+{
+  fprintf(stderr,"Usage: %s [options] [dbname]\n",progname);
+  fprintf(stderr,"\t -a authsvc              set authentication service\n"); 
+  fprintf(stderr,"\t -A                      turn off fill-justification when printing out attributes\n");
+  fprintf(stderr,"\t -c query                run single query (slash commands too)\n");
+  fprintf(stderr,"\t -d dbName               specify database name\n");
+  fprintf(stderr,"\t -e                      echo the query sent to the backend\n");
+  fprintf(stderr,"\t -f filename             use file as a source of queries\n");
+  fprintf(stderr,"\t -F sep                  set the field separator (default is " ")\n");
+  fprintf(stderr,"\t -h                      help information\n");
+  fprintf(stderr,"\t -H host                 set database server host\n");
+  fprintf(stderr,"\t -l                      list available databases\n");
+  fprintf(stderr,"\t -n                      don't use readline library\n");
+  fprintf(stderr,"\t -o filename             send output to filename\n");
+  fprintf(stderr,"\t -p port                 set port number\n");
+  fprintf(stderr,"\t -q                      run quietly (no messages, no prompts)\n");
+  fprintf(stderr,"\t -s                      single step mode (prompts for each query)\n");
+  fprintf(stderr,"\t -S                      single line mode (i.e. query terminated by newline)\n");
+  fprintf(stderr,"\t -T                      turn off printing of attribute names\n");
+  exit(1);
+}
+
+/*
+ * slashUsage
+ *    print out usage for the backslash commands 
+ */
+
+static void
+slashUsage()
+{
+  fprintf(stderr,"\t \\a           -- toggle fill-justification of display of attributes\n");
+  fprintf(stderr,"\t \\d [<table>] -- list tables in database or columns in <table>\n");
+  fprintf(stderr,"\t \\d *         -- list tables in database and columns in all tables\n");
+  fprintf(stderr,"\t \\e [<fname>] -- edit the current query buffer or <fname>\n");
+  fprintf(stderr,"\t \\f <sep>     -- change field separator\n");
+  fprintf(stderr,"\t \\g           -- query to backend\n");
+  fprintf(stderr,"\t \\h <command> -- help on syntax of sql commands\n");
+  fprintf(stderr,"\t \\h *         -- complete description of all sql commands\n");
+  fprintf(stderr,"\t \\g           -- send query to backend\n");
+  fprintf(stderr,"\t \\i <fname>   -- read queries from filename\n");
+  fprintf(stderr,"\t \\l           -- list all databases\n");
+  fprintf(stderr,"\t \\o [<fname>] -- send query results file named <fname> or stdout\n");
+  fprintf(stderr,"\t \\p           -- print the current query buffer\n");
+  fprintf(stderr,"\t \\q           -- quit\n");
+  fprintf(stderr,"\t \\s [<fname>] -- save or print history\n");
+  fprintf(stderr,"\t \\t           -- toggle output field headers (defaults to on)\n");
+  fprintf(stderr,"\t \\! [<cmd>]   -- shell escape\n");
+  fprintf(stderr,"\t \\?           -- help\n");
+}
+
+/*
+ * listAllDbs
+ *
+ * list all the databases in the system
+ *     returns 0 if all went well
+ *    
+ *
+ */
+int 
+listAllDbs(PGconn *db, PsqlSettings *settings)
+{
+  PGresult *results;
+  char* query = "select * from pg_database;";
+
+  results = PQexec(db, query);
+  if (results == NULL) {
+    fprintf(stderr,"%s", PQerrorMessage(db));
+    return 1;
+  }
+
+  if (PQresultStatus(results) != PGRES_TUPLES_OK)
+    {
+      fprintf(stderr,"Unexpected error from executing: %s\n", query);
+      return 2;
+    }
+  else
+    {
+      PQdisplayTuples(results,
+                     settings->queryFout, 
+                     settings->fillAlign,
+                     settings->fieldSep,
+                     settings->printHeader,
+                     settings->quiet);
+      PQclear(results);
+      return 0;
+    }
+}
+
+/*
+ * tableList (PGconn* conn)
+ *
+ * List The Database Tables
+ *     returns 0 if all went well
+ *    
+ */
+int
+tableList (PGconn* conn, int deep_tablelist)
+{
+  char listbuf[256];
+  int nColumns; 
+  int i;
+  char* ru;
+  char* rk;
+  char* rr;
+
+  PGresult* res;
+
+  listbuf[0] = '\0';
+  strcat(listbuf,"SELECT usename, relname, relkind, relhasrules");
+  strcat(listbuf,"  FROM pg_class, pg_user ");
+  strcat(listbuf,"WHERE ( relkind = 'r' OR relkind = 'i') ");
+  strcat(listbuf,"  and relname !~ '^pg_'");
+  strcat(listbuf,"  and relname !~ '^Inv'");
+/* the usesysid = relowner won't work on stock 1.0 dbs, need to 
+   add in the int4oideq function */
+  strcat(listbuf,"  and usesysid = relowner");
+  strcat(listbuf,"  ORDER BY relname ");
+  res = PQexec(conn,listbuf);
+  if (res == NULL) {
+      fprintf(stderr,"%s", PQerrorMessage(conn));
+      return (-1);
+  }
+
+  if ((PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res) <= 0)) {
+      fprintf(stderr,"No tables found in database %s.\n", PQdb(conn));
+      PQclear(res);
+      return (-1);
+  }
+
+  /* first, print out the attribute names */
+  nColumns = PQntuples(res);
+  if (nColumns > 0)
+  {
+      if ( deep_tablelist ) {
+         /* describe everything here */
+         char **table;
+         table = (char**)malloc(nColumns * sizeof(char*));
+         if ( table == NULL )
+             perror("malloc");
+         
+         /* load table table*/
+         for (i=0; i < nColumns; i++) {
+             table[i] = (char *) malloc(PQgetlength(res,i,1) * sizeof(char) + 1);
+             if ( table[i] == NULL )
+                 perror("malloc");
+             strcpy(table[i],PQgetvalue(res,i,1));
+         }
+
+       PQclear(res);
+       for (i=0; i < nColumns; i++) {
+          tableDesc(conn,table[i]);
+       }
+       free(table);
+      }
+      else {
+       /* Display the information */
+
+       printf ("\nDatabase    = %s\n", PQdb(conn));
+       printf (" +------------------+----------------------------------+----------+\n");
+       printf (" |  Owner           |             Relation             |   Type   |\n");
+       printf (" +------------------+----------------------------------+----------+\n");
+
+       /* next, print out the instances */
+       for (i=0; i < PQntuples(res); i++) {
+           printf (" | %-16.16s", PQgetvalue(res,i,0));
+           printf (" | %-32.32s | ", PQgetvalue(res,i,1));
+           rk =  PQgetvalue(res,i,2);
+           rr =  PQgetvalue(res,i,3);
+           if (strcmp(rk, "r") == 0)
+               printf ("%-8.8s |", (rr[0] == 't') ? "view?" : "table" );
+           else
+               printf ("%-8.8s |", "index");
+           printf("\n");
+       }
+       printf (" +------------------+----------------------------------+----------+\n");
+       PQclear(res);
+      }
+      return (0);
+  
+  } else {
+    fprintf (stderr, "Couldn't find any tables!\n");
+    return (-1);
+  }
+}
+
+/*
+ * Describe a table   (PGconn* conn, char* table)
+ *
+ * Describe the columns in a database table.
+ *     returns 0 if all went well
+ *    
+ *
+ */
+int
+tableDesc (PGconn* conn, char* table)
+{
+  char descbuf[256];
+  int nColumns;
+  char *rtype;
+  int i;
+  int rsize;
+
+  PGresult* res;
+
+  /* Build the query */
+
+  descbuf[0] = '\0';
+  strcat(descbuf,"SELECT a.attnum, a.attname, t.typname, a.attlen");
+  strcat(descbuf,"  FROM pg_class c, pg_attribute a, pg_type t ");
+  strcat(descbuf,"    WHERE c.relname = '");
+  strcat(descbuf,table);
+  strcat(descbuf,"'");
+  strcat(descbuf,"    and a.attnum > 0 ");
+  strcat(descbuf,"    and a.attrelid = c.oid ");
+  strcat(descbuf,"    and a.atttypid = t.oid ");
+  strcat(descbuf,"  ORDER BY attnum ");
+  res = PQexec(conn,descbuf);
+  if (res == NULL) {
+    fprintf(stderr,"%s", PQerrorMessage(conn));
+    return (-1);
+   }
+ if ((PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res) <= 0)) {
+   fprintf(stderr,"Couldn't find table %s!\n", table);
+   PQclear(res);
+   return (-1);
+ }
+  /* first, print out the attribute names */
+  nColumns = PQntuples(res);
+  if (nColumns > 0)
+  {
+    /*
+    ** Display the information
+    */
+
+    printf ("\nTable    = %s\n", table);
+    printf ("+----------------------------------+----------------------------------+-------+\n");
+    printf ("|              Field               |              Type                | Length|\n");
+    printf ("+----------------------------------+----------------------------------+-------+\n");
+
+    /* next, print out the instances */
+    for (i=0; i < PQntuples(res); i++) {
+      printf ("| %-32.32s | ", PQgetvalue(res,i,1));
+      rtype = PQgetvalue(res,i,2);
+      rsize = atoi(PQgetvalue(res,i,3));
+      if (strcmp(rtype, "text") == 0) {
+        printf ("%-32.32s |", rtype);
+        printf (" %-6s |",  "var" );
+      }
+      else if (strcmp(rtype, "bpchar") == 0) {
+        printf ("%-32.32s |", "char");
+        printf (" %-6i |", rsize > 0 ? rsize - 4 : 0 );
+      }
+      else if (strcmp(rtype, "varchar") == 0) {
+        printf ("%-32.32s |", rtype);
+        printf (" %-6i |", rsize > 0 ? rsize - 4 : 0 );
+      }
+      else {
+         /* array types start with an underscore */
+         if (rtype[0] != '_')
+             printf ("%-32.32s |", rtype);
+         else  {
+             char *newname;
+             newname = malloc(strlen(rtype) + 2);
+             strcpy(newname, rtype+1);
+             strcat(newname, "[]");
+             printf ("%-32.32s |", newname);
+             free(newname);
+         }
+       if (rsize > 0) 
+           printf ("%-6i |", rsize);
+       else
+           printf ("%-6s |", "var");
+      }
+      printf("\n");
+    }
+    printf ("+----------------------------------+----------------------------------+-------+\n");
+
+    PQclear(res);
+    return (0);
+  
+  } else {
+      fprintf (stderr, "Couldn't find table %s!\n", table);
+    return (-1);
+  }
+}
+
+typedef char* (*READ_ROUTINE)(char* prompt, FILE* source);
+
+/* gets_noreadline  prompt source
+      gets a line of input without calling readline, the source is ignored
+*/
+char* 
+gets_noreadline(char* prompt, FILE* source)
+{
+    fputs(prompt, stdout);
+    fflush(stdout);
+    return(gets_fromFile(prompt,stdin));
+}
+
+/*
+ * gets_readline  prompt source
+ *   the routine to get input from GNU readline(), the source is ignored 
+ * the prompt argument is used as the prompting string
+ */
+char* 
+gets_readline(char* prompt, FILE* source)
+{
+  return (readline(prompt));
+}
+
+
+/*
+ * gets_fromFile  prompt source
+ *    
+ * the routine to read from a file, the prompt argument is ignored
+ * the source argument is a FILE* 
+ */
+char* 
+gets_fromFile(char* prompt, FILE* source)
+{
+  char* line;
+  int len;
+
+  line = malloc(MAX_QUERY_BUFFER+1);
+
+  /* read up to MAX_QUERY_BUFFER characters */
+  if (fgets(line, MAX_QUERY_BUFFER, source) == NULL)
+    return NULL;
+
+  line[MAX_QUERY_BUFFER-1] = '\0';
+  len = strlen(line);
+  if (len == MAX_QUERY_BUFFER)
+    {
+      fprintf(stderr, "line read exceeds maximum length.  Truncating at %d\n", MAX_QUERY_BUFFER);
+    }
+  
+  return line;
+}
+
+/*
+ * SendQuery:
+     SendQuery: send the query string to the backend 
+ *
+ *  return 0 if the query executed successfully
+ *  returns 1 otherwise
+ */
+int
+SendQuery(PGconn* db, char* query, PsqlSettings *settings)
+{
+  PGresult* results;
+  PGnotify* notify;
+  int status = 0;
+
+  if (settings->singleStep)
+       fprintf(stdout, "\n*******************************************************************************\n");
+
+  if (settings->echoQuery || settings->singleStep) {
+      fprintf(stderr,"QUERY: %s\n",query);
+      fflush(stderr);
+  }
+
+  if (settings->singleStep) {
+       fprintf(stdout, "\n*******************************************************************************\n");
+       fflush(stdout);
+       printf("\npress return to continue ..\n");
+       gets_fromFile("",stdin);
+  }
+
+  results = PQexec(db, query);
+  if (results == NULL) {
+    fprintf(stderr,"%s",PQerrorMessage(db));
+    return 1;
+  }
+
+  switch (PQresultStatus(results)) {
+  case PGRES_TUPLES_OK:
+      PQdisplayTuples(results,
+                     settings->queryFout,
+                     settings->fillAlign,
+                     settings->fieldSep,
+                     settings->printHeader,
+                     settings->quiet);
+      PQclear(results);
+      break;
+  case PGRES_EMPTY_QUERY:
+    /* do nothing */
+    break;
+  case PGRES_COMMAND_OK:
+    if (!settings->quiet)
+      fprintf(stdout,"%s\n",PQcmdStatus(results));
+    break;
+  case PGRES_COPY_OUT:
+    handleCopyOut(results, settings->quiet);
+    break;
+  case PGRES_COPY_IN:
+    handleCopyIn(results, settings->quiet);
+    break;
+  case PGRES_NONFATAL_ERROR:
+  case PGRES_FATAL_ERROR:
+  case PGRES_BAD_RESPONSE:
+    status = 1;
+    fprintf(stderr,"%s",PQerrorMessage(db));
+    break;
+
+  } 
+
+  /* check for asynchronous returns */
+  notify = PQnotifies(db);
+  if (notify) {
+      fprintf(stderr,"ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
+             notify->relname, notify->be_pid);
+      free(notify);
+  }
+
+  return status;
+
+}
+
+/*
+  HandleSlashCmds:
+
+  Handles all the different commands that start with \ 
+     db_ptr is a pointer to the TgDb* structure
+     line is the current input line
+     prompt_ptr is a pointer to the prompt string,
+                  a pointer is used because the prompt can be used with 
+                 a connection to a new database
+  returns a status:
+       0 - send currently constructed query to backend (i.e. we got a \g)
+       1 - skip processing of this line, continue building up query
+       2 - terminate processing of this query entirely
+*/
+int
+HandleSlashCmds(PGconn** db_ptr, 
+               char* line, 
+               char** prompt_ptr, 
+               char *query,
+               PsqlSettings *settings)
+{
+  int status = 0;
+  PGconn* db = *db_ptr;
+  char* dbname = PQdb(db);
+  char *optarg = NULL;
+  int len;
+
+  len = strlen(line);
+  if (len > 2)
+      optarg = leftTrim(line+2);
+  switch (line[1])
+    {
+    case 'a': /* toggles to fill fields on output */
+       if (settings->fillAlign)
+           settings->fillAlign = 0;
+       else 
+           settings->fillAlign = 1;
+       if (!settings->quiet)
+           fprintf(stderr,"turning %s fill-justification\n",
+                   (settings->fillAlign) ? "on" : "off" );
+       break;
+    case 'c':  /* \c means connect to new database */
+      {
+         if (!optarg) {
+             fprintf(stderr,"\\c must be followed by a database name\n");
+             status = 1;
+             break;
+         }
+         if (strcmp(optarg, dbname) == 0)  {
+             fprintf(stderr,"already connected to %s\n", dbname);
+             status = 1;
+             break;
+         }
+         else {
+             PGconn *olddb;
+             
+             printf("closing connection to database:%s\n", dbname);
+             olddb = db;
+             db = PQsetdb(PQhost(olddb), PQport(olddb), NULL, NULL, optarg);
+             *db_ptr = db;
+             printf("connecting to new database: %s\n", optarg);
+             if (PQstatus(db) == CONNECTION_BAD) {
+                 fprintf(stderr,"%s\n", PQerrorMessage(db));
+                 printf("reconnecting to %s\n", dbname);
+                 db = PQsetdb(PQhost(olddb), PQport(olddb), 
+                              NULL, NULL, dbname);
+                 *db_ptr = db;
+                 if (PQstatus(db) == CONNECTION_BAD) {
+                     fprintf(stderr, 
+                             "could not reconnect to %s.  exiting\n", dbname);
+                     exit(2);
+                 }
+                 status = 1;
+                 break;
+             }
+             PQfinish(olddb);
+             free(*prompt_ptr);
+             *prompt_ptr = malloc(strlen(optarg) + 10);
+             sprintf(*prompt_ptr,"%s=> ", optarg);
+             status = 1;
+           break;
+         }
+      }
+      break;
+    case 'd':     /* \d describe tables or columns in a table */
+      {
+       if (!optarg) {
+          tableList(db,0);
+         status = 1;
+         break;
+       }
+       if ( strcmp(optarg,"*") == 0 ) {
+          tableList(db, 0);
+          tableList(db, 1);
+       }
+       else {
+          tableDesc(db,optarg);
+       }
+       status = 1;
+       break;
+      }
+    case 'e':
+      {
+       char s[256];
+       int fd;
+       int ql = strlen(query);
+       int f_arg = 0;
+       int cc;
+        if (optarg)
+        {
+               f_arg = 1;
+               strcpy(s, optarg);
+        }
+        else
+        {
+               sprintf(s, "/tmp/psql.%d.%d", getuid(), getpid());
+               unlink(s);
+               if (ql)
+               {
+                       if ((fd=open(s, O_EXCL|O_CREAT|O_WRONLY, 0600))==-1)
+                       {
+                               perror(s);
+                               break;
+                       }
+                       if (query[ql-1]!='\n')
+                               strcat(query, "\n");
+                       if (write(fd, query, ql)!=ql)
+                       {
+                               perror(s);
+                               close(fd);
+                               unlink(s);
+                               break;
+                       }
+                       close(fd);
+               }
+       }
+       {
+           char sys[256];
+           char *editorName;
+           editorName = getenv("EDITOR");
+           if (editorName == NULL)
+               editorName = DEFAULT_EDITOR;
+           sprintf(sys, "exec %s %s", editorName, s);
+           system(sys);
+        }
+       if ((fd=open(s, O_RDONLY))==-1)
+       {
+               if (!f_arg)
+                       unlink(s);
+               break;
+       }
+       if ((cc=read(fd, query, MAX_QUERY_BUFFER))==-1)
+        {
+               perror(s);
+               close(fd);
+               if (!f_arg)
+                       unlink(s);
+               break;
+       }       
+       query[cc]='\0';
+       close(fd);
+       if (!f_arg)
+               unlink(s);
+       rightTrim(query);
+       if (query[strlen(query)-1]==';')
+               return 0;
+       break;
+      }
+  case 'f':
+      if (optarg)
+         strcpy(settings->fieldSep,optarg);
+      else
+          strcpy(settings->fieldSep,DEFAULT_FIELD_SEP);
+      break;
+  case 'g':  /* \g means send query */
+      status = 0;     
+       break;
+    case 'i':     /* \i is include file */
+      {
+       FILE* fd;
+
+       if (!optarg) {
+         fprintf(stderr,"\\i must be followed by a file name\n");
+         status = 1;
+         break;
+       }
+
+       if ( (fd = fopen(optarg, "r")) == NULL)
+         {
+           fprintf(stderr,"file named %s could not be opened\n",optarg);
+           status = 1;
+           break;
+         }
+       MainLoop(&db, fd, settings);
+       fclose(fd);
+       status = 1;
+       break;
+      }
+    case 'h':
+      {
+       char* cmd;
+       int i, numCmds;
+       int all_help = 0;
+
+       if (!optarg) {
+           printf("type \\h <cmd> where <cmd> is one of the following:\n");
+           i = 0;
+           while (QL_HELP[i].cmd != NULL)
+             {
+               printf("\t%s\n", QL_HELP[i].cmd);
+               i++;
+             }
+            printf("type \\h * for a complete description of all commands\n");
+         }
+       else
+         {
+         cmd = optarg;
+
+         numCmds = 0;
+         while (QL_HELP[numCmds++].cmd != NULL);
+
+         numCmds = numCmds - 1;
+
+         if ( strcmp(cmd,"*") == 0 ) {
+            all_help=1;
+         }
+
+         for (i=0; i<numCmds;i++)  {
+             if (strcmp(QL_HELP[i].cmd, cmd) == 0 || all_help)    {
+               printf("Command: %s\n",QL_HELP[i].cmd);
+               printf("Description: %s\n", QL_HELP[i].help);
+               printf("Syntax:\n");
+               printf("%s\n", QL_HELP[i].syntax);
+               if ( all_help ) {
+                  printf("\n");
+               }
+               else {
+                  break;
+              }
+           }
+         }
+         if (i == numCmds && ! all_help)
+           printf("command not found,  try \\h with no arguments to see available help\n");
+       }
+       status = 1;
+       break;
+      }
+    case 'l':     /* \l is list database */
+      listAllDbs(db,settings);
+      status = 1;
+      break;
+    case 'o':
+      settings->queryFout = setFout(optarg);
+      break;
+    case 'p':
+       if (query) {
+           fputs(query, stdout);
+           fputc('\n', stdout);
+       }
+       break;
+    case 'q': /* \q is quit */
+      status = 2;
+      break;
+    case 's': /* \s is save history to a file */
+      {
+       char* fname;
+
+       if (!optarg) {
+         fprintf(stderr,"\\s must be followed by a file name\n");
+         status = 1;
+         break;
+       }
+
+       fname = optarg;
+       if (write_history(fname) != 0)
+         {
+           fprintf(stderr,"cannot write history to %s\n",fname);
+         }
+       status = 1;
+       break;
+      }
+    case 't':
+       if ( settings->printHeader )
+            settings->printHeader = 0;
+       else
+            settings->printHeader = 1;
+       if (!settings->quiet)
+           fprintf(stderr,"turning %s printing of field headers\n",
+                   (settings->printHeader) ? "on" : "off" );
+       break;
+    case '!':
+      if (!optarg) {
+         char sys[256];
+         char *shellName;
+         shellName = getenv("SHELL");
+         if (shellName == NULL) 
+             shellName = DEFAULT_SHELL;
+         sprintf(sys,"exec %s", shellName);
+         system(sys);
+      }
+      else
+         system(optarg);
+      break;
+    default:
+    case '?':     /* \? is help */
+      slashUsage();
+      status = 1;
+      break;
+    }
+  return status;
+}
+
+/* 
+ MainLoop: main processing loop for reading lines of input
+ and sending them to the backend
+
+ this loop is re-entrant.  May be called by \i command
+ which reads input from a file
+
+ *db_ptr must be initialized and set
+*/
+int
+MainLoop(PGconn** db_ptr, 
+        FILE* source,
+        PsqlSettings *settings)
+{
+  char* prompt;                 /* readline prompt */
+  char* line;                   /* line of input*/
+  int len;                      /* length of the line */
+  char query[MAX_QUERY_BUFFER]; /* multi-line query storage */
+  PGconn* db = *db_ptr;
+  char* dbname = PQdb(db);
+  int exitStatus = 0;
+
+  int slashCmdStatus = 0;
+ /* slashCmdStatus can be:
+       0 - send currently constructed query to backend (i.e. we got a \g)
+       1 - skip processing of this line, continue building up query
+       2 - terminate processing of this query entirely
+  */
+
+  int send_query = 0;
+  int interactive;
+  READ_ROUTINE GetNextLine;
+
+  interactive = (source == stdin);
+
+  if (interactive) {
+    prompt = malloc(strlen(dbname) + 10);
+    if (settings->quiet)
+      prompt[0] = '\0';
+    else
+      sprintf(prompt,"%s=> ", dbname);
+    if (settings->useReadline) {
+       using_history();
+       GetNextLine = gets_readline;
+    } else
+       GetNextLine = gets_noreadline;
+
+  }
+  else
+    GetNextLine = gets_fromFile;
+
+  query[0] = '\0';
+  
+  /* main loop for getting queries and executing them */
+  while ((line = GetNextLine(prompt, source)) != NULL)
+    {
+       exitStatus = 0;
+      line = rightTrim(line); /* remove whitespaces on the right, incl. \n's */
+
+      if (line[0] == '\0') {
+         free(line);
+         continue;
+      }
+
+      /* filter out comment lines that begin with --,
+         this could be incorrect if -- is part of a quoted string.
+         But we won't go through the trouble of detecting that.  If you have
+        -- in your quoted string, be careful and don't start a line with it*/
+      if (line[0] == '-' && line[1] == '-') {
+         if (settings->singleStep) /* in single step mode, show comments */
+             fprintf(stdout,"%s\n",line);
+         free(line);
+         continue;
+      }
+
+      len = strlen(line);
+
+      if (interactive && settings->useReadline)
+         add_history(line);      /* save non-empty lines in history */
+      
+      /* do the query immediately if we are doing single line queries 
+       or if the last character is a semicolon */
+      send_query = settings->singleLineMode || (line[len-1] == ';') ;
+
+      /* normally, \ commands have to be start the line,
+        but for backwards compatibility with monitor,
+        check for \g at the end of line */
+      if (len > 2 && !send_query) 
+       {
+         if (line[len-1]=='g' && line[len-2]=='\\')
+           {
+           send_query = 1;
+           line[len-2]='\0';
+         }
+       }
+      
+      /* slash commands have to be on their own line */
+      if (line[0] == '\\') {
+         slashCmdStatus = HandleSlashCmds(db_ptr, 
+                                          line, 
+                                          &prompt, 
+                                          query,
+                                          settings);
+       db = *db_ptr; /* in case \c changed the database */
+       if (slashCmdStatus == 1)
+         continue;
+       if (slashCmdStatus == 2)
+         break;
+       if (slashCmdStatus == 0)
+         send_query = 1;
+      }
+      else
+       if (strlen(query) + len > MAX_QUERY_BUFFER)
+         {
+           fprintf(stderr,"query buffer max length of %d exceeded\n",MAX_QUERY_BUFFER);
+           fprintf(stderr,"query line ignored\n");
+         }
+      else
+       if (query[0]!='\0') {
+           strcat(query,"\n");
+           strcat(query,line);
+       }
+      else
+       strcpy(query,line);
+      
+      if (send_query && query[0] != '\0')
+       {
+           /* echo the line read from the file,
+            unless we are in single_step mode, because single_step mode
+            will echo anyway */
+         if (!interactive && !settings->singleStep) 
+           fprintf(stderr,"%s\n",query);
+
+         exitStatus = SendQuery(db, query, settings);
+         query[0] = '\0';
+       }
+      
+       free(line); /* free storage malloc'd by GetNextLine */
+    } /* while */
+  return exitStatus;
+} 
+
+int
+main(int argc, char** argv)
+{
+  extern char* optarg;
+  extern int optind, opterr;
+  
+  PGconn *db;
+  char* dbname = NULL;
+  char* host = NULL;
+  char* port = NULL;
+  char* qfilename = NULL;
+  char errbuf[ERROR_MSG_LENGTH];
+
+  PsqlSettings settings;
+
+  char* singleQuery = NULL;
+
+  int listDatabases = 0 ;
+  int exitStatus = 0;
+  int singleSlashCmd = 0;
+  int c;
+
+
+#ifdef NOREADLINE
+  settings.useReadline = 0;
+#else
+  settings.useReadline = 1;
+#endif
+
+  settings.quiet = 0;
+  settings.fillAlign = 1;
+  settings.printHeader = 1;
+  settings.echoQuery = 0;
+  settings.singleStep = 0;
+  settings.singleLineMode = 0;
+  settings.queryFout = stdout;
+  strcpy(settings.fieldSep, DEFAULT_FIELD_SEP);
+
+  while ((c = getopt(argc, argv, "Aa:c:d:ef:F:lhH:nso:p:qST")) != EOF) {
+    switch (c) {
+    case 'A':
+       settings.fillAlign = 0;
+       break;
+    case 'a':
+       fe_setauthsvc(optarg, errbuf);
+       break;
+    case 'c':
+       singleQuery = optarg;
+       if ( singleQuery[0] == '\\' ) {
+           singleSlashCmd=1;
+       }
+       break;
+    case 'd':
+      dbname = optarg;
+      break;
+    case 'e':
+      settings.echoQuery = 1;
+      break;
+    case 'f':
+      qfilename = optarg;
+      break;
+    case 'F':
+       strncpy(settings.fieldSep,optarg,MAX_FIELD_SEP_LENGTH); 
+       break;
+    case 'l':
+      listDatabases = 1;
+      break;
+    case 'H':
+      host = optarg;
+      break;
+    case 'n':
+       settings.useReadline = 0;
+       break;
+    case 'o':
+       settings.queryFout = setFout(optarg);
+       break;
+    case 'p':
+      port = optarg;
+      break;
+    case 'q':
+      settings.quiet = 1;
+      break;
+    case 's':
+      settings.singleStep = 1;
+      break;
+    case 'S':
+      settings.singleLineMode = 1;
+      break;
+    case 'T':
+       settings.printHeader = 0;
+       break;
+    case 'h':
+    default:
+      usage(argv[0]);
+      break;
+    }
+  }
+  /* if we still have an argument, use it as the database name */
+  if (argc - optind == 1)
+    dbname = argv[optind];
+
+  if (listDatabases)
+    dbname = "template1";
+  
+  db = PQsetdb(host, port, NULL, NULL, dbname);
+  dbname = PQdb(db);
+
+  if (PQstatus(db) == CONNECTION_BAD) {
+    fprintf(stderr,"Connection to database '%s' failed.\n", dbname);
+    fprintf(stderr,"%s",PQerrorMessage(db));
+    exit(1);
+  }
+  if (listDatabases) {
+      exit(listAllDbs(db,&settings));
+    }
+
+  if (!settings.quiet && !singleQuery && !qfilename) {
+    printf("Welcome to the POSTGRES95 interactive sql monitor:\n");
+    printf("  Please read the file COPYRIGHT for copyright terms of POSTGRES95\n\n");
+    printf("   type \\? for help on slash commands\n");
+    printf("   type \\q to quit\n");
+    printf("   type \\g or terminate with semicolon to execute query\n");
+    printf(" You are currently connected to the database: %s\n\n", dbname);
+     }
+
+  if (qfilename || singleSlashCmd) {
+      /* read in a file full of queries instead of reading in queries
+        interactively */
+      char *line;
+      char prompt[100];
+
+      if ( singleSlashCmd ) {
+       /* Not really a query, but "Do what I mean, not what I say." */
+       line = singleQuery;
+      }
+      else {
+       line = malloc(strlen(qfilename) + 5);
+       sprintf(line,"\\i %s", qfilename);
+      }
+      HandleSlashCmds(&db, line, (char**)prompt, "", &settings);
+      
+   } else {
+       if (singleQuery) {
+          exitStatus = SendQuery(db, singleQuery, &settings);
+       }
+       else 
+          exitStatus = MainLoop(&db, stdin, &settings);
+   }
+
+  PQfinish(db);
+
+  return exitStatus;
+}
+
+
+static void
+handleCopyOut(PGresult *res, int quiet)
+{
+    bool copydone = false;
+    char copybuf[COPYBUFSIZ];
+    int ret;
+
+    if (!quiet)
+       fprintf(stdout, "Copy command returns...\n");
+    
+    while (!copydone) {
+       ret = PQgetline(res->conn, copybuf, COPYBUFSIZ);
+       
+       if (copybuf[0] == '.' && copybuf[1] =='\0') {
+           copydone = true;    /* don't print this... */
+       } else {
+           fputs(copybuf, stdout);
+           switch (ret) {
+           case EOF:
+               copydone = true;
+               /*FALLTHROUGH*/
+           case 0:
+               fputc('\n', stdout);
+               break;
+           case 1:
+               break;
+           }
+       }
+    }
+    fflush(stdout);
+    PQendcopy(res->conn);
+}
+
+
+static void
+handleCopyIn(PGresult *res, int quiet)
+{
+    bool copydone = false;
+    bool firstload;
+    bool linedone;
+    char copybuf[COPYBUFSIZ];
+    char *s;
+    int buflen;
+    int c;
+    
+    if (!quiet) {
+       fputs("Enter info followed by a newline\n", stdout);
+       fputs("End with a dot on a line by itself.\n", stdout);
+    }
+    
+    /*
+     * eat extra newline still in input buffer
+     *
+     */
+    fflush(stdin);
+    if ((c = getc(stdin)) != '\n' && c != EOF) {
+       (void) ungetc(c, stdin);
+    }
+    
+    while (!copydone) {                        /* for each input line ... */
+       if (!quiet) {
+           fputs(">> ", stdout);
+           fflush(stdout);
+       }
+       firstload = true;
+       linedone = false;
+       while (!linedone) {             /* for each buffer ... */
+           s = copybuf;
+           buflen = COPYBUFSIZ;
+           for (; buflen > 1 &&
+                !(linedone = (c = getc(stdin)) == '\n' || c == EOF);
+                --buflen) {
+               *s++ = c;
+           }
+           if (c == EOF) {
+               /* reading from stdin, but from a file */
+               PQputline(res->conn, ".");
+               copydone = true;
+               break;
+           }
+           *s = '\0';
+           PQputline(res->conn, copybuf);
+           if (firstload) {
+               if (!strcmp(copybuf, ".")) {
+                   copydone = true;
+               }
+               firstload = false;
+           }
+       }
+       PQputline(res->conn, "\n");
+    }
+    PQendcopy(res->conn);
+}
+
+
+/* try to open fname and return a FILE*,
+   if it fails, use stdout, instead */
+FILE* 
+setFout(char *fname)
+{
+    FILE *queryFout;
+
+    if (!fname)
+       queryFout = stdout;
+    else {
+       queryFout = fopen(fname, "w");
+       if (!queryFout) {
+           perror(fname);
+           queryFout = stdout;
+       }
+    }
+
+    return queryFout;
+}
diff --git a/src/bin/psql/psqlHelp.h b/src/bin/psql/psqlHelp.h
new file mode 100644 (file)
index 0000000..66c3781
--- /dev/null
@@ -0,0 +1,168 @@
+/*-------------------------------------------------------------------------
+ *
+ * psqlHelp.h--
+ *    Help for query language syntax
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+struct _helpStruct {
+  char* cmd; /* the command name */
+  char* help; /* the help associated with it */
+  char* syntax; /* the syntax associated with it */
+} ;
+static struct _helpStruct QL_HELP[] = {
+  { "abort",
+      "abort the current transaction",
+      "abort [transaction];"},
+  { "abort transaction",
+      "abort the current transaction",
+      "abort [transaction];"},
+  { "alter table",
+      "add/rename attributes, rename tables",
+      "alter table <relname> [*] add column <attr> <type>;\n\talter table <relname> [*] rename [column] <attr1> to <attr2>;\n\talter table <relname1> rename to <relname2>"},
+  { "begin",
+      "begin a new transaction",
+      "begin [transaction|work];"},
+  { "begin transaction",
+      "begin a new transaction",
+      "begin [transaction|work];"},
+  { "begin work",
+      "begin a new transaction",
+      "begin [transaction|work];"},
+  { "cluster",
+      "create a clustered index (from an existing index)",
+      "cluster <index_name> on <relation_name>"},
+  { "close",
+      "close an existing cursor (portal)",
+      "close <portalname>;"},
+  { "commit",
+      "commit a transaction",
+      "commit [work]"},
+  { "commit work",
+      "commit a transaction",
+      "commit [work]"},
+  { "copy",
+      "copy data to and from a table",
+      "copy [binary] [nonulls] <relname>\n\t{to|from} {<filename>|stdin|stdout} [using delimiters <delim>];"},
+  { "create",   
+      "Please more be specific:",
+      "\tcreate aggregate\n\tcreate database\n\tcreate function\n\tcreate index\n\tcreate operator\n\tcreate rule\n\tcreate table\n\tcreate type\n\tcreate view"},
+  { "create aggregate",
+      "define an aggregate function",
+      "create aggregate <agg_name> [as] (basetype = <data_type>, \n\t[sfunc1 = <sfunc_1>, stype1 = <sfunc1_return_type>]\n\t[sfunc2 = <sfunc_2>, stype2 = <sfunc2_return_type>]\n\t[,finalfunc = <final-function>]\n\t[,initcond1 = <initial-cond1>][,initcond2 = <initial-cond2>]);"},
+  { "create database", 
+    "create a database",
+    "create database <dbname>"},
+  { "create function",
+      "create a user-defined function",
+      "create function <function_name> ([<type1>,...<typeN>]) returns <return_type>\n\tas '<object_filename>'|'<sql-queries>'\n\tlanguage 'c'|'sql'|'internal';"},
+  { "create index",
+      "construct an index",
+      "create index <indexname> on <relname> using <access_method> (<attr1>|<funcname>(<attr1>,...) <type_class1>);"},
+  { "create operator",
+      "create a user-defined operator",
+      "create operator <operator_name> (\n\t[leftarg = <type1>][,rightarg = <type2>]\n\t,procedure = <func_name>,\n\t[,commutator = <com_op>][,negator = <neg_op>]\n\t[,restrict = <res_proc>][,hashes]\n\t[,join = <join_proc>][,sort = <sort_op1>...<sort_opN>]);"},
+  { "create rule",
+      "define a new rule",
+      "create rule <rule_name> as on\n\t[select|update|delete|insert]\n\tto <object> [where <qual>]\n\tdo [instead] [<action>|nothing| [<actions>]];"},
+  { "create table",
+      "create a new table",
+      "create table <relname> ( <attr1> <type1>,... <attrN> <typeN>)\n\t[inherits (<relname1>,...<relnameN>\n\tarchive=<archive_mode>\n\tstore=<smgr_name>\n\tarch_store=<smgr_name>];"},
+  { "create type",
+      "create a new user-defined base data type",
+      "create type <typename> (\n\tinternallength = (<number> | variable),\n\t[externallength = (<number>|variable),]\n\tinput=<input_function>, output = <output_function>\n\t[,element = <typename>][,delimiter=<character>][,default=\'<string>\']\n\t[,send = <send_function>][,receive = <receive_function>][,passedbyvalue]);"},
+  { "create view",
+      "create a view",
+      "create view <view_name> as select <expr1>[as <attr1>][,... <exprN>[as <attrN>]] [from <from_list>] [where <qual>];"},
+  { "declare",
+      "set up a cursor (portal)",
+      "declare <portalname> [binary] cursor for\n\tselect [distinct]\n\t<expr1> [as <attr1>],...<exprN> [as <attrN>]\n\t[from <from_list>] [where <qual>]\n\t[order by <attr1> [using <op1>],... <attrN> [using <opN>]];"},
+  { "delete",
+      "delete tuples",
+      "delete from <relname> [where <qual>];"},
+  { "drop",   
+      "Please more be specific:",
+      "\tdrop aggregate\n\tdrop database\n\tdrop function\n\tdrop index\n\tdrop operator\n\tdrop rule\n\tdrop table\n\tdrop type\n\tdrop view"},
+  { "drop aggregate",
+      "remove an aggregate function",
+      "drop aggregate <agg_name>;"},
+  { "drop database",
+     "remove a database",
+     "drop database <dbname>"},
+  { "drop function",
+      "remove a user-defined function",
+      "drop function <funcname> ([<type1>,....<typeN>]);"},
+  { "drop index", 
+      "remove an existing index",
+      "drop index <indexname>;"},
+  { "drop operator",
+      "remove a user-defined operator",
+      "drop operator <operator_name> ([<ltype>|none],[<rtype>|none]);"},
+  { "drop rule",
+      "remove a rule",
+      "drop rule <rulename>;"},
+  { "drop table", 
+      "remove a table", 
+      "drop table <relname>[,...<relnameN];"},
+  { "drop type",
+      "remove a user-defined base type",
+      "drop type <typename>;"},
+  { "drop view",
+      "remove a view",
+      "drop view <view_name>"},
+  { "end",
+      "end the current transaction",
+      "end [transaction];"},
+  { "end transaction",
+      "end the current transaction",
+      "end [transaction];"},
+  { "explain",
+      "explain the query execution plan",
+      "explain [with {cost|full_plan}] <query>"},
+  { "extend index",
+      "extend a partial index",
+      "extend index <indexname> [where <qual>];"},
+  { "fetch",
+      "retrieve tuples from a cursor (portal)",
+      "fetch [forward|backward] [<number>|all] [in <portalname>];"},
+  { "grant",
+      "grant access control to a user or group",
+      "grant <privilege[,privilege,...]> on <rel1>[,...<reln>] to \n[public | group <group> | <username>]\n\t privilege is {ALL | SELECT | INSERT | UPDATE | DELETE | RULE}"},
+  { "insert",
+      "insert tuples",
+      "insert into <relname> [(<attr1>...<attrN>)]\n\t[values (<expr1>...<exprN>); |\n\tselect <expr1>,...<exprN> [from <from_clause>] [where <qual>];"},
+  { "listen",
+       "listen for notification on a relation",
+       "listen <relname>"},
+  { "load",
+      "dynamically load a module",
+      "load <filename>;"},
+  { "notify",
+      "signal all frontends and backends listening on a relation",
+      "notify <relname>"},
+  { "purge",
+      "purge historical data",
+      "purge <relname> [before <abstime>] [after <reltime>];"},
+  { "revoke",
+      "revoke access control from a user or group",
+      "revoke <privilege[,privilege,...]> on <rel1>[,...<reln>] from \n[public | group <group> | <username>]\n\t privilege is {ALL | SELECT | INSERT | UPDATE | DELETE | RULE}"},
+  { "rollback",
+      "abort a transaction",
+      "rollback [work]"},
+  { "select",
+      "retrieve tuples",
+      "select [distinct on <attr>] <expr1> [as <attr1>], ... <exprN> [as <attrN>]\n\t[into table <relname>] [from <from_list>]\n\t[where <qual>]\n\t[order by <attr1>\n\t\t[using <op1>],..<attrN> [[using <opN>] | ASC | DESC]];" },
+  { "update",
+      "update tuples",
+      "update <relname> set <attr1>=<expr1>,...<attrN>=<exprN> [from <from_clause>] [where <qual>];"},
+  { "vacuum",
+      "vacuum the database, i.e. cleans out deleted records, updates statistics",
+      "vacuum;"},
+  { NULL, NULL, NULL}  /* important to keep a NULL terminator here! */
+};
diff --git a/src/bin/psql/rlstubs.c b/src/bin/psql/rlstubs.c
new file mode 100644 (file)
index 0000000..e5dc0ab
--- /dev/null
@@ -0,0 +1,41 @@
+/*-------------------------------------------------------------------------
+ *
+ * rlstubs.c--
+ *    stub routines when compiled without readline and history libraries
+ *
+ * Copyright (c) 1994-5, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+
+char *
+readline(char *prompt)
+{
+    static char buf[500];
+
+    printf("%s");
+    return fgets(buf, 500, stdin);
+}
+
+int
+write_history()
+{
+    return 0;
+}
+
+int
+using_history()
+{
+    return 0;
+}
+
+int
+add_history()
+{
+    return 0;
+}
diff --git a/src/bin/psql/stringutils.c b/src/bin/psql/stringutils.c
new file mode 100644 (file)
index 0000000..7ab5680
--- /dev/null
@@ -0,0 +1,104 @@
+/*-------------------------------------------------------------------------
+ *
+ * stringutils.c--
+ *    simple string manipulation routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include "stringutils.h"
+
+/* all routines assume null-terminated strings! */
+
+/* removes whitespaces from the left, right and both sides of a string */
+/* MODIFIES the string passed in and returns the head of it */
+char* leftTrim(char* s)  
+{
+  char* s2 = s;
+  int shift=0;
+  int j=0;
+
+  while (isspace(*s))
+    { s++; shift++;}
+  if (shift > 0)
+    {
+      while ( (s2[j] = s2[j+shift]) !='\0')
+       j++;
+    }
+
+  return s2;
+}
+
+char* rightTrim(char* s)
+{
+  char* sEnd;
+  sEnd = s+strlen(s)-1;
+  while (isspace(*sEnd))
+    sEnd--;
+  if (sEnd < s)
+    s[0]='\0';
+  else
+    s[sEnd-s+1]='\0';
+  return s;
+}
+
+char* doubleTrim(char* s)
+{
+  strcpy(s,leftTrim(rightTrim(s)));
+  return s;
+}
+
+/* dupstr : copies a string, while allocating space for it. 
+   the CALLER is responsible for freeing the space
+   returns NULL if the argument is NULL*/
+char* dupstr(char *s)
+{
+  char* result;
+
+  if (s == NULL)
+    return NULL;
+
+  result = (char*)malloc(strlen(s)+1);
+  strcpy(result, s);
+  return result;
+}
+
+
+#ifdef STRINGUTILS_TEST
+void testStringUtils()
+{
+  static char* tests[] = {" goodbye  \n", /* space on both ends */
+                         "hello world",  /* no spaces to trim */
+                         "",           /* empty string */
+                         "a",          /* string with one char*/
+                         " ",          /* string with one whitespace*/
+                         NULL_STR};
+
+  int i=0;
+  while (tests[i]!=NULL_STR)
+    {
+      char* t;
+      t = dupstr(tests[i]);
+      printf("leftTrim(%s) = ",t);
+      printf("%sEND\n", leftTrim(t));
+      t = dupstr(tests[i]);
+      printf("rightTrim(%s) = ",t);
+      printf("%sEND\n", rightTrim(t));
+      t = dupstr(tests[i]);
+      printf("doubleTrim(%s) = ",t);
+      printf("%sEND\n", doubleTrim(t));
+      i++;
+    }
+
+}
+
+#endif
diff --git a/src/bin/psql/stringutils.h b/src/bin/psql/stringutils.h
new file mode 100644 (file)
index 0000000..a2640d3
--- /dev/null
@@ -0,0 +1,51 @@
+/*-------------------------------------------------------------------------
+ *
+ * stringutils.h--
+ *    
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef STRINGUTILS_H
+#define STRINGUTILS_H
+
+/* use this for memory checking of alloc and free using Tcl's memory check
+  package*/
+#ifdef TCL_MEM_DEBUG
+#include <tcl.h>
+#define malloc(x) ckalloc(x)
+#define free(x) ckfree(x)
+#define realloc(x,y) ckrealloc(x,y)
+#endif
+
+/* string fiddling utilties */
+
+/* all routines assume null-terminated strings! as arguments */
+
+/* removes whitespaces from the left, right and both sides of a string */
+/* MODIFIES the string passed in and returns the head of it */
+extern char* leftTrim(char* s);  
+extern char* rightTrim(char* s);
+extern char* doubleTrim(char* s);
+
+/* dupstr : copies a string, while making room for it */
+/* the CALLER is responsible for freeing the space */
+/* returns NULL if the argument is NULL */
+extern char* dupstr(char *s);
+
+#ifdef STRINGUTILS_TEST
+extern void testStringUtils();
+#endif
+
+#ifndef NULL_STR
+#define NULL_STR (char*)0
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#endif /* STRINGUTILS_H */
diff --git a/src/interfaces/libpgtcl/Makefile b/src/interfaces/libpgtcl/Makefile
new file mode 100644 (file)
index 0000000..2e5bf8c
--- /dev/null
@@ -0,0 +1,38 @@
+#-------------------------------------------------------------------------
+#
+# Makefile
+#    Makefile for libpgtcl library
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+LIB=   pgtcl
+
+MKDIR= ../mk
+include $(MKDIR)/postgres.mk
+
+CFLAGS+= -I$(HEADERDIR) \
+        -I$(srcdir)/backend/include \
+        -I$(srcdir)/backend \
+        -I$(CURDIR) \
+        -I$(TCL_INCDIR)
+
+ifdef KRBVERS
+CFLAGS+= $(KRBFLAGS)
+endif
+
+LIBSRCS= pgtcl.c pgtclCmds.c pgtclId.c
+
+install-headers:
+       $(INSTALL) $(INSTLOPTS) libpgtcl.h $(HEADERDIR)/libpgtcl.h
+
+
+install:: install-headers
+
+include $(MKDIR)/postgres.lib.mk
+
diff --git a/src/interfaces/libpgtcl/README b/src/interfaces/libpgtcl/README
new file mode 100644 (file)
index 0000000..d2e2d59
--- /dev/null
@@ -0,0 +1,7 @@
+libpgtcl is a library that implements Tcl commands for front-end
+clients to interact with the Postgres95 backend.  See libpgtcl.doc for
+details.
+
+For an example of how to build a new tclsh to use libpgtcl, see the
+directory ../bin/pgtclsh
+
diff --git a/src/interfaces/libpgtcl/libpgtcl.h b/src/interfaces/libpgtcl/libpgtcl.h
new file mode 100644 (file)
index 0000000..34736fe
--- /dev/null
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * libpgtcl.h--
+ *    libpgtcl is a tcl package for front-ends to interface with pglite
+ *   It's the tcl equivalent of the old libpq C interface.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef LIBPGTCL_H
+#define LIBPGTCL_H
+
+#include "tcl.h"
+
+extern int Pg_Init (Tcl_Interp *interp);
+
+#endif /* LIBPGTCL_H */
diff --git a/src/interfaces/libpgtcl/pgtcl.c b/src/interfaces/libpgtcl/pgtcl.c
new file mode 100644 (file)
index 0000000..d57fc21
--- /dev/null
@@ -0,0 +1,105 @@
+/*-------------------------------------------------------------------------
+ *
+ * pgtcl.c--
+ *    
+ *    libpgtcl is a tcl package for front-ends to interface with pglite
+ *   It's the tcl equivalent of the old libpq C interface.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "tcl.h"
+#include "libpgtcl.h"
+#include "pgtclCmds.h"
+
+/*
+ * PG_Init 
+ *    initialization package for the PGLITE Tcl package
+ *
+ */
+
+int
+Pg_Init (Tcl_Interp *interp)
+{
+  /* register all pgtcl commands */
+
+  Tcl_CreateCommand(interp,
+                   "pg_connect",
+                   Pg_connect,
+                   (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+  Tcl_CreateCommand(interp,
+                   "pg_disconnect",
+                   Pg_disconnect,
+                   (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+  
+  Tcl_CreateCommand(interp,
+                   "pg_exec",
+                   Pg_exec,
+                   (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+  
+  Tcl_CreateCommand(interp,
+                   "pg_result",
+                   Pg_result,
+                   (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+  
+  Tcl_CreateCommand(interp,
+                   "pg_lo_open",
+                   Pg_lo_open,
+                   (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+  
+  Tcl_CreateCommand(interp,
+                   "pg_lo_close",
+                   Pg_lo_close,
+                   (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+  Tcl_CreateCommand(interp,
+                   "pg_lo_read",
+                   Pg_lo_read,
+                   (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+  Tcl_CreateCommand(interp,
+                   "pg_lo_write",
+                   Pg_lo_write,
+                   (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+  Tcl_CreateCommand(interp,
+                   "pg_lo_lseek",
+                   Pg_lo_lseek,
+                   (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+  Tcl_CreateCommand(interp,
+                   "pg_lo_creat",
+                   Pg_lo_creat,
+                   (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+  Tcl_CreateCommand(interp,
+                   "pg_lo_tell",
+                   Pg_lo_tell,
+                   (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+  Tcl_CreateCommand(interp,
+                   "pg_lo_unlink",
+                   Pg_lo_unlink,
+                   (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+
+  Tcl_CreateCommand(interp,
+                   "pg_lo_import",
+                   Pg_lo_import,
+                   (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+  
+  Tcl_CreateCommand(interp,
+                   "pg_lo_export",
+                   Pg_lo_export,
+                   (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);
+  
+  return TCL_OK;
+}
+
+
diff --git a/src/interfaces/libpgtcl/pgtclCmds.c b/src/interfaces/libpgtcl/pgtclCmds.c
new file mode 100644 (file)
index 0000000..8cd7695
--- /dev/null
@@ -0,0 +1,812 @@
+/*-------------------------------------------------------------------------
+ *
+ * pgtclCmds.c--
+ *    C functions which implement pg_* tcl commands
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <tcl.h>
+#include <string.h>
+#include "libpq/pqcomm.h"
+#include "libpq-fe.h"
+#include "libpq/libpq-fs.h"
+#include "pgtclCmds.h"
+#include "pgtclId.h"
+
+/**********************************
+ * pg_connect
+ make a connection to a backend.  
+ syntax:
+ pg_connect dbName [-host hostName] [-port portNumber] [-tty pqtty]]
+ the return result is either an error message or a handle for a database
+ connection.  Handles start with the prefix "pgp"
+ **********************************/
+
+int
+Pg_connect(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+    char *pghost = NULL;
+    char *pgtty = NULL;
+    char *pgport = NULL;
+    char *pgoptions = NULL;
+    char *dbName; 
+    int i;
+    PGconn *conn;
+  
+    if (argc == 1) {
+       Tcl_AppendResult(interp, "pg_connect: database name missing\n", 0);
+       Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]", 0);
+       return TCL_ERROR;
+    
+    }
+    if (argc > 2) { 
+       /* parse for pg environment settings */
+       i = 2;
+       while (i+1 < argc) {
+           if (strcmp(argv[i], "-host") == 0) {
+               pghost = argv[i+1];
+               i += 2;
+           }
+           else
+               if (strcmp(argv[i], "-port") == 0) {
+                   pgport = argv[i+1];
+                   i += 2;
+               }
+               else
+                   if (strcmp(argv[i], "-tty") == 0) {
+                       pgtty = argv[i+1];
+                       i += 2;
+                   }
+                   else if (strcmp(argv[i], "-options") == 0) {
+                       pgoptions = argv[i+1];
+                       i += 2;
+                   }
+                   else {
+                       Tcl_AppendResult(interp, "Bad option to pg_connect : \n",
+                                        argv[i], 0);
+                       Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]",0);
+                       return TCL_ERROR;
+                   }
+       } /* while */
+       if ((i % 2 != 0) || i != argc) {
+           Tcl_AppendResult(interp, "wrong # of arguments to pg_connect\n", argv[i],0);
+           Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]",0);
+           return TCL_ERROR;
+       }
+    }
+    dbName = argv[1];
+
+    conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
+    if (conn->status == CONNECTION_OK) {
+       PgSetId(interp->result, (void*)conn);
+       return TCL_OK;
+    }
+    else {
+       Tcl_AppendResult(interp, "Connection to ", dbName, " failed\n", 0);
+       Tcl_AppendResult(interp, conn->errorMessage, 0);
+       return TCL_ERROR;
+    }
+}
+
+
+/**********************************
+ * pg_disconnect
+ close a backend connection
+ syntax:
+ pg_disconnect connection
+ The argument passed in must be a connection pointer.
+ **********************************/
+
+int
+Pg_disconnect(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+    PGconn *conn;
+    char* connPtrName;
+
+    if (argc != 2) {
+       Tcl_AppendResult(interp, "Wrong # of arguments\n", "pg_disconnect connection", 0);
+       return TCL_ERROR;
+    }
+
+    connPtrName = argv[1];
+    if (! PgValidId(connPtrName)) {
+       Tcl_AppendResult(interp, "First argument is not a valid connection\n", 0);
+       return TCL_ERROR;
+    }
+  
+    conn = (PGconn*)PgGetId(connPtrName);
+    PQfinish(conn);
+    return TCL_OK;
+}
+
+/**********************************
+ * pg_exec
+ send a query string to the backend connection
+ syntax:
+ pg_exec connection query
+ the return result is either an error message or a handle for a query
+ result.  Handles start with the prefix "pgp"
+ **********************************/
+
+int
+Pg_exec(AlientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+    PGconn *conn;
+    PGresult *result;
+    char* connPtrName;
+
+    if (argc != 3) {
+       Tcl_AppendResult(interp, "Wrong # of arguments\n",
+                        "pg_exec connection queryString", 0);
+       return TCL_ERROR;
+    }
+    connPtrName = argv[1];
+
+    if (! PgValidId(connPtrName)) {
+       Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+       return TCL_ERROR;
+    }
+  
+    conn = (PGconn*)PgGetId(connPtrName);
+    result = PQexec(conn, argv[2]);
+    if (result) {
+       PgSetId(interp->result, (void*)result);
+       return TCL_OK;
+    }
+    else {
+       /* error occurred during the query */
+       Tcl_SetResult(interp, conn->errorMessage, TCL_STATIC);
+       return TCL_ERROR;
+    }
+    /* check return status of result */
+    return TCL_OK;
+}
+
+/**********************************
+ * pg_result
+ get information about the results of a query
+ syntax:
+ pg_result result ?option? 
+ the options are:
+ -status  
+ the status of the result
+ -conn
+ the connection that produced the result
+ -assign arrayName
+ assign the results to an array
+ -numTuples
+ the number of tuples in the query
+ -attributes
+ returns a list of the name/type pairs of the tuple attributes
+ -getTuple tupleNumber
+ returns the values of the tuple in a list
+ -clear 
+ clear the result buffer. Do not reuse after this
+ **********************************/
+int
+Pg_result(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+    char* resultPtrName;
+    PGresult *result;
+    char *opt;
+    int i;
+    int tupno;
+    char arrayInd[MAX_MESSAGE_LEN];
+    char *arrVar;
+
+    if (argc != 3 && argc != 4) {
+       Tcl_AppendResult(interp, "Wrong # of arguments\n",0);
+       goto Pg_result_errReturn;
+    }
+
+    resultPtrName = argv[1];
+    if (! PgValidId(resultPtrName)) {
+       Tcl_AppendResult(interp, "First argument is not a valid query result\n", 0);
+       return TCL_ERROR;
+    }
+
+    result = (PGresult*)PgGetId(resultPtrName);
+    opt = argv[2];
+
+    if (strcmp(opt, "-status") == 0) {
+       Tcl_AppendResult(interp, pgresStatus[PQresultStatus(result)], 0);
+       return TCL_OK;
+    }
+    else if (strcmp(opt, "-oid") == 0) {
+       Tcl_AppendResult(interp, PQoidStatus(result), 0);
+       return TCL_OK;
+    }
+    else if (strcmp(opt, "-conn") == 0) {
+       PgSetId(interp->result, (void*)result->conn);
+       return TCL_OK;
+    }
+    else if (strcmp(opt, "-clear") == 0) {
+       PQclear(result);
+       return TCL_OK;
+    }
+    else if (strcmp(opt, "-numTuples") == 0) {
+       sprintf(interp->result, "%d", PQntuples(result));
+       return TCL_OK;
+    }
+    else if (strcmp(opt, "-assign") == 0) {
+       if (argc != 4) {
+           Tcl_AppendResult(interp, "-assign option must be followed by a variable name",0);
+           return TCL_ERROR;
+       }
+       arrVar = argv[3];
+       /* this assignment assigns the table of result tuples into a giant
+          array with the name given in the argument,
+          the indices of the array or (tupno,attrName)*/
+       for (tupno = 0; tupno<PQntuples(result); tupno++) {
+           for (i=0;i<PQnfields(result);i++) {
+               sprintf(arrayInd, "%d,%s", tupno, PQfname(result,i));
+               Tcl_SetVar2(interp, arrVar, arrayInd, 
+                           PQgetvalue(result,tupno,i),
+                           TCL_LEAVE_ERR_MSG);
+           }
+       }
+       Tcl_AppendResult(interp, arrVar, 0);
+       return TCL_OK;
+    }
+    else if (strcmp(opt, "-getTuple") == 0) {
+       if (argc != 4) {
+           Tcl_AppendResult(interp, "-getTuple option must be followed by a tuple number",0);
+           return TCL_ERROR;
+       }
+       tupno = atoi(argv[3]);
+       
+       if (tupno >= PQntuples(result)) {
+           Tcl_AppendResult(interp, "argument to getTuple cannot exceed number of tuples - 1",0);
+           return TCL_ERROR;
+       }
+
+/*     Tcl_AppendResult(interp, PQgetvalue(result,tupno,0),NULL); */
+        Tcl_AppendElement(interp, PQgetvalue(result,tupno,0));
+       for (i=1;i<PQnfields(result);i++) {
+/*       Tcl_AppendResult(interp, " ", PQgetvalue(result,tupno,i),NULL);*/
+         Tcl_AppendElement(interp, PQgetvalue(result,tupno,i));
+       }
+       return TCL_OK;
+    }
+    else if (strcmp(opt, "-attributes") == 0) {
+      Tcl_AppendResult(interp, PQfname(result,0),NULL);
+      for (i=1;i<PQnfields(result);i++) {
+       Tcl_AppendResult(interp, " ", PQfname(result,i), NULL);
+      }
+      return TCL_OK;
+    }
+    else   { 
+       Tcl_AppendResult(interp, "Invalid option",0);
+       goto Pg_result_errReturn;
+    }
+  
+
+ Pg_result_errReturn:
+    Tcl_AppendResult(interp, 
+                    "pg_result result ?option? where ?option is\n", 
+                    "\t-status\n",
+                    "\t-conn\n",
+                    "\t-assign arrayVarName\n",
+                    "\t-numTuples\n",
+                    "\t-attributes\n"
+                    "\t-getTuple tupleNumber\n",
+                    "\t-clear\n",
+                    "\t-oid\n",
+                    0);
+    return TCL_ERROR;
+  
+
+}
+
+/**********************************
+ * pg_lo_open
+     open a large object
+ syntax:
+ pg_lo_open conn objOid mode 
+
+ where mode can be either 'r', 'w', or 'rw'
+**********************/
+
+int
+Pg_lo_open(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+    PGconn *conn;
+    char* connPtrName;
+    int lobjId;
+    int mode;
+    int fd;
+
+    if (argc != 4) {
+       Tcl_AppendResult(interp, "Wrong # of arguments\n",
+                        "pg_lo_open connection lobjOid mode", 0);
+       return TCL_ERROR;
+    }
+    connPtrName = argv[1];
+    if (! PgValidId(connPtrName)) {
+       Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+       return TCL_ERROR;
+    }
+  
+    conn = (PGconn*)PgGetId(connPtrName);
+    lobjId = atoi(argv[2]);
+    if (strlen(argv[3]) < 1 ||
+       strlen(argv[3]) > 2)
+       {
+       Tcl_AppendResult(interp,"mode argument must be 'r', 'w', or 'rw'",0);
+        return TCL_ERROR;
+    }
+    switch (argv[3][0]) {
+    case 'r':
+    case 'R':
+       mode = INV_READ;
+       break;
+    case 'w':
+    case 'W':
+       mode = INV_WRITE;
+       break;
+    default:
+       Tcl_AppendResult(interp,"mode argument must be 'r', 'w', or 'rw'",0);
+        return TCL_ERROR;
+   }
+    switch (argv[3][1]) {
+    case '\0':
+       break;
+    case 'r':
+    case 'R':
+       mode = mode & INV_READ;
+       break;
+    case 'w':
+    case 'W':
+       mode = mode & INV_WRITE;
+       break;
+    default:
+       Tcl_AppendResult(interp,"mode argument must be 'r', 'w', or 'rw'",0);
+        return TCL_ERROR;
+    }
+
+    fd = lo_open(conn,lobjId,mode);
+    sprintf(interp->result,"%d",fd);
+    return TCL_OK;
+}
+
+/**********************************
+ * pg_lo_close
+     close a large object
+ syntax:
+ pg_lo_close conn fd 
+
+**********************/
+int
+Pg_lo_close(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+    PGconn *conn;
+    char* connPtrName;
+    int fd;
+
+    if (argc != 3) {
+       Tcl_AppendResult(interp, "Wrong # of arguments\n",
+                        "pg_lo_close connection fd", 0);
+       return TCL_ERROR;
+    }
+
+    connPtrName = argv[1];
+    if (! PgValidId(connPtrName)) {
+       Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+       return TCL_ERROR;
+    }
+  
+    conn = (PGconn*)PgGetId(connPtrName);
+    fd = atoi(argv[2]);
+    sprintf(interp->result,"%d",lo_close(conn,fd));
+    return TCL_OK;
+}
+
+/**********************************
+ * pg_lo_read
+     reads at most len bytes from a large object into a variable named
+ bufVar
+ syntax:
+ pg_lo_read conn fd bufVar len
+
+ bufVar is the name of a variable in which to store the contents of the read
+
+**********************/
+int
+Pg_lo_read(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+    PGconn *conn;
+    char* connPtrName;
+    int fd;
+    int nbytes = 0;
+    char *buf;
+    char *bufVar;
+    int len;
+
+    if (argc != 5) {
+       Tcl_AppendResult(interp, "Wrong # of arguments\n",
+                        " pg_lo_read conn fd bufVar len", 0);
+       return TCL_ERROR;
+    }
+
+    connPtrName = argv[1];
+    if (! PgValidId(connPtrName)) {
+       Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+       return TCL_ERROR;
+    }
+  
+    conn = (PGconn*)PgGetId(connPtrName);
+    fd = atoi(argv[2]);
+
+    bufVar = argv[3];
+
+    len = atoi(argv[4]);
+
+    if (len <= 0) {
+       sprintf(interp->result,"%d",nbytes);
+       return TCL_OK;
+    }
+    buf = malloc(sizeof(len+1));
+
+    nbytes = lo_read(conn,fd,buf,len);
+
+    Tcl_SetVar(interp,bufVar,buf,TCL_LEAVE_ERR_MSG);
+    sprintf(interp->result,"%d",nbytes);
+    free(buf);
+    return TCL_OK;
+    
+}
+
+/***********************************
+Pg_lo_write
+   write at most len bytes to a large object 
+
+ syntax:
+ pg_lo_write conn fd buf len
+
+***********************************/
+int
+Pg_lo_write(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+    PGconn *conn;
+    char *connPtrName;
+    char *buf;
+    int fd;
+    int nbytes = 0;
+    int len;
+
+    if (argc != 5) {
+       Tcl_AppendResult(interp, "Wrong # of arguments\n",
+                        "pg_lo_write conn fd buf len", 0);
+       return TCL_ERROR;
+    }
+
+    connPtrName = argv[1];
+    if (! PgValidId(connPtrName)) {
+       Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+       return TCL_ERROR;
+    }
+  
+    conn = (PGconn*)PgGetId(connPtrName);
+    fd = atoi(argv[2]);
+
+    buf = argv[3];
+
+    len = atoi(argv[4]);
+
+    if (len <= 0) {
+       sprintf(interp->result,"%d",nbytes);
+       return TCL_OK;
+    }
+
+    nbytes = lo_write(conn,fd,buf,len);
+    sprintf(interp->result,"%d",nbytes);
+    return TCL_OK;
+}
+
+/***********************************
+Pg_lo_lseek
+    seek to a certain position in a large object
+
+syntax
+  pg_lo_lseek conn fd offset whence
+
+whence can be either
+"SEEK_CUR", "SEEK_END", or "SEEK_SET"
+***********************************/
+int
+Pg_lo_lseek(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+    PGconn *conn;
+    char* connPtrName;
+    int fd;
+    char *whenceStr;
+    int offset, whence;
+
+    if (argc != 5) {
+       Tcl_AppendResult(interp, "Wrong # of arguments\n",
+                        "pg_lo_lseek conn fd offset whence", 0);
+       return TCL_ERROR;
+    }
+
+    connPtrName = argv[1];
+    if (! PgValidId(connPtrName)) {
+       Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+       return TCL_ERROR;
+    }
+  
+    conn = (PGconn*)PgGetId(connPtrName);
+    fd = atoi(argv[2]);
+
+    offset = atoi(argv[3]);
+
+    whenceStr = argv[4];
+    if (strcmp(whenceStr,"SEEK_SET") == 0) {
+       whence = SEEK_SET;
+    } else if (strcmp(whenceStr,"SEEK_CUR") == 0) {
+       whence = SEEK_CUR;
+    } else if (strcmp(whenceStr,"SEEK_END") == 0) {
+       whence = SEEK_END;
+    } else {
+       Tcl_AppendResult(interp, "the whence argument to Pg_lo_lseek must be SEEK_SET, SEEK_CUR or SEEK_END",0);
+       return TCL_ERROR;
+    }
+       
+    sprintf(interp->result,"%d",lo_lseek(conn,fd,offset,whence));
+    return TCL_OK;
+}
+
+
+/***********************************
+Pg_lo_creat
+   create a new large object with mode
+
+ syntax:
+   pg_lo_creat conn mode
+
+mode can be any OR'ing together of INV_READ, INV_WRITE, and INV_ARCHIVE,
+for now, we don't support any additional storage managers.
+
+***********************************/
+int
+Pg_lo_creat(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+    PGconn *conn;
+    char* connPtrName;
+    char *modeStr;
+    char *modeWord;
+    int mode;
+
+    if (argc != 3) {
+       Tcl_AppendResult(interp, "Wrong # of arguments\n",
+                        "pg_lo_creat conn mode", 0);
+       return TCL_ERROR;
+    }
+
+    connPtrName = argv[1];
+    if (! PgValidId(connPtrName)) {
+       Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+       return TCL_ERROR;
+    }
+  
+    conn = (PGconn*)PgGetId(connPtrName);
+
+    modeStr = argv[2];
+
+    modeWord = strtok(modeStr,"|");
+    if (strcmp(modeWord,"INV_READ") == 0) {
+       mode = INV_READ;
+    } else if (strcmp(modeWord,"INV_WRITE") == 0) {
+       mode = INV_WRITE;
+    } else if (strcmp(modeWord,"INV_ARCHIVE") == 0) {
+       mode = INV_ARCHIVE;
+    } else {
+       Tcl_AppendResult(interp,
+                        "invalid mode argument to Pg_lo_creat\nmode argument must be some OR'd combination of INV_READ, INV_WRITE, and INV_ARCHIVE",
+                        0);
+       return TCL_ERROR;
+    }
+
+    while ( (modeWord = strtok((char*)NULL, "|")) != NULL) {
+       if (strcmp(modeWord,"INV_READ") == 0) {
+           mode |= INV_READ;
+       } else if (strcmp(modeWord,"INV_WRITE") == 0) {
+           mode |= INV_WRITE;
+       } else if (strcmp(modeWord,"INV_ARCHIVE") == 0) {
+           mode |= INV_ARCHIVE;
+       } else {
+           Tcl_AppendResult(interp,
+                            "invalid mode argument to Pg_lo_creat\nmode argument must be some OR'd combination of INV_READ, INV_WRITE, and INV_ARCHIVE",
+                            0);
+           return TCL_ERROR;
+       }
+    }
+    sprintf(interp->result,"%d",lo_creat(conn,mode));
+    return TCL_OK;
+}
+
+/***********************************
+Pg_lo_tell
+    returns the current seek location of the large object
+
+ syntax:
+   pg_lo_tell conn fd
+
+***********************************/
+int
+Pg_lo_tell(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+    PGconn *conn;
+    char* connPtrName;
+    int fd;
+
+    if (argc != 3) {
+       Tcl_AppendResult(interp, "Wrong # of arguments\n",
+                        "pg_lo_tell conn fd", 0);
+       return TCL_ERROR;
+    }
+
+    connPtrName = argv[1];
+    if (! PgValidId(connPtrName)) {
+       Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+       return TCL_ERROR;
+    }
+  
+    conn = (PGconn*)PgGetId(connPtrName);
+    fd = atoi(argv[2]);
+
+    sprintf(interp->result,"%d",lo_tell(conn,fd));
+    return TCL_OK;
+
+}
+
+/***********************************
+Pg_lo_unlink
+    unlink a file based on lobject id 
+
+ syntax:
+   pg_lo_unlink conn lobjId
+
+
+***********************************/
+int
+Pg_lo_unlink(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+    PGconn *conn;
+    char* connPtrName;
+    int lobjId;
+    int retval;
+
+    if (argc != 3) {
+       Tcl_AppendResult(interp, "Wrong # of arguments\n",
+                        "pg_lo_tell conn fd", 0);
+       return TCL_ERROR;
+    }
+
+    connPtrName = argv[1];
+    if (! PgValidId(connPtrName)) {
+       Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+       return TCL_ERROR;
+    }
+  
+    conn = (PGconn*)PgGetId(connPtrName);
+    lobjId = atoi(argv[2]);
+
+    retval = lo_unlink(conn,lobjId);
+    if (retval == -1) {
+       sprintf(interp->result,"Pg_lo_unlink of '%d' failed",lobjId);
+       return TCL_ERROR;
+    }
+       
+    sprintf(interp->result,"%d",retval);
+    return TCL_OK;
+}
+
+/***********************************
+Pg_lo_import
+    import a Unix file into an (inversion) large objct
+ returns the oid of that object upon success
+ returns InvalidOid upon failure
+
+ syntax:
+   pg_lo_import conn filename
+
+***********************************/
+
+int
+Pg_lo_import(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+    PGconn *conn;
+    char* connPtrName;
+    char* filename;
+    Oid lobjId;
+
+    if (argc != 3) {
+       Tcl_AppendResult(interp, "Wrong # of arguments\n",
+                        "pg_lo_import conn filename", 0);
+       return TCL_ERROR;
+    }
+
+    connPtrName = argv[1];
+    if (! PgValidId(connPtrName)) {
+       Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+       return TCL_ERROR;
+    }
+  
+    conn = (PGconn*)PgGetId(connPtrName);
+    filename = argv[2];
+
+    lobjId = lo_import(conn,filename);
+    if (lobjId == InvalidOid) {
+       sprintf(interp->result, "Pg_lo_import of '%s' failed",filename);
+       return TCL_ERROR;
+    }
+    sprintf(interp->result,"%d",lobjId);
+    return TCL_OK;
+}
+
+/***********************************
+Pg_lo_export
+    export an Inversion large object to a Unix file
+    
+ syntax:
+   pg_lo_export conn lobjId filename
+
+***********************************/
+
+int
+Pg_lo_export(ClientData cData, Tcl_Interp *interp, int argc, char* argv[])
+{
+    PGconn *conn;
+    char* connPtrName;
+    char* filename;
+    Oid lobjId;
+    int retval;
+
+    if (argc != 4) {
+       Tcl_AppendResult(interp, "Wrong # of arguments\n",
+                        "pg_lo_export conn lobjId filename", 0);
+       return TCL_ERROR;
+    }
+
+    connPtrName = argv[1];
+    if (! PgValidId(connPtrName)) {
+       Tcl_AppendResult(interp, "Argument passed in is not a valid connection\n", 0);
+       return TCL_ERROR;
+    }
+  
+    conn = (PGconn*)PgGetId(connPtrName);
+    lobjId = atoi(argv[2]);
+    filename = argv[3];
+
+    retval = lo_export(conn,lobjId,filename);
+    if (retval == -1) {
+       sprintf(interp->result, "Pg_lo_export %d %s failed",lobjId, filename);
+       return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+
+
diff --git a/src/interfaces/libpgtcl/pgtclCmds.h b/src/interfaces/libpgtcl/pgtclCmds.h
new file mode 100644 (file)
index 0000000..642badb
--- /dev/null
@@ -0,0 +1,52 @@
+/*-------------------------------------------------------------------------
+ *
+ * pgtclCmds.h--
+ *    declarations for the C functions which implement pg_* tcl commands
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef PGTCLCMDS_H
+#define PGTCLCMDS_H
+
+#include "tcl.h"
+
+/* **************************/
+/* registered Tcl functions */
+/* **************************/
+extern int Pg_connect(
+    ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_disconnect(
+    ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_exec(
+    ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_result(
+    ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_open(
+    ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_close(
+    ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_read(
+    ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_write(
+    ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_lseek(
+    ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_creat(
+    ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_tell(
+    ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_unlink(
+    ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_import(
+    ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+extern int Pg_lo_export(
+    ClientData cData, Tcl_Interp *interp, int argc, char* argv[]);
+
+
+#endif /*PGTCLCMDS_H*/
+
diff --git a/src/interfaces/libpgtcl/pgtclId.c b/src/interfaces/libpgtcl/pgtclId.c
new file mode 100644 (file)
index 0000000..c860942
--- /dev/null
@@ -0,0 +1,51 @@
+/*-------------------------------------------------------------------------
+ *
+ * pgtclId.c--
+ *    useful routines to convert between strings and pointers
+ *  Needed because everything in tcl is a string, but we want pointers
+ *  to data structures
+ *
+ *  ASSUMPTION:  sizeof(long) >= sizeof(void*)
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "tcl.h"
+
+#include "pgtclId.h"
+
+/* convert a pointer into a string */
+void
+PgSetId(char *id, void *ptr)
+{
+  (void) sprintf(id, "pgp%lx", (long) ptr);
+}
+
+
+/* get back a pointer from a string */
+void *
+PgGetId(char *id)
+{
+  long ptr; 
+  ptr = strtol(id+3, NULL, 16);
+  return (void *) ptr;
+}
+
+/* check to see if the string is a valid pgtcl pointer */
+int 
+PgValidId(char* id)
+{
+    if ( (strlen(id) > 3) && id[0]=='p' && id[1] == 'g' && id[2] == 'p')
+       return 1;
+    else
+       return 0;
+}
diff --git a/src/interfaces/libpgtcl/pgtclId.h b/src/interfaces/libpgtcl/pgtclId.h
new file mode 100644 (file)
index 0000000..affe02a
--- /dev/null
@@ -0,0 +1,18 @@
+/*-------------------------------------------------------------------------
+ *
+ * pgtclId.h--
+ *    useful routines to convert between strings and pointers
+ *  Needed because everything in tcl is a string, but often, pointers
+ *  to data structures are needed.
+ *    
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+extern void PgSetId(char *id, void *ptr);
+extern void* PgGetId(char *id);
+extern int PgValidId(char* id);
diff --git a/src/interfaces/libpq++/Makefile b/src/interfaces/libpq++/Makefile
new file mode 100644 (file)
index 0000000..526a122
--- /dev/null
@@ -0,0 +1,54 @@
+#-------------------------------------------------------------------------
+#
+# Makefile
+#    Makefile for libpq++ library
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+CPP_LIB=       true
+
+LIB=   pq++
+
+MKDIR= ../mk
+include $(MKDIR)/postgres.mk
+
+CXXFLAGS = $(CFLAGS)
+
+CXXFLAGS+= -I$(srcdir)/backend/include \
+       -I$(srcdir)/backend \
+       -I$(srcdir)/libpq \
+       -I$(CURDIR) \
+
+ifdef KRBVERS
+CXXFLAGS+= $(KRBFLAGS)
+endif
+
+
+LIBSRCS =  pgenv.cc pgconnection.cc pglobject.cc
+
+.PHONY: beforeinstall-headers install-headers
+
+ifndef NO_BEFOREINSTL
+beforeinstall-headers:
+       @-if [ ! -d $(HEADERDIR) ]; then mkdir $(HEADERDIR); fi
+else
+beforeinstall-headers: .dosomething
+endif
+
+HEADERFILES = libpq++.H
+
+install-headers: beforeinstall-headers
+       @for i in ${HEADERFILES}; do \
+               echo "Installing $(HEADERDIR)/$$i."; \
+               $(INSTALL) -c -m 444 $$i $(HEADERDIR)/$$i; \
+       done
+
+install:: install-headers
+
+include $(MKDIR)/postgres.lib.mk
diff --git a/src/interfaces/libpq++/README b/src/interfaces/libpq++/README
new file mode 100644 (file)
index 0000000..cb5d0ae
--- /dev/null
@@ -0,0 +1,22 @@
+This directory contains libpq++, the C++ language interface to POSTGRES95.
+libpq++ is implemented on of the libpq library.  Users would benefit
+from reading the chapter on libpq in the postgres95 users manual
+before using libpq++.
+
+The initial version of this implementation was done by William Wanders
+
+This is only a preliminary attempt at providing something useful for
+people who would like to use C++ to build frontend applications to
+postgres95.  The API provided herein is subject to change in later
+versions of postgres95.
+
+For details on how to to use libpq++, see the man page in the man/
+subdirectory and the test programs in the examples/ subdirectory. 
+
+libpq++ has been tested with g++, version 2.7.0
+
+- Jolly Chen
+
+Tue Sep  5 11:09:51 PDT 1995
diff --git a/src/interfaces/libpq++/examples/Makefile b/src/interfaces/libpq++/examples/Makefile
new file mode 100644 (file)
index 0000000..5e51d91
--- /dev/null
@@ -0,0 +1,70 @@
+#
+# Makefile for example programs
+#
+
+CPP_PROG = true
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+
+CXXFLAGS+= -I$(HEADERDIR) -I$(srcdir)/libpq -I$(srcdir)/backend \
+        -I$(srcdir)/backend/include
+
+LD_ADD+=-L$(LIBDIR) -lpq++ -lpq
+
+#
+# And where libpq goes, so goes the authentication stuff...
+#
+ifdef KRBVERS
+LD_ADD+= $(KRBLIBS)
+CXXFLAGS+= $(KRBFLAGS)
+endif
+
+P0_PROG:= testlibpq0
+P0_OBJS:= testlibpq0.o
+
+$(P0_PROG):  $(addprefix $(objdir)/,$(P0_OBJS))
+       $(CXX) $(CDEBUG) -o $(objdir)/$(@F) $< $(LD_ADD)
+
+P1_PROG:= testlibpq1
+P1_OBJS:= testlibpq1.o
+
+$(P1_PROG):  $(addprefix $(objdir)/,$(P1_OBJS))
+       $(CXX) $(CDEBUG) -o $(objdir)/$(@F) $< $(LD_ADD)
+
+P2_PROG:= testlibpq2
+P2_OBJS:= testlibpq2.o
+
+$(P2_PROG):  $(addprefix $(objdir)/,$(P2_OBJS))
+       $(CXX) $(CDEBUG) -o $(objdir)/$(@F) $< $(LD_ADD)
+
+P3_PROG:= testlibpq3
+P3_OBJS:= testlibpq3.o
+
+$(P3_PROG):  $(addprefix $(objdir)/,$(P3_OBJS))
+       $(CXX) $(CDEBUG) -o $(objdir)/$(@F) $< $(LD_ADD)
+
+P4_PROG:= testlibpq4
+P4_OBJS:= testlibpq4.o
+
+$(P4_PROG):  $(addprefix $(objdir)/,$(P4_OBJS))
+       $(CXX) $(CDEBUG) -o $(objdir)/$(@F) $< $(LD_ADD)
+
+P5_PROG:= testlo
+P5_OBJS:= testlo.o
+
+$(P5_PROG):  $(addprefix $(objdir)/,$(P5_OBJS))
+       $(CXX) $(CDEBUG) -o $(objdir)/$(@F) $< $(LD_ADD)
+
+OBJS:= $(P0_OBJS) $(P1_OBJS) $(P2_OBJS) $(P3_OBJS) $(P4_OBJS) $(P5_OBJS)
+PROGS:= $(P0_PROG) $(P1_PROG) $(P2_PROG) $(P3_PROG) $(P4_PROG) $(P5_PROG)
+
+CLEANFILES+= $(OBJS) $(PROGS)
+
+all:: $(PROGS)
+
+install:: $(PROGS)
+       @for i in ${PROGS}; do \
+               echo "Installing $$i"; \
+               $(INSTALL) $(objdir)/$$i $(DESTDIR)$(BINDIR)/$$i;\
+       done
diff --git a/src/interfaces/libpq++/examples/testlibpq0.cc b/src/interfaces/libpq++/examples/testlibpq0.cc
new file mode 100644 (file)
index 0000000..978af0a
--- /dev/null
@@ -0,0 +1,49 @@
+/*-------------------------------------------------------------------------
+ *
+ * testlibpq0.c--
+ *    small test program for libpq++, 
+ * small interactive loop where queries can be entered interactively
+ * and sent to the backend
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <stdio.h>
+#include "libpq++.H"
+
+int 
+main(int argc, char** argv)
+{
+  ExecStatusType status;
+  PGenv env;
+  PGdatabase* data;
+
+  char buf[10000];
+  int done = 0;
+  data = new PGdatabase(&env, "template1");
+
+  if (data->status() == CONNECTION_BAD)
+    printf("connection was unsuccessful\n%s\n", data->errormessage());
+  else
+    printf("connection successful\n");
+
+  while (!done)
+    {
+      printf("> ");fflush(stdout);
+      if (gets(buf) && buf[0]!='\0')
+       if((status = data->exec(buf)) == PGRES_TUPLES_OK) 
+            data->printtuples(stdout, 1, "|", 1, 0);
+       else
+            printf("status = %s\nerrorMessage = %s\n", status,
+                                                  data->errormessage());
+      else
+       done = 1;
+    }
+}
diff --git a/src/interfaces/libpq++/examples/testlibpq1.cc b/src/interfaces/libpq++/examples/testlibpq1.cc
new file mode 100644 (file)
index 0000000..1d71f79
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * testlibpq.cc
+ *     Test the C++ version of LIBPQ, the POSTGRES frontend library.
+ *
+ *  queries the template1 database for a list of database names 
+ *
+ */
+#include <stdio.h>
+#include "libpq++.H"
+
+main()
+{
+  char* dbName;
+  int nFields;
+  int i,j;
+
+  /* begin, by creating the parameter environtment for a backend
+     connection. When no parameters are given then the system will
+     try to use reasonable defaults by looking up environment variables 
+     or, failing that, using hardwired constants */
+  PGenv env;
+  PGdatabase* data;
+
+  /* Select a database */
+  dbName = "template1";
+
+  /* make a connection to the database */
+  data = new PGdatabase(&env, dbName);
+
+  /* check to see that the backend connection was successfully made */
+  if (data->status() == CONNECTION_BAD) {
+    fprintf(stderr,"Connection to database '%s' failed.\n", dbName);
+    fprintf(stderr,"%s",data->errormessage());
+    delete data;
+    exit(1);
+  }
+
+  /* start a transaction block */
+  if(data->exec("BEGIN") != PGRES_COMMAND_OK) {
+    fprintf(stderr,"BEGIN command failed\n");
+    delete data;
+    exit(1);
+  }
+
+  /* fetch instances from the pg_database, the system catalog of databases*/
+  if (data->exec("DECLARE myportal CURSOR FOR select * from pg_database")
+      != PGRES_COMMAND_OK) {
+    fprintf(stderr,"DECLARE CURSOR command failed\n");
+    delete data;
+    exit(1);
+  }
+
+  if(data->exec("FETCH ALL in myportal") != PGRES_TUPLES_OK) {
+    fprintf(stderr,"FETCH ALL command didn't return tuples properly\n");
+    delete data;
+    exit(1);
+  }
+  /* first, print out the attribute names */
+  nFields = data->nfields();
+  for (i=0; i < nFields; i++) {
+    printf("%-15s",data->fieldname(i));
+  }
+  printf("\n\n");
+
+  /* next, print out the instances */
+  for (i=0; i < data->ntuples(); i++) {
+    for (j=0  ; j < nFields; j++) {
+      printf("%-15s", data->getvalue(i,j));
+    }
+    printf("\n");
+  }
+
+  /* close the portal */
+  data->exec("CLOSE myportal");
+
+  /* end the transaction */
+  data->exec("END");
+
+  /* close the connection to the database and cleanup */
+  delete data;
+}
+  
+
diff --git a/src/interfaces/libpq++/examples/testlibpq2.cc b/src/interfaces/libpq++/examples/testlibpq2.cc
new file mode 100644 (file)
index 0000000..8eafea0
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * testlibpq2.cc
+ *     Test of the asynchronous notification interface
+ *
+   populate a database with the following:
+
+CREATE TABLE TBL1 (i int4);
+
+CREATE TABLE TBL2 (i int4);
+
+CREATE RULE r1 AS ON INSERT TO TBL1 DO [INSERT INTO TBL2 values (new.i); NOTIFY TBL2];
+
+ * Then start up this program
+ * After the program has begun, do
+
+INSERT INTO TBL1 values (10);
+
+ *
+ *
+ */
+#include <stdio.h>
+#include "libpq++.H"
+
+main()
+{
+  char* dbName;
+  int nFields;
+  int i,j;
+
+  /* begin, by creating the parameter environtment for a backend
+     connection. When no parameters are given then the system will
+     try to use reasonable defaults by looking up environment variables 
+     or, failing that, using hardwired constants */
+  PGenv env;
+  PGdatabase* data;
+  PGnotify* notify;
+
+  dbName = getenv("USER"); /* change this to the name of your test database */
+
+  /* make a connection to the database */
+  data = new PGdatabase(&env, dbName);
+
+  /* check to see that the backend connection was successfully made */
+  if (data->status() == CONNECTION_BAD) {
+    fprintf(stderr,"Connection to database '%s' failed.\n", dbName);
+    fprintf(stderr,"%s",data->errormessage());
+    delete data;
+    exit(1);
+  }
+
+  if (data->exec("LISTEN TBL2") != PGRES_COMMAND_OK) {
+    fprintf(stderr,"LISTEN command failed\n");
+    delete data;
+    exit(1);
+  }
+
+  while (1) {
+      /* check for asynchronous returns */
+      notify = data->notifies();
+      if (notify) {
+         fprintf(stderr,
+                 "ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
+                 notify->relname, notify->be_pid);
+         free(notify);
+         break;
+      }
+  }
+      
+  /* close the connection to the database and cleanup */
+  delete data;
+}
diff --git a/src/interfaces/libpq++/examples/testlibpq2.sql b/src/interfaces/libpq++/examples/testlibpq2.sql
new file mode 100644 (file)
index 0000000..f9c7410
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE TABLE TBL1 (i int4);
+
+CREATE TABLE TBL2 (i int4);
+
+CREATE RULE r1 AS ON INSERT TO TBL1 DO [INSERT INTO TBL2 values (new.i); NOTIFY TBL2];
diff --git a/src/interfaces/libpq++/examples/testlibpq3.cc b/src/interfaces/libpq++/examples/testlibpq3.cc
new file mode 100644 (file)
index 0000000..1146dff
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * testlibpq3.cc
+ *     Test the C++ version of LIBPQ, the POSTGRES frontend library.
+ *   tests the binary cursor interface
+ *
+ *
+ *
+ populate a database by doing the following:
+CREATE TABLE test1 (i int4, d float4, p polygon);
+
+INSERT INTO test1 values (1, 3.567, '(3.0, 4.0, 1.0, 2.0)'::polygon);
+
+INSERT INTO test1 values (2, 89.05, '(4.0, 3.0, 2.0, 1.0)'::polygon);
+
+ the expected output is:
+
+tuple 0: got
+ i = (4 bytes) 1,
+ d = (4 bytes) 3.567000,
+ p = (4 bytes) 2 points         boundbox = (hi=3.000000/4.000000, lo = 1.000000,2.000000)
+tuple 1: got
+ i = (4 bytes) 2,
+ d = (4 bytes) 89.050003,
+ p = (4 bytes) 2 points         boundbox = (hi=4.000000/3.000000, lo = 2.000000,1.000000)
+
+ *
+ */
+#include <stdio.h>
+#include "libpq++.H"
+extern "C" {
+#include "utils/geo-decls.h" /* for the POLYGON type */
+}
+
+main()
+{
+  char* dbName;
+  int nFields;
+  int i,j;
+  int i_fnum, d_fnum, p_fnum;
+
+  /* begin, by creating the parameter environtment for a backend
+     connection. When no parameters are given then the system will
+     try to use reasonable defaults by looking up environment variables 
+     or, failing that, using hardwired constants */
+  PGenv env;
+  PGdatabase* data;
+
+  dbName = getenv("USER"); /* change this to the name of your test database */
+
+  /* make a connection to the database */
+  data = new PGdatabase(&env, dbName);
+
+  /* check to see that the backend connection was successfully made */
+  if (data->status() == CONNECTION_BAD) {
+    fprintf(stderr,"Connection to database '%s' failed.\n", dbName);
+    fprintf(stderr,"%s",data->errormessage());
+    delete data;
+    exit(1);
+  }
+
+  /* start a transaction block */
+  if (data->exec("BEGIN") != PGRES_COMMAND_OK) {
+    fprintf(stderr,"BEGIN command failed\n");
+    delete data;
+    exit(1);
+  }
+
+  /* fetch instances from the pg_database, the system catalog of databases*/
+  if (data->exec("DECLARE mycursor BINARY CURSOR FOR select * from test1")
+      != PGRES_COMMAND_OK) {
+    fprintf(stderr,"DECLARE CURSOR command failed\n");
+    delete data;
+    exit(1);
+  }
+
+  if (data->exec("FETCH ALL in mycursor") != PGRES_TUPLES_OK) {
+    fprintf(stderr,"FETCH ALL command didn't return tuples properly\n");
+    delete data;
+    exit(1);
+  }
+  i_fnum = data->fieldnum("i");
+  d_fnum = data->fieldnum("d");
+  p_fnum = data->fieldnum("p");
+  
+/*
+  for (i=0;i<3;i++) {
+      printf("type[%d] = %d, size[%d] = %d\n",
+            i, data->fieldtype(i), 
+            i, data->fieldsize(i));
+  }
+*/
+
+  for (i=0; i < data->ntuples(); i++) {
+    int *ival; 
+    float *dval;
+    int plen;
+    POLYGON* pval;
+    /* we hard-wire this to the 3 fields we know about */
+    ival = (int*)data->getvalue(i,i_fnum);
+    dval = (float*)data->getvalue(i,d_fnum);
+    plen = data->getlength(i,p_fnum);
+
+    /* plen doesn't include the length field so need to increment by VARHDSZ*/
+    pval = (POLYGON*) malloc(plen + VARHDRSZ); 
+    pval->size = plen;
+    memmove((char*)&pval->npts, data->getvalue(i,p_fnum), plen);
+    printf("tuple %d: got\n", i);
+    printf(" i = (%d bytes) %d,\n",
+          data->getlength(i,i_fnum), *ival);
+    printf(" d = (%d bytes) %f,\n",
+          data->getlength(i,d_fnum), *dval);
+    printf(" p = (%d bytes) %d points \tboundbox = (hi=%f/%f, lo = %f,%f)\n",
+          data->getlength(i,d_fnum),
+          pval->npts,
+          pval->boundbox.xh,
+          pval->boundbox.yh,
+          pval->boundbox.xl,
+          pval->boundbox.yl);
+  }
+  
+  /* close the portal */
+  data->exec("CLOSE mycursor");
+
+  /* end the transaction */
+  data->exec("END");
+
+  /* close the connection to the database and cleanup */
+  delete data;
+}
diff --git a/src/interfaces/libpq++/examples/testlibpq3.sql b/src/interfaces/libpq++/examples/testlibpq3.sql
new file mode 100644 (file)
index 0000000..f024c0b
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE TABLE test1 (i int4, d float4, p polygon);
+
+INSERT INTO test1 values (1, 3.567, '(3.0, 4.0, 1.0, 2.0)'::polygon);
+
+INSERT INTO test1 values (2, 89.05, '(4.0, 3.0, 2.0, 1.0)'::polygon);
+
diff --git a/src/interfaces/libpq++/examples/testlibpq4.cc b/src/interfaces/libpq++/examples/testlibpq4.cc
new file mode 100644 (file)
index 0000000..9d5ca3e
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * testlibpq4.cc
+ *     Test the C++ version of LIBPQ, the POSTGRES frontend library.
+ * tests the copy in features
+ *
+ */
+#include <stdio.h>
+#include "libpq++.H"
+
+#define DEBUG printf("Got here %d\n", __LINE__);
+main()
+{
+  char* dbName;
+  int nFields;
+  int i,j;
+
+  /* begin, by creating the parameter environment for a backend
+     connection. When no parameters are given then the system will
+     try to use reasonable defaults by looking up environment variables 
+     or, failing that, using hardwired constants */
+  PGenv env;
+  PGdatabase* data;
+
+  dbName = getenv("USER"); /* change this to the name of your test database */
+
+  /* make a connection to the database */
+  data = new PGdatabase(&env, dbName);
+
+  /* check to see that the backend connection was successfully made */
+  if (data->status() == CONNECTION_BAD) {
+    fprintf(stderr,"Connection to database '%s' failed.\n", dbName);
+    fprintf(stderr,"%s",data->errormessage());
+    delete data;
+    exit(1);
+  }
+
+  /* start a transaction block */
+  if(data->exec("BEGIN") != PGRES_COMMAND_OK) {
+    fprintf(stderr,"BEGIN command failed\n");
+    delete data;
+    exit(1);
+  }
+
+  if (data->exec("CREATE TABLE foo (a int4, b char16, d float8)") != 
+      PGRES_COMMAND_OK) {
+      fprintf(stderr,"CREATE TABLE foo command failed\n");
+      delete data;
+      exit(1);
+  }
+
+  if (data->exec("COPY foo FROM STDIN") != PGRES_COMMAND_OK) {
+      fprintf(stderr,"COPY foo FROM STDIN\n");
+      delete data;
+      exit(1);      
+  }
+
+  data->putline("3\thello world\t4.5\n");
+  data->putline("4\tgoodbye word\t7.11\n");
+  data->putline(".\n");
+  data->endcopy();
+  data->exec("SELECT * FROM foo");
+  data->printtuples(stdout,1,"|",1,0);
+  data->exec("DROP TABLE foo");
+  // end the transaction 
+  data->exec("END");
+
+  // close the connection to the database and cleanup 
+  delete data;
+}
diff --git a/src/interfaces/libpq++/examples/testlo.cc b/src/interfaces/libpq++/examples/testlo.cc
new file mode 100644 (file)
index 0000000..0dc9e6b
--- /dev/null
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * lotest.cc--
+ *    test using large objects with libpq
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include "libpq++.H"
+extern "C" {
+#include "libpq/libpq-fs.h"
+}
+
+int
+main(int argc, char **argv)
+{
+    char *in_filename, *out_filename;
+    char *database;
+    Oid lobjOid;
+    PGenv env;
+    PGlobj *object;
+
+    if (argc < 4 || argc > 5) {
+       fprintf(stderr, "Usage: %s database_name in_filename out_filename [oid]\n",
+               argv[0]);
+       exit(1);
+    }
+
+    database = argv[1];
+    in_filename = argv[2];
+    out_filename = argv[3];
+
+    /*
+     * set up the connection and create a largeobject for us
+     */
+    if (argc == 4) {
+      object = new PGlobj(&env, database);
+    } else {
+      object = new PGlobj(&env, database, atoi(argv[4]));
+    }
+
+    /* check to see that the backend connection was successfully made */
+    if (object->status() == CONNECTION_BAD) {
+       fprintf(stderr,"Connection to database '%s' failed.\n", database);
+       fprintf(stderr,"%s",object->errormessage());
+       delete object;
+       exit(1);
+    }
+       
+    object->exec("BEGIN");
+    printf("importing file \"%s\" ...\n", in_filename);
+    object->import(in_filename);
+    printf("exporting large object to file \"%s\" ...\n", out_filename);
+    object->export(out_filename);
+    object->exec("END"); // WHY DOES IT CORE DUMP HERE ???
+    delete object;
+}
diff --git a/src/interfaces/libpq++/libpq++.H b/src/interfaces/libpq++/libpq++.H
new file mode 100644 (file)
index 0000000..94e3055
--- /dev/null
@@ -0,0 +1,173 @@
+/*-------------------------------------------------------------------------
+ *
+ * libpq++.H
+ *    
+ *
+ *   DESCRIPTION
+ *     C++ client interface to Postgres
+ *   used for building front-end applications
+ *
+ *   NOTES
+ *      Currently under construction.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *   IDENTIFICATION
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef LIBPQXX_H
+#define LIBPQXX_H
+
+#include <stdio.h>
+#include <strings.h>
+
+extern "C" {
+#include "libpq-fe.h"
+#include "fe-auth.h"
+}
+
+// ****************************************************************
+//
+// PGenv - the environment for setting up a connection to postgres
+//
+// ****************************************************************
+class PGenv {
+  friend class PGconnection;
+  char* pgauth;
+  char* pghost;
+  char* pgport;
+  char* pgoption;
+  char* pgtty;
+public:
+  PGenv();  // default ctor will use reasonable defaults
+            // will use environment  variables PGHOST, PGPORT,
+           // PGOPTION, PGTTY
+  PGenv(char* auth, char* host, char* port, char* option, char* tty);
+   void setValues(char* auth, char* host, char* port, char* option, char* tty);
+   ~PGenv();
+};
+
+// ****************************************************************
+//
+// PGconnection - a connection made to a postgres backend
+//
+// ****************************************************************
+class PGconnection {
+  friend class PGdatabase;
+  friend class PGlobj;
+  PGenv* env; 
+  PGconn* conn; 
+  PGresult* result;
+  
+  char errorMessage[ERROR_MSG_LENGTH];
+public:
+   PGconnection(); // use reasonable defaults
+   PGconnection(PGenv* env, char* dbName); // connect to the database with 
+                                    // given environment and database name
+   ConnStatusType status();
+   char* errormessage() {return errorMessage;};
+  
+  // returns the database name of the connection
+    char* dbName() {return PQdb(conn);}; 
+
+    ExecStatusType exec(char* query);  // send a query to the backend
+    PGnotify* notifies() {exec(" "); return PQnotifies(conn);};
+    ~PGconnection(); // close connection and clean up
+protected:
+  ConnStatusType connect(PGenv* env, char* dbName);
+};
+
+// ****************************************************************
+//
+// PGdatabase - a class for accessing databases
+//
+// ****************************************************************
+class PGdatabase : public PGconnection {
+public:
+  PGdatabase() : PGconnection() {}; // use reasonable defaults
+  // connect to the database with 
+  PGdatabase(PGenv* env, char* dbName) : PGconnection(env, dbName) {};
+  // query result access
+  int ntuples()
+    {return PQntuples(result);};
+  int nfields()
+    {return PQnfields(result);};
+  char* fieldname(int field_num)
+    {return PQfname(result, field_num);};
+  int fieldnum(char* field_name)
+    {return PQfnumber(result, field_name);};
+  Oid fieldtype(int field_num)
+    {return PQftype(result, field_num);};
+  Oid fieldtype(char* field_name)
+    {return PQftype(result, fieldnum(field_name));};
+  int2 fieldsize(int field_num)
+    {return PQfsize(result, field_num);};
+  int2 fieldsize(char* field_name)
+    {return PQfsize(result, fieldnum(field_name));};
+  char* getvalue(int tup_num, int field_num)
+    {return PQgetvalue(result, tup_num, field_num);};
+  char* getvalue(int tup_num, char* field_name)
+    {return PQgetvalue(result, tup_num, fieldnum(field_name));};
+  int getlength(int tup_num, int field_num)
+    {return PQgetlength(result, tup_num, field_num);};
+  int getlength(int tup_num, char* field_name)
+    {return PQgetlength(result, tup_num, fieldnum(field_name));};
+  void printtuples(FILE *out, int fillAlign, char *fieldSep,
+                  int printHeader, int quiet)
+    {PQdisplayTuples(result, out, fillAlign, fieldSep, printHeader, quiet);};
+  // copy command related access
+  int getline(char* string, int length)
+    {return PQgetline(conn, string, length);};
+  void putline(char* string)
+    {PQputline(conn, string);};
+  int endcopy()
+    {return PQendcopy(conn);};
+  ~PGdatabase() {}; // close connection and clean up
+};
+
+// ****************************************************************
+//
+// PGlobj - a class for accessing Large Object in a database
+//
+// ****************************************************************
+class PGlobj : public PGconnection {
+  int fd;
+  Oid object;
+public:
+  PGlobj();          // use reasonable defaults and create large object
+  PGlobj(Oid lobjId); // use reasonable defaults and open large object
+  PGlobj(PGenv* env, char* dbName);            // create large object
+  PGlobj(PGenv* env, char* dbName, Oid lobjId); // open large object
+   int read(char* buf, int len)
+    {return lo_read(conn, fd, buf, len);};
+   int write(char* buf, int len)
+    {return lo_write(conn, fd, buf, len);};
+   int lseek(int offset, int whence)
+    {return lo_lseek(conn, fd, offset, whence);};
+   int tell()
+    {return lo_tell(conn, fd);};
+   int unlink();
+   int import(char* filename);
+   int export(char* filename);
+   ~PGlobj(); // close connection and clean up
+};
+
+//
+// these are the environment variables used for getting defaults
+//
+
+#define ENV_DEFAULT_AUTH   "PGAUTH"
+#define ENV_DEFAULT_DBASE  "PGDATABASE"
+#define ENV_DEFAULT_HOST   "PGHOST"
+#define ENV_DEFAULT_OPTION "PGOPTION"
+#define ENV_DEFAULT_PORT   "PGPORT"
+#define ENV_DEFAULT_TTY    "PGTTY"
+
+// buffer size
+#define BUFSIZE 1024
+
+#endif /* LIBPQXX_H */
diff --git a/src/interfaces/libpq++/man/libpq++.3 b/src/interfaces/libpq++/man/libpq++.3
new file mode 100644 (file)
index 0000000..946a66a
--- /dev/null
@@ -0,0 +1,434 @@
+.\"
+.\" POSTGRES95 Data Base Management System
+.\" 
+.\" Copyright (c) 1994-5 Regents of the University of California
+.\" 
+.\" POSTGRES Data Base Management System
+.\" Copyright (c) 1988,1994 Regents of the University of California
+.\" 
+.\" Permission to use, copy, modify, and distribute this software and its
+.\" documentation for any purpose, without fee, and without a written agreement
+.\" is hereby granted, provided that the above copyright notice and this
+.\" paragraph and the following two paragraphs appear in all copies.
+.\"
+.\" IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+.\" DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+.\" LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
+.\" DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+.\" AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+.\" ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
+.\" PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+.\" 
+.\"
+.\"    $Id$
+.\"
+.\" ------------------------------------------------------------------
+.\" .(l, .)l
+.\"    fake "-me"-style lists
+.de (l 
+.nf
+.ie '\\$1'M' .in +0n
+.el .in +5n
+..
+.de )l
+.fi
+.in
+..
+.\" .(C, .)C
+.\"    constant-width font blocks
+.de (C
+.ft C
+.(b
+.(l \\$1
+.sp
+..
+.de )C
+.sp
+.)l
+.)b
+.ft R
+..
+.\" ------------------------------------------------------------------
+.de SE
+.nr si 0
+.nr so 0
+.nr $0 0
+.nr $i \\n(si*\\n($0
+.in \\n($i+\\n(po
+..
+.\" ------------------------------------------------------------------
+.de SP
+.he '\fB\\$1 (\\$2)'\\$3'\\$1 (\\$2)\fR'
+..
+.\" ------------------------------------------------------------------
+.de SS
+.PP
+.B \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8
+.PP
+..
+.\" ------------------------------------------------------------------
+.SB
+.ds II \s-1INGRES\s0
+.ds PG \s-1POSTGRES95\s0
+.ds UU \s-1UNIX\s0
+.ds PQ \s-1POSTQUEL\s0
+.ds LI \s-1LIBPQ++\s0
+.ds PV 4.2
+.SB
+.TH INTRODUCTION LIBPQ++ 07/24/95
+.XA 0 "Libpq++"
+.BH "LIBPQ++"
+.SH DESCRIPTION
+\*(LI is the C++ API to \*(PG.  \*(LI is a set of classes which allow
+client programs to connect to the \*(PG backend server. These connections
+come in two forms: a Database Class and a Large Object class.
+.PP
+The Database Class is intended for manipulating a database. You can
+send all sorts of SQL queries to the \*(PG backend server and retrieve
+the responses of the server.
+.PP
+The Large Object Class is intended for manipulating a large object
+in a database. Although a Large Object instance can send normal
+queries to the \*(PG backend server it is only intended for simple
+queries that do not return any data. A large object should be seen
+as a file stream. In future it should behave much like the C++ file
+streams
+.IR cin ,
+.IR cout
+and
+.IR cerr .
+This version of the documentation is based on the C library.  Three
+short programs are listed at the end of this section as examples of
+\*(LI programming (though not necessarily of good programming).
+.PP
+There are several examples of \*(LI applications in the following
+directory:
+.(C
+\&.../src/libpq++/examples
+.)C
+.XA 1 "Control and Initialization"
+.SH "CONTROL AND INITIALIZATION"
+.XA 2 "Environment Variables"
+.SS "Environment Variables"
+The following environment variables can be used to set up default
+values for an environment and to avoid hard-coding database names into
+an application program:
+.TP 15n
+.BR PGDATABASE
+sets the default \*(PG database name.
+.TP 15n
+.BR PGHOST
+sets the default server name.
+.TP 15n
+.BR PGOPTIONS
+sets additional runtime options for the \*(PG backend.
+.TP 15n
+.BR PGPORT
+sets the default communication port with the \*(PG backend.
+.TP 15n
+.BR PGTTY
+sets the file or tty on which debugging messages from the backend server
+are displayed.
+.TP 15n
+.BR PGREALM
+sets the
+.IR Kerberos
+realm to use with \*(PG, if it is different from the local realm.  If 
+.SM PGREALM
+is set, \*(PG applications will attempt authentication with servers
+for this realm and use separate ticket files to avoid conflicts with
+local ticket files.  This environment variable is only used if 
+.IR Kerberos
+authentication is enabled.
+.TP 15n
+.BR PGAUTH
+sets the type of authentication which should be used. Currently
+only
+.IR unauth ,
+.IR krb4 ,
+and
+.IR krb5 .
+are supported. Depending on whether you compiled in support for those.
+.XA 1 "Database Connection Functions"
+.SH "DATABASE ENVIRONMENT CLASS: PGenv"
+The database environment class provides C++ objects for manipulating the
+above environment variables.
+.TP 15n
+.BR PGenv
+Create an environment for the running program.
+.(C
+PGenv()
+PGenv(char* auth, char* host, char* port, char* option, char* tty)
+.)C
+The first form of this object's constructor sets up the defaults for
+the program from the environment variables listed above.
+The second allows the programmer to hardcode the values into the program.
+The values of the second form relate directly to the environment variables
+above.
+.SH "DATABASE CLASS: PGdatabase"
+The database class is a provides C++ objects that have a connection
+to a backend server. To create such an object one first need
+the apropriate environment for the backend to access.
+The following constructors deal with making a connection to a backend
+server from a C++ program.
+.TP 15n
+.BR PGdatabase
+Make a new connection to a backend database server.
+.(C
+PGdatabase(PGenv *env, char *dbName); 
+.)C
+After a PGdatabase has been created it should be checked to make sure
+the connection to the database succeded before sending
+queries to the object. This can easily be done by
+retrieving the current status of the PGdatabase object with the
+.IR status
+command.
+.BR PGdatabase::status
+Returns the status of the PGdatabase object.
+
+.(C
+ConnStatus PGdatabase::status()
+.)C
+
+the following values are allowed
+
+.(C
+CONNECTION_OK
+CONNECTION_BAD
+.)C
+
+.XA 1 "Query Execution Functions"
+.SH "QUERY EXECUTION FUNCTIONS"
+.TP 15n
+.BR PGdatabase::exec
+Submits a query to \*(PG and returns result status. In case of an error 
+.IR PGdatabase::errormessage
+can be used to get more information on the error.
+.(C
+void
+ExecStatusType PGdatabase::exec(char *query);
+.)C
+The following status results can be expected.
+.(C
+PGRES_EMPTY_QUERY,
+PGRES_COMMAND_OK,  /* the query was a command */
+PGRES_TUPLES_OK,  /* the query successfully returned tuples */
+PGRES_COPY_OUT, 
+PGRES_COPY_IN,
+PGRES_BAD_RESPONSE, /* an unexpected response was received */
+PGRES_NONFATAL_ERROR,
+PGRES_FATAL_ERROR
+.)C
+.IP
+If the result status is PGRES_TUPLES_OK, then the following routines can
+be used to retrieve the tuples returned by the query.
+.IP
+.BR PGdatabase::ntuples
+returns the number of tuples (instances) in the query result.
+.(C
+int PGdatabase::ntuples();
+.)C
+.BR PGdatabase::nfields
+returns the number of fields (attributes) in the query result.
+.(C
+int PGdatabase::nfields();
+.)C
+.BR PGdatabase::fieldname
+returns the field (attribute) name associated with the given field index.
+Field indices start at 0.
+.(C
+char* PGdatabase::fieldname(int field_index);
+.)C
+.BR PGdatabase::fieldnum
+returns the field (attribute) index associated with the given field name.
+.(C
+int PGdatabase::fieldnum(char* field_name);
+.)C
+.BR PGdatabase::fieldtype
+returns the field type of associated with the given field index or name.
+The integer returned is an internal coding of the type. Field indices start
+at 0.
+.(C
+Oid PGdatabase::fieldtype(int field_index);
+Oid PGdatabase::fieldtype(char* field_name);
+.)C
+.BR PGdatabase::fieldsize
+returns the size in bytes of the field associated with the given field
+index or name. If the size returned is -1, the field is a variable length
+field. Field indices start at 0. 
+.(C
+int2 PGdatabase::fieldsize(int field_index);
+int2 PGdatabase::fieldsize(char* field_name);
+.)C
+.BR PGdatabase::getvalue
+returns the field (attribute) value.  For most queries, the values
+returned by 
+.IR PGdatabase::getvalue
+is a null-terminated ASCII string representation
+of the attribute value.  If the query was a result of a 
+.BR BINARY
+cursor, then the values returned by
+.IR PGdatabase::getvalue
+is the binary representation of the type in the internal format of the
+backend server.  It is the programmer's responsibility to cast and
+convert the data to the correct C++ type.  The value return by 
+.IR PGdatabase::getvalue
+points to storage that is part of the PGdatabase structure.  One must
+explicitly copy the value into other storage if it is to be used past
+the next query.
+.(C
+char* PGdatabase::getvalue(int tup_num, int field_index);
+char* PGdatabase::getvalue(int tup_num, char* field_name);
+.)C
+.BR PGdatabase::getlength
+returns the length of a field (attribute) in bytes.  If the field
+is a
+.IR "struct varlena" ,
+the length returned here does 
+.BR not
+include the size field of the varlena, i.e., it is 4 bytes less.
+.(C
+int PGdatabase::getlength(int tup_num, int field_index);
+int PGdatabase::getlength(int tup_num, char* field_name);
+.)C
+.BR PGdatabase::printtuples
+prints out all the tuples and, optionally, the attribute names to the
+specified output stream.
+.(C
+void PGdatabase::printtuples(
+       FILE* fout,      /* output stream */
+       int printAttName,/* print attribute names or not*/
+       int terseOutput, /* delimiter bars or not?*/
+       int width        /* width of column, variable width if 0*/
+       );
+.)C
+.XA 1 "Asynchronous Notification"
+.SH "ASYNCHRONOUS NOTIFICATION"
+\*(PG supports asynchronous notification via the 
+.IR LISTEN
+and
+.IR NOTIFY
+commands.  A backend registers its interest in a particular relation
+with the LISTEN command.  All backends that are listening on a
+particular relation will be notified asynchronously when a NOTIFY of
+that relation name is executed by another backend.   No additional
+information is passed from the notifier to the listener.  Thus,
+typically, any actual data that needs to be communicated is transferred
+through the relation.
+.PP
+\*(LI applications are notified whenever a connected backend has
+received an asynchronous notification.  However, the communication from
+the backend to the frontend is not asynchronous.  The \*(LI application
+must poll the backend to see if there is any pending notification
+information.  After the execution of a query, a frontend may call 
+.IR PGdatabase::notifies
+to see if any notification data is currently available from the backend. 
+.TP 15n
+.BR PGdatabase::notifies
+returns the notification from a list of unhandled notifications from the
+backend. Returns NULL if there is no pending notifications from the
+backend.   
+.IR PGdatabase::notifies
+behaves like the popping of a stack.  Once a notification is returned
+from
+.IR PGdatabase::notifies,
+it is considered handled and will be removed from the list of
+notifications.
+.(C
+PGnotify* PGdatabase::notifies()
+.)C
+.PP
+The second sample program gives an example of the use of asynchronous
+notification.
+.XA 1 "Functions Associated with the COPY Command"
+.SH "FUNCTIONS ASSOCIATED WITH THE COPY COMMAND"
+The
+.IR copy
+command in \*(PG has options to read from or write to the network
+connection used by \*(LI.  Therefore, functions are necessary to
+access this network connection directly so applications may take full
+advantage of this capability.
+.TP 15n
+.BR PGdatabase::getline
+Reads a newline-terminated line of characters (transmitted by the
+backend server) into a buffer 
+.IR string 
+of size
+.IR length .
+Like
+.IR fgets (3),
+this routine copies up to 
+.IR length "-1"
+characters into 
+.IR string .
+It is like 
+.IR gets (3),
+however, in that it converts the terminating newline into a null
+character.
+.IP
+.IR PGdatabase::getline
+returns EOF at EOF, 0 if the entire line has been read, and 1 if the
+buffer is full but the terminating newline has not yet been read.
+.IP
+Notice that the application must check to see if a new line consists
+of the single character \*(lq.\*(rq, which indicates that the backend
+server has finished sending the results of the 
+.IR copy
+command.  Therefore, if the application ever expects to receive lines
+that are more than
+.IR length "-1"
+characters long, the application must be sure to check the return
+value of 
+.IR PGdatabase::getline
+very carefully.
+.IP
+.(C
+int PGdatabase::getline(char* string, int length)
+.)C
+.TP 15n
+.BR PGdatabase::putline
+Sends a null-terminated 
+.IR string
+to the backend server.
+.IP
+The application must explicitly send the single character \*(lq.\*(rq
+to indicate to the backend that it has finished sending its data.
+.(C
+void PGdatabase::putline(char* string)
+.)C
+.TP 15n
+.BR PGdatabase::endcopy
+Syncs with the backend.  This function waits until the backend has
+finished processing the copy.  It should either be issued when the
+last string has been sent to the backend using
+.IR PGdatabase::putline
+or when the last string has been received from the backend using
+.IR PGdatabase::getline .
+It must be issued or the backend may get \*(lqout of sync\*(rq with
+the frontend.  Upon return from this function, the backend is ready to
+receive the next query.
+.IP
+The return value is 0 on successful completion, nonzero otherwise.
+.(C
+int PGdatabase::endcopy()
+.)C
+As an example:
+.(C
+PGdatabase data;
+data.exec("create table foo (a int4, b char16, d float8)");
+data.exec("copy foo from stdin");
+data.putline("3\etHello World\et4.5\en");
+data.putline("4\etGoodbye World\et7.11\en");
+\&...
+data.putline(".\en");
+data.endcopy();
+.)C
+.SH BUGS
+The query buffer is 8192 bytes long, and queries over that length will
+be silently truncated.
+.bp
+The PGlobj class is largely untested.  Use with caution.
diff --git a/src/interfaces/libpq++/pgconnection.cc b/src/interfaces/libpq++/pgconnection.cc
new file mode 100644 (file)
index 0000000..9f71a10
--- /dev/null
@@ -0,0 +1,94 @@
+/*-------------------------------------------------------------------------
+ *
+ *   FILE
+ *     pgconnection.cc
+ *
+ *   DESCRIPTION
+ *      implementation of the PGconnection class.
+ *   PGconnection encapsulates a frontend to backend connection
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "libpq++.H"
+
+// default constructor
+// checks environment variable for database name
+PGconnection::PGconnection()
+{
+  char* name;
+  PGenv* newenv;
+
+  conn = NULL;
+  result = NULL;
+  errorMessage[0] = '\0';
+
+  newenv = new PGenv(); // use reasonable defaults for the environment
+  if (!(name = getenv(ENV_DEFAULT_DBASE)))
+    return;
+  connect(newenv, name);
+}
+
+// constructor -- for given environment and database name
+PGconnection::PGconnection(PGenv* env, char* dbName)
+{
+  conn = NULL;
+  result = NULL;
+  errorMessage[0] = '\0';
+  connect(env, dbName);
+}
+
+// destructor - closes down the connection and cleanup
+PGconnection::~PGconnection()
+{
+  if (result)  PQclear(result);
+  if (conn)    PQfinish(conn);
+}
+
+// PGconnection::connect
+// establish a connection to a backend
+ConnStatusType
+PGconnection::connect(PGenv* newenv, char* dbName)
+{
+#if 0
+    FILE *debug;
+    debug = fopen("/tmp/trace.out","w");
+    PQtrace(conn, debug);
+#endif
+
+    env = newenv;
+    fe_setauthsvc(env->pgauth, errorMessage); 
+    conn = PQsetdb(env->pghost, env->pgport, env->pgoption, env->pgtty, dbName);
+    if(strlen(errorMessage))
+      return CONNECTION_BAD;
+    else
+      return status();
+}
+
+// PGconnection::status -- return connection or result status
+ConnStatusType
+PGconnection::status()
+{
+    return PQstatus(conn);
+}
+
+// PGconnection::exec  -- send a query to the backend
+ExecStatusType
+PGconnection::exec(char* query)
+{
+  if (result)
+    PQclear(result); 
+
+  result = PQexec(conn, query);
+  if (result)
+      return PQresultStatus(result);
+  else {
+      strcpy(errorMessage, PQerrorMessage(conn));
+      return PGRES_FATAL_ERROR;
+  }
+}
diff --git a/src/interfaces/libpq++/pgenv.cc b/src/interfaces/libpq++/pgenv.cc
new file mode 100644 (file)
index 0000000..77b3ad1
--- /dev/null
@@ -0,0 +1,109 @@
+/*-------------------------------------------------------------------------
+ *
+ *   FILE
+ *     PGenv.cc
+ *
+ *   DESCRIPTION
+ *      PGenv is the environment for setting up a connection to a 
+ *   postgres backend,  captures the host, port, tty, options and
+ *   authentication type.
+ *
+ *   NOTES
+ *      Currently under construction.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <stdlib.h>
+#include "libpq++.H"
+
+#define DefaultAuth DEFAULT_CLIENT_AUTHSVC 
+#define DefaultPort POSTPORT
+
+// default constructor for PGenv
+// checks the environment variables
+PGenv::PGenv()
+{
+  char* temp;
+
+  pgauth = NULL;
+  pghost = NULL;
+  pgport = NULL;
+  pgoption = NULL;
+  pgtty = NULL;
+
+  setValues(getenv(ENV_DEFAULT_AUTH), getenv(ENV_DEFAULT_HOST),
+            getenv(ENV_DEFAULT_PORT), getenv(ENV_DEFAULT_OPTION),
+           getenv(ENV_DEFAULT_TTY));
+}
+
+// constructor for given environment
+PGenv::PGenv(char* auth, char* host, char* port, char* option, char* tty)
+{
+  pgauth = NULL;
+  pghost = NULL;
+  pgport = NULL;
+  pgoption = NULL;
+  pgtty = NULL;
+
+  setValues(auth, host, port, option, tty);
+}
+
+// allocate memory and set internal structures to match
+// required environment
+void
+PGenv::setValues(char* auth, char* host, char* port, char* option, char* tty)
+{
+  char* temp;
+
+  temp = (auth) ? auth : DefaultAuth;
+
+  if (pgauth)
+    free(pgauth);
+  pgauth = strdup(temp);
+
+  temp = (host) ? host : DefaultHost;
+
+  if (pghost)
+    free(pghost);
+  pghost = strdup(temp);
+
+  temp = (port) ? port : DefaultPort;
+
+  if (pgport)
+    free(pgport);
+  pgport = strdup(temp);
+  
+  temp = (option) ? option : DefaultOption;
+
+  if (pgoption)
+    free(pgoption);
+  pgoption = strdup(temp);
+
+  temp = (tty) ? tty : DefaultTty;
+
+  if (pgtty)
+    free(pgtty);
+  pgtty = strdup(temp);
+}
+
+// default destrutor
+// frees allocated memory for internal structures
+PGenv::~PGenv()
+{
+  if (pgauth)
+    free(pgauth);
+  if (pghost)
+    free(pghost);
+  if (pgport)
+    free(pgport);
+  if (pgoption)
+    free(pgoption);
+  if (pgtty)
+    free(pgtty);
+}
diff --git a/src/interfaces/libpq++/pglobject.cc b/src/interfaces/libpq++/pglobject.cc
new file mode 100644 (file)
index 0000000..a6213e0
--- /dev/null
@@ -0,0 +1,152 @@
+
+/*-------------------------------------------------------------------------
+ *
+ *   FILE
+ *     pglobject.cc
+ *
+ *   DESCRIPTION
+ *      implementation of the PGlobj class.
+ *   PGlobj encapsulates a frontend to backend connection
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "libpq++.H"
+
+extern "C" {
+#include "libpq/libpq-fs.h"
+}
+
+// default constructor
+// creates a large object in the default database
+PGlobj::PGlobj() : PGconnection::PGconnection() {
+  object = lo_creat(conn, INV_READ|INV_WRITE);
+  if (object == 0) {
+    sprintf(errorMessage, "PGlobj: can't create large object");
+  }
+  fd = lo_open(conn, object, INV_READ|INV_WRITE);
+  if (fd < 0) {
+    sprintf(errorMessage, "PGlobj: can't open large object %d", object);
+  } else
+    sprintf(errorMessage, "PGlobj: created and opened large object %d",
+           object);
+   
+}
+
+// constructor
+// open an existing large object in the default database
+PGlobj::PGlobj(Oid lobjId) : PGconnection::PGconnection() {
+  object = lobjId;
+  fd = lo_open(conn, object, INV_READ|INV_WRITE);
+  if (fd < 0) {
+    sprintf(errorMessage, "PGlobj: can't open large object %d", object);
+  } else
+    sprintf(errorMessage, "PGlobj: opened large object %d",
+           object);
+}
+
+// constructor
+// create a large object in the given database
+PGlobj::PGlobj(PGenv* env, char* dbName) : PGconnection::PGconnection(env,dbName) {
+  object = lo_creat(conn, INV_READ|INV_WRITE);
+  if (object == 0) {
+    sprintf(errorMessage, "PGlobj: can't create large object");
+  }
+  fd = lo_open(conn, object, INV_READ|INV_WRITE);
+  if (fd < 0) {
+    sprintf(errorMessage, "PGlobj: can't open large object %d", object);
+  } else
+    sprintf(errorMessage, "PGlobj: created and opened large object %d",
+           object);
+}
+
+// constructor
+// open an existing large object in the given database
+PGlobj::PGlobj(PGenv* env, char* dbName, Oid lobjId) : PGconnection::PGconnection(env,dbName) {
+  object = lobjId;
+  fd = lo_open(conn, object, INV_READ|INV_WRITE);
+  if (fd < 0) {
+    sprintf(errorMessage, "PGlobj: can't open large object %d", object);
+  } else
+    sprintf(errorMessage, "PGlobj: created and opened large object %d",
+           object);
+}
+
+// PGlobj::unlink
+// destruct large object and delete from it from the database
+int
+PGlobj::unlink() {
+  int temp = lo_unlink(conn, object);
+  if (temp) {
+    return temp;
+  } else {
+    delete this;
+    return temp;
+  }
+}
+
+// PGlobj::import -- import a given file into the large object
+int
+PGlobj::import(char* filename) {
+    char buf[BUFSIZE];
+    int nbytes, tmp;
+    int in_fd;
+
+    // open the file to be read in
+    in_fd = open(filename, O_RDONLY, 0666);
+    if (in_fd < 0)  {   /* error */
+       sprintf(errorMessage, "PGlobj::import: can't open unix file\"%s\"", filename);
+       return -1;
+    }
+
+    // read in from the Unix file and write to the inversion file
+    while ((nbytes = ::read(in_fd, buf, BUFSIZE)) > 0) {
+      tmp = lo_write(conn, fd, buf, nbytes);
+      if (tmp < nbytes) {
+       sprintf(errorMessage, "PGlobj::import: error while reading \"%s\"",
+         filename);
+         return -1;
+      }
+    }
+    
+    (void) close(in_fd);
+    return 0;
+}
+
+// PGlobj::export -- export large object to given file
+int
+PGlobj::export(char* filename) {
+    int out_fd;
+    char buf[BUFSIZE];
+    int nbytes, tmp;
+
+    // open the file to be written to
+    out_fd = open(filename, O_CREAT|O_WRONLY, 0666);
+    if (out_fd < 0)  {   /* error */
+       sprintf(errorMessage, "PGlobj::export: can't open unix file\"%s\"",
+               filename);
+       return -1;
+    }
+
+    // read in from the Unix file and write to the inversion file
+    while ((nbytes = lo_read(conn, fd, buf, BUFSIZE)) > 0) {
+      tmp = ::write(out_fd, buf, nbytes);
+      if (tmp < nbytes) {
+        sprintf(errorMessage,"PGlobj::export: error while writing \"%s\"",
+           filename);
+       return -1;
+      }
+    }
+    (void) close(out_fd);
+    return 0;
+}
+
+// default destructor -- closes large object
+PGlobj::~PGlobj() {
+  if (fd >= 0)
+    lo_close(conn, fd);
+}
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
new file mode 100644 (file)
index 0000000..e29ddb3
--- /dev/null
@@ -0,0 +1,98 @@
+#-------------------------------------------------------------------------
+#
+# Makefile
+#    Makefile for libpq library
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+LIB=   pq
+
+MKDIR= ../mk
+include $(MKDIR)/postgres.mk
+
+CFLAGS+= -I$(srcdir)/backend/include \
+       -I$(srcdir)/backend \
+       -I$(CURDIR) \
+
+ifdef KRBVERS
+CFLAGS+= $(KRBFLAGS)
+endif
+
+# dllist.c is found in backend/lib
+VPATH:= $(VPATH):$(srcdir)/backend/lib
+
+LIBSRCS= fe-auth.c fe-connect.c fe-exec.c fe-misc.c fe-lobj.c \
+       dllist.c pqsignal.c 
+ifeq ($(PORTNAME), next)
+VPATH:=$(VPATH):$(srcdir)/backend/port/$(PORTNAME)
+LIBSRCS+= getcwd.c putenv.c
+endif
+
+
+.PHONY: beforeinstall-headers install-headers
+
+ifndef NO_BEFOREINSTL
+beforeinstall-headers:
+       @-if [ ! -d $(HEADERDIR) ]; then mkdir $(HEADERDIR); fi
+       @-if [ ! -d $(HEADERDIR)/port ]; then mkdir $(HEADERDIR)/port; fi
+       @-if [ ! -d $(HEADERDIR)/port/$(PORTNAME) ]; \
+               then mkdir $(HEADERDIR)/port/$(PORTNAME); fi
+       @-if [ ! -d $(HEADERDIR)/include ]; \
+               then mkdir $(HEADERDIR)/include; fi
+       @-if [ ! -d $(HEADERDIR)/lib ]; \
+               then mkdir $(HEADERDIR)/lib; fi
+       @-if [ ! -d $(HEADERDIR)/libpq ]; \
+               then mkdir $(HEADERDIR)/libpq; fi
+       @-if [ ! -d $(HEADERDIR)/utils ]; \
+               then mkdir $(HEADERDIR)/utils; fi
+else
+beforeinstall-headers: .dosomething
+endif
+
+HEADERFILES = include/postgres.h \
+             libpq/pqcomm.h \
+             libpq/libpq-fs.h \
+             lib/dllist.h \
+             utils/geo-decls.h
+
+ifeq ($(PORTNAME), hpux)
+HEADERFILES += port/hpux/fixade.h
+endif
+
+
+TEMPDIR=/tmp
+
+install-headers: beforeinstall-headers
+       @for i in ${HEADERFILES}; do \
+               echo "Installing $(HEADERDIR)/$$i."; \
+               $(INSTALL) $(INSTLOPTS) $(srcdir)/backend/$$i $(HEADERDIR)/$$i; \
+       done
+       $(INSTALL) $(INSTLOPTS) libpq-fe.h $(HEADERDIR)/libpq-fe.h
+       @mv -f $(HEADERDIR)/include/* $(HEADERDIR)
+       @rmdir $(HEADERDIR)/include
+#      XXX - installing fmgr.h depends on the backend being built
+       $(INSTALL) $(INSTLOPTS) $(srcdir)/backend/$(objdir)/fmgr.h $(HEADERDIR)/fmgr.h
+       @rm -f $(TEMPDIR)/c.h
+       @echo "#undef PORTNAME" >  $(TEMPDIR)/c.h
+       @echo "#define PORTNAME $(PORTNAME)" >> $(TEMPDIR)/c.h
+       @echo "#undef PORTNAME_$(PORTNAME)" >>  $(TEMPDIR)/c.h
+       @echo "#define PORTNAME_$(PORTNAME)" >> $(TEMPDIR)/c.h
+       @cat $(srcdir)/backend/include/c.h >> $(TEMPDIR)/c.h
+       $(INSTALL) $(INSTLOPTS) $(TEMPDIR)/c.h $(HEADERDIR)/c.h
+       @rm -f $(TEMPDIR)/postgres.h
+# hardwire NAMEDATALEN and OIDNAMELEN into the postgres.h for this installation
+       @echo "#define NAMEDATALEN $(NAMEDATALEN)" >> $(TEMPDIR)/postgres.h
+       @echo "#define OIDNAMELEN $(OIDNAMELEN)" >> $(TEMPDIR)/postgres.h
+       @cat $(srcdir)/backend/include/postgres.h >> $(TEMPDIR)/postgres.h
+       $(INSTALL) $(INSTLOPTS) $(TEMPDIR)/postgres.h $(HEADERDIR)/postgres.h
+
+install:: install-headers
+
+include $(MKDIR)/postgres.lib.mk
+
diff --git a/src/interfaces/libpq/README b/src/interfaces/libpq/README
new file mode 100644 (file)
index 0000000..e581950
--- /dev/null
@@ -0,0 +1 @@
+This directory contains the C version of Libpq, the POSTGRES frontend library.
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
new file mode 100644 (file)
index 0000000..dbf88cf
--- /dev/null
@@ -0,0 +1,544 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-auth.c--
+ *     The front-end (client) authorization routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * INTERFACE ROUTINES
+ *     frontend (client) routines:
+ *     fe_sendauth             send authentication information
+ *     fe_getauthname          get user's name according to the client side
+ *                             of the authentication system
+ *     fe_setauthsvc           set frontend authentication service
+ *     fe_getauthsvc           get current frontend authentication service
+ *
+ *
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h> /* for MAX{HOSTNAME,PATH}LEN, NOFILE */
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include "libpq/pqcomm.h"
+
+#include "libpq-fe.h"
+#include "fe-auth.h"
+
+/*----------------------------------------------------------------
+ * common definitions for generic fe/be routines
+ *----------------------------------------------------------------
+ */
+
+struct authsvc {
+    char       name[16];       /* service nickname (for command line) */
+    MsgType    msgtype;        /* startup packet header type */
+    int                allowed;        /* initially allowed (before command line
+                                * option parsing)?
+                                */
+};
+
+/*
+ * Command-line parsing routines use this structure to map nicknames
+ * onto service types (and the startup packets to use with them).
+ *
+ * Programs receiving an authentication request use this structure to
+ * decide which authentication service types are currently permitted.
+ * By default, all authentication systems compiled into the system are
+ * allowed.  Unauthenticated connections are disallowed unless there
+ * isn't any authentication system.
+ */
+static struct authsvc authsvcs[] = {
+#ifdef KRB4
+    { "krb4",     STARTUP_KRB4_MSG, 1 },
+    { "kerberos", STARTUP_KRB4_MSG, 1 },
+#endif /* KRB4 */
+#ifdef KRB5
+    { "krb5",     STARTUP_KRB5_MSG, 1 },
+    { "kerberos", STARTUP_KRB5_MSG, 1 },
+#endif /* KRB5 */
+    { UNAUTHNAME, STARTUP_MSG,
+#if defined(KRB4) || defined(KRB5)
+         0
+#else /* !(KRB4 || KRB5) */
+         1
+#endif /* !(KRB4 || KRB5) */
+    }
+};
+
+static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
+
+#ifdef KRB4
+/*----------------------------------------------------------------
+ * MIT Kerberos authentication system - protocol version 4
+ *----------------------------------------------------------------
+ */
+
+#include "krb.h"
+
+/* for some reason, this is not defined in krb.h ... */
+extern char    *tkt_string(void);
+    
+/*
+ * pg_krb4_init -- initialization performed before any Kerberos calls are made
+ *
+ * For v4, all we need to do is make sure the library routines get the right
+ * ticket file if we want them to see a special one.  (They will open the file
+ * themselves.)
+ */
+static void pg_krb4_init()
+{
+    char               *realm;
+    static             init_done = 0;
+    
+    if (init_done)
+       return;
+    init_done = 1;
+    
+    /*
+     * If the user set PGREALM, then we use a ticket file with a special
+     * name: <usual-ticket-file-name>@<PGREALM-value>
+     */
+    if (realm = getenv("PGREALM")) {
+       char    tktbuf[MAXPATHLEN];
+       
+       (void) sprintf(tktbuf, "%s@%s", tkt_string(), realm);
+       krb_set_tkt_string(tktbuf);
+    }
+}
+
+/*
+ * pg_krb4_authname -- returns a pointer to static space containing whatever
+ *                    name the user has authenticated to the system
+ *
+ * We obtain this information by digging around in the ticket file.
+ */
+static char *
+pg_krb4_authname(char* PQerrormsg)
+{
+    char instance[INST_SZ];
+    char realm[REALM_SZ];
+    int status;
+    static char name[SNAME_SZ+1] = "";
+    
+    if (name[0])
+       return(name);
+    
+    pg_krb4_init();
+    
+    name[SNAME_SZ] = '\0';
+    status = krb_get_tf_fullname(tkt_string(), name, instance, realm);
+    if (status != KSUCCESS) {
+       (void) sprintf(PQerrormsg,
+                      "pg_krb4_authname: krb_get_tf_fullname: %s\n",
+                      krb_err_txt[status]);
+       return((char *) NULL);
+    }
+    return(name);
+}
+
+/*
+ * pg_krb4_sendauth -- client routine to send authentication information to
+ *                    the server
+ *
+ * This routine does not do mutual authentication, nor does it return enough
+ * information to do encrypted connections.  But then, if we want to do
+ * encrypted connections, we'll have to redesign the whole RPC mechanism
+ * anyway.
+ *
+ * If the user is too lazy to feed us a hostname, we try to come up with
+ * something other than "localhost" since the hostname is used as an
+ * instance and instance names in v4 databases are usually actual hostnames
+ * (canonicalized to omit all domain suffixes).
+ */
+static int
+pg_krb4_sendauth(char* PQerrormsg, int sock,
+                struct sockaddr_in *laddr,
+                struct sockaddr_in *raddr,
+                char *hostname)
+{
+    long               krbopts = 0;    /* one-way authentication */
+    KTEXT_ST   clttkt;
+    int                status;
+    char               hostbuf[MAXHOSTNAMELEN];
+    char               *realm = getenv("PGREALM"); /* NULL == current realm */
+    
+    if (!hostname || !(*hostname)) {
+       if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0)
+           strcpy(hostbuf, "localhost");
+       hostname = hostbuf;
+    }
+    
+    pg_krb4_init();
+    
+    status = krb_sendauth(krbopts,
+                         sock,
+                         &clttkt,
+                         PG_KRB_SRVNAM,
+                         hostname,
+                         realm,
+                         (u_long) 0,
+                         (MSG_DAT *) NULL,
+                         (CREDENTIALS *) NULL,
+                         (Key_schedule *) NULL,
+                         laddr,
+                         raddr,
+                         PG_KRB4_VERSION);
+    if (status != KSUCCESS) {
+       (void) sprintf(PQerrormsg,
+                      "pg_krb4_sendauth: kerberos error: %s\n",
+                      krb_err_txt[status]);
+       return(STATUS_ERROR);
+    }
+    return(STATUS_OK);
+}
+
+#endif /* KRB4 */
+
+#ifdef KRB5
+/*----------------------------------------------------------------
+ * MIT Kerberos authentication system - protocol version 5
+ *----------------------------------------------------------------
+ */
+
+#include "krb5/krb5.h"
+
+/*
+ * pg_an_to_ln -- return the local name corresponding to an authentication
+ *               name
+ *
+ * XXX Assumes that the first aname component is the user name.  This is NOT
+ *     necessarily so, since an aname can actually be something out of your
+ *     worst X.400 nightmare, like
+ *       ORGANIZATION=U. C. Berkeley/NAME=Paul M. [email protected]
+ *     Note that the MIT an_to_ln code does the same thing if you don't
+ *     provide an aname mapping database...it may be a better idea to use
+ *     krb5_an_to_ln, except that it punts if multiple components are found,
+ *     and we can't afford to punt.
+ */
+static char *
+pg_an_to_ln(char *aname)
+{
+    char       *p;
+    
+    if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
+       *p = '\0';
+    return(aname);
+}
+
+
+/*
+ * pg_krb5_init -- initialization performed before any Kerberos calls are made
+ *
+ * With v5, we can no longer set the ticket (credential cache) file name;
+ * we now have to provide a file handle for the open (well, "resolved")
+ * ticket file everywhere.
+ * 
+ */
+static int
+krb5_ccache pg_krb5_init()
+{
+    krb5_error_code            code;
+    char                       *realm, *defname;
+    char                       tktbuf[MAXPATHLEN];
+    static krb5_ccache ccache = (krb5_ccache) NULL;
+    
+    if (ccache)
+       return(ccache);
+    
+    /*
+     * If the user set PGREALM, then we use a ticket file with a special
+     * name: <usual-ticket-file-name>@<PGREALM-value>
+     */
+    if (!(defname = krb5_cc_default_name())) {
+       (void) sprintf(PQerrormsg,
+                      "pg_krb5_init: krb5_cc_default_name failed\n");
+       return((krb5_ccache) NULL);
+    }
+    (void) strcpy(tktbuf, defname);
+    if (realm = getenv("PGREALM")) {
+       (void) strcat(tktbuf, "@");
+       (void) strcat(tktbuf, realm);
+    }
+    
+    if (code = krb5_cc_resolve(tktbuf, &ccache)) {
+       (void) sprintf(PQerrormsg,
+                      "pg_krb5_init: Kerberos error %d in krb5_cc_resolve\n",
+                      code);
+       com_err("pg_krb5_init", code, "in krb5_cc_resolve");
+       return((krb5_ccache) NULL);
+    }
+    return(ccache);
+}
+
+/*
+ * pg_krb5_authname -- returns a pointer to static space containing whatever
+ *                    name the user has authenticated to the system
+ *
+ * We obtain this information by digging around in the ticket file.
+ */
+static char *
+pg_krb5_authname(char* PQerrormsg)
+{
+    krb5_ccache        ccache;
+    krb5_principal     principal;
+    krb5_error_code    code;
+    static char        *authname = (char *) NULL;
+    
+    if (authname)
+       return(authname);
+    
+    ccache = pg_krb5_init();   /* don't free this */
+    
+    if (code = krb5_cc_get_principal(ccache, &principal)) {
+       (void) sprintf(PQerrormsg,
+                      "pg_krb5_authname: Kerberos error %d in krb5_cc_get_principal\n",
+                      code);
+       com_err("pg_krb5_authname", code, "in krb5_cc_get_principal");
+       return((char *) NULL);
+    }
+    if (code = krb5_unparse_name(principal, &authname)) {
+       (void) sprintf(PQerrormsg,
+                      "pg_krb5_authname: Kerberos error %d in krb5_unparse_name\n",
+                      code);
+       com_err("pg_krb5_authname", code, "in krb5_unparse_name");
+       krb5_free_principal(principal);
+       return((char *) NULL);
+    }
+    krb5_free_principal(principal);
+    return(pg_an_to_ln(authname));
+}
+
+/*
+ * pg_krb5_sendauth -- client routine to send authentication information to
+ *                    the server
+ *
+ * This routine does not do mutual authentication, nor does it return enough
+ * information to do encrypted connections.  But then, if we want to do
+ * encrypted connections, we'll have to redesign the whole RPC mechanism
+ * anyway.
+ *
+ * Server hostnames are canonicalized v4-style, i.e., all domain suffixes
+ * are simply chopped off.  Hence, we are assuming that you've entered your
+ * server instances as
+ *     <value-of-PG_KRB_SRVNAM>/<canonicalized-hostname>
+ * in the PGREALM (or local) database.  This is probably a bad assumption.
+ */
+static int
+pg_krb5_sendauth(char* PQerrormsg,int sock,
+                struct sockaddr_in *laddr,
+                struct sockaddr_in *raddr,
+                char *hostname)
+{
+    char                       servbuf[MAXHOSTNAMELEN + 1 +
+                                       sizeof(PG_KRB_SRVNAM)];
+    char                       *hostp;
+    char                       *realm;
+    krb5_error_code            code;
+    krb5_principal             client, server;
+    krb5_ccache                ccache;
+    krb5_error         *error = (krb5_error *) NULL;
+    
+    ccache = pg_krb5_init();   /* don't free this */
+    
+    /*
+     * set up client -- this is easy, we can get it out of the ticket
+     * file.
+     */
+    if (code = krb5_cc_get_principal(ccache, &client)) {
+       (void) sprintf(PQerrormsg,
+                      "pg_krb5_sendauth: Kerberos error %d in krb5_cc_get_principal\n",
+                      code);
+       com_err("pg_krb5_sendauth", code, "in krb5_cc_get_principal");
+       return(STATUS_ERROR);
+    }
+    
+    /*
+     * set up server -- canonicalize as described above
+     */
+    (void) strcpy(servbuf, PG_KRB_SRVNAM);
+    *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
+    if (hostname || *hostname) {
+       (void) strncpy(++hostp, hostname, MAXHOSTNAMELEN);
+    } else {
+       if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
+           (void) strcpy(hostp, "localhost");
+    }
+    if (hostp = strchr(hostp, '.'))
+       *hostp = '\0';
+    if (realm = getenv("PGREALM")) {
+       (void) strcat(servbuf, "@");
+       (void) strcat(servbuf, realm);
+    }
+    if (code = krb5_parse_name(servbuf, &server)) {
+       (void) sprintf(PQerrormsg,
+                      "pg_krb5_sendauth: Kerberos error %d in krb5_parse_name\n",
+                      code);
+       com_err("pg_krb5_sendauth", code, "in krb5_parse_name");
+       krb5_free_principal(client);
+       return(STATUS_ERROR);
+    }
+    
+    /*
+     * The only thing we want back from krb5_sendauth is an error status
+     * and any error messages.
+     */
+    if (code = krb5_sendauth((krb5_pointer) &sock,
+                            PG_KRB5_VERSION,
+                            client,
+                            server,
+                            (krb5_flags) 0,
+                            (krb5_checksum *) NULL,
+                            (krb5_creds *) NULL,
+                            ccache,
+                            (krb5_int32 *) NULL,
+                            (krb5_keyblock **) NULL,
+                            &error,
+                            (krb5_ap_rep_enc_part **) NULL)) {
+       if ((code == KRB5_SENDAUTH_REJECTED) && error) {
+           (void) sprintf(PQerrormsg,
+                          "pg_krb5_sendauth: authentication rejected: \"%*s\"\n",
+                          error->text.length, error->text.data);
+           fputs(PQerrormsg, stderr);
+           pqdebug("%s", PQerrormsg);
+       } else {
+           (void) sprintf(PQerrormsg,
+                          "pg_krb5_sendauth: Kerberos error %d in krb5_sendauth\n",
+                          code);
+           com_err("pg_krb5_sendauth", code, "in krb5_sendauth");
+       }
+    }
+    krb5_free_principal(client);
+    krb5_free_principal(server);
+    return(code ? STATUS_ERROR : STATUS_OK);
+}
+
+#endif /* KRB5 */
+
+
+/*
+ * fe_sendauth -- client demux routine for outgoing authentication information
+ */
+int
+fe_sendauth(MsgType msgtype, Port *port, char *hostname, char* PQerrormsg)
+{
+    switch (msgtype) {
+#ifdef KRB4
+    case STARTUP_KRB4_MSG:
+       if (pg_krb4_sendauth(PQerrormsg, port->sock, &port->laddr, 
+                            &port->raddr,
+                            hostname) != STATUS_OK) {
+           (void) sprintf(PQerrormsg,
+                          "fe_sendauth: krb4 authentication failed\n");
+/*         fputs(PQerrormsg, stderr); */
+           return(STATUS_ERROR);
+       }
+       break;
+#endif
+#ifdef KRB5
+    case STARTUP_KRB5_MSG:
+       if (pg_krb5_sendauth(PQerrormsg,port->sock, &port->laddr, 
+                            &port->raddr,
+                            hostname) != STATUS_OK) {
+           (void) sprintf(PQerrormsg,
+                          "fe_sendauth: krb5 authentication failed\n");
+           return(STATUS_ERROR);
+       }
+       break;
+#endif
+    case STARTUP_MSG:
+       break;
+    default:
+       break;
+    }
+    return(STATUS_OK);
+}
+
+/*
+ * fe_setauthsvc
+ * fe_getauthsvc
+ *
+ * Set/return the authentication service currently selected for use by the
+ * frontend. (You can only use one in the frontend, obviously.)
+ */
+static pg_authsvc = -1;
+
+void
+fe_setauthsvc(char *name, char* PQerrormsg)
+{
+    int i;
+    
+    for (i = 0; i < n_authsvcs; ++i)
+       if (!strcmp(name, authsvcs[i].name)) {
+           pg_authsvc = i;
+           break;
+       }
+    if (i == n_authsvcs) {
+       (void) sprintf(PQerrormsg,
+                      "fe_setauthsvc: invalid name: %s, ignoring...\n",
+                      name);
+    }
+    return;
+}
+
+MsgType
+fe_getauthsvc(char* PQerrormsg)
+{
+    if (pg_authsvc < 0 || pg_authsvc >= n_authsvcs)
+       fe_setauthsvc(DEFAULT_CLIENT_AUTHSVC,PQerrormsg);
+    return(authsvcs[pg_authsvc].msgtype);
+}
+
+/*
+ * fe_getauthname -- returns a pointer to static space containing whatever
+ *                  name the user has authenticated to the system
+ * if there is an error, return the error message in PQerrormsg
+ */
+char*
+fe_getauthname(char* PQerrormsg)
+{
+    char *name = (char *) NULL;
+    MsgType authsvc;
+    
+    authsvc = fe_getauthsvc(PQerrormsg);
+    switch ((int) authsvc) {
+#ifdef KRB4
+    case STARTUP_KRB4_MSG:
+       name = pg_krb4_authname(PQerrormsg);
+       break;
+#endif
+#ifdef KRB5
+    case STARTUP_KRB5_MSG:
+       name = pg_krb5_authname(PQerrormsg);
+       break;
+#endif
+    case STARTUP_MSG:
+       {
+           struct passwd *pw = getpwuid(getuid());
+           if (pw &&
+               pw->pw_name &&
+               (name = (char *) malloc(strlen(pw->pw_name) + 1))) {
+               (void) strcpy(name, pw->pw_name);
+           }
+       }
+       break;
+    default:
+       (void) sprintf(PQerrormsg,
+                      "fe_getauthname: invalid authentication system: %d\n",
+                      authsvc);
+       break;
+    }
+    return(name);
+}
+
+
diff --git a/src/interfaces/libpq/fe-auth.h b/src/interfaces/libpq/fe-auth.h
new file mode 100644 (file)
index 0000000..82481d4
--- /dev/null
@@ -0,0 +1,38 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-auth.h
+ *    
+ *    Definitions for network authentication routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FE_AUTH_H
+#define        FE_AUTH_H
+
+/*----------------------------------------------------------------
+ * Common routines and definitions
+ *----------------------------------------------------------------
+ */
+
+/* what we call "no authentication system" */
+#define        UNAUTHNAME              "unauth"
+
+/* what a frontend uses by default */
+#if !defined(KRB4) && !defined(KRB5)
+#define        DEFAULT_CLIENT_AUTHSVC  UNAUTHNAME
+#else /* KRB4 || KRB5 */
+#define        DEFAULT_CLIENT_AUTHSVC  "kerberos"
+#endif /* KRB4 || KRB5 */
+
+extern int fe_sendauth(MsgType msgtype, Port *port, char *hostname, char* PQerromsg);
+extern void fe_setauthsvc(char *name, char* PQerrormsg);
+
+#define        PG_KRB4_VERSION "PGVER4.1"      /* at most KRB_SENDAUTH_VLEN chars */
+#define        PG_KRB5_VERSION "PGVER5.1"
+
+#endif /* FE_AUTH_H */
+
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
new file mode 100644 (file)
index 0000000..ea54223
--- /dev/null
@@ -0,0 +1,460 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-connect.c--
+ *    functions related to setting up a connection to the backend
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include "libpq/pqcomm.h" /* for decls of MsgType, PacketBuf, StartupInfo */
+#include "fe-auth.h"
+#include "libpq-fe.h"
+
+#if defined(PORTNAME_ultrix4) || defined(PORTNAME_next)
+  /* ultrix is lame and doesn't have strdup in libc for some reason */
+ /* [TRH] So doesn't NEXTSTEP.  But whaddaya expect for a non-ANSI  
+standard function? (My, my. Touchy today, are we?) */
+static
+char *
+strdup(char *string)
+{
+    char *nstr;
+
+    nstr = strcpy((char *)malloc(strlen(string)+1), string);
+    return nstr;
+}
+#endif
+
+/* use a local version instead of the one found in pqpacket.c */
+static ConnStatusType connectDB(PGconn *conn);
+
+static int packetSend(Port *port, PacketBuf *buf, PacketLen len,
+                     bool nonBlocking);
+static void startup2PacketBuf(StartupInfo* s, PacketBuf* res);
+static void freePGconn(PGconn *conn);
+static void closePGconn(PGconn *conn);
+
+#define NOTIFYLIST_INITIAL_SIZE 10
+#define NOTIFYLIST_GROWBY 10
+
+/* ----------------
+ *     PQsetdb
+ * 
+ * establishes a connectin to a postgres backend through the postmaster
+ * at the specified host and port.
+ *
+ * returns a PGconn* which is needed for all subsequent libpq calls
+ * if the status field of the connection returned is CONNECTION_BAD,
+ * then some fields may be null'ed out instead of having valid values 
+ * ----------------
+ */
+PGconn* 
+PQsetdb(char *pghost, char* pgport, char* pgoptions, char* pgtty, char* dbName)
+{
+    PGconn *conn;
+    char *tmp;
+
+    conn = (PGconn*)malloc(sizeof(PGconn));
+
+    conn->Pfout = NULL;
+    conn->Pfin = NULL;
+    conn->Pfdebug = NULL;
+    conn->port = NULL;
+    conn->notifyList = DLNewList();
+
+    if (!pghost || pghost[0] == '\0') {
+       if (!(tmp = getenv("PGHOST"))) {
+           tmp = DefaultHost;
+       }
+       conn->pghost = strdup(tmp);
+    } else
+       conn->pghost = strdup(pghost);
+
+    if (!pgport || pgport == '\0') {
+       if (!(tmp = getenv("PGPORT"))) {
+           tmp = POSTPORT;
+       }
+       conn->pgport = strdup(tmp);
+    } else
+       conn->pgport = strdup(pgport);
+
+    if (!pgtty || pgtty == '\0') {
+       if (!(tmp = getenv("PGTTY"))) {
+           tmp = DefaultTty;
+       }
+       conn->pgtty = strdup(tmp);
+    } else
+       conn->pgtty = strdup(pgtty);
+
+    if (!pgoptions || pgoptions == '\0') {
+       if (!(tmp = getenv("PGOPTIONS"))) {
+           tmp = DefaultOption;
+       }
+       conn->pgoptions = strdup(tmp);
+    } else
+       conn->pgoptions = strdup(pgoptions);
+
+    if (!dbName || dbName[0] == '\0') {
+       char errorMessage[ERROR_MSG_LENGTH];
+       if (!(tmp = getenv("PGDATABASE")) &&
+           !(tmp = fe_getauthname(errorMessage))) {
+           sprintf(conn->errorMessage,
+                   "FATAL: PQsetdb: Unable to determine a database name!\n");
+/*         pqdebug("%s", conn->errorMessage); */
+           conn->dbName = NULL;
+           return conn;
+       }
+       conn->dbName = strdup(tmp);
+    } else
+       conn->dbName = strdup(dbName);
+
+    conn->status = connectDB(conn);
+    return conn;
+}
+
+/*
+ * connectDB -
+ * make a connection to the database,  returns 1 if successful or 0 if not
+ *
+ */
+static ConnStatusType
+connectDB(PGconn *conn)
+{
+    struct hostent *hp;
+
+    StartupInfo startup;
+    PacketBuf   pacBuf;
+    int                status;
+    MsgType    msgtype;
+    int         laddrlen = sizeof(struct sockaddr);
+    Port        *port = conn->port;
+    int         portno;
+    PGresult    *res;
+
+    char        *user;
+    /*
+    //
+    // Initialize the startup packet. 
+    //
+    // This data structure is used for the seq-packet protocol.  It
+    // describes the frontend-backend connection.
+    //
+    //
+    */
+    user = fe_getauthname(conn->errorMessage);
+    if (!user)
+       goto connect_errReturn;
+    strncpy(startup.database,conn->dbName,sizeof(startup.database));
+    strncpy(startup.user,user,sizeof(startup.user));
+    strncpy(startup.tty,conn->pgtty,sizeof(startup.tty));
+    if (conn->pgoptions) {
+       strncpy(startup.options,conn->pgoptions, sizeof(startup.options));
+    }
+    else
+       startup.options[0]='\0'; 
+    startup.execFile[0]='\0';  /* not used */
+
+    /*
+    //
+    // Open a connection to postmaster/backend.
+    //
+    */
+    port = (Port *) malloc(sizeof(Port));
+    memset((char *) port, 0, sizeof(Port));
+
+    if (!(hp = gethostbyname(conn->pghost)) || hp->h_addrtype != AF_INET) {
+       (void) sprintf(conn->errorMessage,
+                      "connectDB() --  unknown hostname: %s\n",
+                      conn->pghost);
+       goto connect_errReturn;
+    }
+    memset((char *) &port->raddr, 0, sizeof(port->raddr));
+    memmove((char *) &(port->raddr.sin_addr),
+           (char *) hp->h_addr, 
+           hp->h_length);
+    port->raddr.sin_family = AF_INET;
+    portno = atoi(conn->pgport);
+    port->raddr.sin_port = htons((unsigned short)(portno));
+    
+    /* connect to the server  */
+    if ((port->sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+       (void) sprintf(conn->errorMessage,
+              "connectDB() -- socket() failed: errno=%d\n%s\n",
+              errno, strerror(errno));
+       goto connect_errReturn; 
+    }
+    if (connect(port->sock, (struct sockaddr *)&port->raddr,
+               sizeof(port->raddr)) < 0) {
+       (void) sprintf(conn->errorMessage,
+                      "connectDB() failed: Is the postmaster running at '%s' on port '%s'?\n",
+                      conn->pghost,conn->pgport);
+       goto connect_errReturn; 
+    }
+    
+
+    /* fill in the client address */
+    if (getsockname(port->sock, (struct sockaddr *) &port->laddr,
+                   &laddrlen) < 0) {
+       (void) sprintf(conn->errorMessage,
+              "connectDB() -- getsockname() failed: errno=%d\n%s\n",
+              errno, strerror(errno));
+       goto connect_errReturn; 
+    }
+    
+    /* by this point, connection has been opened */
+    msgtype = fe_getauthsvc(conn->errorMessage);
+
+/*    pacBuf = startup2PacketBuf(&startup);*/
+    startup2PacketBuf(&startup, &pacBuf);
+    pacBuf.msgtype = htonl(msgtype);
+    status = packetSend(port, &pacBuf, sizeof(PacketBuf), BLOCKING);
+    
+    if (status == STATUS_ERROR)
+       {
+       sprintf(conn->errorMessage,
+              "connectDB() --  couldn't send complete packet: errno=%d\n%s\n", errno,strerror(errno));
+       goto connect_errReturn;
+       }
+
+    /* authenticate as required*/
+    if (fe_sendauth(msgtype, port, conn->pghost, 
+                   conn->errorMessage) != STATUS_OK) {
+      (void) sprintf(conn->errorMessage,
+            "connectDB() --  authentication failed with %s\n",
+              conn->pghost);
+      goto connect_errReturn;  
+    }
+    
+    /* set up the socket file descriptors */
+    conn->Pfout = fdopen(port->sock, "w");
+    conn->Pfin = fdopen(dup(port->sock), "r");
+    if (!conn->Pfout || !conn->Pfin) {
+       (void) sprintf(conn->errorMessage,
+              "connectDB() -- fdopen() failed: errno=%d\n%s\n",
+              errno, strerror(errno));
+      goto connect_errReturn;  
+    }
+    
+    conn->port = port;
+
+    /* we have a connection now,
+       send a blank query down to make sure the database exists*/
+    res = PQexec(conn," ");
+    if (res == NULL || res->resultStatus != PGRES_EMPTY_QUERY) {
+      /* error will already be in conn->errorMessage */
+      goto connect_errReturn;
+    }
+    free(res);
+    return CONNECTION_OK;
+
+connect_errReturn:
+    return CONNECTION_BAD;
+
+}
+
+/*
+ * freePGconn
+ *   - free the PGconn data structure 
+ *
+ */
+static void 
+freePGconn(PGconn *conn)
+{
+  if (conn->pghost) free(conn->pghost);
+  if (conn->pgtty) free(conn->pgtty);
+  if (conn->pgoptions) free(conn->pgoptions);
+  if (conn->pgport) free(conn->pgport);
+  if (conn->dbName) free(conn->dbName);
+  if (conn->notifyList) DLFreeList(conn->notifyList);
+  free(conn);
+}
+
+/*
+   closePGconn
+     - properly close a connection to the backend
+*/
+static void
+closePGconn(PGconn *conn)
+{
+    fputs("X\0", conn->Pfout);
+    fflush(conn->Pfout);
+    if (conn->Pfout) fclose(conn->Pfout);
+    if (conn->Pfin)  fclose(conn->Pfin);
+    if (conn->Pfdebug) fclose(conn->Pfdebug);
+}
+
+/*
+   PQfinish:
+      properly close a connection to the backend
+      also frees the PGconn data structure so it shouldn't be re-used 
+      after this
+*/
+void
+PQfinish(PGconn *conn)
+{
+  if (conn->status == CONNECTION_OK)
+    closePGconn(conn);
+  freePGconn(conn);
+}
+
+/* PQreset :
+   resets the connection to the backend
+   closes the existing connection and makes a new one 
+*/
+void
+PQreset(PGconn *conn)
+{
+    closePGconn(conn);
+    conn->status = connectDB(conn);
+}
+
+/*
+ * PacketSend()
+ *
+ this is just like PacketSend(), defined in backend/libpq/pqpacket.c
+ but we define it here to avoid linking in all of libpq.a
+
+ * packetSend -- send a single-packet message.
+ *
+ * RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise.
+ * SIDE_EFFECTS: may block.
+ * NOTES: Non-blocking writes would significantly complicate 
+ *     buffer management.  For now, we're not going to do it.
+ *
+*/
+static int
+packetSend(Port *port,
+          PacketBuf *buf,
+          PacketLen len,
+          bool nonBlocking)
+{
+    PacketLen  totalLen;
+    int                addrLen = sizeof(struct sockaddr_in);
+    
+    totalLen = len;
+    
+    len = sendto(port->sock, (Addr) buf, totalLen, /* flags */ 0,
+                (struct sockaddr *)&(port->raddr), addrLen);
+    
+    if (len < totalLen) {
+       return(STATUS_ERROR);
+    }
+    
+    return(STATUS_OK);
+}
+
+/*
+ * startup2PacketBuf()
+ *
+ * this is just like StartupInfo2Packet(), defined in backend/libpq/pqpacket.c
+ * but we repeat it here so we don't have to link in libpq.a
+ * 
+ * converts a StartupInfo structure to a PacketBuf
+ */
+static void
+startup2PacketBuf(StartupInfo* s, PacketBuf* res)
+{
+  char* tmp;
+
+/*  res = (PacketBuf*)malloc(sizeof(PacketBuf)); */
+  res->len = htonl(sizeof(PacketBuf));
+  /* use \n to delimit the strings */
+  res->data[0] = '\0';
+
+  tmp= res->data;
+
+  strncpy(tmp, s->database, sizeof(s->database));
+  tmp += sizeof(s->database);
+  strncpy(tmp, s->user, sizeof(s->user));
+  tmp += sizeof(s->user);
+  strncpy(tmp, s->options, sizeof(s->options));
+  tmp += sizeof(s->options);
+  strncpy(tmp, s->execFile, sizeof(s->execFile));
+  tmp += sizeof(s->execFile);
+  strncpy(tmp, s->tty, sizeof(s->execFile));
+
+}
+
+
+/* =========== accessor functions for PGconn ========= */
+char* 
+PQdb(PGconn* conn)
+{
+  return conn->dbName;
+}
+
+char* 
+PQhost(PGconn* conn)
+{
+  return conn->pghost;
+}
+
+char* 
+PQoptions(PGconn* conn)
+{
+  return conn->pgoptions;
+}
+
+char* 
+PQtty(PGconn* conn)
+{
+  return conn->pgtty;
+}
+
+char*
+PQport(PGconn* conn)
+{
+  return conn->pgport;
+}
+
+ConnStatusType
+PQstatus(PGconn* conn)
+{
+  return conn->status;
+}
+
+char* 
+PQerrorMessage(PGconn* conn)
+{
+  return conn->errorMessage;
+}
+
+void
+PQtrace(PGconn *conn, FILE* debug_port)
+{
+  if (conn == NULL ||
+      conn->status == CONNECTION_BAD) {
+    return;
+  }
+  PQuntrace(conn);
+  conn->Pfdebug = debug_port;
+}
+
+void 
+PQuntrace(PGconn *conn)
+{
+  if (conn == NULL ||
+      conn->status == CONNECTION_BAD) {
+    return;
+  }
+  if (conn->Pfdebug) {
+    fflush(conn->Pfdebug);
+    fclose(conn->Pfdebug);
+    conn->Pfdebug = NULL;
+  }
+}
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
new file mode 100644 (file)
index 0000000..644a3ff
--- /dev/null
@@ -0,0 +1,1061 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-exec.c--
+ *    functions related to sending a query down to the backend
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include "postgres.h"
+#include "libpq/pqcomm.h"
+#include "libpq-fe.h"
+#include <signal.h>
+
+/* the tuples array in a PGresGroup  has to grow to accommodate the tuples */
+/* returned.  Each time, we grow by this much: */
+#define TUPARR_GROW_BY 100
+
+/* keep this in same order as ExecStatusType in pgtclCmds.h */
+char* pgresStatus[] = {
+    "PGRES_EMPTY_QUERY",
+    "PGRES_COMMAND_OK",
+    "PGRES_TUPLES_OK",
+    "PGRES_BAD_RESPONSE",
+    "PGRES_NONFATAL_ERROR",
+    "PGRES_FATAL_ERROR"
+};
+
+
+static PGresult* makePGresult(PGconn *conn, char *pname);
+static void addTuple(PGresult *res, PGresAttValue *tup);
+static PGresAttValue* getTuple(PGconn *conn, PGresult *res, int binary);
+static PGresult* makeEmptyPGresult(PGconn *conn, ExecStatusType status);
+static void fill(int length, int max, char filler, FILE *fp);
+
+/*
+ * PQclear -
+ *    free's the memory associated with a PGresult
+ *
+ */
+void
+PQclear(PGresult* res)
+{
+    int i,j;
+
+    if (!res)
+       return;
+
+    /* free all the tuples */
+    for (i=0;i<res->ntups;i++) {
+       for (j=0;j<res->numAttributes;j++) {
+           if (res->tuples[i][j].value)
+               free(res->tuples[i][j].value);
+       }
+       free(res->tuples[i]);
+    }
+    free(res->tuples);
+
+    /* free all the attributes */
+    for (i=0;i<res->numAttributes;i++) {
+       if (res->attDescs[i].name) 
+           free(res->attDescs[i].name);
+    }
+    free(res->attDescs);
+       
+    /* free the structure itself */
+    free(res);
+}
+
+/*
+ * PGresult -
+ *   returns a newly allocated, initialized PGresult
+ *
+ */
+
+static PGresult*
+makeEmptyPGresult(PGconn *conn, ExecStatusType status)
+{
+  PGresult *result;
+
+  result = (PGresult*)malloc(sizeof(PGresult));
+
+  result->conn = conn;
+  result->ntups = 0;
+  result->numAttributes = 0;
+  result->attDescs = NULL;
+  result->tuples = NULL;
+  result->tupArrSize = 0;
+  result->resultStatus = status;
+  result->cmdStatus[0] = '\0';
+  result->binary = 0;
+  return result;
+}
+
+/*
+ * getTuple -
+ *   get the next tuple from the stream
+ *
+ *  the CALLER is responsible from freeing the PGresAttValue returned 
+ */
+
+static PGresAttValue*
+getTuple(PGconn *conn, PGresult* result, int binary)
+{
+  char bitmap[MAX_FIELDS]; /* the backend sends us a bitmap of  */
+                           /* which attributes are null */
+  int bitmap_index = 0;
+  int i;
+  int nbytes;              /* the number of bytes in bitmap  */
+  char         bmap;              /*  One byte of the bitmap */
+  int  bitcnt = 0;        /* number of bits examined in current byte */
+  int  vlen;              /* length of the current field value */
+  FILE *Pfin = conn->Pfin;
+  FILE *Pfdebug = conn->Pfdebug;
+
+  PGresAttValue* tup;
+
+  int nfields = result->numAttributes;
+
+  result->binary = binary;
+
+  tup = (PGresAttValue*) malloc(nfields * sizeof(PGresAttValue));
+
+  nbytes = nfields / BYTELEN;
+  if ( (nfields % BYTELEN) > 0)
+    nbytes++;
+
+  if (pqGetnchar(bitmap, nbytes, Pfin, Pfdebug) == 1){
+      sprintf(conn->errorMessage,
+             "Error reading null-values bitmap from tuple data stream\n");
+      return NULL;
+    }
+
+  bmap = bitmap[bitmap_index];
+  
+  for (i=0;i<nfields;i++) {
+    if (!(bmap & 0200)) {
+       /* if the field value is absent, make it '\0' */
+       /* XXX this makes it impossible to distinguish NULL 
+          attributes from "".  Is that OK?   */
+       tup[i].value = (char*)malloc(1);
+       tup[i].value[0] = '\0';
+       tup[i].len = 0;
+    }
+    else {
+      /* get the value length (the first four bytes are for length) */
+      pqGetInt(&vlen, VARHDRSZ, Pfin, Pfdebug);
+      if (binary == 0) {
+       vlen = vlen - VARHDRSZ;
+       }
+      if (vlen < 0)
+         vlen = 0;
+      tup[i].len = vlen;
+      tup[i].value = (char*) malloc(vlen + 1);
+      /* read in the value; */
+      if (vlen > 0)
+         pqGetnchar((char*)(tup[i].value), vlen, Pfin, Pfdebug);
+      tup[i].value[vlen] = '\0';
+    }
+    /* get the appropriate bitmap */
+    bitcnt++;
+    if (bitcnt == BYTELEN) {
+      bitmap_index++;
+      bmap = bitmap[bitmap_index];
+      bitcnt = 0;
+    } else
+      bmap <<= 1;
+  }
+
+  return tup;
+}
+
+
+/*
+ * addTuple
+ *    add a tuple to the PGresult structure, growing it if necessary
+ *  to accommodate
+ *
+ */
+static void 
+addTuple(PGresult* res, PGresAttValue* tup)
+{
+  if (res->ntups == res->tupArrSize) { 
+    /* grow the array */
+    res->tupArrSize += TUPARR_GROW_BY;
+    
+    if (res->ntups == 0)
+      res->tuples = (PGresAttValue**) 
+       malloc(res->tupArrSize * sizeof(PGresAttValue*));
+    else
+    /* we can use realloc because shallow copying of the structure is okay */
+      res->tuples = (PGresAttValue**) 
+       realloc(res->tuples, res->tupArrSize * sizeof(PGresAttValue*));
+    }
+
+  res->tuples[res->ntups] = tup;
+  res->ntups++;
+}
+
+/*
+ * PGresult
+ *    fill out the PGresult structure with result tuples from the backend
+ *  this is called after query has been successfully run and we have
+ *  a portal name
+ *
+ *  ASSUMPTION: we assume only *1* tuple group is returned from the backend
+ *
+ *  the CALLER is reponsible for free'ing the new PGresult allocated here
+ *
+ */
+
+static PGresult*
+makePGresult(PGconn* conn, char* pname)
+{
+  PGresult* result;
+  int id;
+  int nfields;
+  int i;
+  int done = 0;
+
+  PGresAttValue* newTup;
+
+  FILE* Pfin = conn->Pfin;
+  FILE* Pfdebug = conn->Pfdebug;
+
+  result = makeEmptyPGresult(conn, PGRES_TUPLES_OK);
+  
+  /* makePGresult() should only be called when the */
+  /* id of the stream is 'T' to start with */
+
+  /* the next two bytes are the number of fields  */
+  if (pqGetInt(&nfields, 2, Pfin, Pfdebug) == 1) {
+    sprintf(conn->errorMessage,
+           "could not get the number of fields from the 'T' message\n");
+    goto makePGresult_badResponse_return;
+  }
+  else
+    result->numAttributes = nfields;
+
+  /* allocate space for the attribute descriptors */
+  if (nfields > 0) {
+    result->attDescs = (PGresAttDesc*) malloc(nfields * sizeof(PGresAttDesc));
+  }
+
+  /* get type info */
+  for (i=0;i<nfields;i++) {
+    char typName[MAX_MESSAGE_LEN];
+    int adtid;
+    int adtsize;
+    
+    if ( pqGets(typName, MAX_MESSAGE_LEN, Pfin, Pfdebug) ||
+       pqGetInt(&adtid, 4, Pfin, Pfdebug) ||
+       pqGetInt(&adtsize, 2, Pfin, Pfdebug)) {
+      sprintf(conn->errorMessage,
+             "error reading type information from the 'T' message\n");
+      goto makePGresult_badResponse_return;
+    }
+   result->attDescs[i].name = malloc(strlen(typName)+1);
+   strcpy(result->attDescs[i].name,typName);
+   result->attDescs[i].adtid = adtid;
+   result->attDescs[i].adtsize = adtsize; /* casting from int to int2 here */
+  }
+
+  id = pqGetc(Pfin,Pfdebug);
+
+  /* process the data stream until we're finished */
+  while(!done) {
+    switch (id) {
+    case 'T': /* a new tuple group */
+      sprintf(conn->errorMessage,
+             "makePGresult() -- is not equipped to handle multiple tuple groups.\n");
+      goto makePGresult_badResponse_return;
+    case 'B': /* a tuple in binary format */
+    case 'D': /* a tuple in ASCII format */
+      newTup = getTuple(conn, result, (id == 'B'));
+      if (newTup == NULL) 
+       goto makePGresult_badResponse_return;
+      addTuple(result,newTup);
+      break;
+/*    case 'A':    
+      sprintf(conn->errorMessage, "Asynchronous portals not supported");
+      result->resultStatus = PGRES_NONFATAL_ERROR;
+      return result;
+      break;
+*/
+    case 'C': /* end of portal tuple stream */
+      {
+      char command[MAX_MESSAGE_LEN];
+      pqGets(command,MAX_MESSAGE_LEN, Pfin, Pfdebug); /* read the command tag */
+      done = 1;
+    }
+      break;
+    case 'E': /* errors */
+      if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, Pfin, Pfdebug) == 1) {
+       sprintf(conn->errorMessage,
+               "Error return detected from backend, but error message cannot be read");
+      }
+      result->resultStatus = PGRES_FATAL_ERROR;
+      return result;
+      break;
+    case 'N': /* notices from the backend */
+      if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, Pfin, Pfdebug) == 1) {
+       sprintf(conn->errorMessage,
+       "Notice return detected from backend, but error message cannot be read");
+      }   else
+       /* XXXX send Notices to stderr for now */
+       fprintf(stderr, "%s\n", conn->errorMessage);
+      break;
+    default: /* uh-oh
+               this should never happen but frequently does when the 
+               backend dumps core */
+      sprintf(conn->errorMessage,"FATAL:  unexpected results from the backend, it probably dumped core.");
+      fprintf(stderr, conn->errorMessage);
+      result->resultStatus = PGRES_FATAL_ERROR;
+      return result;
+      break;
+    }
+    if (!done)
+      id = getc(Pfin);
+  } /* while (1) */
+
+  result->resultStatus = PGRES_TUPLES_OK;
+  return result;
+
+makePGresult_badResponse_return:
+  result->resultStatus = PGRES_BAD_RESPONSE;
+  return result;
+
+}
+
+
+
+/*
+ * PQexec
+ *    send a query to the backend and package up the result in a Pgresult
+ *
+ *  if the query failed, return NULL, conn->errorMessage is set to 
+ * a relevant message
+ *  if query is successful, a new PGresult is returned
+ * the use is responsible for freeing that structure when done with it
+ *
+ */
+
+PGresult*
+PQexec(PGconn* conn, char* query)
+{
+  PGresult *result;
+  int id, clear;
+  char buffer[MAX_MESSAGE_LEN];
+  char cmdStatus[MAX_MESSAGE_LEN];
+  char pname[MAX_MESSAGE_LEN]; /* portal name */
+  PGnotify *newNotify;
+  FILE *Pfin = conn->Pfin;
+  FILE *Pfout = conn->Pfout;
+  FILE* Pfdebug = conn->Pfdebug;
+
+  pname[0]='\0';
+
+  /*clear the error string */
+  conn->errorMessage[0] = '\0';
+
+  /* check to see if the query string is too long */
+  if (strlen(query) > MAX_MESSAGE_LEN) {
+    sprintf(conn->errorMessage, "PQexec() -- query is too long.  Maximum length is %d\n", MAX_MESSAGE_LEN -2 );
+    return NULL;
+  }
+
+  /* the frontend-backend protocol uses 'Q' to designate queries */
+  sprintf(buffer,"Q%s",query);
+
+  /* send the query to the backend; */
+  if (pqPuts(buffer,Pfout, Pfdebug) == 1) {
+      (void) sprintf(conn->errorMessage,
+                    "PQexec() -- while sending query:  %s\n-- fprintf to Pfout failed: errno=%d\n%s\n",
+                    query, errno,strerror(errno));
+      return NULL;
+    }
+
+  /* loop forever because multiple messages, especially NOTICES,
+     can come back from the backend
+     NOTICES are output directly to stderr
+   */
+
+  while (1) {
+
+    /* read the result id */
+    id = pqGetc(Pfin,Pfdebug);
+    if (id == EOF) {
+      /* hmm,  no response from the backend-end, that's bad */
+      (void) sprintf(conn->errorMessage,
+                    "PQexec() -- No response from backend\n");
+      return (PGresult*)NULL;
+    }
+
+    switch (id) {
+    case 'A': 
+       newNotify = (PGnotify*)malloc(sizeof(PGnotify));
+       pqGetInt(&(newNotify->be_pid), 4, Pfin, Pfdebug);
+       pqGets(newNotify->relname, NAMEDATALEN, Pfin, Pfdebug);
+       DLAddTail(conn->notifyList, DLNewElem(newNotify));
+       /* async messages are piggy'ed back on other messages,
+          so we stay in the while loop for other messages */
+       break;
+    case 'C': /* portal query command, no tuples returned */
+      if (pqGets(cmdStatus, MAX_MESSAGE_LEN, Pfin, Pfdebug) == 1) {
+       sprintf(conn->errorMessage,
+               "PQexec() -- query command completed, but return message from backend cannot be read");
+       return (PGresult*)NULL;
+      } 
+      else {
+       /*
+       // since backend may produce more than one result for some commands
+       // need to poll until clear 
+       // send an empty query down, and keep reading out of the pipe
+       // until an 'I' is received.
+       */
+       clear = 0;
+
+       pqPuts("Q ",Pfout,Pfdebug); /* send an empty query */
+       while (!clear)
+         {
+           if (pqGets(buffer,ERROR_MSG_LENGTH,Pfin,Pfdebug) == 1)
+             clear = 1;
+           clear = (buffer[0] == 'I');
+         }
+       result = makeEmptyPGresult(conn,PGRES_COMMAND_OK);
+       strncpy(result->cmdStatus,cmdStatus, CMDSTATUS_LEN-1);
+       return result;
+      }
+      break;
+    case 'E': /* error return */
+      if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, Pfin, Pfdebug) == 1) {
+       (void) sprintf(conn->errorMessage,
+                      "PQexec() -- error return detected from backend, but error message cannot be read");
+      }
+      return (PGresult*)NULL;
+      break;
+    case 'I': /* empty query */
+      /* read the throw away the closing '\0' */
+      {
+       int c;
+       if ((c = pqGetc(Pfin,Pfdebug)) != '\0') {
+         fprintf(stderr,"error!, unexpected character %c following 'I'\n", c);
+       }
+       result = makeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
+       return result;
+      }
+      break;
+    case 'N': /* notices from the backend */
+      if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, Pfin, Pfdebug) == 1) {
+       sprintf(conn->errorMessage,
+               "PQexec() -- error return detected from backend, but error message cannot be read");
+       return (PGresult*)NULL;
+      }
+      else
+       fprintf(stderr,"%s", conn->errorMessage);
+      break;
+    case 'P': /* synchronous (normal) portal */
+      pqGets(pname,MAX_MESSAGE_LEN,Pfin, Pfdebug);  /* read in the portal name*/
+      break;
+    case 'T': /* actual tuple results: */
+      return makePGresult(conn, pname);
+      break;
+    case 'D': /* copy command began successfully */
+      return makeEmptyPGresult(conn,PGRES_COPY_IN);
+      break;
+    case 'B': /* copy command began successfully */
+      return makeEmptyPGresult(conn,PGRES_COPY_OUT);
+      break;
+    default:
+      sprintf(conn->errorMessage,
+             "unknown protocol character %c read from backend\n",
+             id);
+      return (PGresult*)NULL;
+    } /* switch */
+} /* while (1)*/
+
+}
+
+/*
+ * PQnotifies
+ *    returns a PGnotify* structure of the latest async notification
+ * that has not yet been handled
+ *
+ * returns NULL, if there is currently 
+ * no unhandled async notification from the backend
+ *
+ * the CALLER is responsible for FREE'ing the structure returned
+ */
+
+PGnotify*
+PQnotifies(PGconn *conn)
+{
+    Dlelem *e;
+    if (conn->status != CONNECTION_OK) 
+       return NULL;
+    /* RemHead returns NULL if list is empy */
+    e = DLRemHead(conn->notifyList);
+    if (e) 
+       return (PGnotify*)DLE_VAL(e);
+    else 
+       return NULL;
+}
+
+/*
+ * PQgetline - gets a newline-terminated string from the backend.
+ * 
+ * Chiefly here so that applications can use "COPY <rel> to stdout"
+ * and read the output string.  Returns a null-terminated string in s.
+ *
+ * PQgetline reads up to maxlen-1 characters (like fgets(3)) but strips
+ * the terminating \n (like gets(3)).
+ *
+ * RETURNS:
+ *     EOF if it is detected or invalid arguments are given
+ *     0 if EOL is reached (i.e., \n has been read)
+ *             (this is required for backward-compatibility -- this
+ *              routine used to always return EOF or 0, assuming that
+ *              the line ended within maxlen bytes.)
+ *     1 in other cases
+ */
+int
+PQgetline(PGconn *conn, char *s, int maxlen)
+{
+    int c = '\0';
+    
+    if (!conn->Pfin || !s || maxlen <= 1)
+       return(EOF);
+    
+    for (; maxlen > 1 && 
+         (c = pqGetc(conn->Pfin, conn->Pfdebug)) != '\n' && 
+          c != EOF;
+        --maxlen) {
+       *s++ = c;
+    }
+    *s = '\0';
+    
+    if (c == EOF) {
+       return(EOF);            /* error -- reached EOF before \n */
+    } else if (c == '\n') {
+       return(0);              /* done with this line */
+    }
+    return(1);                 /* returning a full buffer */
+}
+
+
+/*
+ * PQputline -- sends a string to the backend.
+ * 
+ * Chiefly here so that applications can use "COPY <rel> from stdin".
+ *
+ */
+void
+PQputline(PGconn *conn, char *s)
+{
+    if (conn->Pfout) {
+       (void) fputs(s, conn->Pfout);
+       fflush(conn->Pfout);
+    }
+}
+
+/*
+ * PQendcopy
+ *     called while waiting for the backend to respond with success/failure
+ *     to a "copy".
+ *
+ * RETURNS:
+ *     0 on failure
+ *     1 on success
+ */
+int
+PQendcopy(PGconn *conn)
+{
+    char id;
+    FILE *Pfin = conn->Pfin;
+    FILE* Pfdebug = conn->Pfdebug;
+
+    if ( (id = pqGetc(Pfin,Pfdebug)) > 0)
+       return(0);
+    switch (id) {
+    case 'Z': /* backend finished the copy */
+       return(1);
+    case 'E':
+    case 'N':
+       if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, Pfin, Pfdebug) == 1) {
+           sprintf(conn->errorMessage,
+                   "Error return detected from backend, but error message cannot be read");
+       }
+       return(0);
+       break;
+    default:
+       (void) sprintf(conn->errorMessage,
+                      "FATAL: PQendcopy: protocol error: id=%x\n",
+                      id);
+       fputs(conn->errorMessage, stderr);
+       fprintf(stderr,"resetting connection\n");
+       PQreset(conn);
+       return(0);
+    }
+}
+
+/* simply send out max-length number of filler characters to fp */
+static void
+fill (int length, int max, char filler, FILE *fp)
+{
+  int count;
+  char filltmp[2];
+
+  filltmp[0] = filler;
+  filltmp[1] = 0;
+  count = max - length;
+  while (count-- >= 0)
+    {
+      fprintf(fp, "%s", filltmp);
+    }
+ }
+
+
+/*
+ * PQdisplayTuples()
+ *
+ * a better version of PQprintTuples()
+ * that can optionally do padding of fields with spaces and use different
+ * field separators 
+ */
+void
+PQdisplayTuples(PGresult *res,
+               FILE *fp,      /* where to send the output */
+               int fillAlign, /* pad the fields with spaces */
+               char *fieldSep,  /* field separator */
+               int printHeader, /* display headers? */
+               int quiet
+               )
+{
+#define DEFAULT_FIELD_SEP " "
+
+    char *pager;
+    int i, j;
+    int nFields;
+    int nTuples;
+    int fLength[MAX_FIELDS];
+    int usePipe = 0;
+
+    if (fieldSep == NULL)
+       fieldSep == DEFAULT_FIELD_SEP;
+
+    if (fp == NULL) 
+       fp = stdout;
+    if (fp == stdout) {
+       /* try to pipe to the pager program if possible */
+       pager=getenv("PAGER");
+       if (pager != NULL) {
+           fp = popen(pager, "w");
+           if (fp) {
+               usePipe = 1;
+               signal(SIGPIPE, SIG_IGN);
+           } else
+               fp = stdout;
+       }
+    }
+
+    /* Get some useful info about the results */
+    nFields = PQnfields(res);
+    nTuples = PQntuples(res);
+  
+    /* Zero the initial field lengths */
+    for (j=0  ; j < nFields; j++) {
+      fLength[j] = strlen(PQfname(res,j));
+    }
+    /* Find the max length of each field in the result */
+    /* will be somewhat time consuming for very large results */
+    if (fillAlign) {
+       for (i=0; i < nTuples; i++) {
+           for (j=0  ; j < nFields; j++) {
+               if (PQgetlength(res,i,j) > fLength[j])
+                   fLength[j] = PQgetlength(res,i,j);
+           }
+       }
+    }
+
+    if (printHeader) {
+       /* first, print out the attribute names */
+       for (i=0; i < nFields; i++) {
+           fputs(PQfname(res,i), fp);
+           if (fillAlign)
+               fill (strlen (PQfname(res,i)), fLength[i], ' ', fp);
+           fputs(fieldSep,fp);
+       }
+       fprintf(fp, "\n");
+  
+       /* Underline the attribute names */
+       for (i=0; i < nFields; i++) {
+           if (fillAlign)
+               fill (0, fLength[i], '-', fp);
+           fputs(fieldSep,fp);
+       }
+       fprintf(fp, "\n");
+    }
+
+    /* next, print out the instances */
+    for (i=0; i < nTuples; i++) {
+      for (j=0  ; j < nFields; j++) {
+        fprintf(fp, "%s", PQgetvalue(res,i,j));
+       if (fillAlign)
+           fill (strlen (PQgetvalue(res,i,j)), fLength[j], ' ', fp);
+       fputs(fieldSep,fp);
+      }
+      fprintf(fp, "\n");
+    }
+  
+    if (!quiet)
+       fprintf (fp, "\nQuery returned %d row%s.\n",PQntuples(res),
+                (PQntuples(res) == 1) ? "" : "s");
+  
+    fflush(fp);
+    if (usePipe) {
+       pclose(fp);
+       signal(SIGPIPE, SIG_DFL);
+    }
+}
+
+
+
+/*
+ * PQprintTuples()
+ *
+ * This is the routine that prints out the tuples that
+ *  are returned from the backend.
+ * Right now all columns are of fixed length,
+ * this should be changed to allow wrap around for
+ * tuples values that are wider.
+ */
+void
+PQprintTuples(PGresult *res,
+             FILE* fout,      /* output stream */
+             int PrintAttNames,/* print attribute names or not*/
+             int TerseOutput, /* delimiter bars or not?*/
+             int colWidth   /* width of column, if 0, use variable width */
+             )
+{
+    int nFields; 
+    int nTups;
+    int i,j;
+    char formatString[80];
+
+    char *tborder = NULL;
+
+    nFields = PQnfields(res);
+    nTups = PQntuples(res);
+
+    if (colWidth > 0) {
+      sprintf(formatString,"%%s %%-%ds",colWidth);
+    } else
+      sprintf(formatString,"%%s %%s");
+
+    if ( nFields > 0 ) {  /* only print tuples with at least 1 field.  */
+
+       if (!TerseOutput)
+       {
+           int width;
+           width = nFields * 14;
+           tborder = malloc (width+1);
+           for (i = 0; i <= width; i++) 
+               tborder[i] = '-';
+           tborder[i] = '\0';
+           fprintf(fout,"%s\n",tborder);
+       }
+
+       for (i=0; i < nFields; i++) {
+           if (PrintAttNames) {
+               fprintf(fout,formatString,
+                       TerseOutput ? "" : "|",
+                       PQfname(res, i));
+           }
+       }
+
+       if (PrintAttNames) {
+           if (TerseOutput)
+               fprintf(fout,"\n");
+           else
+               fprintf(fout, "|\n%s\n",tborder);
+       }
+       
+       for (i = 0; i < nTups; i++) {
+           for (j = 0; j < nFields; j++) {
+               char *pval = PQgetvalue(res,i,j);
+               fprintf(fout, formatString,
+                       TerseOutput ? "" : "|",
+                       pval ? pval : "");
+           }
+           if (TerseOutput)
+               fprintf(fout,"\n");
+           else
+               fprintf(fout, "|\n%s\n",tborder);
+       }
+    }
+}
+
+
+/* ----------------
+ *     PQfn -  Send a function call to the POSTGRES backend.
+ *
+ *      conn            : backend connection
+ *     fnid            : function id
+ *     result_buf      : pointer to result buffer (&int if integer)
+ *     result_len      : length of return value.
+ *      actual_result_len: actual length returned. (differs from result_len
+ *                       for varlena structures.)
+ *      result_type     : If the result is an integer, this must be 1,
+ *                        otherwise this should be 0
+ *     args            : pointer to a NULL terminated arg array.
+ *                       (length, if integer, and result-pointer)
+ *     nargs           : # of arguments in args array.
+ *
+ * RETURNS
+ *     NULL on failure.  PQerrormsg will be set.
+ *     "G" if there is a return value.
+ *     "V" if there is no return value.
+ * ----------------
+ */
+
+PGresult*
+PQfn(PGconn *conn,
+     int fnid,
+     int *result_buf,
+     int *actual_result_len,
+     int result_is_int,
+     PQArgBlock *args,
+     int nargs)
+{
+    FILE *Pfin = conn->Pfin;
+    FILE *Pfout = conn->Pfout;
+    FILE* Pfdebug = conn->Pfdebug;
+    int id;
+    int i;
+
+    /* clear the error string */
+    conn->errorMessage[0] = '\0';
+
+    pqPuts("F ",Pfout,Pfdebug);           /* function */
+    pqPutInt(fnid, 4, Pfout, Pfdebug);    /* function id */
+    pqPutInt(nargs, 4, Pfout, Pfdebug);             /* # of args */
+
+    for (i = 0; i < nargs; ++i) { /*   len.int4 + contents     */
+       pqPutInt(args[i].len, 4, Pfout, Pfdebug);
+       if (args[i].isint) {
+           pqPutInt(args[i].u.integer, 4, Pfout, Pfdebug);
+       } else {
+           pqPutnchar((char *)args[i].u.ptr, args[i].len, Pfout, Pfdebug);
+       }
+    }
+    pqFlush(Pfout, Pfdebug);
+
+    id = pqGetc(Pfin, Pfdebug);
+    if (id != 'V') {
+       if (id == 'E') {
+           pqGets(conn->errorMessage,ERROR_MSG_LENGTH,Pfin,Pfdebug);
+       } else
+           sprintf(conn->errorMessage,
+                   "PQfn: expected a 'V' from the backend. Got '%c' instead",
+                   id);
+       return makeEmptyPGresult(conn,PGRES_FATAL_ERROR);
+    }
+
+    id = pqGetc(Pfin, Pfdebug);
+    for (;;) {
+       int c;
+       switch (id) {
+       case 'G':               /* function returned properly */
+           pqGetInt(actual_result_len,4,Pfin,Pfdebug);
+           if (result_is_int) {
+               pqGetInt(result_buf,4,Pfin,Pfdebug);
+           } else {
+               pqGetnchar((char *) result_buf, *actual_result_len,
+                          Pfin, Pfdebug);
+           }
+           c = pqGetc(Pfin, Pfdebug); /* get the last '0'*/
+           return makeEmptyPGresult(conn,PGRES_COMMAND_OK);
+       case 'E':
+           sprintf(conn->errorMessage,
+                   "PQfn: returned an error");
+           return makeEmptyPGresult(conn,PGRES_FATAL_ERROR);
+       case 'N':
+           /* print notice and go back to processing return values */
+           if (pqGets(conn->errorMessage, ERROR_MSG_LENGTH, Pfin, Pfdebug) == 1) {
+               sprintf(conn->errorMessage,
+                       "Notice return detected from backend, but error message cannot be read");
+           }   else
+               fprintf(stderr, "%s\n", conn->errorMessage);
+           /* keep iterating */
+           break;
+       case '0':               /* no return value */
+           return makeEmptyPGresult(conn,PGRES_COMMAND_OK);
+       default:
+           /* The backend violates the protocol. */
+           sprintf(conn->errorMessage,
+                   "FATAL: PQfn: protocol error: id=%x\n", id);
+           return makeEmptyPGresult(conn,PGRES_FATAL_ERROR);
+       }
+    }
+}
+
+
+
+
+/* ====== accessor funcs for PGresult ======== */
+
+ExecStatusType 
+PQresultStatus(PGresult* res)
+{ 
+    return res->resultStatus; 
+}
+
+int 
+PQntuples(PGresult *res) 
+{
+    return res->ntups;
+}
+
+int
+PQnfields(PGresult *res) 
+{
+    return res->numAttributes;
+}
+
+/*
+   returns NULL if the field_num is invalid
+*/
+char* 
+PQfname(PGresult *res, int field_num) 
+{
+    if (field_num > (res->numAttributes - 1))  {
+       fprintf(stderr,
+               "PQfname: ERROR! name of field %d(of %d) is not available", 
+               field_num, res->numAttributes -1);
+       return NULL;
+    }
+    if (res->attDescs) {
+       return res->attDescs[field_num].name;
+    } else
+       return NULL;
+}
+
+/*
+   returns -1 on a bad field name
+*/
+int
+PQfnumber(PGresult *res, char* field_name) 
+{
+  int i;
+
+  if (field_name == NULL ||
+      field_name[0] == '\0' ||
+      res->attDescs == NULL)
+    return  -1;
+
+  for (i=0;i<res->numAttributes;i++) {
+    if ( strcmp(field_name, res->attDescs[i].name) == 0 )
+      return i;
+  }
+  return -1;
+
+}
+
+Oid
+PQftype(PGresult *res, int field_num) 
+{
+    if (field_num > (res->numAttributes - 1))  {
+       fprintf(stderr,
+               "PQftype: ERROR! type of field %d(of %d) is not available", 
+               field_num, res->numAttributes -1);
+    }
+    if (res->attDescs) {
+       return res->attDescs[field_num].adtid;
+    } else
+       return InvalidOid;
+}
+
+int2
+PQfsize(PGresult *res, int field_num) 
+{
+    if (field_num > (res->numAttributes - 1))  {
+       fprintf(stderr,
+               "PQfsize: ERROR! size of field %d(of %d) is not available", 
+               field_num, res->numAttributes -1);
+    }
+    if (res->attDescs) {
+       return res->attDescs[field_num].adtsize;
+    } else
+       return 0;
+}
+
+char* PQcmdStatus(PGresult *res) {
+  return res->cmdStatus;
+}
+
+/*
+   PQoidStatus -
+    if the last command was an INSERT, return the oid string 
+    if not, return ""
+*/
+char* PQoidStatus(PGresult *res) {
+  if (!res->cmdStatus)
+    return "";
+
+  if (strncmp(res->cmdStatus, "INSERT",6) == 0) {
+    return res->cmdStatus+7;
+  } else
+    return "";
+}
+
+/*
+   PQgetvalue:
+    return the attribute value of field 'field_num' of
+    tuple 'tup_num'
+
+    If res is binary, then the value returned is NOT a null-terminated 
+    ASCII string, but the binary representation in the server's native
+    format.
+
+    if res is not binary, a null-terminated ASCII string is returned.
+*/
+char* 
+PQgetvalue(PGresult *res, int tup_num, int field_num)
+{
+    if (tup_num > (res->ntups - 1) ||
+       field_num > (res->numAttributes - 1))  {
+       fprintf(stderr,
+               "PQgetvalue: ERROR! field %d(of %d) of tuple %d(of %d) is not available", 
+               field_num, res->numAttributes - 1, tup_num, res->ntups);
+    }
+       
+    
+    return res->tuples[tup_num][field_num].value;
+}
+
+/* PQgetlength:
+     returns the length of a field value in bytes.  If res is binary,
+     i.e. a result of a binary portal, then the length returned does
+     NOT include the size field of the varlena.
+*/
+int
+PQgetlength(PGresult *res, int tup_num, int field_num)
+{
+    if (tup_num > (res->ntups - 1 )||
+       field_num > (res->numAttributes - 1))  {
+       fprintf(stderr,
+               "PQgetlength: ERROR! field %d(of %d) of tuple %d(of %d) is not available", 
+               field_num, res->numAttributes - 1, tup_num, res->ntups);
+    }
+       
+    return res->tuples[tup_num][field_num].len;
+  }
diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c
new file mode 100644 (file)
index 0000000..00bbc85
--- /dev/null
@@ -0,0 +1,381 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-lobj.c--
+ *    Front-end large object interface
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "postgres.h"
+#include "libpq-fe.h"
+#include "obj/fmgr.h" 
+#include "libpq/libpq-fs.h"
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+
+#define LO_BUFSIZE        1024
+
+/*
+ * lo_open
+ *    opens an existing large object
+ *
+ * returns the file descriptor for use in later lo_* calls
+ * return -1 upon failure.
+ */
+int
+lo_open(PGconn* conn, Oid lobjId, int mode)
+{
+    int fd;
+    int result_len;
+    PQArgBlock argv[2];
+    PGresult *res;
+
+    argv[0].isint = 1;
+    argv[0].len = 4;
+    argv[0].u.integer = lobjId;
+
+    argv[1].isint = 1;
+    argv[1].len = 4;
+    argv[1].u.integer = mode;
+    
+    res = PQfn(conn, F_LO_OPEN,&fd,&result_len,1,argv,2); 
+    if (PQresultStatus(res) == PGRES_COMMAND_OK) {
+       PQclear(res);
+
+       /* have to do this to reset offset in shared fd cache */
+       /* but only if fd is valid */
+       if (fd >= 0 && lo_lseek(conn, fd, 0L, SEEK_SET) < 0)
+           return -1;
+       return fd;
+    } else
+       return -1;
+}
+
+/*
+ * lo_close
+ *    closes an existing large object
+ *
+ * returns 0 upon success
+ * returns -1 upon failure.
+ */
+int
+lo_close(PGconn *conn, int fd)
+{
+    PQArgBlock argv[1];
+    PGresult *res;
+    int retval;
+    int result_len;
+
+    argv[0].isint = 1;
+    argv[0].len = 4;
+    argv[0].u.integer = fd;
+    res = PQfn(conn, F_LO_CLOSE,&retval,&result_len,1,argv,1);
+    if (PQresultStatus(res) == PGRES_COMMAND_OK) {
+       PQclear(res);
+       return retval;
+    } else
+       return -1;
+}
+
+/*
+ * lo_read
+ *    read len bytes of the large object into buf
+ *
+ * returns the length of bytes read.
+ * the CALLER must have allocated enough space to hold the result returned
+ */
+
+int
+lo_read(PGconn *conn, int fd, char *buf, int len)
+{
+    PQArgBlock argv[2];
+    PGresult *res;
+    int result_len;
+
+    argv[0].isint = 1;
+    argv[0].len = 4;
+    argv[0].u.integer = fd;
+
+    argv[1].isint = 1;
+    argv[1].len = 4;
+    argv[1].u.integer = len;
+
+    res = PQfn(conn, F_LOREAD,(int*)buf,&result_len,0,argv,2);
+    if (PQresultStatus(res) == PGRES_COMMAND_OK) {
+       PQclear(res);
+       return result_len;
+    } else
+       return -1;
+}
+
+/*
+ * lo_write
+ *    write len bytes of buf into the large object fd
+ *
+ */
+int
+lo_write(PGconn *conn, int fd, char *buf, int len)
+{
+    PQArgBlock argv[2];
+    PGresult *res;
+    int result_len;
+    int retval;
+
+    if (len <= 0)
+       return 0;
+
+    argv[0].isint = 1;
+    argv[0].len = 4;
+    argv[0].u.integer = fd;
+
+    argv[1].isint = 0;
+    argv[1].len = len;
+    argv[1].u.ptr = (int*)buf;
+
+    res = PQfn(conn, F_LOWRITE,&retval,&result_len,1,argv,2);
+    if (PQresultStatus(res) == PGRES_COMMAND_OK) {
+       PQclear(res);
+       return retval;
+    } else
+       return -1;
+}
+
+/*
+ * lo_lseek
+ *    change the current read or write location on a large object
+ * currently, only L_SET is a legal value for whence
+ *
+ */
+
+int
+lo_lseek(PGconn *conn, int fd, int offset, int whence)
+{
+    PQArgBlock argv[3];
+    PGresult *res;
+    int retval; 
+    int result_len;
+    
+    argv[0].isint = 1;
+    argv[0].len = 4;
+    argv[0].u.integer = fd;
+    
+    argv[1].isint = 1;
+    argv[1].len = 4;
+    argv[1].u.integer = offset;
+
+    argv[2].isint = 1;
+    argv[2].len = 4;
+    argv[2].u.integer = whence;
+
+    res = PQfn(conn, F_LO_LSEEK,&retval,&result_len,1,argv,3);
+    if (PQresultStatus(res) == PGRES_COMMAND_OK) {
+       PQclear(res);
+       return retval;
+    } else
+       return -1;
+}
+
+/*
+ * lo_creat
+ *    create a new large object
+ * the mode is a bitmask describing different attributes of the new object
+ *
+ * returns the oid of the large object created or
+ * InvalidOid upon failure
+ */
+
+Oid
+lo_creat(PGconn *conn, int mode)
+{
+    PQArgBlock argv[1];
+    PGresult *res;
+    int retval;
+    int result_len;
+
+    argv[0].isint = 1;
+    argv[0].len = 4;
+    argv[0].u.integer = mode;
+    res  = PQfn(conn, F_LO_CREAT,&retval,&result_len,1,argv,1);
+    if (PQresultStatus(res) == PGRES_COMMAND_OK) {
+       PQclear(res);
+       return (Oid)retval;
+    } else
+       return InvalidOid;
+}
+
+
+/*
+ * lo_tell
+ *    returns the current seek location of the large object
+ *
+ */
+
+int
+lo_tell(PGconn *conn, int fd)
+{
+    int retval;
+    PQArgBlock argv[1];
+    PGresult *res;
+    int result_len;
+
+    argv[0].isint = 1;
+    argv[0].len = 4;
+    argv[0].u.integer = fd;
+
+    res = PQfn(conn, F_LO_TELL,&retval,&result_len,1,argv,1);
+    if (PQresultStatus(res) == PGRES_COMMAND_OK) {
+       PQclear(res);
+       return retval;
+    } else
+       return -1;
+}
+
+/*
+ * lo_unlink
+ *    delete a file
+ *
+ */
+
+int
+lo_unlink(PGconn *conn, Oid lobjId)
+{
+    PQArgBlock argv[1];
+    PGresult *res;
+    int result_len;
+    int retval;
+
+    argv[0].isint = 1;
+    argv[0].len = 4;
+    argv[0].u.integer = lobjId;
+
+    res = PQfn(conn, F_LO_UNLINK,&retval,&result_len,1,argv,1);
+    if (PQresultStatus(res) == PGRES_COMMAND_OK) {
+       PQclear(res);
+       return retval;
+    } else
+       return -1;
+}
+
+/*
+ * lo_import -
+ *    imports a file as an (inversion) large object.
+ *      returns the oid of that object upon success,
+ * returns InvalidOid upon failure
+ *
+ */
+
+Oid
+lo_import(PGconn *conn, char* filename)
+{
+    int fd;
+    int nbytes, tmp;
+    char buf[LO_BUFSIZE];
+    Oid lobjOid;
+    int lobj;
+    
+    /*
+     * open the file to be read in
+     */
+    fd = open(filename, O_RDONLY, 0666);
+    if (fd < 0)  {   /* error */
+       sprintf(conn->errorMessage,
+               "lo_import: can't open unix file\"%s\"\n", filename);
+       return InvalidOid;
+    }
+
+    /*
+     * create an inversion "object"
+     */
+    lobjOid = lo_creat(conn, INV_READ|INV_WRITE);
+    if (lobjOid == InvalidOid) {
+       sprintf(conn->errorMessage,
+               "lo_import: can't create inv object for \"%s\"", filename);
+       return InvalidOid;
+    }
+
+    lobj = lo_open(conn, lobjOid, INV_WRITE);
+    if (lobj == -1) {
+       sprintf(conn->errorMessage,
+               "lo_import: could not open inv object oid %d",lobjOid);
+       return InvalidOid;
+    }
+    /*
+     * read in from the Unix file and write to the inversion file
+     */
+    while ((nbytes = read(fd, buf, LO_BUFSIZE)) > 0) {
+       tmp = lo_write(conn,lobj, buf, nbytes);
+        if (tmp < nbytes) {
+           sprintf(conn->errorMessage,
+                   "lo_import: error while reading \"%s\"",filename);
+           return InvalidOid;
+       }
+    }
+
+    (void) close(fd);
+    (void) lo_close(conn, lobj);
+
+    return lobjOid;
+}
+
+/*
+ * lo_export -
+ *    exports an (inversion) large object.
+ * returns -1 upon failure, 1 otherwise
+ */
+int
+lo_export(PGconn *conn, Oid lobjId, char *filename)
+{
+    int fd;
+    int nbytes, tmp;
+    char buf[LO_BUFSIZE];
+    int lobj;
+
+    /*
+     * create an inversion "object"
+     */
+    lobj = lo_open(conn, lobjId, INV_READ);
+    if (lobj == -1) {
+       sprintf(conn->errorMessage,
+               "lo_export: can't open inv object %d",lobjId);
+       return -1;
+    }
+
+    /*
+     * open the file to be written to
+     */
+    fd = open(filename, O_CREAT|O_WRONLY, 0666);
+    if (fd < 0)  {   /* error */
+       sprintf(conn->errorMessage,
+               "lo_export: can't open unix file\"%s\"",filename);
+       return 0;
+    }
+
+    /*
+     * read in from the Unix file and write to the inversion file
+     */
+    while ((nbytes = lo_read(conn, lobj, buf, LO_BUFSIZE)) > 0) {
+       tmp = write(fd, buf, nbytes);
+        if (tmp < nbytes) {
+           sprintf(conn->errorMessage,
+                   "lo_export: error while writing \"%s\"",
+                   filename);
+           return -1;
+       }
+    }
+
+    (void) lo_close(conn,lobj);
+    (void) close(fd);
+
+    return 1;
+}
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
new file mode 100644 (file)
index 0000000..d3a8111
--- /dev/null
@@ -0,0 +1,193 @@
+/*-------------------------------------------------------------------------
+ *
+ *   FILE
+ *     fe-misc.c
+ *
+ *   DESCRIPTION
+ *       miscellaneous useful functions
+ *   these routines are analogous to the ones in libpq/pqcomm.c
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/* pqGetc:
+   get a character from stream f
+
+   if debug is set, also echo the character fetched
+*/
+int
+pqGetc(FILE* fin, FILE* debug)
+{
+  int c;
+
+  c = getc(fin);
+  if (debug && c != EOF)
+    putc(c,debug);
+  return c;
+}
+
+/* pqPutnchar:
+   send a string of exactly len length into stream f 
+
+   returns 1 if there was an error, 0 otherwise.
+*/
+int
+pqPutnchar(char* s, int len, FILE *f, FILE *debug)
+{
+    int status;
+
+    if (f == NULL)
+       return 1;
+
+    while (len--) {
+       status = fputc(*s,f);
+       if (debug)
+           fputc(*s,debug);
+       s++;
+       if (status == EOF)
+           return 1;
+    }
+    return 0;
+}
+
+/* pqGetnchar:
+   get a string of exactly len length from stream f 
+*/
+int
+pqGetnchar(char* s, int len, FILE *f, FILE *debug)
+{
+  int c;
+
+  if (f == NULL)
+    return 1;
+  
+  while (len-- && (c = getc(f)) != EOF)
+    *s++ = c;
+  *s = '\0';
+
+  if (debug) {
+      fputs(s,debug);
+  }
+  return 0;
+}
+
+/* pqGets:
+   get a string of up to length len from stream f
+*/
+int
+pqGets(char* s, int len, FILE *f, FILE *debug)
+{
+  int c;
+
+  if (f == NULL)
+    return 1;
+  
+  while (len-- && (c = getc(f)) != EOF && c)
+    *s++ = c;
+  *s = '\0';
+
+  if (debug) {
+      fputs(s,debug);
+  }
+  return 0;
+}
+
+
+/* pgPutInt
+   send an integer of up to 4 bytesto the file stream
+   do this one byte at at time. 
+   This insures that machines with different ENDIANness can talk to each other
+   get a n-byte integer from the stream into result 
+   returns 0 if successful, 1 otherwise
+*/
+int
+pqPutInt(int i, int bytes, FILE* f, FILE *debug)
+{
+    int status;
+
+    if (bytes > 4)
+       bytes = 4;
+
+    while (bytes--) {
+       status = fputc(i & 0xff, f);
+       if (debug)
+           fputc(i & 0xff, debug);
+       i >>= 8;
+       if (status == EOF) {
+           return 1;
+       }
+    }
+    return 0;
+}
+
+/* pgGetInt 
+   reconstructs the integer one byte at a time.
+   This insures that machines with different ENDIANness can talk to each other
+   get a n-byte integer from the stream into result 
+   returns 0 if successful 
+*/
+int
+pqGetInt(int* result, int bytes, FILE* f, FILE *debug)
+{
+  int c;
+  int p;
+  int n;
+
+  if (f == NULL)
+    return 1;
+  
+  p = 0;
+  n = 0;
+  while (bytes && (c = getc(f)) != EOF)
+    {
+      n |= (c & 0xff) << p;
+      p += 8;
+      bytes--;
+    }
+
+  if (bytes != 0)
+    return 1;
+
+  *result = n;
+  if (debug)
+      fprintf(debug,"%d",*result);
+  return 0;
+}
+
+
+int
+pqPuts(char* s, FILE *f, FILE *debug)
+{
+  if (f == NULL)
+    return 1;
+  
+  if (fputs(s,f) == EOF)
+    return 1;
+
+  fputc('\0',f); /* important to send an ending EOF since backend expects it */
+  fflush(f);
+
+  if (debug) {
+      fputs(s,debug);
+  }
+  return 0;
+}
+
+
+void
+pqFlush(FILE *f, FILE *debug)
+{
+    if (f)
+       fflush(f);
+    if (debug)
+       fflush(debug);
+}
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
new file mode 100644 (file)
index 0000000..7a73696
--- /dev/null
@@ -0,0 +1,251 @@
+/*-------------------------------------------------------------------------
+ *
+ * libpq-fe.h--
+ *    This file contains definitions for structures and
+ *    externs for functions used by frontend postgres applications.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef LIBPQ_FE_H
+#define LIBPQ_FE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ----------------
+ *     include stuff common to fe and be
+ * ----------------
+ */
+/* #include "libpq/libpq.h" */
+#include "libpq/pqcomm.h"
+#include "lib/dllist.h"
+
+typedef enum {CONNECTION_OK,
+             CONNECTION_BAD}  ConnStatusType;
+
+typedef enum {
+  PGRES_EMPTY_QUERY = 0,
+  PGRES_COMMAND_OK,  /* a query command that doesn't return */
+                    /* anything was executed properly by the backend */
+  PGRES_TUPLES_OK,  /* a query command that returns tuples */
+                   /* was executed properly by the backend, PGresult */
+                   /* contains the resulttuples */
+  PGRES_COPY_OUT, 
+  PGRES_COPY_IN,
+  PGRES_BAD_RESPONSE, /* an unexpected response was recv'd from the backend */
+  PGRES_NONFATAL_ERROR,
+  PGRES_FATAL_ERROR
+
+} ExecStatusType;
+
+/* string descriptions of the ExecStatusTypes */
+extern char* pgresStatus[]; 
+
+/* 
+ * POSTGRES backend dependent Constants. 
+ */
+
+/* ERROR_MSG_LENGTH should really be the same as ELOG_MAXLEN in utils/elog.h*/
+#define ERROR_MSG_LENGTH 4096
+#define COMMAND_LENGTH 20
+#define REMARK_LENGTH 80
+#define PORTAL_NAME_LENGTH 16
+
+/* ----------------
+ * PQArgBlock --
+ *     Information (pointer to array of this structure) required
+ *     for the PQfn() call.
+ * ----------------
+ */
+typedef struct {
+    int len;
+    int isint;
+    union {
+        int *ptr;      /* can't use void (dec compiler barfs)  */
+       int integer;
+    } u;
+} PQArgBlock;
+
+typedef struct pgresAttDesc {
+  char* name; /* type name */
+  Oid adtid;  /* type id */
+  int2 adtsize; /* type size */
+} PGresAttDesc;
+
+/* use char* for Attribute values,
+   ASCII tuples are guaranteed to be null-terminated
+   For binary tuples, the first four bytes of the value is the size,
+   and the bytes afterwards are the value.  The binary value is 
+   not guaranteed to be null-terminated.  In fact, it can have embedded nulls*/
+typedef struct pgresAttValue {
+  int len; /* length in bytes of the value */
+  char *value; /* actual value */
+} PGresAttValue;
+
+typedef struct pgNotify {
+    char relname[NAMEDATALEN]; /* name of relation containing data */
+    int be_pid;                        /* process id of backend */
+} PGnotify;
+
+/* PGconn encapsulates a connection to the backend */
+typedef struct pg_conn{
+  char *pghost; /* the machine on which the server is running */
+  char *pgtty;  /* tty on which the backend messages is displayed */
+  char *pgport; /* the communication port with the backend */
+  char *pgoptions; /* options to start the backend with */
+  char *dbName; /* database name */
+  ConnStatusType status;
+  char errorMessage[ERROR_MSG_LENGTH];
+  /* pipes for be/fe communication */
+  FILE *Pfin;
+  FILE *Pfout;
+  FILE *Pfdebug;
+  void *port; /* really a Port* */
+  int asyncNotifyWaiting;
+  Dllist* notifyList;
+} PGconn;
+
+#define CMDSTATUS_LEN 40
+
+/* PGresult encapsulates the result of a query */
+/* unlike the old libpq, we assume that queries only return in one group */
+typedef struct pg_result{
+  int ntups;
+  int numAttributes;
+  PGresAttDesc *attDescs;
+  PGresAttValue* *tuples; /* each PGresTuple is an array of PGresAttValue's */
+  int tupArrSize;         /* size of tuples array allocated */
+  ExecStatusType resultStatus;
+  char cmdStatus[CMDSTATUS_LEN]; /* cmd status from the last insert query*/
+  int binary; /* binary tuple values if binary == 1, otherwise ASCII */
+  PGconn* conn;
+} PGresult;
+
+
+/* ===  in fe-connect.c === */
+  /* make a new client connection to the backend */
+extern PGconn* PQsetdb(char* pghost, char* pgport, char* pgoptions, 
+                      char* pgtty, char* dbName);
+  /* close the current connection and free the PGconn data structure */
+extern void PQfinish(PGconn* conn);
+  /* close the current connection and restablish a new one with the same 
+     parameters */
+extern void PQreset(PGconn* conn);
+
+extern char* PQdb(PGconn* conn);
+extern char* PQhost(PGconn* conn);
+extern char* PQoptions(PGconn* conn);
+extern char* PQport(PGconn* conn);
+extern char* PQtty(PGconn* conn);
+extern ConnStatusType PQstatus(PGconn* conn);
+extern char* PQerrorMessage(PGconn* conn);
+extern void PQtrace(PGconn *conn, FILE* debug_port);
+extern void PQuntrace(PGconn *conn);
+
+/* === in fe-exec.c === */
+extern PGresult* PQexec(PGconn* conn, char* query);
+extern int PQgetline(PGconn *conn, char* string, int length);
+extern int PQendcopy(PGconn *conn);
+extern void PQputline(PGconn *conn, char* string);
+extern ExecStatusType PQresultStatus(PGresult* res);
+extern int PQntuples(PGresult *res);
+extern int PQnfields(PGresult *res);
+extern char* PQfname(PGresult *res, int field_num);
+extern int PQfnumber(PGresult *res, char* field_name);
+extern Oid PQftype(PGresult *res, int field_num);
+extern int2 PQfsize(PGresult *res, int field_num);
+extern char* PQcmdStatus(PGresult *res);
+extern char* PQoidStatus(PGresult *res);
+extern char* PQgetvalue(PGresult *res, int tup_num, int field_num);
+extern int PQgetlength(PGresult *res, int tup_num, int field_num);
+extern void PQclear(PGresult* res);
+/* PQdisplayTuples() is a better version of PQprintTuples() */
+extern void PQdisplayTuples(PGresult *res,
+                           FILE *fp,      /* where to send the output */
+                           int fillAlign, /* pad the fields with spaces */
+                           char *fieldSep,  /* field separator */
+                           int printHeader, /* display headers? */
+                           int quiet);
+extern void PQprintTuples(PGresult* res, 
+                         FILE* fout,      /* output stream */
+                         int printAttName,/* print attribute names or not*/
+                         int terseOutput, /* delimiter bars or not?*/
+                         int width        /* width of column, 
+                                             if 0, use variable width */
+                         );
+extern PGnotify* PQnotifies(PGconn *conn);
+extern PGresult* PQfn(PGconn* conn,
+                     int fnid, 
+                     int *result_buf, 
+                     int *result_len,
+                     int result_is_int,
+                     PQArgBlock *args, 
+                     int nargs);
+/* === in fe-auth.c === */
+extern MsgType fe_getauthsvc(char* PQerrormsg);
+extern void fe_setauthsvc(char *name, char* PQerrormsg);
+extern char *fe_getauthname(char* PQerrormsg);
+
+/* === in fe-misc.c === */
+/* pqGets and pqPuts gets and sends strings to the file stream
+   returns 0 if successful 
+   if debug is non-null, debugging output is sent to that stream 
+*/
+extern int pqGets(char* s, int maxlen, FILE* stream, FILE* debug);
+extern int pqGetnchar(char* s, int maxlen, FILE* stream, FILE* debug);
+extern int pqPutnchar(char* s, int maxlen, FILE* stream, FILE* debug);
+extern int pqPuts(char* s, FILE* stream, FILE* debug );
+extern int pqGetc(FILE* stream, FILE *debug);
+/* get a n-byte integer from the stream into result */
+/* returns 0 if successful */
+extern int pqGetInt(int* result, int bytes, FILE* stream, FILE *debug );
+/* put a n-byte integer into the stream */
+/* returns 0 if successful */
+extern int pqPutInt(int n, int bytes, FILE* stream, FILE *debug );
+extern void pqFlush(FILE* stream, FILE* debug);
+
+/* === in fe-lobj.c === */
+int lo_open(PGconn* conn, Oid lobjId, int mode);
+int lo_close(PGconn *conn, int fd);
+int lo_read(PGconn *conn, int fd, char *buf, int len);
+int lo_write(PGconn *conn, int fd, char *buf, int len);
+int lo_lseek(PGconn *conn, int fd, int offset, int whence);
+Oid lo_creat(PGconn *conn, int mode);
+int lo_tell(PGconn *conn, int fd);
+int lo_unlink(PGconn *conn, Oid lobjId);
+Oid lo_import(PGconn *conn, char *filename);
+int lo_export(PGconn *conn, Oid lobjId, char *filename);
+/* max length of message to send  */
+#define MAX_MESSAGE_LEN 8193
+
+/* maximum number of fields in a tuple */
+#define BYTELEN 8
+#define MAX_FIELDS 512
+
+/* fall back options if they are not specified by arguments or defined
+   by environment variables */
+#define DefaultHost    "localhost"
+#define DefaultTty     ""
+#define DefaultOption  ""
+
+typedef void *TUPLE;
+#define palloc malloc
+#define pfree free
+
+#if defined(PORTNAME_sparc)
+extern char *sys_errlist[];
+#define strerror(A) (sys_errlist[(A)])
+#endif /* PORTNAME_sparc */
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* LIBPQ_FE_H */
+
diff --git a/src/interfaces/libpq/pg_hba b/src/interfaces/libpq/pg_hba
new file mode 100644 (file)
index 0000000..22a83be
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# Example config file for Postgres95 host based access
+#
+# Lines starting with "all" apply to all databases.  Otherwise the first
+# column has to match the name of the database being connected to.  Up to
+# ten config lines can apply to each database.  Mask specifies bits that 
+# aren't counted. After those bits are taken out, the connection address 
+# must match the address in the middle column.
+#
+# <name>       <address>       <mask>
+#
+all            127.0.0.1       0.0.0.0
+
diff --git a/src/interfaces/libpq/pqsignal.c b/src/interfaces/libpq/pqsignal.c
new file mode 100644 (file)
index 0000000..b65135e
--- /dev/null
@@ -0,0 +1,40 @@
+/*-------------------------------------------------------------------------
+ *
+ * pqsignal.c--
+ *    reliable BSD-style signal(2) routine stolen from RWW who stole it
+ *    from Stevens...
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ * NOTES
+ *     This shouldn't be in libpq, but the monitor and some other
+ *     things need it...
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "libpq/pqsignal.h"
+
+pqsigfunc
+pqsignal(int signo, pqsigfunc func)
+{
+#if defined(USE_POSIX_SIGNALS)
+    struct sigaction act, oact;
+    
+    act.sa_handler = func;
+    sigemptyset(&act.sa_mask);
+    act.sa_flags = 0;
+    if (signo != SIGALRM) {
+       act.sa_flags |= SA_RESTART;
+    }
+    if (sigaction(signo, &act, &oact) < 0)
+       return(SIG_ERR);
+    return(oact.sa_handler);
+#else /* !USE_POSIX_SIGNALS */
+    exit(1); /* this should never be reached, pqsignal should only
+             be called if USE_POSIX_SIGNALS is true*/
+#endif /* !USE_POSIX_SIGNALS */
+}
diff --git a/src/interfaces/libpq/pqsignal.h b/src/interfaces/libpq/pqsignal.h
new file mode 100644 (file)
index 0000000..8b70494
--- /dev/null
@@ -0,0 +1,32 @@
+/*-------------------------------------------------------------------------
+ *
+ * pqsignal.h--
+ *    prototypes for the reliable BSD-style signal(2) routine.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id$
+ *
+ * NOTES
+ *    This shouldn't be in libpq, but the monitor and some other
+ *    things need it...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PQSIGNAL_H
+#define PQSIGNAL_H
+
+#include <signal.h>
+
+#include "c.h"
+
+typedef void (*pqsigfunc)(int);
+
+extern pqsigfunc pqsignal(int signo, pqsigfunc func);
+
+#if defined(USE_POSIX_SIGNALS)
+#define        signal(signo, handler)  pqsignal(signo, (pqsigfunc)(handler))
+#endif /* USE_POSIX_SIGNALS */
+
+#endif /* PQSIGNAL_H */
diff --git a/src/mk/port/postgres.mk.BSD44_derived b/src/mk/port/postgres.mk.BSD44_derived
new file mode 100644 (file)
index 0000000..9ad4441
--- /dev/null
@@ -0,0 +1,40 @@
+#-------------------------------------------------------------------------
+#
+# postgres.mk.BSD44_derived--
+#    specific rules for OSs derived from 4.4-lite BSD
+#      e.g. NetBSD, FreeBSD and BSD/OS
+#
+# Copyright (c) 1994-5, Regents of the University of California
+#
+#    $Id$
+#
+#-------------------------------------------------------------------------
+ifndef MK_PORT
+MK_PORT=       BSD44_derived
+
+# cc is gcc, but never mind about that...
+CC=            gcc
+
+INSTALL=       /usr/bin/install
+RANLIB=                /usr/bin/ranlib
+
+AROPT = cq
+
+#
+# for postgres.user.mk
+#
+CFLAGS_SL = -fpic -DPIC
+
+SLSUFF=                .so
+
+%.so: %.o
+       $(LD) -x -r -o $(objdir)/$(<F).obj $(objdir)/$(<F)
+       @echo building shared object $(objdir)/$(@F)
+       @rm -f $(objdir)/$(@F).pic
+       @${AR} cq $(objdir)/$(@F).pic `lorder $(objdir)/$(<F).obj | tsort`
+       ${RANLIB} $(objdir)/$(@F).pic
+       @rm -f $(objdir)/$(@F)
+       $(LD) -x -Bshareable -Bforcearchive \
+                   -o $(objdir)/$(@F) $(objdir)/$(@F).pic
+
+endif
diff --git a/src/mk/port/postgres.mk.aix b/src/mk/port/postgres.mk.aix
new file mode 100644 (file)
index 0000000..b35693e
--- /dev/null
@@ -0,0 +1,55 @@
+#-------------------------------------------------------------------------
+#
+# postgres.mk.aix--
+#    IBM POWER/aix specific rules and variables
+#
+# Copyright (c) 1994-5, Regents of the University of California
+#
+#    $Id$
+#
+#   
+#-------------------------------------------------------------------------
+ifndef MK_PORT
+MK_PORT=       aix
+
+# might want to try installbsd instead 
+INSTALL= /usr/ucb/install
+
+#
+# for postgres.mk
+#
+
+# the -lm is because "pow" is defined in libbsd.a and we want pow(3m)
+LDADD_BE=      -lm -lbsd
+
+# MAKE_EXPORTS is required for svr4 loaders that want a file of
+# symbol names to tell them what to export/import.
+MAKE_EXPORTS= true
+
+#
+# Random things that must be passed everywhere to enable 
+# everything to compile.  :-/
+#
+# The -qmaxmem is because of optimizer limits.
+# The HAVE_ANSI_CPP flag indicates that cc isn't ANSI but also doesn't
+# have a Reiser (pcc-style) cpp.
+#
+CFLAGS_BE+= -qchars=signed -qmaxmem=4000 -DHAVE_ANSI_CPP
+
+
+#
+# for postgres.user.mk
+#
+EXPSUFF=       .exp
+SLSUFF=                .so
+
+%$(EXPSUFF):  %.o
+       mkldexport $(objdir)/$(<F) `pwd` > $(objdir)/$(@F)
+
+%.so: %.o %$(EXPSUFF)
+       @echo The link stage here:
+       $(LD) -H512 -T512 -o $(objdir)/$(@F) -e _nostart \
+               -bI:$(LIBDIR)/postgres$(EXPSUFF) -bE:$*$(EXPSUFF) \
+               $*.o -lm -lc 2>/dev/null
+
+endif
diff --git a/src/mk/port/postgres.mk.alpha b/src/mk/port/postgres.mk.alpha
new file mode 100644 (file)
index 0000000..1c0e2b6
--- /dev/null
@@ -0,0 +1,52 @@
+#-------------------------------------------------------------------------
+#
+# postgres.mk.alpha--
+#    DEC Alpha AXP/OSF specific rules and variables
+#
+# Copyright (c) 1994-5, Regents of the University of California
+#
+#    $Id$
+#
+#-------------------------------------------------------------------------
+ifndef MK_PORT
+MK_PORT=       alpha
+
+
+#
+# for postgres.mk
+#
+CFLAGS_BE+= -DUSE_POSIX_SIGNALS
+
+# NOFIXADE disallows unaligned access.
+#      on Ultrix and OSF/1 it invokes an explicit syscall.
+#      on HP-UX it turns off certain compiler options.
+# This is defined here because a bunch of clients include tmp/c.h,
+# which is where the work is done on HP-UX.  It only affects the
+# backend on Ultrix and OSF/1.
+ifdef ENFORCE_ALIGNMENT
+CFLAGS_BE+= -DNOFIXADE
+else
+CFLAGS_BE+= -DNOPRINTADE
+endif
+
+# use the regex library
+USE_REGEX = 1
+
+#
+# for postgres.user.mk
+#
+SLSUFF=                .so
+
+# cd into objdir so that so_locations is also in obj
+%.so:  %.o
+       cd $(objdir); $(LD) -shared -expect_unresolved '*' -o $(@F) $(<F)
+
+CLEANFILES+=   so_locations
+
+#
+# for postgres.shell.mk
+#
+DASH_N=
+BACKSLASH_C='\\\\c'
+
+endif
diff --git a/src/mk/port/postgres.mk.bsdi b/src/mk/port/postgres.mk.bsdi
new file mode 100644 (file)
index 0000000..a2aa1df
--- /dev/null
@@ -0,0 +1,40 @@
+#-------------------------------------------------------------------------
+#
+# postgres.mk.bsdi--
+#    Intel x86/BSDi v2.0 specific rules and variables
+#
+#  for questions about the BSD/OS port, contact Kurt Lidl ([email protected])
+# 
+# Copyright (c) 1994-5, Regents of the University of California
+#
+# NOTE
+#    you may remove lines that start with ## which are general comments
+#-------------------------------------------------------------------------
+ifndef MK_PORT
+MK_PORT=       bsdi
+
+# cc is gcc v1.42
+# gcc is gcc v2.6.3
+CC=                    gcc
+
+RANLIB=                ranlib
+INSTALL=       install
+LEX=           flex
+AROPT=         cq
+
+# use the regex library
+USE_REGEX = 1
+
+LDADD_BE= -ldld -lcompat
+
+#
+# for postgres.user.mk
+#
+SLSUFF=                .o
+
+#
+# for postgres.mk
+#
+CFLAGS_OPT=    -g -DUSE_POSIX_SIGNALS # -O2
+
+endif
diff --git a/src/mk/port/postgres.mk.hpux b/src/mk/port/postgres.mk.hpux
new file mode 100644 (file)
index 0000000..ae61e0a
--- /dev/null
@@ -0,0 +1,64 @@
+#-------------------------------------------------------------------------
+#
+# postgres.mk.hpux--
+#    HP PA-RISC/HP-UX specific rules and variables
+#
+# Copyright (c) 1994-5, Regents of the University of California
+#
+#    $Id$
+#
+#-------------------------------------------------------------------------
+ifndef MK_PORT
+MK_PORT=       hpux
+
+#
+# for postgres.mk
+#
+LDADD_BE=      -lBSD
+
+ifdef ENFORCE_ALIGNMENT
+CFLAGS_BE= -DNOFIXADE
+else
+HPUX_VERS:= $(shell uname -r)
+HPUX_MAJOR=${HPUX_VERS:R:E}
+HPUX_MINOR=${HPUX_VERS:E}
+   ifeq ($(HPUX_MAJOR), 08)
+      CFLAGS_BE+= +u -DHP_S500_ALIGN
+      LDFLAGS_BE+= +u
+   else
+   ifeq ($(HPUX_MAJOR), 09)
+      ifeq ($(CC), cc)
+         CFLAGS_BE+= +u4 
+         LDFLAGS_BE+= +u4
+      endif
+   endif
+   endif
+endif
+
+# (extended) ANSI flag for cc (-Ae is same as -Aa -D_HPUX_SOURCE)
+ifeq ($(CC), cc)
+CFLAGS_BE+= -Ae
+endif
+
+# This is a script from the MIT X11 distribution. 
+INSTALL= bsdinst
+
+# RANLIB is not used on HP-UX
+RANLIB=touch
+
+#
+# for postgres.user.mk
+#
+CFLAGS_SL=     +z
+SLSUFF=                .sl
+
+%.sl: %.o
+       $(LD) -b -o $(objdir)/$(@F) $(objdir)/$(<F)
+
+#
+# for postgres.shell.mk
+#
+DASH_N= ''
+BACKSLASH_C='\\\\c'
+
+endif
\ No newline at end of file
diff --git a/src/mk/port/postgres.mk.irix5 b/src/mk/port/postgres.mk.irix5
new file mode 100644 (file)
index 0000000..560c2be
--- /dev/null
@@ -0,0 +1,49 @@
+#-------------------------------------------------------------------------
+#
+# postgres.mk.irix5--
+#    IRIX5 specific rules and variables. This port is contributed by
+#    Paul 'Shag' Walmsley <[email protected]>.
+#
+# Copyright (c) 1994-5, Regents of the University of California
+#
+#    postgres.mk.sparc_solaris,v 1.5 1995/04/30 07:51:21 andrew Exp
+#
+#-------------------------------------------------------------------------
+ifndef MK_PORT
+MK_PORT=       irix5
+
+CC=            cc
+
+#
+# for postgres.mk
+#
+CFLAGS_BE+= -DUSE_POSIX_SIGNALS
+
+# RANLIB is not used on IRIX 5
+RANLIB=touch
+
+INSTALL=/usr/bin/X11/bsdinst
+
+#
+# Random things that must be passed everywhere to enable 
+# everything to compile.  :-/
+#
+CFLAGS_BE+= -DSYSV_DIRENT 
+
+LD_ADD+= $(LDADD_BE)
+
+SLSUFF=                .so
+
+%.so: %.o
+       $(LD) -G -Bdynamic -o $(objdir)/$(@F) $(objdir)/$(<F)
+
+#
+# for postgres.shell.mk
+#
+DASH_N=''
+BACKSLASH_C='\\\\c'
+
+endif
+
+
+
diff --git a/src/mk/port/postgres.mk.linux b/src/mk/port/postgres.mk.linux
new file mode 100644 (file)
index 0000000..73cf23d
--- /dev/null
@@ -0,0 +1,54 @@
+#-------------------------------------------------------------------------
+#
+# postgres.mk.linux--
+#    Intel x86/Linux specific rules and variables
+#
+# Copyright (c) 1994-5, Regents of the University of California
+#
+#    $Id$
+#
+# NOTE
+#    you may remove lines that start with ## which are general comments
+#-------------------------------------------------------------------------
+ifndef MK_PORT
+MK_PORT=       linux
+
+# Comment the following flag if you're not using Linux ELF
+LINUX_ELF = 1
+
+ifndef LINUX_ELF
+RANLIB=                ranlib
+SLSUFF=                .o
+else
+RANLIB=                ranlib
+SLSUFF=                .so
+LDFLAGS+=      -rdynamic
+endif
+INSTALL=       install
+MK_NO_LORDER=  true
+
+# use the regex library
+USE_REGEX = 1
+
+#
+# for postgres.user.mk
+#
+#CFLAGS_SL=    -fPIC
+CFLAGS_SL=     -fpic
+%.so:  %.o
+       cd $(objdir); $(CC) -shared -o $(@F) $(<F)
+
+#
+# for postgres.mk
+#
+CFLAGS_OPT=    -O2 -pipe -m486
+
+# The Linux gnulib #defines the problem away for you and calls 
+# the BSD routines if you give it the right flags.
+CFLAGS_BE= -D__USE_BSD -D__USE_BSD_SIGNAL
+LDADD_BE= -lbsd
+
+LEX = flex
+#YACC = bison -y
+
+endif
diff --git a/src/mk/port/postgres.mk.sparc b/src/mk/port/postgres.mk.sparc
new file mode 100644 (file)
index 0000000..0c6a77b
--- /dev/null
@@ -0,0 +1,38 @@
+#-------------------------------------------------------------------------
+#
+# postgres.mk.sparc--
+#    SUN SPARC/SunOS 4.x specific rules and variables
+#
+# Copyright (c) 1994-5, Regents of the University of California
+#
+#    $Id$
+#
+#-------------------------------------------------------------------------
+ifndef MK_PORT
+MK_PORT=       sparc
+
+# cc won't work!
+CC=            gcc
+
+# the ar on SunOs is dumb, can't use the s option
+AROPT=          cq
+
+INSTALL=       /usr/bin/install
+RANLIB=                /usr/bin/ranlib
+
+#
+# for postgres.user.mk
+#
+ifeq ($(CC), cc)
+CFLAGS_SL=     -PIC
+else
+CFLAGS_SL=     -fPIC
+endif
+
+SLSUFF=                .so
+
+%.so: %.o
+       $(LD) -dc -dp -Bdynamic -o $(objdir)/$(@F) $(objdir)/$(<F)
+
+
+endif
diff --git a/src/mk/port/postgres.mk.sparc_solaris b/src/mk/port/postgres.mk.sparc_solaris
new file mode 100644 (file)
index 0000000..1aa98a3
--- /dev/null
@@ -0,0 +1,57 @@
+#-------------------------------------------------------------------------
+#
+# postgres.mk.sparc_solaris--
+#    SUN SPARC/solaris specific rules and variables
+#
+# Copyright (c) 1994-5, Regents of the University of California
+#
+#    $Id$
+#
+#-------------------------------------------------------------------------
+ifndef MK_PORT
+MK_PORT=       sparc_solaris
+
+# cc won't work!
+CC=            gcc
+
+#
+# for postgres.mk
+#
+CFLAGS_BE+= -DUSE_POSIX_SIGNALS
+
+# RANLIB is not used on solaris
+RANLIB=touch
+
+INSTALL=/usr/ucb/install
+
+#
+# Random things that must be passed everywhere to enable 
+# everything to compile.  :-/
+#
+# The extra -I flag is to scoop up extra BSD-emulating headers.
+CFLAGS_BE+= -DSYSV_DIRENT -I$(POSTGRESDIR)/src/backend/port/sparc_solaris
+LDADD_BE+= -lsocket -lnsl
+
+LD_ADD+= $(LDADD_BE)
+
+#
+# for postgres.user.mk
+#
+ifeq ($(CC), cc)
+CFLAGS_SL=     -K PIC
+else
+CFLAGS_SL=     -fPIC
+endif
+
+SLSUFF=                .so
+
+%.so: %.o
+       $(LD) -G -Bdynamic -o $(objdir)/$(@F) $(objdir)/$(<F)
+
+#
+# for postgres.shell.mk
+#
+DASH_N=''
+BACKSLASH_C='\\\\c'
+
+endif
diff --git a/src/mk/port/postgres.mk.svr4 b/src/mk/port/postgres.mk.svr4
new file mode 100644 (file)
index 0000000..5b288b8
--- /dev/null
@@ -0,0 +1,35 @@
+#-------------------------------------------------------------------------
+#
+# postgres.mk.svr4--
+#    Intel x86/Intel SVR4 specific rules and variables
+#
+# Copyright (c) 1994-5, Regents of the University of California
+#
+#    $Id$
+#
+# NOTE
+#    This file has not been tested.    -ay 3/95
+#
+#-------------------------------------------------------------------------
+ifndef MK_PORT
+MK_PORT=       svr4
+
+#
+# for postgres.mk
+#
+CFLAGS_BE+= -DUSE_POSIX_SIGNALS
+
+# MAKE_EXPORTS is required for svr4 loaders that want a file of
+# symbol names to tell them what to export/import.
+MAKE_EXPORTS= true
+
+#
+# for postgres.user.mk
+#
+CFLAGS_SL=     -K pic
+SLSUFF=                .so
+
+%.so:  %.o
+       $(LD) -G $(LDFLAGS) -o $(objdir)/$(@F) $(objdir)/$(<F)
+
+endif
diff --git a/src/mk/port/postgres.mk.ultrix4 b/src/mk/port/postgres.mk.ultrix4
new file mode 100644 (file)
index 0000000..30eff4e
--- /dev/null
@@ -0,0 +1,34 @@
+#-------------------------------------------------------------------------
+#
+# postgres.mk.ultrix4--
+#    DEC MIPS/Ultrix 4.x specific rules and variables
+#
+# Copyright (c) 1994-5, Regents of the University of California
+#
+#    $Id$
+#
+#-------------------------------------------------------------------------
+ifndef MK_PORT
+MK_PORT=       ultrix4
+
+#
+# for postgres.mk
+#
+ifdef ENFORCE_ALIGNMENT
+CFLAGS_BE= -DNOFIXADE
+endif
+
+# install creates intermediate directories
+NO_BEFOREINSTL=        true
+
+INSTALL=       /usr/bin/install
+RANLIB=                /usr/bin/ranlib
+
+#
+# for postgres.user.mk
+#
+CFLAGS_SL=     -G 0
+SLSUFF=                .o
+
+
+endif
diff --git a/src/mk/postgres.lib.mk b/src/mk/postgres.lib.mk
new file mode 100644 (file)
index 0000000..5003b82
--- /dev/null
@@ -0,0 +1,51 @@
+#-------------------------------------------------------------------------
+#
+# postgres.lib.mk--
+#    rules for building libraries. To use the rules, set the following
+#    variables:
+#      LIBSRCS    - source files for objects to be built in the library
+#      LIB        - name of the library (eg. LIB=pq for libpq.a)
+#    postgres.mk should be included before this file.
+#
+# Copyright (c) 1994-5, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+LIBOBJS:=     $(addsuffix .o, $(basename $(LIBSRCS)))
+#LIBSOBJS:=    $(addsuffix .so, $(basename $(LIBSRCS)))
+lib:=          lib$(LIB).a
+shlib:=                lib$(LIB).so.1
+
+ifndef LINUX_ELF
+$(lib):        $(addprefix $(objdir)/,$(LIBOBJS))
+else
+$(lib):        $(addprefix $(objdir)/,$(LIBOBJS))
+endif
+       @rm -f $(objdir)/$(lib)
+ifdef MK_NO_LORDER
+       cd $(objdir); $(AR) $(AROPT) $(lib) $(LIBOBJS); $(RANLIB) $(lib)
+else
+       cd $(objdir); $(AR) $(AROPT) $(lib) `lorder $(LIBOBJS) | tsort`; $(RANLIB) $(lib)
+endif
+
+$(shlib):      $(addprefix $(objdir)/,$(LIBOBJS))
+       @rm -f $(objdir)/$(shlib)
+       cd $(objdir); $(CC) -shared $(LIBOBJS) -o $(shlib) 
+
+CLEANFILES+= $(LIBOBJS) $(lib) $(shlib)
+
+ifdef LINUX_ELF
+install:: localobj $(lib) $(shlib)
+       $(INSTALL) $(INSTL_LIB_OPTS) $(objdir)/$(lib) $(DESTDIR)$(LIBDIR)/$(lib)
+       $(INSTALL) $(INSTL_LIB_OPTS) $(objdir)/$(shlib) $(DESTDIR)$(LIBDIR)/$(shlib)
+else
+install:: localobj $(lib)
+       $(INSTALL) $(INSTL_LIB_OPTS) $(objdir)/$(lib) $(DESTDIR)$(LIBDIR)/$(lib)
+endif
+#      @cd $(DESTDIR)$(LIBDIR); $(RANLIB) $(lib)
+
+
diff --git a/src/mk/postgres.mk b/src/mk/postgres.mk
new file mode 100644 (file)
index 0000000..0ed0b73
--- /dev/null
@@ -0,0 +1,150 @@
+#-------------------------------------------------------------------------
+#
+# postgres.mk--
+#    The master postgres makefile for implicit rules, definitions and 
+#    variables. Every postgres makefile (except those that include 
+#    postgres.subdir.mk only) should include this file.
+#
+# Copyright (c) 1994-5, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+
+##############################################################################
+#
+# Default first rule (all):
+#    This is here so that people doing "gmake" without arguments will
+#    build the program (PROG), shell script (SHPROG) or library (LIB). To 
+#    override this, you could define a rule before including postgres.mk.
+#    (See .dosomething: for an explanation of its presence.)
+#
+
+ifdef PROG
+all:   localobj $(PROG) .dosomething
+else
+ifdef SHPROG
+all:   localobj $(SHPROG) .dosomething
+else
+ifdef LIB
+#all:  localobj lib$(LIB).a install-headers .dosomething
+all:   localobj lib$(LIB).a
+else
+# if you don't define PROG, SHPROG or LIB before including this, use :: for
+# your all. (this is here so that clean is not the first rule)
+all::  localobj
+endif
+endif
+endif
+
+##############################################################################
+#
+# Flags for programs (ar, yacc, etc.)
+#
+
+YFLAGS=                -d
+RANLIB=                touch
+AROPT=         crs
+#AROPT=                cq
+LINTFLAGS = 
+
+
+#
+# Installation. 
+#
+# This is the default for all platforms. If your platform uses a different
+# BSD-style install program, change it in src/mk/port/postgres.mk.$PORTNAME
+INSTALL=       installbsd
+
+INSTLOPTS=     -c -m 444
+INSTL_EXE_OPTS=        -c -m 555
+INSTL_LIB_OPTS= -c -m 664
+
+##############################################################################
+#
+# Canned command sequences
+#
+
+# making partial objects (if BIGOBJS is defined)
+define make_partial
+       $(LD) -r -o $(objdir)/$(@F) $(addprefix $(objdir)/,$(notdir $^))
+endef
+
+# compiling a .c which is generated (and is in $objdir)
+define cc_inobjdir
+       $(CC) -c $(CFLAGS) $(CPPFLAGS) $(objdir)/$(<F) -o $(objdir)/$(@F)
+endef
+
+
+##############################################################################
+#
+# Variables
+#
+
+# Makefile.global is where the user configurations are. (objdir is defined
+# there)
+include $(MKDIR)/../Makefile.global
+-include $(MKDIR)/port/postgres.mk.$(PORTNAME)
+
+CURDIR:= $(shell pwd)
+
+# This is where we put all the .o's and the generated files.
+VPATH:= $(CURDIR)/$(objdir)
+
+
+##############################################################################
+#
+# General rules
+#
+
+.PHONY: clean .dosomething localobj beforeinstall
+
+# clean up the objects and generated files
+clean:
+       cd $(objdir); rm -f $(CLEANFILES)
+
+# just a matter of personal taste; make sure we do something and don't
+# get this message: "gmake[1]: Nothing to be done for 'all'."
+.dosomething: 
+       @cat /dev/null
+
+localobj:
+       @if test ! -d $(objdir); then mkdir $(objdir); else true; fi;
+
+#
+# create the directories before doing install
+#
+ifndef NO_BEFOREINSTL
+beforeinstall: localobj
+       @-if test ! -d $(DESTDIR)$(LIBDIR); \
+               then mkdir $(DESTDIR)$(LIBDIR); fi
+       @-if test ! -d $(DESTDIR)$(BINDIR); \
+               then mkdir $(DESTDIR)$(BINDIR); fi
+       @-if test ! -d $(DESTDIR)$(DATADIR); \
+               then mkdir $(DESTDIR)$(DATADIR); fi
+       @-if test ! -d $(DESTDIR)$(DATADIR)/files; \
+               then mkdir $(DESTDIR)$(DATADIR)/files; fi
+else
+beforeinstall: localobj
+endif
+
+##############################################################################
+#
+# Implicit rules
+#
+
+# building .o from C++ sources
+$(objdir)/%.o: %.cc
+       $(CXX) $(CXXFLAGS) -c $< -o $@
+
+# building .o from .c (in $objdir):
+$(objdir)/%.o: %.c 
+       $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $(objdir)/$(@F)
+
+# building .o from .s (in $objdir):
+$(objdir)/%.o: %.s
+       $(AS) $(ASFLAGS) $< -o $(objdir)/$(@F)
+
diff --git a/src/mk/postgres.prog.mk b/src/mk/postgres.prog.mk
new file mode 100644 (file)
index 0000000..d7a7f34
--- /dev/null
@@ -0,0 +1,26 @@
+#-------------------------------------------------------------------------
+#
+# postgres.prog.mk--
+#    rules for building binaries. To use the rules, set the following
+#    variables:
+#      PROG       - name of the program (eg. PROG=monitor for monitor)
+#    postgres.mk should be included before this file.
+#
+# Copyright (c) 1994-5, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+PROGOBJS:= $(SRCS:%.c=%.o)
+
+$(PROG):  $(addprefix $(objdir)/,$(PROGOBJS))
+       $(CC) $(CDEBUG) -o $(objdir)/$(@F) $(addprefix $(objdir)/,$(PROGOBJS)) $(LD_ADD)
+
+CLEANFILES+= $(PROGOBJS) $(PROG)
+
+install::      localobj $(PROG)
+       $(INSTALL) $(INSTL_EXE_OPTS) $(objdir)/$(PROG) $(DESTDIR)$(BINDIR)/$(PROG)
+
diff --git a/src/mk/postgres.shell.mk b/src/mk/postgres.shell.mk
new file mode 100644 (file)
index 0000000..3232d5e
--- /dev/null
@@ -0,0 +1,62 @@
+#-------------------------------------------------------------------------
+#
+# postgres.shell.mk--
+#    rules for building shell scripts. To use the rules, set the following
+#    variables:
+#      SRCS    - source for the shell script
+#      SHPROG  - name of the executable
+#    postgres.mk should be included before this file.
+#
+# Copyright (c) 1994-5, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+# NOTES
+#    the shell script you write might include the following strings which
+#    will be turned into the values listed below:
+#
+#      _fUnKy_BINDIR_sTuFf_      -  location of installed binaries
+#      _fUnKy_DATADIR_sTuFf_     -  location of the data directory
+#      _fUnKy_POSTGRESDIR_sTuFf_ -  location of the postgres "home" directory
+#      _fUnKy_IPCCLEANPATH_sTuFf_ - location of the ipcs and ipcrm programs
+#      _fUnKy_DASH_N_sTuFf_      -  -n flag used in echo
+#      _fUnKy_BACKSLASH_C_sTuFf_ -  continuation (echo)
+#-------------------------------------------------------------------------
+
+#
+# And all the shell scripts here get stuffed with the default
+# values for BINDIR, DATADIR, and POSTGRESDIR
+#
+SEDSCRIPT= -e "s^_fUnKy_BINDIR_sTuFf_^$(BINDIR)^g" \
+       -e "s^_fUnKy_DATADIR_sTuFf_^$(DATADIR)^g" \
+       -e "s^_fUnKy_IPCCLEANPATH_sTuFf_^$(IPCSDIR)^g" \
+       -e "s^_fUnKy_POSTGRESDIR_sTuFf_^$(POSTGRESDIR)^g"
+
+#
+# We also need to fix up the scripts to deal with the lack of installed
+# 'echo' commands that accept the -n option.
+#
+ifndef DASH_N
+DASH_N=-n
+endif
+ifndef BACKSLASH_C
+BACKSLASH_C=
+endif
+
+SEDSCRIPT+= -e "s^_fUnKy_DASH_N_sTuFf_^$(DASH_N)^g" \
+       -e "s^_fUnKy_BACKSLASH_C_sTuFf_^$(BACKSLASH_C)^g"
+
+
+OBJS:= $(SRCS:%.c=%.o)
+
+$(SHPROG):  $(SHPROG).sh
+       sed $(SEDSCRIPT) < $< > $(objdir)/$(SHPROG)
+
+CLEANFILES+= $(SHPROG)
+
+install:       localobj $(SHPROG)
+       $(INSTALL) $(INSTL_EXE_OPTS) $(objdir)/$(SHPROG) $(DESTDIR)$(BINDIR)/$(SHPROG)
+
+
diff --git a/src/mk/postgres.subdir.mk b/src/mk/postgres.subdir.mk
new file mode 100644 (file)
index 0000000..bc0b85e
--- /dev/null
@@ -0,0 +1,21 @@
+#-------------------------------------------------------------------------
+#
+# postgres.lib.mk--
+#    include this to do recursive make on the subdirectories specified in
+#    SUBDIR.
+#
+# Copyright (c) 1994-5, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+.PHONY: all
+
+.DEFAULT all:
+       @for dir in $(SUBDIR); do \
+               echo "===> $$dir"; \
+               $(MAKE) -C $$dir --no-print-directory $@; \
+       done
diff --git a/src/mk/postgres.user.mk b/src/mk/postgres.user.mk
new file mode 100644 (file)
index 0000000..1350793
--- /dev/null
@@ -0,0 +1,79 @@
+#-------------------------------------------------------------------------
+#
+# postgres.user.mk--
+#    rules for building object/shared libraries used in dynamic loading. 
+#    To use the rules, set the following variables:
+#      DLOBJS     - objects to be linked in dynamically
+#    This makefile adds the files you need to build to CREATEFILES.
+#
+#    For building user modules (user functions to be loaded in dynamically).
+#    Make sure the following variables are set properly (You can either
+#    define them manually or include postgres.mk which defines them.):
+#      MKDIR       - where postgres makefiles are
+#      includedir  - where header files are installed
+#      PORTNAME    - your platform (alpha, sparc, sparc_solaris, etc.)
+#      objdir      - where to put the generated files
+#
+#    An SQL script foo.sql or a shell script foo.sh generated from foo.source.
+#    Occurrence of the following strings will be replaced with the respective
+#    values. This is a feeble attempt to provide "portable" scripts.
+#      _CWD_       - current working directory
+#      _OBJWD_     - where the generated files (eg. object files) are
+#      _SLSUFF_    - suffix of the shared library or object for
+#                    dynamic loading
+#      _USER_      - the login of the user
+#
+# Copyright (c) 1994-5, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+-include $(MKDIR)/port/postgres.mk.$(PORTNAME)
+CFLAGS+=  -I$(includedir) $(CFLAGS_SL)
+
+%.sql: %.source
+       if [ -z "$$USER" ]; then USER=$$LOGNAME; fi; \
+       if [ -z "$$USER" ]; then USER=`whoami`; fi; \
+       if [ -z "$$USER" ]; then echo 'Cannot deduce $$USER.'; exit 1; fi; \
+       rm -f $(objdir)/$*.sql; \
+       C=`pwd`; \
+       sed -e "s:_CWD_:$$C:g" \
+           -e "s:_OBJWD_:$$C/$(objdir):g" \
+           -e "s:_SLSUFF_:$(SLSUFF):g" \
+           -e "s/_USER_/$$USER/g" < $*.source > $(objdir)/$*.sql
+
+#How to create a dynamic lib
+%.so.1:        %.so
+       @rm -f $(objdir)/$(@F)
+       $(CC) -shared $< -o $(objdir)/$(@F)
+
+%.sh: %.source
+       if [ -z "$$USER" ]; then USER=$$LOGNAME; fi; \
+       if [ -z "$$USER" ]; then USER=`whoami`; fi; \
+       if [ -z "$$USER" ]; then echo 'Cannot deduce $USER.'; exit 1; fi; \
+       rm -f $(objdir)/$*.sh; \
+       C="`pwd`/"; \
+       sed -e "s:_CWD_:$$C:g" \
+           -e "s:_OBJWD_:$$C/$(objdir):g" \
+           -e "s:_SLSUFF_:$(SLSUFF):g" \
+           -e "s/_USER_/$$USER/g" < $*.source > $(objdir)/$*.sh
+
+#
+# plus exports files
+#
+ifdef EXPSUFF
+CREATEFILES+= $(DLOBJS:.o=$(EXPSUFF))
+endif
+
+#
+# plus shared libraries
+#
+ifdef SLSUFF
+ifneq ($(SLSUFF), '.o')
+CREATEFILES+= $(DLOBJS:.so=$(SLSUFF))
+endif
+endif
+
diff --git a/src/test/Makefile b/src/test/Makefile
new file mode 100644 (file)
index 0000000..2befc12
--- /dev/null
@@ -0,0 +1,18 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for test suites
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SUBDIR= bench regress
+
+include ../mk/postgres.subdir.mk
+
+
diff --git a/src/test/bench/Makefile b/src/test/bench/Makefile
new file mode 100644 (file)
index 0000000..202360d
--- /dev/null
@@ -0,0 +1,62 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for the Wisconsin Benchmark
+#
+# Copyright (c) 1994-5, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+
+
+CREATEFILES= create.sql bench.sql
+
+include $(MKDIR)/postgres.user.mk
+
+
+OUTFILES= bench.out bench.out.perquery
+CLEANFILES+= $(CREATEFILES) $(OUTFILES)
+
+bench.sql: 
+       cat > $(objdir)/$@ < /dev/null
+       x=1; \
+       for i in `ls query[0-9][0-9]`; do \
+               echo "select $$x as x" >> $(objdir)/$@; \
+               cat $$i >> $(objdir)/$@; \
+               x=`expr $$x + 1`; \
+       done
+
+bench2.pq: 
+       cat > ${.TARGET} < /dev/null
+       C=`pwd`; cd ${.CURDIR}; \
+       for i in 1 2 3 4 5 6; do \
+               echo "select timeofday();" >> $$C/${.TARGET}; \
+       done; \
+       x=1; \
+       for i in `ls query[0-9][0-9]`; do \
+               echo "select $$x as x;" >> $$C/${.TARGET}; \
+               echo "select timeofday();" >> $$C/${.TARGET}; \
+               cat $$i >> $$C/${.TARGET}; \
+               echo "select timeofday();" >> $$C/${.TARGET}; \
+               x=`expr $$x + 1`; \
+       done
+
+bench.out: $(CREATEFILES)
+       $(SHELL) ./create.sh && \
+       $(SHELL) ./runwisc.sh > $(objdir)/$@ 2>&1
+       @echo "RESULTS OF BENCHMARK ARE SAVED IN ${MAKEOBJDIR}/bench.out";
+
+bench.out.perquery: bench.out
+       $(SHELL) ./perquery < $(objdir)/bench.out 2>&1 > $@
+       @echo "BREAKDOWN OF BENCHMARK IS SAVED IN ${MAKEOBJDIR}/bench.out.perquery";
+
+all:: $(CREATEFILES)
+       rm -f $(OUTFILES)
+
+runtest: ${OUTFILES}
diff --git a/src/test/bench/WISC-README b/src/test/bench/WISC-README
new file mode 100644 (file)
index 0000000..7142802
--- /dev/null
@@ -0,0 +1,28 @@
+The Postgres Wisconsin Benchmark
+
+In this directory are the queries and raw data files used to populate the
+Postgres version of the Wisconsin benchmark.  In order to run the benchmark,
+you'll initially need to execute the script
+
+./create.sh
+
+which will populate the "bench" database, create the indices, and vacuum the
+database.  This will take from 10 minutes or so on a Sparc II/DECstation 5000
+class machine to an hour on a Sun 3.
+
+Once create.sh completes, you can execute the benchmark by running the
+script
+
+./runwisc.sh
+
+into an output file.  This output file may be quite large (300K or so)
+so make sure you have sufficient disk space.  Once the benchmark run has
+completed, query execution times can be obtained by running the 
+
+./perquery
+
+script on the output file.  It will generate a nicely formatted, numbered
+set of output with times for each query indicated.  (Note that each query
+is run twice.)
+
+  !!! WARNING!  DO NOT RUN THESE SCRIPTS IF THE POSTMASTER IS RUNNING !!!
diff --git a/src/test/bench/create.sh b/src/test/bench/create.sh
new file mode 100755 (executable)
index 0000000..3604faa
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+# $Header$
+# 
+if [ -d ./obj ]; then
+       cd ./obj
+fi
+
+echo =============== destroying old bench database... =================
+echo "drop database bench" | postgres template1 > /dev/null
+
+echo =============== creating new bench database... =================
+echo "create database bench" | postgres template1 > /dev/null
+if [ $? -ne 0 ]; then
+       echo createdb failed
+       exit 1
+fi
+
+postgres -Q bench < create.sql > /dev/null
+if [ $? -ne 0 ]; then
+       echo initial database load failed
+       exit 1
+fi
+
+exit 0
diff --git a/src/test/bench/create.source b/src/test/bench/create.source
new file mode 100644 (file)
index 0000000..986ff3c
--- /dev/null
@@ -0,0 +1,17 @@
+create table onek(unique1 int4,unique2 int4,two int4,four int4,ten int4,twenty int4, hundred int4,thousand int4,twothousand int4,fivethous int4,tenthous int4,odd int4, even int4,stringu1 char16,stringu2 char16,string4 char16);
+create table tenk1 (unique1 int4,unique2 int4, two int4,four int4,ten int4,twenty int4,hundred int4,thousand int4,twothousand int4,fivethous int4,tenthous int4,odd int4,even int4,stringu1 char16,stringu2 char16,string4 char16);
+create table tenk2 (unique1 int4, unique2 int4, two int4, four int4,ten int4, twenty int4, hundred int4, thousand int4, twothousand int4,fivethous int4, tenthous int4, odd int4, even int4,stringu1 char16,stringu2 char16, string4 char16);
+copy onek from '_CWD_/../regress/data/onek.data';
+copy tenk1 from '_CWD_/../regress/data/tenk.data';
+copy tenk2 from '_CWD_/../regress/data/tenk.data';
+create index onek_unique1 on onek using btree(unique1 int4_ops);
+create index onek_unique2 on onek using btree(unique2 int4_ops);
+create index onek_hundred on onek using btree(hundred int4_ops);
+create index tenk1_unique1 on tenk1 using btree(unique1 int4_ops);
+create index tenk1_unique2 on tenk1 using btree(unique2 int4_ops);
+create index tenk1_hundred on tenk1 using btree(hundred int4_ops);
+create index tenk2_unique1 on tenk2 using btree(unique1 int4_ops);
+create index tenk2_unique2 on tenk2 using btree(unique2 int4_ops);
+create index tenk2_hundred on tenk2 using btree(hundred int4_ops);
+select * into table Bprime from tenk1 t where t.unique2 < 1000;
+vacuum;
diff --git a/src/test/bench/perquery b/src/test/bench/perquery
new file mode 100644 (file)
index 0000000..4f0ba03
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+egrep 'x = "|elapse' > /tmp/foo$$
+
+awk 'BEGIN { x = 0; y = 0; z = 0; a = 0; } \
+     /.*elapse.*/ {x = $2 + x; y = $4 + y; z = $6 + z;} \
+     /.*x = ".*/ { \
+        printf "query %2d: %7.3f real %7.3f user %7.3f sys\n", a, x, y, z; \
+        x = 0; y = 0; z = 0; a = a + 1; } \
+     END {printf("query %2d: %7.3f real %7.3f user %7.3f sys\n", a, x, y, z);}' \
+        < /tmp/foo$$
+        
diff --git a/src/test/bench/query01 b/src/test/bench/query01
new file mode 100644 (file)
index 0000000..0ed3a7b
--- /dev/null
@@ -0,0 +1,4 @@
+select * into table temp from tenk1 where (unique2 > 301) and (unique2 < 402);
+drop table temp;
+select * into table temp from tenk1 where (unique2 > 301) and (unique2 < 402);
+drop table temp;
diff --git a/src/test/bench/query02 b/src/test/bench/query02
new file mode 100644 (file)
index 0000000..2d253ac
--- /dev/null
@@ -0,0 +1,4 @@
+select * into table temp from tenk1 where (unique1 > 647) and (unique1 < 1648);
+drop table temp;
+select * into table temp from tenk1 where (unique1 > 647) and (unique1 < 1648);
+drop table temp;
diff --git a/src/test/bench/query03 b/src/test/bench/query03
new file mode 100644 (file)
index 0000000..0ed3a7b
--- /dev/null
@@ -0,0 +1,4 @@
+select * into table temp from tenk1 where (unique2 > 301) and (unique2 < 402);
+drop table temp;
+select * into table temp from tenk1 where (unique2 > 301) and (unique2 < 402);
+drop table temp;
diff --git a/src/test/bench/query04 b/src/test/bench/query04
new file mode 100644 (file)
index 0000000..2d253ac
--- /dev/null
@@ -0,0 +1,4 @@
+select * into table temp from tenk1 where (unique1 > 647) and (unique1 < 1648);
+drop table temp;
+select * into table temp from tenk1 where (unique1 > 647) and (unique1 < 1648);
+drop table temp;
diff --git a/src/test/bench/query05 b/src/test/bench/query05
new file mode 100644 (file)
index 0000000..0ed3a7b
--- /dev/null
@@ -0,0 +1,4 @@
+select * into table temp from tenk1 where (unique2 > 301) and (unique2 < 402);
+drop table temp;
+select * into table temp from tenk1 where (unique2 > 301) and (unique2 < 402);
+drop table temp;
diff --git a/src/test/bench/query06 b/src/test/bench/query06
new file mode 100644 (file)
index 0000000..209c132
--- /dev/null
@@ -0,0 +1,4 @@
+select * into table temp from tenk1 where (unique2 > 647) and (unique2 < 1648);
+drop table temp;
+select * into table temp from tenk1 where (unique2 > 647) and (unique2 < 1648);
+drop table temp;
diff --git a/src/test/bench/query07 b/src/test/bench/query07
new file mode 100644 (file)
index 0000000..ae8fccb
--- /dev/null
@@ -0,0 +1,2 @@
+select * from tenk1 where unique2 = 2001;
+select * from tenk1 where unique2 = 2001;
diff --git a/src/test/bench/query08 b/src/test/bench/query08
new file mode 100644 (file)
index 0000000..5310648
--- /dev/null
@@ -0,0 +1,2 @@
+select * from tenk1 where (unique2 > 301) and (unique2 < 402);
+select * from tenk1 where (unique2 > 301) and (unique2 < 402);
diff --git a/src/test/bench/query09 b/src/test/bench/query09
new file mode 100644 (file)
index 0000000..66d33ca
--- /dev/null
@@ -0,0 +1,4 @@
+select t1.*, t2.unique1 AS t2unique1, t2.unique2 AS t2unique2, t2.two AS t2two, t2.four AS t2four, t2.ten AS t2ten, t2.twenty AS t2twenty, t2.hundred AS t2hundred, t2.thousand AS t2thousand, t2.twothousand AS t2twothousand, t2.fivethous AS t2fivethous, t2.tenthous AS t2tenthous, t2.odd AS t2odd, t2.even AS t2even, t2.stringu1 AS t2stringu1, t2.stringu2 AS t2stringu2, t2.string4 AS t2string4 into table temp from tenk1 t1, tenk1 t2 where (t1.unique2 = t2.unique2) and (t2.unique2 < 1000);
+drop table temp;
+select t1.*, t2.unique1 AS t2unique1, t2.unique2 AS t2unique2, t2.two AS t2two, t2.four AS t2four, t2.ten AS t2ten, t2.twenty AS t2twenty, t2.hundred AS t2hundred, t2.thousand AS t2thousand, t2.twothousand AS t2twothousand, t2.fivethous AS t2fivethous, t2.tenthous AS t2tenthous, t2.odd AS t2odd, t2.even AS t2even, t2.stringu1 AS t2stringu1, t2.stringu2 AS t2stringu2, t2.string4 AS t2string4 into table temp from tenk1 t1, tenk1 t2 where (t1.unique2 = t2.unique2) and (t2.unique2 < 1000);
+drop table temp;
diff --git a/src/test/bench/query10 b/src/test/bench/query10
new file mode 100644 (file)
index 0000000..de372aa
--- /dev/null
@@ -0,0 +1,4 @@
+select t.*,B.unique1 AS Bunique1,B.unique2 AS Bunique2,B.two AS Btwo,B.four AS Bfour,B.ten AS Bten,B.twenty AS Btwenty,B.hundred AS Bhundred,B.thousand AS Bthousand,B.twothousand AS Btwothousand,B.fivethous AS Bfivethous,B.tenthous AS Btenthous,B.odd AS Bodd,B.even AS Beven,B.stringu1 AS Bstringu1,B.stringu2 AS Bstringu2,B.string4 AS Bstring4 into table temp from tenk1 t, Bprime B where t.unique2 = B.unique2;
+drop table temp;
+select t.*,B.unique1 AS Bunique1,B.unique2 AS Bunique2,B.two AS Btwo,B.four AS Bfour,B.ten AS Bten,B.twenty AS Btwenty,B.hundred AS Bhundred,B.thousand AS Bthousand,B.twothousand AS Btwothousand,B.fivethous AS Bfivethous,B.tenthous AS Btenthous,B.odd AS Bodd,B.even AS Beven,B.stringu1 AS Bstringu1,B.stringu2 AS Bstringu2,B.string4 AS Bstring4 into table temp from tenk1 t, Bprime B where t.unique2 = B.unique2;
+drop table temp;
diff --git a/src/test/bench/query11 b/src/test/bench/query11
new file mode 100644 (file)
index 0000000..abbce82
--- /dev/null
@@ -0,0 +1,4 @@
+select t1.*,o.unique1 AS ounique1,o.unique2 AS ounique2,o.two AS otwo,o.four AS ofour,o.ten AS oten,o.twenty AS otwenty,o.hundred AS ohundred,o.thousand AS othousand,o.twothousand AS otwothousand,o.fivethous AS ofivethous,o.tenthous AS otenthous,o.odd AS oodd, o.even AS oeven,o.stringu1 AS ostringu1,o.stringu2 AS ostringu2,o.string4 AS ostring4 into table temp from onek o, tenk1 t1, tenk1 t2 where (o.unique2 = t1.unique2) and (t1.unique2 = t2.unique2) and (t1.unique2 < 1000) and (t2.unique2 < 1000);
+drop table temp;
+select t1.*,o.unique1 AS ounique1,o.unique2 AS ounique2,o.two AS otwo,o.four AS ofour,o.ten AS oten,o.twenty AS otwenty,o.hundred AS ohundred,o.thousand AS othousand,o.twothousand AS otwothousand,o.fivethous AS ofivethous,o.tenthous AS otenthous,o.odd AS oodd, o.even AS oeven,o.stringu1 AS ostringu1,o.stringu2 AS ostringu2,o.string4 AS ostring4 into table temp from onek o, tenk1 t1, tenk1 t2 where (o.unique2 = t1.unique2) and (t1.unique2 = t2.unique2) and (t1.unique2 < 1000) and (t2.unique2 < 1000);
+drop table temp;
diff --git a/src/test/bench/query12 b/src/test/bench/query12
new file mode 100644 (file)
index 0000000..d1b4611
--- /dev/null
@@ -0,0 +1,4 @@
+select t1.*,t2.unique1 AS t2unique1,t2.unique2 AS t2unique2,t2.two AS t2two, t2.four AS t2four,t2.ten AS t2ten,t2.twenty AS t2twenty,t2.hundred AS t2hundred,t2.thousand AS t2thousand,t2.twothousand AS t2twothousand, t2.fivethous AS t2fivethous,t2.tenthous AS t2tenthous,t2.odd AS t2odd, t2.even AS t2even,t2.stringu1 AS t2stringu1,t2.stringu2 AS t2stringu2, t2.string4 AS t2string4 into table temp from tenk1 t1, tenk2 t2 where (t1.unique2 = t2.unique2) and (t2.unique2 < 1000);
+drop table temp;
+select t1.*,t2.unique1 AS t2unique1,t2.unique2 AS t2unique2,t2.two AS t2two, t2.four AS t2four,t2.ten AS t2ten,t2.twenty AS t2twenty,t2.hundred AS t2hundred,t2.thousand AS t2thousand,t2.twothousand AS t2twothousand, t2.fivethous AS t2fivethous,t2.tenthous AS t2tenthous,t2.odd AS t2odd, t2.even AS t2even,t2.stringu1 AS t2stringu1,t2.stringu2 AS t2stringu2, t2.string4 AS t2string4 into table temp from tenk1 t1, tenk2 t2 where (t1.unique2 = t2.unique2) and (t2.unique2 < 1000);
+drop table temp;
diff --git a/src/test/bench/query13 b/src/test/bench/query13
new file mode 100644 (file)
index 0000000..de372aa
--- /dev/null
@@ -0,0 +1,4 @@
+select t.*,B.unique1 AS Bunique1,B.unique2 AS Bunique2,B.two AS Btwo,B.four AS Bfour,B.ten AS Bten,B.twenty AS Btwenty,B.hundred AS Bhundred,B.thousand AS Bthousand,B.twothousand AS Btwothousand,B.fivethous AS Bfivethous,B.tenthous AS Btenthous,B.odd AS Bodd,B.even AS Beven,B.stringu1 AS Bstringu1,B.stringu2 AS Bstringu2,B.string4 AS Bstring4 into table temp from tenk1 t, Bprime B where t.unique2 = B.unique2;
+drop table temp;
+select t.*,B.unique1 AS Bunique1,B.unique2 AS Bunique2,B.two AS Btwo,B.four AS Bfour,B.ten AS Bten,B.twenty AS Btwenty,B.hundred AS Bhundred,B.thousand AS Bthousand,B.twothousand AS Btwothousand,B.fivethous AS Bfivethous,B.tenthous AS Btenthous,B.odd AS Bodd,B.even AS Beven,B.stringu1 AS Bstringu1,B.stringu2 AS Bstringu2,B.string4 AS Bstring4 into table temp from tenk1 t, Bprime B where t.unique2 = B.unique2;
+drop table temp;
diff --git a/src/test/bench/query14 b/src/test/bench/query14
new file mode 100644 (file)
index 0000000..abbce82
--- /dev/null
@@ -0,0 +1,4 @@
+select t1.*,o.unique1 AS ounique1,o.unique2 AS ounique2,o.two AS otwo,o.four AS ofour,o.ten AS oten,o.twenty AS otwenty,o.hundred AS ohundred,o.thousand AS othousand,o.twothousand AS otwothousand,o.fivethous AS ofivethous,o.tenthous AS otenthous,o.odd AS oodd, o.even AS oeven,o.stringu1 AS ostringu1,o.stringu2 AS ostringu2,o.string4 AS ostring4 into table temp from onek o, tenk1 t1, tenk1 t2 where (o.unique2 = t1.unique2) and (t1.unique2 = t2.unique2) and (t1.unique2 < 1000) and (t2.unique2 < 1000);
+drop table temp;
+select t1.*,o.unique1 AS ounique1,o.unique2 AS ounique2,o.two AS otwo,o.four AS ofour,o.ten AS oten,o.twenty AS otwenty,o.hundred AS ohundred,o.thousand AS othousand,o.twothousand AS otwothousand,o.fivethous AS ofivethous,o.tenthous AS otenthous,o.odd AS oodd, o.even AS oeven,o.stringu1 AS ostringu1,o.stringu2 AS ostringu2,o.string4 AS ostring4 into table temp from onek o, tenk1 t1, tenk1 t2 where (o.unique2 = t1.unique2) and (t1.unique2 = t2.unique2) and (t1.unique2 < 1000) and (t2.unique2 < 1000);
+drop table temp;
diff --git a/src/test/bench/query15 b/src/test/bench/query15
new file mode 100644 (file)
index 0000000..be5fb5b
--- /dev/null
@@ -0,0 +1,4 @@
+select t1.*, t2.unique1 AS t2unique1, t2.unique2 AS t2unique2, t2.two AS t2two, t2.four AS t2four, t2.ten AS t2ten, t2.twenty AS t2twenty, t2.hundred AS t2hundred, t2.thousand AS t2thousand,t2.twothousand AS t2twothousand, t2.fivethous AS t2fivethous, t2.tenthous AS t2tenthous, t2.odd AS t2odd, t2.even AS t2even, t2.stringu1 AS t2stringu1, t2.stringu2 AS t2stringu2, t2.string4 AS t2string4 into table temp from tenk1 t1, tenk2 t2 where (t1.unique1 = t2.unique1) and (t2.unique1 < 1000);
+drop table temp;
+select t1.*, t2.unique1 AS t2unique1, t2.unique2 AS t2unique2, t2.two AS t2two, t2.four AS t2four, t2.ten AS t2ten, t2.twenty AS t2twenty, t2.hundred AS t2hundred, t2.thousand AS t2thousand,t2.twothousand AS t2twothousand, t2.fivethous AS t2fivethous, t2.tenthous AS t2tenthous, t2.odd AS t2odd, t2.even AS t2even, t2.stringu1 AS t2stringu1, t2.stringu2 AS t2stringu2, t2.string4 AS t2string4 into table temp from tenk1 t1, tenk2 t2 where (t1.unique1 = t2.unique1) and (t2.unique1 < 1000);
+drop table temp;
diff --git a/src/test/bench/query16 b/src/test/bench/query16
new file mode 100644 (file)
index 0000000..6e36649
--- /dev/null
@@ -0,0 +1,4 @@
+select t.*, B.unique1 AS Bunique1,B.unique2 AS Bunique2,B.two AS Btwo,B.four AS Bfour,B.ten AS Bten, B.twenty AS Btwenty, B.hundred AS Bhundred,B.thousand AS Bthousand,B.twothousand AS Btwothousand, B.fivethous AS Bfivethous,B.tenthous AS Btenthous,B.odd AS Bodd, B.even AS Beven,B.stringu1 AS Bstringu1,B.stringu2 AS Bstringu2,B.string4 AS Bstring4 into table temp  from tenk1 t, Bprime B where t.unique1 = B.unique1;
+drop table temp;
+select t.*, B.unique1 AS Bunique1,B.unique2 AS Bunique2,B.two AS Btwo,B.four AS Bfour,B.ten AS Bten, B.twenty AS Btwenty, B.hundred AS Bhundred,B.thousand AS Bthousand,B.twothousand AS Btwothousand, B.fivethous AS Bfivethous,B.tenthous AS Btenthous,B.odd AS Bodd, B.even AS Beven,B.stringu1 AS Bstringu1,B.stringu2 AS Bstringu2,B.string4 AS Bstring4 into table temp  from tenk1 t, Bprime B where t.unique1 = B.unique1;
+drop table temp;
diff --git a/src/test/bench/query17 b/src/test/bench/query17
new file mode 100644 (file)
index 0000000..e3b501b
--- /dev/null
@@ -0,0 +1,4 @@
+select t1.*, o.unique1 AS ounique1,o.unique2 AS ounique2,o.two AS otwo,o.four AS ofour,o.ten AS oten,o.twenty AS otwenty,o.hundred AS  ohundred,o.thousand AS othousand,o.twothousand AS otwothousand,o.fivethous AS ofivethous,o.tenthous AS otenthous,o.odd AS oodd, o.even AS oeven,o.stringu1 AS ostringu1,o.stringu2 AS ostringu2,o.string4 AS ostring4 into table temp from onek o, tenk1 t1, tenk2 t2 where (o.unique1 = t1.unique1) and (t1.unique1 = t2.unique1) and (t1.unique1 < 1000) and (t2.unique1 < 1000);
+drop table temp;
+select t1.*, o.unique1 AS ounique1,o.unique2 AS ounique2,o.two AS otwo,o.four AS ofour,o.ten AS oten,o.twenty AS otwenty,o.hundred AS  ohundred,o.thousand AS othousand,o.twothousand AS otwothousand,o.fivethous AS ofivethous,o.tenthous AS otenthous,o.odd AS oodd, o.even AS oeven,o.stringu1 AS ostringu1,o.stringu2 AS ostringu2,o.string4 AS ostring4 into table temp from onek o, tenk1 t1, tenk2 t2 where (o.unique1 = t1.unique1) and (t1.unique1 = t2.unique1) and (t1.unique1 < 1000) and (t2.unique1 < 1000);
+drop table temp;
diff --git a/src/test/bench/query18 b/src/test/bench/query18
new file mode 100644 (file)
index 0000000..92fe5f7
--- /dev/null
@@ -0,0 +1,4 @@
+select two, four, ten, twenty, hundred, string4 into table temp from tenk1;
+drop table temp;
+select two, four, ten, twenty, hundred, string4 into table temp from tenk1;
+drop table temp;
diff --git a/src/test/bench/query19 b/src/test/bench/query19
new file mode 100644 (file)
index 0000000..1ed160d
--- /dev/null
@@ -0,0 +1,4 @@
+select * into table temp from onek;
+drop table temp;
+select * into table temp from onek;
+drop table temp;
diff --git a/src/test/bench/query20 b/src/test/bench/query20
new file mode 100644 (file)
index 0000000..e544ea6
--- /dev/null
@@ -0,0 +1,4 @@
+select int4min(unique2) as x into table temp from tenk1;
+drop table temp;
+select int4min(unique2) as x into table temp from tenk1;
+drop table temp;
diff --git a/src/test/bench/query21 b/src/test/bench/query21
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/test/bench/query22 b/src/test/bench/query22
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/test/bench/query23 b/src/test/bench/query23
new file mode 100644 (file)
index 0000000..e544ea6
--- /dev/null
@@ -0,0 +1,4 @@
+select int4min(unique2) as x into table temp from tenk1;
+drop table temp;
+select int4min(unique2) as x into table temp from tenk1;
+drop table temp;
diff --git a/src/test/bench/query24 b/src/test/bench/query24
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/test/bench/query25 b/src/test/bench/query25
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/test/bench/query26 b/src/test/bench/query26
new file mode 100644 (file)
index 0000000..715c4c3
--- /dev/null
@@ -0,0 +1,2 @@
+insert into tenk1 (unique1, unique2, two, four, ten, twenty, hundred, thousand, twothousand, fivethous, tenthous, odd, even,stringu1,stringu2, string4) values (1000, 74, 0, 2, 0, 10, 50, 688, 1950, 4950, 9950, 1, 100, "ron may choi","jae kwang choi", "u. c. berkeley");
+insert into tenk1 (unique1, unique2, two, four, ten, twenty, hundred, thousand, twothousand, fivethous, tenthous, odd, even, stringu1, stringu2, string4) values (1999, 60, 0, 2, 0, 10, 50, 688, 1950, 4950, 9950, 1, 100, "ron may choi", "jae kwang choi", "u. c. berkeley");
diff --git a/src/test/bench/query27 b/src/test/bench/query27
new file mode 100644 (file)
index 0000000..c837735
--- /dev/null
@@ -0,0 +1,2 @@
+delete from tenk1 where tenk1.unique2 = 877;
+delete from tenk1 where tenk1.unique2 = 876;
diff --git a/src/test/bench/query28 b/src/test/bench/query28
new file mode 100644 (file)
index 0000000..c3d01c9
--- /dev/null
@@ -0,0 +1,2 @@
+update tenk1 set unique2 = 10001 where tenk1.unique2 =1491;
+update tenk1 set unique2 = 10023 where tenk1.unique2 =1480;
diff --git a/src/test/bench/query29 b/src/test/bench/query29
new file mode 100644 (file)
index 0000000..3600685
--- /dev/null
@@ -0,0 +1,2 @@
+insert into tenk1 (unique1, unique2, two, four, ten, twenty, hundred, thousand, twothousand, fivethous, tenthous, odd, even, stringu1, stringu2, string4) values (1000, 70, 0, 2, 0, 10, 50, 688, 1950, 4950, 9950, 1, 100, "ron may choi", "jae kwang choi", "u. c. berkeley");
+insert into tenk1 (unique1, unique2, two, four, ten, twenty, hundred, thousand, twothousand, fivethous, tenthous, odd, even, stringu1, stringu2, string4) values (500, 40, 0, 2, 0, 10, 50, 688, 1950, 4950, 9950, 1, 100, "ron may choi", "jae kwang choi", "u. c. berkeley");
diff --git a/src/test/bench/query30 b/src/test/bench/query30
new file mode 100644 (file)
index 0000000..e8b37e6
--- /dev/null
@@ -0,0 +1,2 @@
+delete from tenk1 where tenk1.unique2 = 10001;
+delete from tenk1 where tenk1.unique2 = 900;
diff --git a/src/test/bench/query31 b/src/test/bench/query31
new file mode 100644 (file)
index 0000000..cbdb85d
--- /dev/null
@@ -0,0 +1,2 @@
+update tenk1 set unique2 = 10088 where tenk1.unique2 =187;
+update tenk1 set unique2 = 10003 where tenk1.unique2 =2000;
diff --git a/src/test/bench/query32 b/src/test/bench/query32
new file mode 100644 (file)
index 0000000..3b4159c
--- /dev/null
@@ -0,0 +1,2 @@
+update tenk1 set unique2 = 10020 where tenk1.unique2 =1974;
+update tenk1 set unique2 = 160 where tenk1.unique2 =1140;
diff --git a/src/test/bench/runwisc.sh b/src/test/bench/runwisc.sh
new file mode 100755 (executable)
index 0000000..1edb80f
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+# $Header$
+# 
+# Note that in our published benchmark numbers, we executed the command in the
+# following fashion:
+#
+# time $POSTGRES -texecutor -tplanner -f hashjoin -Q bench
+#
+if [ -d ./obj ]; then
+       cd ./obj
+fi
+
+echo =============== vacuuming benchmark database... =================
+echo "vacuum" | postgres -Q bench > /dev/null
+
+echo =============== running benchmark... =================
+time postgres -texecutor -tplanner -Q bench < bench.sql
diff --git a/src/test/bench/wholebench.sh b/src/test/bench/wholebench.sh
new file mode 100755 (executable)
index 0000000..b684449
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+sh create.sh
+echo Running the benchmark....
+sh runwisc.sh
diff --git a/src/test/examples/Makefile b/src/test/examples/Makefile
new file mode 100644 (file)
index 0000000..fb79062
--- /dev/null
@@ -0,0 +1,74 @@
+#
+# Makefile for example programs
+#
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+
+CFLAGS+= -I$(HEADERDIR) -I$(srcdir)/backend -I$(srcdir)/backend/include
+
+LIBPQ:=  -L$(LIBDIR) -lpq
+
+LD_ADD+=$(LIBPQ)
+
+#
+# And where libpq goes, so goes the authentication stuff...
+#
+ifdef KRBVERS
+LD_ADD+= $(KRBLIBS)
+CFLAGS+= $(KRBFLAGS)
+endif
+
+P1_PROG:= testlibpq
+P1_OBJS:= testlibpq.o
+
+$(P1_PROG):  $(addprefix $(objdir)/,$(P1_OBJS))
+       $(CC) $(CDEBUG) -o $(objdir)/$(@F) $< $(LD_ADD)
+
+P2_PROG:= testlibpq2
+P2_OBJS:= testlibpq2.o
+
+$(P2_PROG):  $(addprefix $(objdir)/,$(P2_OBJS))
+       $(CC) $(CDEBUG) -o $(objdir)/$(@F) $< $(LD_ADD)
+
+
+P3_PROG:= testlibpq3
+P3_OBJS:= testlibpq3.o
+
+$(P3_PROG):  $(addprefix $(objdir)/,$(P3_OBJS))
+       $(CC) $(CDEBUG) -o $(objdir)/$(@F) $< $(LD_ADD)
+
+P4_PROG:= testlo
+P4_OBJS:= testlo.o
+
+$(P4_PROG):  $(addprefix $(objdir)/,$(P4_OBJS))
+       $(CC) $(CDEBUG) -o $(objdir)/$(@F) $< $(LD_ADD)
+
+OBJS:= $(P1_OBJS) $(P2_OBJS) $(P3_OBJS) $(P4_OBJS)
+PROGS:= $(P1_PROG) $(P2_PROG) $(P3_PROG) $(P4_PROG)
+
+CLEANFILES+= $(OBJS) $(PROGS)
+
+all:: $(PROGS)
+
+install:: $(PROGS)
+       @for i in ${PROGS}; do \
+               echo "Installing $$i"; \
+               $(INSTALL) $(objdir)/$$i $(DESTDIR)$(BINDIR)/$$i;\
+       done
+       
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/examples/testlibpq.c b/src/test/examples/testlibpq.c
new file mode 100644 (file)
index 0000000..f3b9964
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * testlibpq.c
+ *     Test the C version of LIBPQ, the POSTGRES frontend library.
+ *
+ *
+ */
+#include <stdio.h>
+#include "libpq-fe.h"
+
+void 
+exit_nicely(PGconn* conn)
+{
+  PQfinish(conn);
+  exit(1);
+}
+
+main()
+{
+  char *pghost, *pgport, *pgoptions, *pgtty;
+  char* dbName;
+  int nFields;
+  int i,j;
+
+#ifdef DEBUG
+ FILE *debug; 
+#endif /* DEBUG */
+
+  PGconn* conn;
+  PGresult* res;
+
+  /* begin, by setting the parameters for a backend connection
+     if the parameters are null, then the system will try to use
+     reasonable defaults by looking up environment variables 
+     or, failing that, using hardwired constants */
+  pghost = NULL;  /* host name of the backend server */
+  pgport = NULL;  /* port of the backend server */
+  pgoptions = NULL; /* special options to start up the backend server */
+  pgtty = NULL;     /* debugging tty for the backend server */
+  dbName = "template1";
+
+  /* make a connection to the database */
+  conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
+
+  /* check to see that the backend connection was successfully made */
+  if (PQstatus(conn) == CONNECTION_BAD) {
+    fprintf(stderr,"Connection to database '%s' failed.\n", dbName);
+    fprintf(stderr,"%s",PQerrorMessage(conn));
+    exit_nicely(conn);
+  }
+
+#ifdef DEBUG
+  debug = fopen("/tmp/trace.out","w");  
+  PQtrace(conn, debug);  
+#endif /* DEBUG */
+
+  /* start a transaction block */
+  res = PQexec(conn,"BEGIN"); 
+  if (PQresultStatus(res) != PGRES_COMMAND_OK) {
+    fprintf(stderr,"BEGIN command failed\n");
+    PQclear(res);
+    exit_nicely(conn);
+  }
+  /* should PQclear PGresult whenever it is no longer needed to avoid
+     memory leaks */
+  PQclear(res); 
+
+  /* fetch instances from the pg_database, the system catalog of databases*/
+  res = PQexec(conn,"DECLARE myportal CURSOR FOR select * from pg_database");
+  if (PQresultStatus(res) != PGRES_COMMAND_OK) {
+    fprintf(stderr,"DECLARE CURSOR command failed\n");
+    PQclear(res);
+    exit_nicely(conn);
+  }
+  PQclear(res);
+
+  res = PQexec(conn,"FETCH ALL in myportal");
+  if (PQresultStatus(res) != PGRES_TUPLES_OK) {
+    fprintf(stderr,"FETCH ALL command didn't return tuples properly\n");
+    PQclear(res);
+    exit_nicely(conn);
+  }
+  /* first, print out the attribute names */
+  nFields = PQnfields(res);
+  for (i=0; i < nFields; i++) {
+    printf("%-15s",PQfname(res,i));
+  }
+  printf("\n\n");
+
+  /* next, print out the instances */
+  for (i=0; i < PQntuples(res); i++) {
+    for (j=0  ; j < nFields; j++) {
+      printf("%-15s", PQgetvalue(res,i,j));
+    }
+    printf("\n");
+  }
+
+  PQclear(res);
+  
+  /* close the portal */
+  res = PQexec(conn, "CLOSE myportal");
+  PQclear(res);
+
+  /* end the transaction */
+  res = PQexec(conn, "END");
+  PQclear(res);
+
+  /* close the connection to the database and cleanup */
+  PQfinish(conn);
+
+#ifdef DEBUG
+  fclose(debug); 
+#endif /* DEBUG */
+
+  exit(0);
+}
+  
+
diff --git a/src/test/examples/testlibpq2.c b/src/test/examples/testlibpq2.c
new file mode 100644 (file)
index 0000000..801feec
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * testlibpq2.c
+ *     Test of the asynchronous notification interface
+ *
+   populate a database with the following:
+
+CREATE TABLE TBL1 (i int4);
+
+CREATE TABLE TBL2 (i int4);
+
+CREATE RULE r1 AS ON INSERT TO TBL1 DO [INSERT INTO TBL2 values (new.i); NOTIFY TBL2];
+
+ * Then start up this program
+ * After the program has begun, do
+
+INSERT INTO TBL1 values (10);
+
+ *
+ *
+ */
+#include <stdio.h>
+#include "libpq-fe.h"
+
+void exit_nicely(PGconn* conn)
+{
+  PQfinish(conn);
+  exit(1);
+}
+
+main()
+{
+  char *pghost, *pgport, *pgoptions, *pgtty;
+  char* dbName;
+  int nFields;
+  int i,j;
+
+  PGconn* conn;
+  PGresult* res;
+  PGnotify* notify;
+
+  /* begin, by setting the parameters for a backend connection
+     if the parameters are null, then the system will try to use
+     reasonable defaults by looking up environment variables 
+     or, failing that, using hardwired constants */
+  pghost = NULL;  /* host name of the backend server */
+  pgport = NULL;  /* port of the backend server */
+  pgoptions = NULL; /* special options to start up the backend server */
+  pgtty = NULL;     /* debugging tty for the backend server */
+  dbName = getenv("USER"); /* change this to the name of your test database*/
+
+  /* make a connection to the database */
+  conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
+
+  /* check to see that the backend connection was successfully made */
+  if (PQstatus(conn) == CONNECTION_BAD) {
+    fprintf(stderr,"Connection to database '%s' failed.\n", dbName);
+    fprintf(stderr,"%s",PQerrorMessage(conn));
+    exit_nicely(conn);
+  }
+
+  res = PQexec(conn, "LISTEN TBL2");
+  if (PQresultStatus(res) != PGRES_COMMAND_OK) {
+    fprintf(stderr,"LISTEN command failed\n");
+    PQclear(res);
+    exit_nicely(conn);
+  }
+  /* should PQclear PGresult whenever it is no longer needed to avoid
+     memory leaks */
+  PQclear(res); 
+
+  while (1) {
+      /* async notification only come back as a result of a query*/
+      /* we can send empty queries */
+      res = PQexec(conn, " ");
+/*      printf("res->status = %s\n", pgresStatus[PQresultStatus(res)]); */
+      /* check for asynchronous returns */
+      notify = PQnotifies(conn);
+      if (notify) {
+         fprintf(stderr,
+                 "ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
+                 notify->relname, notify->be_pid);
+         free(notify);
+         break;
+      }
+      PQclear(res);
+  }
+      
+  /* close the connection to the database and cleanup */
+  PQfinish(conn);
+
+}
+  
+
diff --git a/src/test/examples/testlibpq2.sql b/src/test/examples/testlibpq2.sql
new file mode 100644 (file)
index 0000000..f9c7410
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE TABLE TBL1 (i int4);
+
+CREATE TABLE TBL2 (i int4);
+
+CREATE RULE r1 AS ON INSERT TO TBL1 DO [INSERT INTO TBL2 values (new.i); NOTIFY TBL2];
diff --git a/src/test/examples/testlibpq3.c b/src/test/examples/testlibpq3.c
new file mode 100644 (file)
index 0000000..bab22b3
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * testlibpq3.c
+ *     Test the C version of LIBPQ, the POSTGRES frontend library.
+ *   tests the binary cursor interface
+ *
+ *
+ *
+ populate a database by doing the following:
+CREATE TABLE test1 (i int4, d float4, p polygon);
+
+INSERT INTO test1 values (1, 3.567, '(3.0, 4.0, 1.0, 2.0)'::polygon);
+
+INSERT INTO test1 values (2, 89.05, '(4.0, 3.0, 2.0, 1.0)'::polygon);
+
+ the expected output is:
+
+tuple 0: got
+ i = (4 bytes) 1,
+ d = (4 bytes) 3.567000,
+ p = (4 bytes) 2 points         boundbox = (hi=3.000000/4.000000, lo = 1.000000,2.000000)
+tuple 1: got
+ i = (4 bytes) 2,
+ d = (4 bytes) 89.050003,
+ p = (4 bytes) 2 points         boundbox = (hi=4.000000/3.000000, lo = 2.000000,1.000000)
+
+ *
+ */
+#include <stdio.h>
+#include "libpq-fe.h"
+#include "utils/geo-decls.h" /* for the POLYGON type */
+
+void exit_nicely(PGconn* conn)
+{
+  PQfinish(conn);
+  exit(1);
+}
+
+main()
+{
+  char *pghost, *pgport, *pgoptions, *pgtty;
+  char* dbName;
+  int nFields;
+  int i,j;
+  int i_fnum, d_fnum, p_fnum;
+
+  PGconn* conn;
+  PGresult* res;
+
+  /* begin, by setting the parameters for a backend connection
+     if the parameters are null, then the system will try to use
+     reasonable defaults by looking up environment variables 
+     or, failing that, using hardwired constants */
+  pghost = NULL;  /* host name of the backend server */
+  pgport = NULL;  /* port of the backend server */
+  pgoptions = NULL; /* special options to start up the backend server */
+  pgtty = NULL;     /* debugging tty for the backend server */
+
+  dbName = getenv("USER");  /* change this to the name of your test database*/
+
+  /* make a connection to the database */
+  conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
+
+  /* check to see that the backend connection was successfully made */
+  if (PQstatus(conn) == CONNECTION_BAD) {
+    fprintf(stderr,"Connection to database '%s' failed.\n", dbName);
+    fprintf(stderr,"%s",PQerrorMessage(conn));
+    exit_nicely(conn);
+  }
+
+  /* start a transaction block */
+  res = PQexec(conn,"BEGIN"); 
+  if (PQresultStatus(res) != PGRES_COMMAND_OK) {
+    fprintf(stderr,"BEGIN command failed\n");
+    PQclear(res);
+    exit_nicely(conn);
+  }
+  /* should PQclear PGresult whenever it is no longer needed to avoid
+     memory leaks */
+  PQclear(res); 
+
+  /* fetch instances from the pg_database, the system catalog of databases*/
+  res = PQexec(conn,"DECLARE mycursor BINARY CURSOR FOR select * from test1");
+  if (res == NULL || 
+      PQresultStatus(res) != PGRES_COMMAND_OK) {
+    fprintf(stderr,"DECLARE CURSOR command failed\n");
+    if (res)
+       PQclear(res);
+    exit_nicely(conn);
+  }
+  PQclear(res);
+
+  res = PQexec(conn,"FETCH ALL in mycursor");
+  if (res == NULL ||
+      PQresultStatus(res) != PGRES_TUPLES_OK) {
+    fprintf(stderr,"FETCH ALL command didn't return tuples properly\n");
+    if (res)
+       PQclear(res);
+    exit_nicely(conn);
+  }
+  i_fnum = PQfnumber(res,"i");
+  d_fnum = PQfnumber(res,"d");
+  p_fnum = PQfnumber(res,"p");
+  
+  for (i=0;i<3;i++) {
+      printf("type[%d] = %d, size[%d] = %d\n",
+            i, PQftype(res,i), 
+            i, PQfsize(res,i));
+  }
+  for (i=0; i < PQntuples(res); i++) {
+    int *ival; 
+    float *dval;
+    int plen;
+    POLYGON* pval;
+    /* we hard-wire this to the 3 fields we know about */
+    ival =  (int*)PQgetvalue(res,i,i_fnum);
+    dval =  (float*)PQgetvalue(res,i,d_fnum);
+    plen = PQgetlength(res,i,p_fnum);
+
+    /* plen doesn't include the length field so need to increment by VARHDSZ*/
+    pval = (POLYGON*) malloc(plen + VARHDRSZ); 
+    pval->size = plen;
+    memmove((char*)&pval->npts, PQgetvalue(res,i,p_fnum), plen);
+    printf("tuple %d: got\n", i);
+    printf(" i = (%d bytes) %d,\n",
+          PQgetlength(res,i,i_fnum), *ival);
+    printf(" d = (%d bytes) %f,\n",
+          PQgetlength(res,i,d_fnum), *dval);
+    printf(" p = (%d bytes) %d points \tboundbox = (hi=%f/%f, lo = %f,%f)\n",
+          PQgetlength(res,i,d_fnum),
+          pval->npts,
+          pval->boundbox.xh,
+          pval->boundbox.yh,
+          pval->boundbox.xl,
+          pval->boundbox.yl);
+  }
+
+  PQclear(res);
+  
+  /* close the portal */
+  res = PQexec(conn, "CLOSE mycursor");
+  PQclear(res);
+
+  /* end the transaction */
+  res = PQexec(conn, "END");
+  PQclear(res);
+
+  /* close the connection to the database and cleanup */
+  PQfinish(conn);
+
+}
+  
+
diff --git a/src/test/examples/testlibpq3.sql b/src/test/examples/testlibpq3.sql
new file mode 100644 (file)
index 0000000..f024c0b
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE TABLE test1 (i int4, d float4, p polygon);
+
+INSERT INTO test1 values (1, 3.567, '(3.0, 4.0, 1.0, 2.0)'::polygon);
+
+INSERT INTO test1 values (2, 89.05, '(4.0, 3.0, 2.0, 1.0)'::polygon);
+
diff --git a/src/test/examples/testlibpq4.c b/src/test/examples/testlibpq4.c
new file mode 100644 (file)
index 0000000..5e04097
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * testlibpq4.c
+ *     this test programs shows to use LIBPQ to make multiple backend
+ * connections
+ *
+ *
+ */
+#include <stdio.h>
+#include "libpq-fe.h"
+
+void 
+exit_nicely(PGconn* conn1, PGconn* conn2)
+{
+    if (conn1)
+       PQfinish(conn1);
+    if (conn2)
+       PQfinish(conn2);
+    exit(1);
+}
+
+void check_conn(PGconn* conn)
+{
+  /* check to see that the backend connection was successfully made */
+  if (PQstatus(conn) == CONNECTION_BAD) {
+    fprintf(stderr,"Connection to database '%s' failed.\n", dbName);
+    fprintf(stderr,"%s",PQerrorMessage(conn));
+    exit(1);
+  }
+}
+
+main()
+{
+  char *pghost, *pgport, *pgoptions, *pgtty;
+  char* dbName1, dbName2;
+  char* tblName;
+  int nFields;
+  int i,j;
+
+  PGconn* conn1, conn2;
+  PGresult* res1, res2;
+
+  if (argc != 4) 
+      {
+         fprintf(stderr,"usage: %s tableName dbName1 dbName2\n",argv[0]);
+         fprintf(stderr,"      compares two tables in two databases\n");
+         exit(1);
+      }
+  tblName = argv[1];
+  dbName1 = argv[2];
+  dbName2 = argv[3];
+  
+
+  /* begin, by setting the parameters for a backend connection
+     if the parameters are null, then the system will try to use
+     reasonable defaults by looking up environment variables 
+     or, failing that, using hardwired constants */
+  pghost = NULL;  /* host name of the backend server */
+  pgport = NULL;  /* port of the backend server */
+  pgoptions = NULL; /* special options to start up the backend server */
+  pgtty = NULL;     /* debugging tty for the backend server */
+
+  /* make a connection to the database */
+  conn1 = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName1);
+  check_conn(conn1);
+
+  conn2 = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName2);
+  check_conn(conn2);
+
+  /* start a transaction block */
+  res1 = PQexec(conn1,"BEGIN"); 
+  if (PQresultStatus(res1) != PGRES_COMMAND_OK) {
+    fprintf(stderr,"BEGIN command failed\n");
+    PQclear(res1);
+    exit_nicely(conn1,conn2);
+  }
+  /* should PQclear PGresult whenever it is no longer needed to avoid
+     memory leaks */
+  PQclear(res1); 
+
+  /* fetch instances from the pg_database, the system catalog of databases*/
+  res = PQexec(conn,"DECLARE myportal CURSOR FOR select * from pg_database");
+  if (PQresultStatus(res) != PGRES_COMMAND_OK) {
+    fprintf(stderr,"DECLARE CURSOR command failed\n");
+    PQclear(res);
+    exit_nicely(conn);
+  }
+  PQclear(res);
+
+  res = PQexec(conn,"FETCH ALL in myportal");
+  if (PQresultStatus(res) != PGRES_TUPLES_OK) {
+    fprintf(stderr,"FETCH ALL command didn't return tuples properly\n");
+    PQclear(res);
+    exit_nicely(conn);
+  }
+  /* first, print out the attribute names */
+  nFields = PQnfields(res);
+  for (i=0; i < nFields; i++) {
+    printf("%-15s",PQfname(res,i));
+  }
+  printf("\n\n");
+
+  /* next, print out the instances */
+  for (i=0; i < PQntuples(res); i++) {
+    for (j=0  ; j < nFields; j++) {
+      printf("%-15s", PQgetvalue(res,i,j));
+    }
+    printf("\n");
+  }
+
+  PQclear(res);
+  
+  /* close the portal */
+  res = PQexec(conn, "CLOSE myportal");
+  PQclear(res);
+
+  /* end the transaction */
+  res = PQexec(conn, "END");
+  PQclear(res);
+
+  /* close the connection to the database and cleanup */
+  PQfinish(conn);
+
+/*   fclose(debug); */
+}
+  
+
diff --git a/src/test/examples/testlo.c b/src/test/examples/testlo.c
new file mode 100644 (file)
index 0000000..fd3dcad
--- /dev/null
@@ -0,0 +1,232 @@
+/*-------------------------------------------------------------------------
+ *
+ * testlo.c--
+ *    test using large objects with libpq
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include "libpq-fe.h"
+#include "libpq/libpq-fs.h"
+
+#define BUFSIZE                1024
+
+/*
+ * importFile -
+ *    import file "in_filename" into database as large object "lobjOid"
+ *
+ */
+Oid importFile(PGconn *conn, char *filename)
+{
+    Oid lobjId;
+    int lobj_fd;
+    char buf[BUFSIZE];
+    int nbytes, tmp;
+    int fd;
+
+    /*
+     * open the file to be read in
+     */
+    fd = open(filename, O_RDONLY, 0666);
+    if (fd < 0)  {   /* error */
+       fprintf(stderr, "can't open unix file\"%s\"\n", filename);
+    }
+
+    /*
+     * create the large object
+     */
+    lobjId = lo_creat(conn, INV_READ|INV_WRITE);
+    if (lobjId == 0) {
+       fprintf(stderr, "can't create large object");
+    }
+    
+    lobj_fd = lo_open(conn, lobjId, INV_WRITE);
+    /*
+     * read in from the Unix file and write to the inversion file
+     */
+    while ((nbytes = read(fd, buf, BUFSIZE)) > 0) {
+       tmp = lo_write(conn, lobj_fd, buf, nbytes);
+       if (tmp < nbytes) {
+           fprintf(stderr, "error while reading \"%s\"", filename);
+       }
+    }
+    
+    (void) close(fd);
+    (void) lo_close(conn, lobj_fd);
+
+    return lobjId;
+}
+
+void pickout(PGconn *conn, Oid lobjId, int start, int len)
+{
+    int lobj_fd;
+    char* buf;
+    int nbytes;
+    int nread;
+
+    lobj_fd = lo_open(conn, lobjId, INV_READ);
+    if (lobj_fd < 0) {
+       fprintf(stderr,"can't open large object %d",
+               lobjId);
+    }
+
+    lo_lseek(conn, lobj_fd, start, SEEK_SET);
+    buf = malloc(len+1);
+    
+    nread = 0;
+    while (len - nread > 0) {
+       nbytes = lo_read(conn, lobj_fd, buf, len - nread);
+       buf[nbytes] = '\0';
+       fprintf(stderr,">>> %s", buf);
+       nread += nbytes;
+    }
+    fprintf(stderr,"\n");
+    lo_close(conn, lobj_fd);
+}
+
+void overwrite(PGconn *conn, Oid lobjId, int start, int len)
+{
+    int lobj_fd;
+    char* buf;
+    int nbytes;
+    int nwritten;
+    int i;
+
+    lobj_fd = lo_open(conn, lobjId, INV_READ);
+    if (lobj_fd < 0) {
+       fprintf(stderr,"can't open large object %d",
+               lobjId);
+    }
+
+    lo_lseek(conn, lobj_fd, start, SEEK_SET);
+    buf = malloc(len+1);
+    
+    for (i=0;i<len;i++)
+       buf[i] = 'X';
+    buf[i] = '\0';
+
+    nwritten = 0;
+    while (len - nwritten > 0) {
+       nbytes = lo_write(conn, lobj_fd, buf + nwritten, len - nwritten);
+       nwritten += nbytes;
+    }
+    fprintf(stderr,"\n");
+    lo_close(conn, lobj_fd);
+}
+
+
+/*
+ * exportFile -
+ *    export large object "lobjOid" to file "out_filename"
+ *
+ */
+void exportFile(PGconn *conn, Oid lobjId, char *filename)
+{
+    int lobj_fd;
+    char buf[BUFSIZE];
+    int nbytes, tmp;
+    int fd;
+
+    /*
+     * create an inversion "object"
+     */
+    lobj_fd = lo_open(conn, lobjId, INV_READ);
+    if (lobj_fd < 0) {
+       fprintf(stderr,"can't open large object %d",
+               lobjId);
+    }
+
+    /*
+     * open the file to be written to
+     */
+    fd = open(filename, O_CREAT|O_WRONLY, 0666);
+    if (fd < 0)  {   /* error */
+       fprintf(stderr, "can't open unix file\"%s\"",
+               filename);
+    }
+
+    /*
+     * read in from the Unix file and write to the inversion file
+     */
+    while ((nbytes = lo_read(conn, lobj_fd, buf, BUFSIZE)) > 0) {
+       tmp = write(fd, buf, nbytes);
+        if (tmp < nbytes) {
+           fprintf(stderr,"error while writing \"%s\"",
+                   filename);
+       }
+    }
+
+    (void) lo_close(conn, lobj_fd);
+    (void) close(fd);
+
+    return;
+}
+
+void 
+exit_nicely(PGconn* conn)
+{
+  PQfinish(conn);
+  exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+    char *in_filename, *out_filename;
+    char *database;
+    Oid lobjOid;
+    PGconn *conn;
+    PGresult *res;
+
+    if (argc != 4) {
+       fprintf(stderr, "Usage: %s database_name in_filename out_filename\n",
+               argv[0]);
+       exit(1);
+    }
+
+    database = argv[1];
+    in_filename = argv[2];
+    out_filename = argv[3];
+
+    /*
+     * set up the connection
+     */
+    conn = PQsetdb(NULL, NULL, NULL, NULL, database);
+
+    /* check to see that the backend connection was successfully made */
+    if (PQstatus(conn) == CONNECTION_BAD) {
+       fprintf(stderr,"Connection to database '%s' failed.\n", database);
+       fprintf(stderr,"%s",PQerrorMessage(conn));
+       exit_nicely(conn);
+    }
+       
+    res = PQexec(conn, "begin");
+    PQclear(res);
+    printf("importing file \"%s\" ...\n", in_filename);
+/*  lobjOid = importFile(conn, in_filename); */
+    lobjOid = lo_import(conn, in_filename); 
+/*
+    printf("\tas large object %d.\n", lobjOid);
+
+    printf("picking out bytes 1000-2000 of the large object\n");
+    pickout(conn, lobjOid, 1000, 1000);
+
+    printf("overwriting bytes 1000-2000 of the large object with X's\n");
+    overwrite(conn, lobjOid, 1000, 1000);
+*/
+
+    printf("exporting large object to file \"%s\" ...\n", out_filename);
+/*    exportFile(conn, lobjOid, out_filename); */
+    lo_export(conn, lobjOid,out_filename);
+
+    res = PQexec(conn, "end");
+    PQclear(res);
+    PQfinish(conn);
+    exit(0);
+}
diff --git a/src/test/examples/testlo2.c b/src/test/examples/testlo2.c
new file mode 100644 (file)
index 0000000..faaad9e
--- /dev/null
@@ -0,0 +1,233 @@
+/*-------------------------------------------------------------------------
+ *
+ * lotest.c--
+ *    test using large objects with libpq
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include "libpq-fe.h"
+#include "libpq/libpq-fs.h"
+
+#define BUFSIZE                1024
+
+/*
+ * importFile -
+ *    import file "in_filename" into database as large object "lobjOid"
+ *
+ */
+Oid importFile(PGconn *conn, char *filename)
+{
+    Oid lobjId;
+    int lobj_fd;
+    char buf[BUFSIZE];
+    int nbytes, tmp;
+    int fd;
+
+    /*
+     * open the file to be read in
+     */
+    fd = open(filename, O_RDONLY, 0666);
+    if (fd < 0)  {   /* error */
+       fprintf(stderr, "can't open unix file\"%s\"\n", filename);
+    }
+
+    /*
+     * create the large object
+     */
+    lobjId = lo_creat(conn, INV_READ|INV_WRITE);
+    if (lobjId == 0) {
+       fprintf(stderr, "can't create large object");
+    }
+    
+    lobj_fd = lo_open(conn, lobjId, INV_WRITE);
+    /*
+     * read in from the Unix file and write to the inversion file
+     */
+    while ((nbytes = read(fd, buf, BUFSIZE)) > 0) {
+       tmp = lo_write(conn, lobj_fd, buf, nbytes);
+       if (tmp < nbytes) {
+           fprintf(stderr, "error while reading \"%s\"", filename);
+       }
+    }
+    
+    (void) close(fd);
+    (void) lo_close(conn, lobj_fd);
+
+    return lobjId;
+}
+
+void pickout(PGconn *conn, Oid lobjId, int start, int len)
+{
+    int lobj_fd;
+    char* buf;
+    int nbytes;
+    int nread;
+
+    lobj_fd = lo_open(conn, lobjId, INV_READ);
+    if (lobj_fd < 0) {
+       fprintf(stderr,"can't open large object %d",
+               lobjId);
+    }
+
+    lo_lseek(conn, lobj_fd, start, SEEK_SET);
+    buf = malloc(len+1);
+    
+    nread = 0;
+    while (len - nread > 0) {
+       nbytes = lo_read(conn, lobj_fd, buf, len - nread);
+       buf[nbytes] = '\0';
+       fprintf(stderr,">>> %s", buf);
+       nread += nbytes;
+    }
+    fprintf(stderr,"\n");
+    lo_close(conn, lobj_fd);
+}
+
+void overwrite(PGconn *conn, Oid lobjId, int start, int len)
+{
+    int lobj_fd;
+    char* buf;
+    int nbytes;
+    int nwritten;
+    int i;
+
+    lobj_fd = lo_open(conn, lobjId, INV_READ);
+    if (lobj_fd < 0) {
+       fprintf(stderr,"can't open large object %d",
+               lobjId);
+    }
+
+    lo_lseek(conn, lobj_fd, start, SEEK_SET);
+    buf = malloc(len+1);
+    
+    for (i=0;i<len;i++)
+       buf[i] = 'X';
+    buf[i] = '\0';
+
+    nwritten = 0;
+    while (len - nwritten > 0) {
+       nbytes = lo_write(conn, lobj_fd, buf + nwritten, len - nwritten);
+       nwritten += nbytes;
+    }
+    fprintf(stderr,"\n");
+    lo_close(conn, lobj_fd);
+}
+
+
+/*
+ * exportFile -
+ *    export large object "lobjOid" to file "out_filename"
+ *
+ */
+void exportFile(PGconn *conn, Oid lobjId, char *filename)
+{
+    int lobj_fd;
+    char buf[BUFSIZE];
+    int nbytes, tmp;
+    int fd;
+
+    /*
+     * create an inversion "object"
+     */
+    lobj_fd = lo_open(conn, lobjId, INV_READ);
+    if (lobj_fd < 0) {
+       fprintf(stderr,"can't open large object %d",
+               lobjId);
+    }
+
+    /*
+     * open the file to be written to
+     */
+    fd = open(filename, O_CREAT|O_WRONLY, 0666);
+    if (fd < 0)  {   /* error */
+       fprintf(stderr, "can't open unix file\"%s\"",
+               filename);
+    }
+
+    /*
+     * read in from the Unix file and write to the inversion file
+     */
+    while ((nbytes = lo_read(conn, lobj_fd, buf, BUFSIZE)) > 0) {
+       tmp = write(fd, buf, nbytes);
+        if (tmp < nbytes) {
+           fprintf(stderr,"error while writing \"%s\"",
+                   filename);
+       }
+    }
+
+    (void) lo_close(conn, lobj_fd);
+    (void) close(fd);
+
+    return;
+}
+
+void 
+exit_nicely(PGconn* conn)
+{
+  PQfinish(conn);
+  exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+    char *in_filename, *out_filename;
+    char *database;
+    Oid lobjOid;
+    PGconn *conn;
+    PGresult *res;
+
+    if (argc != 4) {
+       fprintf(stderr, "Usage: %s database_name in_filename out_filename\n",
+               argv[0]);
+       exit(1);
+    }
+
+    database = argv[1];
+    in_filename = argv[2];
+    out_filename = argv[3];
+
+    /*
+     * set up the connection
+     */
+    conn = PQsetdb(NULL, NULL, NULL, NULL, database);
+
+    /* check to see that the backend connection was successfully made */
+    if (PQstatus(conn) == CONNECTION_BAD) {
+       fprintf(stderr,"Connection to database '%s' failed.\n", database);
+       fprintf(stderr,"%s",PQerrorMessage(conn));
+       exit_nicely(conn);
+    }
+       
+    res = PQexec(conn, "begin");
+    PQclear(res);
+
+    printf("importing file \"%s\" ...\n", in_filename);
+/*    lobjOid = importFile(conn, in_filename); */
+    lobjOid = lo_import(conn, in_filename);
+/*
+    printf("\tas large object %d.\n", lobjOid);
+
+    printf("picking out bytes 1000-2000 of the large object\n");
+    pickout(conn, lobjOid, 1000, 1000);
+
+    printf("overwriting bytes 1000-2000 of the large object with X's\n");
+    overwrite(conn, lobjOid, 1000, 1000);
+*/
+
+    printf("exporting large object to file \"%s\" ...\n", out_filename);
+/*    exportFile(conn, lobjOid, out_filename); */
+    lo_export(conn, lobjOid,out_filename);
+
+    res = PQexec(conn, "end");
+    PQclear(res);
+    PQfinish(conn);
+    exit(0);
+}
diff --git a/src/test/regress/Makefile b/src/test/regress/Makefile
new file mode 100644 (file)
index 0000000..c14fc9e
--- /dev/null
@@ -0,0 +1,62 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for regress (the regression test)
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+MKDIR= ../../mk
+include $(MKDIR)/postgres.mk
+include $(MKDIR)/postgres.user.mk
+
+CFLAGS+=-I$(HEADERDIR)
+
+#
+# try locating libpq.a in the following places
+#
+LIBPQ:=  -L$(srcdir)/libpq/$(objdir) -L$(LIBDIR) -lpq
+
+LDADD+= $(LIBPQ)
+
+
+#
+# build dynamically-loaded object files
+#
+DLOBJS= regress$(SLSUFF)
+
+#
+# ... plus test query inputs
+#
+CREATEFILES= $(DLOBJS:%=$(objdir)/%) \
+       create.sql queries.sql errors.sql destroy.sql security.sql
+
+
+OUTFILES= stud_emp.data onek.data regress.out aportal.out
+
+CLEANFILES+= $(notdir $(CREATEFILES)) $(OUTFILES)
+
+$(OUTFILES): $(CREATEFILES)
+       $(SHELL) ./regress.sh 2>&1 | tee $(objdir)/regress.out
+       @echo "RESULTS OF REGRESSION ARE SAVED IN $(objdir)/regress.out"
+
+#
+# prepare to run the test (including clean-up after the last run)
+#
+all:: $(CREATEFILES)
+       cd $(objdir); rm -f $(OUTFILES)
+
+#
+# run the test
+#
+runtest: regress.out
+
+#
+# installation
+#
+install: localobj all
diff --git a/src/test/regress/create.source b/src/test/regress/create.source
new file mode 100644 (file)
index 0000000..f3fb711
--- /dev/null
@@ -0,0 +1,765 @@
+--
+-- create.source
+--
+--
+
+--
+-- ABSTRACT DATA TYPE DEFINITIONS
+--
+
+CREATE FUNCTION circle_in(opaque)
+   RETURNS circle
+   AS '_OBJWD_/regress_SLSUFF_'
+   LANGUAGE 'c';
+
+CREATE FUNCTION circle_out(opaque)
+   RETURNS opaque
+   AS '_OBJWD_/regress_SLSUFF_'
+   LANGUAGE 'c';
+
+CREATE TYPE circle (
+   internallength = 24, 
+   input = circle_in,
+   output = circle_out,
+   alignment = double
+);
+
+CREATE TYPE city_budget ( 
+   internallength = 16, 
+   input = int44in, 
+   output = int44out, 
+   element = int4
+);
+
+--
+-- CLASS DEFINITIONS
+--
+CREATE TABLE hobbies_r (
+       name            text, 
+       person          text
+);
+
+CREATE TABLE equipment_r (
+       name            text,
+       hobby           text
+);
+
+CREATE TABLE onek (
+       unique1         int4,
+       unique2         int4,
+       two             int4,
+       four            int4,
+       ten             int4,
+       twenty          int4,
+       hundred         int4,
+       thousand        int4,
+       twothousand     int4,
+       fivethous       int4,
+       tenthous        int4,
+       odd             int4,
+       even            int4,
+       stringu1        char16,
+       stringu2        char16,
+       string4         char16
+);
+
+CREATE TABLE tenk1 (
+       unique1         int4,
+       unique2         int4,
+       two             int4,
+       four            int4,
+       ten             int4,
+       twenty          int4,
+       hundred         int4,
+       thousand        int4,
+       twothousand     int4,
+       fivethous       int4,
+       tenthous        int4,
+       odd             int4,
+       even            int4,
+       stringu1        char16,
+       stringu2        char16,
+       string4         char16
+);
+
+CREATE TABLE tenk2 (
+       unique1         int4,
+       unique2         int4,
+       two             int4,
+       four            int4,
+       ten             int4,
+       twenty          int4,
+       hundred         int4,
+       thousand        int4,
+       twothousand     int4,
+       fivethous       int4,
+       tenthous        int4,
+       odd             int4,
+       even            int4,
+       stringu1        char16,
+       stringu2        char16,
+       string4         char16
+);
+
+
+CREATE TABLE person (
+       name            text,
+       age             int4,
+       location        point
+);
+
+
+CREATE TABLE emp (
+       salary          int4,
+       manager         char16
+) INHERITS (person);
+
+
+CREATE TABLE student (
+       gpa             float8
+) INHERITS (person);
+
+
+CREATE TABLE stud_emp (
+       percent         int4
+) INHERITS (emp, student);
+
+
+CREATE TABLE city (
+       name            char16,
+       location        box,
+       budget          city_budget
+);
+
+CREATE TABLE dept (
+       dname           char16,
+       mgrname         text
+);
+
+CREATE TABLE slow_emp4000 (
+       home_base        box
+);
+
+CREATE TABLE fast_emp4000 (
+       home_base        box
+);
+
+CREATE TABLE road (
+       name            text,
+       thepath         path
+);
+
+CREATE TABLE ihighway () INHERITS (road);
+
+CREATE TABLE shighway (
+       surface         text
+) INHERITS (road);
+
+CREATE TABLE real_city (
+       pop             int4,
+       cname           text,
+       outline         path
+);
+
+--
+-- test the "star" operators a bit more thoroughly -- this time,
+-- throw in lots of NULL fields...
+--
+-- a is the type root
+-- b and c inherit from a (one-level single inheritance)
+-- d inherits from b and c (two-level multiple inheritance)
+-- e inherits from c (two-level single inheritance)
+-- f inherits from e (three-level single inheritance)
+--
+CREATE TABLE a_star (
+       class           char, 
+       a               int4
+);
+
+CREATE TABLE b_star (
+       b               text
+) INHERITS (a_star);
+
+CREATE TABLE c_star (
+       c               char16
+) INHERITS (a_star);
+
+CREATE TABLE d_star (
+       d               float8
+) INHERITS (b_star, c_star);
+
+CREATE TABLE e_star (
+       e               int2
+) INHERITS (c_star);
+
+CREATE TABLE f_star (
+       f               polygon
+) INHERITS (e_star);
+
+CREATE TABLE aggtest (
+       a               int2,
+       b               float4
+);
+
+CREATE TABLE arrtest (
+       a               int2[],
+       b               int4[][][],
+       c               char16[],
+       d               text[][], 
+       e               float8[]
+);
+
+CREATE TABLE hash_i4_heap (
+       seqno           int4,
+       random          int4
+);
+
+CREATE TABLE hash_c16_heap (
+       seqno           int4,
+       random          char16
+);
+
+CREATE TABLE hash_txt_heap (
+       seqno           int4,
+       random          text
+);
+
+CREATE TABLE hash_f8_heap (
+       seqno           int4,
+       random          float8
+);
+
+-- don't include the hash_ovfl_heap stuff in the distribution
+-- the data set is too large for what it's worth
+-- 
+-- CREATE TABLE hash_ovfl_heap (
+--     x               int4,
+--     y               int4
+-- );
+
+CREATE TABLE bt_i4_heap (
+       seqno           int4,
+       random          int4
+);
+
+CREATE TABLE bt_c16_heap (
+       seqno           char16,
+       random          int4
+);
+
+CREATE TABLE bt_txt_heap (
+       seqno           text,
+       random          int4
+);
+
+CREATE TABLE bt_f8_heap (
+       seqno           float8, 
+       random          int4
+);
+
+--
+-- FUNCTION DEFINITIONS
+--
+CREATE FUNCTION hobbies(person)
+   RETURNS setof hobbies_r 
+   AS 'select * from hobbies_r where person = $1.name'
+   LANGUAGE 'sql';
+
+
+CREATE FUNCTION hobby_construct(text, text)
+   RETURNS hobbies_r
+   AS 'select $1 as name, $2 as hobby'
+   LANGUAGE 'sql';
+
+
+CREATE FUNCTION equipment(hobbies_r)
+   RETURNS setof equipment_r
+   AS 'select * from equipment_r where hobby = $1.name'
+   LANGUAGE 'sql';
+
+
+CREATE FUNCTION user_relns()
+   RETURNS setof name
+   AS 'select relname 
+       from pg_class 
+       where relname !~ ''pg_.*'' and
+             relkind <> ''i'' '
+   LANGUAGE 'sql';
+
+CREATE FUNCTION pt_in_circle(point, circle)
+   RETURNS int4
+   AS '_OBJWD_/regress_SLSUFF_'
+   LANGUAGE 'c';
+
+CREATE FUNCTION overpaid(emp)
+   RETURNS bool
+   AS '_OBJWD_/regress_SLSUFF_'
+   LANGUAGE 'c';
+
+CREATE FUNCTION boxarea(box)
+   RETURNS int4
+   AS '_OBJWD_/regress_SLSUFF_'
+   LANGUAGE 'c';
+
+CREATE FUNCTION interpt_pp(path, path)
+   RETURNS point
+   AS '_OBJWD_/regress_SLSUFF_'
+   LANGUAGE 'c';
+
+CREATE FUNCTION reverse_c16(char16)
+   RETURNS char16
+   AS '_OBJWD_/regress_SLSUFF_'
+   LANGUAGE 'c';
+
+--
+-- FUNCTION DYNAMIC LOADING
+--
+LOAD '_OBJWD_/regress_SLSUFF_'
+
+--
+-- CLASS POPULATION
+--     (any resemblance to real life is purely coincidental)
+--
+COPY onek FROM '_CWD_/data/onek.data';
+
+COPY tenk1 FROM '_CWD_/data/tenk.data';
+
+INSERT INTO tenk2 VALUES (tenk1.*);
+
+SELECT * INTO TABLE onek2 FROM onek;
+
+COPY slow_emp4000 FROM '_CWD_/data/rect.data';
+
+INSERT INTO fast_emp4000 VALUES (slow_emp4000.*);
+
+COPY person FROM '_CWD_/data/person.data';
+
+COPY emp FROM '_CWD_/data/emp.data';
+
+COPY student FROM '_CWD_/data/student.data';
+
+COPY stud_emp FROM '_CWD_/data/stud_emp.data';
+
+SELECT *
+   INTO TABLE Bprime
+   FROM tenk1
+   WHERE unique2 < 1000;
+
+INSERT INTO hobbies_r (name, person)
+   SELECT 'posthacking', p.name
+   FROM person* p
+   WHERE p.name = 'mike' or p.name = 'jeff';
+
+INSERT INTO hobbies_r (name, person)
+   SELECT 'basketball', p.name
+   FROM person p
+   WHERE p.name = 'joe' or p.name = 'sally';
+
+INSERT INTO hobbies_r (name) VALUES ('skywalking');
+
+INSERT INTO equipment_r (name, hobby) VALUES ('advil', 'posthacking');
+
+INSERT INTO equipment_r (name, hobby) VALUES ('peet''s coffee', 'posthacking');
+
+INSERT INTO equipment_r (name, hobby) VALUES ('hightops', 'basketball');
+
+INSERT INTO equipment_r (name, hobby) VALUES ('guts', 'skywalking');
+
+COPY road FROM '_CWD_/data/streets.data';
+
+COPY real_city FROM '_CWD_/data/real_city.data';
+
+SELECT *
+   INTO TABLE ramp
+   FROM road
+   WHERE name ~ '.*Ramp';
+
+INSERT INTO ihighway 
+   SELECT * 
+   FROM road 
+   WHERE name ~ 'I- .*';
+
+INSERT INTO shighway 
+   SELECT * 
+   FROM road 
+   WHERE name ~ 'State Hwy.*';
+
+UPDATE shighway
+   SET surface = 'asphalt'
+
+INSERT INTO a_star (class, a) VALUES ('a', 1);
+
+INSERT INTO a_star (class, a) VALUES ('a', 2);
+
+INSERT INTO a_star (class) VALUES ('a');
+
+INSERT INTO b_star (class, a, b) VALUES ('b', 3, 'mumble'::text);
+
+INSERT INTO b_star (class, a) VALUES ('b', 4);
+
+INSERT INTO b_star (class, b) VALUES ('b', 'bumble'::text);
+
+INSERT INTO b_star (class) VALUES ('b');
+
+INSERT INTO c_star (class, a, c) VALUES ('c', 5, 'hi mom'::char16);
+
+INSERT INTO c_star (class, a) VALUES ('c', 6);
+
+INSERT INTO c_star (class, c) VALUES ('c', 'hi paul'::char16);
+
+INSERT INTO c_star (class) VALUES ('c');
+
+INSERT INTO d_star (class, a, b, c, d)
+   VALUES ('d', 7, 'grumble'::text, 'hi sunita'::char16, '0.0'::float8);
+
+INSERT INTO d_star (class, a, b, c)
+   VALUES ('d', 8, 'stumble'::text, 'hi koko'::char16);
+
+INSERT INTO d_star (class, a, b, d)
+   VALUES ('d', 9, 'rumble'::text, '1.1'::float8);
+
+INSERT INTO d_star (class, a, c, d)
+   VALUES ('d', 10, 'hi kristin'::char16, '10.01'::float8);
+
+INSERT INTO d_star (class, b, c, d)
+   VALUES ('d', 'crumble'::text, 'hi boris'::char16, '100.001'::float8);
+
+INSERT INTO d_star (class, a, b)
+   VALUES ('d', 11, 'fumble'::text)
+
+INSERT INTO d_star (class, a, c)
+   VALUES ('d', 12, 'hi avi'::char16);
+
+INSERT INTO d_star (class, a, d)
+   VALUES ('d', 13, '1000.0001'::float8);
+
+INSERT INTO d_star (class, b, c)
+   VALUES ('d', 'tumble'::text, 'hi andrew'::char16);
+
+INSERT INTO d_star (class, b, d)
+   VALUES ('d', 'humble'::text, '10000.00001'::float8);
+
+INSERT INTO d_star (class, c, d)
+   VALUES ('d', 'hi ginger'::char16, '100000.000001'::float8);
+
+INSERT INTO d_star (class, a) VALUES ('d', 14);
+
+INSERT INTO d_star (class, b) VALUES ('d', 'jumble'::text);
+
+INSERT INTO d_star (class, c) VALUES ('d', 'hi jolly'::char16);
+
+INSERT INTO d_star (class, d) VALUES ('d', '1000000.0000001'::float8);
+
+INSERT INTO d_star (class) VALUES ('d');
+
+INSERT INTO e_star (class, a, c, e)
+   VALUES ('e', 15, 'hi carol'::char16, '-1'::int2);
+
+INSERT INTO e_star (class, a, c)
+   VALUES ('e', 16, 'hi bob'::char16);
+
+INSERT INTO e_star (class, a, e)
+   VALUES ('e', 17, '-2'::int2);
+
+INSERT INTO e_star (class, c, e)
+   VALUES ('e', 'hi michelle'::char16, '-3'::int2);
+
+INSERT INTO e_star (class, a)
+   VALUES ('e', 18);
+
+INSERT INTO e_star (class, c)
+   VALUES ('e', 'hi elisa'::char16);
+
+INSERT INTO e_star (class, e)
+   VALUES ('e', '-4'::int2);
+
+INSERT INTO f_star (class, a, c, e, f)
+   VALUES ('f', 19, 'hi claire'::char16, '-5'::int2, '(1,2,3,4)'::polygon);
+
+INSERT INTO f_star (class, a, c, e)
+   VALUES ('f', 20, 'hi mike'::char16, '-6'::int2);
+
+INSERT INTO f_star (class, a, c, f)
+   VALUES ('f', 21, 'hi marcel'::char16, '(11,22,33,44,55,66)'::polygon);
+
+INSERT INTO f_star (class, a, e, f)
+   VALUES ('f', 22, '-7'::int2, '(111,222,333,444,555,666,777,888)'::polygon);
+
+INSERT INTO f_star (class, c, e, f)
+   VALUES ('f', 'hi keith'::char16, '-8'::int2, 
+          '(1111,2222,3333,4444)'::polygon);
+
+INSERT INTO f_star (class, a, c)
+   VALUES ('f', 24, 'hi marc'::char16);
+
+INSERT INTO f_star (class, a, e)
+   VALUES ('f', 25, '-9'::int2);
+
+INSERT INTO f_star (class, a, f)
+   VALUES ('f', 26, '(11111,22222,33333,44444)'::polygon); 
+
+INSERT INTO f_star (class, c, e)
+   VALUES ('f', 'hi allison'::char16, '-10'::int2);
+
+INSERT INTO f_star (class, c, f)
+   VALUES ('f', 'hi jeff'::char16,
+           '(111111,222222,333333,444444)'::polygon);
+
+INSERT INTO f_star (class, e, f)
+   VALUES ('f', '-11'::int2, '(1111111,2222222,3333333,4444444)'::polygon);
+
+INSERT INTO f_star (class, a) VALUES ('f', 27);
+
+INSERT INTO f_star (class, c) VALUES ('f', 'hi carl'::char16);
+
+INSERT INTO f_star (class, e) VALUES ('f', '-12'::int2);
+
+INSERT INTO f_star (class, f) 
+   VALUES ('f', '(11111111,22222222,33333333,44444444)'::polygon);
+
+INSERT INTO f_star (class) VALUES ('f');
+
+COPY hash_i4_heap FROM '_CWD_/data/hash.data';
+
+COPY hash_c16_heap FROM '_CWD_/data/hash.data';
+
+COPY hash_txt_heap FROM '_CWD_/data/hash.data';
+
+COPY hash_f8_heap FROM '_CWD_/data/hash.data';
+
+--
+-- the data in this file has a lot of duplicates in the index key
+-- fields, leading to long bucket chains and lots of table expansion.
+-- this is therefore a stress test of the bucket overflow code (unlike
+-- the data in hash.data, which has unique index keys).
+--
+-- COPY hash_ovfl_heap FROM '_CWD_/data/hashovfl.data';
+
+COPY bt_i4_heap FROM '_CWD_/data/desc.data';
+
+COPY bt_c16_heap FROM '_CWD_/data/hash.data';
+
+COPY bt_txt_heap FROM '_CWD_/data/desc.data';
+
+COPY bt_f8_heap FROM '_CWD_/data/hash.data';
+
+--
+-- ARRAYS
+--
+
+--
+-- only this array as a 0-based 'e', the others are 1-based.
+-- 'e' is also a large object.
+--
+
+INSERT INTO arrtest (a[5], b[2][1][2], c, d)
+   VALUES ('{1,2,3,4,5}', '{{{},{1,2}}}', '{}', '{}');
+
+-- UPDATE arrtest SET e[0] = '1.1';
+
+-- UPDATE arrtest SET e[1] = '2.2';
+
+INSERT INTO arrtest (a, b[2][2][1], c, d, e)
+   VALUES ('{11,12,23}', '{{3,4},{4,5}}', '{"foobar"}', 
+           '{{"elt1", "elt2"}}', '{"3.4", "6.7"}');
+
+INSERT INTO arrtest (a, b[1][2][2], c, d[2][1])
+   VALUES ('{}', '{3,4}', '{foo,bar}', '{bar,foo}');
+
+
+--
+-- for internal portal (cursor) tests
+--
+CREATE TABLE iportaltest (
+       i               int4, 
+       d               float4, 
+       p               polygon
+);
+
+INSERT INTO iportaltest (i, d, p)
+   VALUES (1, 3.567, '(3.0,4.0,1.0,2.0)'::polygon);
+
+INSERT INTO iportaltest (i, d, p)
+   VALUES (2, 89.05, '(4.0,3.0,2.0,1.0)'::polygon);
+
+--
+-- CREATE ancillary data structures (i.e. indices)
+--
+
+--
+-- BTREE
+--
+CREATE INDEX onek_unique1 ON onek USING btree(unique1 int4_ops);
+
+CREATE INDEX onek_unique2 ON onek USING btree(unique2 int4_ops);
+
+CREATE INDEX onek_hundred ON onek USING btree(hundred int4_ops);
+
+CREATE INDEX onek_stringu1 ON onek USING btree(stringu1 char16_ops);
+
+CREATE INDEX tenk1_unique1 ON tenk1 USING btree(unique1 int4_ops);
+
+CREATE INDEX tenk1_unique2 ON tenk1 USING btree(unique2 int4_ops);
+
+CREATE INDEX tenk1_hundred ON tenk1 USING btree(hundred int4_ops);
+
+CREATE INDEX tenk2_unique1 ON tenk2 USING btree(unique1 int4_ops);
+
+CREATE INDEX tenk2_unique2 ON tenk2 USING btree(unique2 int4_ops);
+
+CREATE INDEX tenk2_hundred ON tenk2 USING btree(hundred int4_ops);
+
+CREATE INDEX rix ON road USING btree (name text_ops);
+
+CREATE INDEX iix ON ihighway USING btree (name text_ops);
+
+CREATE INDEX six ON shighway USING btree (name text_ops);
+
+--
+-- BTREE ascending/descending cases
+--
+-- we load int4/text from pure descending data (each key is a new
+-- low key) and c16/f8 from pure ascending data (each key is a new
+-- high key).  we had a bug where new low keys would sometimes be
+-- "lost".
+--
+CREATE INDEX bt_i4_index ON bt_i4_heap USING btree (seqno int4_ops);
+
+CREATE INDEX bt_c16_index ON bt_c16_heap USING btree (seqno char16_ops);
+
+CREATE INDEX bt_txt_index ON bt_txt_heap USING btree (seqno text_ops);
+
+CREATE INDEX bt_f8_index ON bt_f8_heap USING btree (seqno float8_ops);
+
+--
+-- BTREE partial indices
+-- partial indices are not supported in postgres95
+--
+--CREATE INDEX onek2_u1_prtl ON onek2 USING btree(unique1 int4_ops)
+--     where onek2.unique1 < 20 or onek2.unique1 > 980;
+
+--CREATE INDEX onek2_u2_prtl ON onek2 USING btree(unique2 int4_ops)
+--     where onek2.stringu1 < 'B';
+
+-- EXTEND INDEX onek2_u2_prtl where onek2.stringu1 < 'C';
+
+-- EXTEND INDEX onek2_u2_prtl;
+
+-- CREATE INDEX onek2_stu1_prtl ON onek2 USING btree(stringu1 char16_ops)
+--     where onek2.stringu1 >= 'J' and onek2.stringu1 < 'K';
+
+--
+-- RTREE
+-- 
+-- rtrees use a quadratic page-splitting algorithm that takes a
+-- really, really long time.  we don't test all rtree opclasses
+-- in the regression test (we check them USING the sequoia 2000
+-- benchmark).
+--
+CREATE INDEX rect2ind ON fast_emp4000 USING rtree (home_base bigbox_ops);
+
+
+--
+-- HASH
+--
+CREATE INDEX hash_i4_index ON hash_i4_heap USING hash (random int4_ops);
+
+CREATE INDEX hash_c16_index ON hash_c16_heap USING hash (random char16_ops);
+
+CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
+
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+
+-- CREATE INDEX hash_ovfl_index ON hash_ovfl_heap USING hash (x int4_ops);
+
+--
+-- OPERATOR DEFINITIONS
+--
+CREATE OPERATOR ## ( 
+   leftarg = path,
+   rightarg = path,
+   procedure = path_inter,
+   commutator = ## 
+);
+
+CREATE OPERATOR <% (
+   leftarg = point,
+   rightarg = circle,
+   procedure = pt_in_circle,
+   commutator = >=% 
+);
+
+CREATE OPERATOR @#@ (
+   rightarg = int4,            -- left unary 
+   procedure = int4fac 
+);
+
+CREATE OPERATOR #@# (
+   leftarg = int4,             -- right unary
+   procedure = int4fac 
+);
+
+CREATE OPERATOR #%# ( 
+   leftarg = int4,             -- right unary 
+   procedure = int4fac 
+);
+
+--
+-- VIRTUAL CLASS DEFINITIONS
+--     (this also tests the query rewrite system)
+--
+
+CREATE VIEW street AS
+   SELECT r.name, r.thepath, c.cname AS cname 
+   FROM road r, real_city c
+   WHERE c.outline ## r.thepath;
+
+CREATE VIEW iexit AS
+   SELECT ih.name, ih.thepath, 
+       interpt_pp(ih.thepath, r.thepath) AS exit
+   FROM ihighway ih, ramp r
+   WHERE ih.thepath ## r.thepath;
+
+CREATE VIEW toyemp AS
+   SELECT name, age, location, 12*salary AS annualsal
+   FROM emp;
+
+--
+-- RULES ???
+--
+
+--
+-- AGGREGATE DEFINITIONS
+--
+
+-- all functions CREATEd
+CREATE AGGREGATE newavg (
+   sfunc1 = int4pl, basetype = int4, stype1 = int4, 
+   sfunc2 = int4inc, stype2 = int4,
+   finalfunc = int4div,
+   initcond1 = '0', initcond2 = '0'
+);
+
+-- sfunc1 (value-dependent) only 
+CREATE AGGREGATE newsum (
+   sfunc1 = int4pl, basetype = int4, stype1 = int4, 
+   initcond1 = '0'
+);
+
+-- sfunc2 (value-independent) only 
+CREATE AGGREGATE newcnt (
+   sfunc2 = int4inc, basetype = int4, stype2 = int4, 
+   initcond2 = '0'
+);
+
+VACUUM;
+
+--
+-- sanity check, if we don't have indices the test will take years to
+-- complete.
+--
+SELECT relname, relhasindex
+   FROM pg_class
+   WHERE relhasindex
+   ORDER BY relname;
+
diff --git a/src/test/regress/data/dept.data b/src/test/regress/data/dept.data
new file mode 100644 (file)
index 0000000..c35dd57
--- /dev/null
@@ -0,0 +1,2 @@
+toy    sharon
+shoe   bob
diff --git a/src/test/regress/data/desc.data b/src/test/regress/data/desc.data
new file mode 100644 (file)
index 0000000..4ff1457
--- /dev/null
@@ -0,0 +1,10000 @@
+9999   1227676208
+9998   1673273198
+9997   868304211
+9996   999647871
+9995   310717580
+9994   1600952573
+9993   1081719182
+9992   242349705
+9991   1831758177
+9990   463935293
+9989   2044302343
+9988   1923557716
+9987   1243580926
+9986   18212056
+9985   757774765
+9984   1995958541
+9983   677073243
+9982   1290996901
+9981   947110578
+9980   1807402518
+9979   1756582238
+9978   910567096
+9977   2071380026
+9976   924516791
+9975   780851952
+9974   1605398760
+9973   1596663587
+9972   1261944440
+9971   625318191
+9970   1416577811
+9969   1272626725
+9968   228028337
+9967   1362555617
+9966   1414835286
+9965   2065412336
+9964   68367875
+9963   1916678044
+9962   617783889
+9961   345531010
+9960   2055684109
+9959   1367838015
+9958   2026090287
+9957   1165782951
+9956   1395106032
+9955   1488622461
+9954   1614261512
+9953   1048847963
+9952   1017154373
+9951   1681898311
+9950   36543482
+9949   1883506140
+9948   832065446
+9947   129715143
+9946   465981266
+9945   1475336852
+9944   1666391160
+9943   980080569
+9942   180085775
+9941   2136801363
+9940   397289853
+9939   54022194
+9938   2005275087
+9937   310099649
+9936   1294187742
+9935   1645640890
+9934   1447628447
+9933   1870320513
+9932   2008477583
+9931   1397429521
+9930   466924371
+9929   889901157
+9928   2120215631
+9927   537467826
+9926   1699005087
+9925   346258069
+9924   471468088
+9923   2079846849
+9922   1012304481
+9921   1281131881
+9920   849832864
+9919   2054311986
+9918   1417524873
+9917   1504212242
+9916   610807631
+9915   1633384345
+9914   1295251077
+9913   1677073445
+9912   582790715
+9911   126063581
+9910   131526276
+9909   87190204
+9908   907318099
+9907   359634197
+9906   1009954849
+9905   1571350877
+9904   1784646955
+9903   50198926
+9902   1403396142
+9901   1118576425
+9900   1424697538
+9899   2076940193
+9898   1338379718
+9897   1773957562
+9896   65999738
+9895   1766641886
+9894   1481437235
+9893   1337819855
+9892   1230013984
+9891   1105476143
+9890   2011090655
+9889   1493104270
+9888   1443504355
+9887   1931624176
+9886   208961165
+9885   1081217833
+9884   1050593630
+9883   1169187495
+9882   1545547169
+9881   495600511
+9880   1366229130
+9879   1919375727
+9878   1224719003
+9877   1483450870
+9876   722470890
+9875   959755923
+9874   167954735
+9873   666070529
+9872   772985035
+9871   1473939597
+9870   1927680355
+9869   1798223624
+9868   2010940455
+9867   1719221479
+9866   292520326
+9865   875663531
+9864   536627902
+9863   375961092
+9862   1474212847
+9861   1884393362
+9860   1809455435
+9859   79466479
+9858   1284143105
+9857   362286522
+9856   881030546
+9855   1187257317
+9854   1683154312
+9853   554993119
+9852   1950442013
+9851   1773655090
+9850   1418365156
+9849   2030261908
+9848   1196904837
+9847   264963079
+9846   1315496134
+9845   56400360
+9844   186770888
+9843   841498786
+9842   885873822
+9841   1122245059
+9840   1610482790
+9839   208458876
+9838   1505703298
+9837   1135276924
+9836   1182593577
+9835   2064042882
+9834   1548934331
+9833   799718188
+9832   713989305
+9831   1394746368
+9830   600250256
+9829   1447168913
+9828   1345919581
+9827   96885788
+9826   826615858
+9825   326037427
+9824   1384298952
+9823   2056982869
+9822   1284111611
+9821   2067663753
+9820   576750253
+9819   1153402076
+9818   714765773
+9817   1140504476
+9816   78192191
+9815   473997348
+9814   1318010186
+9813   1212009477
+9812   1378499644
+9811   677414946
+9810   1764025409
+9809   475205866
+9808   1173348946
+9807   1589144064
+9806   1733826240
+9805   382875389
+9804   1350053577
+9803   154187963
+9802   199467931
+9801   1414304039
+9800   48826786
+9799   503364468
+9798   620553055
+9797   1019882154
+9796   860070484
+9795   917116636
+9794   1189409464
+9793   1464118846
+9792   1480232616
+9791   130709534
+9790   1352897980
+9789   1583729425
+9788   1075209885
+9787   240768425
+9786   1969977938
+9785   1013666362
+9784   1242981351
+9783   640595240
+9782   1595467716
+9781   903293778
+9780   1651549647
+9779   174881345
+9778   888863273
+9777   790473557
+9776   239090487
+9775   1579638277
+9774   183407458
+9773   2083233185
+9772   105361177
+9771   1843587111
+9770   793750984
+9769   1176428280
+9768   1790777632
+9767   1850920067
+9766   1977956338
+9765   1543435285
+9764   1584367668
+9763   1058699929
+9762   111220866
+9761   2043986839
+9760   1202983297
+9759   1112129554
+9758   1761235135
+9757   61543522
+9756   1145270722
+9755   1329382697
+9754   1565682294
+9753   339687573
+9752   1136529241
+9751   1420586371
+9750   14430505
+9749   861076090
+9748   2083274506
+9747   1456708644
+9746   607066099
+9745   303340949
+9744   1474277100
+9743   487303995
+9742   1289482201
+9741   1076416545
+9740   52809478
+9739   1090314565
+9738   1345955589
+9737   247342347
+9736   266552398
+9735   919256409
+9734   1432214419
+9733   1687864477
+9732   2003200280
+9731   1146574960
+9730   282751704
+9729   1141439775
+9728   2114342480
+9727   431852437
+9726   643344876
+9725   805583149
+9724   192853456
+9723   145095923
+9722   325257068
+9721   275453150
+9720   1484795513
+9719   705205508
+9718   254009991
+9717   1779933556
+9716   2129915192
+9715   119762104
+9714   1161342396
+9713   397860555
+9712   434494516
+9711   199167636
+9710   1877944603
+9709   1952950779
+9708   823762166
+9707   426699180
+9706   962611576
+9705   726171569
+9704   1063539777
+9703   285639459
+9702   1405112773
+9701   861760505
+9700   1179716127
+9699   1998382914
+9698   498094899
+9697   1308759331
+9696   238998981
+9695   498248952
+9694   480326081
+9693   2064883954
+9692   807784058
+9691   1767535207
+9690   21443159
+9689   1852345604
+9688   722773964
+9687   134247887
+9686   618591160
+9685   1732054637
+9684   1832751235
+9683   962174759
+9682   667399599
+9681   629027385
+9680   1522889118
+9679   1451245423
+9678   990339203
+9677   97590597
+9676   1510643051
+9675   676972117
+9674   1468542443
+9673   201779272
+9672   1253406979
+9671   1554213507
+9670   363665606
+9669   2018440444
+9668   1759383933
+9667   2147329594
+9666   828433250
+9665   321598675
+9664   1837948542
+9663   860274521
+9662   2043440795
+9661   1102922102
+9660   1044761243
+9659   2034678919
+9658   1233754444
+9657   1138202974
+9656   448980300
+9655   1803900049
+9654   1064655038
+9653   1203723850
+9652   1586769289
+9651   1363637824
+9650   1786171829
+9649   1425298520
+9648   2088086019
+9647   313367086
+9646   776531802
+9645   1308863779
+9644   1571048785
+9643   2061812584
+9642   1985597314
+9641   1382450183
+9640   1942313221
+9639   363819659
+9638   1190007193
+9637   1437785258
+9636   309381052
+9635   2115642377
+9634   425641528
+9633   735026440
+9632   1962996926
+9631   8761875
+9630   2016651306
+9629   2054041917
+9628   1585698619
+9627   1577338043
+9626   73547936
+9625   1392740097
+9624   217130759
+9623   1848500861
+9622   1565035669
+9621   161470769
+9620   1423035453
+9619   1472804743
+9618   648766718
+9617   779222240
+9616   889801949
+9615   862202865
+9614   1470750113
+9613   188598602
+9612   119499363
+9611   1621777654
+9610   192442990
+9609   504527963
+9608   54438607
+9607   1221848464
+9606   1012143730
+9605   1721838260
+9604   152645451
+9603   416879652
+9602   865858782
+9601   2056438657
+9600   570546904
+9599   439313263
+9598   1980493980
+9597   192958522
+9596   1360207283
+9595   372530723
+9594   1975188076
+9593   55659990
+9592   425465408
+9591   92230926
+9590   1660187698
+9589   643813213
+9588   583002794
+9587   1934047501
+9586   1455955774
+9585   701203347
+9584   742703502
+9583   1996456107
+9582   2143639260
+9581   1762455048
+9580   1567339047
+9579   1118078173
+9578   1639867880
+9577   480083994
+9576   1069203013
+9575   595264078
+9574   855979478
+9573   243690442
+9572   1993816396
+9571   426545519
+9570   75944676
+9569   377588382
+9568   1226589627
+9567   1607963257
+9566   365254093
+9565   1304547293
+9564   2094548962
+9563   1882957150
+9562   542955940
+9561   1929135843
+9560   1656711780
+9559   1873623845
+9558   1335341086
+9557   2029283095
+9556   1191343998
+9555   1606983315
+9554   705047735
+9553   1127732102
+9552   429117059
+9551   1025561086
+9550   122587167
+9549   1087255053
+9548   48875160
+9547   1044603802
+9546   1771588164
+9545   825512571
+9544   748931329
+9543   429433959
+9542   167745765
+9541   1616228014
+9540   1347439539
+9539   615465067
+9538   12334288
+9537   2069525982
+9536   1660897943
+9535   629780591
+9534   761591353
+9533   165413119
+9532   226245370
+9531   816815742
+9530   593794757
+9529   1774912333
+9528   682279847
+9527   1875841419
+9526   1324235360
+9525   63611896
+9524   1177866256
+9523   1826970296
+9522   1005144935
+9521   1489345654
+9520   976685926
+9519   1225467013
+9518   1463150537
+9517   1370846236
+9516   295672473
+9515   1342154204
+9514   657766806
+9513   1280186963
+9512   1229478068
+9511   1699764346
+9510   1603893726
+9509   1425397205
+9508   1102050772
+9507   1530037345
+9506   1307934629
+9505   1495484824
+9504   403535220
+9503   2092259258
+9502   1719102010
+9501   598816685
+9500   134535895
+9499   865436986
+9498   450676973
+9497   618667951
+9496   697975163
+9495   1644748711
+9494   1205950609
+9493   1836004249
+9492   850284370
+9491   1927161570
+9490   26195117
+9489   1753323338
+9488   929794540
+9487   120996332
+9486   713079430
+9485   1162969158
+9484   112676136
+9483   1105486107
+9482   1823776885
+9481   1951564511
+9480   597713574
+9479   73856380
+9478   117462575
+9477   1754049596
+9476   1126502125
+9475   1363159019
+9474   1923866462
+9473   1952202183
+9472   1957723363
+9471   853665024
+9470   148139712
+9469   1663351592
+9468   167461823
+9467   953411909
+9466   1560200990
+9465   1009454561
+9464   794464341
+9463   1426272687
+9462   1809809132
+9461   1244444680
+9460   997367030
+9459   2052682433
+9458   1040243907
+9457   1914309030
+9456   8320196
+9455   1755076971
+9454   1486675921
+9453   308595273
+9452   507772533
+9451   1749920504
+9450   1834101935
+9449   991147626
+9448   1094837903
+9447   901787204
+9446   1977666782
+9445   1321783590
+9444   1552919304
+9443   1070201438
+9442   1804062470
+9441   294371770
+9440   686203201
+9439   1342211451
+9438   103150602
+9437   1305490909
+9436   158947568
+9435   133928303
+9434   1347129077
+9433   1697503309
+9432   428905657
+9431   1904610347
+9430   204200772
+9429   1230541648
+9428   2044362237
+9427   1432650584
+9426   427633109
+9425   1847208570
+9424   1247304438
+9423   1884239064
+9422   621976986
+9421   1664108554
+9420   655082601
+9419   932314731
+9418   1160964492
+9417   1920537961
+9416   1496351548
+9415   907465344
+9414   1665204767
+9413   1258547533
+9412   383998237
+9411   461851019
+9410   191221168
+9409   1528195939
+9408   1183263883
+9407   2116705947
+9406   2105845480
+9405   608927906
+9404   1852506294
+9403   1590002378
+9402   1493302537
+9401   1345847657
+9400   2007731758
+9399   919033836
+9398   802908539
+9397   197153666
+9396   185346146
+9395   690877692
+9394   1225231584
+9393   1730679531
+9392   1229156463
+9391   1837145903
+9390   503144062
+9389   882028287
+9388   1583446830
+9387   253499148
+9386   255333194
+9385   237804015
+9384   523467107
+9383   1203353748
+9382   1067326365
+9381   1003285945
+9380   1426070784
+9379   221998868
+9378   1569834107
+9377   574335976
+9376   264199653
+9375   515843101
+9374   1263109017
+9373   506658637
+9372   1729754268
+9371   574268701
+9370   542939118
+9369   1810578091
+9368   733687689
+9367   112030846
+9366   1119405730
+9365   602150263
+9364   1609204877
+9363   1535569329
+9362   1227535469
+9361   347128176
+9360   253699072
+9359   249644913
+9358   626695093
+9357   1345642815
+9356   1877515689
+9355   1199463094
+9354   1317961297
+9353   1667664809
+9352   1924766611
+9351   845327497
+9350   1580935486
+9349   851734808
+9348   2105282863
+9347   1053991006
+9346   1458710607
+9345   1905024664
+9344   933572481
+9343   688840316
+9342   2111203167
+9341   2066659825
+9340   1988064659
+9339   430908271
+9338   691172361
+9337   131537426
+9336   650309617
+9335   1731320048
+9334   1522098441
+9333   1262076701
+9332   1281870257
+9331   977890556
+9330   1867916730
+9329   1055539905
+9328   519612872
+9327   1574715647
+9326   27681518
+9325   209850880
+9324   1422180130
+9323   472633800
+9322   86729323
+9321   1073031803
+9320   887528282
+9319   526944480
+9318   1540507849
+9317   200258198
+9316   120418525
+9315   769870290
+9314   1941305145
+9313   1014396304
+9312   848259305
+9311   1680294895
+9310   1375487463
+9309   1856527233
+9308   1928082302
+9307   1107335961
+9306   756922633
+9305   1535716564
+9304   449449791
+9303   544207885
+9302   1541643618
+9301   226330352
+9300   458277684
+9299   293201083
+9298   1027858387
+9297   309761992
+9296   152535517
+9295   1702531365
+9294   123121556
+9293   349148327
+9292   1732589166
+9291   1707268491
+9290   1680007602
+9289   687270083
+9288   406525955
+9287   770637558
+9286   406436701
+9285   1253505869
+9284   2069094633
+9283   261010250
+9282   1786392488
+9281   1139215720
+9280   1899696241
+9279   268151502
+9278   1099604600
+9277   392365737
+9276   657886169
+9275   212714747
+9274   2141556594
+9273   223119439
+9272   85930201
+9271   1248442535
+9270   1345955613
+9269   148515692
+9268   140665566
+9267   1472810669
+9266   186640435
+9265   1950870838
+9264   2117425847
+9263   563336713
+9262   816624372
+9261   1045319083
+9260   1300742536
+9259   909370044
+9258   280833382
+9257   1300503734
+9256   849026573
+9255   145426451
+9254   1614597028
+9253   929878913
+9252   508797656
+9251   1518240986
+9250   39611120
+9249   1507330504
+9248   1757748981
+9247   886889852
+9246   398292791
+9245   434766730
+9244   126784546
+9243   893114058
+9242   1024647474
+9241   2084898157
+9240   1107776969
+9239   2020628591
+9238   2109358904
+9237   337278376
+9236   1502868470
+9235   1770787370
+9234   1134246465
+9233   1072106764
+9232   1410077824
+9231   2054737976
+9230   764485700
+9229   238802
+9228   60343471
+9227   135406931
+9226   1833390353
+9225   2066631307
+9224   1784112442
+9223   96356042
+9222   890267793
+9221   1148950800
+9220   1907975653
+9219   1300204915
+9218   1109037712
+9217   1322982251
+9216   760105306
+9215   1652662381
+9214   1557602903
+9213   189370036
+9212   1932820737
+9211   1151502531
+9210   2123022901
+9209   770498593
+9208   517760121
+9207   338571534
+9206   1350515558
+9205   430761706
+9204   360709546
+9203   1226992137
+9202   307621063
+9201   1409839022
+9200   1994394505
+9199   629078769
+9198   314332097
+9197   141195811
+9196   498778137
+9195   1737034311
+9194   1176363514
+9193   635161642
+9192   335864037
+9191   1737546526
+9190   39913088
+9189   584993402
+9188   540099609
+9187   1603858979
+9186   1912862995
+9185   570735270
+9184   1867325292
+9183   406100372
+9182   213830783
+9181   1162322143
+9180   633742410
+9179   1784451367
+9178   1567466683
+9177   86998414
+9176   2125345636
+9175   123523421
+9174   123140643
+9173   1098354172
+9172   1380081279
+9171   1826025942
+9170   1095506925
+9169   1853198694
+9168   130300632
+9167   724781434
+9166   1112315945
+9165   2011100143
+9164   1401170273
+9163   1586300636
+9162   595248554
+9161   1898354283
+9160   1197446917
+9159   583537756
+9158   819614054
+9157   2116847987
+9156   1884017335
+9155   1506762623
+9154   356904486
+9153   705003148
+9152   1919841610
+9151   576863064
+9150   1742339108
+9149   546743995
+9148   1806589379
+9147   1443943261
+9146   2111341419
+9145   1026991464
+9144   890925790
+9143   444598348
+9142   2847247
+9141   1674366233
+9140   1695725310
+9139   370725491
+9138   740882748
+9137   266684137
+9136   1471094808
+9135   1673498957
+9134   1415851589
+9133   1650299638
+9132   388853719
+9131   11710797
+9130   1078740229
+9129   1228082578
+9128   847004069
+9127   1460335079
+9126   1759943500
+9125   1179014187
+9124   1734404660
+9123   1927525070
+9122   1110147688
+9121   1373097615
+9120   917757333
+9119   298395847
+9118   582886224
+9117   779597915
+9116   553017471
+9115   1666743071
+9114   1024144217
+9113   1364043204
+9112   896356686
+9111   1779605404
+9110   933483485
+9109   1429041173
+9108   1047114330
+9107   1214867439
+9106   998316196
+9105   1968278818
+9104   1284645238
+9103   1404140791
+9102   571559409
+9101   1308254789
+9100   1312190376
+9099   1765888797
+9098   1615622725
+9097   1815473530
+9096   1873414067
+9095   1979902078
+9094   68866499
+9093   361307045
+9092   1009767736
+9091   811751841
+9090   790211391
+9089   138159418
+9088   1892862023
+9087   1063626801
+9086   1902937346
+9085   1336457915
+9084   770386385
+9083   1392022461
+9082   430559719
+9081   1614799160
+9080   732491073
+9079   1866099694
+9078   430724977
+9077   1226319160
+9076   2077705848
+9075   1741659052
+9074   1396719409
+9073   2123874097
+9072   91950415
+9071   953154259
+9070   1840115711
+9069   1644200494
+9068   2039958378
+9067   1783204295
+9066   1746607031
+9065   1512107021
+9064   970134342
+9063   1404598306
+9062   1718579302
+9061   871608318
+9060   1066373465
+9059   1874068238
+9058   382705720
+9057   556404108
+9056   293240416
+9055   510914885
+9054   905898195
+9053   1303070872
+9052   659531387
+9051   711943673
+9050   1184074183
+9049   1653655561
+9048   1935877493
+9047   836549573
+9046   1977083398
+9045   2101315399
+9044   1649708637
+9043   443565150
+9042   283758386
+9041   595233568
+9040   1060679529
+9039   56911416
+9038   2045077111
+9037   527851357
+9036   813069953
+9035   342008725
+9034   1941011367
+9033   98526024
+9032   338224840
+9031   1991994712
+9030   488902597
+9029   509969357
+9028   1580827822
+9027   2019274483
+9026   1797989561
+9025   1137653191
+9024   1998867145
+9023   193954522
+9022   118996689
+9021   1153359474
+9020   923549828
+9019   347524610
+9018   1824055811
+9017   1982045742
+9016   1334324583
+9015   1533518248
+9014   1817557013
+9013   1054475069
+9012   1530369269
+9011   226846969
+9010   697640105
+9009   532828172
+9008   1391325111
+9007   1703068386
+9006   734323638
+9005   714543929
+9004   3783884
+9003   2096500302
+9002   1757107074
+9001   1975739131
+9000   411166890
+8999   617111762
+8998   859463444
+8997   443174630
+8996   20407338
+8995   1604035039
+8994   1018656502
+8993   845507671
+8992   1417888342
+8991   1918955727
+8990   1476787311
+8989   1088987733
+8988   1160683674
+8987   290537562
+8986   164488729
+8985   279849514
+8984   3148979
+8983   1590710043
+8982   356834964
+8981   997541097
+8980   983005506
+8979   1142055366
+8978   1945988182
+8977   676781182
+8976   1699284502
+8975   785306983
+8974   1104920502
+8973   175528401
+8972   1685333412
+8971   1139995312
+8970   1116275687
+8969   2115475908
+8968   596704424
+8967   1402912053
+8966   1572001776
+8965   1322383314
+8964   186146697
+8963   1247184422
+8962   1516204008
+8961   328900608
+8960   758272053
+8959   1186249748
+8958   924499004
+8957   880834160
+8956   287388583
+8955   721262334
+8954   2070498198
+8953   1153091530
+8952   607704537
+8951   1362263245
+8950   1199036563
+8949   306224323
+8948   1590254512
+8947   1160681198
+8946   1719344328
+8945   1523756101
+8944   1247457219
+8943   2112408838
+8942   1206736361
+8941   1717341152
+8940   543290888
+8939   1860847282
+8938   543474131
+8937   1421804757
+8936   1216765356
+8935   324817354
+8934   1953662954
+8933   2004729736
+8932   488912369
+8931   329954260
+8930   1551885252
+8929   2024921541
+8928   898861165
+8927   203236670
+8926   957819609
+8925   1281780700
+8924   113557796
+8923   708234953
+8922   2101538615
+8921   301480214
+8920   1919492381
+8919   38355364
+8918   734363643
+8917   66498411
+8916   2060707627
+8915   1754419138
+8914   317019739
+8913   1677599715
+8912   1569117949
+8911   1493372727
+8910   1173867020
+8909   1268969779
+8908   644081926
+8907   218656777
+8906   1615625451
+8905   1359519267
+8904   1983388632
+8903   1623708694
+8902   452844484
+8901   611474476
+8900   1578576742
+8899   1348648582
+8898   1067101931
+8897   1764564113
+8896   89678873
+8895   249584656
+8894   1327725733
+8893   1959561230
+8892   936226220
+8891   2063183251
+8890   1714600218
+8889   1852993969
+8888   125131385
+8887   1127428153
+8886   1896962320
+8885   383107911
+8884   185301188
+8883   971130660
+8882   503732695
+8881   300148170
+8880   849290800
+8879   955210243
+8878   1800827975
+8877   1432046307
+8876   382751793
+8875   2139400405
+8874   906674783
+8873   1371914156
+8872   45131951
+8871   1251679549
+8870   1691856193
+8869   1961496277
+8868   1258969709
+8867   817517275
+8866   436838380
+8865   277601291
+8864   1460842084
+8863   1412026130
+8862   244961012
+8861   1230715898
+8860   1938051865
+8859   587172065
+8858   2103515297
+8857   1889507122
+8856   942126965
+8855   925831659
+8854   2026858864
+8853   2032636666
+8852   121839860
+8851   1696006100
+8850   646803843
+8849   1564728141
+8848   572458450
+8847   1808911218
+8846   525371523
+8845   1158321285
+8844   2094268454
+8843   1802478882
+8842   1827541611
+8841   231119322
+8840   2140193488
+8839   874338918
+8838   1524657897
+8837   981368418
+8836   1504158838
+8835   1172295898
+8834   32640279
+8833   230126186
+8832   1621457912
+8831   1805272595
+8830   1274684249
+8829   48544743
+8828   1792528748
+8827   1177683638
+8826   2010131905
+8825   1056973947
+8824   803991799
+8823   330852764
+8822   1385832823
+8821   704595366
+8820   1123547650
+8819   985376273
+8818   1039356618
+8817   1561620813
+8816   1862126412
+8815   870376289
+8814   1478263322
+8813   1863149132
+8812   1809769041
+8811   953202693
+8810   853945072
+8809   1158825070
+8808   1517663727
+8807   352361999
+8806   948728139
+8805   1274032652
+8804   1698321633
+8803   374851332
+8802   1102925585
+8801   1572913169
+8800   12743847
+8799   97000611
+8798   185896486
+8797   735554801
+8796   373691838
+8795   1679279141
+8794   1818624772
+8793   99396433
+8792   1354788762
+8791   400456550
+8790   1812722396
+8789   1709410485
+8788   1270733509
+8787   168980328
+8786   83357491
+8785   2146460928
+8784   1208090896
+8783   525060629
+8782   1009204059
+8781   650943971
+8780   1583022613
+8779   501583073
+8778   210096931
+8777   243631075
+8776   801524014
+8775   573876807
+8774   171107067
+8773   125408464
+8772   362107485
+8771   1005924974
+8770   1387016683
+8769   1424672694
+8768   1870792420
+8767   654100993
+8766   1064413677
+8765   274295405
+8764   324490378
+8763   1418168222
+8762   434157684
+8761   1792861925
+8760   1277206689
+8759   1643742068
+8758   1626052994
+8757   1271756229
+8756   1108373080
+8755   1705780510
+8754   1137256868
+8753   557146925
+8752   1089521663
+8751   507620986
+8750   440847039
+8749   1339391538
+8748   1847542707
+8747   1783703772
+8746   72524007
+8745   676115549
+8744   211769322
+8743   1312665741
+8742   885875429
+8741   1084918439
+8740   1282616201
+8739   732915690
+8738   360259017
+8737   1596497015
+8736   329610614
+8735   1793729103
+8734   1987621369
+8733   679112101
+8732   140961533
+8731   937899264
+8730   166808931
+8729   5450460
+8728   535368987
+8727   2067756132
+8726   134499360
+8725   551226155
+8724   616258846
+8723   629635882
+8722   116299885
+8721   1897613773
+8720   807561927
+8719   804626915
+8718   1266867531
+8717   1171427157
+8716   1571934450
+8715   907341914
+8714   1937723768
+8713   1274334531
+8712   30049540
+8711   152959739
+8710   724659422
+8709   1833602834
+8708   403305075
+8707   714013562
+8706   1756359294
+8705   1797982161
+8704   1652767570
+8703   1049722104
+8702   512303169
+8701   135511073
+8700   402530277
+8699   246536447
+8698   2018434747
+8697   2131626480
+8696   1451497285
+8695   1652347126
+8694   434926270
+8693   866128721
+8692   1969557602
+8691   1459156618
+8690   630746242
+8689   1783618418
+8688   1380176112
+8687   359525617
+8686   1381187037
+8685   297599919
+8684   877292374
+8683   1784764028
+8682   549675109
+8681   343930353
+8680   1897138312
+8679   10645860
+8678   77243540
+8677   752806562
+8676   1208729640
+8675   706637189
+8674   1285678992
+8673   1517256497
+8672   647191827
+8671   265766722
+8670   264559973
+8669   418387445
+8668   942522810
+8667   366087621
+8666   1696700210
+8665   585368564
+8664   1830273172
+8663   1123253299
+8662   235382479
+8661   185939184
+8660   78980506
+8659   271220625
+8658   402431380
+8657   1082576193
+8656   1629716891
+8655   1743906657
+8654   1895408458
+8653   533362020
+8652   2035109364
+8651   539029249
+8650   266686813
+8649   1144331750
+8648   949399868
+8647   1518089999
+8646   1614611218
+8645   1838956791
+8644   59445362
+8643   1019912270
+8642   1252696523
+8641   228804382
+8640   1470727560
+8639   2045956000
+8638   869170883
+8637   357154246
+8636   683298097
+8635   573446910
+8634   349986084
+8633   1644333987
+8632   1044272793
+8631   2111645502
+8630   1930991452
+8629   1143887961
+8628   788987382
+8627   806008371
+8626   1334651382
+8625   1096354870
+8624   1856280940
+8623   1356379209
+8622   266675207
+8621   890777614
+8620   1737113029
+8619   896080462
+8618   1677204180
+8617   1257926725
+8616   1458644637
+8615   594698948
+8614   586260267
+8613   1978124627
+8612   1696668358
+8611   1354224171
+8610   1507117147
+8609   1113573314
+8608   1362657903
+8607   295723972
+8606   7168161
+8605   1186447757
+8604   1676657765
+8603   385824230
+8602   1860826183
+8601   2047868480
+8600   1322658120
+8599   1124983080
+8598   1956720226
+8597   1840116159
+8596   1097211079
+8595   2125755821
+8594   829679663
+8593   965503326
+8592   1766749828
+8591   1518078393
+8590   1361057082
+8589   479186304
+8588   1584919473
+8587   1082505232
+8586   671666457
+8585   1628003657
+8584   1045514238
+8583   1379519744
+8582   471007480
+8581   583095044
+8580   2139049915
+8579   1211393175
+8578   1106405152
+8577   176210146
+8576   766549855
+8575   1768827579
+8574   1473105222
+8573   1776272932
+8572   1210649757
+8571   735843103
+8570   91148254
+8569   1630025609
+8568   27772001
+8567   1978448053
+8566   1010436496
+8565   131707753
+8564   359005992
+8563   1459084917
+8562   1751929891
+8561   1287563524
+8560   2080642568
+8559   278551850
+8558   1955003494
+8557   2104399463
+8556   36990994
+8555   1439630361
+8554   1156996177
+8553   462419194
+8552   1387953477
+8551   1407097953
+8550   1624173539
+8549   1962839769
+8548   444843319
+8547   1485061221
+8546   850588572
+8545   1137760571
+8544   558177822
+8543   737262119
+8542   1685124678
+8541   1728107796
+8540   708071101
+8539   260183848
+8538   619589112
+8537   2043547896
+8536   1619442061
+8535   1698835227
+8534   527261509
+8533   1218926116
+8532   1525925997
+8531   1473378041
+8530   1480043678
+8529   2123726753
+8528   241560856
+8527   515373133
+8526   947403286
+8525   1722055448
+8524   51676884
+8523   1897381872
+8522   985729302
+8521   1572597355
+8520   962254633
+8519   139112318
+8518   1112251197
+8517   1454566396
+8516   926883399
+8515   113326453
+8514   1600119540
+8513   977553673
+8512   29191017
+8511   1424940830
+8510   1108518684
+8509   812006853
+8508   788225435
+8507   1068237533
+8506   1516286387
+8505   400515945
+8504   172909230
+8503   1201367116
+8502   1886366086
+8501   1549682892
+8500   1231817184
+8499   964670544
+8498   1176323467
+8497   666989056
+8496   463696249
+8495   1197505061
+8494   736326145
+8493   626563176
+8492   935127239
+8491   846616984
+8490   460346158
+8489   1655171885
+8488   1359712567
+8487   998924744
+8486   2001930504
+8485   2096813373
+8484   84135435
+8483   175178710
+8482   2016518637
+8481   1364667812
+8480   636715394
+8479   40281150
+8478   1443204114
+8477   387709490
+8476   895328303
+8475   314919270
+8474   661633507
+8473   770709986
+8472   2117033580
+8471   921695541
+8470   373359425
+8469   564828128
+8468   500974295
+8467   2126302053
+8466   2078146559
+8465   1984616721
+8464   262377822
+8463   2037192809
+8462   166217018
+8461   1427439002
+8460   1634388064
+8459   1608905061
+8458   1800725029
+8457   1410382842
+8456   914789309
+8455   1826751793
+8454   80294736
+8453   866951271
+8452   1685946964
+8451   1976237487
+8450   2068947346
+8449   249005904
+8448   1292436495
+8447   1128284843
+8446   1873559631
+8445   124618317
+8444   345369338
+8443   1887421613
+8442   397350561
+8441   1552205452
+8440   420721246
+8439   394541019
+8438   634165217
+8437   663841222
+8436   1863924231
+8435   40953749
+8434   1818399702
+8433   982422468
+8432   402804745
+8431   704795605
+8430   1774197621
+8429   224005222
+8428   694115752
+8427   2121456883
+8426   1330088106
+8425   47838038
+8424   140804829
+8423   251540897
+8422   945487572
+8421   1436941060
+8420   683800992
+8419   940662503
+8418   522929920
+8417   1167818177
+8416   782915505
+8415   2133621666
+8414   1874751404
+8413   940647534
+8412   1466700367
+8411   2809541
+8410   918040235
+8409   1904363672
+8408   678100436
+8407   593211467
+8406   992925167
+8405   881501762
+8404   1785632652
+8403   1113604097
+8402   1355708495
+8401   178799522
+8400   10679852
+8399   1800224385
+8398   1041400764
+8397   646277714
+8396   1980652054
+8395   1078547209
+8394   1249834113
+8393   851347417
+8392   1715223553
+8391   4825069
+8390   914011139
+8389   1663466462
+8388   157746998
+8387   536791902
+8386   1440550421
+8385   1989751618
+8384   666921299
+8383   1871941863
+8382   22607299
+8381   1709820342
+8380   1472192753
+8379   324828767
+8378   911438505
+8377   1944082322
+8376   955062463
+8375   2026804718
+8374   1673276915
+8373   1606833130
+8372   1102924245
+8371   1702967758
+8370   1284882406
+8369   1511885786
+8368   1967055979
+8367   2110337203
+8366   1543927249
+8365   1129304636
+8364   1510807304
+8363   1245009044
+8362   2084819926
+8361   51757090
+8360   1994561719
+8359   377219237
+8358   222916041
+8357   315479027
+8356   2017354251
+8355   1716092206
+8354   1967144319
+8353   1104584604
+8352   399749110
+8351   1845262180
+8350   798381837
+8349   1675594079
+8348   1517249952
+8347   1032117435
+8346   270805407
+8345   1495712981
+8344   923880473
+8343   2117792805
+8342   321950724
+8341   1738522107
+8340   1883395426
+8339   1322028850
+8338   2115442185
+8337   837751343
+8336   599529899
+8335   298918205
+8334   1191968358
+8333   1459050213
+8332   1397930972
+8331   1707600689
+8330   1976324697
+8329   1679082692
+8328   507348633
+8327   654307483
+8326   495818356
+8325   912769647
+8324   1316343096
+8323   121882139
+8322   306202767
+8321   871638679
+8320   328012227
+8319   1913748050
+8318   1404788672
+8317   21536971
+8316   108236962
+8315   300496250
+8314   1173762257
+8313   1332842014
+8312   234397378
+8311   1147405521
+8310   1770563570
+8309   1045644083
+8308   722498951
+8307   1816523980
+8306   1793266632
+8305   1287963334
+8304   1048470880
+8303   1631851317
+8302   1630209164
+8301   1866331928
+8300   1200252055
+8299   1322017213
+8298   1183264335
+8297   1742062634
+8296   1485448035
+8295   373936217
+8294   606566880
+8293   444704417
+8292   1941353559
+8291   539938364
+8290   1614333655
+8289   306475256
+8288   1805511088
+8287   1104292422
+8286   995258362
+8285   922878596
+8284   66098871
+8283   26356735
+8282   1709762092
+8281   1336236943
+8280   424906570
+8279   2101523238
+8278   1399861099
+8277   1582019265
+8276   768053099
+8275   161415315
+8274   1805237817
+8273   1329622600
+8272   431599262
+8271   308191951
+8270   683067593
+8269   1605673069
+8268   1984052826
+8267   809328118
+8266   1135495754
+8265   1040743618
+8264   580066306
+8263   66628515
+8262   977854410
+8261   1634878303
+8260   881910924
+8259   510041233
+8258   1458700541
+8257   882632492
+8256   1038193550
+8255   968901627
+8254   1360600152
+8253   877345576
+8252   1748933813
+8251   1755722502
+8250   2083859492
+8249   990370953
+8248   1333470138
+8247   1238445784
+8246   1924265095
+8245   1585914147
+8244   1877299701
+8243   1497045866
+8242   646555007
+8241   973409841
+8240   471622773
+8239   2021223123
+8238   470177314
+8237   943309207
+8236   229261812
+8235   1068867239
+8234   62889208
+8233   1092671650
+8232   1332201239
+8231   467813177
+8230   176177762
+8229   2146762079
+8228   1619331330
+8227   489798914
+8226   1669515988
+8225   160847974
+8224   1367451462
+8223   1752361298
+8222   940969732
+8221   758562859
+8220   422252363
+8219   845413708
+8218   1213589506
+8217   1895039639
+8216   1508629731
+8215   427219229
+8214   939359140
+8213   903889860
+8212   1025423093
+8211   772815532
+8210   503232526
+8209   1675797213
+8208   1791961311
+8207   1548793723
+8206   880419999
+8205   1284073809
+8204   1884149647
+8203   1742559679
+8202   916493888
+8201   1332922808
+8200   995965494
+8199   1833862495
+8198   477246091
+8197   1458483356
+8196   1269831100
+8195   2064638338
+8194   1367361889
+8193   608888602
+8192   1330108934
+8191   95556024
+8190   1692457001
+8189   674696372
+8188   1484267625
+8187   786370277
+8186   955680498
+8185   604739871
+8184   1549279783
+8183   166543608
+8182   400657333
+8181   1497109528
+8180   1128337869
+8179   1101922451
+8178   795377214
+8177   507887501
+8176   1812127724
+8175   1285343967
+8174   367579921
+8173   551226839
+8172   746594185
+8171   1230115041
+8170   855676717
+8169   1684965786
+8168   564031395
+8167   560091400
+8166   91121467
+8165   660942498
+8164   734529404
+8163   1271805865
+8162   1063915249
+8161   655412562
+8160   758772047
+8159   906086724
+8158   1866499522
+8157   879527754
+8156   1384574141
+8155   789136890
+8154   204082537
+8153   52170255
+8152   1185689387
+8151   1446218530
+8150   701732313
+8149   620450367
+8148   1437278375
+8147   1657516895
+8146   140307580
+8145   1260900884
+8144   538749782
+8143   1284948528
+8142   1843033770
+8141   1209112047
+8140   666083646
+8139   295585316
+8138   1593844319
+8137   2050572545
+8136   1973045644
+8135   966799250
+8134   1744510897
+8133   79116842
+8132   513033817
+8131   157828524
+8130   936396688
+8129   2026727941
+8128   1668996231
+8127   1077362632
+8126   675445216
+8125   1332403886
+8124   1750931150
+8123   905347655
+8122   1497921590
+8121   565239020
+8120   8940155
+8119   1191699066
+8118   480142787
+8117   176377490
+8116   1118767112
+8115   1002842700
+8114   1565350762
+8113   1477121383
+8112   618864882
+8111   1547448454
+8110   1762751376
+8109   762994749
+8108   470023320
+8107   627045069
+8106   306061648
+8105   1893928802
+8104   453765432
+8103   1586682372
+8102   1290203802
+8101   633789524
+8100   636315941
+8099   2006517704
+8098   1351282725
+8097   336592345
+8096   1473915129
+8095   1917581209
+8094   1981965944
+8093   1185692130
+8092   896407499
+8091   306222523
+8090   85096233
+8089   1980046313
+8088   72931954
+8087   1624783734
+8086   758510376
+8085   1789129377
+8084   383977818
+8083   17902308
+8082   1861853655
+8081   2003353781
+8080   1077425134
+8079   1135706307
+8078   456933101
+8077   723578165
+8076   173279636
+8075   866862923
+8074   603725000
+8073   1967459556
+8072   950366431
+8071   1431169746
+8070   1429990447
+8069   299723596
+8068   532602574
+8067   1581185163
+8066   502110049
+8065   288222999
+8064   1021173710
+8063   1675743420
+8062   1100595897
+8061   1063844834
+8060   233290569
+8059   607796146
+8058   1221535936
+8057   431286225
+8056   1240805916
+8055   740608068
+8054   2074759369
+8053   528107685
+8052   1087960822
+8051   726147348
+8050   1546420680
+8049   353846968
+8048   962426670
+8047   1737553825
+8046   119853165
+8045   353303728
+8044   2063980140
+8043   1320038902
+8042   537469109
+8041   650642834
+8040   898567171
+8039   1996288931
+8038   1945097195
+8037   244379575
+8036   560011453
+8035   973850276
+8034   1335110749
+8033   2104812523
+8032   1442452851
+8031   492799751
+8030   1989792546
+8029   1949487992
+8028   1514473878
+8027   480927868
+8026   504010503
+8025   712698230
+8024   1800130894
+8023   1348612021
+8022   1129170653
+8021   734113853
+8020   1911204326
+8019   1956350502
+8018   233993803
+8017   609122942
+8016   1821057333
+8015   947297910
+8014   1963318266
+8013   1413337306
+8012   421471731
+8011   688663826
+8010   853029287
+8009   654187596
+8008   1436277478
+8007   971246919
+8006   1056752474
+8005   602682578
+8004   1678881073
+8003   842310998
+8002   115019977
+8001   1640448506
+8000   1125809520
+7999   1508864678
+7998   1445477489
+7997   801775648
+7996   828280621
+7995   1302882130
+7994   1731011225
+7993   1066017041
+7992   1584891343
+7991   1320303799
+7990   500120050
+7989   1302081383
+7988   135293169
+7987   1434179541
+7986   793288324
+7985   407720027
+7984   525826179
+7983   1274654440
+7982   560308019
+7981   1914767783
+7980   1399869996
+7979   2029266016
+7978   1744918770
+7977   833594900
+7976   1439849493
+7975   214441475
+7974   487662600
+7973   38432567
+7972   1863985126
+7971   753638947
+7970   194971017
+7969   324033872
+7968   680584056
+7967   142595358
+7966   1218248071
+7965   1909747228
+7964   1865474435
+7963   410707426
+7962   565896991
+7961   282809959
+7960   1185010629
+7959   1213424157
+7958   508793059
+7957   1875056790
+7956   908353361
+7955   1666117531
+7954   1994895656
+7953   758542044
+7952   2022268092
+7951   678525651
+7950   169849013
+7949   566275096
+7948   589416522
+7947   1530477294
+7946   345932299
+7945   1401416926
+7944   497939997
+7943   1881507301
+7942   1990945197
+7941   1539951253
+7940   73054891
+7939   52375659
+7938   1253269449
+7937   918320476
+7936   424604571
+7935   807540645
+7934   1343850237
+7933   1582664476
+7932   1373180444
+7931   1499956482
+7930   1921500548
+7929   1457437487
+7928   305070795
+7927   990159176
+7926   2027644782
+7925   149811317
+7924   1791333087
+7923   1316370005
+7922   588693031
+7921   1455992996
+7920   89109128
+7919   786855366
+7918   220342796
+7917   1335483244
+7916   1032537297
+7915   611908646
+7914   1557955377
+7913   1105472392
+7912   1808452410
+7911   1938569538
+7910   286681804
+7909   1302218063
+7908   1775254736
+7907   445728804
+7906   1721953886
+7905   989423743
+7904   1581843848
+7903   1991377403
+7902   1808647576
+7901   2063226605
+7900   1194885686
+7899   931341372
+7898   1577276352
+7897   155259478
+7896   1346309737
+7895   711274777
+7894   1708601933
+7893   335340090
+7892   1227260876
+7891   1949321313
+7890   1235650200
+7889   901109532
+7888   1901801717
+7887   1755917798
+7886   1925011515
+7885   2074548553
+7884   950939884
+7883   1766869486
+7882   818790588
+7881   506234347
+7880   163314802
+7879   1988436647
+7878   1727747824
+7877   785830993
+7876   1011368604
+7875   1878060131
+7874   1328259815
+7873   1666100891
+7872   796491717
+7871   877306204
+7870   485950253
+7869   1039626208
+7868   1732515283
+7867   866001575
+7866   594141193
+7865   1010969646
+7864   1631497549
+7863   1906764268
+7862   1247173538
+7861   326151344
+7860   47519595
+7859   1627216050
+7858   1123581665
+7857   1974736812
+7856   804977913
+7855   158142028
+7854   1255757965
+7853   444705537
+7852   1603554684
+7851   977837588
+7850   925970170
+7849   1524967457
+7848   977068043
+7847   1997171341
+7846   1605054826
+7845   1492751361
+7844   1081568414
+7843   450953611
+7842   1180150638
+7841   1904349157
+7840   1292274569
+7839   1767432326
+7838   445485015
+7837   721545636
+7836   1381987674
+7835   834860572
+7834   1911279756
+7833   1305346205
+7832   1859244673
+7831   1767441136
+7830   1350053326
+7829   220266431
+7828   822238136
+7827   965439637
+7826   718978847
+7825   360272376
+7824   702070992
+7823   277920376
+7822   1666219015
+7821   78587226
+7820   769545
+7819   1076282477
+7818   2067396279
+7817   1631800330
+7816   915602927
+7815   1154101215
+7814   312600723
+7813   1324702905
+7812   1306162690
+7811   1560201960
+7810   1458864142
+7809   570728932
+7808   385444652
+7807   1758108090
+7806   957749528
+7805   76641469
+7804   1123099547
+7803   143838619
+7802   2102776526
+7801   1638978242
+7800   945203000
+7799   384613689
+7798   1648771231
+7797   461965760
+7796   263368644
+7795   441058471
+7794   841537009
+7793   623483766
+7792   277150831
+7791   589936538
+7790   158674595
+7789   516452862
+7788   160679549
+7787   913295064
+7786   1319199607
+7785   1738383670
+7784   1995422173
+7783   899882411
+7782   2013322411
+7781   735433757
+7780   1174757308
+7779   1848239699
+7778   1760463052
+7777   308803183
+7776   635008543
+7775   813910909
+7774   121348590
+7773   1631604953
+7772   1346119267
+7771   1718162837
+7770   2137690658
+7769   483237239
+7768   121245045
+7767   1207712760
+7766   1767912399
+7765   1787368526
+7764   163907639
+7763   251600471
+7762   464809171
+7761   1908181617
+7760   429256988
+7759   1392863178
+7758   1344736903
+7757   569779527
+7756   1065356539
+7755   419317196
+7754   1872544907
+7753   1259988415
+7752   1872608751
+7751   165082711
+7750   1122454353
+7749   865954125
+7748   1213231156
+7747   946552143
+7746   187454592
+7745   1150887237
+7744   1615275289
+7743   550669401
+7742   1641397943
+7741   862882028
+7740   1596917792
+7739   929977898
+7738   862808488
+7737   481360166
+7736   1043805121
+7735   1516311928
+7734   1322559355
+7733   403209670
+7732   1969827130
+7731   1219429640
+7730   563444714
+7729   2006961109
+7728   327506639
+7727   925419706
+7726   844718267
+7725   1952851772
+7724   694192093
+7723   1707462196
+7722   137534062
+7721   1006654626
+7720   1099335203
+7719   175902210
+7718   678499532
+7717   62343919
+7716   1478760501
+7715   1784268839
+7714   1656972942
+7713   752393261
+7712   1101235257
+7711   711420045
+7710   73540
+7709   1115557625
+7708   2033656425
+7707   1493980207
+7706   1306284459
+7705   640595450
+7704   1693968446
+7703   103129715
+7702   1987248604
+7701   2110349669
+7700   891923001
+7699   1785508655
+7698   1162242842
+7697   522138515
+7696   231227613
+7695   1284739719
+7694   1815317710
+7693   1835021115
+7692   608126993
+7691   2109115499
+7690   328155093
+7689   1036991284
+7688   844625357
+7687   1041714341
+7686   552854624
+7685   726367240
+7684   683033581
+7683   945552897
+7682   752319721
+7681   2133161280
+7680   825247268
+7679   653576980
+7678   1956756814
+7677   1393060974
+7676   1947495409
+7675   1203154744
+7674   800830494
+7673   1731102425
+7672   1358690361
+7671   201739949
+7670   948106827
+7669   369784486
+7668   1554281042
+7667   2024986770
+7666   854304453
+7665   543690145
+7664   676612726
+7663   1853685858
+7662   1506866022
+7661   1718619357
+7660   1264490142
+7659   1433924400
+7658   484136659
+7657   118258117
+7656   358680760
+7655   1754785375
+7654   2121531166
+7653   697355949
+7652   120305629
+7651   98742741
+7650   176404465
+7649   1579669941
+7648   853565219
+7647   753602070
+7646   592230480
+7645   216392984
+7644   1991948030
+7643   599090545
+7642   782995598
+7641   988905875
+7640   794942554
+7639   1070603704
+7638   1662963681
+7637   1010590897
+7636   1348374044
+7635   1148102242
+7634   1184307771
+7633   1105477017
+7632   589195716
+7631   72941622
+7630   1234482697
+7629   1146232025
+7628   1075243640
+7627   876834932
+7626   144210598
+7625   1808808458
+7624   1634479745
+7623   2022788425
+7622   520951484
+7621   688119336
+7620   1392661170
+7619   1570286043
+7618   987439461
+7617   637172234
+7616   909137688
+7615   2140623583
+7614   1580881034
+7613   1003042155
+7612   1951631638
+7611   1859875541
+7610   1473425841
+7609   1931835305
+7608   1869713308
+7607   514861439
+7606   1973766773
+7605   242897026
+7604   558906526
+7603   1111366149
+7602   2018477968
+7601   1590447338
+7600   1145181630
+7599   357647765
+7598   1002021427
+7597   1413918829
+7596   1389838835
+7595   268905821
+7594   1287856974
+7593   946360409
+7592   630127255
+7591   1098149089
+7590   1848163523
+7589   755488935
+7588   661148355
+7587   994299525
+7586   1203774848
+7585   2053579180
+7584   188991945
+7583   1868489141
+7582   1677099962
+7581   19796333
+7580   2137645881
+7579   958564402
+7578   2105552180
+7577   1626816282
+7576   2103438560
+7575   862400624
+7574   371902706
+7573   1115942836
+7572   2113668167
+7571   1660830203
+7570   588425911
+7569   1878746448
+7568   1115292578
+7567   733115606
+7566   126061855
+7565   443478425
+7564   1786262213
+7563   189707885
+7562   1245680534
+7561   2022121968
+7560   437000734
+7559   853863997
+7558   1699197735
+7557   755052822
+7556   805307580
+7555   1482769355
+7554   376479218
+7553   169195612
+7552   1878326908
+7551   718535559
+7550   61727801
+7549   510829599
+7548   1002609490
+7547   1243151556
+7546   1254913575
+7545   987495724
+7544   896216104
+7543   858556151
+7542   527516924
+7541   234921719
+7540   545537625
+7539   2002793953
+7538   1752684593
+7537   671814152
+7536   1094337040
+7535   2083837617
+7534   1345281539
+7533   1911623893
+7532   1900190799
+7531   391816537
+7530   322924232
+7529   1829431559
+7528   48556417
+7527   216428380
+7526   378573604
+7525   636111968
+7524   1751926095
+7523   1805427307
+7522   107467811
+7521   1367497309
+7520   1863409717
+7519   966059893
+7518   1403399671
+7517   15113765
+7516   346935451
+7515   396357424
+7514   459978800
+7513   661294385
+7512   313018526
+7511   672206619
+7510   629720773
+7509   2021207120
+7508   908456913
+7507   1816330624
+7506   1474016261
+7505   1330196795
+7504   183646818
+7503   953465002
+7502   1588699661
+7501   70759240
+7500   343260120
+7499   106495852
+7498   1450857955
+7497   1559928096
+7496   611985933
+7495   720629945
+7494   528644157
+7493   384428786
+7492   2089501237
+7491   1288891566
+7490   2111581285
+7489   1848295952
+7488   619124441
+7487   1007042247
+7486   1702618613
+7485   1833124714
+7484   83338897
+7483   1935255829
+7482   31573612
+7481   439295054
+7480   1911233354
+7479   960873797
+7478   547190859
+7477   1725743766
+7476   1632683806
+7475   520551259
+7474   1888980782
+7473   112887578
+7472   610204882
+7471   1482203809
+7470   767384932
+7469   930815671
+7468   1641993566
+7467   730228010
+7466   1031283939
+7465   227557147
+7464   778612355
+7463   1387236239
+7462   420331148
+7461   241205285
+7460   669767124
+7459   1104539038
+7458   145677338
+7457   933483375
+7456   923703350
+7455   1914846432
+7454   1801551102
+7453   1791527491
+7452   24022475
+7451   1218183462
+7450   2039587843
+7449   185489588
+7448   1475673639
+7447   26639599
+7446   1984246632
+7445   1519796228
+7444   2057830025
+7443   406776973
+7442   1492986293
+7441   1826872858
+7440   1987693890
+7439   37156922
+7438   2047015380
+7437   1414436419
+7436   2099099303
+7435   1791531347
+7434   1954709647
+7433   537407070
+7432   717469115
+7431   1463275758
+7430   95527947
+7429   1883767397
+7428   180835688
+7427   378314554
+7426   1279415921
+7425   1279659506
+7424   1890823957
+7423   583367639
+7422   1899423296
+7421   1986016535
+7420   1889993471
+7419   2012948243
+7418   348726604
+7417   2103361059
+7416   116293222
+7415   1577469659
+7414   26809934
+7413   230957167
+7412   566566730
+7411   1455829371
+7410   1927341126
+7409   573257471
+7408   85541267
+7407   255484033
+7406   1607210420
+7405   1561692233
+7404   1074062232
+7403   491433888
+7402   441879123
+7401   981185366
+7400   1282440070
+7399   1864697041
+7398   604351475
+7397   1048659829
+7396   634974244
+7395   696048282
+7394   1527719858
+7393   2052291070
+7392   840857816
+7391   2033958701
+7390   1637289931
+7389   1934116059
+7388   1896655021
+7387   918740593
+7386   2076551125
+7385   2032819703
+7384   1010902928
+7383   718464211
+7382   451099689
+7381   2140792907
+7380   1370288104
+7379   1671857093
+7378   1113530699
+7377   671332682
+7376   1328905448
+7375   1115776531
+7374   1119813110
+7373   92876866
+7372   1356477466
+7371   724665730
+7370   376833890
+7369   233780241
+7368   1229722796
+7367   2055786841
+7366   1668423619
+7365   730166822
+7364   2002674113
+7363   1641244805
+7362   415001139
+7361   1054225405
+7360   137303679
+7359   718549338
+7358   2005048582
+7357   2011318966
+7356   2055321312
+7355   1358086914
+7354   1581720014
+7353   1017593669
+7352   1495659754
+7351   926726244
+7350   1027262207
+7349   698955422
+7348   342951644
+7347   2145237816
+7346   1699003220
+7345   1236028582
+7344   1906782713
+7343   395147380
+7342   1863526624
+7341   1122697225
+7340   1642426581
+7339   468530697
+7338   712840269
+7337   499555974
+7336   53112728
+7335   27178814
+7334   315165682
+7333   948448708
+7332   1503941125
+7331   1843935449
+7330   1196660470
+7329   273468361
+7328   810711673
+7327   646961668
+7326   429598952
+7325   1037727643
+7324   2009910807
+7323   654993770
+7322   2137815110
+7321   796704332
+7320   583774599
+7319   1029508039
+7318   1147435850
+7317   1254406710
+7316   238455102
+7315   1303855840
+7314   1519985606
+7313   784085488
+7312   900204446
+7311   1394995927
+7310   409856955
+7309   1142870607
+7308   415417969
+7307   685661455
+7306   184390292
+7305   1252147667
+7304   670721337
+7303   618713881
+7302   1899271886
+7301   1230472764
+7300   1033223776
+7299   549698802
+7298   1991353056
+7297   1920467678
+7296   784534509
+7295   1922088830
+7294   1047396181
+7293   1213206475
+7292   71219170
+7291   1108307070
+7290   1796752129
+7289   1476851537
+7288   791052937
+7287   1991063658
+7286   1881904752
+7285   1601853262
+7284   403651393
+7283   124989679
+7282   374228533
+7281   1904817487
+7280   979577958
+7279   1871679148
+7278   958480315
+7277   1310753949
+7276   14940118
+7275   1713160059
+7274   1500359429
+7273   1587732220
+7272   1732973753
+7271   1349573084
+7270   1386603356
+7269   1260239745
+7268   1912647941
+7267   69264226
+7266   873071497
+7265   1718811681
+7264   1850869660
+7263   2086572758
+7262   1563937994
+7261   741851281
+7260   317254133
+7259   1953172119
+7258   1742430432
+7257   1336683323
+7256   1587412265
+7255   1756915073
+7254   1227624729
+7253   646317554
+7252   1292895369
+7251   650033032
+7250   946337172
+7249   1816307656
+7248   1856739030
+7247   1392803904
+7246   1957878168
+7245   574691545
+7244   2127669954
+7243   150786345
+7242   201128864
+7241   472734007
+7240   1584408791
+7239   1317339130
+7238   387168248
+7237   193836259
+7236   365878214
+7235   933982387
+7234   154873687
+7233   1109018378
+7232   1769318625
+7231   1758249523
+7230   1146904497
+7229   1128054458
+7228   365759854
+7227   2132999007
+7226   109058594
+7225   941094711
+7224   464019704
+7223   577591696
+7222   1847464029
+7221   1624071360
+7220   940777650
+7219   1701016916
+7218   2005913136
+7217   1282047485
+7216   1412617598
+7215   1807091822
+7214   373562681
+7213   1654935946
+7212   713861202
+7211   1031273382
+7210   85565759
+7209   1390572531
+7208   951460916
+7207   1600669509
+7206   38962572
+7205   1404343483
+7204   1312147410
+7203   544107812
+7202   2109597529
+7201   641264166
+7200   1392489669
+7199   1161389138
+7198   1018995864
+7197   1572148791
+7196   1668979302
+7195   1678950545
+7194   1241114329
+7193   987431992
+7192   1784297694
+7191   146447113
+7190   1765641872
+7189   1806213813
+7188   288399318
+7187   198821314
+7186   908484804
+7185   1905165299
+7184   1093230620
+7183   1489772946
+7182   1569370187
+7181   1470772319
+7180   79812466
+7179   632379898
+7178   1351609959
+7177   1694601080
+7176   288522099
+7175   1642338407
+7174   1442229602
+7173   670883243
+7172   1299101791
+7171   948208391
+7170   1769751950
+7169   1967824526
+7168   1639893483
+7167   1487528967
+7166   331034461
+7165   681547310
+7164   2042136499
+7163   1094667216
+7162   1369273768
+7161   2125567529
+7160   2005531442
+7159   1566820558
+7158   897729009
+7157   530717667
+7156   1253074342
+7155   1566195505
+7154   335795112
+7153   1769941949
+7152   1409960480
+7151   936990288
+7150   119162359
+7149   532695034
+7148   343857799
+7147   1856755200
+7146   252371478
+7145   1765122503
+7144   343236616
+7143   494021210
+7142   1048614941
+7141   1478760913
+7140   1455798556
+7139   282222983
+7138   1636790064
+7137   958346173
+7136   1592876116
+7135   1383850893
+7134   1459757190
+7133   2064052617
+7132   1236619422
+7131   1949936858
+7130   1227838520
+7129   1474813775
+7128   313746216
+7127   1479017151
+7126   194922554
+7125   1630616041
+7124   156235025
+7123   1546288472
+7122   1650779589
+7121   877265446
+7120   593132489
+7119   409890807
+7118   280323555
+7117   726218944
+7116   1513518584
+7115   1905833916
+7114   716507562
+7113   1011959350
+7112   1185706302
+7111   766391958
+7110   1989454497
+7109   497452383
+7108   836830515
+7107   252939171
+7106   1646072630
+7105   1676307146
+7104   147231471
+7103   1657303980
+7102   836214097
+7101   1909289294
+7100   1636190642
+7099   1896305017
+7098   1279891221
+7097   830613823
+7096   1322782126
+7095   796117730
+7094   2127320099
+7093   1426453227
+7092   953155983
+7091   1240888782
+7090   596941890
+7089   2014397193
+7088   1043855871
+7087   521973287
+7086   9711382
+7085   501559233
+7084   720127613
+7083   2097599251
+7082   1169988501
+7081   688253919
+7080   2077045091
+7079   1736515325
+7078   998863400
+7077   1308007016
+7076   105707700
+7075   2136252298
+7074   840093049
+7073   385425824
+7072   21113338
+7071   1087392728
+7070   629398073
+7069   805576819
+7068   573522891
+7067   483773490
+7066   850777371
+7065   2043812546
+7064   1990445395
+7063   886431317
+7062   829511337
+7061   1086242438
+7060   197032910
+7059   74968603
+7058   2004685811
+7057   542296638
+7056   1949329322
+7055   59595778
+7054   1479054380
+7053   31873694
+7052   20554160
+7051   1580956824
+7050   1836874167
+7049   769038075
+7048   1630807625
+7047   1010094750
+7046   467913967
+7045   1867765524
+7044   2115138959
+7043   1900183969
+7042   1903511399
+7041   1363020167
+7040   513869837
+7039   145624583
+7038   2102283095
+7037   677193992
+7036   640811743
+7035   2111829702
+7034   1214301209
+7033   904202957
+7032   689398407
+7031   754542734
+7030   1229040275
+7029   1802219920
+7028   273122929
+7027   1945090032
+7026   1210725906
+7025   1917455628
+7024   39041618
+7023   2045581204
+7022   342483175
+7021   1398999733
+7020   2097632847
+7019   826779416
+7018   301124108
+7017   1910525749
+7016   1042439439
+7015   715213645
+7014   2111737773
+7013   752118792
+7012   1386314132
+7011   1757886816
+7010   1408220720
+7009   1984159492
+7008   1652296488
+7007   2137937041
+7006   1610376431
+7005   1884092433
+7004   1422431295
+7003   459758475
+7002   1822646330
+7001   1034662134
+7000   481419805
+6999   1431433890
+6998   591494014
+6997   503150949
+6996   1906048414
+6995   1312628350
+6994   1574972453
+6993   787525533
+6992   2095432005
+6991   1663187406
+6990   1097875625
+6989   187107098
+6988   1931823625
+6987   1733394110
+6986   1946271624
+6985   290320647
+6984   1476383161
+6983   353850957
+6982   1491381720
+6981   1549638288
+6980   105590328
+6979   1417767326
+6978   373783061
+6977   1915687702
+6976   715505746
+6975   1150617955
+6974   61446103
+6973   387769160
+6972   2125822318
+6971   391212440
+6970   443168120
+6969   2125752504
+6968   1672869124
+6967   1426349312
+6966   1075662144
+6965   1118522880
+6964   1364679993
+6963   2059268694
+6962   1837133556
+6961   1908324907
+6960   1878847429
+6959   1511965162
+6958   388319122
+6957   1641502978
+6956   257010949
+6955   1592420667
+6954   946422575
+6953   2074228521
+6952   248260629
+6951   73614393
+6950   1175855226
+6949   337386273
+6948   702261580
+6947   1370648754
+6946   1854241599
+6945   327736586
+6944   1172279285
+6943   1817717311
+6942   2092084688
+6941   69814
+6940   865826963
+6939   1164302455
+6938   1050090360
+6937   554346244
+6936   61669319
+6935   1163877097
+6934   1428872972
+6933   1603838734
+6932   180421265
+6931   325168394
+6930   1520005785
+6929   237344450
+6928   1254954213
+6927   943382103
+6926   695080403
+6925   330266076
+6924   1344160038
+6923   872808181
+6922   898373294
+6921   2058358003
+6920   1518836461
+6919   1952690120
+6918   630628322
+6917   374524994
+6916   198369469
+6915   36524288
+6914   383135545
+6913   1172209470
+6912   951890347
+6911   927782233
+6910   1097463102
+6909   311480719
+6908   1102633136
+6907   2033696910
+6906   1272956920
+6905   605314233
+6904   983455832
+6903   1103704578
+6902   83832949
+6901   2090560463
+6900   1217697829
+6899   576623682
+6898   1689747695
+6897   924688136
+6896   1746705713
+6895   1969755870
+6894   1579376430
+6893   1433285682
+6892   1501455368
+6891   1093166822
+6890   1427729681
+6889   1144311467
+6888   1754320651
+6887   594104033
+6886   2138873096
+6885   1173643646
+6884   1232117589
+6883   1602836960
+6882   74746368
+6881   640409628
+6880   1972632745
+6879   1211249840
+6878   1186007447
+6877   497318902
+6876   1050241078
+6875   169252342
+6874   521481284
+6873   1040379017
+6872   2033490397
+6871   1654692915
+6870   400812768
+6869   293009692
+6868   977401617
+6867   1867475473
+6866   1492795354
+6865   313420030
+6864   468300502
+6863   486209608
+6862   5556001
+6861   357143900
+6860   1486329818
+6859   833625648
+6858   1152922019
+6857   580677005
+6856   1509470092
+6855   536036136
+6854   1098897278
+6853   591707961
+6852   1777687863
+6851   1010980176
+6850   1601885828
+6849   1475313842
+6848   161008761
+6847   1016755105
+6846   2123321266
+6845   9862061
+6844   283245593
+6843   1014272017
+6842   639566249
+6841   1740480704
+6840   677291298
+6839   680820943
+6838   947697986
+6837   663981586
+6836   1399174971
+6835   1006585746
+6834   307864029
+6833   111156601
+6832   1147363437
+6831   1319414001
+6830   1351705529
+6829   905652813
+6828   1471639203
+6827   616885883
+6826   1629263374
+6825   917762131
+6824   905831920
+6823   87917102
+6822   1137305780
+6821   302374021
+6820   849971414
+6819   585130723
+6818   1499476224
+6817   151146700
+6816   733509512
+6815   1109049248
+6814   1517779460
+6813   690248536
+6812   336980719
+6811   2106228954
+6810   792782718
+6809   13309711
+6808   1429129620
+6807   2088595887
+6806   356117557
+6805   1288018369
+6804   2006705957
+6803   1135933676
+6802   906934720
+6801   241710624
+6800   1995258445
+6799   734819646
+6798   1423873087
+6797   553877072
+6796   1858537610
+6795   1541346272
+6794   1927939999
+6793   603457899
+6792   1385429336
+6791   552175057
+6790   950381444
+6789   698824714
+6788   1999104858
+6787   390426976
+6786   780850887
+6785   43260976
+6784   772068529
+6783   1559034154
+6782   2044949466
+6781   323671008
+6780   677099334
+6779   851670479
+6778   1804675802
+6777   141111250
+6776   81889930
+6775   1367667528
+6774   381083649
+6773   1764995333
+6772   1288158879
+6771   172115073
+6770   965321185
+6769   1441381373
+6768   1023765684
+6767   2030010463
+6766   773420721
+6765   1255079711
+6764   155916936
+6763   1375764941
+6762   1800560103
+6761   686604621
+6760   700553847
+6759   559954468
+6758   2065457475
+6757   1955843882
+6756   1765842095
+6755   1369300381
+6754   145795158
+6753   448397521
+6752   881934820
+6751   1193278987
+6750   666478853
+6749   535988083
+6748   769780548
+6747   437008274
+6746   1907511249
+6745   464378245
+6744   79508649
+6743   208968576
+6742   799674148
+6741   1994261153
+6740   1295833037
+6739   1082794370
+6738   667960652
+6737   1916169621
+6736   1874093527
+6735   1545139427
+6734   1602003256
+6733   1616795962
+6732   675211094
+6731   1240605634
+6730   768630794
+6729   892193612
+6728   941596021
+6727   696157094
+6726   1810048724
+6725   1317444574
+6724   487365560
+6723   1099999819
+6722   1929402315
+6721   345946737
+6720   423498438
+6719   229470579
+6718   775960482
+6717   305402303
+6716   357499624
+6715   1698542673
+6714   1812187745
+6713   232731144
+6712   1060619186
+6711   1864363426
+6710   1326300501
+6709   1527147064
+6708   1356184491
+6707   1270304873
+6706   314166365
+6705   257297564
+6704   869928333
+6703   361397621
+6702   848165168
+6701   1930501130
+6700   299009613
+6699   72473700
+6698   1229628536
+6697   1771635095
+6696   208791533
+6695   710048905
+6694   1535525906
+6693   141418823
+6692   676501380
+6691   1699931736
+6690   1717469902
+6689   118096135
+6688   2019454603
+6687   1224901457
+6686   640698205
+6685   124768480
+6684   637923486
+6683   2095307967
+6682   1053914291
+6681   1680955770
+6680   508178935
+6679   55995628
+6678   1212980699
+6677   1098886926
+6676   400376540
+6675   2100252391
+6674   1556616044
+6673   1086910851
+6672   62388008
+6671   775691467
+6670   700872594
+6669   674858165
+6668   2011165815
+6667   519579630
+6666   236109189
+6665   67372710
+6664   33547525
+6663   1983077818
+6662   571432569
+6661   558405245
+6660   1827960781
+6659   492568445
+6658   1624881578
+6657   1894686122
+6656   586977971
+6655   692873886
+6654   1218337837
+6653   1104451364
+6652   1587129032
+6651   997918663
+6650   467975070
+6649   1556775656
+6648   1803102736
+6647   1260211956
+6646   1689754530
+6645   1460949337
+6644   2037864383
+6643   780924577
+6642   386038257
+6641   1535013491
+6640   912009300
+6639   181292963
+6638   438748976
+6637   1943793105
+6636   486032105
+6635   400515018
+6634   1643423789
+6633   1622625928
+6632   155117037
+6631   78864124
+6630   1081007315
+6629   2080758306
+6628   2053074122
+6627   932007692
+6626   676348285
+6625   1630010254
+6624   1253228501
+6623   220419174
+6622   636476294
+6621   30353376
+6620   1342299575
+6619   1355246762
+6618   2014504774
+6617   342153399
+6616   1369831221
+6615   908829953
+6614   1074911080
+6613   502850892
+6612   2016398924
+6611   204745293
+6610   1096264514
+6609   1115699843
+6608   1842744506
+6607   38233958
+6606   300369316
+6605   1010889825
+6604   245397981
+6603   1564559665
+6602   541618613
+6601   221842379
+6600   173273650
+6599   148999623
+6598   1404410021
+6597   423063867
+6596   1826262838
+6595   455929110
+6594   993533960
+6593   1222875125
+6592   1025603247
+6591   1428713179
+6590   163332249
+6589   1000146176
+6588   2132899189
+6587   1105674821
+6586   1414725967
+6585   866980329
+6584   1039914676
+6583   870165786
+6582   1554070025
+6581   900699081
+6580   509484435
+6579   1058030556
+6578   815330527
+6577   831854680
+6576   1940319625
+6575   883293299
+6574   469271212
+6573   23555602
+6572   1391286015
+6571   392618990
+6570   964916005
+6569   1897693430
+6568   470220432
+6567   948480911
+6566   1577013555
+6565   603387713
+6564   1577809511
+6563   1712304429
+6562   1059542876
+6561   25457071
+6560   1443297638
+6559   1205141076
+6558   1732903857
+6557   1265918860
+6556   65760145
+6555   544560180
+6554   1460393951
+6553   139215595
+6552   360681351
+6551   496039469
+6550   85368553
+6549   1825113403
+6548   1265194579
+6547   2079520876
+6546   362583468
+6545   1916764023
+6544   1639490932
+6543   76652222
+6542   1206123244
+6541   1641076232
+6540   2069882205
+6539   16435094
+6538   320679875
+6537   2014316367
+6536   1518155048
+6535   2012192774
+6534   1691328485
+6533   1552352439
+6532   269006791
+6531   2001885448
+6530   440036862
+6529   177378777
+6528   1139380931
+6527   1188343676
+6526   1953008557
+6525   2074028197
+6524   183878829
+6523   964354482
+6522   53847042
+6521   683051596
+6520   1378328537
+6519   153331325
+6518   1462529935
+6517   1495914204
+6516   440029944
+6515   285931245
+6514   710640778
+6513   2145898347
+6512   154253665
+6511   1189688150
+6510   1320396357
+6509   55565838
+6508   645763694
+6507   455970749
+6506   322987882
+6505   2113286256
+6504   1743185983
+6503   1836926685
+6502   1112315577
+6501   91628013
+6500   862504517
+6499   1399176834
+6498   371853868
+6497   1212836381
+6496   1004464847
+6495   988654074
+6494   2020181155
+6493   1648310881
+6492   1733509593
+6491   2047999365
+6490   1368005309
+6489   2029897981
+6488   1860785028
+6487   1176598689
+6486   785273426
+6485   441615245
+6484   131677580
+6483   1668436276
+6482   825501990
+6481   98687827
+6480   543924455
+6479   864425607
+6478   1880061603
+6477   679961086
+6476   860268414
+6475   633544845
+6474   1000970679
+6473   1651557969
+6472   974422168
+6471   1860622391
+6470   1867257793
+6469   1797151783
+6468   394711987
+6467   1530683442
+6466   1340138874
+6465   1503637613
+6464   1402628129
+6463   2119665438
+6462   280305572
+6461   1851095260
+6460   187214336
+6459   191406619
+6458   1244624555
+6457   1419169783
+6456   1044921109
+6455   1264320797
+6454   1763596902
+6453   32989753
+6452   1124511821
+6451   2108560031
+6450   366109871
+6449   2011447017
+6448   4157193
+6447   1246516758
+6446   1826474054
+6445   1356194093
+6444   1806606325
+6443   1287831936
+6442   1931783824
+6441   1324754032
+6440   1465910404
+6439   336574351
+6438   457012909
+6437   1038558021
+6436   128055312
+6435   1367957083
+6434   1223332041
+6433   1699016517
+6432   1932451102
+6431   88898953
+6430   606470705
+6429   915528201
+6428   1293969158
+6427   2127787405
+6426   1803056529
+6425   1011931355
+6424   139808976
+6423   1802520519
+6422   1814363530
+6421   1260548451
+6420   2104402838
+6419   1267076761
+6418   184972963
+6417   795446748
+6416   1587394080
+6415   538642118
+6414   1571893916
+6413   481852293
+6412   1969405180
+6411   1595209473
+6410   867741123
+6409   427352382
+6408   208519038
+6407   1236539474
+6406   1962709628
+6405   576522443
+6404   1582989629
+6403   1134433088
+6402   1092545812
+6401   1016922901
+6400   942413442
+6399   626166947
+6398   1259955320
+6397   282037803
+6396   1987978429
+6395   536009
+6394   1345051473
+6393   1026744173
+6392   1845601329
+6391   547286768
+6390   1075575488
+6389   1308956090
+6388   1827166329
+6387   1793814493
+6386   1371036479
+6385   1105541787
+6384   716720585
+6383   2124168091
+6382   1761594818
+6381   1542052798
+6380   1386690435
+6379   1778685297
+6378   612126402
+6377   1779480243
+6376   1801033492
+6375   828276540
+6374   1631460278
+6373   566066728
+6372   192019645
+6371   466378865
+6370   1904451229
+6369   660375639
+6368   785672166
+6367   1259419310
+6366   1084469977
+6365   961234256
+6364   302418328
+6363   797764705
+6362   2098652332
+6361   536645239
+6360   867604087
+6359   1429244643
+6358   2085403258
+6357   721624541
+6356   1077093907
+6355   1394352036
+6354   1491430617
+6353   1322151435
+6352   737477656
+6351   2130393169
+6350   929926396
+6349   1754693839
+6348   2125135452
+6347   1931333509
+6346   148019965
+6345   1234966764
+6344   636256895
+6343   1165081413
+6342   809099147
+6341   1679127654
+6340   1828190346
+6339   645031918
+6338   1723389310
+6337   1971921558
+6336   957000982
+6335   286705272
+6334   1010065571
+6333   1913256736
+6332   2077644265
+6331   669407689
+6330   598725629
+6329   145979546
+6328   352150736
+6327   691051222
+6326   1377677572
+6325   1902426120
+6324   656874380
+6323   1508521096
+6322   392225039
+6321   1130267464
+6320   5257716
+6319   1146076534
+6318   1606673874
+6317   890168688
+6316   1295076614
+6315   1130422199
+6314   425867616
+6313   1104612889
+6312   1484374715
+6311   164067229
+6310   2103221992
+6309   2003752436
+6308   1835514584
+6307   1436684037
+6306   961855987
+6305   1191227894
+6304   356544655
+6303   340657882
+6302   1314531107
+6301   1931664719
+6300   317256953
+6299   2055158055
+6298   915785622
+6297   597208264
+6296   34176841
+6295   2016640123
+6294   1510201080
+6293   1674090564
+6292   1503263380
+6291   1393632153
+6290   1671077238
+6289   1262572676
+6288   1998483568
+6287   476251675
+6286   464301072
+6285   190463725
+6284   1793531132
+6283   261800387
+6282   1148874545
+6281   1628105927
+6280   476036293
+6279   666537954
+6278   1041896449
+6277   644286690
+6276   1080139382
+6275   621198104
+6274   2024180434
+6273   572363583
+6272   23400929
+6271   1406856700
+6270   1015879097
+6269   1867532337
+6268   2020981213
+6267   1046629146
+6266   1234490831
+6265   507569925
+6264   513376743
+6263   116568927
+6262   3013326
+6261   240690704
+6260   1542632233
+6259   1194825563
+6258   798271604
+6257   1808019842
+6256   830204190
+6255   202500684
+6254   1189072828
+6253   165425205
+6252   1933247742
+6251   482336590
+6250   586209478
+6249   1979233251
+6248   1733882220
+6247   420698344
+6246   767589903
+6245   507775799
+6244   597797175
+6243   617323734
+6242   1703968134
+6241   303352240
+6240   1533359135
+6239   2116733599
+6238   633041505
+6237   1513411288
+6236   533252403
+6235   1117921904
+6234   504556599
+6233   272686039
+6232   721420342
+6231   955671411
+6230   1589902748
+6229   1882096038
+6228   364621372
+6227   595770919
+6226   618947014
+6225   664778985
+6224   416736590
+6223   706736238
+6222   1726699375
+6221   2101498139
+6220   895938018
+6219   165511133
+6218   1211643347
+6217   1226106421
+6216   1970384817
+6215   150266169
+6214   951291313
+6213   294444934
+6212   1231448247
+6211   1734718183
+6210   1817794383
+6209   19947847
+6208   1583481196
+6207   1662603249
+6206   1008854688
+6205   260566363
+6204   396501561
+6203   1696368836
+6202   830266939
+6201   986807952
+6200   591050038
+6199   994131828
+6198   1263149024
+6197   1847326035
+6196   179034329
+6195   2059694424
+6194   1085563257
+6193   462722098
+6192   1958281867
+6191   1561188242
+6190   889854792
+6189   1817315245
+6188   342609964
+6187   1061377178
+6186   274815108
+6185   1675939883
+6184   1066301570
+6183   1364056778
+6182   624134199
+6181   1211500400
+6180   151236987
+6179   155191133
+6178   1158576806
+6177   1322914832
+6176   1266101688
+6175   1459969500
+6174   1577783072
+6173   1557177257
+6172   1105318798
+6171   1983618759
+6170   1871142575
+6169   891207651
+6168   815097499
+6167   1350938248
+6166   761762778
+6165   1863795879
+6164   101412556
+6163   1671858663
+6162   1720350954
+6161   140966622
+6160   1218578278
+6159   1975961262
+6158   1542500137
+6157   814153729
+6156   2142559255
+6155   1058241978
+6154   1051805683
+6153   2002284818
+6152   1212819791
+6151   468943066
+6150   52923593
+6149   975805802
+6148   1036573093
+6147   1846090954
+6146   1892615408
+6145   1856408078
+6144   354650702
+6143   1741647961
+6142   1833518330
+6141   214111147
+6140   1168521260
+6139   520204327
+6138   129444873
+6137   1098785268
+6136   1249525692
+6135   1237387762
+6134   143444924
+6133   2107929582
+6132   453280385
+6131   1891873340
+6130   745950132
+6129   404424549
+6128   1980885654
+6127   484258159
+6126   1909831693
+6125   140274437
+6124   1992905835
+6123   582862617
+6122   1949361225
+6121   237013988
+6120   1579853620
+6119   354316287
+6118   1230674042
+6117   1327648663
+6116   1491440252
+6115   150967447
+6114   22889748
+6113   140539555
+6112   573126701
+6111   1313314003
+6110   84666274
+6109   69735992
+6108   1418162283
+6107   1039540758
+6106   955340343
+6105   1289079757
+6104   784107377
+6103   399055232
+6102   1361979450
+6101   48855836
+6100   2058471334
+6099   261691973
+6098   642076503
+6097   1840611217
+6096   638835972
+6095   1326969076
+6094   338396860
+6093   1755891846
+6092   1150492645
+6091   1595044938
+6090   1153823594
+6089   252204957
+6088   1010359682
+6087   1079706594
+6086   1304758914
+6085   1350900697
+6084   1725324394
+6083   857059393
+6082   55873281
+6081   503390709
+6080   2042635368
+6079   1192609163
+6078   1261879296
+6077   129082525
+6076   255433381
+6075   556285111
+6074   2074583955
+6073   735251540
+6072   488067546
+6071   1100287477
+6070   1554262981
+6069   217860116
+6068   1770339648
+6067   1462591075
+6066   1502214357
+6065   1030427774
+6064   176476431
+6063   890835570
+6062   602068252
+6061   898287687
+6060   584685255
+6059   74117000
+6058   1094929691
+6057   1806942633
+6056   1501865848
+6055   447699521
+6054   1295027416
+6053   1221933685
+6052   961907673
+6051   1010747765
+6050   1388995060
+6049   1913552842
+6048   937175782
+6047   705594185
+6046   201982218
+6045   1667665489
+6044   68217565
+6043   974296478
+6042   1328472207
+6041   270207429
+6040   1477431476
+6039   91671905
+6038   863129407
+6037   739911874
+6036   1286114644
+6035   611378787
+6034   428359522
+6033   1425672391
+6032   306150314
+6031   527951252
+6030   1950841644
+6029   925226270
+6028   719734800
+6027   647230170
+6026   511915216
+6025   279932162
+6024   1633275495
+6023   284279651
+6022   1980422273
+6021   1195838479
+6020   73571983
+6019   683400875
+6018   1711570624
+6017   1416993941
+6016   637376619
+6015   1375169387
+6014   339193281
+6013   1945493784
+6012   1644348650
+6011   1236800302
+6010   1554561670
+6009   737519602
+6008   953040909
+6007   251750619
+6006   311552352
+6005   2007925901
+6004   305228473
+6003   2047891918
+6002   1622314395
+6001   1528407692
+6000   1955700100
+5999   1303611474
+5998   413311054
+5997   439802637
+5996   1161438322
+5995   227635565
+5994   446993537
+5993   437437016
+5992   210707667
+5991   1297021397
+5990   1631751502
+5989   804061690
+5988   46024256
+5987   336401237
+5986   1077800659
+5985   839366483
+5984   1878304385
+5983   1249876627
+5982   390932113
+5981   906829048
+5980   283759393
+5979   1302811051
+5978   425967249
+5977   1092598656
+5976   2094005794
+5975   411144082
+5974   385611506
+5973   924304429
+5972   92191818
+5971   318702920
+5970   1115096638
+5969   1515897462
+5968   142173152
+5967   185675488
+5966   2140292748
+5965   724001306
+5964   16927898
+5963   1297455788
+5962   953169162
+5961   1554129625
+5960   1250997141
+5959   1295350265
+5958   1873744679
+5957   1354141420
+5956   605580499
+5955   1975407680
+5954   448434370
+5953   971475337
+5952   966117234
+5951   1235604710
+5950   480861798
+5949   1338644385
+5948   1356288904
+5947   14823167
+5946   706987150
+5945   1169701365
+5944   318952264
+5943   66908585
+5942   1956691439
+5941   723778003
+5940   176529768
+5939   929421149
+5938   1523088362
+5937   1565655494
+5936   168747590
+5935   842836960
+5934   1918315792
+5933   610281921
+5932   46458646
+5931   1805302544
+5930   1827868594
+5929   2044339369
+5928   689769766
+5927   2045820647
+5926   905707050
+5925   1781588810
+5924   1009290445
+5923   1360313307
+5922   490613539
+5921   1774956497
+5920   2026799454
+5919   466038631
+5918   631657235
+5917   186587539
+5916   1843354550
+5915   640078564
+5914   1360493574
+5913   1742657909
+5912   2037862465
+5911   1027270289
+5910   1348173289
+5909   758357922
+5908   760673559
+5907   680251402
+5906   1794823350
+5905   1705949317
+5904   796378313
+5903   113013247
+5902   929896975
+5901   149602925
+5900   1115532778
+5899   1929531595
+5898   1138632318
+5897   1055664604
+5896   1036530201
+5895   1692877391
+5894   1290975271
+5893   1381817596
+5892   1480997501
+5891   24574907
+5890   1143299262
+5889   1840211915
+5888   770167729
+5887   2139062318
+5886   973577612
+5885   100696641
+5884   749699747
+5883   333223285
+5882   394484620
+5881   1279504542
+5880   266596730
+5879   667921886
+5878   1111018220
+5877   1202207889
+5876   2031356737
+5875   1681810102
+5874   776052342
+5873   646775388
+5872   1144964117
+5871   1147849028
+5870   1158454255
+5869   59868174
+5868   893001393
+5867   1593238575
+5866   1912172981
+5865   1802196253
+5864   211879889
+5863   1266400363
+5862   238518333
+5861   1788269234
+5860   1401890826
+5859   1151720592
+5858   866634302
+5857   669471087
+5856   1389362571
+5855   640354327
+5854   1853695669
+5853   1617678853
+5852   66626554
+5851   1874046381
+5850   168486322
+5849   1211872489
+5848   784048797
+5847   1576691766
+5846   426155547
+5845   1384581349
+5844   536845985
+5843   1775686962
+5842   1635804781
+5841   1085095942
+5840   254847634
+5839   1712699327
+5838   295178841
+5837   1238288788
+5836   1381358686
+5835   645772617
+5834   1563677920
+5833   571094303
+5832   2011993185
+5831   1234281389
+5830   921634932
+5829   732419739
+5828   1909841669
+5827   226279975
+5826   963259066
+5825   1919167366
+5824   573727773
+5823   2127132936
+5822   1449192531
+5821   1002237713
+5820   1089997584
+5819   739278204
+5818   785716942
+5817   1546951096
+5816   1039845780
+5815   797952232
+5814   1896260216
+5813   1599233691
+5812   1520839328
+5811   2070589101
+5810   789917101
+5809   1164042494
+5808   331340641
+5807   1796889872
+5806   1822094516
+5805   810264383
+5804   781263080
+5803   329396530
+5802   1796943019
+5801   1279573446
+5800   1471923368
+5799   695354957
+5798   1916644321
+5797   2138157951
+5796   1800035850
+5795   983609778
+5794   469974835
+5793   1718973707
+5792   1037135352
+5791   709914327
+5790   216520771
+5789   1690530135
+5788   1846916071
+5787   2135248357
+5786   1798174528
+5785   1588095737
+5784   1424596552
+5783   1973154762
+5782   809316590
+5781   356796833
+5780   1739248460
+5779   1140510877
+5778   1489431626
+5777   1668559906
+5776   1015626791
+5775   1492697985
+5774   1160805012
+5773   1649173282
+5772   1004956810
+5771   1101588062
+5770   1510412773
+5769   1481249065
+5768   1042802755
+5767   933034543
+5766   1668183116
+5765   81062142
+5764   2093958074
+5763   1907544156
+5762   1502452936
+5761   1494088864
+5760   1010481903
+5759   228756062
+5758   2039839255
+5757   258820334
+5756   710651805
+5755   1972503414
+5754   778779147
+5753   1067799719
+5752   233906302
+5751   1816289361
+5750   1014848855
+5749   70688553
+5748   124884085
+5747   2144217289
+5746   507754894
+5745   1513937157
+5744   487741175
+5743   59216950
+5742   138760509
+5741   1671191392
+5740   58785307
+5739   577378230
+5738   1960549597
+5737   961740612
+5736   986560117
+5735   1908122608
+5734   726092854
+5733   599869209
+5732   897062252
+5731   1273696874
+5730   1601733257
+5729   751661569
+5728   1665587905
+5727   67335841
+5726   1627524834
+5725   1790335734
+5724   1738597111
+5723   1109973434
+5722   52950864
+5721   163217749
+5720   1691405275
+5719   1018115214
+5718   1710417307
+5717   758430576
+5716   1656476113
+5715   448537944
+5714   1375176647
+5713   964033431
+5712   431643
+5711   1708865927
+5710   1858125443
+5709   1244528342
+5708   1738301761
+5707   52426989
+5706   235647758
+5705   386690907
+5704   1011060355
+5703   1599879628
+5702   1145619600
+5701   145400683
+5700   1755592617
+5699   1534397416
+5698   1271620383
+5697   2022735819
+5696   476222377
+5695   517551400
+5694   1737384870
+5693   1575379362
+5692   1566051807
+5691   1182319298
+5690   600284090
+5689   932974699
+5688   1509122748
+5687   1261879362
+5686   1530737577
+5685   692442682
+5684   448106301
+5683   1813794368
+5682   1253391636
+5681   903386948
+5680   2118047814
+5679   1805698453
+5678   1008880584
+5677   1351610853
+5676   1188850282
+5675   783251777
+5674   1388554955
+5673   865659672
+5672   1991770659
+5671   1758705831
+5670   1021263948
+5669   1880340446
+5668   1058175039
+5667   754068983
+5666   285350949
+5665   1048326663
+5664   1098983241
+5663   555065572
+5662   975095272
+5661   633077108
+5660   1820680197
+5659   1485888375
+5658   1549720770
+5657   816680066
+5656   813773061
+5655   1864426857
+5654   1586534694
+5653   1692203001
+5652   1843230201
+5651   1595176830
+5650   2041990012
+5649   766436961
+5648   616848171
+5647   225628807
+5646   2110539546
+5645   323190609
+5644   938964766
+5643   1777332772
+5642   1991879372
+5641   111430213
+5640   700530792
+5639   267194965
+5638   1594989497
+5637   9848376
+5636   1802569390
+5635   1877769025
+5634   73231390
+5633   465906133
+5632   881869022
+5631   1636690545
+5630   1230839986
+5629   1004000131
+5628   672115314
+5627   1832777561
+5626   1377629019
+5625   1269053708
+5624   21196655
+5623   2138841512
+5622   1797696637
+5621   1076793240
+5620   978328659
+5619   1816361205
+5618   803381063
+5617   293657562
+5616   1434147689
+5615   333206774
+5614   478794885
+5613   827534552
+5612   1076801979
+5611   1724684407
+5610   663924364
+5609   690682416
+5608   612109223
+5607   1864704120
+5606   2084100633
+5605   1336663257
+5604   995900002
+5603   584024493
+5602   1382549795
+5601   2025352539
+5600   964575230
+5599   1545546073
+5598   1773854760
+5597   1550545254
+5596   1811580905
+5595   1386271155
+5594   1618840719
+5593   1091887063
+5592   1160512853
+5591   2128819080
+5590   273412177
+5589   684671097
+5588   382213516
+5587   470174289
+5586   1962346325
+5585   606613136
+5584   1403888442
+5583   901594125
+5582   163610188
+5581   386119563
+5580   1112575184
+5579   946703892
+5578   754065431
+5577   1422929614
+5576   868804117
+5575   1500076140
+5574   2101597110
+5573   1118031111
+5572   1766932911
+5571   1984487370
+5570   251497779
+5569   1561513624
+5568   1881448815
+5567   387583604
+5566   2079188183
+5565   719693842
+5564   225758302
+5563   1637505287
+5562   818474885
+5561   475841756
+5560   1746605564
+5559   1950721536
+5558   869808420
+5557   1923084027
+5556   1213769494
+5555   1060752199
+5554   443002948
+5553   1017768879
+5552   1936502589
+5551   1364389943
+5550   1779537780
+5549   1837129218
+5548   77899775
+5547   1401472939
+5546   1468816152
+5545   1898256654
+5544   1880626877
+5543   117109740
+5542   866533332
+5541   205419287
+5540   103038554
+5539   2011397822
+5538   1629809088
+5537   1161754973
+5536   161825302
+5535   441682896
+5534   2048702605
+5533   1897400194
+5532   2038383371
+5531   1015236997
+5530   1753516984
+5529   1971005184
+5528   736952042
+5527   1956539868
+5526   1480081079
+5525   196000615
+5524   1271733258
+5523   1226096653
+5522   1385714747
+5521   99373370
+5520   1286490168
+5519   378064841
+5518   368313066
+5517   327126769
+5516   1668329710
+5515   1351706412
+5514   1031723321
+5513   1675207590
+5512   14071185
+5511   1002619158
+5510   723093846
+5509   1088767229
+5508   1849572520
+5507   1188126192
+5506   1260536016
+5505   411908755
+5504   550783173
+5503   1033465608
+5502   143883210
+5501   67378186
+5500   278284955
+5499   1944460763
+5498   490924105
+5497   540951427
+5496   684806610
+5495   253984426
+5494   957769515
+5493   1172359888
+5492   2087090132
+5491   1007649906
+5490   1878543952
+5489   959363399
+5488   857218779
+5487   1164090302
+5486   1442887095
+5485   2140605768
+5484   1337635226
+5483   29104163
+5482   952113743
+5481   1072787604
+5480   1300530285
+5479   1682451302
+5478   1975714861
+5477   1437663765
+5476   637343018
+5475   227070408
+5474   268025545
+5473   483404987
+5472   755180653
+5471   346906095
+5470   1723937729
+5469   1884817176
+5468   1259654153
+5467   236939679
+5466   1730665559
+5465   1659930370
+5464   314377941
+5463   2097603257
+5462   1441299584
+5461   1127726733
+5460   150431127
+5459   714453649
+5458   1663959952
+5457   864096659
+5456   1973938724
+5455   1413782932
+5454   1188492024
+5453   264847622
+5452   876057526
+5451   1417146089
+5450   1244556390
+5449   2010350168
+5448   1045108284
+5447   1748644453
+5446   1169638220
+5445   153938031
+5444   1619373403
+5443   2068603098
+5442   906950906
+5441   1017847125
+5440   1234735589
+5439   1486998049
+5438   154151616
+5437   1747207431
+5436   2070045386
+5435   1780545950
+5434   218630786
+5433   1334134856
+5432   1947172129
+5431   726845934
+5430   1611250429
+5429   1433818116
+5428   887998573
+5427   250177020
+5426   1823088282
+5425   1709091101
+5424   537725406
+5423   1918829583
+5422   1167774880
+5421   1013191005
+5420   372037805
+5419   1643395585
+5418   840711948
+5417   891170252
+5416   129271050
+5415   1248518770
+5414   1394470773
+5413   601526278
+5412   833867508
+5411   1567436504
+5410   863695508
+5409   1635011806
+5408   1564436311
+5407   521089314
+5406   1528576645
+5405   735910529
+5404   1980857469
+5403   1639268499
+5402   1870368075
+5401   513354013
+5400   1986331009
+5399   1361073409
+5398   1758213482
+5397   1326391120
+5396   1859935262
+5395   2051742347
+5394   541316221
+5393   1672018048
+5392   1546791778
+5391   1671862943
+5390   172479057
+5389   1628351200
+5388   1514124534
+5387   1739676826
+5386   1644183127
+5385   1675228420
+5384   414651261
+5383   1974517917
+5382   1885314417
+5381   1346339350
+5380   3000193
+5379   342606194
+5378   106435160
+5377   828525782
+5376   687715493
+5375   2036791794
+5374   1013026102
+5373   1467503456
+5372   1800421138
+5371   509294666
+5370   902624179
+5369   659939889
+5368   1648621795
+5367   1853954783
+5366   785074898
+5365   187917213
+5364   504950568
+5363   1016936926
+5362   1499538991
+5361   2065924226
+5360   157738408
+5359   580285878
+5358   2131651721
+5357   1986379762
+5356   1325025565
+5355   1817148858
+5354   1937397651
+5353   1215795559
+5352   1971517724
+5351   1542708223
+5350   1239904190
+5349   1321958059
+5348   1802374349
+5347   217127014
+5346   1962983328
+5345   1367695685
+5344   236370655
+5343   503731435
+5342   564879277
+5341   1140481249
+5340   1008156519
+5339   1196153044
+5338   2022348638
+5337   1460704581
+5336   1349004214
+5335   1915621620
+5334   835861870
+5333   586509990
+5332   859198518
+5331   919253113
+5330   2081756152
+5329   318842294
+5328   1402743961
+5327   314502863
+5326   48982111
+5325   109230006
+5324   1993114782
+5323   394689428
+5322   2123375017
+5321   649559665
+5320   1887817521
+5319   1022777175
+5318   1506458379
+5317   434678664
+5316   2128240007
+5315   1459251892
+5314   802816408
+5313   1243373054
+5312   1643058564
+5311   1516209881
+5310   1265616259
+5309   1694935586
+5308   1994632477
+5307   106727018
+5306   624842711
+5305   762494224
+5304   1056423102
+5303   2064092405
+5302   652237486
+5301   540356223
+5300   1663992799
+5299   1767253289
+5298   269860183
+5297   1293513955
+5296   468871729
+5295   1801776331
+5294   133338637
+5293   1343555117
+5292   654355554
+5291   1100597841
+5290   1290584934
+5289   1453138857
+5288   1042020816
+5287   47206486
+5286   1779345904
+5285   884866953
+5284   1963676976
+5283   1434090175
+5282   2125240443
+5281   2095606626
+5280   1669061051
+5279   1158889240
+5278   1070092874
+5277   1232138253
+5276   1197787564
+5275   708233954
+5274   110256738
+5273   516066878
+5272   400099605
+5271   1032467845
+5270   270496040
+5269   370478844
+5268   1298381559
+5267   615567500
+5266   1160175318
+5265   1272800260
+5264   1147420776
+5263   1180224443
+5262   52970183
+5261   1348700345
+5260   58577025
+5259   1243378447
+5258   1821276600
+5257   157153863
+5256   231013158
+5255   345255729
+5254   907110158
+5253   2015553998
+5252   1912512771
+5251   966351202
+5250   1025513751
+5249   436922798
+5248   2108585324
+5247   361858920
+5246   1121881515
+5245   681720686
+5244   308134349
+5243   1225272541
+5242   245570838
+5241   29620761
+5240   1881569933
+5239   1802412187
+5238   1357787173
+5237   25581299
+5236   1615630372
+5235   2127434523
+5234   1219830077
+5233   1946204079
+5232   1121647418
+5231   957075383
+5230   1674907393
+5229   2048906809
+5228   1012365289
+5227   1476020871
+5226   1397527353
+5225   362942807
+5224   580226606
+5223   2088242603
+5222   990040247
+5221   1475589973
+5220   1005249526
+5219   663654831
+5218   1462524930
+5217   1426864638
+5216   53724571
+5215   2044092622
+5214   436149847
+5213   278513587
+5212   1491186255
+5211   590642299
+5210   819317236
+5209   1855988634
+5208   186781815
+5207   1377836298
+5206   953234869
+5205   1816909941
+5204   1005787104
+5203   262754694
+5202   271296686
+5201   1220224257
+5200   2092193742
+5199   198886522
+5198   651379456
+5197   649422482
+5196   895794265
+5195   1456768398
+5194   1520386208
+5193   1252120280
+5192   1082993077
+5191   326385415
+5190   13065043
+5189   1725868536
+5188   609930260
+5187   1565915956
+5186   990714790
+5185   1922694631
+5184   552906367
+5183   1992991196
+5182   1606679999
+5181   1782681269
+5180   403860483
+5179   1588964585
+5178   902753765
+5177   517355522
+5176   372049194
+5175   690480175
+5174   1545613255
+5173   1933046495
+5172   318044600
+5171   72410164
+5170   568844801
+5169   1442771260
+5168   1450575905
+5167   1342094706
+5166   1276519921
+5165   1791157632
+5164   373775321
+5163   1194000793
+5162   1239055237
+5161   1504608188
+5160   1863938803
+5159   594632735
+5158   735153746
+5157   834719277
+5156   1013009589
+5155   1145207242
+5154   316014632
+5153   917708746
+5152   1589130713
+5151   17715414
+5150   879927504
+5149   2033988609
+5148   1216915391
+5147   212273589
+5146   1119225915
+5145   586486346
+5144   372435575
+5143   1473203091
+5142   1364201694
+5141   1022756988
+5140   769317907
+5139   1374233743
+5138   166251338
+5137   1806901920
+5136   968319385
+5135   82519128
+5134   552102395
+5133   1016650780
+5132   1477545638
+5131   644422502
+5130   769454442
+5129   1029219526
+5128   1729106794
+5127   1737430152
+5126   518704645
+5125   95300843
+5124   1703560177
+5123   298299218
+5122   37781242
+5121   1702625752
+5120   948283670
+5119   667653914
+5118   914762693
+5117   630429045
+5116   1987321662
+5115   1793506472
+5114   1369768300
+5113   1497162235
+5112   703885184
+5111   2137451599
+5110   856505649
+5109   1109899634
+5108   405914358
+5107   83732210
+5106   1254799525
+5105   2099152252
+5104   752457138
+5103   2055163540
+5102   247196338
+5101   448326112
+5100   1062799356
+5099   1179507938
+5098   510514881
+5097   1633805951
+5096   33869975
+5095   220405427
+5094   57519601
+5093   934425
+5092   1497499195
+5091   1517610975
+5090   787863058
+5089   317854625
+5088   827815900
+5087   1268739869
+5086   1408144393
+5085   490159426
+5084   1089621288
+5083   1379800348
+5082   640656586
+5081   1741469197
+5080   1731537241
+5079   772773439
+5078   2002583757
+5077   454245753
+5076   1478758719
+5075   1347119633
+5074   1851955914
+5073   304131026
+5072   992364184
+5071   1215172047
+5070   2085294879
+5069   1576477053
+5068   1145637963
+5067   290109454
+5066   1576286350
+5065   32935550
+5064   870389879
+5063   687392273
+5062   1360555014
+5061   1179644570
+5060   689795075
+5059   1666606837
+5058   1057193880
+5057   337656474
+5056   179118580
+5055   28344044
+5054   1996986488
+5053   1495635739
+5052   1795746755
+5051   2015366794
+5050   1886369088
+5049   1277291488
+5048   1441498368
+5047   655464124
+5046   749773487
+5045   1174627693
+5044   354755449
+5043   636783867
+5042   366319795
+5041   1563370778
+5040   69534084
+5039   1795185425
+5038   190703
+5037   1112702413
+5036   1567203222
+5035   888894076
+5034   819864183
+5033   1838228957
+5032   2145080846
+5031   1841431825
+5030   122450689
+5029   352138601
+5028   1487488257
+5027   1028849836
+5026   488153633
+5025   830966489
+5024   380080937
+5023   2129103342
+5022   1756750298
+5021   518455267
+5020   573868426
+5019   1230904964
+5018   527518001
+5017   266870675
+5016   300708675
+5015   112989620
+5014   808307897
+5013   938868318
+5012   567249783
+5011   718618018
+5010   1563180075
+5009   1104315318
+5008   227982202
+5007   1258780275
+5006   292838230
+5005   1876457913
+5004   891296878
+5003   1125916006
+5002   1715778268
+5001   1792942245
+5000   353943568
+4999   1241084501
+4998   2011468615
+4997   656521767
+4996   648768898
+4995   506533939
+4994   1221699839
+4993   2009109318
+4992   1555234915
+4991   525845334
+4990   2138420914
+4989   306997751
+4988   930196289
+4987   414528381
+4986   1606046425
+4985   1509324004
+4984   1693223485
+4983   89689879
+4982   1523171891
+4981   1610418112
+4980   490635816
+4979   304399800
+4978   811477088
+4977   499007937
+4976   367483397
+4975   1314405871
+4974   160679645
+4973   1245838280
+4972   771972438
+4971   474693766
+4970   1928957278
+4969   1844905448
+4968   592315603
+4967   1504934676
+4966   1582305576
+4965   787143228
+4964   1098782672
+4963   695854505
+4962   2018172052
+4961   1248237164
+4960   1743132692
+4959   1723892533
+4958   848434974
+4957   1568355933
+4956   868788544
+4955   1516356546
+4954   2133635761
+4953   82805372
+4952   1746537711
+4951   1218772091
+4950   798941024
+4949   2139111526
+4948   2084400051
+4947   1644554865
+4946   338328292
+4945   1269128764
+4944   542433433
+4943   1833469526
+4942   1464364650
+4941   1074550638
+4940   2029861811
+4939   424022602
+4938   262599872
+4937   1952656023
+4936   406152004
+4935   886451071
+4934   916454823
+4933   1998029156
+4932   1100205460
+4931   294279519
+4930   399802190
+4929   174776759
+4928   855103989
+4927   1479562075
+4926   1582203820
+4925   785983171
+4924   1917302483
+4923   914863669
+4922   1431347996
+4921   1754909832
+4920   1281855688
+4919   1301869807
+4918   1800783234
+4917   815271286
+4916   1102121432
+4915   652342414
+4914   1952247762
+4913   1615366443
+4912   1951091363
+4911   1040342048
+4910   811950766
+4909   77205788
+4908   17870598
+4907   1523632448
+4906   1036201199
+4905   555606496
+4904   1933729259
+4903   622175304
+4902   1598226966
+4901   925428701
+4900   1586659178
+4899   1067723762
+4898   740056587
+4897   69120817
+4896   1709743240
+4895   667340150
+4894   1502118823
+4893   162392651
+4892   1780491629
+4891   129478189
+4890   2101610246
+4889   466584402
+4888   199748375
+4887   1148440820
+4886   1010507172
+4885   1634238637
+4884   848734699
+4883   911905713
+4882   803415677
+4881   1873885574
+4880   1022471450
+4879   1435801965
+4878   1188488237
+4877   1609747750
+4876   1737386837
+4875   414025895
+4874   1104863178
+4873   1008300558
+4872   1182999773
+4871   530503203
+4870   185372113
+4869   1517538361
+4868   1505464170
+4867   72716437
+4866   714485642
+4865   1547350589
+4864   1034332169
+4863   1372640633
+4862   208266052
+4861   1313907227
+4860   2077213462
+4859   953169426
+4858   1603560877
+4857   712993386
+4856   299706121
+4855   98601459
+4854   830822960
+4853   1122332772
+4852   2036917911
+4851   1515097359
+4850   685397337
+4849   1560207348
+4848   1845898776
+4847   774462341
+4846   504884572
+4845   729086279
+4844   1378509770
+4843   574359974
+4842   822928444
+4841   1812945060
+4840   1172522681
+4839   112655676
+4838   803052718
+4837   2105597229
+4836   1185867915
+4835   1489328657
+4834   1339084536
+4833   1867908590
+4832   1442910819
+4831   1402580274
+4830   1857829997
+4829   1364220076
+4828   653463305
+4827   1504959418
+4826   2029654074
+4825   1324856996
+4824   209167196
+4823   1463209248
+4822   436935435
+4821   476710562
+4820   1816682231
+4819   2058418644
+4818   1055322776
+4817   1116812496
+4816   1543436219
+4815   2078008245
+4814   2053641483
+4813   1713048357
+4812   1549320941
+4811   710272768
+4810   1009892342
+4809   1214409099
+4808   1074271409
+4807   1461207709
+4806   766512693
+4805   1465442973
+4804   46417838
+4803   2083987910
+4802   10078593
+4801   78690742
+4800   749116968
+4799   352870579
+4798   1482049650
+4797   1476089957
+4796   1295792222
+4795   566444825
+4794   887921561
+4793   1879940281
+4792   1794010665
+4791   526000439
+4790   1568871434
+4789   699869735
+4788   514982424
+4787   1124798179
+4786   1210654661
+4785   1977871510
+4784   528687304
+4783   1343368715
+4782   703156015
+4781   334911842
+4780   1783485007
+4779   1696168280
+4778   447896406
+4777   1756312084
+4776   1414789871
+4775   830008430
+4774   1455364380
+4773   2115210743
+4772   1334870942
+4771   1804691662
+4770   744124740
+4769   1420510659
+4768   1204562004
+4767   915604825
+4766   588168395
+4765   1563335589
+4764   919917808
+4763   361921122
+4762   311068847
+4761   1094140930
+4760   11018014
+4759   444073255
+4758   1636698721
+4757   684594562
+4756   596110875
+4755   2014769594
+4754   1274715494
+4753   193775462
+4752   1707367356
+4751   1154471383
+4750   2034499083
+4749   27172923
+4748   281378409
+4747   1765371624
+4746   300947704
+4745   1447062776
+4744   1642621136
+4743   1798156366
+4742   1371086003
+4741   2061843930
+4740   600129657
+4739   1976003563
+4738   832342264
+4737   1788710063
+4736   2143170664
+4735   226247273
+4734   1252266741
+4733   1973260526
+4732   350903108
+4731   2014479240
+4730   1604925856
+4729   1473907100
+4728   1995446028
+4727   1769412775
+4726   1557362716
+4725   402335413
+4724   307402238
+4723   120244111
+4722   306760026
+4721   1680194433
+4720   873092974
+4719   269127459
+4718   1873708867
+4717   981799281
+4716   122750487
+4715   650274986
+4714   75976772
+4713   1728260854
+4712   1198026708
+4711   1542566088
+4710   1229501666
+4709   958903242
+4708   1980316546
+4707   606094991
+4706   536443322
+4705   169910138
+4704   2022827813
+4703   1385271149
+4702   368334670
+4701   1024479656
+4700   19033212
+4699   1982996729
+4698   2064028032
+4697   1593110615
+4696   1462010536
+4695   1437118604
+4694   95575386
+4693   774691453
+4692   1394634785
+4691   37632567
+4690   1953969214
+4689   2038777341
+4688   146376972
+4687   1223433881
+4686   905822508
+4685   541973281
+4684   1599731925
+4683   680894332
+4682   498759187
+4681   239123466
+4680   1709733190
+4679   623406675
+4678   422459920
+4677   1810406408
+4676   730750826
+4675   1298655821
+4674   1949059116
+4673   998348157
+4672   1366237936
+4671   532821588
+4670   1107935272
+4669   573406245
+4668   520986193
+4667   626909427
+4666   1497535228
+4665   687319083
+4664   42483819
+4663   57942819
+4662   968205887
+4661   1503341092
+4660   2038739243
+4659   730535333
+4658   1132954832
+4657   1751887338
+4656   1771185603
+4655   224928176
+4654   43214094
+4653   1360608459
+4652   1118644790
+4651   2022836160
+4650   1964147194
+4649   2046810429
+4648   2040139497
+4647   1271287747
+4646   2008830940
+4645   1879886317
+4644   2079901532
+4643   1416237527
+4642   2037896533
+4641   792831691
+4640   11835395
+4639   481025844
+4638   1223354665
+4637   1981150758
+4636   584425608
+4635   1439592409
+4634   1866596843
+4633   686626374
+4632   166687224
+4631   237670554
+4630   370386260
+4629   286851904
+4628   1106833377
+4627   908026656
+4626   1708673244
+4625   410577144
+4624   1253767034
+4623   167861582
+4622   1543944912
+4621   1219318008
+4620   2130180310
+4619   692859447
+4618   37979489
+4617   160253180
+4616   1338869862
+4615   592593413
+4614   1989473432
+4613   1287069841
+4612   1404402132
+4611   1556870688
+4610   1716960674
+4609   178168285
+4608   2044083884
+4607   1931245904
+4606   114553914
+4605   2045282882
+4604   1272905184
+4603   1628926289
+4602   316240114
+4601   2027318968
+4600   1278320825
+4599   1609843252
+4598   725662308
+4597   696256233
+4596   1801743269
+4595   1540811662
+4594   1014115880
+4593   34449026
+4592   185164919
+4591   851085465
+4590   1181338519
+4589   1969927130
+4588   1501473233
+4587   1592869724
+4586   318263396
+4585   51800021
+4584   1335674929
+4583   432602743
+4582   1717592815
+4581   1226233846
+4580   1660270452
+4579   1933198418
+4578   63614371
+4577   2146284650
+4576   658340719
+4575   633111273
+4574   1729042767
+4573   1393069864
+4572   350605464
+4571   853880510
+4570   1301656660
+4569   582064592
+4568   1955583630
+4567   1332334293
+4566   1829624001
+4565   1767294243
+4564   1355646743
+4563   163030415
+4562   1000594154
+4561   362721437
+4560   1497095880
+4559   1735952443
+4558   1651663733
+4557   1449673212
+4556   257194795
+4555   2033144301
+4554   481690853
+4553   109441082
+4552   919815939
+4551   1931878045
+4550   1162619475
+4549   1661469450
+4548   1274857698
+4547   1577986745
+4546   417241882
+4545   1412754503
+4544   282505809
+4543   875162257
+4542   91413204
+4541   1916024520
+4540   1045780528
+4539   2116806014
+4538   899924239
+4537   188289387
+4536   2124171198
+4535   1666593586
+4534   766700089
+4533   992925305
+4532   813418183
+4531   1412125359
+4530   858541352
+4529   47422668
+4528   1478757648
+4527   1766003080
+4526   967982358
+4525   147753712
+4524   1113328362
+4523   697296456
+4522   1094305255
+4521   1405830136
+4520   657020347
+4519   1732116378
+4518   1244227568
+4517   2009586843
+4516   1295480936
+4515   1689563273
+4514   1321341298
+4513   513964937
+4512   1976865377
+4511   122090838
+4510   1016100281
+4509   857491141
+4508   2140118464
+4507   1380814301
+4506   1569072946
+4505   1131245893
+4504   853175403
+4503   1502058378
+4502   134383953
+4501   765995515
+4500   2080851358
+4499   1240021919
+4498   1226923957
+4497   1331003936
+4496   652674718
+4495   270685902
+4494   1200932105
+4493   1854981873
+4492   40276109
+4491   1509672525
+4490   161602568
+4489   794917151
+4488   436635442
+4487   1702147942
+4486   688245545
+4485   781515998
+4484   1860181544
+4483   1199250460
+4482   1645348304
+4481   1119374236
+4480   129456022
+4479   1782769628
+4478   1435901843
+4477   1008872571
+4476   527638898
+4475   67014568
+4474   996861939
+4473   87179888
+4472   1568690667
+4471   1041845682
+4470   1686555205
+4469   749847422
+4468   587347201
+4467   956238055
+4466   130071830
+4465   945176493
+4464   230409793
+4463   1838743228
+4462   1693379305
+4461   1392842605
+4460   1073037083
+4459   606938274
+4458   106671606
+4457   1802603091
+4456   1989450046
+4455   1636478732
+4454   1283651342
+4453   740807308
+4452   1069794438
+4451   2010062324
+4450   1830956041
+4449   1268067099
+4448   1255130730
+4447   1368887275
+4446   12010631
+4445   440459010
+4444   645807548
+4443   2102499905
+4442   548108330
+4441   818843245
+4440   454498481
+4439   730317150
+4438   619775592
+4437   1789654356
+4436   725828261
+4435   438812250
+4434   1399280836
+4433   985050836
+4432   765706145
+4431   1086441031
+4430   1286170999
+4429   1417917639
+4428   764971876
+4427   617676522
+4426   518951749
+4425   1248642737
+4424   566684294
+4423   1421072665
+4422   1057334915
+4421   1949210987
+4420   754931594
+4419   462068766
+4418   1256056467
+4417   814671720
+4416   723079726
+4415   56994374
+4414   2039834327
+4413   1974447951
+4412   1648001424
+4411   1965274828
+4410   199067653
+4409   812327773
+4408   4488889
+4407   180963342
+4406   390373520
+4405   1888261073
+4404   1820589752
+4403   312839805
+4402   1846363485
+4401   1495272154
+4400   321469155
+4399   668494477
+4398   898965890
+4397   1663812786
+4396   50992228
+4395   1245362732
+4394   191307822
+4393   764956954
+4392   666141071
+4391   595266149
+4390   693154520
+4389   2087743522
+4388   1886472687
+4387   1199062093
+4386   922321040
+4385   896115423
+4384   556476597
+4383   74559499
+4382   1775380298
+4381   835673651
+4380   1960785939
+4379   18104311
+4378   421954253
+4377   263711463
+4376   507857237
+4375   77533715
+4374   41897588
+4373   325317598
+4372   2138854298
+4371   1177869008
+4370   596306264
+4369   805140016
+4368   617502249
+4367   1801086806
+4366   1472504964
+4365   1433518921
+4364   579221661
+4363   1743525320
+4362   71802434
+4361   725881196
+4360   856277110
+4359   1641576074
+4358   1165422482
+4357   990357264
+4356   642585496
+4355   847761541
+4354   1268218772
+4353   1868286594
+4352   261257208
+4351   1757275987
+4350   413719398
+4349   1697074475
+4348   1657730721
+4347   344420538
+4346   221813875
+4345   182539639
+4344   86163065
+4343   1011512228
+4342   1876494982
+4341   1333714281
+4340   560366759
+4339   942703106
+4338   1480118700
+4337   1331466975
+4336   1221865145
+4335   1876463292
+4334   1361716487
+4333   2000824112
+4332   887248210
+4331   577710008
+4330   1707942362
+4329   2013403493
+4328   998990578
+4327   317660941
+4326   1869622140
+4325   921782550
+4324   586504332
+4323   1658426433
+4322   1454567195
+4321   711666381
+4320   99545266
+4319   69298860
+4318   1475260600
+4317   1475191082
+4316   258257473
+4315   1357785294
+4314   453528304
+4313   899932431
+4312   451145469
+4311   933791876
+4310   2001079229
+4309   1376383431
+4308   1868321609
+4307   1751139056
+4306   2117234136
+4305   1368524680
+4304   989215081
+4303   784006479
+4302   292881750
+4301   1021328365
+4300   1726203077
+4299   1390281421
+4298   143781353
+4297   77208028
+4296   1878640256
+4295   211195707
+4294   1614699002
+4293   2022321599
+4292   1558881167
+4291   1385268335
+4290   1383889428
+4289   771837831
+4288   1958525035
+4287   117475306
+4286   1021662778
+4285   1505808689
+4284   906639825
+4283   1667220076
+4282   1046336850
+4281   1222245686
+4280   1212953915
+4279   249940173
+4278   1406632943
+4277   499796928
+4276   761923974
+4275   1333227657
+4274   1075642930
+4273   2115370364
+4272   1205287049
+4271   1050083976
+4270   877547011
+4269   1648995049
+4268   1659124813
+4267   2080069294
+4266   609992674
+4265   2003802305
+4264   799798188
+4263   229430667
+4262   638432171
+4261   787043335
+4260   1574226947
+4259   1266414122
+4258   1897658701
+4257   452716346
+4256   1358319129
+4255   1501926350
+4254   459471839
+4253   1831877787
+4252   454266160
+4251   796396676
+4250   1963096391
+4249   713156987
+4248   1635499847
+4247   73405285
+4246   1571637646
+4245   794037258
+4244   127940608
+4243   25558954
+4242   1237823353
+4241   1703775648
+4240   1538442811
+4239   944961365
+4238   1039002375
+4237   1802806156
+4236   1280271106
+4235   380562006
+4234   1365370134
+4233   12754852
+4232   802687368
+4231   1519501696
+4230   1036868282
+4229   1121510601
+4228   2055578641
+4227   395732351
+4226   2140728154
+4225   1673924990
+4224   1047660189
+4223   1810558811
+4222   2016265044
+4221   1888592821
+4220   1308380477
+4219   1889691105
+4218   1289002989
+4217   841462589
+4216   2092948325
+4215   1546078692
+4214   1703697553
+4213   571648607
+4212   634599790
+4211   292861988
+4210   664773273
+4209   1883120303
+4208   1812173906
+4207   658440368
+4206   437436022
+4205   1267516254
+4204   1725358286
+4203   1993352085
+4202   1123370218
+4201   1828660414
+4200   1611406703
+4199   641135930
+4198   1128266095
+4197   381653651
+4196   1495555810
+4195   330169343
+4194   1805143594
+4193   1306551016
+4192   502178333
+4191   126573938
+4190   599589832
+4189   466917888
+4188   1944226428
+4187   1890407945
+4186   1285248684
+4185   1521299718
+4184   911478901
+4183   1410835565
+4182   2054358982
+4181   898963135
+4180   628171729
+4179   6332905
+4178   1445684281
+4177   544657652
+4176   1080565730
+4175   591567584
+4174   144146035
+4173   2044181520
+4172   381945382
+4171   482234288
+4170   700394319
+4169   1229753051
+4168   1293063768
+4167   798096751
+4166   723993705
+4165   189004794
+4164   1975474658
+4163   1678569655
+4162   706961183
+4161   35260445
+4160   329831158
+4159   856665535
+4158   1329152852
+4157   422926710
+4156   978929043
+4155   2021896767
+4154   1614424384
+4153   12515766
+4152   782663835
+4151   2048026077
+4150   1600762501
+4149   83514077
+4148   1073250823
+4147   854116697
+4146   400511617
+4145   1183867858
+4144   209622202
+4143   1809395395
+4142   1343787200
+4141   1299675979
+4140   1336654167
+4139   2049781216
+4138   505759346
+4137   1104058974
+4136   970105741
+4135   1192907698
+4134   1629527258
+4133   1940214213
+4132   1348738497
+4131   1997779296
+4130   853591240
+4129   2054388096
+4128   2025220140
+4127   1454739733
+4126   955985974
+4125   966413277
+4124   1239232931
+4123   1713881955
+4122   559236913
+4121   699149758
+4120   974775254
+4119   746645804
+4118   1830486108
+4117   2036866613
+4116   644494495
+4115   738599869
+4114   1987564305
+4113   1057429871
+4112   472741228
+4111   1441489632
+4110   793916633
+4109   232595193
+4108   1079675474
+4107   1460335296
+4106   1622015364
+4105   1177375176
+4104   1991652849
+4103   1779231610
+4102   1086622972
+4101   1441834048
+4100   2120042804
+4099   1546335155
+4098   1098402122
+4097   1058806863
+4096   215506802
+4095   1389587667
+4094   407176364
+4093   540083173
+4092   739106701
+4091   1960074756
+4090   1016147298
+4089   1085392289
+4088   102151309
+4087   1091886238
+4086   49302307
+4085   1734548272
+4084   265858641
+4083   546074673
+4082   263513238
+4081   240146035
+4080   361814158
+4079   1481064985
+4078   758063477
+4077   2049783946
+4076   1616166095
+4075   1990267401
+4074   90752204
+4073   549818800
+4072   1806672454
+4071   1687771465
+4070   343431926
+4069   1061235941
+4068   1330828353
+4067   1856298103
+4066   651630499
+4065   1822907277
+4064   650480966
+4063   594585255
+4062   1671419522
+4061   1801198060
+4060   1857923447
+4059   2071744708
+4058   1036089981
+4057   515086685
+4056   826027597
+4055   1650711282
+4054   1471035034
+4053   25712606
+4052   184260515
+4051   929931901
+4050   1629566206
+4049   459513860
+4048   2012382538
+4047   915279723
+4046   1959031742
+4045   1066347294
+4044   183594947
+4043   550464386
+4042   206386874
+4041   745436513
+4040   356943112
+4039   634617470
+4038   409605442
+4037   1655404724
+4036   1205817137
+4035   57045243
+4034   151487754
+4033   996766554
+4032   884145456
+4031   1747158462
+4030   765108078
+4029   1342836761
+4028   1245717111
+4027   1532862347
+4026   1191535299
+4025   800314990
+4024   1466450767
+4023   541103133
+4022   543630048
+4021   1872230303
+4020   1065033011
+4019   714286482
+4018   647965766
+4017   946035243
+4016   731684776
+4015   1408567355
+4014   859960420
+4013   1585642081
+4012   193521274
+4011   1719253052
+4010   335831071
+4009   849022036
+4008   1576283981
+4007   352560198
+4006   1503916969
+4005   209050583
+4004   1320383435
+4003   551812940
+4002   231658475
+4001   1688792343
+4000   501441351
+3999   1379729379
+3998   151301462
+3997   445402120
+3996   66411579
+3995   650432166
+3994   256684942
+3993   1741704112
+3992   1623553770
+3991   1976827214
+3990   1224264537
+3989   118997767
+3988   2130085354
+3987   1386882058
+3986   86074823
+3985   1293526343
+3984   1215046081
+3983   1288191016
+3982   1249811010
+3981   1491982885
+3980   142969071
+3979   2130754521
+3978   1492588715
+3977   1367233397
+3976   1179660411
+3975   952104029
+3974   2124875756
+3973   1779074740
+3972   50371588
+3971   999412744
+3970   1537490881
+3969   56039231
+3968   1313317800
+3967   1648352943
+3966   188717178
+3965   472191115
+3964   1174362044
+3963   427341376
+3962   517439575
+3961   1504556002
+3960   1994225508
+3959   1984866126
+3958   32922944
+3957   836559011
+3956   171835977
+3955   945367455
+3954   43715333
+3953   1870546844
+3952   1145221945
+3951   1266540137
+3950   2146877818
+3949   923219321
+3948   951094109
+3947   540484685
+3946   1389841289
+3945   1548069319
+3944   901732441
+3943   1125463012
+3942   241583859
+3941   2141816005
+3940   1833578592
+3939   2036621585
+3938   2014805700
+3937   841126685
+3936   473990899
+3935   1908859450
+3934   2102235187
+3933   1817289690
+3932   580599516
+3931   680057097
+3930   1471633058
+3929   1157666497
+3928   1813030149
+3927   1235039136
+3926   792843678
+3925   448772781
+3924   1947629158
+3923   924658844
+3922   1871152673
+3921   222002623
+3920   315446027
+3919   1606393133
+3918   1680861680
+3917   1550508438
+3916   1786235892
+3915   264378277
+3914   1306485460
+3913   907400083
+3912   1439368068
+3911   352445921
+3910   127010305
+3909   992451907
+3908   1562630686
+3907   105946250
+3906   886375145
+3905   804184857
+3904   1328259934
+3903   1422178090
+3902   345656631
+3901   1570416667
+3900   1014510595
+3899   236593922
+3898   364822819
+3897   1364257368
+3896   1434893626
+3895   2015668482
+3894   725103755
+3893   1725626535
+3892   609212816
+3891   264759540
+3890   688624591
+3889   912421237
+3888   1967640888
+3887   1416483402
+3886   244022977
+3885   878835809
+3884   972493857
+3883   954039539
+3882   780389778
+3881   446916161
+3880   937298883
+3879   21064055
+3878   106076761
+3877   758445829
+3876   925169963
+3875   1611680703
+3874   458528225
+3873   1905326915
+3872   407667495
+3871   109062709
+3870   1205593848
+3869   1797736875
+3868   949183944
+3867   496637985
+3866   639153613
+3865   1856750739
+3864   1406455665
+3863   460344215
+3862   1037001943
+3861   1844275227
+3860   444602300
+3859   1419624837
+3858   668398260
+3857   1088805079
+3856   443989545
+3855   1437467086
+3854   98446030
+3853   525577696
+3852   16740656
+3851   759325723
+3850   340839399
+3849   178853053
+3848   1243377739
+3847   641879706
+3846   299917604
+3845   1167326696
+3844   1204013208
+3843   349465516
+3842   699733067
+3841   757414268
+3840   1307362413
+3839   708955863
+3838   1158583262
+3837   1239916853
+3836   1237665967
+3835   178809398
+3834   819748795
+3833   1709664086
+3832   15741915
+3831   1764860754
+3830   1175876967
+3829   1503280868
+3828   975635292
+3827   1378414821
+3826   990359049
+3825   2065895496
+3824   1420726430
+3823   1486603955
+3822   184738297
+3821   1985371250
+3820   1663431632
+3819   1846443341
+3818   2026419097
+3817   76051043
+3816   1585350146
+3815   2097935736
+3814   467593628
+3813   446598940
+3812   1189586750
+3811   2138260852
+3810   1746314654
+3809   67445560
+3808   1618773543
+3807   979773864
+3806   420168057
+3805   1675485529
+3804   163067483
+3803   1202371689
+3802   533787119
+3801   659944694
+3800   789225462
+3799   1944945793
+3798   512921819
+3797   1057223443
+3796   2105172039
+3795   1651238742
+3794   1881157199
+3793   1582838828
+3792   1970655971
+3791   485778604
+3790   2106435801
+3789   1587380588
+3788   261093194
+3787   2075967009
+3786   1755941063
+3785   1138751206
+3784   908348985
+3783   476816424
+3782   847767934
+3781   1122141190
+3780   519487309
+3779   766540790
+3778   1794761151
+3777   2090771662
+3776   816706381
+3775   1365280016
+3774   1141698409
+3773   1650606436
+3772   413146226
+3771   736324974
+3770   147022875
+3769   1879485667
+3768   1987257402
+3767   1009166725
+3766   1323549892
+3765   522333211
+3764   1828066419
+3763   1395378595
+3762   1623886675
+3761   383275382
+3760   224685409
+3759   30468791
+3758   1978923173
+3757   1269825636
+3756   1167618024
+3755   1279124639
+3754   290983272
+3753   1933691443
+3752   2104812763
+3751   81227144
+3750   1474863687
+3749   576199294
+3748   2097318057
+3747   429481134
+3746   949073253
+3745   1313583592
+3744   952133790
+3743   405373435
+3742   1503583561
+3741   681144207
+3740   896551219
+3739   1285339797
+3738   555935775
+3737   1464924191
+3736   1328583954
+3735   2075654944
+3734   1045930184
+3733   1444791036
+3732   1170693186
+3731   1593417883
+3730   551835857
+3729   1102343421
+3728   1010334415
+3727   699798534
+3726   978842363
+3725   1381410228
+3724   1321795524
+3723   209756128
+3722   458827756
+3721   1528613468
+3720   131392735
+3719   1045382552
+3718   1774609689
+3717   783734464
+3716   1624830992
+3715   543699817
+3714   1957483679
+3713   270989582
+3712   1656305864
+3711   218243764
+3710   125208432
+3709   1579110676
+3708   2104239491
+3707   627764478
+3706   418994007
+3705   2031276566
+3704   904961758
+3703   1599995948
+3702   892955179
+3701   68349764
+3700   583083468
+3699   1999520970
+3698   123501058
+3697   1776407835
+3696   1525486658
+3695   769086235
+3694   922582472
+3693   1940665703
+3692   78363393
+3691   1560928852
+3690   1901487427
+3689   1495141918
+3688   1568035208
+3687   1230909872
+3686   973734433
+3685   1353841410
+3684   1034877601
+3683   1739239915
+3682   145781150
+3681   77195188
+3680   261487921
+3679   1644927602
+3678   1160116668
+3677   72962925
+3676   1870286368
+3675   966481707
+3674   1138321386
+3673   836611994
+3672   1016912480
+3671   1040917857
+3670   2092332354
+3669   954159281
+3668   474034312
+3667   1501898471
+3666   853825363
+3665   1732304603
+3664   690722841
+3663   1509137268
+3662   39178276
+3661   730705123
+3660   2140377292
+3659   670577555
+3658   521407485
+3657   214193798
+3656   196032270
+3655   1381978166
+3654   1208060260
+3653   957682413
+3652   1477751994
+3651   648337196
+3650   1064562167
+3649   188524996
+3648   1922124882
+3647   193634961
+3646   1082125186
+3645   1033674374
+3644   2097052874
+3643   97403529
+3642   891763287
+3641   62753199
+3640   566883545
+3639   590433883
+3638   100333918
+3637   889213357
+3636   811175629
+3635   1492171743
+3634   1693126326
+3633   2107501366
+3632   1516243624
+3631   1516084369
+3630   209297638
+3629   1926183494
+3628   474545284
+3627   1286912966
+3626   1153617186
+3625   1385833505
+3624   2051709820
+3623   559723064
+3622   2040603894
+3621   1289226998
+3620   873695962
+3619   870927206
+3618   1253883457
+3617   888450508
+3616   244065735
+3615   984721657
+3614   141911086
+3613   2034299675
+3612   1678003632
+3611   301329404
+3610   2109902929
+3609   1825153836
+3608   1926741902
+3607   755645823
+3606   1343570678
+3605   851157911
+3604   2123411767
+3603   177041957
+3602   1898203728
+3601   1737543778
+3600   1041539085
+3599   1069868319
+3598   772566308
+3597   1236195427
+3596   1382686794
+3595   593894122
+3594   1492713259
+3593   762482822
+3592   1833510750
+3591   1169676688
+3590   35343540
+3589   2132729102
+3588   626861471
+3587   269161800
+3586   746539421
+3585   357249708
+3584   1454201672
+3583   1988065330
+3582   2071880393
+3581   2000333444
+3580   522071150
+3579   1354257106
+3578   481583157
+3577   1075583991
+3576   779717704
+3575   1166528721
+3574   1100437830
+3573   385867989
+3572   1282986520
+3571   828335409
+3570   964977470
+3569   1952827305
+3568   1834665173
+3567   178672186
+3566   1890965816
+3565   620203971
+3564   907867020
+3563   323036571
+3562   727139282
+3561   1848265296
+3560   542815217
+3559   1913665388
+3558   1386189680
+3557   269611763
+3556   962443776
+3555   905957739
+3554   432852962
+3553   1601351876
+3552   1465994180
+3551   717623287
+3550   1518750287
+3549   1593970807
+3548   574539402
+3547   1462538084
+3546   2122629808
+3545   393849715
+3544   2031025849
+3543   272102421
+3542   1568374167
+3541   1477642862
+3540   1141153883
+3539   786305284
+3538   61861489
+3537   1214461202
+3536   1418288814
+3535   1567929245
+3534   2040548337
+3533   1207085372
+3532   1927705002
+3531   960957542
+3530   462075615
+3529   273203454
+3528   951221612
+3527   480231941
+3526   1984242448
+3525   1508575548
+3524   1587447206
+3523   1862713323
+3522   82601589
+3521   2019507021
+3520   143083884
+3519   56212203
+3518   1618824646
+3517   180689687
+3516   1578995882
+3515   1850527387
+3514   972959196
+3513   553382987
+3512   1278432186
+3511   782068883
+3510   1415781373
+3509   2074176329
+3508   1515500118
+3507   641415891
+3506   1321396512
+3505   211203442
+3504   1787707891
+3503   1079590795
+3502   745009756
+3501   1654501548
+3500   9735930
+3499   2129327322
+3498   436444653
+3497   1590129712
+3496   1040268383
+3495   121529125
+3494   1425973959
+3493   1715423833
+3492   1719629439
+3491   26389386
+3490   400682375
+3489   2109877845
+3488   624699968
+3487   1915780907
+3486   1355214139
+3485   1025612895
+3484   572095201
+3483   190890313
+3482   1285085261
+3481   1351739504
+3480   1414052413
+3479   774365482
+3478   752779817
+3477   1304296676
+3476   1001191648
+3475   241805717
+3474   1613677333
+3473   133206343
+3472   1069854865
+3471   763166082
+3470   1218056894
+3469   567089866
+3468   1089058939
+3467   314915528
+3466   164155753
+3465   1472328198
+3464   549383334
+3463   1399584573
+3462   1314741458
+3461   1757235242
+3460   1549173065
+3459   632385115
+3458   754663705
+3457   1746570721
+3456   1343685706
+3455   1164323826
+3454   1888011282
+3453   1367839344
+3452   924321548
+3451   510719779
+3450   598959687
+3449   109755737
+3448   1920657482
+3447   510974100
+3446   1838102990
+3445   867985304
+3444   1319434499
+3443   850511251
+3442   1062633097
+3441   502764999
+3440   1821590791
+3439   903141366
+3438   402934113
+3437   1764214389
+3436   1913015842
+3435   912054828
+3434   157586740
+3433   939631740
+3432   1997895155
+3431   682356342
+3430   1002571536
+3429   1950085992
+3428   1436183057
+3427   1737823527
+3426   2006043087
+3425   2123330010
+3424   240002278
+3423   1377291502
+3422   768879657
+3421   814565811
+3420   737545945
+3419   87985587
+3418   419136394
+3417   1052672177
+3416   1339023248
+3415   987591739
+3414   1952835855
+3413   816669500
+3412   1176404107
+3411   159491731
+3410   99830886
+3409   57376402
+3408   1137609171
+3407   1638362933
+3406   1606627649
+3405   973384102
+3404   1061643320
+3403   1622714045
+3402   2084543851
+3401   47809163
+3400   1393656933
+3399   1412231657
+3398   2091526553
+3397   1460336695
+3396   1497821248
+3395   628751584
+3394   1354450353
+3393   1572920115
+3392   639745557
+3391   680894069
+3390   395429416
+3389   1832357416
+3388   896445987
+3387   1579028303
+3386   1247319970
+3385   522353748
+3384   1958671280
+3383   1793344124
+3382   716838613
+3381   1119027705
+3380   1169366207
+3379   608951601
+3378   598232401
+3377   164225069
+3376   576719612
+3375   2131397251
+3374   1036323899
+3373   1013834157
+3372   229057112
+3371   672312194
+3370   103766257
+3369   2080803886
+3368   2061894057
+3367   1462774969
+3366   105886342
+3365   2072384781
+3364   2136489675
+3363   673556283
+3362   1177490699
+3361   954871789
+3360   1931931730
+3359   963884761
+3358   585037446
+3357   374092238
+3356   1767840671
+3355   1601459493
+3354   1952998783
+3353   839643575
+3352   623977917
+3351   107887012
+3350   520795303
+3349   1005141138
+3348   32231989
+3347   614318798
+3346   1275384818
+3345   1710369103
+3344   1902340139
+3343   364011705
+3342   910067900
+3341   295736873
+3340   757901785
+3339   788474936
+3338   1974917544
+3337   2136992924
+3336   1473768941
+3335   1579813706
+3334   894894082
+3333   1181617886
+3332   889108201
+3331   213605937
+3330   369834343
+3329   1557839492
+3328   1343527738
+3327   1131061600
+3326   568577103
+3325   928197096
+3324   977481576
+3323   1845111771
+3322   318848271
+3321   1766320426
+3320   75655023
+3319   2053960153
+3318   1877239968
+3317   469346534
+3316   859462306
+3315   911373113
+3314   800301203
+3313   1606603266
+3312   1753593568
+3311   121592963
+3310   468302977
+3309   768392509
+3308   1462189643
+3307   395103838
+3306   1242098842
+3305   292151055
+3304   690705505
+3303   681288144
+3302   811783543
+3301   1478752357
+3300   1017561847
+3299   1386256390
+3298   989262388
+3297   415330642
+3296   153580024
+3295   870948980
+3294   609348824
+3293   1358644798
+3292   1769456748
+3291   412371766
+3290   2036564106
+3289   1753792137
+3288   1194497847
+3287   965866855
+3286   1816528979
+3285   1400342688
+3284   1305263193
+3283   678708239
+3282   1138300289
+3281   985201059
+3280   806886968
+3279   73199139
+3278   1673777315
+3277   1170038588
+3276   1851881980
+3275   560810697
+3274   1627851159
+3273   1359436796
+3272   1811209945
+3271   1573010801
+3270   489489968
+3269   602231204
+3268   1232676366
+3267   118313408
+3266   1953465466
+3265   942418874
+3264   1248975880
+3263   196977058
+3262   1469564340
+3261   15664611
+3260   1365357567
+3259   1070697250
+3258   2084746806
+3257   1941638806
+3256   1808087310
+3255   1137820739
+3254   262042399
+3253   320062134
+3252   2019304919
+3251   1065101150
+3250   1458907392
+3249   1784332028
+3248   368800806
+3247   1112966617
+3246   1689671076
+3245   492445184
+3244   897084400
+3243   54840358
+3242   869946828
+3241   1208978741
+3240   340334434
+3239   371176560
+3238   796249386
+3237   290257492
+3236   1016821175
+3235   1756488407
+3234   1620338182
+3233   1233311269
+3232   979103139
+3231   398867089
+3230   78401453
+3229   1571202408
+3228   1410093588
+3227   946926066
+3226   1679596407
+3225   1488025176
+3224   1265999468
+3223   1344424897
+3222   1008638390
+3221   234972890
+3220   696300343
+3219   345940774
+3218   94660952
+3217   2023839270
+3216   215882217
+3215   1634830718
+3214   1769982004
+3213   1835589307
+3212   1861989572
+3211   498770267
+3210   412729354
+3209   50076942
+3208   1501839033
+3207   1187244627
+3206   817402958
+3205   1930993554
+3204   777385268
+3203   1221471092
+3202   1154909816
+3201   1555384379
+3200   1136257149
+3199   1278959034
+3198   2039089649
+3197   2069552059
+3196   1828410246
+3195   335171509
+3194   479386786
+3193   1031026578
+3192   648124554
+3191   662697615
+3190   140311938
+3189   819944721
+3188   130058557
+3187   607313882
+3186   253857266
+3185   527776558
+3184   1920324794
+3183   1271211736
+3182   1422859952
+3181   1811912630
+3180   1144414882
+3179   1372968375
+3178   1380157631
+3177   1718329127
+3176   409859359
+3175   1743415514
+3174   776083737
+3173   1369484537
+3172   85213943
+3171   2023434430
+3170   1663778377
+3169   1214188738
+3168   1598032436
+3167   1703918140
+3166   1590165273
+3165   797383668
+3164   1834530603
+3163   1964172819
+3162   890714639
+3161   1975663481
+3160   532639058
+3159   1680481704
+3158   566087454
+3157   1749765646
+3156   834472736
+3155   1130129178
+3154   1252400254
+3153   108412164
+3152   126796854
+3151   49891577
+3150   431754998
+3149   1573569403
+3148   963109016
+3147   1784225765
+3146   942245389
+3145   1187858470
+3144   1658201571
+3143   900132955
+3142   1853189807
+3141   1018508853
+3140   425401993
+3139   2107343885
+3138   1771507113
+3137   800648768
+3136   2016871184
+3135   1773476102
+3134   2054152676
+3133   2006350770
+3132   1431533760
+3131   1357716583
+3130   1409576026
+3129   930357060
+3128   846008968
+3127   1583441924
+3126   497365392
+3125   726060572
+3124   1003332324
+3123   1202508677
+3122   1824140813
+3121   700711098
+3120   1234266208
+3119   795012881
+3118   631324014
+3117   1922734194
+3116   126024194
+3115   42112434
+3114   1482152310
+3113   639692718
+3112   474730962
+3111   1893329570
+3110   1394485388
+3109   1772236873
+3108   90472701
+3107   2145514659
+3106   893979740
+3105   10520414
+3104   341942341
+3103   696436093
+3102   596774744
+3101   501176700
+3100   511707614
+3099   1973617750
+3098   432991667
+3097   119948396
+3096   580109600
+3095   1442340363
+3094   1049403406
+3093   302621225
+3092   2115726116
+3091   1029127932
+3090   69387084
+3089   1459015662
+3088   668988686
+3087   589211580
+3086   440581884
+3085   1633815124
+3084   1714865120
+3083   1736306388
+3082   1392690978
+3081   849977737
+3080   1802856869
+3079   1396454377
+3078   878257133
+3077   79952287
+3076   1803572317
+3075   197543646
+3074   1561229318
+3073   1988249289
+3072   184728479
+3071   770640642
+3070   68185033
+3069   391759218
+3068   1393508149
+3067   1138134952
+3066   1218028638
+3065   277488375
+3064   1474097895
+3063   20275474
+3062   233234141
+3061   656710454
+3060   360139246
+3059   1627659152
+3058   1018433778
+3057   1182657210
+3056   1021830108
+3055   851759143
+3054   241124146
+3053   864887383
+3052   2080933167
+3051   2143720249
+3050   2119204252
+3049   1722904582
+3048   1740365707
+3047   680713486
+3046   666206617
+3045   1962806676
+3044   12815167
+3043   790588676
+3042   1920064256
+3041   1940452909
+3040   1524616140
+3039   1077533729
+3038   1321214228
+3037   1116019774
+3036   1811520705
+3035   1197753164
+3034   44254234
+3033   817387440
+3032   1807619876
+3031   753058636
+3030   1785760324
+3029   1324965684
+3028   605829044
+3027   166674634
+3026   941533063
+3025   156942725
+3024   918309624
+3023   244887545
+3022   893166779
+3021   358028585
+3020   403354541
+3019   1438490765
+3018   1056697965
+3017   1925042679
+3016   667898319
+3015   2023101589
+3014   42742420
+3013   219845906
+3012   1413456183
+3011   842530527
+3010   619238681
+3009   408596366
+3008   1413496672
+3007   123461064
+3006   1071765540
+3005   994133264
+3004   1537616936
+3003   1438679245
+3002   1179110764
+3001   482654192
+3000   147229592
+2999   1619085690
+2998   383432620
+2997   448886319
+2996   1395848658
+2995   696645518
+2994   1411259594
+2993   560281038
+2992   1989016652
+2991   1602159661
+2990   1448814268
+2989   625795510
+2988   770592446
+2987   1181080024
+2986   1882300258
+2985   448052412
+2984   609645405
+2983   1347695541
+2982   1748090873
+2981   1004859817
+2980   1576517503
+2979   495777617
+2978   1484314473
+2977   419363407
+2976   733327776
+2975   1780569943
+2974   1962506148
+2973   1054962744
+2972   1291449653
+2971   1707508722
+2970   99221571
+2969   1845826920
+2968   223237031
+2967   1834270750
+2966   1185110373
+2965   835567620
+2964   855112514
+2963   1956583580
+2962   1258950418
+2961   1363221141
+2960   831567215
+2959   267734244
+2958   890978900
+2957   322540034
+2956   571434618
+2955   534604717
+2954   847445187
+2953   1752269236
+2952   1918661686
+2951   1252313256
+2950   1668028992
+2949   1157154095
+2948   1909933489
+2947   1851228178
+2946   604340907
+2945   1825848680
+2944   489120289
+2943   254997426
+2942   955741172
+2941   1593106381
+2940   1484271690
+2939   412434469
+2938   660716547
+2937   1535153059
+2936   979158236
+2935   1376010441
+2934   1724100850
+2933   1639375020
+2932   1125016365
+2931   991216173
+2930   472242241
+2929   509027181
+2928   1843783274
+2927   356374183
+2926   1622578495
+2925   966649030
+2924   763426678
+2923   1742615578
+2922   84240244
+2921   761507591
+2920   1489863415
+2919   1964284461
+2918   552813188
+2917   84084809
+2916   1362107889
+2915   349343480
+2914   870107507
+2913   1043497556
+2912   918209384
+2911   543306703
+2910   932389834
+2909   2096602279
+2908   1580759880
+2907   1432189754
+2906   1958535857
+2905   1487266864
+2904   250994075
+2903   732884676
+2902   1167132779
+2901   615989184
+2900   1294916547
+2899   115868058
+2898   1033932334
+2897   877134243
+2896   1740431152
+2895   2027446564
+2894   882408786
+2893   1919087
+2892   252752163
+2891   267439430
+2890   208694402
+2889   1405778606
+2888   602176572
+2887   203469708
+2886   1361460949
+2885   318610332
+2884   1578617744
+2883   326800804
+2882   111107722
+2881   969090753
+2880   1110030471
+2879   1647683728
+2878   138066421
+2877   93493016
+2876   1181195678
+2875   1225651181
+2874   320134085
+2873   1782488539
+2872   1585451777
+2871   1051264720
+2870   1729540498
+2869   417782304
+2868   522920554
+2867   1153969417
+2866   2142209105
+2865   1738512065
+2864   1774694401
+2863   614969356
+2862   1940708333
+2861   994457204
+2860   1812746506
+2859   5224694
+2858   44317657
+2857   283566240
+2856   772335611
+2855   1034660145
+2854   207502610
+2853   609526991
+2852   1364253981
+2851   610907642
+2850   831024331
+2849   1016537454
+2848   466488049
+2847   1059898888
+2846   1920842579
+2845   1546190787
+2844   1787683052
+2843   1416353012
+2842   52948040
+2841   1167669473
+2840   528344166
+2839   575571081
+2838   423056847
+2837   931892137
+2836   1526758664
+2835   1527239749
+2834   1945287380
+2833   780237197
+2832   949706498
+2831   1935483638
+2830   950139547
+2829   1529180266
+2828   1380372731
+2827   1157141159
+2826   76063630
+2825   162808620
+2824   1817889812
+2823   1744078615
+2822   1925986308
+2821   347716526
+2820   144419593
+2819   1918609091
+2818   1243178523
+2817   1067780910
+2816   1419699484
+2815   504489567
+2814   1493242747
+2813   620013579
+2812   888008846
+2811   1624860607
+2810   744612626
+2809   1743935677
+2808   1196296065
+2807   1043300746
+2806   1134088405
+2805   746521467
+2804   577533251
+2803   9803741
+2802   1977581297
+2801   1568009880
+2800   555110907
+2799   1940482036
+2798   1453116636
+2797   1217564111
+2796   1486734995
+2795   479468662
+2794   384305960
+2793   1470173286
+2792   1599659022
+2791   7377217
+2790   1252021651
+2789   1224122331
+2788   498909606
+2787   738688956
+2786   1722021811
+2785   799685905
+2784   1763964369
+2783   2015865787
+2782   2022884601
+2781   1291556816
+2780   428564542
+2779   1848795528
+2778   609847272
+2777   449774598
+2776   465767495
+2775   1124284663
+2774   916423817
+2773   1157007019
+2772   1602176482
+2771   37099261
+2770   114893244
+2769   1485030444
+2768   453747041
+2767   973647973
+2766   833258151
+2765   16561709
+2764   1027293288
+2763   376928743
+2762   218151634
+2761   375536691
+2760   1655951258
+2759   513332695
+2758   1649584168
+2757   1846707349
+2756   1122208235
+2755   1853639671
+2754   924284952
+2753   472407552
+2752   1587301245
+2751   174089073
+2750   681709544
+2749   2126273592
+2748   1383028033
+2747   1633046257
+2746   1680834428
+2745   1456244124
+2744   1669591829
+2743   879324556
+2742   1042113775
+2741   117146037
+2740   1730835868
+2739   1288728918
+2738   651772293
+2737   437185332
+2736   2093838333
+2735   456329408
+2734   1945893722
+2733   651756596
+2732   868461132
+2731   1852302587
+2730   873436171
+2729   1956727557
+2728   1538608108
+2727   1943428144
+2726   922422396
+2725   649800682
+2724   266338426
+2723   750195879
+2722   1938181656
+2721   1608511300
+2720   938544688
+2719   1196146935
+2718   445439164
+2717   2074267557
+2716   2110938075
+2715   801509872
+2714   414130349
+2713   1552445792
+2712   1295972335
+2711   1900868504
+2710   1612857392
+2709   1293650536
+2708   1342374233
+2707   195442885
+2706   638775257
+2705   1442081737
+2704   1735351923
+2703   93591135
+2702   1925804073
+2701   1059217223
+2700   313694478
+2699   1077491675
+2698   1034305161
+2697   888807426
+2696   1677089718
+2695   172226517
+2694   859102674
+2693   805310774
+2692   1959134839
+2691   742034721
+2690   1163072136
+2689   1011760779
+2688   1232692507
+2687   1791412939
+2686   1660137208
+2685   558492283
+2684   1653021185
+2683   660745492
+2682   2087072048
+2681   2321799
+2680   558494271
+2679   1417414506
+2678   654875279
+2677   2047776144
+2676   607574610
+2675   545184122
+2674   1663761312
+2673   676134700
+2672   1927380305
+2671   848312398
+2670   24912062
+2669   1572370700
+2668   1547885605
+2667   862078644
+2666   29704752
+2665   871778944
+2664   360575325
+2663   117067952
+2662   1789722285
+2661   947374060
+2660   1656825862
+2659   1519142845
+2658   1499107219
+2657   674200224
+2656   138391754
+2655   999391715
+2654   618903883
+2653   1650699386
+2652   102251221
+2651   669657541
+2650   1494930168
+2649   658201775
+2648   809839896
+2647   109691157
+2646   384014832
+2645   2078923557
+2644   765287465
+2643   815448914
+2642   651222638
+2641   355009604
+2640   1447910441
+2639   1310317066
+2638   1542665948
+2637   676106661
+2636   501503318
+2635   2060120447
+2634   1229540306
+2633   1560684913
+2632   607725738
+2631   270579440
+2630   1595750489
+2629   982625638
+2628   1380751090
+2627   499715503
+2626   55296340
+2625   635176016
+2624   897140494
+2623   2096729990
+2622   155769218
+2621   1591533093
+2620   2007301293
+2619   1385239011
+2618   274186943
+2617   878399987
+2616   1491887340
+2615   1716049566
+2614   1427700919
+2613   410277860
+2612   1515022121
+2611   1488389220
+2610   959827304
+2609   771803780
+2608   808813747
+2607   1630029149
+2606   1594050002
+2605   1088302053
+2604   1452394709
+2603   958960866
+2602   2112418071
+2601   1772583748
+2600   1037311998
+2599   1096034986
+2598   927329297
+2597   745575074
+2596   1750058657
+2595   106049998
+2594   479406798
+2593   1453091049
+2592   89428697
+2591   918013855
+2590   1317346150
+2589   1128901306
+2588   2040835319
+2587   705621025
+2586   1598182716
+2585   1081609479
+2584   201027445
+2583   2086795346
+2582   1597934204
+2581   743218341
+2580   679575473
+2579   1477281803
+2578   1325237425
+2577   1867995342
+2576   177634440
+2575   635089136
+2574   1123367630
+2573   1827294608
+2572   2069132516
+2571   1016383085
+2570   845254451
+2569   291736924
+2568   1493459977
+2567   821279299
+2566   266168275
+2565   296967608
+2564   16621301
+2563   1708876591
+2562   135744899
+2561   1108011039
+2560   1024662184
+2559   611725124
+2558   1678202238
+2557   959225839
+2556   504593580
+2555   1658871017
+2554   1631158923
+2553   1605292752
+2552   1407219873
+2551   120652401
+2550   1565464563
+2549   959063779
+2548   1299647363
+2547   690148289
+2546   744627712
+2545   497823479
+2544   713440268
+2543   106984544
+2542   982040157
+2541   1777395592
+2540   1670406756
+2539   23975152
+2538   25568648
+2537   1196492369
+2536   804657997
+2535   704775332
+2534   161222709
+2533   1056093910
+2532   684214407
+2531   1671503422
+2530   1577292449
+2529   65436344
+2528   107131544
+2527   19331220
+2526   1475550564
+2525   1046784476
+2524   251651144
+2523   1510506521
+2522   39828188
+2521   448156094
+2520   968488686
+2519   875316274
+2518   214436067
+2517   801823883
+2516   2124191668
+2515   637643167
+2514   1663266970
+2513   1083528324
+2512   584061436
+2511   958065005
+2510   1751826943
+2509   473914387
+2508   1366800802
+2507   1468276964
+2506   1035269660
+2505   1896047735
+2504   20560924
+2503   637202934
+2502   1626285109
+2501   618778063
+2500   1564371878
+2499   1557961228
+2498   737369428
+2497   1207830715
+2496   1915163724
+2495   2112527691
+2494   1006956288
+2493   1950978697
+2492   542017835
+2491   1311995562
+2490   233720027
+2489   166664803
+2488   898608254
+2487   1724276547
+2486   1286040561
+2485   1040663344
+2484   53581731
+2483   705201964
+2482   1479185029
+2481   110147048
+2480   1738747851
+2479   283549979
+2478   1586128375
+2477   1618236715
+2476   1447716040
+2475   398066725
+2474   269762625
+2473   1549266509
+2472   1220314704
+2471   68323881
+2470   2028892283
+2469   356541163
+2468   1790281152
+2467   772325385
+2466   200874427
+2465   2111668675
+2464   1570509856
+2463   1842444374
+2462   1717258670
+2461   375353032
+2460   413387308
+2459   656927128
+2458   1028107889
+2457   2005428558
+2456   1670694816
+2455   580838597
+2454   1708961963
+2453   2090918331
+2452   1113937761
+2451   1195635050
+2450   671502321
+2449   120511135
+2448   983317587
+2447   1188061650
+2446   1348474090
+2445   2045933178
+2444   1325235669
+2443   201438744
+2442   1667857874
+2441   863773541
+2440   425526377
+2439   1256566898
+2438   155666735
+2437   1826096125
+2436   1349299177
+2435   505913701
+2434   394410005
+2433   1195156824
+2432   1429057066
+2431   1060331542
+2430   1494728790
+2429   555442398
+2428   1133715960
+2427   447269292
+2426   296466595
+2425   1727260133
+2424   1614384483
+2423   513326913
+2422   1419416010
+2421   993426626
+2420   212317463
+2419   1630924319
+2418   919520693
+2417   1084868056
+2416   2010309628
+2415   1147035345
+2414   378075304
+2413   461462128
+2412   1923396015
+2411   411290976
+2410   708106805
+2409   746913900
+2408   2054751369
+2407   1797236682
+2406   1431686120
+2405   154142353
+2404   1224340283
+2403   1481562111
+2402   1847911681
+2401   873614668
+2400   2074099230
+2399   1047459498
+2398   258975803
+2397   1553939475
+2396   980368457
+2395   1930623330
+2394   307844123
+2393   620957857
+2392   301009450
+2391   1935975339
+2390   73905932
+2389   1274933054
+2388   1768098338
+2387   1919968996
+2386   706792752
+2385   1548847500
+2384   1371122978
+2383   2114267975
+2382   1900838971
+2381   1176482115
+2380   504023255
+2379   1058353771
+2378   1462711428
+2377   1900609016
+2376   572896398
+2375   2097607657
+2374   453714319
+2373   350725615
+2372   1554946529
+2371   800452183
+2370   614638865
+2369   520159755
+2368   67091041
+2367   475836121
+2366   1246095352
+2365   359410599
+2364   1629613880
+2363   519352432
+2362   547051925
+2361   1173560043
+2360   167877000
+2359   301420584
+2358   568140302
+2357   219250838
+2356   548846018
+2355   740008425
+2354   1795492177
+2353   194640862
+2352   1610244720
+2351   842485199
+2350   1861254335
+2349   750897887
+2348   485457373
+2347   1512587419
+2346   1446894696
+2345   222170783
+2344   542661128
+2343   1800745784
+2342   1883570398
+2341   1034786774
+2340   733361142
+2339   138802744
+2338   1421548051
+2337   1855164089
+2336   993705889
+2335   726742920
+2334   1959842322
+2333   456053836
+2332   351475431
+2331   245631340
+2330   605419741
+2329   2096109810
+2328   1900058214
+2327   1975615525
+2326   571242309
+2325   354205155
+2324   1277247353
+2323   953006977
+2322   480870175
+2321   859346832
+2320   357027826
+2319   348666916
+2318   1451486839
+2317   263286590
+2316   969926291
+2315   1793632560
+2314   486084032
+2313   1655358002
+2312   1067384641
+2311   1744767654
+2310   1760722371
+2309   1025680701
+2308   1292580503
+2307   694805131
+2306   2042805415
+2305   537652052
+2304   375267488
+2303   1714210982
+2302   1998117743
+2301   402849269
+2300   493056774
+2299   777287864
+2298   1524867500
+2297   1545853059
+2296   698368172
+2295   1765718980
+2294   2020818628
+2293   417900520
+2292   595979151
+2291   132203258
+2290   1555343641
+2289   93741236
+2288   1526224273
+2287   1805337926
+2286   1924686205
+2285   1462051937
+2284   726247919
+2283   888800026
+2282   2042119279
+2281   41703940
+2280   452187151
+2279   1065917240
+2278   1130358934
+2277   754928450
+2276   319537642
+2275   328594433
+2274   687017957
+2273   2119901867
+2272   1221154208
+2271   1220829878
+2270   1025465417
+2269   1094687363
+2268   78919692
+2267   1906632168
+2266   1672518078
+2265   280467651
+2264   1169739829
+2263   1888615370
+2262   1010040527
+2261   502237914
+2260   753462633
+2259   1897489363
+2258   316538679
+2257   64172336
+2256   1079090007
+2255   1035886179
+2254   1567416306
+2253   684543978
+2252   436612874
+2251   976202039
+2250   1058828654
+2249   1844742349
+2248   746379597
+2247   801764501
+2246   67910493
+2245   347119423
+2244   1254923873
+2243   1613671727
+2242   1094436450
+2241   126466845
+2240   1141910186
+2239   1266316896
+2238   1569652932
+2237   1945935689
+2236   736892339
+2235   1931386356
+2234   1417910772
+2233   667501914
+2232   1135152737
+2231   1260034812
+2230   185699235
+2229   689290296
+2228   818399355
+2227   1428136147
+2226   644239678
+2225   394546029
+2224   599273305
+2223   591214267
+2222   1773198972
+2221   739354173
+2220   229822441
+2219   257064153
+2218   1776831856
+2217   399260174
+2216   1694324276
+2215   601722414
+2214   1400166621
+2213   1128457028
+2212   471761541
+2211   1975603201
+2210   704297560
+2209   1343458145
+2208   529424557
+2207   1785750224
+2206   528024916
+2205   69390425
+2204   796233619
+2203   157875960
+2202   481802679
+2201   445862440
+2200   441635456
+2199   905046736
+2198   45050618
+2197   423853326
+2196   828862842
+2195   53025411
+2194   768830705
+2193   2007402779
+2192   361391825
+2191   1516134818
+2190   1110005965
+2189   1978045915
+2188   710223525
+2187   1175109442
+2186   1146577200
+2185   565867248
+2184   129960873
+2183   1572047068
+2182   424159467
+2181   1275787044
+2180   1446178644
+2179   1066030984
+2178   815433228
+2177   460034132
+2176   989516604
+2175   370148956
+2174   1735071394
+2173   350371179
+2172   1863724152
+2171   1724239591
+2170   400811822
+2169   17782130
+2168   76183893
+2167   2139508854
+2166   1802506269
+2165   968943711
+2164   1839117234
+2163   1400179534
+2162   897396814
+2161   530829558
+2160   805911293
+2159   2082380171
+2158   831468715
+2157   144356277
+2156   1045148569
+2155   1722013780
+2154   141707780
+2153   1001657477
+2152   125868423
+2151   1505612131
+2150   460353815
+2149   986144512
+2148   76514380
+2147   445284272
+2146   872446386
+2145   639145425
+2144   653908452
+2143   10831803
+2142   2097043004
+2141   1845942022
+2140   1648055697
+2139   408786616
+2138   362759508
+2137   1254723830
+2136   300391620
+2135   402326735
+2134   71546897
+2133   1308287676
+2132   594268241
+2131   962500290
+2130   1846844491
+2129   661555015
+2128   1037231602
+2127   1256938582
+2126   2648497
+2125   43491092
+2124   1596145357
+2123   783579297
+2122   541303661
+2121   1287207559
+2120   1429097751
+2119   15069543
+2118   113698126
+2117   1584852602
+2116   1938859468
+2115   861614583
+2114   689586069
+2113   955450078
+2112   510259753
+2111   1688256388
+2110   1483182513
+2109   393331867
+2108   108394995
+2107   2107916421
+2106   1183176933
+2105   1139587592
+2104   1955542141
+2103   1256530254
+2102   1608926833
+2101   2080196874
+2100   2072752336
+2099   589905908
+2098   658906518
+2097   993740510
+2096   1808276873
+2095   1366552847
+2094   1649671078
+2093   308937798
+2092   1501965194
+2091   526234118
+2090   1173509432
+2089   1991728796
+2088   223693722
+2087   1399567191
+2086   895266533
+2085   983409390
+2084   351354829
+2083   1148813328
+2082   1619751212
+2081   116927886
+2080   1579861393
+2079   1522749740
+2078   1357638581
+2077   1116291051
+2076   152374280
+2075   2074130327
+2074   1678144407
+2073   2022828915
+2072   1331261566
+2071   1019020924
+2070   1421290355
+2069   1079011825
+2068   929112683
+2067   1439837319
+2066   1491553080
+2065   1499339075
+2064   2012071301
+2063   1123436960
+2062   1282912013
+2061   1657720046
+2060   302540396
+2059   1921425889
+2058   1096462263
+2057   1387767980
+2056   1048212362
+2055   1893936853
+2054   1511141826
+2053   234426943
+2052   1716435583
+2051   97001472
+2050   906772953
+2049   463570342
+2048   1370375460
+2047   1430991902
+2046   1585630291
+2045   277029012
+2044   742868760
+2043   659123483
+2042   601538560
+2041   252249741
+2040   89908241
+2039   2128936684
+2038   1734942393
+2037   1577257255
+2036   1575249666
+2035   368116120
+2034   216427062
+2033   354351255
+2032   820896564
+2031   1508969772
+2030   561257783
+2029   1062256064
+2028   873213527
+2027   1350009058
+2026   2024109802
+2025   813785419
+2024   177501269
+2023   1414140353
+2022   1475137638
+2021   1252865241
+2020   874109660
+2019   1623264698
+2018   1025423698
+2017   1093346447
+2016   688123142
+2015   926506808
+2014   1822974100
+2013   490619019
+2012   569215241
+2011   620085523
+2010   664790995
+2009   660134634
+2008   553687018
+2007   1366826273
+2006   1360830193
+2005   1220898411
+2004   1694703204
+2003   854940937
+2002   1940577120
+2001   1906124148
+2000   635756245
+1999   1358732373
+1998   1185629910
+1997   59428108
+1996   1172507788
+1995   609969448
+1994   1486131429
+1993   1072119676
+1992   540030693
+1991   1999356587
+1990   227441543
+1989   1928246861
+1988   935141556
+1987   98916890
+1986   1417855995
+1985   197504122
+1984   357291567
+1983   1202888577
+1982   1973311672
+1981   2056564255
+1980   66398505
+1979   1445448370
+1978   1446788088
+1977   1480272255
+1976   1819606717
+1975   505889256
+1974   1427804939
+1973   1936062704
+1972   219184692
+1971   581844747
+1970   720494238
+1969   576328137
+1968   186224584
+1967   575660461
+1966   720780327
+1965   100388112
+1964   69938755
+1963   1634258489
+1962   844678133
+1961   759267480
+1960   1064215031
+1959   128524653
+1958   510390866
+1957   737637434
+1956   1889108971
+1955   214967418
+1954   371676098
+1953   448210959
+1952   1136490072
+1951   527863302
+1950   609776167
+1949   733609897
+1948   1773325300
+1947   940898832
+1946   52467316
+1945   2031027661
+1944   286704564
+1943   845960192
+1942   1215568466
+1941   1790340202
+1940   395620162
+1939   144833776
+1938   2003031458
+1937   85836472
+1936   505721706
+1935   1234005485
+1934   1403193627
+1933   1458154923
+1932   570043458
+1931   716153480
+1930   248876614
+1929   326577597
+1928   386899330
+1927   295423448
+1926   365961335
+1925   1440898011
+1924   1225960994
+1923   1991296444
+1922   1985918440
+1921   402880174
+1920   902021649
+1919   1816360982
+1918   681142581
+1917   1889781287
+1916   654194268
+1915   1353990772
+1914   815459195
+1913   643848009
+1912   450340029
+1911   1070734689
+1910   1934792392
+1909   309783690
+1908   1786595718
+1907   769025973
+1906   830126492
+1905   1195050430
+1904   663962027
+1903   687040147
+1902   1209278309
+1901   243465861
+1900   329254150
+1899   2100936814
+1898   2108099909
+1897   1093484966
+1896   1216946102
+1895   522148539
+1894   1602463219
+1893   823080819
+1892   1089274795
+1891   169557458
+1890   1869221241
+1889   1159724010
+1888   1162166714
+1887   1474635456
+1886   1074322091
+1885   10346259
+1884   903650743
+1883   1892208154
+1882   856539265
+1881   140556339
+1880   1431622619
+1879   1165766419
+1878   1627140846
+1877   591545288
+1876   105063946
+1875   143086345
+1874   2133255769
+1873   420496165
+1872   357785997
+1871   1255825143
+1870   282849600
+1869   1383252831
+1868   883990712
+1867   1585951370
+1866   1638505395
+1865   393865282
+1864   1580357392
+1863   1432905761
+1862   1101343226
+1861   2077034432
+1860   1154874392
+1859   394585785
+1858   85401919
+1857   1151820455
+1856   570984713
+1855   1329597585
+1854   1301290641
+1853   763094404
+1852   460585535
+1851   1838256494
+1850   660899141
+1849   840077331
+1848   1060702473
+1847   1484054501
+1846   605773167
+1845   1832051428
+1844   1932783995
+1843   877430625
+1842   137646565
+1841   1122016814
+1840   371834431
+1839   844381877
+1838   1892231084
+1837   490125429
+1836   5593978
+1835   205599634
+1834   1440005704
+1833   1650806607
+1832   278031369
+1831   706757441
+1830   1991632513
+1829   3053937
+1828   1971084719
+1827   903287981
+1826   1998013461
+1825   1955373957
+1824   869012050
+1823   1610517795
+1822   102195263
+1821   1767991852
+1820   777554021
+1819   1324328288
+1818   234304164
+1817   1376134692
+1816   1698754153
+1815   1875826189
+1814   1694404863
+1813   810767181
+1812   505596194
+1811   1440748336
+1810   1377269378
+1809   2029192650
+1808   838787899
+1807   1686631449
+1806   1197603373
+1805   502271019
+1804   2075051913
+1803   733248263
+1802   1806657742
+1801   274977432
+1800   883156369
+1799   1088344532
+1798   152524123
+1797   15710762
+1796   34275931
+1795   387495666
+1794   1853178694
+1793   1248503846
+1792   832963773
+1791   925350623
+1790   1533687688
+1789   1548902977
+1788   1773057782
+1787   505961622
+1786   1829213477
+1785   887986972
+1784   1370229995
+1783   253656527
+1782   1580981451
+1781   623887192
+1780   601960437
+1779   1838121576
+1778   831589277
+1777   336516880
+1776   1759063184
+1775   464355109
+1774   843096925
+1773   1800074481
+1772   1997575542
+1771   718313210
+1770   122453308
+1769   867445607
+1768   1054068601
+1767   1912512105
+1766   310015715
+1765   933255732
+1764   1702015541
+1763   927828071
+1762   1862299806
+1761   1431544444
+1760   1299776488
+1759   1027726065
+1758   1867173147
+1757   885070810
+1756   1283215275
+1755   1575556950
+1754   1454489168
+1753   746342803
+1752   1799179738
+1751   1890343523
+1750   1939781563
+1749   265443557
+1748   79058392
+1747   367234167
+1746   1640903603
+1745   2106472350
+1744   614263215
+1743   124783715
+1742   1677621173
+1741   1130129934
+1740   1811728257
+1739   357424851
+1738   557429892
+1737   120812868
+1736   210496564
+1735   1529671292
+1734   1218439574
+1733   270471096
+1732   1775535231
+1731   834573741
+1730   1711854945
+1729   414705678
+1728   1891994438
+1727   291616197
+1726   1578065290
+1725   536872471
+1724   1923860860
+1723   1711629293
+1722   954044888
+1721   1533736181
+1720   1811285130
+1719   1572547395
+1718   772023602
+1717   120069690
+1716   1900454600
+1715   1516119888
+1714   428851177
+1713   1631616929
+1712   460539106
+1711   1320196321
+1710   572700042
+1709   1690915388
+1708   146928287
+1707   1175242248
+1706   1049856942
+1705   2087509115
+1704   1901619709
+1703   383865833
+1702   706099799
+1701   1360829553
+1700   1090062950
+1699   1420238748
+1698   984124036
+1697   1355121967
+1696   515238984
+1695   2013919644
+1694   1730311231
+1693   390124679
+1692   2047827811
+1691   1528981141
+1690   761712579
+1689   1691215440
+1688   1819576443
+1687   1403387362
+1686   1838702160
+1685   268837671
+1684   1055580782
+1683   1256138504
+1682   1058916886
+1681   917107365
+1680   1173268034
+1679   1544941442
+1678   641058446
+1677   206902820
+1676   1421106187
+1675   665991109
+1674   1381409316
+1673   540790156
+1672   1441286530
+1671   1433344699
+1670   376705517
+1669   1882424631
+1668   904999763
+1667   1117688039
+1666   1772294384
+1665   125114305
+1664   2113575481
+1663   201330090
+1662   1775895748
+1661   356612370
+1660   1856888345
+1659   1505808865
+1658   1999996928
+1657   1550738772
+1656   347806580
+1655   582563656
+1654   1357404432
+1653   138473416
+1652   82870470
+1651   1661459092
+1650   276048919
+1649   966365214
+1648   123835255
+1647   2122550984
+1646   972977152
+1645   880316031
+1644   1372188227
+1643   2095548264
+1642   164084639
+1641   1706345547
+1640   528344936
+1639   1406501125
+1638   110130247
+1637   779885458
+1636   1151596206
+1635   1570964294
+1634   496702204
+1633   1756963111
+1632   491925392
+1631   270086883
+1630   504099090
+1629   306149573
+1628   1158002285
+1627   1417433271
+1626   193334340
+1625   209333163
+1624   499693186
+1623   1843428988
+1622   2009908145
+1621   1263988904
+1620   1537623836
+1619   300981583
+1618   2140871710
+1617   1391002872
+1616   750362757
+1615   1024912535
+1614   716231392
+1613   1813326328
+1612   1567203328
+1611   905067161
+1610   1596215299
+1609   1895943125
+1608   254904919
+1607   686649601
+1606   283183254
+1605   1542116743
+1604   1079038901
+1603   226615321
+1602   1252864020
+1601   185775819
+1600   1259568246
+1599   1234149467
+1598   112815233
+1597   948669121
+1596   917740085
+1595   497388999
+1594   346908666
+1593   1383187930
+1592   305805152
+1591   1708926562
+1590   1270600842
+1589   146620964
+1588   1698102473
+1587   1115959174
+1586   674771480
+1585   1084520077
+1584   1605192855
+1583   1958647878
+1582   217111028
+1581   1818743851
+1580   650162242
+1579   909565698
+1578   1612759871
+1577   860271824
+1576   1755094348
+1575   56567933
+1574   289252722
+1573   893263082
+1572   1114530722
+1571   18714553
+1570   72960586
+1569   310899125
+1568   316409382
+1567   1762909881
+1566   601760455
+1565   1682035802
+1564   191583847
+1563   785465752
+1562   112587088
+1561   159184188
+1560   10824088
+1559   154641667
+1558   1619333131
+1557   613582396
+1556   1658249967
+1555   863607250
+1554   867409049
+1553   1933932652
+1552   1308485636
+1551   1455028978
+1550   205983979
+1549   1937374065
+1548   1301954998
+1547   1556191938
+1546   571019102
+1545   861831266
+1544   1089520858
+1543   270538169
+1542   820302495
+1541   803631597
+1540   1849788819
+1539   457534353
+1538   1856622318
+1537   781857227
+1536   1571326034
+1535   1963778350
+1534   1569448714
+1533   32399659
+1532   774641664
+1531   2105429069
+1530   687334704
+1529   1544725340
+1528   643875348
+1527   755725881
+1526   1893656995
+1525   1871800963
+1524   1702605261
+1523   1559863718
+1522   1727948672
+1521   1518595219
+1520   153073980
+1519   797275689
+1518   1366354963
+1517   440123732
+1516   466671080
+1515   300480933
+1514   41528770
+1513   285889261
+1512   568232997
+1511   362768142
+1510   1094492927
+1509   1067931592
+1508   1033691967
+1507   2040327615
+1506   1359892161
+1505   1538926374
+1504   1189136686
+1503   1611503293
+1502   1492548603
+1501   1377399971
+1500   1461553721
+1499   2079092471
+1498   1798551993
+1497   919558032
+1496   1200604268
+1495   333793276
+1494   143852291
+1493   184010042
+1492   1406789738
+1491   930672983
+1490   152240255
+1489   1860433896
+1488   330604609
+1487   1065874030
+1486   398594961
+1485   180781819
+1484   1879731583
+1483   1826244276
+1482   1338879981
+1481   1647785053
+1480   1476559823
+1479   1201648960
+1478   1855523078
+1477   1642249240
+1476   851190929
+1475   1895872516
+1474   46377771
+1473   1959220363
+1472   149949572
+1471   1560939780
+1470   1726331626
+1469   541995688
+1468   878488203
+1467   1464758717
+1466   775705741
+1465   1016594225
+1464   1074487186
+1463   1360662955
+1462   31769787
+1461   1693839489
+1460   600068374
+1459   1233849873
+1458   1461838935
+1457   149822790
+1456   1333626095
+1455   719834333
+1454   989385485
+1453   231946530
+1452   349684452
+1451   137231021
+1450   1939745623
+1449   1981794231
+1448   350458031
+1447   2107134210
+1446   1595871469
+1445   1039454214
+1444   1745922944
+1443   632921639
+1442   232888737
+1441   1755437531
+1440   682451577
+1439   261572909
+1438   1913773595
+1437   2009377625
+1436   390271530
+1435   1562526434
+1434   984824438
+1433   1528131345
+1432   760594581
+1431   945403562
+1430   232000554
+1429   450245584
+1428   2047707426
+1427   742004601
+1426   1307920952
+1425   1101679565
+1424   370149881
+1423   852154464
+1422   439684555
+1421   515373869
+1420   1934256638
+1419   1980095061
+1418   385922762
+1417   1458487465
+1416   361211265
+1415   962949829
+1414   806565477
+1413   2137969061
+1412   2097953710
+1411   2118799476
+1410   1989147584
+1409   820557599
+1408   2018785026
+1407   351247161
+1406   1024553187
+1405   1009623833
+1404   801931853
+1403   39420876
+1402   1296130790
+1401   310348996
+1400   1045179784
+1399   1637479601
+1398   1289808280
+1397   946027861
+1396   371854720
+1395   455766488
+1394   661995010
+1393   2002259659
+1392   1065381473
+1391   607073142
+1390   129451107
+1389   475769173
+1388   1618883795
+1387   1570456580
+1386   651921988
+1385   370725852
+1384   1012479767
+1383   835249649
+1382   148821476
+1381   1277396111
+1380   100014449
+1379   1637900423
+1378   1943488060
+1377   1009161193
+1376   1696798956
+1375   985132310
+1374   1860976691
+1373   491582856
+1372   1141724740
+1371   1806134837
+1370   1168024364
+1369   99151923
+1368   1265624880
+1367   834041791
+1366   284032851
+1365   517078709
+1364   1537868663
+1363   54921868
+1362   1872808552
+1361   589612300
+1360   1135672994
+1359   706478175
+1358   1971330833
+1357   1248157943
+1356   557976813
+1355   1964155987
+1354   221904376
+1353   1882567304
+1352   735235199
+1351   658404701
+1350   1481391698
+1349   1238336904
+1348   2088585115
+1347   958355750
+1346   1295668150
+1345   1205216099
+1344   1990891218
+1343   54841853
+1342   1471042140
+1341   1042572817
+1340   540509957
+1339   333982573
+1338   1962602720
+1337   748546171
+1336   1443656776
+1335   229110983
+1334   791753805
+1333   948256363
+1332   1066732521
+1331   1166330377
+1330   765765114
+1329   2034998699
+1328   148501361
+1327   7174846
+1326   1026253567
+1325   822893157
+1324   1228920787
+1323   1710983323
+1322   401175605
+1321   1644381943
+1320   717303233
+1319   523035948
+1318   2090152402
+1317   883369016
+1316   1114948180
+1315   1240826296
+1314   1881657607
+1313   948318400
+1312   1661815544
+1311   1137059567
+1310   1227453745
+1309   1939447433
+1308   1037809444
+1307   1733491737
+1306   2104276014
+1305   495400413
+1304   1309862109
+1303   1772907076
+1302   182491248
+1301   1179217470
+1300   1017829015
+1299   758590268
+1298   1008745132
+1297   1473091852
+1296   925737707
+1295   1462753892
+1294   421717552
+1293   1732022492
+1292   993680089
+1291   2025623305
+1290   1701713188
+1289   1981417865
+1288   1555571416
+1287   849326106
+1286   1149195056
+1285   166629779
+1284   1726494400
+1283   744598040
+1282   1868348303
+1281   1869851759
+1280   99250122
+1279   1641445656
+1278   1982655067
+1277   542409031
+1276   423629627
+1275   331368938
+1274   312909165
+1273   130644639
+1272   755078060
+1271   1571384628
+1270   170472337
+1269   1692220811
+1268   1980336209
+1267   1693474888
+1266   1051374300
+1265   1341198862
+1264   469073802
+1263   543577895
+1262   30309304
+1261   1159745872
+1260   470051888
+1259   852387082
+1258   832222809
+1257   1388941637
+1256   1270315354
+1255   404597016
+1254   445765124
+1253   2004126289
+1252   645347918
+1251   226902646
+1250   2034680340
+1249   1704324739
+1248   1217816029
+1247   1651286128
+1246   229499866
+1245   292984988
+1244   1723774526
+1243   889008184
+1242   2107655950
+1241   1210340897
+1240   1738532067
+1239   624481097
+1238   640846511
+1237   639137346
+1236   1224401086
+1235   507796405
+1234   1310889558
+1233   1456811578
+1232   73526006
+1231   1325405869
+1230   327523063
+1229   1228593899
+1228   1729555376
+1227   427625793
+1226   943176512
+1225   1413672713
+1224   1906732746
+1223   218862478
+1222   2116929597
+1221   1088506826
+1220   1156570265
+1219   383394211
+1218   1474824873
+1217   924831041
+1216   2074995250
+1215   1487975329
+1214   332812686
+1213   513433629
+1212   1297959765
+1211   1483174853
+1210   569494385
+1209   1099394721
+1208   1547563659
+1207   133050106
+1206   1475731436
+1205   1915073155
+1204   434270398
+1203   2132967337
+1202   1129288515
+1201   992415755
+1200   1743334141
+1199   2047380918
+1198   285417387
+1197   315882663
+1196   668376695
+1195   724314034
+1194   1444226764
+1193   818225919
+1192   1209775860
+1191   1733535385
+1190   1761165601
+1189   231739224
+1188   455882609
+1187   2134333192
+1186   592018355
+1185   1561561621
+1184   190015564
+1183   997121481
+1182   2091422891
+1181   198565044
+1180   2083094842
+1179   436444279
+1178   1771146933
+1177   1779974151
+1176   1846263356
+1175   1490247747
+1174   785784640
+1173   1589338291
+1172   389633196
+1171   1229391245
+1170   706998368
+1169   1427451477
+1168   1379004223
+1167   1708587000
+1166   1019139547
+1165   1997634423
+1164   1662021822
+1163   1858175026
+1162   1204543966
+1161   978036636
+1160   1277652776
+1159   1774316057
+1158   1787204517
+1157   1041804635
+1156   1944317627
+1155   1742380522
+1154   1617622378
+1153   2138934168
+1152   1061510287
+1151   1654978612
+1150   574901759
+1149   303120690
+1148   737664571
+1147   280899186
+1146   994189511
+1145   256925064
+1144   1100614551
+1143   1703877042
+1142   882339923
+1141   1109665366
+1140   1997870670
+1139   1145895015
+1138   408311930
+1137   1528853447
+1136   46565178
+1135   1308448169
+1134   793090457
+1133   683985186
+1132   580522250
+1131   1577711557
+1130   1338315766
+1129   235848141
+1128   1977482077
+1127   44823995
+1126   1571665905
+1125   1952867107
+1124   680870235
+1123   2110127413
+1122   1564032409
+1121   758389596
+1120   917314041
+1119   294002573
+1118   1456414827
+1117   480739506
+1116   1327768283
+1115   1437796117
+1114   1522068789
+1113   2138432832
+1112   1853490020
+1111   1883928556
+1110   701353436
+1109   469017223
+1108   1099329837
+1107   1247347409
+1106   735762990
+1105   1510063640
+1104   727925919
+1103   1362862547
+1102   1493153067
+1101   344674109
+1100   1747713128
+1099   1293491771
+1098   811665883
+1097   24614970
+1096   1511437408
+1095   1609022140
+1094   388834697
+1093   2069964286
+1092   1192813372
+1091   1270029836
+1090   1449458417
+1089   436574535
+1088   1113717938
+1087   18618710
+1086   1106154365
+1085   1336819098
+1084   1731789745
+1083   1785623881
+1082   1437079396
+1081   1384472797
+1080   784598719
+1079   1601489675
+1078   1880737880
+1077   1736749844
+1076   519421490
+1075   1520384091
+1074   16910573
+1073   383251810
+1072   1762633067
+1071   199661296
+1070   1680491873
+1069   1723098157
+1068   1929538010
+1067   1350127391
+1066   1783263921
+1065   1588956770
+1064   416208768
+1063   1266288509
+1062   620505869
+1061   756238837
+1060   156311898
+1059   1430839707
+1058   1477903817
+1057   1924382487
+1056   434312613
+1055   1468014132
+1054   2047223350
+1053   347316948
+1052   1001025162
+1051   1983073368
+1050   1651218564
+1049   1195332522
+1048   1082068185
+1047   360353789
+1046   1719839271
+1045   136169680
+1044   1905234671
+1043   1964732924
+1042   850243584
+1041   39534910
+1040   417606934
+1039   330364482
+1038   2087317884
+1037   340581240
+1036   933918623
+1035   516975412
+1034   968450901
+1033   1807453579
+1032   1109976610
+1031   1337149809
+1030   1425818667
+1029   379413059
+1028   996527094
+1027   9889685
+1026   2024642785
+1025   86995665
+1024   466988970
+1023   64149982
+1022   843582031
+1021   1953176287
+1020   901005183
+1019   1290864775
+1018   1622976899
+1017   945898505
+1016   602602766
+1015   1902589995
+1014   1433409743
+1013   1865699761
+1012   1547125990
+1011   519879102
+1010   99700673
+1009   77025693
+1008   1543929506
+1007   1570342472
+1006   1519613987
+1005   1273948692
+1004   1554482449
+1003   1778784739
+1002   381634911
+1001   730563551
+1000   340622715
+999    1415928982
+998    502253922
+997    909531429
+996    1690384362
+995    1960492803
+994    1390897281
+993    661296331
+992    1310628447
+991    1700200904
+990    330199388
+989    2102590325
+988    688262009
+987    1867870552
+986    1659972410
+985    884386652
+984    355464004
+983    913530641
+982    1765999088
+981    1470100297
+980    1123433244
+979    676841849
+978    704895354
+977    269980814
+976    15860023
+975    1888312896
+974    892313781
+973    823918898
+972    1438162024
+971    1113189577
+970    228309629
+969    1578574933
+968    1873028268
+967    689244767
+966    1666117796
+965    1029088031
+964    649864356
+963    1838180025
+962    331096942
+961    1355521769
+960    1011938895
+959    609812484
+958    442617915
+957    1951359004
+956    1512406547
+955    746441769
+954    1265871212
+953    1032847355
+952    1937581045
+951    1089157239
+950    765204943
+949    853452430
+948    660981826
+947    964066106
+946    1525150681
+945    1339424773
+944    450150871
+943    1926607852
+942    595609268
+941    2007070739
+940    1387644957
+939    1686548510
+938    2059940785
+937    843940236
+936    39380411
+935    1975421419
+934    697991089
+933    1441826234
+932    826241129
+931    1868768106
+930    912903854
+929    1208063539
+928    1244889585
+927    1843659794
+926    685487792
+925    479559192
+924    956344372
+923    176713973
+922    267642412
+921    1084128615
+920    428175413
+919    1948622485
+918    1475785397
+917    1469040701
+916    513915234
+915    1746026477
+914    743815504
+913    590563780
+912    538962895
+911    1056544406
+910    2094613601
+909    543704720
+908    1647168099
+907    84519366
+906    145949147
+905    745037824
+904    1149180289
+903    976706631
+902    528922380
+901    1765661238
+900    623878521
+899    1216727707
+898    522575747
+897    765330393
+896    887315422
+895    508773818
+894    211916779
+893    2019699405
+892    1896022208
+891    466503575
+890    1755826866
+889    1106618360
+888    1434707250
+887    1877242568
+886    725225196
+885    2070835102
+884    1207063582
+883    1834754746
+882    643433827
+881    2142741822
+880    1556859954
+879    2010094235
+878    397755573
+877    902130275
+876    1082822725
+875    1316726164
+874    216115444
+873    1531002699
+872    352828110
+871    1459678321
+870    1243085491
+869    2006031776
+868    329412285
+867    13801928
+866    553413613
+865    1015099665
+864    760235258
+863    1892896852
+862    263872539
+861    789403848
+860    1179279973
+859    2026067946
+858    381393163
+857    1511355796
+856    670178986
+855    1037954098
+854    1427401275
+853    1211805407
+852    277894792
+851    780823240
+850    1744986249
+849    654729679
+848    927271510
+847    1228513056
+846    686014831
+845    1699303674
+844    963898054
+843    903920771
+842    287917207
+841    494279982
+840    1130266036
+839    1229283563
+838    1452618162
+837    1461796267
+836    1401050318
+835    808000409
+834    751227126
+833    2118315057
+832    713616879
+831    385288241
+830    408010685
+829    1815407824
+828    1355888960
+827    1490922713
+826    83954521
+825    1605857226
+824    760059306
+823    646578035
+822    1614302806
+821    1770648760
+820    2001035378
+819    516473193
+818    2116198496
+817    1375451484
+816    264615002
+815    1929577708
+814    1411386466
+813    469618072
+812    1921138383
+811    1206117292
+810    1189145467
+809    1815953416
+808    1975716892
+807    644617753
+806    710569141
+805    1430218909
+804    94383530
+803    365938885
+802    1710304372
+801    1045692702
+800    1176882929
+799    1064571619
+798    1731453303
+797    1897515381
+796    730863407
+795    1584860134
+794    2139038068
+793    1136894193
+792    793026305
+791    1097829613
+790    1801933912
+789    625583894
+788    251858191
+787    186620788
+786    2111548665
+785    1942480577
+784    8439325
+783    205269174
+782    1427956253
+781    105184966
+780    1377884048
+779    544527714
+778    1105384275
+777    545497983
+776    550234222
+775    344630255
+774    1867398184
+773    1196174476
+772    1336539604
+771    645732753
+770    1461723047
+769    1426851195
+768    333708212
+767    146593168
+766    1905960961
+765    1741452862
+764    791833829
+763    1041208455
+762    1482443929
+761    167442411
+760    845971422
+759    1615313123
+758    661518876
+757    456861261
+756    178181463
+755    1906279491
+754    514524324
+753    2050738006
+752    974868774
+751    883428538
+750    1147284339
+749    832386064
+748    2141777140
+747    760754020
+746    825583447
+745    1501543394
+744    1155574299
+743    1221665431
+742    1881935076
+741    2057172057
+740    312024541
+739    1315129879
+738    1668373882
+737    739738998
+736    1502242987
+735    864752505
+734    259008932
+733    624391418
+732    195237033
+731    2014614454
+730    1653407182
+729    389110160
+728    1437131660
+727    902723033
+726    2089820585
+725    274927105
+724    931410717
+723    1778579434
+722    903453667
+721    142482709
+720    889135046
+719    386530319
+718    6802617
+717    640233745
+716    1752663369
+715    1751401663
+714    1767091966
+713    1245885890
+712    909640890
+711    566805197
+710    388798174
+709    1719769191
+708    1960370540
+707    803621377
+706    480730065
+705    877851568
+704    669515472
+703    391878126
+702    1118467884
+701    1953610521
+700    577482794
+699    750684149
+698    446773223
+697    1162204555
+696    2118795963
+695    311241151
+694    1518957085
+693    788928008
+692    889444387
+691    516923348
+690    135680092
+689    248901301
+688    781350598
+687    402884602
+686    1020625427
+685    506777479
+684    841760773
+683    1200286768
+682    857087715
+681    1337355347
+680    753918305
+679    1732660445
+678    1239039125
+677    1082518971
+676    134105905
+675    88851939
+674    1906867332
+673    863388599
+672    1961878980
+671    367783734
+670    1506837297
+669    1562761887
+668    779371834
+667    135532072
+666    1790731117
+665    1329867955
+664    1569280411
+663    1002033737
+662    653247916
+661    640543086
+660    1883056398
+659    1880279138
+658    1375759521
+657    274573119
+656    1708607477
+655    1967822307
+654    1797173411
+653    1651889073
+652    446368463
+651    1271910918
+650    98316222
+649    1818882982
+648    1598554540
+647    1150187186
+646    1323135287
+645    1418200954
+644    274456606
+643    1539083598
+642    1504034949
+641    399117093
+640    1735895548
+639    1371305225
+638    1919514417
+637    1596987526
+636    713735309
+635    788697380
+634    676620039
+633    928737325
+632    1266460986
+631    920452426
+630    1412267213
+629    1608483279
+628    171671661
+627    1555420862
+626    624883355
+625    56718403
+624    1521453844
+623    525262493
+622    1553572851
+621    774969129
+620    1820840025
+619    1095612683
+618    495747695
+617    180353586
+616    875730580
+615    1931535337
+614    2061649652
+613    2022823161
+612    1950671697
+611    132729724
+610    627086324
+609    138908022
+608    657569916
+607    1130817037
+606    920367487
+605    1932481632
+604    1669720042
+603    1903651261
+602    1663953760
+601    1805461355
+600    748780765
+599    2004329998
+598    983599924
+597    114953257
+596    33967018
+595    99620862
+594    650629200
+593    746484715
+592    851906116
+591    457960168
+590    279221434
+589    1640486439
+588    219882103
+587    711696006
+586    266187582
+585    1000391067
+584    2128347287
+583    1928919928
+582    1395736837
+581    1811763675
+580    1622643456
+579    1643752935
+578    1366024183
+577    872571932
+576    1608580643
+575    1164199873
+574    268527872
+573    2011742335
+572    1154870496
+571    1807107409
+570    821861431
+569    633827507
+568    1970362980
+567    883979062
+566    1611807705
+565    1434965951
+564    1395198394
+563    192669032
+562    467263281
+561    1358903325
+560    238078064
+559    1715009076
+558    1374298857
+557    1366974684
+556    730832366
+555    484751302
+554    1752137878
+553    316583612
+552    306276471
+551    1899467550
+550    445739492
+549    750071524
+548    35172292
+547    201824309
+546    604044060
+545    1744321956
+544    9329377
+543    608904110
+542    1189880904
+541    521042989
+540    1984228077
+539    2085366017
+538    1169503450
+537    535397028
+536    1636264316
+535    1419138673
+534    967702670
+533    36295069
+532    2102074615
+531    899737853
+530    2132088116
+529    1018587028
+528    984176709
+527    889547555
+526    1762320454
+525    414248754
+524    178474830
+523    2000153976
+522    2018327767
+521    1703688595
+520    1864295258
+519    243915183
+518    146027464
+517    438333984
+516    192494932
+515    2142623597
+514    554441052
+513    1635770036
+512    772159681
+511    1251998535
+510    1499023187
+509    1448831049
+508    449101701
+507    1897848424
+506    1715178006
+505    1599969247
+504    1464547706
+503    67964817
+502    51690601
+501    1083487587
+500    2063044791
+499    1242540561
+498    1403750221
+497    569927955
+496    711072724
+495    1909650126
+494    543404635
+493    622269883
+492    135858718
+491    1774412584
+490    1557661130
+489    1425961274
+488    51420250
+487    150887515
+486    2031376580
+485    704208544
+484    1370463916
+483    1449926165
+482    136746849
+481    1470812280
+480    802896834
+479    1748658410
+478    1881136691
+477    996616102
+476    433300718
+475    1647213188
+474    1548278646
+473    381060118
+472    152403674
+471    956633688
+470    1827221014
+469    1493116836
+468    531467836
+467    1641583743
+466    26523320
+465    88802841
+464    1773791408
+463    916475698
+462    1212092401
+461    857381092
+460    1722992334
+459    1406773615
+458    1542068342
+457    1494695354
+456    927907246
+455    581450415
+454    567461695
+453    2047135284
+452    647029331
+451    535572086
+450    1737159237
+449    1953764380
+448    1315357692
+447    233923502
+446    1595821104
+445    52240600
+444    1494809514
+443    591644958
+442    701322752
+441    806770485
+440    425165851
+439    185637271
+438    1466593516
+437    442664995
+436    2015275982
+435    1257531269
+434    1024194088
+433    916410316
+432    1340967012
+431    1952802433
+430    1462796398
+429    228296980
+428    478866369
+427    960617927
+426    927233658
+425    1028255610
+424    2081904732
+423    31889609
+422    309976047
+421    840748599
+420    1367698042
+419    1503235734
+418    357943276
+417    1263117092
+416    886597636
+415    1004176146
+414    1498401495
+413    688039029
+412    166479106
+411    515685481
+410    1487660617
+409    2129984504
+408    317844936
+407    209062247
+406    1565954555
+405    1098865666
+404    2064047905
+403    1218875302
+402    1601097566
+401    1112670032
+400    1473936064
+399    502178471
+398    1448546969
+397    1598094407
+396    1026196843
+395    895344049
+394    718279562
+393    1241156133
+392    811675214
+391    954223961
+390    482805323
+389    104580950
+388    616638098
+387    1501250778
+386    1912199244
+385    198558607
+384    837697039
+383    982716014
+382    1347862060
+381    183978250
+380    197840545
+379    1278598370
+378    564029948
+377    1366462918
+376    292497990
+375    347079253
+374    1645251747
+373    951377872
+372    1892422886
+371    1098919095
+370    1811606711
+369    2023325305
+368    1623465276
+367    553202920
+366    879814844
+365    1932524358
+364    83668835
+363    1911539249
+362    758350810
+361    707094264
+360    337585862
+359    1129038193
+358    339865354
+357    418079491
+356    663553738
+355    929483230
+354    998180195
+353    653718789
+352    784875469
+351    69263690
+350    1767431950
+349    978861274
+348    986100380
+347    216950695
+346    1868694819
+345    1488603765
+344    602140015
+343    546332652
+342    1287254809
+341    2016581228
+340    1622937467
+339    1258403791
+338    1143510461
+337    1838424566
+336    469534085
+335    1115759243
+334    1174173548
+333    1524058219
+332    1573953387
+331    1776796265
+330    367228910
+329    2066990019
+328    465484454
+327    1557865771
+326    1567382944
+325    9834949
+324    144607761
+323    928916505
+322    1033770487
+321    1953497842
+320    1230646958
+319    1550481254
+318    1257650103
+317    1644980262
+316    1762294328
+315    1322362167
+314    201348956
+313    733042434
+312    1070878832
+311    28851018
+310    873070767
+309    1931996549
+308    788869706
+307    27751218
+306    664251018
+305    1092959514
+304    1689289504
+303    1544860931
+302    1156829309
+301    1654447016
+300    1311311810
+299    956846786
+298    499607074
+297    455649505
+296    1413258010
+295    638466439
+294    1123548110
+293    338593567
+292    1845753195
+291    1630772880
+290    695847739
+289    1733150343
+288    1935670574
+287    2082771584
+286    1443631306
+285    1029251894
+284    251483334
+283    172497937
+282    2007455315
+281    1286365931
+280    1387464960
+279    845319549
+278    1267745531
+277    1843393840
+276    485945362
+275    1266873735
+274    2083613853
+273    34842488
+272    233549120
+271    199982522
+270    1154839941
+269    855662305
+268    1691072424
+267    2008624283
+266    1479585042
+265    1074664443
+264    940196892
+263    1640258878
+262    1790229476
+261    112602851
+260    1842585954
+259    760559802
+258    289519037
+257    906418680
+256    1831288250
+255    1271133369
+254    1169280227
+253    1112601051
+252    932516625
+251    1162135765
+250    18620399
+249    1691554768
+248    359374187
+247    871796
+246    1907263635
+245    451102874
+244    1033324614
+243    1883631330
+242    1027486194
+241    1525370463
+240    656393746
+239    1293699306
+238    1523560911
+237    616407981
+236    1068427390
+235    1986809812
+234    1431918615
+233    827594041
+232    1945156571
+231    1029669673
+230    1970567462
+229    936167274
+228    1076755200
+227    1165869316
+226    1884622101
+225    718687198
+224    338616744
+223    7144461
+222    1093980652
+221    1388445504
+220    802761578
+219    17748603
+218    1931774781
+217    2055754961
+216    1115030830
+215    23632304
+214    1571100327
+213    1655437799
+212    1227237584
+211    1881270536
+210    1809552
+209    39985764
+208    225271916
+207    1684234746
+206    1331973014
+205    240833349
+204    41653241
+203    402248941
+202    1004510226
+201    1008989297
+200    2100398121
+199    804698146
+198    1199028821
+197    358068002
+196    827252572
+195    1877477639
+194    1772190194
+193    1097654887
+192    1351866531
+191    1076232049
+190    1604154371
+189    894490264
+188    1050201421
+187    1908142477
+186    484654634
+185    1607076678
+184    943878368
+183    1837313439
+182    1653628247
+181    1187251820
+180    1655998620
+179    465058453
+178    855496398
+177    2131922214
+176    1642581505
+175    929724073
+174    1383806771
+173    1180147592
+172    449334468
+171    199812080
+170    1957444123
+169    1742330119
+168    2124929222
+167    1469034830
+166    733361455
+165    1877081332
+164    525611108
+163    695958145
+162    1640984164
+161    457376267
+160    26030628
+159    1843495542
+158    409835630
+157    1590608390
+156    964264109
+155    794824842
+154    2100932079
+153    1904110196
+152    181314819
+151    1188569794
+150    331755422
+149    1671560053
+148    969960596
+147    2073255973
+146    748115443
+145    462433913
+144    480389604
+143    1183994691
+142    1370187116
+141    854487997
+140    222366505
+139    488409293
+138    1008968663
+137    247847890
+136    943423722
+135    37403310
+134    236097168
+133    68234841
+132    669927517
+131    1944972270
+130    47540636
+129    582905885
+128    879231433
+127    1762494436
+126    1637159959
+125    1207637561
+124    613510023
+123    912362285
+122    1572354774
+121    657238414
+120    218609198
+119    405983097
+118    923444610
+117    507526682
+116    1592866368
+115    1711604400
+114    1239730445
+113    1773385255
+112    961628185
+111    881777823
+110    1993002982
+109    2122002263
+108    1692469219
+107    971565353
+106    11750722
+105    875188881
+104    1514959440
+103    438608545
+102    20694204
+101    87021632
+100    1065740837
+99     432529848
+98     1093229574
+97     1819077520
+96     1148984413
+95     724797674
+94     1782766435
+93     2103755257
+92     693753087
+91     1166371677
+90     1881277452
+89     1858566163
+88     960600376
+87     1359323857
+86     1415279885
+85     1966964761
+84     749976215
+83     357952622
+82     1927865921
+81     987109570
+80     1336792251
+79     1021437628
+78     2110251541
+77     817280338
+76     1604089561
+75     1720625824
+74     854494676
+73     1427937808
+72     1520351356
+71     1735648004
+70     1141275706
+69     1394146965
+68     1431029083
+67     368431899
+66     36311085
+65     1192712804
+64     31044587
+63     616394758
+62     222477805
+61     982670571
+60     205771300
+59     521953594
+58     443286278
+57     1141119263
+56     609347642
+55     1057327263
+54     39098840
+53     1910350293
+52     1168644018
+51     906428292
+50     1024341676
+49     519511913
+48     1564831715
+47     389625717
+46     2110269310
+45     176151752
+44     200274468
+43     1266330320
+42     286662102
+41     126204390
+40     304618920
+39     772843806
+38     1357835880
+37     238316279
+36     337387312
+35     1567399975
+34     970234999
+33     1195857664
+32     410623457
+31     1848007858
+30     539384293
+29     1212135685
+28     2060089600
+27     1533442662
+26     1102020422
+25     846480997
+24     2036166893
+23     1280154196
+22     886008616
+21     649132105
+20     1489080225
+19     634715959
+18     556726251
+17     1388679963
+16     189351248
+15     843938989
+14     2036973298
+13     74070078
+12     961711400
+11     1661301944
+10     915852158
+9      66302641
+8      435456494
+7      1937919553
+6      1415564928
+5      1289013296
+4      1156776517
+3      1269710788
+2      656473370
+1      1345971420
+0      1935401906
diff --git a/src/test/regress/data/emp.data b/src/test/regress/data/emp.data
new file mode 100644 (file)
index 0000000..5fc17ff
--- /dev/null
@@ -0,0 +1,3 @@
+sharon 25      (15,12) 1000    sam
+sam    30      (10,5)  2000    bill
+bill   20      (11,10) 1000    sharon
diff --git a/src/test/regress/data/hash.data b/src/test/regress/data/hash.data
new file mode 100644 (file)
index 0000000..97e9709
--- /dev/null
@@ -0,0 +1,10000 @@
+0      1935401906
+1      1345971420
+2      656473370
+3      1269710788
+4      1156776517
+5      1289013296
+6      1415564928
+7      1937919553
+8      435456494
+9      66302641
+10     915852158
+11     1661301944
+12     961711400
+13     74070078
+14     2036973298
+15     843938989
+16     189351248
+17     1388679963
+18     556726251
+19     634715959
+20     1489080225
+21     649132105
+22     886008616
+23     1280154196
+24     2036166893
+25     846480997
+26     1102020422
+27     1533442662
+28     2060089600
+29     1212135685
+30     539384293
+31     1848007858
+32     410623457
+33     1195857664
+34     970234999
+35     1567399975
+36     337387312
+37     238316279
+38     1357835880
+39     772843806
+40     304618920
+41     126204390
+42     286662102
+43     1266330320
+44     200274468
+45     176151752
+46     2110269310
+47     389625717
+48     1564831715
+49     519511913
+50     1024341676
+51     906428292
+52     1168644018
+53     1910350293
+54     39098840
+55     1057327263
+56     609347642
+57     1141119263
+58     443286278
+59     521953594
+60     205771300
+61     982670571
+62     222477805
+63     616394758
+64     31044587
+65     1192712804
+66     36311085
+67     368431899
+68     1431029083
+69     1394146965
+70     1141275706
+71     1735648004
+72     1520351356
+73     1427937808
+74     854494676
+75     1720625824
+76     1604089561
+77     817280338
+78     2110251541
+79     1021437628
+80     1336792251
+81     987109570
+82     1927865921
+83     357952622
+84     749976215
+85     1966964761
+86     1415279885
+87     1359323857
+88     960600376
+89     1858566163
+90     1881277452
+91     1166371677
+92     693753087
+93     2103755257
+94     1782766435
+95     724797674
+96     1148984413
+97     1819077520
+98     1093229574
+99     432529848
+100    1065740837
+101    87021632
+102    20694204
+103    438608545
+104    1514959440
+105    875188881
+106    11750722
+107    971565353
+108    1692469219
+109    2122002263
+110    1993002982
+111    881777823
+112    961628185
+113    1773385255
+114    1239730445
+115    1711604400
+116    1592866368
+117    507526682
+118    923444610
+119    405983097
+120    218609198
+121    657238414
+122    1572354774
+123    912362285
+124    613510023
+125    1207637561
+126    1637159959
+127    1762494436
+128    879231433
+129    582905885
+130    47540636
+131    1944972270
+132    669927517
+133    68234841
+134    236097168
+135    37403310
+136    943423722
+137    247847890
+138    1008968663
+139    488409293
+140    222366505
+141    854487997
+142    1370187116
+143    1183994691
+144    480389604
+145    462433913
+146    748115443
+147    2073255973
+148    969960596
+149    1671560053
+150    331755422
+151    1188569794
+152    181314819
+153    1904110196
+154    2100932079
+155    794824842
+156    964264109
+157    1590608390
+158    409835630
+159    1843495542
+160    26030628
+161    457376267
+162    1640984164
+163    695958145
+164    525611108
+165    1877081332
+166    733361455
+167    1469034830
+168    2124929222
+169    1742330119
+170    1957444123
+171    199812080
+172    449334468
+173    1180147592
+174    1383806771
+175    929724073
+176    1642581505
+177    2131922214
+178    855496398
+179    465058453
+180    1655998620
+181    1187251820
+182    1653628247
+183    1837313439
+184    943878368
+185    1607076678
+186    484654634
+187    1908142477
+188    1050201421
+189    894490264
+190    1604154371
+191    1076232049
+192    1351866531
+193    1097654887
+194    1772190194
+195    1877477639
+196    827252572
+197    358068002
+198    1199028821
+199    804698146
+200    2100398121
+201    1008989297
+202    1004510226
+203    402248941
+204    41653241
+205    240833349
+206    1331973014
+207    1684234746
+208    225271916
+209    39985764
+210    1809552
+211    1881270536
+212    1227237584
+213    1655437799
+214    1571100327
+215    23632304
+216    1115030830
+217    2055754961
+218    1931774781
+219    17748603
+220    802761578
+221    1388445504
+222    1093980652
+223    7144461
+224    338616744
+225    718687198
+226    1884622101
+227    1165869316
+228    1076755200
+229    936167274
+230    1970567462
+231    1029669673
+232    1945156571
+233    827594041
+234    1431918615
+235    1986809812
+236    1068427390
+237    616407981
+238    1523560911
+239    1293699306
+240    656393746
+241    1525370463
+242    1027486194
+243    1883631330
+244    1033324614
+245    451102874
+246    1907263635
+247    871796
+248    359374187
+249    1691554768
+250    18620399
+251    1162135765
+252    932516625
+253    1112601051
+254    1169280227
+255    1271133369
+256    1831288250
+257    906418680
+258    289519037
+259    760559802
+260    1842585954
+261    112602851
+262    1790229476
+263    1640258878
+264    940196892
+265    1074664443
+266    1479585042
+267    2008624283
+268    1691072424
+269    855662305
+270    1154839941
+271    199982522
+272    233549120
+273    34842488
+274    2083613853
+275    1266873735
+276    485945362
+277    1843393840
+278    1267745531
+279    845319549
+280    1387464960
+281    1286365931
+282    2007455315
+283    172497937
+284    251483334
+285    1029251894
+286    1443631306
+287    2082771584
+288    1935670574
+289    1733150343
+290    695847739
+291    1630772880
+292    1845753195
+293    338593567
+294    1123548110
+295    638466439
+296    1413258010
+297    455649505
+298    499607074
+299    956846786
+300    1311311810
+301    1654447016
+302    1156829309
+303    1544860931
+304    1689289504
+305    1092959514
+306    664251018
+307    27751218
+308    788869706
+309    1931996549
+310    873070767
+311    28851018
+312    1070878832
+313    733042434
+314    201348956
+315    1322362167
+316    1762294328
+317    1644980262
+318    1257650103
+319    1550481254
+320    1230646958
+321    1953497842
+322    1033770487
+323    928916505
+324    144607761
+325    9834949
+326    1567382944
+327    1557865771
+328    465484454
+329    2066990019
+330    367228910
+331    1776796265
+332    1573953387
+333    1524058219
+334    1174173548
+335    1115759243
+336    469534085
+337    1838424566
+338    1143510461
+339    1258403791
+340    1622937467
+341    2016581228
+342    1287254809
+343    546332652
+344    602140015
+345    1488603765
+346    1868694819
+347    216950695
+348    986100380
+349    978861274
+350    1767431950
+351    69263690
+352    784875469
+353    653718789
+354    998180195
+355    929483230
+356    663553738
+357    418079491
+358    339865354
+359    1129038193
+360    337585862
+361    707094264
+362    758350810
+363    1911539249
+364    83668835
+365    1932524358
+366    879814844
+367    553202920
+368    1623465276
+369    2023325305
+370    1811606711
+371    1098919095
+372    1892422886
+373    951377872
+374    1645251747
+375    347079253
+376    292497990
+377    1366462918
+378    564029948
+379    1278598370
+380    197840545
+381    183978250
+382    1347862060
+383    982716014
+384    837697039
+385    198558607
+386    1912199244
+387    1501250778
+388    616638098
+389    104580950
+390    482805323
+391    954223961
+392    811675214
+393    1241156133
+394    718279562
+395    895344049
+396    1026196843
+397    1598094407
+398    1448546969
+399    502178471
+400    1473936064
+401    1112670032
+402    1601097566
+403    1218875302
+404    2064047905
+405    1098865666
+406    1565954555
+407    209062247
+408    317844936
+409    2129984504
+410    1487660617
+411    515685481
+412    166479106
+413    688039029
+414    1498401495
+415    1004176146
+416    886597636
+417    1263117092
+418    357943276
+419    1503235734
+420    1367698042
+421    840748599
+422    309976047
+423    31889609
+424    2081904732
+425    1028255610
+426    927233658
+427    960617927
+428    478866369
+429    228296980
+430    1462796398
+431    1952802433
+432    1340967012
+433    916410316
+434    1024194088
+435    1257531269
+436    2015275982
+437    442664995
+438    1466593516
+439    185637271
+440    425165851
+441    806770485
+442    701322752
+443    591644958
+444    1494809514
+445    52240600
+446    1595821104
+447    233923502
+448    1315357692
+449    1953764380
+450    1737159237
+451    535572086
+452    647029331
+453    2047135284
+454    567461695
+455    581450415
+456    927907246
+457    1494695354
+458    1542068342
+459    1406773615
+460    1722992334
+461    857381092
+462    1212092401
+463    916475698
+464    1773791408
+465    88802841
+466    26523320
+467    1641583743
+468    531467836
+469    1493116836
+470    1827221014
+471    956633688
+472    152403674
+473    381060118
+474    1548278646
+475    1647213188
+476    433300718
+477    996616102
+478    1881136691
+479    1748658410
+480    802896834
+481    1470812280
+482    136746849
+483    1449926165
+484    1370463916
+485    704208544
+486    2031376580
+487    150887515
+488    51420250
+489    1425961274
+490    1557661130
+491    1774412584
+492    135858718
+493    622269883
+494    543404635
+495    1909650126
+496    711072724
+497    569927955
+498    1403750221
+499    1242540561
+500    2063044791
+501    1083487587
+502    51690601
+503    67964817
+504    1464547706
+505    1599969247
+506    1715178006
+507    1897848424
+508    449101701
+509    1448831049
+510    1499023187
+511    1251998535
+512    772159681
+513    1635770036
+514    554441052
+515    2142623597
+516    192494932
+517    438333984
+518    146027464
+519    243915183
+520    1864295258
+521    1703688595
+522    2018327767
+523    2000153976
+524    178474830
+525    414248754
+526    1762320454
+527    889547555
+528    984176709
+529    1018587028
+530    2132088116
+531    899737853
+532    2102074615
+533    36295069
+534    967702670
+535    1419138673
+536    1636264316
+537    535397028
+538    1169503450
+539    2085366017
+540    1984228077
+541    521042989
+542    1189880904
+543    608904110
+544    9329377
+545    1744321956
+546    604044060
+547    201824309
+548    35172292
+549    750071524
+550    445739492
+551    1899467550
+552    306276471
+553    316583612
+554    1752137878
+555    484751302
+556    730832366
+557    1366974684
+558    1374298857
+559    1715009076
+560    238078064
+561    1358903325
+562    467263281
+563    192669032
+564    1395198394
+565    1434965951
+566    1611807705
+567    883979062
+568    1970362980
+569    633827507
+570    821861431
+571    1807107409
+572    1154870496
+573    2011742335
+574    268527872
+575    1164199873
+576    1608580643
+577    872571932
+578    1366024183
+579    1643752935
+580    1622643456
+581    1811763675
+582    1395736837
+583    1928919928
+584    2128347287
+585    1000391067
+586    266187582
+587    711696006
+588    219882103
+589    1640486439
+590    279221434
+591    457960168
+592    851906116
+593    746484715
+594    650629200
+595    99620862
+596    33967018
+597    114953257
+598    983599924
+599    2004329998
+600    748780765
+601    1805461355
+602    1663953760
+603    1903651261
+604    1669720042
+605    1932481632
+606    920367487
+607    1130817037
+608    657569916
+609    138908022
+610    627086324
+611    132729724
+612    1950671697
+613    2022823161
+614    2061649652
+615    1931535337
+616    875730580
+617    180353586
+618    495747695
+619    1095612683
+620    1820840025
+621    774969129
+622    1553572851
+623    525262493
+624    1521453844
+625    56718403
+626    624883355
+627    1555420862
+628    171671661
+629    1608483279
+630    1412267213
+631    920452426
+632    1266460986
+633    928737325
+634    676620039
+635    788697380
+636    713735309
+637    1596987526
+638    1919514417
+639    1371305225
+640    1735895548
+641    399117093
+642    1504034949
+643    1539083598
+644    274456606
+645    1418200954
+646    1323135287
+647    1150187186
+648    1598554540
+649    1818882982
+650    98316222
+651    1271910918
+652    446368463
+653    1651889073
+654    1797173411
+655    1967822307
+656    1708607477
+657    274573119
+658    1375759521
+659    1880279138
+660    1883056398
+661    640543086
+662    653247916
+663    1002033737
+664    1569280411
+665    1329867955
+666    1790731117
+667    135532072
+668    779371834
+669    1562761887
+670    1506837297
+671    367783734
+672    1961878980
+673    863388599
+674    1906867332
+675    88851939
+676    134105905
+677    1082518971
+678    1239039125
+679    1732660445
+680    753918305
+681    1337355347
+682    857087715
+683    1200286768
+684    841760773
+685    506777479
+686    1020625427
+687    402884602
+688    781350598
+689    248901301
+690    135680092
+691    516923348
+692    889444387
+693    788928008
+694    1518957085
+695    311241151
+696    2118795963
+697    1162204555
+698    446773223
+699    750684149
+700    577482794
+701    1953610521
+702    1118467884
+703    391878126
+704    669515472
+705    877851568
+706    480730065
+707    803621377
+708    1960370540
+709    1719769191
+710    388798174
+711    566805197
+712    909640890
+713    1245885890
+714    1767091966
+715    1751401663
+716    1752663369
+717    640233745
+718    6802617
+719    386530319
+720    889135046
+721    142482709
+722    903453667
+723    1778579434
+724    931410717
+725    274927105
+726    2089820585
+727    902723033
+728    1437131660
+729    389110160
+730    1653407182
+731    2014614454
+732    195237033
+733    624391418
+734    259008932
+735    864752505
+736    1502242987
+737    739738998
+738    1668373882
+739    1315129879
+740    312024541
+741    2057172057
+742    1881935076
+743    1221665431
+744    1155574299
+745    1501543394
+746    825583447
+747    760754020
+748    2141777140
+749    832386064
+750    1147284339
+751    883428538
+752    974868774
+753    2050738006
+754    514524324
+755    1906279491
+756    178181463
+757    456861261
+758    661518876
+759    1615313123
+760    845971422
+761    167442411
+762    1482443929
+763    1041208455
+764    791833829
+765    1741452862
+766    1905960961
+767    146593168
+768    333708212
+769    1426851195
+770    1461723047
+771    645732753
+772    1336539604
+773    1196174476
+774    1867398184
+775    344630255
+776    550234222
+777    545497983
+778    1105384275
+779    544527714
+780    1377884048
+781    105184966
+782    1427956253
+783    205269174
+784    8439325
+785    1942480577
+786    2111548665
+787    186620788
+788    251858191
+789    625583894
+790    1801933912
+791    1097829613
+792    793026305
+793    1136894193
+794    2139038068
+795    1584860134
+796    730863407
+797    1897515381
+798    1731453303
+799    1064571619
+800    1176882929
+801    1045692702
+802    1710304372
+803    365938885
+804    94383530
+805    1430218909
+806    710569141
+807    644617753
+808    1975716892
+809    1815953416
+810    1189145467
+811    1206117292
+812    1921138383
+813    469618072
+814    1411386466
+815    1929577708
+816    264615002
+817    1375451484
+818    2116198496
+819    516473193
+820    2001035378
+821    1770648760
+822    1614302806
+823    646578035
+824    760059306
+825    1605857226
+826    83954521
+827    1490922713
+828    1355888960
+829    1815407824
+830    408010685
+831    385288241
+832    713616879
+833    2118315057
+834    751227126
+835    808000409
+836    1401050318
+837    1461796267
+838    1452618162
+839    1229283563
+840    1130266036
+841    494279982
+842    287917207
+843    903920771
+844    963898054
+845    1699303674
+846    686014831
+847    1228513056
+848    927271510
+849    654729679
+850    1744986249
+851    780823240
+852    277894792
+853    1211805407
+854    1427401275
+855    1037954098
+856    670178986
+857    1511355796
+858    381393163
+859    2026067946
+860    1179279973
+861    789403848
+862    263872539
+863    1892896852
+864    760235258
+865    1015099665
+866    553413613
+867    13801928
+868    329412285
+869    2006031776
+870    1243085491
+871    1459678321
+872    352828110
+873    1531002699
+874    216115444
+875    1316726164
+876    1082822725
+877    902130275
+878    397755573
+879    2010094235
+880    1556859954
+881    2142741822
+882    643433827
+883    1834754746
+884    1207063582
+885    2070835102
+886    725225196
+887    1877242568
+888    1434707250
+889    1106618360
+890    1755826866
+891    466503575
+892    1896022208
+893    2019699405
+894    211916779
+895    508773818
+896    887315422
+897    765330393
+898    522575747
+899    1216727707
+900    623878521
+901    1765661238
+902    528922380
+903    976706631
+904    1149180289
+905    745037824
+906    145949147
+907    84519366
+908    1647168099
+909    543704720
+910    2094613601
+911    1056544406
+912    538962895
+913    590563780
+914    743815504
+915    1746026477
+916    513915234
+917    1469040701
+918    1475785397
+919    1948622485
+920    428175413
+921    1084128615
+922    267642412
+923    176713973
+924    956344372
+925    479559192
+926    685487792
+927    1843659794
+928    1244889585
+929    1208063539
+930    912903854
+931    1868768106
+932    826241129
+933    1441826234
+934    697991089
+935    1975421419
+936    39380411
+937    843940236
+938    2059940785
+939    1686548510
+940    1387644957
+941    2007070739
+942    595609268
+943    1926607852
+944    450150871
+945    1339424773
+946    1525150681
+947    964066106
+948    660981826
+949    853452430
+950    765204943
+951    1089157239
+952    1937581045
+953    1032847355
+954    1265871212
+955    746441769
+956    1512406547
+957    1951359004
+958    442617915
+959    609812484
+960    1011938895
+961    1355521769
+962    331096942
+963    1838180025
+964    649864356
+965    1029088031
+966    1666117796
+967    689244767
+968    1873028268
+969    1578574933
+970    228309629
+971    1113189577
+972    1438162024
+973    823918898
+974    892313781
+975    1888312896
+976    15860023
+977    269980814
+978    704895354
+979    676841849
+980    1123433244
+981    1470100297
+982    1765999088
+983    913530641
+984    355464004
+985    884386652
+986    1659972410
+987    1867870552
+988    688262009
+989    2102590325
+990    330199388
+991    1700200904
+992    1310628447
+993    661296331
+994    1390897281
+995    1960492803
+996    1690384362
+997    909531429
+998    502253922
+999    1415928982
+1000   340622715
+1001   730563551
+1002   381634911
+1003   1778784739
+1004   1554482449
+1005   1273948692
+1006   1519613987
+1007   1570342472
+1008   1543929506
+1009   77025693
+1010   99700673
+1011   519879102
+1012   1547125990
+1013   1865699761
+1014   1433409743
+1015   1902589995
+1016   602602766
+1017   945898505
+1018   1622976899
+1019   1290864775
+1020   901005183
+1021   1953176287
+1022   843582031
+1023   64149982
+1024   466988970
+1025   86995665
+1026   2024642785
+1027   9889685
+1028   996527094
+1029   379413059
+1030   1425818667
+1031   1337149809
+1032   1109976610
+1033   1807453579
+1034   968450901
+1035   516975412
+1036   933918623
+1037   340581240
+1038   2087317884
+1039   330364482
+1040   417606934
+1041   39534910
+1042   850243584
+1043   1964732924
+1044   1905234671
+1045   136169680
+1046   1719839271
+1047   360353789
+1048   1082068185
+1049   1195332522
+1050   1651218564
+1051   1983073368
+1052   1001025162
+1053   347316948
+1054   2047223350
+1055   1468014132
+1056   434312613
+1057   1924382487
+1058   1477903817
+1059   1430839707
+1060   156311898
+1061   756238837
+1062   620505869
+1063   1266288509
+1064   416208768
+1065   1588956770
+1066   1783263921
+1067   1350127391
+1068   1929538010
+1069   1723098157
+1070   1680491873
+1071   199661296
+1072   1762633067
+1073   383251810
+1074   16910573
+1075   1520384091
+1076   519421490
+1077   1736749844
+1078   1880737880
+1079   1601489675
+1080   784598719
+1081   1384472797
+1082   1437079396
+1083   1785623881
+1084   1731789745
+1085   1336819098
+1086   1106154365
+1087   18618710
+1088   1113717938
+1089   436574535
+1090   1449458417
+1091   1270029836
+1092   1192813372
+1093   2069964286
+1094   388834697
+1095   1609022140
+1096   1511437408
+1097   24614970
+1098   811665883
+1099   1293491771
+1100   1747713128
+1101   344674109
+1102   1493153067
+1103   1362862547
+1104   727925919
+1105   1510063640
+1106   735762990
+1107   1247347409
+1108   1099329837
+1109   469017223
+1110   701353436
+1111   1883928556
+1112   1853490020
+1113   2138432832
+1114   1522068789
+1115   1437796117
+1116   1327768283
+1117   480739506
+1118   1456414827
+1119   294002573
+1120   917314041
+1121   758389596
+1122   1564032409
+1123   2110127413
+1124   680870235
+1125   1952867107
+1126   1571665905
+1127   44823995
+1128   1977482077
+1129   235848141
+1130   1338315766
+1131   1577711557
+1132   580522250
+1133   683985186
+1134   793090457
+1135   1308448169
+1136   46565178
+1137   1528853447
+1138   408311930
+1139   1145895015
+1140   1997870670
+1141   1109665366
+1142   882339923
+1143   1703877042
+1144   1100614551
+1145   256925064
+1146   994189511
+1147   280899186
+1148   737664571
+1149   303120690
+1150   574901759
+1151   1654978612
+1152   1061510287
+1153   2138934168
+1154   1617622378
+1155   1742380522
+1156   1944317627
+1157   1041804635
+1158   1787204517
+1159   1774316057
+1160   1277652776
+1161   978036636
+1162   1204543966
+1163   1858175026
+1164   1662021822
+1165   1997634423
+1166   1019139547
+1167   1708587000
+1168   1379004223
+1169   1427451477
+1170   706998368
+1171   1229391245
+1172   389633196
+1173   1589338291
+1174   785784640
+1175   1490247747
+1176   1846263356
+1177   1779974151
+1178   1771146933
+1179   436444279
+1180   2083094842
+1181   198565044
+1182   2091422891
+1183   997121481
+1184   190015564
+1185   1561561621
+1186   592018355
+1187   2134333192
+1188   455882609
+1189   231739224
+1190   1761165601
+1191   1733535385
+1192   1209775860
+1193   818225919
+1194   1444226764
+1195   724314034
+1196   668376695
+1197   315882663
+1198   285417387
+1199   2047380918
+1200   1743334141
+1201   992415755
+1202   1129288515
+1203   2132967337
+1204   434270398
+1205   1915073155
+1206   1475731436
+1207   133050106
+1208   1547563659
+1209   1099394721
+1210   569494385
+1211   1483174853
+1212   1297959765
+1213   513433629
+1214   332812686
+1215   1487975329
+1216   2074995250
+1217   924831041
+1218   1474824873
+1219   383394211
+1220   1156570265
+1221   1088506826
+1222   2116929597
+1223   218862478
+1224   1906732746
+1225   1413672713
+1226   943176512
+1227   427625793
+1228   1729555376
+1229   1228593899
+1230   327523063
+1231   1325405869
+1232   73526006
+1233   1456811578
+1234   1310889558
+1235   507796405
+1236   1224401086
+1237   639137346
+1238   640846511
+1239   624481097
+1240   1738532067
+1241   1210340897
+1242   2107655950
+1243   889008184
+1244   1723774526
+1245   292984988
+1246   229499866
+1247   1651286128
+1248   1217816029
+1249   1704324739
+1250   2034680340
+1251   226902646
+1252   645347918
+1253   2004126289
+1254   445765124
+1255   404597016
+1256   1270315354
+1257   1388941637
+1258   832222809
+1259   852387082
+1260   470051888
+1261   1159745872
+1262   30309304
+1263   543577895
+1264   469073802
+1265   1341198862
+1266   1051374300
+1267   1693474888
+1268   1980336209
+1269   1692220811
+1270   170472337
+1271   1571384628
+1272   755078060
+1273   130644639
+1274   312909165
+1275   331368938
+1276   423629627
+1277   542409031
+1278   1982655067
+1279   1641445656
+1280   99250122
+1281   1869851759
+1282   1868348303
+1283   744598040
+1284   1726494400
+1285   166629779
+1286   1149195056
+1287   849326106
+1288   1555571416
+1289   1981417865
+1290   1701713188
+1291   2025623305
+1292   993680089
+1293   1732022492
+1294   421717552
+1295   1462753892
+1296   925737707
+1297   1473091852
+1298   1008745132
+1299   758590268
+1300   1017829015
+1301   1179217470
+1302   182491248
+1303   1772907076
+1304   1309862109
+1305   495400413
+1306   2104276014
+1307   1733491737
+1308   1037809444
+1309   1939447433
+1310   1227453745
+1311   1137059567
+1312   1661815544
+1313   948318400
+1314   1881657607
+1315   1240826296
+1316   1114948180
+1317   883369016
+1318   2090152402
+1319   523035948
+1320   717303233
+1321   1644381943
+1322   401175605
+1323   1710983323
+1324   1228920787
+1325   822893157
+1326   1026253567
+1327   7174846
+1328   148501361
+1329   2034998699
+1330   765765114
+1331   1166330377
+1332   1066732521
+1333   948256363
+1334   791753805
+1335   229110983
+1336   1443656776
+1337   748546171
+1338   1962602720
+1339   333982573
+1340   540509957
+1341   1042572817
+1342   1471042140
+1343   54841853
+1344   1990891218
+1345   1205216099
+1346   1295668150
+1347   958355750
+1348   2088585115
+1349   1238336904
+1350   1481391698
+1351   658404701
+1352   735235199
+1353   1882567304
+1354   221904376
+1355   1964155987
+1356   557976813
+1357   1248157943
+1358   1971330833
+1359   706478175
+1360   1135672994
+1361   589612300
+1362   1872808552
+1363   54921868
+1364   1537868663
+1365   517078709
+1366   284032851
+1367   834041791
+1368   1265624880
+1369   99151923
+1370   1168024364
+1371   1806134837
+1372   1141724740
+1373   491582856
+1374   1860976691
+1375   985132310
+1376   1696798956
+1377   1009161193
+1378   1943488060
+1379   1637900423
+1380   100014449
+1381   1277396111
+1382   148821476
+1383   835249649
+1384   1012479767
+1385   370725852
+1386   651921988
+1387   1570456580
+1388   1618883795
+1389   475769173
+1390   129451107
+1391   607073142
+1392   1065381473
+1393   2002259659
+1394   661995010
+1395   455766488
+1396   371854720
+1397   946027861
+1398   1289808280
+1399   1637479601
+1400   1045179784
+1401   310348996
+1402   1296130790
+1403   39420876
+1404   801931853
+1405   1009623833
+1406   1024553187
+1407   351247161
+1408   2018785026
+1409   820557599
+1410   1989147584
+1411   2118799476
+1412   2097953710
+1413   2137969061
+1414   806565477
+1415   962949829
+1416   361211265
+1417   1458487465
+1418   385922762
+1419   1980095061
+1420   1934256638
+1421   515373869
+1422   439684555
+1423   852154464
+1424   370149881
+1425   1101679565
+1426   1307920952
+1427   742004601
+1428   2047707426
+1429   450245584
+1430   232000554
+1431   945403562
+1432   760594581
+1433   1528131345
+1434   984824438
+1435   1562526434
+1436   390271530
+1437   2009377625
+1438   1913773595
+1439   261572909
+1440   682451577
+1441   1755437531
+1442   232888737
+1443   632921639
+1444   1745922944
+1445   1039454214
+1446   1595871469
+1447   2107134210
+1448   350458031
+1449   1981794231
+1450   1939745623
+1451   137231021
+1452   349684452
+1453   231946530
+1454   989385485
+1455   719834333
+1456   1333626095
+1457   149822790
+1458   1461838935
+1459   1233849873
+1460   600068374
+1461   1693839489
+1462   31769787
+1463   1360662955
+1464   1074487186
+1465   1016594225
+1466   775705741
+1467   1464758717
+1468   878488203
+1469   541995688
+1470   1726331626
+1471   1560939780
+1472   149949572
+1473   1959220363
+1474   46377771
+1475   1895872516
+1476   851190929
+1477   1642249240
+1478   1855523078
+1479   1201648960
+1480   1476559823
+1481   1647785053
+1482   1338879981
+1483   1826244276
+1484   1879731583
+1485   180781819
+1486   398594961
+1487   1065874030
+1488   330604609
+1489   1860433896
+1490   152240255
+1491   930672983
+1492   1406789738
+1493   184010042
+1494   143852291
+1495   333793276
+1496   1200604268
+1497   919558032
+1498   1798551993
+1499   2079092471
+1500   1461553721
+1501   1377399971
+1502   1492548603
+1503   1611503293
+1504   1189136686
+1505   1538926374
+1506   1359892161
+1507   2040327615
+1508   1033691967
+1509   1067931592
+1510   1094492927
+1511   362768142
+1512   568232997
+1513   285889261
+1514   41528770
+1515   300480933
+1516   466671080
+1517   440123732
+1518   1366354963
+1519   797275689
+1520   153073980
+1521   1518595219
+1522   1727948672
+1523   1559863718
+1524   1702605261
+1525   1871800963
+1526   1893656995
+1527   755725881
+1528   643875348
+1529   1544725340
+1530   687334704
+1531   2105429069
+1532   774641664
+1533   32399659
+1534   1569448714
+1535   1963778350
+1536   1571326034
+1537   781857227
+1538   1856622318
+1539   457534353
+1540   1849788819
+1541   803631597
+1542   820302495
+1543   270538169
+1544   1089520858
+1545   861831266
+1546   571019102
+1547   1556191938
+1548   1301954998
+1549   1937374065
+1550   205983979
+1551   1455028978
+1552   1308485636
+1553   1933932652
+1554   867409049
+1555   863607250
+1556   1658249967
+1557   613582396
+1558   1619333131
+1559   154641667
+1560   10824088
+1561   159184188
+1562   112587088
+1563   785465752
+1564   191583847
+1565   1682035802
+1566   601760455
+1567   1762909881
+1568   316409382
+1569   310899125
+1570   72960586
+1571   18714553
+1572   1114530722
+1573   893263082
+1574   289252722
+1575   56567933
+1576   1755094348
+1577   860271824
+1578   1612759871
+1579   909565698
+1580   650162242
+1581   1818743851
+1582   217111028
+1583   1958647878
+1584   1605192855
+1585   1084520077
+1586   674771480
+1587   1115959174
+1588   1698102473
+1589   146620964
+1590   1270600842
+1591   1708926562
+1592   305805152
+1593   1383187930
+1594   346908666
+1595   497388999
+1596   917740085
+1597   948669121
+1598   112815233
+1599   1234149467
+1600   1259568246
+1601   185775819
+1602   1252864020
+1603   226615321
+1604   1079038901
+1605   1542116743
+1606   283183254
+1607   686649601
+1608   254904919
+1609   1895943125
+1610   1596215299
+1611   905067161
+1612   1567203328
+1613   1813326328
+1614   716231392
+1615   1024912535
+1616   750362757
+1617   1391002872
+1618   2140871710
+1619   300981583
+1620   1537623836
+1621   1263988904
+1622   2009908145
+1623   1843428988
+1624   499693186
+1625   209333163
+1626   193334340
+1627   1417433271
+1628   1158002285
+1629   306149573
+1630   504099090
+1631   270086883
+1632   491925392
+1633   1756963111
+1634   496702204
+1635   1570964294
+1636   1151596206
+1637   779885458
+1638   110130247
+1639   1406501125
+1640   528344936
+1641   1706345547
+1642   164084639
+1643   2095548264
+1644   1372188227
+1645   880316031
+1646   972977152
+1647   2122550984
+1648   123835255
+1649   966365214
+1650   276048919
+1651   1661459092
+1652   82870470
+1653   138473416
+1654   1357404432
+1655   582563656
+1656   347806580
+1657   1550738772
+1658   1999996928
+1659   1505808865
+1660   1856888345
+1661   356612370
+1662   1775895748
+1663   201330090
+1664   2113575481
+1665   125114305
+1666   1772294384
+1667   1117688039
+1668   904999763
+1669   1882424631
+1670   376705517
+1671   1433344699
+1672   1441286530
+1673   540790156
+1674   1381409316
+1675   665991109
+1676   1421106187
+1677   206902820
+1678   641058446
+1679   1544941442
+1680   1173268034
+1681   917107365
+1682   1058916886
+1683   1256138504
+1684   1055580782
+1685   268837671
+1686   1838702160
+1687   1403387362
+1688   1819576443
+1689   1691215440
+1690   761712579
+1691   1528981141
+1692   2047827811
+1693   390124679
+1694   1730311231
+1695   2013919644
+1696   515238984
+1697   1355121967
+1698   984124036
+1699   1420238748
+1700   1090062950
+1701   1360829553
+1702   706099799
+1703   383865833
+1704   1901619709
+1705   2087509115
+1706   1049856942
+1707   1175242248
+1708   146928287
+1709   1690915388
+1710   572700042
+1711   1320196321
+1712   460539106
+1713   1631616929
+1714   428851177
+1715   1516119888
+1716   1900454600
+1717   120069690
+1718   772023602
+1719   1572547395
+1720   1811285130
+1721   1533736181
+1722   954044888
+1723   1711629293
+1724   1923860860
+1725   536872471
+1726   1578065290
+1727   291616197
+1728   1891994438
+1729   414705678
+1730   1711854945
+1731   834573741
+1732   1775535231
+1733   270471096
+1734   1218439574
+1735   1529671292
+1736   210496564
+1737   120812868
+1738   557429892
+1739   357424851
+1740   1811728257
+1741   1130129934
+1742   1677621173
+1743   124783715
+1744   614263215
+1745   2106472350
+1746   1640903603
+1747   367234167
+1748   79058392
+1749   265443557
+1750   1939781563
+1751   1890343523
+1752   1799179738
+1753   746342803
+1754   1454489168
+1755   1575556950
+1756   1283215275
+1757   885070810
+1758   1867173147
+1759   1027726065
+1760   1299776488
+1761   1431544444
+1762   1862299806
+1763   927828071
+1764   1702015541
+1765   933255732
+1766   310015715
+1767   1912512105
+1768   1054068601
+1769   867445607
+1770   122453308
+1771   718313210
+1772   1997575542
+1773   1800074481
+1774   843096925
+1775   464355109
+1776   1759063184
+1777   336516880
+1778   831589277
+1779   1838121576
+1780   601960437
+1781   623887192
+1782   1580981451
+1783   253656527
+1784   1370229995
+1785   887986972
+1786   1829213477
+1787   505961622
+1788   1773057782
+1789   1548902977
+1790   1533687688
+1791   925350623
+1792   832963773
+1793   1248503846
+1794   1853178694
+1795   387495666
+1796   34275931
+1797   15710762
+1798   152524123
+1799   1088344532
+1800   883156369
+1801   274977432
+1802   1806657742
+1803   733248263
+1804   2075051913
+1805   502271019
+1806   1197603373
+1807   1686631449
+1808   838787899
+1809   2029192650
+1810   1377269378
+1811   1440748336
+1812   505596194
+1813   810767181
+1814   1694404863
+1815   1875826189
+1816   1698754153
+1817   1376134692
+1818   234304164
+1819   1324328288
+1820   777554021
+1821   1767991852
+1822   102195263
+1823   1610517795
+1824   869012050
+1825   1955373957
+1826   1998013461
+1827   903287981
+1828   1971084719
+1829   3053937
+1830   1991632513
+1831   706757441
+1832   278031369
+1833   1650806607
+1834   1440005704
+1835   205599634
+1836   5593978
+1837   490125429
+1838   1892231084
+1839   844381877
+1840   371834431
+1841   1122016814
+1842   137646565
+1843   877430625
+1844   1932783995
+1845   1832051428
+1846   605773167
+1847   1484054501
+1848   1060702473
+1849   840077331
+1850   660899141
+1851   1838256494
+1852   460585535
+1853   763094404
+1854   1301290641
+1855   1329597585
+1856   570984713
+1857   1151820455
+1858   85401919
+1859   394585785
+1860   1154874392
+1861   2077034432
+1862   1101343226
+1863   1432905761
+1864   1580357392
+1865   393865282
+1866   1638505395
+1867   1585951370
+1868   883990712
+1869   1383252831
+1870   282849600
+1871   1255825143
+1872   357785997
+1873   420496165
+1874   2133255769
+1875   143086345
+1876   105063946
+1877   591545288
+1878   1627140846
+1879   1165766419
+1880   1431622619
+1881   140556339
+1882   856539265
+1883   1892208154
+1884   903650743
+1885   10346259
+1886   1074322091
+1887   1474635456
+1888   1162166714
+1889   1159724010
+1890   1869221241
+1891   169557458
+1892   1089274795
+1893   823080819
+1894   1602463219
+1895   522148539
+1896   1216946102
+1897   1093484966
+1898   2108099909
+1899   2100936814
+1900   329254150
+1901   243465861
+1902   1209278309
+1903   687040147
+1904   663962027
+1905   1195050430
+1906   830126492
+1907   769025973
+1908   1786595718
+1909   309783690
+1910   1934792392
+1911   1070734689
+1912   450340029
+1913   643848009
+1914   815459195
+1915   1353990772
+1916   654194268
+1917   1889781287
+1918   681142581
+1919   1816360982
+1920   902021649
+1921   402880174
+1922   1985918440
+1923   1991296444
+1924   1225960994
+1925   1440898011
+1926   365961335
+1927   295423448
+1928   386899330
+1929   326577597
+1930   248876614
+1931   716153480
+1932   570043458
+1933   1458154923
+1934   1403193627
+1935   1234005485
+1936   505721706
+1937   85836472
+1938   2003031458
+1939   144833776
+1940   395620162
+1941   1790340202
+1942   1215568466
+1943   845960192
+1944   286704564
+1945   2031027661
+1946   52467316
+1947   940898832
+1948   1773325300
+1949   733609897
+1950   609776167
+1951   527863302
+1952   1136490072
+1953   448210959
+1954   371676098
+1955   214967418
+1956   1889108971
+1957   737637434
+1958   510390866
+1959   128524653
+1960   1064215031
+1961   759267480
+1962   844678133
+1963   1634258489
+1964   69938755
+1965   100388112
+1966   720780327
+1967   575660461
+1968   186224584
+1969   576328137
+1970   720494238
+1971   581844747
+1972   219184692
+1973   1936062704
+1974   1427804939
+1975   505889256
+1976   1819606717
+1977   1480272255
+1978   1446788088
+1979   1445448370
+1980   66398505
+1981   2056564255
+1982   1973311672
+1983   1202888577
+1984   357291567
+1985   197504122
+1986   1417855995
+1987   98916890
+1988   935141556
+1989   1928246861
+1990   227441543
+1991   1999356587
+1992   540030693
+1993   1072119676
+1994   1486131429
+1995   609969448
+1996   1172507788
+1997   59428108
+1998   1185629910
+1999   1358732373
+2000   635756245
+2001   1906124148
+2002   1940577120
+2003   854940937
+2004   1694703204
+2005   1220898411
+2006   1360830193
+2007   1366826273
+2008   553687018
+2009   660134634
+2010   664790995
+2011   620085523
+2012   569215241
+2013   490619019
+2014   1822974100
+2015   926506808
+2016   688123142
+2017   1093346447
+2018   1025423698
+2019   1623264698
+2020   874109660
+2021   1252865241
+2022   1475137638
+2023   1414140353
+2024   177501269
+2025   813785419
+2026   2024109802
+2027   1350009058
+2028   873213527
+2029   1062256064
+2030   561257783
+2031   1508969772
+2032   820896564
+2033   354351255
+2034   216427062
+2035   368116120
+2036   1575249666
+2037   1577257255
+2038   1734942393
+2039   2128936684
+2040   89908241
+2041   252249741
+2042   601538560
+2043   659123483
+2044   742868760
+2045   277029012
+2046   1585630291
+2047   1430991902
+2048   1370375460
+2049   463570342
+2050   906772953
+2051   97001472
+2052   1716435583
+2053   234426943
+2054   1511141826
+2055   1893936853
+2056   1048212362
+2057   1387767980
+2058   1096462263
+2059   1921425889
+2060   302540396
+2061   1657720046
+2062   1282912013
+2063   1123436960
+2064   2012071301
+2065   1499339075
+2066   1491553080
+2067   1439837319
+2068   929112683
+2069   1079011825
+2070   1421290355
+2071   1019020924
+2072   1331261566
+2073   2022828915
+2074   1678144407
+2075   2074130327
+2076   152374280
+2077   1116291051
+2078   1357638581
+2079   1522749740
+2080   1579861393
+2081   116927886
+2082   1619751212
+2083   1148813328
+2084   351354829
+2085   983409390
+2086   895266533
+2087   1399567191
+2088   223693722
+2089   1991728796
+2090   1173509432
+2091   526234118
+2092   1501965194
+2093   308937798
+2094   1649671078
+2095   1366552847
+2096   1808276873
+2097   993740510
+2098   658906518
+2099   589905908
+2100   2072752336
+2101   2080196874
+2102   1608926833
+2103   1256530254
+2104   1955542141
+2105   1139587592
+2106   1183176933
+2107   2107916421
+2108   108394995
+2109   393331867
+2110   1483182513
+2111   1688256388
+2112   510259753
+2113   955450078
+2114   689586069
+2115   861614583
+2116   1938859468
+2117   1584852602
+2118   113698126
+2119   15069543
+2120   1429097751
+2121   1287207559
+2122   541303661
+2123   783579297
+2124   1596145357
+2125   43491092
+2126   2648497
+2127   1256938582
+2128   1037231602
+2129   661555015
+2130   1846844491
+2131   962500290
+2132   594268241
+2133   1308287676
+2134   71546897
+2135   402326735
+2136   300391620
+2137   1254723830
+2138   362759508
+2139   408786616
+2140   1648055697
+2141   1845942022
+2142   2097043004
+2143   10831803
+2144   653908452
+2145   639145425
+2146   872446386
+2147   445284272
+2148   76514380
+2149   986144512
+2150   460353815
+2151   1505612131
+2152   125868423
+2153   1001657477
+2154   141707780
+2155   1722013780
+2156   1045148569
+2157   144356277
+2158   831468715
+2159   2082380171
+2160   805911293
+2161   530829558
+2162   897396814
+2163   1400179534
+2164   1839117234
+2165   968943711
+2166   1802506269
+2167   2139508854
+2168   76183893
+2169   17782130
+2170   400811822
+2171   1724239591
+2172   1863724152
+2173   350371179
+2174   1735071394
+2175   370148956
+2176   989516604
+2177   460034132
+2178   815433228
+2179   1066030984
+2180   1446178644
+2181   1275787044
+2182   424159467
+2183   1572047068
+2184   129960873
+2185   565867248
+2186   1146577200
+2187   1175109442
+2188   710223525
+2189   1978045915
+2190   1110005965
+2191   1516134818
+2192   361391825
+2193   2007402779
+2194   768830705
+2195   53025411
+2196   828862842
+2197   423853326
+2198   45050618
+2199   905046736
+2200   441635456
+2201   445862440
+2202   481802679
+2203   157875960
+2204   796233619
+2205   69390425
+2206   528024916
+2207   1785750224
+2208   529424557
+2209   1343458145
+2210   704297560
+2211   1975603201
+2212   471761541
+2213   1128457028
+2214   1400166621
+2215   601722414
+2216   1694324276
+2217   399260174
+2218   1776831856
+2219   257064153
+2220   229822441
+2221   739354173
+2222   1773198972
+2223   591214267
+2224   599273305
+2225   394546029
+2226   644239678
+2227   1428136147
+2228   818399355
+2229   689290296
+2230   185699235
+2231   1260034812
+2232   1135152737
+2233   667501914
+2234   1417910772
+2235   1931386356
+2236   736892339
+2237   1945935689
+2238   1569652932
+2239   1266316896
+2240   1141910186
+2241   126466845
+2242   1094436450
+2243   1613671727
+2244   1254923873
+2245   347119423
+2246   67910493
+2247   801764501
+2248   746379597
+2249   1844742349
+2250   1058828654
+2251   976202039
+2252   436612874
+2253   684543978
+2254   1567416306
+2255   1035886179
+2256   1079090007
+2257   64172336
+2258   316538679
+2259   1897489363
+2260   753462633
+2261   502237914
+2262   1010040527
+2263   1888615370
+2264   1169739829
+2265   280467651
+2266   1672518078
+2267   1906632168
+2268   78919692
+2269   1094687363
+2270   1025465417
+2271   1220829878
+2272   1221154208
+2273   2119901867
+2274   687017957
+2275   328594433
+2276   319537642
+2277   754928450
+2278   1130358934
+2279   1065917240
+2280   452187151
+2281   41703940
+2282   2042119279
+2283   888800026
+2284   726247919
+2285   1462051937
+2286   1924686205
+2287   1805337926
+2288   1526224273
+2289   93741236
+2290   1555343641
+2291   132203258
+2292   595979151
+2293   417900520
+2294   2020818628
+2295   1765718980
+2296   698368172
+2297   1545853059
+2298   1524867500
+2299   777287864
+2300   493056774
+2301   402849269
+2302   1998117743
+2303   1714210982
+2304   375267488
+2305   537652052
+2306   2042805415
+2307   694805131
+2308   1292580503
+2309   1025680701
+2310   1760722371
+2311   1744767654
+2312   1067384641
+2313   1655358002
+2314   486084032
+2315   1793632560
+2316   969926291
+2317   263286590
+2318   1451486839
+2319   348666916
+2320   357027826
+2321   859346832
+2322   480870175
+2323   953006977
+2324   1277247353
+2325   354205155
+2326   571242309
+2327   1975615525
+2328   1900058214
+2329   2096109810
+2330   605419741
+2331   245631340
+2332   351475431
+2333   456053836
+2334   1959842322
+2335   726742920
+2336   993705889
+2337   1855164089
+2338   1421548051
+2339   138802744
+2340   733361142
+2341   1034786774
+2342   1883570398
+2343   1800745784
+2344   542661128
+2345   222170783
+2346   1446894696
+2347   1512587419
+2348   485457373
+2349   750897887
+2350   1861254335
+2351   842485199
+2352   1610244720
+2353   194640862
+2354   1795492177
+2355   740008425
+2356   548846018
+2357   219250838
+2358   568140302
+2359   301420584
+2360   167877000
+2361   1173560043
+2362   547051925
+2363   519352432
+2364   1629613880
+2365   359410599
+2366   1246095352
+2367   475836121
+2368   67091041
+2369   520159755
+2370   614638865
+2371   800452183
+2372   1554946529
+2373   350725615
+2374   453714319
+2375   2097607657
+2376   572896398
+2377   1900609016
+2378   1462711428
+2379   1058353771
+2380   504023255
+2381   1176482115
+2382   1900838971
+2383   2114267975
+2384   1371122978
+2385   1548847500
+2386   706792752
+2387   1919968996
+2388   1768098338
+2389   1274933054
+2390   73905932
+2391   1935975339
+2392   301009450
+2393   620957857
+2394   307844123
+2395   1930623330
+2396   980368457
+2397   1553939475
+2398   258975803
+2399   1047459498
+2400   2074099230
+2401   873614668
+2402   1847911681
+2403   1481562111
+2404   1224340283
+2405   154142353
+2406   1431686120
+2407   1797236682
+2408   2054751369
+2409   746913900
+2410   708106805
+2411   411290976
+2412   1923396015
+2413   461462128
+2414   378075304
+2415   1147035345
+2416   2010309628
+2417   1084868056
+2418   919520693
+2419   1630924319
+2420   212317463
+2421   993426626
+2422   1419416010
+2423   513326913
+2424   1614384483
+2425   1727260133
+2426   296466595
+2427   447269292
+2428   1133715960
+2429   555442398
+2430   1494728790
+2431   1060331542
+2432   1429057066
+2433   1195156824
+2434   394410005
+2435   505913701
+2436   1349299177
+2437   1826096125
+2438   155666735
+2439   1256566898
+2440   425526377
+2441   863773541
+2442   1667857874
+2443   201438744
+2444   1325235669
+2445   2045933178
+2446   1348474090
+2447   1188061650
+2448   983317587
+2449   120511135
+2450   671502321
+2451   1195635050
+2452   1113937761
+2453   2090918331
+2454   1708961963
+2455   580838597
+2456   1670694816
+2457   2005428558
+2458   1028107889
+2459   656927128
+2460   413387308
+2461   375353032
+2462   1717258670
+2463   1842444374
+2464   1570509856
+2465   2111668675
+2466   200874427
+2467   772325385
+2468   1790281152
+2469   356541163
+2470   2028892283
+2471   68323881
+2472   1220314704
+2473   1549266509
+2474   269762625
+2475   398066725
+2476   1447716040
+2477   1618236715
+2478   1586128375
+2479   283549979
+2480   1738747851
+2481   110147048
+2482   1479185029
+2483   705201964
+2484   53581731
+2485   1040663344
+2486   1286040561
+2487   1724276547
+2488   898608254
+2489   166664803
+2490   233720027
+2491   1311995562
+2492   542017835
+2493   1950978697
+2494   1006956288
+2495   2112527691
+2496   1915163724
+2497   1207830715
+2498   737369428
+2499   1557961228
+2500   1564371878
+2501   618778063
+2502   1626285109
+2503   637202934
+2504   20560924
+2505   1896047735
+2506   1035269660
+2507   1468276964
+2508   1366800802
+2509   473914387
+2510   1751826943
+2511   958065005
+2512   584061436
+2513   1083528324
+2514   1663266970
+2515   637643167
+2516   2124191668
+2517   801823883
+2518   214436067
+2519   875316274
+2520   968488686
+2521   448156094
+2522   39828188
+2523   1510506521
+2524   251651144
+2525   1046784476
+2526   1475550564
+2527   19331220
+2528   107131544
+2529   65436344
+2530   1577292449
+2531   1671503422
+2532   684214407
+2533   1056093910
+2534   161222709
+2535   704775332
+2536   804657997
+2537   1196492369
+2538   25568648
+2539   23975152
+2540   1670406756
+2541   1777395592
+2542   982040157
+2543   106984544
+2544   713440268
+2545   497823479
+2546   744627712
+2547   690148289
+2548   1299647363
+2549   959063779
+2550   1565464563
+2551   120652401
+2552   1407219873
+2553   1605292752
+2554   1631158923
+2555   1658871017
+2556   504593580
+2557   959225839
+2558   1678202238
+2559   611725124
+2560   1024662184
+2561   1108011039
+2562   135744899
+2563   1708876591
+2564   16621301
+2565   296967608
+2566   266168275
+2567   821279299
+2568   1493459977
+2569   291736924
+2570   845254451
+2571   1016383085
+2572   2069132516
+2573   1827294608
+2574   1123367630
+2575   635089136
+2576   177634440
+2577   1867995342
+2578   1325237425
+2579   1477281803
+2580   679575473
+2581   743218341
+2582   1597934204
+2583   2086795346
+2584   201027445
+2585   1081609479
+2586   1598182716
+2587   705621025
+2588   2040835319
+2589   1128901306
+2590   1317346150
+2591   918013855
+2592   89428697
+2593   1453091049
+2594   479406798
+2595   106049998
+2596   1750058657
+2597   745575074
+2598   927329297
+2599   1096034986
+2600   1037311998
+2601   1772583748
+2602   2112418071
+2603   958960866
+2604   1452394709
+2605   1088302053
+2606   1594050002
+2607   1630029149
+2608   808813747
+2609   771803780
+2610   959827304
+2611   1488389220
+2612   1515022121
+2613   410277860
+2614   1427700919
+2615   1716049566
+2616   1491887340
+2617   878399987
+2618   274186943
+2619   1385239011
+2620   2007301293
+2621   1591533093
+2622   155769218
+2623   2096729990
+2624   897140494
+2625   635176016
+2626   55296340
+2627   499715503
+2628   1380751090
+2629   982625638
+2630   1595750489
+2631   270579440
+2632   607725738
+2633   1560684913
+2634   1229540306
+2635   2060120447
+2636   501503318
+2637   676106661
+2638   1542665948
+2639   1310317066
+2640   1447910441
+2641   355009604
+2642   651222638
+2643   815448914
+2644   765287465
+2645   2078923557
+2646   384014832
+2647   109691157
+2648   809839896
+2649   658201775
+2650   1494930168
+2651   669657541
+2652   102251221
+2653   1650699386
+2654   618903883
+2655   999391715
+2656   138391754
+2657   674200224
+2658   1499107219
+2659   1519142845
+2660   1656825862
+2661   947374060
+2662   1789722285
+2663   117067952
+2664   360575325
+2665   871778944
+2666   29704752
+2667   862078644
+2668   1547885605
+2669   1572370700
+2670   24912062
+2671   848312398
+2672   1927380305
+2673   676134700
+2674   1663761312
+2675   545184122
+2676   607574610
+2677   2047776144
+2678   654875279
+2679   1417414506
+2680   558494271
+2681   2321799
+2682   2087072048
+2683   660745492
+2684   1653021185
+2685   558492283
+2686   1660137208
+2687   1791412939
+2688   1232692507
+2689   1011760779
+2690   1163072136
+2691   742034721
+2692   1959134839
+2693   805310774
+2694   859102674
+2695   172226517
+2696   1677089718
+2697   888807426
+2698   1034305161
+2699   1077491675
+2700   313694478
+2701   1059217223
+2702   1925804073
+2703   93591135
+2704   1735351923
+2705   1442081737
+2706   638775257
+2707   195442885
+2708   1342374233
+2709   1293650536
+2710   1612857392
+2711   1900868504
+2712   1295972335
+2713   1552445792
+2714   414130349
+2715   801509872
+2716   2110938075
+2717   2074267557
+2718   445439164
+2719   1196146935
+2720   938544688
+2721   1608511300
+2722   1938181656
+2723   750195879
+2724   266338426
+2725   649800682
+2726   922422396
+2727   1943428144
+2728   1538608108
+2729   1956727557
+2730   873436171
+2731   1852302587
+2732   868461132
+2733   651756596
+2734   1945893722
+2735   456329408
+2736   2093838333
+2737   437185332
+2738   651772293
+2739   1288728918
+2740   1730835868
+2741   117146037
+2742   1042113775
+2743   879324556
+2744   1669591829
+2745   1456244124
+2746   1680834428
+2747   1633046257
+2748   1383028033
+2749   2126273592
+2750   681709544
+2751   174089073
+2752   1587301245
+2753   472407552
+2754   924284952
+2755   1853639671
+2756   1122208235
+2757   1846707349
+2758   1649584168
+2759   513332695
+2760   1655951258
+2761   375536691
+2762   218151634
+2763   376928743
+2764   1027293288
+2765   16561709
+2766   833258151
+2767   973647973
+2768   453747041
+2769   1485030444
+2770   114893244
+2771   37099261
+2772   1602176482
+2773   1157007019
+2774   916423817
+2775   1124284663
+2776   465767495
+2777   449774598
+2778   609847272
+2779   1848795528
+2780   428564542
+2781   1291556816
+2782   2022884601
+2783   2015865787
+2784   1763964369
+2785   799685905
+2786   1722021811
+2787   738688956
+2788   498909606
+2789   1224122331
+2790   1252021651
+2791   7377217
+2792   1599659022
+2793   1470173286
+2794   384305960
+2795   479468662
+2796   1486734995
+2797   1217564111
+2798   1453116636
+2799   1940482036
+2800   555110907
+2801   1568009880
+2802   1977581297
+2803   9803741
+2804   577533251
+2805   746521467
+2806   1134088405
+2807   1043300746
+2808   1196296065
+2809   1743935677
+2810   744612626
+2811   1624860607
+2812   888008846
+2813   620013579
+2814   1493242747
+2815   504489567
+2816   1419699484
+2817   1067780910
+2818   1243178523
+2819   1918609091
+2820   144419593
+2821   347716526
+2822   1925986308
+2823   1744078615
+2824   1817889812
+2825   162808620
+2826   76063630
+2827   1157141159
+2828   1380372731
+2829   1529180266
+2830   950139547
+2831   1935483638
+2832   949706498
+2833   780237197
+2834   1945287380
+2835   1527239749
+2836   1526758664
+2837   931892137
+2838   423056847
+2839   575571081
+2840   528344166
+2841   1167669473
+2842   52948040
+2843   1416353012
+2844   1787683052
+2845   1546190787
+2846   1920842579
+2847   1059898888
+2848   466488049
+2849   1016537454
+2850   831024331
+2851   610907642
+2852   1364253981
+2853   609526991
+2854   207502610
+2855   1034660145
+2856   772335611
+2857   283566240
+2858   44317657
+2859   5224694
+2860   1812746506
+2861   994457204
+2862   1940708333
+2863   614969356
+2864   1774694401
+2865   1738512065
+2866   2142209105
+2867   1153969417
+2868   522920554
+2869   417782304
+2870   1729540498
+2871   1051264720
+2872   1585451777
+2873   1782488539
+2874   320134085
+2875   1225651181
+2876   1181195678
+2877   93493016
+2878   138066421
+2879   1647683728
+2880   1110030471
+2881   969090753
+2882   111107722
+2883   326800804
+2884   1578617744
+2885   318610332
+2886   1361460949
+2887   203469708
+2888   602176572
+2889   1405778606
+2890   208694402
+2891   267439430
+2892   252752163
+2893   1919087
+2894   882408786
+2895   2027446564
+2896   1740431152
+2897   877134243
+2898   1033932334
+2899   115868058
+2900   1294916547
+2901   615989184
+2902   1167132779
+2903   732884676
+2904   250994075
+2905   1487266864
+2906   1958535857
+2907   1432189754
+2908   1580759880
+2909   2096602279
+2910   932389834
+2911   543306703
+2912   918209384
+2913   1043497556
+2914   870107507
+2915   349343480
+2916   1362107889
+2917   84084809
+2918   552813188
+2919   1964284461
+2920   1489863415
+2921   761507591
+2922   84240244
+2923   1742615578
+2924   763426678
+2925   966649030
+2926   1622578495
+2927   356374183
+2928   1843783274
+2929   509027181
+2930   472242241
+2931   991216173
+2932   1125016365
+2933   1639375020
+2934   1724100850
+2935   1376010441
+2936   979158236
+2937   1535153059
+2938   660716547
+2939   412434469
+2940   1484271690
+2941   1593106381
+2942   955741172
+2943   254997426
+2944   489120289
+2945   1825848680
+2946   604340907
+2947   1851228178
+2948   1909933489
+2949   1157154095
+2950   1668028992
+2951   1252313256
+2952   1918661686
+2953   1752269236
+2954   847445187
+2955   534604717
+2956   571434618
+2957   322540034
+2958   890978900
+2959   267734244
+2960   831567215
+2961   1363221141
+2962   1258950418
+2963   1956583580
+2964   855112514
+2965   835567620
+2966   1185110373
+2967   1834270750
+2968   223237031
+2969   1845826920
+2970   99221571
+2971   1707508722
+2972   1291449653
+2973   1054962744
+2974   1962506148
+2975   1780569943
+2976   733327776
+2977   419363407
+2978   1484314473
+2979   495777617
+2980   1576517503
+2981   1004859817
+2982   1748090873
+2983   1347695541
+2984   609645405
+2985   448052412
+2986   1882300258
+2987   1181080024
+2988   770592446
+2989   625795510
+2990   1448814268
+2991   1602159661
+2992   1989016652
+2993   560281038
+2994   1411259594
+2995   696645518
+2996   1395848658
+2997   448886319
+2998   383432620
+2999   1619085690
+3000   147229592
+3001   482654192
+3002   1179110764
+3003   1438679245
+3004   1537616936
+3005   994133264
+3006   1071765540
+3007   123461064
+3008   1413496672
+3009   408596366
+3010   619238681
+3011   842530527
+3012   1413456183
+3013   219845906
+3014   42742420
+3015   2023101589
+3016   667898319
+3017   1925042679
+3018   1056697965
+3019   1438490765
+3020   403354541
+3021   358028585
+3022   893166779
+3023   244887545
+3024   918309624
+3025   156942725
+3026   941533063
+3027   166674634
+3028   605829044
+3029   1324965684
+3030   1785760324
+3031   753058636
+3032   1807619876
+3033   817387440
+3034   44254234
+3035   1197753164
+3036   1811520705
+3037   1116019774
+3038   1321214228
+3039   1077533729
+3040   1524616140
+3041   1940452909
+3042   1920064256
+3043   790588676
+3044   12815167
+3045   1962806676
+3046   666206617
+3047   680713486
+3048   1740365707
+3049   1722904582
+3050   2119204252
+3051   2143720249
+3052   2080933167
+3053   864887383
+3054   241124146
+3055   851759143
+3056   1021830108
+3057   1182657210
+3058   1018433778
+3059   1627659152
+3060   360139246
+3061   656710454
+3062   233234141
+3063   20275474
+3064   1474097895
+3065   277488375
+3066   1218028638
+3067   1138134952
+3068   1393508149
+3069   391759218
+3070   68185033
+3071   770640642
+3072   184728479
+3073   1988249289
+3074   1561229318
+3075   197543646
+3076   1803572317
+3077   79952287
+3078   878257133
+3079   1396454377
+3080   1802856869
+3081   849977737
+3082   1392690978
+3083   1736306388
+3084   1714865120
+3085   1633815124
+3086   440581884
+3087   589211580
+3088   668988686
+3089   1459015662
+3090   69387084
+3091   1029127932
+3092   2115726116
+3093   302621225
+3094   1049403406
+3095   1442340363
+3096   580109600
+3097   119948396
+3098   432991667
+3099   1973617750
+3100   511707614
+3101   501176700
+3102   596774744
+3103   696436093
+3104   341942341
+3105   10520414
+3106   893979740
+3107   2145514659
+3108   90472701
+3109   1772236873
+3110   1394485388
+3111   1893329570
+3112   474730962
+3113   639692718
+3114   1482152310
+3115   42112434
+3116   126024194
+3117   1922734194
+3118   631324014
+3119   795012881
+3120   1234266208
+3121   700711098
+3122   1824140813
+3123   1202508677
+3124   1003332324
+3125   726060572
+3126   497365392
+3127   1583441924
+3128   846008968
+3129   930357060
+3130   1409576026
+3131   1357716583
+3132   1431533760
+3133   2006350770
+3134   2054152676
+3135   1773476102
+3136   2016871184
+3137   800648768
+3138   1771507113
+3139   2107343885
+3140   425401993
+3141   1018508853
+3142   1853189807
+3143   900132955
+3144   1658201571
+3145   1187858470
+3146   942245389
+3147   1784225765
+3148   963109016
+3149   1573569403
+3150   431754998
+3151   49891577
+3152   126796854
+3153   108412164
+3154   1252400254
+3155   1130129178
+3156   834472736
+3157   1749765646
+3158   566087454
+3159   1680481704
+3160   532639058
+3161   1975663481
+3162   890714639
+3163   1964172819
+3164   1834530603
+3165   797383668
+3166   1590165273
+3167   1703918140
+3168   1598032436
+3169   1214188738
+3170   1663778377
+3171   2023434430
+3172   85213943
+3173   1369484537
+3174   776083737
+3175   1743415514
+3176   409859359
+3177   1718329127
+3178   1380157631
+3179   1372968375
+3180   1144414882
+3181   1811912630
+3182   1422859952
+3183   1271211736
+3184   1920324794
+3185   527776558
+3186   253857266
+3187   607313882
+3188   130058557
+3189   819944721
+3190   140311938
+3191   662697615
+3192   648124554
+3193   1031026578
+3194   479386786
+3195   335171509
+3196   1828410246
+3197   2069552059
+3198   2039089649
+3199   1278959034
+3200   1136257149
+3201   1555384379
+3202   1154909816
+3203   1221471092
+3204   777385268
+3205   1930993554
+3206   817402958
+3207   1187244627
+3208   1501839033
+3209   50076942
+3210   412729354
+3211   498770267
+3212   1861989572
+3213   1835589307
+3214   1769982004
+3215   1634830718
+3216   215882217
+3217   2023839270
+3218   94660952
+3219   345940774
+3220   696300343
+3221   234972890
+3222   1008638390
+3223   1344424897
+3224   1265999468
+3225   1488025176
+3226   1679596407
+3227   946926066
+3228   1410093588
+3229   1571202408
+3230   78401453
+3231   398867089
+3232   979103139
+3233   1233311269
+3234   1620338182
+3235   1756488407
+3236   1016821175
+3237   290257492
+3238   796249386
+3239   371176560
+3240   340334434
+3241   1208978741
+3242   869946828
+3243   54840358
+3244   897084400
+3245   492445184
+3246   1689671076
+3247   1112966617
+3248   368800806
+3249   1784332028
+3250   1458907392
+3251   1065101150
+3252   2019304919
+3253   320062134
+3254   262042399
+3255   1137820739
+3256   1808087310
+3257   1941638806
+3258   2084746806
+3259   1070697250
+3260   1365357567
+3261   15664611
+3262   1469564340
+3263   196977058
+3264   1248975880
+3265   942418874
+3266   1953465466
+3267   118313408
+3268   1232676366
+3269   602231204
+3270   489489968
+3271   1573010801
+3272   1811209945
+3273   1359436796
+3274   1627851159
+3275   560810697
+3276   1851881980
+3277   1170038588
+3278   1673777315
+3279   73199139
+3280   806886968
+3281   985201059
+3282   1138300289
+3283   678708239
+3284   1305263193
+3285   1400342688
+3286   1816528979
+3287   965866855
+3288   1194497847
+3289   1753792137
+3290   2036564106
+3291   412371766
+3292   1769456748
+3293   1358644798
+3294   609348824
+3295   870948980
+3296   153580024
+3297   415330642
+3298   989262388
+3299   1386256390
+3300   1017561847
+3301   1478752357
+3302   811783543
+3303   681288144
+3304   690705505
+3305   292151055
+3306   1242098842
+3307   395103838
+3308   1462189643
+3309   768392509
+3310   468302977
+3311   121592963
+3312   1753593568
+3313   1606603266
+3314   800301203
+3315   911373113
+3316   859462306
+3317   469346534
+3318   1877239968
+3319   2053960153
+3320   75655023
+3321   1766320426
+3322   318848271
+3323   1845111771
+3324   977481576
+3325   928197096
+3326   568577103
+3327   1131061600
+3328   1343527738
+3329   1557839492
+3330   369834343
+3331   213605937
+3332   889108201
+3333   1181617886
+3334   894894082
+3335   1579813706
+3336   1473768941
+3337   2136992924
+3338   1974917544
+3339   788474936
+3340   757901785
+3341   295736873
+3342   910067900
+3343   364011705
+3344   1902340139
+3345   1710369103
+3346   1275384818
+3347   614318798
+3348   32231989
+3349   1005141138
+3350   520795303
+3351   107887012
+3352   623977917
+3353   839643575
+3354   1952998783
+3355   1601459493
+3356   1767840671
+3357   374092238
+3358   585037446
+3359   963884761
+3360   1931931730
+3361   954871789
+3362   1177490699
+3363   673556283
+3364   2136489675
+3365   2072384781
+3366   105886342
+3367   1462774969
+3368   2061894057
+3369   2080803886
+3370   103766257
+3371   672312194
+3372   229057112
+3373   1013834157
+3374   1036323899
+3375   2131397251
+3376   576719612
+3377   164225069
+3378   598232401
+3379   608951601
+3380   1169366207
+3381   1119027705
+3382   716838613
+3383   1793344124
+3384   1958671280
+3385   522353748
+3386   1247319970
+3387   1579028303
+3388   896445987
+3389   1832357416
+3390   395429416
+3391   680894069
+3392   639745557
+3393   1572920115
+3394   1354450353
+3395   628751584
+3396   1497821248
+3397   1460336695
+3398   2091526553
+3399   1412231657
+3400   1393656933
+3401   47809163
+3402   2084543851
+3403   1622714045
+3404   1061643320
+3405   973384102
+3406   1606627649
+3407   1638362933
+3408   1137609171
+3409   57376402
+3410   99830886
+3411   159491731
+3412   1176404107
+3413   816669500
+3414   1952835855
+3415   987591739
+3416   1339023248
+3417   1052672177
+3418   419136394
+3419   87985587
+3420   737545945
+3421   814565811
+3422   768879657
+3423   1377291502
+3424   240002278
+3425   2123330010
+3426   2006043087
+3427   1737823527
+3428   1436183057
+3429   1950085992
+3430   1002571536
+3431   682356342
+3432   1997895155
+3433   939631740
+3434   157586740
+3435   912054828
+3436   1913015842
+3437   1764214389
+3438   402934113
+3439   903141366
+3440   1821590791
+3441   502764999
+3442   1062633097
+3443   850511251
+3444   1319434499
+3445   867985304
+3446   1838102990
+3447   510974100
+3448   1920657482
+3449   109755737
+3450   598959687
+3451   510719779
+3452   924321548
+3453   1367839344
+3454   1888011282
+3455   1164323826
+3456   1343685706
+3457   1746570721
+3458   754663705
+3459   632385115
+3460   1549173065
+3461   1757235242
+3462   1314741458
+3463   1399584573
+3464   549383334
+3465   1472328198
+3466   164155753
+3467   314915528
+3468   1089058939
+3469   567089866
+3470   1218056894
+3471   763166082
+3472   1069854865
+3473   133206343
+3474   1613677333
+3475   241805717
+3476   1001191648
+3477   1304296676
+3478   752779817
+3479   774365482
+3480   1414052413
+3481   1351739504
+3482   1285085261
+3483   190890313
+3484   572095201
+3485   1025612895
+3486   1355214139
+3487   1915780907
+3488   624699968
+3489   2109877845
+3490   400682375
+3491   26389386
+3492   1719629439
+3493   1715423833
+3494   1425973959
+3495   121529125
+3496   1040268383
+3497   1590129712
+3498   436444653
+3499   2129327322
+3500   9735930
+3501   1654501548
+3502   745009756
+3503   1079590795
+3504   1787707891
+3505   211203442
+3506   1321396512
+3507   641415891
+3508   1515500118
+3509   2074176329
+3510   1415781373
+3511   782068883
+3512   1278432186
+3513   553382987
+3514   972959196
+3515   1850527387
+3516   1578995882
+3517   180689687
+3518   1618824646
+3519   56212203
+3520   143083884
+3521   2019507021
+3522   82601589
+3523   1862713323
+3524   1587447206
+3525   1508575548
+3526   1984242448
+3527   480231941
+3528   951221612
+3529   273203454
+3530   462075615
+3531   960957542
+3532   1927705002
+3533   1207085372
+3534   2040548337
+3535   1567929245
+3536   1418288814
+3537   1214461202
+3538   61861489
+3539   786305284
+3540   1141153883
+3541   1477642862
+3542   1568374167
+3543   272102421
+3544   2031025849
+3545   393849715
+3546   2122629808
+3547   1462538084
+3548   574539402
+3549   1593970807
+3550   1518750287
+3551   717623287
+3552   1465994180
+3553   1601351876
+3554   432852962
+3555   905957739
+3556   962443776
+3557   269611763
+3558   1386189680
+3559   1913665388
+3560   542815217
+3561   1848265296
+3562   727139282
+3563   323036571
+3564   907867020
+3565   620203971
+3566   1890965816
+3567   178672186
+3568   1834665173
+3569   1952827305
+3570   964977470
+3571   828335409
+3572   1282986520
+3573   385867989
+3574   1100437830
+3575   1166528721
+3576   779717704
+3577   1075583991
+3578   481583157
+3579   1354257106
+3580   522071150
+3581   2000333444
+3582   2071880393
+3583   1988065330
+3584   1454201672
+3585   357249708
+3586   746539421
+3587   269161800
+3588   626861471
+3589   2132729102
+3590   35343540
+3591   1169676688
+3592   1833510750
+3593   762482822
+3594   1492713259
+3595   593894122
+3596   1382686794
+3597   1236195427
+3598   772566308
+3599   1069868319
+3600   1041539085
+3601   1737543778
+3602   1898203728
+3603   177041957
+3604   2123411767
+3605   851157911
+3606   1343570678
+3607   755645823
+3608   1926741902
+3609   1825153836
+3610   2109902929
+3611   301329404
+3612   1678003632
+3613   2034299675
+3614   141911086
+3615   984721657
+3616   244065735
+3617   888450508
+3618   1253883457
+3619   870927206
+3620   873695962
+3621   1289226998
+3622   2040603894
+3623   559723064
+3624   2051709820
+3625   1385833505
+3626   1153617186
+3627   1286912966
+3628   474545284
+3629   1926183494
+3630   209297638
+3631   1516084369
+3632   1516243624
+3633   2107501366
+3634   1693126326
+3635   1492171743
+3636   811175629
+3637   889213357
+3638   100333918
+3639   590433883
+3640   566883545
+3641   62753199
+3642   891763287
+3643   97403529
+3644   2097052874
+3645   1033674374
+3646   1082125186
+3647   193634961
+3648   1922124882
+3649   188524996
+3650   1064562167
+3651   648337196
+3652   1477751994
+3653   957682413
+3654   1208060260
+3655   1381978166
+3656   196032270
+3657   214193798
+3658   521407485
+3659   670577555
+3660   2140377292
+3661   730705123
+3662   39178276
+3663   1509137268
+3664   690722841
+3665   1732304603
+3666   853825363
+3667   1501898471
+3668   474034312
+3669   954159281
+3670   2092332354
+3671   1040917857
+3672   1016912480
+3673   836611994
+3674   1138321386
+3675   966481707
+3676   1870286368
+3677   72962925
+3678   1160116668
+3679   1644927602
+3680   261487921
+3681   77195188
+3682   145781150
+3683   1739239915
+3684   1034877601
+3685   1353841410
+3686   973734433
+3687   1230909872
+3688   1568035208
+3689   1495141918
+3690   1901487427
+3691   1560928852
+3692   78363393
+3693   1940665703
+3694   922582472
+3695   769086235
+3696   1525486658
+3697   1776407835
+3698   123501058
+3699   1999520970
+3700   583083468
+3701   68349764
+3702   892955179
+3703   1599995948
+3704   904961758
+3705   2031276566
+3706   418994007
+3707   627764478
+3708   2104239491
+3709   1579110676
+3710   125208432
+3711   218243764
+3712   1656305864
+3713   270989582
+3714   1957483679
+3715   543699817
+3716   1624830992
+3717   783734464
+3718   1774609689
+3719   1045382552
+3720   131392735
+3721   1528613468
+3722   458827756
+3723   209756128
+3724   1321795524
+3725   1381410228
+3726   978842363
+3727   699798534
+3728   1010334415
+3729   1102343421
+3730   551835857
+3731   1593417883
+3732   1170693186
+3733   1444791036
+3734   1045930184
+3735   2075654944
+3736   1328583954
+3737   1464924191
+3738   555935775
+3739   1285339797
+3740   896551219
+3741   681144207
+3742   1503583561
+3743   405373435
+3744   952133790
+3745   1313583592
+3746   949073253
+3747   429481134
+3748   2097318057
+3749   576199294
+3750   1474863687
+3751   81227144
+3752   2104812763
+3753   1933691443
+3754   290983272
+3755   1279124639
+3756   1167618024
+3757   1269825636
+3758   1978923173
+3759   30468791
+3760   224685409
+3761   383275382
+3762   1623886675
+3763   1395378595
+3764   1828066419
+3765   522333211
+3766   1323549892
+3767   1009166725
+3768   1987257402
+3769   1879485667
+3770   147022875
+3771   736324974
+3772   413146226
+3773   1650606436
+3774   1141698409
+3775   1365280016
+3776   816706381
+3777   2090771662
+3778   1794761151
+3779   766540790
+3780   519487309
+3781   1122141190
+3782   847767934
+3783   476816424
+3784   908348985
+3785   1138751206
+3786   1755941063
+3787   2075967009
+3788   261093194
+3789   1587380588
+3790   2106435801
+3791   485778604
+3792   1970655971
+3793   1582838828
+3794   1881157199
+3795   1651238742
+3796   2105172039
+3797   1057223443
+3798   512921819
+3799   1944945793
+3800   789225462
+3801   659944694
+3802   533787119
+3803   1202371689
+3804   163067483
+3805   1675485529
+3806   420168057
+3807   979773864
+3808   1618773543
+3809   67445560
+3810   1746314654
+3811   2138260852
+3812   1189586750
+3813   446598940
+3814   467593628
+3815   2097935736
+3816   1585350146
+3817   76051043
+3818   2026419097
+3819   1846443341
+3820   1663431632
+3821   1985371250
+3822   184738297
+3823   1486603955
+3824   1420726430
+3825   2065895496
+3826   990359049
+3827   1378414821
+3828   975635292
+3829   1503280868
+3830   1175876967
+3831   1764860754
+3832   15741915
+3833   1709664086
+3834   819748795
+3835   178809398
+3836   1237665967
+3837   1239916853
+3838   1158583262
+3839   708955863
+3840   1307362413
+3841   757414268
+3842   699733067
+3843   349465516
+3844   1204013208
+3845   1167326696
+3846   299917604
+3847   641879706
+3848   1243377739
+3849   178853053
+3850   340839399
+3851   759325723
+3852   16740656
+3853   525577696
+3854   98446030
+3855   1437467086
+3856   443989545
+3857   1088805079
+3858   668398260
+3859   1419624837
+3860   444602300
+3861   1844275227
+3862   1037001943
+3863   460344215
+3864   1406455665
+3865   1856750739
+3866   639153613
+3867   496637985
+3868   949183944
+3869   1797736875
+3870   1205593848
+3871   109062709
+3872   407667495
+3873   1905326915
+3874   458528225
+3875   1611680703
+3876   925169963
+3877   758445829
+3878   106076761
+3879   21064055
+3880   937298883
+3881   446916161
+3882   780389778
+3883   954039539
+3884   972493857
+3885   878835809
+3886   244022977
+3887   1416483402
+3888   1967640888
+3889   912421237
+3890   688624591
+3891   264759540
+3892   609212816
+3893   1725626535
+3894   725103755
+3895   2015668482
+3896   1434893626
+3897   1364257368
+3898   364822819
+3899   236593922
+3900   1014510595
+3901   1570416667
+3902   345656631
+3903   1422178090
+3904   1328259934
+3905   804184857
+3906   886375145
+3907   105946250
+3908   1562630686
+3909   992451907
+3910   127010305
+3911   352445921
+3912   1439368068
+3913   907400083
+3914   1306485460
+3915   264378277
+3916   1786235892
+3917   1550508438
+3918   1680861680
+3919   1606393133
+3920   315446027
+3921   222002623
+3922   1871152673
+3923   924658844
+3924   1947629158
+3925   448772781
+3926   792843678
+3927   1235039136
+3928   1813030149
+3929   1157666497
+3930   1471633058
+3931   680057097
+3932   580599516
+3933   1817289690
+3934   2102235187
+3935   1908859450
+3936   473990899
+3937   841126685
+3938   2014805700
+3939   2036621585
+3940   1833578592
+3941   2141816005
+3942   241583859
+3943   1125463012
+3944   901732441
+3945   1548069319
+3946   1389841289
+3947   540484685
+3948   951094109
+3949   923219321
+3950   2146877818
+3951   1266540137
+3952   1145221945
+3953   1870546844
+3954   43715333
+3955   945367455
+3956   171835977
+3957   836559011
+3958   32922944
+3959   1984866126
+3960   1994225508
+3961   1504556002
+3962   517439575
+3963   427341376
+3964   1174362044
+3965   472191115
+3966   188717178
+3967   1648352943
+3968   1313317800
+3969   56039231
+3970   1537490881
+3971   999412744
+3972   50371588
+3973   1779074740
+3974   2124875756
+3975   952104029
+3976   1179660411
+3977   1367233397
+3978   1492588715
+3979   2130754521
+3980   142969071
+3981   1491982885
+3982   1249811010
+3983   1288191016
+3984   1215046081
+3985   1293526343
+3986   86074823
+3987   1386882058
+3988   2130085354
+3989   118997767
+3990   1224264537
+3991   1976827214
+3992   1623553770
+3993   1741704112
+3994   256684942
+3995   650432166
+3996   66411579
+3997   445402120
+3998   151301462
+3999   1379729379
+4000   501441351
+4001   1688792343
+4002   231658475
+4003   551812940
+4004   1320383435
+4005   209050583
+4006   1503916969
+4007   352560198
+4008   1576283981
+4009   849022036
+4010   335831071
+4011   1719253052
+4012   193521274
+4013   1585642081
+4014   859960420
+4015   1408567355
+4016   731684776
+4017   946035243
+4018   647965766
+4019   714286482
+4020   1065033011
+4021   1872230303
+4022   543630048
+4023   541103133
+4024   1466450767
+4025   800314990
+4026   1191535299
+4027   1532862347
+4028   1245717111
+4029   1342836761
+4030   765108078
+4031   1747158462
+4032   884145456
+4033   996766554
+4034   151487754
+4035   57045243
+4036   1205817137
+4037   1655404724
+4038   409605442
+4039   634617470
+4040   356943112
+4041   745436513
+4042   206386874
+4043   550464386
+4044   183594947
+4045   1066347294
+4046   1959031742
+4047   915279723
+4048   2012382538
+4049   459513860
+4050   1629566206
+4051   929931901
+4052   184260515
+4053   25712606
+4054   1471035034
+4055   1650711282
+4056   826027597
+4057   515086685
+4058   1036089981
+4059   2071744708
+4060   1857923447
+4061   1801198060
+4062   1671419522
+4063   594585255
+4064   650480966
+4065   1822907277
+4066   651630499
+4067   1856298103
+4068   1330828353
+4069   1061235941
+4070   343431926
+4071   1687771465
+4072   1806672454
+4073   549818800
+4074   90752204
+4075   1990267401
+4076   1616166095
+4077   2049783946
+4078   758063477
+4079   1481064985
+4080   361814158
+4081   240146035
+4082   263513238
+4083   546074673
+4084   265858641
+4085   1734548272
+4086   49302307
+4087   1091886238
+4088   102151309
+4089   1085392289
+4090   1016147298
+4091   1960074756
+4092   739106701
+4093   540083173
+4094   407176364
+4095   1389587667
+4096   215506802
+4097   1058806863
+4098   1098402122
+4099   1546335155
+4100   2120042804
+4101   1441834048
+4102   1086622972
+4103   1779231610
+4104   1991652849
+4105   1177375176
+4106   1622015364
+4107   1460335296
+4108   1079675474
+4109   232595193
+4110   793916633
+4111   1441489632
+4112   472741228
+4113   1057429871
+4114   1987564305
+4115   738599869
+4116   644494495
+4117   2036866613
+4118   1830486108
+4119   746645804
+4120   974775254
+4121   699149758
+4122   559236913
+4123   1713881955
+4124   1239232931
+4125   966413277
+4126   955985974
+4127   1454739733
+4128   2025220140
+4129   2054388096
+4130   853591240
+4131   1997779296
+4132   1348738497
+4133   1940214213
+4134   1629527258
+4135   1192907698
+4136   970105741
+4137   1104058974
+4138   505759346
+4139   2049781216
+4140   1336654167
+4141   1299675979
+4142   1343787200
+4143   1809395395
+4144   209622202
+4145   1183867858
+4146   400511617
+4147   854116697
+4148   1073250823
+4149   83514077
+4150   1600762501
+4151   2048026077
+4152   782663835
+4153   12515766
+4154   1614424384
+4155   2021896767
+4156   978929043
+4157   422926710
+4158   1329152852
+4159   856665535
+4160   329831158
+4161   35260445
+4162   706961183
+4163   1678569655
+4164   1975474658
+4165   189004794
+4166   723993705
+4167   798096751
+4168   1293063768
+4169   1229753051
+4170   700394319
+4171   482234288
+4172   381945382
+4173   2044181520
+4174   144146035
+4175   591567584
+4176   1080565730
+4177   544657652
+4178   1445684281
+4179   6332905
+4180   628171729
+4181   898963135
+4182   2054358982
+4183   1410835565
+4184   911478901
+4185   1521299718
+4186   1285248684
+4187   1890407945
+4188   1944226428
+4189   466917888
+4190   599589832
+4191   126573938
+4192   502178333
+4193   1306551016
+4194   1805143594
+4195   330169343
+4196   1495555810
+4197   381653651
+4198   1128266095
+4199   641135930
+4200   1611406703
+4201   1828660414
+4202   1123370218
+4203   1993352085
+4204   1725358286
+4205   1267516254
+4206   437436022
+4207   658440368
+4208   1812173906
+4209   1883120303
+4210   664773273
+4211   292861988
+4212   634599790
+4213   571648607
+4214   1703697553
+4215   1546078692
+4216   2092948325
+4217   841462589
+4218   1289002989
+4219   1889691105
+4220   1308380477
+4221   1888592821
+4222   2016265044
+4223   1810558811
+4224   1047660189
+4225   1673924990
+4226   2140728154
+4227   395732351
+4228   2055578641
+4229   1121510601
+4230   1036868282
+4231   1519501696
+4232   802687368
+4233   12754852
+4234   1365370134
+4235   380562006
+4236   1280271106
+4237   1802806156
+4238   1039002375
+4239   944961365
+4240   1538442811
+4241   1703775648
+4242   1237823353
+4243   25558954
+4244   127940608
+4245   794037258
+4246   1571637646
+4247   73405285
+4248   1635499847
+4249   713156987
+4250   1963096391
+4251   796396676
+4252   454266160
+4253   1831877787
+4254   459471839
+4255   1501926350
+4256   1358319129
+4257   452716346
+4258   1897658701
+4259   1266414122
+4260   1574226947
+4261   787043335
+4262   638432171
+4263   229430667
+4264   799798188
+4265   2003802305
+4266   609992674
+4267   2080069294
+4268   1659124813
+4269   1648995049
+4270   877547011
+4271   1050083976
+4272   1205287049
+4273   2115370364
+4274   1075642930
+4275   1333227657
+4276   761923974
+4277   499796928
+4278   1406632943
+4279   249940173
+4280   1212953915
+4281   1222245686
+4282   1046336850
+4283   1667220076
+4284   906639825
+4285   1505808689
+4286   1021662778
+4287   117475306
+4288   1958525035
+4289   771837831
+4290   1383889428
+4291   1385268335
+4292   1558881167
+4293   2022321599
+4294   1614699002
+4295   211195707
+4296   1878640256
+4297   77208028
+4298   143781353
+4299   1390281421
+4300   1726203077
+4301   1021328365
+4302   292881750
+4303   784006479
+4304   989215081
+4305   1368524680
+4306   2117234136
+4307   1751139056
+4308   1868321609
+4309   1376383431
+4310   2001079229
+4311   933791876
+4312   451145469
+4313   899932431
+4314   453528304
+4315   1357785294
+4316   258257473
+4317   1475191082
+4318   1475260600
+4319   69298860
+4320   99545266
+4321   711666381
+4322   1454567195
+4323   1658426433
+4324   586504332
+4325   921782550
+4326   1869622140
+4327   317660941
+4328   998990578
+4329   2013403493
+4330   1707942362
+4331   577710008
+4332   887248210
+4333   2000824112
+4334   1361716487
+4335   1876463292
+4336   1221865145
+4337   1331466975
+4338   1480118700
+4339   942703106
+4340   560366759
+4341   1333714281
+4342   1876494982
+4343   1011512228
+4344   86163065
+4345   182539639
+4346   221813875
+4347   344420538
+4348   1657730721
+4349   1697074475
+4350   413719398
+4351   1757275987
+4352   261257208
+4353   1868286594
+4354   1268218772
+4355   847761541
+4356   642585496
+4357   990357264
+4358   1165422482
+4359   1641576074
+4360   856277110
+4361   725881196
+4362   71802434
+4363   1743525320
+4364   579221661
+4365   1433518921
+4366   1472504964
+4367   1801086806
+4368   617502249
+4369   805140016
+4370   596306264
+4371   1177869008
+4372   2138854298
+4373   325317598
+4374   41897588
+4375   77533715
+4376   507857237
+4377   263711463
+4378   421954253
+4379   18104311
+4380   1960785939
+4381   835673651
+4382   1775380298
+4383   74559499
+4384   556476597
+4385   896115423
+4386   922321040
+4387   1199062093
+4388   1886472687
+4389   2087743522
+4390   693154520
+4391   595266149
+4392   666141071
+4393   764956954
+4394   191307822
+4395   1245362732
+4396   50992228
+4397   1663812786
+4398   898965890
+4399   668494477
+4400   321469155
+4401   1495272154
+4402   1846363485
+4403   312839805
+4404   1820589752
+4405   1888261073
+4406   390373520
+4407   180963342
+4408   4488889
+4409   812327773
+4410   199067653
+4411   1965274828
+4412   1648001424
+4413   1974447951
+4414   2039834327
+4415   56994374
+4416   723079726
+4417   814671720
+4418   1256056467
+4419   462068766
+4420   754931594
+4421   1949210987
+4422   1057334915
+4423   1421072665
+4424   566684294
+4425   1248642737
+4426   518951749
+4427   617676522
+4428   764971876
+4429   1417917639
+4430   1286170999
+4431   1086441031
+4432   765706145
+4433   985050836
+4434   1399280836
+4435   438812250
+4436   725828261
+4437   1789654356
+4438   619775592
+4439   730317150
+4440   454498481
+4441   818843245
+4442   548108330
+4443   2102499905
+4444   645807548
+4445   440459010
+4446   12010631
+4447   1368887275
+4448   1255130730
+4449   1268067099
+4450   1830956041
+4451   2010062324
+4452   1069794438
+4453   740807308
+4454   1283651342
+4455   1636478732
+4456   1989450046
+4457   1802603091
+4458   106671606
+4459   606938274
+4460   1073037083
+4461   1392842605
+4462   1693379305
+4463   1838743228
+4464   230409793
+4465   945176493
+4466   130071830
+4467   956238055
+4468   587347201
+4469   749847422
+4470   1686555205
+4471   1041845682
+4472   1568690667
+4473   87179888
+4474   996861939
+4475   67014568
+4476   527638898
+4477   1008872571
+4478   1435901843
+4479   1782769628
+4480   129456022
+4481   1119374236
+4482   1645348304
+4483   1199250460
+4484   1860181544
+4485   781515998
+4486   688245545
+4487   1702147942
+4488   436635442
+4489   794917151
+4490   161602568
+4491   1509672525
+4492   40276109
+4493   1854981873
+4494   1200932105
+4495   270685902
+4496   652674718
+4497   1331003936
+4498   1226923957
+4499   1240021919
+4500   2080851358
+4501   765995515
+4502   134383953
+4503   1502058378
+4504   853175403
+4505   1131245893
+4506   1569072946
+4507   1380814301
+4508   2140118464
+4509   857491141
+4510   1016100281
+4511   122090838
+4512   1976865377
+4513   513964937
+4514   1321341298
+4515   1689563273
+4516   1295480936
+4517   2009586843
+4518   1244227568
+4519   1732116378
+4520   657020347
+4521   1405830136
+4522   1094305255
+4523   697296456
+4524   1113328362
+4525   147753712
+4526   967982358
+4527   1766003080
+4528   1478757648
+4529   47422668
+4530   858541352
+4531   1412125359
+4532   813418183
+4533   992925305
+4534   766700089
+4535   1666593586
+4536   2124171198
+4537   188289387
+4538   899924239
+4539   2116806014
+4540   1045780528
+4541   1916024520
+4542   91413204
+4543   875162257
+4544   282505809
+4545   1412754503
+4546   417241882
+4547   1577986745
+4548   1274857698
+4549   1661469450
+4550   1162619475
+4551   1931878045
+4552   919815939
+4553   109441082
+4554   481690853
+4555   2033144301
+4556   257194795
+4557   1449673212
+4558   1651663733
+4559   1735952443
+4560   1497095880
+4561   362721437
+4562   1000594154
+4563   163030415
+4564   1355646743
+4565   1767294243
+4566   1829624001
+4567   1332334293
+4568   1955583630
+4569   582064592
+4570   1301656660
+4571   853880510
+4572   350605464
+4573   1393069864
+4574   1729042767
+4575   633111273
+4576   658340719
+4577   2146284650
+4578   63614371
+4579   1933198418
+4580   1660270452
+4581   1226233846
+4582   1717592815
+4583   432602743
+4584   1335674929
+4585   51800021
+4586   318263396
+4587   1592869724
+4588   1501473233
+4589   1969927130
+4590   1181338519
+4591   851085465
+4592   185164919
+4593   34449026
+4594   1014115880
+4595   1540811662
+4596   1801743269
+4597   696256233
+4598   725662308
+4599   1609843252
+4600   1278320825
+4601   2027318968
+4602   316240114
+4603   1628926289
+4604   1272905184
+4605   2045282882
+4606   114553914
+4607   1931245904
+4608   2044083884
+4609   178168285
+4610   1716960674
+4611   1556870688
+4612   1404402132
+4613   1287069841
+4614   1989473432
+4615   592593413
+4616   1338869862
+4617   160253180
+4618   37979489
+4619   692859447
+4620   2130180310
+4621   1219318008
+4622   1543944912
+4623   167861582
+4624   1253767034
+4625   410577144
+4626   1708673244
+4627   908026656
+4628   1106833377
+4629   286851904
+4630   370386260
+4631   237670554
+4632   166687224
+4633   686626374
+4634   1866596843
+4635   1439592409
+4636   584425608
+4637   1981150758
+4638   1223354665
+4639   481025844
+4640   11835395
+4641   792831691
+4642   2037896533
+4643   1416237527
+4644   2079901532
+4645   1879886317
+4646   2008830940
+4647   1271287747
+4648   2040139497
+4649   2046810429
+4650   1964147194
+4651   2022836160
+4652   1118644790
+4653   1360608459
+4654   43214094
+4655   224928176
+4656   1771185603
+4657   1751887338
+4658   1132954832
+4659   730535333
+4660   2038739243
+4661   1503341092
+4662   968205887
+4663   57942819
+4664   42483819
+4665   687319083
+4666   1497535228
+4667   626909427
+4668   520986193
+4669   573406245
+4670   1107935272
+4671   532821588
+4672   1366237936
+4673   998348157
+4674   1949059116
+4675   1298655821
+4676   730750826
+4677   1810406408
+4678   422459920
+4679   623406675
+4680   1709733190
+4681   239123466
+4682   498759187
+4683   680894332
+4684   1599731925
+4685   541973281
+4686   905822508
+4687   1223433881
+4688   146376972
+4689   2038777341
+4690   1953969214
+4691   37632567
+4692   1394634785
+4693   774691453
+4694   95575386
+4695   1437118604
+4696   1462010536
+4697   1593110615
+4698   2064028032
+4699   1982996729
+4700   19033212
+4701   1024479656
+4702   368334670
+4703   1385271149
+4704   2022827813
+4705   169910138
+4706   536443322
+4707   606094991
+4708   1980316546
+4709   958903242
+4710   1229501666
+4711   1542566088
+4712   1198026708
+4713   1728260854
+4714   75976772
+4715   650274986
+4716   122750487
+4717   981799281
+4718   1873708867
+4719   269127459
+4720   873092974
+4721   1680194433
+4722   306760026
+4723   120244111
+4724   307402238
+4725   402335413
+4726   1557362716
+4727   1769412775
+4728   1995446028
+4729   1473907100
+4730   1604925856
+4731   2014479240
+4732   350903108
+4733   1973260526
+4734   1252266741
+4735   226247273
+4736   2143170664
+4737   1788710063
+4738   832342264
+4739   1976003563
+4740   600129657
+4741   2061843930
+4742   1371086003
+4743   1798156366
+4744   1642621136
+4745   1447062776
+4746   300947704
+4747   1765371624
+4748   281378409
+4749   27172923
+4750   2034499083
+4751   1154471383
+4752   1707367356
+4753   193775462
+4754   1274715494
+4755   2014769594
+4756   596110875
+4757   684594562
+4758   1636698721
+4759   444073255
+4760   11018014
+4761   1094140930
+4762   311068847
+4763   361921122
+4764   919917808
+4765   1563335589
+4766   588168395
+4767   915604825
+4768   1204562004
+4769   1420510659
+4770   744124740
+4771   1804691662
+4772   1334870942
+4773   2115210743
+4774   1455364380
+4775   830008430
+4776   1414789871
+4777   1756312084
+4778   447896406
+4779   1696168280
+4780   1783485007
+4781   334911842
+4782   703156015
+4783   1343368715
+4784   528687304
+4785   1977871510
+4786   1210654661
+4787   1124798179
+4788   514982424
+4789   699869735
+4790   1568871434
+4791   526000439
+4792   1794010665
+4793   1879940281
+4794   887921561
+4795   566444825
+4796   1295792222
+4797   1476089957
+4798   1482049650
+4799   352870579
+4800   749116968
+4801   78690742
+4802   10078593
+4803   2083987910
+4804   46417838
+4805   1465442973
+4806   766512693
+4807   1461207709
+4808   1074271409
+4809   1214409099
+4810   1009892342
+4811   710272768
+4812   1549320941
+4813   1713048357
+4814   2053641483
+4815   2078008245
+4816   1543436219
+4817   1116812496
+4818   1055322776
+4819   2058418644
+4820   1816682231
+4821   476710562
+4822   436935435
+4823   1463209248
+4824   209167196
+4825   1324856996
+4826   2029654074
+4827   1504959418
+4828   653463305
+4829   1364220076
+4830   1857829997
+4831   1402580274
+4832   1442910819
+4833   1867908590
+4834   1339084536
+4835   1489328657
+4836   1185867915
+4837   2105597229
+4838   803052718
+4839   112655676
+4840   1172522681
+4841   1812945060
+4842   822928444
+4843   574359974
+4844   1378509770
+4845   729086279
+4846   504884572
+4847   774462341
+4848   1845898776
+4849   1560207348
+4850   685397337
+4851   1515097359
+4852   2036917911
+4853   1122332772
+4854   830822960
+4855   98601459
+4856   299706121
+4857   712993386
+4858   1603560877
+4859   953169426
+4860   2077213462
+4861   1313907227
+4862   208266052
+4863   1372640633
+4864   1034332169
+4865   1547350589
+4866   714485642
+4867   72716437
+4868   1505464170
+4869   1517538361
+4870   185372113
+4871   530503203
+4872   1182999773
+4873   1008300558
+4874   1104863178
+4875   414025895
+4876   1737386837
+4877   1609747750
+4878   1188488237
+4879   1435801965
+4880   1022471450
+4881   1873885574
+4882   803415677
+4883   911905713
+4884   848734699
+4885   1634238637
+4886   1010507172
+4887   1148440820
+4888   199748375
+4889   466584402
+4890   2101610246
+4891   129478189
+4892   1780491629
+4893   162392651
+4894   1502118823
+4895   667340150
+4896   1709743240
+4897   69120817
+4898   740056587
+4899   1067723762
+4900   1586659178
+4901   925428701
+4902   1598226966
+4903   622175304
+4904   1933729259
+4905   555606496
+4906   1036201199
+4907   1523632448
+4908   17870598
+4909   77205788
+4910   811950766
+4911   1040342048
+4912   1951091363
+4913   1615366443
+4914   1952247762
+4915   652342414
+4916   1102121432
+4917   815271286
+4918   1800783234
+4919   1301869807
+4920   1281855688
+4921   1754909832
+4922   1431347996
+4923   914863669
+4924   1917302483
+4925   785983171
+4926   1582203820
+4927   1479562075
+4928   855103989
+4929   174776759
+4930   399802190
+4931   294279519
+4932   1100205460
+4933   1998029156
+4934   916454823
+4935   886451071
+4936   406152004
+4937   1952656023
+4938   262599872
+4939   424022602
+4940   2029861811
+4941   1074550638
+4942   1464364650
+4943   1833469526
+4944   542433433
+4945   1269128764
+4946   338328292
+4947   1644554865
+4948   2084400051
+4949   2139111526
+4950   798941024
+4951   1218772091
+4952   1746537711
+4953   82805372
+4954   2133635761
+4955   1516356546
+4956   868788544
+4957   1568355933
+4958   848434974
+4959   1723892533
+4960   1743132692
+4961   1248237164
+4962   2018172052
+4963   695854505
+4964   1098782672
+4965   787143228
+4966   1582305576
+4967   1504934676
+4968   592315603
+4969   1844905448
+4970   1928957278
+4971   474693766
+4972   771972438
+4973   1245838280
+4974   160679645
+4975   1314405871
+4976   367483397
+4977   499007937
+4978   811477088
+4979   304399800
+4980   490635816
+4981   1610418112
+4982   1523171891
+4983   89689879
+4984   1693223485
+4985   1509324004
+4986   1606046425
+4987   414528381
+4988   930196289
+4989   306997751
+4990   2138420914
+4991   525845334
+4992   1555234915
+4993   2009109318
+4994   1221699839
+4995   506533939
+4996   648768898
+4997   656521767
+4998   2011468615
+4999   1241084501
+5000   353943568
+5001   1792942245
+5002   1715778268
+5003   1125916006
+5004   891296878
+5005   1876457913
+5006   292838230
+5007   1258780275
+5008   227982202
+5009   1104315318
+5010   1563180075
+5011   718618018
+5012   567249783
+5013   938868318
+5014   808307897
+5015   112989620
+5016   300708675
+5017   266870675
+5018   527518001
+5019   1230904964
+5020   573868426
+5021   518455267
+5022   1756750298
+5023   2129103342
+5024   380080937
+5025   830966489
+5026   488153633
+5027   1028849836
+5028   1487488257
+5029   352138601
+5030   122450689
+5031   1841431825
+5032   2145080846
+5033   1838228957
+5034   819864183
+5035   888894076
+5036   1567203222
+5037   1112702413
+5038   190703
+5039   1795185425
+5040   69534084
+5041   1563370778
+5042   366319795
+5043   636783867
+5044   354755449
+5045   1174627693
+5046   749773487
+5047   655464124
+5048   1441498368
+5049   1277291488
+5050   1886369088
+5051   2015366794
+5052   1795746755
+5053   1495635739
+5054   1996986488
+5055   28344044
+5056   179118580
+5057   337656474
+5058   1057193880
+5059   1666606837
+5060   689795075
+5061   1179644570
+5062   1360555014
+5063   687392273
+5064   870389879
+5065   32935550
+5066   1576286350
+5067   290109454
+5068   1145637963
+5069   1576477053
+5070   2085294879
+5071   1215172047
+5072   992364184
+5073   304131026
+5074   1851955914
+5075   1347119633
+5076   1478758719
+5077   454245753
+5078   2002583757
+5079   772773439
+5080   1731537241
+5081   1741469197
+5082   640656586
+5083   1379800348
+5084   1089621288
+5085   490159426
+5086   1408144393
+5087   1268739869
+5088   827815900
+5089   317854625
+5090   787863058
+5091   1517610975
+5092   1497499195
+5093   934425
+5094   57519601
+5095   220405427
+5096   33869975
+5097   1633805951
+5098   510514881
+5099   1179507938
+5100   1062799356
+5101   448326112
+5102   247196338
+5103   2055163540
+5104   752457138
+5105   2099152252
+5106   1254799525
+5107   83732210
+5108   405914358
+5109   1109899634
+5110   856505649
+5111   2137451599
+5112   703885184
+5113   1497162235
+5114   1369768300
+5115   1793506472
+5116   1987321662
+5117   630429045
+5118   914762693
+5119   667653914
+5120   948283670
+5121   1702625752
+5122   37781242
+5123   298299218
+5124   1703560177
+5125   95300843
+5126   518704645
+5127   1737430152
+5128   1729106794
+5129   1029219526
+5130   769454442
+5131   644422502
+5132   1477545638
+5133   1016650780
+5134   552102395
+5135   82519128
+5136   968319385
+5137   1806901920
+5138   166251338
+5139   1374233743
+5140   769317907
+5141   1022756988
+5142   1364201694
+5143   1473203091
+5144   372435575
+5145   586486346
+5146   1119225915
+5147   212273589
+5148   1216915391
+5149   2033988609
+5150   879927504
+5151   17715414
+5152   1589130713
+5153   917708746
+5154   316014632
+5155   1145207242
+5156   1013009589
+5157   834719277
+5158   735153746
+5159   594632735
+5160   1863938803
+5161   1504608188
+5162   1239055237
+5163   1194000793
+5164   373775321
+5165   1791157632
+5166   1276519921
+5167   1342094706
+5168   1450575905
+5169   1442771260
+5170   568844801
+5171   72410164
+5172   318044600
+5173   1933046495
+5174   1545613255
+5175   690480175
+5176   372049194
+5177   517355522
+5178   902753765
+5179   1588964585
+5180   403860483
+5181   1782681269
+5182   1606679999
+5183   1992991196
+5184   552906367
+5185   1922694631
+5186   990714790
+5187   1565915956
+5188   609930260
+5189   1725868536
+5190   13065043
+5191   326385415
+5192   1082993077
+5193   1252120280
+5194   1520386208
+5195   1456768398
+5196   895794265
+5197   649422482
+5198   651379456
+5199   198886522
+5200   2092193742
+5201   1220224257
+5202   271296686
+5203   262754694
+5204   1005787104
+5205   1816909941
+5206   953234869
+5207   1377836298
+5208   186781815
+5209   1855988634
+5210   819317236
+5211   590642299
+5212   1491186255
+5213   278513587
+5214   436149847
+5215   2044092622
+5216   53724571
+5217   1426864638
+5218   1462524930
+5219   663654831
+5220   1005249526
+5221   1475589973
+5222   990040247
+5223   2088242603
+5224   580226606
+5225   362942807
+5226   1397527353
+5227   1476020871
+5228   1012365289
+5229   2048906809
+5230   1674907393
+5231   957075383
+5232   1121647418
+5233   1946204079
+5234   1219830077
+5235   2127434523
+5236   1615630372
+5237   25581299
+5238   1357787173
+5239   1802412187
+5240   1881569933
+5241   29620761
+5242   245570838
+5243   1225272541
+5244   308134349
+5245   681720686
+5246   1121881515
+5247   361858920
+5248   2108585324
+5249   436922798
+5250   1025513751
+5251   966351202
+5252   1912512771
+5253   2015553998
+5254   907110158
+5255   345255729
+5256   231013158
+5257   157153863
+5258   1821276600
+5259   1243378447
+5260   58577025
+5261   1348700345
+5262   52970183
+5263   1180224443
+5264   1147420776
+5265   1272800260
+5266   1160175318
+5267   615567500
+5268   1298381559
+5269   370478844
+5270   270496040
+5271   1032467845
+5272   400099605
+5273   516066878
+5274   110256738
+5275   708233954
+5276   1197787564
+5277   1232138253
+5278   1070092874
+5279   1158889240
+5280   1669061051
+5281   2095606626
+5282   2125240443
+5283   1434090175
+5284   1963676976
+5285   884866953
+5286   1779345904
+5287   47206486
+5288   1042020816
+5289   1453138857
+5290   1290584934
+5291   1100597841
+5292   654355554
+5293   1343555117
+5294   133338637
+5295   1801776331
+5296   468871729
+5297   1293513955
+5298   269860183
+5299   1767253289
+5300   1663992799
+5301   540356223
+5302   652237486
+5303   2064092405
+5304   1056423102
+5305   762494224
+5306   624842711
+5307   106727018
+5308   1994632477
+5309   1694935586
+5310   1265616259
+5311   1516209881
+5312   1643058564
+5313   1243373054
+5314   802816408
+5315   1459251892
+5316   2128240007
+5317   434678664
+5318   1506458379
+5319   1022777175
+5320   1887817521
+5321   649559665
+5322   2123375017
+5323   394689428
+5324   1993114782
+5325   109230006
+5326   48982111
+5327   314502863
+5328   1402743961
+5329   318842294
+5330   2081756152
+5331   919253113
+5332   859198518
+5333   586509990
+5334   835861870
+5335   1915621620
+5336   1349004214
+5337   1460704581
+5338   2022348638
+5339   1196153044
+5340   1008156519
+5341   1140481249
+5342   564879277
+5343   503731435
+5344   236370655
+5345   1367695685
+5346   1962983328
+5347   217127014
+5348   1802374349
+5349   1321958059
+5350   1239904190
+5351   1542708223
+5352   1971517724
+5353   1215795559
+5354   1937397651
+5355   1817148858
+5356   1325025565
+5357   1986379762
+5358   2131651721
+5359   580285878
+5360   157738408
+5361   2065924226
+5362   1499538991
+5363   1016936926
+5364   504950568
+5365   187917213
+5366   785074898
+5367   1853954783
+5368   1648621795
+5369   659939889
+5370   902624179
+5371   509294666
+5372   1800421138
+5373   1467503456
+5374   1013026102
+5375   2036791794
+5376   687715493
+5377   828525782
+5378   106435160
+5379   342606194
+5380   3000193
+5381   1346339350
+5382   1885314417
+5383   1974517917
+5384   414651261
+5385   1675228420
+5386   1644183127
+5387   1739676826
+5388   1514124534
+5389   1628351200
+5390   172479057
+5391   1671862943
+5392   1546791778
+5393   1672018048
+5394   541316221
+5395   2051742347
+5396   1859935262
+5397   1326391120
+5398   1758213482
+5399   1361073409
+5400   1986331009
+5401   513354013
+5402   1870368075
+5403   1639268499
+5404   1980857469
+5405   735910529
+5406   1528576645
+5407   521089314
+5408   1564436311
+5409   1635011806
+5410   863695508
+5411   1567436504
+5412   833867508
+5413   601526278
+5414   1394470773
+5415   1248518770
+5416   129271050
+5417   891170252
+5418   840711948
+5419   1643395585
+5420   372037805
+5421   1013191005
+5422   1167774880
+5423   1918829583
+5424   537725406
+5425   1709091101
+5426   1823088282
+5427   250177020
+5428   887998573
+5429   1433818116
+5430   1611250429
+5431   726845934
+5432   1947172129
+5433   1334134856
+5434   218630786
+5435   1780545950
+5436   2070045386
+5437   1747207431
+5438   154151616
+5439   1486998049
+5440   1234735589
+5441   1017847125
+5442   906950906
+5443   2068603098
+5444   1619373403
+5445   153938031
+5446   1169638220
+5447   1748644453
+5448   1045108284
+5449   2010350168
+5450   1244556390
+5451   1417146089
+5452   876057526
+5453   264847622
+5454   1188492024
+5455   1413782932
+5456   1973938724
+5457   864096659
+5458   1663959952
+5459   714453649
+5460   150431127
+5461   1127726733
+5462   1441299584
+5463   2097603257
+5464   314377941
+5465   1659930370
+5466   1730665559
+5467   236939679
+5468   1259654153
+5469   1884817176
+5470   1723937729
+5471   346906095
+5472   755180653
+5473   483404987
+5474   268025545
+5475   227070408
+5476   637343018
+5477   1437663765
+5478   1975714861
+5479   1682451302
+5480   1300530285
+5481   1072787604
+5482   952113743
+5483   29104163
+5484   1337635226
+5485   2140605768
+5486   1442887095
+5487   1164090302
+5488   857218779
+5489   959363399
+5490   1878543952
+5491   1007649906
+5492   2087090132
+5493   1172359888
+5494   957769515
+5495   253984426
+5496   684806610
+5497   540951427
+5498   490924105
+5499   1944460763
+5500   278284955
+5501   67378186
+5502   143883210
+5503   1033465608
+5504   550783173
+5505   411908755
+5506   1260536016
+5507   1188126192
+5508   1849572520
+5509   1088767229
+5510   723093846
+5511   1002619158
+5512   14071185
+5513   1675207590
+5514   1031723321
+5515   1351706412
+5516   1668329710
+5517   327126769
+5518   368313066
+5519   378064841
+5520   1286490168
+5521   99373370
+5522   1385714747
+5523   1226096653
+5524   1271733258
+5525   196000615
+5526   1480081079
+5527   1956539868
+5528   736952042
+5529   1971005184
+5530   1753516984
+5531   1015236997
+5532   2038383371
+5533   1897400194
+5534   2048702605
+5535   441682896
+5536   161825302
+5537   1161754973
+5538   1629809088
+5539   2011397822
+5540   103038554
+5541   205419287
+5542   866533332
+5543   117109740
+5544   1880626877
+5545   1898256654
+5546   1468816152
+5547   1401472939
+5548   77899775
+5549   1837129218
+5550   1779537780
+5551   1364389943
+5552   1936502589
+5553   1017768879
+5554   443002948
+5555   1060752199
+5556   1213769494
+5557   1923084027
+5558   869808420
+5559   1950721536
+5560   1746605564
+5561   475841756
+5562   818474885
+5563   1637505287
+5564   225758302
+5565   719693842
+5566   2079188183
+5567   387583604
+5568   1881448815
+5569   1561513624
+5570   251497779
+5571   1984487370
+5572   1766932911
+5573   1118031111
+5574   2101597110
+5575   1500076140
+5576   868804117
+5577   1422929614
+5578   754065431
+5579   946703892
+5580   1112575184
+5581   386119563
+5582   163610188
+5583   901594125
+5584   1403888442
+5585   606613136
+5586   1962346325
+5587   470174289
+5588   382213516
+5589   684671097
+5590   273412177
+5591   2128819080
+5592   1160512853
+5593   1091887063
+5594   1618840719
+5595   1386271155
+5596   1811580905
+5597   1550545254
+5598   1773854760
+5599   1545546073
+5600   964575230
+5601   2025352539
+5602   1382549795
+5603   584024493
+5604   995900002
+5605   1336663257
+5606   2084100633
+5607   1864704120
+5608   612109223
+5609   690682416
+5610   663924364
+5611   1724684407
+5612   1076801979
+5613   827534552
+5614   478794885
+5615   333206774
+5616   1434147689
+5617   293657562
+5618   803381063
+5619   1816361205
+5620   978328659
+5621   1076793240
+5622   1797696637
+5623   2138841512
+5624   21196655
+5625   1269053708
+5626   1377629019
+5627   1832777561
+5628   672115314
+5629   1004000131
+5630   1230839986
+5631   1636690545
+5632   881869022
+5633   465906133
+5634   73231390
+5635   1877769025
+5636   1802569390
+5637   9848376
+5638   1594989497
+5639   267194965
+5640   700530792
+5641   111430213
+5642   1991879372
+5643   1777332772
+5644   938964766
+5645   323190609
+5646   2110539546
+5647   225628807
+5648   616848171
+5649   766436961
+5650   2041990012
+5651   1595176830
+5652   1843230201
+5653   1692203001
+5654   1586534694
+5655   1864426857
+5656   813773061
+5657   816680066
+5658   1549720770
+5659   1485888375
+5660   1820680197
+5661   633077108
+5662   975095272
+5663   555065572
+5664   1098983241
+5665   1048326663
+5666   285350949
+5667   754068983
+5668   1058175039
+5669   1880340446
+5670   1021263948
+5671   1758705831
+5672   1991770659
+5673   865659672
+5674   1388554955
+5675   783251777
+5676   1188850282
+5677   1351610853
+5678   1008880584
+5679   1805698453
+5680   2118047814
+5681   903386948
+5682   1253391636
+5683   1813794368
+5684   448106301
+5685   692442682
+5686   1530737577
+5687   1261879362
+5688   1509122748
+5689   932974699
+5690   600284090
+5691   1182319298
+5692   1566051807
+5693   1575379362
+5694   1737384870
+5695   517551400
+5696   476222377
+5697   2022735819
+5698   1271620383
+5699   1534397416
+5700   1755592617
+5701   145400683
+5702   1145619600
+5703   1599879628
+5704   1011060355
+5705   386690907
+5706   235647758
+5707   52426989
+5708   1738301761
+5709   1244528342
+5710   1858125443
+5711   1708865927
+5712   431643
+5713   964033431
+5714   1375176647
+5715   448537944
+5716   1656476113
+5717   758430576
+5718   1710417307
+5719   1018115214
+5720   1691405275
+5721   163217749
+5722   52950864
+5723   1109973434
+5724   1738597111
+5725   1790335734
+5726   1627524834
+5727   67335841
+5728   1665587905
+5729   751661569
+5730   1601733257
+5731   1273696874
+5732   897062252
+5733   599869209
+5734   726092854
+5735   1908122608
+5736   986560117
+5737   961740612
+5738   1960549597
+5739   577378230
+5740   58785307
+5741   1671191392
+5742   138760509
+5743   59216950
+5744   487741175
+5745   1513937157
+5746   507754894
+5747   2144217289
+5748   124884085
+5749   70688553
+5750   1014848855
+5751   1816289361
+5752   233906302
+5753   1067799719
+5754   778779147
+5755   1972503414
+5756   710651805
+5757   258820334
+5758   2039839255
+5759   228756062
+5760   1010481903
+5761   1494088864
+5762   1502452936
+5763   1907544156
+5764   2093958074
+5765   81062142
+5766   1668183116
+5767   933034543
+5768   1042802755
+5769   1481249065
+5770   1510412773
+5771   1101588062
+5772   1004956810
+5773   1649173282
+5774   1160805012
+5775   1492697985
+5776   1015626791
+5777   1668559906
+5778   1489431626
+5779   1140510877
+5780   1739248460
+5781   356796833
+5782   809316590
+5783   1973154762
+5784   1424596552
+5785   1588095737
+5786   1798174528
+5787   2135248357
+5788   1846916071
+5789   1690530135
+5790   216520771
+5791   709914327
+5792   1037135352
+5793   1718973707
+5794   469974835
+5795   983609778
+5796   1800035850
+5797   2138157951
+5798   1916644321
+5799   695354957
+5800   1471923368
+5801   1279573446
+5802   1796943019
+5803   329396530
+5804   781263080
+5805   810264383
+5806   1822094516
+5807   1796889872
+5808   331340641
+5809   1164042494
+5810   789917101
+5811   2070589101
+5812   1520839328
+5813   1599233691
+5814   1896260216
+5815   797952232
+5816   1039845780
+5817   1546951096
+5818   785716942
+5819   739278204
+5820   1089997584
+5821   1002237713
+5822   1449192531
+5823   2127132936
+5824   573727773
+5825   1919167366
+5826   963259066
+5827   226279975
+5828   1909841669
+5829   732419739
+5830   921634932
+5831   1234281389
+5832   2011993185
+5833   571094303
+5834   1563677920
+5835   645772617
+5836   1381358686
+5837   1238288788
+5838   295178841
+5839   1712699327
+5840   254847634
+5841   1085095942
+5842   1635804781
+5843   1775686962
+5844   536845985
+5845   1384581349
+5846   426155547
+5847   1576691766
+5848   784048797
+5849   1211872489
+5850   168486322
+5851   1874046381
+5852   66626554
+5853   1617678853
+5854   1853695669
+5855   640354327
+5856   1389362571
+5857   669471087
+5858   866634302
+5859   1151720592
+5860   1401890826
+5861   1788269234
+5862   238518333
+5863   1266400363
+5864   211879889
+5865   1802196253
+5866   1912172981
+5867   1593238575
+5868   893001393
+5869   59868174
+5870   1158454255
+5871   1147849028
+5872   1144964117
+5873   646775388
+5874   776052342
+5875   1681810102
+5876   2031356737
+5877   1202207889
+5878   1111018220
+5879   667921886
+5880   266596730
+5881   1279504542
+5882   394484620
+5883   333223285
+5884   749699747
+5885   100696641
+5886   973577612
+5887   2139062318
+5888   770167729
+5889   1840211915
+5890   1143299262
+5891   24574907
+5892   1480997501
+5893   1381817596
+5894   1290975271
+5895   1692877391
+5896   1036530201
+5897   1055664604
+5898   1138632318
+5899   1929531595
+5900   1115532778
+5901   149602925
+5902   929896975
+5903   113013247
+5904   796378313
+5905   1705949317
+5906   1794823350
+5907   680251402
+5908   760673559
+5909   758357922
+5910   1348173289
+5911   1027270289
+5912   2037862465
+5913   1742657909
+5914   1360493574
+5915   640078564
+5916   1843354550
+5917   186587539
+5918   631657235
+5919   466038631
+5920   2026799454
+5921   1774956497
+5922   490613539
+5923   1360313307
+5924   1009290445
+5925   1781588810
+5926   905707050
+5927   2045820647
+5928   689769766
+5929   2044339369
+5930   1827868594
+5931   1805302544
+5932   46458646
+5933   610281921
+5934   1918315792
+5935   842836960
+5936   168747590
+5937   1565655494
+5938   1523088362
+5939   929421149
+5940   176529768
+5941   723778003
+5942   1956691439
+5943   66908585
+5944   318952264
+5945   1169701365
+5946   706987150
+5947   14823167
+5948   1356288904
+5949   1338644385
+5950   480861798
+5951   1235604710
+5952   966117234
+5953   971475337
+5954   448434370
+5955   1975407680
+5956   605580499
+5957   1354141420
+5958   1873744679
+5959   1295350265
+5960   1250997141
+5961   1554129625
+5962   953169162
+5963   1297455788
+5964   16927898
+5965   724001306
+5966   2140292748
+5967   185675488
+5968   142173152
+5969   1515897462
+5970   1115096638
+5971   318702920
+5972   92191818
+5973   924304429
+5974   385611506
+5975   411144082
+5976   2094005794
+5977   1092598656
+5978   425967249
+5979   1302811051
+5980   283759393
+5981   906829048
+5982   390932113
+5983   1249876627
+5984   1878304385
+5985   839366483
+5986   1077800659
+5987   336401237
+5988   46024256
+5989   804061690
+5990   1631751502
+5991   1297021397
+5992   210707667
+5993   437437016
+5994   446993537
+5995   227635565
+5996   1161438322
+5997   439802637
+5998   413311054
+5999   1303611474
+6000   1955700100
+6001   1528407692
+6002   1622314395
+6003   2047891918
+6004   305228473
+6005   2007925901
+6006   311552352
+6007   251750619
+6008   953040909
+6009   737519602
+6010   1554561670
+6011   1236800302
+6012   1644348650
+6013   1945493784
+6014   339193281
+6015   1375169387
+6016   637376619
+6017   1416993941
+6018   1711570624
+6019   683400875
+6020   73571983
+6021   1195838479
+6022   1980422273
+6023   284279651
+6024   1633275495
+6025   279932162
+6026   511915216
+6027   647230170
+6028   719734800
+6029   925226270
+6030   1950841644
+6031   527951252
+6032   306150314
+6033   1425672391
+6034   428359522
+6035   611378787
+6036   1286114644
+6037   739911874
+6038   863129407
+6039   91671905
+6040   1477431476
+6041   270207429
+6042   1328472207
+6043   974296478
+6044   68217565
+6045   1667665489
+6046   201982218
+6047   705594185
+6048   937175782
+6049   1913552842
+6050   1388995060
+6051   1010747765
+6052   961907673
+6053   1221933685
+6054   1295027416
+6055   447699521
+6056   1501865848
+6057   1806942633
+6058   1094929691
+6059   74117000
+6060   584685255
+6061   898287687
+6062   602068252
+6063   890835570
+6064   176476431
+6065   1030427774
+6066   1502214357
+6067   1462591075
+6068   1770339648
+6069   217860116
+6070   1554262981
+6071   1100287477
+6072   488067546
+6073   735251540
+6074   2074583955
+6075   556285111
+6076   255433381
+6077   129082525
+6078   1261879296
+6079   1192609163
+6080   2042635368
+6081   503390709
+6082   55873281
+6083   857059393
+6084   1725324394
+6085   1350900697
+6086   1304758914
+6087   1079706594
+6088   1010359682
+6089   252204957
+6090   1153823594
+6091   1595044938
+6092   1150492645
+6093   1755891846
+6094   338396860
+6095   1326969076
+6096   638835972
+6097   1840611217
+6098   642076503
+6099   261691973
+6100   2058471334
+6101   48855836
+6102   1361979450
+6103   399055232
+6104   784107377
+6105   1289079757
+6106   955340343
+6107   1039540758
+6108   1418162283
+6109   69735992
+6110   84666274
+6111   1313314003
+6112   573126701
+6113   140539555
+6114   22889748
+6115   150967447
+6116   1491440252
+6117   1327648663
+6118   1230674042
+6119   354316287
+6120   1579853620
+6121   237013988
+6122   1949361225
+6123   582862617
+6124   1992905835
+6125   140274437
+6126   1909831693
+6127   484258159
+6128   1980885654
+6129   404424549
+6130   745950132
+6131   1891873340
+6132   453280385
+6133   2107929582
+6134   143444924
+6135   1237387762
+6136   1249525692
+6137   1098785268
+6138   129444873
+6139   520204327
+6140   1168521260
+6141   214111147
+6142   1833518330
+6143   1741647961
+6144   354650702
+6145   1856408078
+6146   1892615408
+6147   1846090954
+6148   1036573093
+6149   975805802
+6150   52923593
+6151   468943066
+6152   1212819791
+6153   2002284818
+6154   1051805683
+6155   1058241978
+6156   2142559255
+6157   814153729
+6158   1542500137
+6159   1975961262
+6160   1218578278
+6161   140966622
+6162   1720350954
+6163   1671858663
+6164   101412556
+6165   1863795879
+6166   761762778
+6167   1350938248
+6168   815097499
+6169   891207651
+6170   1871142575
+6171   1983618759
+6172   1105318798
+6173   1557177257
+6174   1577783072
+6175   1459969500
+6176   1266101688
+6177   1322914832
+6178   1158576806
+6179   155191133
+6180   151236987
+6181   1211500400
+6182   624134199
+6183   1364056778
+6184   1066301570
+6185   1675939883
+6186   274815108
+6187   1061377178
+6188   342609964
+6189   1817315245
+6190   889854792
+6191   1561188242
+6192   1958281867
+6193   462722098
+6194   1085563257
+6195   2059694424
+6196   179034329
+6197   1847326035
+6198   1263149024
+6199   994131828
+6200   591050038
+6201   986807952
+6202   830266939
+6203   1696368836
+6204   396501561
+6205   260566363
+6206   1008854688
+6207   1662603249
+6208   1583481196
+6209   19947847
+6210   1817794383
+6211   1734718183
+6212   1231448247
+6213   294444934
+6214   951291313
+6215   150266169
+6216   1970384817
+6217   1226106421
+6218   1211643347
+6219   165511133
+6220   895938018
+6221   2101498139
+6222   1726699375
+6223   706736238
+6224   416736590
+6225   664778985
+6226   618947014
+6227   595770919
+6228   364621372
+6229   1882096038
+6230   1589902748
+6231   955671411
+6232   721420342
+6233   272686039
+6234   504556599
+6235   1117921904
+6236   533252403
+6237   1513411288
+6238   633041505
+6239   2116733599
+6240   1533359135
+6241   303352240
+6242   1703968134
+6243   617323734
+6244   597797175
+6245   507775799
+6246   767589903
+6247   420698344
+6248   1733882220
+6249   1979233251
+6250   586209478
+6251   482336590
+6252   1933247742
+6253   165425205
+6254   1189072828
+6255   202500684
+6256   830204190
+6257   1808019842
+6258   798271604
+6259   1194825563
+6260   1542632233
+6261   240690704
+6262   3013326
+6263   116568927
+6264   513376743
+6265   507569925
+6266   1234490831
+6267   1046629146
+6268   2020981213
+6269   1867532337
+6270   1015879097
+6271   1406856700
+6272   23400929
+6273   572363583
+6274   2024180434
+6275   621198104
+6276   1080139382
+6277   644286690
+6278   1041896449
+6279   666537954
+6280   476036293
+6281   1628105927
+6282   1148874545
+6283   261800387
+6284   1793531132
+6285   190463725
+6286   464301072
+6287   476251675
+6288   1998483568
+6289   1262572676
+6290   1671077238
+6291   1393632153
+6292   1503263380
+6293   1674090564
+6294   1510201080
+6295   2016640123
+6296   34176841
+6297   597208264
+6298   915785622
+6299   2055158055
+6300   317256953
+6301   1931664719
+6302   1314531107
+6303   340657882
+6304   356544655
+6305   1191227894
+6306   961855987
+6307   1436684037
+6308   1835514584
+6309   2003752436
+6310   2103221992
+6311   164067229
+6312   1484374715
+6313   1104612889
+6314   425867616
+6315   1130422199
+6316   1295076614
+6317   890168688
+6318   1606673874
+6319   1146076534
+6320   5257716
+6321   1130267464
+6322   392225039
+6323   1508521096
+6324   656874380
+6325   1902426120
+6326   1377677572
+6327   691051222
+6328   352150736
+6329   145979546
+6330   598725629
+6331   669407689
+6332   2077644265
+6333   1913256736
+6334   1010065571
+6335   286705272
+6336   957000982
+6337   1971921558
+6338   1723389310
+6339   645031918
+6340   1828190346
+6341   1679127654
+6342   809099147
+6343   1165081413
+6344   636256895
+6345   1234966764
+6346   148019965
+6347   1931333509
+6348   2125135452
+6349   1754693839
+6350   929926396
+6351   2130393169
+6352   737477656
+6353   1322151435
+6354   1491430617
+6355   1394352036
+6356   1077093907
+6357   721624541
+6358   2085403258
+6359   1429244643
+6360   867604087
+6361   536645239
+6362   2098652332
+6363   797764705
+6364   302418328
+6365   961234256
+6366   1084469977
+6367   1259419310
+6368   785672166
+6369   660375639
+6370   1904451229
+6371   466378865
+6372   192019645
+6373   566066728
+6374   1631460278
+6375   828276540
+6376   1801033492
+6377   1779480243
+6378   612126402
+6379   1778685297
+6380   1386690435
+6381   1542052798
+6382   1761594818
+6383   2124168091
+6384   716720585
+6385   1105541787
+6386   1371036479
+6387   1793814493
+6388   1827166329
+6389   1308956090
+6390   1075575488
+6391   547286768
+6392   1845601329
+6393   1026744173
+6394   1345051473
+6395   536009
+6396   1987978429
+6397   282037803
+6398   1259955320
+6399   626166947
+6400   942413442
+6401   1016922901
+6402   1092545812
+6403   1134433088
+6404   1582989629
+6405   576522443
+6406   1962709628
+6407   1236539474
+6408   208519038
+6409   427352382
+6410   867741123
+6411   1595209473
+6412   1969405180
+6413   481852293
+6414   1571893916
+6415   538642118
+6416   1587394080
+6417   795446748
+6418   184972963
+6419   1267076761
+6420   2104402838
+6421   1260548451
+6422   1814363530
+6423   1802520519
+6424   139808976
+6425   1011931355
+6426   1803056529
+6427   2127787405
+6428   1293969158
+6429   915528201
+6430   606470705
+6431   88898953
+6432   1932451102
+6433   1699016517
+6434   1223332041
+6435   1367957083
+6436   128055312
+6437   1038558021
+6438   457012909
+6439   336574351
+6440   1465910404
+6441   1324754032
+6442   1931783824
+6443   1287831936
+6444   1806606325
+6445   1356194093
+6446   1826474054
+6447   1246516758
+6448   4157193
+6449   2011447017
+6450   366109871
+6451   2108560031
+6452   1124511821
+6453   32989753
+6454   1763596902
+6455   1264320797
+6456   1044921109
+6457   1419169783
+6458   1244624555
+6459   191406619
+6460   187214336
+6461   1851095260
+6462   280305572
+6463   2119665438
+6464   1402628129
+6465   1503637613
+6466   1340138874
+6467   1530683442
+6468   394711987
+6469   1797151783
+6470   1867257793
+6471   1860622391
+6472   974422168
+6473   1651557969
+6474   1000970679
+6475   633544845
+6476   860268414
+6477   679961086
+6478   1880061603
+6479   864425607
+6480   543924455
+6481   98687827
+6482   825501990
+6483   1668436276
+6484   131677580
+6485   441615245
+6486   785273426
+6487   1176598689
+6488   1860785028
+6489   2029897981
+6490   1368005309
+6491   2047999365
+6492   1733509593
+6493   1648310881
+6494   2020181155
+6495   988654074
+6496   1004464847
+6497   1212836381
+6498   371853868
+6499   1399176834
+6500   862504517
+6501   91628013
+6502   1112315577
+6503   1836926685
+6504   1743185983
+6505   2113286256
+6506   322987882
+6507   455970749
+6508   645763694
+6509   55565838
+6510   1320396357
+6511   1189688150
+6512   154253665
+6513   2145898347
+6514   710640778
+6515   285931245
+6516   440029944
+6517   1495914204
+6518   1462529935
+6519   153331325
+6520   1378328537
+6521   683051596
+6522   53847042
+6523   964354482
+6524   183878829
+6525   2074028197
+6526   1953008557
+6527   1188343676
+6528   1139380931
+6529   177378777
+6530   440036862
+6531   2001885448
+6532   269006791
+6533   1552352439
+6534   1691328485
+6535   2012192774
+6536   1518155048
+6537   2014316367
+6538   320679875
+6539   16435094
+6540   2069882205
+6541   1641076232
+6542   1206123244
+6543   76652222
+6544   1639490932
+6545   1916764023
+6546   362583468
+6547   2079520876
+6548   1265194579
+6549   1825113403
+6550   85368553
+6551   496039469
+6552   360681351
+6553   139215595
+6554   1460393951
+6555   544560180
+6556   65760145
+6557   1265918860
+6558   1732903857
+6559   1205141076
+6560   1443297638
+6561   25457071
+6562   1059542876
+6563   1712304429
+6564   1577809511
+6565   603387713
+6566   1577013555
+6567   948480911
+6568   470220432
+6569   1897693430
+6570   964916005
+6571   392618990
+6572   1391286015
+6573   23555602
+6574   469271212
+6575   883293299
+6576   1940319625
+6577   831854680
+6578   815330527
+6579   1058030556
+6580   509484435
+6581   900699081
+6582   1554070025
+6583   870165786
+6584   1039914676
+6585   866980329
+6586   1414725967
+6587   1105674821
+6588   2132899189
+6589   1000146176
+6590   163332249
+6591   1428713179
+6592   1025603247
+6593   1222875125
+6594   993533960
+6595   455929110
+6596   1826262838
+6597   423063867
+6598   1404410021
+6599   148999623
+6600   173273650
+6601   221842379
+6602   541618613
+6603   1564559665
+6604   245397981
+6605   1010889825
+6606   300369316
+6607   38233958
+6608   1842744506
+6609   1115699843
+6610   1096264514
+6611   204745293
+6612   2016398924
+6613   502850892
+6614   1074911080
+6615   908829953
+6616   1369831221
+6617   342153399
+6618   2014504774
+6619   1355246762
+6620   1342299575
+6621   30353376
+6622   636476294
+6623   220419174
+6624   1253228501
+6625   1630010254
+6626   676348285
+6627   932007692
+6628   2053074122
+6629   2080758306
+6630   1081007315
+6631   78864124
+6632   155117037
+6633   1622625928
+6634   1643423789
+6635   400515018
+6636   486032105
+6637   1943793105
+6638   438748976
+6639   181292963
+6640   912009300
+6641   1535013491
+6642   386038257
+6643   780924577
+6644   2037864383
+6645   1460949337
+6646   1689754530
+6647   1260211956
+6648   1803102736
+6649   1556775656
+6650   467975070
+6651   997918663
+6652   1587129032
+6653   1104451364
+6654   1218337837
+6655   692873886
+6656   586977971
+6657   1894686122
+6658   1624881578
+6659   492568445
+6660   1827960781
+6661   558405245
+6662   571432569
+6663   1983077818
+6664   33547525
+6665   67372710
+6666   236109189
+6667   519579630
+6668   2011165815
+6669   674858165
+6670   700872594
+6671   775691467
+6672   62388008
+6673   1086910851
+6674   1556616044
+6675   2100252391
+6676   400376540
+6677   1098886926
+6678   1212980699
+6679   55995628
+6680   508178935
+6681   1680955770
+6682   1053914291
+6683   2095307967
+6684   637923486
+6685   124768480
+6686   640698205
+6687   1224901457
+6688   2019454603
+6689   118096135
+6690   1717469902
+6691   1699931736
+6692   676501380
+6693   141418823
+6694   1535525906
+6695   710048905
+6696   208791533
+6697   1771635095
+6698   1229628536
+6699   72473700
+6700   299009613
+6701   1930501130
+6702   848165168
+6703   361397621
+6704   869928333
+6705   257297564
+6706   314166365
+6707   1270304873
+6708   1356184491
+6709   1527147064
+6710   1326300501
+6711   1864363426
+6712   1060619186
+6713   232731144
+6714   1812187745
+6715   1698542673
+6716   357499624
+6717   305402303
+6718   775960482
+6719   229470579
+6720   423498438
+6721   345946737
+6722   1929402315
+6723   1099999819
+6724   487365560
+6725   1317444574
+6726   1810048724
+6727   696157094
+6728   941596021
+6729   892193612
+6730   768630794
+6731   1240605634
+6732   675211094
+6733   1616795962
+6734   1602003256
+6735   1545139427
+6736   1874093527
+6737   1916169621
+6738   667960652
+6739   1082794370
+6740   1295833037
+6741   1994261153
+6742   799674148
+6743   208968576
+6744   79508649
+6745   464378245
+6746   1907511249
+6747   437008274
+6748   769780548
+6749   535988083
+6750   666478853
+6751   1193278987
+6752   881934820
+6753   448397521
+6754   145795158
+6755   1369300381
+6756   1765842095
+6757   1955843882
+6758   2065457475
+6759   559954468
+6760   700553847
+6761   686604621
+6762   1800560103
+6763   1375764941
+6764   155916936
+6765   1255079711
+6766   773420721
+6767   2030010463
+6768   1023765684
+6769   1441381373
+6770   965321185
+6771   172115073
+6772   1288158879
+6773   1764995333
+6774   381083649
+6775   1367667528
+6776   81889930
+6777   141111250
+6778   1804675802
+6779   851670479
+6780   677099334
+6781   323671008
+6782   2044949466
+6783   1559034154
+6784   772068529
+6785   43260976
+6786   780850887
+6787   390426976
+6788   1999104858
+6789   698824714
+6790   950381444
+6791   552175057
+6792   1385429336
+6793   603457899
+6794   1927939999
+6795   1541346272
+6796   1858537610
+6797   553877072
+6798   1423873087
+6799   734819646
+6800   1995258445
+6801   241710624
+6802   906934720
+6803   1135933676
+6804   2006705957
+6805   1288018369
+6806   356117557
+6807   2088595887
+6808   1429129620
+6809   13309711
+6810   792782718
+6811   2106228954
+6812   336980719
+6813   690248536
+6814   1517779460
+6815   1109049248
+6816   733509512
+6817   151146700
+6818   1499476224
+6819   585130723
+6820   849971414
+6821   302374021
+6822   1137305780
+6823   87917102
+6824   905831920
+6825   917762131
+6826   1629263374
+6827   616885883
+6828   1471639203
+6829   905652813
+6830   1351705529
+6831   1319414001
+6832   1147363437
+6833   111156601
+6834   307864029
+6835   1006585746
+6836   1399174971
+6837   663981586
+6838   947697986
+6839   680820943
+6840   677291298
+6841   1740480704
+6842   639566249
+6843   1014272017
+6844   283245593
+6845   9862061
+6846   2123321266
+6847   1016755105
+6848   161008761
+6849   1475313842
+6850   1601885828
+6851   1010980176
+6852   1777687863
+6853   591707961
+6854   1098897278
+6855   536036136
+6856   1509470092
+6857   580677005
+6858   1152922019
+6859   833625648
+6860   1486329818
+6861   357143900
+6862   5556001
+6863   486209608
+6864   468300502
+6865   313420030
+6866   1492795354
+6867   1867475473
+6868   977401617
+6869   293009692
+6870   400812768
+6871   1654692915
+6872   2033490397
+6873   1040379017
+6874   521481284
+6875   169252342
+6876   1050241078
+6877   497318902
+6878   1186007447
+6879   1211249840
+6880   1972632745
+6881   640409628
+6882   74746368
+6883   1602836960
+6884   1232117589
+6885   1173643646
+6886   2138873096
+6887   594104033
+6888   1754320651
+6889   1144311467
+6890   1427729681
+6891   1093166822
+6892   1501455368
+6893   1433285682
+6894   1579376430
+6895   1969755870
+6896   1746705713
+6897   924688136
+6898   1689747695
+6899   576623682
+6900   1217697829
+6901   2090560463
+6902   83832949
+6903   1103704578
+6904   983455832
+6905   605314233
+6906   1272956920
+6907   2033696910
+6908   1102633136
+6909   311480719
+6910   1097463102
+6911   927782233
+6912   951890347
+6913   1172209470
+6914   383135545
+6915   36524288
+6916   198369469
+6917   374524994
+6918   630628322
+6919   1952690120
+6920   1518836461
+6921   2058358003
+6922   898373294
+6923   872808181
+6924   1344160038
+6925   330266076
+6926   695080403
+6927   943382103
+6928   1254954213
+6929   237344450
+6930   1520005785
+6931   325168394
+6932   180421265
+6933   1603838734
+6934   1428872972
+6935   1163877097
+6936   61669319
+6937   554346244
+6938   1050090360
+6939   1164302455
+6940   865826963
+6941   69814
+6942   2092084688
+6943   1817717311
+6944   1172279285
+6945   327736586
+6946   1854241599
+6947   1370648754
+6948   702261580
+6949   337386273
+6950   1175855226
+6951   73614393
+6952   248260629
+6953   2074228521
+6954   946422575
+6955   1592420667
+6956   257010949
+6957   1641502978
+6958   388319122
+6959   1511965162
+6960   1878847429
+6961   1908324907
+6962   1837133556
+6963   2059268694
+6964   1364679993
+6965   1118522880
+6966   1075662144
+6967   1426349312
+6968   1672869124
+6969   2125752504
+6970   443168120
+6971   391212440
+6972   2125822318
+6973   387769160
+6974   61446103
+6975   1150617955
+6976   715505746
+6977   1915687702
+6978   373783061
+6979   1417767326
+6980   105590328
+6981   1549638288
+6982   1491381720
+6983   353850957
+6984   1476383161
+6985   290320647
+6986   1946271624
+6987   1733394110
+6988   1931823625
+6989   187107098
+6990   1097875625
+6991   1663187406
+6992   2095432005
+6993   787525533
+6994   1574972453
+6995   1312628350
+6996   1906048414
+6997   503150949
+6998   591494014
+6999   1431433890
+7000   481419805
+7001   1034662134
+7002   1822646330
+7003   459758475
+7004   1422431295
+7005   1884092433
+7006   1610376431
+7007   2137937041
+7008   1652296488
+7009   1984159492
+7010   1408220720
+7011   1757886816
+7012   1386314132
+7013   752118792
+7014   2111737773
+7015   715213645
+7016   1042439439
+7017   1910525749
+7018   301124108
+7019   826779416
+7020   2097632847
+7021   1398999733
+7022   342483175
+7023   2045581204
+7024   39041618
+7025   1917455628
+7026   1210725906
+7027   1945090032
+7028   273122929
+7029   1802219920
+7030   1229040275
+7031   754542734
+7032   689398407
+7033   904202957
+7034   1214301209
+7035   2111829702
+7036   640811743
+7037   677193992
+7038   2102283095
+7039   145624583
+7040   513869837
+7041   1363020167
+7042   1903511399
+7043   1900183969
+7044   2115138959
+7045   1867765524
+7046   467913967
+7047   1010094750
+7048   1630807625
+7049   769038075
+7050   1836874167
+7051   1580956824
+7052   20554160
+7053   31873694
+7054   1479054380
+7055   59595778
+7056   1949329322
+7057   542296638
+7058   2004685811
+7059   74968603
+7060   197032910
+7061   1086242438
+7062   829511337
+7063   886431317
+7064   1990445395
+7065   2043812546
+7066   850777371
+7067   483773490
+7068   573522891
+7069   805576819
+7070   629398073
+7071   1087392728
+7072   21113338
+7073   385425824
+7074   840093049
+7075   2136252298
+7076   105707700
+7077   1308007016
+7078   998863400
+7079   1736515325
+7080   2077045091
+7081   688253919
+7082   1169988501
+7083   2097599251
+7084   720127613
+7085   501559233
+7086   9711382
+7087   521973287
+7088   1043855871
+7089   2014397193
+7090   596941890
+7091   1240888782
+7092   953155983
+7093   1426453227
+7094   2127320099
+7095   796117730
+7096   1322782126
+7097   830613823
+7098   1279891221
+7099   1896305017
+7100   1636190642
+7101   1909289294
+7102   836214097
+7103   1657303980
+7104   147231471
+7105   1676307146
+7106   1646072630
+7107   252939171
+7108   836830515
+7109   497452383
+7110   1989454497
+7111   766391958
+7112   1185706302
+7113   1011959350
+7114   716507562
+7115   1905833916
+7116   1513518584
+7117   726218944
+7118   280323555
+7119   409890807
+7120   593132489
+7121   877265446
+7122   1650779589
+7123   1546288472
+7124   156235025
+7125   1630616041
+7126   194922554
+7127   1479017151
+7128   313746216
+7129   1474813775
+7130   1227838520
+7131   1949936858
+7132   1236619422
+7133   2064052617
+7134   1459757190
+7135   1383850893
+7136   1592876116
+7137   958346173
+7138   1636790064
+7139   282222983
+7140   1455798556
+7141   1478760913
+7142   1048614941
+7143   494021210
+7144   343236616
+7145   1765122503
+7146   252371478
+7147   1856755200
+7148   343857799
+7149   532695034
+7150   119162359
+7151   936990288
+7152   1409960480
+7153   1769941949
+7154   335795112
+7155   1566195505
+7156   1253074342
+7157   530717667
+7158   897729009
+7159   1566820558
+7160   2005531442
+7161   2125567529
+7162   1369273768
+7163   1094667216
+7164   2042136499
+7165   681547310
+7166   331034461
+7167   1487528967
+7168   1639893483
+7169   1967824526
+7170   1769751950
+7171   948208391
+7172   1299101791
+7173   670883243
+7174   1442229602
+7175   1642338407
+7176   288522099
+7177   1694601080
+7178   1351609959
+7179   632379898
+7180   79812466
+7181   1470772319
+7182   1569370187
+7183   1489772946
+7184   1093230620
+7185   1905165299
+7186   908484804
+7187   198821314
+7188   288399318
+7189   1806213813
+7190   1765641872
+7191   146447113
+7192   1784297694
+7193   987431992
+7194   1241114329
+7195   1678950545
+7196   1668979302
+7197   1572148791
+7198   1018995864
+7199   1161389138
+7200   1392489669
+7201   641264166
+7202   2109597529
+7203   544107812
+7204   1312147410
+7205   1404343483
+7206   38962572
+7207   1600669509
+7208   951460916
+7209   1390572531
+7210   85565759
+7211   1031273382
+7212   713861202
+7213   1654935946
+7214   373562681
+7215   1807091822
+7216   1412617598
+7217   1282047485
+7218   2005913136
+7219   1701016916
+7220   940777650
+7221   1624071360
+7222   1847464029
+7223   577591696
+7224   464019704
+7225   941094711
+7226   109058594
+7227   2132999007
+7228   365759854
+7229   1128054458
+7230   1146904497
+7231   1758249523
+7232   1769318625
+7233   1109018378
+7234   154873687
+7235   933982387
+7236   365878214
+7237   193836259
+7238   387168248
+7239   1317339130
+7240   1584408791
+7241   472734007
+7242   201128864
+7243   150786345
+7244   2127669954
+7245   574691545
+7246   1957878168
+7247   1392803904
+7248   1856739030
+7249   1816307656
+7250   946337172
+7251   650033032
+7252   1292895369
+7253   646317554
+7254   1227624729
+7255   1756915073
+7256   1587412265
+7257   1336683323
+7258   1742430432
+7259   1953172119
+7260   317254133
+7261   741851281
+7262   1563937994
+7263   2086572758
+7264   1850869660
+7265   1718811681
+7266   873071497
+7267   69264226
+7268   1912647941
+7269   1260239745
+7270   1386603356
+7271   1349573084
+7272   1732973753
+7273   1587732220
+7274   1500359429
+7275   1713160059
+7276   14940118
+7277   1310753949
+7278   958480315
+7279   1871679148
+7280   979577958
+7281   1904817487
+7282   374228533
+7283   124989679
+7284   403651393
+7285   1601853262
+7286   1881904752
+7287   1991063658
+7288   791052937
+7289   1476851537
+7290   1796752129
+7291   1108307070
+7292   71219170
+7293   1213206475
+7294   1047396181
+7295   1922088830
+7296   784534509
+7297   1920467678
+7298   1991353056
+7299   549698802
+7300   1033223776
+7301   1230472764
+7302   1899271886
+7303   618713881
+7304   670721337
+7305   1252147667
+7306   184390292
+7307   685661455
+7308   415417969
+7309   1142870607
+7310   409856955
+7311   1394995927
+7312   900204446
+7313   784085488
+7314   1519985606
+7315   1303855840
+7316   238455102
+7317   1254406710
+7318   1147435850
+7319   1029508039
+7320   583774599
+7321   796704332
+7322   2137815110
+7323   654993770
+7324   2009910807
+7325   1037727643
+7326   429598952
+7327   646961668
+7328   810711673
+7329   273468361
+7330   1196660470
+7331   1843935449
+7332   1503941125
+7333   948448708
+7334   315165682
+7335   27178814
+7336   53112728
+7337   499555974
+7338   712840269
+7339   468530697
+7340   1642426581
+7341   1122697225
+7342   1863526624
+7343   395147380
+7344   1906782713
+7345   1236028582
+7346   1699003220
+7347   2145237816
+7348   342951644
+7349   698955422
+7350   1027262207
+7351   926726244
+7352   1495659754
+7353   1017593669
+7354   1581720014
+7355   1358086914
+7356   2055321312
+7357   2011318966
+7358   2005048582
+7359   718549338
+7360   137303679
+7361   1054225405
+7362   415001139
+7363   1641244805
+7364   2002674113
+7365   730166822
+7366   1668423619
+7367   2055786841
+7368   1229722796
+7369   233780241
+7370   376833890
+7371   724665730
+7372   1356477466
+7373   92876866
+7374   1119813110
+7375   1115776531
+7376   1328905448
+7377   671332682
+7378   1113530699
+7379   1671857093
+7380   1370288104
+7381   2140792907
+7382   451099689
+7383   718464211
+7384   1010902928
+7385   2032819703
+7386   2076551125
+7387   918740593
+7388   1896655021
+7389   1934116059
+7390   1637289931
+7391   2033958701
+7392   840857816
+7393   2052291070
+7394   1527719858
+7395   696048282
+7396   634974244
+7397   1048659829
+7398   604351475
+7399   1864697041
+7400   1282440070
+7401   981185366
+7402   441879123
+7403   491433888
+7404   1074062232
+7405   1561692233
+7406   1607210420
+7407   255484033
+7408   85541267
+7409   573257471
+7410   1927341126
+7411   1455829371
+7412   566566730
+7413   230957167
+7414   26809934
+7415   1577469659
+7416   116293222
+7417   2103361059
+7418   348726604
+7419   2012948243
+7420   1889993471
+7421   1986016535
+7422   1899423296
+7423   583367639
+7424   1890823957
+7425   1279659506
+7426   1279415921
+7427   378314554
+7428   180835688
+7429   1883767397
+7430   95527947
+7431   1463275758
+7432   717469115
+7433   537407070
+7434   1954709647
+7435   1791531347
+7436   2099099303
+7437   1414436419
+7438   2047015380
+7439   37156922
+7440   1987693890
+7441   1826872858
+7442   1492986293
+7443   406776973
+7444   2057830025
+7445   1519796228
+7446   1984246632
+7447   26639599
+7448   1475673639
+7449   185489588
+7450   2039587843
+7451   1218183462
+7452   24022475
+7453   1791527491
+7454   1801551102
+7455   1914846432
+7456   923703350
+7457   933483375
+7458   145677338
+7459   1104539038
+7460   669767124
+7461   241205285
+7462   420331148
+7463   1387236239
+7464   778612355
+7465   227557147
+7466   1031283939
+7467   730228010
+7468   1641993566
+7469   930815671
+7470   767384932
+7471   1482203809
+7472   610204882
+7473   112887578
+7474   1888980782
+7475   520551259
+7476   1632683806
+7477   1725743766
+7478   547190859
+7479   960873797
+7480   1911233354
+7481   439295054
+7482   31573612
+7483   1935255829
+7484   83338897
+7485   1833124714
+7486   1702618613
+7487   1007042247
+7488   619124441
+7489   1848295952
+7490   2111581285
+7491   1288891566
+7492   2089501237
+7493   384428786
+7494   528644157
+7495   720629945
+7496   611985933
+7497   1559928096
+7498   1450857955
+7499   106495852
+7500   343260120
+7501   70759240
+7502   1588699661
+7503   953465002
+7504   183646818
+7505   1330196795
+7506   1474016261
+7507   1816330624
+7508   908456913
+7509   2021207120
+7510   629720773
+7511   672206619
+7512   313018526
+7513   661294385
+7514   459978800
+7515   396357424
+7516   346935451
+7517   15113765
+7518   1403399671
+7519   966059893
+7520   1863409717
+7521   1367497309
+7522   107467811
+7523   1805427307
+7524   1751926095
+7525   636111968
+7526   378573604
+7527   216428380
+7528   48556417
+7529   1829431559
+7530   322924232
+7531   391816537
+7532   1900190799
+7533   1911623893
+7534   1345281539
+7535   2083837617
+7536   1094337040
+7537   671814152
+7538   1752684593
+7539   2002793953
+7540   545537625
+7541   234921719
+7542   527516924
+7543   858556151
+7544   896216104
+7545   987495724
+7546   1254913575
+7547   1243151556
+7548   1002609490
+7549   510829599
+7550   61727801
+7551   718535559
+7552   1878326908
+7553   169195612
+7554   376479218
+7555   1482769355
+7556   805307580
+7557   755052822
+7558   1699197735
+7559   853863997
+7560   437000734
+7561   2022121968
+7562   1245680534
+7563   189707885
+7564   1786262213
+7565   443478425
+7566   126061855
+7567   733115606
+7568   1115292578
+7569   1878746448
+7570   588425911
+7571   1660830203
+7572   2113668167
+7573   1115942836
+7574   371902706
+7575   862400624
+7576   2103438560
+7577   1626816282
+7578   2105552180
+7579   958564402
+7580   2137645881
+7581   19796333
+7582   1677099962
+7583   1868489141
+7584   188991945
+7585   2053579180
+7586   1203774848
+7587   994299525
+7588   661148355
+7589   755488935
+7590   1848163523
+7591   1098149089
+7592   630127255
+7593   946360409
+7594   1287856974
+7595   268905821
+7596   1389838835
+7597   1413918829
+7598   1002021427
+7599   357647765
+7600   1145181630
+7601   1590447338
+7602   2018477968
+7603   1111366149
+7604   558906526
+7605   242897026
+7606   1973766773
+7607   514861439
+7608   1869713308
+7609   1931835305
+7610   1473425841
+7611   1859875541
+7612   1951631638
+7613   1003042155
+7614   1580881034
+7615   2140623583
+7616   909137688
+7617   637172234
+7618   987439461
+7619   1570286043
+7620   1392661170
+7621   688119336
+7622   520951484
+7623   2022788425
+7624   1634479745
+7625   1808808458
+7626   144210598
+7627   876834932
+7628   1075243640
+7629   1146232025
+7630   1234482697
+7631   72941622
+7632   589195716
+7633   1105477017
+7634   1184307771
+7635   1148102242
+7636   1348374044
+7637   1010590897
+7638   1662963681
+7639   1070603704
+7640   794942554
+7641   988905875
+7642   782995598
+7643   599090545
+7644   1991948030
+7645   216392984
+7646   592230480
+7647   753602070
+7648   853565219
+7649   1579669941
+7650   176404465
+7651   98742741
+7652   120305629
+7653   697355949
+7654   2121531166
+7655   1754785375
+7656   358680760
+7657   118258117
+7658   484136659
+7659   1433924400
+7660   1264490142
+7661   1718619357
+7662   1506866022
+7663   1853685858
+7664   676612726
+7665   543690145
+7666   854304453
+7667   2024986770
+7668   1554281042
+7669   369784486
+7670   948106827
+7671   201739949
+7672   1358690361
+7673   1731102425
+7674   800830494
+7675   1203154744
+7676   1947495409
+7677   1393060974
+7678   1956756814
+7679   653576980
+7680   825247268
+7681   2133161280
+7682   752319721
+7683   945552897
+7684   683033581
+7685   726367240
+7686   552854624
+7687   1041714341
+7688   844625357
+7689   1036991284
+7690   328155093
+7691   2109115499
+7692   608126993
+7693   1835021115
+7694   1815317710
+7695   1284739719
+7696   231227613
+7697   522138515
+7698   1162242842
+7699   1785508655
+7700   891923001
+7701   2110349669
+7702   1987248604
+7703   103129715
+7704   1693968446
+7705   640595450
+7706   1306284459
+7707   1493980207
+7708   2033656425
+7709   1115557625
+7710   73540
+7711   711420045
+7712   1101235257
+7713   752393261
+7714   1656972942
+7715   1784268839
+7716   1478760501
+7717   62343919
+7718   678499532
+7719   175902210
+7720   1099335203
+7721   1006654626
+7722   137534062
+7723   1707462196
+7724   694192093
+7725   1952851772
+7726   844718267
+7727   925419706
+7728   327506639
+7729   2006961109
+7730   563444714
+7731   1219429640
+7732   1969827130
+7733   403209670
+7734   1322559355
+7735   1516311928
+7736   1043805121
+7737   481360166
+7738   862808488
+7739   929977898
+7740   1596917792
+7741   862882028
+7742   1641397943
+7743   550669401
+7744   1615275289
+7745   1150887237
+7746   187454592
+7747   946552143
+7748   1213231156
+7749   865954125
+7750   1122454353
+7751   165082711
+7752   1872608751
+7753   1259988415
+7754   1872544907
+7755   419317196
+7756   1065356539
+7757   569779527
+7758   1344736903
+7759   1392863178
+7760   429256988
+7761   1908181617
+7762   464809171
+7763   251600471
+7764   163907639
+7765   1787368526
+7766   1767912399
+7767   1207712760
+7768   121245045
+7769   483237239
+7770   2137690658
+7771   1718162837
+7772   1346119267
+7773   1631604953
+7774   121348590
+7775   813910909
+7776   635008543
+7777   308803183
+7778   1760463052
+7779   1848239699
+7780   1174757308
+7781   735433757
+7782   2013322411
+7783   899882411
+7784   1995422173
+7785   1738383670
+7786   1319199607
+7787   913295064
+7788   160679549
+7789   516452862
+7790   158674595
+7791   589936538
+7792   277150831
+7793   623483766
+7794   841537009
+7795   441058471
+7796   263368644
+7797   461965760
+7798   1648771231
+7799   384613689
+7800   945203000
+7801   1638978242
+7802   2102776526
+7803   143838619
+7804   1123099547
+7805   76641469
+7806   957749528
+7807   1758108090
+7808   385444652
+7809   570728932
+7810   1458864142
+7811   1560201960
+7812   1306162690
+7813   1324702905
+7814   312600723
+7815   1154101215
+7816   915602927
+7817   1631800330
+7818   2067396279
+7819   1076282477
+7820   769545
+7821   78587226
+7822   1666219015
+7823   277920376
+7824   702070992
+7825   360272376
+7826   718978847
+7827   965439637
+7828   822238136
+7829   220266431
+7830   1350053326
+7831   1767441136
+7832   1859244673
+7833   1305346205
+7834   1911279756
+7835   834860572
+7836   1381987674
+7837   721545636
+7838   445485015
+7839   1767432326
+7840   1292274569
+7841   1904349157
+7842   1180150638
+7843   450953611
+7844   1081568414
+7845   1492751361
+7846   1605054826
+7847   1997171341
+7848   977068043
+7849   1524967457
+7850   925970170
+7851   977837588
+7852   1603554684
+7853   444705537
+7854   1255757965
+7855   158142028
+7856   804977913
+7857   1974736812
+7858   1123581665
+7859   1627216050
+7860   47519595
+7861   326151344
+7862   1247173538
+7863   1906764268
+7864   1631497549
+7865   1010969646
+7866   594141193
+7867   866001575
+7868   1732515283
+7869   1039626208
+7870   485950253
+7871   877306204
+7872   796491717
+7873   1666100891
+7874   1328259815
+7875   1878060131
+7876   1011368604
+7877   785830993
+7878   1727747824
+7879   1988436647
+7880   163314802
+7881   506234347
+7882   818790588
+7883   1766869486
+7884   950939884
+7885   2074548553
+7886   1925011515
+7887   1755917798
+7888   1901801717
+7889   901109532
+7890   1235650200
+7891   1949321313
+7892   1227260876
+7893   335340090
+7894   1708601933
+7895   711274777
+7896   1346309737
+7897   155259478
+7898   1577276352
+7899   931341372
+7900   1194885686
+7901   2063226605
+7902   1808647576
+7903   1991377403
+7904   1581843848
+7905   989423743
+7906   1721953886
+7907   445728804
+7908   1775254736
+7909   1302218063
+7910   286681804
+7911   1938569538
+7912   1808452410
+7913   1105472392
+7914   1557955377
+7915   611908646
+7916   1032537297
+7917   1335483244
+7918   220342796
+7919   786855366
+7920   89109128
+7921   1455992996
+7922   588693031
+7923   1316370005
+7924   1791333087
+7925   149811317
+7926   2027644782
+7927   990159176
+7928   305070795
+7929   1457437487
+7930   1921500548
+7931   1499956482
+7932   1373180444
+7933   1582664476
+7934   1343850237
+7935   807540645
+7936   424604571
+7937   918320476
+7938   1253269449
+7939   52375659
+7940   73054891
+7941   1539951253
+7942   1990945197
+7943   1881507301
+7944   497939997
+7945   1401416926
+7946   345932299
+7947   1530477294
+7948   589416522
+7949   566275096
+7950   169849013
+7951   678525651
+7952   2022268092
+7953   758542044
+7954   1994895656
+7955   1666117531
+7956   908353361
+7957   1875056790
+7958   508793059
+7959   1213424157
+7960   1185010629
+7961   282809959
+7962   565896991
+7963   410707426
+7964   1865474435
+7965   1909747228
+7966   1218248071
+7967   142595358
+7968   680584056
+7969   324033872
+7970   194971017
+7971   753638947
+7972   1863985126
+7973   38432567
+7974   487662600
+7975   214441475
+7976   1439849493
+7977   833594900
+7978   1744918770
+7979   2029266016
+7980   1399869996
+7981   1914767783
+7982   560308019
+7983   1274654440
+7984   525826179
+7985   407720027
+7986   793288324
+7987   1434179541
+7988   135293169
+7989   1302081383
+7990   500120050
+7991   1320303799
+7992   1584891343
+7993   1066017041
+7994   1731011225
+7995   1302882130
+7996   828280621
+7997   801775648
+7998   1445477489
+7999   1508864678
+8000   1125809520
+8001   1640448506
+8002   115019977
+8003   842310998
+8004   1678881073
+8005   602682578
+8006   1056752474
+8007   971246919
+8008   1436277478
+8009   654187596
+8010   853029287
+8011   688663826
+8012   421471731
+8013   1413337306
+8014   1963318266
+8015   947297910
+8016   1821057333
+8017   609122942
+8018   233993803
+8019   1956350502
+8020   1911204326
+8021   734113853
+8022   1129170653
+8023   1348612021
+8024   1800130894
+8025   712698230
+8026   504010503
+8027   480927868
+8028   1514473878
+8029   1949487992
+8030   1989792546
+8031   492799751
+8032   1442452851
+8033   2104812523
+8034   1335110749
+8035   973850276
+8036   560011453
+8037   244379575
+8038   1945097195
+8039   1996288931
+8040   898567171
+8041   650642834
+8042   537469109
+8043   1320038902
+8044   2063980140
+8045   353303728
+8046   119853165
+8047   1737553825
+8048   962426670
+8049   353846968
+8050   1546420680
+8051   726147348
+8052   1087960822
+8053   528107685
+8054   2074759369
+8055   740608068
+8056   1240805916
+8057   431286225
+8058   1221535936
+8059   607796146
+8060   233290569
+8061   1063844834
+8062   1100595897
+8063   1675743420
+8064   1021173710
+8065   288222999
+8066   502110049
+8067   1581185163
+8068   532602574
+8069   299723596
+8070   1429990447
+8071   1431169746
+8072   950366431
+8073   1967459556
+8074   603725000
+8075   866862923
+8076   173279636
+8077   723578165
+8078   456933101
+8079   1135706307
+8080   1077425134
+8081   2003353781
+8082   1861853655
+8083   17902308
+8084   383977818
+8085   1789129377
+8086   758510376
+8087   1624783734
+8088   72931954
+8089   1980046313
+8090   85096233
+8091   306222523
+8092   896407499
+8093   1185692130
+8094   1981965944
+8095   1917581209
+8096   1473915129
+8097   336592345
+8098   1351282725
+8099   2006517704
+8100   636315941
+8101   633789524
+8102   1290203802
+8103   1586682372
+8104   453765432
+8105   1893928802
+8106   306061648
+8107   627045069
+8108   470023320
+8109   762994749
+8110   1762751376
+8111   1547448454
+8112   618864882
+8113   1477121383
+8114   1565350762
+8115   1002842700
+8116   1118767112
+8117   176377490
+8118   480142787
+8119   1191699066
+8120   8940155
+8121   565239020
+8122   1497921590
+8123   905347655
+8124   1750931150
+8125   1332403886
+8126   675445216
+8127   1077362632
+8128   1668996231
+8129   2026727941
+8130   936396688
+8131   157828524
+8132   513033817
+8133   79116842
+8134   1744510897
+8135   966799250
+8136   1973045644
+8137   2050572545
+8138   1593844319
+8139   295585316
+8140   666083646
+8141   1209112047
+8142   1843033770
+8143   1284948528
+8144   538749782
+8145   1260900884
+8146   140307580
+8147   1657516895
+8148   1437278375
+8149   620450367
+8150   701732313
+8151   1446218530
+8152   1185689387
+8153   52170255
+8154   204082537
+8155   789136890
+8156   1384574141
+8157   879527754
+8158   1866499522
+8159   906086724
+8160   758772047
+8161   655412562
+8162   1063915249
+8163   1271805865
+8164   734529404
+8165   660942498
+8166   91121467
+8167   560091400
+8168   564031395
+8169   1684965786
+8170   855676717
+8171   1230115041
+8172   746594185
+8173   551226839
+8174   367579921
+8175   1285343967
+8176   1812127724
+8177   507887501
+8178   795377214
+8179   1101922451
+8180   1128337869
+8181   1497109528
+8182   400657333
+8183   166543608
+8184   1549279783
+8185   604739871
+8186   955680498
+8187   786370277
+8188   1484267625
+8189   674696372
+8190   1692457001
+8191   95556024
+8192   1330108934
+8193   608888602
+8194   1367361889
+8195   2064638338
+8196   1269831100
+8197   1458483356
+8198   477246091
+8199   1833862495
+8200   995965494
+8201   1332922808
+8202   916493888
+8203   1742559679
+8204   1884149647
+8205   1284073809
+8206   880419999
+8207   1548793723
+8208   1791961311
+8209   1675797213
+8210   503232526
+8211   772815532
+8212   1025423093
+8213   903889860
+8214   939359140
+8215   427219229
+8216   1508629731
+8217   1895039639
+8218   1213589506
+8219   845413708
+8220   422252363
+8221   758562859
+8222   940969732
+8223   1752361298
+8224   1367451462
+8225   160847974
+8226   1669515988
+8227   489798914
+8228   1619331330
+8229   2146762079
+8230   176177762
+8231   467813177
+8232   1332201239
+8233   1092671650
+8234   62889208
+8235   1068867239
+8236   229261812
+8237   943309207
+8238   470177314
+8239   2021223123
+8240   471622773
+8241   973409841
+8242   646555007
+8243   1497045866
+8244   1877299701
+8245   1585914147
+8246   1924265095
+8247   1238445784
+8248   1333470138
+8249   990370953
+8250   2083859492
+8251   1755722502
+8252   1748933813
+8253   877345576
+8254   1360600152
+8255   968901627
+8256   1038193550
+8257   882632492
+8258   1458700541
+8259   510041233
+8260   881910924
+8261   1634878303
+8262   977854410
+8263   66628515
+8264   580066306
+8265   1040743618
+8266   1135495754
+8267   809328118
+8268   1984052826
+8269   1605673069
+8270   683067593
+8271   308191951
+8272   431599262
+8273   1329622600
+8274   1805237817
+8275   161415315
+8276   768053099
+8277   1582019265
+8278   1399861099
+8279   2101523238
+8280   424906570
+8281   1336236943
+8282   1709762092
+8283   26356735
+8284   66098871
+8285   922878596
+8286   995258362
+8287   1104292422
+8288   1805511088
+8289   306475256
+8290   1614333655
+8291   539938364
+8292   1941353559
+8293   444704417
+8294   606566880
+8295   373936217
+8296   1485448035
+8297   1742062634
+8298   1183264335
+8299   1322017213
+8300   1200252055
+8301   1866331928
+8302   1630209164
+8303   1631851317
+8304   1048470880
+8305   1287963334
+8306   1793266632
+8307   1816523980
+8308   722498951
+8309   1045644083
+8310   1770563570
+8311   1147405521
+8312   234397378
+8313   1332842014
+8314   1173762257
+8315   300496250
+8316   108236962
+8317   21536971
+8318   1404788672
+8319   1913748050
+8320   328012227
+8321   871638679
+8322   306202767
+8323   121882139
+8324   1316343096
+8325   912769647
+8326   495818356
+8327   654307483
+8328   507348633
+8329   1679082692
+8330   1976324697
+8331   1707600689
+8332   1397930972
+8333   1459050213
+8334   1191968358
+8335   298918205
+8336   599529899
+8337   837751343
+8338   2115442185
+8339   1322028850
+8340   1883395426
+8341   1738522107
+8342   321950724
+8343   2117792805
+8344   923880473
+8345   1495712981
+8346   270805407
+8347   1032117435
+8348   1517249952
+8349   1675594079
+8350   798381837
+8351   1845262180
+8352   399749110
+8353   1104584604
+8354   1967144319
+8355   1716092206
+8356   2017354251
+8357   315479027
+8358   222916041
+8359   377219237
+8360   1994561719
+8361   51757090
+8362   2084819926
+8363   1245009044
+8364   1510807304
+8365   1129304636
+8366   1543927249
+8367   2110337203
+8368   1967055979
+8369   1511885786
+8370   1284882406
+8371   1702967758
+8372   1102924245
+8373   1606833130
+8374   1673276915
+8375   2026804718
+8376   955062463
+8377   1944082322
+8378   911438505
+8379   324828767
+8380   1472192753
+8381   1709820342
+8382   22607299
+8383   1871941863
+8384   666921299
+8385   1989751618
+8386   1440550421
+8387   536791902
+8388   157746998
+8389   1663466462
+8390   914011139
+8391   4825069
+8392   1715223553
+8393   851347417
+8394   1249834113
+8395   1078547209
+8396   1980652054
+8397   646277714
+8398   1041400764
+8399   1800224385
+8400   10679852
+8401   178799522
+8402   1355708495
+8403   1113604097
+8404   1785632652
+8405   881501762
+8406   992925167
+8407   593211467
+8408   678100436
+8409   1904363672
+8410   918040235
+8411   2809541
+8412   1466700367
+8413   940647534
+8414   1874751404
+8415   2133621666
+8416   782915505
+8417   1167818177
+8418   522929920
+8419   940662503
+8420   683800992
+8421   1436941060
+8422   945487572
+8423   251540897
+8424   140804829
+8425   47838038
+8426   1330088106
+8427   2121456883
+8428   694115752
+8429   224005222
+8430   1774197621
+8431   704795605
+8432   402804745
+8433   982422468
+8434   1818399702
+8435   40953749
+8436   1863924231
+8437   663841222
+8438   634165217
+8439   394541019
+8440   420721246
+8441   1552205452
+8442   397350561
+8443   1887421613
+8444   345369338
+8445   124618317
+8446   1873559631
+8447   1128284843
+8448   1292436495
+8449   249005904
+8450   2068947346
+8451   1976237487
+8452   1685946964
+8453   866951271
+8454   80294736
+8455   1826751793
+8456   914789309
+8457   1410382842
+8458   1800725029
+8459   1608905061
+8460   1634388064
+8461   1427439002
+8462   166217018
+8463   2037192809
+8464   262377822
+8465   1984616721
+8466   2078146559
+8467   2126302053
+8468   500974295
+8469   564828128
+8470   373359425
+8471   921695541
+8472   2117033580
+8473   770709986
+8474   661633507
+8475   314919270
+8476   895328303
+8477   387709490
+8478   1443204114
+8479   40281150
+8480   636715394
+8481   1364667812
+8482   2016518637
+8483   175178710
+8484   84135435
+8485   2096813373
+8486   2001930504
+8487   998924744
+8488   1359712567
+8489   1655171885
+8490   460346158
+8491   846616984
+8492   935127239
+8493   626563176
+8494   736326145
+8495   1197505061
+8496   463696249
+8497   666989056
+8498   1176323467
+8499   964670544
+8500   1231817184
+8501   1549682892
+8502   1886366086
+8503   1201367116
+8504   172909230
+8505   400515945
+8506   1516286387
+8507   1068237533
+8508   788225435
+8509   812006853
+8510   1108518684
+8511   1424940830
+8512   29191017
+8513   977553673
+8514   1600119540
+8515   113326453
+8516   926883399
+8517   1454566396
+8518   1112251197
+8519   139112318
+8520   962254633
+8521   1572597355
+8522   985729302
+8523   1897381872
+8524   51676884
+8525   1722055448
+8526   947403286
+8527   515373133
+8528   241560856
+8529   2123726753
+8530   1480043678
+8531   1473378041
+8532   1525925997
+8533   1218926116
+8534   527261509
+8535   1698835227
+8536   1619442061
+8537   2043547896
+8538   619589112
+8539   260183848
+8540   708071101
+8541   1728107796
+8542   1685124678
+8543   737262119
+8544   558177822
+8545   1137760571
+8546   850588572
+8547   1485061221
+8548   444843319
+8549   1962839769
+8550   1624173539
+8551   1407097953
+8552   1387953477
+8553   462419194
+8554   1156996177
+8555   1439630361
+8556   36990994
+8557   2104399463
+8558   1955003494
+8559   278551850
+8560   2080642568
+8561   1287563524
+8562   1751929891
+8563   1459084917
+8564   359005992
+8565   131707753
+8566   1010436496
+8567   1978448053
+8568   27772001
+8569   1630025609
+8570   91148254
+8571   735843103
+8572   1210649757
+8573   1776272932
+8574   1473105222
+8575   1768827579
+8576   766549855
+8577   176210146
+8578   1106405152
+8579   1211393175
+8580   2139049915
+8581   583095044
+8582   471007480
+8583   1379519744
+8584   1045514238
+8585   1628003657
+8586   671666457
+8587   1082505232
+8588   1584919473
+8589   479186304
+8590   1361057082
+8591   1518078393
+8592   1766749828
+8593   965503326
+8594   829679663
+8595   2125755821
+8596   1097211079
+8597   1840116159
+8598   1956720226
+8599   1124983080
+8600   1322658120
+8601   2047868480
+8602   1860826183
+8603   385824230
+8604   1676657765
+8605   1186447757
+8606   7168161
+8607   295723972
+8608   1362657903
+8609   1113573314
+8610   1507117147
+8611   1354224171
+8612   1696668358
+8613   1978124627
+8614   586260267
+8615   594698948
+8616   1458644637
+8617   1257926725
+8618   1677204180
+8619   896080462
+8620   1737113029
+8621   890777614
+8622   266675207
+8623   1356379209
+8624   1856280940
+8625   1096354870
+8626   1334651382
+8627   806008371
+8628   788987382
+8629   1143887961
+8630   1930991452
+8631   2111645502
+8632   1044272793
+8633   1644333987
+8634   349986084
+8635   573446910
+8636   683298097
+8637   357154246
+8638   869170883
+8639   2045956000
+8640   1470727560
+8641   228804382
+8642   1252696523
+8643   1019912270
+8644   59445362
+8645   1838956791
+8646   1614611218
+8647   1518089999
+8648   949399868
+8649   1144331750
+8650   266686813
+8651   539029249
+8652   2035109364
+8653   533362020
+8654   1895408458
+8655   1743906657
+8656   1629716891
+8657   1082576193
+8658   402431380
+8659   271220625
+8660   78980506
+8661   185939184
+8662   235382479
+8663   1123253299
+8664   1830273172
+8665   585368564
+8666   1696700210
+8667   366087621
+8668   942522810
+8669   418387445
+8670   264559973
+8671   265766722
+8672   647191827
+8673   1517256497
+8674   1285678992
+8675   706637189
+8676   1208729640
+8677   752806562
+8678   77243540
+8679   10645860
+8680   1897138312
+8681   343930353
+8682   549675109
+8683   1784764028
+8684   877292374
+8685   297599919
+8686   1381187037
+8687   359525617
+8688   1380176112
+8689   1783618418
+8690   630746242
+8691   1459156618
+8692   1969557602
+8693   866128721
+8694   434926270
+8695   1652347126
+8696   1451497285
+8697   2131626480
+8698   2018434747
+8699   246536447
+8700   402530277
+8701   135511073
+8702   512303169
+8703   1049722104
+8704   1652767570
+8705   1797982161
+8706   1756359294
+8707   714013562
+8708   403305075
+8709   1833602834
+8710   724659422
+8711   152959739
+8712   30049540
+8713   1274334531
+8714   1937723768
+8715   907341914
+8716   1571934450
+8717   1171427157
+8718   1266867531
+8719   804626915
+8720   807561927
+8721   1897613773
+8722   116299885
+8723   629635882
+8724   616258846
+8725   551226155
+8726   134499360
+8727   2067756132
+8728   535368987
+8729   5450460
+8730   166808931
+8731   937899264
+8732   140961533
+8733   679112101
+8734   1987621369
+8735   1793729103
+8736   329610614
+8737   1596497015
+8738   360259017
+8739   732915690
+8740   1282616201
+8741   1084918439
+8742   885875429
+8743   1312665741
+8744   211769322
+8745   676115549
+8746   72524007
+8747   1783703772
+8748   1847542707
+8749   1339391538
+8750   440847039
+8751   507620986
+8752   1089521663
+8753   557146925
+8754   1137256868
+8755   1705780510
+8756   1108373080
+8757   1271756229
+8758   1626052994
+8759   1643742068
+8760   1277206689
+8761   1792861925
+8762   434157684
+8763   1418168222
+8764   324490378
+8765   274295405
+8766   1064413677
+8767   654100993
+8768   1870792420
+8769   1424672694
+8770   1387016683
+8771   1005924974
+8772   362107485
+8773   125408464
+8774   171107067
+8775   573876807
+8776   801524014
+8777   243631075
+8778   210096931
+8779   501583073
+8780   1583022613
+8781   650943971
+8782   1009204059
+8783   525060629
+8784   1208090896
+8785   2146460928
+8786   83357491
+8787   168980328
+8788   1270733509
+8789   1709410485
+8790   1812722396
+8791   400456550
+8792   1354788762
+8793   99396433
+8794   1818624772
+8795   1679279141
+8796   373691838
+8797   735554801
+8798   185896486
+8799   97000611
+8800   12743847
+8801   1572913169
+8802   1102925585
+8803   374851332
+8804   1698321633
+8805   1274032652
+8806   948728139
+8807   352361999
+8808   1517663727
+8809   1158825070
+8810   853945072
+8811   953202693
+8812   1809769041
+8813   1863149132
+8814   1478263322
+8815   870376289
+8816   1862126412
+8817   1561620813
+8818   1039356618
+8819   985376273
+8820   1123547650
+8821   704595366
+8822   1385832823
+8823   330852764
+8824   803991799
+8825   1056973947
+8826   2010131905
+8827   1177683638
+8828   1792528748
+8829   48544743
+8830   1274684249
+8831   1805272595
+8832   1621457912
+8833   230126186
+8834   32640279
+8835   1172295898
+8836   1504158838
+8837   981368418
+8838   1524657897
+8839   874338918
+8840   2140193488
+8841   231119322
+8842   1827541611
+8843   1802478882
+8844   2094268454
+8845   1158321285
+8846   525371523
+8847   1808911218
+8848   572458450
+8849   1564728141
+8850   646803843
+8851   1696006100
+8852   121839860
+8853   2032636666
+8854   2026858864
+8855   925831659
+8856   942126965
+8857   1889507122
+8858   2103515297
+8859   587172065
+8860   1938051865
+8861   1230715898
+8862   244961012
+8863   1412026130
+8864   1460842084
+8865   277601291
+8866   436838380
+8867   817517275
+8868   1258969709
+8869   1961496277
+8870   1691856193
+8871   1251679549
+8872   45131951
+8873   1371914156
+8874   906674783
+8875   2139400405
+8876   382751793
+8877   1432046307
+8878   1800827975
+8879   955210243
+8880   849290800
+8881   300148170
+8882   503732695
+8883   971130660
+8884   185301188
+8885   383107911
+8886   1896962320
+8887   1127428153
+8888   125131385
+8889   1852993969
+8890   1714600218
+8891   2063183251
+8892   936226220
+8893   1959561230
+8894   1327725733
+8895   249584656
+8896   89678873
+8897   1764564113
+8898   1067101931
+8899   1348648582
+8900   1578576742
+8901   611474476
+8902   452844484
+8903   1623708694
+8904   1983388632
+8905   1359519267
+8906   1615625451
+8907   218656777
+8908   644081926
+8909   1268969779
+8910   1173867020
+8911   1493372727
+8912   1569117949
+8913   1677599715
+8914   317019739
+8915   1754419138
+8916   2060707627
+8917   66498411
+8918   734363643
+8919   38355364
+8920   1919492381
+8921   301480214
+8922   2101538615
+8923   708234953
+8924   113557796
+8925   1281780700
+8926   957819609
+8927   203236670
+8928   898861165
+8929   2024921541
+8930   1551885252
+8931   329954260
+8932   488912369
+8933   2004729736
+8934   1953662954
+8935   324817354
+8936   1216765356
+8937   1421804757
+8938   543474131
+8939   1860847282
+8940   543290888
+8941   1717341152
+8942   1206736361
+8943   2112408838
+8944   1247457219
+8945   1523756101
+8946   1719344328
+8947   1160681198
+8948   1590254512
+8949   306224323
+8950   1199036563
+8951   1362263245
+8952   607704537
+8953   1153091530
+8954   2070498198
+8955   721262334
+8956   287388583
+8957   880834160
+8958   924499004
+8959   1186249748
+8960   758272053
+8961   328900608
+8962   1516204008
+8963   1247184422
+8964   186146697
+8965   1322383314
+8966   1572001776
+8967   1402912053
+8968   596704424
+8969   2115475908
+8970   1116275687
+8971   1139995312
+8972   1685333412
+8973   175528401
+8974   1104920502
+8975   785306983
+8976   1699284502
+8977   676781182
+8978   1945988182
+8979   1142055366
+8980   983005506
+8981   997541097
+8982   356834964
+8983   1590710043
+8984   3148979
+8985   279849514
+8986   164488729
+8987   290537562
+8988   1160683674
+8989   1088987733
+8990   1476787311
+8991   1918955727
+8992   1417888342
+8993   845507671
+8994   1018656502
+8995   1604035039
+8996   20407338
+8997   443174630
+8998   859463444
+8999   617111762
+9000   411166890
+9001   1975739131
+9002   1757107074
+9003   2096500302
+9004   3783884
+9005   714543929
+9006   734323638
+9007   1703068386
+9008   1391325111
+9009   532828172
+9010   697640105
+9011   226846969
+9012   1530369269
+9013   1054475069
+9014   1817557013
+9015   1533518248
+9016   1334324583
+9017   1982045742
+9018   1824055811
+9019   347524610
+9020   923549828
+9021   1153359474
+9022   118996689
+9023   193954522
+9024   1998867145
+9025   1137653191
+9026   1797989561
+9027   2019274483
+9028   1580827822
+9029   509969357
+9030   488902597
+9031   1991994712
+9032   338224840
+9033   98526024
+9034   1941011367
+9035   342008725
+9036   813069953
+9037   527851357
+9038   2045077111
+9039   56911416
+9040   1060679529
+9041   595233568
+9042   283758386
+9043   443565150
+9044   1649708637
+9045   2101315399
+9046   1977083398
+9047   836549573
+9048   1935877493
+9049   1653655561
+9050   1184074183
+9051   711943673
+9052   659531387
+9053   1303070872
+9054   905898195
+9055   510914885
+9056   293240416
+9057   556404108
+9058   382705720
+9059   1874068238
+9060   1066373465
+9061   871608318
+9062   1718579302
+9063   1404598306
+9064   970134342
+9065   1512107021
+9066   1746607031
+9067   1783204295
+9068   2039958378
+9069   1644200494
+9070   1840115711
+9071   953154259
+9072   91950415
+9073   2123874097
+9074   1396719409
+9075   1741659052
+9076   2077705848
+9077   1226319160
+9078   430724977
+9079   1866099694
+9080   732491073
+9081   1614799160
+9082   430559719
+9083   1392022461
+9084   770386385
+9085   1336457915
+9086   1902937346
+9087   1063626801
+9088   1892862023
+9089   138159418
+9090   790211391
+9091   811751841
+9092   1009767736
+9093   361307045
+9094   68866499
+9095   1979902078
+9096   1873414067
+9097   1815473530
+9098   1615622725
+9099   1765888797
+9100   1312190376
+9101   1308254789
+9102   571559409
+9103   1404140791
+9104   1284645238
+9105   1968278818
+9106   998316196
+9107   1214867439
+9108   1047114330
+9109   1429041173
+9110   933483485
+9111   1779605404
+9112   896356686
+9113   1364043204
+9114   1024144217
+9115   1666743071
+9116   553017471
+9117   779597915
+9118   582886224
+9119   298395847
+9120   917757333
+9121   1373097615
+9122   1110147688
+9123   1927525070
+9124   1734404660
+9125   1179014187
+9126   1759943500
+9127   1460335079
+9128   847004069
+9129   1228082578
+9130   1078740229
+9131   11710797
+9132   388853719
+9133   1650299638
+9134   1415851589
+9135   1673498957
+9136   1471094808
+9137   266684137
+9138   740882748
+9139   370725491
+9140   1695725310
+9141   1674366233
+9142   2847247
+9143   444598348
+9144   890925790
+9145   1026991464
+9146   2111341419
+9147   1443943261
+9148   1806589379
+9149   546743995
+9150   1742339108
+9151   576863064
+9152   1919841610
+9153   705003148
+9154   356904486
+9155   1506762623
+9156   1884017335
+9157   2116847987
+9158   819614054
+9159   583537756
+9160   1197446917
+9161   1898354283
+9162   595248554
+9163   1586300636
+9164   1401170273
+9165   2011100143
+9166   1112315945
+9167   724781434
+9168   130300632
+9169   1853198694
+9170   1095506925
+9171   1826025942
+9172   1380081279
+9173   1098354172
+9174   123140643
+9175   123523421
+9176   2125345636
+9177   86998414
+9178   1567466683
+9179   1784451367
+9180   633742410
+9181   1162322143
+9182   213830783
+9183   406100372
+9184   1867325292
+9185   570735270
+9186   1912862995
+9187   1603858979
+9188   540099609
+9189   584993402
+9190   39913088
+9191   1737546526
+9192   335864037
+9193   635161642
+9194   1176363514
+9195   1737034311
+9196   498778137
+9197   141195811
+9198   314332097
+9199   629078769
+9200   1994394505
+9201   1409839022
+9202   307621063
+9203   1226992137
+9204   360709546
+9205   430761706
+9206   1350515558
+9207   338571534
+9208   517760121
+9209   770498593
+9210   2123022901
+9211   1151502531
+9212   1932820737
+9213   189370036
+9214   1557602903
+9215   1652662381
+9216   760105306
+9217   1322982251
+9218   1109037712
+9219   1300204915
+9220   1907975653
+9221   1148950800
+9222   890267793
+9223   96356042
+9224   1784112442
+9225   2066631307
+9226   1833390353
+9227   135406931
+9228   60343471
+9229   238802
+9230   764485700
+9231   2054737976
+9232   1410077824
+9233   1072106764
+9234   1134246465
+9235   1770787370
+9236   1502868470
+9237   337278376
+9238   2109358904
+9239   2020628591
+9240   1107776969
+9241   2084898157
+9242   1024647474
+9243   893114058
+9244   126784546
+9245   434766730
+9246   398292791
+9247   886889852
+9248   1757748981
+9249   1507330504
+9250   39611120
+9251   1518240986
+9252   508797656
+9253   929878913
+9254   1614597028
+9255   145426451
+9256   849026573
+9257   1300503734
+9258   280833382
+9259   909370044
+9260   1300742536
+9261   1045319083
+9262   816624372
+9263   563336713
+9264   2117425847
+9265   1950870838
+9266   186640435
+9267   1472810669
+9268   140665566
+9269   148515692
+9270   1345955613
+9271   1248442535
+9272   85930201
+9273   223119439
+9274   2141556594
+9275   212714747
+9276   657886169
+9277   392365737
+9278   1099604600
+9279   268151502
+9280   1899696241
+9281   1139215720
+9282   1786392488
+9283   261010250
+9284   2069094633
+9285   1253505869
+9286   406436701
+9287   770637558
+9288   406525955
+9289   687270083
+9290   1680007602
+9291   1707268491
+9292   1732589166
+9293   349148327
+9294   123121556
+9295   1702531365
+9296   152535517
+9297   309761992
+9298   1027858387
+9299   293201083
+9300   458277684
+9301   226330352
+9302   1541643618
+9303   544207885
+9304   449449791
+9305   1535716564
+9306   756922633
+9307   1107335961
+9308   1928082302
+9309   1856527233
+9310   1375487463
+9311   1680294895
+9312   848259305
+9313   1014396304
+9314   1941305145
+9315   769870290
+9316   120418525
+9317   200258198
+9318   1540507849
+9319   526944480
+9320   887528282
+9321   1073031803
+9322   86729323
+9323   472633800
+9324   1422180130
+9325   209850880
+9326   27681518
+9327   1574715647
+9328   519612872
+9329   1055539905
+9330   1867916730
+9331   977890556
+9332   1281870257
+9333   1262076701
+9334   1522098441
+9335   1731320048
+9336   650309617
+9337   131537426
+9338   691172361
+9339   430908271
+9340   1988064659
+9341   2066659825
+9342   2111203167
+9343   688840316
+9344   933572481
+9345   1905024664
+9346   1458710607
+9347   1053991006
+9348   2105282863
+9349   851734808
+9350   1580935486
+9351   845327497
+9352   1924766611
+9353   1667664809
+9354   1317961297
+9355   1199463094
+9356   1877515689
+9357   1345642815
+9358   626695093
+9359   249644913
+9360   253699072
+9361   347128176
+9362   1227535469
+9363   1535569329
+9364   1609204877
+9365   602150263
+9366   1119405730
+9367   112030846
+9368   733687689
+9369   1810578091
+9370   542939118
+9371   574268701
+9372   1729754268
+9373   506658637
+9374   1263109017
+9375   515843101
+9376   264199653
+9377   574335976
+9378   1569834107
+9379   221998868
+9380   1426070784
+9381   1003285945
+9382   1067326365
+9383   1203353748
+9384   523467107
+9385   237804015
+9386   255333194
+9387   253499148
+9388   1583446830
+9389   882028287
+9390   503144062
+9391   1837145903
+9392   1229156463
+9393   1730679531
+9394   1225231584
+9395   690877692
+9396   185346146
+9397   197153666
+9398   802908539
+9399   919033836
+9400   2007731758
+9401   1345847657
+9402   1493302537
+9403   1590002378
+9404   1852506294
+9405   608927906
+9406   2105845480
+9407   2116705947
+9408   1183263883
+9409   1528195939
+9410   191221168
+9411   461851019
+9412   383998237
+9413   1258547533
+9414   1665204767
+9415   907465344
+9416   1496351548
+9417   1920537961
+9418   1160964492
+9419   932314731
+9420   655082601
+9421   1664108554
+9422   621976986
+9423   1884239064
+9424   1247304438
+9425   1847208570
+9426   427633109
+9427   1432650584
+9428   2044362237
+9429   1230541648
+9430   204200772
+9431   1904610347
+9432   428905657
+9433   1697503309
+9434   1347129077
+9435   133928303
+9436   158947568
+9437   1305490909
+9438   103150602
+9439   1342211451
+9440   686203201
+9441   294371770
+9442   1804062470
+9443   1070201438
+9444   1552919304
+9445   1321783590
+9446   1977666782
+9447   901787204
+9448   1094837903
+9449   991147626
+9450   1834101935
+9451   1749920504
+9452   507772533
+9453   308595273
+9454   1486675921
+9455   1755076971
+9456   8320196
+9457   1914309030
+9458   1040243907
+9459   2052682433
+9460   997367030
+9461   1244444680
+9462   1809809132
+9463   1426272687
+9464   794464341
+9465   1009454561
+9466   1560200990
+9467   953411909
+9468   167461823
+9469   1663351592
+9470   148139712
+9471   853665024
+9472   1957723363
+9473   1952202183
+9474   1923866462
+9475   1363159019
+9476   1126502125
+9477   1754049596
+9478   117462575
+9479   73856380
+9480   597713574
+9481   1951564511
+9482   1823776885
+9483   1105486107
+9484   112676136
+9485   1162969158
+9486   713079430
+9487   120996332
+9488   929794540
+9489   1753323338
+9490   26195117
+9491   1927161570
+9492   850284370
+9493   1836004249
+9494   1205950609
+9495   1644748711
+9496   697975163
+9497   618667951
+9498   450676973
+9499   865436986
+9500   134535895
+9501   598816685
+9502   1719102010
+9503   2092259258
+9504   403535220
+9505   1495484824
+9506   1307934629
+9507   1530037345
+9508   1102050772
+9509   1425397205
+9510   1603893726
+9511   1699764346
+9512   1229478068
+9513   1280186963
+9514   657766806
+9515   1342154204
+9516   295672473
+9517   1370846236
+9518   1463150537
+9519   1225467013
+9520   976685926
+9521   1489345654
+9522   1005144935
+9523   1826970296
+9524   1177866256
+9525   63611896
+9526   1324235360
+9527   1875841419
+9528   682279847
+9529   1774912333
+9530   593794757
+9531   816815742
+9532   226245370
+9533   165413119
+9534   761591353
+9535   629780591
+9536   1660897943
+9537   2069525982
+9538   12334288
+9539   615465067
+9540   1347439539
+9541   1616228014
+9542   167745765
+9543   429433959
+9544   748931329
+9545   825512571
+9546   1771588164
+9547   1044603802
+9548   48875160
+9549   1087255053
+9550   122587167
+9551   1025561086
+9552   429117059
+9553   1127732102
+9554   705047735
+9555   1606983315
+9556   1191343998
+9557   2029283095
+9558   1335341086
+9559   1873623845
+9560   1656711780
+9561   1929135843
+9562   542955940
+9563   1882957150
+9564   2094548962
+9565   1304547293
+9566   365254093
+9567   1607963257
+9568   1226589627
+9569   377588382
+9570   75944676
+9571   426545519
+9572   1993816396
+9573   243690442
+9574   855979478
+9575   595264078
+9576   1069203013
+9577   480083994
+9578   1639867880
+9579   1118078173
+9580   1567339047
+9581   1762455048
+9582   2143639260
+9583   1996456107
+9584   742703502
+9585   701203347
+9586   1455955774
+9587   1934047501
+9588   583002794
+9589   643813213
+9590   1660187698
+9591   92230926
+9592   425465408
+9593   55659990
+9594   1975188076
+9595   372530723
+9596   1360207283
+9597   192958522
+9598   1980493980
+9599   439313263
+9600   570546904
+9601   2056438657
+9602   865858782
+9603   416879652
+9604   152645451
+9605   1721838260
+9606   1012143730
+9607   1221848464
+9608   54438607
+9609   504527963
+9610   192442990
+9611   1621777654
+9612   119499363
+9613   188598602
+9614   1470750113
+9615   862202865
+9616   889801949
+9617   779222240
+9618   648766718
+9619   1472804743
+9620   1423035453
+9621   161470769
+9622   1565035669
+9623   1848500861
+9624   217130759
+9625   1392740097
+9626   73547936
+9627   1577338043
+9628   1585698619
+9629   2054041917
+9630   2016651306
+9631   8761875
+9632   1962996926
+9633   735026440
+9634   425641528
+9635   2115642377
+9636   309381052
+9637   1437785258
+9638   1190007193
+9639   363819659
+9640   1942313221
+9641   1382450183
+9642   1985597314
+9643   2061812584
+9644   1571048785
+9645   1308863779
+9646   776531802
+9647   313367086
+9648   2088086019
+9649   1425298520
+9650   1786171829
+9651   1363637824
+9652   1586769289
+9653   1203723850
+9654   1064655038
+9655   1803900049
+9656   448980300
+9657   1138202974
+9658   1233754444
+9659   2034678919
+9660   1044761243
+9661   1102922102
+9662   2043440795
+9663   860274521
+9664   1837948542
+9665   321598675
+9666   828433250
+9667   2147329594
+9668   1759383933
+9669   2018440444
+9670   363665606
+9671   1554213507
+9672   1253406979
+9673   201779272
+9674   1468542443
+9675   676972117
+9676   1510643051
+9677   97590597
+9678   990339203
+9679   1451245423
+9680   1522889118
+9681   629027385
+9682   667399599
+9683   962174759
+9684   1832751235
+9685   1732054637
+9686   618591160
+9687   134247887
+9688   722773964
+9689   1852345604
+9690   21443159
+9691   1767535207
+9692   807784058
+9693   2064883954
+9694   480326081
+9695   498248952
+9696   238998981
+9697   1308759331
+9698   498094899
+9699   1998382914
+9700   1179716127
+9701   861760505
+9702   1405112773
+9703   285639459
+9704   1063539777
+9705   726171569
+9706   962611576
+9707   426699180
+9708   823762166
+9709   1952950779
+9710   1877944603
+9711   199167636
+9712   434494516
+9713   397860555
+9714   1161342396
+9715   119762104
+9716   2129915192
+9717   1779933556
+9718   254009991
+9719   705205508
+9720   1484795513
+9721   275453150
+9722   325257068
+9723   145095923
+9724   192853456
+9725   805583149
+9726   643344876
+9727   431852437
+9728   2114342480
+9729   1141439775
+9730   282751704
+9731   1146574960
+9732   2003200280
+9733   1687864477
+9734   1432214419
+9735   919256409
+9736   266552398
+9737   247342347
+9738   1345955589
+9739   1090314565
+9740   52809478
+9741   1076416545
+9742   1289482201
+9743   487303995
+9744   1474277100
+9745   303340949
+9746   607066099
+9747   1456708644
+9748   2083274506
+9749   861076090
+9750   14430505
+9751   1420586371
+9752   1136529241
+9753   339687573
+9754   1565682294
+9755   1329382697
+9756   1145270722
+9757   61543522
+9758   1761235135
+9759   1112129554
+9760   1202983297
+9761   2043986839
+9762   111220866
+9763   1058699929
+9764   1584367668
+9765   1543435285
+9766   1977956338
+9767   1850920067
+9768   1790777632
+9769   1176428280
+9770   793750984
+9771   1843587111
+9772   105361177
+9773   2083233185
+9774   183407458
+9775   1579638277
+9776   239090487
+9777   790473557
+9778   888863273
+9779   174881345
+9780   1651549647
+9781   903293778
+9782   1595467716
+9783   640595240
+9784   1242981351
+9785   1013666362
+9786   1969977938
+9787   240768425
+9788   1075209885
+9789   1583729425
+9790   1352897980
+9791   130709534
+9792   1480232616
+9793   1464118846
+9794   1189409464
+9795   917116636
+9796   860070484
+9797   1019882154
+9798   620553055
+9799   503364468
+9800   48826786
+9801   1414304039
+9802   199467931
+9803   154187963
+9804   1350053577
+9805   382875389
+9806   1733826240
+9807   1589144064
+9808   1173348946
+9809   475205866
+9810   1764025409
+9811   677414946
+9812   1378499644
+9813   1212009477
+9814   1318010186
+9815   473997348
+9816   78192191
+9817   1140504476
+9818   714765773
+9819   1153402076
+9820   576750253
+9821   2067663753
+9822   1284111611
+9823   2056982869
+9824   1384298952
+9825   326037427
+9826   826615858
+9827   96885788
+9828   1345919581
+9829   1447168913
+9830   600250256
+9831   1394746368
+9832   713989305
+9833   799718188
+9834   1548934331
+9835   2064042882
+9836   1182593577
+9837   1135276924
+9838   1505703298
+9839   208458876
+9840   1610482790
+9841   1122245059
+9842   885873822
+9843   841498786
+9844   186770888
+9845   56400360
+9846   1315496134
+9847   264963079
+9848   1196904837
+9849   2030261908
+9850   1418365156
+9851   1773655090
+9852   1950442013
+9853   554993119
+9854   1683154312
+9855   1187257317
+9856   881030546
+9857   362286522
+9858   1284143105
+9859   79466479
+9860   1809455435
+9861   1884393362
+9862   1474212847
+9863   375961092
+9864   536627902
+9865   875663531
+9866   292520326
+9867   1719221479
+9868   2010940455
+9869   1798223624
+9870   1927680355
+9871   1473939597
+9872   772985035
+9873   666070529
+9874   167954735
+9875   959755923
+9876   722470890
+9877   1483450870
+9878   1224719003
+9879   1919375727
+9880   1366229130
+9881   495600511
+9882   1545547169
+9883   1169187495
+9884   1050593630
+9885   1081217833
+9886   208961165
+9887   1931624176
+9888   1443504355
+9889   1493104270
+9890   2011090655
+9891   1105476143
+9892   1230013984
+9893   1337819855
+9894   1481437235
+9895   1766641886
+9896   65999738
+9897   1773957562
+9898   1338379718
+9899   2076940193
+9900   1424697538
+9901   1118576425
+9902   1403396142
+9903   50198926
+9904   1784646955
+9905   1571350877
+9906   1009954849
+9907   359634197
+9908   907318099
+9909   87190204
+9910   131526276
+9911   126063581
+9912   582790715
+9913   1677073445
+9914   1295251077
+9915   1633384345
+9916   610807631
+9917   1504212242
+9918   1417524873
+9919   2054311986
+9920   849832864
+9921   1281131881
+9922   1012304481
+9923   2079846849
+9924   471468088
+9925   346258069
+9926   1699005087
+9927   537467826
+9928   2120215631
+9929   889901157
+9930   466924371
+9931   1397429521
+9932   2008477583
+9933   1870320513
+9934   1447628447
+9935   1645640890
+9936   1294187742
+9937   310099649
+9938   2005275087
+9939   54022194
+9940   397289853
+9941   2136801363
+9942   180085775
+9943   980080569
+9944   1666391160
+9945   1475336852
+9946   465981266
+9947   129715143
+9948   832065446
+9949   1883506140
+9950   36543482
+9951   1681898311
+9952   1017154373
+9953   1048847963
+9954   1614261512
+9955   1488622461
+9956   1395106032
+9957   1165782951
+9958   2026090287
+9959   1367838015
+9960   2055684109
+9961   345531010
+9962   617783889
+9963   1916678044
+9964   68367875
+9965   2065412336
+9966   1414835286
+9967   1362555617
+9968   228028337
+9969   1272626725
+9970   1416577811
+9971   625318191
+9972   1261944440
+9973   1596663587
+9974   1605398760
+9975   780851952
+9976   924516791
+9977   2071380026
+9978   910567096
+9979   1756582238
+9980   1807402518
+9981   947110578
+9982   1290996901
+9983   677073243
+9984   1995958541
+9985   757774765
+9986   18212056
+9987   1243580926
+9988   1923557716
+9989   2044302343
+9990   463935293
+9991   1831758177
+9992   242349705
+9993   1081719182
+9994   1600952573
+9995   310717580
+9996   999647871
+9997   868304211
+9998   1673273198
+9999   1227676208
diff --git a/src/test/regress/data/onek.data b/src/test/regress/data/onek.data
new file mode 100644 (file)
index 0000000..1605bbe
--- /dev/null
@@ -0,0 +1,1000 @@
+147    0       1       3       7       7       7       47      147     147     147     14      15      RFAAAA  AAAAAA  AAAAxx
+931    1       1       3       1       11      1       31      131     431     931     2       3       VJAAAA  BAAAAA  HHHHxx
+714    2       0       2       4       14      4       14      114     214     714     8       9       MBAAAA  CAAAAA  OOOOxx
+711    3       1       3       1       11      1       11      111     211     711     2       3       JBAAAA  DAAAAA  VVVVxx
+883    4       1       3       3       3       3       83      83      383     883     6       7       ZHAAAA  EAAAAA  AAAAxx
+439    5       1       3       9       19      9       39      39      439     439     18      19      XQAAAA  FAAAAA  HHHHxx
+670    6       0       2       0       10      0       70      70      170     670     0       1       UZAAAA  GAAAAA  OOOOxx
+543    7       1       3       3       3       3       43      143     43      543     6       7       XUAAAA  HAAAAA  VVVVxx
+425    8       1       1       5       5       5       25      25      425     425     10      11      JQAAAA  IAAAAA  AAAAxx
+800    9       0       0       0       0       0       0       0       300     800     0       1       UEAAAA  JAAAAA  HHHHxx
+489    10      1       1       9       9       9       89      89      489     489     18      19      VSAAAA  KAAAAA  OOOOxx
+494    11      0       2       4       14      4       94      94      494     494     8       9       ATAAAA  LAAAAA  VVVVxx
+880    12      0       0       0       0       0       80      80      380     880     0       1       WHAAAA  MAAAAA  AAAAxx
+611    13      1       3       1       11      1       11      11      111     611     2       3       NXAAAA  NAAAAA  HHHHxx
+226    14      0       2       6       6       6       26      26      226     226     12      13      SIAAAA  OAAAAA  OOOOxx
+774    15      0       2       4       14      4       74      174     274     774     8       9       UDAAAA  PAAAAA  VVVVxx
+298    16      0       2       8       18      8       98      98      298     298     16      17      MLAAAA  QAAAAA  AAAAxx
+682    17      0       2       2       2       2       82      82      182     682     4       5       GAAAAA  RAAAAA  HHHHxx
+864    18      0       0       4       4       4       64      64      364     864     8       9       GHAAAA  SAAAAA  OOOOxx
+183    19      1       3       3       3       3       83      183     183     183     6       7       BHAAAA  TAAAAA  VVVVxx
+885    20      1       1       5       5       5       85      85      385     885     10      11      BIAAAA  UAAAAA  AAAAxx
+997    21      1       1       7       17      7       97      197     497     997     14      15      JMAAAA  VAAAAA  HHHHxx
+966    22      0       2       6       6       6       66      166     466     966     12      13      ELAAAA  WAAAAA  OOOOxx
+389    23      1       1       9       9       9       89      189     389     389     18      19      ZOAAAA  XAAAAA  VVVVxx
+846    24      0       2       6       6       6       46      46      346     846     12      13      OGAAAA  YAAAAA  AAAAxx
+206    25      0       2       6       6       6       6       6       206     206     12      13      YHAAAA  ZAAAAA  HHHHxx
+239    26      1       3       9       19      9       39      39      239     239     18      19      FJAAAA  ABAAAA  OOOOxx
+365    27      1       1       5       5       5       65      165     365     365     10      11      BOAAAA  BBAAAA  VVVVxx
+204    28      0       0       4       4       4       4       4       204     204     8       9       WHAAAA  CBAAAA  AAAAxx
+690    29      0       2       0       10      0       90      90      190     690     0       1       OAAAAA  DBAAAA  HHHHxx
+69     30      1       1       9       9       9       69      69      69      69      18      19      RCAAAA  EBAAAA  OOOOxx
+358    31      0       2       8       18      8       58      158     358     358     16      17      UNAAAA  FBAAAA  VVVVxx
+269    32      1       1       9       9       9       69      69      269     269     18      19      JKAAAA  GBAAAA  AAAAxx
+663    33      1       3       3       3       3       63      63      163     663     6       7       NZAAAA  HBAAAA  HHHHxx
+608    34      0       0       8       8       8       8       8       108     608     16      17      KXAAAA  IBAAAA  OOOOxx
+398    35      0       2       8       18      8       98      198     398     398     16      17      IPAAAA  JBAAAA  VVVVxx
+330    36      0       2       0       10      0       30      130     330     330     0       1       SMAAAA  KBAAAA  AAAAxx
+529    37      1       1       9       9       9       29      129     29      529     18      19      JUAAAA  LBAAAA  HHHHxx
+555    38      1       3       5       15      5       55      155     55      555     10      11      JVAAAA  MBAAAA  OOOOxx
+746    39      0       2       6       6       6       46      146     246     746     12      13      SCAAAA  NBAAAA  VVVVxx
+558    40      0       2       8       18      8       58      158     58      558     16      17      MVAAAA  OBAAAA  AAAAxx
+574    41      0       2       4       14      4       74      174     74      574     8       9       CWAAAA  PBAAAA  HHHHxx
+343    42      1       3       3       3       3       43      143     343     343     6       7       FNAAAA  QBAAAA  OOOOxx
+120    43      0       0       0       0       0       20      120     120     120     0       1       QEAAAA  RBAAAA  VVVVxx
+461    44      1       1       1       1       1       61      61      461     461     2       3       TRAAAA  SBAAAA  AAAAxx
+754    45      0       2       4       14      4       54      154     254     754     8       9       ADAAAA  TBAAAA  HHHHxx
+772    46      0       0       2       12      2       72      172     272     772     4       5       SDAAAA  UBAAAA  OOOOxx
+749    47      1       1       9       9       9       49      149     249     749     18      19      VCAAAA  VBAAAA  VVVVxx
+386    48      0       2       6       6       6       86      186     386     386     12      13      WOAAAA  WBAAAA  AAAAxx
+9      49      1       1       9       9       9       9       9       9       9       18      19      JAAAAA  XBAAAA  HHHHxx
+771    50      1       3       1       11      1       71      171     271     771     2       3       RDAAAA  YBAAAA  OOOOxx
+470    51      0       2       0       10      0       70      70      470     470     0       1       CSAAAA  ZBAAAA  VVVVxx
+238    52      0       2       8       18      8       38      38      238     238     16      17      EJAAAA  ACAAAA  AAAAxx
+86     53      0       2       6       6       6       86      86      86      86      12      13      IDAAAA  BCAAAA  HHHHxx
+56     54      0       0       6       16      6       56      56      56      56      12      13      ECAAAA  CCAAAA  OOOOxx
+767    55      1       3       7       7       7       67      167     267     767     14      15      NDAAAA  DCAAAA  VVVVxx
+363    56      1       3       3       3       3       63      163     363     363     6       7       ZNAAAA  ECAAAA  AAAAxx
+655    57      1       3       5       15      5       55      55      155     655     10      11      FZAAAA  FCAAAA  HHHHxx
+394    58      0       2       4       14      4       94      194     394     394     8       9       EPAAAA  GCAAAA  OOOOxx
+223    59      1       3       3       3       3       23      23      223     223     6       7       PIAAAA  HCAAAA  VVVVxx
+946    60      0       2       6       6       6       46      146     446     946     12      13      KKAAAA  ICAAAA  AAAAxx
+863    61      1       3       3       3       3       63      63      363     863     6       7       FHAAAA  JCAAAA  HHHHxx
+913    62      1       1       3       13      3       13      113     413     913     6       7       DJAAAA  KCAAAA  OOOOxx
+737    63      1       1       7       17      7       37      137     237     737     14      15      JCAAAA  LCAAAA  VVVVxx
+65     64      1       1       5       5       5       65      65      65      65      10      11      NCAAAA  MCAAAA  AAAAxx
+251    65      1       3       1       11      1       51      51      251     251     2       3       RJAAAA  NCAAAA  HHHHxx
+686    66      0       2       6       6       6       86      86      186     686     12      13      KAAAAA  OCAAAA  OOOOxx
+971    67      1       3       1       11      1       71      171     471     971     2       3       JLAAAA  PCAAAA  VVVVxx
+775    68      1       3       5       15      5       75      175     275     775     10      11      VDAAAA  QCAAAA  AAAAxx
+577    69      1       1       7       17      7       77      177     77      577     14      15      FWAAAA  RCAAAA  HHHHxx
+830    70      0       2       0       10      0       30      30      330     830     0       1       YFAAAA  SCAAAA  OOOOxx
+787    71      1       3       7       7       7       87      187     287     787     14      15      HEAAAA  TCAAAA  VVVVxx
+898    72      0       2       8       18      8       98      98      398     898     16      17      OIAAAA  UCAAAA  AAAAxx
+588    73      0       0       8       8       8       88      188     88      588     16      17      QWAAAA  VCAAAA  HHHHxx
+872    74      0       0       2       12      2       72      72      372     872     4       5       OHAAAA  WCAAAA  OOOOxx
+397    75      1       1       7       17      7       97      197     397     397     14      15      HPAAAA  XCAAAA  VVVVxx
+51     76      1       3       1       11      1       51      51      51      51      2       3       ZBAAAA  YCAAAA  AAAAxx
+381    77      1       1       1       1       1       81      181     381     381     2       3       ROAAAA  ZCAAAA  HHHHxx
+632    78      0       0       2       12      2       32      32      132     632     4       5       IYAAAA  ADAAAA  OOOOxx
+31     79      1       3       1       11      1       31      31      31      31      2       3       FBAAAA  BDAAAA  VVVVxx
+855    80      1       3       5       15      5       55      55      355     855     10      11      XGAAAA  CDAAAA  AAAAxx
+699    81      1       3       9       19      9       99      99      199     699     18      19      XAAAAA  DDAAAA  HHHHxx
+562    82      0       2       2       2       2       62      162     62      562     4       5       QVAAAA  EDAAAA  OOOOxx
+681    83      1       1       1       1       1       81      81      181     681     2       3       FAAAAA  FDAAAA  VVVVxx
+585    84      1       1       5       5       5       85      185     85      585     10      11      NWAAAA  GDAAAA  AAAAxx
+35     85      1       3       5       15      5       35      35      35      35      10      11      JBAAAA  HDAAAA  HHHHxx
+962    86      0       2       2       2       2       62      162     462     962     4       5       ALAAAA  IDAAAA  OOOOxx
+282    87      0       2       2       2       2       82      82      282     282     4       5       WKAAAA  JDAAAA  VVVVxx
+254    88      0       2       4       14      4       54      54      254     254     8       9       UJAAAA  KDAAAA  AAAAxx
+514    89      0       2       4       14      4       14      114     14      514     8       9       UTAAAA  LDAAAA  HHHHxx
+406    90      0       2       6       6       6       6       6       406     406     12      13      QPAAAA  MDAAAA  OOOOxx
+544    91      0       0       4       4       4       44      144     44      544     8       9       YUAAAA  NDAAAA  VVVVxx
+704    92      0       0       4       4       4       4       104     204     704     8       9       CBAAAA  ODAAAA  AAAAxx
+948    93      0       0       8       8       8       48      148     448     948     16      17      MKAAAA  PDAAAA  HHHHxx
+412    94      0       0       2       12      2       12      12      412     412     4       5       WPAAAA  QDAAAA  OOOOxx
+200    95      0       0       0       0       0       0       0       200     200     0       1       SHAAAA  RDAAAA  VVVVxx
+583    96      1       3       3       3       3       83      183     83      583     6       7       LWAAAA  SDAAAA  AAAAxx
+486    97      0       2       6       6       6       86      86      486     486     12      13      SSAAAA  TDAAAA  HHHHxx
+666    98      0       2       6       6       6       66      66      166     666     12      13      QZAAAA  UDAAAA  OOOOxx
+436    99      0       0       6       16      6       36      36      436     436     12      13      UQAAAA  VDAAAA  VVVVxx
+842    100     0       2       2       2       2       42      42      342     842     4       5       KGAAAA  WDAAAA  AAAAxx
+99     101     1       3       9       19      9       99      99      99      99      18      19      VDAAAA  XDAAAA  HHHHxx
+656    102     0       0       6       16      6       56      56      156     656     12      13      GZAAAA  YDAAAA  OOOOxx
+673    103     1       1       3       13      3       73      73      173     673     6       7       XZAAAA  ZDAAAA  VVVVxx
+371    104     1       3       1       11      1       71      171     371     371     2       3       HOAAAA  AEAAAA  AAAAxx
+869    105     1       1       9       9       9       69      69      369     869     18      19      LHAAAA  BEAAAA  HHHHxx
+569    106     1       1       9       9       9       69      169     69      569     18      19      XVAAAA  CEAAAA  OOOOxx
+616    107     0       0       6       16      6       16      16      116     616     12      13      SXAAAA  DEAAAA  VVVVxx
+612    108     0       0       2       12      2       12      12      112     612     4       5       OXAAAA  EEAAAA  AAAAxx
+505    109     1       1       5       5       5       5       105     5       505     10      11      LTAAAA  FEAAAA  HHHHxx
+922    110     0       2       2       2       2       22      122     422     922     4       5       MJAAAA  GEAAAA  OOOOxx
+221    111     1       1       1       1       1       21      21      221     221     2       3       NIAAAA  HEAAAA  VVVVxx
+388    112     0       0       8       8       8       88      188     388     388     16      17      YOAAAA  IEAAAA  AAAAxx
+567    113     1       3       7       7       7       67      167     67      567     14      15      VVAAAA  JEAAAA  HHHHxx
+58     114     0       2       8       18      8       58      58      58      58      16      17      GCAAAA  KEAAAA  OOOOxx
+316    115     0       0       6       16      6       16      116     316     316     12      13      EMAAAA  LEAAAA  VVVVxx
+659    116     1       3       9       19      9       59      59      159     659     18      19      JZAAAA  MEAAAA  AAAAxx
+501    117     1       1       1       1       1       1       101     1       501     2       3       HTAAAA  NEAAAA  HHHHxx
+815    118     1       3       5       15      5       15      15      315     815     10      11      JFAAAA  OEAAAA  OOOOxx
+638    119     0       2       8       18      8       38      38      138     638     16      17      OYAAAA  PEAAAA  VVVVxx
+696    120     0       0       6       16      6       96      96      196     696     12      13      UAAAAA  QEAAAA  AAAAxx
+734    121     0       2       4       14      4       34      134     234     734     8       9       GCAAAA  REAAAA  HHHHxx
+237    122     1       1       7       17      7       37      37      237     237     14      15      DJAAAA  SEAAAA  OOOOxx
+816    123     0       0       6       16      6       16      16      316     816     12      13      KFAAAA  TEAAAA  VVVVxx
+917    124     1       1       7       17      7       17      117     417     917     14      15      HJAAAA  UEAAAA  AAAAxx
+844    125     0       0       4       4       4       44      44      344     844     8       9       MGAAAA  VEAAAA  HHHHxx
+657    126     1       1       7       17      7       57      57      157     657     14      15      HZAAAA  WEAAAA  OOOOxx
+952    127     0       0       2       12      2       52      152     452     952     4       5       QKAAAA  XEAAAA  VVVVxx
+519    128     1       3       9       19      9       19      119     19      519     18      19      ZTAAAA  YEAAAA  AAAAxx
+792    129     0       0       2       12      2       92      192     292     792     4       5       MEAAAA  ZEAAAA  HHHHxx
+275    130     1       3       5       15      5       75      75      275     275     10      11      PKAAAA  AFAAAA  OOOOxx
+319    131     1       3       9       19      9       19      119     319     319     18      19      HMAAAA  BFAAAA  VVVVxx
+487    132     1       3       7       7       7       87      87      487     487     14      15      TSAAAA  CFAAAA  AAAAxx
+945    133     1       1       5       5       5       45      145     445     945     10      11      JKAAAA  DFAAAA  HHHHxx
+584    134     0       0       4       4       4       84      184     84      584     8       9       MWAAAA  EFAAAA  OOOOxx
+765    135     1       1       5       5       5       65      165     265     765     10      11      LDAAAA  FFAAAA  VVVVxx
+814    136     0       2       4       14      4       14      14      314     814     8       9       IFAAAA  GFAAAA  AAAAxx
+359    137     1       3       9       19      9       59      159     359     359     18      19      VNAAAA  HFAAAA  HHHHxx
+548    138     0       0       8       8       8       48      148     48      548     16      17      CVAAAA  IFAAAA  OOOOxx
+811    139     1       3       1       11      1       11      11      311     811     2       3       FFAAAA  JFAAAA  VVVVxx
+531    140     1       3       1       11      1       31      131     31      531     2       3       LUAAAA  KFAAAA  AAAAxx
+104    141     0       0       4       4       4       4       104     104     104     8       9       AEAAAA  LFAAAA  HHHHxx
+33     142     1       1       3       13      3       33      33      33      33      6       7       HBAAAA  MFAAAA  OOOOxx
+404    143     0       0       4       4       4       4       4       404     404     8       9       OPAAAA  NFAAAA  VVVVxx
+995    144     1       3       5       15      5       95      195     495     995     10      11      HMAAAA  OFAAAA  AAAAxx
+408    145     0       0       8       8       8       8       8       408     408     16      17      SPAAAA  PFAAAA  HHHHxx
+93     146     1       1       3       13      3       93      93      93      93      6       7       PDAAAA  QFAAAA  OOOOxx
+794    147     0       2       4       14      4       94      194     294     794     8       9       OEAAAA  RFAAAA  VVVVxx
+833    148     1       1       3       13      3       33      33      333     833     6       7       BGAAAA  SFAAAA  AAAAxx
+615    149     1       3       5       15      5       15      15      115     615     10      11      RXAAAA  TFAAAA  HHHHxx
+333    150     1       1       3       13      3       33      133     333     333     6       7       VMAAAA  UFAAAA  OOOOxx
+357    151     1       1       7       17      7       57      157     357     357     14      15      TNAAAA  VFAAAA  VVVVxx
+999    152     1       3       9       19      9       99      199     499     999     18      19      LMAAAA  WFAAAA  AAAAxx
+515    153     1       3       5       15      5       15      115     15      515     10      11      VTAAAA  XFAAAA  HHHHxx
+685    154     1       1       5       5       5       85      85      185     685     10      11      JAAAAA  YFAAAA  OOOOxx
+692    155     0       0       2       12      2       92      92      192     692     4       5       QAAAAA  ZFAAAA  VVVVxx
+627    156     1       3       7       7       7       27      27      127     627     14      15      DYAAAA  AGAAAA  AAAAxx
+654    157     0       2       4       14      4       54      54      154     654     8       9       EZAAAA  BGAAAA  HHHHxx
+115    158     1       3       5       15      5       15      115     115     115     10      11      LEAAAA  CGAAAA  OOOOxx
+75     159     1       3       5       15      5       75      75      75      75      10      11      XCAAAA  DGAAAA  VVVVxx
+14     160     0       2       4       14      4       14      14      14      14      8       9       OAAAAA  EGAAAA  AAAAxx
+148    161     0       0       8       8       8       48      148     148     148     16      17      SFAAAA  FGAAAA  HHHHxx
+201    162     1       1       1       1       1       1       1       201     201     2       3       THAAAA  GGAAAA  OOOOxx
+862    163     0       2       2       2       2       62      62      362     862     4       5       EHAAAA  HGAAAA  VVVVxx
+634    164     0       2       4       14      4       34      34      134     634     8       9       KYAAAA  IGAAAA  AAAAxx
+589    165     1       1       9       9       9       89      189     89      589     18      19      RWAAAA  JGAAAA  HHHHxx
+142    166     0       2       2       2       2       42      142     142     142     4       5       MFAAAA  KGAAAA  OOOOxx
+545    167     1       1       5       5       5       45      145     45      545     10      11      ZUAAAA  LGAAAA  VVVVxx
+983    168     1       3       3       3       3       83      183     483     983     6       7       VLAAAA  MGAAAA  AAAAxx
+87     169     1       3       7       7       7       87      87      87      87      14      15      JDAAAA  NGAAAA  HHHHxx
+335    170     1       3       5       15      5       35      135     335     335     10      11      XMAAAA  OGAAAA  OOOOxx
+915    171     1       3       5       15      5       15      115     415     915     10      11      FJAAAA  PGAAAA  VVVVxx
+286    172     0       2       6       6       6       86      86      286     286     12      13      ALAAAA  QGAAAA  AAAAxx
+361    173     1       1       1       1       1       61      161     361     361     2       3       XNAAAA  RGAAAA  HHHHxx
+97     174     1       1       7       17      7       97      97      97      97      14      15      TDAAAA  SGAAAA  OOOOxx
+98     175     0       2       8       18      8       98      98      98      98      16      17      UDAAAA  TGAAAA  VVVVxx
+377    176     1       1       7       17      7       77      177     377     377     14      15      NOAAAA  UGAAAA  AAAAxx
+525    177     1       1       5       5       5       25      125     25      525     10      11      FUAAAA  VGAAAA  HHHHxx
+448    178     0       0       8       8       8       48      48      448     448     16      17      GRAAAA  WGAAAA  OOOOxx
+154    179     0       2       4       14      4       54      154     154     154     8       9       YFAAAA  XGAAAA  VVVVxx
+866    180     0       2       6       6       6       66      66      366     866     12      13      IHAAAA  YGAAAA  AAAAxx
+741    181     1       1       1       1       1       41      141     241     741     2       3       NCAAAA  ZGAAAA  HHHHxx
+172    182     0       0       2       12      2       72      172     172     172     4       5       QGAAAA  AHAAAA  OOOOxx
+843    183     1       3       3       3       3       43      43      343     843     6       7       LGAAAA  BHAAAA  VVVVxx
+378    184     0       2       8       18      8       78      178     378     378     16      17      OOAAAA  CHAAAA  AAAAxx
+804    185     0       0       4       4       4       4       4       304     804     8       9       YEAAAA  DHAAAA  HHHHxx
+596    186     0       0       6       16      6       96      196     96      596     12      13      YWAAAA  EHAAAA  OOOOxx
+77     187     1       1       7       17      7       77      77      77      77      14      15      ZCAAAA  FHAAAA  VVVVxx
+572    188     0       0       2       12      2       72      172     72      572     4       5       AWAAAA  GHAAAA  AAAAxx
+444    189     0       0       4       4       4       44      44      444     444     8       9       CRAAAA  HHAAAA  HHHHxx
+47     190     1       3       7       7       7       47      47      47      47      14      15      VBAAAA  IHAAAA  OOOOxx
+274    191     0       2       4       14      4       74      74      274     274     8       9       OKAAAA  JHAAAA  VVVVxx
+40     192     0       0       0       0       0       40      40      40      40      0       1       OBAAAA  KHAAAA  AAAAxx
+339    193     1       3       9       19      9       39      139     339     339     18      19      BNAAAA  LHAAAA  HHHHxx
+13     194     1       1       3       13      3       13      13      13      13      6       7       NAAAAA  MHAAAA  OOOOxx
+878    195     0       2       8       18      8       78      78      378     878     16      17      UHAAAA  NHAAAA  VVVVxx
+53     196     1       1       3       13      3       53      53      53      53      6       7       BCAAAA  OHAAAA  AAAAxx
+939    197     1       3       9       19      9       39      139     439     939     18      19      DKAAAA  PHAAAA  HHHHxx
+928    198     0       0       8       8       8       28      128     428     928     16      17      SJAAAA  QHAAAA  OOOOxx
+886    199     0       2       6       6       6       86      86      386     886     12      13      CIAAAA  RHAAAA  VVVVxx
+267    200     1       3       7       7       7       67      67      267     267     14      15      HKAAAA  SHAAAA  AAAAxx
+105    201     1       1       5       5       5       5       105     105     105     10      11      BEAAAA  THAAAA  HHHHxx
+312    202     0       0       2       12      2       12      112     312     312     4       5       AMAAAA  UHAAAA  OOOOxx
+552    203     0       0       2       12      2       52      152     52      552     4       5       GVAAAA  VHAAAA  VVVVxx
+918    204     0       2       8       18      8       18      118     418     918     16      17      IJAAAA  WHAAAA  AAAAxx
+114    205     0       2       4       14      4       14      114     114     114     8       9       KEAAAA  XHAAAA  HHHHxx
+805    206     1       1       5       5       5       5       5       305     805     10      11      ZEAAAA  YHAAAA  OOOOxx
+875    207     1       3       5       15      5       75      75      375     875     10      11      RHAAAA  ZHAAAA  VVVVxx
+225    208     1       1       5       5       5       25      25      225     225     10      11      RIAAAA  AIAAAA  AAAAxx
+495    209     1       3       5       15      5       95      95      495     495     10      11      BTAAAA  BIAAAA  HHHHxx
+150    210     0       2       0       10      0       50      150     150     150     0       1       UFAAAA  CIAAAA  OOOOxx
+759    211     1       3       9       19      9       59      159     259     759     18      19      FDAAAA  DIAAAA  VVVVxx
+149    212     1       1       9       9       9       49      149     149     149     18      19      TFAAAA  EIAAAA  AAAAxx
+480    213     0       0       0       0       0       80      80      480     480     0       1       MSAAAA  FIAAAA  HHHHxx
+1      214     1       1       1       1       1       1       1       1       1       2       3       BAAAAA  GIAAAA  OOOOxx
+557    215     1       1       7       17      7       57      157     57      557     14      15      LVAAAA  HIAAAA  VVVVxx
+295    216     1       3       5       15      5       95      95      295     295     10      11      JLAAAA  IIAAAA  AAAAxx
+854    217     0       2       4       14      4       54      54      354     854     8       9       WGAAAA  JIAAAA  HHHHxx
+420    218     0       0       0       0       0       20      20      420     420     0       1       EQAAAA  KIAAAA  OOOOxx
+414    219     0       2       4       14      4       14      14      414     414     8       9       YPAAAA  LIAAAA  VVVVxx
+758    220     0       2       8       18      8       58      158     258     758     16      17      EDAAAA  MIAAAA  AAAAxx
+879    221     1       3       9       19      9       79      79      379     879     18      19      VHAAAA  NIAAAA  HHHHxx
+332    222     0       0       2       12      2       32      132     332     332     4       5       UMAAAA  OIAAAA  OOOOxx
+78     223     0       2       8       18      8       78      78      78      78      16      17      ADAAAA  PIAAAA  VVVVxx
+851    224     1       3       1       11      1       51      51      351     851     2       3       TGAAAA  QIAAAA  AAAAxx
+592    225     0       0       2       12      2       92      192     92      592     4       5       UWAAAA  RIAAAA  HHHHxx
+979    226     1       3       9       19      9       79      179     479     979     18      19      RLAAAA  SIAAAA  OOOOxx
+989    227     1       1       9       9       9       89      189     489     989     18      19      BMAAAA  TIAAAA  VVVVxx
+752    228     0       0       2       12      2       52      152     252     752     4       5       YCAAAA  UIAAAA  AAAAxx
+214    229     0       2       4       14      4       14      14      214     214     8       9       GIAAAA  VIAAAA  HHHHxx
+453    230     1       1       3       13      3       53      53      453     453     6       7       LRAAAA  WIAAAA  OOOOxx
+540    231     0       0       0       0       0       40      140     40      540     0       1       UUAAAA  XIAAAA  VVVVxx
+597    232     1       1       7       17      7       97      197     97      597     14      15      ZWAAAA  YIAAAA  AAAAxx
+356    233     0       0       6       16      6       56      156     356     356     12      13      SNAAAA  ZIAAAA  HHHHxx
+720    234     0       0       0       0       0       20      120     220     720     0       1       SBAAAA  AJAAAA  OOOOxx
+367    235     1       3       7       7       7       67      167     367     367     14      15      DOAAAA  BJAAAA  VVVVxx
+762    236     0       2       2       2       2       62      162     262     762     4       5       IDAAAA  CJAAAA  AAAAxx
+986    237     0       2       6       6       6       86      186     486     986     12      13      YLAAAA  DJAAAA  HHHHxx
+924    238     0       0       4       4       4       24      124     424     924     8       9       OJAAAA  EJAAAA  OOOOxx
+779    239     1       3       9       19      9       79      179     279     779     18      19      ZDAAAA  FJAAAA  VVVVxx
+684    240     0       0       4       4       4       84      84      184     684     8       9       IAAAAA  GJAAAA  AAAAxx
+413    241     1       1       3       13      3       13      13      413     413     6       7       XPAAAA  HJAAAA  HHHHxx
+479    242     1       3       9       19      9       79      79      479     479     18      19      LSAAAA  IJAAAA  OOOOxx
+731    243     1       3       1       11      1       31      131     231     731     2       3       DCAAAA  JJAAAA  VVVVxx
+409    244     1       1       9       9       9       9       9       409     409     18      19      TPAAAA  KJAAAA  AAAAxx
+372    245     0       0       2       12      2       72      172     372     372     4       5       IOAAAA  LJAAAA  HHHHxx
+139    246     1       3       9       19      9       39      139     139     139     18      19      JFAAAA  MJAAAA  OOOOxx
+717    247     1       1       7       17      7       17      117     217     717     14      15      PBAAAA  NJAAAA  VVVVxx
+539    248     1       3       9       19      9       39      139     39      539     18      19      TUAAAA  OJAAAA  AAAAxx
+318    249     0       2       8       18      8       18      118     318     318     16      17      GMAAAA  PJAAAA  HHHHxx
+208    250     0       0       8       8       8       8       8       208     208     16      17      AIAAAA  QJAAAA  OOOOxx
+797    251     1       1       7       17      7       97      197     297     797     14      15      REAAAA  RJAAAA  VVVVxx
+661    252     1       1       1       1       1       61      61      161     661     2       3       LZAAAA  SJAAAA  AAAAxx
+50     253     0       2       0       10      0       50      50      50      50      0       1       YBAAAA  TJAAAA  HHHHxx
+102    254     0       2       2       2       2       2       102     102     102     4       5       YDAAAA  UJAAAA  OOOOxx
+484    255     0       0       4       4       4       84      84      484     484     8       9       QSAAAA  VJAAAA  VVVVxx
+108    256     0       0       8       8       8       8       108     108     108     16      17      EEAAAA  WJAAAA  AAAAxx
+140    257     0       0       0       0       0       40      140     140     140     0       1       KFAAAA  XJAAAA  HHHHxx
+996    258     0       0       6       16      6       96      196     496     996     12      13      IMAAAA  YJAAAA  OOOOxx
+687    259     1       3       7       7       7       87      87      187     687     14      15      LAAAAA  ZJAAAA  VVVVxx
+241    260     1       1       1       1       1       41      41      241     241     2       3       HJAAAA  AKAAAA  AAAAxx
+923    261     1       3       3       3       3       23      123     423     923     6       7       NJAAAA  BKAAAA  HHHHxx
+500    262     0       0       0       0       0       0       100     0       500     0       1       GTAAAA  CKAAAA  OOOOxx
+536    263     0       0       6       16      6       36      136     36      536     12      13      QUAAAA  DKAAAA  VVVVxx
+490    264     0       2       0       10      0       90      90      490     490     0       1       WSAAAA  EKAAAA  AAAAxx
+773    265     1       1       3       13      3       73      173     273     773     6       7       TDAAAA  FKAAAA  HHHHxx
+19     266     1       3       9       19      9       19      19      19      19      18      19      TAAAAA  GKAAAA  OOOOxx
+534    267     0       2       4       14      4       34      134     34      534     8       9       OUAAAA  HKAAAA  VVVVxx
+941    268     1       1       1       1       1       41      141     441     941     2       3       FKAAAA  IKAAAA  AAAAxx
+477    269     1       1       7       17      7       77      77      477     477     14      15      JSAAAA  JKAAAA  HHHHxx
+173    270     1       1       3       13      3       73      173     173     173     6       7       RGAAAA  KKAAAA  OOOOxx
+113    271     1       1       3       13      3       13      113     113     113     6       7       JEAAAA  LKAAAA  VVVVxx
+526    272     0       2       6       6       6       26      126     26      526     12      13      GUAAAA  MKAAAA  AAAAxx
+727    273     1       3       7       7       7       27      127     227     727     14      15      ZBAAAA  NKAAAA  HHHHxx
+302    274     0       2       2       2       2       2       102     302     302     4       5       QLAAAA  OKAAAA  OOOOxx
+789    275     1       1       9       9       9       89      189     289     789     18      19      JEAAAA  PKAAAA  VVVVxx
+447    276     1       3       7       7       7       47      47      447     447     14      15      FRAAAA  QKAAAA  AAAAxx
+884    277     0       0       4       4       4       84      84      384     884     8       9       AIAAAA  RKAAAA  HHHHxx
+718    278     0       2       8       18      8       18      118     218     718     16      17      QBAAAA  SKAAAA  OOOOxx
+818    279     0       2       8       18      8       18      18      318     818     16      17      MFAAAA  TKAAAA  VVVVxx
+466    280     0       2       6       6       6       66      66      466     466     12      13      YRAAAA  UKAAAA  AAAAxx
+131    281     1       3       1       11      1       31      131     131     131     2       3       BFAAAA  VKAAAA  HHHHxx
+503    282     1       3       3       3       3       3       103     3       503     6       7       JTAAAA  WKAAAA  OOOOxx
+364    283     0       0       4       4       4       64      164     364     364     8       9       AOAAAA  XKAAAA  VVVVxx
+934    284     0       2       4       14      4       34      134     434     934     8       9       YJAAAA  YKAAAA  AAAAxx
+542    285     0       2       2       2       2       42      142     42      542     4       5       WUAAAA  ZKAAAA  HHHHxx
+146    286     0       2       6       6       6       46      146     146     146     12      13      QFAAAA  ALAAAA  OOOOxx
+652    287     0       0       2       12      2       52      52      152     652     4       5       CZAAAA  BLAAAA  VVVVxx
+566    288     0       2       6       6       6       66      166     66      566     12      13      UVAAAA  CLAAAA  AAAAxx
+788    289     0       0       8       8       8       88      188     288     788     16      17      IEAAAA  DLAAAA  HHHHxx
+168    290     0       0       8       8       8       68      168     168     168     16      17      MGAAAA  ELAAAA  OOOOxx
+736    291     0       0       6       16      6       36      136     236     736     12      13      ICAAAA  FLAAAA  VVVVxx
+795    292     1       3       5       15      5       95      195     295     795     10      11      PEAAAA  GLAAAA  AAAAxx
+103    293     1       3       3       3       3       3       103     103     103     6       7       ZDAAAA  HLAAAA  HHHHxx
+763    294     1       3       3       3       3       63      163     263     763     6       7       JDAAAA  ILAAAA  OOOOxx
+256    295     0       0       6       16      6       56      56      256     256     12      13      WJAAAA  JLAAAA  VVVVxx
+63     296     1       3       3       3       3       63      63      63      63      6       7       LCAAAA  KLAAAA  AAAAxx
+702    297     0       2       2       2       2       2       102     202     702     4       5       ABAAAA  LLAAAA  HHHHxx
+390    298     0       2       0       10      0       90      190     390     390     0       1       APAAAA  MLAAAA  OOOOxx
+116    299     0       0       6       16      6       16      116     116     116     12      13      MEAAAA  NLAAAA  VVVVxx
+354    300     0       2       4       14      4       54      154     354     354     8       9       QNAAAA  OLAAAA  AAAAxx
+162    301     0       2       2       2       2       62      162     162     162     4       5       GGAAAA  PLAAAA  HHHHxx
+71     302     1       3       1       11      1       71      71      71      71      2       3       TCAAAA  QLAAAA  OOOOxx
+916    303     0       0       6       16      6       16      116     416     916     12      13      GJAAAA  RLAAAA  VVVVxx
+565    304     1       1       5       5       5       65      165     65      565     10      11      TVAAAA  SLAAAA  AAAAxx
+509    305     1       1       9       9       9       9       109     9       509     18      19      PTAAAA  TLAAAA  HHHHxx
+20     306     0       0       0       0       0       20      20      20      20      0       1       UAAAAA  ULAAAA  OOOOxx
+813    307     1       1       3       13      3       13      13      313     813     6       7       HFAAAA  VLAAAA  VVVVxx
+80     308     0       0       0       0       0       80      80      80      80      0       1       CDAAAA  WLAAAA  AAAAxx
+400    309     0       0       0       0       0       0       0       400     400     0       1       KPAAAA  XLAAAA  HHHHxx
+888    310     0       0       8       8       8       88      88      388     888     16      17      EIAAAA  YLAAAA  OOOOxx
+825    311     1       1       5       5       5       25      25      325     825     10      11      TFAAAA  ZLAAAA  VVVVxx
+401    312     1       1       1       1       1       1       1       401     401     2       3       LPAAAA  AMAAAA  AAAAxx
+158    313     0       2       8       18      8       58      158     158     158     16      17      CGAAAA  BMAAAA  HHHHxx
+973    314     1       1       3       13      3       73      173     473     973     6       7       LLAAAA  CMAAAA  OOOOxx
+324    315     0       0       4       4       4       24      124     324     324     8       9       MMAAAA  DMAAAA  VVVVxx
+873    316     1       1       3       13      3       73      73      373     873     6       7       PHAAAA  EMAAAA  AAAAxx
+676    317     0       0       6       16      6       76      76      176     676     12      13      AAAAAA  FMAAAA  HHHHxx
+199    318     1       3       9       19      9       99      199     199     199     18      19      RHAAAA  GMAAAA  OOOOxx
+304    319     0       0       4       4       4       4       104     304     304     8       9       SLAAAA  HMAAAA  VVVVxx
+338    320     0       2       8       18      8       38      138     338     338     16      17      ANAAAA  IMAAAA  AAAAxx
+743    321     1       3       3       3       3       43      143     243     743     6       7       PCAAAA  JMAAAA  HHHHxx
+730    322     0       2       0       10      0       30      130     230     730     0       1       CCAAAA  KMAAAA  OOOOxx
+130    323     0       2       0       10      0       30      130     130     130     0       1       AFAAAA  LMAAAA  VVVVxx
+224    324     0       0       4       4       4       24      24      224     224     8       9       QIAAAA  MMAAAA  AAAAxx
+216    325     0       0       6       16      6       16      16      216     216     12      13      IIAAAA  NMAAAA  HHHHxx
+2      326     0       2       2       2       2       2       2       2       2       4       5       CAAAAA  OMAAAA  OOOOxx
+836    327     0       0       6       16      6       36      36      336     836     12      13      EGAAAA  PMAAAA  VVVVxx
+443    328     1       3       3       3       3       43      43      443     443     6       7       BRAAAA  QMAAAA  AAAAxx
+777    329     1       1       7       17      7       77      177     277     777     14      15      XDAAAA  RMAAAA  HHHHxx
+126    330     0       2       6       6       6       26      126     126     126     12      13      WEAAAA  SMAAAA  OOOOxx
+117    331     1       1       7       17      7       17      117     117     117     14      15      NEAAAA  TMAAAA  VVVVxx
+633    332     1       1       3       13      3       33      33      133     633     6       7       JYAAAA  UMAAAA  AAAAxx
+310    333     0       2       0       10      0       10      110     310     310     0       1       YLAAAA  VMAAAA  HHHHxx
+622    334     0       2       2       2       2       22      22      122     622     4       5       YXAAAA  WMAAAA  OOOOxx
+268    335     0       0       8       8       8       68      68      268     268     16      17      IKAAAA  XMAAAA  VVVVxx
+384    336     0       0       4       4       4       84      184     384     384     8       9       UOAAAA  YMAAAA  AAAAxx
+460    337     0       0       0       0       0       60      60      460     460     0       1       SRAAAA  ZMAAAA  HHHHxx
+475    338     1       3       5       15      5       75      75      475     475     10      11      HSAAAA  ANAAAA  OOOOxx
+624    339     0       0       4       4       4       24      24      124     624     8       9       AYAAAA  BNAAAA  VVVVxx
+826    340     0       2       6       6       6       26      26      326     826     12      13      UFAAAA  CNAAAA  AAAAxx
+680    341     0       0       0       0       0       80      80      180     680     0       1       EAAAAA  DNAAAA  HHHHxx
+306    342     0       2       6       6       6       6       106     306     306     12      13      ULAAAA  ENAAAA  OOOOxx
+896    343     0       0       6       16      6       96      96      396     896     12      13      MIAAAA  FNAAAA  VVVVxx
+30     344     0       2       0       10      0       30      30      30      30      0       1       EBAAAA  GNAAAA  AAAAxx
+576    345     0       0       6       16      6       76      176     76      576     12      13      EWAAAA  HNAAAA  HHHHxx
+551    346     1       3       1       11      1       51      151     51      551     2       3       FVAAAA  INAAAA  OOOOxx
+639    347     1       3       9       19      9       39      39      139     639     18      19      PYAAAA  JNAAAA  VVVVxx
+975    348     1       3       5       15      5       75      175     475     975     10      11      NLAAAA  KNAAAA  AAAAxx
+882    349     0       2       2       2       2       82      82      382     882     4       5       YHAAAA  LNAAAA  HHHHxx
+160    350     0       0       0       0       0       60      160     160     160     0       1       EGAAAA  MNAAAA  OOOOxx
+522    351     0       2       2       2       2       22      122     22      522     4       5       CUAAAA  NNAAAA  VVVVxx
+620    352     0       0       0       0       0       20      20      120     620     0       1       WXAAAA  ONAAAA  AAAAxx
+719    353     1       3       9       19      9       19      119     219     719     18      19      RBAAAA  PNAAAA  HHHHxx
+88     354     0       0       8       8       8       88      88      88      88      16      17      KDAAAA  QNAAAA  OOOOxx
+614    355     0       2       4       14      4       14      14      114     614     8       9       QXAAAA  RNAAAA  VVVVxx
+54     356     0       2       4       14      4       54      54      54      54      8       9       CCAAAA  SNAAAA  AAAAxx
+209    357     1       1       9       9       9       9       9       209     209     18      19      BIAAAA  TNAAAA  HHHHxx
+67     358     1       3       7       7       7       67      67      67      67      14      15      PCAAAA  UNAAAA  OOOOxx
+809    359     1       1       9       9       9       9       9       309     809     18      19      DFAAAA  VNAAAA  VVVVxx
+982    360     0       2       2       2       2       82      182     482     982     4       5       ULAAAA  WNAAAA  AAAAxx
+817    361     1       1       7       17      7       17      17      317     817     14      15      LFAAAA  XNAAAA  HHHHxx
+187    362     1       3       7       7       7       87      187     187     187     14      15      FHAAAA  YNAAAA  OOOOxx
+992    363     0       0       2       12      2       92      192     492     992     4       5       EMAAAA  ZNAAAA  VVVVxx
+580    364     0       0       0       0       0       80      180     80      580     0       1       IWAAAA  AOAAAA  AAAAxx
+658    365     0       2       8       18      8       58      58      158     658     16      17      IZAAAA  BOAAAA  HHHHxx
+222    366     0       2       2       2       2       22      22      222     222     4       5       OIAAAA  COAAAA  OOOOxx
+667    367     1       3       7       7       7       67      67      167     667     14      15      RZAAAA  DOAAAA  VVVVxx
+715    368     1       3       5       15      5       15      115     215     715     10      11      NBAAAA  EOAAAA  AAAAxx
+990    369     0       2       0       10      0       90      190     490     990     0       1       CMAAAA  FOAAAA  HHHHxx
+22     370     0       2       2       2       2       22      22      22      22      4       5       WAAAAA  GOAAAA  OOOOxx
+362    371     0       2       2       2       2       62      162     362     362     4       5       YNAAAA  HOAAAA  VVVVxx
+376    372     0       0       6       16      6       76      176     376     376     12      13      MOAAAA  IOAAAA  AAAAxx
+246    373     0       2       6       6       6       46      46      246     246     12      13      MJAAAA  JOAAAA  HHHHxx
+300    374     0       0       0       0       0       0       100     300     300     0       1       OLAAAA  KOAAAA  OOOOxx
+231    375     1       3       1       11      1       31      31      231     231     2       3       XIAAAA  LOAAAA  VVVVxx
+151    376     1       3       1       11      1       51      151     151     151     2       3       VFAAAA  MOAAAA  AAAAxx
+29     377     1       1       9       9       9       29      29      29      29      18      19      DBAAAA  NOAAAA  HHHHxx
+297    378     1       1       7       17      7       97      97      297     297     14      15      LLAAAA  OOAAAA  OOOOxx
+403    379     1       3       3       3       3       3       3       403     403     6       7       NPAAAA  POAAAA  VVVVxx
+716    380     0       0       6       16      6       16      116     216     716     12      13      OBAAAA  QOAAAA  AAAAxx
+260    381     0       0       0       0       0       60      60      260     260     0       1       AKAAAA  ROAAAA  HHHHxx
+170    382     0       2       0       10      0       70      170     170     170     0       1       OGAAAA  SOAAAA  OOOOxx
+285    383     1       1       5       5       5       85      85      285     285     10      11      ZKAAAA  TOAAAA  VVVVxx
+82     384     0       2       2       2       2       82      82      82      82      4       5       EDAAAA  UOAAAA  AAAAxx
+958    385     0       2       8       18      8       58      158     458     958     16      17      WKAAAA  VOAAAA  HHHHxx
+175    386     1       3       5       15      5       75      175     175     175     10      11      TGAAAA  WOAAAA  OOOOxx
+671    387     1       3       1       11      1       71      71      171     671     2       3       VZAAAA  XOAAAA  VVVVxx
+822    388     0       2       2       2       2       22      22      322     822     4       5       QFAAAA  YOAAAA  AAAAxx
+573    389     1       1       3       13      3       73      173     73      573     6       7       BWAAAA  ZOAAAA  HHHHxx
+723    390     1       3       3       3       3       23      123     223     723     6       7       VBAAAA  APAAAA  OOOOxx
+195    391     1       3       5       15      5       95      195     195     195     10      11      NHAAAA  BPAAAA  VVVVxx
+197    392     1       1       7       17      7       97      197     197     197     14      15      PHAAAA  CPAAAA  AAAAxx
+755    393     1       3       5       15      5       55      155     255     755     10      11      BDAAAA  DPAAAA  HHHHxx
+42     394     0       2       2       2       2       42      42      42      42      4       5       QBAAAA  EPAAAA  OOOOxx
+897    395     1       1       7       17      7       97      97      397     897     14      15      NIAAAA  FPAAAA  VVVVxx
+309    396     1       1       9       9       9       9       109     309     309     18      19      XLAAAA  GPAAAA  AAAAxx
+724    397     0       0       4       4       4       24      124     224     724     8       9       WBAAAA  HPAAAA  HHHHxx
+474    398     0       2       4       14      4       74      74      474     474     8       9       GSAAAA  IPAAAA  OOOOxx
+345    399     1       1       5       5       5       45      145     345     345     10      11      HNAAAA  JPAAAA  VVVVxx
+678    400     0       2       8       18      8       78      78      178     678     16      17      CAAAAA  KPAAAA  AAAAxx
+757    401     1       1       7       17      7       57      157     257     757     14      15      DDAAAA  LPAAAA  HHHHxx
+600    402     0       0       0       0       0       0       0       100     600     0       1       CXAAAA  MPAAAA  OOOOxx
+184    403     0       0       4       4       4       84      184     184     184     8       9       CHAAAA  NPAAAA  VVVVxx
+155    404     1       3       5       15      5       55      155     155     155     10      11      ZFAAAA  OPAAAA  AAAAxx
+136    405     0       0       6       16      6       36      136     136     136     12      13      GFAAAA  PPAAAA  HHHHxx
+889    406     1       1       9       9       9       89      89      389     889     18      19      FIAAAA  QPAAAA  OOOOxx
+95     407     1       3       5       15      5       95      95      95      95      10      11      RDAAAA  RPAAAA  VVVVxx
+549    408     1       1       9       9       9       49      149     49      549     18      19      DVAAAA  SPAAAA  AAAAxx
+81     409     1       1       1       1       1       81      81      81      81      2       3       DDAAAA  TPAAAA  HHHHxx
+679    410     1       3       9       19      9       79      79      179     679     18      19      DAAAAA  UPAAAA  OOOOxx
+27     411     1       3       7       7       7       27      27      27      27      14      15      BBAAAA  VPAAAA  VVVVxx
+748    412     0       0       8       8       8       48      148     248     748     16      17      UCAAAA  WPAAAA  AAAAxx
+107    413     1       3       7       7       7       7       107     107     107     14      15      DEAAAA  XPAAAA  HHHHxx
+870    414     0       2       0       10      0       70      70      370     870     0       1       MHAAAA  YPAAAA  OOOOxx
+848    415     0       0       8       8       8       48      48      348     848     16      17      QGAAAA  ZPAAAA  VVVVxx
+764    416     0       0       4       4       4       64      164     264     764     8       9       KDAAAA  AQAAAA  AAAAxx
+535    417     1       3       5       15      5       35      135     35      535     10      11      PUAAAA  BQAAAA  HHHHxx
+211    418     1       3       1       11      1       11      11      211     211     2       3       DIAAAA  CQAAAA  OOOOxx
+625    419     1       1       5       5       5       25      25      125     625     10      11      BYAAAA  DQAAAA  VVVVxx
+96     420     0       0       6       16      6       96      96      96      96      12      13      SDAAAA  EQAAAA  AAAAxx
+828    421     0       0       8       8       8       28      28      328     828     16      17      WFAAAA  FQAAAA  HHHHxx
+229    422     1       1       9       9       9       29      29      229     229     18      19      VIAAAA  GQAAAA  OOOOxx
+602    423     0       2       2       2       2       2       2       102     602     4       5       EXAAAA  HQAAAA  VVVVxx
+742    424     0       2       2       2       2       42      142     242     742     4       5       OCAAAA  IQAAAA  AAAAxx
+451    425     1       3       1       11      1       51      51      451     451     2       3       JRAAAA  JQAAAA  HHHHxx
+991    426     1       3       1       11      1       91      191     491     991     2       3       DMAAAA  KQAAAA  OOOOxx
+301    427     1       1       1       1       1       1       101     301     301     2       3       PLAAAA  LQAAAA  VVVVxx
+510    428     0       2       0       10      0       10      110     10      510     0       1       QTAAAA  MQAAAA  AAAAxx
+299    429     1       3       9       19      9       99      99      299     299     18      19      NLAAAA  NQAAAA  HHHHxx
+961    430     1       1       1       1       1       61      161     461     961     2       3       ZKAAAA  OQAAAA  OOOOxx
+3      431     1       3       3       3       3       3       3       3       3       6       7       DAAAAA  PQAAAA  VVVVxx
+106    432     0       2       6       6       6       6       106     106     106     12      13      CEAAAA  QQAAAA  AAAAxx
+591    433     1       3       1       11      1       91      191     91      591     2       3       TWAAAA  RQAAAA  HHHHxx
+700    434     0       0       0       0       0       0       100     200     700     0       1       YAAAAA  SQAAAA  OOOOxx
+841    435     1       1       1       1       1       41      41      341     841     2       3       JGAAAA  TQAAAA  VVVVxx
+829    436     1       1       9       9       9       29      29      329     829     18      19      XFAAAA  UQAAAA  AAAAxx
+508    437     0       0       8       8       8       8       108     8       508     16      17      OTAAAA  VQAAAA  HHHHxx
+750    438     0       2       0       10      0       50      150     250     750     0       1       WCAAAA  WQAAAA  OOOOxx
+665    439     1       1       5       5       5       65      65      165     665     10      11      PZAAAA  XQAAAA  VVVVxx
+157    440     1       1       7       17      7       57      157     157     157     14      15      BGAAAA  YQAAAA  AAAAxx
+694    441     0       2       4       14      4       94      94      194     694     8       9       SAAAAA  ZQAAAA  HHHHxx
+176    442     0       0       6       16      6       76      176     176     176     12      13      UGAAAA  ARAAAA  OOOOxx
+950    443     0       2       0       10      0       50      150     450     950     0       1       OKAAAA  BRAAAA  VVVVxx
+970    444     0       2       0       10      0       70      170     470     970     0       1       ILAAAA  CRAAAA  AAAAxx
+496    445     0       0       6       16      6       96      96      496     496     12      13      CTAAAA  DRAAAA  HHHHxx
+429    446     1       1       9       9       9       29      29      429     429     18      19      NQAAAA  ERAAAA  OOOOxx
+907    447     1       3       7       7       7       7       107     407     907     14      15      XIAAAA  FRAAAA  VVVVxx
+72     448     0       0       2       12      2       72      72      72      72      4       5       UCAAAA  GRAAAA  AAAAxx
+186    449     0       2       6       6       6       86      186     186     186     12      13      EHAAAA  HRAAAA  HHHHxx
+713    450     1       1       3       13      3       13      113     213     713     6       7       LBAAAA  IRAAAA  OOOOxx
+432    451     0       0       2       12      2       32      32      432     432     4       5       QQAAAA  JRAAAA  VVVVxx
+735    452     1       3       5       15      5       35      135     235     735     10      11      HCAAAA  KRAAAA  AAAAxx
+516    453     0       0       6       16      6       16      116     16      516     12      13      WTAAAA  LRAAAA  HHHHxx
+964    454     0       0       4       4       4       64      164     464     964     8       9       CLAAAA  MRAAAA  OOOOxx
+840    455     0       0       0       0       0       40      40      340     840     0       1       IGAAAA  NRAAAA  VVVVxx
+550    456     0       2       0       10      0       50      150     50      550     0       1       EVAAAA  ORAAAA  AAAAxx
+360    457     0       0       0       0       0       60      160     360     360     0       1       WNAAAA  PRAAAA  HHHHxx
+827    458     1       3       7       7       7       27      27      327     827     14      15      VFAAAA  QRAAAA  OOOOxx
+959    459     1       3       9       19      9       59      159     459     959     18      19      XKAAAA  RRAAAA  VVVVxx
+454    460     0       2       4       14      4       54      54      454     454     8       9       MRAAAA  SRAAAA  AAAAxx
+819    461     1       3       9       19      9       19      19      319     819     18      19      NFAAAA  TRAAAA  HHHHxx
+745    462     1       1       5       5       5       45      145     245     745     10      11      RCAAAA  URAAAA  OOOOxx
+279    463     1       3       9       19      9       79      79      279     279     18      19      TKAAAA  VRAAAA  VVVVxx
+426    464     0       2       6       6       6       26      26      426     426     12      13      KQAAAA  WRAAAA  AAAAxx
+70     465     0       2       0       10      0       70      70      70      70      0       1       SCAAAA  XRAAAA  HHHHxx
+637    466     1       1       7       17      7       37      37      137     637     14      15      NYAAAA  YRAAAA  OOOOxx
+417    467     1       1       7       17      7       17      17      417     417     14      15      BQAAAA  ZRAAAA  VVVVxx
+586    468     0       2       6       6       6       86      186     86      586     12      13      OWAAAA  ASAAAA  AAAAxx
+314    469     0       2       4       14      4       14      114     314     314     8       9       CMAAAA  BSAAAA  HHHHxx
+101    470     1       1       1       1       1       1       101     101     101     2       3       XDAAAA  CSAAAA  OOOOxx
+205    471     1       1       5       5       5       5       5       205     205     10      11      XHAAAA  DSAAAA  VVVVxx
+969    472     1       1       9       9       9       69      169     469     969     18      19      HLAAAA  ESAAAA  AAAAxx
+217    473     1       1       7       17      7       17      17      217     217     14      15      JIAAAA  FSAAAA  HHHHxx
+281    474     1       1       1       1       1       81      81      281     281     2       3       VKAAAA  GSAAAA  OOOOxx
+984    475     0       0       4       4       4       84      184     484     984     8       9       WLAAAA  HSAAAA  VVVVxx
+366    476     0       2       6       6       6       66      166     366     366     12      13      COAAAA  ISAAAA  AAAAxx
+483    477     1       3       3       3       3       83      83      483     483     6       7       PSAAAA  JSAAAA  HHHHxx
+838    478     0       2       8       18      8       38      38      338     838     16      17      GGAAAA  KSAAAA  OOOOxx
+64     479     0       0       4       4       4       64      64      64      64      8       9       MCAAAA  LSAAAA  VVVVxx
+981    480     1       1       1       1       1       81      181     481     981     2       3       TLAAAA  MSAAAA  AAAAxx
+538    481     0       2       8       18      8       38      138     38      538     16      17      SUAAAA  NSAAAA  HHHHxx
+39     482     1       3       9       19      9       39      39      39      39      18      19      NBAAAA  OSAAAA  OOOOxx
+60     483     0       0       0       0       0       60      60      60      60      0       1       ICAAAA  PSAAAA  VVVVxx
+874    484     0       2       4       14      4       74      74      374     874     8       9       QHAAAA  QSAAAA  AAAAxx
+955    485     1       3       5       15      5       55      155     455     955     10      11      TKAAAA  RSAAAA  HHHHxx
+347    486     1       3       7       7       7       47      147     347     347     14      15      JNAAAA  SSAAAA  OOOOxx
+227    487     1       3       7       7       7       27      27      227     227     14      15      TIAAAA  TSAAAA  VVVVxx
+44     488     0       0       4       4       4       44      44      44      44      8       9       SBAAAA  USAAAA  AAAAxx
+446    489     0       2       6       6       6       46      46      446     446     12      13      ERAAAA  VSAAAA  HHHHxx
+605    490     1       1       5       5       5       5       5       105     605     10      11      HXAAAA  WSAAAA  OOOOxx
+570    491     0       2       0       10      0       70      170     70      570     0       1       YVAAAA  XSAAAA  VVVVxx
+895    492     1       3       5       15      5       95      95      395     895     10      11      LIAAAA  YSAAAA  AAAAxx
+760    493     0       0       0       0       0       60      160     260     760     0       1       GDAAAA  ZSAAAA  HHHHxx
+428    494     0       0       8       8       8       28      28      428     428     16      17      MQAAAA  ATAAAA  OOOOxx
+628    495     0       0       8       8       8       28      28      128     628     16      17      EYAAAA  BTAAAA  VVVVxx
+933    496     1       1       3       13      3       33      133     433     933     6       7       XJAAAA  CTAAAA  AAAAxx
+263    497     1       3       3       3       3       63      63      263     263     6       7       DKAAAA  DTAAAA  HHHHxx
+729    498     1       1       9       9       9       29      129     229     729     18      19      BCAAAA  ETAAAA  OOOOxx
+860    499     0       0       0       0       0       60      60      360     860     0       1       CHAAAA  FTAAAA  VVVVxx
+76     500     0       0       6       16      6       76      76      76      76      12      13      YCAAAA  GTAAAA  AAAAxx
+293    501     1       1       3       13      3       93      93      293     293     6       7       HLAAAA  HTAAAA  HHHHxx
+296    502     0       0       6       16      6       96      96      296     296     12      13      KLAAAA  ITAAAA  OOOOxx
+124    503     0       0       4       4       4       24      124     124     124     8       9       UEAAAA  JTAAAA  VVVVxx
+568    504     0       0       8       8       8       68      168     68      568     16      17      WVAAAA  KTAAAA  AAAAxx
+337    505     1       1       7       17      7       37      137     337     337     14      15      ZMAAAA  LTAAAA  HHHHxx
+464    506     0       0       4       4       4       64      64      464     464     8       9       WRAAAA  MTAAAA  OOOOxx
+582    507     0       2       2       2       2       82      182     82      582     4       5       KWAAAA  NTAAAA  VVVVxx
+207    508     1       3       7       7       7       7       7       207     207     14      15      ZHAAAA  OTAAAA  AAAAxx
+518    509     0       2       8       18      8       18      118     18      518     16      17      YTAAAA  PTAAAA  HHHHxx
+513    510     1       1       3       13      3       13      113     13      513     6       7       TTAAAA  QTAAAA  OOOOxx
+127    511     1       3       7       7       7       27      127     127     127     14      15      XEAAAA  RTAAAA  VVVVxx
+396    512     0       0       6       16      6       96      196     396     396     12      13      GPAAAA  STAAAA  AAAAxx
+781    513     1       1       1       1       1       81      181     281     781     2       3       BEAAAA  TTAAAA  HHHHxx
+233    514     1       1       3       13      3       33      33      233     233     6       7       ZIAAAA  UTAAAA  OOOOxx
+709    515     1       1       9       9       9       9       109     209     709     18      19      HBAAAA  VTAAAA  VVVVxx
+325    516     1       1       5       5       5       25      125     325     325     10      11      NMAAAA  WTAAAA  AAAAxx
+143    517     1       3       3       3       3       43      143     143     143     6       7       NFAAAA  XTAAAA  HHHHxx
+824    518     0       0       4       4       4       24      24      324     824     8       9       SFAAAA  YTAAAA  OOOOxx
+122    519     0       2       2       2       2       22      122     122     122     4       5       SEAAAA  ZTAAAA  VVVVxx
+10     520     0       2       0       10      0       10      10      10      10      0       1       KAAAAA  AUAAAA  AAAAxx
+41     521     1       1       1       1       1       41      41      41      41      2       3       PBAAAA  BUAAAA  HHHHxx
+618    522     0       2       8       18      8       18      18      118     618     16      17      UXAAAA  CUAAAA  OOOOxx
+161    523     1       1       1       1       1       61      161     161     161     2       3       FGAAAA  DUAAAA  VVVVxx
+801    524     1       1       1       1       1       1       1       301     801     2       3       VEAAAA  EUAAAA  AAAAxx
+768    525     0       0       8       8       8       68      168     268     768     16      17      ODAAAA  FUAAAA  HHHHxx
+642    526     0       2       2       2       2       42      42      142     642     4       5       SYAAAA  GUAAAA  OOOOxx
+803    527     1       3       3       3       3       3       3       303     803     6       7       XEAAAA  HUAAAA  VVVVxx
+317    528     1       1       7       17      7       17      117     317     317     14      15      FMAAAA  IUAAAA  AAAAxx
+938    529     0       2       8       18      8       38      138     438     938     16      17      CKAAAA  JUAAAA  HHHHxx
+649    530     1       1       9       9       9       49      49      149     649     18      19      ZYAAAA  KUAAAA  OOOOxx
+738    531     0       2       8       18      8       38      138     238     738     16      17      KCAAAA  LUAAAA  VVVVxx
+344    532     0       0       4       4       4       44      144     344     344     8       9       GNAAAA  MUAAAA  AAAAxx
+399    533     1       3       9       19      9       99      199     399     399     18      19      JPAAAA  NUAAAA  HHHHxx
+609    534     1       1       9       9       9       9       9       109     609     18      19      LXAAAA  OUAAAA  OOOOxx
+677    535     1       1       7       17      7       77      77      177     677     14      15      BAAAAA  PUAAAA  VVVVxx
+478    536     0       2       8       18      8       78      78      478     478     16      17      KSAAAA  QUAAAA  AAAAxx
+452    537     0       0       2       12      2       52      52      452     452     4       5       KRAAAA  RUAAAA  HHHHxx
+261    538     1       1       1       1       1       61      61      261     261     2       3       BKAAAA  SUAAAA  OOOOxx
+449    539     1       1       9       9       9       49      49      449     449     18      19      HRAAAA  TUAAAA  VVVVxx
+433    540     1       1       3       13      3       33      33      433     433     6       7       RQAAAA  UUAAAA  AAAAxx
+5      541     1       1       5       5       5       5       5       5       5       10      11      FAAAAA  VUAAAA  HHHHxx
+664    542     0       0       4       4       4       64      64      164     664     8       9       OZAAAA  WUAAAA  OOOOxx
+887    543     1       3       7       7       7       87      87      387     887     14      15      DIAAAA  XUAAAA  VVVVxx
+546    544     0       2       6       6       6       46      146     46      546     12      13      AVAAAA  YUAAAA  AAAAxx
+253    545     1       1       3       13      3       53      53      253     253     6       7       TJAAAA  ZUAAAA  HHHHxx
+235    546     1       3       5       15      5       35      35      235     235     10      11      BJAAAA  AVAAAA  OOOOxx
+258    547     0       2       8       18      8       58      58      258     258     16      17      YJAAAA  BVAAAA  VVVVxx
+621    548     1       1       1       1       1       21      21      121     621     2       3       XXAAAA  CVAAAA  AAAAxx
+998    549     0       2       8       18      8       98      198     498     998     16      17      KMAAAA  DVAAAA  HHHHxx
+236    550     0       0       6       16      6       36      36      236     236     12      13      CJAAAA  EVAAAA  OOOOxx
+537    551     1       1       7       17      7       37      137     37      537     14      15      RUAAAA  FVAAAA  VVVVxx
+769    552     1       1       9       9       9       69      169     269     769     18      19      PDAAAA  GVAAAA  AAAAxx
+921    553     1       1       1       1       1       21      121     421     921     2       3       LJAAAA  HVAAAA  HHHHxx
+951    554     1       3       1       11      1       51      151     451     951     2       3       PKAAAA  IVAAAA  OOOOxx
+240    555     0       0       0       0       0       40      40      240     240     0       1       GJAAAA  JVAAAA  VVVVxx
+644    556     0       0       4       4       4       44      44      144     644     8       9       UYAAAA  KVAAAA  AAAAxx
+352    557     0       0       2       12      2       52      152     352     352     4       5       ONAAAA  LVAAAA  HHHHxx
+613    558     1       1       3       13      3       13      13      113     613     6       7       PXAAAA  MVAAAA  OOOOxx
+784    559     0       0       4       4       4       84      184     284     784     8       9       EEAAAA  NVAAAA  VVVVxx
+61     560     1       1       1       1       1       61      61      61      61      2       3       JCAAAA  OVAAAA  AAAAxx
+144    561     0       0       4       4       4       44      144     144     144     8       9       OFAAAA  PVAAAA  HHHHxx
+94     562     0       2       4       14      4       94      94      94      94      8       9       QDAAAA  QVAAAA  OOOOxx
+270    563     0       2       0       10      0       70      70      270     270     0       1       KKAAAA  RVAAAA  VVVVxx
+942    564     0       2       2       2       2       42      142     442     942     4       5       GKAAAA  SVAAAA  AAAAxx
+756    565     0       0       6       16      6       56      156     256     756     12      13      CDAAAA  TVAAAA  HHHHxx
+321    566     1       1       1       1       1       21      121     321     321     2       3       JMAAAA  UVAAAA  OOOOxx
+36     567     0       0       6       16      6       36      36      36      36      12      13      KBAAAA  VVAAAA  VVVVxx
+232    568     0       0       2       12      2       32      32      232     232     4       5       YIAAAA  WVAAAA  AAAAxx
+430    569     0       2       0       10      0       30      30      430     430     0       1       OQAAAA  XVAAAA  HHHHxx
+177    570     1       1       7       17      7       77      177     177     177     14      15      VGAAAA  YVAAAA  OOOOxx
+220    571     0       0       0       0       0       20      20      220     220     0       1       MIAAAA  ZVAAAA  VVVVxx
+109    572     1       1       9       9       9       9       109     109     109     18      19      FEAAAA  AWAAAA  AAAAxx
+419    573     1       3       9       19      9       19      19      419     419     18      19      DQAAAA  BWAAAA  HHHHxx
+135    574     1       3       5       15      5       35      135     135     135     10      11      FFAAAA  CWAAAA  OOOOxx
+610    575     0       2       0       10      0       10      10      110     610     0       1       MXAAAA  DWAAAA  VVVVxx
+956    576     0       0       6       16      6       56      156     456     956     12      13      UKAAAA  EWAAAA  AAAAxx
+626    577     0       2       6       6       6       26      26      126     626     12      13      CYAAAA  FWAAAA  HHHHxx
+375    578     1       3       5       15      5       75      175     375     375     10      11      LOAAAA  GWAAAA  OOOOxx
+976    579     0       0       6       16      6       76      176     476     976     12      13      OLAAAA  HWAAAA  VVVVxx
+152    580     0       0       2       12      2       52      152     152     152     4       5       WFAAAA  IWAAAA  AAAAxx
+308    581     0       0       8       8       8       8       108     308     308     16      17      WLAAAA  JWAAAA  HHHHxx
+445    582     1       1       5       5       5       45      45      445     445     10      11      DRAAAA  KWAAAA  OOOOxx
+326    583     0       2       6       6       6       26      126     326     326     12      13      OMAAAA  LWAAAA  VVVVxx
+422    584     0       2       2       2       2       22      22      422     422     4       5       GQAAAA  MWAAAA  AAAAxx
+972    585     0       0       2       12      2       72      172     472     972     4       5       KLAAAA  NWAAAA  HHHHxx
+45     586     1       1       5       5       5       45      45      45      45      10      11      TBAAAA  OWAAAA  OOOOxx
+725    587     1       1       5       5       5       25      125     225     725     10      11      XBAAAA  PWAAAA  VVVVxx
+753    588     1       1       3       13      3       53      153     253     753     6       7       ZCAAAA  QWAAAA  AAAAxx
+493    589     1       1       3       13      3       93      93      493     493     6       7       ZSAAAA  RWAAAA  HHHHxx
+601    590     1       1       1       1       1       1       1       101     601     2       3       DXAAAA  SWAAAA  OOOOxx
+463    591     1       3       3       3       3       63      63      463     463     6       7       VRAAAA  TWAAAA  VVVVxx
+303    592     1       3       3       3       3       3       103     303     303     6       7       RLAAAA  UWAAAA  AAAAxx
+59     593     1       3       9       19      9       59      59      59      59      18      19      HCAAAA  VWAAAA  HHHHxx
+595    594     1       3       5       15      5       95      195     95      595     10      11      XWAAAA  WWAAAA  OOOOxx
+807    595     1       3       7       7       7       7       7       307     807     14      15      BFAAAA  XWAAAA  VVVVxx
+424    596     0       0       4       4       4       24      24      424     424     8       9       IQAAAA  YWAAAA  AAAAxx
+521    597     1       1       1       1       1       21      121     21      521     2       3       BUAAAA  ZWAAAA  HHHHxx
+341    598     1       1       1       1       1       41      141     341     341     2       3       DNAAAA  AXAAAA  OOOOxx
+571    599     1       3       1       11      1       71      171     71      571     2       3       ZVAAAA  BXAAAA  VVVVxx
+165    600     1       1       5       5       5       65      165     165     165     10      11      JGAAAA  CXAAAA  AAAAxx
+908    601     0       0       8       8       8       8       108     408     908     16      17      YIAAAA  DXAAAA  HHHHxx
+351    602     1       3       1       11      1       51      151     351     351     2       3       NNAAAA  EXAAAA  OOOOxx
+334    603     0       2       4       14      4       34      134     334     334     8       9       WMAAAA  FXAAAA  VVVVxx
+636    604     0       0       6       16      6       36      36      136     636     12      13      MYAAAA  GXAAAA  AAAAxx
+138    605     0       2       8       18      8       38      138     138     138     16      17      IFAAAA  HXAAAA  HHHHxx
+438    606     0       2       8       18      8       38      38      438     438     16      17      WQAAAA  IXAAAA  OOOOxx
+391    607     1       3       1       11      1       91      191     391     391     2       3       BPAAAA  JXAAAA  VVVVxx
+395    608     1       3       5       15      5       95      195     395     395     10      11      FPAAAA  KXAAAA  AAAAxx
+502    609     0       2       2       2       2       2       102     2       502     4       5       ITAAAA  LXAAAA  HHHHxx
+85     610     1       1       5       5       5       85      85      85      85      10      11      HDAAAA  MXAAAA  OOOOxx
+786    611     0       2       6       6       6       86      186     286     786     12      13      GEAAAA  NXAAAA  VVVVxx
+619    612     1       3       9       19      9       19      19      119     619     18      19      VXAAAA  OXAAAA  AAAAxx
+440    613     0       0       0       0       0       40      40      440     440     0       1       YQAAAA  PXAAAA  HHHHxx
+949    614     1       1       9       9       9       49      149     449     949     18      19      NKAAAA  QXAAAA  OOOOxx
+691    615     1       3       1       11      1       91      91      191     691     2       3       PAAAAA  RXAAAA  VVVVxx
+348    616     0       0       8       8       8       48      148     348     348     16      17      KNAAAA  SXAAAA  AAAAxx
+506    617     0       2       6       6       6       6       106     6       506     12      13      MTAAAA  TXAAAA  HHHHxx
+192    618     0       0       2       12      2       92      192     192     192     4       5       KHAAAA  UXAAAA  OOOOxx
+369    619     1       1       9       9       9       69      169     369     369     18      19      FOAAAA  VXAAAA  VVVVxx
+311    620     1       3       1       11      1       11      111     311     311     2       3       ZLAAAA  WXAAAA  AAAAxx
+273    621     1       1       3       13      3       73      73      273     273     6       7       NKAAAA  XXAAAA  HHHHxx
+770    622     0       2       0       10      0       70      170     270     770     0       1       QDAAAA  YXAAAA  OOOOxx
+191    623     1       3       1       11      1       91      191     191     191     2       3       JHAAAA  ZXAAAA  VVVVxx
+90     624     0       2       0       10      0       90      90      90      90      0       1       MDAAAA  AYAAAA  AAAAxx
+163    625     1       3       3       3       3       63      163     163     163     6       7       HGAAAA  BYAAAA  HHHHxx
+350    626     0       2       0       10      0       50      150     350     350     0       1       MNAAAA  CYAAAA  OOOOxx
+55     627     1       3       5       15      5       55      55      55      55      10      11      DCAAAA  DYAAAA  VVVVxx
+488    628     0       0       8       8       8       88      88      488     488     16      17      USAAAA  EYAAAA  AAAAxx
+215    629     1       3       5       15      5       15      15      215     215     10      11      HIAAAA  FYAAAA  HHHHxx
+732    630     0       0       2       12      2       32      132     232     732     4       5       ECAAAA  GYAAAA  OOOOxx
+688    631     0       0       8       8       8       88      88      188     688     16      17      MAAAAA  HYAAAA  VVVVxx
+520    632     0       0       0       0       0       20      120     20      520     0       1       AUAAAA  IYAAAA  AAAAxx
+62     633     0       2       2       2       2       62      62      62      62      4       5       KCAAAA  JYAAAA  HHHHxx
+423    634     1       3       3       3       3       23      23      423     423     6       7       HQAAAA  KYAAAA  OOOOxx
+242    635     0       2       2       2       2       42      42      242     242     4       5       IJAAAA  LYAAAA  VVVVxx
+193    636     1       1       3       13      3       93      193     193     193     6       7       LHAAAA  MYAAAA  AAAAxx
+648    637     0       0       8       8       8       48      48      148     648     16      17      YYAAAA  NYAAAA  HHHHxx
+459    638     1       3       9       19      9       59      59      459     459     18      19      RRAAAA  OYAAAA  OOOOxx
+196    639     0       0       6       16      6       96      196     196     196     12      13      OHAAAA  PYAAAA  VVVVxx
+476    640     0       0       6       16      6       76      76      476     476     12      13      ISAAAA  QYAAAA  AAAAxx
+903    641     1       3       3       3       3       3       103     403     903     6       7       TIAAAA  RYAAAA  HHHHxx
+974    642     0       2       4       14      4       74      174     474     974     8       9       MLAAAA  SYAAAA  OOOOxx
+603    643     1       3       3       3       3       3       3       103     603     6       7       FXAAAA  TYAAAA  VVVVxx
+12     644     0       0       2       12      2       12      12      12      12      4       5       MAAAAA  UYAAAA  AAAAxx
+599    645     1       3       9       19      9       99      199     99      599     18      19      BXAAAA  VYAAAA  HHHHxx
+914    646     0       2       4       14      4       14      114     414     914     8       9       EJAAAA  WYAAAA  OOOOxx
+7      647     1       3       7       7       7       7       7       7       7       14      15      HAAAAA  XYAAAA  VVVVxx
+213    648     1       1       3       13      3       13      13      213     213     6       7       FIAAAA  YYAAAA  AAAAxx
+174    649     0       2       4       14      4       74      174     174     174     8       9       SGAAAA  ZYAAAA  HHHHxx
+392    650     0       0       2       12      2       92      192     392     392     4       5       CPAAAA  AZAAAA  OOOOxx
+674    651     0       2       4       14      4       74      74      174     674     8       9       YZAAAA  BZAAAA  VVVVxx
+650    652     0       2       0       10      0       50      50      150     650     0       1       AZAAAA  CZAAAA  AAAAxx
+8      653     0       0       8       8       8       8       8       8       8       16      17      IAAAAA  DZAAAA  HHHHxx
+492    654     0       0       2       12      2       92      92      492     492     4       5       YSAAAA  EZAAAA  OOOOxx
+322    655     0       2       2       2       2       22      122     322     322     4       5       KMAAAA  FZAAAA  VVVVxx
+315    656     1       3       5       15      5       15      115     315     315     10      11      DMAAAA  GZAAAA  AAAAxx
+380    657     0       0       0       0       0       80      180     380     380     0       1       QOAAAA  HZAAAA  HHHHxx
+353    658     1       1       3       13      3       53      153     353     353     6       7       PNAAAA  IZAAAA  OOOOxx
+892    659     0       0       2       12      2       92      92      392     892     4       5       IIAAAA  JZAAAA  VVVVxx
+932    660     0       0       2       12      2       32      132     432     932     4       5       WJAAAA  KZAAAA  AAAAxx
+993    661     1       1       3       13      3       93      193     493     993     6       7       FMAAAA  LZAAAA  HHHHxx
+859    662     1       3       9       19      9       59      59      359     859     18      19      BHAAAA  MZAAAA  OOOOxx
+806    663     0       2       6       6       6       6       6       306     806     12      13      AFAAAA  NZAAAA  VVVVxx
+145    664     1       1       5       5       5       45      145     145     145     10      11      PFAAAA  OZAAAA  AAAAxx
+373    665     1       1       3       13      3       73      173     373     373     6       7       JOAAAA  PZAAAA  HHHHxx
+418    666     0       2       8       18      8       18      18      418     418     16      17      CQAAAA  QZAAAA  OOOOxx
+865    667     1       1       5       5       5       65      65      365     865     10      11      HHAAAA  RZAAAA  VVVVxx
+462    668     0       2       2       2       2       62      62      462     462     4       5       URAAAA  SZAAAA  AAAAxx
+24     669     0       0       4       4       4       24      24      24      24      8       9       YAAAAA  TZAAAA  HHHHxx
+920    670     0       0       0       0       0       20      120     420     920     0       1       KJAAAA  UZAAAA  OOOOxx
+672    671     0       0       2       12      2       72      72      172     672     4       5       WZAAAA  VZAAAA  VVVVxx
+92     672     0       0       2       12      2       92      92      92      92      4       5       ODAAAA  WZAAAA  AAAAxx
+721    673     1       1       1       1       1       21      121     221     721     2       3       TBAAAA  XZAAAA  HHHHxx
+646    674     0       2       6       6       6       46      46      146     646     12      13      WYAAAA  YZAAAA  OOOOxx
+910    675     0       2       0       10      0       10      110     410     910     0       1       AJAAAA  ZZAAAA  VVVVxx
+909    676     1       1       9       9       9       9       109     409     909     18      19      ZIAAAA  AABAAA  AAAAxx
+630    677     0       2       0       10      0       30      30      130     630     0       1       GYAAAA  BABAAA  HHHHxx
+482    678     0       2       2       2       2       82      82      482     482     4       5       OSAAAA  CABAAA  OOOOxx
+559    679     1       3       9       19      9       59      159     59      559     18      19      NVAAAA  DABAAA  VVVVxx
+853    680     1       1       3       13      3       53      53      353     853     6       7       VGAAAA  EABAAA  AAAAxx
+141    681     1       1       1       1       1       41      141     141     141     2       3       LFAAAA  FABAAA  HHHHxx
+266    682     0       2       6       6       6       66      66      266     266     12      13      GKAAAA  GABAAA  OOOOxx
+835    683     1       3       5       15      5       35      35      335     835     10      11      DGAAAA  HABAAA  VVVVxx
+164    684     0       0       4       4       4       64      164     164     164     8       9       IGAAAA  IABAAA  AAAAxx
+629    685     1       1       9       9       9       29      29      129     629     18      19      FYAAAA  JABAAA  HHHHxx
+203    686     1       3       3       3       3       3       3       203     203     6       7       VHAAAA  KABAAA  OOOOxx
+411    687     1       3       1       11      1       11      11      411     411     2       3       VPAAAA  LABAAA  VVVVxx
+930    688     0       2       0       10      0       30      130     430     930     0       1       UJAAAA  MABAAA  AAAAxx
+435    689     1       3       5       15      5       35      35      435     435     10      11      TQAAAA  NABAAA  HHHHxx
+563    690     1       3       3       3       3       63      163     63      563     6       7       RVAAAA  OABAAA  OOOOxx
+960    691     0       0       0       0       0       60      160     460     960     0       1       YKAAAA  PABAAA  VVVVxx
+733    692     1       1       3       13      3       33      133     233     733     6       7       FCAAAA  QABAAA  AAAAxx
+967    693     1       3       7       7       7       67      167     467     967     14      15      FLAAAA  RABAAA  HHHHxx
+668    694     0       0       8       8       8       68      68      168     668     16      17      SZAAAA  SABAAA  OOOOxx
+994    695     0       2       4       14      4       94      194     494     994     8       9       GMAAAA  TABAAA  VVVVxx
+129    696     1       1       9       9       9       29      129     129     129     18      19      ZEAAAA  UABAAA  AAAAxx
+954    697     0       2       4       14      4       54      154     454     954     8       9       SKAAAA  VABAAA  HHHHxx
+68     698     0       0       8       8       8       68      68      68      68      16      17      QCAAAA  WABAAA  OOOOxx
+79     699     1       3       9       19      9       79      79      79      79      18      19      BDAAAA  XABAAA  VVVVxx
+121    700     1       1       1       1       1       21      121     121     121     2       3       REAAAA  YABAAA  AAAAxx
+740    701     0       0       0       0       0       40      140     240     740     0       1       MCAAAA  ZABAAA  HHHHxx
+902    702     0       2       2       2       2       2       102     402     902     4       5       SIAAAA  ABBAAA  OOOOxx
+695    703     1       3       5       15      5       95      95      195     695     10      11      TAAAAA  BBBAAA  VVVVxx
+455    704     1       3       5       15      5       55      55      455     455     10      11      NRAAAA  CBBAAA  AAAAxx
+89     705     1       1       9       9       9       89      89      89      89      18      19      LDAAAA  DBBAAA  HHHHxx
+893    706     1       1       3       13      3       93      93      393     893     6       7       JIAAAA  EBBAAA  OOOOxx
+202    707     0       2       2       2       2       2       2       202     202     4       5       UHAAAA  FBBAAA  VVVVxx
+132    708     0       0       2       12      2       32      132     132     132     4       5       CFAAAA  GBBAAA  AAAAxx
+782    709     0       2       2       2       2       82      182     282     782     4       5       CEAAAA  HBBAAA  HHHHxx
+512    710     0       0       2       12      2       12      112     12      512     4       5       STAAAA  IBBAAA  OOOOxx
+857    711     1       1       7       17      7       57      57      357     857     14      15      ZGAAAA  JBBAAA  VVVVxx
+248    712     0       0       8       8       8       48      48      248     248     16      17      OJAAAA  KBBAAA  AAAAxx
+858    713     0       2       8       18      8       58      58      358     858     16      17      AHAAAA  LBBAAA  HHHHxx
+527    714     1       3       7       7       7       27      127     27      527     14      15      HUAAAA  MBBAAA  OOOOxx
+450    715     0       2       0       10      0       50      50      450     450     0       1       IRAAAA  NBBAAA  VVVVxx
+712    716     0       0       2       12      2       12      112     212     712     4       5       KBAAAA  OBBAAA  AAAAxx
+153    717     1       1       3       13      3       53      153     153     153     6       7       XFAAAA  PBBAAA  HHHHxx
+587    718     1       3       7       7       7       87      187     87      587     14      15      PWAAAA  QBBAAA  OOOOxx
+593    719     1       1       3       13      3       93      193     93      593     6       7       VWAAAA  RBBAAA  VVVVxx
+249    720     1       1       9       9       9       49      49      249     249     18      19      PJAAAA  SBBAAA  AAAAxx
+128    721     0       0       8       8       8       28      128     128     128     16      17      YEAAAA  TBBAAA  HHHHxx
+675    722     1       3       5       15      5       75      75      175     675     10      11      ZZAAAA  UBBAAA  OOOOxx
+929    723     1       1       9       9       9       29      129     429     929     18      19      TJAAAA  VBBAAA  VVVVxx
+156    724     0       0       6       16      6       56      156     156     156     12      13      AGAAAA  WBBAAA  AAAAxx
+415    725     1       3       5       15      5       15      15      415     415     10      11      ZPAAAA  XBBAAA  HHHHxx
+28     726     0       0       8       8       8       28      28      28      28      16      17      CBAAAA  YBBAAA  OOOOxx
+18     727     0       2       8       18      8       18      18      18      18      16      17      SAAAAA  ZBBAAA  VVVVxx
+255    728     1       3       5       15      5       55      55      255     255     10      11      VJAAAA  ACBAAA  AAAAxx
+793    729     1       1       3       13      3       93      193     293     793     6       7       NEAAAA  BCBAAA  HHHHxx
+554    730     0       2       4       14      4       54      154     54      554     8       9       IVAAAA  CCBAAA  OOOOxx
+467    731     1       3       7       7       7       67      67      467     467     14      15      ZRAAAA  DCBAAA  VVVVxx
+410    732     0       2       0       10      0       10      10      410     410     0       1       UPAAAA  ECBAAA  AAAAxx
+651    733     1       3       1       11      1       51      51      151     651     2       3       BZAAAA  FCBAAA  HHHHxx
+287    734     1       3       7       7       7       87      87      287     287     14      15      BLAAAA  GCBAAA  OOOOxx
+640    735     0       0       0       0       0       40      40      140     640     0       1       QYAAAA  HCBAAA  VVVVxx
+245    736     1       1       5       5       5       45      45      245     245     10      11      LJAAAA  ICBAAA  AAAAxx
+21     737     1       1       1       1       1       21      21      21      21      2       3       VAAAAA  JCBAAA  HHHHxx
+83     738     1       3       3       3       3       83      83      83      83      6       7       FDAAAA  KCBAAA  OOOOxx
+228    739     0       0       8       8       8       28      28      228     228     16      17      UIAAAA  LCBAAA  VVVVxx
+323    740     1       3       3       3       3       23      123     323     323     6       7       LMAAAA  MCBAAA  AAAAxx
+594    741     0       2       4       14      4       94      194     94      594     8       9       WWAAAA  NCBAAA  HHHHxx
+528    742     0       0       8       8       8       28      128     28      528     16      17      IUAAAA  OCBAAA  OOOOxx
+276    743     0       0       6       16      6       76      76      276     276     12      13      QKAAAA  PCBAAA  VVVVxx
+598    744     0       2       8       18      8       98      198     98      598     16      17      AXAAAA  QCBAAA  AAAAxx
+635    745     1       3       5       15      5       35      35      135     635     10      11      LYAAAA  RCBAAA  HHHHxx
+868    746     0       0       8       8       8       68      68      368     868     16      17      KHAAAA  SCBAAA  OOOOxx
+290    747     0       2       0       10      0       90      90      290     290     0       1       ELAAAA  TCBAAA  VVVVxx
+468    748     0       0       8       8       8       68      68      468     468     16      17      ASAAAA  UCBAAA  AAAAxx
+689    749     1       1       9       9       9       89      89      189     689     18      19      NAAAAA  VCBAAA  HHHHxx
+799    750     1       3       9       19      9       99      199     299     799     18      19      TEAAAA  WCBAAA  OOOOxx
+210    751     0       2       0       10      0       10      10      210     210     0       1       CIAAAA  XCBAAA  VVVVxx
+346    752     0       2       6       6       6       46      146     346     346     12      13      INAAAA  YCBAAA  AAAAxx
+957    753     1       1       7       17      7       57      157     457     957     14      15      VKAAAA  ZCBAAA  HHHHxx
+905    754     1       1       5       5       5       5       105     405     905     10      11      VIAAAA  ADBAAA  OOOOxx
+523    755     1       3       3       3       3       23      123     23      523     6       7       DUAAAA  BDBAAA  VVVVxx
+899    756     1       3       9       19      9       99      99      399     899     18      19      PIAAAA  CDBAAA  AAAAxx
+867    757     1       3       7       7       7       67      67      367     867     14      15      JHAAAA  DDBAAA  HHHHxx
+11     758     1       3       1       11      1       11      11      11      11      2       3       LAAAAA  EDBAAA  OOOOxx
+320    759     0       0       0       0       0       20      120     320     320     0       1       IMAAAA  FDBAAA  VVVVxx
+766    760     0       2       6       6       6       66      166     266     766     12      13      MDAAAA  GDBAAA  AAAAxx
+84     761     0       0       4       4       4       84      84      84      84      8       9       GDAAAA  HDBAAA  HHHHxx
+507    762     1       3       7       7       7       7       107     7       507     14      15      NTAAAA  IDBAAA  OOOOxx
+471    763     1       3       1       11      1       71      71      471     471     2       3       DSAAAA  JDBAAA  VVVVxx
+517    764     1       1       7       17      7       17      117     17      517     14      15      XTAAAA  KDBAAA  AAAAxx
+234    765     0       2       4       14      4       34      34      234     234     8       9       AJAAAA  LDBAAA  HHHHxx
+988    766     0       0       8       8       8       88      188     488     988     16      17      AMAAAA  MDBAAA  OOOOxx
+473    767     1       1       3       13      3       73      73      473     473     6       7       FSAAAA  NDBAAA  VVVVxx
+66     768     0       2       6       6       6       66      66      66      66      12      13      OCAAAA  ODBAAA  AAAAxx
+530    769     0       2       0       10      0       30      130     30      530     0       1       KUAAAA  PDBAAA  HHHHxx
+834    770     0       2       4       14      4       34      34      334     834     8       9       CGAAAA  QDBAAA  OOOOxx
+894    771     0       2       4       14      4       94      94      394     894     8       9       KIAAAA  RDBAAA  VVVVxx
+481    772     1       1       1       1       1       81      81      481     481     2       3       NSAAAA  SDBAAA  AAAAxx
+280    773     0       0       0       0       0       80      80      280     280     0       1       UKAAAA  TDBAAA  HHHHxx
+705    774     1       1       5       5       5       5       105     205     705     10      11      DBAAAA  UDBAAA  OOOOxx
+218    775     0       2       8       18      8       18      18      218     218     16      17      KIAAAA  VDBAAA  VVVVxx
+560    776     0       0       0       0       0       60      160     60      560     0       1       OVAAAA  WDBAAA  AAAAxx
+123    777     1       3       3       3       3       23      123     123     123     6       7       TEAAAA  XDBAAA  HHHHxx
+289    778     1       1       9       9       9       89      89      289     289     18      19      DLAAAA  YDBAAA  OOOOxx
+189    779     1       1       9       9       9       89      189     189     189     18      19      HHAAAA  ZDBAAA  VVVVxx
+541    780     1       1       1       1       1       41      141     41      541     2       3       VUAAAA  AEBAAA  AAAAxx
+876    781     0       0       6       16      6       76      76      376     876     12      13      SHAAAA  BEBAAA  HHHHxx
+504    782     0       0       4       4       4       4       104     4       504     8       9       KTAAAA  CEBAAA  OOOOxx
+643    783     1       3       3       3       3       43      43      143     643     6       7       TYAAAA  DEBAAA  VVVVxx
+73     784     1       1       3       13      3       73      73      73      73      6       7       VCAAAA  EEBAAA  AAAAxx
+465    785     1       1       5       5       5       65      65      465     465     10      11      XRAAAA  FEBAAA  HHHHxx
+861    786     1       1       1       1       1       61      61      361     861     2       3       DHAAAA  GEBAAA  OOOOxx
+355    787     1       3       5       15      5       55      155     355     355     10      11      RNAAAA  HEBAAA  VVVVxx
+441    788     1       1       1       1       1       41      41      441     441     2       3       ZQAAAA  IEBAAA  AAAAxx
+219    789     1       3       9       19      9       19      19      219     219     18      19      LIAAAA  JEBAAA  HHHHxx
+839    790     1       3       9       19      9       39      39      339     839     18      19      HGAAAA  KEBAAA  OOOOxx
+271    791     1       3       1       11      1       71      71      271     271     2       3       LKAAAA  LEBAAA  VVVVxx
+212    792     0       0       2       12      2       12      12      212     212     4       5       EIAAAA  MEBAAA  AAAAxx
+904    793     0       0       4       4       4       4       104     404     904     8       9       UIAAAA  NEBAAA  HHHHxx
+244    794     0       0       4       4       4       44      44      244     244     8       9       KJAAAA  OEBAAA  OOOOxx
+751    795     1       3       1       11      1       51      151     251     751     2       3       XCAAAA  PEBAAA  VVVVxx
+944    796     0       0       4       4       4       44      144     444     944     8       9       IKAAAA  QEBAAA  AAAAxx
+305    797     1       1       5       5       5       5       105     305     305     10      11      TLAAAA  REBAAA  HHHHxx
+617    798     1       1       7       17      7       17      17      117     617     14      15      TXAAAA  SEBAAA  OOOOxx
+891    799     1       3       1       11      1       91      91      391     891     2       3       HIAAAA  TEBAAA  VVVVxx
+653    800     1       1       3       13      3       53      53      153     653     6       7       DZAAAA  UEBAAA  AAAAxx
+845    801     1       1       5       5       5       45      45      345     845     10      11      NGAAAA  VEBAAA  HHHHxx
+936    802     0       0       6       16      6       36      136     436     936     12      13      AKAAAA  WEBAAA  OOOOxx
+91     803     1       3       1       11      1       91      91      91      91      2       3       NDAAAA  XEBAAA  VVVVxx
+442    804     0       2       2       2       2       42      42      442     442     4       5       ARAAAA  YEBAAA  AAAAxx
+498    805     0       2       8       18      8       98      98      498     498     16      17      ETAAAA  ZEBAAA  HHHHxx
+987    806     1       3       7       7       7       87      187     487     987     14      15      ZLAAAA  AFBAAA  OOOOxx
+194    807     0       2       4       14      4       94      194     194     194     8       9       MHAAAA  BFBAAA  VVVVxx
+927    808     1       3       7       7       7       27      127     427     927     14      15      RJAAAA  CFBAAA  AAAAxx
+607    809     1       3       7       7       7       7       7       107     607     14      15      JXAAAA  DFBAAA  HHHHxx
+119    810     1       3       9       19      9       19      119     119     119     18      19      PEAAAA  EFBAAA  OOOOxx
+182    811     0       2       2       2       2       82      182     182     182     4       5       AHAAAA  FFBAAA  VVVVxx
+606    812     0       2       6       6       6       6       6       106     606     12      13      IXAAAA  GFBAAA  AAAAxx
+849    813     1       1       9       9       9       49      49      349     849     18      19      RGAAAA  HFBAAA  HHHHxx
+34     814     0       2       4       14      4       34      34      34      34      8       9       IBAAAA  IFBAAA  OOOOxx
+683    815     1       3       3       3       3       83      83      183     683     6       7       HAAAAA  JFBAAA  VVVVxx
+134    816     0       2       4       14      4       34      134     134     134     8       9       EFAAAA  KFBAAA  AAAAxx
+331    817     1       3       1       11      1       31      131     331     331     2       3       TMAAAA  LFBAAA  HHHHxx
+808    818     0       0       8       8       8       8       8       308     808     16      17      CFAAAA  MFBAAA  OOOOxx
+703    819     1       3       3       3       3       3       103     203     703     6       7       BBAAAA  NFBAAA  VVVVxx
+669    820     1       1       9       9       9       69      69      169     669     18      19      TZAAAA  OFBAAA  AAAAxx
+264    821     0       0       4       4       4       64      64      264     264     8       9       EKAAAA  PFBAAA  HHHHxx
+277    822     1       1       7       17      7       77      77      277     277     14      15      RKAAAA  QFBAAA  OOOOxx
+877    823     1       1       7       17      7       77      77      377     877     14      15      THAAAA  RFBAAA  VVVVxx
+783    824     1       3       3       3       3       83      183     283     783     6       7       DEAAAA  SFBAAA  AAAAxx
+791    825     1       3       1       11      1       91      191     291     791     2       3       LEAAAA  TFBAAA  HHHHxx
+171    826     1       3       1       11      1       71      171     171     171     2       3       PGAAAA  UFBAAA  OOOOxx
+564    827     0       0       4       4       4       64      164     64      564     8       9       SVAAAA  VFBAAA  VVVVxx
+230    828     0       2       0       10      0       30      30      230     230     0       1       WIAAAA  WFBAAA  AAAAxx
+881    829     1       1       1       1       1       81      81      381     881     2       3       XHAAAA  XFBAAA  HHHHxx
+890    830     0       2       0       10      0       90      90      390     890     0       1       GIAAAA  YFBAAA  OOOOxx
+374    831     0       2       4       14      4       74      174     374     374     8       9       KOAAAA  ZFBAAA  VVVVxx
+697    832     1       1       7       17      7       97      97      197     697     14      15      VAAAAA  AGBAAA  AAAAxx
+4      833     0       0       4       4       4       4       4       4       4       8       9       EAAAAA  BGBAAA  HHHHxx
+385    834     1       1       5       5       5       85      185     385     385     10      11      VOAAAA  CGBAAA  OOOOxx
+739    835     1       3       9       19      9       39      139     239     739     18      19      LCAAAA  DGBAAA  VVVVxx
+623    836     1       3       3       3       3       23      23      123     623     6       7       ZXAAAA  EGBAAA  AAAAxx
+547    837     1       3       7       7       7       47      147     47      547     14      15      BVAAAA  FGBAAA  HHHHxx
+532    838     0       0       2       12      2       32      132     32      532     4       5       MUAAAA  GGBAAA  OOOOxx
+383    839     1       3       3       3       3       83      183     383     383     6       7       TOAAAA  HGBAAA  VVVVxx
+181    840     1       1       1       1       1       81      181     181     181     2       3       ZGAAAA  IGBAAA  AAAAxx
+327    841     1       3       7       7       7       27      127     327     327     14      15      PMAAAA  JGBAAA  HHHHxx
+701    842     1       1       1       1       1       1       101     201     701     2       3       ZAAAAA  KGBAAA  OOOOxx
+111    843     1       3       1       11      1       11      111     111     111     2       3       HEAAAA  LGBAAA  VVVVxx
+977    844     1       1       7       17      7       77      177     477     977     14      15      PLAAAA  MGBAAA  AAAAxx
+431    845     1       3       1       11      1       31      31      431     431     2       3       PQAAAA  NGBAAA  HHHHxx
+456    846     0       0       6       16      6       56      56      456     456     12      13      ORAAAA  OGBAAA  OOOOxx
+368    847     0       0       8       8       8       68      168     368     368     16      17      EOAAAA  PGBAAA  VVVVxx
+32     848     0       0       2       12      2       32      32      32      32      4       5       GBAAAA  QGBAAA  AAAAxx
+125    849     1       1       5       5       5       25      125     125     125     10      11      VEAAAA  RGBAAA  HHHHxx
+847    850     1       3       7       7       7       47      47      347     847     14      15      PGAAAA  SGBAAA  OOOOxx
+485    851     1       1       5       5       5       85      85      485     485     10      11      RSAAAA  TGBAAA  VVVVxx
+387    852     1       3       7       7       7       87      187     387     387     14      15      XOAAAA  UGBAAA  AAAAxx
+288    853     0       0       8       8       8       88      88      288     288     16      17      CLAAAA  VGBAAA  HHHHxx
+919    854     1       3       9       19      9       19      119     419     919     18      19      JJAAAA  WGBAAA  OOOOxx
+393    855     1       1       3       13      3       93      193     393     393     6       7       DPAAAA  XGBAAA  VVVVxx
+953    856     1       1       3       13      3       53      153     453     953     6       7       RKAAAA  YGBAAA  AAAAxx
+798    857     0       2       8       18      8       98      198     298     798     16      17      SEAAAA  ZGBAAA  HHHHxx
+940    858     0       0       0       0       0       40      140     440     940     0       1       EKAAAA  AHBAAA  OOOOxx
+198    859     0       2       8       18      8       98      198     198     198     16      17      QHAAAA  BHBAAA  VVVVxx
+25     860     1       1       5       5       5       25      25      25      25      10      11      ZAAAAA  CHBAAA  AAAAxx
+190    861     0       2       0       10      0       90      190     190     190     0       1       IHAAAA  DHBAAA  HHHHxx
+820    862     0       0       0       0       0       20      20      320     820     0       1       OFAAAA  EHBAAA  OOOOxx
+15     863     1       3       5       15      5       15      15      15      15      10      11      PAAAAA  FHBAAA  VVVVxx
+427    864     1       3       7       7       7       27      27      427     427     14      15      LQAAAA  GHBAAA  AAAAxx
+349    865     1       1       9       9       9       49      149     349     349     18      19      LNAAAA  HHBAAA  HHHHxx
+785    866     1       1       5       5       5       85      185     285     785     10      11      FEAAAA  IHBAAA  OOOOxx
+340    867     0       0       0       0       0       40      140     340     340     0       1       CNAAAA  JHBAAA  VVVVxx
+292    868     0       0       2       12      2       92      92      292     292     4       5       GLAAAA  KHBAAA  AAAAxx
+17     869     1       1       7       17      7       17      17      17      17      14      15      RAAAAA  LHBAAA  HHHHxx
+985    870     1       1       5       5       5       85      185     485     985     10      11      XLAAAA  MHBAAA  OOOOxx
+645    871     1       1       5       5       5       45      45      145     645     10      11      VYAAAA  NHBAAA  VVVVxx
+631    872     1       3       1       11      1       31      31      131     631     2       3       HYAAAA  OHBAAA  AAAAxx
+761    873     1       1       1       1       1       61      161     261     761     2       3       HDAAAA  PHBAAA  HHHHxx
+707    874     1       3       7       7       7       7       107     207     707     14      15      FBAAAA  QHBAAA  OOOOxx
+776    875     0       0       6       16      6       76      176     276     776     12      13      WDAAAA  RHBAAA  VVVVxx
+856    876     0       0       6       16      6       56      56      356     856     12      13      YGAAAA  SHBAAA  AAAAxx
+978    877     0       2       8       18      8       78      178     478     978     16      17      QLAAAA  THBAAA  HHHHxx
+710    878     0       2       0       10      0       10      110     210     710     0       1       IBAAAA  UHBAAA  OOOOxx
+604    879     0       0       4       4       4       4       4       104     604     8       9       GXAAAA  VHBAAA  VVVVxx
+291    880     1       3       1       11      1       91      91      291     291     2       3       FLAAAA  WHBAAA  AAAAxx
+747    881     1       3       7       7       7       47      147     247     747     14      15      TCAAAA  XHBAAA  HHHHxx
+837    882     1       1       7       17      7       37      37      337     837     14      15      FGAAAA  YHBAAA  OOOOxx
+722    883     0       2       2       2       2       22      122     222     722     4       5       UBAAAA  ZHBAAA  VVVVxx
+925    884     1       1       5       5       5       25      125     425     925     10      11      PJAAAA  AIBAAA  AAAAxx
+49     885     1       1       9       9       9       49      49      49      49      18      19      XBAAAA  BIBAAA  HHHHxx
+832    886     0       0       2       12      2       32      32      332     832     4       5       AGAAAA  CIBAAA  OOOOxx
+336    887     0       0       6       16      6       36      136     336     336     12      13      YMAAAA  DIBAAA  VVVVxx
+185    888     1       1       5       5       5       85      185     185     185     10      11      DHAAAA  EIBAAA  AAAAxx
+434    889     0       2       4       14      4       34      34      434     434     8       9       SQAAAA  FIBAAA  HHHHxx
+284    890     0       0       4       4       4       84      84      284     284     8       9       YKAAAA  GIBAAA  OOOOxx
+812    891     0       0       2       12      2       12      12      312     812     4       5       GFAAAA  HIBAAA  VVVVxx
+810    892     0       2       0       10      0       10      10      310     810     0       1       EFAAAA  IIBAAA  AAAAxx
+252    893     0       0       2       12      2       52      52      252     252     4       5       SJAAAA  JIBAAA  HHHHxx
+965    894     1       1       5       5       5       65      165     465     965     10      11      DLAAAA  KIBAAA  OOOOxx
+110    895     0       2       0       10      0       10      110     110     110     0       1       GEAAAA  LIBAAA  VVVVxx
+698    896     0       2       8       18      8       98      98      198     698     16      17      WAAAAA  MIBAAA  AAAAxx
+283    897     1       3       3       3       3       83      83      283     283     6       7       XKAAAA  NIBAAA  HHHHxx
+533    898     1       1       3       13      3       33      133     33      533     6       7       NUAAAA  OIBAAA  OOOOxx
+662    899     0       2       2       2       2       62      62      162     662     4       5       MZAAAA  PIBAAA  VVVVxx
+329    900     1       1       9       9       9       29      129     329     329     18      19      RMAAAA  QIBAAA  AAAAxx
+250    901     0       2       0       10      0       50      50      250     250     0       1       QJAAAA  RIBAAA  HHHHxx
+407    902     1       3       7       7       7       7       7       407     407     14      15      RPAAAA  SIBAAA  OOOOxx
+823    903     1       3       3       3       3       23      23      323     823     6       7       RFAAAA  TIBAAA  VVVVxx
+852    904     0       0       2       12      2       52      52      352     852     4       5       UGAAAA  UIBAAA  AAAAxx
+871    905     1       3       1       11      1       71      71      371     871     2       3       NHAAAA  VIBAAA  HHHHxx
+118    906     0       2       8       18      8       18      118     118     118     16      17      OEAAAA  WIBAAA  OOOOxx
+912    907     0       0       2       12      2       12      112     412     912     4       5       CJAAAA  XIBAAA  VVVVxx
+458    908     0       2       8       18      8       58      58      458     458     16      17      QRAAAA  YIBAAA  AAAAxx
+926    909     0       2       6       6       6       26      126     426     926     12      13      QJAAAA  ZIBAAA  HHHHxx
+328    910     0       0       8       8       8       28      128     328     328     16      17      QMAAAA  AJBAAA  OOOOxx
+980    911     0       0       0       0       0       80      180     480     980     0       1       SLAAAA  BJBAAA  VVVVxx
+259    912     1       3       9       19      9       59      59      259     259     18      19      ZJAAAA  CJBAAA  AAAAxx
+900    913     0       0       0       0       0       0       100     400     900     0       1       QIAAAA  DJBAAA  HHHHxx
+137    914     1       1       7       17      7       37      137     137     137     14      15      HFAAAA  EJBAAA  OOOOxx
+159    915     1       3       9       19      9       59      159     159     159     18      19      DGAAAA  FJBAAA  VVVVxx
+243    916     1       3       3       3       3       43      43      243     243     6       7       JJAAAA  GJBAAA  AAAAxx
+472    917     0       0       2       12      2       72      72      472     472     4       5       ESAAAA  HJBAAA  HHHHxx
+796    918     0       0       6       16      6       96      196     296     796     12      13      QEAAAA  IJBAAA  OOOOxx
+382    919     0       2       2       2       2       82      182     382     382     4       5       SOAAAA  JJBAAA  VVVVxx
+911    920     1       3       1       11      1       11      111     411     911     2       3       BJAAAA  KJBAAA  AAAAxx
+179    921     1       3       9       19      9       79      179     179     179     18      19      XGAAAA  LJBAAA  HHHHxx
+778    922     0       2       8       18      8       78      178     278     778     16      17      YDAAAA  MJBAAA  OOOOxx
+405    923     1       1       5       5       5       5       5       405     405     10      11      PPAAAA  NJBAAA  VVVVxx
+265    924     1       1       5       5       5       65      65      265     265     10      11      FKAAAA  OJBAAA  AAAAxx
+556    925     0       0       6       16      6       56      156     56      556     12      13      KVAAAA  PJBAAA  HHHHxx
+16     926     0       0       6       16      6       16      16      16      16      12      13      QAAAAA  QJBAAA  OOOOxx
+706    927     0       2       6       6       6       6       106     206     706     12      13      EBAAAA  RJBAAA  VVVVxx
+497    928     1       1       7       17      7       97      97      497     497     14      15      DTAAAA  SJBAAA  AAAAxx
+708    929     0       0       8       8       8       8       108     208     708     16      17      GBAAAA  TJBAAA  HHHHxx
+46     930     0       2       6       6       6       46      46      46      46      12      13      UBAAAA  UJBAAA  OOOOxx
+901    931     1       1       1       1       1       1       101     401     901     2       3       RIAAAA  VJBAAA  VVVVxx
+416    932     0       0       6       16      6       16      16      416     416     12      13      AQAAAA  WJBAAA  AAAAxx
+307    933     1       3       7       7       7       7       107     307     307     14      15      VLAAAA  XJBAAA  HHHHxx
+166    934     0       2       6       6       6       66      166     166     166     12      13      KGAAAA  YJBAAA  OOOOxx
+178    935     0       2       8       18      8       78      178     178     178     16      17      WGAAAA  ZJBAAA  VVVVxx
+499    936     1       3       9       19      9       99      99      499     499     18      19      FTAAAA  AKBAAA  AAAAxx
+257    937     1       1       7       17      7       57      57      257     257     14      15      XJAAAA  BKBAAA  HHHHxx
+342    938     0       2       2       2       2       42      142     342     342     4       5       ENAAAA  CKBAAA  OOOOxx
+850    939     0       2       0       10      0       50      50      350     850     0       1       SGAAAA  DKBAAA  VVVVxx
+313    940     1       1       3       13      3       13      113     313     313     6       7       BMAAAA  EKBAAA  AAAAxx
+831    941     1       3       1       11      1       31      31      331     831     2       3       ZFAAAA  FKBAAA  HHHHxx
+57     942     1       1       7       17      7       57      57      57      57      14      15      FCAAAA  GKBAAA  OOOOxx
+37     943     1       1       7       17      7       37      37      37      37      14      15      LBAAAA  HKBAAA  VVVVxx
+511    944     1       3       1       11      1       11      111     11      511     2       3       RTAAAA  IKBAAA  AAAAxx
+578    945     0       2       8       18      8       78      178     78      578     16      17      GWAAAA  JKBAAA  HHHHxx
+100    946     0       0       0       0       0       0       100     100     100     0       1       WDAAAA  KKBAAA  OOOOxx
+935    947     1       3       5       15      5       35      135     435     935     10      11      ZJAAAA  LKBAAA  VVVVxx
+821    948     1       1       1       1       1       21      21      321     821     2       3       PFAAAA  MKBAAA  AAAAxx
+294    949     0       2       4       14      4       94      94      294     294     8       9       ILAAAA  NKBAAA  HHHHxx
+575    950     1       3       5       15      5       75      175     75      575     10      11      DWAAAA  OKBAAA  OOOOxx
+272    951     0       0       2       12      2       72      72      272     272     4       5       MKAAAA  PKBAAA  VVVVxx
+491    952     1       3       1       11      1       91      91      491     491     2       3       XSAAAA  QKBAAA  AAAAxx
+43     953     1       3       3       3       3       43      43      43      43      6       7       RBAAAA  RKBAAA  HHHHxx
+167    954     1       3       7       7       7       67      167     167     167     14      15      LGAAAA  SKBAAA  OOOOxx
+457    955     1       1       7       17      7       57      57      457     457     14      15      PRAAAA  TKBAAA  VVVVxx
+647    956     1       3       7       7       7       47      47      147     647     14      15      XYAAAA  UKBAAA  AAAAxx
+180    957     0       0       0       0       0       80      180     180     180     0       1       YGAAAA  VKBAAA  HHHHxx
+48     958     0       0       8       8       8       48      48      48      48      16      17      WBAAAA  WKBAAA  OOOOxx
+553    959     1       1       3       13      3       53      153     53      553     6       7       HVAAAA  XKBAAA  VVVVxx
+188    960     0       0       8       8       8       88      188     188     188     16      17      GHAAAA  YKBAAA  AAAAxx
+262    961     0       2       2       2       2       62      62      262     262     4       5       CKAAAA  ZKBAAA  HHHHxx
+728    962     0       0       8       8       8       28      128     228     728     16      17      ACAAAA  ALBAAA  OOOOxx
+581    963     1       1       1       1       1       81      181     81      581     2       3       JWAAAA  BLBAAA  VVVVxx
+937    964     1       1       7       17      7       37      137     437     937     14      15      BKAAAA  CLBAAA  AAAAxx
+370    965     0       2       0       10      0       70      170     370     370     0       1       GOAAAA  DLBAAA  HHHHxx
+590    966     0       2       0       10      0       90      190     90      590     0       1       SWAAAA  ELBAAA  OOOOxx
+421    967     1       1       1       1       1       21      21      421     421     2       3       FQAAAA  FLBAAA  VVVVxx
+693    968     1       1       3       13      3       93      93      193     693     6       7       RAAAAA  GLBAAA  AAAAxx
+906    969     0       2       6       6       6       6       106     406     906     12      13      WIAAAA  HLBAAA  HHHHxx
+802    970     0       2       2       2       2       2       2       302     802     4       5       WEAAAA  ILBAAA  OOOOxx
+38     971     0       2       8       18      8       38      38      38      38      16      17      MBAAAA  JLBAAA  VVVVxx
+790    972     0       2       0       10      0       90      190     290     790     0       1       KEAAAA  KLBAAA  AAAAxx
+726    973     0       2       6       6       6       26      126     226     726     12      13      YBAAAA  LLBAAA  HHHHxx
+23     974     1       3       3       3       3       23      23      23      23      6       7       XAAAAA  MLBAAA  OOOOxx
+641    975     1       1       1       1       1       41      41      141     641     2       3       RYAAAA  NLBAAA  VVVVxx
+524    976     0       0       4       4       4       24      124     24      524     8       9       EUAAAA  OLBAAA  AAAAxx
+169    977     1       1       9       9       9       69      169     169     169     18      19      NGAAAA  PLBAAA  HHHHxx
+6      978     0       2       6       6       6       6       6       6       6       12      13      GAAAAA  QLBAAA  OOOOxx
+943    979     1       3       3       3       3       43      143     443     943     6       7       HKAAAA  RLBAAA  VVVVxx
+26     980     0       2       6       6       6       26      26      26      26      12      13      ABAAAA  SLBAAA  AAAAxx
+469    981     1       1       9       9       9       69      69      469     469     18      19      BSAAAA  TLBAAA  HHHHxx
+968    982     0       0       8       8       8       68      168     468     968     16      17      GLAAAA  ULBAAA  OOOOxx
+947    983     1       3       7       7       7       47      147     447     947     14      15      LKAAAA  VLBAAA  VVVVxx
+133    984     1       1       3       13      3       33      133     133     133     6       7       DFAAAA  WLBAAA  AAAAxx
+52     985     0       0       2       12      2       52      52      52      52      4       5       ACAAAA  XLBAAA  HHHHxx
+660    986     0       0       0       0       0       60      60      160     660     0       1       KZAAAA  YLBAAA  OOOOxx
+780    987     0       0       0       0       0       80      180     280     780     0       1       AEAAAA  ZLBAAA  VVVVxx
+963    988     1       3       3       3       3       63      163     463     963     6       7       BLAAAA  AMBAAA  AAAAxx
+561    989     1       1       1       1       1       61      161     61      561     2       3       PVAAAA  BMBAAA  HHHHxx
+402    990     0       2       2       2       2       2       2       402     402     4       5       MPAAAA  CMBAAA  OOOOxx
+437    991     1       1       7       17      7       37      37      437     437     14      15      VQAAAA  DMBAAA  VVVVxx
+112    992     0       0       2       12      2       12      112     112     112     4       5       IEAAAA  EMBAAA  AAAAxx
+247    993     1       3       7       7       7       47      47      247     247     14      15      NJAAAA  FMBAAA  HHHHxx
+579    994     1       3       9       19      9       79      179     79      579     18      19      HWAAAA  GMBAAA  OOOOxx
+379    995     1       3       9       19      9       79      179     379     379     18      19      POAAAA  HMBAAA  VVVVxx
+74     996     0       2       4       14      4       74      74      74      74      8       9       WCAAAA  IMBAAA  AAAAxx
+744    997     0       0       4       4       4       44      144     244     744     8       9       QCAAAA  JMBAAA  HHHHxx
+0      998     0       0       0       0       0       0       0       0       0       0       1       AAAAAA  KMBAAA  OOOOxx
+278    999     0       2       8       18      8       78      78      278     278     16      17      SKAAAA  LMBAAA  VVVVxx
diff --git a/src/test/regress/data/person.data b/src/test/regress/data/person.data
new file mode 100644 (file)
index 0000000..57a8fa6
--- /dev/null
@@ -0,0 +1,50 @@
+mike   40      (3.1,6.2)
+joe    20      (5.5,2.5)
+sally  34      (3.8,45.8)
+sandra 19      (9.345,09.6)
+alex   30      (1.352,8.2)
+sue    50      (8.34,7.375)
+denise 24      (3.78,87.90)
+sarah  88      (8.4,2.3)
+teresa 38      (7.7,1.8)
+nan    28      (6.35,0.43)
+leah   68      (0.6,3.37)
+wendy  78      (2.62,03.3)
+melissa        28      (3.089,087.23)
+joan   18      (9.4,47.04)
+mary   08      (3.7,39.20)
+jane   58      (1.34,0.44)
+liza   38      (9.76,6.90)
+jean   28      (8.561,7.3)
+jenifer        38      (6.6,23.3)
+juanita        58      (4.57,35.8)
+susan  78      (6.579,3)
+zena   98      (0.35,0)
+martie 88      (8.358,.93)
+chris  78      (9.78,2)
+pat    18      (1.19,0.6)
+zola   58      (2.56,4.3)
+louise 98      (5.0,8.7)
+edna   18      (1.53,3.5)
+bertha 88      (2.75,9.4)
+sumi   38      (1.15,0.6)
+koko   88      (1.7,5.5)
+gina   18      (9.82,7.5)
+rean   48      (8.5,5.0)
+sharon 78      (9.237,8.8)
+paula  68      (0.5,0.5)
+julie  68      (3.6,7.2)
+belinda        38      (8.9,1.7)
+karen  48      (8.73,0.0)
+carina 58      (4.27,8.8)
+diane  18      (5.912,5.3)
+esther 98      (5.36,7.6)
+trudy  88      (6.01,0.5)
+fanny  08      (1.2,0.9)
+carmen 78      (3.8,8.2)
+lita   25      (1.3,8.7)
+pamela 48      (8.21,9.3)
+sandy  38      (3.8,0.2)
+trisha 88      (1.29,2.2)
+vera   78      (9.73,6.4)
+velma  68      (8.8,8.9)
diff --git a/src/test/regress/data/real_city.data b/src/test/regress/data/real_city.data
new file mode 100644 (file)
index 0000000..f4365f2
--- /dev/null
@@ -0,0 +1,5 @@
+0      Oakland (1, 4 ,-122.0,37.9, -121.7,37.9, -121.7,37.4, -122.0,37.4)
+0      Oakland (1, 6 ,-121.7,37.4, -121.7,37.0, -122.1,37.0, -122.1,37.3, -122.0,37.3, -122.0,37.4)
+0      Oakland (1, 3, -122.1,37.3, -122.2,37.5, -122.0,37.5)
+0      Berkeley        (1, 4, -122.3,37.9, -122.0,37.9, -122.0,37.6, -122.3,37.6)
+0      Lafayette       (1, 4, -122.3,37.4, -122.2,37.4, -122.2,37.0, -122.3,37.0)
diff --git a/src/test/regress/data/rect.data b/src/test/regress/data/rect.data
new file mode 100644 (file)
index 0000000..a8d36d0
--- /dev/null
@@ -0,0 +1,3100 @@
+(12699,9028,12654,8987)
+(22689,4680,22614,4626)
+(43263,47296,43217,47217)
+(6184,8397,6182,8379)
+(863,28537,788,28456)
+(33783,4733,33746,4693)
+(40456,47134,40426,47087)
+(45950,8153,45887,8060)
+(33433,36474,33399,36460)
+(41106,22017,41086,21962)
+(19214,36781,19179,36767)
+(11582,40823,11498,40737)
+(35565,5404,35546,5360)
+(26489,17387,26405,17356)
+(30874,13849,30796,13814)
+(38255,1619,38227,1593)
+(4445,32006,4405,31914)
+(3923,32921,3876,32913)
+(36054,39464,36032,39434)
+(46540,6780,46524,6758)
+(12184,45811,12118,45787)
+(13198,17090,13143,17051)
+(30939,44578,30865,44486)
+(12502,4939,12431,4902)
+(3250,1108,3169,1063)
+(34029,41240,33976,41180)
+(47057,44018,46967,43927)
+(699,10114,686,10058)
+(5925,26020,5845,25979)
+(9462,39388,9382,39388)
+(270,32616,226,32607)
+(3959,49145,3861,49115)
+(207,40886,179,40879)
+(48480,43312,48412,43233)
+(37183,37209,37161,37110)
+(13576,13505,13521,13487)
+(5877,1037,5818,1036)
+(6777,16694,6776,16692)
+(49362,13905,49299,13845)
+(29356,14606,29313,14562)
+(5492,6976,5441,6971)
+(288,49588,204,49571)
+(36698,37213,36682,37158)
+(718,41336,645,41272)
+(8725,23369,8660,23333)
+(40115,9894,40025,9818)
+(40051,41181,40015,41153)
+(5739,1740,5715,1731)
+(25120,27935,25054,27876)
+(27475,46084,27447,46003)
+(33197,3252,33161,3245)
+(10892,15691,10869,15662)
+(39012,44712,38995,44640)
+(4506,6484,4458,6459)
+(13970,26316,13964,26236)
+(28009,28104,27968,28030)
+(5991,27613,5906,27607)
+(23649,6338,23610,6314)
+(25942,10008,25911,9928)
+(25651,29943,25590,29906)
+(24555,40334,24546,40330)
+(46870,43762,46789,43709)
+(20030,2752,19945,2687)
+(30758,26754,30718,26678)
+(4320,44673,4286,44625)
+(1011,15576,939,15574)
+(41936,40699,41854,40655)
+(20594,19002,20561,18995)
+(9388,41056,9325,41042)
+(34771,46693,34751,46645)
+(49398,46359,49332,46357)
+(23115,35380,23036,35306)
+(46305,34840,46283,34765)
+(16768,21692,16691,21647)
+(28695,3128,28654,3112)
+(22182,7107,22107,7074)
+(14567,1210,14468,1139)
+(14156,37139,14136,37119)
+(33500,38351,33477,38286)
+(39983,41981,39944,41954)
+(26773,20824,26719,20813)
+(42516,22947,42460,22932)
+(26127,10701,26044,10650)
+(17808,13803,17724,13710)
+(14913,49873,14849,49836)
+(37013,820,36955,736)
+(39071,1399,39022,1381)
+(9785,42546,9687,42540)
+(13423,14066,13354,14052)
+(3417,14558,3336,14478)
+(25212,46368,25128,46316)
+(10124,39848,10027,39820)
+(39722,39226,39656,39162)
+(6298,28101,6250,28076)
+(45852,5846,45809,5750)
+(48292,4885,48290,4841)
+(18905,4454,18894,4424)
+(18965,43474,18902,43444)
+(39843,28239,39761,28199)
+(18087,44660,18019,44632)
+(33886,10382,33794,10286)
+(38383,13163,38362,13092)
+(18861,25050,18842,24965)
+(29887,14326,29806,14274)
+(18733,11644,18698,11644)
+(5119,37952,5089,37950)
+(16191,34884,16149,34864)
+(29544,1104,29496,1062)
+(27740,41555,27701,41540)
+(4672,4087,4633,4060)
+(45441,38994,45377,38958)
+(3272,1176,3232,1146)
+(12820,26606,12790,26575)
+(30910,7590,30877,7512)
+(42476,39152,42377,39127)
+(6562,38490,6542,38447)
+(30046,20332,29988,20259)
+(40723,15950,40671,15949)
+(4945,46857,4908,46817)
+(47986,16882,47963,16877)
+(9842,22339,9805,22305)
+(29831,23169,29818,23122)
+(12322,34404,12250,34312)
+(22846,11091,22759,10992)
+(47627,2424,47603,2397)
+(18375,43632,18347,43577)
+(40441,974,40394,965)
+(34260,10573,34194,10522)
+(32914,9549,32828,9503)
+(49023,37827,48978,37799)
+(22183,10691,22111,10669)
+(38036,15828,38014,15759)
+(34604,16801,34508,16746)
+(26737,29997,26675,29976)
+(47375,40298,47293,40210)
+(771,2661,732,2649)
+(28514,25659,28504,25577)
+(13438,46494,13376,46455)
+(7187,17877,7125,17786)
+(49957,43390,49897,43384)
+(26543,20067,26482,20057)
+(16416,29803,16385,29724)
+(36353,7484,36286,7414)
+(26498,3377,26415,3358)
+(28990,32205,28936,32193)
+(45005,3842,45001,3816)
+(21672,23566,21603,23566)
+(33360,43465,33302,43429)
+(29884,9544,29838,9520)
+(5599,15012,5596,14930)
+(22396,21481,22344,21422)
+(24810,14955,24780,14887)
+(47114,18866,47081,18784)
+(39013,39245,38953,39237)
+(12863,40534,12803,40529)
+(351,37068,310,37019)
+(12916,34327,12891,34240)
+(49191,2694,49170,2628)
+(24127,38407,24050,38325)
+(3264,23053,3213,23007)
+(8172,30385,8144,30336)
+(19630,35716,19573,35640)
+(42554,5148,42521,5117)
+(42168,33453,42136,33426)
+(17732,32093,17666,32057)
+(1039,16626,1037,16587)
+(21287,7757,21265,7679)
+(47063,8260,47039,8225)
+(38645,16238,38561,16204)
+(18258,25358,18196,25341)
+(30458,1742,30458,1695)
+(35147,9273,35121,9233)
+(7670,16625,7642,16545)
+(49503,23432,49484,23383)
+(31089,23146,31062,23093)
+(47758,2734,47670,2703)
+(35276,1027,35259,972)
+(26337,17603,26313,17579)
+(35649,16777,35626,16777)
+(42454,5105,42362,5101)
+(21682,24951,21646,24920)
+(48383,25174,48303,25156)
+(14672,3532,14601,3460)
+(22570,22587,22515,22512)
+(23566,25623,23484,25573)
+(9530,24542,9504,24459)
+(41271,451,41236,401)
+(5556,37528,5502,37527)
+(12479,25042,12447,24991)
+(16568,22916,16499,22864)
+(42700,13084,42676,12992)
+(35523,40973,35504,40932)
+(32948,16962,32857,16901)
+(7808,13469,7712,13469)
+(13920,35203,13870,35131)
+(22731,31563,22658,31557)
+(22909,43956,22900,43857)
+(33077,35080,33074,35030)
+(48064,29307,48022,29280)
+(20232,46682,20212,46613)
+(29949,16790,29867,16711)
+(30260,32029,30180,31979)
+(17184,34503,17110,34482)
+(16066,42687,16039,42648)
+(2947,19819,2857,19788)
+(4900,47934,4818,47894)
+(27193,19014,27174,18976)
+(15597,27948,15590,27939)
+(11090,28623,11002,28589)
+(26956,18651,26920,18620)
+(3107,47753,3103,47711)
+(6745,24151,6711,24083)
+(43923,19213,43871,19124)
+(33451,23578,33370,23534)
+(8944,20605,8862,20601)
+(14905,7536,14892,7441)
+(2412,18357,2383,18354)
+(37060,1443,36974,1366)
+(15501,6230,15429,6190)
+(30333,50,30273,6)
+(35567,9965,35482,9912)
+(49847,7128,49798,7067)
+(27685,36396,27668,36384)
+(43832,18491,43825,18431)
+(36849,34600,36785,34589)
+(2348,47938,2307,47902)
+(20473,22131,20445,22113)
+(38486,4293,38471,4288)
+(30611,30451,30553,30400)
+(3883,21299,3819,21260)
+(7696,37555,7644,37534)
+(22399,7913,22317,7911)
+(42565,38605,42500,38598)
+(36595,12151,36500,12106)
+(587,35217,571,35123)
+(5764,15300,5764,15231)
+(12003,21265,11983,21210)
+(42564,4803,42470,4737)
+(42359,36834,42271,36746)
+(44700,14680,44658,14670)
+(19690,5627,19620,5607)
+(17780,43602,17714,43565)
+(45073,3491,45041,3434)
+(35043,2136,35017,2084)
+(39653,19215,39646,19198)
+(23970,25560,23935,25502)
+(28698,49233,28600,49223)
+(30266,3605,30245,3540)
+(25538,7857,25500,7791)
+(17711,1757,17708,1756)
+(5248,594,5190,587)
+(2730,32454,2671,32436)
+(1722,49089,1635,49067)
+(40954,5743,40921,5722)
+(21382,4426,21298,4331)
+(7885,18629,7872,18605)
+(42838,6459,42748,6451)
+(8217,19894,8207,19845)
+(20489,18524,20433,18520)
+(17383,23559,17309,23515)
+(38952,38968,38934,38913)
+(44665,18137,44636,18051)
+(22416,41220,22383,41213)
+(9901,664,9818,646)
+(23475,21981,23449,21973)
+(41875,17991,41818,17988)
+(36517,47731,36509,47713)
+(37595,49849,37581,49834)
+(38771,32720,38748,32684)
+(810,38523,736,38452)
+(29695,14942,29665,14907)
+(31911,15168,31906,15113)
+(3454,36839,3438,36831)
+(4832,47554,4820,47473)
+(11590,8292,11539,8272)
+(8193,33323,8106,33317)
+(16043,14799,16001,14710)
+(19574,11395,19514,11316)
+(26290,41424,26224,41342)
+(22844,12516,22807,12471)
+(15709,49580,15655,49553)
+(13387,28084,13379,28066)
+(2780,38807,2690,38711)
+(22031,32458,22028,32377)
+(13511,3351,13440,3297)
+(14648,26473,14614,26383)
+(17798,19885,17726,19852)
+(32355,27940,32324,27861)
+(43773,21031,43767,20985)
+(15419,45759,15403,45666)
+(770,38863,729,38806)
+(21221,35619,21183,35596)
+(38924,31021,38894,30961)
+(7395,32439,7345,32416)
+(2324,25118,2268,25074)
+(2958,15089,2935,15087)
+(2424,160,2424,81)
+(12123,18644,12099,18616)
+(7459,30276,7422,30218)
+(15847,45488,15814,45428)
+(26409,29897,26389,29863)
+(12336,34322,12279,34322)
+(9440,23550,9396,23466)
+(4991,30850,4905,30768)
+(47262,11940,47201,11939)
+(30584,42868,30555,42838)
+(23144,24089,23056,24067)
+(35930,11609,35847,11573)
+(7812,17271,7789,17203)
+(17946,37554,17878,37480)
+(27356,32869,27298,32813)
+(29971,47783,29933,47697)
+(26075,46494,25988,46451)
+(39314,41366,39289,41269)
+(31708,42900,31688,42865)
+(4510,10231,4439,10203)
+(43806,8482,43758,8446)
+(45990,49694,45927,49617)
+(48815,27640,48782,27573)
+(41675,26733,41622,26723)
+(23229,7709,23175,7693)
+(48976,17733,48962,17731)
+(10686,41470,10597,41434)
+(18053,27059,17989,27012)
+(35495,25950,35459,25912)
+(41896,45014,41881,44999)
+(22654,41896,22572,41801)
+(18581,7087,18524,6988)
+(14697,22406,14681,22311)
+(40092,28122,40043,28030)
+(35844,24243,35816,24238)
+(1254,25653,1250,25644)
+(1603,21730,1556,21640)
+(33048,21779,32991,21763)
+(29979,1632,29916,1592)
+(8620,633,8580,620)
+(22992,27035,22932,27008)
+(21409,29315,21390,29309)
+(3610,44748,3547,44699)
+(20402,9318,20343,9267)
+(31001,8709,30908,8658)
+(46840,47640,46773,47551)
+(49173,4705,49143,4630)
+(5339,31657,5251,31622)
+(8644,49668,8630,49648)
+(45387,2893,45309,2885)
+(47641,31020,47584,30941)
+(40238,10636,40208,10568)
+(19247,36924,19227,36924)
+(917,19957,827,19887)
+(40967,17841,40870,17820)
+(15850,4109,15794,4085)
+(20181,30916,20085,30870)
+(161,24465,107,24374)
+(21737,49690,21667,49663)
+(10328,20911,10232,20852)
+(24187,49823,24128,49768)
+(36084,4578,36007,4501)
+(38771,31741,38673,31674)
+(2202,30102,2111,30006)
+(27322,16074,27228,16039)
+(6843,17280,6765,17248)
+(16972,39744,16912,39700)
+(10608,38741,10553,38708)
+(4917,34801,4828,34766)
+(39281,33659,39268,33618)
+(31706,7119,31645,7063)
+(3427,44006,3422,44004)
+(10134,42608,10044,42599)
+(26294,32080,26200,32068)
+(21777,34680,21769,34606)
+(23373,25957,23314,25915)
+(10710,8401,10681,8400)
+(42062,19458,42019,19394)
+(26530,43036,26458,43004)
+(3394,46081,3360,46077)
+(38743,33953,38677,33924)
+(32438,8226,32345,8160)
+(9210,27333,9118,27301)
+(19594,1600,19568,1551)
+(10003,12278,9952,12255)
+(31737,7206,31650,7146)
+(16594,15821,16502,15759)
+(28208,30296,28189,30278)
+(30602,46237,30555,46185)
+(20715,5155,20697,5140)
+(48892,35271,48793,35210)
+(3175,5590,3113,5525)
+(34220,27947,34132,27865)
+(35105,39792,35011,39727)
+(21919,27314,21839,27286)
+(23963,3723,23917,3699)
+(16312,14078,16236,14045)
+(19233,49824,19185,49794)
+(1447,11768,1356,11699)
+(17311,17709,17224,17653)
+(11962,31709,11871,31627)
+(21355,40131,21355,40085)
+(33750,35273,33724,35180)
+(38896,25539,38879,25524)
+(39569,44899,39569,44893)
+(11075,41547,11039,41500)
+(3215,12202,3199,12127)
+(46215,33458,46132,33455)
+(15121,38012,15083,37974)
+(44448,18726,44412,18690)
+(3899,38263,3870,38262)
+(13854,13353,13786,13298)
+(8252,5402,8191,5320)
+(46849,37968,46820,37897)
+(16422,13957,16376,13897)
+(47369,7665,47353,7629)
+(11982,40874,11956,40806)
+(9552,27580,9496,27562)
+(32247,19399,32176,19337)
+(32704,2169,32635,2091)
+(7471,44213,7411,44130)
+(48433,7096,48379,7089)
+(37357,6543,37338,6452)
+(30460,29624,30433,29535)
+(20350,28794,20341,28705)
+(6326,32360,6267,32317)
+(1711,47519,1654,47430)
+(49540,16510,49521,16426)
+(26975,618,26908,579)
+(24118,30880,24020,30821)
+(3675,15477,3625,15418)
+(44953,9577,44953,9530)
+(38323,7965,38235,7910)
+(6629,36482,6579,36448)
+(33953,16460,33878,16408)
+(49222,16790,49186,16695)
+(17308,16951,17274,16904)
+(14135,6888,14077,6833)
+(38617,47768,38603,47760)
+(7345,10992,7290,10914)
+(35261,42152,35176,42096)
+(28586,4809,28544,4735)
+(37521,25299,37495,25217)
+(41941,17954,41912,17915)
+(1209,46863,1171,46863)
+(20103,34947,20048,34896)
+(32716,33816,32656,33769)
+(11113,6531,11036,6467)
+(48635,7321,48563,7262)
+(28435,37059,28349,37014)
+(12311,17208,12232,17112)
+(1466,48010,1379,48008)
+(11226,11997,11223,11925)
+(46896,32540,46821,32510)
+(32661,31255,32632,31187)
+(37739,20376,37655,20306)
+(44002,43326,43920,43257)
+(30337,1023,30271,968)
+(34436,23357,34432,23345)
+(21367,8168,21353,8091)
+(36370,21611,36369,21569)
+(4152,36488,4080,36476)
+(17696,13924,17664,13853)
+(34252,19395,34159,19316)
+(12574,3072,12573,2975)
+(3995,21243,3943,21167)
+(44553,30126,44513,30108)
+(4599,45275,4552,45254)
+(33191,11404,33176,11348)
+(14245,18633,14177,18540)
+(32457,20705,32393,20700)
+(40052,10499,40016,10457)
+(29824,44065,29785,44037)
+(31613,12565,31557,12543)
+(42692,29000,42652,28996)
+(40680,22219,40603,22140)
+(33575,27661,33488,27644)
+(46194,1385,46184,1355)
+(38442,48501,38407,48426)
+(25305,21544,25236,21523)
+(15562,8226,15561,8208)
+(20844,43614,20752,43558)
+(22566,30541,22554,30532)
+(2760,47802,2672,47789)
+(25515,30745,25433,30675)
+(48382,45134,48382,45093)
+(9940,27094,9871,27087)
+(48690,44361,48610,44338)
+(18992,11585,18899,11582)
+(21551,49983,21492,49885)
+(46778,29113,46770,29071)
+(43219,9593,43212,9548)
+(40291,1248,40224,1190)
+(12687,22225,12635,22219)
+(49372,38790,49306,38721)
+(49503,46808,49411,46798)
+(24745,5162,24732,5138)
+(5046,26517,5023,26424)
+(5583,46538,5495,46531)
+(6084,35950,6079,35895)
+(3503,23096,3437,23024)
+(45275,8420,45244,8418)
+(13514,45251,13491,45249)
+(42112,2748,42047,2668)
+(7810,21907,7806,21878)
+(48378,36029,48303,35979)
+(32568,48605,32510,48563)
+(859,18915,810,18915)
+(41963,17950,41939,17915)
+(42723,8031,42685,7955)
+(19587,5965,19556,5961)
+(8713,33083,8629,32996)
+(21243,7769,21226,7740)
+(43752,43026,43720,42944)
+(7883,41311,7859,41242)
+(10178,47874,10157,47826)
+(32177,48725,32093,48646)
+(22960,2784,22953,2774)
+(25101,49159,25087,49090)
+(32142,48915,32086,48850)
+(6636,44887,6590,44825)
+(37814,11606,37769,11578)
+(2870,23198,2820,23121)
+(21025,16364,20947,16271)
+(31341,36137,31269,36114)
+(38921,7906,38888,7831)
+(6966,17259,6922,17199)
+(32426,13344,32401,13253)
+(8084,30572,8078,30572)
+(42230,47674,42150,47603)
+(20724,44854,20724,44830)
+(27471,38453,27454,38430)
+(24590,37973,24544,37941)
+(45832,26077,45772,26031)
+(9589,24239,9582,24156)
+(37484,49472,37409,49432)
+(30044,19340,30004,19333)
+(16966,14632,16936,14572)
+(9439,40491,9403,40482)
+(28945,5814,28913,5805)
+(43788,41302,43746,41231)
+(33631,43451,33614,43354)
+(17590,49396,17510,49324)
+(15173,32572,15109,32507)
+(1912,23580,1840,23504)
+(38165,16185,38076,16154)
+(6729,1179,6637,1177)
+(6994,45406,6983,45325)
+(2912,21327,2908,21305)
+(14678,14244,14659,14222)
+(29944,14959,29898,14900)
+(47432,35658,47407,35610)
+(25542,39243,25466,39149)
+(5330,7206,5304,7165)
+(24790,27196,24695,27118)
+(38806,1961,38795,1906)
+(23290,4487,23212,4416)
+(35035,24337,34990,24297)
+(5549,38948,5549,38891)
+(24558,15492,24501,15425)
+(4636,3011,4574,2933)
+(26522,39986,26451,39940)
+(33486,18424,33410,18366)
+(36638,14324,36625,14287)
+(35115,41236,35055,41191)
+(31927,16896,31841,16806)
+(5796,43937,5697,43886)
+(25681,41645,25663,41608)
+(10962,42777,10894,42732)
+(32715,11026,32672,10991)
+(45803,20406,45710,20371)
+(34730,17672,34658,17606)
+(8809,6323,8798,6232)
+(39471,23837,39390,23749)
+(34078,17435,33987,17433)
+(9133,4544,9041,4509)
+(47274,29126,47242,29060)
+(6404,28488,6403,28475)
+(48894,49751,48846,49694)
+(17324,43023,17301,42972)
+(15599,8433,15557,8386)
+(48575,10202,48488,10175)
+(27638,24428,27608,24378)
+(45277,47456,45240,47422)
+(26482,46607,26482,46570)
+(41400,33898,41397,33802)
+(49853,18504,49848,18503)
+(11528,25165,11476,25080)
+(49902,41752,49818,41746)
+(1956,47506,1922,47424)
+(21834,22058,21802,21964)
+(19414,21842,19386,21822)
+(34801,13722,34744,13681)
+(13924,29243,13835,29160)
+(47749,21986,47664,21894)
+(47051,39582,46974,39489)
+(31287,49923,31236,49913)
+(47429,8625,47337,8585)
+(46987,44364,46901,44277)
+(16158,27510,16099,27467)
+(41184,6400,41148,6317)
+(1847,42471,1829,42426)
+(14409,48602,14320,48555)
+(38137,42951,38045,42918)
+(42875,2312,42832,2243)
+(27242,30617,27181,30535)
+(24882,44559,24812,44548)
+(22021,1596,22015,1581)
+(24300,1523,24250,1443)
+(43946,35909,43869,35868)
+(816,15988,776,15967)
+(25243,9401,25237,9332)
+(27967,25958,27928,25949)
+(6575,33949,6484,33900)
+(44812,35980,44800,35913)
+(37577,13064,37495,13019)
+(30891,29967,30814,29884)
+(15829,28836,15753,28807)
+(11128,34180,11126,34117)
+(9834,12537,9801,12508)
+(4899,29069,4809,29024)
+(29370,38459,29276,38382)
+(40743,46653,40647,46559)
+(9618,2723,9578,2631)
+(32542,26837,32515,26769)
+(5625,13409,5576,13355)
+(47490,19229,47472,19203)
+(48118,40275,48063,40203)
+(19245,20549,19227,20546)
+(25312,22243,25280,22164)
+(18797,28934,18723,28881)
+(31609,49393,31512,49366)
+(26183,32888,26135,32824)
+(46198,26153,46180,26149)
+(45383,16904,45353,16888)
+(7132,11408,7091,11338)
+(48262,43227,48236,43159)
+(31722,12861,31675,12810)
+(41695,48924,41691,48921)
+(48318,12877,48287,12802)
+(12069,32241,11978,32231)
+(8395,2694,8380,2661)
+(19552,34590,19550,34497)
+(12203,26166,12187,26143)
+(35745,9571,35654,9542)
+(22384,22535,22352,22439)
+(21459,28189,21360,28189)
+(7418,7203,7343,7182)
+(39497,48412,39413,48318)
+(1058,11132,979,11051)
+(45623,31417,45548,31381)
+(23887,31921,23876,31891)
+(7797,1244,7785,1155)
+(23679,43650,23594,43644)
+(21891,30561,21833,30485)
+(4069,6870,4019,6785)
+(5134,25117,5103,25034)
+(36101,41895,36085,41810)
+(39617,39211,39544,39191)
+(37437,6604,37434,6585)
+(7749,32601,7740,32515)
+(26203,34991,26159,34946)
+(31856,39006,31783,39003)
+(45828,24767,45788,24723)
+(49836,35965,49757,35871)
+(44113,49024,44033,48995)
+(38237,22326,38187,22253)
+(45235,19087,45190,19005)
+(1588,45285,1520,45254)
+(46628,8701,46552,8665)
+(47707,18258,47668,18250)
+(9377,26162,9325,26079)
+(28331,16766,28302,16731)
+(15792,27875,15727,27809)
+(16454,1972,16415,1967)
+(21012,15828,20972,15784)
+(27465,30603,27390,30560)
+(39256,7697,39225,7604)
+(25908,32801,25854,32770)
+(25215,40109,25201,40106)
+(23280,4613,23190,4596)
+(32440,30879,32405,30807)
+(49156,4224,49126,4126)
+(20005,40423,19911,40370)
+(20978,8226,20930,8170)
+(32127,22611,32126,22579)
+(21764,26509,21701,26455)
+(32923,2834,32914,2830)
+(7499,25331,7426,25300)
+(6163,36942,6107,36908)
+(41118,14583,41034,14486)
+(21211,33369,21208,33331)
+(7899,27682,7853,27603)
+(16546,48436,16535,48400)
+(24898,40195,24855,40174)
+(43029,982,43004,952)
+(26266,7962,26252,7950)
+(11308,44367,11210,44322)
+(8902,28402,8808,28334)
+(11671,19619,11665,19549)
+(47202,23593,47153,23505)
+(21981,40220,21905,40160)
+(46721,2514,46687,2471)
+(3450,33839,3424,33811)
+(41854,45864,41762,45792)
+(40183,47816,40114,47742)
+(26119,33910,26077,33816)
+(3430,16518,3365,16500)
+(40063,32176,40005,32166)
+(38702,15253,38679,15187)
+(17719,12291,17658,12257)
+(46131,30669,46068,30587)
+(42738,10952,42731,10907)
+(8721,45155,8650,45076)
+(45317,26123,45244,26113)
+(42694,11561,42614,11490)
+(10043,12479,10009,12391)
+(27584,2345,27578,2257)
+(30889,8253,30866,8167)
+(5176,48928,5107,48838)
+(9781,21023,9745,20976)
+(32430,27908,32404,27859)
+(3984,7391,3973,7352)
+(18904,8094,18842,8091)
+(20573,5508,20482,5496)
+(7806,44368,7753,44297)
+(18875,41452,18817,41376)
+(6632,12142,6566,12079)
+(33066,17865,33055,17854)
+(45726,19628,45714,19589)
+(26971,18459,26941,18423)
+(26554,23641,26515,23592)
+(45503,1325,45441,1231)
+(11898,20164,11880,20115)
+(27868,22837,27843,22776)
+(34931,8206,34855,8144)
+(42375,33603,42350,33539)
+(3184,8308,3129,8238)
+(26667,15813,26661,15785)
+(5760,49617,5730,49546)
+(794,27001,777,26992)
+(13518,45289,13459,45235)
+(34430,29754,34363,29736)
+(37912,24574,37880,24543)
+(8130,2270,8083,2258)
+(26930,21516,26848,21455)
+(3634,33511,3592,33489)
+(33080,5036,33035,4972)
+(48389,13942,48316,13915)
+(9231,5298,9150,5232)
+(1357,10601,1321,10548)
+(35175,15295,35091,15269)
+(33917,36863,33879,36784)
+(8279,12052,8239,12021)
+(11868,19083,11862,19034)
+(24019,30777,24006,30703)
+(44619,6959,44618,6938)
+(28610,2626,28523,2582)
+(29579,41801,29482,41775)
+(23448,37609,23396,37534)
+(40676,11252,40670,11191)
+(39656,14077,39564,13999)
+(33060,31042,33033,30950)
+(11720,6816,11654,6792)
+(13775,28873,13730,28868)
+(47851,39121,47802,39084)
+(30923,40255,30860,40199)
+(44169,15070,44085,15015)
+(42574,28664,42558,28590)
+(8993,43487,8941,43460)
+(40782,11648,40763,11631)
+(18516,10143,18423,10137)
+(39068,551,39005,491)
+(39672,12000,39575,11913)
+(18508,37761,18464,37712)
+(19083,35318,19079,35280)
+(30286,13736,30222,13672)
+(7223,9164,7132,9069)
+(20764,29286,20700,29210)
+(5733,8063,5699,8058)
+(8566,43873,8549,43797)
+(22126,27444,22062,27366)
+(15105,8717,15078,8660)
+(43987,33145,43940,33083)
+(46833,38652,46755,38612)
+(47768,27202,47681,27169)
+(22792,1183,22731,1152)
+(25650,43310,25562,43247)
+(37084,20116,37045,20057)
+(47461,32556,47423,32555)
+(41225,18124,41215,18117)
+(17623,25218,17553,25158)
+(13770,21703,13770,21700)
+(48958,35441,48870,35388)
+(2976,1808,2892,1802)
+(45118,22318,45049,22224)
+(42287,26616,42281,26560)
+(25525,6327,25468,6244)
+(40756,31634,40713,31568)
+(23105,26565,23078,26565)
+(48268,39862,48265,39827)
+(41656,26254,41567,26243)
+(28062,17920,28045,17825)
+(6443,17321,6402,17238)
+(10191,45466,10151,45447)
+(18097,39706,18043,39649)
+(37592,3244,37569,3197)
+(29809,5978,29762,5950)
+(12145,11251,12130,11202)
+(37507,42999,37446,42956)
+(10820,2866,10782,2830)
+(36440,42904,36421,42832)
+(38370,3386,38279,3311)
+(9345,17279,9313,17197)
+(20477,14864,20395,14807)
+(37147,37769,37110,37729)
+(15325,36135,15284,36053)
+(29034,32897,29009,32854)
+(2116,22274,2037,22216)
+(15078,38330,15048,38251)
+(7968,33600,7914,33573)
+(832,23851,770,23786)
+(38669,4348,38594,4344)
+(8521,48573,8425,48564)
+(1060,43320,969,43289)
+(26170,10150,26144,10069)
+(32324,8539,32285,8506)
+(13121,18044,13109,18021)
+(1597,9383,1594,9367)
+(49539,35164,49505,35065)
+(39464,10295,39409,10261)
+(8921,37898,8825,37803)
+(31171,47076,31093,47039)
+(7178,41397,7108,41304)
+(16240,34832,16162,34761)
+(2829,20119,2782,20091)
+(45854,21265,45810,21250)
+(6382,12106,6315,12030)
+(22301,46291,22291,46274)
+(34142,14181,34078,14158)
+(11258,29748,11198,29742)
+(37450,6943,37398,6882)
+(41675,27207,41643,27130)
+(13578,49562,13573,49479)
+(37132,37397,37081,37301)
+(49404,37193,49332,37170)
+(33536,31809,33444,31735)
+(45990,42751,45893,42708)
+(38852,20510,38802,20509)
+(27453,15836,27391,15802)
+(9347,29004,9284,28946)
+(44871,27727,44778,27668)
+(14978,19646,14970,19644)
+(23243,47091,23166,47080)
+(45204,21431,45167,21370)
+(14082,22316,14078,22235)
+(42778,22694,42744,22606)
+(4834,25241,4760,25196)
+(20497,18110,20494,18038)
+(45738,35524,45706,35496)
+(21575,5151,21493,5092)
+(2194,10052,2172,9960)
+(47735,24472,47682,24460)
+(46740,35700,46695,35609)
+(24647,42807,24568,42779)
+(18000,30576,17975,30506)
+(48638,46630,48544,46628)
+(48508,33600,48477,33578)
+(38703,45408,38670,45313)
+(21712,15015,21625,14956)
+(5840,42007,5768,41992)
+(44011,11138,43953,11117)
+(3899,33262,3897,33238)
+(30142,23967,30096,23927)
+(36950,13226,36908,13141)
+(13130,26915,13071,26873)
+(38576,35408,38539,35392)
+(16776,46244,16700,46176)
+(38251,25969,38168,25948)
+(3512,32256,3417,32242)
+(31923,31225,31832,31197)
+(5144,4969,5124,4937)
+(34499,46164,34430,46162)
+(39432,31907,39388,31828)
+(17316,24606,17221,24533)
+(20751,49352,20709,49323)
+(41673,30418,41623,30377)
+(29026,24400,28971,24345)
+(21929,30617,21894,30598)
+(35539,12421,35536,12355)
+(24938,45583,24870,45525)
+(27442,33090,27353,33064)
+(23949,12046,23949,12036)
+(11399,377,11360,294)
+(47099,9989,47023,9942)
+(641,33118,639,33084)
+(13687,41308,13682,41290)
+(3682,17727,3645,17660)
+(13262,19396,13185,19357)
+(18791,389,18774,366)
+(12489,45384,12403,45369)
+(12065,6364,12015,6325)
+(32705,23886,32619,23827)
+(7004,37333,6911,37240)
+(28594,38078,28530,38050)
+(5805,21797,5710,21701)
+(41145,18905,41058,18873)
+(35599,10002,35591,9956)
+(5387,39087,5326,38994)
+(11703,14003,11671,13912)
+(4093,10472,4091,10470)
+(14110,49740,14063,49695)
+(4170,470,4097,463)
+(22219,17296,22164,17221)
+(2505,20879,2446,20842)
+(47235,24744,47151,24667)
+(30035,23234,30013,23197)
+(3489,11659,3461,11607)
+(38435,46322,38429,46230)
+(12315,32880,12277,32854)
+(33350,35297,33317,35263)
+(18845,37671,18836,37589)
+(24855,23554,24783,23520)
+(48251,44461,48188,44408)
+(17695,43353,17605,43286)
+(4964,21292,4893,21270)
+(33919,29907,33852,29878)
+(29139,40010,29084,39957)
+(41611,37750,41572,37741)
+(41773,34717,41682,34700)
+(8225,7424,8221,7363)
+(1785,28248,1771,28219)
+(21553,36307,21505,36257)
+(7552,18199,7527,18119)
+(14410,30977,14349,30944)
+(20940,49142,20901,49069)
+(36892,5522,36810,5478)
+(40192,20926,40179,20926)
+(44702,15182,44641,15117)
+(43431,4921,43337,4827)
+(41129,21654,41084,21642)
+(6205,42785,6113,42722)
+(23714,10224,23666,10205)
+(9318,35175,9274,35139)
+(40698,12676,40618,12627)
+(49954,1340,49905,1294)
+(32774,33062,32763,33062)
+(4336,22183,4241,22157)
+(10241,47657,10151,47592)
+(6746,16718,6666,16634)
+(26842,49694,26839,49680)
+(34870,47437,34820,47347)
+(26365,22266,26326,22183)
+(39859,932,39829,840)
+(33995,10888,33902,10793)
+(32972,22342,32951,22340)
+(19951,10161,19932,10111)
+(26779,45188,26745,45151)
+(11235,13593,11184,13589)
+(27334,20968,27288,20953)
+(9586,43102,9488,43085)
+(43935,49759,43925,49680)
+(10548,37032,10474,36955)
+(9326,14927,9295,14848)
+(41340,11312,41311,11303)
+(6500,44553,6454,44515)
+(8198,26841,8104,26749)
+(47761,34183,47702,34140)
+(43637,17912,43577,17910)
+(17623,11138,17590,11122)
+(48122,13132,48077,13060)
+(27911,39796,27908,39777)
+(1108,7918,1080,7832)
+(18776,24329,18699,24326)
+(1171,37901,1075,37871)
+(38437,33948,38364,33907)
+(1913,11593,1817,11533)
+(22684,266,22656,181)
+(13299,17075,13241,17074)
+(6924,30196,6851,30113)
+(4367,13150,4298,13053)
+(37381,6101,37380,6046)
+(10307,28383,10270,28349)
+(12283,8636,12256,8610)
+(20230,32775,20144,32723)
+(32942,12812,32905,12714)
+(46140,7138,46140,7047)
+(37235,29436,37161,29425)
+(42486,25454,42478,25444)
+(47860,46973,47842,46961)
+(41760,21026,41662,20955)
+(29663,20088,29566,20026)
+(19167,33241,19101,33235)
+(12306,37845,12301,37803)
+(11288,873,11203,857)
+(30309,5120,30282,5060)
+(46927,19737,46856,19687)
+(16664,20052,16649,19989)
+(7330,8675,7296,8613)
+(45067,45724,44991,45631)
+(45317,10862,45218,10842)
+(15012,47009,14998,46956)
+(47882,10146,47813,10099)
+(31571,46215,31511,46148)
+(32257,2619,32187,2531)
+(38924,41305,38872,41285)
+(49981,34876,49898,34786)
+(30501,35099,30418,35011)
+(45862,41438,45854,41434)
+(38448,31878,38391,31822)
+(8278,43463,8274,43378)
+(5883,30629,5878,30564)
+(49501,40346,49447,40275)
+(31651,43116,31560,43106)
+(44244,32940,44244,32926)
+(17941,18079,17938,18035)
+(9518,32524,9470,32511)
+(30707,43469,30686,43457)
+(3284,46542,3187,46477)
+(43423,29642,43393,29602)
+(19940,16825,19877,16736)
+(26194,47446,26194,47407)
+(30386,24675,30333,24652)
+(42707,44466,42688,44456)
+(43395,18525,43320,18467)
+(28346,32259,28276,32196)
+(45106,40786,45026,40767)
+(36734,20414,36722,20363)
+(37140,11569,37099,11475)
+(8967,6409,8882,6341)
+(31036,27923,30993,27890)
+(22442,47682,22347,47663)
+(32511,24029,32482,23970)
+(22593,34444,22519,34399)
+(41534,15495,41518,15455)
+(35862,19997,35818,19928)
+(31419,8323,31404,8285)
+(31036,19023,30978,19000)
+(46900,15192,46891,15102)
+(12774,9651,12765,9604)
+(49985,6436,49927,6338)
+(7184,47344,7089,47285)
+(12792,45021,12740,45011)
+(15019,27192,14940,27096)
+(35415,23106,35381,23095)
+(42129,14283,42095,14245)
+(29375,45807,29347,45743)
+(21763,24916,21700,24889)
+(47656,8794,47579,8774)
+(6139,49571,6059,49472)
+(44492,45607,44483,45532)
+(22699,4301,22628,4240)
+(27407,24241,27335,24158)
+(38424,34460,38403,34458)
+(46572,48456,46554,48402)
+(39676,29056,39643,28981)
+(4202,33076,4107,33010)
+(32499,10592,32482,10575)
+(22504,45417,22459,45378)
+(49619,40322,49619,40268)
+(14463,9305,14426,9224)
+(10070,20300,10035,20211)
+(35060,28561,34965,28553)
+(23970,47522,23887,47428)
+(46803,19155,46790,19131)
+(46151,49848,46058,49830)
+(45266,40766,45209,40738)
+(31041,32195,31007,32110)
+(41401,17245,41334,17224)
+(37445,654,37435,602)
+(45568,31904,45508,31857)
+(29326,7923,29285,7896)
+(27078,34643,27027,34606)
+(34492,43443,34437,43345)
+(34109,4307,34083,4265)
+(2755,45325,2727,45312)
+(12571,24218,12536,24195)
+(41224,2454,41149,2445)
+(711,34828,655,34788)
+(9104,18865,9036,18850)
+(3508,26816,3456,26771)
+(20159,16212,20116,16160)
+(36871,7425,36777,7421)
+(2751,45244,2734,45222)
+(35867,28071,35769,28052)
+(46878,35730,46850,35725)
+(20610,35086,20513,35037)
+(3903,32612,3887,32517)
+(9330,40226,9289,40169)
+(6338,28242,6329,28184)
+(35668,18344,35606,18304)
+(29892,48927,29878,48879)
+(26999,646,26932,612)
+(36377,38898,36338,38847)
+(40289,31459,40236,31436)
+(30377,1164,30306,1069)
+(7642,12183,7590,12112)
+(40325,1716,40296,1662)
+(36412,38787,36318,38691)
+(3967,33268,3923,33261)
+(33914,40774,33873,40763)
+(45978,41431,45963,41332)
+(39195,12546,39120,12520)
+(29962,30878,29941,30846)
+(9365,10732,9310,10726)
+(28801,23943,28740,23885)
+(28934,38858,28928,38807)
+(22126,45897,22068,45803)
+(2923,33832,2918,33751)
+(25116,2276,25083,2272)
+(31174,14546,31144,14460)
+(11728,9072,11658,9004)
+(19804,49195,19730,49125)
+(23090,28826,23010,28787)
+(33989,27553,33947,27486)
+(39702,47613,39641,47553)
+(31397,3607,31304,3519)
+(5835,9262,5791,9226)
+(40112,37022,40038,36926)
+(12346,29356,12282,29344)
+(28503,9623,28469,9591)
+(38449,43143,38378,43066)
+(36950,37311,36905,37265)
+(34824,5729,34818,5706)
+(9288,26969,9225,26900)
+(2535,42176,2478,42159)
+(29098,49051,29085,49031)
+(44759,33326,44727,33230)
+(42849,2970,42821,2919)
+(46014,27193,45985,27151)
+(14506,13713,14417,13626)
+(19342,44905,19332,44895)
+(38178,37003,38147,36925)
+(29179,27310,29084,27288)
+(42713,10158,42671,10060)
+(43336,38389,43290,38326)
+(41260,34410,41245,34327)
+(27907,2695,27830,2596)
+(16309,44972,16222,44966)
+(6230,22262,6214,22249)
+(9266,39458,9175,39447)
+(33120,33548,33087,33538)
+(43659,11416,43599,11375)
+(49707,39258,49702,39159)
+(23520,22140,23486,22072)
+(24736,46502,24668,46412)
+(7826,16851,7730,16807)
+(39114,6048,39056,5965)
+(11859,8753,11764,8701)
+(42254,48367,42240,48328)
+(26136,49185,26056,49175)
+(38395,11209,38334,11137)
+(33249,9425,33209,9348)
+(22131,38502,22112,38460)
+(5306,24344,5267,24268)
+(30292,1198,30233,1149)
+(9903,10896,9850,10806)
+(25568,22911,25487,22868)
+(22048,43391,22043,43362)
+(20852,25827,20851,25766)
+(35204,17119,35114,17093)
+(5575,43431,5554,43410)
+(17727,13623,17678,13560)
+(14721,29520,14709,29461)
+(40317,42220,40267,42166)
+(31435,31012,31386,30931)
+(40655,10103,40645,10006)
+(35783,17802,35773,17763)
+(34874,10210,34856,10200)
+(3694,14279,3610,14239)
+(27854,5493,27799,5433)
+(34913,7234,34894,7220)
+(15758,26445,15738,26421)
+(23710,7272,23705,7270)
+(33679,13468,33628,13415)
+(31271,40495,31178,40461)
+(759,187,662,163)
+(14419,40434,14402,40381)
+(45879,42933,45814,42872)
+(167,17214,92,17184)
+(9964,12210,9958,12195)
+(35834,46257,35817,46211)
+(26077,5629,25978,5621)
+(46177,44640,46082,44544)
+(44780,28753,44707,28692)
+(35491,24729,35425,24690)
+(33914,34190,33914,34131)
+(17709,33253,17668,33227)
+(45516,11888,45423,11848)
+(24497,24752,24411,24710)
+(30333,5952,30331,5886)
+(444,12587,430,12497)
+(7592,22353,7541,22287)
+(13387,37414,13329,37318)
+(21504,35227,21449,35210)
+(18533,12909,18438,12848)
+(41049,27148,41048,27088)
+(18205,12222,18151,12140)
+(18026,5164,18026,5156)
+(34104,29862,34006,29815)
+(18520,49686,18454,49602)
+(37000,41493,36920,41424)
+(43025,25711,42986,25687)
+(38620,47018,38535,46934)
+(24119,36813,24023,36739)
+(48887,26359,48879,26302)
+(47827,14625,47810,14609)
+(10792,30746,10776,30716)
+(30384,40672,30318,40582)
+(48417,22790,48358,22746)
+(14854,5819,14785,5798)
+(19142,44414,19085,44406)
+(31179,27081,31145,27005)
+(19692,8711,19659,8642)
+(39689,14082,39603,14051)
+(11181,39091,11119,39002)
+(46015,23374,45936,23328)
+(12517,49702,12427,49690)
+(21926,21137,21841,21111)
+(31956,12509,31870,12494)
+(5895,2030,5851,2020)
+(27094,5447,27014,5377)
+(35781,8717,35780,8618)
+(14012,12023,13972,12015)
+(1702,12442,1696,12419)
+(28549,5251,28462,5248)
+(26441,21007,26360,20925)
+(49820,7990,49771,7967)
+(26424,29698,26339,29693)
+(35146,6820,35071,6817)
+(15438,18788,15435,18729)
+(47115,5235,47096,5143)
+(33982,9002,33915,8925)
+(14206,37041,14174,36955)
+(24300,36616,24232,36613)
+(44658,1788,44580,1769)
+(31539,43550,31463,43464)
+(16722,9673,16633,9652)
+(44813,20573,44733,20544)
+(42114,32559,42040,32552)
+(41561,36244,41477,36241)
+(39589,33796,39548,33716)
+(20365,26770,20329,26709)
+(28511,208,28479,114)
+(10010,25524,9930,25508)
+(1549,45666,1512,45621)
+(16193,1927,16166,1869)
+(34486,11500,34421,11401)
+(14048,37944,13994,37901)
+(21692,9594,21617,9496)
+(2568,37899,2557,37811)
+(4360,24503,4278,24443)
+(50027,49230,49951,49214)
+(44849,14867,44836,14813)
+(16695,34896,16683,34840)
+(12600,35217,12593,35129)
+(23113,24009,23030,23962)
+(49907,30225,49810,30158)
+(18026,25208,17970,25208)
+(49711,39844,49651,39790)
+(5427,42682,5357,42637)
+(23901,14221,23802,14184)
+(15470,12185,15376,12163)
+(47302,34023,47292,34001)
+(24336,17418,24315,17393)
+(13948,17043,13903,16970)
+(8555,8986,8530,8953)
+(48830,6038,48743,5986)
+(48720,40687,48623,40610)
+(21161,30970,21146,30896)
+(9507,36316,9411,36261)
+(36643,18136,36614,18106)
+(1858,7457,1851,7402)
+(24452,44306,24372,44252)
+(3292,807,3205,806)
+(6845,30694,6792,30627)
+(21333,25786,21237,25751)
+(23008,22574,22999,22511)
+(8790,8893,8772,8806)
+(43333,47968,43264,47900)
+(5377,24103,5302,24076)
+(18410,23993,18329,23907)
+(24752,19126,24713,19069)
+(49772,11378,49696,11293)
+(3468,12920,3396,12873)
+(1746,40342,1736,40333)
+(49187,29737,49139,29681)
+(27657,44952,27581,44917)
+(35407,30177,35345,30151)
+(4071,40568,4058,40544)
+(25998,30513,25965,30452)
+(8195,45403,8097,45310)
+(8276,41689,8183,41670)
+(48435,28550,48355,28455)
+(8139,25449,8136,25380)
+(20302,25574,20297,25531)
+(22055,46659,22034,46567)
+(3531,49962,3463,49934)
+(46828,46938,46739,46902)
+(42294,786,42212,739)
+(8779,3292,8761,3275)
+(48146,46170,48082,46151)
+(21571,10000,21531,9919)
+(35526,26029,35450,25945)
+(38893,22225,38865,22197)
+(22189,37520,22132,37497)
+(810,43261,751,43198)
+(10352,39144,10290,39093)
+(8740,35435,8720,35432)
+(31657,13551,31583,13484)
+(39803,4019,39755,4014)
+(46353,7853,46312,7824)
+(30078,48975,30021,48970)
+(2847,32036,2819,31966)
+(25250,10147,25165,10140)
+(15643,38953,15585,38947)
+(40792,29798,40731,29731)
+(43249,26858,43215,26835)
+(47229,2199,47201,2134)
+(10052,23601,9958,23570)
+(38981,21615,38892,21604)
+(3651,45004,3570,44917)
+(21503,8261,21409,8166)
+(13518,34201,13465,34105)
+(13899,25117,13836,25114)
+(18327,17403,18301,17349)
+(19503,13648,19483,13607)
+(3554,19487,3529,19466)
+(41102,43355,41070,43314)
+(4663,45858,4583,45765)
+(3971,3023,3931,2975)
+(37124,7061,37080,6993)
+(48530,47172,48459,47160)
+(14575,29843,14509,29750)
+(43443,23124,43357,23038)
+(8864,48290,8857,48263)
+(41597,39852,41577,39791)
+(35610,33392,35556,33353)
+(36415,17906,36328,17846)
+(24919,43933,24839,43883)
+(7457,14056,7395,14051)
+(43851,4090,43801,4080)
+(43567,18468,43471,18388)
+(16711,6084,16652,6055)
+(45888,45934,45846,45880)
+(45630,9313,45585,9248)
+(27119,25969,27094,25884)
+(36155,11420,36120,11405)
+(41880,47111,41808,47049)
+(17554,20379,17482,20374)
+(38848,5936,38763,5869)
+(28324,31019,28276,30944)
+(43257,17152,43176,17091)
+(42717,24613,42691,24527)
+(16786,41486,16763,41403)
+(19259,28780,19160,28711)
+(25843,28265,25760,28171)
+(48645,34816,48546,34755)
+(7004,49289,6976,49236)
+(30261,21833,30181,21776)
+(5290,46672,5219,46661)
+(21237,31901,21188,31849)
+(23340,38537,23253,38472)
+(17269,3682,17183,3586)
+(48200,15377,48110,15369)
+(16546,22195,16477,22142)
+(21436,8460,21378,8449)
+(46598,17235,46577,17138)
+(30212,36184,30152,36092)
+(18037,155,17941,109)
+(4945,29201,4933,29184)
+(32835,18782,32770,18750)
+(34160,33104,34120,33007)
+(5151,26989,5149,26909)
+(1801,15549,1710,15461)
+(48988,34819,48951,34764)
+(20904,32547,20856,32497)
+(32654,35183,32606,35144)
+(14336,11763,14328,11712)
+(30546,23808,30463,23773)
+(6813,21006,6781,20924)
+(14199,22030,14185,21934)
+(3783,14709,3747,14658)
+(49428,47052,49422,46973)
+(29551,27682,29470,27654)
+(29170,37260,29151,37181)
+(48924,24689,48894,24680)
+(48497,34052,48453,33966)
+(21263,8203,21242,8176)
+(46537,3797,46462,3735)
+(18406,14579,18393,14563)
+(11583,16529,11536,16471)
+(10564,46257,10478,46228)
+(49769,34513,49761,34458)
+(9202,6482,9138,6391)
+(40387,37411,40357,37360)
+(11966,11802,11888,11751)
+(15551,47438,15486,47406)
+(12017,43288,11969,43230)
+(9717,22574,9701,22495)
+(35083,49443,35075,49355)
+(33857,9320,33813,9269)
+(32106,10581,32012,10560)
+(14345,12485,14273,12424)
+(24187,46416,24175,46402)
+(43854,42159,43808,42129)
+(35399,40707,35359,40646)
+(29585,25576,29493,25556)
+(24919,7829,24911,7753)
+(17049,48390,17022,48304)
+(25224,35012,25217,34922)
+(47397,20853,47346,20779)
+(17221,16558,17181,16516)
+(8669,16491,8645,16486)
+(23502,44241,23484,44164)
+(36169,37046,36072,37010)
+(44775,32394,44763,32357)
+(30685,36871,30662,36792)
+(21783,47642,21714,47630)
+(34847,27467,34761,27372)
+(43925,49912,43888,49878)
+(16455,27861,16364,27813)
+(38406,18310,38329,18309)
+(5408,9461,5319,9426)
+(41856,36900,41784,36854)
+(23723,4460,23646,4448)
+(18454,40138,18430,40046)
+(17505,36822,17418,36763)
+(36686,33534,36641,33476)
+(11347,9454,11289,9436)
+(27816,34752,27745,34736)
+(44213,8559,44162,8461)
+(45359,26789,45315,26776)
+(31249,19475,31224,19421)
+(25917,44239,25819,44149)
+(47313,40691,47264,40685)
+(40577,33848,40513,33794)
+(9606,45253,9582,45174)
+(30005,24521,29910,24496)
+(49332,35375,49309,35299)
+(12164,33871,12075,33820)
+(19598,43327,19593,43314)
+(3818,28584,3815,28504)
+(35579,8611,35541,8604)
+(8811,20986,8750,20954)
+(16139,44777,16128,44686)
+(35550,41501,35534,41458)
+(43180,11927,43109,11891)
+(45798,8465,45711,8460)
+(18196,6886,18126,6845)
+(1774,32167,1701,32073)
+(7030,40790,7029,40711)
+(11676,23009,11665,22915)
+(33990,22561,33953,22474)
+(30366,9447,30284,9353)
+(37626,32913,37596,32853)
+(7730,42561,7665,42470)
+(49347,8403,49315,8387)
+(6874,3499,6812,3458)
+(44189,16999,44169,16964)
+(6312,30167,6231,30083)
+(18932,6611,18909,6518)
+(32262,13076,32223,13057)
+(45989,249,45910,222)
+(42710,855,42692,796)
+(25562,9849,25535,9802)
+(13348,46719,13260,46689)
+(30022,42196,30005,42160)
+(22263,45954,22243,45950)
+(18918,18890,18820,18795)
+(31918,12003,31852,11989)
+(12252,39453,12211,39398)
+(40208,9789,40194,9759)
+(35943,21767,35914,21693)
+(18439,10706,18383,10618)
+(2803,18999,2778,18925)
+(14953,27444,14875,27397)
+(12587,22025,12545,21928)
+(33930,21090,33918,21009)
+(10444,2606,10407,2553)
+(28700,29782,28665,29703)
+(1402,13497,1397,13465)
+(24155,3075,24083,3062)
+(38378,1864,38339,1849)
+(29261,49910,29247,49818)
+(38139,37073,38098,37057)
+(24468,41130,24418,41053)
+(9989,1015,9959,939)
+(47001,33561,46994,33518)
+(47058,16030,46983,16012)
+(35509,1814,35426,1748)
+(3630,48019,3597,47923)
+(47781,12986,47741,12947)
+(16364,9908,16356,9882)
+(17290,41508,17287,41410)
+(42423,26477,42349,26434)
+(10039,920,9952,833)
+(16851,21338,16846,21314)
+(23104,7700,23062,7688)
+(5619,2079,5611,2075)
+(31471,49632,31375,49549)
+(25793,12526,25783,12456)
+(3935,29528,3866,29513)
+(5957,1646,5947,1595)
+(2467,22376,2429,22349)
+(43715,32673,43664,32595)
+(6726,13093,6636,12994)
+(31477,18347,31421,18299)
+(34232,36635,34200,36552)
+(49061,14516,49008,14442)
+(43996,6129,43955,6074)
+(7728,33802,7670,33703)
+(6131,36766,6053,36749)
+(35791,16361,35696,16329)
+(45759,8935,45675,8886)
+(43634,2029,43537,1940)
+(4916,32233,4844,32181)
+(46701,23508,46623,23477)
+(29590,4893,29552,4871)
+(38647,4423,38574,4396)
+(7593,25845,7497,25751)
+(8510,43552,8432,43492)
+(18791,39181,18730,39162)
+(7462,2956,7454,2858)
+(1394,26795,1392,26780)
+(16707,21993,16609,21932)
+(26838,10866,26803,10836)
+(31642,29842,31585,29760)
+(21891,3502,21863,3406)
+(13258,587,13250,507)
+(6072,47397,6021,47369)
+(16605,49730,16579,49659)
+(42830,40981,42791,40981)
+(12975,3706,12913,3637)
+(30925,21660,30826,21649)
+(1455,14229,1410,14156)
+(17583,16486,17562,16474)
+(33377,3387,33333,3381)
+(784,6177,750,6095)
+(22111,44110,22106,44013)
+(1444,403,1346,344)
+(4010,46220,3982,46212)
+(17932,8150,17861,8127)
+(38685,31466,38636,31416)
+(14257,11549,14242,11522)
+(14990,15217,14904,15211)
+(21395,21533,21307,21520)
+(31948,33725,31885,33694)
+(433,49033,390,48961)
+(45205,609,45173,523)
+(25065,35494,25003,35455)
+(33265,6677,33224,6611)
+(18179,22345,18133,22256)
+(3916,13759,3820,13732)
+(1696,13478,1604,13436)
+(47203,25980,47130,25907)
+(24913,13361,24868,13268)
+(13824,40177,13792,40130)
+(25671,13555,25585,13494)
+(20133,37769,20105,37679)
+(26368,16734,26288,16726)
+(30545,35438,30458,35376)
+(48816,22926,48812,22831)
+(48807,31389,48739,31330)
+(11003,10859,10950,10765)
+(17288,8570,17247,8485)
+(38377,31415,38331,31379)
+(19085,23425,19059,23326)
+(40059,17068,40052,17006)
+(18811,13493,18734,13394)
+(36319,17197,36225,17181)
+(14939,38780,14863,38714)
+(49539,17656,49479,17629)
+(42530,45951,42466,45854)
+(27318,26654,27233,26610)
+(49980,35004,49937,34963)
+(18326,32558,18322,32502)
+(45951,28555,45896,28481)
+(12104,33531,12014,33501)
+(22311,41113,22215,41066)
+(25073,18721,25047,18656)
+(14524,13486,14510,13390)
+(40040,36688,40000,36599)
+(21594,11473,21563,11436)
+(44031,22274,43938,22187)
+(729,30683,668,30601)
+(14114,20873,14102,20803)
+(28239,41377,28222,41308)
+(26404,11922,26317,11843)
+(41660,34586,41585,34501)
+(21128,2384,21101,2368)
+(30209,16952,30156,16858)
+(39078,24963,39045,24898)
+(5598,1348,5499,1294)
+(38474,7436,38450,7364)
+(15117,45734,15024,45693)
+(23909,39853,23888,39780)
+(24292,30183,24282,30148)
+(48871,17661,48868,17637)
+(918,18752,847,18708)
+(43615,16162,43606,16104)
+(33763,47410,33751,47409)
+(4798,6485,4773,6388)
+(18524,41539,18433,41518)
+(47745,42449,47651,42364)
+(38936,21237,38864,21204)
+(5251,3516,5194,3475)
+(22269,36269,22183,36228)
+(18736,40983,18685,40947)
+(38393,15444,38356,15363)
+(38134,29898,38103,29862)
+(37789,39557,37732,39474)
+(31906,23005,31838,23003)
+(10647,40094,10560,40040)
+(9914,41547,9867,41545)
+(44221,443,44125,433)
+(41479,10936,41381,10847)
+(42586,6301,42563,6235)
+(2504,17588,2449,17554)
+(7045,18782,7028,18764)
+(41840,32018,41768,31938)
+(38416,17158,38330,17060)
+(8605,39015,8605,38933)
+(5764,43548,5719,43496)
+(20789,29902,20696,29843)
+(36104,47896,36079,47816)
+(31736,13834,31722,13832)
+(32617,19701,32597,19684)
+(1671,18997,1622,18945)
+(36007,26545,36005,26535)
+(31864,17494,31820,17455)
+(27346,28388,27303,28289)
+(8191,9653,8133,9589)
+(7501,21616,7405,21536)
+(35450,9580,35368,9563)
+(29281,37276,29247,37255)
+(6225,17192,6200,17135)
+(43689,8119,43670,8028)
+(41917,49601,41835,49563)
+(44295,13116,44205,13078)
+(22721,44772,22667,44748)
+(32640,11107,32636,11050)
+(20639,28851,20613,28839)
+(32479,10159,32446,10061)
+(27251,16978,27196,16959)
+(41401,33148,41339,33074)
+(49001,8538,48989,8444)
+(37958,35843,37874,35802)
+(46969,41229,46903,41138)
+(18541,8876,18541,8870)
+(4080,31634,4061,31627)
+(8097,35240,8040,35152)
+(18470,21414,18463,21412)
+(20914,17897,20838,17869)
+(42688,11681,42666,11641)
+(47525,25005,47443,24907)
+(32439,14438,32397,14400)
+(39667,19626,39622,19542)
+(1212,44525,1169,44516)
+(29766,4433,29668,4401)
+(25847,49657,25813,49605)
+(33859,17356,33827,17263)
+(28989,45953,28904,45854)
+(37211,30830,37113,30819)
+(45220,26382,45219,26340)
+(12312,43250,12234,43246)
+(37775,41504,37762,41421)
+(45889,33499,45822,33411)
+(49461,22601,49369,22553)
+(39857,33844,39816,33824)
+(46102,15822,46030,15778)
+(46605,31239,46598,31170)
+(23925,5856,23862,5808)
+(15459,4262,15407,4241)
+(12019,4907,12015,4818)
+(38258,17973,38229,17923)
+(40575,29566,40477,29521)
+(29715,45919,29697,45891)
+(11694,9510,11670,9490)
+(7053,44257,7012,44231)
+(16465,8603,16391,8505)
+(29170,15592,29098,15527)
+(20400,37354,20345,37328)
+(5281,10265,5252,10184)
+(6084,48782,6058,48727)
+(11006,6889,10971,6796)
+(16299,19461,16286,19411)
+(13718,29192,13642,29106)
+(3999,2965,3963,2903)
+(18509,12235,18430,12208)
+(49542,38575,49537,38534)
+(15093,41715,15071,41634)
+(6802,8385,6714,8300)
+(15127,17507,15097,17424)
+(36921,3025,36835,2995)
+(32117,24327,32101,24262)
+(27244,24151,27165,24104)
+(36339,42360,36313,42358)
+(47288,46252,47245,46184)
+(37867,6649,37818,6565)
+(14886,22103,14865,22089)
+(39611,17952,39513,17951)
+(37329,31436,37298,31436)
+(5715,39115,5698,39099)
+(13266,7364,13203,7296)
+(16076,10945,16006,10942)
+(7197,41509,7126,41413)
+(14411,40868,14330,40772)
+(12872,33481,12862,33454)
+(17786,19616,17758,19560)
+(1052,37358,996,37311)
+(42825,12643,42762,12625)
+(20007,49858,19921,49778)
+(27155,6355,27072,6257)
+(14117,40208,14022,40155)
+(47280,34069,47279,34028)
+(17551,15803,17482,15763)
+(1725,6673,1676,6649)
+(43984,31128,43961,31105)
+(43772,47042,43731,47038)
+(46901,47317,46817,47228)
+(19877,14179,19837,14168)
+(20691,19989,20675,19935)
+(4011,18914,3963,18817)
+(1023,23378,933,23317)
+(30051,46118,29966,46039)
+(43499,46488,43496,46409)
+(43531,2412,43447,2396)
+(16034,32285,15976,32220)
+(12817,21365,12740,21298)
+(7607,47293,7585,47293)
+(32512,12218,32463,12170)
+(1848,21496,1839,21439)
+(17567,23073,17478,23046)
+(35813,31847,35807,31792)
+(563,30859,540,30842)
+(13145,15488,13063,15433)
+(36754,37479,36731,37411)
+(1125,26069,1057,25997)
+(4539,20676,4519,20618)
+(8476,34721,8409,34681)
+(7794,25691,7727,25656)
+(23842,514,23800,473)
+(47678,41396,47668,41365)
+(6837,25974,6799,25892)
+(13355,11174,13304,11161)
+(37243,25548,37158,25471)
+(12528,30208,12441,30205)
+(14929,1672,14886,1607)
+(27263,49026,27263,49010)
+(15892,21645,15835,21642)
+(29446,48978,29360,48967)
+(41304,9892,41211,9825)
+(37418,49393,37338,49296)
+(41146,32178,41120,32165)
+(28738,13326,28722,13266)
+(14899,36595,14873,36559)
+(1973,31435,1921,31426)
+(19485,17742,19421,17661)
+(33072,20995,32980,20903)
+(47091,30055,47080,30037)
+(45753,12998,45686,12992)
+(11528,7826,11509,7794)
+(21104,13921,21060,13836)
+(16768,15491,16747,15470)
+(13279,20396,13249,20326)
+(4342,49518,4339,49446)
+(20413,15476,20349,15447)
+(45532,5649,45484,5627)
+(18647,27196,18619,27115)
+(1326,17473,1261,17400)
+(47646,19644,47588,19609)
+(35088,1813,35080,1732)
+(38461,34839,38410,34838)
+(34358,11540,34285,11506)
+(26969,7078,26953,6989)
+(12629,40352,12617,40264)
+(33800,7037,33731,6992)
+(24462,13518,24392,13486)
+(33164,47357,33096,47329)
+(15422,18451,15413,18376)
+(19643,12916,19567,12912)
+(40860,42125,40770,42050)
+(49103,29614,49039,29606)
+(36319,35582,36222,35528)
+(8924,36083,8873,36018)
+(49603,44022,49505,44021)
+(7783,40633,7702,40618)
+(25388,49107,25346,49042)
+(28375,38947,28306,38919)
+(47324,22672,47321,22660)
+(2287,8808,2266,8719)
+(44343,16339,44248,16318)
+(2374,28839,2336,28798)
+(22913,40710,22819,40688)
+(47747,684,47658,627)
+(16043,46011,16021,45984)
+(34958,32168,34903,32092)
+(4840,49328,4752,49258)
+(24341,2087,24330,2009)
+(18378,19374,18327,19358)
+(48165,7217,48156,7141)
+(14232,6044,14182,6004)
+(23080,4196,22983,4191)
+(259,1850,175,1820)
+(270,29508,264,29440)
+(45088,11375,45050,11295)
+(29666,39386,29656,39302)
+(8712,8782,8660,8713)
+(15900,6650,15855,6561)
+(28946,28348,28917,28347)
+(32544,25845,32538,25779)
+(44047,6957,43951,6942)
+(36465,588,36382,503)
+(28167,26679,28150,26673)
+(16065,4268,15975,4180)
+(12950,23494,12893,23494)
+(30145,24679,30056,24654)
+(3027,16162,3001,16071)
+(8259,34537,8202,34484)
+(41447,1515,41427,1454)
+(18407,28362,18309,28303)
+(21393,41872,21328,41816)
+(46040,26497,45996,26408)
+(49944,25163,49902,25153)
+(16195,11843,16159,11831)
+(44257,15270,44254,15214)
+(49760,4791,49699,4713)
+(22558,33709,22519,33681)
+(28375,10003,28336,9938)
+(18179,24310,18106,24256)
+(707,30688,664,30669)
+(5851,26118,5822,26037)
+(4266,1292,4221,1217)
+(16516,11331,16432,11248)
+(32374,38277,32313,38245)
+(21939,8015,21927,7952)
+(34322,32051,34242,32003)
+(6262,35977,6260,35953)
+(16717,38594,16622,38498)
+(14564,3433,14535,3425)
+(21078,1000,20994,974)
+(28584,956,28575,868)
+(5538,9962,5465,9870)
+(34183,44102,34175,44085)
+(42507,10289,42441,10288)
+(12671,19936,12594,19920)
+(24835,12179,24770,12173)
+(15664,11538,15598,11494)
+(28892,24446,28821,24350)
+(41654,26720,41570,26632)
+(36583,387,36503,357)
+(10842,34824,10795,34788)
+(11518,42588,11429,42565)
+(12577,40322,12486,40266)
+(2453,4045,2439,3956)
+(31837,33705,31803,33681)
+(24403,27711,24383,27705)
+(4431,2748,4337,2656)
+(3036,2887,3014,2826)
+(37664,16118,37615,16022)
+(8606,18063,8587,18038)
+(24738,25458,24656,25362)
+(45756,34022,45671,33948)
+(34079,15236,33981,15171)
+(9251,22488,9228,22470)
+(25136,2809,25126,2717)
+(5548,47695,5543,47685)
+(13765,40800,13707,40754)
+(25216,30678,25144,30677)
+(22441,17169,22392,17106)
+(1091,4770,1054,4734)
+(36311,50073,36258,49987)
+(22461,33163,22457,33128)
+(35873,28907,35845,28867)
+(42907,15848,42904,15785)
+(6549,24897,6540,24861)
+(21928,37764,21891,37681)
+(21237,41132,21139,41086)
+(12207,24266,12173,24235)
+(40643,49770,40574,49687)
+(32833,35686,32815,35674)
+(14545,18143,14541,18098)
+(33892,42783,33884,42707)
+(33933,8381,33921,8369)
+(12450,19044,12403,19002)
+(10176,45158,10088,45145)
+(35828,12080,35732,12022)
+(28102,13694,28061,13666)
+(49432,31744,49340,31711)
+(16192,37743,16162,37697)
+(46830,867,46756,790)
+(9200,28048,9159,27986)
+(13397,19369,13340,19288)
+(30879,43562,30785,43545)
+(21995,48224,21920,48143)
+(11871,47569,11809,47568)
+(29366,22196,29280,22154)
+(26243,28176,26203,28116)
+(28995,35031,28906,35014)
+(29384,39276,29352,39183)
+(8497,13798,8471,13789)
+(7412,27226,7334,27220)
+(25403,47678,25363,47654)
+(11599,5556,11574,5502)
+(44056,5123,44008,5111)
+(49603,30877,49579,30840)
+(32261,45876,32206,45865)
+(35104,41659,35048,41587)
+(5457,35844,5376,35782)
+(29423,3977,29354,3959)
+(18059,3001,17965,2961)
+(8509,5691,8463,5620)
+(27118,5762,27083,5747)
+(2991,48605,2939,48559)
+(44482,3484,44425,3459)
+(45143,16439,45046,16365)
+(2236,37531,2147,37530)
+(41561,3217,41490,3210)
+(6270,27200,6171,27166)
+(49195,24871,49138,24798)
+(46985,38881,46897,38845)
+(37486,23522,37404,23441)
+(26907,14490,26900,14391)
+(30829,16111,30756,16056)
+(3644,17291,3587,17262)
+(20508,49775,20472,49680)
+(43279,8972,43198,8936)
+(33744,7470,33734,7439)
+(46303,20538,46284,20498)
+(10365,48246,10291,48154)
+(12636,24987,12545,24933)
+(40998,46992,40989,46916)
+(30536,6073,30531,6018)
+(22102,9643,22051,9594)
+(18616,34348,18530,34332)
+(8222,8907,8123,8848)
+(45698,28860,45698,28770)
+(26958,1748,26924,1726)
+(26735,35073,26659,35025)
+(48370,40813,48293,40737)
+(13140,993,13108,934)
+(10588,22893,10528,22883)
+(23645,40789,23567,40698)
+(49548,12374,49546,12329)
+(41135,39626,41100,39602)
+(41374,10856,41328,10769)
+(12234,5765,12146,5674)
+(12832,46941,12764,46917)
+(47886,34532,47851,34500)
+(23777,10549,23735,10495)
+(1291,16913,1194,16873)
+(29239,30554,29202,30500)
+(36485,30007,36454,29924)
+(7067,11320,7045,11229)
+(16939,30482,16904,30462)
+(27423,34386,27379,34303)
+(35170,32021,35155,31979)
+(42570,36477,42474,36457)
+(19695,679,19682,594)
+(47537,39450,47446,39450)
+(19410,22942,19375,22922)
+(34216,40166,34152,40158)
+(37000,24351,36972,24299)
+(24989,1681,24954,1672)
+(54,38679,3,38602)
+(41461,40693,41411,40599)
+(7576,46054,7545,45963)
+(35505,28262,35413,28222)
+(1158,16976,1145,16927)
+(23494,42291,23437,42229)
+(32894,32519,32880,32485)
+(604,13413,509,13401)
+(18396,19712,18355,19646)
+(26657,28234,26597,28191)
+(24240,47211,24154,47191)
+(41778,10741,41766,10730)
+(44022,43776,44010,43677)
+(35967,30055,35906,29969)
+(28878,18042,28806,18027)
+(31507,27302,31428,27267)
+(13267,21935,13265,21872)
+(122,46832,64,46762)
+(10348,45916,10306,45844)
+(22962,12644,22927,12607)
+(6320,22290,6284,22247)
+(2297,11372,2216,11298)
+(29366,36660,29325,36654)
+(13962,39307,13921,39220)
+(11094,19151,11092,19143)
+(32289,23776,32258,23760)
+(36044,17356,35956,17273)
+(46304,38692,46232,38675)
+(10934,42999,10922,42909)
+(4271,21177,4207,21093)
+(7837,19926,7747,19905)
+(25537,36605,25477,36584)
+(22161,14999,22079,14962)
+(5127,31243,5074,31213)
+(14904,40664,14838,40593)
+(29308,8480,29268,8438)
+(17731,7410,17699,7352)
+(44840,29293,44797,29248)
+(15523,31519,15505,31485)
+(34429,38479,34421,38478)
+(3530,23456,3440,23390)
+(4699,6889,4603,6796)
+(47405,48524,47389,48514)
+(23357,43160,23305,43156)
+(16923,1995,16860,1937)
+(47592,33853,47537,33758)
+(31624,37490,31595,37473)
+(42321,13380,42303,13337)
+(3088,16094,3079,16060)
+(22884,2955,22856,2857)
+(17784,23073,17724,23044)
+(32638,45577,32553,45512)
+(13876,44091,13801,44000)
+(27844,24384,27758,24330)
+(28178,10225,28155,10167)
+(39910,14277,39857,14241)
+(30372,19524,30301,19514)
+(38732,43151,38724,43151)
+(32628,2068,32547,2068)
+(13950,28652,13932,28566)
+(38996,41070,38919,40993)
+(31759,45246,31676,45215)
+(5424,34145,5382,34106)
+(14727,45600,14699,45547)
+(31429,21537,31414,21499)
+(14740,3420,14650,3323)
+(21793,39498,21743,39471)
+(18102,25924,18037,25868)
+(33299,683,33213,594)
+(45882,48765,45809,48721)
+(49215,4098,49180,4067)
+(49698,33743,49614,33663)
+(21532,5215,21514,5151)
+(24840,26877,24826,26808)
+(32680,28433,32631,28364)
+(20661,27511,20584,27414)
+(28048,30385,28009,30315)
+(45403,42533,45389,42464)
+(46531,36947,46531,36850)
+(36943,32817,36865,32737)
+(37984,43763,37888,43748)
+(20593,10650,20557,10610)
+(5387,40595,5326,40585)
+(34412,10600,34352,10539)
+(7237,47546,7206,47451)
+(39931,26644,39915,26598)
+(29843,4734,29800,4669)
+(37503,8867,37406,8821)
+(2583,2373,2570,2294)
+(29275,46433,29256,46350)
+(3332,45620,3287,45581)
+(22472,39287,22472,39257)
+(36786,18907,36708,18884)
+(45503,28576,45482,28494)
+(33262,28386,33163,28365)
+(3606,49757,3538,49697)
+(2082,49380,1991,49281)
+(12065,3734,11983,3663)
+(15606,9048,15596,9028)
+(14687,19309,14637,19263)
+(4568,15461,4499,15428)
+(43938,7429,43923,7391)
+(2168,50012,2108,49914)
+(16022,8934,15963,8928)
+(24567,39147,24561,39102)
+(42781,14149,42765,14088)
+(39501,21084,39468,21078)
+(6697,29628,6693,29584)
+(11441,16164,11364,16125)
+(39946,1920,39868,1844)
+(18138,45512,18111,45438)
+(20799,41217,20718,41138)
+(30264,16697,30240,16639)
+(30746,50040,30727,49992)
+(37429,43273,37423,43205)
+(22854,28863,22789,28810)
+(11380,48298,11287,48242)
+(16471,37273,16439,37223)
+(32737,39842,32661,39811)
+(30959,3447,30949,3357)
+(36396,13263,36348,13187)
+(29607,14625,29531,14619)
+(7851,43399,7824,43334)
+(38515,14575,38496,14492)
+(29125,3289,29086,3264)
+(6866,10476,6839,10424)
+(318,31489,235,31404)
+(1140,7007,1113,6945)
+(36574,9291,36484,9275)
+(40320,40937,40246,40866)
+(588,25849,552,25801)
+(6728,42539,6645,42507)
+(12180,6185,12123,6123)
+(32913,44123,32899,44037)
+(25464,16803,25441,16749)
+(23711,5829,23695,5750)
+(31424,34930,31377,34906)
+(42171,8298,42124,8222)
+(451,31104,375,31083)
+(39996,3278,39943,3260)
+(25816,40396,25735,40362)
+(34471,28587,34399,28547)
+(45344,21540,45297,21496)
+(27269,16787,27246,16763)
+(18070,4469,18022,4423)
+(12668,16367,12645,16295)
+(13823,17276,13730,17251)
+(20555,45544,20511,45498)
+(35893,42189,35861,42177)
+(37081,45730,37076,45705)
+(17270,15651,17201,15552)
+(48690,46034,48667,45945)
+(456,16088,368,16023)
+(48707,12416,48670,12363)
+(29692,11509,29614,11483)
+(7005,3668,6981,3574)
+(12162,389,12103,309)
+(12371,24983,12366,24964)
+(6886,48414,6868,48327)
+(10653,26234,10624,26142)
+(8526,48205,8517,48117)
+(10521,31892,10480,31798)
+(43353,1086,43281,1071)
+(21007,35650,20998,35649)
+(2343,4396,2310,4320)
+(29379,12895,29284,12891)
+(27662,17407,27570,17313)
+(9845,29346,9807,29321)
+(43855,38669,43790,38599)
+(20461,44189,20397,44158)
+(11627,17368,11581,17289)
+(2971,38855,2938,38807)
+(43204,47082,43128,47018)
+(9930,46902,9909,46871)
+(30561,48461,30536,48365)
+(44059,7591,44038,7563)
+(46260,16898,46162,16886)
+(27491,2891,27396,2814)
+(36512,26034,36455,25941)
+(31193,20022,31100,19942)
+(17057,13643,16960,13621)
+(26897,3399,26844,3318)
+(1760,5504,1683,5431)
+(29347,5511,29346,5450)
+(38761,42083,38688,41999)
+(11226,4089,11165,4068)
+(46427,42983,46361,42970)
+(12958,30737,12912,30712)
+(44432,46521,44333,46443)
+(16124,2948,16113,2852)
+(24704,25422,24635,25340)
+(30833,46152,30790,46122)
+(4487,37006,4473,36968)
+(41047,23376,41036,23327)
+(16312,49392,16298,49330)
+(30081,14687,30042,14660)
+(11160,13954,11103,13938)
+(33207,23246,33143,23168)
+(14872,7635,14860,7585)
+(20139,23987,20059,23955)
+(10946,49757,10923,49746)
+(39438,36158,39426,36134)
+(35502,2385,35464,2327)
+(17073,42173,16987,42130)
+(6079,17258,6068,17195)
+(40458,15752,40364,15728)
+(23340,7879,23313,7806)
+(31819,15096,31762,15059)
+(31159,40864,31158,40780)
+(26975,32144,26915,32113)
+(34530,10378,34440,10298)
+(18855,49577,18780,49528)
+(16787,16625,16723,16586)
+(32330,26538,32314,26458)
+(34270,28674,34265,28595)
+(10022,16026,10006,15962)
+(23143,1479,23095,1469)
+(33676,4483,33583,4408)
+(31066,22074,31059,22035)
+(21603,47121,21563,47082)
+(30051,4244,30021,4157)
+(30634,39478,30615,39446)
+(34404,48724,34393,48724)
+(31103,21414,31039,21380)
+(22945,47397,22849,47313)
+(18133,32025,18073,31941)
+(4053,25759,3977,25667)
+(39185,39091,39102,39068)
+(43287,7407,43225,7314)
+(13137,31188,13112,31182)
+(46264,1438,46258,1389)
+(22804,43892,22769,43822)
+(7542,1044,7487,983)
+(33022,8321,32925,8267)
+(384,39161,286,39073)
+(28205,24401,28142,24382)
+(31708,39086,31696,39026)
+(36626,15708,36560,15690)
+(17099,16924,17079,16924)
+(10817,6989,10747,6955)
+(24338,19293,24291,19277)
+(27566,17576,27544,17545)
+(23041,38384,22970,38320)
+(12786,8485,12702,8435)
+(13876,49473,13813,49448)
+(31585,46998,31490,46929)
+(30227,8768,30206,8715)
+(32062,39306,32023,39292)
+(25003,35753,24921,35687)
+(3281,6758,3232,6704)
+(11395,30299,11376,30220)
+(5088,15275,5007,15203)
+(31100,39538,31003,39444)
+(2741,17877,2726,17793)
+(42897,48620,42860,48537)
+(4230,15778,4181,15776)
+(17835,27530,17815,27431)
+(34189,10933,34135,10921)
+(7537,39974,7494,39973)
+(21554,3507,21528,3476)
+(9350,32326,9273,32275)
+(16455,8874,16420,8793)
+(7346,34235,7330,34224)
+(16417,48134,16352,48066)
+(41916,4971,41849,4886)
+(15856,1522,15807,1521)
+(41549,40218,41494,40144)
+(9978,16226,9972,16181)
+(14856,13312,14808,13283)
+(38490,41641,38428,41583)
+(25828,7438,25807,7378)
+(21876,30633,21796,30587)
+(1908,14279,1825,14247)
+(32207,10251,32121,10184)
+(370,9493,328,9441)
+(42072,17634,41974,17600)
+(47298,9910,47235,9846)
+(17856,11266,17782,11225)
+(35009,21400,34956,21396)
+(18337,11145,18335,11133)
+(25425,9139,25381,9085)
+(35642,27783,35621,27782)
+(3629,33164,3575,33163)
+(17151,41255,17115,41204)
+(17417,5835,17402,5751)
+(33407,14226,33329,14141)
+(1930,29955,1889,29931)
+(41101,10942,41065,10844)
+(36333,27288,36281,27233)
+(21423,36868,21367,36825)
+(36385,19566,36341,19510)
+(27073,38301,27066,38232)
+(43989,34187,43984,34174)
+(48366,7488,48316,7483)
+(37497,36075,37415,36043)
+(46917,9891,46887,9870)
+(37179,657,37103,634)
+(3877,44736,3811,44684)
+(30556,2975,30547,2962)
+(7629,11447,7547,11416)
+(45687,48147,45591,48088)
+(5635,7184,5571,7146)
+(9611,47327,9541,47246)
+(7119,48224,7117,48152)
+(15233,26480,15138,26430)
+(37468,1526,37466,1513)
+(20855,2786,20828,2711)
+(30538,44084,30480,44061)
+(42231,41527,42149,41454)
+(14963,13239,14952,13146)
+(26819,43996,26745,43934)
+(42172,35953,42086,35928)
+(28785,12611,28710,12534)
+(14089,1704,14047,1629)
+(4343,26242,4341,26169)
+(20327,42244,20231,42212)
+(33671,12700,33666,12630)
+(42144,32642,42128,32569)
+(26590,19483,26503,19442)
+(21741,46259,21723,46226)
+(8822,34700,8760,34693)
+(2710,33521,2675,33505)
+(26067,19998,26026,19989)
+(12244,34509,12202,34489)
+(47162,598,47119,499)
+(33093,49382,33068,49359)
+(35170,26340,35153,26264)
+(22552,35785,22490,35735)
+(36791,23032,36781,22976)
+(22857,10857,22833,10797)
+(47207,37405,47138,37365)
+(21867,2836,21854,2811)
+(3387,31487,3311,31456)
+(47174,48121,47167,48101)
+(24415,22232,24366,22224)
+(7970,29251,7959,29211)
+(18635,31294,18539,31221)
+(8403,13380,8370,13372)
+(738,18097,737,18054)
+(37238,19195,37218,19114)
+(582,47934,570,47897)
+(12359,4635,12350,4619)
+(43272,2013,43195,1958)
+(47568,27149,47521,27088)
+(24695,12827,24661,12796)
+(26259,14077,26168,14019)
+(48478,36135,48425,36092)
+(5230,39250,5206,39174)
+(3488,18562,3423,18489)
+(39502,16331,39460,16275)
+(18296,1478,18233,1471)
+(28627,12430,28559,12410)
+(25257,21981,25206,21954)
+(2410,41192,2325,41142)
+(43681,9631,43587,9538)
+(15086,45309,15064,45270)
+(13824,40807,13759,40787)
+(7090,2207,7062,2159)
+(3685,2480,3630,2391)
+(14810,38335,14801,38275)
+(26668,38018,26581,38012)
+(45562,1517,45506,1424)
+(11001,32481,10962,32402)
+(27743,25245,27673,25161)
+(15952,10598,15948,10535)
+(12705,13308,12694,13232)
+(31992,21195,31975,21118)
+(25834,16652,25745,16626)
+(21022,43625,20990,43576)
+(45094,27254,45000,27240)
+(9688,42601,9643,42533)
+(17746,24659,17694,24616)
+(1509,38859,1503,38809)
+(2067,20438,2041,20369)
+(7885,44528,7839,44444)
+(27432,33052,27422,32987)
+(26577,17157,26563,17142)
+(10815,35985,10734,35908)
+(44891,24067,44794,23979)
+(48626,1900,48595,1850)
+(40659,35541,40659,35489)
+(22231,26628,22210,26579)
+(37408,23016,37375,22919)
+(5920,15916,5906,15895)
+(33125,9952,33037,9880)
+(12142,29705,12141,29670)
+(3672,20995,3649,20899)
+(39147,31967,39101,31907)
+(33812,48458,33748,48399)
+(25038,14639,24978,14586)
+(3859,16010,3857,15994)
+(31926,39496,31889,39417)
+(49300,28064,49297,28026)
+(24121,38305,24048,38256)
+(9252,4205,9155,4149)
+(36124,30451,36056,30395)
+(28809,49557,28794,49533)
+(30500,44504,30471,44476)
+(26866,42395,26822,42332)
+(48195,1784,48101,1734)
+(46201,14109,46112,14097)
+(2415,9975,2354,9914)
+(30485,9581,30415,9558)
+(6385,36838,6305,36838)
+(2799,11189,2723,11095)
+(21998,20503,21923,20406)
+(29151,10714,29090,10671)
+(28850,29276,28757,29207)
+(43386,48845,43305,48834)
+(25173,8310,25101,8294)
+(34244,32352,34204,32342)
+(35595,23728,35533,23672)
+(1122,13581,1119,13538)
+(388,21716,296,21678)
+(48782,11064,48701,11005)
+(40293,12997,40213,12927)
+(28194,46428,28113,46414)
+(4791,18118,4708,18105)
+(471,29808,448,29775)
+(3536,37803,3447,37737)
+(1336,28416,1275,28392)
+(16484,48478,16422,48454)
+(25846,19320,25811,19296)
+(48669,27703,48575,27615)
+(24032,44217,24029,44127)
+(12236,5019,12233,4986)
+(1179,29838,1113,29778)
+(33893,22049,33867,21955)
+(16718,19462,16700,19440)
+(17992,49438,17894,49433)
+(35163,39941,35081,39885)
+(33897,8362,33853,8328)
+(2480,6640,2456,6599)
+(28011,19729,27937,19679)
+(15819,41516,15809,41440)
+(29818,9136,29747,9089)
+(28551,37016,28529,36941)
+(36406,26879,36374,26872)
+(16821,48925,16758,48914)
+(23692,48163,23595,48160)
+(4803,10619,4759,10522)
+(46600,33581,46553,33518)
+(41349,11767,41310,11710)
+(20856,29642,20799,29562)
+(16559,46161,16504,46131)
+(23041,1300,23003,1287)
+(16630,44902,16554,44853)
+(43065,14299,43013,14274)
+(24818,22397,24796,22348)
+(22282,24949,22218,24921)
+(36668,28538,36631,28456)
+(8080,1220,8018,1146)
+(47282,34302,47277,34269)
+(35603,33558,35557,33495)
+(44764,32189,44700,32175)
+(46488,23965,46449,23868)
+(46314,15047,46216,15013)
+(6348,25381,6286,25363)
+(3871,49288,3819,49251)
+(462,38894,398,38867)
+(23196,29214,23136,29169)
+(29024,9775,29016,9759)
+(42016,18555,41934,18472)
+(8772,45981,8692,45973)
+(11028,1351,10986,1278)
+(26684,21668,26641,21656)
+(37262,26005,37260,25947)
+(14899,44069,14814,44066)
+(39635,18701,39587,18698)
+(28528,22948,28457,22857)
+(7755,36528,7681,36454)
+(32461,1172,32427,1106)
+(18775,27359,18736,27329)
+(15379,20031,15337,19934)
+(45888,33592,45881,33544)
+(44013,24694,43962,24645)
+(43347,10699,43343,10699)
+(49999,27218,49908,27176)
+(13698,17326,13630,17317)
+(34850,44313,34775,44302)
+(38076,49235,37983,49214)
+(35570,40218,35500,40136)
+(40062,28973,40032,28878)
+(3567,39847,3523,39781)
+(498,2442,480,2401)
+(29660,43620,29577,43561)
+(10946,47356,10878,47351)
+(8073,44233,8005,44144)
+(9720,13473,9710,13462)
+(3643,38014,3598,37932)
+(16887,1408,16810,1375)
+(7559,27914,7508,27874)
+(30356,18573,30275,18569)
+(12193,48176,12130,48116)
+(11884,7756,11819,7731)
+(18293,33272,18227,33234)
+(46697,47874,46696,47828)
+(35788,32517,35760,32446)
+(33877,36987,33821,36958)
+(31253,22819,31184,22808)
+(7744,23115,7729,23103)
+(21291,39817,21219,39778)
+(13877,43379,13861,43290)
+(42955,1406,42876,1382)
+(49232,15950,49210,15880)
+(48419,32001,48326,31902)
+(18940,43246,18860,43150)
+(32317,38240,32310,38201)
+(11307,48298,11304,48222)
+(38015,18190,38000,18176)
+(27821,1177,27818,1131)
+(18935,26757,18865,26682)
+(42659,48284,42562,48244)
+(30185,23350,30146,23291)
+(16496,11970,16441,11919)
+(162,26040,120,25963)
+(24238,47784,24185,47746)
+(32326,8612,32274,8568)
+(26141,13423,26051,13407)
+(40132,22815,40089,22812)
+(21151,48794,21056,48740)
+(22044,28358,22031,28334)
+(6680,14746,6605,14669)
+(40686,25139,40632,25070)
+(22823,27549,22816,27507)
+(2513,22841,2427,22811)
+(36316,27787,36218,27728)
+(554,35489,540,35441)
+(536,30674,534,30609)
+(25385,38468,25295,38416)
+(19467,47386,19437,47317)
+(22425,38591,22387,38536)
+(32493,17321,32396,17298)
+(40115,47315,40109,47235)
+(25002,2107,24963,2104)
+(3901,9790,3898,9706)
+(40316,1721,40315,1658)
+(40089,3454,40074,3443)
+(793,17897,761,17897)
+(6490,43552,6434,43522)
+(10825,487,10820,405)
+(47703,36067,47641,36011)
+(4480,11671,4468,11653)
+(37713,10642,37711,10615)
+(12315,5302,12273,5203)
+(8709,6617,8647,6557)
+(24467,30535,24455,30494)
+(40440,32757,40369,32668)
+(49449,42447,49426,42428)
+(44867,11197,44792,11137)
+(39173,33241,39143,33187)
+(43836,2212,43803,2184)
+(23819,47613,23739,47575)
+(20583,2134,20485,2042)
+(48922,6169,48889,6111)
+(5230,44613,5131,44604)
+(37060,8051,37032,7975)
+(19148,36711,19112,36704)
+(36305,4216,36243,4118)
+(6329,39089,6302,39047)
+(36703,26367,36623,26307)
+(44753,19721,44701,19631)
+(42094,43310,42094,43285)
+(4276,22377,4241,22352)
+(30329,18906,30327,18815)
+(21970,19605,21871,19590)
+(23722,41924,23709,41861)
+(30965,39775,30908,39692)
+(32394,37895,32351,37890)
+(23968,42162,23873,42095)
+(1776,2621,1732,2548)
+(24951,47758,24900,47679)
+(32917,35771,32847,35753)
+(5428,27773,5343,27769)
+(19650,142,19630,51)
+(39769,17276,39743,17229)
+(5171,24562,5119,24470)
+(32976,35249,32917,35199)
+(4174,24603,4099,24504)
+(38565,36960,38535,36926)
+(39084,4328,39031,4301)
+(32153,38043,32070,37990)
+(38085,30640,38041,30603)
+(14269,18426,14185,18422)
+(42941,30850,42892,30788)
+(32403,25999,32339,25960)
+(16906,191,16816,139)
+(3456,48722,3418,48721)
+(3050,18287,3022,18243)
+(6331,8439,6234,8364)
+(5331,20797,5319,20793)
+(39225,37408,39216,37348)
+(34510,19838,34488,19810)
+(45789,33873,45770,33786)
+(369,1457,278,1409)
+(16531,43785,16482,43729)
+(11974,14789,11973,14730)
+(23128,6811,23094,6798)
+(43962,33659,43944,33599)
+(20967,3115,20947,3079)
+(39257,38606,39241,38595)
+(22431,8246,22381,8235)
+(26007,14672,25996,14593)
+(24762,4261,24675,4261)
+(35402,32077,35343,31988)
+(5141,16476,5139,16393)
+(16439,17564,16344,17472)
+(36983,46663,36903,46567)
+(35170,14144,35162,14048)
+(22290,7841,22283,7810)
+(22414,38398,22404,38319)
+(9011,18177,8932,18150)
+(154,4019,138,3990)
+(20447,4998,20383,4970)
+(38867,35757,38795,35659)
+(32322,15845,32227,15804)
+(29889,12142,29852,12055)
+(36235,36918,36217,36897)
+(41620,6581,41568,6581)
+(24758,38504,24731,38483)
+(42524,12904,42473,12895)
+(17954,49975,17865,49915)
+(1938,39019,1927,39013)
+(4864,33279,4817,33258)
+(45373,41967,45313,41885)
+(28786,19028,28782,18978)
+(41913,44950,41911,44908)
+(33408,14698,33392,14681)
+(27602,3460,27576,3419)
+(3336,3728,3334,3715)
+(9099,910,9080,813)
+(34141,6403,34071,6367)
+(48270,17216,48252,17130)
+(2549,16546,2461,16474)
+(27802,33669,27735,33642)
+(48419,1682,48323,1583)
+(5094,41211,5002,41123)
+(11192,6217,11190,6146)
+(6979,18503,6959,18421)
+(41210,48187,41140,48143)
+(15303,29527,15273,29441)
+(12326,45572,12267,45570)
+(29293,5861,29212,5826)
+(23847,37241,23761,37178)
+(44656,23926,44653,23831)
+(30043,16194,29977,16105)
+(902,9358,879,9339)
+(23850,46501,23834,46494)
+(42333,13300,42287,13246)
+(25226,18086,25169,18005)
+(40252,12082,40183,12038)
+(49275,18076,49216,18055)
+(8255,28878,8238,28862)
+(11325,41286,11320,41235)
+(16948,18588,16926,18528)
+(31394,1099,31374,1038)
+(30705,35772,30637,35766)
+(3858,39131,3771,39125)
+(17565,24892,17515,24808)
+(9221,49715,9216,49661)
+(44945,25769,44875,25722)
+(33408,13563,33310,13527)
+(48505,4407,48408,4373)
+(21859,37217,21763,37217)
+(39393,14422,39335,14364)
+(19905,1154,19841,1098)
+(25946,10388,25906,10366)
+(10104,13748,10027,13746)
+(5822,24629,5820,24599)
+(38194,11287,38127,11252)
+(15694,46757,15625,46716)
+(326,18837,285,18817)
+(49611,47078,49533,47052)
+(48233,18850,48150,18842)
+(29239,9962,29208,9875)
+(40062,44554,39973,44460)
+(19135,20729,19059,20643)
+(31969,40664,31896,40643)
+(3725,9191,3711,9095)
+(44280,40158,44264,40108)
+(37236,42756,37160,42694)
+(27958,19055,27888,18959)
+(45270,17661,45187,17601)
+(12115,39546,12061,39525)
+(10227,32295,10168,32231)
+(39264,31123,39226,31085)
+(6566,40000,6532,39904)
+(30058,6975,30012,6903)
+(49631,6909,49597,6823)
+(42168,10926,42134,10905)
+(44892,30042,44858,29970)
+(19540,19803,19495,19788)
+(18403,25454,18371,25404)
+(22929,26795,22841,26722)
+(16648,30213,16626,30174)
+(3440,7495,3429,7468)
+(30708,49028,30643,48998)
+(26258,14164,26255,14151)
+(44206,31653,44121,31637)
+(1510,15179,1426,15130)
+(6986,30496,6887,30416)
+(7192,43403,7138,43339)
+(39921,22071,39866,21976)
+(45870,17011,45796,16919)
+(15939,9563,15917,9539)
+(23728,24737,23691,24725)
+(6444,40416,6363,40375)
+(21899,23861,21857,23765)
+(20610,36765,20533,36742)
+(46520,33082,46433,32983)
+(21406,20902,21311,20895)
+(37913,42300,37814,42269)
+(18216,8177,18161,8173)
+(32967,8258,32899,8244)
+(14978,40230,14971,40149)
+(30343,39152,30266,39101)
+(25917,5835,25843,5806)
+(5169,45366,5141,45314)
+(16221,20898,16209,20875)
+(13151,19869,13145,19811)
+(44399,2801,44337,2713)
+(10959,48311,10957,48230)
+(4794,11711,4732,11661)
+(764,10149,762,10091)
+(15985,46067,15898,46028)
+(41434,22870,41342,22867)
+(43769,23796,43743,23756)
+(10017,18440,9919,18384)
+(21141,43119,21097,43112)
+(7782,13424,7694,13398)
+(25088,36224,25059,36150)
+(46325,48722,46241,48631)
+(11042,33125,11011,33071)
+(22347,13460,22290,13375)
+(3508,20538,3483,20536)
+(5331,42945,5272,42875)
+(2368,15537,2339,15503)
+(45314,31830,45254,31817)
+(34358,2649,34319,2589)
+(17576,30407,17572,30323)
+(29836,41324,29746,41287)
+(21036,39996,21014,39899)
+(26886,6460,26787,6400)
+(15709,5625,15627,5558)
+(37415,15979,37414,15911)
+(47761,16860,47728,16813)
+(35814,48252,35755,48173)
+(28559,20810,28496,20715)
+(12034,11921,12002,11905)
+(1818,27450,1805,27406)
+(33810,45499,33806,45413)
+(17376,18175,17323,18138)
+(34106,28135,34049,28106)
+(44947,23165,44919,23091)
+(37670,41904,37616,41840)
+(12614,15027,12555,14969)
+(43301,75,43227,43)
+(27526,15096,27450,15088)
+(26947,33409,26853,33333)
+(1537,43572,1471,43499)
+(21607,35452,21605,35375)
+(24869,46565,24818,46531)
+(4774,30335,4723,30257)
+(11615,18316,11579,18310)
+(18444,15819,18354,15763)
+(47267,22574,47203,22518)
+(22287,49538,22203,49511)
+(43010,16270,43010,16202)
+(1623,8350,1578,8254)
+(21220,43808,21137,43748)
+(40397,16471,40358,16434)
+(34839,1377,34744,1327)
+(17096,5730,17090,5637)
+(28156,37782,28155,37723)
+(3672,5686,3586,5638)
+(21856,48656,21840,48638)
+(6907,7791,6892,7761)
+(17952,21370,17862,21350)
+(37793,13461,37784,13381)
+(14740,49655,14709,49604)
+(21690,6337,21593,6289)
+(10423,33548,10364,33498)
+(39187,23274,39136,23197)
+(21882,37247,21835,37167)
+(11343,16957,11281,16914)
+(38279,43400,38264,43352)
+(23167,30271,23086,30224)
+(46278,6037,46180,5964)
+(28626,31165,28605,31095)
+(31018,367,30946,333)
+(23541,12541,23530,12523)
+(49741,14535,49691,14511)
+(31444,12702,31425,12612)
+(22406,26536,22316,26534)
+(6807,9761,6758,9723)
+(15698,1941,15687,1848)
+(49310,4625,49295,4584)
+(21345,18939,21269,18887)
+(31433,30493,31411,30439)
+(44980,12400,44950,12372)
+(25054,13949,24984,13949)
+(40538,7253,40483,7212)
+(16967,8627,16936,8604)
+(26872,3646,26804,3594)
+(24575,42883,24530,42883)
+(11823,5755,11771,5721)
+(2553,46189,2513,46174)
+(24993,14552,24898,14470)
+(28453,1719,28419,1665)
+(8925,22603,8878,22589)
+(47635,15380,47546,15378)
+(35378,18112,35324,18058)
+(27347,22264,27293,22200)
+(44323,29044,44273,28958)
+(41538,38324,41484,38290)
+(19128,49932,19112,49849)
+(17904,12548,17867,12503)
+(35103,14426,35092,14336)
+(29807,10142,29714,10052)
+(44507,22903,44462,22847)
+(11419,13324,11399,13251)
+(8573,42221,8562,42123)
+(46798,45843,46765,45765)
+(12028,31783,11967,31749)
+(10635,45300,10604,45251)
+(9626,8248,9587,8194)
+(18290,741,18246,732)
+(39949,44672,39932,44641)
+(7897,11692,7893,11637)
+(20165,42246,20112,42168)
+(4341,48390,4285,48338)
+(30126,28913,30088,28869)
+(40565,1733,40472,1721)
+(9981,30147,9915,30133)
+(47292,25511,47217,25462)
+(20137,24489,20104,24392)
+(2385,28283,2381,28189)
+(20429,10052,20357,10009)
+(8395,38568,8348,38480)
+(17381,36112,17349,36038)
+(37845,30953,37759,30926)
+(27452,12732,27411,12652)
+(38196,32186,38114,32116)
+(6527,49356,6508,49315)
+(43891,29789,43856,29723)
+(6146,37192,6085,37107)
+(42012,28897,41939,28808)
+(14909,13815,14846,13757)
+(11120,24095,11035,24049)
+(3132,41545,3053,41526)
+(40084,40315,39994,40261)
+(39671,17445,39576,17361)
+(47135,35853,47085,35831)
+(39297,1941,39290,1911)
+(47143,35898,47072,35880)
+(16017,6711,15989,6686)
+(47110,30305,47087,30213)
+(38102,27639,38091,27602)
+(17954,22544,17863,22453)
+(39891,11791,39815,11739)
+(13996,20290,13922,20278)
+(22284,23143,22190,23081)
+(25345,24019,25313,24017)
+(47134,44803,47055,44761)
+(41360,16573,41326,16503)
+(10464,1071,10457,998)
+(23515,47517,23451,47499)
+(9308,8452,9238,8392)
+(28695,5657,28671,5644)
+(45104,9913,45077,9871)
+(337,455,240,359)
+(11562,45479,11472,45428)
+(11952,18466,11931,18425)
+(35789,5154,35775,5128)
+(19024,18299,18979,18230)
+(43056,38113,42975,38067)
+(10075,26847,10064,26806)
+(3065,8107,3029,8038)
+(24766,19059,24749,18985)
+(14438,24805,14413,24708)
+(9523,3058,9485,2998)
+(24516,31262,24478,31204)
+(49513,26044,49434,26035)
+(14110,38528,14103,38461)
+(31679,35618,31619,35618)
+(10029,20258,10008,20248)
+(39269,37586,39233,37539)
+(12343,8197,12247,8113)
+(11155,44223,11111,44134)
+(25437,20606,25338,20534)
+(46604,16156,46570,16131)
+(4636,14004,4592,13941)
+(15975,29628,15912,29556)
+(49887,24274,49805,24184)
+(11812,13440,11723,13418)
+(21589,38179,21531,38085)
+(32255,44463,32219,44454)
+(15023,12698,14989,12687)
+(28906,48630,28818,48568)
+(28886,38905,28861,38832)
+(34786,22285,34740,22240)
+(46513,46780,46425,46780)
+(26626,31759,26551,31677)
+(19792,25967,19763,25933)
+(20432,14394,20388,14365)
+(27092,7301,27052,7278)
+(22283,987,22198,928)
+(6197,24363,6112,24311)
+(46601,49259,46551,49231)
+(12392,48052,12363,48038)
+(46116,31386,46067,31356)
+(7354,16855,7289,16778)
+(47501,42808,47495,42761)
+(16461,25487,16391,25398)
+(42678,18798,42678,18756)
+(9466,18207,9419,18185)
+(17467,14177,17416,14097)
+(28533,31886,28487,31832)
+(13225,38472,13188,38395)
+(5180,40970,5173,40902)
+(83,10271,15,10265)
+(2111,6784,2016,6690)
+(41835,11064,41798,10995)
+(29273,48585,29181,48536)
+(29066,21615,28985,21543)
+(19805,44143,19727,44128)
+(48919,21468,48875,21467)
+(28790,34287,28721,34251)
+(10911,33074,10869,32989)
+(6111,16519,6032,16489)
+(43889,33838,43837,33768)
+(32323,21685,32304,21644)
+(9552,27819,9539,27753)
+(38266,49852,38233,49844)
+(37672,48362,37663,48277)
+(32550,47029,32529,46931)
+(46307,6620,46272,6616)
+(23192,46608,23105,46566)
+(30399,48330,30335,48239)
+(36268,25058,36235,24984)
+(19181,8120,19089,8098)
+(24376,19983,24294,19925)
+(18297,18375,18202,18292)
+(31608,6215,31575,6168)
+(12788,49510,12784,49468)
+(46071,13013,46035,12991)
+(27647,8218,27582,8201)
+(49580,11076,49537,11050)
+(35501,33782,35501,33687)
+(19969,3148,19964,3082)
+(37728,49153,37726,49152)
+(5322,48440,5321,48435)
+(48003,10096,47904,10005)
+(39361,22318,39348,22236)
+(30488,7456,30437,7430)
+(18533,39476,18481,39394)
+(39462,23701,39433,23604)
+(26701,18300,26686,18235)
+(17405,35577,17387,35517)
+(33971,29928,33953,29919)
+(6328,10241,6276,10217)
+(32459,44259,32453,44217)
+(1715,42385,1647,42357)
+(48113,6960,48103,6872)
+(30561,4255,30476,4240)
+(38907,43619,38827,43553)
+(29149,20773,29070,20698)
+(17006,1543,16970,1497)
+(11737,18808,11714,18788)
+(13019,30534,13005,30481)
+(39224,31729,39191,31683)
+(4942,41680,4907,41596)
+(12287,37187,12188,37172)
+(30758,29579,30725,29531)
+(16604,17963,16581,17912)
+(19459,15888,19409,15812)
+(34696,24783,34600,24725)
+(21621,14159,21558,14110)
+(12193,46149,12145,46096)
+(37781,4715,37692,4635)
+(41854,44125,41807,44040)
+(23604,23585,23571,23533)
+(7853,36967,7797,36908)
+(2755,13279,2720,13206)
+(4314,15424,4283,15383)
+(29584,12685,29493,12594)
+(25138,33726,25042,33691)
+(38393,10270,38326,10185)
+(4247,12615,4225,12567)
+(36100,33156,36100,33107)
+(20024,40796,20016,40708)
+(3927,44892,3914,44843)
+(10317,43168,10226,43096)
+(22057,3419,22042,3334)
+(37097,21814,37025,21811)
+(32084,21564,31996,21491)
+(34079,39921,34058,39911)
+(23078,47459,23018,47373)
+(38109,616,38082,568)
+(11862,40382,11764,40292)
+(33403,33320,33389,33289)
+(36639,24829,36623,24829)
+(12995,45080,12992,45040)
+(16545,19981,16532,19891)
+(26155,10659,26154,10634)
+(24423,255,24360,213)
+(823,22487,781,22442)
+(12823,20064,12735,20040)
+(19688,11710,19681,11654)
+(2892,20452,2836,20424)
+(15533,10807,15464,10711)
+(46994,41143,46955,41082)
+(18155,2421,18069,2392)
+(2628,12688,2605,12602)
+(35128,8396,35044,8365)
+(44765,49615,44758,49524)
+(11226,44529,11178,44515)
+(31334,32463,31291,32456)
+(43224,23387,43168,23364)
+(30882,10414,30798,10395)
+(29139,967,29139,923)
+(29959,45244,29877,45223)
+(19946,217,19941,118)
+(49732,22033,49642,22012)
+(32914,15360,32879,15290)
+(47825,21097,47747,21030)
+(10788,5131,10746,5086)
+(15497,9698,15481,9678)
+(10617,47195,10601,47117)
+(42392,10583,42340,10550)
+(10753,33520,10669,33509)
+(5553,21580,5521,21527)
+(36840,12336,36817,12320)
+(49785,12554,49702,12553)
+(17737,38349,17639,38277)
+(48000,7823,47956,7814)
+(5019,3184,4931,3160)
+(30120,3524,30063,3492)
+(37044,2016,37001,1942)
+(23496,38566,23469,38528)
+(17255,48957,17200,48903)
+(27815,2138,27808,2090)
+(40440,11129,40368,11105)
+(35305,21772,35272,21717)
+(41308,45065,41229,44973)
+(14893,28807,14817,28789)
+(30776,45824,30731,45772)
+(742,40724,652,40672)
+(5985,41133,5927,41097)
+(9576,10226,9540,10218)
+(21407,23207,21323,23160)
+(44880,34228,44877,34169)
+(29146,49694,29143,49682)
+(28502,34886,28471,34832)
+(30662,5584,30604,5528)
+(12612,26081,12552,26001)
+(17166,49308,17098,49270)
+(9586,14116,9488,14104)
+(37323,47576,37264,47482)
+(48009,49713,48004,49614)
+(49308,23780,49297,23760)
+(8667,32342,8592,32294)
+(37826,48560,37822,48485)
+(24493,18653,24486,18616)
+(17914,3850,17887,3775)
+(34270,43873,34231,43826)
+(7753,44715,7660,44651)
+(44328,36364,44265,36350)
+(10146,3030,10111,2975)
+(35273,40106,35269,40062)
+(38566,43846,38547,43760)
+(12400,41394,12377,41378)
+(45196,38286,45153,38250)
+(48511,14972,48428,14883)
+(25939,36328,25886,36277)
+(38997,11007,38979,10917)
+(30342,518,30244,453)
+(6876,7468,6867,7454)
+(17566,27575,17566,27480)
+(18869,28538,18858,28475)
+(16825,33309,16726,33255)
+(14585,26111,14490,26035)
+(28743,49392,28664,49349)
+(26652,23359,26618,23297)
+(40129,33653,40102,33584)
+(41074,26393,41038,26389)
+(3869,33564,3869,33536)
+(28455,14205,28364,14163)
+(13866,45603,13770,45543)
+(21666,30586,21578,30544)
+(29978,11931,29893,11868)
+(1594,1043,1517,971)
+(948,1201,907,1156)
+(27547,13692,27545,13677)
+(13661,38184,13566,38154)
+(2389,40026,2317,39938)
+(35481,46379,35481,46320)
+(26917,45698,26864,45689)
+(23933,41617,23909,41539)
+(8912,8471,8862,8401)
+(9625,4747,9558,4692)
+(34743,35056,34721,34969)
+(39544,21762,39475,21717)
+(11741,26330,11656,26293)
+(39015,1315,38966,1285)
+(13418,44237,13326,44202)
+(2107,17672,2093,17616)
+(42448,28844,42370,28764)
+(49843,5175,49808,5145)
+(6536,23000,6467,22958)
+(11114,5822,11027,5739)
+(48457,11074,48384,11024)
+(12343,23110,12310,23074)
+(17300,24847,17276,24825)
+(8823,8253,8793,8238)
+(3449,171,3354,108)
+(21650,23955,21605,23883)
+(13260,3234,13193,3214)
+(25361,10896,25305,10806)
+(25051,25042,25011,25001)
+(25044,25088,25015,25005)
+(25007,25061,25002,25013)
+(25066,25105,25003,25007)
+(25028,25012,25015,25011)
+(25031,25057,25006,25018)
+(25015,25042,25004,25012)
+(25091,25049,25019,25019)
+(25023,25011,25000,25004)
+(25053,25104,25010,25012)
+(25058,25001,25018,25000)
+(25059,25051,25008,25016)
+(25043,25069,25007,25004)
+(25006,25101,25002,25002)
+(25095,25012,25014,25007)
+(25054,25052,25019,25013)
+(25108,25077,25009,25018)
+(25007,25023,25003,25002)
+(25076,25098,25002,25016)
+(25030,25077,25012,25006)
diff --git a/src/test/regress/data/streets.data b/src/test/regress/data/streets.data
new file mode 100644 (file)
index 0000000..929d749
--- /dev/null
@@ -0,0 +1,5124 @@
+Arroyo                        Road     (0, 2, -121.749307, 37.147170, -121.748100, 37.149570 )
+Mendenhall                    Road     (0, 2, -121.681342, 37.033540, -121.684794, 37.044300 )
+Mines                         Road     (0, 2, -121.633014, 37.957410, -121.642288, 37.988740 )
+Geary                         Road     (0, 2, -121.770923, 37.998050, -121.770222, 37.997600 )
+Seawall                       Dr       (0, 2, -122.316154, 37.631260, -122.315489, 37.600450 )
+Dry Creek                              (0, 2, -121.706461, 37.120060, -121.708998, 37.155250 )
+Del Valle                     Road     (0, 2, -121.688828, 37.708960, -121.691152, 37.745800 )
+Shafer Creek                           (0, 2, -121.681036, 37.125450, -121.694801, 37.251120 )
+South Fork Trout Creek                 (0, 2, -121.658085, 37.998740, -121.657591, 37.024230 )
+Arroyo del Valle                       (0, 2, -121.654588, 37.365070, -121.656972, 37.408800 )
+Hetch Hetchy Aqueduct                  (0, 2, -121.740080, 37.810720, -121.739425, 37.812110 )
+Hetch Hetchy Aqueduct                  (0, 2, -121.687586, 37.921020, -121.686938, 37.922410 )
+Arroyo Mocho                           (0, 3, -121.660579, 37.013880, -121.668949, 37.027000 , -121.682578, 37.108170)
+Hetch Hetchy Aqueduct                  (0, 2, -121.635378, 37.063400, -121.630012, 37.074820 )
+Whitlock Creek                         (0, 2, -121.746830, 37.912760, -121.733107, 37.000000 )
+Arroyo del Valle                       (0, 2, -121.607487, 37.898410, -121.612773, 37.926380 )
+Tarraville Creek                       (0, 2, -121.536091, 37.997210, -121.536763, 37.000000 )
+Tarraville Creek                       (0, 2, -121.534290, 37.989430, -121.532627, 37.987220 )
+Tarraville Creek              Road     (0, 2, -121.530642, 37.982800, -121.530608, 37.982780 )
+Mines                         Road     (0, 2, -121.531666, 37.832190, -121.534092, 37.943660 )
+Tarraville Creek                       (0, 2, -121.525440, 37.955690, -121.525089, 37.948000 )
+Tarraville Creek              Road     (0, 2, -121.525820, 37.964590, -121.525715, 37.962960 )
+Tarraville Creek                       (0, 2, -121.528505, 37.983700, -121.528147, 37.979970 )
+Vasco                         Road     (0, 2, -121.737000, 37.725000, -121.728200, 37.489000 )
+California Aqueduct                    (0, 2, -121.622944, 37.984430, -121.622669, 37.986110 )
+Bruns                         Road     (0, 2, -121.603992, 37.953070, -121.604600, 37.049000 )
+Southern Pacific Railroad              (0, 2, -121.558002, 37.006630, -121.576000, 37.136000 )
+120 Canal                              (0, 2, -121.587482, 37.882040, -121.587833, 37.882500 )
+Kelso                         Road     (0, 2, -121.584782, 37.949790, -121.585132, 37.949710 )
+70 Canal                               (0, 2, -121.576176, 37.919580, -121.576450, 37.919960 )
+Lindemann                     Road     (0, 2, -121.558002, 37.002130, -121.558002, 37.006630 )
+Christensen                   Road     (0, 2, -121.625309, 37.797740, -121.621265, 37.839930 )
+California Aqueduct                    (0, 2, -121.587742, 37.652010, -121.600239, 37.709390 )
+Delta Mendota Canal                    (0, 2, -121.589243, 37.843550, -121.589222, 37.843890 )
+Mtn House                     Road     (0, 2, -121.576862, 37.783550, -121.576389, 37.804220 )
+Mtn House                     Road     (0, 2, -121.579578, 37.738070, -121.578037, 37.753560 )
+Delta Mendota Canal                    (0, 2, -121.578190, 37.718700, -121.578403, 37.719760 )
+Mtn House Creek                        (0, 2, -121.577000, 37.504310, -121.578144, 37.495540 )
+Midway                        Road     (0, 2, -121.572056, 37.500490, -121.574787, 37.512470 )
+Grant Line                    Road     (0, 2, -121.583469, 37.457460, -121.583118, 37.464100 )
+Mtn House Creek                        (0, 2, -121.570759, 37.601890, -121.566196, 37.638890 )
+Stream                                 (0, 2, -121.574573, 37.549480, -121.574039, 37.572130 )
+Delta Mendota Canal                    (0, 2, -121.562031, 37.557100, -121.573749, 37.573740 )
+Colonial Loma Verde           Dr       (0, 2, -122.118400, 37.849000, -122.117278, 37.855280 )
+Patterson Pass                Road     (0, 2, -121.574131, 37.075380, -121.573093, 37.090030 )
+Patterson Pass                Road     (0, 2, -121.556654, 37.147530, -121.556000, 37.148000 )
+Tesla                         Road     (0, 2, -121.613819, 37.518060, -121.623340, 37.463890 )
+Tunnel Creek                           (0, 2, -121.610752, 37.582390, -121.624698, 37.830190 )
+Tesla                         Road     (0, 2, -121.588398, 37.408500, -121.596897, 37.439700 )
+Tesla                         Road     (0, 2, -121.570042, 37.387520, -121.573978, 37.376610 )
+Wyndham                       Pl       (0, 2, -122.027000, 37.724000, -122.028600, 37.730000 )
+Arroyo Mocho                           (0, 2, -121.625000, 37.833160, -121.624698, 37.830190 )
+Alden                         Lane     (0, 2, -121.786092, 37.560570, -121.783700, 37.560000 )
+Mines                         Road     (0, 2, -121.570515, 37.780680, -121.568708, 37.801830 )
+Arroyo Mocho                           (0, 2, -121.553409, 37.252570, -121.565204, 37.373270 )
+Mowry Slough                           (0, 2, -122.039300, 37.918000, -122.055200, 37.908000 )
+Warm Springs                  Blvd     (0, 2, -121.933956, 37.000000, -121.934300, 37.970000 )
+Access Rd 162                          (0, 2, -121.946900, 37.993000, -121.947500, 37.993000 )
+Landing                       Road     (0, 2, -121.947000, 37.809000, -121.944400, 37.820000 )
+Access Rd 29                           (0, 2, -121.933900, 37.854000, -121.934300, 37.850000 )
+Lakeview                      Blvd     (0, 2, -121.931300, 37.702000, -121.936000, 37.784000 )
+Agua Fria Creek                        (0, 2, -121.935000, 37.828000, -121.935600, 37.826000 )
+Hotchkiss                     St       (0, 2, -121.928300, 37.947000, -121.928700, 37.958000 )
+Tissiack                      Way      (0, 2, -121.920364, 37.000000, -121.920800, 37.995000 )
+Agua Fria Creek                        (0, 2, -121.925400, 37.922000, -121.928100, 37.889000 )
+Mission                       Blvd     (0, 2, -121.921400, 37.961000, -121.921700, 37.960000 )
+Aztec                         Ct       (0, 2, -121.922000, 37.920000, -121.921000, 37.920000 )
+Warren                        Ave      (0, 2, -121.931400, 37.855000, -121.933000, 37.849000 )
+Wp Railroad                            (0, 2, -121.930400, 37.856000, -121.926800, 37.789000 )
+Access Rd 25                           (0, 2, -121.928300, 37.894000, -121.928300, 37.900000 )
+Warren                        Ave      (0, 2, -121.928500, 37.866000, -121.930200, 37.859000 )
+Sp Railroad                            (0, 2, -121.927100, 37.788000, -121.918500, 37.626000 )
+Chemult Com                            (0, 2, -121.925400, 37.878000, -121.925500, 37.881000 )
+Bodie                         Ter      (0, 2, -121.925300, 37.884000, -121.924700, 37.887000 )
+Warm Springs                  Blvd     (0, 2, -121.925800, 37.851000, -121.924700, 37.833000 )
+Armata                        St       (0, 2, -121.923600, 37.858000, -121.923200, 37.853000 )
+Havasu                        St       (0, 2, -121.920900, 37.887000, -121.920400, 37.878000 )
+Toroges Creek                          (0, 2, -121.925000, 37.795000, -121.926800, 37.789000 )
+Morengo                       Way      (0, 2, -121.920900, 37.837000, -121.920300, 37.840000 )
+Toroges Creek                          (0, 2, -121.921700, 37.808000, -121.922457, 37.804850 )
+Klamath                       St       (0, 2, -121.914200, 37.982000, -121.914500, 37.978000 )
+Mission                       Blvd     (0, 3, -121.918886, 37.000000, -121.919400, 37.976000 , -121.919800, 37.975000)
+Windmill                      Ct       (0, 2, -121.916300, 37.948000, -121.916700, 37.944000 )
+Aloe                          Ct       (0, 2, -121.915800, 37.922000, -121.915200, 37.927000 )
+Sundance                      Dr       (0, 2, -121.911300, 37.988000, -121.909700, 37.992000 )
+Rancho Higuera                Road     (0, 2, -121.911200, 37.960000, -121.910500, 37.959000 )
+Curtner                       Road     (0, 2, -121.911700, 37.939000, -121.910500, 37.930000 )
+Curtner                       Road     (0, 2, -121.909000, 37.928000, -121.908400, 37.928000 )
+Topawa                        Dr       (0, 2, -121.920400, 37.878000, -121.919700, 37.880000 )
+Choctaw                       Dr       (0, 2, -121.917900, 37.870000, -121.917200, 37.876000 )
+Hoyt                          St       (0, 2, -121.919500, 37.842000, -121.918400, 37.824000 )
+Merrill                       Ave      (0, 2, -121.920500, 37.807000, -121.919600, 37.811000 )
+Gable                         Dr       (0, 2, -121.917800, 37.798000, -121.917500, 37.799000 )
+Papago                        St       (0, 2, -121.915500, 37.826000, -121.914700, 37.811000 )
+Maya                          St       (0, 2, -121.914800, 37.792000, -121.914300, 37.784000 )
+Zapotec                       Dr       (0, 2, -121.910800, 37.899000, -121.909700, 37.898000 )
+Ulmeca                        Pl       (0, 2, -121.912900, 37.786000, -121.912500, 37.779000 )
+Galindo                       Dr       (0, 2, -121.907100, 37.895000, -121.904100, 37.891000 )
+Scott Creek                            (0, 2, -121.869400, 37.814000, -121.869400, 37.803000 )
+Coyote River                           (0, 2, -121.974600, 37.617000, -121.986300, 37.648000 )
+Sp Railroad                            (0, 2, -121.973600, 37.616000, -121.973700, 37.608000 )
+Coyote River                           (0, 2, -121.950500, 37.629000, -121.958200, 37.646000 )
+Fremont                       Blvd     (0, 2, -121.934700, 37.663000, -121.932400, 37.650000 )
+Warm Springs                  Blvd     (0, 2, -121.920900, 37.769000, -121.919800, 37.751000 )
+Lyra                          St       (0, 2, -121.918600, 37.766000, -121.918400, 37.762000 )
+Warm Springs                  Blvd     (0, 2, -121.918400, 37.728000, -121.916800, 37.703000 )
+Leigh                         St       (0, 2, -121.915400, 37.776000, -121.915300, 37.774000 )
+Starlite                      Way      (0, 2, -121.916700, 37.738000, -121.916200, 37.745000 )
+Whitney                       Pl       (0, 2, -121.916800, 37.703000, -121.918800, 37.695000 )
+Arkansas                      Pl       (0, 2, -121.914800, 37.696000, -121.914900, 37.699000 )
+Tonopah                       Ct       (0, 2, -121.914000, 37.684000, -121.913800, 37.682000 )
+Cottonwood                    St       (0, 2, -121.912000, 37.740000, -121.911800, 37.735000 )
+Cottonwood                    St       (0, 2, -121.911600, 37.732000, -121.911500, 37.725000 )
+Kansas                        Way      (0, 2, -121.911500, 37.710000, -121.912000, 37.706000 )
+Plomosa                       Road     (0, 2, -121.910600, 37.703000, -121.910200, 37.696000 )
+Hobart                        Ct       (0, 2, -121.910800, 37.709000, -121.910200, 37.711000 )
+Gamay                         Dr       (0, 2, -121.909300, 37.676000, -121.909000, 37.670000 )
+Kato                          Road     (0, 2, -121.918500, 37.626000, -121.918100, 37.627000 )
+Yampa                         Way      (0, 2, -121.911700, 37.641000, -121.910900, 37.644000 )
+Scott Creek                   Road     (0, 2, -121.909800, 37.651000, -121.908600, 37.655000 )
+Scott Creek                   Road     (0, 2, -121.904700, 37.667000, -121.903400, 37.670000 )
+Scott Creek                   Road     (0, 2, -121.899900, 37.678000, -121.897500, 37.685000 )
+Oakridge                      Road     (0, 2, -121.818200, 37.930000, -121.820700, 37.931000 )
+Oakridge                      Road     (0, 2, -121.764600, 37.841000, -121.768787, 37.841420 )
+Altamont Pass                 Road     (0, 2, -121.659901, 37.444490, -121.666828, 37.410160 )
+Bernal                        Ave      (0, 2, -121.895208, 37.578370, -121.884914, 37.576030 )
+Carrol                        Road     (0, 2, -121.659839, 37.194940, -121.659626, 37.193260 )
+Flynn                         Road     (0, 2, -121.657764, 37.188910, -121.657276, 37.189520 )
+Stream                                 (0, 2, -121.648853, 37.057230, -121.651539, 37.149240 )
+Cross                         Road     (0, 2, -121.666843, 37.738700, -121.664768, 37.747400 )
+Victoria                      Lane     (0, 2, -121.663807, 37.543160, -121.663944, 37.561390 )
+Arroyo Seco                            (0, 2, -121.655796, 37.506840, -121.657215, 37.510960 )
+Tesla                         Road     (0, 2, -121.646458, 37.454500, -121.646946, 37.458470 )
+Dagnino                       Road     (0, 2, -121.746200, 37.306000, -121.746100, 37.379000 )
+Raymond                       Road     (0, 2, -121.746200, 37.306000, -121.732600, 37.305000 )
+Buckskin                      Road     (0, 2, -121.742100, 37.213000, -121.742100, 37.220000 )
+Ponderosa                     Dr       (0, 2, -121.749629, 37.123630, -121.749670, 37.117790 )
+Galloway                      St       (0, 2, -121.747300, 37.177000, -121.745900, 37.180000 )
+Altamont Creek                         (0, 2, -121.742200, 37.178000, -121.741300, 37.203000 )
+Bluebell                      Dr       (0, 2, -121.740000, 37.151000, -121.741100, 37.161000 )
+Oleander                      St       (0, 2, -121.747100, 37.126000, -121.745900, 37.129000 )
+Golf                          Dr       (0, 2, -121.744900, 37.136000, -121.744300, 37.142000 )
+Honeysuckle                   Road     (0, 2, -121.745800, 37.102000, -121.745000, 37.096000 )
+Larkspur                      Dr       (0, 2, -121.743100, 37.084000, -121.743500, 37.090000 )
+Broadmoor                     St       (0, 2, -121.731300, 37.257000, -121.731300, 37.263000 )
+Broadmoor                     St       (0, 2, -121.731400, 37.213000, -121.731400, 37.221000 )
+Libra                         Ct       (0, 2, -121.738900, 37.179000, -121.739100, 37.187000 )
+Scenic                        Ave      (0, 2, -121.741400, 37.166000, -121.738020, 37.169780 )
+Marigold                      Road     (0, 2, -121.738300, 37.123000, -121.739300, 37.128000 )
+Azalea                        Ct       (0, 2, -121.736500, 37.130000, -121.735700, 37.136000 )
+Broadmoor                     St       (0, 2, -121.731400, 37.194000, -121.731400, 37.199000 )
+Scenic                        Ave      (0, 2, -121.731400, 37.171000, -121.730500, 37.170000 )
+Berwind                       Ave      (0, 2, -121.730800, 37.183000, -121.730300, 37.181000 )
+Wisteria                      Way      (0, 2, -121.732200, 37.118000, -121.733200, 37.114000 )
+Starflower                    Way      (0, 2, -121.729800, 37.099000, -121.732300, 37.095000 )
+Lassen                        Road     (0, 2, -121.742800, 37.050000, -121.742211, 37.056870 )
+1st                           St       (0, 2, -121.742500, 37.976000, -121.741700, 37.986000 )
+Hillcrest                     Ave      (0, 2, -121.747200, 37.839000, -121.747300, 37.834000 )
+Polk                          Way      (0, 2, -121.745000, 37.867000, -121.745000, 37.858000 )
+Lincoln                       Ave      (0, 2, -121.744900, 37.849000, -121.745000, 37.832000 )
+Jackson                       Ave      (0, 2, -121.741600, 37.868000, -121.741600, 37.862000 )
+Springtown                    Blvd     (0, 2, -121.740000, 37.039000, -121.740700, 37.059000 )
+1st                           St       (0, 2, -121.740100, 37.018000, -121.740100, 37.024000 )
+Sunstream                     Lane     (0, 2, -121.734500, 37.059000, -121.734700, 37.066000 )
+1st                           St       (0, 2, -121.740200, 37.015000, -121.740100, 37.018000 )
+Sunrise                       Dr       (0, 2, -121.734700, 37.066000, -121.734300, 37.069000 )
+Moonflower                    Way      (0, 2, -121.729700, 37.075000, -121.732200, 37.071000 )
+Arroyo Las Positas                     (0, 2, -121.734900, 37.943000, -121.734048, 37.926200 )
+Bianca                        Way      (0, 2, -121.728100, 37.939000, -121.729000, 37.937000 )
+Theresa                       Way      (0, 2, -121.728900, 37.906000, -121.728000, 37.899000 )
+Arroyo Las Positas                     (0, 2, -121.730800, 37.870000, -121.727720, 37.854350 )
+Roxanne                       St       (0, 2, -121.728800, 37.853000, -121.728700, 37.849000 )
+Pasatiempo                    St       (0, 2, -121.725200, 37.262000, -121.725200, 37.270000 )
+Running Hills                 Ave      (0, 2, -121.726200, 37.220000, -121.723800, 37.213000 )
+Cypress Point                 Dr       (0, 2, -121.725100, 37.240000, -121.724000, 37.240000 )
+Singing Hills                 Ave      (0, 2, -121.726200, 37.199000, -121.723700, 37.206000 )
+Scenic                        Ave      (0, 2, -121.726200, 37.171000, -121.723200, 37.171000 )
+Vasco                         Road     (0, 2, -121.723100, 37.108000, -121.723200, 37.114000 )
+Vasco                         Road     (0, 2, -121.722900, 37.093000, -121.722800, 37.089000 )
+Herman                        Ave      (0, 2, -121.716300, 37.123000, -121.716400, 37.142000 )
+South Front                   Road     (0, 2, -121.711600, 37.134000, -121.709200, 37.145000 )
+Vasco                         Road     (0, 2, -121.721700, 37.062000, -121.720000, 37.039000 )
+Sp Railroad                            (0, 2, -121.718200, 37.017000, -121.716200, 37.025000 )
+Rachelle                      St       (0, 2, -121.725700, 37.945000, -121.725700, 37.932000 )
+Bianca                        Way      (0, 2, -121.724400, 37.946000, -121.725700, 37.945000 )
+Theresa                       Way      (0, 2, -121.727300, 37.877000, -121.727200, 37.868000 )
+Charlotte                     Way      (0, 2, -121.726100, 37.856000, -121.725400, 37.851000 )
+Charlotte                     Way      (0, 2, -121.724700, 37.839000, -121.724600, 37.836000 )
+Vaughn                        Ave      (0, 2, -121.711800, 37.061000, -121.707400, 37.080000 )
+Southern Pacific Railroad              (0, 2, -121.669500, 37.391000, -121.666889, 37.413370 )
+Western Pacific Railroad               (0, 2, -121.695300, 37.215000, -121.695500, 37.223000 )
+Greenville                    Road     (0, 2, -121.699900, 37.175000, -121.699300, 37.169000 )
+Greenville                    Road     (0, 2, -121.695900, 37.044000, -121.695900, 37.034000 )
+South Bay Aqueduct                     (0, 2, -121.678600, 37.942000, -121.676067, 37.898100 )
+Stanford                      Way      (0, 2, -121.747300, 37.828000, -121.745900, 37.826000 )
+Princeton                     Way      (0, 2, -121.750000, 37.814000, -121.747200, 37.814000 )
+Stanford                      Way      (0, 2, -121.745100, 37.826000, -121.743925, 37.822640 )
+Xavier                        Way      (0, 2, -121.745800, 37.778000, -121.745900, 37.770000 )
+Nielsen                       Lane     (0, 2, -121.744000, 37.777000, -121.744000, 37.770000 )
+Adams                         Ave      (0, 2, -121.742000, 37.829000, -121.742000, 37.822000 )
+East                          Ave      (0, 2, -121.742400, 37.799000, -121.741600, 37.799000 )
+Madison                       Ave      (0, 2, -121.741400, 37.777000, -121.741500, 37.770000 )
+Rutgers                       Way      (0, 2, -121.742100, 37.744000, -121.742000, 37.739000 )
+Wente                         St       (0, 2, -121.748600, 37.661000, -121.748600, 37.657150 )
+Almond                        Ave      (0, 2, -121.738800, 37.778000, -121.738700, 37.772000 )
+Lillian                       St       (0, 2, -121.730800, 37.829000, -121.730700, 37.824000 )
+Kathy                         Way      (0, 2, -121.729200, 37.825000, -121.729061, 37.825140 )
+Arroyo Mocho                           (0, 2, -121.731600, 37.595000, -121.718600, 37.466000 )
+East                          Ave      (0, 2, -121.729600, 37.800000, -121.725100, 37.800000 )
+East                          Ave      (0, 2, -121.720300, 37.799000, -121.717600, 37.801000 )
+Arroyo Seco                            (0, 2, -121.707300, 37.766000, -121.699700, 37.729000 )
+Greenville                    Road     (0, 2, -121.695700, 37.798000, -121.695600, 37.778000 )
+Jerrold                       Road     (0, 2, -121.690700, 37.681000, -121.690800, 37.653000 )
+Tesla                         Road     (0, 2, -121.677400, 37.632000, -121.675000, 37.633000 )
+Tesla                         Road     (0, 2, -121.670500, 37.607000, -121.667684, 37.556080 )
+Gateview                      Ave      (0, 2, -122.304700, 37.921000, -122.303300, 37.900000 )
+Cleveland                     Ave      (0, 2, -122.306100, 37.895000, -122.305800, 37.889000 )
+Pierce                        St       (0, 2, -122.304500, 37.891000, -122.304200, 37.884000 )
+Kains                         Ave      (0, 3, -122.299200, 37.983000, -122.298900, 37.970000 , -122.298400, 37.953000)
+Cerrito                       St       (0, 2, -122.302300, 37.930000, -122.301800, 37.918000 )
+Washington                    Ave      (0, 2, -122.303300, 37.900000, -122.302300, 37.905000 )
+Washington                    Ave      (0, 2, -122.301000, 37.919000, -122.300100, 37.921000 )
+Talbot                        Ave      (0, 3, -122.296700, 37.989000, -122.296400, 37.975000 , -122.295900, 37.959000)
+Brighton                      Ave      (0, 2, -122.294400, 37.979000, -122.293400, 37.979000 )
+Portland                      Ave      (0, 2, -122.294500, 37.945000, -122.293700, 37.946000 )
+Stannage                      Ave      (0, 2, -122.297000, 37.939000, -122.296500, 37.923000 )
+Cornell                       Ave      (0, 3, -122.295600, 37.925000, -122.294900, 37.906000 , -122.293900, 37.875000)
+Washington                    Ave      (0, 2, -122.291200, 37.932000, -122.290300, 37.931000 )
+Key Route                     Blvd     (0, 2, -122.292100, 37.910000, -122.292000, 37.908000 )
+Carmel                        Ave      (0, 3, -122.289100, 37.979000, -122.289300, 37.968000 , -122.289300, 37.950000)
+Curtis                        St       (0, 3, -122.286600, 37.981000, -122.286600, 37.968000 , -122.286700, 37.949000)
+Portland                      Ave      (0, 2, -122.286100, 37.949000, -122.285800, 37.949000 )
+San Carlos                    Ave      (0, 2, -122.288600, 37.931000, -122.288500, 37.910000 )
+Solano                        Ave      (0, 2, -122.289000, 37.909000, -122.288500, 37.910000 )
+San Lorenzo                   Ave      (0, 2, -122.286100, 37.930000, -122.285700, 37.929000 )
+Solano                        Ave      (0, 2, -122.287100, 37.910000, -122.286700, 37.910000 )
+Marin                         Ave      (0, 2, -122.286500, 37.891000, -122.285600, 37.895000 )
+Colusa                        Ave      (0, 2, -122.284700, 37.973000, -122.284600, 37.967000 )
+Thousand Oaks                 Blvd     (0, 2, -122.283000, 37.972000, -122.282400, 37.974000 )
+The Alameda                            (0, 2, -122.280500, 37.996000, -122.280800, 37.988000 )
+Santa Clara                   Ave      (0, 2, -122.279100, 37.994000, -122.279800, 37.989000 )
+Thousand Oaks                 Blvd     (0, 2, -122.279900, 37.975000, -122.277900, 37.972000 )
+Ensenada                      Ave      (0, 2, -122.283300, 37.933000, -122.282500, 37.928000 )
+Solano                        Ave      (0, 2, -122.282900, 37.912000, -122.281900, 37.913000 )
+Colusa                        Ave      (0, 2, -122.281200, 37.943000, -122.280500, 37.939000 )
+Colusa                        Ave      (0, 2, -122.279400, 37.922000, -122.279300, 37.916000 )
+Tulare                        Ave      (0, 2, -122.281100, 37.889000, -122.281600, 37.868000 )
+Arlington                     Ave      (0, 2, -122.276000, 37.024000, -122.276000, 37.014000 )
+Rugby                         Ave      (0, 2, -122.274000, 37.045000, -122.273800, 37.037000 )
+Maryland                      Ave      (0, 2, -122.273400, 37.031000, -122.272000, 37.032000 )
+Kentucky                      Ave      (0, 2, -122.271900, 37.026000, -122.270600, 37.013000 )
+Grizzly Peak                  Blvd     (0, 2, -122.268900, 37.035000, -122.266700, 37.020000 )
+Wildcat Canyon                Road     (0, 2, -122.265800, 37.046000, -122.264000, 37.041000 )
+Euclid                        Ave      (0, 2, -122.267100, 37.009000, -122.266600, 37.987000 )
+Arlington                     Ave      (0, 2, -122.276000, 37.988000, -122.275300, 37.974000 )
+Yosemite                      Road     (0, 2, -122.276700, 37.968000, -122.275700, 37.958000 )
+Contra Costa                  Ave      (0, 2, -122.275400, 37.944000, -122.275400, 37.918000 )
+Southampton                   Ave      (0, 2, -122.274500, 37.948000, -122.274200, 37.962000 )
+Somerset                      Pl       (0, 2, -122.273200, 37.949000, -122.272460, 37.946890 )
+The Alameda                            (0, 2, -122.276500, 37.916000, -122.276200, 37.902000 )
+The Alameda                            (0, 2, -122.276000, 37.890000, -122.275900, 37.882000 )
+San Mateo                     Road     (0, 2, -122.272400, 37.925000, -122.272800, 37.928000 )
+Indian Rock                   Path     (0, 2, -122.271700, 37.919000, -122.272700, 37.922000 )
+Marin                         Ave      (0, 2, -122.274100, 37.894000, -122.272000, 37.901000 )
+Spruce                        St       (0, 2, -122.269800, 37.990000, -122.269300, 37.981000 )
+San Luis                      Road     (0, 2, -122.271000, 37.959000, -122.270100, 37.933000 )
+Halkin                        Lane     (0, 2, -122.268400, 37.983000, -122.267700, 37.985000 )
+Poplar                        Path     (0, 2, -122.267800, 37.968000, -122.268700, 37.967000 )
+Cragmont                      Ave      (0, 2, -122.266000, 37.950000, -122.265600, 37.943000 )
+The                           Cir      (0, 2, -122.272100, 37.904000, -122.271800, 37.905000 )
+Terrace                       Walk     (0, 2, -122.271900, 37.890000, -122.270500, 37.884000 )
+Santa Barbara                 Road     (0, 2, -122.267900, 37.928000, -122.266600, 37.920000 )
+Shattuck                      Ave      (0, 2, -122.268600, 37.904000, -122.268600, 37.897000 )
+San Francisco Bay                      (0, 2, -122.317600, 37.669000, -122.310800, 37.652000 )
+Petroleum                     St       (0, 2, -122.316800, 37.130000, -122.317700, 37.130000 )
+7th                           St       (0, 2, -122.321500, 37.099000, -122.326000, 37.102000 )
+San Francisco Bay                      (0, 2, -122.311500, 37.814000, -122.309600, 37.777000 )
+Codornices Creek                       (0, 2, -122.306900, 37.818000, -122.307400, 37.817000 )
+Frontage                      Road     (0, 2, -122.307400, 37.781000, -122.304800, 37.710000 )
+Eastshore                     Hwy      (0, 2, -122.305700, 37.785000, -122.305100, 37.755000 )
+Spinnaker                     Way      (0, 2, -122.313800, 37.694000, -122.317100, 37.687000 )
+F Bay                                  (0, 2, -122.313800, 37.645000, -122.315400, 37.643000 )
+Frontage                      Road     (0, 2, -122.305100, 37.664000, -122.299400, 37.500000 )
+Buchanan                      St       (0, 2, -122.302200, 37.877000, -122.301400, 37.878000 )
+6th                           St       (0, 2, -122.301600, 37.831000, -122.301600, 37.826000 )
+Riley                         Dr       (0, 2, -122.299900, 37.858000, -122.299000, 37.860000 )
+Codornices Creek                       (0, 2, -122.298600, 37.830000, -122.299400, 37.828000 )
+Park                          Way      (0, 2, -122.303800, 37.798000, -122.303100, 37.800000 )
+6th                           St       (0, 2, -122.301200, 37.813000, -122.300600, 37.795000 )
+5th                           St       (0, 2, -122.301100, 37.775000, -122.300800, 37.763000 )
+7th                           St       (0, 3, -122.299600, 37.797000, -122.299000, 37.779000 , -122.298500, 37.767000)
+Marin                         Ave      (0, 2, -122.295600, 37.871000, -122.294800, 37.873000 )
+San Pablo                     Ave      (0, 2, -122.296100, 37.840000, -122.295900, 37.832000 )
+Kains                         Ave      (0, 2, -122.294900, 37.828000, -122.294800, 37.825000 )
+Marin                         Ave      (0, 2, -122.292800, 37.877000, -122.292500, 37.877000 )
+Stannage                      Ave      (0, 2, -122.293900, 37.844000, -122.293900, 37.828000 )
+Evelyn                        Ave      (0, 2, -122.291900, 37.843000, -122.290900, 37.830000 )
+Gilman                        St       (0, 2, -122.296200, 37.803000, -122.295100, 37.806000 )
+Jones                         St       (0, 2, -122.298200, 37.755000, -122.297100, 37.757000 )
+Gilman                        St       (0, 2, -122.294200, 37.808000, -122.293300, 37.810000 )
+Camelia                       St       (0, 2, -122.292800, 37.792000, -122.291600, 37.792000 )
+Cedar                         St       (0, 2, -122.294500, 37.750000, -122.293400, 37.753000 )
+Kains                         Ave      (0, 2, -122.292300, 37.758000, -122.292200, 37.754000 )
+Cedar                         St       (0, 2, -122.304300, 37.729000, -122.303200, 37.732000 )
+Cedar                         St       (0, 2, -122.301100, 37.737000, -122.299900, 37.739000 )
+6th                           St       (0, 2, -122.298200, 37.724000, -122.297700, 37.705000 )
+Hearst                        Ave      (0, 2, -122.302700, 37.682000, -122.301900, 37.685000 )
+Southern Pacific Railroad              (0, 2, -122.300200, 37.674000, -122.299900, 37.661000 )
+5th                           St       (0, 3, -122.297700, 37.665000, -122.297200, 37.651000 , -122.296600, 37.633000)
+8th                           St       (0, 2, -122.296900, 37.751000, -122.296700, 37.746000 )
+10th                          St       (0, 2, -122.294500, 37.750000, -122.293900, 37.732000 )
+8th                           St       (0, 2, -122.295500, 37.709000, -122.295200, 37.698000 )
+San Pablo                     Ave      (0, 3, -122.292200, 37.725000, -122.292200, 37.716000 , -122.291800, 37.704000)
+6th                           St       (0, 2, -122.296500, 37.668000, -122.296000, 37.653000 )
+7th                           St       (0, 2, -122.294500, 37.635000, -122.293800, 37.620000 )
+10th                          St       (0, 2, -122.291600, 37.662000, -122.291000, 37.644000 )
+Powell                        St       (0, 2, -122.310100, 37.375000, -122.310800, 37.375000 )
+5th                           St       (0, 2, -122.296000, 37.615000, -122.295300, 37.598000 )
+Sp Railroad                            (0, 2, -122.296500, 37.560000, -122.295900, 37.545000 )
+Dwight                        Way      (0, 3, -122.293300, 37.602000, -122.292000, 37.605000 , -122.291000, 37.607000)
+7th                           St       (0, 2, -122.291800, 37.562000, -122.291600, 37.553000 )
+Heinz                         Ave      (0, 2, -122.295300, 37.527000, -122.291200, 37.536000 )
+Anchor                        Dr       (0, 2, -122.302700, 37.374000, -122.303200, 37.383000 )
+64th                          St       (0, 2, -122.294500, 37.441000, -122.296200, 37.439000 )
+Sp Railroad                            (0, 2, -122.293900, 37.484000, -122.293600, 37.475000 )
+Hollis                        St       (0, 2, -122.291000, 37.480000, -122.291300, 37.490000 )
+Powell                        St       (0, 2, -122.292600, 37.388000, -122.293700, 37.387000 )
+Curtis                        St       (0, 2, -122.288100, 37.848000, -122.288300, 37.831000 )
+Santa Fe                      Ave      (0, 2, -122.289900, 37.817000, -122.289900, 37.815000 )
+Albany                        Ter      (0, 2, -122.286400, 37.868000, -122.285600, 37.867000 )
+Manor                         Way      (0, 2, -122.285300, 37.857000, -122.284400, 37.855000 )
+Tevlin                        St       (0, 2, -122.286600, 37.830000, -122.287100, 37.819000 )
+Evelyn                        Ave      (0, 2, -122.290600, 37.814000, -122.290300, 37.807000 )
+Gilman                        St       (0, 2, -122.288300, 37.813000, -122.287700, 37.812000 )
+Cedar                         St       (0, 2, -122.291300, 37.755000, -122.290500, 37.756000 )
+Belvedere                     Ave      (0, 2, -122.289200, 37.767000, -122.288800, 37.759000 )
+At and Sf Railroad                     (0, 2, -122.287800, 37.788000, -122.287400, 37.783000 )
+Rose                          St       (0, 2, -122.288200, 37.769000, -122.287000, 37.771000 )
+Cedar                         St       (0, 2, -122.286400, 37.760000, -122.285800, 37.762000 )
+Ventura                       Ave      (0, 2, -122.283200, 37.864000, -122.283509, 37.855500 )
+Posen                         Ave      (0, 2, -122.282800, 37.848000, -122.282200, 37.850000 )
+Hopkins                       Ct       (0, 2, -122.282900, 37.819000, -122.282200, 37.822000 )
+Monterey                      Ave      (0, 2, -122.279300, 37.863000, -122.279900, 37.857000 )
+Colusa                        Ave      (0, 2, -122.278600, 37.835000, -122.278400, 37.831000 )
+Hopkins                       St       (0, 2, -122.284000, 37.802000, -122.283400, 37.805000 )
+Rose                          St       (0, 2, -122.284300, 37.782000, -122.282900, 37.787000 )
+Keoncrest                     Dr       (0, 2, -122.284200, 37.770000, -122.283500, 37.771000 )
+Cedar                         St       (0, 2, -122.284100, 37.764000, -122.281800, 37.766000 )
+Ada                           St       (0, 2, -122.280700, 37.807000, -122.279700, 37.811000 )
+Buena                         Ave      (0, 2, -122.281300, 37.781000, -122.280700, 37.782000 )
+California                    St       (0, 2, -122.279500, 37.761000, -122.279500, 37.751000 )
+Hearst                        Ave      (0, 2, -122.291800, 37.704000, -122.288700, 37.709000 )
+Chestnut                      St       (0, 2, -122.287300, 37.722000, -122.287300, 37.711000 )
+Francisco                     St       (0, 3, -122.286000, 37.733000, -122.285000, 37.735000 , -122.283900, 37.737000)
+Addison                       St       (0, 2, -122.287400, 37.686000, -122.286400, 37.688000 )
+Addison                       St       (0, 2, -122.285600, 37.688000, -122.285400, 37.689000 )
+Cowper                        St       (0, 2, -122.290800, 37.673000, -122.289400, 37.675000 )
+Bancroft                      Way      (0, 2, -122.291000, 37.644000, -122.289900, 37.647000 )
+Browning                      St       (0, 2, -122.287400, 37.686000, -122.287200, 37.669000 )
+Curtis                        St       (0, 2, -122.287700, 37.650000, -122.287700, 37.639000 )
+Bonar                         St       (0, 2, -122.285700, 37.653000, -122.285600, 37.642000 )
+Hearst                        Ave      (0, 2, -122.285800, 37.714000, -122.284700, 37.715000 )
+Acton                         Cir      (0, 2, -122.282400, 37.681000, -122.282400, 37.688000 )
+California                    St       (0, 2, -122.279300, 37.743000, -122.279400, 37.733000 )
+Sacramento                    St       (0, 2, -122.281300, 37.703000, -122.281100, 37.695000 )
+California                    St       (0, 2, -122.279100, 37.698000, -122.278700, 37.681000 )
+Bancroft                      Way      (0, 2, -122.284600, 37.654000, -122.283747, 37.655000 )
+Channing                      Way      (0, 2, -122.284200, 37.636000, -122.283500, 37.637000 )
+Allston                       Way      (0, 2, -122.279900, 37.677000, -122.278700, 37.681000 )
+Channing                      Way      (0, 2, -122.280600, 37.641000, -122.279500, 37.641000 )
+Sonoma                        Ave      (0, 2, -122.277600, 37.858000, -122.276500, 37.856000 )
+The Alameda                            (0, 2, -122.274900, 37.863000, -122.274400, 37.855000 )
+Yolo                          Ave      (0, 2, -122.274000, 37.850000, -122.273000, 37.855000 )
+Bonita                        Ave      (0, 2, -122.272700, 37.843000, -122.272500, 37.835000 )
+Buena                         Ave      (0, 2, -122.278600, 37.792000, -122.277300, 37.797000 )
+Jaynes                        St       (0, 2, -122.278900, 37.779000, -122.278100, 37.781000 )
+Edith                         St       (0, 2, -122.276400, 37.774000, -122.276300, 37.765000 )
+Vine                          St       (0, 2, -122.275500, 37.793000, -122.274300, 37.794000 )
+Virginia                      St       (0, 2, -122.275100, 37.756000, -122.273900, 37.759000 )
+Henry                         St       (0, 2, -122.270600, 37.857000, -122.270400, 37.843000 )
+Milvia                        St       (0, 3, -122.271100, 37.817000, -122.270800, 37.800000 , -122.270700, 37.781000)
+Rose                          St       (0, 2, -122.269600, 37.820000, -122.268900, 37.820000 )
+Spruce                        St       (0, 2, -122.265900, 37.849000, -122.265500, 37.839000 )
+Milvia                        St       (0, 2, -122.270700, 37.772000, -122.270700, 37.763000 )
+Walnut                        St       (0, 2, -122.267500, 37.804000, -122.267200, 37.785000 )
+Shattuck                      Ave      (0, 2, -122.268300, 37.776000, -122.268300, 37.766000 )
+Mc Gee                        Ave      (0, 2, -122.277200, 37.746000, -122.277100, 37.736000 )
+Mc Gee                        Ave      (0, 2, -122.276800, 37.709000, -122.276800, 37.702000 )
+Hearst                        Ave      (0, 2, -122.277000, 37.727000, -122.274800, 37.731000 )
+Berkeley                      Way      (0, 2, -122.274700, 37.722000, -122.272600, 37.725000 )
+Addison                       St       (0, 2, -122.273500, 37.705000, -122.272200, 37.707000 )
+Jefferson                     Ave      (0, 3, -122.277500, 37.663000, -122.277400, 37.645000 , -122.277100, 37.627000)
+Bancroft                      Way      (0, 2, -122.275300, 37.667000, -122.274200, 37.668000 )
+Grant                         St       (0, 2, -122.273800, 37.632000, -122.273500, 37.623000 )
+Hearst                        Ave      (0, 2, -122.272600, 37.734000, -122.271500, 37.735000 )
+University                    Ave      (0, 2, -122.271100, 37.718000, -122.270000, 37.719000 )
+Hearst                        Ave      (0, 2, -122.269100, 37.738000, -122.268000, 37.740000 )
+Oxford                        St       (0, 2, -122.265700, 37.742000, -122.265600, 37.734000 )
+Shattuck                      Ave      (0, 2, -122.267500, 37.712000, -122.267400, 37.704000 )
+Center                        St       (0, 2, -122.267400, 37.704000, -122.267000, 37.704000 )
+Channing                      Way      (0, 2, -122.272700, 37.652000, -122.271700, 37.653000 )
+Milvia                        St       (0, 2, -122.269200, 37.637000, -122.269100, 37.629000 )
+Channing                      Way      (0, 2, -122.269500, 37.657000, -122.266900, 37.660000 )
+9th                           St       (0, 2, -122.289900, 37.576000, -122.289700, 37.567000 )
+10th                          St       (0, 2, -122.288600, 37.570000, -122.288300, 37.560000 )
+Dwight                        Way      (0, 2, -122.286000, 37.616000, -122.285100, 37.617000 )
+Grayson                       St       (0, 2, -122.288300, 37.560000, -122.287100, 37.563000 )
+Grayson                       St       (0, 2, -122.291600, 37.553000, -122.290600, 37.556000 )
+7th                           St       (0, 2, -122.290300, 37.511000, -122.290200, 37.508000 )
+Murray                        St       (0, 2, -122.288500, 37.513000, -122.288000, 37.514000 )
+San Pablo                     Ave      (0, 2, -122.286400, 37.537000, -122.286200, 37.532000 )
+San Pablo                     Ave      (0, 2, -122.285900, 37.518000, -122.285700, 37.515000 )
+Blake                         St       (0, 2, -122.286400, 37.605000, -122.284500, 37.608000 )
+At and Sf Railroad                     (0, 2, -122.282700, 37.611000, -122.282100, 37.603000 )
+Mabel                         St       (0, 2, -122.284100, 37.591000, -122.284000, 37.583000 )
+Ward                          St       (0, 2, -122.283800, 37.575000, -122.282700, 37.575000 )
+Sacramento                    St       (0, 2, -122.279900, 37.606000, -122.279700, 37.597000 )
+Dohr                          St       (0, 2, -122.280500, 37.560000, -122.280400, 37.548000 )
+Oregon                        St       (0, 2, -122.279400, 37.561000, -122.278900, 37.562000 )
+Park                          St       (0, 2, -122.282500, 37.544000, -122.282424, 37.542100 )
+67th                          St       (0, 2, -122.282800, 37.504000, -122.283232, 37.503100 )
+Burnett                       St       (0, 2, -122.282300, 37.539000, -122.281000, 37.541000 )
+At and Sf Railroad                     (0, 2, -122.278500, 37.544000, -122.278400, 37.535000 )
+Haskell                       St       (0, 2, -122.282500, 37.512000, -122.280500, 37.516000 )
+Prince                        St       (0, 2, -122.280200, 37.512000, -122.279400, 37.514000 )
+67th                          St       (0, 2, -122.288700, 37.495000, -122.291300, 37.490000 )
+Hollis                        St       (0, 2, -122.290100, 37.450000, -122.290300, 37.458000 )
+Hollis                        St       (0, 2, -122.289400, 37.428000, -122.289500, 37.433000 )
+Vallejo                       St       (0, 2, -122.286900, 37.460000, -122.287200, 37.466000 )
+64th                          St       (0, 2, -122.286900, 37.460000, -122.288400, 37.455000 )
+61st                          St       (0, 2, -122.286100, 37.436000, -122.287700, 37.433000 )
+Hollis                        St       (0, 2, -122.288500, 37.397000, -122.289000, 37.414000 )
+Peladeau                      St       (0, 2, -122.288800, 37.383000, -122.288900, 37.386000 )
+Sp Railroad                            (0, 2, -122.288800, 37.383000, -122.289300, 37.380000 )
+59th                          St       (0, 2, -122.286300, 37.420000, -122.287200, 37.418000 )
+Sp Railroad                            (0, 2, -122.286400, 37.393000, -122.288100, 37.387000 )
+65th                          St       (0, 2, -122.284700, 37.481000, -122.287400, 37.476000 )
+Salem                         St       (0, 2, -122.283200, 37.474000, -122.283500, 37.479000 )
+Alcatraz                      Ave      (0, 2, -122.281700, 37.475000, -122.282500, 37.475000 )
+San Pablo                     Ave      (0, 2, -122.283400, 37.442000, -122.283800, 37.450000 )
+60th                          St       (0, 2, -122.281600, 37.435000, -122.283100, 37.432000 )
+Baker                         St       (0, 2, -122.279200, 37.495000, -122.279100, 37.488000 )
+Idaho                         St       (0, 2, -122.280000, 37.437000, -122.280100, 37.446000 )
+Fremont                       St       (0, 2, -122.284000, 37.403000, -122.284100, 37.407000 )
+San Pablo                     Ave      (0, 2, -122.282200, 37.405000, -122.282400, 37.411000 )
+55th                          St       (0, 2, -122.282400, 37.386000, -122.283400, 37.384000 )
+57th                          St       (0, 2, -122.279800, 37.409000, -122.282200, 37.405000 )
+View Crest                    Ct       (0, 2, -122.165164, 37.825510, -122.164162, 37.826000 )
+Salem                         St       (0, 2, -122.279400, 37.361000, -122.279600, 37.368000 )
+Mc Gee                        Ave      (0, 2, -122.275500, 37.611000, -122.275400, 37.603000 )
+California                    St       (0, 2, -122.276700, 37.563000, -122.276700, 37.554000 )
+Grant                         St       (0, 2, -122.272900, 37.597000, -122.272700, 37.588000 )
+Grant                         St       (0, 2, -122.272500, 37.579000, -122.272300, 37.570000 )
+Tyler                         St       (0, 2, -122.278200, 37.525000, -122.278041, 37.525230 )
+Fairview                      St       (0, 2, -122.279200, 37.495000, -122.277600, 37.498000 )
+King                          St       (0, 2, -122.273800, 37.558000, -122.273700, 37.550000 )
+Ellis                         St       (0, 2, -122.272600, 37.542000, -122.272300, 37.524000 )
+King                          St       (0, 2, -122.273300, 37.504000, -122.273200, 37.496000 )
+Martin Luther King Jr         Way      (0, 2, -122.271200, 37.608000, -122.271100, 37.599000 )
+Milvia                        St       (0, 2, -122.268900, 37.593000, -122.268700, 37.585000 )
+Martin Luther King Jr         Way      (0, 2, -122.270700, 37.563000, -122.270600, 37.545000 )
+Derby                         St       (0, 2, -122.268800, 37.602000, -122.266300, 37.606000 )
+Russell                       St       (0, 2, -122.269500, 37.564000, -122.268400, 37.566000 )
+Newberry                      St       (0, 2, -122.267100, 37.568000, -122.266800, 37.554000 )
+Prince                        St       (0, 2, -122.272300, 37.524000, -122.271300, 37.525000 )
+Martin Luther King Jr         Way      (0, 2, -122.270300, 37.527000, -122.270200, 37.517000 )
+Emerson                       St       (0, 2, -122.268800, 37.543000, -122.267800, 37.544000 )
+Essex                         St       (0, 2, -122.267700, 37.537000, -122.265700, 37.539000 )
+Tremont                       St       (0, 2, -122.267000, 37.503000, -122.267200, 37.514000 )
+Alcatraz                      Ave      (0, 2, -122.279000, 37.479000, -122.277300, 37.482000 )
+62nd                          St       (0, 3, -122.276100, 37.465000, -122.276639, 37.463200 , -122.276700, 37.463000)
+At and Sf Railroad                     (0, 2, -122.276700, 37.463000, -122.276600, 37.454000 )
+Stanford                      Ave      (0, 2, -122.276400, 37.438000, -122.277300, 37.433000 )
+62nd                          St       (0, 2, -122.275500, 37.466000, -122.274900, 37.467000 )
+Genoa                         St       (0, 2, -122.272500, 37.460000, -122.272700, 37.467000 )
+Market                        St       (0, 2, -122.274100, 37.426000, -122.274200, 37.433000 )
+Adeline                       St       (0, 2, -122.272800, 37.442000, -122.272500, 37.451000 )
+Stanford                      Ave      (0, 2, -122.278100, 37.430000, -122.280200, 37.420000 )
+Grace                         Ave      (0, 2, -122.276000, 37.431000, -122.276400, 37.430000 )
+Lowell                        St       (0, 2, -122.275700, 37.410000, -122.275900, 37.417000 )
+48th                          St       (0, 2, -122.278200, 37.373000, -122.278200, 37.372240 )
+Lowell                        St       (0, 2, -122.275000, 37.384000, -122.275200, 37.389000 )
+57th                          St       (0, 2, -122.274000, 37.418000, -122.275900, 37.417000 )
+56th                          St       (0, 2, -122.273500, 37.404000, -122.274300, 37.403000 )
+54th                          St       (0, 2, -122.273300, 37.386000, -122.275000, 37.384000 )
+Martin Luther King Jr         Way      (0, 2, -122.270900, 37.489000, -122.270800, 37.481000 )
+63rd                          St       (0, 2, -122.270800, 37.481000, -122.269000, 37.484000 )
+58th                          St       (0, 2, -122.270100, 37.437000, -122.272000, 37.435000 )
+62nd                          St       (0, 2, -122.268000, 37.477000, -122.268500, 37.476000 )
+59th                          St       (0, 2, -122.266500, 37.445000, -122.267500, 37.445000 )
+58th                          St       (0, 2, -122.266300, 37.437000, -122.267300, 37.435000 )
+Arlington                     Ave      (0, 2, -122.269900, 37.430000, -122.271900, 37.428000 )
+56th                          St       (0, 2, -122.269200, 37.409000, -122.269500, 37.409000 )
+52nd                          St       (0, 2, -122.270600, 37.372000, -122.272800, 37.369000 )
+57th                          St       (0, 2, -122.266100, 37.428000, -122.267100, 37.426000 )
+52nd                          St       (0, 2, -122.268000, 37.376000, -122.268300, 37.376000 )
+Ferry                         St       (0, 2, -122.318500, 37.097000, -122.315500, 37.122000 )
+Ferry                         St       (0, 2, -122.312800, 37.147000, -122.311300, 37.159000 )
+Maritime                      St       (0, 2, -122.307200, 37.156000, -122.308400, 37.142000 )
+Temescal Creek                         (0, 2, -122.292200, 37.344000, -122.293700, 37.341000 )
+14th                          St       (0, 2, -122.299000, 37.147000, -122.300000, 37.148000 )
+Wood                          St       (0, 2, -122.299300, 37.107000, -122.298800, 37.113000 )
+24th                          St       (0, 2, -122.290100, 37.198000, -122.291400, 37.204000 )
+17th                          St       (0, 2, -122.293800, 37.149000, -122.295000, 37.155000 )
+20th                          St       (0, 2, -122.291200, 37.163000, -122.292600, 37.170000 )
+Campbell                      St       (0, 2, -122.294100, 37.123000, -122.293600, 37.129000 )
+16th                          St       (0, 2, -122.292300, 37.129000, -122.292600, 37.132000 )
+Peralta                       St       (0, 2, -122.293300, 37.113000, -122.292600, 37.116000 )
+Navy Roadway                           (0, 2, -122.309300, 37.092000, -122.312300, 37.109000 )
+Sp Railroad                            (0, 2, -122.308600, 37.087000, -122.310000, 37.085000 )
+Cedar                         St       (0, 2, -122.304500, 37.078000, -122.304100, 37.087000 )
+Avenue A                               (0, 2, -122.303500, 37.885000, -122.307600, 37.886000 )
+Pine                          St       (0, 2, -122.303000, 37.074000, -122.302600, 37.084000 )
+Pine                          St       (0, 2, -122.303400, 37.063000, -122.303200, 37.069000 )
+Wood                          St       (0, 2, -122.302000, 37.060000, -122.301900, 37.066000 )
+Willow                        St       (0, 2, -122.299800, 37.077000, -122.299600, 37.081000 )
+Campbell                      St       (0, 2, -122.298500, 37.066000, -122.298100, 37.077000 )
+3rd                           St       (0, 2, -122.297200, 37.034000, -122.298200, 37.036000 )
+12th                          St       (0, 3, -122.294300, 37.103000, -122.295500, 37.109000 , -122.296400, 37.113000)
+Peralta                       St       (0, 2, -122.297400, 37.061000, -122.297300, 37.063000 )
+Chester                       St       (0, 2, -122.294900, 37.070000, -122.294600, 37.078000 )
+Peralta                       St       (0, 2, -122.294300, 37.103000, -122.293700, 37.111000 )
+12th                          St       (0, 2, -122.292600, 37.103000, -122.293700, 37.102000 )
+10th                          St       (0, 2, -122.291800, 37.084000, -122.292000, 37.085000 )
+Cypress                       St       (0, 2, -122.293100, 37.047000, -122.292800, 37.055000 )
+7th                           St       (0, 2, -122.291600, 37.052000, -122.292600, 37.055000 )
+5th                           St       (0, 2, -122.293300, 37.041000, -122.294600, 37.045000 )
+5th                           St       (0, 2, -122.290100, 37.036000, -122.292000, 37.040000 )
+Ferro                         St       (0, 2, -122.299600, 37.975000, -122.302500, 37.968000 )
+Avenue A                               (0, 2, -122.300500, 37.885000, -122.302400, 37.885000 )
+4th                           St       (0, 2, -122.300500, 37.885000, -122.300600, 37.871000 )
+5th                           St       (0, 2, -122.297900, 37.866000, -122.297900, 37.870000 )
+Barbers Point                 Road     (0, 2, -122.295700, 37.896000, -122.296500, 37.894000 )
+Alameda                       Road     (0, 2, -122.295500, 37.875000, -122.296300, 37.871000 )
+Norfolk                       Road     (0, 2, -122.293600, 37.877000, -122.293700, 37.859000 )
+Sp Railroad                            (0, 3, -122.289800, 37.349000, -122.288700, 37.319000 , -122.288300, 37.307000)
+Hollis                        St       (0, 2, -122.286600, 37.355000, -122.286900, 37.362000 )
+Sp Railroad                            (0, 2, -122.285300, 37.355000, -122.284700, 37.334000 )
+Hollis                        St       (0, 2, -122.285100, 37.314000, -122.285700, 37.332000 )
+At and Sf Railroad                     (0, 2, -122.288000, 37.300000, -122.288000, 37.294000 )
+Cypress                       St       (0, 2, -122.288600, 37.241000, -122.288300, 37.247000 )
+Doyle                         St       (0, 2, -122.283500, 37.337000, -122.283600, 37.344000 )
+Park                          Ave      (0, 2, -122.284100, 37.316000, -122.285100, 37.314000 )
+45th                          St       (0, 2, -122.281400, 37.341000, -122.282500, 37.339000 )
+San Pablo                     Ave      (0, 2, -122.279700, 37.326000, -122.279800, 37.332000 )
+At and Sf Railroad                     (0, 2, -122.284400, 37.293000, -122.284800, 37.291000 )
+32nd                          St       (0, 2, -122.283900, 37.242000, -122.285000, 37.239000 )
+Peralta                       St       (0, 2, -122.283200, 37.243000, -122.282900, 37.246000 )
+Watts                         St       (0, 2, -122.280500, 37.280000, -122.280400, 37.285000 )
+Yerba Buena                   Ave      (0, 2, -122.278900, 37.301000, -122.284300, 37.290000 )
+Apgar                         St       (0, 2, -122.278000, 37.291000, -122.278500, 37.291000 )
+36th                          St       (0, 2, -122.277900, 37.273000, -122.279000, 37.277000 )
+35th                          St       (0, 2, -122.278300, 37.266000, -122.279200, 37.269000 )
+32nd                          St       (0, 2, -122.289300, 37.230000, -122.290100, 37.229000 )
+Willow                        St       (0, 3, -122.291300, 37.184000, -122.290100, 37.198000 , -122.289100, 37.212000)
+Cypress                       St       (0, 2, -122.288300, 37.177000, -122.288400, 37.184000 )
+Hannah                        St       (0, 2, -122.285400, 37.216000, -122.286100, 37.237000 )
+24th                          St       (0, 2, -122.286800, 37.183000, -122.287700, 37.186000 )
+Union                         St       (0, 3, -122.285400, 37.164000, -122.285200, 37.178000 , -122.284400, 37.195000)
+Cypress                       St       (0, 2, -122.289900, 37.142000, -122.289400, 37.156000 )
+17th                          St       (0, 2, -122.289800, 37.132000, -122.290000, 37.132000 )
+Cypress                       St       (0, 2, -122.290800, 37.104000, -122.290500, 37.113000 )
+Kirkham                       St       (0, 2, -122.289000, 37.122000, -122.288700, 37.129000 )
+Union                         St       (0, 2, -122.286200, 37.140000, -122.285800, 37.152000 )
+Magnolia                      St       (0, 2, -122.286400, 37.104000, -122.286000, 37.115000 )
+Union                         St       (0, 2, -122.283900, 37.210000, -122.283300, 37.227000 )
+30th                          St       (0, 2, -122.278900, 37.218000, -122.280100, 37.220000 )
+26th                          St       (0, 2, -122.279100, 37.183000, -122.280000, 37.185000 )
+Grand                         Ave      (0, 3, -122.281200, 37.154000, -122.282300, 37.156000 , -122.283400, 37.159000)
+Linden                        St       (0, 2, -122.283200, 37.096000, -122.282700, 37.112000 )
+Filbert                       St       (0, 2, -122.280200, 37.151000, -122.279600, 37.168000 )
+18th                          St       (0, 2, -122.280300, 37.116000, -122.281400, 37.119000 )
+Market                        St       (0, 2, -122.279400, 37.109000, -122.279300, 37.114000 )
+43rd                          St       (0, 2, -122.276300, 37.338000, -122.276800, 37.339000 )
+At and Sf Railroad                     (0, 2, -122.276500, 37.347000, -122.276800, 37.339000 )
+41st                          St       (0, 2, -122.276800, 37.324000, -122.277300, 37.325000 )
+42nd                          St       (0, 2, -122.275500, 37.330000, -122.276500, 37.332000 )
+Linden                        St       (0, 2, -122.275100, 37.344000, -122.275000, 37.350000 )
+Market                        St       (0, 2, -122.272700, 37.333000, -122.272500, 37.340000 )
+Adeline                       St       (0, 2, -122.278500, 37.291000, -122.278300, 37.296000 )
+37th                          St       (0, 2, -122.274300, 37.274000, -122.276800, 37.278000 )
+35th                          St       (0, 2, -122.274600, 37.257000, -122.277900, 37.266000 )
+Market                        St       (0, 2, -122.273800, 37.292000, -122.273600, 37.301000 )
+46th                          St       (0, 2, -122.266900, 37.345000, -122.269400, 37.349000 )
+43rd                          St       (0, 2, -122.267300, 37.325000, -122.269800, 37.329000 )
+Martin Luther King Jr         Way      (0, 2, -122.266700, 37.353000, -122.266600, 37.357000 )
+41st                          St       (0, 2, -122.267100, 37.308000, -122.267700, 37.308000 )
+Apgar                         St       (0, 2, -122.270900, 37.288000, -122.271900, 37.290000 )
+West                          St       (0, 2, -122.271900, 37.243000, -122.271700, 37.251000 )
+35th                          St       (0, 2, -122.268500, 37.243000, -122.269200, 37.244000 )
+Mac Arthur                    Blvd     (0, 2, -122.267300, 37.272000, -122.267600, 37.273000 )
+Bay Area Rapid Transit                 (0, 2, -122.267508, 37.253680, -122.267400, 37.258000 )
+37th                          St       (0, 2, -122.265000, 37.260000, -122.266300, 37.271000 )
+Filbert                       St       (0, 2, -122.277900, 37.215000, -122.277200, 37.233000 )
+32nd                          St       (0, 2, -122.275400, 37.227000, -122.276500, 37.228000 )
+31st                          St       (0, 2, -122.272800, 37.212000, -122.275600, 37.217000 )
+28th                          St       (0, 2, -122.275400, 37.192000, -122.276400, 37.193000 )
+Market                        St       (0, 2, -122.275400, 37.227000, -122.275100, 37.235000 )
+27th                          St       (0, 2, -122.275100, 37.181000, -122.276000, 37.180000 )
+29th                          St       (0, 2, -122.270700, 37.189000, -122.273300, 37.194000 )
+22nd                          St       (0, 2, -122.275600, 37.131000, -122.276900, 37.136000 )
+West                          St       (0, 2, -122.276000, 37.124000, -122.275600, 37.131000 )
+26th                          St       (0, 2, -122.273900, 37.171000, -122.274900, 37.172000 )
+Sycamore                      St       (0, 2, -122.271500, 37.160000, -122.273900, 37.163000 )
+West                          St       (0, 2, -122.275400, 37.136000, -122.275100, 37.141000 )
+Brush Ramp                    St       (0, 3, -122.275800, 37.107000, -122.275110, 37.113040 , -122.275000, 37.114000)
+Grand                         Ave      (0, 2, -122.272200, 37.128000, -122.272900, 37.129000 )
+Castro                        St       (0, 2, -122.273900, 37.114000, -122.273500, 37.121000 )
+Martin Luther King Jr         Way      (0, 2, -122.269800, 37.223000, -122.269700, 37.229000 )
+Martin Luther King Jr         Way      (0, 2, -122.270500, 37.194000, -122.270400, 37.199000 )
+29th                          St       (0, 2, -122.269300, 37.192000, -122.270000, 37.194000 )
+28th                          St       (0, 2, -122.267100, 37.176000, -122.268400, 37.179000 )
+29th                          St       (0, 2, -122.266000, 37.183000, -122.266800, 37.184000 )
+Martin Luther King Jr         Way      (0, 2, -122.272000, 37.146000, -122.271500, 37.153000 )
+21st                          St       (0, 2, -122.268800, 37.109000, -122.271900, 37.114000 )
+Grand                         Ave      (0, 2, -122.268400, 37.123000, -122.269233, 37.123880 )
+Telegraph                     Ave      (0, 2, -122.267900, 37.147000, -122.267800, 37.149000 )
+Telegraph                     Ave      (0, 2, -122.268800, 37.109000, -122.268600, 37.114000 )
+23rd                          St       (0, 2, -122.264800, 37.124000, -122.265800, 37.125000 )
+12th                          St       (0, 2, -122.288900, 37.094000, -122.290000, 37.094000 )
+Union                         St       (0, 2, -122.288100, 37.092000, -122.287600, 37.107000 )
+Chestnut                      St       (0, 2, -122.285300, 37.069000, -122.284800, 37.084000 )
+Union                         St       (0, 2, -122.289800, 37.042000, -122.289700, 37.044000 )
+3rd                           St       (0, 2, -122.289400, 37.019000, -122.290500, 37.021000 )
+Magnolia                      St       (0, 2, -122.289900, 37.005000, -122.289400, 37.019000 )
+7th                           St       (0, 2, -122.286400, 37.041000, -122.287500, 37.043000 )
+Linden                        St       (0, 2, -122.285400, 37.035000, -122.285300, 37.038000 )
+Linden                        St       (0, 2, -122.286700, 37.998000, -122.286400, 37.008000 )
+Linden                        St       (0, 2, -122.284200, 37.067000, -122.283700, 37.082000 )
+Myrtle                        St       (0, 2, -122.281200, 37.092000, -122.280500, 37.108000 )
+18th                          St       (0, 2, -122.277500, 37.106000, -122.279400, 37.109000 )
+12th                          St       (0, 2, -122.280700, 37.073000, -122.279600, 37.068000 )
+Brush                         St       (0, 2, -122.278800, 37.065000, -122.278400, 37.070000 )
+Market                        St       (0, 2, -122.282600, 37.021000, -122.282500, 37.023000 )
+Embarcadero                            (0, 2, -122.283600, 37.990000, -122.283000, 37.989000 )
+Brush                         St       (0, 2, -122.283000, 37.989000, -122.282700, 37.994000 )
+Castro                        St       (0, 2, -122.278700, 37.030000, -122.278500, 37.038000 )
+Martin Luther King Jr         Way      (0, 3, -122.278700, 37.014000, -122.278400, 37.020000 , -122.277900, 37.027000)
+5th                           St       (0, 3, -122.278000, 37.000000, -122.279200, 37.005000 , -122.280300, 37.009000)
+4th                           St       (0, 2, -122.277300, 37.988000, -122.278400, 37.993000 )
+2nd                           St       (0, 2, -122.278100, 37.975000, -122.279200, 37.979000 )
+18th                          St       (0, 2, -122.277500, 37.106000, -122.276200, 37.101000 )
+Castro                        St       (0, 2, -122.275700, 37.084000, -122.275300, 37.091000 )
+14th                          St       (0, 2, -122.277000, 37.073000, -122.276500, 37.071000 )
+Castro                        St       (0, 2, -122.278200, 37.045000, -122.277800, 37.052000 )
+14th                          St       (0, 2, -122.275500, 37.068000, -122.274100, 37.063000 )
+Castro                        St       (0, 2, -122.274900, 37.100000, -122.274600, 37.103000 )
+17th                          St       (0, 2, -122.274300, 37.087000, -122.272900, 37.082000 )
+San Pablo                     Ave      (0, 2, -122.271800, 37.084000, -122.272100, 37.093000 )
+Jefferson                     St       (0, 2, -122.274900, 37.049000, -122.274500, 37.055000 )
+Clay                          St       (0, 2, -122.273300, 37.051000, -122.272900, 37.059000 )
+8th                           St       (0, 2, -122.275500, 37.017000, -122.276600, 37.022000 )
+7th                           St       (0, 2, -122.275900, 37.009000, -122.277100, 37.014000 )
+5th                           St       (0, 2, -122.275600, 37.991000, -122.276800, 37.995000 )
+6th                           St       (0, 2, -122.274000, 37.993000, -122.275200, 37.998000 )
+Clay                          St       (0, 2, -122.275500, 37.017000, -122.275100, 37.024000 )
+Broadway                               (0, 2, -122.272700, 37.015000, -122.272300, 37.021000 )
+5th                           St       (0, 2, -122.273200, 37.981000, -122.274400, 37.986000 )
+Telegraph                     Ave      (0, 2, -122.269300, 37.089000, -122.269100, 37.096000 )
+16th                          St       (0, 2, -122.269900, 37.067000, -122.271300, 37.069000 )
+Telegraph                     Ave      (0, 2, -122.269900, 37.067000, -122.269700, 37.074000 )
+19th                          St       (0, 2, -122.267200, 37.079000, -122.268500, 37.084000 )
+Franklin                      St       (0, 2, -122.268700, 37.055000, -122.267900, 37.068000 )
+Broadway                               (0, 2, -122.271900, 37.028000, -122.271400, 37.036000 )
+Franklin                      St       (0, 2, -122.270200, 37.030000, -122.269800, 37.037000 )
+Franklin                      St       (0, 2, -122.271900, 37.003000, -122.271500, 37.009000 )
+Webster                       St       (0, 2, -122.270300, 37.005000, -122.269900, 37.011000 )
+Webster                       St       (0, 2, -122.269100, 37.026000, -122.268700, 37.033000 )
+14th                          St       (0, 2, -122.265900, 37.030000, -122.267100, 37.035000 )
+10th                          St       (0, 2, -122.267500, 37.002000, -122.268700, 37.007000 )
+Jackson                       St       (0, 2, -122.266800, 37.991000, -122.266400, 37.997000 )
+Washington                    St       (0, 2, -122.277200, 37.965000, -122.276900, 37.970000 )
+Embarcadero                            (0, 2, -122.276000, 37.961000, -122.274700, 37.956000 )
+Franklin                      St       (0, 2, -122.274400, 37.961000, -122.274100, 37.968000 )
+Webster                       St       (0, 2, -122.272400, 37.970000, -122.272000, 37.977000 )
+Webster                       St       (0, 2, -122.275200, 37.925000, -122.274600, 37.936000 )
+Sp Railroad                            (0, 2, -122.272400, 37.946000, -122.271300, 37.942000 )
+Webster                       St       (0, 2, -122.276000, 37.903000, -122.276000, 37.913000 )
+Posey                         Loop     (0, 2, -122.276000, 37.854000, -122.276000, 37.859000 )
+6th                           St       (0, 2, -122.269100, 37.975000, -122.270400, 37.980000 )
+Harrison                      St       (0, 2, -122.272200, 37.952000, -122.271700, 37.958000 )
+Alice                         St       (0, 2, -122.269600, 37.967000, -122.269500, 37.969000 )
+Alice                         St       (0, 2, -122.272200, 37.927000, -122.271400, 37.940000 )
+Wp Railroad                            (0, 2, -122.269300, 37.949000, -122.268200, 37.945000 )
+Jackson                       St       (0, 2, -122.268900, 37.955000, -122.268500, 37.962000 )
+Madison                       St       (0, 2, -122.267000, 37.962000, -122.266800, 37.965000 )
+Madison                       St       (0, 2, -122.268600, 37.937000, -122.268200, 37.945000 )
+Oak                           St       (0, 2, -122.266600, 37.947000, -122.266400, 37.950000 )
+Fallon                        St       (0, 2, -122.266100, 37.931000, -122.265800, 37.935000 )
+Avenue D                               (0, 2, -122.298000, 37.848000, -122.302400, 37.849000 )
+Avenue L                               (0, 2, -122.296000, 37.757000, -122.298500, 37.757000 )
+Avenue F                               (0, 2, -122.294300, 37.831000, -122.297100, 37.832000 )
+Norfolk                       Road     (0, 2, -122.293700, 37.848000, -122.294100, 37.845000 )
+8th                           St       (0, 2, -122.296000, 37.748000, -122.296000, 37.733000 )
+Redwood                       Road     (0, 2, -122.072600, 37.180790, -122.072600, 37.179000 )
+Main                          St       (0, 2, -122.290700, 37.832000, -122.290700, 37.839000 )
+Maple                         St       (0, 2, -122.288800, 37.796000, -122.287200, 37.795000 )
+Central                       Ave      (0, 2, -122.290600, 37.769000, -122.290500, 37.756000 )
+Haight                        Ave      (0, 2, -122.288100, 37.767000, -122.288300, 37.756000 )
+3rd                           St       (0, 2, -122.287300, 37.767000, -122.287300, 37.775000 )
+3rd                           St       (0, 2, -122.287400, 37.739000, -122.287400, 37.748000 )
+4th                           St       (0, 2, -122.285300, 37.738000, -122.285400, 37.742000 )
+Atlantic                      Ave      (0, 2, -122.283100, 37.804000, -122.281600, 37.803000 )
+5th                           St       (0, 2, -122.281600, 37.747000, -122.281600, 37.757000 )
+5th                           St       (0, 2, -122.281500, 37.766000, -122.281400, 37.777000 )
+Lincoln                       Ave      (0, 2, -122.278600, 37.756000, -122.276000, 37.755000 )
+Tideway                       Dr       (0, 2, -122.287700, 37.692000, -122.285400, 37.704000 )
+F Bay                                  (0, 2, -122.284200, 37.681000, -122.284100, 37.680000 )
+Central                       Ave      (0, 3, -122.278700, 37.718000, -122.277700, 37.717000 , -122.276200, 37.717000)
+Webster                       St       (0, 2, -122.276000, 37.809000, -122.276000, 37.841000 )
+Sp Railroad                            (0, 2, -122.274400, 37.802000, -122.274300, 37.795000 )
+Webster                       St       (0, 2, -122.276000, 37.775000, -122.276000, 37.785000 )
+Santa Clara                   Ave      (0, 2, -122.278700, 37.736000, -122.276100, 37.736000 )
+Sp Railroad                            (0, 2, -122.273700, 37.774000, -122.273100, 37.765000 )
+Lincoln                       Ave      (0, 2, -122.272100, 37.754000, -122.269900, 37.753000 )
+Alameda Belt Line Railroad             (0, 2, -122.269700, 37.798000, -122.270900, 37.797000 )
+Buena Vista                   Ave      (0, 2, -122.271000, 37.774000, -122.269800, 37.774000 )
+9th                           St       (0, 2, -122.270000, 37.725000, -122.270000, 37.734000 )
+Buena Vista                   Ave      (0, 2, -122.268700, 37.774000, -122.267300, 37.773000 )
+Lincoln                       Ave      (0, 2, -122.267400, 37.752000, -122.266700, 37.753000 )
+Portola                       Ave      (0, 2, -122.272300, 37.696000, -122.271300, 37.691000 )
+Central                       Ave      (0, 2, -122.270000, 37.715000, -122.268500, 37.714000 )
+Waterfall Isle                         (0, 2, -122.269900, 37.668000, -122.269400, 37.677000 )
+Caroline                      St       (0, 2, -122.267600, 37.695000, -122.267600, 37.706000 )
+Shoreline                     Dr       (0, 2, -122.271400, 37.626000, -122.269100, 37.616000 )
+Larchmont Isle                         (0, 2, -122.266700, 37.654000, -122.267100, 37.647000 )
+Shoreline                     Dr       (0, 2, -122.265700, 37.603000, -122.264800, 37.600000 )
+Wildcat Canyon                Road     (0, 2, -122.262800, 37.035000, -122.264000, 37.041000 )
+Creston                       Road     (0, 4, -122.263900, 37.002000, -122.261300, 37.986000 , -122.260200, 37.978000, -122.259800, 37.973000)
+Hilldale                      Ave      (0, 2, -122.262900, 37.960000, -122.262400, 37.956000 )
+Grizzly Peak                  Blvd     (0, 2, -122.260000, 37.965000, -122.259000, 37.952000 )
+Euclid                        Ave      (0, 2, -122.263200, 37.942000, -122.264400, 37.930000 )
+Euclid                        Ave      (0, 2, -122.262700, 37.907000, -122.262100, 37.902000 )
+Cragmont                      Ave      (0, 2, -122.261600, 37.921000, -122.260300, 37.911000 )
+Euclid                        Ave      (0, 2, -122.261800, 37.893000, -122.261200, 37.887000 )
+Keith                         Ave      (0, 2, -122.260300, 37.894000, -122.260000, 37.893000 )
+Marin                         Ave      (0, 2, -122.258000, 37.969000, -122.258000, 37.966000 )
+Latham                        Lane     (0, 2, -122.257200, 37.943000, -122.256500, 37.947000 )
+The                           Cres     (0, 2, -122.253400, 37.945000, -122.254000, 37.941000 )
+Latham                        Walk     (0, 2, -122.257500, 37.941000, -122.257200, 37.943000 )
+Keeler                        Ave      (0, 2, -122.257800, 37.906000, -122.257900, 37.899000 )
+Twain                         Ave      (0, 2, -122.257400, 37.904000, -122.256300, 37.907000 )
+Whitaker                      Ave      (0, 2, -122.255500, 37.909000, -122.255400, 37.912000 )
+Grizzly Peak                  Blvd     (0, 2, -122.254000, 37.915000, -122.253600, 37.912000 )
+Keeler                        Ave      (0, 2, -122.255200, 37.892000, -122.254900, 37.882000 )
+Hillview                      Road     (0, 2, -122.253000, 37.934000, -122.250000, 37.920000 )
+Hill                          Road     (0, 2, -122.249800, 37.881000, -122.248600, 37.868000 )
+Oak                           St       (0, 2, -122.265000, 37.877000, -122.264000, 37.879000 )
+Arch                          St       (0, 2, -122.264700, 37.846000, -122.264600, 37.844000 )
+Rose                          St       (0, 2, -122.264400, 37.830000, -122.263462, 37.833350 )
+Tamalpais                     Path     (0, 2, -122.259600, 37.866000, -122.259900, 37.872000 )
+Hawthorne                     Ter      (0, 2, -122.261000, 37.825000, -122.262000, 37.823000 )
+Leroy                         Ave      (0, 2, -122.259800, 37.819000, -122.259792, 37.818200 )
+Arch                          St       (0, 2, -122.263900, 37.790000, -122.263800, 37.782000 )
+Hilgard                       Ave      (0, 2, -122.263800, 37.782000, -122.262400, 37.783000 )
+Buena Vista                   Way      (0, 2, -122.260900, 37.805000, -122.259700, 37.809000 )
+Scenic                        Ave      (0, 2, -122.261900, 37.762000, -122.261700, 37.749000 )
+Tamalpais                     Road     (0, 2, -122.259300, 37.836000, -122.258800, 37.842000 )
+Rose                          St       (0, 2, -122.258600, 37.834000, -122.257500, 37.836000 )
+El Portal                     Ct       (0, 2, -122.255400, 37.844000, -122.255200, 37.850000 )
+La Loma                       Ave      (0, 2, -122.255400, 37.844000, -122.255900, 37.841000 )
+Hilgard                       Ave      (0, 2, -122.260300, 37.787000, -122.258500, 37.789000 )
+La Vereda                     Road     (0, 2, -122.256200, 37.801000, -122.255800, 37.792000 )
+Virginia                      St       (0, 3, -122.258200, 37.780000, -122.256500, 37.782000 , -122.255700, 37.784000)
+Hearst                        Ave      (0, 2, -122.255100, 37.757000, -122.254500, 37.758000 )
+Oxford                        St       (0, 2, -122.265100, 37.702000, -122.265100, 37.699000 )
+Durant                        Ave      (0, 2, -122.267100, 37.669000, -122.265200, 37.670000 )
+Channing                      Way      (0, 3, -122.263800, 37.664000, -122.262900, 37.665000 , -122.260600, 37.669000)
+Parker                        St       (0, 2, -122.266400, 37.623000, -122.264300, 37.626000 )
+Blake                         St       (0, 2, -122.262200, 37.639000, -122.259900, 37.642000 )
+Durant                        Ave      (0, 2, -122.260300, 37.678000, -122.258400, 37.680000 )
+Dwight                        Way      (0, 2, -122.257300, 37.654000, -122.256000, 37.656000 )
+Telegraph                     Ave      (0, 2, -122.258100, 37.635000, -122.258300, 37.625000 )
+Bowditch                      St       (0, 2, -122.255900, 37.665000, -122.255700, 37.656000 )
+Dwight                        Way      (0, 2, -122.254600, 37.657000, -122.253300, 37.659000 )
+Arcade                        Lane     (0, 2, -122.251400, 37.865000, -122.251558, 37.863160 )
+Olympus                       Ave      (0, 2, -122.251200, 37.834000, -122.250200, 37.818000 )
+Summit                        Road     (0, 2, -122.247900, 37.874000, -122.247800, 37.870000 )
+Senior                        Ave      (0, 2, -122.248700, 37.826000, -122.247300, 37.827000 )
+Parnassus                     Road     (0, 2, -122.252500, 37.814000, -122.251800, 37.814000 )
+Summit                        Road     (0, 2, -122.246400, 37.816000, -122.244700, 37.820000 )
+Prospect                      St       (0, 2, -122.249200, 37.699000, -122.249000, 37.695000 )
+Orchard                       Lane     (0, 2, -122.247500, 37.694000, -122.246700, 37.692000 )
+Piedmont                      Cres     (0, 2, -122.251300, 37.671000, -122.250300, 37.661000 )
+Piedmont                      Ave      (0, 2, -122.251100, 37.644000, -122.251000, 37.624000 )
+Dwight                        Way      (0, 2, -122.248300, 37.662000, -122.247200, 37.661000 )
+Panoramic                     Way      (0, 2, -122.245400, 37.695000, -122.243600, 37.688000 )
+Derby                         St       (0, 2, -122.246000, 37.628000, -122.245100, 37.629000 )
+Tanglewood                    Path     (0, 2, -122.243100, 37.628000, -122.242700, 37.626000 )
+Russell                       St       (0, 2, -122.266100, 37.569000, -122.265200, 37.571000 )
+Carleton                      St       (0, 2, -122.264100, 37.617000, -122.261900, 37.620000 )
+Derby                         St       (0, 2, -122.261700, 37.612000, -122.259500, 37.615000 )
+Ellsworth                     St       (0, 2, -122.261100, 37.575000, -122.260800, 37.570000 )
+Ashby                         Ave      (0, 2, -122.264000, 37.557000, -122.263000, 37.559000 )
+65th                          St       (0, 2, -122.265300, 37.505000, -122.266100, 37.504000 )
+Racine                        St       (0, 2, -122.262200, 37.494000, -122.262400, 37.501000 )
+Telegraph                     Ave      (0, 2, -122.259400, 37.547000, -122.259600, 37.541000 )
+Alcatraz                      Ave      (0, 2, -122.261700, 37.502000, -122.262400, 37.501000 )
+Telegraph                     Ave      (0, 2, -122.258500, 37.616000, -122.258600, 37.607000 )
+Telegraph                     Ave      (0, 2, -122.258900, 37.588000, -122.259100, 37.578000 )
+Oregon                        St       (0, 2, -122.258900, 37.588000, -122.256500, 37.591000 )
+Hillegass                     Ave      (0, 2, -122.255700, 37.619000, -122.255400, 37.601000 )
+Stuart                        St       (0, 2, -122.255400, 37.601000, -122.254100, 37.602000 )
+Hillegass                     Ave      (0, 2, -122.254500, 37.561000, -122.254100, 37.539000 )
+Dana                          St       (0, 2, -122.258300, 37.548000, -122.258200, 37.541000 )
+Prince                        St       (0, 2, -122.258200, 37.541000, -122.256800, 37.543000 )
+Dana                          St       (0, 2, -122.257800, 37.501000, -122.257900, 37.507000 )
+Hospital                      Dr       (0, 2, -122.257000, 37.548000, -122.255900, 37.549000 )
+Woolsey                       St       (0, 2, -122.255300, 37.538000, -122.254500, 37.538000 )
+Hillegass                     Ave      (0, 2, -122.254100, 37.503000, -122.254300, 37.513000 )
+Shattuck                      Ave      (0, 2, -122.264800, 37.468000, -122.264800, 37.474000 )
+63rd                          St       (0, 2, -122.262200, 37.492000, -122.265100, 37.489000 )
+63rd                          St       (0, 2, -122.260400, 37.490000, -122.262100, 37.487000 )
+60th                          St       (0, 2, -122.260600, 37.469000, -122.261500, 37.466000 )
+57th                          St       (0, 2, -122.260900, 37.433000, -122.262000, 37.435000 )
+Vicente                       St       (0, 2, -122.259800, 37.427000, -122.259800, 37.432000 )
+55th                          St       (0, 2, -122.264100, 37.408000, -122.266700, 37.404000 )
+Bay Area Rapid Transit                 (0, 2, -122.265800, 37.337000, -122.264400, 37.380000 )
+Aileen                        St       (0, 2, -122.261200, 37.420000, -122.262200, 37.421000 )
+Telegraph                     Ave      (0, 2, -122.261400, 37.411000, -122.261300, 37.414000 )
+Telegraph                     Ave      (0, 2, -122.261800, 37.384000, -122.261742, 37.388060 )
+Claremont                     Ave      (0, 2, -122.261200, 37.386000, -122.260400, 37.393000 )
+63rd                          St       (0, 3, -122.256300, 37.495000, -122.257600, 37.493000 , -122.259600, 37.490000)
+59th                          St       (0, 2, -122.258700, 37.456000, -122.260800, 37.454000 )
+Ayala                         Ave      (0, 2, -122.258700, 37.429000, -122.258400, 37.435000 )
+62nd                          St       (0, 2, -122.254000, 37.494000, -122.256300, 37.491000 )
+Forest                        St       (0, 2, -122.254100, 37.443000, -122.254700, 37.445000 )
+Locksley                      Ave      (0, 2, -122.254700, 37.422000, -122.253400, 37.438000 )
+Claremont                     Ave      (0, 2, -122.259100, 37.408000, -122.258600, 37.413000 )
+Bay Area Rapid Transit                 (0, 2, -122.257100, 37.427000, -122.256300, 37.431000 )
+Miles                         Ave      (0, 2, -122.259900, 37.373000, -122.258900, 37.382000 )
+Cavour                        St       (0, 2, -122.255500, 37.375000, -122.256100, 37.379000 )
+Shafter                       Ave      (0, 2, -122.256900, 37.383000, -122.255600, 37.399000 )
+Clifton                       St       (0, 2, -122.252600, 37.383000, -122.253300, 37.388000 )
+Stuart                        St       (0, 3, -122.251800, 37.600000, -122.250700, 37.601000 , -122.249100, 37.606000)
+Ashby                         Ave      (0, 2, -122.252600, 37.574000, -122.251800, 37.574000 )
+Ashby                         Ave      (0, 2, -122.249400, 37.579000, -122.248500, 37.579000 )
+Webster                       St       (0, 2, -122.247400, 37.563000, -122.246100, 37.562000 )
+Alcatraz                      Ave      (0, 2, -122.252500, 37.515000, -122.253100, 37.514000 )
+Claremont                     Ave      (0, 2, -122.250800, 37.509000, -122.250500, 37.514000 )
+Auburn                        Ave      (0, 2, -122.250000, 37.489000, -122.250350, 37.494500 )
+The Uplands                            (0, 3, -122.247700, 37.545000, -122.246000, 37.543000 , -122.245000, 37.538000)
+Florio                        St       (0, 2, -122.250400, 37.502000, -122.249400, 37.504000 )
+Harwood                       Ave      (0, 2, -122.247100, 37.497000, -122.246600, 37.498000 )
+Avalon                        Ave      (0, 2, -122.247700, 37.597000, -122.246000, 37.598000 )
+Claremont                     Ave      (0, 2, -122.246000, 37.565000, -122.246100, 37.562000 )
+Claremont                     Ave      (0, 2, -122.242900, 37.607000, -122.242100, 37.609000 )
+Tunnel                        Road     (0, 2, -122.242700, 37.581000, -122.241400, 37.579000 )
+Tunnel                        Road     (0, 2, -122.240500, 37.574000, -122.240200, 37.570000 )
+The South Crossways                    (0, 2, -122.244200, 37.529000, -122.244300, 37.522000 )
+Ross                          Cir      (0, 2, -122.246600, 37.502000, -122.247400, 37.514000 )
+Chabot                        Ct       (0, 2, -122.243200, 37.489000, -122.243900, 37.500000 )
+El Camino Real                         (0, 2, -122.240900, 37.559000, -122.240700, 37.556000 )
+Chabot Crest                           (0, 2, -122.242500, 37.504000, -122.242700, 37.514000 )
+College                       Ave      (0, 2, -122.251600, 37.474000, -122.251800, 37.483000 )
+Shafter                       Ave      (0, 2, -122.253900, 37.418000, -122.252700, 37.434000 )
+Harwood                       Ave      (0, 2, -122.250000, 37.489000, -122.247500, 37.495000 )
+Mc Millan                     St       (0, 2, -122.247900, 37.454000, -122.248000, 37.457000 )
+College                       Ave      (0, 2, -122.251100, 37.421000, -122.251200, 37.429000 )
+Ada                           St       (0, 2, -122.248700, 37.398000, -122.249600, 37.401000 )
+College                       Ave      (0, 2, -122.250600, 37.367000, -122.250800, 37.374000 )
+Broadway                               (0, 2, -122.247200, 37.418000, -122.246800, 37.426000 )
+Thomas                        Ave      (0, 2, -122.248700, 37.375000, -122.247900, 37.390000 )
+Presley                       Way      (0, 2, -122.246100, 37.462000, -122.246200, 37.465000 )
+Broadway                               (0, 3, -122.245000, 37.450000, -122.244300, 37.460000 , -122.243600, 37.469000)
+Rockridge                     Blvd     (0, 2, -122.242000, 37.457000, -122.241600, 37.464000 )
+Rockridge                     Blvd     (0, 2, -122.242400, 37.454000, -122.241700, 37.453000 )
+Acacia                        Ave      (0, 2, -122.241500, 37.435000, -122.240700, 37.437000 )
+Manila                        Ave      (0, 2, -122.244800, 37.425000, -122.243700, 37.428000 )
+Broadway                      Ter      (0, 2, -122.242900, 37.393000, -122.241300, 37.397000 )
+Rispen                        Dr       (0, 2, -122.234200, 37.621000, -122.232600, 37.634000 )
+Grizzly Peak                  Blvd     (0, 2, -122.221300, 37.638000, -122.212700, 37.581000 )
+Alvarado                      Road     (0, 2, -122.239100, 37.573000, -122.239700, 37.580000 )
+Sunset                        Trl      (0, 2, -122.237500, 37.574000, -122.237200, 37.571000 )
+Willow                        Walk     (0, 2, -122.236400, 37.572000, -122.236640, 37.567800 )
+Tunnel                        Road     (0, 2, -122.238200, 37.555000, -122.237400, 37.553000 )
+Golden Gate                   Way      (0, 2, -122.237800, 37.497000, -122.238055, 37.495220 )
+Bay Area Rapid Transit                 (0, 2, -122.234900, 37.525000, -122.233900, 37.532000 )
+Alvarado                      Road     (0, 2, -122.233900, 37.608000, -122.232200, 37.616000 )
+Vicente                       Pl       (0, 2, -122.232900, 37.572000, -122.232000, 37.578000 )
+Westview                      Pl       (0, 2, -122.230900, 37.588000, -122.230300, 37.585000 )
+Dorothy                       Pl       (0, 2, -122.230100, 37.566000, -122.230200, 37.571000 )
+North Hill                    Ct       (0, 2, -122.231000, 37.533000, -122.232500, 37.541000 )
+Caldecott                     Lane     (0, 2, -122.231200, 37.512000, -122.226100, 37.491000 )
+Hiller                        Dr       (0, 2, -122.227500, 37.551000, -122.226300, 37.542000 )
+Broadway                               (0, 2, -122.239100, 37.493000, -122.238600, 37.495000 )
+Novara                        Road     (0, 2, -122.237700, 37.470000, -122.236800, 37.472000 )
+Alpine                        Ter      (0, 2, -122.237400, 37.445000, -122.237700, 37.459000 )
+Buena Vista                   Ave      (0, 2, -122.235900, 37.470000, -122.235300, 37.468000 )
+Acacia                        Ave      (0, 2, -122.236400, 37.436000, -122.236400, 37.443000 )
+Acacia                        Ave      (0, 2, -122.235300, 37.457000, -122.234400, 37.461000 )
+Westminster                   Dr       (0, 2, -122.240900, 37.406000, -122.239100, 37.413000 )
+Ostrander                     Road     (0, 2, -122.236400, 37.413000, -122.235600, 37.429000 )
+Clarewood                     Lane     (0, 2, -122.234300, 37.393000, -122.232365, 37.388020 )
+Goldengate                    Ave      (0, 2, -122.232400, 37.451000, -122.232584, 37.451920 )
+Buena Vista                   Ave      (0, 2, -122.230100, 37.437000, -122.229500, 37.424000 )
+Mandalay                      Road     (0, 2, -122.232200, 37.397000, -122.232100, 37.403000 )
+Hermosa                       Ave      (0, 3, -122.230900, 37.415000, -122.231000, 37.404000 , -122.230100, 37.404000)
+Sheridan                      Road     (0, 3, -122.227900, 37.425000, -122.225300, 37.411000 , -122.222300, 37.377000)
+Biehs                         Ct       (0, 2, -122.228900, 37.386000, -122.228300, 37.391000 )
+Schooner Hill                          (0, 2, -122.226300, 37.560000, -122.226900, 37.564000 )
+Buckingham                    Blvd     (0, 2, -122.223100, 37.590000, -122.221400, 37.606000 )
+Binnacle Hill                          (0, 2, -122.226900, 37.533000, -122.227400, 37.523000 )
+Star View                     Ct       (0, 2, -122.225100, 37.516000, -122.224800, 37.511000 )
+Broadway                               (0, 2, -122.221200, 37.500000, -122.220400, 37.517000 )
+Mountain                      Blvd     (0, 2, -122.226100, 37.465000, -122.224100, 37.451000 )
+Proctor                       Ave      (0, 2, -122.226700, 37.406000, -122.225100, 37.386000 )
+Florence                      Ave      (0, 2, -122.225300, 37.376000, -122.225000, 37.373000 )
+Pinewood                      Road     (0, 2, -122.221900, 37.424000, -122.221100, 37.429000 )
+Estates                       Dr       (0, 2, -122.222500, 37.370000, -122.218617, 37.341670 )
+Mountain                      Blvd     (0, 2, -122.222000, 37.435000, -122.220400, 37.436000 )
+Avoca                         Ave      (0, 2, -122.221100, 37.413000, -122.220400, 37.416000 )
+Capricorn                     Ave      (0, 2, -122.217600, 37.404000, -122.216400, 37.384000 )
+Taurus                        Ave      (0, 2, -122.215900, 37.416000, -122.212800, 37.389000 )
+48th                          St       (0, 2, -122.263300, 37.354000, -122.264300, 37.356000 )
+Shattuck                      Ave      (0, 2, -122.263300, 37.339000, -122.263400, 37.347000 )
+41st                          St       (0, 2, -122.265500, 37.305000, -122.265300, 37.314000 )
+Telegraph                     Ave      (0, 2, -122.262500, 37.353000, -122.262300, 37.361000 )
+Webster                       St       (0, 2, -122.259500, 37.330000, -122.259600, 37.337000 )
+Webster                       St       (0, 2, -122.259900, 37.305000, -122.259600, 37.313000 )
+Telegraph                     Ave      (0, 2, -122.264700, 37.266000, -122.264700, 37.274000 )
+37th                          St       (0, 2, -122.261300, 37.249000, -122.265000, 37.257000 )
+Mac Arthur                    Blvd     (0, 2, -122.262000, 37.258000, -122.263100, 37.260000 )
+40th                          St       (0, 2, -122.258500, 37.286000, -122.259400, 37.288000 )
+38th                          St       (0, 2, -122.258700, 37.268000, -122.259700, 37.269000 )
+Lawton                        Ave      (0, 2, -122.256300, 37.360000, -122.256000, 37.366000 )
+Shafter                       Ave      (0, 2, -122.258600, 37.313000, -122.258300, 37.323000 )
+49th                          St       (0, 2, -122.254500, 37.348000, -122.255200, 37.349000 )
+Emerald                       St       (0, 2, -122.255700, 37.309000, -122.255700, 37.315000 )
+41st                          St       (0, 2, -122.256200, 37.290000, -122.257100, 37.291000 )
+38th                          St       (0, 2, -122.257100, 37.266000, -122.258300, 37.268000 )
+Ridgeway                      Ave      (0, 2, -122.253900, 37.299000, -122.254800, 37.302000 )
+Howe                          St       (0, 2, -122.254100, 37.265000, -122.253700, 37.267000 )
+34th                          St       (0, 2, -122.263700, 37.223000, -122.264700, 37.225000 )
+29th                          St       (0, 2, -122.263300, 37.179000, -122.264000, 37.181000 )
+Broadway                               (0, 3, -122.263200, 37.167000, -122.262600, 37.177000 , -122.261700, 37.190000)
+Broadway                               (0, 2, -122.259800, 37.222000, -122.259600, 37.227000 )
+Webster                       St       (0, 2, -122.264300, 37.145000, -122.264200, 37.152000 )
+27th                          St       (0, 2, -122.262500, 37.151000, -122.262600, 37.154000 )
+22nd                          St       (0, 2, -122.265000, 37.114000, -122.265500, 37.114000 )
+Orin                          Dr       (0, 2, -122.262300, 37.133000, -122.262600, 37.142000 )
+Hamilton                      Pl       (0, 2, -122.259200, 37.151000, -122.260400, 37.160000 )
+Harrison St                   Ramp     (0, 2, -122.262000, 37.110000, -122.261300, 37.114000 )
+Croxton                       Ave      (0, 2, -122.259100, 37.219000, -122.258400, 37.211000 )
+Richmond                      Blvd     (0, 2, -122.258400, 37.211000, -122.257300, 37.218000 )
+Richmond                      Blvd     (0, 2, -122.259200, 37.195000, -122.258700, 37.201000 )
+Harrison                      St       (0, 2, -122.257700, 37.172000, -122.256900, 37.176000 )
+Santa Clara                   Ave      (0, 2, -122.255100, 37.211000, -122.254000, 37.205000 )
+Fairmount                     Ave      (0, 2, -122.256100, 37.194000, -122.255300, 37.202000 )
+Pearl                         St       (0, 2, -122.255100, 37.179000, -122.254600, 37.174000 )
+Harrison                      St       (0, 2, -122.254400, 37.197000, -122.254200, 37.199000 )
+Oakland                       Ave      (0, 2, -122.258300, 37.162000, -122.256900, 37.167000 )
+Montecito                     Ave      (0, 2, -122.260200, 37.114000, -122.258900, 37.122000 )
+Lee                           St       (0, 2, -122.256100, 37.115000, -122.256900, 37.097000 )
+Jayne                         Ave      (0, 2, -122.256600, 37.130000, -122.254900, 37.142000 )
+Bellevue                      Ave      (0, 2, -122.252900, 37.130000, -122.252100, 37.111000 )
+Broadway                               (0, 2, -122.253900, 37.316000, -122.252500, 37.337000 )
+Terrace                       St       (0, 2, -122.253900, 37.299000, -122.252300, 37.321000 )
+Montgomery                    St       (0, 2, -122.251800, 37.292000, -122.250400, 37.305000 )
+Montgomery                    St       (0, 2, -122.248900, 37.316000, -122.247100, 37.330000 )
+Ridgeway                      Ave      (0, 2, -122.251300, 37.286000, -122.251800, 37.292000 )
+Piedmont                      Ave      (0, 2, -122.252700, 37.263000, -122.252100, 37.268000 )
+Glen                          Ave      (0, 2, -122.251400, 37.257000, -122.250500, 37.258000 )
+Arroyuelo                     Ave      (0, 2, -122.249600, 37.271000, -122.249564, 37.272800 )
+Rose                          Ave      (0, 2, -122.247600, 37.256000, -122.246900, 37.268000 )
+Pleasant Valley               Ct       (0, 2, -122.245500, 37.298000, -122.243900, 37.305000 )
+Moraga                        Ave      (0, 2, -122.244700, 37.288000, -122.242300, 37.302000 )
+York                          Dr       (0, 2, -122.241500, 37.283000, -122.240600, 37.261000 )
+Cambridge                     Way      (0, 2, -122.244200, 37.231000, -122.241900, 37.244000 )
+Fairmount                     Ave      (0, 2, -122.252500, 37.230000, -122.251100, 37.234000 )
+Oakland                       Ave      (0, 2, -122.252900, 37.197000, -122.252100, 37.203000 )
+Vernon                        St       (0, 2, -122.250900, 37.179000, -122.250300, 37.184000 )
+Sunnyside                     Ave      (0, 2, -122.246900, 37.232000, -122.245300, 37.221000 )
+Jean                          St       (0, 3, -122.247700, 37.180000, -122.246800, 37.187000 , -122.245900, 37.196000)
+Chetwood                      St       (0, 2, -122.252100, 37.167000, -122.251300, 37.169000 )
+Van Buren                     Ave      (0, 2, -122.252100, 37.111000, -122.251200, 37.113000 )
+Alta Vista                    Ave      (0, 2, -122.248300, 37.174000, -122.247300, 37.167000 )
+Grand                         Ave      (0, 2, -122.247900, 37.111000, -122.247800, 37.115000 )
+Olive                         Ave      (0, 2, -122.246200, 37.219000, -122.245100, 37.202000 )
+Grand                         Ave      (0, 2, -122.244200, 37.180000, -122.244000, 37.184000 )
+Fairview                      Ave      (0, 2, -122.240400, 37.220000, -122.240100, 37.227000 )
+Boulevard                     Way      (0, 2, -122.242700, 37.180000, -122.242300, 37.181000 )
+Valle Vista                   Ave      (0, 2, -122.246000, 37.162000, -122.246100, 37.168000 )
+Walker                        Ave      (0, 2, -122.243700, 37.153000, -122.243300, 37.161000 )
+Warfield                      Ave      (0, 2, -122.244000, 37.124000, -122.243500, 37.130000 )
+Vermont                       St       (0, 2, -122.242400, 37.160000, -122.241500, 37.168000 )
+Warfield                      Ave      (0, 2, -122.240400, 37.153000, -122.240200, 37.157000 )
+Erie                          St       (0, 2, -122.241900, 37.122000, -122.240800, 37.134000 )
+21st                          St       (0, 2, -122.262400, 37.099000, -122.264300, 37.102000 )
+17th                          St       (0, 2, -122.264200, 37.057000, -122.265500, 37.059000 )
+Harrison                      St       (0, 2, -122.262100, 37.108000, -122.262000, 37.110000 )
+Jackson                       St       (0, 2, -122.264600, 37.025000, -122.264100, 37.034000 )
+12th                          St       (0, 2, -122.264400, 37.006000, -122.265500, 37.011000 )
+13th                          St       (0, 2, -122.262800, 37.009000, -122.263900, 37.014000 )
+17th                          St       (0, 2, -122.260700, 37.043000, -122.261900, 37.047000 )
+12th                          St       (0, 2, -122.261100, 37.996000, -122.262000, 37.998000 )
+Perkins                       St       (0, 2, -122.255300, 37.105000, -122.255700, 37.096000 )
+Staten                        Ave      (0, 2, -122.253300, 37.103000, -122.253400, 37.094000 )
+Lakeshore                     Ave      (0, 2, -122.258600, 37.990000, -122.255600, 37.006000 )
+Hanover                       Ave      (0, 2, -122.253600, 37.031000, -122.253000, 37.031000 )
+1st                           Ave      (0, 2, -122.256700, 37.992000, -122.255800, 37.999000 )
+18th                          St       (0, 2, -122.254000, 37.012000, -122.253500, 37.010000 )
+Oak                           St       (0, 2, -122.265300, 37.967000, -122.264900, 37.974000 )
+Fallon                        St       (0, 2, -122.263700, 37.969000, -122.263300, 37.977000 )
+Wp Railroad                            (0, 2, -122.262000, 37.923000, -122.260700, 37.921000 )
+Oakland Inner Harbor                   (0, 2, -122.262500, 37.913000, -122.260016, 37.894840 )
+Merritt Channel                        (0, 2, -122.259100, 37.968000, -122.260600, 37.942000 )
+12th                          St       (0, 2, -122.256600, 37.974000, -122.256000, 37.971000 )
+8th                           St       (0, 2, -122.257200, 37.935000, -122.256523, 37.928980 )
+15th                          St       (0, 2, -122.253400, 37.976000, -122.252500, 37.969000 )
+10th                          St       (0, 2, -122.255300, 37.935000, -122.254500, 37.927000 )
+10th                          St       (0, 2, -122.254100, 37.924000, -122.253700, 37.920000 )
+8th                           St       (0, 2, -122.254600, 37.914000, -122.253300, 37.909000 )
+Wp Railroad                            (0, 2, -122.254000, 37.902000, -122.250600, 37.891000 )
+Burk                          St       (0, 2, -122.250100, 37.101000, -122.250200, 37.106000 )
+Brooklyn                      Ave      (0, 2, -122.250200, 37.055000, -122.249500, 37.053000 )
+Beacon                        St       (0, 2, -122.248400, 37.090000, -122.247200, 37.088000 )
+Merritt                       Ave      (0, 2, -122.249500, 37.053000, -122.248700, 37.066000 )
+Wesley                        Ave      (0, 2, -122.248200, 37.056000, -122.247600, 37.059000 )
+Athol                         Ave      (0, 2, -122.253500, 37.010000, -122.252300, 37.016000 )
+17th                          St       (0, 2, -122.251400, 37.990000, -122.250700, 37.982000 )
+Stow                          Ave      (0, 2, -122.250800, 37.042000, -122.249100, 37.043000 )
+Hanover                       Ave      (0, 2, -122.248800, 37.034000, -122.247700, 37.037000 )
+19th                          St       (0, 2, -122.248800, 37.994000, -122.247900, 37.986000 )
+20th                          St       (0, 2, -122.246800, 37.994000, -122.245900, 37.985000 )
+Merritt                       Ave      (0, 2, -122.247100, 37.078000, -122.244600, 37.078000 )
+Wesley                        Ave      (0, 2, -122.245700, 37.068000, -122.244600, 37.078000 )
+Cleveland                     St       (0, 2, -122.243500, 37.048000, -122.241800, 37.042000 )
+Haddon                        Pl       (0, 2, -122.241600, 37.099000, -122.241100, 37.103000 )
+Hillgirt                      Cir      (0, 2, -122.242900, 37.079000, -122.242400, 37.076000 )
+Mac Arthur                    Blvd     (0, 2, -122.240500, 37.072000, -122.240200, 37.070000 )
+Brooklyn                      Ave      (0, 2, -122.245500, 37.040000, -122.244500, 37.036000 )
+Park                          Blvd     (0, 2, -122.246100, 37.013000, -122.245700, 37.013000 )
+7th                           Ave      (0, 2, -122.245900, 37.985000, -122.244900, 37.991000 )
+Brooklyn                      Ave      (0, 2, -122.242500, 37.029000, -122.241600, 37.026000 )
+7th                           Ave      (0, 2, -122.241300, 37.015000, -122.240600, 37.021000 )
+Ivy                           Dr       (0, 2, -122.243400, 37.010000, -122.241300, 37.015000 )
+4th                           Ave      (0, 2, -122.253400, 37.976000, -122.252400, 37.983000 )
+5th                           Ave      (0, 2, -122.251600, 37.975000, -122.250700, 37.982000 )
+7th                           Ave      (0, 2, -122.252800, 37.940000, -122.251800, 37.947000 )
+9th                           Ave      (0, 2, -122.251600, 37.922000, -122.251100, 37.925000 )
+8th                           Ave      (0, 2, -122.248900, 37.952000, -122.248000, 37.958000 )
+9th                           Ave      (0, 2, -122.249100, 37.938000, -122.248000, 37.945000 )
+15th                          St       (0, 2, -122.247200, 37.923000, -122.246400, 37.915000 )
+Sp Railroad                            (0, 2, -122.250600, 37.891000, -122.250100, 37.889000 )
+11th                          Ave      (0, 2, -122.249300, 37.910000, -122.248300, 37.916000 )
+12th                          St       (0, 2, -122.246900, 37.891000, -122.245900, 37.882000 )
+18th                          St       (0, 2, -122.246100, 37.957000, -122.245300, 37.950000 )
+20th                          St       (0, 2, -122.243300, 37.963000, -122.242400, 37.955000 )
+Foothill                      Blvd     (0, 2, -122.245400, 37.921000, -122.244200, 37.910000 )
+21st                          St       (0, 2, -122.243000, 37.975000, -122.242300, 37.970000 )
+23rd                          St       (0, 2, -122.240400, 37.982000, -122.239500, 37.974000 )
+18th                          St       (0, 2, -122.242400, 37.924000, -122.241600, 37.918000 )
+14th                          Ave      (0, 2, -122.240800, 37.923000, -122.240700, 37.925000 )
+Solano                        Way      (0, 2, -122.245100, 37.889000, -122.244300, 37.885000 )
+Marin                         Way      (0, 2, -122.244300, 37.898000, -122.243600, 37.893000 )
+8th                           St       (0, 2, -122.245900, 37.882000, -122.245600, 37.879000 )
+16th                          Ave      (0, 2, -122.243800, 37.874000, -122.243800, 37.876000 )
+Wp Railroad                            (0, 2, -122.243400, 37.865000, -122.241000, 37.851000 )
+14th                          Ave      (0, 2, -122.242600, 37.911000, -122.241900, 37.916000 )
+16th                          Ave      (0, 2, -122.242200, 37.892000, -122.241800, 37.896000 )
+Foothill                      Blvd     (0, 2, -122.241400, 37.900000, -122.240300, 37.893000 )
+14th                          St       (0, 2, -122.241900, 37.877000, -122.240800, 37.871000 )
+15th                          St       (0, 2, -122.241000, 37.885000, -122.240100, 37.879000 )
+19th                          Ave      (0, 2, -122.240800, 37.854000, -122.240600, 37.856000 )
+Bonita                        Ave      (0, 2, -122.235500, 37.306000, -122.235000, 37.296000 )
+Ramona                        Ave      (0, 2, -122.239100, 37.291000, -122.237300, 37.293000 )
+Park                          Way      (0, 2, -122.236600, 37.289000, -122.235700, 37.288000 )
+Blair                         Ave      (0, 2, -122.236400, 37.263000, -122.235900, 37.267000 )
+Mesa                          Ave      (0, 2, -122.233000, 37.304000, -122.232200, 37.291000 )
+Park                          Way      (0, 2, -122.233900, 37.288000, -122.233200, 37.288000 )
+Pala                          Ave      (0, 2, -122.232800, 37.280000, -122.231900, 37.283000 )
+Oakland                       Ave      (0, 2, -122.233200, 37.258000, -122.231900, 37.262000 )
+Scenic                        Ave      (0, 2, -122.228200, 37.278000, -122.228400, 37.296000 )
+Mountain                      Ave      (0, 2, -122.229100, 37.256000, -122.228800, 37.257000 )
+Oakland                       Ave      (0, 3, -122.240000, 37.235000, -122.239300, 37.237000 , -122.238700, 37.240000)
+Nova                          Dr       (0, 2, -122.239700, 37.213000, -122.239400, 37.214000 )
+Larmer                        Ct       (0, 2, -122.237100, 37.215000, -122.236500, 37.212000 )
+Warfield                      Ave      (0, 2, -122.240300, 37.160000, -122.238200, 37.178000 )
+Hillside                      Ct       (0, 2, -122.234500, 37.231000, -122.234200, 37.225000 )
+Winsor                        Ave      (0, 2, -122.235600, 37.203000, -122.235100, 37.203000 )
+Kenmore                       Ave      (0, 2, -122.237800, 37.173000, -122.238600, 37.162000 )
+Santa Ray                     Ave      (0, 2, -122.239300, 37.122000, -122.238100, 37.113000 )
+Calmar                        Ave      (0, 2, -122.238400, 37.105000, -122.238100, 37.113000 )
+Paloma                        Ave      (0, 2, -122.234700, 37.126000, -122.235300, 37.136000 )
+Wildwood                      Ave      (0, 2, -122.234100, 37.194000, -122.232800, 37.199000 )
+Requa                         Road     (0, 2, -122.232300, 37.199000, -122.229300, 37.210000 )
+Highland                      Ave      (0, 2, -122.228600, 37.210000, -122.228500, 37.200000 )
+Annerley                      Road     (0, 2, -122.232800, 37.168000, -122.232500, 37.170000 )
+Park                          Lane     (0, 2, -122.230900, 37.163000, -122.231300, 37.166000 )
+Carlston                      Ave      (0, 2, -122.230000, 37.132000, -122.230700, 37.141000 )
+Portal                        Ave      (0, 2, -122.228100, 37.148000, -122.228200, 37.157000 )
+Clarendon                     Cres     (0, 2, -122.227800, 37.126000, -122.226600, 37.119000 )
+Hilltop                       Cres     (0, 2, -122.225600, 37.329000, -122.224600, 37.319000 )
+Maxwelton                     Road     (0, 2, -122.224800, 37.310000, -122.225200, 37.320000 )
+Proctor                       Ave      (0, 2, -122.222200, 37.364000, -122.221700, 37.360000 )
+Moraga                        Ave      (0, 2, -122.225000, 37.300000, -122.224300, 37.300000 )
+Mountain                      Ave      (0, 2, -122.226700, 37.240000, -122.226100, 37.231000 )
+Blair                         Ave      (0, 2, -122.222500, 37.270000, -122.221700, 37.276000 )
+Dudley                        Ave      (0, 2, -122.222000, 37.241000, -122.222100, 37.234000 )
+Estates                       Dr       (0, 2, -122.217800, 37.330000, -122.218300, 37.325000 )
+Wood                          Dr       (0, 2, -122.219400, 37.280000, -122.217400, 37.266000 )
+Bullard                       Dr       (0, 2, -122.215700, 37.297000, -122.213800, 37.276000 )
+Richardson                    Way      (0, 2, -122.226300, 37.224000, -122.225000, 37.220000 )
+Wistaria                      Way      (0, 2, -122.225100, 37.182000, -122.224900, 37.188000 )
+Crocker                       Ave      (0, 2, -122.224200, 37.186000, -122.224300, 37.171000 )
+Lakeview                      Ave      (0, 2, -122.222500, 37.219000, -122.223700, 37.221000 )
+Hampton                       Road     (0, 2, -122.222000, 37.177000, -122.220900, 37.179000 )
+La Salle                      Ave      (0, 2, -122.224200, 37.153000, -122.225500, 37.155000 )
+Sunnyhills                    Road     (0, 2, -122.225700, 37.111000, -122.224600, 37.114000 )
+Indian                        Road     (0, 2, -122.220900, 37.153000, -122.221600, 37.142000 )
+Glen Alpine                   Road     (0, 3, -122.219800, 37.223000, -122.218500, 37.239000 , -122.217349, 37.236000)
+La Salle                      Ave      (0, 2, -122.219100, 37.176000, -122.220600, 37.158000 )
+Crest                         Road     (0, 2, -122.214900, 37.216000, -122.215300, 37.221000 )
+Hampton                       Road     (0, 2, -122.214600, 37.189000, -122.213900, 37.183000 )
+El Centro                     Ave      (0, 2, -122.219100, 37.116000, -122.218500, 37.118000 )
+Sandringham                   Road     (0, 2, -122.215500, 37.175000, -122.216300, 37.165000 )
+Wrenn                         St       (0, 2, -122.206300, 37.117000, -122.205600, 37.117000 )
+Spruce                        St       (0, 2, -122.239800, 37.048000, -122.239300, 37.056000 )
+Northvale                     Road     (0, 2, -122.236100, 37.088000, -122.233500, 37.083000 )
+Mac Arthur                    Blvd     (0, 2, -122.235300, 37.054000, -122.233800, 37.044000 )
+Excelsior                     Ave      (0, 2, -122.233800, 37.059000, -122.231200, 37.051000 )
+Park                          Blvd     (0, 3, -122.238700, 37.027000, -122.237700, 37.028000 , -122.236200, 37.031000)
+28th                          St       (0, 2, -122.235600, 37.999000, -122.235000, 37.994000 )
+13th                          Ave      (0, 2, -122.235000, 37.984000, -122.234500, 37.990000 )
+Longridge                     Road     (0, 2, -122.234500, 37.096000, -122.231700, 37.099000 )
+Park                          Blvd     (0, 2, -122.231200, 37.051000, -122.230300, 37.049000 )
+Hillcroft                     Cir      (0, 2, -122.230400, 37.089000, -122.230100, 37.093000 )
+Park Blvd                     Way      (0, 2, -122.230300, 37.049000, -122.228700, 37.049000 )
+Mac Arthur                    Blvd     (0, 2, -122.232800, 37.039000, -122.230700, 37.022000 )
+Kingsley                      St       (0, 2, -122.231100, 37.042000, -122.230600, 37.046000 )
+31st                          St       (0, 2, -122.232300, 37.995000, -122.230300, 37.992000 )
+36th                          St       (0, 2, -122.228000, 37.022000, -122.227000, 37.020000 )
+Mac Arthur                    Blvd     (0, 2, -122.228100, 37.014000, -122.227800, 37.013000 )
+14th                          Ave      (0, 2, -122.228500, 37.981000, -122.228300, 37.988000 )
+12th                          Ave      (0, 2, -122.239600, 37.958000, -122.238500, 37.965000 )
+13th                          Ave      (0, 2, -122.239600, 37.944000, -122.238700, 37.950000 )
+16th                          Ave      (0, 2, -122.239100, 37.924000, -122.238700, 37.928000 )
+17th                          Ave      (0, 2, -122.238000, 37.918000, -122.237700, 37.921000 )
+14th                          Ave      (0, 2, -122.235700, 37.953000, -122.235400, 37.953000 )
+24th                          St       (0, 2, -122.235200, 37.950000, -122.235000, 37.948000 )
+17th                          St       (0, 2, -122.238500, 37.895000, -122.237500, 37.889000 )
+18th                          Ave      (0, 2, -122.238500, 37.895000, -122.238100, 37.899000 )
+19th                          Ave      (0, 2, -122.239400, 37.868000, -122.239000, 37.873000 )
+19th                          Ave      (0, 2, -122.238600, 37.877000, -122.238200, 37.881000 )
+20th                          Ave      (0, 2, -122.238000, 37.867000, -122.237600, 37.871000 )
+19th                          Ave      (0, 2, -122.236600, 37.897000, -122.235900, 37.905000 )
+19th                          Ave      (0, 3, -122.235500, 37.910000, -122.235100, 37.914000 , -122.234400, 37.922000)
+21st                          Ave      (0, 2, -122.234100, 37.890000, -122.233700, 37.893000 )
+21st                          Ave      (0, 2, -122.236800, 37.859000, -122.236500, 37.865000 )
+22nd                          Ave      (0, 2, -122.234300, 37.871000, -122.233800, 37.875000 )
+14th                          Ave      (0, 2, -122.234200, 37.957000, -122.233200, 37.961000 )
+24th                          St       (0, 2, -122.233000, 37.936000, -122.230900, 37.923000 )
+14th                          Ave      (0, 2, -122.232000, 37.965000, -122.229300, 37.975000 )
+26th                          St       (0, 2, -122.229400, 37.939000, -122.228900, 37.936000 )
+27th                          St       (0, 2, -122.228700, 37.945000, -122.227800, 37.939000 )
+San Antonio                   Way      (0, 2, -122.232300, 37.891000, -122.231400, 37.886000 )
+20th                          St       (0, 2, -122.232700, 37.887000, -122.230800, 37.876000 )
+23rd                          Ave      (0, 2, -122.229800, 37.892000, -122.229800, 37.894000 )
+23rd                          St       (0, 2, -122.228000, 37.896000, -122.227100, 37.890000 )
+24th                          Ave      (0, 3, -122.228500, 37.873000, -122.227700, 37.880000 , -122.227100, 37.890000)
+Creed                         Road     (0, 2, -122.224900, 37.094000, -122.225600, 37.101000 )
+Hampel                        St       (0, 2, -122.224800, 37.078000, -122.224400, 37.073000 )
+Fleet                         Road     (0, 2, -122.223500, 37.085000, -122.222900, 37.078000 )
+14th                          Ave      (0, 2, -122.224500, 37.036000, -122.223800, 37.062000 )
+Beaumont                      Ave      (0, 2, -122.226300, 37.033000, -122.226200, 37.040000 )
+14th                          Ave      (0, 2, -122.224400, 37.028000, -122.224200, 37.036000 )
+Mac Arthur                    Blvd     (0, 2, -122.225800, 37.010000, -122.225100, 37.008000 )
+32nd                          St       (0, 2, -122.225000, 37.990000, -122.224400, 37.988000 )
+Excelsior                     Ave      (0, 2, -122.222400, 37.019000, -122.221500, 37.016000 )
+Mac Arthur                    Blvd     (0, 2, -122.223900, 37.007000, -122.222900, 37.005000 )
+Elbert                        St       (0, 2, -122.220500, 37.107000, -122.219500, 37.095000 )
+Everett                       Ave      (0, 2, -122.219000, 37.094000, -122.218000, 37.087000 )
+Woodruff                      Ave      (0, 2, -122.221400, 37.030000, -122.220600, 37.057000 )
+Park                          Blvd     (0, 2, -122.218000, 37.087000, -122.217300, 37.091000 )
+La Cresta                     Ave      (0, 2, -122.217500, 37.060000, -122.217000, 37.073000 )
+38th                          St       (0, 2, -122.220400, 37.029000, -122.219900, 37.028000 )
+Everett                       Ave      (0, 2, -122.218200, 37.020000, -122.217200, 37.050000 )
+Dimond                        Ave      (0, 2, -122.216700, 37.994000, -122.216200, 37.006000 )
+Sausal Creek                           (0, 2, -122.217500, 37.985000, -122.218000, 37.982000 )
+Fruitvale                     Ave      (0, 2, -122.215800, 37.985000, -122.215700, 37.987000 )
+31st                          St       (0, 2, -122.225200, 37.982000, -122.224000, 37.979000 )
+23rd                          Ave      (0, 2, -122.227200, 37.914000, -122.226600, 37.920000 )
+27th                          St       (0, 2, -122.224300, 37.928000, -122.223100, 37.921000 )
+25th                          Ave      (0, 2, -122.222200, 37.940000, -122.222000, 37.942000 )
+23rd                          St       (0, 2, -122.225800, 37.878000, -122.225200, 37.875000 )
+26th                          Ave      (0, 2, -122.225200, 37.875000, -122.224400, 37.884000 )
+27th                          St       (0, 2, -122.222400, 37.918000, -122.221600, 37.912000 )
+Sausal Creek                           (0, 2, -122.221700, 37.893000, -122.222100, 37.884000 )
+Sausal Creek                           (0, 2, -122.222800, 37.862000, -122.223200, 37.856000 )
+Sausal Creek                           (0, 2, -122.218000, 37.982000, -122.218834, 37.958300 )
+Fruitvale                     Ave      (0, 2, -122.218200, 37.923000, -122.218000, 37.929000 )
+Fruitvale                     Ave      (0, 2, -122.216600, 37.966000, -122.216300, 37.970000 )
+Champion                      St       (0, 2, -122.214600, 37.977000, -122.214500, 37.982000 )
+Nicol                         Ave      (0, 2, -122.217400, 37.943000, -122.216400, 37.940000 )
+Lynde                         St       (0, 3, -122.219500, 37.912000, -122.218700, 37.910000 , -122.217300, 37.908000)
+Hyde                          St       (0, 2, -122.219800, 37.883000, -122.217100, 37.872000 )
+Sunset                        Ave      (0, 2, -122.217600, 37.901000, -122.217300, 37.908000 )
+Deering                       St       (0, 2, -122.214600, 37.904000, -122.212600, 37.897000 )
+Coolidge                      Ave      (0, 2, -122.217100, 37.872000, -122.216900, 37.875000 )
+Salisbury                     St       (0, 2, -122.216200, 37.863000, -122.214600, 37.848000 )
+Bay Forest                    Dr       (0, 2, -122.213900, 37.561000, -122.214200, 37.565000 )
+Grizzly Peak                  Blvd     (0, 2, -122.211200, 37.568000, -122.210500, 37.561000 )
+Skyline                       Blvd     (0, 2, -122.209100, 37.506000, -122.207600, 37.506000 )
+Pine Needle                   Dr       (0, 2, -122.212700, 37.478000, -122.212500, 37.473000 )
+Virgo                         Road     (0, 2, -122.215200, 37.415000, -122.214600, 37.435000 )
+Merriewood                    Dr       (0, 2, -122.213600, 37.373000, -122.212800, 37.389000 )
+Woodhaven                     Way      (0, 2, -122.208000, 37.417000, -122.204500, 37.411000 )
+Alhambra                      Lane     (0, 2, -122.210700, 37.368000, -122.210200, 37.367000 )
+Jewell                        Ct       (0, 2, -122.204800, 37.467000, -122.204300, 37.452000 )
+Lauriston                     Ct       (0, 2, -122.203200, 37.450000, -122.203400, 37.460000 )
+Aspinwall                     Road     (0, 2, -122.207400, 37.390000, -122.206800, 37.390000 )
+Indian                        Way      (0, 2, -122.206600, 37.398000, -122.204500, 37.411000 )
+Thornhill                     Dr       (0, 2, -122.204000, 37.390000, -122.201000, 37.389000 )
+Wild Current                  Way      (0, 2, -122.198700, 37.395000, -122.199300, 37.402000 )
+Oakwood                       Dr       (0, 2, -122.198900, 37.424000, -122.198000, 37.437000 )
+Skyline                       Blvd     (0, 2, -122.196500, 37.405000, -122.190866, 37.381220 )
+Chambers                      Lane     (0, 2, -122.200100, 37.359000, -122.197500, 37.371000 )
+Arrowhead                     Dr       (0, 2, -122.194300, 37.389000, -122.190800, 37.366000 )
+Thornhill                     Dr       (0, 2, -122.213100, 37.335000, -122.211200, 37.350000 )
+Mountain                      Blvd     (0, 2, -122.211000, 37.308000, -122.210200, 37.297000 )
+Estates                       Dr       (0, 2, -122.213900, 37.272000, -122.213500, 37.260000 )
+Dawes                         St       (0, 2, -122.212200, 37.240000, -122.211200, 37.226000 )
+Moraga                        Ave      (0, 2, -122.209600, 37.265000, -122.208800, 37.254000 )
+Mountain                      Blvd     (0, 2, -122.208500, 37.261000, -122.208800, 37.254000 )
+Snake                         Road     (0, 2, -122.206600, 37.331000, -122.204800, 37.345000 )
+Drake                         Dr       (0, 2, -122.205600, 37.311000, -122.204300, 37.302000 )
+Asilomar                      Dr       (0, 2, -122.204100, 37.333000, -122.204000, 37.312000 )
+Shepherd Canyon               Road     (0, 2, -122.205200, 37.245000, -122.202700, 37.236000 )
+Asilomar                      Dr       (0, 2, -122.202800, 37.298000, -122.203550, 37.290500 )
+Sandringham                   Road     (0, 2, -122.213900, 37.183000, -122.214200, 37.179000 )
+Sims                          Dr       (0, 2, -122.210000, 37.207000, -122.210100, 37.237000 )
+Estates                       Dr       (0, 2, -122.213600, 37.158000, -122.212600, 37.169000 )
+Sausal Creek                           (0, 2, -122.212600, 37.122000, -122.213944, 37.106910 )
+Bridgeview                    Dr       (0, 2, -122.211200, 37.133000, -122.210000, 37.138000 )
+Hoover                        Ave      (0, 2, -122.208800, 37.116000, -122.208600, 37.115000 )
+Park                          Blvd     (0, 2, -122.205800, 37.222000, -122.204700, 37.230000 )
+Leimert                       Blvd     (0, 2, -122.205900, 37.169000, -122.204800, 37.169000 )
+Oakview                       Dr       (0, 2, -122.207900, 37.123000, -122.207400, 37.116000 )
+Monterey                      Blvd     (0, 2, -122.202900, 37.167000, -122.201200, 37.146000 )
+Heartwood                     Dr       (0, 2, -122.200600, 37.341000, -122.199200, 37.338000 )
+Chambers                      Dr       (0, 2, -122.200400, 37.352000, -122.197200, 37.368000 )
+Balboa                        Dr       (0, 2, -122.198200, 37.319000, -122.197100, 37.333000 )
+Westover                      Dr       (0, 2, -122.198500, 37.286000, -122.195900, 37.302000 )
+Escher                        Dr       (0, 2, -122.196200, 37.258000, -122.198800, 37.302000 )
+Saroni                        Dr       (0, 2, -122.195400, 37.345000, -122.194400, 37.347000 )
+Shepherd Canyon               Road     (0, 2, -122.198800, 37.302000, -122.196132, 37.311370 )
+Gunn                          Dr       (0, 3, -122.190200, 37.347000, -122.190933, 37.356370 , -122.192000, 37.357000)
+Girvin                        Dr       (0, 2, -122.195700, 37.291000, -122.190400, 37.299000 )
+Haverhill                     Dr       (0, 2, -122.193800, 37.246000, -122.191500, 37.242000 )
+Chelton                       Dr       (0, 2, -122.189000, 37.293000, -122.188700, 37.304000 )
+Scarborough                   Dr       (0, 3, -122.199300, 37.214000, -122.199900, 37.240000 , -122.199400, 37.241000)
+Mountaingate                  Way      (0, 2, -122.199900, 37.198000, -122.199400, 37.193000 )
+Ascot                         Dr       (0, 2, -122.196900, 37.211000, -122.195100, 37.207000 )
+Mountain                      Blvd     (0, 2, -122.201300, 37.164000, -122.200400, 37.150000 )
+Lincoln                       Way      (0, 2, -122.198700, 37.117000, -122.198000, 37.101000 )
+Kearney                       Ave      (0, 2, -122.198100, 37.124000, -122.196300, 37.135000 )
+Ascot                         Dr       (0, 2, -122.193400, 37.217000, -122.192600, 37.219000 )
+Camelford                     Pl       (0, 2, -122.193300, 37.211000, -122.191900, 37.216000 )
+Castle Park                   Way      (0, 2, -122.193600, 37.150000, -122.193300, 37.151000 )
+Joaquin Miller                Road     (0, 3, -122.191400, 37.128000, -122.186200, 37.119000 , -122.184200, 37.106000)
+Hanly                         Road     (0, 2, -122.213700, 37.067000, -122.213500, 37.075000 )
+Lyman                         Road     (0, 2, -122.212400, 37.068000, -122.210500, 37.093000 )
+Forest Hill                   Ave      (0, 2, -122.211800, 37.034000, -122.208600, 37.069000 )
+Champion                      St       (0, 2, -122.214000, 37.991000, -122.214700, 37.002000 )
+Wilbur                        St       (0, 2, -122.210000, 37.039000, -122.209300, 37.034000 )
+Lincoln                       Ave      (0, 2, -122.209300, 37.034000, -122.208700, 37.041000 )
+Laguna                        Ave      (0, 2, -122.209900, 37.989000, -122.208900, 37.000000 )
+Tiffin                        Road     (0, 2, -122.208600, 37.069000, -122.207600, 37.070000 )
+Lincoln                       Ave      (0, 2, -122.206400, 37.063000, -122.205900, 37.065000 )
+Alida                         St       (0, 2, -122.202500, 37.060000, -122.201900, 37.056000 )
+Laguna                        Ave      (0, 2, -122.206200, 37.027000, -122.205800, 37.030000 )
+Mac Arthur                    Blvd     (0, 2, -122.207000, 37.985000, -122.204000, 37.973000 )
+California                    St       (0, 2, -122.203200, 37.005000, -122.201600, 37.996000 )
+Boston                        Ave      (0, 2, -122.213200, 37.961000, -122.212900, 37.969000 )
+School                        St       (0, 2, -122.211600, 37.945000, -122.210300, 37.940000 )
+Coolidge                      Ave      (0, 2, -122.210400, 37.957000, -122.209900, 37.962000 )
+Pleitner                      Ave      (0, 2, -122.209800, 37.946000, -122.209400, 37.953000 )
+Texas                         St       (0, 2, -122.208700, 37.942000, -122.207600, 37.937000 )
+Perlata Creek                          (0, 2, -122.213300, 37.891000, -122.214000, 37.885000 )
+Humboldt                      Ave      (0, 2, -122.214500, 37.872000, -122.213300, 37.879000 )
+Maple                         Ave      (0, 2, -122.210000, 37.909000, -122.209600, 37.915000 )
+Brookdale                     Ave      (0, 2, -122.209500, 37.888000, -122.208800, 37.882000 )
+Brookdale                     Ave      (0, 2, -122.209200, 37.878000, -122.208568, 37.872080 )
+Curran                        Way      (0, 2, -122.207400, 37.960000, -122.207000, 37.966000 )
+Suter                         St       (0, 2, -122.207200, 37.940000, -122.206100, 37.931000 )
+Perlata Creek                          (0, 2, -122.205900, 37.920000, -122.209200, 37.906000 )
+Maple                         Ave      (0, 2, -122.205100, 37.962000, -122.204700, 37.965000 )
+Perlata Creek                          (0, 2, -122.202700, 37.941000, -122.203100, 37.936000 )
+Delaware                      St       (0, 2, -122.201600, 37.926000, -122.201500, 37.925000 )
+Bartlett                      St       (0, 2, -122.207100, 37.902000, -122.205300, 37.913000 )
+Allendale                     Ave      (0, 2, -122.206700, 37.882000, -122.206500, 37.879000 )
+Allendale                     Ave      (0, 3, -122.204800, 37.863000, -122.204100, 37.858000 , -122.203500, 37.851000)
+Octavia                       St       (0, 2, -122.202600, 37.906000, -122.201300, 37.906000 )
+Penniman                      Ave      (0, 2, -122.201800, 37.863000, -122.201200, 37.856000 )
+Coolidge                      Ave      (0, 2, -122.200700, 37.058000, -122.199200, 37.060000 )
+Monterey                      Blvd     (0, 2, -122.197100, 37.103000, -122.196100, 37.096000 )
+Steinmetz                     Way      (0, 2, -122.197900, 37.061000, -122.197600, 37.051000 )
+Wisconsin                     St       (0, 3, -122.199400, 37.017000, -122.197500, 37.998000 , -122.197100, 37.994000)
+Maple                         Ave      (0, 2, -122.198700, 37.024000, -122.197000, 37.035000 )
+Burdeck                       Dr       (0, 2, -122.193900, 37.099000, -122.193200, 37.091000 )
+Monterey                      Blvd     (0, 2, -122.193100, 37.068000, -122.191800, 37.051000 )
+Herrier                       St       (0, 2, -122.194300, 37.006000, -122.193600, 37.998000 )
+Victor                        Ave      (0, 2, -122.192200, 37.987000, -122.191800, 37.981000 )
+35th                          Ave      (0, 2, -122.191400, 37.983000, -122.190900, 37.988000 )
+Monterey                      Blvd     (0, 2, -122.189000, 37.987000, -122.189100, 37.971000 )
+Arizona                       St       (0, 2, -122.198500, 37.978000, -122.198100, 37.974000 )
+Midvale                       Ave      (0, 2, -122.201600, 37.933000, -122.200900, 37.938000 )
+35th                          Ave      (0, 2, -122.200500, 37.929000, -122.199600, 37.935000 )
+Norton                        Ave      (0, 2, -122.197700, 37.967000, -122.196855, 37.971650 )
+Magee                         Ave      (0, 2, -122.199100, 37.928000, -122.197700, 37.937000 )
+California                    St       (0, 2, -122.195200, 37.942000, -122.194600, 37.935000 )
+Suter                         St       (0, 2, -122.200900, 37.890000, -122.200700, 37.888000 )
+Eastman                       Ave      (0, 2, -122.202800, 37.845000, -122.201200, 37.856000 )
+Culver                        St       (0, 2, -122.199800, 37.865000, -122.199600, 37.862000 )
+Redding                       St       (0, 2, -122.197800, 37.901000, -122.197500, 37.895000 )
+38th                          Ave      (0, 2, -122.196300, 37.907000, -122.195400, 37.912000 )
+Maybelle                      Way      (0, 2, -122.197400, 37.873000, -122.197100, 37.874000 )
+Quigley                       Pl       (0, 2, -122.196200, 37.867000, -122.195700, 37.861000 )
+Wisconsin                     St       (0, 2, -122.194500, 37.965000, -122.193900, 37.959000 )
+Loma Vista                    Ave      (0, 2, -122.195200, 37.942000, -122.193300, 37.952000 )
+Wisconsin                     St       (0, 2, -122.192700, 37.945000, -122.192100, 37.940000 )
+Victor                        Ave      (0, 2, -122.191400, 37.972000, -122.190100, 37.964000 )
+Maybelle                      Ave      (0, 2, -122.195700, 37.878000, -122.194800, 37.883000 )
+Redding                       St       (0, 2, -122.194300, 37.858000, -122.193400, 37.854000 )
+High                          St       (0, 2, -122.190100, 37.889000, -122.189900, 37.891000 )
+Tulip                         Ave      (0, 2, -122.190100, 37.881000, -122.189800, 37.877000 )
+Moore                         Dr       (0, 2, -122.190100, 37.335000, -122.188400, 37.351000 )
+Forestland                    Way      (0, 2, -122.188600, 37.334000, -122.190100, 37.329000 )
+Shepherd Canyon               Road     (0, 2, -122.184500, 37.355000, -122.183500, 37.355000 )
+Skyline                       Blvd     (0, 2, -122.188500, 37.275000, -122.187300, 37.266000 )
+Skyline                       Blvd     (0, 2, -122.186500, 37.250000, -122.186000, 37.247000 )
+Totterdell                    St       (0, 2, -122.187000, 37.228000, -122.186400, 37.224000 )
+Robinson                      Dr       (0, 2, -122.182500, 37.096000, -122.180700, 37.054000 )
+Redwood                       Road     (0, 2, -122.188200, 37.986000, -122.187700, 37.986000 )
+Klamath                       St       (0, 2, -122.183200, 37.035000, -122.181500, 37.023000 )
+Joaquin Miller                Road     (0, 2, -122.179700, 37.083000, -122.177800, 37.051000 )
+Crestmont                     Dr       (0, 2, -122.177500, 37.029000, -122.179800, 37.044000 )
+Skyline                       Blvd     (0, 2, -122.177200, 37.039000, -122.177400, 37.037000 )
+Atlas                         Ave      (0, 2, -122.188900, 37.964000, -122.188200, 37.966000 )
+Harbor View                   Ave      (0, 2, -122.187200, 37.927000, -122.186600, 37.921000 )
+Reinhardt                     Dr       (0, 2, -122.183100, 37.922000, -122.182800, 37.918000 )
+Steele                        St       (0, 2, -122.188000, 37.892000, -122.187400, 37.886000 )
+Huntington                    St       (0, 2, -122.186600, 37.895000, -122.186000, 37.899000 )
+Daisy                         St       (0, 2, -122.185700, 37.858000, -122.185000, 37.851000 )
+Carson                        St       (0, 2, -122.184600, 37.900000, -122.184300, 37.901000 )
+Davenport                     Ave      (0, 2, -122.182700, 37.892000, -122.182300, 37.872000 )
+Redwood                       Road     (0, 2, -122.181900, 37.978000, -122.181100, 37.968000 )
+Aliso                         Ave      (0, 2, -122.180900, 37.953000, -122.180000, 37.946000 )
+Mountain                      Blvd     (0, 2, -122.178900, 37.934000, -122.178700, 37.930000 )
+Mountain                      Blvd     (0, 2, -122.177400, 37.889000, -122.177100, 37.887000 )
+Mountain                      Blvd     (0, 2, -122.176500, 37.857000, -122.177000, 37.850000 )
+Skyline                       Blvd     (0, 2, -122.173800, 37.010000, -122.171400, 37.996000 )
+Skyline                       Blvd     (0, 2, -122.170100, 37.988000, -122.167000, 37.982000 )
+Balmoral                      Dr       (0, 2, -122.165800, 37.027000, -122.165600, 37.042000 )
+Balmoral                      Dr       (0, 2, -122.163900, 37.981000, -122.163500, 37.988000 )
+Campus                        Dr       (0, 3, -122.170400, 37.905000, -122.167800, 37.868000 , -122.167100, 37.865000)
+Stalker                       Way      (0, 2, -122.264000, 37.807000, -122.267200, 37.816000 )
+St Charles                    St       (0, 2, -122.265400, 37.763000, -122.265300, 37.773000 )
+Sherman                       St       (0, 2, -122.262800, 37.781000, -122.262700, 37.787000 )
+Bay                           St       (0, 2, -122.264200, 37.732000, -122.264100, 37.751000 )
+Alameda Belt Line Railroad             (0, 2, -122.258600, 37.769000, -122.262400, 37.784000 )
+Lincoln                       Ave      (0, 2, -122.262800, 37.751000, -122.261500, 37.751000 )
+Benton                        St       (0, 2, -122.260500, 37.713000, -122.260500, 37.731000 )
+Arbor                         St       (0, 2, -122.258700, 37.758000, -122.258200, 37.765000 )
+Cottage                       St       (0, 2, -122.259300, 37.713000, -122.258931, 37.718030 )
+Eagle                         Ave      (0, 2, -122.253900, 37.760000, -122.253215, 37.756570 )
+Lincoln                       Ave      (0, 2, -122.254900, 37.729000, -122.254100, 37.726000 )
+Pacific                       Ave      (0, 2, -122.253500, 37.735000, -122.252700, 37.731000 )
+Sherman                       St       (0, 2, -122.263000, 37.695000, -122.262932, 37.707990 )
+Sherman                       St       (0, 2, -122.263100, 37.667000, -122.263100, 37.674000 )
+Alameda                       Ave      (0, 2, -122.260500, 37.713000, -122.258800, 37.704000 )
+Paru                          St       (0, 2, -122.260100, 37.687000, -122.259400, 37.695000 )
+Harbor Light                  Road     (0, 2, -122.266700, 37.606000, -122.265100, 37.631000 )
+Grand                         St       (0, 2, -122.261200, 37.637000, -122.260900, 37.643000 )
+Otis                          Dr       (0, 2, -122.260500, 37.627000, -122.259300, 37.625000 )
+Paru                          St       (0, 2, -122.258300, 37.712000, -122.257400, 37.725000 )
+San Antonio                   Ave      (0, 2, -122.258500, 37.679000, -122.256600, 37.672000 )
+Union                         St       (0, 2, -122.255500, 37.689000, -122.254900, 37.698000 )
+Alameda                       Ave      (0, 2, -122.255500, 37.689000, -122.253400, 37.680000 )
+Union                         St       (0, 2, -122.257900, 37.654000, -122.257300, 37.663000 )
+Otis                          Dr       (0, 2, -122.256400, 37.619000, -122.256000, 37.618000 )
+Lafayette                     St       (0, 2, -122.255900, 37.646000, -122.255300, 37.654000 )
+San Jose                      Ave      (0, 2, -122.254300, 37.650000, -122.251000, 37.635000 )
+Clement                       Ave      (0, 2, -122.252500, 37.765000, -122.252147, 37.764290 )
+Union                         St       (0, 2, -122.252700, 37.731000, -122.252100, 37.739000 )
+Buena Vista                   Ave      (0, 2, -122.251000, 37.735000, -122.249900, 37.730000 )
+Chestnut                      St       (0, 2, -122.248200, 37.733000, -122.247500, 37.742000 )
+Sp Railroad                            (0, 2, -122.241100, 37.850000, -122.235900, 37.814000 )
+Dennison                      St       (0, 3, -122.242800, 37.791000, -122.241200, 37.793000 , -122.239000, 37.795000)
+Tidal Canal                            (0, 2, -122.242800, 37.791000, -122.238600, 37.745000 )
+Lincoln                       Ave      (0, 2, -122.251000, 37.712000, -122.250000, 37.707000 )
+Santa Clara                   Ave      (0, 2, -122.250900, 37.695000, -122.247900, 37.682000 )
+Willow                        St       (0, 2, -122.249400, 37.661000, -122.248700, 37.670000 )
+Willow                        St       (0, 2, -122.251600, 37.627000, -122.251000, 37.635000 )
+Walnut                        St       (0, 2, -122.248900, 37.613000, -122.248300, 37.622000 )
+Willow                        St       (0, 2, -122.245900, 37.711000, -122.245300, 37.720000 )
+Walnut                        St       (0, 2, -122.244200, 37.682000, -122.243700, 37.690000 )
+Pacific                       Ave      (0, 2, -122.241900, 37.683000, -122.240800, 37.678000 )
+Walnut                        St       (0, 2, -122.246500, 37.648000, -122.245900, 37.658000 )
+San Jose                      Ave      (0, 2, -122.245500, 37.609000, -122.244500, 37.605000 )
+Oak                           St       (0, 2, -122.243700, 37.636000, -122.243100, 37.644000 )
+Park                          Ave      (0, 2, -122.240700, 37.634000, -122.240400, 37.638000 )
+Park                          Ave      (0, 2, -122.242000, 37.616000, -122.241600, 37.621000 )
+Shoreline                     Dr       (0, 2, -122.263800, 37.596000, -122.260700, 37.584000 )
+Willow                        St       (0, 2, -122.255900, 37.567000, -122.255100, 37.580000 )
+Sea View                      Pkwy     (0, 2, -122.254700, 37.434000, -122.255000, 37.439000 )
+Park                          St       (0, 2, -122.249100, 37.541000, -122.247562, 37.564070 )
+Aughinbaugh                   Way      (0, 2, -122.249100, 37.473000, -122.249000, 37.471000 )
+Park                          Ave      (0, 2, -122.246100, 37.565000, -122.244300, 37.589000 )
+Delmar                        Ave      (0, 2, -122.245300, 37.545000, -122.244400, 37.556000 )
+San Jose                      Ave      (0, 2, -122.242300, 37.594000, -122.240900, 37.586000 )
+Washington                    St       (0, 2, -122.240500, 37.568000, -122.240000, 37.566000 )
+Calhoun                       St       (0, 2, -122.240900, 37.553000, -122.240500, 37.551000 )
+Broadway                               (0, 2, -122.245700, 37.528000, -122.245500, 37.529000 )
+Gainsborough                  Ct       (0, 2, -122.244800, 37.473000, -122.244700, 37.461000 )
+Sea View                      Pkwy     (0, 2, -122.253000, 37.455000, -122.251900, 37.457000 )
+Sea View                      Pkwy     (0, 2, -122.249900, 37.466000, -122.249600, 37.468000 )
+Dublin                        Way      (0, 2, -122.251000, 37.425000, -122.251300, 37.430000 )
+Kofman                        Ct       (0, 2, -122.249800, 37.422000, -122.249700, 37.417000 )
+Sherwood                      Lane     (0, 2, -122.247400, 37.467000, -122.246514, 37.459850 )
+Cove                          Road     (0, 2, -122.246800, 37.425000, -122.247400, 37.408000 )
+Montego Bay                            (0, 2, -122.249100, 37.389000, -122.248500, 37.396000 )
+Galway Bay                             (0, 2, -122.247300, 37.389000, -122.247500, 37.384000 )
+Mecartney                     Road     (0, 2, -122.247300, 37.374000, -122.245500, 37.369000 )
+Sheffield                     Road     (0, 2, -122.245700, 37.441000, -122.244800, 37.440000 )
+Channing                      Way      (0, 2, -122.245300, 37.425000, -122.246600, 37.408000 )
+Clipper                       Dr       (0, 2, -122.243800, 37.420000, -122.243700, 37.406000 )
+Sheffield                     Road     (0, 2, -122.242100, 37.440000, -122.242700, 37.437000 )
+Sheffield                     Road     (0, 2, -122.241000, 37.450000, -122.241300, 37.446000 )
+Sable Pointe                           (0, 2, -122.241000, 37.424000, -122.241600, 37.418000 )
+Bay Walk                      Road     (0, 2, -122.247100, 37.389000, -122.246200, 37.389000 )
+Fontana                       Dr       (0, 2, -122.245800, 37.360000, -122.246300, 37.354000 )
+Calle de Monte                         (0, 2, -122.245200, 37.344000, -122.245600, 37.340000 )
+El Portal                              (0, 2, -122.242900, 37.359000, -122.243400, 37.361000 )
+La Cresta                              (0, 2, -122.243300, 37.353000, -122.242500, 37.349000 )
+Oyster Pond                   Road     (0, 2, -122.240800, 37.407000, -122.240400, 37.403000 )
+Marianas                               (0, 2, -122.242600, 37.344000, -122.243000, 37.339000 )
+Tahiti                        Lane     (0, 3, -122.241100, 37.343000, -122.240607, 37.349900 , -122.240100, 37.357000)
+21st                          Ave      (0, 2, -122.238500, 37.843000, -122.238000, 37.847000 )
+Dennison                      St       (0, 2, -122.237900, 37.796000, -122.237400, 37.796000 )
+23rd                          Ave      (0, 2, -122.235700, 37.824000, -122.235600, 37.825000 )
+23rd                          Ave      (0, 2, -122.235900, 37.817000, -122.236000, 37.820000 )
+Miller                        Ave      (0, 2, -122.235300, 37.806000, -122.235000, 37.809000 )
+Wp Railroad                            (0, 2, -122.234200, 37.806000, -122.229500, 37.777000 )
+23rd Av                       Ovps     (0, 2, -122.236000, 37.783000, -122.235600, 37.768000 )
+23rd Av                       Ovps     (0, 2, -122.235600, 37.768000, -122.235000, 37.754000 )
+9th                           St       (0, 2, -122.234900, 37.779000, -122.234000, 37.773000 )
+7th                           St       (0, 2, -122.236200, 37.753000, -122.235000, 37.754000 )
+23rd                          Ave      (0, 2, -122.233900, 37.841000, -122.233000, 37.849000 )
+Miller                        Ave      (0, 2, -122.232200, 37.844000, -122.231900, 37.848000 )
+16th                          St       (0, 2, -122.231000, 37.837000, -122.230600, 37.834000 )
+Foothill                      Blvd     (0, 2, -122.228900, 37.846000, -122.227800, 37.845000 )
+14th                          St       (0, 2, -122.228800, 37.798000, -122.228000, 37.792000 )
+27th                          Ave      (0, 2, -122.234000, 37.773000, -122.233500, 37.778000 )
+Portwood                      Ave      (0, 2, -122.232200, 37.760000, -122.231700, 37.765000 )
+29th Av                       Ovps     (0, 2, -122.232300, 37.748000, -122.232100, 37.752000 )
+Peterson                      St       (0, 2, -122.232000, 37.733000, -122.231600, 37.739000 )
+Sp Railroad                            (0, 2, -122.228100, 37.761000, -122.226800, 37.753000 )
+Oak                           St       (0, 2, -122.238300, 37.713000, -122.238000, 37.719000 )
+Park                          St       (0, 2, -122.237700, 37.686000, -122.237000, 37.695000 )
+Sp Railroad                            (0, 2, -122.234600, 37.727000, -122.234300, 37.726000 )
+Everett                       St       (0, 2, -122.236000, 37.678000, -122.235300, 37.687000 )
+Eagle                         Ave      (0, 2, -122.234200, 37.668000, -122.233500, 37.664000 )
+Everett                       St       (0, 2, -122.238500, 37.647000, -122.238000, 37.653000 )
+Broadway                               (0, 2, -122.240900, 37.586000, -122.239500, 37.601000 )
+Broadway                               (0, 2, -122.237200, 37.631000, -122.236753, 37.636750 )
+Buena Vista                   Ave      (0, 2, -122.233700, 37.651000, -122.232800, 37.645000 )
+Pearl                         St       (0, 2, -122.238300, 37.594000, -122.236600, 37.615000 )
+Central                       Ave      (0, 2, -122.234300, 37.602000, -122.233100, 37.595000 )
+Blanding                      Ave      (0, 2, -122.231300, 37.680000, -122.232800, 37.686000 )
+Pearl                         St       (0, 2, -122.231900, 37.672000, -122.231600, 37.676000 )
+Tidal Canal                            (0, 2, -122.230200, 37.697000, -122.229000, 37.694000 )
+Fruitvale                     Ave      (0, 2, -122.228500, 37.693000, -122.227522, 37.712560 )
+Windsor                       Dr       (0, 2, -122.230300, 37.673000, -122.228900, 37.665000 )
+Pearl                         St       (0, 2, -122.233700, 37.651000, -122.232900, 37.661000 )
+Northwood                     Dr       (0, 2, -122.231700, 37.629000, -122.231100, 37.637000 )
+Gibbons                       Dr       (0, 2, -122.232500, 37.625000, -122.231700, 37.629000 )
+Northwood                     Dr       (0, 2, -122.229900, 37.640000, -122.229100, 37.638000 )
+Gibbons                       Dr       (0, 2, -122.228900, 37.633000, -122.228300, 37.633000 )
+Fountain                      St       (0, 2, -122.230600, 37.593000, -122.229300, 37.605000 )
+High                          St       (0, 3, -122.228100, 37.605000, -122.227300, 37.611000 , -122.226700, 37.618000)
+Mitchell                      St       (0, 2, -122.225700, 37.844000, -122.224900, 37.852000 )
+15th                          St       (0, 3, -122.226400, 37.794000, -122.225800, 37.791000 , -122.225100, 37.789000)
+22nd                          St       (0, 2, -122.222700, 37.854000, -122.222000, 37.851000 )
+Foothill                      Blvd     (0, 2, -122.221600, 37.837000, -122.220600, 37.833000 )
+33rd                          Ave      (0, 2, -122.223500, 37.776000, -122.222500, 37.798000 )
+31st                          Ave      (0, 2, -122.225900, 37.771000, -122.225400, 37.779000 )
+Fruitvale                     Ave      (0, 2, -122.225700, 37.754000, -122.225600, 37.756000 )
+Sp Railroad                            (0, 2, -122.226900, 37.730000, -122.227200, 37.726000 )
+San Leandro                   St       (0, 2, -122.225100, 37.748000, -122.224200, 37.743000 )
+34th                          Ave      (0, 2, -122.225100, 37.728000, -122.224600, 37.737000 )
+34th                          Ave      (0, 2, -122.223200, 37.755000, -122.222500, 37.765000 )
+35th                          Ave      (0, 2, -122.224300, 37.724000, -122.223800, 37.731000 )
+36th                          Ave      (0, 2, -122.222100, 37.737000, -122.222000, 37.738000 )
+37th                          Ave      (0, 2, -122.221100, 37.732000, -122.221000, 37.733000 )
+34th                          Ave      (0, 2, -122.220500, 37.811000, -122.219600, 37.829000 )
+18th                          St       (0, 2, -122.220500, 37.811000, -122.219000, 37.806000 )
+36th                          Ave      (0, 2, -122.219600, 37.778000, -122.218000, 37.802000 )
+Galindo                       St       (0, 2, -122.215800, 37.840000, -122.214900, 37.837000 )
+San Juan                      St       (0, 2, -122.215800, 37.803000, -122.214900, 37.795000 )
+36th                          Ave      (0, 2, -122.221400, 37.746000, -122.220600, 37.755000 )
+38th                          Ave      (0, 2, -122.220200, 37.729000, -122.219700, 37.734000 )
+39th                          Ave      (0, 2, -122.216300, 37.763000, -122.216000, 37.768000 )
+40th                          Ave      (0, 2, -122.215600, 37.760000, -122.215200, 37.764000 )
+Perlata Creek                          (0, 2, -122.215800, 37.753000, -122.215500, 37.739000 )
+41st                          Ave      (0, 2, -122.215300, 37.735000, -122.215100, 37.738000 )
+Fruitvale                     Ave      (0, 2, -122.227200, 37.719000, -122.226900, 37.725000 )
+36th                          Ave      (0, 2, -122.223300, 37.721000, -122.223000, 37.725000 )
+Fernside                      Blvd     (0, 2, -122.227100, 37.645000, -122.226500, 37.640000 )
+High                          St       (0, 2, -122.223300, 37.647000, -122.222600, 37.652000 )
+San Leandro                   St       (0, 2, -122.219500, 37.721000, -122.218600, 37.716000 )
+Bay Area Rapid Transit                 (0, 2, -122.217200, 37.713000, -122.218400, 37.719000 )
+Alameda                       Ave      (0, 2, -122.219700, 37.680000, -122.219200, 37.680000 )
+41st                          Ave      (0, 2, -122.217000, 37.719000, -122.216900, 37.721000 )
+12th                          St       (0, 2, -122.216300, 37.718000, -122.215100, 37.712000 )
+High                          St       (0, 2, -122.215100, 37.712000, -122.214500, 37.716000 )
+Wp Railroad                            (0, 2, -122.214500, 37.694000, -122.213700, 37.688000 )
+Tidewater                     Ave      (0, 2, -122.219300, 37.625000, -122.217400, 37.609000 )
+Versailles                    Ave      (0, 2, -122.238300, 37.577000, -122.237400, 37.589000 )
+Mound                         St       (0, 2, -122.240200, 37.531000, -122.239500, 37.537000 )
+Washington                    St       (0, 2, -122.237800, 37.553000, -122.237000, 37.548000 )
+Grove                         St       (0, 2, -122.236400, 37.582000, -122.235700, 37.590000 )
+Mound                         St       (0, 2, -122.234600, 37.582000, -122.233900, 37.588000 )
+Adams                         St       (0, 2, -122.236400, 37.553000, -122.235700, 37.548000 )
+Washington                    St       (0, 2, -122.235500, 37.537000, -122.234700, 37.532000 )
+Adams                         St       (0, 2, -122.234900, 37.542000, -122.234100, 37.537000 )
+Bayview                       Dr       (0, 2, -122.238600, 37.511000, -122.237900, 37.514000 )
+High                          St       (0, 2, -122.237900, 37.514000, -122.236700, 37.526000 )
+Peach                         St       (0, 2, -122.235200, 37.502000, -122.235400, 37.514000 )
+Fernside                      Ave      (0, 2, -122.233700, 37.514000, -122.234300, 37.509000 )
+Court                         St       (0, 3, -122.233100, 37.583000, -122.232400, 37.589000 , -122.231400, 37.598000)
+Central                       Ave      (0, 2, -122.230900, 37.579000, -122.227600, 37.557000 )
+High                          St       (0, 2, -122.229500, 37.592000, -122.228800, 37.597000 )
+Peach                         St       (0, 3, -122.233900, 37.527000, -122.233400, 37.532000 , -122.232800, 37.537000)
+Packet Landing                Road     (0, 2, -122.237600, 37.471000, -122.237200, 37.458000 )
+Sable Pointe                           (0, 2, -122.239300, 37.439000, -122.240000, 37.434000 )
+Coleport Landing                       (0, 2, -122.237400, 37.426000, -122.237800, 37.420000 )
+Sea Bridge                    Way      (0, 2, -122.236400, 37.426000, -122.235900, 37.417000 )
+Oyster Shoals                          (0, 2, -122.237200, 37.394000, -122.236500, 37.393000 )
+Victoria Bay                           (0, 2, -122.236300, 37.399000, -122.236500, 37.393000 )
+Island                        Dr       (0, 2, -122.232900, 37.463000, -122.233600, 37.455000 )
+Fernside                      Blvd     (0, 2, -122.225600, 37.575000, -122.224900, 37.581000 )
+San Leandro Bay                        (0, 2, -122.224100, 37.553000, -122.225300, 37.542000 )
+Earhart                       Road     (0, 2, -122.218600, 37.394000, -122.218100, 37.389000 )
+Piper                         St       (0, 2, -122.216000, 37.390000, -122.215500, 37.385000 )
+Northrup                      St       (0, 2, -122.214100, 37.371000, -122.215000, 37.381000 )
+Catalina                      Ave      (0, 2, -122.245800, 37.329000, -122.246102, 37.331090 )
+Verdemar                      Dr       (0, 2, -122.245400, 37.333000, -122.245300, 37.335000 )
+Camino del Valle                       (0, 2, -122.243100, 37.343000, -122.243700, 37.334000 )
+Admirality                    Lane     (0, 2, -122.242400, 37.323000, -122.242900, 37.318000 )
+Island                        Dr       (0, 2, -122.241100, 37.318000, -122.240600, 37.325000 )
+Island                        Dr       (0, 3, -122.239900, 37.334000, -122.239400, 37.340000 , -122.238300, 37.352000)
+Fir                           Ave      (0, 2, -122.238500, 37.318000, -122.237900, 37.314000 )
+Holly                         St       (0, 2, -122.239300, 37.300000, -122.239500, 37.297000 )
+Solomon                       Lane     (0, 2, -122.237200, 37.299000, -122.237400, 37.300000 )
+Capella                       Lane     (0, 2, -122.234500, 37.322000, -122.235200, 37.326000 )
+Camanoe                       Lane     (0, 2, -122.233900, 37.330000, -122.233400, 37.326000 )
+Oleander                      Ave      (0, 2, -122.235800, 37.302000, -122.236192, 37.304240 )
+Oleander                      Ave      (0, 2, -122.233800, 37.296000, -122.234600, 37.295000 )
+Melrose                       Ave      (0, 2, -122.232800, 37.331000, -122.231900, 37.335000 )
+Magnolia                      Dr       (0, 2, -122.231300, 37.296000, -122.231700, 37.285000 )
+Maitland                      Dr       (0, 2, -122.228600, 37.273000, -122.227700, 37.265000 )
+35th                          Ave      (0, 2, -122.214000, 37.850000, -122.213800, 37.852000 )
+Santa Rita                    St       (0, 2, -122.212900, 37.832000, -122.210400, 37.839000 )
+Harrington                    Ave      (0, 2, -122.211500, 37.847000, -122.210800, 37.851000 )
+Agua Vista                    St       (0, 2, -122.208900, 37.839000, -122.206900, 37.819000 )
+San Carlos                    Walk     (0, 2, -122.208700, 37.795000, -122.208000, 37.789000 )
+Foothill                      Blvd     (0, 2, -122.213600, 37.770000, -122.212900, 37.763000 )
+41st                          Ave      (0, 2, -122.214000, 37.750000, -122.213500, 37.756000 )
+Bond                          St       (0, 2, -122.212600, 37.750000, -122.211600, 37.739000 )
+42nd                          Ave      (0, 2, -122.210400, 37.767000, -122.209700, 37.773000 )
+Ygnacio                       Ave      (0, 2, -122.210300, 37.756000, -122.209800, 37.754000 )
+45th                          Ave      (0, 2, -122.208800, 37.749000, -122.208000, 37.758000 )
+39th                          Ave      (0, 2, -122.205400, 37.850000, -122.205000, 37.852000 )
+Santa Rita                    St       (0, 2, -122.206500, 37.804000, -122.205800, 37.796000 )
+Brookdale                     Ave      (0, 2, -122.204300, 37.834000, -122.203200, 37.824000 )
+Courtland                     Ave      (0, 2, -122.204100, 37.801000, -122.203200, 37.815000 )
+Courtland                     Ave      (0, 2, -122.208400, 37.760000, -122.206800, 37.772000 )
+Melrose                       Ave      (0, 2, -122.207500, 37.756000, -122.207200, 37.754000 )
+48th                          Ave      (0, 2, -122.205900, 37.736000, -122.205200, 37.745000 )
+Fairfax                       Ave      (0, 2, -122.202200, 37.784000, -122.201500, 37.778000 )
+Melrose                       Ave      (0, 2, -122.203200, 37.736000, -122.202900, 37.733000 )
+14th                          St       (0, 2, -122.212800, 37.717000, -122.211800, 37.713000 )
+46th                          Ave      (0, 2, -122.214000, 37.685000, -122.213700, 37.688000 )
+Bay Area Rapid Transit                 (0, 2, -122.212900, 37.676000, -122.212000, 37.668000 )
+45th                          Ave      (0, 2, -122.211800, 37.713000, -122.210900, 37.724000 )
+47th                          Ave      (0, 2, -122.208600, 37.719000, -122.208200, 37.723000 )
+50th                          Ave      (0, 2, -122.210800, 37.663000, -122.209600, 37.675000 )
+50th                          Ave      (0, 2, -122.213500, 37.638000, -122.212300, 37.650000 )
+Coliseum                      Way      (0, 3, -122.211300, 37.626000, -122.208500, 37.592000 , -122.206300, 37.568000)
+51st                          Ave      (0, 2, -122.210300, 37.658000, -122.210000, 37.661000 )
+52nd                          Ave      (0, 2, -122.209600, 37.653000, -122.209200, 37.657000 )
+Bay Area Rapid Transit                 (0, 2, -122.208600, 37.641000, -122.206100, 37.619000 )
+Bond                          St       (0, 2, -122.207100, 37.718000, -122.206700, 37.716000 )
+50th                          Ave      (0, 2, -122.205900, 37.718000, -122.205400, 37.724000 )
+53rd                          Ave      (0, 2, -122.207500, 37.659000, -122.205000, 37.679000 )
+Bancroft                      Ave      (0, 2, -122.204100, 37.716000, -122.202000, 37.716000 )
+Holland                       St       (0, 2, -122.203800, 37.688000, -122.201600, 37.677000 )
+54th                          Ave      (0, 2, -122.207600, 37.649000, -122.206800, 37.654000 )
+Wp Railroad                            (0, 2, -122.204300, 37.608000, -122.203628, 37.602370 )
+High                          St       (0, 2, -122.200700, 37.837000, -122.199700, 37.842000 )
+Lilac                         St       (0, 2, -122.201400, 37.799000, -122.200800, 37.804000 )
+High                          St       (0, 2, -122.199000, 37.845000, -122.198300, 37.849000 )
+Renwick                       St       (0, 2, -122.198900, 37.797000, -122.198200, 37.802000 )
+Gordon                        St       (0, 2, -122.200100, 37.788000, -122.199800, 37.785000 )
+Monticello                    Ave      (0, 2, -122.200000, 37.771000, -122.199000, 37.778000 )
+Fairfax                       Ave      (0, 2, -122.199700, 37.733000, -122.199700, 37.724000 )
+Cole                          St       (0, 2, -122.197500, 37.749000, -122.196200, 37.760000 )
+Kingsland                     Ave      (0, 2, -122.195700, 37.743000, -122.195600, 37.753000 )
+Storer                        Ave      (0, 2, -122.194400, 37.852000, -122.193400, 37.854000 )
+Knowland                      Ave      (0, 2, -122.195700, 37.816000, -122.194800, 37.823000 )
+Redding                       Pl       (0, 2, -122.191900, 37.843000, -122.192100, 37.841000 )
+Madera                        Ave      (0, 2, -122.190000, 37.814000, -122.191400, 37.824000 )
+Morcom                        Pl       (0, 2, -122.190100, 37.826000, -122.189700, 37.829000 )
+Birdsall                      Ave      (0, 2, -122.191000, 37.789000, -122.191100, 37.796000 )
+Fleming                       Ave      (0, 2, -122.194900, 37.791000, -122.194800, 37.783000 )
+Walnut                        St       (0, 2, -122.194800, 37.763000, -122.192300, 37.742000 )
+Laverne                       Ave      (0, 2, -122.193800, 37.731000, -122.192800, 37.721000 )
+Birdsall                      Ave      (0, 2, -122.190700, 37.774000, -122.190700, 37.781000 )
+Normandie                     Ave      (0, 2, -122.190200, 37.783000, -122.188600, 37.768000 )
+Walnut                        St       (0, 2, -122.191300, 37.734000, -122.190900, 37.729000 )
+Foothill                      Blvd     (0, 2, -122.201800, 37.724000, -122.200800, 37.724000 )
+Wentworth                     Ave      (0, 2, -122.199700, 37.698000, -122.198600, 37.698000 )
+55th                          Ave      (0, 2, -122.199600, 37.689000, -122.199100, 37.695000 )
+Cole                          St       (0, 2, -122.197400, 37.724000, -122.197500, 37.716000 )
+55th                          Ave      (0, 2, -122.197000, 37.709000, -122.196300, 37.714000 )
+57th                          Ave      (0, 2, -122.196900, 37.686000, -122.196400, 37.689000 )
+Eastlawn                      St       (0, 2, -122.200500, 37.644000, -122.200000, 37.638000 )
+Tevis                         St       (0, 2, -122.201100, 37.621000, -122.200700, 37.616000 )
+Eastlawn                      St       (0, 2, -122.198900, 37.629000, -122.198200, 37.623000 )
+61st                          Ave      (0, 2, -122.197300, 37.643000, -122.196200, 37.650000 )
+62nd                          Ave      (0, 2, -122.199200, 37.617000, -122.198200, 37.623000 )
+Foothill                      Blvd     (0, 2, -122.194800, 37.725000, -122.192800, 37.721000 )
+57th                          Ave      (0, 2, -122.193300, 37.711000, -122.192800, 37.721000 )
+Harmon                        Ave      (0, 3, -122.194000, 37.680000, -122.192400, 37.664000 , -122.191100, 37.652000)
+Bancroft                      Ave      (0, 2, -122.190300, 37.706000, -122.189000, 37.705000 )
+62nd                          Ave      (0, 2, -122.191600, 37.670000, -122.191100, 37.673000 )
+17th                          St       (0, 2, -122.194700, 37.648000, -122.193400, 37.637000 )
+14th                          St       (0, 2, -122.194500, 37.629000, -122.193600, 37.625000 )
+64th                          Ave      (0, 2, -122.192100, 37.645000, -122.191100, 37.652000 )
+Havenscourt                   Blvd     (0, 3, -122.189100, 37.634000, -122.188800, 37.638000 , -122.188200, 37.643000)
+Oakport                       St       (0, 2, -122.212300, 37.590000, -122.211700, 37.585000 )
+66th                          Ave      (0, 2, -122.205400, 37.542000, -122.204200, 37.546000 )
+Damon Slough                           (0, 2, -122.205700, 37.533000, -122.206300, 37.531000 )
+San Leandro Creek Canal                (0, 2, -122.208100, 37.409000, -122.207600, 37.401000 )
+Corvair                       St       (0, 2, -122.212600, 37.364000, -122.213200, 37.360000 )
+Edgewater                     Dr       (0, 2, -122.201000, 37.379000, -122.204200, 37.410000 )
+66th                          Ave      (0, 2, -122.201300, 37.556000, -122.199700, 37.564000 )
+Brentford                     St       (0, 2, -122.196500, 37.581000, -122.196400, 37.564000 )
+69th                          Ave      (0, 2, -122.195900, 37.560000, -122.195400, 37.563000 )
+Coliseum                      Way      (0, 2, -122.200100, 37.470000, -122.197800, 37.516000 )
+71st                          Ave      (0, 2, -122.195000, 37.548000, -122.194400, 37.553000 )
+69th                          Ave      (0, 2, -122.192700, 37.582000, -122.191900, 37.587000 )
+71st                          Ave      (0, 2, -122.190800, 37.576000, -122.189000, 37.588000 )
+73rd                          Ave      (0, 2, -122.191500, 37.554000, -122.189700, 37.565000 )
+Hegenberger                   Exwy     (0, 2, -122.194600, 37.520000, -122.194700, 37.497000 )
+Hawley                        St       (0, 2, -122.192100, 37.531000, -122.191500, 37.526000 )
+Sp Railroad                            (0, 2, -122.194700, 37.497000, -122.193328, 37.484800 )
+81st                          Ave      (0, 2, -122.191200, 37.493000, -122.191000, 37.495000 )
+Oakport                       St       (0, 2, -122.197500, 37.422000, -122.196300, 37.386000 )
+Hegenberger                   Road     (0, 2, -122.195300, 37.401000, -122.195300, 37.404000 )
+Hegenberger                   Road     (0, 2, -122.195500, 37.378000, -122.195400, 37.385000 )
+Edes                          Ave      (0, 2, -122.194800, 37.444000, -122.194100, 37.444000 )
+Mc Clary                      Ave      (0, 2, -122.192400, 37.443000, -122.191700, 37.448000 )
+Worth                         St       (0, 2, -122.191500, 37.378000, -122.191600, 37.379000 )
+Worth                         St       (0, 2, -122.190300, 37.362000, -122.190300, 37.365000 )
+Mac Arthur                    Blvd     (0, 2, -122.188000, 37.837000, -122.187100, 37.833000 )
+Calaveras                     Ave      (0, 2, -122.186400, 37.845000, -122.185400, 37.841000 )
+Simmons                       St       (0, 2, -122.188100, 37.803000, -122.187400, 37.805000 )
+Underwood                     Ave      (0, 2, -122.185200, 37.828000, -122.184600, 37.809000 )
+55th                          Ave      (0, 2, -122.188600, 37.768000, -122.188300, 37.770000 )
+Mac Arthur                    Blvd     (0, 2, -122.186800, 37.773000, -122.186100, 37.767000 )
+58th                          Ave      (0, 2, -122.187600, 37.749000, -122.186700, 37.755000 )
+Mac Arthur                    Blvd     (0, 2, -122.185000, 37.757000, -122.184300, 37.753000 )
+Murdock                       Ct       (0, 2, -122.182800, 37.753000, -122.183200, 37.756000 )
+Daisy                         St       (0, 3, -122.181700, 37.843000, -122.180000, 37.848000 , -122.179000, 37.851000)
+Majestic                      Ave      (0, 2, -122.178400, 37.793000, -122.178900, 37.788000 )
+Seminary                      Ave      (0, 2, -122.181400, 37.772000, -122.180700, 37.778000 )
+Camden                        St       (0, 2, -122.182300, 37.735000, -122.181700, 37.730000 )
+Outlook                       Ave      (0, 2, -122.178900, 37.788000, -122.178600, 37.787000 )
+62nd                          Ave      (0, 2, -122.179400, 37.751000, -122.178700, 37.755000 )
+64th Av                       Pl       (0, 2, -122.178400, 37.734000, -122.176700, 37.744000 )
+60th                          Ave      (0, 2, -122.188100, 37.712000, -122.186800, 37.722000 )
+Fortune                       Way      (0, 2, -122.187700, 37.693000, -122.187000, 37.688000 )
+63rd                          Ave      (0, 2, -122.185300, 37.700000, -122.184400, 37.706000 )
+64th                          Ave      (0, 2, -122.188000, 37.673000, -122.185200, 37.691000 )
+Flora                         St       (0, 2, -122.188200, 37.625000, -122.187900, 37.622000 )
+70th                          Ave      (0, 2, -122.187000, 37.611000, -122.186200, 37.616000 )
+Weld                          St       (0, 2, -122.182700, 37.640000, -122.181000, 37.623000 )
+73rd                          Ave      (0, 2, -122.183700, 37.606000, -122.182900, 37.611000 )
+Brann                         St       (0, 2, -122.180600, 37.709000, -122.178500, 37.705000 )
+Bancroft                      Ave      (0, 2, -122.179600, 37.689000, -122.179200, 37.684000 )
+Foothill                      Blvd     (0, 2, -122.177500, 37.697000, -122.176400, 37.696000 )
+Church                        St       (0, 2, -122.179000, 37.675000, -122.178500, 37.678000 )
+73rd                          Ave      (0, 2, -122.180200, 37.628000, -122.179400, 37.633000 )
+Lockwood                      St       (0, 2, -122.180200, 37.628000, -122.177800, 37.601000 )
+73rd                          Ave      (0, 2, -122.176800, 37.651000, -122.176100, 37.655000 )
+Deerwood                      St       (0, 2, -122.177500, 37.623000, -122.177000, 37.618000 )
+Leona                         St       (0, 2, -122.175100, 37.840000, -122.173900, 37.836000 )
+Seminary                      Ave      (0, 2, -122.177200, 37.796000, -122.175600, 37.798000 )
+Sunnymere                     Ave      (0, 2, -122.172900, 37.803000, -122.171800, 37.795000 )
+Van Mourik                    Ave      (0, 2, -122.171800, 37.795000, -122.171600, 37.801000 )
+64th                          Ave      (0, 2, -122.175800, 37.760000, -122.175300, 37.767000 )
+Outlook                       Ave      (0, 2, -122.174300, 37.750000, -122.173600, 37.746000 )
+Sunnymere                     Ave      (0, 2, -122.170700, 37.791000, -122.169900, 37.786000 )
+Russell                       Ave      (0, 2, -122.169400, 37.819000, -122.168700, 37.845000 )
+Sunnymere                     Ave      (0, 2, -122.168100, 37.776000, -122.167200, 37.770000 )
+Simson                        St       (0, 2, -122.169300, 37.737000, -122.168700, 37.740000 )
+Edwards                       Ave      (0, 2, -122.165200, 37.749000, -122.163800, 37.759000 )
+Garfield                      Ave      (0, 2, -122.174600, 37.664000, -122.174000, 37.660000 )
+73rd                          Ave      (0, 2, -122.174600, 37.664000, -122.172400, 37.680000 )
+Mac Arthur                    Blvd     (0, 2, -122.169700, 37.680000, -122.169600, 37.679000 )
+74th                          Ave      (0, 2, -122.175000, 37.653000, -122.174000, 37.660000 )
+Dashwood                      Ave      (0, 2, -122.177000, 37.618000, -122.175500, 37.627000 )
+Garfield                      Ave      (0, 2, -122.171800, 37.638000, -122.171300, 37.632000 )
+Bancroft                      Ave      (0, 2, -122.171800, 37.620000, -122.171500, 37.617000 )
+Atherton                      St       (0, 2, -122.170100, 37.612000, -122.169600, 37.606000 )
+Outlook                       Ave      (0, 2, -122.168000, 37.698000, -122.167800, 37.697000 )
+77th                          Ave      (0, 2, -122.170000, 37.656000, -122.168200, 37.668000 )
+Greenly                       Dr       (0, 2, -122.163500, 37.727000, -122.161000, 37.716000 )
+Parker                        Ave      (0, 2, -122.164700, 37.685000, -122.164300, 37.686000 )
+78th                          Ave      (0, 2, -122.169700, 37.652000, -122.167400, 37.663000 )
+82nd                          Ave      (0, 2, -122.169500, 37.596000, -122.168100, 37.603000 )
+79th                          Ave      (0, 2, -122.168600, 37.639000, -122.166200, 37.650000 )
+82nd                          Ave      (0, 2, -122.165900, 37.614000, -122.165300, 37.619000 )
+Mac Arthur                    Blvd     (0, 2, -122.163600, 37.618000, -122.162900, 37.612000 )
+Hegenberger                   Exwy     (0, 2, -122.187400, 37.572000, -122.189100, 37.560000 )
+Hamilton                      St       (0, 2, -122.188000, 37.549000, -122.187500, 37.544000 )
+14th                          St       (0, 2, -122.184500, 37.581000, -122.183900, 37.579000 )
+Rudsdale                      St       (0, 2, -122.185500, 37.552000, -122.184900, 37.547000 )
+85th                          Ave      (0, 2, -122.187700, 37.466000, -122.186000, 37.476000 )
+E                             St       (0, 3, -122.183200, 37.505000, -122.182600, 37.498000 , -122.182000, 37.490000)
+Locust                        St       (0, 2, -122.181300, 37.578000, -122.180700, 37.572000 )
+80th                          Ave      (0, 2, -122.180700, 37.563000, -122.179300, 37.570000 )
+78th                          Ave      (0, 2, -122.179100, 37.593000, -122.178400, 37.597000 )
+Plymouth                      St       (0, 2, -122.176300, 37.593000, -122.175800, 37.586000 )
+14th                          St       (0, 2, -122.177100, 37.544000, -122.176800, 37.540000 )
+D                             St       (0, 2, -122.181100, 37.505000, -122.180500, 37.497000 )
+87th                          Ave      (0, 2, -122.183400, 37.474000, -122.181400, 37.484000 )
+83rd                          Ave      (0, 2, -122.179900, 37.531000, -122.179000, 37.535000 )
+14th                          St       (0, 3, -122.176100, 37.529000, -122.175700, 37.524000 , -122.175400, 37.520000)
+D                             St       (0, 2, -122.179000, 37.476000, -122.178500, 37.470000 )
+Wp Railroad                            (0, 2, -122.187700, 37.466000, -122.183400, 37.430000 )
+Industrial                    St       (0, 2, -122.183400, 37.424000, -122.183300, 37.425000 )
+Clara                         St       (0, 2, -122.187000, 37.339000, -122.186500, 37.357000 )
+Clara                         St       (0, 2, -122.185500, 37.377000, -122.185100, 37.381000 )
+Railroad                      Ave      (0, 2, -122.183500, 37.394000, -122.182800, 37.388000 )
+Maddux                        Dr       (0, 2, -122.184800, 37.366000, -122.184800, 37.360000 )
+Edes                          Ave      (0, 2, -122.183300, 37.363000, -122.182100, 37.359000 )
+89th                          Ave      (0, 2, -122.182200, 37.459000, -122.180300, 37.471000 )
+90th                          Ave      (0, 2, -122.179800, 37.464000, -122.178500, 37.470000 )
+C                             St       (0, 2, -122.176800, 37.460000, -122.174900, 37.435000 )
+Nevada                        St       (0, 2, -122.182100, 37.370000, -122.181500, 37.376000 )
+Lyndhurst                     St       (0, 2, -122.182300, 37.354000, -122.180700, 37.348000 )
+98th                          Ave      (0, 2, -122.179100, 37.382000, -122.178800, 37.384000 )
+98th                          Ave      (0, 2, -122.178300, 37.388000, -122.177300, 37.396000 )
+100th                         Ave      (0, 2, -122.178900, 37.364000, -122.178500, 37.367000 )
+Sp Railroad                            (0, 2, -122.178500, 37.355000, -122.178000, 37.351000 )
+Tartarian                     St       (0, 2, -122.177400, 37.350000, -122.176800, 37.352000 )
+82nd                          Ave      (0, 2, -122.176400, 37.562000, -122.174700, 37.570000 )
+85th                          Ave      (0, 2, -122.176300, 37.533000, -122.174800, 37.540000 )
+83rd                          Ave      (0, 2, -122.174100, 37.563000, -122.172400, 37.571000 )
+85th                          Ave      (0, 2, -122.173000, 37.548000, -122.171300, 37.556000 )
+Holly                         St       (0, 2, -122.174200, 37.532000, -122.173700, 37.526000 )
+90th                          Ave      (0, 2, -122.176900, 37.477000, -122.175300, 37.485000 )
+92nd                          Ave      (0, 2, -122.175900, 37.464000, -122.174200, 37.472000 )
+88th                          Ave      (0, 2, -122.172800, 37.514000, -122.171100, 37.521000 )
+Holly                         St       (0, 2, -122.171500, 37.488000, -122.170400, 37.472000 )
+94th                          Ave      (0, 2, -122.171400, 37.466000, -122.170400, 37.472000 )
+84th                          Ave      (0, 2, -122.168300, 37.580000, -122.166500, 37.589000 )
+87th                          Ave      (0, 2, -122.169800, 37.536000, -122.168100, 37.544000 )
+85th                          Ave      (0, 2, -122.167700, 37.573000, -122.166000, 37.581000 )
+Auseon                        Ave      (0, 2, -122.165300, 37.565000, -122.165000, 37.567000 )
+87th                          Ave      (0, 2, -122.164600, 37.561000, -122.164300, 37.563000 )
+Cherry                        St       (0, 2, -122.169100, 37.512000, -122.168400, 37.502000 )
+Birch                         St       (0, 2, -122.167300, 37.509000, -122.166100, 37.492000 )
+Cherry                        St       (0, 2, -122.167100, 37.488000, -122.166100, 37.474000 )
+Bancroft                      Ave      (0, 3, -122.164300, 37.523000, -122.163100, 37.508000 , -122.162100, 37.493000)
+Birch                         St       (0, 2, -122.165300, 37.478000, -122.164100, 37.464000 )
+B                             St       (0, 2, -122.174900, 37.451000, -122.174300, 37.443000 )
+98th                          Ave      (0, 2, -122.176700, 37.401000, -122.175800, 37.408000 )
+C                             St       (0, 3, -122.173700, 37.418000, -122.172300, 37.399000 , -122.171600, 37.393000)
+14th                          St       (0, 2, -122.170900, 37.459000, -122.170400, 37.453000 )
+99th Av                       Ct       (0, 2, -122.169800, 37.427000, -122.169400, 37.422000 )
+Russet                        St       (0, 2, -122.175800, 37.361000, -122.175300, 37.355000 )
+Pippin                        St       (0, 2, -122.174700, 37.344000, -122.173400, 37.332000 )
+Royal Ann                     St       (0, 2, -122.170500, 37.380000, -122.170200, 37.376000 )
+Royal Ann                     St       (0, 2, -122.169600, 37.367000, -122.169100, 37.362000 )
+98th                          Ave      (0, 2, -122.169300, 37.438000, -122.168200, 37.444000 )
+14th                          St       (0, 2, -122.168200, 37.422000, -122.168000, 37.419000 )
+Plymouth                      St       (0, 2, -122.166100, 37.454000, -122.165200, 37.440000 )
+100th                         Ave      (0, 2, -122.165700, 37.429000, -122.164700, 37.432000 )
+Plymouth                      St       (0, 2, -122.164300, 37.425000, -122.164100, 37.418000 )
+104th                         Ave      (0, 2, -122.169700, 37.375000, -122.168100, 37.383000 )
+Pontiac                       St       (0, 2, -122.166500, 37.383000, -122.166309, 37.377900 )
+Bristol                       Blvd     (0, 2, -122.167400, 37.353000, -122.169800, 37.342000 )
+14th                          St       (0, 2, -122.165500, 37.387000, -122.165101, 37.380010 )
+Pontiac                       St       (0, 3, -122.165000, 37.365000, -122.164700, 37.359000 , -122.164300, 37.354000)
+Boeing                        St       (0, 2, -122.212200, 37.340000, -122.211200, 37.322000 )
+Earhart                       Road     (0, 2, -122.208600, 37.296000, -122.201200, 37.255000 )
+Airport                       Road     (0, 2, -122.208500, 37.147000, -122.210100, 37.154000 )
+Pardee                        Dr       (0, 2, -122.199100, 37.286000, -122.197400, 37.270000 )
+Earhart                       Road     (0, 2, -122.200400, 37.256000, -122.200400, 37.259000 )
+98th                          Ave      (0, 2, -122.200100, 37.258000, -122.197400, 37.270000 )
+Sextus                        Road     (0, 2, -122.193800, 37.330000, -122.189600, 37.327000 )
+Coral                         Road     (0, 2, -122.190700, 37.340000, -122.190200, 37.334000 )
+98th                          Ave      (0, 2, -122.191400, 37.294000, -122.190400, 37.298000 )
+Koford                        Road     (0, 2, -122.190300, 37.296000, -122.188400, 37.286000 )
+Adams                         Ave      (0, 2, -122.190600, 37.253000, -122.189300, 37.272000 )
+Denslowe                      St       (0, 2, -122.185100, 37.336000, -122.184700, 37.332000 )
+Maddux                        Dr       (0, 2, -122.182900, 37.343000, -122.182300, 37.341000 )
+Bernhardt                     Dr       (0, 2, -122.185200, 37.297000, -122.184700, 37.292000 )
+Empire                        Road     (0, 2, -122.187300, 37.278000, -122.185800, 37.277000 )
+Cascade                       Road     (0, 2, -122.183200, 37.241000, -122.180800, 37.216000 )
+Hunter                        Ave      (0, 2, -122.182400, 37.312000, -122.181600, 37.339000 )
+Kerwin                        Ave      (0, 2, -122.181000, 37.296000, -122.180700, 37.295000 )
+Knight                        St       (0, 2, -122.180000, 37.291000, -122.179400, 37.288000 )
+June                          Ct       (0, 2, -122.178600, 37.316000, -122.178000, 37.314000 )
+105th                         Ave      (0, 2, -122.177400, 37.325000, -122.177100, 37.327000 )
+Malta                         Ct       (0, 2, -122.183200, 37.273000, -122.182300, 37.278000 )
+Tudor                         Road     (0, 2, -122.180800, 37.216000, -122.180100, 37.216000 )
+North                         Blvd     (0, 2, -122.176800, 37.243000, -122.177500, 37.242000 )
+Davis                         St       (0, 2, -122.185700, 37.158000, -122.192100, 37.139000 )
+Davis                         St       (0, 2, -122.183100, 37.165000, -122.183500, 37.165000 )
+Melcher                       St       (0, 2, -122.179200, 37.210000, -122.177800, 37.198000 )
+Sp Railroad                            (0, 2, -122.180100, 37.115000, -122.178000, 37.088000 )
+Sp Railroad                            (0, 2, -122.174800, 37.322000, -122.174000, 37.315000 )
+Colorados                     Dr       (0, 2, -122.175700, 37.281000, -122.174800, 37.301000 )
+Robledo                       Dr       (0, 2, -122.173400, 37.304000, -122.170600, 37.281000 )
+San Leandro                   St       (0, 2, -122.171700, 37.335000, -122.170900, 37.329000 )
+San Leandro                   St       (0, 2, -122.170300, 37.323000, -122.168900, 37.312000 )
+Pueblo                        Dr       (0, 2, -122.174800, 37.269000, -122.174300, 37.269000 )
+Frederick                     Road     (0, 2, -122.174000, 37.244000, -122.173500, 37.225000 )
+Catron                        Dr       (0, 2, -122.171600, 37.270000, -122.171100, 37.275000 )
+Douglas                       Dr       (0, 2, -122.170500, 37.227000, -122.169800, 37.214000 )
+Bay Area Rapid Transit                 (0, 2, -122.169400, 37.311000, -122.167900, 37.300000 )
+Pershing                      Dr       (0, 2, -122.166200, 37.339000, -122.165800, 37.333000 )
+Pershing                      Dr       (0, 2, -122.165000, 37.321000, -122.164500, 37.315000 )
+Pleasant                      Way      (0, 2, -122.165300, 37.311000, -122.164600, 37.302000 )
+Pershing                      Dr       (0, 2, -122.163300, 37.299000, -122.162800, 37.291000 )
+Preda                         St       (0, 2, -122.168600, 37.244000, -122.169100, 37.255000 )
+Peralta                       Ave      (0, 2, -122.163900, 37.267000, -122.165300, 37.270000 )
+Antonio                       St       (0, 2, -122.164200, 37.251000, -122.165300, 37.247000 )
+Leonard                       Dr       (0, 3, -122.173100, 37.185000, -122.173100, 37.172000 , -122.172500, 37.165000)
+Davis                         St       (0, 2, -122.171900, 37.199000, -122.172500, 37.198000 )
+Davis                         St       (0, 2, -122.170200, 37.203000, -122.171000, 37.201000 )
+Valley                        St       (0, 2, -122.170000, 37.160000, -122.170700, 37.158000 )
+Merced                        St       (0, 2, -122.170800, 37.102000, -122.170700, 37.099000 )
+Pierce                        Ave      (0, 2, -122.168300, 37.186000, -122.167200, 37.168000 )
+Pacific                       Ave      (0, 2, -122.166100, 37.214000, -122.165400, 37.204000 )
+Pacific                       Ave      (0, 3, -122.163200, 37.171000, -122.162100, 37.156000 , -122.161300, 37.150000)
+Sundberg                      Ave      (0, 2, -122.166700, 37.119000, -122.165900, 37.116000 )
+Williams                      St       (0, 2, -122.164900, 37.143000, -122.165500, 37.141000 )
+Williams                      St       (0, 2, -122.163400, 37.151000, -122.164000, 37.148000 )
+Marina                        Blvd     (0, 2, -122.164188, 37.105140, -122.165100, 37.101000 )
+Neptune                       Dr       (0, 2, -122.188100, 37.032000, -122.187500, 37.007000 )
+Avenue 130th                           (0, 2, -122.185100, 37.044000, -122.187200, 37.036000 )
+Neptune                       Dr       (0, 2, -122.187800, 37.989000, -122.185300, 37.958000 )
+Juneau                        St       (0, 2, -122.181400, 37.068000, -122.180100, 37.049000 )
+Doolittle                     Dr       (0, 2, -122.179300, 37.052000, -122.178700, 37.043000 )
+Avenue 134th                           (0, 2, -122.182300, 37.002000, -122.185100, 37.992000 )
+Aurora                        Dr       (0, 2, -122.180400, 37.973000, -122.180000, 37.966000 )
+Jamaica                       Way      (0, 2, -122.178500, 37.971000, -122.179200, 37.969000 )
+Santiago                      Road     (0, 2, -122.176000, 37.962000, -122.175600, 37.956000 )
+Outrigger                     Dr       (0, 2, -122.180200, 37.950000, -122.178449, 37.919700 )
+Finback                       Way      (0, 2, -122.178300, 37.934000, -122.177600, 37.936000 )
+Belvedere                     Ave      (0, 2, -122.176800, 37.918000, -122.177200, 37.918000 )
+Fairway                       Dr       (0, 2, -122.175300, 37.994000, -122.176433, 37.989330 )
+Fiji                          Way      (0, 2, -122.174700, 37.968000, -122.175300, 37.965000 )
+Republic                      Ave      (0, 2, -122.168800, 37.046000, -122.172100, 37.032000 )
+Avenue 140th                           (0, 2, -122.165600, 37.003000, -122.169100, 37.988000 )
+Driftwood                     Way      (0, 2, -122.172600, 37.924000, -122.173400, 37.924000 )
+Farallon                      Dr       (0, 2, -122.168100, 37.938000, -122.169800, 37.937000 )
+Ridge Top                     Road     (0, 2, -122.153800, 37.164000, -122.156600, 37.179000 )
+Campus                        Dr       (0, 2, -122.162600, 37.858000, -122.161100, 37.843000 )
+Redwood                       Road     (0, 2, -122.149300, 37.980000, -122.143700, 37.001000 )
+Saddle Brook                  Dr       (0, 3, -122.147800, 37.909000, -122.145400, 37.904000 , -122.145100, 37.888000)
+Parkridge                     Dr       (0, 2, -122.143800, 37.884000, -122.142800, 37.900000 )
+Redwood Creek                          (0, 2, -122.136600, 37.968000, -122.130200, 37.918000 )
+Kaiser Creek                  Road     (0, 2, -122.092800, 37.885000, -122.091800, 37.802000 )
+Cull Creek                             (0, 2, -122.062400, 37.875000, -122.058200, 37.527000 )
+Columbian                     Dr       (0, 2, -122.163500, 37.727000, -122.162700, 37.734000 )
+Field                         St       (0, 2, -122.160400, 37.721000, -122.159600, 37.728000 )
+Sanford                       St       (0, 2, -122.154600, 37.737000, -122.154300, 37.728000 )
+Crest                         Ave      (0, 2, -122.162000, 37.699000, -122.156800, 37.664000 )
+Greenly                       Dr       (0, 2, -122.159500, 37.704000, -122.157800, 37.696000 )
+Outlook                       Ave      (0, 2, -122.161600, 37.636000, -122.161200, 37.632000 )
+Cosgrave                      Ave      (0, 2, -122.162100, 37.620000, -122.161600, 37.626000 )
+Partridge                     Ave      (0, 2, -122.159700, 37.655000, -122.158400, 37.652000 )
+El Monte                      Ave      (0, 2, -122.159400, 37.624000, -122.159100, 37.630000 )
+Earl                          St       (0, 2, -122.156400, 37.709000, -122.155200, 37.697000 )
+Keller                        Ave      (0, 2, -122.154000, 37.723000, -122.153100, 37.722000 )
+Winthrope                     St       (0, 2, -122.155900, 37.694000, -122.154800, 37.678000 )
+Mountain                      Blvd     (0, 2, -122.153500, 37.702000, -122.153000, 37.696000 )
+Fontaine                      Ct       (0, 2, -122.152500, 37.685000, -122.151700, 37.688000 )
+Mountain                      Blvd     (0, 2, -122.151000, 37.659000, -122.148400, 37.640000 )
+Skyline                       Blvd     (0, 2, -122.144000, 37.851000, -122.140900, 37.829000 )
+Skyline                       Blvd     (0, 2, -122.140900, 37.819000, -122.130700, 37.730000 )
+Coach                         Dr       (0, 2, -122.138300, 37.735000, -122.135500, 37.706000 )
+Barcelona                     St       (0, 2, -122.145900, 37.639000, -122.144500, 37.623000 )
+Briar Cliff                   Road     (0, 2, -122.140900, 37.647000, -122.138200, 37.658000 )
+Oak Hill                      Road     (0, 2, -122.140900, 37.647000, -122.138200, 37.658000 )
+Hillside                      St       (0, 2, -122.162800, 37.561000, -122.162500, 37.553000 )
+Mac Arthur                    Blvd     (0, 2, -122.160500, 37.553000, -122.159600, 37.525000 )
+96th                          Ave      (0, 2, -122.162100, 37.493000, -122.161000, 37.498000 )
+Lawlor                        St       (0, 2, -122.157300, 37.533000, -122.156700, 37.517000 )
+Springfield                   St       (0, 2, -122.159800, 37.485000, -122.159700, 37.477000 )
+Warner                        Ave      (0, 2, -122.157900, 37.477000, -122.157100, 37.482000 )
+Sarazen                       Ave      (0, 2, -122.153900, 37.580000, -122.153200, 37.585000 )
+Oak Knoll                     Blvd     (0, 2, -122.151000, 37.559000, -122.150400, 37.556000 )
+Stearns                       Ave      (0, 2, -122.156400, 37.533000, -122.153300, 37.512000 )
+98th                          Ave      (0, 2, -122.156800, 37.498000, -122.155800, 37.502000 )
+Stanley                       Ave      (0, 3, -122.150400, 37.485000, -122.150000, 37.478000 , -122.149800, 37.469000)
+Sunnyside                     St       (0, 2, -122.161100, 37.462000, -122.160400, 37.453000 )
+Birch                         St       (0, 2, -122.161700, 37.425000, -122.161400, 37.417000 )
+Bancroft                      Ave      (0, 2, -122.158500, 37.445000, -122.158300, 37.441000 )
+101st                         Ave      (0, 2, -122.159500, 37.438000, -122.158300, 37.441000 )
+104th                         Ave      (0, 2, -122.161200, 37.411000, -122.158700, 37.417000 )
+104th                         Ave      (0, 2, -122.158300, 37.417000, -122.157300, 37.423000 )
+14th                          St       (0, 2, -122.162600, 37.348000, -122.162100, 37.344000 )
+Beverly                       Ave      (0, 2, -122.158600, 37.395000, -122.158200, 37.388000 )
+Beverly                       Ave      (0, 2, -122.157800, 37.382000, -122.157200, 37.375000 )
+Cambridge                     Ave      (0, 2, -122.161600, 37.335000, -122.157500, 37.353000 )
+Mac Arthur                    Blvd     (0, 2, -122.155200, 37.454000, -122.154100, 37.446000 )
+106th                         Ave      (0, 2, -122.158000, 37.404000, -122.156000, 37.409000 )
+Shaw                          St       (0, 2, -122.151800, 37.459000, -122.151100, 37.455000 )
+107th                         Ave      (0, 2, -122.155500, 37.403000, -122.153100, 37.410000 )
+Myers                         St       (0, 2, -122.151100, 37.423000, -122.151000, 37.415000 )
+Sunnyside                     St       (0, 2, -122.156400, 37.386000, -122.155900, 37.380000 )
+Broadmoor                     Blvd     (0, 2, -122.156000, 37.358000, -122.154600, 37.364000 )
+Kenilworth                    Ave      (0, 2, -122.151000, 37.393000, -122.150500, 37.383000 )
+Bancroft                      Ave      (0, 2, -122.151800, 37.358000, -122.151100, 37.349000 )
+Golf Links                    Road     (0, 2, -122.149100, 37.533000, -122.148200, 37.536000 )
+Dorisa                        Ave      (0, 2, -122.146200, 37.555000, -122.144800, 37.572000 )
+Golf Links                    Road     (0, 2, -122.142500, 37.565000, -122.141500, 37.569000 )
+Stella                        St       (0, 2, -122.142720, 37.482400, -122.141800, 37.469000 )
+Mac Arthur                    Blvd     (0, 2, -122.149500, 37.412000, -122.148700, 37.408000 )
+Sheldon                       St       (0, 2, -122.146000, 37.455000, -122.145400, 37.451000 )
+108th                         Ave      (0, 2, -122.147400, 37.419000, -122.146800, 37.423000 )
+Foothill                      Blvd     (0, 2, -122.145500, 37.421000, -122.145202, 37.417520 )
+Kenilworth                    Ave      (0, 2, -122.150100, 37.376000, -122.149800, 37.371000 )
+Merle                         Ct       (0, 2, -122.148200, 37.368000, -122.147900, 37.358000 )
+Broadmoor                     Blvd     (0, 2, -122.147000, 37.397000, -122.146600, 37.399000 )
+Merle                         Ct       (0, 2, -122.146700, 37.373000, -122.146700, 37.366000 )
+Dowling                       Blvd     (0, 2, -122.146700, 37.359000, -122.144700, 37.359000 )
+Hellman                       St       (0, 2, -122.140300, 37.471000, -122.140600, 37.464000 )
+Mitchell                      Ave      (0, 2, -122.143800, 37.376000, -122.143900, 37.374000 )
+Lewis                         Ave      (0, 2, -122.143000, 37.359000, -122.143300, 37.339000 )
+Mac Arthur                    Blvd     (0, 2, -122.140700, 37.372000, -122.140000, 37.364000 )
+Mac Arthur                    Blvd     (0, 2, -122.139400, 37.356000, -122.139376, 37.355030 )
+Hansom                        Dr       (0, 2, -122.136900, 37.750000, -122.135400, 37.726000 )
+Keller                        Ave      (0, 2, -122.135300, 37.702000, -122.134500, 37.705000 )
+Mendenhall                    Road     (0, 2, -122.134000, 37.677000, -122.132400, 37.685000 )
+Donna                         Way      (0, 2, -122.133300, 37.606000, -122.131600, 37.599000 )
+Chimney Rock                           (0, 2, -122.130000, 37.701000, -122.129050, 37.701950 )
+Sigourney Elysian Fields      Dr       (0, 2, -122.128000, 37.674000, -122.127700, 37.667000 )
+Elysian Fields                Dr       (0, 2, -122.127500, 37.646000, -122.127500, 37.649000 )
+Grass Valley                  Road     (0, 2, -122.124600, 37.764000, -122.117600, 37.634000 )
+Elysian Fields                Dr       (0, 2, -122.136900, 37.589000, -122.136100, 37.595000 )
+Elysian Fields                Dr       (0, 2, -122.133600, 37.595000, -122.133000, 37.594000 )
+Malcolm                       Ave      (0, 2, -122.136600, 37.469000, -122.135900, 37.483000 )
+Cameron                       Ave      (0, 2, -122.131600, 37.502000, -122.132700, 37.481000 )
+Elvessa                       St       (0, 2, -122.129600, 37.528000, -122.129000, 37.522000 )
+Fallbrook                     Way      (0, 2, -122.128500, 37.533000, -122.127800, 37.522000 )
+Malcolm                       Ave      (0, 2, -122.126900, 37.516000, -122.125000, 37.514000 )
+Ziegler                       Ave      (0, 2, -122.125800, 37.483000, -122.125000, 37.499000 )
+Marlow                        Dr       (0, 2, -122.137500, 37.346000, -122.137400, 37.351000 )
+Revere                        Ave      (0, 2, -122.134700, 37.359000, -122.134000, 37.362000 )
+Key                           Ct       (0, 2, -122.124600, 37.545000, -122.124400, 37.553000 )
+Stacy                         St       (0, 2, -122.121800, 37.544000, -122.119900, 37.532000 )
+Dunkirk                       Ave      (0, 2, -122.125400, 37.526000, -122.122800, 37.531000 )
+Grass Valley                  Ct       (0, 2, -122.119400, 37.518000, -122.118700, 37.516000 )
+Sun Valley                    Dr       (0, 2, -122.117400, 37.493000, -122.117300, 37.488000 )
+Sun Valley                    Dr       (0, 2, -122.119100, 37.470000, -122.120100, 37.465000 )
+Belleview                     Dr       (0, 2, -122.162600, 37.325000, -122.163500, 37.320000 )
+Best                          Ave      (0, 2, -122.162200, 37.284000, -122.163600, 37.278000 )
+Lafayette                     Ave      (0, 2, -122.160200, 37.293000, -122.159700, 37.287000 )
+Peralta                       Ave      (0, 2, -122.158500, 37.293000, -122.159200, 37.290000 )
+Pershing                      Dr       (0, 3, -122.161800, 37.278000, -122.161300, 37.271000 , -122.160900, 37.266000)
+Davis                         St       (0, 3, -122.162400, 37.225000, -122.163200, 37.222000 , -122.164700, 37.218000)
+Estudillo                     Ave      (0, 2, -122.160800, 37.220000, -122.162100, 37.214000 )
+Toler                         Ave      (0, 2, -122.157500, 37.269000, -122.159300, 37.261000 )
+Estudillo                     Ave      (0, 2, -122.158400, 37.229000, -122.159500, 37.225000 )
+Clarke                        St       (0, 2, -122.156800, 37.225000, -122.156200, 37.217000 )
+Dowling                       Blvd     (0, 2, -122.155200, 37.323000, -122.154200, 37.335000 )
+Dutton                        Ave      (0, 2, -122.155000, 37.319000, -122.153400, 37.323000 )
+Oakes                         Blvd     (0, 2, -122.152300, 37.316000, -122.151100, 37.318000 )
+Lee                           Ave      (0, 2, -122.150700, 37.298000, -122.150600, 37.291000 )
+14th                          St       (0, 2, -122.156200, 37.262000, -122.155700, 37.253000 )
+Harrison                      St       (0, 2, -122.153900, 37.268000, -122.153500, 37.259000 )
+Estudillo                     Ave      (0, 2, -122.154700, 37.245000, -122.155100, 37.244000 )
+Washington                    Ave      (0, 2, -122.153600, 37.222000, -122.153400, 37.217000 )
+Santa Rosa                    St       (0, 2, -122.151300, 37.265000, -122.150900, 37.256000 )
+Santa Rosa                    St       (0, 2, -122.150600, 37.247000, -122.150200, 37.238000 )
+Martinez                      St       (0, 2, -122.159000, 37.191000, -122.158200, 37.185000 )
+Thornton                      St       (0, 2, -122.157200, 37.189000, -122.157600, 37.188000 )
+Williams                      St       (0, 2, -122.156800, 37.179000, -122.157700, 37.175000 )
+Castro                        St       (0, 2, -122.162900, 37.144000, -122.163600, 37.141000 )
+Marina                        Blvd     (0, 2, -122.163000, 37.111000, -122.164000, 37.106000 )
+Carpentier                    St       (0, 2, -122.156700, 37.203000, -122.156100, 37.194000 )
+Hays                          St       (0, 2, -122.153800, 37.203000, -122.153300, 37.194000 )
+San Leandro                   Blvd     (0, 2, -122.155700, 37.174000, -122.154500, 37.168000 )
+Estabrook                     St       (0, 2, -122.153900, 37.160000, -122.154700, 37.156000 )
+Thornton                      St       (0, 2, -122.150500, 37.215000, -122.152600, 37.206000 )
+Estabrook                     St       (0, 2, -122.152700, 37.164000, -122.153500, 37.161000 )
+Cherry                        St       (0, 2, -122.151100, 37.161000, -122.150300, 37.149000 )
+Aladdin                       Ave      (0, 2, -122.153200, 37.088000, -122.157700, 37.068000 )
+Begier                        Ave      (0, 2, -122.150000, 37.314000, -122.148800, 37.317000 )
+Bancroft                      Ave      (0, 2, -122.148500, 37.311000, -122.148100, 37.303000 )
+Bancroft                      Ave      (0, 2, -122.147500, 37.288000, -122.147000, 37.276000 )
+Glen                          Dr       (0, 2, -122.145900, 37.316000, -122.142200, 37.315000 )
+Estudillo                     Ave      (0, 2, -122.150900, 37.256000, -122.149800, 37.260000 )
+Dolores                       Ave      (0, 2, -122.151700, 37.224000, -122.149800, 37.229000 )
+Juana                         Ave      (0, 2, -122.148100, 37.243000, -122.145900, 37.249000 )
+Elsie                         Ave      (0, 2, -122.149700, 37.210000, -122.144700, 37.223000 )
+Superior                      Ave      (0, 2, -122.142000, 37.324000, -122.142200, 37.315000 )
+Estudillo                     Ave      (0, 2, -122.144600, 37.274000, -122.142500, 37.280000 )
+Dutton                        Ave      (0, 2, -122.140300, 37.339000, -122.139400, 37.340000 )
+Mac Arthur                    Blvd     (0, 2, -122.139000, 37.340000, -122.138812, 37.340000 )
+Collier                       Dr       (0, 2, -122.140900, 37.299000, -122.140000, 37.302000 )
+Trombas                       Ave      (0, 2, -122.142800, 37.217000, -122.142500, 37.211000 )
+Juana                         Ave      (0, 2, -122.141700, 37.262000, -122.139600, 37.267000 )
+Maud                          Ave      (0, 2, -122.138700, 37.250000, -122.137900, 37.252000 )
+San Rafael                    St       (0, 2, -122.139800, 37.225000, -122.139500, 37.219000 )
+Harlan                        St       (0, 2, -122.147700, 37.195000, -122.151000, 37.182000 )
+Washington                    Ave      (0, 3, -122.149100, 37.154000, -122.149000, 37.151000 , -122.148500, 37.144000)
+14th                          St       (0, 2, -122.145700, 37.181000, -122.145000, 37.176000 )
+Dundee                        Ct       (0, 2, -122.145800, 37.166000, -122.145100, 37.160000 )
+Hudson                        Lane     (0, 2, -122.148300, 37.140000, -122.149000, 37.137000 )
+Rose                          Dr       (0, 2, -122.145100, 37.142000, -122.144500, 37.138000 )
+Sybil                         Ave      (0, 2, -122.144300, 37.213000, -122.143500, 37.215000 )
+Valita                        Dr       (0, 2, -122.141700, 37.192000, -122.141000, 37.195000 )
+School                        St       (0, 3, -122.137800, 37.209000, -122.136900, 37.199000 , -122.136400, 37.197000)
+14th                          St       (0, 2, -122.141000, 37.147000, -122.139700, 37.138000 )
+Orchid                        Dr       (0, 2, -122.141600, 37.098000, -122.139700, 37.085000 )
+14th                          St       (0, 2, -122.137800, 37.124000, -122.137600, 37.123000 )
+Miller                        St       (0, 2, -122.162700, 37.048000, -122.161400, 37.032000 )
+Amherst                       Ct       (0, 2, -122.157100, 37.036000, -122.157500, 37.034000 )
+Locust                        St       (0, 2, -122.160600, 37.007000, -122.159300, 37.987000 )
+Willow                        Ave      (0, 2, -122.160000, 37.961000, -122.160800, 37.960000 )
+Spruce                        St       (0, 2, -122.157800, 37.994000, -122.158600, 37.991000 )
+Juniper                       St       (0, 2, -122.159200, 37.961000, -122.159100, 37.955000 )
+Alvarado                      St       (0, 2, -122.150500, 37.050000, -122.149400, 37.030000 )
+Colgate                       St       (0, 2, -122.154500, 37.019000, -122.153800, 37.014000 )
+Fisk                          Ct       (0, 2, -122.155000, 37.972000, -122.154500, 37.968000 )
+Portola                       Dr       (0, 2, -122.150500, 37.019000, -122.151600, 37.016000 )
+Corvallis                     St       (0, 2, -122.152700, 37.974000, -122.152100, 37.970000 )
+Hubbard                       Ave      (0, 2, -122.158500, 37.914000, -122.160200, 37.914000 )
+Manor                         Blvd     (0, 2, -122.158400, 37.906000, -122.159700, 37.905000 )
+Santa Paula                            (0, 2, -122.160700, 37.848000, -122.159600, 37.834000 )
+Wicks                         Blvd     (0, 2, -122.159600, 37.856000, -122.157800, 37.833000 )
+Estudillo Canal                        (0, 2, -122.156900, 37.861000, -122.158700, 37.858000 )
+Purdue                        St       (0, 2, -122.155500, 37.950000, -122.156500, 37.949000 )
+Wiley                         St       (0, 3, -122.155300, 37.930000, -122.154800, 37.921000 , -122.154600, 37.916000)
+Farnsworth                    St       (0, 2, -122.150700, 37.952000, -122.150700, 37.944000 )
+Devonshire                    Ave      (0, 2, -122.150700, 37.924000, -122.151700, 37.924000 )
+Chapel                        Ct       (0, 2, -122.150800, 37.897000, -122.150800, 37.894000 )
+Laverne                       Dr       (0, 2, -122.156400, 37.852000, -122.154900, 37.847000 )
+Inverness                     St       (0, 2, -122.152100, 37.882000, -122.152300, 37.877000 )
+Galt                          St       (0, 2, -122.150700, 37.886000, -122.150700, 37.880000 )
+Trojan                        Ave      (0, 2, -122.151400, 37.858000, -122.152200, 37.857000 )
+Portola                       Dr       (0, 2, -122.148200, 37.024000, -122.148800, 37.021000 )
+Fremont                       Ave      (0, 2, -122.142900, 37.008000, -122.145652, 37.044050 )
+Monterey                      Blvd     (0, 2, -122.149400, 37.018000, -122.148600, 37.013000 )
+Pepperdine                    St       (0, 2, -122.150100, 37.989000, -122.148800, 37.980000 )
+Floresta                      Blvd     (0, 2, -122.146000, 37.994000, -122.147800, 37.980000 )
+Crosby                        St       (0, 2, -122.146000, 37.960000, -122.145000, 37.953000 )
+143rd                         Ave      (0, 2, -122.140800, 37.077000, -122.139700, 37.085000 )
+McClure                       Ave      (0, 2, -122.143100, 37.001000, -122.143600, 37.998000 )
+Carmel                        Way      (0, 2, -122.141900, 37.980000, -122.141100, 37.980000 )
+Caliente                      Dr       (0, 3, -122.139300, 37.993000, -122.140900, 37.990000 , -122.141700, 37.993000)
+Carmel                        Way      (0, 2, -122.139400, 37.979000, -122.138600, 37.980000 )
+Edgemoor                      St       (0, 2, -122.149200, 37.918000, -122.149200, 37.910000 )
+Cumberland                    Ave      (0, 2, -122.146700, 37.945000, -122.150700, 37.944000 )
+Devonshire                    Ave      (0, 2, -122.146600, 37.926000, -122.149800, 37.924000 )
+Manor                         Blvd     (0, 2, -122.145200, 37.911000, -122.146100, 37.910000 )
+Fargo                         Ave      (0, 2, -122.146800, 37.881000, -122.147600, 37.887000 )
+Dewey                         St       (0, 2, -122.148300, 37.862000, -122.148200, 37.857000 )
+Trojan                        Ave      (0, 2, -122.146700, 37.864000, -122.148300, 37.862000 )
+Trojan                        Ave      (0, 2, -122.144000, 37.868000, -122.144600, 37.867000 )
+Endicott                      St       (0, 2, -122.143600, 37.931000, -122.143500, 37.911000 )
+Manor                         Blvd     (0, 2, -122.142100, 37.912000, -122.142600, 37.912000 )
+Manor                         Blvd     (0, 2, -122.140200, 37.912000, -122.140900, 37.913000 )
+Washington                    Ave      (0, 2, -122.137900, 37.959000, -122.137900, 37.954000 )
+Estudillo Canal                        (0, 2, -122.139700, 37.923000, -122.140660, 37.922040 )
+Washington                    Ave      (0, 2, -122.137800, 37.897000, -122.137900, 37.891000 )
+Burkhart                      Ave      (0, 2, -122.142200, 37.856000, -122.143100, 37.859000 )
+Greer                         Ave      (0, 2, -122.139300, 37.889000, -122.140100, 37.889000 )
+Kramer                        St       (0, 2, -122.140600, 37.834000, -122.141600, 37.826000 )
+Via Vega                               (0, 2, -122.138800, 37.837000, -122.138900, 37.818000 )
+Roxbury                       Ave      (0, 2, -122.135400, 37.339000, -122.134600, 37.336000 )
+Norene                        Way      (0, 2, -122.136500, 37.286000, -122.136000, 37.287000 )
+Daniels                       Dr       (0, 2, -122.134600, 37.317000, -122.133500, 37.323000 )
+Sandelin                      Ave      (0, 2, -122.135100, 37.294000, -122.134300, 37.298000 )
+Lake Chabot                   Road     (0, 2, -122.132300, 37.308000, -122.132000, 37.307000 )
+View                          Dr       (0, 2, -122.136400, 37.276000, -122.129500, 37.271000 )
+Evergreen                     Ave      (0, 2, -122.136700, 37.234000, -122.136100, 37.224000 )
+Scenicview                    Dr       (0, 2, -122.135400, 37.260000, -122.132500, 37.252000 )
+Marineview                    Dr       (0, 2, -122.131800, 37.241000, -122.130500, 37.239000 )
+Edgehill                      Ct       (0, 2, -122.130500, 37.239000, -122.130400, 37.232000 )
+Longview                      Dr       (0, 2, -122.127700, 37.257000, -122.127700, 37.252000 )
+Skyview                       Dr       (0, 2, -122.127600, 37.242000, -122.127400, 37.244000 )
+136th                         Ave      (0, 2, -122.139900, 37.158000, -122.137100, 37.180000 )
+137th                         Ave      (0, 2, -122.136600, 37.176000, -122.135100, 37.180000 )
+Benedict                      Dr       (0, 2, -122.132600, 37.204000, -122.132300, 37.199000 )
+142nd                         Ave      (0, 2, -122.134800, 37.142000, -122.133600, 37.153000 )
+140th                         Ave      (0, 2, -122.138600, 37.130000, -122.137400, 37.140000 )
+141st                         Ave      (0, 2, -122.136800, 37.136000, -122.135500, 37.147000 )
+145th                         Ave      (0, 2, -122.136800, 37.082000, -122.135900, 37.090000 )
+Graham                        Way      (0, 2, -122.132800, 37.128000, -122.132100, 37.134000 )
+Bancroft                      Ct       (0, 2, -122.132900, 37.109000, -122.132200, 37.116000 )
+Ardmore                       Dr       (0, 2, -122.130800, 37.211000, -122.129300, 37.212000 )
+Marineview                    Dr       (0, 2, -122.126100, 37.210000, -122.125400, 37.205000 )
+Darius                        Way      (0, 2, -122.127200, 37.180000, -122.126700, 37.164000 )
+Saturn                        Dr       (0, 2, -122.128300, 37.145000, -122.123800, 37.117000 )
+Towers                        St       (0, 2, -122.129600, 37.096000, -122.128600, 37.089000 )
+148th                         Ave      (0, 2, -122.128400, 37.119000, -122.127700, 37.122000 )
+Sidney                        Ave      (0, 2, -122.127900, 37.096000, -122.127400, 37.100000 )
+Jamison                       Way      (0, 2, -122.075555, 37.980230, -122.073719, 37.980740 )
+Starview                      Dr       (0, 2, -122.124800, 37.197000, -122.123100, 37.201000 )
+Luna                          Ave      (0, 2, -122.123800, 37.117000, -122.123000, 37.124000 )
+150th                         Ave      (0, 2, -122.124100, 37.085000, -122.123984, 37.085830 )
+Van                           Ave      (0, 2, -122.121200, 37.142000, -122.120600, 37.128000 )
+Fairmont                      Dr       (0, 2, -122.113500, 37.144000, -122.110900, 37.143000 )
+Western                       Ave      (0, 2, -122.136500, 37.049000, -122.135800, 37.044000 )
+Lillian                       Ave      (0, 2, -122.135800, 37.044000, -122.133700, 37.063000 )
+14th                          St       (0, 2, -122.131600, 37.081000, -122.128700, 37.060000 )
+Ark                           Dr       (0, 2, -122.131300, 37.029000, -122.131300, 37.036000 )
+Bradrick                      Dr       (0, 2, -122.138000, 37.962000, -122.136100, 37.963000 )
+Begonia                       Dr       (0, 2, -122.133400, 37.010000, -122.134200, 37.010000 )
+Adason                        Dr       (0, 2, -122.131500, 37.016000, -122.128800, 37.009000 )
+Vera                          Ave      (0, 2, -122.131800, 37.984000, -122.131700, 37.977000 )
+Peters                        St       (0, 2, -122.128700, 37.068000, -122.128000, 37.073000 )
+Dillo                         St       (0, 2, -122.130300, 37.024000, -122.130300, 37.016000 )
+150th                         Ave      (0, 2, -122.126600, 37.065000, -122.125800, 37.071000 )
+Ruth                          Ct       (0, 2, -122.130100, 37.004000, -122.128800, 37.002000 )
+Upton                         Ave      (0, 2, -122.129800, 37.990000, -122.129700, 37.984000 )
+Hesperian                     Blvd     (0, 2, -122.128700, 37.989000, -122.128700, 37.984000 )
+Estudillo Canal                        (0, 2, -122.128200, 37.969000, -122.128800, 37.964000 )
+Pagano                        Ct       (0, 2, -122.137100, 37.959000, -122.137900, 37.954000 )
+Firth                         Ct       (0, 2, -122.135900, 37.930000, -122.136300, 37.924000 )
+Brunetti                      Lane     (0, 2, -122.136000, 37.910000, -122.135100, 37.906000 )
+Cape Cod                      Dr       (0, 2, -122.135100, 37.928000, -122.133100, 37.928000 )
+Estudillo Canal                        (0, 3, -122.132300, 37.929000, -122.136300, 37.924000 , -122.136900, 37.924000)
+Drew                          St       (0, 2, -122.128800, 37.938000, -122.128100, 37.939000 )
+Hesperian                     Blvd     (0, 2, -122.128800, 37.922000, -122.128800, 37.913000 )
+Colby                         St       (0, 2, -122.128200, 37.959000, -122.127900, 37.959000 )
+Wagner                        St       (0, 2, -122.125400, 37.959000, -122.124500, 37.952000 )
+Paseo del Rio                          (0, 3, -122.130900, 37.842000, -122.133200, 37.836000 , -122.134000, 37.835000)
+Usher                         St       (0, 2, -122.127600, 37.885000, -122.127500, 37.865000 )
+Ronda                         St       (0, 2, -122.125000, 37.865000, -122.125100, 37.856000 )
+Via Arroyo                             (0, 2, -122.125600, 37.835000, -122.123600, 37.823000 )
+Freedom                       Ave      (0, 2, -122.121900, 37.081000, -122.121500, 37.075000 )
+14th                          St       (0, 3, -122.124100, 37.027000, -122.123500, 37.023000 , -122.122600, 37.017000)
+152nd                         Ave      (0, 2, -122.121800, 37.072000, -122.121500, 37.075000 )
+Oriole                        Ave      (0, 2, -122.120900, 37.051000, -122.120500, 37.071000 )
+Mooney                        Ave      (0, 2, -122.123400, 37.972000, -122.123400, 37.978000 )
+14th                          St       (0, 2, -122.121200, 37.007000, -122.120600, 37.002000 )
+Windsor                       Dr       (0, 2, -122.112500, 37.071000, -122.110500, 37.048000 )
+14th                          St       (0, 2, -122.117800, 37.983000, -122.116000, 37.971000 )
+Vassar                        Ave      (0, 2, -122.124500, 37.952000, -122.124600, 37.945000 )
+Elgin                         St       (0, 2, -122.122200, 37.950000, -122.121300, 37.945000 )
+Mills                         Ave      (0, 2, -122.122200, 37.917000, -122.122200, 37.908000 )
+Yale                          Ave      (0, 2, -122.120500, 37.913000, -122.120500, 37.907000 )
+Via Cordoba                            (0, 2, -122.124600, 37.853000, -122.123900, 37.853000 )
+Sp Railroad                            (0, 2, -122.121000, 37.857000, -122.118700, 37.837000 )
+Ashland                       Ave      (0, 2, -122.117800, 37.941000, -122.117800, 37.930000 )
+Ashland                       Ave      (0, 2, -122.117900, 37.914000, -122.117900, 37.913000 )
+Kent                          Ave      (0, 2, -122.113000, 37.937000, -122.113000, 37.929000 )
+Lewelling                     Blvd     (0, 2, -122.121900, 37.865000, -122.117800, 37.866000 )
+Daryl                         Ave      (0, 2, -122.115000, 37.883000, -122.114900, 37.866000 )
+Kent                          Ave      (0, 2, -122.113000, 37.891000, -122.113100, 37.887000 )
+Big Burn                      Road     (0, 2, -122.091800, 37.802000, -122.109100, 37.788000 )
+Miller                        Road     (0, 2, -122.090200, 37.645000, -122.086500, 37.545000 )
+Lake Chabot                            (0, 2, -122.075300, 37.378000, -122.076200, 37.367000 )
+Lake Chabot                   Road     (0, 2, -122.106100, 37.171000, -122.104700, 37.155000 )
+Sheffield                     Road     (0, 2, -122.103200, 37.097000, -122.102600, 37.095000 )
+Grovenor                      Dr       (0, 2, -122.089300, 37.224000, -122.088700, 37.209000 )
+Peterson                      Way      (0, 2, -122.096700, 37.162000, -122.095200, 37.159000 )
+Lake Chabot                   Road     (0, 2, -122.098800, 37.133000, -122.097700, 37.130000 )
+Carmel                        Dr       (0, 2, -122.096500, 37.135000, -122.095800, 37.133000 )
+Brookdale                     Blvd     (0, 2, -122.096500, 37.123000, -122.095800, 37.133000 )
+Lakecrest                     Ct       (0, 2, -122.094700, 37.107000, -122.093900, 37.103000 )
+Garland                       Ct       (0, 2, -122.092400, 37.192000, -122.092600, 37.177000 )
+Brookdale                     Blvd     (0, 2, -122.091500, 37.164000, -122.091200, 37.166000 )
+Vineyard                      Road     (0, 2, -122.087800, 37.200000, -122.087700, 37.191000 )
+Parker                        Road     (0, 2, -122.087600, 37.181000, -122.085900, 37.170000 )
+Huber                         Dr       (0, 3, -122.090400, 37.090000, -122.090420, 37.080400 , -122.090100, 37.074000)
+Manchester                    Road     (0, 2, -122.112500, 37.071000, -122.111600, 37.071000 )
+Carolyn                       St       (0, 2, -122.110800, 37.038000, -122.109100, 37.028000 )
+Strang                        Ave      (0, 2, -122.108700, 37.034000, -122.107600, 37.037000 )
+Maubert                       Ave      (0, 2, -122.111400, 37.009000, -122.109600, 37.995000 )
+163rd                         Ave      (0, 2, -122.110800, 37.985000, -122.109600, 37.995000 )
+164th                         Ave      (0, 2, -122.110100, 37.964000, -122.109600, 37.968000 )
+164th                         Ave      (0, 2, -122.106800, 37.993000, -122.106900, 37.998000 )
+Los Banos                     St       (0, 2, -122.106400, 37.965000, -122.105700, 37.956000 )
+Crest                         Ave      (0, 2, -122.103900, 37.067000, -122.103800, 37.066000 )
+Prosperity                    Way      (0, 2, -122.103300, 37.031000, -122.104400, 37.042000 )
+Miramonte                     Ave      (0, 2, -122.105200, 37.986000, -122.104200, 37.997000 )
+Ehle                          St       (0, 2, -122.103000, 37.973000, -122.101700, 37.959000 )
+166th                         Ave      (0, 2, -122.100800, 37.990000, -122.100600, 37.997000 )
+167th                         Ave      (0, 2, -122.101200, 37.966000, -122.100600, 37.973000 )
+Elgin                         St       (0, 2, -122.113000, 37.911000, -122.111900, 37.911000 )
+Elgin                         St       (0, 2, -122.110600, 37.911000, -122.110200, 37.911000 )
+Lewelling                     Blvd     (0, 2, -122.111800, 37.868000, -122.111200, 37.869000 )
+Ragland                       St       (0, 2, -122.109000, 37.877000, -122.108900, 37.872000 )
+170th                         Ave      (0, 2, -122.108300, 37.891000, -122.107500, 37.893000 )
+Hampton                       Road     (0, 2, -122.108600, 37.838000, -122.107300, 37.840000 )
+168th                         Ave      (0, 2, -122.104600, 37.934000, -122.104100, 37.938000 )
+170th                         Ave      (0, 2, -122.102900, 37.924000, -122.102500, 37.930000 )
+171st                         Ave      (0, 2, -122.104200, 37.908000, -122.103100, 37.916000 )
+Liberty                       St       (0, 2, -122.101900, 37.934000, -122.100900, 37.924000 )
+Los Banos                     St       (0, 2, -122.102000, 37.914000, -122.101300, 37.910000 )
+Mission                       Blvd     (0, 2, -122.103100, 37.882000, -122.102400, 37.877000 )
+Georgean                      St       (0, 2, -122.102400, 37.877000, -122.100600, 37.892000 )
+Miramar                       Ave      (0, 2, -122.100900, 37.025000, -122.099089, 37.032090 )
+Stanton                       Ave      (0, 2, -122.095300, 37.027000, -122.094400, 37.022000 )
+Kildare                       Road     (0, 2, -122.096800, 37.016000, -122.095900, 37.000000 )
+Pomar Vista                   Ave      (0, 2, -122.098900, 37.958000, -122.097300, 37.969000 )
+Ranspot                       Dr       (0, 2, -122.097200, 37.999000, -122.095900, 37.000000 )
+Stanton Hill                  Road     (0, 2, -122.093500, 37.963000, -122.092900, 37.964000 )
+Massachusetts                 St       (0, 2, -122.092500, 37.051000, -122.090900, 37.056000 )
+Barlow                        Dr       (0, 2, -122.091500, 37.030000, -122.090300, 37.032000 )
+Barlow                        Dr       (0, 3, -122.089100, 37.034000, -122.088000, 37.037000 , -122.086558, 37.039880)
+Somerset                      Ave      (0, 2, -122.093600, 37.994000, -122.092200, 37.998000 )
+Somerset                      Ave      (0, 2, -122.090700, 37.003000, -122.089900, 37.005000 )
+Somerset                      Ave      (0, 2, -122.089300, 37.006000, -122.088500, 37.008000 )
+Park                          Way      (0, 2, -122.087500, 37.970000, -122.087418, 37.968240 )
+173rd                         Ave      (0, 2, -122.098400, 37.935000, -122.097600, 37.936000 )
+Foothill                      Blvd     (0, 2, -122.098300, 37.907000, -122.098100, 37.905000 )
+Bramble                       Ct       (0, 2, -122.094400, 37.941000, -122.095100, 37.940000 )
+John                          Dr       (0, 2, -122.093900, 37.925000, -122.093400, 37.919000 )
+Webb                          Ave      (0, 2, -122.097500, 37.890000, -122.097100, 37.894000 )
+Montgomery                    Ave      (0, 2, -122.098900, 37.838000, -122.097700, 37.829000 )
+Mattox                        Road     (0, 2, -122.095800, 37.872000, -122.095700, 37.876000 )
+San Carlos                    Ave      (0, 2, -122.091200, 37.941000, -122.089900, 37.940000 )
+Stanton                       Ave      (0, 2, -122.088900, 37.939000, -122.088500, 37.928000 )
+Strobridge                    Ave      (0, 2, -122.088400, 37.911000, -122.088200, 37.908000 )
+Foothill                      Blvd     (0, 2, -122.092600, 37.862000, -122.091800, 37.857000 )
+Apple                         Ave      (0, 2, -122.090900, 37.850000, -122.090100, 37.857000 )
+Bridge                        Ct       (0, 2, -122.087900, 37.848000, -122.087400, 37.844000 )
+Mira Vista                    Dr       (0, 2, -122.082900, 37.220000, -122.082700, 37.213000 )
+School                        Way      (0, 2, -122.083800, 37.180000, -122.083300, 37.177000 )
+Seven Hills                   Road     (0, 3, -122.087600, 37.100000, -122.086344, 37.101200 , -122.085500, 37.102000)
+Almond                        Road     (0, 2, -122.081800, 37.132000, -122.083100, 37.116000 )
+Moreland                      Dr       (0, 2, -122.080200, 37.110000, -122.079300, 37.109000 )
+Sorani                        Way      (0, 2, -122.078500, 37.172000, -122.078200, 37.171000 )
+Proctor                       Road     (0, 2, -122.076100, 37.177000, -122.073739, 37.172400 )
+Plymouth                      Dr       (0, 2, -122.079800, 37.132000, -122.080200, 37.110000 )
+Milmar                        Blvd     (0, 2, -122.078500, 37.108000, -122.078300, 37.101000 )
+Lawrence                      Dr       (0, 2, -122.077900, 37.133000, -122.075600, 37.141000 )
+Joseph                        Dr       (0, 2, -122.074200, 37.120000, -122.073200, 37.109000 )
+Whispering Pine               Ct       (0, 2, -122.060000, 37.222000, -122.060800, 37.226000 )
+Redwood                       Road     (0, 2, -122.072600, 37.155000, -122.072600, 37.139000 )
+Tyler                         Lane     (0, 2, -122.067400, 37.157000, -122.066000, 37.146000 )
+Emily                         Ct       (0, 2, -122.072600, 37.121000, -122.071200, 37.124000 )
+Malabar                       Ave      (0, 2, -122.072700, 37.103000, -122.071100, 37.103000 )
+Audrey                        Dr       (0, 2, -122.069000, 37.130000, -122.068300, 37.131000 )
+Proctor                       Road     (0, 2, -122.067100, 37.192000, -122.067000, 37.200000 )
+Trenton                       Dr       (0, 2, -122.065500, 37.155000, -122.064700, 37.142000 )
+Brickell                      Way      (0, 2, -122.067000, 37.104000, -122.067000, 37.101000 )
+Sandy                         Road     (0, 2, -122.064300, 37.089000, -122.064500, 37.086000 )
+Seaview                       Ave      (0, 2, -122.062300, 37.113000, -122.059900, 37.111000 )
+Schuster                      Ave      (0, 2, -122.085400, 37.081000, -122.085400, 37.074000 )
+Parsons                       Ct       (0, 2, -122.082000, 37.056000, -122.081200, 37.053000 )
+Vaughn                        Ave      (0, 2, -122.080200, 37.053000, -122.080100, 37.026000 )
+Butterfield                   Dr       (0, 2, -122.083800, 37.002000, -122.083400, 37.987000 )
+Somerset                      Ave      (0, 2, -122.082700, 37.016000, -122.081100, 37.016000 )
+Garrison                      Ave      (0, 2, -122.078500, 37.075000, -122.078500, 37.051000 )
+San Miguel                    Ave      (0, 2, -122.079300, 37.052000, -122.079224, 37.025400 )
+Wilson                        Ave      (0, 2, -122.075100, 37.073000, -122.072700, 37.070000 )
+Santa Maria                   Ave      (0, 2, -122.077300, 37.000000, -122.077300, 37.980000 )
+Village                       Dr       (0, 2, -122.077300, 37.967000, -122.076600, 37.967000 )
+Castro Valley                 Blvd     (0, 2, -122.086000, 37.939000, -122.085300, 37.942000 )
+Tyee                          Ct       (0, 2, -122.084000, 37.918000, -122.084000, 37.913000 )
+Castro Valley                 Blvd     (0, 2, -122.081000, 37.954000, -122.080100, 37.955000 )
+San Miguel                    Ave      (0, 2, -122.080100, 37.927000, -122.080000, 37.911000 )
+Gail                          Dr       (0, 2, -122.085300, 37.858000, -122.085400, 37.853000 )
+Vivian                        St       (0, 2, -122.083100, 37.895000, -122.082000, 37.895000 )
+Chester                       St       (0, 2, -122.079100, 37.955000, -122.079000, 37.932000 )
+Dawe                          Ave      (0, 2, -122.078300, 37.927000, -122.078300, 37.912000 )
+Wilbeam                       Ave      (0, 2, -122.075900, 37.936000, -122.076000, 37.932000 )
+Orange                        Ave      (0, 2, -122.078700, 37.867000, -122.078900, 37.842000 )
+Dolores                       St       (0, 2, -122.078000, 37.842000, -122.077900, 37.833000 )
+Grove                         Way      (0, 2, -122.078000, 37.842000, -122.075900, 37.843000 )
+Jeffer                        St       (0, 2, -122.074600, 37.856000, -122.073900, 37.860000 )
+Mabel                         Ave      (0, 2, -122.077400, 37.029000, -122.072800, 37.029000 )
+Corey                         Way      (0, 2, -122.069900, 37.054000, -122.069800, 37.046000 )
+Forest                        Pl       (0, 2, -122.067200, 37.051000, -122.067300, 37.044000 )
+Stevens                       St       (0, 2, -122.071800, 37.999000, -122.070700, 37.999000 )
+Kenmore                       Ct       (0, 2, -122.071300, 37.965000, -122.070600, 37.967000 )
+Meadowlark                    Dr       (0, 2, -122.069200, 37.985000, -122.069200, 37.973000 )
+Heyer                         Ave      (0, 2, -122.067300, 37.044000, -122.065700, 37.044000 )
+Madison                       Ave      (0, 2, -122.062800, 37.078000, -122.063000, 37.071000 )
+James                         Ave      (0, 2, -122.063400, 37.055000, -122.059800, 37.052000 )
+Omega                         Ave      (0, 2, -122.067800, 37.988000, -122.066400, 37.989000 )
+Marshall                      St       (0, 2, -122.063700, 37.990000, -122.063800, 37.982000 )
+Normandy                      Ct       (0, 2, -122.062900, 37.971000, -122.063000, 37.968000 )
+Redwood                       Road     (0, 2, -122.072700, 37.955000, -122.072700, 37.948000 )
+Redwood                       Road     (0, 2, -122.072600, 37.909000, -122.072700, 37.906000 )
+Cato                          Ct       (0, 2, -122.069100, 37.944000, -122.069400, 37.938000 )
+Greenview                     Dr       (0, 3, -122.068800, 37.905000, -122.068300, 37.905000 , -122.066300, 37.907000)
+Reading                       Ave      (0, 2, -122.077900, 37.874000, -122.073500, 37.875000 )
+Lessley                       Ave      (0, 2, -122.072700, 37.866000, -122.071800, 37.867000 )
+Vergil                        St       (0, 2, -122.068500, 37.886000, -122.068100, 37.880000 )
+Linden                        St       (0, 2, -122.069200, 37.830000, -122.067800, 37.833000 )
+David                         St       (0, 2, -122.063700, 37.958000, -122.060800, 37.958000 )
+Greenview                     Dr       (0, 2, -122.065200, 37.907000, -122.064379, 37.908170 )
+Grove                         Way      (0, 4, -122.064300, 37.884000, -122.062679, 37.891620 , -122.061796, 37.895780, -122.060900, 37.900000)
+Vernetti                      Way      (0, 2, -122.064379, 37.908170, -122.063821, 37.901170 )
+Wingate                       Way      (0, 2, -122.065200, 37.838000, -122.064300, 37.846000 )
+Woodridge                     Dr       (0, 2, -122.063100, 37.836000, -122.061600, 37.820000 )
+Crow Canyon Creek                      (0, 2, -122.043000, 37.905000, -122.036800, 37.710000 )
+Cull Canyon                   Road     (0, 2, -122.053600, 37.435000, -122.049900, 37.315000 )
+Crow Canyon                   Road     (0, 2, -122.010600, 37.674000, -122.010200, 37.675000 )
+Norris Canyon                 Road     (0, 2, -122.032900, 37.332000, -122.027800, 37.371000 )
+Norris Canyon                 Road     (0, 2, -122.015000, 37.545000, -122.010300, 37.541000 )
+San Franciscan                Dr       (0, 2, -122.058900, 37.240000, -122.058200, 37.246000 )
+Charter Oaks                  Dr       (0, 2, -122.057400, 37.212000, -122.056800, 37.220000 )
+Columbia                      Dr       (0, 2, -122.057400, 37.168000, -122.056800, 37.183000 )
+Center                        St       (0, 2, -122.059900, 37.111000, -122.060000, 37.106000 )
+Crane                         Ave      (0, 2, -122.057800, 37.103000, -122.058000, 37.086000 )
+Alborg                        Ct       (0, 2, -122.049200, 37.154000, -122.048400, 37.150000 )
+Tinder                        Ct       (0, 2, -122.049600, 37.109000, -122.048800, 37.120000 )
+Cavendish                     Dr       (0, 2, -122.047700, 37.158000, -122.047500, 37.151000 )
+Greenridge                    Road     (0, 3, -122.045400, 37.151000, -122.043982, 37.158800 , -122.044100, 37.178000)
+Cotton                        Ct       (0, 2, -122.046200, 37.123000, -122.046900, 37.117000 )
+Shadow Ridge                  Dr       (0, 2, -122.041000, 37.144000, -122.041100, 37.162000 )
+Dawn View                     Ct       (0, 2, -122.041000, 37.144000, -122.040300, 37.143000 )
+Bruce                         Ct       (0, 2, -122.059500, 37.084000, -122.058800, 37.076000 )
+Center                        St       (0, 2, -122.059800, 37.052000, -122.059300, 37.046000 )
+Gliddon                       St       (0, 2, -122.057500, 37.066000, -122.058000, 37.045000 )
+Cull Canyon Reservoir                  (0, 2, -122.054600, 37.039000, -122.055300, 37.089000 )
+Edwards                       Lane     (0, 2, -122.059900, 37.016000, -122.058514, 37.015510 )
+Center                        St       (0, 2, -122.060600, 37.968000, -122.060800, 37.958000 )
+Parkview                      Road     (0, 2, -122.054800, 37.023000, -122.054800, 37.020000 )
+Crow Canyon                   Road     (0, 2, -122.055200, 37.938000, -122.054500, 37.967000 )
+Crow Canyon                   Road     (0, 2, -122.049700, 37.029000, -122.047900, 37.028000 )
+Manter                        Road     (0, 2, -122.053100, 37.984000, -122.052200, 37.980000 )
+Castro Valley                 Blvd     (0, 2, -122.060400, 37.920000, -122.058500, 37.925000 )
+San Lorenzo Creek                      (0, 2, -122.054400, 37.907000, -122.054700, 37.908000 )
+Bayview                       Ave      (0, 2, -122.058400, 37.864000, -122.058100, 37.855000 )
+Kelly                         St       (0, 2, -122.058300, 37.842000, -122.058000, 37.842000 )
+Nula                          Way      (0, 2, -122.055300, 37.865000, -122.055200, 37.855000 )
+Woodroe                       Ave      (0, 2, -122.053900, 37.888000, -122.054000, 37.891000 )
+Woodroe                       Ave      (0, 2, -122.053600, 37.855000, -122.053400, 37.847000 )
+Northview                     Dr       (0, 2, -122.050400, 37.887000, -122.051100, 37.892000 )
+Kelly                         St       (0, 2, -122.051900, 37.852000, -122.050300, 37.856000 )
+Waterford                     Pl       (0, 2, -122.047200, 37.026000, -122.047300, 37.021000 )
+Crow Canyon Creek                      (0, 2, -122.042500, 37.051000, -122.042600, 37.049000 )
+Castro Valley                 Blvd     (0, 2, -122.047800, 37.966000, -122.047000, 37.969000 )
+Cold Water                    Dr       (0, 2, -122.040300, 37.068000, -122.041000, 37.069000 )
+Fraga                         Road     (0, 2, -122.039700, 37.975000, -122.039300, 37.987000 )
+Toyon                         Pl       (0, 2, -122.046900, 37.951000, -122.046400, 37.953000 )
+Valley Brook                  Ct       (0, 2, -122.047900, 37.871000, -122.047800, 37.867000 )
+Glen Ellen                    Dr       (0, 2, -122.047900, 37.850000, -122.047500, 37.851000 )
+Sheila                        St       (0, 2, -122.045300, 37.852000, -122.045000, 37.847000 )
+Lori                          Way      (0, 2, -122.044600, 37.845000, -122.044100, 37.835000 )
+Giannini                      Ct       (0, 2, -122.025400, 37.160000, -122.025600, 37.157000 )
+Boone                         Dr       (0, 2, -122.027100, 37.151000, -122.028150, 37.141240 )
+Kit                           Lane     (0, 2, -122.023700, 37.124000, -122.023600, 37.132000 )
+Villareal                     Dr       (0, 2, -122.020400, 37.147000, -122.021700, 37.142000 )
+Palo Verde                    Road     (0, 3, -122.024200, 37.959000, -122.023000, 37.955000 , -122.022200, 37.961000)
+Eden Creek                             (0, 2, -122.021800, 37.996000, -122.022200, 37.961000 )
+Palomares                     Road     (0, 2, -122.019100, 37.873000, -122.005600, 37.744000 )
+Dougherty                     Road     (0, 2, -121.908447, 37.255200, -121.908380, 37.237900 )
+Cowing                        Road     (0, 2, -122.000200, 37.934000, -121.977200, 37.782000 )
+Donlan Canyon Creek                    (0, 2, -121.968700, 37.097000, -121.958400, 37.101000 )
+Fenwick                       Way      (0, 2, -121.947300, 37.192000, -121.947200, 37.199000 )
+Zapata                        Ct       (0, 2, -121.945600, 37.171000, -121.945650, 37.170500 )
+Alcosta                       Blvd     (0, 2, -121.938000, 37.237000, -121.939200, 37.235000 )
+Edenberry                     St       (0, 2, -121.934700, 37.230000, -121.934900, 37.235000 )
+Bloomington                   Way      (0, 2, -121.944800, 37.205000, -121.943400, 37.204000 )
+Southwick                     Ct       (0, 2, -121.944100, 37.185000, -121.943600, 37.180000 )
+Koopmann Creek                         (0, 2, -121.944200, 37.181000, -121.943465, 37.176710 )
+Via Zapata                             (0, 2, -121.943400, 37.147000, -121.944100, 37.161000 )
+Peppertree                    Ct       (0, 2, -121.941600, 37.160000, -121.941200, 37.163000 )
+Castilian                     Road     (0, 2, -121.944700, 37.135000, -121.944500, 37.140000 )
+Peppertree                    Road     (0, 2, -121.942100, 37.127000, -121.942220, 37.130200 )
+Denise                        Ct       (0, 2, -121.941800, 37.142000, -121.941400, 37.127000 )
+Silvergate                    Dr       (0, 2, -121.941000, 37.097000, -121.940200, 37.095000 )
+San Sabana                    Road     (0, 2, -121.940200, 37.066000, -121.940200, 37.095000 )
+Deervale                      Road     (0, 2, -121.937600, 37.178000, -121.937400, 37.184000 )
+Vomac                         Road     (0, 2, -121.936700, 37.166000, -121.937000, 37.173000 )
+Cavalier                      Lane     (0, 2, -121.936100, 37.175000, -121.936300, 37.189000 )
+Starward                      Dr       (0, 2, -121.936100, 37.115000, -121.936300, 37.128000 )
+Starward                      Dr       (0, 2, -121.935600, 37.103000, -121.936000, 37.109000 )
+Sunwood                       Dr       (0, 2, -121.933200, 37.113000, -121.933500, 37.132000 )
+Betlen                        Dr       (0, 2, -121.950700, 37.018000, -121.950121, 37.016780 )
+Ladera                        Ct       (0, 2, -121.944400, 37.068000, -121.942900, 37.070000 )
+Amarillo                      Ct       (0, 2, -121.943900, 37.044000, -121.943200, 37.046000 )
+Plata                         Way      (0, 2, -121.940200, 37.066000, -121.939400, 37.069000 )
+Betlen                        Dr       (0, 2, -121.940700, 37.026000, -121.939700, 37.029000 )
+Circle                        Way      (0, 2, -121.941800, 37.013000, -121.942200, 37.020000 )
+Dublin Creek                           (0, 2, -121.942200, 37.974000, -121.955000, 37.984000 )
+Regional                      St       (0, 2, -121.932800, 37.029000, -121.934700, 37.072000 )
+Foothill                      Road     (0, 2, -121.932000, 37.930000, -121.933500, 37.958000 )
+Bandon                        Dr       (0, 2, -121.931100, 37.234000, -121.931000, 37.237000 )
+Shamrock                      Pl       (0, 2, -121.929000, 37.240000, -121.929500, 37.247000 )
+Mulberry                      Pl       (0, 2, -121.923800, 37.249000, -121.924900, 37.261000 )
+Davona                        Dr       (0, 2, -121.926100, 37.222000, -121.927800, 37.218000 )
+Mulberry                      Pl       (0, 2, -121.922100, 37.234000, -121.922500, 37.232000 )
+Brighton                      Dr       (0, 2, -121.931000, 37.198000, -121.931200, 37.197000 )
+Tamarack                      Dr       (0, 2, -121.930400, 37.160000, -121.931300, 37.163000 )
+Brighton                      Dr       (0, 3, -121.926300, 37.188000, -121.927700, 37.189000 , -121.928500, 37.190000)
+Tamarack                      Dr       (0, 2, -121.925500, 37.155000, -121.926600, 37.156000 )
+Donohue                       Dr       (0, 2, -121.932000, 37.091000, -121.932700, 37.106000 )
+Bedford                       Way      (0, 2, -121.928000, 37.149000, -121.928800, 37.150000 )
+Canterbury                    Lane     (0, 2, -121.927700, 37.141000, -121.927600, 37.149000 )
+Village                       Pkwy     (0, 2, -121.925600, 37.098000, -121.926700, 37.113000 )
+Frederiksen                   Lane     (0, 2, -121.925100, 37.162000, -121.925600, 37.162000 )
+Burnham                       Way      (0, 2, -121.924200, 37.176000, -121.924300, 37.183000 )
+Langmuir                      Lane     (0, 2, -121.919900, 37.190000, -121.921500, 37.197000 )
+Tamarack                      Dr       (0, 2, -121.920700, 37.159000, -121.922000, 37.155000 )
+Emerald                       Ave      (0, 2, -121.924700, 37.130000, -121.925000, 37.136000 )
+Portage                       Road     (0, 2, -121.924100, 37.092000, -121.925700, 37.109000 )
+Elba                          Way      (0, 2, -121.922300, 37.143000, -121.923000, 37.139000 )
+York                          Dr       (0, 2, -121.921200, 37.098000, -121.922700, 37.104000 )
+Newcastle                     Lane     (0, 2, -121.918000, 37.167000, -121.919100, 37.182000 )
+Prince                        Dr       (0, 2, -121.916400, 37.155000, -121.918500, 37.145000 )
+Amador Valley                 Blvd     (0, 2, -121.919800, 37.146000, -121.921100, 37.138000 )
+King                          Way      (0, 2, -121.917600, 37.133000, -121.918500, 37.130000 )
+Tory                          Way      (0, 2, -121.916200, 37.139000, -121.917000, 37.132000 )
+Hickory                       Lane     (0, 2, -121.916300, 37.102000, -121.916040, 37.107200 )
+8th                           St       (0, 2, -121.908300, 37.161000, -121.908100, 37.160000 )
+5th                           St       (0, 2, -121.908200, 37.114000, -121.904500, 37.113000 )
+Stoneridge Mall               Road     (0, 2, -121.928700, 37.963000, -121.928300, 37.941000 )
+Pike                          Ct       (0, 2, -121.921900, 37.080000, -121.922400, 37.079000 )
+Stoneridge Mall               Road     (0, 2, -121.927400, 37.926000, -121.925000, 37.925000 )
+Pleasant Hill                 Road     (0, 2, -121.926400, 37.854000, -121.926800, 37.860000 )
+Rosedale                      Ct       (0, 2, -121.923200, 37.900000, -121.924000, 37.897000 )
+Brookside                     Ct       (0, 2, -121.921800, 37.902000, -121.921300, 37.908000 )
+Springdale                    Ave      (0, 2, -121.922900, 37.884000, -121.923300, 37.888000 )
+Kentwood                      Way      (0, 2, -121.923500, 37.841000, -121.926000, 37.838000 )
+Riverdale                     Ct       (0, 2, -121.919800, 37.891000, -121.920100, 37.893000 )
+Springdale                    Ave      (0, 2, -121.919600, 37.845000, -121.920000, 37.854000 )
+Cedar                         Lane     (0, 4, -121.917300, 37.080000, -121.918300, 37.083000 , -121.919600, 37.089000, -121.920000, 37.098000)
+Johnson Industrial            Dr       (0, 2, -121.909600, 37.014000, -121.917200, 37.016000 )
+Dublin                        Blvd     (0, 2, -121.909600, 37.047000, -121.911500, 37.034000 )
+Johnson                       Dr       (0, 2, -121.914500, 37.901000, -121.915000, 37.877000 )
+Stonedale                     Dr       (0, 2, -121.917100, 37.877000, -121.917300, 37.882000 )
+Hillview                      Ct       (0, 2, -121.917800, 37.841000, -121.919100, 37.838000 )
+Stoneridge                    Dr       (0, 2, -121.908200, 37.905000, -121.908900, 37.904000 )
+Payne                         Ct       (0, 2, -121.913300, 37.841000, -121.913900, 37.840000 )
+Herrin                        Way      (0, 2, -121.909800, 37.888000, -121.910000, 37.893000 )
+Alvord                        Way      (0, 2, -121.908500, 37.891000, -121.909300, 37.889000 )
+Denker                        Dr       (0, 3, -121.908600, 37.861000, -121.908200, 37.863000 , -121.907900, 37.864000)
+12th                          St       (0, 3, -121.902600, 37.204000, -121.900288, 37.197710 , -121.896959, 37.199730)
+12th                          St       (0, 2, -121.896000, 37.194000, -121.893200, 37.194000 )
+Arnold                        Road     (0, 2, -121.892300, 37.113000, -121.892400, 37.111000 )
+Sp Railroad                            (0, 2, -121.897700, 37.022000, -121.902200, 37.054000 )
+Chabot Canal                           (0, 2, -121.903600, 37.013000, -121.904400, 37.017000 )
+Chabot Canal                           (0, 2, -121.904400, 37.017000, -121.903700, 37.020000 )
+Hopyard                       Road     (0, 2, -121.902600, 37.975000, -121.903300, 37.985000 )
+Gibraltar                     Dr       (0, 2, -121.902500, 37.960000, -121.900800, 37.960000 )
+Franklin                      Dr       (0, 2, -121.904900, 37.954000, -121.908400, 37.934000 )
+Addison                       Way      (0, 2, -121.904400, 37.895000, -121.904400, 37.899000 )
+Benner                        Ct       (0, 2, -121.906300, 37.891000, -121.907600, 37.888000 )
+Addison                       Way      (0, 2, -121.904400, 37.881000, -121.904400, 37.889000 )
+Dorman                        Road     (0, 2, -121.906100, 37.834000, -121.906300, 37.841000 )
+Coronado                      Lane     (0, 2, -121.902600, 37.843000, -121.902800, 37.843000 )
+Arnold                        Road     (0, 2, -121.892400, 37.060000, -121.892400, 37.062000 )
+Stoneridge                    Dr       (0, 2, -121.894000, 37.919000, -121.890200, 37.925000 )
+Tassajara Creek                        (0, 2, -121.886200, 37.901000, -121.884700, 37.924000 )
+Sutter Gate                   Ave      (0, 2, -121.883800, 37.866000, -121.884500, 37.864000 )
+Funston Gate                  Ct       (0, 2, -121.882400, 37.847000, -121.883000, 37.842000 )
+Tassajara                     Road     (0, 2, -121.870900, 37.099000, -121.871300, 37.048000 )
+Fairlands                     Dr       (0, 2, -121.871000, 37.976000, -121.871900, 37.961000 )
+Saginaw                       Ct       (0, 2, -121.880300, 37.898000, -121.880600, 37.901000 )
+Crow                          Ct       (0, 2, -121.879700, 37.911000, -121.880100, 37.910000 )
+Gresham                       Ct       (0, 2, -121.875200, 37.943000, -121.875400, 37.946000 )
+Navajo                        Ct       (0, 2, -121.877900, 37.901000, -121.878300, 37.900000 )
+Hartley Gate                  Ct       (0, 2, -121.880300, 37.863000, -121.881200, 37.871000 )
+Ross Gate                     Way      (0, 2, -121.879400, 37.845000, -121.881800, 37.836000 )
+Tanager                       Dr       (0, 2, -121.877500, 37.831000, -121.877800, 37.838000 )
+Fairlands                     Dr       (0, 2, -121.873100, 37.956000, -121.873800, 37.952000 )
+Beecham                       Ct       (0, 2, -121.869300, 37.959000, -121.870400, 37.959000 )
+Suffolk                       Way      (0, 2, -121.869900, 37.932000, -121.873200, 37.920000 )
+Krause                        St       (0, 2, -121.873100, 37.863000, -121.871900, 37.865000 )
+Alexander                     Ct       (0, 2, -121.870800, 37.845000, -121.870600, 37.841000 )
+Lansdown                      Ct       (0, 2, -121.865900, 37.949000, -121.866100, 37.960000 )
+Gulfstream                    St       (0, 2, -121.864500, 37.976000, -121.864500, 37.993000 )
+Pimlico                       Dr       (0, 2, -121.861600, 37.998000, -121.861800, 37.008000 )
+Ballantyne                    Dr       (0, 2, -121.861100, 37.986000, -121.860500, 37.985000 )
+Rockingham                    Dr       (0, 2, -121.868100, 37.948000, -121.868900, 37.944000 )
+Las Positas                   Blvd     (0, 2, -121.864200, 37.957000, -121.864500, 37.955000 )
+Martin                        Ave      (0, 3, -121.861800, 37.818000, -121.861800, 37.826960 , -121.861800, 37.848000)
+Arroyo Las Positas                     (0, 2, -121.847300, 37.965000, -121.831200, 37.992000 )
+Clubhouse                     Dr       (0, 2, -121.817900, 37.971000, -121.818100, 37.972000 )
+Lindbergh                     Ave      (0, 3, -121.815100, 37.972000, -121.811800, 37.971000 , -121.809800, 37.973000)
+Lindbergh                     Ave      (0, 2, -121.808900, 37.973000, -121.807200, 37.973000 )
+Kitty Hawk                    Road     (0, 2, -121.804800, 37.797000, -121.804900, 37.867000 )
+Livermore                     Ave      (0, 2, -121.768700, 37.448000, -121.769000, 37.375000 )
+Hartman                       Road     (0, 2, -121.787600, 37.217000, -121.795300, 37.211000 )
+Arroyo Las Positas                     (0, 2, -121.797300, 37.997000, -121.795700, 37.005000 )
+Yosemite                      Pl       (0, 2, -121.801900, 37.853000, -121.802400, 37.855000 )
+Everglades                    Lane     (0, 2, -121.800500, 37.870000, -121.800259, 37.874690 )
+Las Positas                   Blvd     (0, 2, -121.798800, 37.889000, -121.798400, 37.889000 )
+Gull                          Way      (0, 2, -121.800400, 37.846000, -121.799600, 37.845000 )
+Swan                          Dr       (0, 2, -121.799600, 37.845000, -121.799100, 37.834000 )
+Arlington                     Road     (0, 2, -121.795700, 37.898000, -121.795600, 37.906000 )
+Hanover                       St       (0, 2, -121.791100, 37.942000, -121.792300, 37.950000 )
+Hanover                       St       (0, 2, -121.793900, 37.918000, -121.792800, 37.918000 )
+Fontonett                     Ave      (0, 2, -121.795158, 37.619000, -121.794700, 37.619000 )
+Cedar                         Dr       (0, 3, -121.796400, 37.859000, -121.794100, 37.858000 , -121.793100, 37.858000)
+Montecito                     Cir      (0, 2, -121.789000, 37.970000, -121.789270, 37.970900 )
+Montecito                     Cir      (0, 3, -121.789000, 37.970000, -121.788300, 37.967000 , -121.786700, 37.962000)
+Via Montalvo                           (0, 2, -121.786700, 37.962000, -121.786100, 37.967000 )
+Arroyo Las Positas                     (0, 2, -121.783600, 37.997000, -121.783492, 37.996050 )
+Covington                     Way      (0, 2, -121.793500, 37.936000, -121.791100, 37.942000 )
+Dover                         Way      (0, 2, -121.792900, 37.906000, -121.791700, 37.906000 )
+Cortland                      Way      (0, 2, -121.789200, 37.934000, -121.788500, 37.939000 )
+Murrieta                      Blvd     (0, 2, -121.786500, 37.934000, -121.785800, 37.936000 )
+Iroquois                      Ave      (0, 2, -121.787600, 37.891000, -121.787600, 37.899000 )
+Pelican                       Ct       (0, 2, -121.790700, 37.839000, -121.790200, 37.832000 )
+Partridge Com                          (0, 2, -121.787600, 37.877000, -121.787600, 37.882000 )
+Pine                          St       (0, 2, -121.786900, 37.882000, -121.786400, 37.883000 )
+Oriole                        Ave      (0, 2, -121.787900, 37.827000, -121.787900, 37.851000 )
+Marylin                       Ave      (0, 2, -121.785700, 37.838000, -121.783300, 37.837000 )
+Murrieta                      Blvd     (0, 2, -121.784700, 37.940000, -121.784200, 37.942000 )
+Butte                         Ct       (0, 2, -121.783000, 37.938000, -121.783000, 37.934000 )
+Portola                       Ave      (0, 3, -121.782200, 37.948000, -121.780600, 37.940000 , -121.779400, 37.935000)
+Juniper                       St       (0, 2, -121.782300, 37.897000, -121.781500, 37.900000 )
+Algonquin                     Ave      (0, 2, -121.785100, 37.888000, -121.785200, 37.891000 )
+Rincon                        Ave      (0, 2, -121.782400, 37.828000, -121.782400, 37.837000 )
+Elm                           St       (0, 2, -121.781500, 37.865000, -121.780700, 37.865000 )
+Adelle                        St       (0, 2, -121.779300, 37.841000, -121.779700, 37.849000 )
+Pine                          St       (0, 2, -121.778400, 37.894000, -121.777600, 37.897000 )
+Pine                          St       (0, 2, -121.775800, 37.902000, -121.774600, 37.906000 )
+Locust                        St       (0, 2, -121.781500, 37.876000, -121.779100, 37.881000 )
+Linden                        St       (0, 3, -121.778200, 37.861000, -121.777000, 37.865000 , -121.775700, 37.868000)
+Holladay                      Ct       (0, 2, -121.777300, 37.842000, -121.778000, 37.841000 )
+Walnut                        St       (0, 2, -121.774000, 37.863000, -121.772800, 37.866000 )
+M                             St       (0, 2, -121.773100, 37.842000, -121.773600, 37.853000 )
+Cromwell                      Way      (0, 2, -121.772300, 37.932000, -121.771300, 37.933000 )
+Lambeth                       Road     (0, 2, -121.768600, 37.942000, -121.768400, 37.947000 )
+Enos                          Way      (0, 2, -121.767700, 37.896000, -121.767300, 37.910000 )
+Linden                        St       (0, 2, -121.773300, 37.876000, -121.772000, 37.879000 )
+Livermore                     Ave      (0, 2, -121.769900, 37.863000, -121.770300, 37.874000 )
+Park                          St       (0, 2, -121.771100, 37.860000, -121.769900, 37.863000 )
+Walnut                        St       (0, 2, -121.769000, 37.877000, -121.768300, 37.880000 )
+I                             St       (0, 2, -121.767500, 37.848000, -121.768200, 37.857000 )
+Southern Pacific Railroad              (0, 2, -121.767400, 37.843000, -121.768600, 37.840000 )
+Las Positas                   Road     (0, 2, -121.772600, 37.976000, -121.768410, 37.984260 )
+Briarwood                     Dr       (0, 2, -121.766300, 37.915000, -121.765200, 37.916000 )
+Waverly                       Way      (0, 2, -121.763400, 37.940000, -121.762632, 37.940550 )
+Lee                           Ave      (0, 2, -121.761400, 37.878000, -121.761500, 37.898000 )
+Railroad                      Ave      (0, 2, -121.766100, 37.841000, -121.765400, 37.842000 )
+Wp Railroad                            (0, 3, -121.767500, 37.848000, -121.765632, 37.853750 , -121.763600, 37.860000)
+First                         St       (0, 2, -121.763600, 37.843000, -121.764400, 37.840000 )
+Wood                          St       (0, 2, -121.762000, 37.839000, -121.761100, 37.834000 )
+Michell                       Ct       (0, 2, -121.758800, 37.897000, -121.758300, 37.893000 )
+First                         St       (0, 2, -121.757200, 37.875000, -121.757600, 37.863000 )
+Rose                          St       (0, 2, -121.757000, 37.840000, -121.757000, 37.831000 )
+Vista                         Ct       (0, 2, -121.755000, 37.840000, -121.755100, 37.838000 )
+Altamont Creek                         (0, 2, -121.750900, 37.149000, -121.747400, 37.154000 )
+Santa Clara                   Way      (0, 3, -121.753900, 37.854000, -121.750800, 37.856000 , -121.750000, 37.856000)
+Santa Teresa                           (0, 3, -122.157600, 37.826000, -122.156500, 37.812000 , -122.155300, 37.797000)
+Lewelling                     Blvd     (0, 2, -122.155500, 37.793000, -122.157200, 37.787000 )
+Laverne                       Dr       (0, 2, -122.153300, 37.821000, -122.153200, 37.814000 )
+Randy                         St       (0, 2, -122.151700, 37.809000, -122.152300, 37.807000 )
+Quebec                        Ave      (0, 2, -122.152800, 37.786000, -122.153500, 37.785000 )
+San Lorenzo Creek                      (0, 2, -122.153900, 37.747000, -122.161600, 37.703000 )
+Grant                         Ave      (0, 2, -122.152300, 37.717000, -122.153500, 37.712000 )
+Grant                         Ave      (0, 2, -122.154500, 37.703000, -122.156700, 37.683000 )
+Marne                         St       (0, 2, -122.148000, 37.811000, -122.147600, 37.805000 )
+Elko                          Ct       (0, 2, -122.149900, 37.778000, -122.149600, 37.773000 )
+Argonne                       St       (0, 3, -122.146000, 37.806000, -122.145500, 37.801000 , -122.145100, 37.796000)
+Via Barrett                            (0, 2, -122.149500, 37.736000, -122.150300, 37.733000 )
+Grant                         Ave      (0, 2, -122.149100, 37.732000, -122.151200, 37.722000 )
+Via Harriet                            (0, 3, -122.147400, 37.709000, -122.147300, 37.691000 , -122.145600, 37.674000)
+Via Nueva                              (0, 2, -122.146100, 37.743000, -122.145600, 37.738000 )
+Via Lacqua                             (0, 2, -122.146300, 37.734000, -122.147100, 37.730000 )
+Kramer                        St       (0, 2, -122.143000, 37.819000, -122.143500, 37.812000 )
+Via Hermana                            (0, 2, -122.143300, 37.791000, -122.144200, 37.786000 )
+Via Enrico                             (0, 2, -122.137900, 37.820000, -122.138900, 37.818000 )
+Corte Yolanda                          (0, 2, -122.142600, 37.753000, -122.142300, 37.749000 )
+Via Escondido                          (0, 2, -122.140900, 37.747000, -122.141278, 37.745210 )
+Via Redondo                            (0, 2, -122.141000, 37.700000, -122.142300, 37.707000 )
+Via Amigos                             (0, 2, -122.140600, 37.731000, -122.142400, 37.722000 )
+Via Lucas                              (0, 2, -122.138100, 37.710000, -122.139600, 37.710000 )
+Via Natal                              (0, 2, -122.144900, 37.677000, -122.145600, 37.674000 )
+Via Natal                              (0, 2, -122.143400, 37.684000, -122.144100, 37.680000 )
+Via Carmen                             (0, 2, -122.140100, 37.674000, -122.139700, 37.650000 )
+Via Frances                            (0, 2, -122.138700, 37.680000, -122.138600, 37.666000 )
+Via Annette                            (0, 2, -122.138900, 37.650000, -122.138800, 37.631000 )
+F Bay                                  (0, 2, -122.152000, 37.358000, -122.150473, 37.249980 )
+Via del Prado                          (0, 2, -122.135000, 37.797000, -122.133900, 37.780000 )
+Via Pinale                             (0, 2, -122.133300, 37.793000, -122.131500, 37.773000 )
+Via Vista                              (0, 2, -122.136400, 37.745000, -122.139100, 37.735000 )
+Channel                       St       (0, 2, -122.137200, 37.710000, -122.136900, 37.706000 )
+Via Chiquita                           (0, 2, -122.133700, 37.731000, -122.133300, 37.724000 )
+Via Chiquita                           (0, 2, -122.132200, 37.708000, -122.132100, 37.702000 )
+Paseo Largavista                       (0, 2, -122.128700, 37.822000, -122.128100, 37.811000 )
+Via Paro                               (0, 2, -122.129000, 37.780000, -122.127600, 37.757000 )
+Hesperian                     Blvd     (0, 2, -122.125700, 37.792000, -122.125100, 37.781000 )
+Via Perdido                            (0, 2, -122.129500, 37.741000, -122.128100, 37.727000 )
+Via Alamitos                           (0, 2, -122.130200, 37.704000, -122.130300, 37.697000 )
+Via Manzanas                           (0, 2, -122.124800, 37.761000, -122.126500, 37.753000 )
+Hacienda                      Ave      (0, 2, -122.125000, 37.729000, -122.125900, 37.719000 )
+Via la Jolla                           (0, 2, -122.134600, 37.699000, -122.134500, 37.691000 )
+Via Buena Vista                        (0, 2, -122.136600, 37.652000, -122.137400, 37.651000 )
+Via el Cerrito                         (0, 2, -122.133700, 37.691000, -122.133600, 37.676000 )
+Via Chiquita                           (0, 2, -122.132100, 37.693000, -122.131900, 37.681000 )
+Via Tovita                             (0, 2, -122.133600, 37.640000, -122.135600, 37.638000 )
+Sp Railroad                            (0, 2, -122.137000, 37.576000, -122.132700, 37.530000 )
+Via Alamitos                           (0, 2, -122.130200, 37.675000, -122.130000, 37.668000 )
+Via Esperanza                          (0, 2, -122.127300, 37.676000, -122.130200, 37.675000 )
+Sunol                         Road     (0, 2, -122.125400, 37.671000, -122.125400, 37.666000 )
+Via Rodriguez                          (0, 2, -122.123400, 37.809000, -122.123300, 37.806000 )
+Paseo Grande                           (0, 2, -122.123100, 37.812000, -122.123400, 37.809000 )
+Via Primero                            (0, 2, -122.122300, 37.774000, -122.121100, 37.755000 )
+Paseo Grande                           (0, 2, -122.119700, 37.830000, -122.120400, 37.826000 )
+Via Segundo                            (0, 2, -122.120700, 37.778000, -122.118900, 37.765000 )
+Hacienda                      Ave      (0, 2, -122.122500, 37.742000, -122.123500, 37.738000 )
+Via Arriba                             (0, 2, -122.122000, 37.712000, -122.121600, 37.702000 )
+Hacienda                      Ave      (0, 2, -122.119200, 37.754000, -122.121400, 37.746000 )
+Bockman                       Road     (0, 2, -122.120600, 37.713000, -122.122000, 37.712000 )
+Via Matero                             (0, 2, -122.116000, 37.806000, -122.117500, 37.797000 )
+Via Verde                              (0, 2, -122.116500, 37.788000, -122.117500, 37.782000 )
+Meekland                      Ave      (0, 2, -122.113000, 37.812000, -122.112800, 37.809000 )
+Corte Eulalia                          (0, 2, -122.114200, 37.780000, -122.115400, 37.776000 )
+Ricardo                       Ave      (0, 2, -122.117600, 37.761000, -122.114800, 37.745000 )
+Shirley                       Ave      (0, 2, -122.115700, 37.717000, -122.117000, 37.712000 )
+Solano                        Ave      (0, 2, -122.113100, 37.735000, -122.116100, 37.724000 )
+Firestone                     Ct       (0, 2, -122.124600, 37.671000, -122.124600, 37.666000 )
+Clubhouse                     Dr       (0, 2, -122.122700, 37.671000, -122.123400, 37.670000 )
+Clubhouse                     Dr       (0, 2, -122.121000, 37.670000, -122.121500, 37.671000 )
+A                             St       (0, 2, -122.117200, 37.659000, -122.119506, 37.656610 )
+Skywest                       Dr       (0, 2, -122.116100, 37.620000, -122.112300, 37.586000 )
+Hesperian                     Blvd     (0, 2, -122.113200, 37.600000, -122.112300, 37.586000 )
+Lincoln                       Ave      (0, 2, -122.132100, 37.499000, -122.134900, 37.499000 )
+Bernhardt                     St       (0, 2, -122.132600, 37.399000, -122.132200, 37.449000 )
+American                      Ave      (0, 2, -122.127100, 37.478000, -122.128100, 37.489000 )
+Cabot                         Blvd     (0, 2, -122.133400, 37.412000, -122.132600, 37.399000 )
+Depot                         Road     (0, 2, -122.130200, 37.380000, -122.132300, 37.379000 )
+Winton                        Ave      (0, 2, -122.123100, 37.533000, -122.123100, 37.530000 )
+National                      Ave      (0, 2, -122.119200, 37.500000, -122.128100, 37.489000 )
+Dunn                          Road     (0, 2, -122.119500, 37.452000, -122.121100, 37.451000 )
+Winton                        Ave      (0, 2, -122.115100, 37.533000, -122.116500, 37.532000 )
+Eden                          Ave      (0, 2, -122.114300, 37.505000, -122.114200, 37.491000 )
+Mohr                          Dr       (0, 2, -122.113200, 37.466000, -122.113000, 37.460000 )
+Clawiter                      Road     (0, 2, -122.118700, 37.442000, -122.118800, 37.435000 )
+Diablo                        Ave      (0, 2, -122.118600, 37.358000, -122.123600, 37.358000 )
+Clawiter                      Road     (0, 2, -122.118600, 37.321000, -122.118600, 37.308000 )
+Laguna                        Dr       (0, 2, -122.112800, 37.418000, -122.113200, 37.418000 )
+Monte Vista                   Dr       (0, 2, -122.112700, 37.400000, -122.112600, 37.388000 )
+Point Eden                    Way      (0, 2, -122.120800, 37.255000, -122.126200, 37.256000 )
+Breakwater                    Ave      (0, 2, -122.119600, 37.294000, -122.120300, 37.282000 )
+Eden Landing                  Road     (0, 2, -122.120400, 37.268000, -122.120400, 37.267000 )
+Eden Landing                  Road     (0, 2, -122.121300, 37.226000, -122.121300, 37.223000 )
+Sp Railroad                            (0, 2, -122.112900, 37.315000, -122.112500, 37.311000 )
+Alden                         Road     (0, 3, -122.111600, 37.817000, -122.110686, 37.819890 , -122.109700, 37.823000)
+Hathaway                      Ave      (0, 2, -122.110900, 37.742000, -122.110500, 37.739000 )
+Lucot                         St       (0, 2, -122.109100, 37.746000, -122.109600, 37.743000 )
+Blossom                       Way      (0, 3, -122.109600, 37.758000, -122.108700, 37.764000 , -122.105700, 37.774000)
+Flint                         Ct       (0, 2, -122.107400, 37.711000, -122.108500, 37.704000 )
+Western                       Blvd     (0, 2, -122.104300, 37.819000, -122.102300, 37.805000 )
+June                          Ct       (0, 2, -122.104100, 37.760000, -122.104500, 37.769000 )
+Medford                       Ave      (0, 3, -122.101700, 37.828000, -122.101500, 37.829000 , -122.100200, 37.832000)
+Western                       Blvd     (0, 2, -122.100400, 37.792000, -122.098300, 37.778000 )
+Grove                         Way      (0, 2, -122.104100, 37.760000, -122.102947, 37.763180 )
+Willow                        Ave      (0, 2, -122.101200, 37.748000, -122.100200, 37.754000 )
+Victory                       Dr       (0, 2, -122.109100, 37.635000, -122.109281, 37.642600 )
+A                             St       (0, 3, -122.107000, 37.664000, -122.107101, 37.664250 , -122.107400, 37.665000)
+Sueirro                       St       (0, 2, -122.111300, 37.628000, -122.112100, 37.627000 )
+Marin                         Ave      (0, 2, -122.110100, 37.608000, -122.110700, 37.607000 )
+Teakwood                      St       (0, 2, -122.110900, 37.580000, -122.110400, 37.574000 )
+Garden                        Ave      (0, 2, -122.107700, 37.626000, -122.107500, 37.610000 )
+Leonardo                      Way      (0, 2, -122.107300, 37.577000, -122.108000, 37.575000 )
+Happyland                     Ave      (0, 2, -122.104400, 37.666000, -122.104441, 37.661520 )
+Santa Clara                   St       (0, 3, -122.101200, 37.667000, -122.101200, 37.663890 , -122.101200, 37.657180)
+Amador                        St       (0, 2, -122.099900, 37.664000, -122.099000, 37.655000 )
+Marin                         Ave      (0, 2, -122.104400, 37.614000, -122.105500, 37.613000 )
+Longwood                      Ct       (0, 2, -122.103600, 37.606000, -122.103800, 37.603000 )
+Bluefield                     Lane     (0, 2, -122.102400, 37.584000, -122.103300, 37.561000 )
+Santa Clara                   St       (0, 2, -122.100900, 37.618000, -122.100600, 37.613000 )
+Montgomery                    Ave      (0, 2, -122.097100, 37.824000, -122.095500, 37.811000 )
+Bay Area Rapid Transit                 (0, 2, -122.098100, 37.779000, -122.096300, 37.767000 )
+Mission                       Blvd     (0, 2, -122.095500, 37.824000, -122.094700, 37.817000 )
+Mission                       Blvd     (0, 3, -122.092800, 37.802000, -122.091900, 37.796000 , -122.091600, 37.793000)
+Poplar                        Ave      (0, 2, -122.101800, 37.704000, -122.098000, 37.721000 )
+Sunset                        Blvd     (0, 2, -122.094400, 37.750000, -122.094100, 37.751000 )
+Cotter                        Way      (0, 2, -122.090400, 37.818000, -122.088200, 37.829000 )
+Foothill                      Blvd     (0, 2, -122.088200, 37.829000, -122.086900, 37.822000 )
+Sunset                        Blvd     (0, 2, -122.089900, 37.779000, -122.088800, 37.788000 )
+Sunset                        Blvd     (0, 2, -122.093200, 37.755000, -122.092100, 37.761000 )
+Flagg                         St       (0, 2, -122.092100, 37.710000, -122.091400, 37.700000 )
+Montgomery                    St       (0, 2, -122.088200, 37.738000, -122.087500, 37.727000 )
+A                             St       (0, 2, -122.089000, 37.710000, -122.088600, 37.711000 )
+A                             St       (0, 2, -122.098500, 37.671000, -122.098100, 37.674000 )
+A                             St       (0, 2, -122.099100, 37.668000, -122.098800, 37.669000 )
+Amador                        St       (0, 2, -122.098100, 37.647000, -122.096600, 37.635000 )
+B                             St       (0, 2, -122.095500, 37.673000, -122.094400, 37.677000 )
+Redbud                        Lane     (0, 2, -122.096900, 37.627000, -122.097800, 37.627000 )
+Amador                        St       (0, 2, -122.096300, 37.614000, -122.096200, 37.609000 )
+Ocie                          Way      (0, 2, -122.096600, 37.605000, -122.097000, 37.603000 )
+Myrtle                        St       (0, 2, -122.092400, 37.685000, -122.091900, 37.676000 )
+C                             St       (0, 2, -122.090600, 37.681000, -122.089600, 37.684000 )
+Meek                          Ave      (0, 2, -122.091900, 37.641000, -122.089700, 37.642000 )
+Arnold                        Ct       (0, 2, -122.088700, 37.669000, -122.089400, 37.666000 )
+Sp Railroad                            (0, 3, -122.091400, 37.601000, -122.087000, 37.560000 , -122.086408, 37.555100)
+Hesperian                     Blvd     (0, 2, -122.110200, 37.551000, -122.109100, 37.534000 )
+Stonewall                     Ave      (0, 2, -122.107600, 37.568000, -122.106700, 37.554000 )
+Hesperian                     Blvd     (0, 2, -122.107900, 37.513000, -122.107600, 37.507000 )
+Denton                        Ave      (0, 2, -122.111800, 37.467000, -122.112277, 37.466660 )
+West                          St       (0, 2, -122.106600, 37.480000, -122.108083, 37.473750 )
+Sangamore                     St       (0, 2, -122.106900, 37.471000, -122.107600, 37.468000 )
+La Playa                      Dr       (0, 2, -122.103900, 37.545000, -122.101000, 37.493000 )
+Calaroga                      Ave      (0, 2, -122.101000, 37.493000, -122.100600, 37.487000 )
+Citron                        Way      (0, 2, -122.100800, 37.461000, -122.101700, 37.460000 )
+Occidental                    Road     (0, 2, -122.110200, 37.403000, -122.110496, 37.402770 )
+Gettysburg                    Ave      (0, 2, -122.108900, 37.366000, -122.108900, 37.357000 )
+Industrial                    Blvd     (0, 2, -122.109100, 37.328000, -122.108500, 37.326000 )
+Newport                       St       (0, 2, -122.105900, 37.328000, -122.105400, 37.315000 )
+Seaver                        St       (0, 2, -122.101600, 37.427000, -122.101600, 37.419000 )
+Adrian                        Ave      (0, 2, -122.101900, 37.389000, -122.101900, 37.369000 )
+Trafalgar                     Ave      (0, 2, -122.103600, 37.369000, -122.104300, 37.375000 )
+Cryer                         St       (0, 2, -122.102400, 37.357000, -122.103500, 37.351000 )
+Bahama                        Ave      (0, 2, -122.103900, 37.335000, -122.103100, 37.321000 )
+Tallahassee                   St       (0, 2, -122.098900, 37.352000, -122.101200, 37.345000 )
+Sleepy Hollow                 Ave      (0, 2, -122.100300, 37.335000, -122.100800, 37.332000 )
+Beechmont                     Lane     (0, 2, -122.097100, 37.558000, -122.098400, 37.555000 )
+Willimet                      Way      (0, 2, -122.096400, 37.517000, -122.094900, 37.493000 )
+Broadmore                     Ave      (0, 2, -122.095000, 37.522000, -122.093600, 37.497000 )
+Magnolia                      St       (0, 2, -122.097100, 37.500000, -122.096200, 37.484000 )
+Kay                           Ave      (0, 2, -122.097000, 37.461000, -122.096900, 37.457000 )
+Banbury                       St       (0, 2, -122.094300, 37.495000, -122.094900, 37.493000 )
+Elmhurst                      St       (0, 2, -122.091600, 37.568000, -122.091800, 37.564000 )
+Santa Clara                   St       (0, 2, -122.092300, 37.510000, -122.091900, 37.504000 )
+Santa Clara                   St       (0, 2, -122.090100, 37.496000, -122.088500, 37.485000 )
+Booker                        Way      (0, 2, -122.089800, 37.464000, -122.090200, 37.454000 )
+Cascade                       St       (0, 2, -122.089400, 37.448000, -122.088700, 37.431000 )
+Evergreen                     St       (0, 2, -122.087700, 37.458000, -122.087000, 37.455000 )
+Kay                           Ave      (0, 2, -122.096800, 37.433000, -122.096800, 37.427000 )
+Kay                           Ave      (0, 2, -122.096900, 37.398000, -122.097100, 37.389000 )
+Peterman                      Ave      (0, 2, -122.094500, 37.392000, -122.091800, 37.421000 )
+Jackson                       St       (0, 2, -122.098100, 37.368000, -122.098800, 37.365000 )
+Lauderdale                    Ave      (0, 2, -122.098300, 37.344000, -122.097700, 37.334000 )
+Hesperian                     Blvd     (0, 3, -122.097000, 37.333000, -122.095600, 37.310000 , -122.094600, 37.293000)
+Sleepy Hollow                 Ave      (0, 2, -122.093000, 37.350000, -122.092700, 37.343000 )
+Eldridge                      Ave      (0, 2, -122.089600, 37.423000, -122.088800, 37.408000 )
+Calaroga                      Ave      (0, 2, -122.090000, 37.386000, -122.089700, 37.380000 )
+Eldridge                      Ave      (0, 2, -122.088300, 37.402000, -122.087800, 37.395000 )
+Palatka                       Lane     (0, 2, -122.091500, 37.350000, -122.092700, 37.354000 )
+Calaroga                      Ave      (0, 2, -122.089200, 37.374000, -122.088800, 37.361000 )
+Rockford                      Road     (0, 2, -122.084800, 37.819000, -122.084200, 37.814000 )
+Vista del Plaza               Lane     (0, 2, -122.083400, 37.809000, -122.082900, 37.804000 )
+Sevilla                       Road     (0, 2, -122.082900, 37.809000, -122.083100, 37.810000 )
+Alamo Creek                            (0, 2, -121.910523, 37.261100, -121.910923, 37.263740 )
+M                             St       (0, 2, -122.085100, 37.754000, -122.084500, 37.742000 )
+Main                          St       (0, 2, -122.084400, 37.758000, -122.083600, 37.750000 )
+B                             St       (0, 2, -122.087000, 37.707000, -122.086300, 37.709000 )
+Levine                        Ct       (0, 2, -122.083600, 37.750000, -122.083100, 37.753000 )
+Main                          St       (0, 2, -122.081700, 37.729000, -122.080700, 37.719000 )
+4th                           St       (0, 2, -122.077500, 37.831000, -122.077200, 37.824000 )
+Russell                       Way      (0, 2, -122.080000, 37.771000, -122.078100, 37.783000 )
+Ruby                          St       (0, 2, -122.076500, 37.815000, -122.075100, 37.802000 )
+San Lorenzo Creek                      (0, 2, -122.074100, 37.799000, -122.075700, 37.789000 )
+B                             St       (0, 2, -122.079900, 37.742000, -122.078200, 37.753000 )
+Foothill                      Blvd     (0, 2, -122.079800, 37.709000, -122.079800, 37.688000 )
+C                             St       (0, 2, -122.077300, 37.742000, -122.075600, 37.754000 )
+D                             St       (0, 2, -122.074600, 37.745000, -122.074100, 37.749000 )
+Atherton                      St       (0, 2, -122.083800, 37.700000, -122.082900, 37.690000 )
+Alice                         St       (0, 2, -122.086000, 37.644000, -122.084800, 37.625000 )
+Atherton                      St       (0, 2, -122.081900, 37.680000, -122.080900, 37.669000 )
+Jackson                       St       (0, 2, -122.080900, 37.669000, -122.080400, 37.677000 )
+Bay Area Rapid Transit                 (0, 2, -122.081300, 37.661000, -122.080600, 37.654000 )
+Jackson                       St       (0, 2, -122.083800, 37.614000, -122.083200, 37.624000 )
+Jackson                       St       (0, 2, -122.084500, 37.600000, -122.084200, 37.606000 )
+Sycamore                      Ave      (0, 2, -122.082600, 37.609000, -122.081800, 37.606000 )
+Glade                         St       (0, 2, -122.081900, 37.592000, -122.081710, 37.597700 )
+Mission                       Blvd     (0, 2, -122.079800, 37.688000, -122.077900, 37.668000 )
+Fletcher                      Lane     (0, 2, -122.077900, 37.668000, -122.076200, 37.683000 )
+Leighton                      St       (0, 2, -122.080500, 37.628000, -122.079700, 37.632000 )
+Edith                         St       (0, 2, -122.077000, 37.638000, -122.076400, 37.631000 )
+Joyce                         St       (0, 2, -122.079200, 37.604000, -122.077400, 37.581000 )
+Sycamore                      Ave      (0, 2, -122.075900, 37.633000, -122.075200, 37.636000 )
+Wp Railroad                            (0, 2, -122.075500, 37.589000, -122.073100, 37.560000 )
+5th                           St       (0, 2, -122.073200, 37.800000, -122.072500, 37.789000 )
+C                             St       (0, 2, -122.073700, 37.767000, -122.072200, 37.778000 )
+7th                           St       (0, 2, -122.069300, 37.803000, -122.068700, 37.793000 )
+7th                           St       (0, 2, -122.067400, 37.771000, -122.066700, 37.761000 )
+5th                           St       (0, 2, -122.071000, 37.754000, -122.070700, 37.749000 )
+Kings                         Ct       (0, 2, -122.069500, 37.764000, -122.068900, 37.756000 )
+B                             St       (0, 2, -122.065600, 37.823000, -122.065200, 37.825000 )
+Panda                         Way      (0, 2, -122.066800, 37.773000, -122.066200, 37.762000 )
+Antelope                      Ct       (0, 2, -122.065300, 37.773000, -122.064800, 37.773000 )
+Forest Glen                   Pl       (0, 3, -122.067707, 37.018750, -122.068338, 37.019450 , -122.068308, 37.026300)
+Sulphur Creek                          (0, 2, -122.065500, 37.766000, -122.065900, 37.764000 )
+Azevedo                       Ave      (0, 2, -122.063900, 37.756000, -122.064100, 37.750000 )
+Ward Creek                             (0, 2, -122.071700, 37.679000, -122.077000, 37.657000 )
+2nd                           St       (0, 2, -122.069700, 37.696000, -122.067400, 37.688000 )
+Ward Creek Branch                      (0, 2, -122.061500, 37.620000, -122.069914, 37.644170 )
+Belmont                       Ave      (0, 2, -122.070800, 37.588000, -122.070300, 37.582000 )
+Central                       Blvd     (0, 2, -122.070300, 37.582000, -122.069700, 37.586000 )
+Ward Creek                             (0, 2, -122.056800, 37.644000, -122.058701, 37.648150 )
+Orchard                       Ave      (0, 2, -122.085800, 37.555000, -122.083300, 37.551000 )
+Soto                          Road     (0, 2, -122.081200, 37.561000, -122.079800, 37.545000 )
+Regal                         Ave      (0, 2, -122.083900, 37.468000, -122.085700, 37.449000 )
+Underwood                     Ave      (0, 2, -122.082300, 37.477000, -122.082200, 37.469000 )
+Muir                          St       (0, 2, -122.078700, 37.562000, -122.077900, 37.553000 )
+Hermes                        Ct       (0, 2, -122.078600, 37.516000, -122.078400, 37.514000 )
+Muir                          St       (0, 2, -122.076100, 37.529000, -122.075600, 37.524000 )
+Harder                        Road     (0, 2, -122.079300, 37.489000, -122.080200, 37.488000 )
+Huntwood                      Ave      (0, 2, -122.078100, 37.480000, -122.077400, 37.473000 )
+Mocine                        Ave      (0, 2, -122.075100, 37.497000, -122.074800, 37.476000 )
+Mockingbird                   Lane     (0, 2, -122.086200, 37.419000, -122.086100, 37.412000 )
+Stanwood                      Ave      (0, 2, -122.083900, 37.416000, -122.083900, 37.409000 )
+Cascade                       St       (0, 2, -122.083900, 37.416000, -122.083100, 37.416000 )
+Inglewood                     St       (0, 2, -122.080200, 37.397000, -122.082300, 37.397000 )
+Gading                        Road     (0, 2, -122.080200, 37.388000, -122.080200, 37.380000 )
+Mc Farlane                    Lane     (0, 2, -122.082300, 37.377000, -122.086300, 37.374000 )
+Scott                         Pl       (0, 2, -122.080200, 37.380000, -122.081500, 37.381000 )
+Hamrick                       Lane     (0, 2, -122.083100, 37.344000, -122.083900, 37.342000 )
+Gading                        Road     (0, 2, -122.080100, 37.343000, -122.080000, 37.336000 )
+Chisholm                      Ct       (0, 2, -122.077300, 37.420000, -122.077000, 37.409000 )
+Tyrrell                       Ave      (0, 2, -122.075100, 37.441000, -122.075900, 37.428000 )
+Foster                        Ct       (0, 2, -122.074500, 37.396000, -122.074447, 37.393880 )
+Westwood                      Pl       (0, 2, -122.077300, 37.360000, -122.078000, 37.362000 )
+Lakewood                      Way      (0, 2, -122.079500, 37.389000, -122.079300, 37.366000 )
+Cheryl Ann                    Cir      (0, 2, -122.075400, 37.352000, -122.076000, 37.358000 )
+Tampa                         Ave      (0, 3, -122.074700, 37.327000, -122.074700, 37.314000 , -122.074600, 37.308000)
+Whitman                       St       (0, 2, -122.072000, 37.540000, -122.071200, 37.530000 )
+Mission                       Blvd     (0, 2, -122.070000, 37.567000, -122.069200, 37.555000 )
+Torrano                       Ave      (0, 2, -122.068500, 37.547000, -122.067900, 37.551000 )
+Eastman                       Ct       (0, 2, -122.073200, 37.492000, -122.072200, 37.486000 )
+Harder                        Road     (0, 2, -122.069300, 37.506000, -122.068800, 37.506000 )
+Virginia                      St       (0, 2, -122.074800, 37.470000, -122.069700, 37.477000 )
+Joshua                        St       (0, 2, -122.068600, 37.455000, -122.068600, 37.449000 )
+Central                       Blvd     (0, 3, -122.064300, 37.553000, -122.063300, 37.552000 , -122.062200, 37.545000)
+Mission                       Blvd     (0, 2, -122.064100, 37.491000, -122.062000, 37.464000 )
+Laurette                      Pl       (0, 2, -122.065100, 37.476000, -122.064600, 37.479000 )
+Colette                       St       (0, 2, -122.063000, 37.460000, -122.062300, 37.451000 )
+Ranker                        Pl       (0, 2, -122.072500, 37.400000, -122.073700, 37.395000 )
+Shepherd                      Ave      (0, 2, -122.070700, 37.383000, -122.073700, 37.367000 )
+Tyrrell                       Ave      (0, 2, -122.070900, 37.334000, -122.070800, 37.329000 )
+Harris                        Road     (0, 2, -122.068100, 37.360000, -122.070500, 37.347000 )
+Brian                         St       (0, 2, -122.068600, 37.348000, -122.069300, 37.344000 )
+Duffel                        Pl       (0, 2, -122.064100, 37.434000, -122.063700, 37.429000 )
+Whitman                       St       (0, 2, -122.063300, 37.399000, -122.063000, 37.392000 )
+Harris                        Road     (0, 2, -122.065900, 37.372000, -122.067500, 37.363000 )
+Huntwood                      Ave      (0, 3, -122.064100, 37.336000, -122.063367, 37.325850 , -122.062800, 37.318000)
+Medlar                        Dr       (0, 2, -122.062700, 37.378000, -122.062500, 37.375000 )
+Rosewood                      Ct       (0, 2, -122.062200, 37.370000, -122.061800, 37.372000 )
+Tennyson                      Road     (0, 2, -122.062000, 37.345000, -122.062500, 37.343000 )
+Portsmouth                    Ave      (0, 2, -122.106400, 37.315000, -122.106400, 37.308000 )
+Sleepy Hollow                 Ave      (0, 2, -122.104500, 37.316000, -122.105400, 37.315000 )
+Tennyson                      Road     (0, 2, -122.103500, 37.272000, -122.104100, 37.268000 )
+Darwin                        St       (0, 2, -122.099600, 37.317000, -122.102400, 37.311000 )
+Portsmouth                    Ave      (0, 2, -122.102300, 37.262000, -122.101300, 37.254000 )
+Arden                         Road     (0, 2, -122.097800, 37.177000, -122.100000, 37.177000 )
+Baumberg                      Ave      (0, 2, -122.098700, 37.241000, -122.098500, 37.237000 )
+Pueblo Spring                          (0, 2, -122.096400, 37.238000, -122.096500, 37.222000 )
+Pueblo Creek                           (0, 2, -122.095800, 37.203000, -122.096500, 37.205000 )
+Pueblo Serena                          (0, 2, -122.095800, 37.222000, -122.095800, 37.203000 )
+Cabrillo                      Dr       (0, 2, -122.091000, 37.218000, -122.093200, 37.222000 )
+Tennyson                      Road     (0, 2, -122.089100, 37.317000, -122.092700, 37.317000 )
+Bolero                        Ave      (0, 2, -122.090400, 37.297000, -122.091300, 37.297000 )
+Barcelona                     Ave      (0, 2, -122.089600, 37.276000, -122.089400, 37.253000 )
+Calaroga                      Ave      (0, 2, -122.088600, 37.297000, -122.088500, 37.276000 )
+Decatur                       Way      (0, 2, -122.086800, 37.296000, -122.086300, 37.267000 )
+Hesperian                     Blvd     (0, 2, -122.091600, 37.245000, -122.089600, 37.214000 )
+Peachtree                     Dr       (0, 2, -122.091000, 37.209000, -122.091300, 37.199000 )
+Keys                          Pl       (0, 2, -122.087100, 37.253000, -122.087500, 37.252000 )
+Bradshire                     Road     (0, 2, -122.088500, 37.204000, -122.088300, 37.200000 )
+Bourbon                       Dr       (0, 2, -122.086900, 37.194000, -122.087800, 37.192000 )
+Hesperian                     Blvd     (0, 2, -122.087800, 37.182000, -122.087300, 37.174000 )
+Coyote Hills Slough                    (0, 2, -122.090400, 37.850000, -122.095300, 37.829000 )
+Melbourne                     Ave      (0, 2, -122.084200, 37.285000, -122.083700, 37.269000 )
+Everglade                     St       (0, 2, -122.082200, 37.275000, -122.083000, 37.272000 )
+Catalpa                       Way      (0, 2, -122.085200, 37.218000, -122.088000, 37.207000 )
+Tilgrim                       Way      (0, 2, -122.083100, 37.211000, -122.084000, 37.211000 )
+Elder                         Way      (0, 2, -122.081900, 37.229000, -122.082700, 37.229000 )
+Miami                         Ave      (0, 2, -122.081200, 37.234000, -122.081200, 37.229000 )
+Sparrow                       Road     (0, 2, -122.082700, 37.209000, -122.082600, 37.203000 )
+Mantilla                      Ave      (0, 2, -122.078100, 37.310000, -122.079000, 37.309000 )
+Lanai                         Ct       (0, 2, -122.076800, 37.269000, -122.076800, 37.260000 )
+Sumatra                       St       (0, 2, -122.074300, 37.277000, -122.075100, 37.276000 )
+Hesse                         Dr       (0, 2, -122.078200, 37.208000, -122.078200, 37.204000 )
+Murcia                        St       (0, 2, -122.076000, 37.235000, -122.075800, 37.230000 )
+Granada                       Cir      (0, 2, -122.074100, 37.234000, -122.074200, 37.230000 )
+Almeria                       Dr       (0, 2, -122.071100, 37.224000, -122.073600, 37.224000 )
+Granada                       Dr       (0, 2, -122.073800, 37.220000, -122.073600, 37.216000 )
+Willard                       Way      (0, 2, -122.087100, 37.169000, -122.084300, 37.177000 )
+Sp Railroad                            (0, 2, -122.086000, 37.079000, -122.081000, 37.036000 )
+Hopkins                       St       (0, 2, -122.077800, 37.184000, -122.077500, 37.159000 )
+Faber                         St       (0, 2, -122.069400, 37.105000, -122.075000, 37.106000 )
+Thackeray                     Ave      (0, 2, -122.072000, 37.305000, -122.071500, 37.298000 )
+Biscayne                      Ave      (0, 2, -122.073400, 37.278000, -122.073400, 37.274000 )
+Island Pine                   Ct       (0, 2, -122.069000, 37.310000, -122.069700, 37.310000 )
+Tennyson                      Road     (0, 2, -122.068200, 37.318000, -122.068500, 37.317000 )
+Fabian                        Way      (0, 2, -122.070300, 37.270000, -122.068400, 37.269000 )
+Granada                       Cir      (0, 2, -122.073400, 37.229000, -122.073600, 37.228000 )
+Granada                       Dr       (0, 2, -122.073400, 37.212000, -122.073400, 37.207000 )
+Miranda                       St       (0, 2, -122.071500, 37.211000, -122.071700, 37.206000 )
+Chelsea                       Way      (0, 2, -122.068000, 37.224000, -122.068600, 37.223000 )
+Buckingham                    Way      (0, 2, -122.068900, 37.208000, -122.069300, 37.207000 )
+Ruus                          Road     (0, 2, -122.066700, 37.296000, -122.066000, 37.280000 )
+Minerva                       St       (0, 2, -122.065200, 37.282000, -122.066000, 37.280000 )
+Celia                         St       (0, 2, -122.062300, 37.297000, -122.063100, 37.296000 )
+Celia                         St       (0, 2, -122.061100, 37.300000, -122.061600, 37.299000 )
+Folsom                        Ave      (0, 2, -122.062800, 37.259000, -122.062855, 37.258820 )
+Sussex                        Way      (0, 3, -122.065700, 37.227000, -122.066900, 37.222000 , -122.067300, 37.219000)
+Buckingham                    Way      (0, 2, -122.064700, 37.214000, -122.065300, 37.214000 )
+Sandburg                      Way      (0, 2, -122.062500, 37.226000, -122.062500, 37.203000 )
+Sandy Hook                    Dr       (0, 2, -122.061500, 37.220000, -122.062000, 37.221000 )
+Industrial                    Pkwy     (0, 2, -122.072300, 37.183000, -122.073100, 37.184000 )
+Zephyr                        Ave      (0, 2, -122.058400, 37.107000, -122.060900, 37.107000 )
+Union City                    Blvd     (0, 2, -122.080500, 37.986000, -122.080400, 37.972000 )
+Brier                         St       (0, 2, -122.080600, 37.959000, -122.080500, 37.963000 )
+Granger                       Ave      (0, 2, -122.077800, 37.984000, -122.078400, 37.985000 )
+Union City                    Blvd     (0, 2, -122.079700, 37.964000, -122.079700, 37.959000 )
+Smith                         St       (0, 2, -122.075900, 37.963000, -122.077800, 37.964000 )
+Randall                       Ct       (0, 2, -122.074900, 37.976000, -122.075600, 37.968000 )
+Union City                    Blvd     (0, 2, -122.079500, 37.923000, -122.079700, 37.907000 )
+Solano                        Way      (0, 2, -122.077300, 37.921000, -122.078500, 37.921000 )
+Agua Vista                             (0, 2, -122.079600, 37.896000, -122.079200, 37.896000 )
+Reyes                         Dr       (0, 2, -122.079100, 37.873000, -122.078700, 37.873000 )
+Fredi                         St       (0, 2, -122.076400, 37.934000, -122.076700, 37.930000 )
+Encinitas                     Way      (0, 2, -122.075900, 37.917000, -122.075600, 37.911000 )
+Victoria                      Ave      (0, 2, -122.073900, 37.905000, -122.075900, 37.903000 )
+Queen Anne                    Dr       (0, 2, -122.075100, 37.883000, -122.077000, 37.865000 )
+Barcelona                     Way      (0, 2, -122.078700, 37.860000, -122.078300, 37.858000 )
+Regents                       Blvd     (0, 2, -122.077000, 37.865000, -122.076100, 37.862000 )
+Cabello                       St       (0, 2, -122.078000, 37.811000, -122.078300, 37.807000 )
+Allison                       Dr       (0, 2, -122.074800, 37.863000, -122.073000, 37.855000 )
+Jean                          Ct       (0, 2, -122.075500, 37.809000, -122.076600, 37.798000 )
+Sp Railroad                            (0, 2, -122.073400, 37.001000, -122.073400, 37.997000 )
+Whipple                       Road     (0, 3, -122.067600, 37.055000, -122.067800, 37.057000 , -122.075200, 37.057000)
+Dyer                          St       (0, 2, -122.068400, 37.028000, -122.069100, 37.005000 )
+Smith                         St       (0, 2, -122.072700, 37.965000, -122.074200, 37.964000 )
+Dyer                          St       (0, 2, -122.070100, 37.969000, -122.070300, 37.965000 )
+San Luces                     Way      (0, 2, -122.067200, 37.963000, -122.069500, 37.954000 )
+Franklin                      Ave      (0, 2, -122.060500, 37.028000, -122.061400, 37.039000 )
+San Luis                      Ct       (0, 2, -122.065000, 37.975000, -122.065700, 37.973000 )
+San Marco                     Way      (0, 2, -122.066600, 37.950000, -122.068700, 37.942000 )
+San Andreas                   Dr       (0, 2, -122.062100, 37.973000, -122.061400, 37.972000 )
+Santa Maria                   Dr       (0, 2, -122.063300, 37.965000, -122.062400, 37.961000 )
+San Bernardino                Way      (0, 2, -122.062100, 37.936000, -122.062800, 37.933000 )
+Tumbleweed                    Ct       (0, 2, -122.071500, 37.906000, -122.071800, 37.908000 )
+Feldspar                      Ct       (0, 2, -122.072200, 37.874000, -122.073400, 37.870000 )
+San Andreas                   Dr       (0, 2, -122.066800, 37.926000, -122.067200, 37.931000 )
+Aquarius                      Cir      (0, 2, -122.066900, 37.877000, -122.067400, 37.880000 )
+Meteor                        Dr       (0, 2, -122.071300, 37.868000, -122.070200, 37.862000 )
+Edith                         Way      (0, 2, -122.073300, 37.825000, -122.071600, 37.804000 )
+Alice                         Way      (0, 2, -122.071500, 37.836000, -122.072000, 37.833000 )
+Agena                         Cir      (0, 2, -122.069400, 37.847000, -122.069600, 37.839000 )
+Galaxy                        Dr       (0, 2, -122.067500, 37.866000, -122.068000, 37.860000 )
+Ellen                         Way      (0, 2, -122.068500, 37.830000, -122.068700, 37.828000 )
+San Andreas                   Dr       (0, 2, -122.065800, 37.907000, -122.066100, 37.914000 )
+Lunar                         Way      (0, 2, -122.066800, 37.883000, -122.066900, 37.877000 )
+San Andreas                   Dr       (0, 2, -122.063500, 37.878000, -122.064800, 37.891000 )
+San Mateo                     Way      (0, 2, -122.060900, 37.926000, -122.061842, 37.923910 )
+San Andreas                   Dr       (0, 2, -122.060900, 37.900000, -122.061400, 37.895000 )
+Chippendale                   Dr       (0, 2, -122.066500, 37.843000, -122.068000, 37.828000 )
+Endeavour                     Way      (0, 2, -122.065500, 37.842000, -122.064700, 37.840000 )
+Fellows                       St       (0, 2, -122.065200, 37.814000, -122.066000, 37.805000 )
+Sp Railroad                            (0, 2, -122.062600, 37.857000, -122.061600, 37.845000 )
+Holt                          St       (0, 2, -122.063000, 37.813000, -122.063508, 37.802570 )
+Coyote Hills Slough                    (0, 2, -122.107500, 37.687000, -122.128500, 37.643000 )
+Union City                    Blvd     (0, 2, -122.079100, 37.760000, -122.078600, 37.751000 )
+Jean                          Dr       (0, 2, -122.076600, 37.798000, -122.075900, 37.794000 )
+Jean                          Dr       (0, 2, -122.073900, 37.780000, -122.073400, 37.774000 )
+Louise                        Lane     (0, 2, -122.074900, 37.764000, -122.075400, 37.758000 )
+Christine                     Dr       (0, 2, -122.075900, 37.739000, -122.075600, 37.734000 )
+Delores                       Dr       (0, 2, -122.074200, 37.735000, -122.075200, 37.727000 )
+Ellen                         Way      (0, 2, -122.071100, 37.801000, -122.071600, 37.796000 )
+Shiela                        Way      (0, 2, -122.073400, 37.757000, -122.073000, 37.750000 )
+Blythe                        St       (0, 2, -122.070400, 37.745000, -122.071100, 37.739000 )
+Fellows                       St       (0, 2, -122.068200, 37.780000, -122.068700, 37.775000 )
+Regents                       Blvd     (0, 2, -122.067300, 37.759000, -122.067700, 37.751000 )
+Bel Aire                      St       (0, 2, -122.071700, 37.726000, -122.071400, 37.725000 )
+Rocklin                       Dr       (0, 2, -122.071900, 37.698000, -122.072200, 37.689000 )
+Regents                       Blvd     (0, 2, -122.068100, 37.740000, -122.068000, 37.730000 )
+Corning                       Ct       (0, 2, -122.068900, 37.688000, -122.068500, 37.680000 )
+McKeown                       Ct       (0, 2, -122.067600, 37.681000, -122.067100, 37.674000 )
+Regents                       Blvd     (0, 2, -122.066200, 37.779000, -122.066625, 37.772160 )
+Oakdale                       St       (0, 2, -122.064800, 37.767000, -122.065400, 37.761000 )
+Korbel                        St       (0, 2, -122.064800, 37.742000, -122.065500, 37.723000 )
+Soquel                        St       (0, 2, -122.066400, 37.711000, -122.065700, 37.707000 )
+Novato                        St       (0, 2, -122.062800, 37.731000, -122.063300, 37.723000 )
+Union City                    Blvd     (0, 2, -122.067400, 37.657000, -122.066900, 37.654000 )
+Patterson Ranch               Road     (0, 3, -122.070200, 37.545000, -122.085500, 37.509000 , -122.090200, 37.515000)
+Thornton                      Ave      (0, 2, -122.063600, 37.335000, -122.062600, 37.303000 )
+San Francisco Bay                      (0, 2, -122.108000, 37.032000, -122.104800, 37.001000 )
+Plummer Creek                          (0, 2, -122.077800, 37.095000, -122.085200, 37.069000 )
+Romey                         Lane     (0, 2, -122.060300, 37.825000, -122.058700, 37.825000 )
+Fairlands                     Road     (0, 2, -122.058300, 37.784000, -122.059000, 37.781000 )
+D                             St       (0, 3, -122.055000, 37.798000, -122.054100, 37.796000 , -122.052900, 37.794000)
+Hidden                        Lane     (0, 3, -122.055300, 37.757000, -122.053010, 37.752420 , -122.050300, 37.747000)
+Hansen                        Road     (0, 2, -122.055100, 37.706000, -122.055000, 37.712000 )
+Fairview                      Ave      (0, 2, -122.049000, 37.788000, -122.048200, 37.775000 )
+Sulphur                       Dr       (0, 2, -122.051700, 37.719000, -122.050600, 37.715000 )
+Campus                        Dr       (0, 2, -122.057800, 37.665000, -122.054500, 37.660000 )
+Panitz                        St       (0, 2, -122.055300, 37.686000, -122.055300, 37.679000 )
+West Loop                     Road     (0, 2, -122.057600, 37.604000, -122.060200, 37.586000 )
+2nd                           St       (0, 2, -122.055300, 37.679000, -122.053900, 37.682000 )
+2nd                           St       (0, 2, -122.052200, 37.685000, -122.052050, 37.685400 )
+Warwick                       Pl       (0, 2, -122.049800, 37.647000, -122.048700, 37.634000 )
+Hillcrest                     Ave      (0, 2, -122.049200, 37.591000, -122.048500, 37.587000 )
+Fairview                      Ave      (0, 2, -122.043200, 37.752000, -122.042800, 37.751000 )
+Fairview                      Ave      (0, 2, -122.041800, 37.748000, -122.040200, 37.764000 )
+East                          Ave      (0, 2, -122.036100, 37.719000, -122.035200, 37.733000 )
+Cobblestone                   Dr       (0, 2, -122.001220, 37.849200, -122.000944, 37.847950 )
+Parkside                      Dr       (0, 2, -122.047500, 37.603000, -122.044300, 37.596000 )
+Roxbury                       Lane     (0, 2, -122.035900, 37.623000, -122.035600, 37.619000 )
+10th                          St       (0, 2, -122.060300, 37.402000, -122.059300, 37.390000 )
+Webster                       St       (0, 2, -122.059300, 37.390000, -122.058700, 37.393000 )
+15th                          St       (0, 2, -122.056400, 37.416000, -122.056000, 37.411000 )
+13th                          St       (0, 2, -122.056500, 37.386000, -122.055400, 37.374000 )
+Tennyson                      Road     (0, 2, -122.060500, 37.352000, -122.060200, 37.354000 )
+Rochelle                      Ave      (0, 2, -122.060300, 37.347000, -122.059400, 37.329000 )
+May                           Ct       (0, 2, -122.058900, 37.323000, -122.057700, 37.331000 )
+Monticello                    St       (0, 2, -122.056100, 37.370000, -122.055400, 37.374000 )
+Cole                          Pl       (0, 2, -122.057000, 37.343000, -122.056400, 37.334000 )
+Calhoun                       St       (0, 2, -122.054200, 37.430000, -122.052100, 37.428000 )
+16th                          St       (0, 2, -122.054000, 37.414000, -122.053400, 37.405000 )
+Dixon                         St       (0, 2, -122.052400, 37.320000, -122.051400, 37.311000 )
+Call                          Ave      (0, 2, -122.043500, 37.560000, -122.043600, 37.566000 )
+Dobbel                        Ave      (0, 2, -122.041700, 37.520000, -122.040800, 37.515000 )
+Contreras                     Pl       (0, 2, -122.038600, 37.553000, -122.038700, 37.550000 )
+Hayward                       Blvd     (0, 2, -122.038300, 37.556000, -122.038100, 37.557000 )
+Roundhill                     Dr       (0, 2, -122.037600, 37.542000, -122.037700, 37.536000 )
+Nobhill                       Ct       (0, 2, -122.035400, 37.523000, -122.035300, 37.517000 )
+Mallard                       Ct       (0, 2, -122.035800, 37.486000, -122.036700, 37.492000 )
+Hayward                       Blvd     (0, 2, -122.037570, 37.559600, -122.033997, 37.554490 )
+Durham                        Way      (0, 2, -122.032100, 37.656000, -122.031100, 37.642000 )
+Roxbury                       Lane     (0, 2, -122.033300, 37.615000, -122.032900, 37.618000 )
+Quercus                       Ct       (0, 2, -122.026700, 37.590000, -122.026900, 37.572000 )
+Hayward                       Blvd     (0, 2, -122.033500, 37.552000, -122.032996, 37.550940 )
+Farm Hill                     Dr       (0, 2, -122.032800, 37.523000, -122.033300, 37.511000 )
+Dobbel                        Ave      (0, 2, -122.031900, 37.460000, -122.032800, 37.463000 )
+Deer Park                     Way      (0, 2, -122.027900, 37.526000, -122.026600, 37.524000 )
+Hayward                       Blvd     (0, 2, -122.022400, 37.563000, -122.017900, 37.544000 )
+Skyline                       Dr       (0, 2, -122.027700, 37.500000, -122.028400, 37.498000 )
+Fairview                      Ave      (0, 2, -122.016500, 37.549000, -122.016000, 37.525000 )
+Mercury                       St       (0, 2, -122.059800, 37.291000, -122.059500, 37.284000 )
+Huntwood                      Ave      (0, 2, -122.060000, 37.279000, -122.059800, 37.273000 )
+Folsom                        Ave      (0, 2, -122.058700, 37.271000, -122.059600, 37.269000 )
+De la Cruz                    Road     (0, 2, -122.055400, 37.305000, -122.055000, 37.301000 )
+Pacific                       St       (0, 2, -122.054400, 37.294000, -122.054800, 37.297000 )
+Whalebone                     Way      (0, 2, -122.059200, 37.244000, -122.059000, 37.242000 )
+New England Village           Dr       (0, 2, -122.058000, 37.237000, -122.058500, 37.235000 )
+Taylor                        Ave      (0, 3, -122.054700, 37.245000, -122.054100, 37.241000 , -122.053500, 37.237000)
+Sp Railroad                            (0, 3, -122.055300, 37.212000, -122.065200, 37.134000 , -122.065400, 37.131000)
+Chance                        St       (0, 2, -122.053600, 37.259000, -122.053400, 37.256000 )
+Alameda Creek                          (0, 2, -122.051300, 37.248000, -122.055600, 37.215000 )
+Bart                          Ramp     (0, 2, -122.049500, 37.208000, -122.047300, 37.196000 )
+Zephyr                        Ave      (0, 2, -122.057200, 37.107000, -122.055600, 37.107000 )
+San Clemente                  St       (0, 2, -122.051100, 37.188000, -122.051000, 37.177000 )
+Huntwood                      Ave      (0, 2, -122.053100, 37.093000, -122.053100, 37.078000 )
+Medallion                     Dr       (0, 2, -122.050200, 37.080000, -122.050200, 37.059000 )
+Periwinkle                    Road     (0, 2, -122.045100, 37.301000, -122.044758, 37.298440 )
+Holiday                       St       (0, 2, -122.042100, 37.306000, -122.041800, 37.304000 )
+Wp Railroad                            (0, 2, -122.047600, 37.214000, -122.047300, 37.196000 )
+Sunnydale                     Ct       (0, 2, -122.042900, 37.203000, -122.042900, 37.200000 )
+Gisler                        Way      (0, 2, -122.042000, 37.285000, -122.039200, 37.298000 )
+Treeview                      St       (0, 2, -122.039000, 37.282000, -122.038700, 37.278000 )
+Audubon                       St       (0, 2, -122.038800, 37.261000, -122.038300, 37.258000 )
+Treeview                      St       (0, 2, -122.037900, 37.267000, -122.037500, 37.262000 )
+Vanderbilt                    St       (0, 2, -122.039000, 37.254000, -122.038600, 37.243000 )
+Meadowbrook                   Ave      (0, 2, -122.040600, 37.198000, -122.038900, 37.183000 )
+Fairway                       St       (0, 2, -122.037500, 37.221000, -122.036800, 37.226000 )
+Prestwick                     Ave      (0, 2, -122.036900, 37.208000, -122.036300, 37.202000 )
+San Antonio                   St       (0, 2, -122.047200, 37.155000, -122.047700, 37.108000 )
+Birkdale                      Way      (0, 2, -122.040600, 37.170000, -122.038600, 37.153000 )
+Sp Railroad                            (0, 2, -122.038600, 37.133000, -122.033500, 37.089000 )
+Ganton                        Ct       (0, 2, -122.037900, 37.146000, -122.038000, 37.152000 )
+Hermitage                     Lane     (0, 2, -122.036200, 37.137000, -122.035400, 37.136000 )
+Knapp                         St       (0, 2, -122.059400, 37.062000, -122.059300, 37.049000 )
+Whipple                       Road     (0, 2, -122.053200, 37.059000, -122.057600, 37.059000 )
+Parkside                      Dr       (0, 2, -122.059500, 37.008000, -122.059200, 37.012000 )
+Crest                         Ct       (0, 2, -122.056600, 37.049000, -122.057100, 37.050000 )
+Downing                       Pl       (0, 2, -122.054900, 37.035000, -122.054600, 37.041000 )
+Almaden                       Blvd     (0, 2, -122.055100, 37.008000, -122.055100, 37.016000 )
+San Andreas                   Dr       (0, 2, -122.059200, 37.957000, -122.058500, 37.954000 )
+Balmoral                      St       (0, 2, -122.055000, 37.971000, -122.055500, 37.979000 )
+Crest                         Lane     (0, 2, -122.055800, 37.047000, -122.054600, 37.047000 )
+Claremont                     Pl       (0, 2, -122.054200, 37.995000, -122.054200, 37.008000 )
+Becket                        Dr       (0, 2, -122.050900, 37.005000, -122.050900, 37.033000 )
+Sheffield                     Lane     (0, 2, -122.051100, 37.006000, -122.051292, 37.001780 )
+Diablo                        Pl       (0, 2, -122.054300, 37.973000, -122.053400, 37.974000 )
+Minturn                       Ct       (0, 2, -122.054500, 37.944000, -122.053800, 37.946000 )
+Kenita                        Way      (0, 2, -122.050800, 37.944000, -122.050300, 37.944000 )
+Salton Sea                    Lane     (0, 2, -122.059700, 37.880000, -122.059100, 37.869000 )
+Lake Pillsbury                Dr       (0, 2, -122.056100, 37.906000, -122.057300, 37.901000 )
+Grand Lake                    Dr       (0, 2, -122.055300, 37.890000, -122.056000, 37.894000 )
+Bucks Lake                    St       (0, 2, -122.055900, 37.882000, -122.054600, 37.874000 )
+Grand Lake                    Dr       (0, 2, -122.059100, 37.864000, -122.058700, 37.861000 )
+Warbler                       Loop     (0, 2, -122.060100, 37.813000, -122.059700, 37.793000 )
+Tule Lake                     Lane     (0, 2, -122.056800, 37.871000, -122.055900, 37.866000 )
+Lake Ontario                  Dr       (0, 2, -122.056200, 37.852000, -122.056700, 37.847000 )
+Lake Ontario                  Dr       (0, 2, -122.055400, 37.863000, -122.055700, 37.859000 )
+Alvarado                      Blvd     (0, 2, -122.056200, 37.829000, -122.055814, 37.827230 )
+Isola                         Ct       (0, 2, -122.055100, 37.815000, -122.055500, 37.811000 )
+Acapulco                      Way      (0, 2, -122.051700, 37.910000, -122.051900, 37.911000 )
+Lake Mead                     Dr       (0, 3, -122.053300, 37.873000, -122.052300, 37.858000 , -122.052400, 37.853000)
+Medallion                     Dr       (0, 2, -122.050200, 37.929000, -122.050200, 37.936000 )
+Palm                          Dr       (0, 2, -122.050300, 37.898000, -122.050100, 37.897000 )
+Tropicana                     Way      (0, 3, -122.049200, 37.892000, -122.050057, 37.882560 , -122.051300, 37.874000)
+Hop Ranch                     Road     (0, 2, -122.046100, 37.942000, -122.045974, 37.929450 )
+Western Pacific Railroad Spur          (0, 2, -122.039400, 37.018000, -122.039400, 37.961000 )
+Atlantic                      St       (0, 2, -122.037100, 37.018000, -122.038200, 37.018000 )
+Pacific                       St       (0, 2, -122.034200, 37.962000, -122.037100, 37.962000 )
+Hula                          Cir      (0, 2, -122.046500, 37.869000, -122.046800, 37.875000 )
+Dowe                          Ave      (0, 2, -122.043900, 37.903000, -122.043630, 37.910980 )
+Molaka                        Cir      (0, 2, -122.047600, 37.863000, -122.047900, 37.871000 )
+Whimbrel                      Road     (0, 2, -122.045000, 37.842000, -122.044700, 37.839000 )
+Quail Run                     Road     (0, 2, -122.044800, 37.808000, -122.043900, 37.805000 )
+Whimbrel                      Road     (0, 2, -122.043900, 37.832000, -122.043200, 37.828000 )
+Beard                         Road     (0, 2, -122.041700, 37.819000, -122.042400, 37.810000 )
+Junipero Com                           (0, 2, -121.991900, 37.796000, -121.992000, 37.791000 )
+Alameda Creek                          (0, 2, -122.038000, 37.877000, -122.044600, 37.873000 )
+Arizona                       St       (0, 2, -122.038100, 37.901000, -122.036700, 37.898000 )
+Sylvester                     Dr       (0, 2, -122.041000, 37.815000, -122.040500, 37.812000 )
+Freeman                       Pl       (0, 2, -122.037500, 37.866000, -122.037548, 37.865360 )
+Seymour                       Pl       (0, 2, -122.035700, 37.840000, -122.036700, 37.837000 )
+Hillside                      Ave      (0, 3, -121.997183, 37.845710, -121.998118, 37.837590 , -121.998300, 37.836000)
+Fairway                       St       (0, 2, -122.035300, 37.237000, -122.034300, 37.238000 )
+Jayar                         Pl       (0, 2, -122.034500, 37.206000, -122.033700, 37.209000 )
+Tina                          Way      (0, 2, -122.032200, 37.185000, -122.030500, 37.196000 )
+Gresel                        St       (0, 2, -122.034500, 37.178000, -122.033800, 37.182000 )
+Hugh                          Way      (0, 2, -122.032200, 37.185000, -122.031700, 37.180000 )
+Albany                        St       (0, 2, -122.032700, 37.129000, -122.032600, 37.115000 )
+Erica                         Pl       (0, 2, -122.029900, 37.165000, -122.029300, 37.169000 )
+Kennet                        St       (0, 2, -122.030900, 37.129000, -122.031000, 37.115000 )
+Westchester                   St       (0, 2, -122.031000, 37.135000, -122.029300, 37.136000 )
+Bart Access                   Road     (0, 2, -122.034600, 37.081000, -122.032900, 37.057000 )
+Seneca                        St       (0, 2, -122.030000, 37.122000, -122.030000, 37.115000 )
+Ithaca                        St       (0, 2, -122.031800, 37.090000, -122.031700, 37.087000 )
+Moccasin                      St       (0, 2, -122.030200, 37.085000, -122.029700, 37.078000 )
+Revere                        Ave      (0, 2, -122.030000, 37.129000, -122.028700, 37.129000 )
+Dearborn                      St       (0, 2, -122.027400, 37.107000, -122.027500, 37.101000 )
+Pulaski                       Dr       (0, 2, -122.026200, 37.107000, -122.025400, 37.100000 )
+Warner                        Ave      (0, 2, -122.024900, 37.096000, -122.024000, 37.103000 )
+Pinto                         Ct       (0, 2, -122.022800, 37.080000, -122.022400, 37.073000 )
+Mission                       Blvd     (0, 2, -122.022100, 37.084000, -122.021325, 37.077620 )
+Palmetto                      Dr       (0, 2, -122.018900, 37.111000, -122.018000, 37.106000 )
+South Dry Creek Branch                 (0, 2, -122.016800, 37.083000, -122.017300, 37.084000 )
+May                           Road     (0, 2, -122.016800, 37.083000, -122.015000, 37.095000 )
+Bay Area Rapid Transit                 (0, 3, -122.030900, 37.057000, -122.028100, 37.027000 , -122.026200, 37.001000)
+Western Pacific Railroad               (0, 2, -122.030200, 37.963000, -122.030200, 37.990000 )
+Hartford                      Dr       (0, 2, -122.029700, 37.940000, -122.029500, 37.944000 )
+A                             St       (0, 2, -122.026500, 37.049000, -122.027100, 37.045000 )
+B                             St       (0, 2, -122.024100, 37.050000, -122.024800, 37.045000 )
+5th                           St       (0, 2, -122.023500, 37.054000, -122.022400, 37.045000 )
+Vasquez                       Ct       (0, 2, -122.025200, 37.029000, -122.025392, 37.027560 )
+D                             St       (0, 2, -122.023900, 37.017000, -122.024200, 37.015000 )
+University                    Dr       (0, 2, -122.027500, 37.971000, -122.026400, 37.970000 )
+Colgate                       Dr       (0, 2, -122.027100, 37.940000, -122.024900, 37.933000 )
+F                             St       (0, 2, -122.024600, 37.976000, -122.024700, 37.974000 )
+F                             St       (0, 2, -122.022300, 37.993000, -122.022500, 37.993000 )
+13th                          St       (0, 2, -122.024200, 37.962000, -122.023100, 37.954000 )
+14th                          St       (0, 2, -122.023800, 37.949000, -122.022600, 37.938000 )
+Alvarado Niles                Road     (0, 2, -122.032500, 37.903000, -122.031600, 37.900000 )
+Dalton                        Way      (0, 2, -122.029300, 37.927000, -122.029700, 37.915000 )
+Nevada                        St       (0, 2, -122.031300, 37.891000, -122.031700, 37.882000 )
+Hilton                        St       (0, 2, -122.030000, 37.877000, -122.030500, 37.864000 )
+Sterne                        Pl       (0, 2, -122.031800, 37.838000, -122.032700, 37.835000 )
+Chaucer                       Dr       (0, 2, -122.033900, 37.813000, -122.034200, 37.812000 )
+Paseo Padre                   Pkwy     (0, 2, -122.033200, 37.819000, -122.030700, 37.809000 )
+Calcott                       Ct       (0, 2, -122.030600, 37.822000, -122.031100, 37.817000 )
+Baylor                        St       (0, 2, -122.027200, 37.930000, -122.028400, 37.903000 )
+Royal Ann                     Dr       (0, 2, -122.026700, 37.889000, -122.026800, 37.880000 )
+Royal Ann                     Dr       (0, 2, -122.027300, 37.871000, -122.027800, 37.862000 )
+Perry                         Road     (0, 2, -122.026500, 37.846000, -122.025600, 37.844000 )
+Maraschino                    Ct       (0, 2, -122.024900, 37.842000, -122.025200, 37.833000 )
+Cherrywood                    Dr       (0, 2, -122.023000, 37.838000, -122.023700, 37.820000 )
+C                             St       (0, 2, -122.021800, 37.050000, -122.022400, 37.045000 )
+6th                           St       (0, 2, -122.021900, 37.032000, -122.020800, 37.022000 )
+E                             St       (0, 4, -122.019000, 37.037000, -122.019500, 37.032000 , -122.020300, 37.027000, -122.020800, 37.022000)
+F                             St       (0, 2, -122.019100, 37.017000, -122.019800, 37.013000 )
+G                             St       (0, 2, -122.016700, 37.018000, -122.017400, 37.013000 )
+10th                          St       (0, 2, -122.021100, 37.969000, -122.019800, 37.958000 )
+H                             St       (0, 2, -122.020400, 37.974000, -122.020700, 37.972000 )
+Wp Railroad                            (0, 2, -122.022300, 37.959000, -122.021100, 37.949000 )
+11th                          St       (0, 3, -122.020600, 37.952000, -122.019200, 37.941000 , -122.018500, 37.934000)
+7th                           St       (0, 2, -122.016800, 37.978000, -122.014900, 37.974000 )
+Decoto                        Road     (0, 2, -122.017500, 37.961000, -122.017700, 37.955000 )
+Decoto                        Road     (0, 3, -122.015900, 37.006000, -122.016000, 37.002000 , -122.016400, 37.993000)
+Bay Area Rapid Transit                 (0, 2, -122.020000, 37.935000, -122.019300, 37.926000 )
+Decoto                        Road     (0, 2, -122.018900, 37.925000, -122.019000, 37.921000 )
+Skylark                       Dr       (0, 2, -122.019200, 37.850000, -122.018600, 37.848000 )
+Kennedy                       Ave      (0, 2, -122.020100, 37.832000, -122.020300, 37.824000 )
+Canary                        Ct       (0, 2, -122.019000, 37.856000, -122.019200, 37.850000 )
+Mann                          Ave      (0, 2, -122.016500, 37.844000, -122.017100, 37.830000 )
+Gem                           Ave      (0, 2, -122.016700, 37.823000, -122.017100, 37.809000 )
+Godwit                        Ct       (0, 2, -122.014100, 37.840000, -122.014100, 37.845000 )
+Grouse                        Way      (0, 2, -122.013600, 37.821000, -122.013412, 37.830990 )
+Skylark                       Dr       (0, 2, -122.011700, 37.839000, -122.011000, 37.835000 )
+Palomares                     Road     (0, 2, -121.997600, 37.676000, -121.997000, 37.674000 )
+Fairview                      Ave      (0, 2, -121.999000, 37.428000, -121.986300, 37.351000 )
+Palomares                     Road     (0, 2, -121.968300, 37.439000, -121.968600, 37.436000 )
+O Connell                     Lane     (0, 2, -122.003800, 37.972000, -122.003600, 37.975000 )
+Appian                        Way      (0, 2, -122.002200, 37.980000, -122.001900, 37.983000 )
+Riviera                       Dr       (0, 2, -122.000300, 37.960000, -122.000300, 37.968000 )
+Monaco                        Ave      (0, 2, -122.002400, 37.966000, -122.002300, 37.967000 )
+Riviera                       Dr       (0, 2, -122.000300, 37.954000, -122.000300, 37.957000 )
+7th                           St       (0, 2, -122.004700, 37.916000, -122.004100, 37.920000 )
+Bay Area Rapid Transit                 (0, 2, -122.007000, 37.833000, -122.006200, 37.827000 )
+Bay Area Rapid Transit                 (0, 2, -122.004900, 37.816000, -122.004000, 37.809000 )
+Mission                       Blvd     (0, 2, -122.000600, 37.896000, -121.998900, 37.880000 )
+El Portal                     Ave      (0, 2, -121.999300, 37.830000, -121.999700, 37.817000 )
+O Connell                     Lane     (0, 2, -121.994100, 37.985000, -121.991700, 37.974000 )
+McKeown                       Ter      (0, 2, -121.992000, 37.825000, -121.992203, 37.822750 )
+Easterday                     Way      (0, 2, -121.991600, 37.829000, -121.989900, 37.814000 )
+Blaisdell                     Way      (0, 2, -121.985800, 37.816000, -121.985300, 37.811000 )
+Stenhammer                    Dr       (0, 2, -121.961200, 37.840000, -121.960700, 37.826000 )
+Gull                          Ct       (0, 2, -122.059400, 37.775000, -122.059500, 37.782000 )
+Sp Railroad                            (0, 2, -122.059400, 37.750000, -122.059300, 37.743000 )
+Lark                          Way      (0, 2, -122.056300, 37.800000, -122.055300, 37.797000 )
+Caliban                       Dr       (0, 2, -122.055300, 37.765000, -122.053955, 37.758060 )
+Bardolph                      Cir      (0, 2, -122.058300, 37.718000, -122.058600, 37.726000 )
+Slender                       Ct       (0, 2, -122.056500, 37.730000, -122.055000, 37.735000 )
+Romeo                         Pl       (0, 2, -122.056200, 37.692000, -122.057100, 37.694000 )
+Flint River                   Ter      (0, 2, -122.054000, 37.780000, -122.053600, 37.778000 )
+Cheyenne River Com                     (0, 2, -122.052100, 37.779000, -122.052400, 37.775000 )
+Deep Creek                    Road     (0, 2, -122.053300, 37.749000, -122.053709, 37.742180 )
+Horatio                       Way      (0, 2, -122.049900, 37.781000, -122.050500, 37.774000 )
+Shylock                       Dr       (0, 2, -122.048800, 37.748000, -122.048600, 37.740000 )
+Deep Creek                    Road     (0, 3, -122.053600, 37.697000, -122.051700, 37.674000 , -122.051300, 37.668000)
+Paseo Padre                   Pkwy     (0, 2, -122.049000, 37.684000, -122.050000, 37.679990 )
+Beard                         Road     (0, 2, -122.044700, 37.778000, -122.045000, 37.775000 )
+Sora Com                               (0, 2, -122.043000, 37.743000, -122.042800, 37.746000 )
+Donalban                      Cir      (0, 2, -122.046400, 37.710000, -122.047500, 37.709000 )
+Blackstone                    Way      (0, 2, -122.041800, 37.736000, -122.043000, 37.743000 )
+Colville                      Pl       (0, 2, -122.041900, 37.709000, -122.040200, 37.702000 )
+Gordon                        St       (0, 2, -122.040500, 37.752000, -122.040800, 37.748000 )
+Fremont                       Blvd     (0, 2, -122.039200, 37.746000, -122.039154, 37.745690 )
+Chaucer                       Dr       (0, 2, -122.035700, 37.794000, -122.036200, 37.788000 )
+Gainsborough                  Ter      (0, 2, -122.036900, 37.764000, -122.036900, 37.756000 )
+Darwin                        Dr       (0, 2, -122.035900, 37.763000, -122.036600, 37.755000 )
+Paseo Padre                   Pkwy     (0, 2, -122.041400, 37.734000, -122.044500, 37.708000 )
+Blackstone                    Way      (0, 2, -122.039300, 37.724000, -122.038800, 37.721000 )
+Salinas                       Pl       (0, 2, -122.041000, 37.689000, -122.040400, 37.686000 )
+Fielding                      Ct       (0, 2, -122.039300, 37.689000, -122.038400, 37.688000 )
+Fremont                       Blvd     (0, 2, -122.037300, 37.736000, -122.036100, 37.730000 )
+Caribbean Com                          (0, 2, -122.036100, 37.730000, -122.036600, 37.724000 )
+Bahama Com                             (0, 3, -122.036100, 37.720000, -122.036031, 37.720460 , -122.035800, 37.722000)
+Santee                        Road     (0, 2, -122.037100, 37.691000, -122.037600, 37.685000 )
+Rousillon                     Ave      (0, 2, -122.046000, 37.672000, -122.045100, 37.674000 )
+Decoto                        Road     (0, 2, -122.036100, 37.656000, -122.036400, 37.652000 )
+Edgewater                     Dr       (0, 2, -122.040100, 37.570000, -122.040500, 37.565000 )
+Edgewater                     Dr       (0, 2, -122.038700, 37.579000, -122.038315, 37.577850 )
+Severn                        Pl       (0, 2, -122.036300, 37.586000, -122.036700, 37.581000 )
+Scarborough                   Dr       (0, 2, -122.036200, 37.568000, -122.036200, 37.556000 )
+Haley                         St       (0, 2, -122.058500, 37.436000, -122.057900, 37.432000 )
+B                             St       (0, 2, -122.053100, 37.434000, -122.053700, 37.434000 )
+Normandy                      Dr       (0, 2, -122.050100, 37.457000, -122.050600, 37.451000 )
+Garrone                       Ave      (0, 2, -122.049800, 37.437000, -122.051300, 37.434000 )
+Jarvis                        Ave      (0, 2, -122.055000, 37.423000, -122.055200, 37.420000 )
+Spruce                        St       (0, 2, -122.056600, 37.368000, -122.056500, 37.361000 )
+D                             St       (0, 2, -122.052900, 37.421000, -122.053400, 37.420000 )
+Hermitage                     Ave      (0, 2, -122.054200, 37.384000, -122.056600, 37.368000 )
+Birkdale                      Dr       (0, 2, -122.051500, 37.373000, -122.052100, 37.368000 )
+Tozier                        St       (0, 2, -122.050200, 37.394000, -122.049700, 37.390000 )
+Fairway                       Ct       (0, 2, -122.053600, 37.334000, -122.053900, 37.330000 )
+Bridgepointe                  Dr       (0, 2, -122.051400, 37.305000, -122.050900, 37.299000 )
+Olympic                       Ct       (0, 2, -122.050900, 37.321000, -122.051800, 37.310000 )
+Mayhews Landing               Road     (0, 2, -122.048200, 37.299000, -122.048900, 37.295000 )
+Donegal                       Ct       (0, 2, -122.043300, 37.549000, -122.042100, 37.543000 )
+Chapman                       Dr       (0, 2, -122.042100, 37.504000, -122.041400, 37.498000 )
+Orleans                       Dr       (0, 2, -122.047300, 37.438000, -122.046300, 37.412000 )
+Breton                        Dr       (0, 2, -122.043500, 37.463000, -122.043000, 37.458000 )
+Cluny                         Pl       (0, 2, -122.043800, 37.432000, -122.043300, 37.432000 )
+Lake                          Blvd     (0, 2, -122.039300, 37.539000, -122.038700, 37.537000 )
+Lake                          Blvd     (0, 2, -122.038100, 37.533000, -122.037100, 37.530000 )
+Nelson                        Pl       (0, 2, -122.036600, 37.522000, -122.036800, 37.519000 )
+Ruschin                       Dr       (0, 2, -122.039700, 37.459000, -122.039300, 37.465000 )
+Madelaine                     Pl       (0, 2, -122.040100, 37.448000, -122.039500, 37.448000 )
+Newark                        Blvd     (0, 2, -122.035200, 37.438000, -122.034100, 37.423000 )
+Orleans                       Dr       (0, 2, -122.046000, 37.393000, -122.046400, 37.383000 )
+Cherry                        St       (0, 2, -122.043700, 37.420000, -122.043400, 37.413000 )
+Cherry                        St       (0, 2, -122.042900, 37.396000, -122.042400, 37.392000 )
+Haley                         St       (0, 2, -122.044400, 37.367000, -122.042309, 37.350740 )
+Bettencourt                   St       (0, 2, -122.047900, 37.340000, -122.047300, 37.337000 )
+Buckeye                       Pl       (0, 2, -122.044800, 37.336000, -122.045200, 37.332000 )
+Mayhews Landing               Road     (0, 2, -122.045800, 37.318000, -122.046800, 37.309000 )
+Mayhews Landing               Road     (0, 2, -122.041700, 37.346000, -122.042400, 37.341000 )
+Locust                        St       (0, 2, -122.043500, 37.315000, -122.043500, 37.312000 )
+Lafayette                     Ave      (0, 2, -122.039500, 37.411000, -122.041800, 37.398000 )
+Montcalm                      Ave      (0, 2, -122.039400, 37.373000, -122.040000, 37.369000 )
+Bellhaven                     Ave      (0, 2, -122.035400, 37.414000, -122.036400, 37.405000 )
+Christine                     St       (0, 2, -122.036400, 37.385000, -122.035700, 37.379000 )
+Magnolia                      St       (0, 2, -122.040900, 37.333000, -122.039400, 37.325000 )
+Ash                           St       (0, 2, -122.040800, 37.310000, -122.040000, 37.292000 )
+Mulberry                      St       (0, 2, -122.036300, 37.332000, -122.034000, 37.320000 )
+Magnolia                      St       (0, 2, -122.036100, 37.306000, -122.035400, 37.303000 )
+Darwin                        Dr       (0, 2, -122.033500, 37.776000, -122.034900, 37.767000 )
+Bates                         Dr       (0, 2, -122.032800, 37.748000, -122.032200, 37.745000 )
+Warwick                       Road     (0, 2, -122.029000, 37.777000, -122.029800, 37.768000 )
+Fremont                       Blvd     (0, 2, -122.034800, 37.724000, -122.031500, 37.707000 )
+Seal Rock                     Ter      (0, 3, -122.033374, 37.691860, -122.033500, 37.692000 , -122.033800, 37.694000)
+Decoto                        Road     (0, 2, -122.029200, 37.733000, -122.031500, 37.707000 )
+Ardo                          St       (0, 2, -122.029500, 37.682000, -122.030200, 37.674000 )
+Warwick                       Road     (0, 2, -122.027900, 37.791000, -122.028200, 37.787000 )
+Clover                        St       (0, 2, -122.023000, 37.805000, -122.021700, 37.801000 )
+Gawain                        Ct       (0, 2, -122.025100, 37.745000, -122.023740, 37.738200 )
+Cornish                       Dr       (0, 2, -122.022800, 37.750000, -122.022500, 37.754000 )
+Baldwin                       Pl       (0, 2, -122.027400, 37.697000, -122.026500, 37.692000 )
+Purcell                       Pl       (0, 2, -122.024900, 37.718000, -122.022400, 37.699000 )
+Cabral                        Dr       (0, 2, -122.034800, 37.648000, -122.035000, 37.643000 )
+Adriano                       St       (0, 2, -122.032000, 37.663000, -122.031200, 37.653000 )
+Cabrillo                      Dr       (0, 2, -122.032500, 37.637000, -122.031800, 37.633000 )
+Ardo                          St       (0, 2, -122.030600, 37.659000, -122.031200, 37.653000 )
+Ribera                        Ct       (0, 2, -122.029200, 37.644000, -122.028600, 37.641000 )
+Roca                          Dr       (0, 2, -122.033500, 37.609000, -122.031400, 37.599000 )
+Edgewater                     Dr       (0, 2, -122.032700, 37.552000, -122.031500, 37.546000 )
+Cabral                        Dr       (0, 2, -122.029400, 37.569000, -122.028800, 37.563000 )
+Mateo                         Ct       (0, 2, -122.026600, 37.659000, -122.027300, 37.650000 )
+Gibraltar                     Dr       (0, 2, -122.026000, 37.613000, -122.026476, 37.615220 )
+Colima                        Ct       (0, 2, -122.025100, 37.664000, -122.024500, 37.661000 )
+Cadiz                         Dr       (0, 2, -122.023900, 37.655000, -122.023500, 37.653000 )
+Nicolet                       Ct       (0, 2, -122.024200, 37.641000, -122.023600, 37.638000 )
+Wellington                    Pl       (0, 2, -122.024200, 37.628000, -122.022700, 37.632000 )
+Adobe                         Dr       (0, 2, -122.030400, 37.579000, -122.027800, 37.592000 )
+Nicolet                       Ave      (0, 2, -122.026400, 37.574000, -122.026900, 37.567000 )
+Gibraltar                     Dr       (0, 2, -122.024200, 37.603000, -122.025200, 37.608000 )
+Nicolet                       Ave      (0, 2, -122.023800, 37.615000, -122.023800, 37.610000 )
+Pizarro                       Dr       (0, 2, -122.025000, 37.556000, -122.026000, 37.544000 )
+Begonia                       St       (0, 2, -122.021800, 37.797000, -122.022000, 37.789000 )
+Blossom                       Ct       (0, 2, -122.021200, 37.772000, -122.021300, 37.769000 )
+Lilac                         Loop     (0, 2, -122.018200, 37.805000, -122.018400, 37.798000 )
+Alameda Creek                          (0, 2, -122.013600, 37.734000, -122.016500, 37.748000 )
+Tamayo                        St       (0, 2, -122.020100, 37.723000, -122.020400, 37.720000 )
+Dering                        Pl       (0, 2, -122.019200, 37.701000, -122.018300, 37.693000 )
+Tamayo                        St       (0, 2, -122.018400, 37.732000, -122.019000, 37.729000 )
+Augustine                     Pl       (0, 2, -122.016900, 37.732000, -122.016300, 37.725000 )
+Nicolet                       Ave      (0, 2, -122.017400, 37.696000, -122.018300, 37.693000 )
+Begonia                       St       (0, 2, -122.015300, 37.785000, -122.015600, 37.772000 )
+Dominici                      Dr       (0, 2, -122.014900, 37.756000, -122.015100, 37.750000 )
+Harrisburg                    Ave      (0, 2, -122.011000, 37.776000, -122.011300, 37.773000 )
+Isherwood                     Way      (0, 2, -122.013800, 37.733000, -122.015600, 37.702000 )
+Mackenzie                     Pl       (0, 2, -122.012500, 37.698000, -122.011500, 37.715000 )
+Gibraltar                     Dr       (0, 2, -122.020200, 37.624000, -122.020700, 37.619000 )
+Perkins                       St       (0, 2, -122.018500, 37.617000, -122.017700, 37.613000 )
+Alicante                      Dr       (0, 2, -122.021100, 37.587000, -122.019800, 37.582000 )
+Cambio                        Ct       (0, 2, -122.021700, 37.558000, -122.022600, 37.552000 )
+Theta                         St       (0, 2, -122.019700, 37.553000, -122.020000, 37.550000 )
+Polvorosa                     Ct       (0, 2, -122.017800, 37.594000, -122.017400, 37.591000 )
+Bedelio                       Ter      (0, 2, -122.019000, 37.579000, -122.018000, 37.574000 )
+Theta                         St       (0, 2, -122.018100, 37.565000, -122.018394, 37.563240 )
+Fenico                        Ter      (0, 2, -122.016600, 37.562000, -122.016800, 37.559000 )
+Alder                         Ct       (0, 2, -122.011700, 37.660000, -122.010900, 37.653000 )
+Dunbar                        Pl       (0, 2, -122.011000, 37.632000, -122.012100, 37.621000 )
+Allen                         Ct       (0, 2, -122.013100, 37.602000, -122.011700, 37.597000 )
+Dusterberry                   Way      (0, 2, -122.014100, 37.565000, -122.013500, 37.562000 )
+Thornton                      Ave      (0, 2, -122.012700, 37.583000, -122.013200, 37.575000 )
+Hebrides                      Ct       (0, 2, -122.034300, 37.529000, -122.034000, 37.531000 )
+Sandalwood                    St       (0, 2, -122.034700, 37.493000, -122.032700, 37.483000 )
+Edgewater                     Dr       (0, 2, -122.030600, 37.542000, -122.029800, 37.538000 )
+Nottingham                    Ct       (0, 2, -122.029700, 37.528000, -122.030300, 37.522000 )
+Falmouth                      Pl       (0, 2, -122.030100, 37.513000, -122.031000, 37.502000 )
+Somerset                      Pl       (0, 2, -122.029500, 37.490000, -122.029900, 37.485000 )
+Lafayette                     Ave      (0, 2, -122.033600, 37.451000, -122.035200, 37.438000 )
+Lafayette                     Ave      (0, 2, -122.030900, 37.474000, -122.032800, 37.458000 )
+Biddle                        Ave      (0, 2, -122.031700, 37.425000, -122.032900, 37.417000 )
+Edgewater                     Dr       (0, 2, -122.028500, 37.526000, -122.028611, 37.522950 )
+Enfield                       Dr       (0, 2, -122.027300, 37.519000, -122.027700, 37.507000 )
+Leon                          Ct       (0, 2, -122.024900, 37.525000, -122.025500, 37.521000 )
+Charles                       St       (0, 2, -122.025500, 37.505000, -122.025200, 37.499000 )
+Lakewood                      Dr       (0, 2, -122.028800, 37.480000, -122.026900, 37.477000 )
+Lakewood                      Ct       (0, 2, -122.026100, 37.472000, -122.026300, 37.469000 )
+Cedar                         Blvd     (0, 2, -122.028200, 37.446000, -122.026500, 37.430000 )
+Milani                        Ave      (0, 2, -122.023500, 37.438000, -122.024000, 37.432000 )
+Newark                        Blvd     (0, 2, -122.032900, 37.398000, -122.032000, 37.382000 )
+Bonnie                        St       (0, 2, -122.033200, 37.381000, -122.032400, 37.378000 )
+Fair                          Ave      (0, 2, -122.029800, 37.395000, -122.030400, 37.386000 )
+Thornton                      Ave      (0, 2, -122.029100, 37.379000, -122.030000, 37.370000 )
+Rich                          Ave      (0, 2, -122.035100, 37.309000, -122.035400, 37.303000 )
+Graham                        Ave      (0, 2, -122.031100, 37.321000, -122.032000, 37.310000 )
+Burdick                       St       (0, 2, -122.027300, 37.421000, -122.026600, 37.418000 )
+Birch                         St       (0, 2, -122.026900, 37.368000, -122.025400, 37.360000 )
+St Matthew                    Dr       (0, 2, -122.024000, 37.394000, -122.023400, 37.391000 )
+Civic Terrace                 Ave      (0, 2, -122.025100, 37.389000, -122.026300, 37.374000 )
+Sp Railroad                            (0, 2, -122.025700, 37.349000, -122.028900, 37.310000 )
+Cherry                        St       (0, 2, -122.026600, 37.297000, -122.025800, 37.294000 )
+Market                        Ave      (0, 2, -122.022900, 37.328000, -122.024400, 37.312000 )
+El Rey                        Ave      (0, 2, -122.021900, 37.536000, -122.022500, 37.530000 )
+Balboa                        Way      (0, 2, -122.020500, 37.519000, -122.020700, 37.517000 )
+Thornton                      Ave      (0, 2, -122.016400, 37.537000, -122.017500, 37.523000 )
+Thornton                      Ave      (0, 2, -122.021100, 37.477000, -122.021400, 37.473000 )
+Harlon                        Ct       (0, 2, -122.022300, 37.447000, -122.022900, 37.445000 )
+Edith                         St       (0, 2, -122.020000, 37.430000, -122.019700, 37.429000 )
+Blacow                        Road     (0, 2, -122.017900, 37.469000, -122.016700, 37.465000 )
+Contra Costa                  Ave      (0, 2, -122.015300, 37.551000, -122.014100, 37.545000 )
+Cabrillo                      Dr       (0, 2, -122.015300, 37.515000, -122.014400, 37.511000 )
+Rockwood                      Dr       (0, 2, -122.012800, 37.492000, -122.010900, 37.482000 )
+Hansen                        Ave      (0, 2, -122.012100, 37.523000, -122.012400, 37.519000 )
+Willowood                     Dr       (0, 2, -122.012600, 37.498000, -122.009600, 37.483000 )
+Carriage Circle Com                    (0, 2, -122.012800, 37.433000, -122.012900, 37.431000 )
+Driftwood                     Dr       (0, 2, -122.010900, 37.482000, -122.011300, 37.477000 )
+Central                       Ave      (0, 2, -122.011800, 37.455000, -122.012900, 37.443000 )
+Alta                          Dr       (0, 2, -122.010900, 37.424000, -122.010100, 37.419000 )
+Cedar                         Blvd     (0, 2, -122.021400, 37.402000, -122.019300, 37.391000 )
+Timber                        St       (0, 2, -122.017500, 37.380000, -122.013900, 37.362000 )
+Mistflower                    Ave      (0, 2, -122.019500, 37.329000, -122.019900, 37.323000 )
+Fuschia                       Ct       (0, 2, -122.020400, 37.325000, -122.019900, 37.323000 )
+Robertson                     Ave      (0, 2, -122.016400, 37.330000, -122.017300, 37.318000 )
+Central                       Ave      (0, 2, -122.014800, 37.415000, -122.015700, 37.404000 )
+Farwell                       Dr       (0, 3, -122.013300, 37.392000, -122.012500, 37.389000 , -122.011860, 37.385800)
+Brayton                       Ct       (0, 2, -122.012300, 37.423000, -122.011400, 37.418000 )
+Radele                        Ct       (0, 2, -122.010100, 37.363000, -122.010500, 37.357000 )
+Columbine                     Pl       (0, 2, -122.012200, 37.321000, -122.013000, 37.330000 )
+Granville                     Dr       (0, 2, -122.010500, 37.357000, -122.010000, 37.354000 )
+Bellflower                    Dr       (0, 2, -122.010300, 37.317000, -122.009979, 37.313870 )
+Willow                        St       (0, 2, -122.051900, 37.279000, -122.051700, 37.275000 )
+Peachtree                     Ave      (0, 2, -122.051100, 37.277000, -122.051700, 37.275000 )
+Laurel                        St       (0, 2, -122.048300, 37.265000, -122.047600, 37.251000 )
+Hickory                       St       (0, 2, -122.052400, 37.214000, -122.052300, 37.211000 )
+Peach                         Ct       (0, 2, -122.047700, 37.295000, -122.047300, 37.293000 )
+Juniper                       Ave      (0, 2, -122.046400, 37.255000, -122.047600, 37.251000 )
+Walnut                        St       (0, 2, -122.042500, 37.262000, -122.041700, 37.246000 )
+Elm                           St       (0, 2, -122.040000, 37.270000, -122.039200, 37.254000 )
+Ash                           St       (0, 2, -122.038400, 37.259000, -122.038800, 37.276000 )
+Hetch Hetchy Aqueduct                  (0, 2, -122.039000, 37.250000, -122.040400, 37.247000 )
+Sycamore                      St       (0, 2, -122.036100, 37.294000, -122.035400, 37.291000 )
+Filbert                       St       (0, 2, -122.036000, 37.259000, -122.035600, 37.252000 )
+Central                       Ave      (0, 2, -122.048700, 37.144000, -122.045200, 37.158000 )
+Sp Railroad                            (0, 2, -122.032100, 37.271000, -122.034700, 37.265000 )
+Sp Railroad                            (0, 2, -122.031500, 37.251000, -122.029200, 37.240000 )
+Central                       Ave      (0, 2, -122.030200, 37.226000, -122.032500, 37.196000 )
+Hetch Hetchy Aqueduct                  (0, 2, -122.025500, 37.283000, -122.024500, 37.286000 )
+Robertson                     Ave      (0, 2, -122.021000, 37.275000, -122.021300, 37.270000 )
+Hetch Hetchy Aqueduct                  (0, 2, -122.023300, 37.291000, -122.020432, 37.293880 )
+Manzanita                     St       (0, 2, -122.018800, 37.263000, -122.018200, 37.258000 )
+Jacaranda                     Dr       (0, 2, -122.014700, 37.288000, -122.013000, 37.287000 )
+Moores                        Ave      (0, 2, -122.013800, 37.237000, -122.014000, 37.233000 )
+Moores                        Ave      (0, 2, -122.009800, 37.287000, -122.010200, 37.281000 )
+Rockrose                      Dr       (0, 2, -122.010500, 37.248000, -122.011400, 37.252000 )
+Larkspur                      St       (0, 2, -122.011800, 37.231000, -122.011000, 37.227000 )
+Mowry                         Ave      (0, 2, -122.011300, 37.158000, -122.012900, 37.124000 )
+Surry                         Pl       (0, 2, -122.005200, 37.685000, -122.006000, 37.679000 )
+Vanda                         Way      (0, 2, -121.998900, 37.807000, -121.999300, 37.800000 )
+Carnation                     Way      (0, 2, -121.997500, 37.775000, -121.996000, 37.773000 )
+Atwater                       Ct       (0, 2, -122.007600, 37.662000, -122.008400, 37.654000 )
+Thornton                      Ave      (0, 2, -122.006800, 37.644000, -122.008300, 37.630000 )
+Thornton                      Ave      (0, 2, -122.003600, 37.671000, -122.004800, 37.660000 )
+Bridgewood                    Ter      (0, 2, -122.004200, 37.639000, -122.004700, 37.632000 )
+Bonde                         Way      (0, 2, -122.007700, 37.590000, -122.008400, 37.580000 )
+Baine                         Ave      (0, 2, -122.008900, 37.565000, -122.010400, 37.546000 )
+Peralta                       Blvd     (0, 2, -122.007300, 37.573000, -122.008200, 37.562000 )
+Meadowbrook Com                        (0, 2, -122.004300, 37.608000, -122.003600, 37.609000 )
+Fremont                       Blvd     (0, 2, -122.004300, 37.573000, -122.003800, 37.570000 )
+Paseo Padre                   Pkwy     (0, 2, -122.002100, 37.639000, -121.996000, 37.628000 )
+Peralta                       Blvd     (0, 2, -122.002200, 37.595000, -122.004500, 37.591000 )
+Sutton                        Loop     (0, 2, -121.999400, 37.586000, -121.998100, 37.576000 )
+Alton                         Ct       (0, 2, -121.997700, 37.581000, -121.998100, 37.576000 )
+Ames                          Ter      (0, 2, -121.996200, 37.763000, -121.995500, 37.762000 )
+Niles                         Blvd     (0, 2, -121.993300, 37.803000, -121.992900, 37.803000 )
+Rancho Arroyo                 Pkwy     (0, 3, -121.993200, 37.785000, -121.992900, 37.774000 , -121.992600, 37.769000)
+Nursery                       Ave      (0, 2, -121.989700, 37.802000, -121.990100, 37.795000 )
+Montecito                     Dr       (0, 2, -121.989900, 37.743000, -121.989200, 37.762000 )
+Martinez                      Dr       (0, 2, -121.984000, 37.793000, -121.984000, 37.796000 )
+Washburn                      Dr       (0, 2, -121.987700, 37.739000, -121.988100, 37.728000 )
+Bishop                        Ave      (0, 2, -121.991100, 37.635000, -121.992100, 37.632000 )
+Camden                        St       (0, 2, -121.995600, 37.603000, -121.995500, 37.598000 )
+Galsworthy                    Ct       (0, 2, -121.995000, 37.586000, -121.995500, 37.585000 )
+Canterbury                    St       (0, 2, -121.995000, 37.573000, -121.994800, 37.570000 )
+Dalton Com                             (0, 2, -121.994000, 37.555000, -121.994400, 37.556000 )
+Eggers                        Ct       (0, 2, -121.992400, 37.585000, -121.991000, 37.576000 )
+Camden                        St       (0, 2, -121.993200, 37.571000, -121.992000, 37.564000 )
+Davy                          Ct       (0, 2, -121.990200, 37.629000, -121.990000, 37.623000 )
+Archer                        Ave      (0, 2, -121.987900, 37.627000, -121.988800, 37.626000 )
+Peralta                       Blvd     (0, 2, -121.985600, 37.630000, -121.986700, 37.627000 )
+Ann                           St       (0, 2, -121.988800, 37.604000, -121.989400, 37.603000 )
+Paseo Padre                   Pkwy     (0, 2, -121.988500, 37.578000, -121.987700, 37.574000 )
+Craig                         St       (0, 2, -121.986900, 37.610000, -121.986400, 37.601000 )
+Central                       Ave      (0, 2, -122.006400, 37.524000, -122.007900, 37.505000 )
+Faulkner                      Dr       (0, 2, -122.003400, 37.530000, -122.004900, 37.515000 )
+Logan                         Ct       (0, 2, -122.005300, 37.492000, -122.006100, 37.484000 )
+Glendale                      Dr       (0, 2, -122.007400, 37.460000, -122.006700, 37.456000 )
+Westwood                      Ave      (0, 2, -122.007900, 37.454000, -122.008033, 37.450000 )
+Glenmoor                      Dr       (0, 2, -122.004600, 37.466000, -122.003800, 37.461000 )
+Rogers                        Ave      (0, 2, -122.004400, 37.430000, -122.006100, 37.409000 )
+Mattos                        Dr       (0, 2, -122.000200, 37.520000, -122.000500, 37.513000 )
+Mattos                        Dr       (0, 2, -122.000500, 37.502000, -122.000898, 37.496830 )
+Mattos                        Dr       (0, 2, -121.998300, 37.542000, -121.999200, 37.530000 )
+Dorsey                        Ave      (0, 2, -121.999200, 37.495000, -121.999700, 37.489000 )
+Eggers                        Dr       (0, 2, -122.000200, 37.465000, -122.001700, 37.451000 )
+Kerlin                        St       (0, 2, -121.998600, 37.469000, -121.997900, 37.465000 )
+Richmond                      Ave      (0, 2, -121.996900, 37.452000, -121.997500, 37.442000 )
+Paxton                        Ct       (0, 2, -122.009200, 37.388000, -122.010100, 37.378000 )
+Blacow                        Road     (0, 2, -122.006100, 37.409000, -122.005300, 37.405000 )
+Vernon                        Ave      (0, 2, -122.003900, 37.383000, -122.006000, 37.358000 )
+Moores                        Ave      (0, 2, -122.008700, 37.301000, -122.009400, 37.292000 )
+Burnside                      Ct       (0, 2, -122.006300, 37.345000, -122.006900, 37.338000 )
+Glenview                      Dr       (0, 2, -122.001000, 37.413000, -122.000200, 37.409000 )
+Mildred                       Ct       (0, 2, -122.000200, 37.388000, -121.999800, 37.386000 )
+Mildred                       Dr       (0, 2, -121.997300, 37.421000, -121.997700, 37.417000 )
+Donner                        Way      (0, 2, -121.996900, 37.370000, -121.994500, 37.357000 )
+Hetch Hetchy Aqueduct                  (0, 2, -122.000700, 37.313000, -122.000500, 37.313000 )
+Tyler                         Pl       (0, 2, -121.997300, 37.293000, -121.998600, 37.289000 )
+Fabian Com                             (0, 2, -121.995100, 37.537000, -121.994700, 37.534000 )
+Canfield                      Dr       (0, 2, -121.995200, 37.488000, -121.995500, 37.482000 )
+Lorren                        Dr       (0, 2, -121.991100, 37.491000, -121.992600, 37.473000 )
+Mildred                       Dr       (0, 2, -121.995800, 37.440000, -121.996400, 37.433000 )
+Argonaut                      Way      (0, 2, -121.993000, 37.475000, -121.992600, 37.473000 )
+Merol                         Ave      (0, 2, -121.992400, 37.453000, -121.991800, 37.450000 )
+Country                       Dr       (0, 2, -121.990300, 37.520000, -121.991600, 37.506000 )
+Pennsylvania                  Ave      (0, 2, -121.985400, 37.532000, -121.986200, 37.523000 )
+Mowry                         Ave      (0, 2, -121.989600, 37.459000, -121.990400, 37.449000 )
+Sacramento                    Ave      (0, 2, -121.986100, 37.440000, -121.986500, 37.436000 )
+Gertrude                      Dr       (0, 2, -121.996000, 37.398000, -121.996600, 37.390000 )
+Sutter                        Dr       (0, 2, -121.995100, 37.377000, -121.994300, 37.373000 )
+Logan                         Dr       (0, 2, -121.991300, 37.423000, -121.990341, 37.417630 )
+Calaveras                     Ave      (0, 2, -121.992400, 37.364000, -121.992700, 37.359000 )
+Poinciana                     Pl       (0, 2, -121.994600, 37.341000, -121.994000, 37.337000 )
+Royal Palm                    Dr       (0, 2, -121.994500, 37.315000, -121.992200, 37.303000 )
+Blacow                        Road     (0, 2, -121.990900, 37.330000, -121.989500, 37.324000 )
+Coco Palm                     Dr       (0, 2, -121.990500, 37.311000, -121.991000, 37.305000 )
+Pardee                        Ave      (0, 2, -121.988500, 37.367000, -121.989300, 37.359000 )
+Logan                         Dr       (0, 2, -121.986200, 37.390000, -121.985600, 37.384000 )
+Sutter                        Dr       (0, 2, -121.989300, 37.359000, -121.987000, 37.350000 )
+Banyan Tree                   Road     (0, 2, -121.990100, 37.317000, -121.987700, 37.304000 )
+Sundale                       Dr       (0, 2, -121.986800, 37.342000, -121.986700, 37.338000 )
+Cody                          Ct       (0, 2, -121.985300, 37.324000, -121.986000, 37.316000 )
+Mission                       Blvd     (0, 3, -121.982300, 37.791000, -121.976500, 37.788000 , -121.975100, 37.786000)
+2nd                           St       (0, 2, -121.982500, 37.768000, -121.980600, 37.763000 )
+2nd                           St       (0, 2, -121.978700, 37.760000, -121.977500, 37.757000 )
+J                             St       (0, 2, -121.976200, 37.754000, -121.976700, 37.743000 )
+Vallejo                       St       (0, 2, -121.971800, 37.765000, -121.971900, 37.769000 )
+Riverside                     Ave      (0, 2, -121.975800, 37.740000, -121.976400, 37.725000 )
+Alameda Creek                          (0, 2, -121.972400, 37.727000, -121.973800, 37.726000 )
+Gilbert                       Pl       (0, 2, -121.982300, 37.655000, -121.983600, 37.653000 )
+Shinn                         St       (0, 2, -121.981800, 37.640000, -121.981700, 37.636000 )
+Ridley                        Dr       (0, 2, -121.979400, 37.650000, -121.979000, 37.644000 )
+Burdette                      St       (0, 2, -121.980000, 37.620000, -121.980100, 37.626000 )
+Parkside                      Dr       (0, 3, -121.983600, 37.598000, -121.985100, 37.594000 , -121.986100, 37.592000)
+Cabot                         Ct       (0, 2, -121.984800, 37.583000, -121.983300, 37.583000 )
+Burdette                      St       (0, 2, -121.978900, 37.609000, -121.979500, 37.611000 )
+Mowry                         Ave      (0, 2, -121.980700, 37.567000, -121.980900, 37.565000 )
+Mowry                         Ave      (0, 2, -121.974500, 37.659000, -121.975803, 37.651550 )
+Bonner                        Ave      (0, 2, -121.974800, 37.635000, -121.976000, 37.631000 )
+Spence                        Ave      (0, 2, -121.973300, 37.616000, -121.972800, 37.619000 )
+Alameda Creek                          (0, 2, -121.969100, 37.748000, -121.970000, 37.730000 )
+Anita                         Ct       (0, 2, -121.965500, 37.744000, -121.965300, 37.738000 )
+Sp Railroad                            (0, 3, -121.968400, 37.715000, -121.966900, 37.701000 , -121.965500, 37.690000)
+Oliver                        Way      (0, 2, -121.966500, 37.739000, -121.966295, 37.736440 )
+Old Canyon                    Road     (0, 2, -121.965000, 37.794000, -121.963000, 37.800000 )
+Barton                        Dr       (0, 2, -121.965500, 37.744000, -121.964400, 37.749000 )
+Wasatch                       Dr       (0, 2, -121.962000, 37.752000, -121.962300, 37.756000 )
+Canyon Heights                Dr       (0, 2, -121.959500, 37.760000, -121.959600, 37.753000 )
+Pickering                     Ave      (0, 2, -121.964000, 37.698000, -121.963700, 37.700000 )
+Mission                       Blvd     (0, 2, -121.963200, 37.683000, -121.963150, 37.682500 )
+Chrisholm                     Pl       (0, 2, -121.959900, 37.726000, -121.959200, 37.732000 )
+Almaden                       Pl       (0, 2, -121.960300, 37.697000, -121.960100, 37.693000 )
+Yerba Buena                   Pl       (0, 2, -121.960200, 37.678000, -121.959500, 37.683000 )
+Spetti                        Dr       (0, 2, -121.968400, 37.665000, -121.969600, 37.658000 )
+Preston                       Ct       (0, 2, -121.966600, 37.659000, -121.965200, 37.668000 )
+Walnut                        Ave      (0, 2, -121.968900, 37.601000, -121.969899, 37.594340 )
+Overacker                     Ave      (0, 3, -121.961700, 37.652000, -121.961400, 37.648000 , -121.960700, 37.643000)
+Zacate                        Ave      (0, 2, -121.959400, 37.662000, -121.958900, 37.654000 )
+Mowry                         Ave      (0, 2, -121.982500, 37.547000, -121.983800, 37.532000 )
+Liberty                       St       (0, 2, -121.979500, 37.499000, -121.978500, 37.495000 )
+Walnut                        Ave      (0, 2, -121.980400, 37.471000, -121.981700, 37.456000 )
+Park Center                   Lane     (0, 2, -121.979300, 37.431000, -121.979800, 37.425000 )
+Twin Peaks                    Ter      (0, 2, -121.978500, 37.427000, -121.978300, 37.431000 )
+Sundale                       Dr       (0, 2, -121.976000, 37.481000, -121.977600, 37.464000 )
+Bidwell                       Dr       (0, 2, -121.974800, 37.448000, -121.976300, 37.427000 )
+Nelson                        St       (0, 2, -121.982200, 37.362000, -121.984600, 37.348000 )
+Placer                        Way      (0, 2, -121.978900, 37.415000, -121.976900, 37.407000 )
+Boone                         Dr       (0, 2, -121.982500, 37.329000, -121.982900, 37.324000 )
+Sundale                       Dr       (0, 2, -121.983600, 37.315000, -121.980900, 37.301000 )
+Selkirk                       St       (0, 2, -121.979800, 37.336000, -121.979600, 37.331000 )
+Natalie                       Ave      (0, 2, -121.979200, 37.307000, -121.979900, 37.296000 )
+Bidwell                       Dr       (0, 2, -121.976300, 37.422000, -121.976400, 37.420000 )
+Stevenson                     Blvd     (0, 2, -121.975800, 37.367000, -121.978200, 37.334000 )
+Urban                         St       (0, 2, -121.971400, 37.412000, -121.971000, 37.410000 )
+Blewett                       St       (0, 2, -121.973200, 37.373000, -121.973300, 37.369000 )
+Margery                       Dr       (0, 2, -121.976600, 37.338000, -121.977200, 37.330000 )
+Besco                         Dr       (0, 2, -121.976400, 37.320000, -121.976100, 37.311000 )
+Val                           St       (0, 2, -121.973900, 37.325000, -121.974600, 37.324000 )
+Laiolo                        Road     (0, 2, -121.971500, 37.322000, -121.970700, 37.318000 )
+Sailway                       Dr       (0, 2, -121.967300, 37.495000, -121.968600, 37.502000 )
+Michelle                      St       (0, 2, -121.969100, 37.461000, -121.968214, 37.457710 )
+Spady                         St       (0, 2, -121.968900, 37.424000, -121.969174, 37.417610 )
+Paseo Padre                   Pkwy     (0, 2, -121.964300, 37.462000, -121.961800, 37.449000 )
+Paseo Padre                   Pkwy     (0, 2, -121.959400, 37.433000, -121.956900, 37.419000 )
+Blanchard                     St       (0, 2, -121.972900, 37.380000, -121.970900, 37.383000 )
+Blanchard                     St       (0, 2, -121.970000, 37.382000, -121.969300, 37.382000 )
+Foster                        St       (0, 2, -121.968300, 37.367000, -121.966400, 37.358000 )
+Cosmic                        Way      (0, 2, -121.965900, 37.423000, -121.966600, 37.414000 )
+Eugene                        St       (0, 2, -121.965900, 37.364000, -121.966400, 37.358000 )
+Bullard                       St       (0, 2, -121.969400, 37.355000, -121.970000, 37.349000 )
+Robin                         St       (0, 2, -121.970100, 37.297000, -121.969800, 37.287000 )
+Grimmer                       Blvd     (0, 2, -121.967000, 37.332000, -121.968200, 37.315000 )
+Leslie                        St       (0, 2, -121.963900, 37.412000, -121.963400, 37.405000 )
+Grimmer                       Blvd     (0, 2, -121.962500, 37.394000, -121.963300, 37.383000 )
+Fremont                       Blvd     (0, 2, -121.964000, 37.362000, -121.963200, 37.357000 )
+Hetch Hetchy Aqueduct                  (0, 2, -121.961500, 37.407000, -121.960853, 37.408540 )
+High                          St       (0, 2, -121.960100, 37.381000, -121.959380, 37.377400 )
+Irvington                     Ave      (0, 2, -121.962400, 37.308000, -121.966100, 37.307000 )
+Bay                           St       (0, 2, -121.961100, 37.330000, -121.962700, 37.329000 )
+Washington                    Blvd     (0, 2, -121.955700, 37.328000, -121.958400, 37.330000 )
+Bitterroot                    Ave      (0, 2, -122.009100, 37.276000, -122.008700, 37.282000 )
+Mowry                         Ave      (0, 2, -122.007400, 37.245000, -122.008000, 37.236000 )
+Benecia                       Ave      (0, 2, -122.007700, 37.222000, -122.007600, 37.225000 )
+Ebbetts                       St       (0, 2, -122.006100, 37.205000, -122.005000, 37.203000 )
+Monterey                      Dr       (0, 2, -121.909595, 37.127320, -121.910168, 37.154440 )
+Farwell                       Dr       (0, 2, -121.996000, 37.291000, -121.995200, 37.287000 )
+Truman                        Pl       (0, 2, -121.993300, 37.285000, -121.993800, 37.279000 )
+Roosevelt                     Pl       (0, 2, -121.991800, 37.267000, -121.992300, 37.263000 )
+Joaquin Murieta               Ave      (0, 2, -121.995800, 37.208000, -121.996200, 37.201000 )
+Trade Wind                    Lane     (0, 2, -121.988700, 37.294000, -121.989100, 37.288000 )
+Lemke                         Pl       (0, 2, -121.988200, 37.261000, -121.988350, 37.259500 )
+Farwell                       Dr       (0, 2, -121.986700, 37.255000, -121.984100, 37.264000 )
+Parada                        St       (0, 2, -121.994900, 37.166000, -121.993100, 37.153000 )
+Mission                       Ct       (0, 2, -121.929057, 37.905950, -121.929602, 37.900630 )
+Hutton                        Ct       (0, 2, -121.982600, 37.274000, -121.982200, 37.272000 )
+Omar                          St       (0, 2, -121.982500, 37.255000, -121.983100, 37.259000 )
+Bach                          Ct       (0, 2, -121.977800, 37.295000, -121.976900, 37.292000 )
+Landon                        Ave      (0, 2, -121.978500, 37.258000, -121.978000, 37.255000 )
+Peony                         Ct       (0, 2, -121.979800, 37.204000, -121.980400, 37.198000 )
+Stewart                       Ave      (0, 2, -121.979700, 37.174000, -121.982100, 37.149000 )
+Curtis                        St       (0, 2, -121.976500, 37.246000, -121.977800, 37.229000 )
+Essex                         Way      (0, 2, -121.973500, 37.257000, -121.974900, 37.249000 )
+Allegro                       Ct       (0, 2, -121.975500, 37.201000, -121.976400, 37.201000 )
+Valpey Park                   Ave      (0, 2, -121.974800, 37.188000, -121.975600, 37.186000 )
+Omar                          St       (0, 2, -121.971900, 37.221000, -121.972400, 37.222000 )
+Mauna Loa Park                Dr       (0, 2, -121.972300, 37.191000, -121.972000, 37.196000 )
+Simm                          Ct       (0, 2, -121.975700, 37.150000, -121.974800, 37.153000 )
+Turban                        Ct       (0, 2, -121.973700, 37.141000, -121.972900, 37.143000 )
+Stratford                     Ave      (0, 2, -121.969600, 37.271000, -121.973200, 37.254000 )
+Wadsworth                     Ct       (0, 2, -121.970000, 37.249000, -121.970800, 37.241000 )
+Carol                         Ave      (0, 2, -121.965700, 37.270000, -121.967900, 37.268000 )
+Everglades Park               Dr       (0, 2, -121.970300, 37.175000, -121.971800, 37.164000 )
+Isle Royal                    St       (0, 3, -121.969500, 37.178000, -121.968600, 37.150000 , -121.968300, 37.149000)
+Bayfield                      Pl       (0, 2, -121.966500, 37.204000, -121.966400, 37.196000 )
+Chateau Park                  Ct       (0, 2, -121.965800, 37.196000, -121.966000, 37.193000 )
+Seneca Park                   Ave      (0, 2, -121.964900, 37.170000, -121.965300, 37.167000 )
+Stanley                       Ave      (0, 2, -121.962100, 37.242000, -121.963200, 37.240000 )
+Meiggs                        St       (0, 2, -121.959600, 37.267000, -121.959300, 37.255000 )
+Chetwood                      Ave      (0, 2, -121.959100, 37.232000, -121.960057, 37.230870 )
+Applewood                     St       (0, 2, -121.962900, 37.192000, -121.961600, 37.168000 )
+Michael                       Ave      (0, 2, -121.960500, 37.208000, -121.961000, 37.206000 )
+Wixon                         Dr       (0, 2, -121.961300, 37.199000, -121.960400, 37.180000 )
+Yellowstone Park              Dr       (0, 2, -121.968600, 37.111000, -121.970500, 37.117000 )
+Grandbrook Park               Ct       (0, 2, -121.966100, 37.136000, -121.965800, 37.140000 )
+Mayfair Park                  Ave      (0, 2, -121.963100, 37.148000, -121.962900, 37.143000 )
+Grimmer                       Blvd     (0, 2, -121.963600, 37.133000, -121.962341, 37.115770 )
+Crestwood                     St       (0, 2, -121.958900, 37.159000, -121.961000, 37.156000 )
+Delta                         Ter      (0, 2, -121.959200, 37.113000, -121.959000, 37.111000 )
+Durham                        Road     (0, 2, -121.963400, 37.081000, -121.964200, 37.078000 )
+Durham                        Road     (0, 2, -121.960500, 37.091000, -121.961600, 37.087000 )
+Christy                       St       (0, 2, -121.966200, 37.022000, -121.965800, 37.019000 )
+Sinbad Creek                           (0, 2, -121.949800, 37.613000, -121.955600, 37.643000 )
+Sunol Ridge                   Trl      (0, 2, -121.941900, 37.455000, -121.934500, 37.380000 )
+Fairbrook                     Ct       (0, 2, -121.920700, 37.829000, -121.921800, 37.826000 )
+Driftwood                     Way      (0, 2, -121.919000, 37.794000, -121.920000, 37.795000 )
+Muirwood                      Dr       (0, 2, -121.916200, 37.824000, -121.916800, 37.831000 )
+Lakewood                      St       (0, 2, -121.918900, 37.763000, -121.919100, 37.772000 )
+Bristolwood                   Road     (0, 2, -121.916500, 37.780000, -121.916400, 37.787000 )
+Las Positas                   Blvd     (0, 2, -121.915000, 37.769000, -121.918700, 37.759000 )
+Marigold                      Ct       (0, 2, -121.917700, 37.748000, -121.918700, 37.746000 )
+Oak Creek                     Ct       (0, 2, -121.915300, 37.737000, -121.917100, 37.735000 )
+Holland                       Dr       (0, 2, -121.912400, 37.814000, -121.912600, 37.821000 )
+Holland                       Dr       (0, 2, -121.911500, 37.798000, -121.911800, 37.801000 )
+Alamo Canal                            (0, 2, -121.911700, 37.768000, -121.911261, 37.756480 )
+Alma                          Ct       (0, 2, -121.908700, 37.799000, -121.908900, 37.814000 )
+Las Positas                   Blvd     (0, 2, -121.909400, 37.790000, -121.909900, 37.787000 )
+Pecan                         Ct       (0, 2, -121.912600, 37.743000, -121.914200, 37.739000 )
+Aster                         Ct       (0, 2, -121.912500, 37.720000, -121.912900, 37.719000 )
+Valley Trails                 Dr       (0, 2, -121.909800, 37.756000, -121.910000, 37.753000 )
+Valley Trails                 Dr       (0, 2, -121.908000, 37.739000, -121.907500, 37.739000 )
+Redwood                       Ct       (0, 2, -121.914200, 37.690000, -121.914400, 37.696000 )
+Prairie                       Dr       (0, 2, -121.912400, 37.667000, -121.912300, 37.664000 )
+Prarie                        Dr       (0, 2, -121.910200, 37.652000, -121.910115, 37.652260 )
+Foothill                      Road     (0, 2, -121.907500, 37.547000, -121.907696, 37.551410 )
+Sinbad Creek                           (0, 2, -121.917700, 37.343000, -121.920200, 37.366000 )
+Palomares                     Road     (0, 2, -121.950100, 37.318000, -121.948000, 37.304000 )
+Stony Brook                            (0, 2, -121.942900, 37.244000, -121.943200, 37.210000 )
+Palomares                     Road     (0, 2, -121.940400, 37.123000, -121.940700, 37.110000 )
+Alameda Creek                          (0, 2, -121.946600, 37.974000, -121.950300, 37.973000 )
+Sp Railroad                            (0, 2, -121.956500, 37.898000, -121.956200, 37.900000 )
+Niles Canyon                  Road     (0, 2, -121.935200, 37.912000, -121.934600, 37.910000 )
+Glenora                       Way      (0, 2, -121.914300, 37.317000, -121.914600, 37.316000 )
+Ruth Glen                              (0, 2, -121.912100, 37.281000, -121.912400, 37.278000 )
+Niles Canyon                  Road     (0, 2, -121.929200, 37.955000, -121.925931, 37.981500 )
+Niles Canyon                  Road     (0, 2, -121.933300, 37.913000, -121.931700, 37.919000 )
+Suddard                       Ct       (0, 2, -121.905700, 37.823000, -121.906400, 37.822000 )
+Valley Trails                 Dr       (0, 3, -121.906600, 37.776000, -121.907500, 37.775000 , -121.908400, 37.774000)
+Chabot Canal                           (0, 2, -121.902700, 37.804000, -121.902700, 37.812000 )
+Harpers Ferry                 Ct       (0, 2, -121.901900, 37.758000, -121.902271, 37.763300 )
+Bryce Canyon                  Ct       (0, 2, -121.900800, 37.780000, -121.901700, 37.780000 )
+Valley Trails                 Dr       (0, 2, -121.903800, 37.750000, -121.902900, 37.754000 )
+Acadia                        Ct       (0, 2, -121.900700, 37.773000, -121.901600, 37.768000 )
+Cheryl                        Cir      (0, 2, -121.897900, 37.800000, -121.895700, 37.794000 )
+Via de Los Milagros                    (0, 2, -121.898200, 37.746000, -121.897500, 37.743000 )
+Valley                        Dr       (0, 2, -121.896700, 37.761000, -121.898200, 37.746000 )
+Black                         Ave      (0, 2, -121.896400, 37.701000, -121.896700, 37.706000 )
+Marianas                               (0, 2, -122.244000, 37.327000, -122.244408, 37.323050 )
+Paseo Santa Cruz                       (0, 2, -121.905200, 37.650000, -121.904000, 37.650000 )
+Corte Vera Cruz                        (0, 2, -121.903600, 37.639000, -121.903800, 37.642000 )
+Arroyo de la Laguna                    (0, 2, -121.906400, 37.612000, -121.904700, 37.551000 )
+Calle Morelia                          (0, 2, -121.900300, 37.684000, -121.900600, 37.691000 )
+Hansen                        Dr       (0, 2, -121.898400, 37.700000, -121.898897, 37.697740 )
+Calle Altamira                         (0, 2, -121.899400, 37.669000, -121.898800, 37.656000 )
+Valley                        Ave      (0, 3, -121.896200, 37.644000, -121.896320, 37.608600 , -121.896359, 37.597160)
+Parkside                      Dr       (0, 2, -121.892500, 37.806000, -121.891600, 37.809000 )
+Valley                        Ave      (0, 3, -121.887000, 37.767000, -121.887800, 37.767000 , -121.888800, 37.767000)
+Via Peralta                            (0, 2, -121.894100, 37.729000, -121.893800, 37.726000 )
+Goldcrest                     Cir      (0, 2, -121.890900, 37.725000, -121.891700, 37.730000 )
+Black                         Ave      (0, 2, -121.890900, 37.704000, -121.892000, 37.698000 )
+Blackbird                     Way      (0, 3, -121.886700, 37.801000, -121.887600, 37.801000 , -121.888200, 37.801000)
+Hummingbird                   Road     (0, 2, -121.886200, 37.774000, -121.887100, 37.774000 )
+Minivet                       Ct       (0, 2, -121.883400, 37.805000, -121.883800, 37.811000 )
+Crestline                     Road     (0, 2, -121.887000, 37.789000, -121.884600, 37.793000 )
+Hummingbird                   Road     (0, 2, -121.882400, 37.777000, -121.885300, 37.773000 )
+Northway                      Road     (0, 2, -121.885700, 37.748000, -121.887100, 37.750000 )
+Elmridge                      Ct       (0, 2, -121.886700, 37.707000, -121.887100, 37.702000 )
+Beachwood                     Way      (0, 2, -121.881700, 37.731000, -121.880700, 37.731000 )
+Black                         Ave      (0, 2, -121.881600, 37.721000, -121.882600, 37.721000 )
+Paseo del Cajon                        (0, 2, -121.890100, 37.699000, -121.889101, 37.684110 )
+Greentree                     Ct       (0, 2, -121.885100, 37.696000, -121.885500, 37.692000 )
+Hopyard                       Road     (0, 2, -121.882800, 37.674000, -121.883300, 37.678000 )
+Rose                          Ave      (0, 2, -121.879200, 37.616000, -121.883800, 37.656000 )
+Arroyo                        Dr       (0, 2, -121.904900, 37.509000, -121.902900, 37.516000 )
+Longview                      Dr       (0, 2, -121.904000, 37.486000, -121.904700, 37.482000 )
+Foothill                      Road     (0, 2, -121.895600, 37.413000, -121.896700, 37.419000 )
+Greens                        Lane     (0, 2, -121.893800, 37.339000, -121.894000, 37.311000 )
+Castlewood                    Dr       (0, 2, -121.889500, 37.376000, -121.890200, 37.373000 )
+Pleasanton Sunol              Road     (0, 2, -121.885100, 37.390000, -121.885800, 37.387000 )
+Foxswallow                    Road     (0, 2, -121.877600, 37.793000, -121.879600, 37.794000 )
+Mohr                          Ave      (0, 3, -121.877000, 37.819000, -121.879300, 37.816000 , -121.879900, 37.819000)
+Greenwood                     Road     (0, 2, -121.879500, 37.745000, -121.879600, 37.766000 )
+Greenwood                     Road     (0, 2, -121.879500, 37.705000, -121.879500, 37.721000 )
+Alameda                       Dr       (0, 2, -121.875600, 37.746000, -121.876300, 37.746000 )
+Mairmont                      Dr       (0, 2, -121.872500, 37.800000, -121.873400, 37.800000 )
+Crisfield                     Lane     (0, 2, -121.871000, 37.814000, -121.871800, 37.813000 )
+Kolln                         St       (0, 2, -121.870000, 37.792000, -121.870500, 37.796000 )
+Cedarwood                     Lane     (0, 2, -121.874600, 37.706000, -121.874600, 37.721000 )
+Francisco                     St       (0, 2, -121.868700, 37.749000, -121.869600, 37.750000 )
+Harvest                       Road     (0, 2, -121.879700, 37.687000, -121.880700, 37.691000 )
+Wp Railroad                            (0, 2, -121.874759, 37.657280, -121.872700, 37.665000 )
+St Johns                      St       (0, 2, -121.876400, 37.643000, -121.877700, 37.646000 )
+Angela                        St       (0, 2, -121.879500, 37.607000, -121.879800, 37.608000 )
+Pleasanton                    Ave      (0, 2, -121.878200, 37.636000, -121.878400, 37.630000 )
+Peters                        Ave      (0, 2, -121.876900, 37.600000, -121.876300, 37.608000 )
+Main                          St       (0, 2, -121.876900, 37.571000, -121.876403, 37.579070 )
+Arroyo del Valle                       (0, 2, -121.875100, 37.656000, -121.873100, 37.646000 )
+Stanley                       Blvd     (0, 2, -121.870500, 37.659000, -121.871000, 37.660000 )
+Main                          St       (0, 2, -121.874300, 37.615000, -121.873900, 37.624000 )
+Main                          St       (0, 2, -121.875400, 37.595000, -121.875000, 37.604000 )
+1st                           St       (0, 2, -121.872800, 37.596000, -121.873800, 37.587000 )
+Spring                        St       (0, 2, -121.870200, 37.620000, -121.871000, 37.621000 )
+Arendt                        Way      (0, 2, -121.871700, 37.606000, -121.871000, 37.602000 )
+Neal                          St       (0, 2, -121.869200, 37.577000, -121.868300, 37.574000 )
+Orloff                        Dr       (0, 2, -121.867700, 37.768000, -121.866900, 37.768000 )
+Valley                        Ave      (0, 2, -121.867300, 37.761000, -121.867700, 37.760000 )
+Orloff                        Dr       (0, 2, -121.865900, 37.767000, -121.865000, 37.765000 )
+Valley                        Ave      (0, 2, -121.859100, 37.733000, -121.862800, 37.760000 )
+Sp Railroad                            (0, 2, -121.859100, 37.701000, -121.858600, 37.705000 )
+Sp Railroad                            (0, 2, -121.869900, 37.631000, -121.867800, 37.651000 )
+Birch Creek                   Dr       (0, 2, -121.864100, 37.629000, -121.864200, 37.640000 )
+Christina                     Ct       (0, 2, -121.865400, 37.629000, -121.865100, 37.616000 )
+Cabernet                      Ct       (0, 2, -121.863600, 37.593000, -121.864100, 37.582000 )
+Washington                    St       (0, 2, -121.861500, 37.684000, -121.859600, 37.694000 )
+Utah                          St       (0, 2, -121.859100, 37.680000, -121.858400, 37.682000 )
+Mavis                         Dr       (0, 2, -121.860000, 37.628000, -121.859800, 37.606000 )
+Kottinger                     Dr       (0, 2, -121.859600, 37.580000, -121.859000, 37.570000 )
+Mavis                         Ct       (0, 2, -121.858400, 37.606000, -121.857700, 37.603000 )
+Mission                       Dr       (0, 2, -121.879000, 37.526000, -121.878800, 37.526000 )
+Monaco                        Ct       (0, 2, -121.876200, 37.544000, -121.876600, 37.538000 )
+Sonoma                        Dr       (0, 2, -121.879600, 37.487000, -121.879900, 37.488000 )
+San Luis                      Ct       (0, 2, -121.876800, 37.483000, -121.876600, 37.474000 )
+Dolores                       Dr       (0, 2, -121.873200, 37.524000, -121.872400, 37.519000 )
+Puerto Vallarta                        (0, 2, -121.871900, 37.540000, -121.872800, 37.528000 )
+Windmill                      Lane     (0, 2, -121.868800, 37.531000, -121.867800, 37.532000 )
+Sunol                         Blvd     (0, 2, -121.880500, 37.447000, -121.880700, 37.422000 )
+Arlington                     Dr       (0, 2, -121.880200, 37.408000, -121.880700, 37.394000 )
+Matthew                       Ct       (0, 2, -121.880700, 37.394000, -121.878300, 37.395000 )
+Blossom                       Ct       (0, 2, -121.876600, 37.395000, -121.876493, 37.394690 )
+Alisal                        St       (0, 2, -121.870000, 37.382000, -121.869500, 37.350000 )
+Linda                         Way      (0, 2, -121.865700, 37.573000, -121.866000, 37.566000 )
+Independence                  Dr       (0, 2, -121.868300, 37.521000, -121.868866, 37.507630 )
+Mirador                       Ct       (0, 2, -121.864400, 37.556000, -121.863300, 37.552000 )
+Ridge                         Trl      (0, 2, -121.861500, 37.438000, -121.859211, 37.428990 )
+South                         Road     (0, 2, -121.894100, 37.283000, -121.894000, 37.279000 )
+Niles Canyon                  Road     (0, 2, -121.909000, 37.940000, -121.903400, 37.957000 )
+Niles Canyon                  Road     (0, 2, -121.895400, 37.940000, -121.894960, 37.939630 )
+Railroad                      Ave      (0, 2, -121.891000, 37.940000, -121.892400, 37.941000 )
+Main                          St       (0, 2, -121.887500, 37.939000, -121.890700, 37.936000 )
+Wp Railroad                            (0, 2, -121.886100, 37.940000, -121.884700, 37.952000 )
+Wp Railroad                            (0, 3, -121.880400, 37.270000, -121.880600, 37.272000 , -121.886780, 37.365280)
+Carver                        Lane     (0, 2, -121.877000, 37.057000, -121.881100, 37.068000 )
+Pleasanton Sunol              Road     (0, 2, -121.876500, 37.987000, -121.877500, 37.982000 )
+Vallecitos                    Road     (0, 2, -121.869900, 37.916000, -121.870300, 37.891000 )
+Vallecitos                    Road     (0, 2, -121.863100, 37.951000, -121.868000, 37.930000 )
+Pickering                     Ave      (0, 2, -121.959600, 37.722000, -121.958868, 37.726180 )
+Maar                          Ave      (0, 2, -121.957300, 37.693000, -121.956500, 37.701000 )
+Zacate                        Ave      (0, 2, -121.957500, 37.646000, -121.956700, 37.640000 )
+Canyon Heights                Dr       (0, 2, -121.951300, 37.618000, -121.952800, 37.627000 )
+Mission                       Blvd     (0, 2, -121.956200, 37.607000, -121.954800, 37.598000 )
+San Moreno                    Ct       (0, 2, -121.954000, 37.576000, -121.953200, 37.558000 )
+Hidalgo                       Ct       (0, 2, -121.950500, 37.614000, -121.951200, 37.608000 )
+Santa Teresa Com                       (0, 2, -121.950500, 37.564000, -121.949600, 37.559000 )
+Segovia                       Pl       (0, 2, -121.947800, 37.596000, -121.948900, 37.585000 )
+Loro                          Pl       (0, 2, -121.944700, 37.577000, -121.945800, 37.582000 )
+Canyon Heights                Dr       (0, 2, -121.943100, 37.568000, -121.943300, 37.571000 )
+Las Palmas                    Ave      (0, 2, -121.952100, 37.547000, -121.951300, 37.539000 )
+Norbridge                     Ave      (0, 2, -122.084000, 37.911000, -122.082900, 37.911000 )
+Vaca                          Dr       (0, 2, -121.953100, 37.448000, -121.953100, 37.442000 )
+Seville                       Pl       (0, 2, -121.951400, 37.522000, -121.952200, 37.518000 )
+Quintana                      Way      (0, 2, -121.950400, 37.490000, -121.950800, 37.489000 )
+Dalgo                         Road     (0, 2, -121.947000, 37.529000, -121.947500, 37.524000 )
+Quintana                      Ct       (0, 2, -121.951600, 37.464000, -121.952200, 37.463000 )
+Ambar                         Pl       (0, 2, -121.949400, 37.482000, -121.947900, 37.474000 )
+Marabu                        Way      (0, 2, -121.950300, 37.454000, -121.950800, 37.450000 )
+Camero                        Way      (0, 2, -121.948100, 37.459000, -121.948800, 37.456000 )
+Camero                        Pl       (0, 2, -121.946100, 37.463000, -121.945300, 37.454000 )
+Durillo                       Dr       (0, 2, -121.946400, 37.446000, -121.945700, 37.441000 )
+Hancock                       Dr       (0, 2, -121.956900, 37.419000, -121.957000, 37.413000 )
+Wolcott                       Dr       (0, 2, -121.956800, 37.393000, -121.956400, 37.389000 )
+Rodney Com                             (0, 2, -121.956200, 37.385000, -121.955500, 37.382000 )
+Union                         St       (0, 2, -121.957800, 37.334000, -121.958400, 37.330000 )
+Roberts                       Ave      (0, 2, -121.955400, 37.299000, -121.955000, 37.282000 )
+Timbercreek                   Ter      (0, 2, -121.952700, 37.351000, -121.952496, 37.350390 )
+Washington                    Blvd     (0, 2, -121.951200, 37.335000, -121.952200, 37.335000 )
+Bairo                         Ct       (0, 2, -121.950500, 37.398000, -121.949800, 37.393000 )
+Corriea                       Way      (0, 2, -121.950100, 37.402000, -121.950500, 37.398000 )
+Carmen                        St       (0, 2, -121.950100, 37.380000, -121.948400, 37.369000 )
+Driscoll                      Road     (0, 2, -121.948200, 37.403000, -121.948451, 37.399950 )
+Apricot                       Lane     (0, 2, -121.947100, 37.401000, -121.945600, 37.392000 )
+Denise                        St       (0, 2, -121.946900, 37.370000, -121.945000, 37.359000 )
+Olive                         Ave      (0, 2, -121.945800, 37.340000, -121.949300, 37.337000 )
+Mission                       Blvd     (0, 2, -121.944900, 37.536000, -121.943800, 37.530000 )
+Las Palmas                    Ct       (0, 2, -121.944000, 37.502000, -121.944300, 37.513000 )
+Jacaranda                     Ct       (0, 2, -121.943100, 37.490000, -121.942600, 37.486000 )
+Mission                       Blvd     (0, 2, -121.941800, 37.517000, -121.938900, 37.499000 )
+Chiltern                      Dr       (0, 2, -121.944700, 37.457000, -121.944200, 37.454000 )
+Hetch Hetchy Aqueduct                  (0, 2, -121.946500, 37.448000, -121.945888, 37.449600 )
+Austin                        St       (0, 2, -121.943000, 37.422000, -121.943500, 37.425000 )
+Chiltern                      Dr       (0, 2, -121.941400, 37.433000, -121.941200, 37.424000 )
+St Anthony                    Dr       (0, 2, -121.939000, 37.474000, -121.938500, 37.471000 )
+Plymouth                      Ave      (0, 2, -121.936900, 37.422000, -121.937800, 37.424000 )
+Msn Creek                     Dr       (0, 2, -121.935400, 37.428000, -121.934400, 37.427000 )
+Dorne                         Pl       (0, 2, -121.945500, 37.397000, -121.945600, 37.392000 )
+Higgins                       Way      (0, 2, -121.943300, 37.392000, -121.942400, 37.392000 )
+Dayle                         Ct       (0, 2, -121.943000, 37.370000, -121.942100, 37.369000 )
+Lockwood                      Ave      (0, 2, -121.940100, 37.366000, -121.940800, 37.364000 )
+Bruce                         Dr       (0, 2, -121.944200, 37.309000, -121.945000, 37.312000 )
+Middlefield                   Ave      (0, 2, -121.944200, 37.309000, -121.942600, 37.311000 )
+Washington                    Blvd     (0, 2, -121.941800, 37.314000, -121.942200, 37.314000 )
+Castillejo                    Road     (0, 2, -121.939700, 37.313000, -121.939600, 37.304000 )
+Wisteria                      Dr       (0, 2, -121.936900, 37.398000, -121.936900, 37.391000 )
+Wisteria                      Dr       (0, 2, -121.936200, 37.373000, -121.936300, 37.367000 )
+Bedford                       St       (0, 2, -121.933300, 37.403000, -121.933800, 37.402000 )
+Jackson                       Ct       (0, 2, -121.937700, 37.336000, -121.938300, 37.337000 )
+Paseo Padre                   Pkwy     (0, 2, -121.935700, 37.342000, -121.932700, 37.318000 )
+Mission Creek                          (0, 2, -121.924400, 37.614000, -121.923800, 37.608000 )
+Camino Santa Barbara                   (0, 2, -121.931400, 37.446000, -121.930300, 37.436000 )
+Olive                         Ave      (0, 2, -121.932600, 37.360000, -121.933300, 37.356000 )
+Palm                          Ave      (0, 2, -121.932000, 37.329000, -121.931900, 37.319000 )
+Poda                          Ct       (0, 2, -121.932100, 37.308000, -121.931500, 37.312000 )
+Emerson                       St       (0, 2, -121.927600, 37.345000, -121.928400, 37.347000 )
+Gallegos                      Ave      (0, 2, -121.928600, 37.324000, -121.928500, 37.318000 )
+Nandina                       Ct       (0, 2, -121.927400, 37.299000, -121.928100, 37.297000 )
+Starr                         Ct       (0, 2, -121.924900, 37.373000, -121.925140, 37.372200 )
+Starr                         Ct       (0, 2, -121.923800, 37.371000, -121.924600, 37.373000 )
+Loma                          Dr       (0, 2, -121.920200, 37.390000, -121.920400, 37.394000 )
+Starr                         St       (0, 2, -121.921300, 37.378000, -121.921600, 37.376000 )
+Coit                          Ave      (0, 2, -121.924400, 37.346000, -121.924400, 37.338000 )
+Jerome                        Ave      (0, 2, -121.924700, 37.314000, -121.924400, 37.309000 )
+Bryant                        St       (0, 2, -121.921600, 37.321000, -121.921300, 37.316000 )
+Hetch Hetchy Aqueduct                  (0, 2, -121.935500, 37.477000, -121.916900, 37.530000 )
+Pico                          Road     (0, 2, -121.916500, 37.524000, -121.917700, 37.505000 )
+Vargas                        Road     (0, 2, -121.916100, 37.512000, -121.916500, 37.504000 )
+Linmore                       Dr       (0, 2, -121.920200, 37.390000, -121.919100, 37.388000 )
+Panorama                      Trl      (0, 2, -121.914800, 37.366000, -121.914100, 37.356000 )
+Anza                          St       (0, 2, -121.918400, 37.306000, -121.919700, 37.304000 )
+Panorama                      Trl      (0, 2, -121.909600, 37.370000, -121.909800, 37.371000 )
+Hochler                       Dr       (0, 2, -121.911100, 37.317000, -121.909900, 37.305000 )
+Carol                         Ave      (0, 2, -121.957400, 37.279000, -121.959000, 37.277000 )
+Carol                         Ave      (0, 2, -121.953700, 37.283000, -121.955000, 37.282000 )
+Kay                           Ct       (0, 2, -121.953000, 37.277000, -121.953500, 37.277000 )
+Ronald                        Ct       (0, 2, -121.952800, 37.259000, -121.953700, 37.259000 )
+Woodcrest                     Dr       (0, 2, -121.957900, 37.225000, -121.958900, 37.224000 )
+Gatewood                      St       (0, 2, -121.957400, 37.185000, -121.957300, 37.178000 )
+Steuben                       Ct       (0, 2, -121.954700, 37.227000, -121.955300, 37.225000 )
+Fairwood                      St       (0, 2, -121.953900, 37.183000, -121.957300, 37.178000 )
+Howe                          Ct       (0, 2, -121.951400, 37.252000, -121.954400, 37.250000 )
+Osgood                        Road     (0, 2, -121.949000, 37.262000, -121.946000, 37.208000 )
+Delaware                      Dr       (0, 2, -121.951000, 37.203000, -121.951700, 37.201000 )
+Yorktown                      Road     (0, 2, -121.950700, 37.173000, -121.951300, 37.172000 )
+Independence                  Road     (0, 2, -121.947400, 37.172000, -121.950800, 37.163000 )
+Doane                         St       (0, 2, -121.958100, 37.135000, -121.959700, 37.130000 )
+Southlake Com                          (0, 2, -121.957200, 37.113000, -121.956500, 37.116000 )
+Doane                         St       (0, 2, -121.953100, 37.149000, -121.956200, 37.140000 )
+Technology                    Dr       (0, 2, -121.953900, 37.109000, -121.953600, 37.099000 )
+Grimmer                       Blvd     (0, 2, -121.955500, 37.054000, -121.953400, 37.050000 )
+Budwing                       Ter      (0, 2, -121.951600, 37.136000, -121.951826, 37.135550 )
+Masonic                       Ter      (0, 2, -121.951200, 37.123000, -121.950800, 37.124000 )
+Jamestown                     Road     (0, 2, -121.947000, 37.165000, -121.947600, 37.163000 )
+Old Warm Springs              Blvd     (0, 2, -121.948200, 37.092000, -121.947300, 37.084000 )
+Sabercat                      Road     (0, 3, -121.945300, 37.266000, -121.944385, 37.251560 , -121.940800, 37.195000)
+Sabercat                      Road     (0, 3, -121.939600, 37.176000, -121.938800, 37.165000 , -121.937000, 37.159000)
+Luzon                         Ct       (0, 2, -121.932800, 37.280000, -121.932100, 37.279000 )
+Yale                          Way      (0, 2, -121.944300, 37.098000, -121.948200, 37.092000 )
+Edison                        Way      (0, 2, -121.944300, 37.098000, -121.947300, 37.084000 )
+Sparrow                       Dr       (0, 2, -121.933100, 37.139000, -121.932800, 37.134000 )
+Osgood                        Road     (0, 2, -121.937100, 37.071000, -121.936300, 37.060000 )
+Boxwood                       Way      (0, 2, -121.932900, 37.094000, -121.933500, 37.096000 )
+Enterprise                    Pl       (0, 2, -121.956800, 37.009000, -121.955800, 37.002000 )
+Noria                         Ct       (0, 2, -121.931900, 37.288000, -121.932400, 37.286000 )
+Ocaso Camino                           (0, 2, -121.929300, 37.261000, -121.930300, 37.252000 )
+Jerome                        Ave      (0, 2, -121.923900, 37.294000, -121.923900, 37.287000 )
+Bautista                      St       (0, 2, -121.922700, 37.290000, -121.922500, 37.284000 )
+Pine                          St       (0, 2, -121.921600, 37.243000, -121.922600, 37.235000 )
+Paseo Padre                   Pkwy     (0, 2, -121.930200, 37.137000, -121.929800, 37.146000 )
+Parkmeadow                    Dr       (0, 2, -121.932400, 37.099000, -121.931700, 37.104000 )
+Sioux                         Dr       (0, 2, -121.928000, 37.142000, -121.926147, 37.143740 )
+Rosemary                      Ct       (0, 2, -121.931400, 37.088000, -121.931400, 37.085000 )
+Parkmeadow                    Dr       (0, 2, -121.931900, 37.062000, -121.932200, 37.066000 )
+Onondaga                      Dr       (0, 2, -121.931200, 37.054000, -121.930000, 37.058000 )
+Paseo Padre                   Pkwy     (0, 2, -121.929200, 37.083000, -121.929500, 37.088000 )
+Onondaga                      Dr       (0, 2, -121.928800, 37.054000, -121.928200, 37.048000 )
+Durham                        Road     (0, 2, -121.921600, 37.146000, -121.923400, 37.149000 )
+Washo                         Dr       (0, 2, -121.921300, 37.117000, -121.920400, 37.114000 )
+Potawatami                    Dr       (0, 2, -121.923800, 37.063000, -121.924000, 37.056000 )
+Little Foot                   Dr       (0, 2, -121.922300, 37.064000, -121.922800, 37.064000 )
+Omak                          St       (0, 2, -121.920900, 37.064000, -121.920900, 37.071000 )
+Cedar                         St       (0, 2, -121.918800, 37.277000, -121.920000, 37.276000 )
+Nansa                         Ct       (0, 2, -121.918800, 37.259000, -121.918711, 37.254530 )
+Olazaba                       Ter      (0, 2, -121.917600, 37.247000, -121.917664, 37.243230 )
+Crystaline                    Dr       (0, 2, -121.925856, 37.000000, -121.925869, 37.005270 )
+Vineyard                      Road     (0, 2, -121.919000, 37.056000, -121.912800, 37.069000 )
+Sodaville                     Ct       (0, 2, -121.926600, 37.029000, -121.927800, 37.033000 )
+Cayuga                        Way      (0, 2, -121.923300, 37.013000, -121.923900, 37.008000 )
+Cayuga                        Pl       (0, 2, -121.920500, 37.023000, -121.921800, 37.020000 )
+Paseo Padre                   Pkwy     (0, 2, -121.918800, 37.004000, -121.917700, 37.004000 )
+Paseo Padre                   Pkwy     (0, 2, -121.914300, 37.005000, -121.913522, 37.000000 )
+Andrade                       Road     (0, 2, -121.884200, 37.741000, -121.884100, 37.738000 )
+Andrade                       Road     (0, 2, -121.885300, 37.565000, -121.885500, 37.564000 )
+Panorama                      Trl      (0, 2, -121.902300, 37.342000, -121.899500, 37.303000 )
+Via Granada                            (0, 2, -122.121893, 37.823930, -122.121700, 37.822000 )
+San Antonio Creek                      (0, 2, -121.872200, 37.759000, -121.864100, 37.771000 )
+Calaveras Creek                        (0, 2, -121.863700, 37.611000, -121.862800, 37.587000 )
+Panorama                      Trl      (0, 2, -121.899600, 37.291000, -121.909800, 37.262000 )
+Saguare Com                            (0, 2, -121.904900, 37.022000, -121.904300, 37.017000 )
+Mohr                          Ave      (0, 2, -121.849500, 37.819000, -121.861800, 37.818000 )
+Oneil                         Ave      (0, 2, -122.076754, 37.624760, -122.074500, 37.595000 )
+Touriga                       Dr       (0, 2, -121.851200, 37.644000, -121.851100, 37.648000 )
+Concord                       St       (0, 2, -121.855100, 37.602000, -121.856000, 37.593000 )
+Palomino                      Dr       (0, 2, -121.853600, 37.590000, -121.852000, 37.589000 )
+Touriga                       Dr       (0, 2, -121.851200, 37.611000, -121.851283, 37.608930 )
+Arbor                         Dr       (0, 2, -121.850600, 37.576000, -121.852100, 37.578000 )
+Vineyard                      Ave      (0, 2, -121.846300, 37.647000, -121.846448, 37.647110 )
+Sylvaner                      Dr       (0, 2, -121.847500, 37.610000, -121.848200, 37.607000 )
+Oneil                         Ave      (0, 2, -122.073989, 37.588600, -122.073631, 37.584130 )
+Petronave                     Dr       (0, 2, -121.834400, 37.469000, -121.837510, 37.557380 )
+Touriga                       Dr       (0, 2, -121.852100, 37.566000, -121.852300, 37.559000 )
+Wp Railroad                            (0, 2, -121.834600, 37.733000, -121.825900, 37.743000 )
+Sp Railroad                            (0, 2, -121.820400, 37.746000, -121.813900, 37.753000 )
+Arroyo del Valle                       (0, 2, -121.804900, 37.539000, -121.785600, 37.463000 )
+Vallecitos Creek                       (0, 2, -121.842300, 37.033000, -121.837900, 37.094000 )
+Vallecitos                    Road     (0, 2, -121.817700, 37.142000, -121.836000, 37.053000 )
+Glacier                       Dr       (0, 2, -121.801600, 37.804000, -121.801700, 37.809000 )
+Rainier                       Ave      (0, 2, -121.800900, 37.803000, -121.799900, 37.804000 )
+Murdell                       Lane     (0, 2, -121.800100, 37.746000, -121.799800, 37.741000 )
+Amber                         Way      (0, 2, -121.802500, 37.707000, -121.801500, 37.708000 )
+Amber                         Ct       (0, 2, -121.799700, 37.708000, -121.799700, 37.704000 )
+Turnstone                     Ct       (0, 2, -121.796700, 37.812000, -121.796700, 37.814000 )
+Stanley                       Blvd     (0, 2, -121.797100, 37.769000, -121.794800, 37.772000 )
+Anna Maria                    St       (0, 2, -121.795700, 37.756000, -121.795800, 37.725000 )
+Alice                         Way      (0, 2, -121.796800, 37.718000, -121.795800, 37.718000 )
+Ruth                          Way      (0, 2, -121.792800, 37.766000, -121.792000, 37.767000 )
+Ruby                          Road     (0, 2, -121.802900, 37.688000, -121.802300, 37.680000 )
+Peridot                       Dr       (0, 2, -121.802400, 37.648000, -121.802400, 37.645000 )
+Diamond                       Dr       (0, 2, -121.800800, 37.669000, -121.798800, 37.682000 )
+Agate                         Ct       (0, 2, -121.801000, 37.653000, -121.800800, 37.649000 )
+Opal                          Way      (0, 2, -121.798400, 37.679000, -121.797500, 37.679000 )
+Encino                        Dr       (0, 2, -121.795900, 37.688000, -121.794700, 37.688000 )
+El Caminito                            (0, 3, -121.792200, 37.696000, -121.790700, 37.685000 , -121.789800, 37.681000)
+Murdell                       Lane     (0, 2, -121.797700, 37.619000, -121.797719, 37.607740 )
+Wall                          St       (0, 2, -121.790400, 37.776000, -121.789300, 37.767000 )
+Northwood Com                          (0, 2, -121.788500, 37.815000, -121.788231, 37.809620 )
+Cardinal                      Dr       (0, 2, -121.786500, 37.805000, -121.785800, 37.805000 )
+Murrieta                      Blvd     (0, 2, -121.786400, 37.791000, -121.786600, 37.797000 )
+Coleen                        St       (0, 2, -121.790000, 37.763000, -121.789200, 37.744000 )
+Moraga                        Dr       (0, 2, -121.791500, 37.701000, -121.790600, 37.705000 )
+Alexander                     St       (0, 2, -121.788800, 37.724000, -121.787400, 37.724000 )
+Delmar                        Ave      (0, 2, -121.788900, 37.693000, -121.787000, 37.704000 )
+Lambaren                      Ave      (0, 2, -121.782500, 37.822000, -121.781600, 37.821000 )
+Wp Railroad                            (0, 2, -121.786400, 37.791000, -121.776100, 37.822000 )
+Glenwood                      St       (0, 2, -121.779900, 37.771000, -121.779400, 37.771000 )
+Murrieta                      Blvd     (0, 2, -121.782500, 37.758000, -121.781600, 37.758000 )
+Verona                        Ave      (0, 2, -121.780800, 37.721000, -121.780100, 37.721000 )
+Catalina                      Dr       (0, 2, -121.789200, 37.660000, -121.789600, 37.649000 )
+El Caminito                            (0, 2, -121.788300, 37.673000, -121.785200, 37.670000 )
+Leland                        Way      (0, 2, -121.786700, 37.655000, -121.785900, 37.656000 )
+Via del Sol                            (0, 2, -121.789900, 37.624000, -121.790100, 37.615000 )
+Mayview                       Way      (0, 2, -121.786900, 37.637000, -121.784800, 37.636000 )
+Neptune                       Road     (0, 2, -121.788500, 37.607000, -121.788500, 37.599000 )
+Camelia                       Dr       (0, 2, -121.785200, 37.695000, -121.785200, 37.686000 )
+Wagoner                       Dr       (0, 2, -121.784000, 37.670000, -121.784000, 37.663000 )
+Miranda                       Way      (0, 2, -121.783700, 37.649000, -121.780700, 37.648000 )
+Holmes                        St       (0, 2, -121.779400, 37.697000, -121.779400, 37.670000 )
+El Dorado                     Dr       (0, 2, -121.780300, 37.659000, -121.780100, 37.647000 )
+Roselli                       Dr       (0, 2, -121.784800, 37.636000, -121.784800, 37.628000 )
+Catalina                      Dr       (0, 2, -121.783700, 37.628000, -121.783400, 37.628000 )
+Mars                          Road     (0, 2, -121.783400, 37.586000, -121.782700, 37.575000 )
+Lomitas                       Ave      (0, 2, -121.780700, 37.574000, -121.780400, 37.586000 )
+Alden                         Lane     (0, 2, -121.797800, 37.561000, -121.795183, 37.561000 )
+Vallecitos                    Road     (0, 3, -121.787500, 37.454000, -121.785600, 37.463000 , -121.782200, 37.482000)
+Holmes                        St       (0, 2, -121.781600, 37.566000, -121.781800, 37.560000 )
+Norwood                       Pl       (0, 2, -121.780700, 37.534000, -121.781100, 37.534000 )
+Olivina                       Ave      (0, 2, -121.779100, 37.829000, -121.778700, 37.829000 )
+Holmes                        St       (0, 2, -121.778400, 37.776000, -121.778512, 37.770400 )
+N                             St       (0, 2, -121.773700, 37.824000, -121.773900, 37.829000 )
+4th                           St       (0, 3, -121.777800, 37.760000, -121.775300, 37.769000 , -121.774100, 37.772000)
+Holmes                        St       (0, 2, -121.778900, 37.751000, -121.778900, 37.747000 )
+Anza                          Way      (0, 2, -121.779400, 37.714000, -121.778800, 37.714000 )
+5th                           St       (0, 2, -121.776600, 37.757000, -121.775100, 37.761000 )
+Florence                      Road     (0, 2, -121.774300, 37.711000, -121.774300, 37.687000 )
+1st                           St       (0, 2, -121.771600, 37.805000, -121.770200, 37.809000 )
+5th                           St       (0, 2, -121.773700, 37.765000, -121.772700, 37.769000 )
+5th                           St       (0, 2, -121.771300, 37.772000, -121.770100, 37.775000 )
+L                             St       (0, 2, -121.769400, 37.795000, -121.769200, 37.788000 )
+L                             St       (0, 2, -121.768400, 37.772000, -121.768000, 37.762000 )
+McGlinchey                    Dr       (0, 2, -121.772100, 37.745000, -121.772200, 37.729000 )
+Old Tower                     Road     (0, 2, -121.772200, 37.729000, -121.770400, 37.729000 )
+College                       Ave      (0, 2, -121.769300, 37.744000, -121.769000, 37.744000 )
+Peary                         Ct       (0, 2, -121.769000, 37.711000, -121.768600, 37.706000 )
+Columbus                      Ave      (0, 2, -121.778600, 37.680000, -121.777700, 37.681000 )
+Vancouver                     Way      (0, 2, -121.777500, 37.667000, -121.776800, 37.666000 )
+Columbus                      Ave      (0, 2, -121.776000, 37.679000, -121.775300, 37.682000 )
+Helsinki                      Way      (0, 2, -121.775300, 37.659000, -121.774400, 37.658000 )
+Vancouver                     Way      (0, 2, -121.775300, 37.665000, -121.774200, 37.666000 )
+Concannon                     Blvd     (0, 2, -121.780400, 37.608000, -121.779000, 37.608000 )
+Evans                         St       (0, 2, -121.777800, 37.607000, -121.777700, 37.602000 )
+Heidelberg                    Dr       (0, 2, -121.776100, 37.614000, -121.775100, 37.614000 )
+Innsbruck                     St       (0, 2, -121.770600, 37.680000, -121.770800, 37.668000 )
+Heidelberg                    Dr       (0, 2, -121.769200, 37.638000, -121.771400, 37.645000 )
+Bordeaux                      St       (0, 2, -121.768500, 37.688000, -121.768700, 37.664000 )
+Arroyo                        Road     (0, 3, -121.767100, 37.654000, -121.767086, 37.652140 , -121.767000, 37.641000)
+Warsaw                        Ave      (0, 2, -121.771400, 37.623000, -121.770640, 37.626800 )
+Sydney                        Ave      (0, 2, -121.767800, 37.636000, -121.766900, 37.636000 )
+Livermore                     Ave      (0, 2, -121.766200, 37.811000, -121.765600, 37.808000 )
+Maple                         St       (0, 2, -121.763800, 37.824000, -121.762700, 37.820000 )
+I                             St       (0, 2, -121.765500, 37.798000, -121.765200, 37.791000 )
+5th                           St       (0, 2, -121.763800, 37.795000, -121.763300, 37.795000 )
+East                          Ave      (0, 2, -121.764100, 37.800000, -121.763000, 37.800000 )
+6th                           St       (0, 2, -121.763400, 37.785000, -121.762400, 37.789000 )
+7th                           St       (0, 2, -121.761800, 37.779000, -121.761500, 37.783000 )
+College                       Ave      (0, 2, -121.767500, 37.745000, -121.765800, 37.745000 )
+Bess                          Ave      (0, 2, -121.765239, 37.625000, -121.763602, 37.625000 )
+H                             St       (0, 2, -121.762500, 37.766000, -121.762100, 37.756000 )
+Chateau                       Way      (0, 2, -121.762700, 37.727000, -121.762700, 37.723000 )
+7th                           St       (0, 2, -121.760600, 37.790000, -121.759700, 37.798000 )
+East                          Ave      (0, 2, -121.759700, 37.798000, -121.755800, 37.800000 )
+Jensen                        St       (0, 2, -121.754000, 37.805000, -121.754000, 37.800000 )
+Livermore                     Ave      (0, 2, -121.758900, 37.765000, -121.758400, 37.763000 )
+Livermore                     Ave      (0, 2, -121.755300, 37.744000, -121.753900, 37.737000 )
+Westbrook                     Pl       (0, 2, -121.778700, 37.565000, -121.778400, 37.564000 )
+Windsor                       Pl       (0, 2, -121.778100, 37.521000, -121.778100, 37.525000 )
+Oxford                        Pl       (0, 2, -121.775200, 37.532000, -121.774700, 37.535000 )
+Chatsworth                    St       (0, 2, -121.776600, 37.502000, -121.776600, 37.496000 )
+Superior                      Dr       (0, 2, -121.772400, 37.543000, -121.772700, 37.543000 )
+Walker                        Pl       (0, 2, -121.769200, 37.537000, -121.769300, 37.544000 )
+Hogan                         Pl       (0, 2, -121.769600, 37.507000, -121.768900, 37.502000 )
+Reed                          Ave      (0, 2, -121.758100, 37.516000, -121.755900, 37.516000 )
+Indian Creek                  Road     (0, 2, -121.788900, 37.843000, -121.800000, 37.791000 )
+Arroyo                        Road     (0, 2, -121.755500, 37.258000, -121.755600, 37.251000 )
+Arroyo del Valle                       (0, 2, -121.750000, 37.204840, -121.758400, 37.208000 )
+San Antonio Reservoir                  (0, 2, -121.848700, 37.728000, -121.835900, 37.670000 )
+Calaveras Creek                        (0, 2, -121.853100, 37.337000, -121.851700, 37.316000 )
+Welch Creek                   Road     (0, 2, -121.844000, 37.370000, -121.828900, 37.315000 )
+Hayfield                      Road     (0, 2, -121.829200, 37.314000, -121.828100, 37.295000 )
+Calaveras                     Road     (0, 2, -121.847600, 37.209000, -121.843100, 37.178000 )
+Calaveras                     Road     (0, 2, -121.838900, 37.143000, -121.833800, 37.128000 )
+Oakridge                      Road     (0, 2, -121.831600, 37.049000, -121.828382, 37.000000 )
+Indian Joe Creek                       (0, 2, -121.827300, 37.142000, -121.806000, 37.295000 )
+Alameda Creek                          (0, 2, -121.828300, 37.151000, -121.827300, 37.142000 )
+Geary                         Road     (0, 2, -121.827700, 37.073000, -121.827900, 37.082000 )
+Calaveras Creek                        (0, 2, -121.820300, 37.035000, -121.820700, 37.931000 )
+Welch Creek                            (0, 2, -121.810700, 37.349000, -121.801200, 37.397000 )
+Welch Creek                   Road     (0, 2, -121.789500, 37.360000, -121.771700, 37.218000 )
+Indian Creek                  Road     (0, 2, -121.775100, 37.798000, -121.764600, 37.799000 )
+Welch Creek                   Road     (0, 2, -121.769500, 37.386000, -121.773700, 37.413000 )
+Geary                         Road     (0, 2, -121.778400, 37.014000, -121.798600, 37.060000 )
+Indian Creek                           (0, 2, -121.764200, 37.246000, -121.765500, 37.244000 )
+Harvard                       Way      (0, 2, -121.754000, 37.805000, -121.750800, 37.806000 )
+Livermore                     Ave      (0, 2, -121.750900, 37.715000, -121.750474, 37.712290 )
+Arroyo                        Road     (0, 2, -121.750600, 37.189000, -121.750000, 37.185410 )
+Wicks                         Blvd     (0, 3, -122.163486, 37.970940, -122.163200, 37.966000 , -122.162400, 37.936000)
+Fairview                      Ave      (0, 3, -122.027650, 37.685210, -122.027400, 37.669000 , -122.026700, 37.646000)
+Benson                        Road     (0, 2, -122.083217, 37.947650, -122.089100, 37.928000 )
+Tupelo                        Ter      (0, 2, -122.061851, 37.626750, -122.060600, 37.620000 )
+Indian Creek                  Road     (0, 2, -121.863754, 37.774990, -121.864400, 37.782000 )
+Corral Hollow Creek                    (0, 2, -121.590572, 37.011160, -121.599735, 37.106760 )
+Agua Fria Creek                        (0, 2, -121.912500, 37.953670, -121.913800, 37.958000 )
+Sp Railroad                            (0, 2, -122.305229, 37.839260, -122.304900, 37.822000 )
+At and Sf Railroad                     (0, 2, -122.281389, 37.306950, -122.282488, 37.304910 )
+Bay Area Rapid Transit                 (0, 2, -122.231147, 37.549120, -122.229000, 37.562000 )
+Sausal Creek                           (0, 3, -122.219108, 37.948700, -122.219300, 37.942000 , -122.219513, 37.933130)
+Perlata Creek                          (0, 2, -122.218530, 37.831630, -122.218800, 37.826000 )
+San Leandro Creek                      (0, 2, -122.154489, 37.279780, -122.155000, 37.281000 )
+Sp Railroad                            (0, 3, -122.137792, 37.003000, -122.136500, 37.992000 , -122.131257, 37.946120)
+San Lorenzo Creek                      (0, 2, -122.124957, 37.853000, -122.127100, 37.849000 )
+San Lorenzo Creek                      (0, 2, -122.063257, 37.859660, -122.064271, 37.853390 )
+Crow Canyon Creek                      (0, 2, -122.046308, 37.001500, -122.046833, 37.001330 )
+Gold Creek                             (0, 2, -121.922096, 37.888590, -121.920744, 37.884630 )
+Sp Railroad                            (0, 2, -122.106290, 37.730420, -122.105400, 37.723000 )
+Sp Railroad                            (0, 2, -122.076691, 37.999140, -122.075907, 37.992430 )
+Sulphur Creek                          (0, 2, -122.058701, 37.762160, -122.060900, 37.772000 )
+Dry Creek                              (0, 2, -122.036700, 37.883860, -122.038000, 37.877000 )
+Sp Railroad                            (0, 2, -121.993831, 37.816690, -121.992146, 37.810700 )
+Alameda Creek                          (0, 2, -122.022956, 37.773060, -122.025000, 37.781000 )
+Sp Railroad                            (0, 2, -122.041400, 37.268000, -122.042509, 37.263380 )
+Alameda Creek                          (0, 2, -121.978805, 37.721430, -121.983900, 37.717000 )
+Bay Area Rapid Transit                 (0, 2, -121.983355, 37.643290, -121.982907, 37.638420 )
+Wp Railroad                            (0, 2, -121.958612, 37.678230, -121.958100, 37.670000 )
+Arroyo Mocho Canal                     (0, 2, -121.908540, 37.780990, -121.907797, 37.783920 )
+Pleasanton Canal                       (0, 2, -121.886052, 37.822280, -121.883300, 37.835000 )
+Western Pacific Railroad               (0, 2, -121.956860, 37.650800, -121.954800, 37.598000 )
+Hetch Hetchy Aqueduct                  (0, 2, -121.949680, 37.438800, -121.948800, 37.441000 )
+Sp Railroad                            (0, 2, -121.950757, 37.252430, -121.950600, 37.250000 )
+Fremont                       Blvd     (0, 2, -122.006647, 37.585060, -122.007100, 37.587000 )
+Agua Fria Creek                        (0, 2, -121.909487, 37.944850, -121.910653, 37.948090 )
+Alameda Diversion                      (0, 2, -121.774647, 37.973330, -121.772718, 37.985910 )
+Fordham                       Way      (0, 3, -121.748800, 37.869000, -121.748100, 37.869000 , -121.747200, 37.868000)
+Findlay                       Way      (0, 2, -121.746300, 37.753000, -121.745400, 37.753000 )
+Vistamont                     Ave      (0, 2, -122.265432, 37.035600, -122.264300, 37.029000 )
+Channing                      Way      (0, 2, -122.292641, 37.623570, -122.292500, 37.624000 )
+67th                          St       (0, 2, -122.284070, 37.501350, -122.285200, 37.499000 )
+Railroad                      Ave      (0, 2, -121.779215, 37.797980, -121.779265, 37.796350 )
+Ballena                       Blvd     (0, 2, -122.285400, 37.691000, -122.285393, 37.689240 )
+Taylor                        Ave      (0, 2, -122.280873, 37.729750, -122.280388, 37.729580 )
+Cedar                         St       (0, 2, -122.260055, 37.795580, -122.258200, 37.798000 )
+Grandview                     Dr       (0, 2, -122.229712, 37.582340, -122.230071, 37.576180 )
+Ramona                        Ave      (0, 2, -122.244153, 37.314990, -122.243523, 37.311090 )
+Oakmont                       Ave      (0, 2, -122.231698, 37.171130, -122.232100, 37.168000 )
+Trestle Glen                  Road     (0, 3, -122.221239, 37.120590, -122.220700, 37.125000 , -122.220100, 37.133000)
+Sausal Creek                           (0, 2, -122.215521, 37.035320, -122.216544, 37.018270 )
+Davis                         St       (0, 2, -122.217903, 37.893370, -122.216000, 37.885000 )
+Elverton                      Dr       (0, 2, -122.202800, 37.487000, -122.201700, 37.471000 )
+Snake                         Road     (0, 3, -122.206567, 37.276030, -122.206200, 37.281000 , -122.205700, 37.292000)
+Burlington                    St       (0, 2, -122.204600, 37.057000, -122.204200, 37.058000 )
+Eastman                       Ave      (0, 2, -122.200240, 37.862170, -122.199800, 37.865000 )
+Pampas                        Ave      (0, 2, -122.188335, 37.876350, -122.187800, 37.872000 )
+Santa Clara                   Ave      (0, 2, -122.254714, 37.713170, -122.254000, 37.710000 )
+Broadway                               (0, 2, -122.243008, 37.559610, -122.242700, 37.563000 )
+Mecartney                     Road     (0, 2, -122.248350, 37.377690, -122.247665, 37.376100 )
+Sea View                      Pkwy     (0, 3, -122.242913, 37.472480, -122.242100, 37.472000 , -122.241100, 37.471000)
+36th                          Ave      (0, 2, -122.224140, 37.709880, -122.223800, 37.716000 )
+Coleport Landing                       (0, 2, -122.237889, 37.412930, -122.237900, 37.412000 )
+El Paseo                               (0, 2, -122.244718, 37.330160, -122.245400, 37.333000 )
+Redding                       St       (0, 2, -122.191432, 37.842420, -122.191100, 37.842000 )
+Coliseum                      Way      (0, 2, -122.197590, 37.453300, -122.194800, 37.444000 )
+Halliday                      Ave      (0, 2, -122.175147, 37.633690, -122.174700, 37.629000 )
+Graffian                      St       (0, 2, -122.167740, 37.375800, -122.167700, 37.375000 )
+San Leandro Creek                      (0, 2, -122.175792, 37.248640, -122.180700, 37.247000 )
+Sp Railroad                            (0, 2, -122.172213, 37.033990, -122.167800, 37.059000 )
+Bancroft                      Ave      (0, 2, -122.157140, 37.424200, -122.156000, 37.409000 )
+Sigourney Elysian Fields      Dr       (0, 2, -122.127563, 37.664950, -122.127500, 37.649000 )
+Stoakes                       Ave      (0, 2, -122.161722, 37.315740, -122.161950, 37.314670 )
+Alexandria                    St       (0, 2, -122.141100, 37.892000, -122.141700, 37.892000 )
+Springlake                    Dr       (0, 2, -122.133113, 37.931340, -122.134000, 37.930000 )
+Saratoga                      St       (0, 2, -122.102400, 37.997000, -122.101900, 37.997000 )
+Knoll                         Way      (0, 2, -122.086400, 37.848000, -122.083900, 37.836000 )
+Christensen                   Ct       (0, 2, -122.086300, 37.074000, -122.086300, 37.065000 )
+Vegas                         Ave      (0, 2, -122.075906, 37.892900, -122.072600, 37.894000 )
+Monika                        Lane     (0, 2, -122.051987, 37.885530, -122.052536, 37.881710 )
+Wooster                       Ct       (0, 2, -122.023499, 37.151180, -122.023700, 37.147000 )
+Dublin Green                  Dr       (0, 2, -121.939669, 37.105160, -121.938400, 37.097000 )
+Tamarack                      Dr       (0, 2, -121.932412, 37.175160, -121.932700, 37.179000 )
+Dougherty                     Road     (0, 3, -121.909939, 37.286690, -121.909373, 37.302340 , -121.909200, 37.305000)
+Dougherty                     Road     (0, 2, -121.909300, 37.127000, -121.909300, 37.154180 )
+Inglewood                     Dr       (0, 2, -121.908600, 37.877000, -121.908800, 37.877000 )
+Santa Rita                    Road     (0, 2, -121.877461, 37.914200, -121.877500, 37.922000 )
+Marlboro                      Way      (0, 2, -121.866120, 37.999400, -121.866000, 37.999000 )
+Oakbrook                      Ct       (0, 2, -121.865488, 37.966530, -121.866100, 37.965000 )
+Hanover                       St       (0, 2, -121.789629, 37.924810, -121.789200, 37.934000 )
+Andrews                       St       (0, 2, -121.781400, 37.834000, -121.781400, 37.829000 )
+Arena                         St       (0, 2, -122.155014, 37.823470, -122.155900, 37.820000 )
+Via Escondido                          (0, 2, -122.142774, 37.738120, -122.143583, 37.734290 )
+Via Diego                              (0, 2, -122.118617, 37.819030, -122.119400, 37.814000 )
+Via Bellita                            (0, 2, -122.121893, 37.823930, -122.122570, 37.819590 )
+A                             St       (0, 2, -122.106469, 37.664460, -122.106700, 37.664000 )
+Smalley                       Ave      (0, 2, -122.095194, 37.697140, -122.094500, 37.700000 )
+Bennington                    Lane     (0, 2, -122.103818, 37.361360, -122.104500, 37.361000 )
+Lilly                         St       (0, 2, -122.078139, 37.641030, -122.077500, 37.643000 )
+Carlos Bee                    Blvd     (0, 2, -122.065049, 37.599280, -122.063900, 37.596000 )
+Colette                       St       (0, 2, -122.065650, 37.482500, -122.064600, 37.479000 )
+Hesperian                     Blvd     (0, 2, -122.088064, 37.186840, -122.087886, 37.183580 )
+Union City                    Blvd     (0, 2, -122.079700, 37.883000, -122.079700, 37.878000 )
+Apollo                        Cir      (0, 2, -122.068531, 37.876540, -122.068600, 37.877000 )
+Regents                       Blvd     (0, 2, -122.068594, 37.807780, -122.068100, 37.805000 )
+Union City                    Blvd     (0, 2, -122.078284, 37.745310, -122.078001, 37.740220 )
+Rocklin                       Dr       (0, 2, -122.070801, 37.717010, -122.071200, 37.713000 )
+Edgebrook                     Dr       (0, 2, -122.057639, 37.715230, -122.057506, 37.724720 )
+Tennyson                      Road     (0, 2, -122.056941, 37.358350, -122.056200, 37.360000 )
+Oakes                         Dr       (0, 2, -122.034734, 37.624230, -122.032900, 37.618000 )
+Alvarado Niles                Road     (0, 2, -122.049848, 37.951150, -122.047300, 37.945000 )
+Arizona                       St       (0, 2, -122.044507, 37.905000, -122.044300, 37.904000 )
+Stonehenge                    Road     (0, 2, -122.037162, 37.846790, -122.037700, 37.841000 )
+Railroad                      Ave      (0, 3, -122.024500, 37.013000, -122.023400, 37.003000 , -122.022300, 37.993000)
+Starling                      Dr       (0, 2, -122.015929, 37.824890, -122.017100, 37.830000 )
+O Connell                     Lane     (0, 2, -122.002350, 37.987500, -121.996400, 37.995000 )
+Capulet                       Cir      (0, 2, -122.057612, 37.692680, -122.057800, 37.700000 )
+Paseo Padre                   Pkwy     (0, 2, -122.045898, 37.700230, -122.047000, 37.695000 )
+Jarvis                        Ave      (0, 2, -122.053935, 37.435930, -122.054359, 37.430780 )
+Spruce                        St       (0, 2, -122.050600, 37.327000, -122.049600, 37.322000 )
+Cherry                        St       (0, 2, -122.040954, 37.379180, -122.040000, 37.369000 )
+Merrimac River                St       (0, 2, -122.032907, 37.706660, -122.033200, 37.703000 )
+Fremont                       Blvd     (0, 2, -122.028260, 37.690800, -122.026940, 37.684200 )
+Cutler                        Ave      (0, 2, -122.013942, 37.749130, -122.014200, 37.745000 )
+Portola                       Dr       (0, 2, -122.019284, 37.568390, -122.019900, 37.563000 )
+Chelsea                       Dr       (0, 2, -122.030440, 37.522800, -122.030300, 37.522000 )
+Port Sailwood                 Dr       (0, 2, -122.026100, 37.467000, -122.026500, 37.463000 )
+Hamlin                        St       (0, 2, -122.009871, 37.368050, -122.009200, 37.365000 )
+Regailia                      Ave      (0, 2, -121.867010, 37.629440, -121.867052, 37.621820 )
+Hibiscus                      Ave      (0, 2, -121.999801, 37.802230, -121.999300, 37.800000 )
+Jason                         Way      (0, 2, -122.004400, 37.584000, -122.003300, 37.577000 )
+2nd                           St       (0, 3, -121.986342, 37.776090, -121.985400, 37.774000 , -121.984200, 37.772000)
+Palmer                        Dr       (0, 2, -122.003005, 37.528520, -122.002600, 37.527000 )
+Farwell                       Dr       (0, 2, -121.996600, 37.316000, -121.996200, 37.314000 )
+Mission                       Blvd     (0, 2, -121.971801, 37.770410, -121.970600, 37.762000 )
+Mowry                         Ave      (0, 2, -121.976381, 37.648220, -121.976900, 37.646000 )
+Yerba Buena                   St       (0, 2, -121.960541, 37.681900, -121.960200, 37.678000 )
+Ogden                         Dr       (0, 2, -121.978963, 37.368630, -121.980800, 37.368000 )
+Hawkins                       St       (0, 2, -121.969533, 37.367020, -121.970300, 37.358000 )
+Fremont                       Blvd     (0, 2, -121.960056, 37.339200, -121.958400, 37.330000 )
+Whitecap                      Way      (0, 2, -121.991567, 37.285080, -121.990960, 37.282220 )
+Stevenson                     Blvd     (0, 2, -121.982964, 37.277150, -121.983567, 37.270170 )
+Grimmer                       Blvd     (0, 2, -121.966619, 37.178000, -121.966000, 37.165000 )
+Las Positas                   Blvd     (0, 2, -121.907779, 37.797340, -121.908481, 37.794160 )
+Parkside                      Dr       (0, 2, -121.895065, 37.795880, -121.894900, 37.797000 )
+Valley                        Dr       (0, 3, -121.897701, 37.686600, -121.897200, 37.677000 , -121.896920, 37.662070)
+Greenwood                     Road     (0, 2, -121.879600, 37.801000, -121.879600, 37.802000 )
+Santa Rita                    Road     (0, 2, -121.872608, 37.752820, -121.873100, 37.766000 )
+Valley                        Dr       (0, 2, -121.863815, 37.760630, -121.864400, 37.761000 )
+Angela                        St       (0, 2, -121.865522, 37.553240, -121.865200, 37.552000 )
+Mission                       Blvd     (0, 2, -121.949133, 37.563390, -121.948176, 37.557200 )
+Gomes                         Road     (0, 2, -121.949156, 37.421920, -121.949300, 37.420000 )
+Mission                       Blvd     (0, 2, -121.937204, 37.488030, -121.936483, 37.483360 )
+Mission                       Blvd     (0, 2, -121.928695, 37.437670, -121.925000, 37.417000 )
+Mission                       Blvd     (0, 2, -121.920783, 37.371680, -121.920400, 37.367000 )
+Durham                        Road     (0, 2, -121.957278, 37.100330, -121.957885, 37.098570 )
+Winding                       Lane     (0, 2, -121.931438, 37.078490, -121.930600, 37.078000 )
+Lurene                        Dr       (0, 2, -121.918603, 37.249720, -121.919428, 37.247850 )
+Vineyard                      Ave      (0, 2, -121.852400, 37.648000, -121.851100, 37.648000 )
+Owl                           Ct       (0, 2, -121.916540, 37.196360, -121.915693, 37.190310 )
+Lisbon                        Ave      (0, 2, -121.781800, 37.710000, -121.780800, 37.710000 )
+Berlin                        Way      (0, 2, -121.777400, 37.649000, -121.776600, 37.649000 )
+Windsor                       Way      (0, 2, -121.777200, 37.521000, -121.776200, 37.521000 )
+Marsala                       Ct       (0, 3, -121.848563, 37.641160, -121.847736, 37.635010 , -121.847146, 37.633920)
+Talbot                        Ave      (0, 2, -122.292500, 37.847000, -122.292500, 37.843000 )
+Chabolyn                      Ter      (0, 2, -122.242959, 37.519200, -122.242900, 37.519000 )
+Sacramento                    St       (0, 2, -122.277606, 37.508150, -122.277616, 37.505910 )
+Temescal Creek                         (0, 2, -122.284945, 37.360170, -122.285500, 37.359000 )
+Temescal                      Cir      (0, 2, -122.282639, 37.363260, -122.282060, 37.366500 )
+Sp Railroad                            (0, 2, -122.275429, 37.884740, -122.275400, 37.883000 )
+3rd                           St       (0, 2, -122.287200, 37.803000, -122.287191, 37.808400 )
+Singleton                     Ave      (0, 2, -122.289600, 37.873970, -122.288161, 37.873940 )
+Mayport                       Cir      (0, 2, -122.283588, 37.880640, -122.284724, 37.879920 )
+Lakehurst                     Cir      (0, 2, -122.284729, 37.890250, -122.286096, 37.903640 )
+Hiller                        Dr       (0, 2, -122.228415, 37.508490, -122.228400, 37.508000 )
+Fitzerald                     St       (0, 2, -122.281850, 37.262080, -122.282237, 37.262730 )
+McSherry                      Way      (0, 2, -122.235166, 37.291990, -122.234053, 37.276350 )
+Silva                         Lane     (0, 2, -122.237019, 37.281320, -122.235461, 37.278880 )
+Rose                          Ave      (0, 3, -122.244329, 37.282370, -122.244100, 37.284000 , -122.243800, 37.287000)
+Canon                         Ave      (0, 2, -122.217150, 37.019510, -122.215100, 37.053000 )
+Redwood                       Road     (0, 2, -122.174191, 37.961910, -122.174000, 37.966000 )
+Jensen                        Road     (0, 2, -122.037810, 37.038910, -122.037700, 37.040000 )
+Springbrook                   Lane     (0, 2, -122.057162, 37.299520, -122.056705, 37.291510 )
+Columbia                      Dr       (0, 2, -122.057463, 37.288110, -122.057463, 37.285930 )
+Knight                        Dr       (0, 2, -122.089468, 37.176810, -122.089434, 37.173370 )
+Sunshine                      Pl       (0, 2, -122.097403, 37.112090, -122.096541, 37.114360 )
+Portofino                     Cir      (0, 2, -122.123984, 37.085830, -122.124375, 37.088090 )
+Majestic                      Way      (0, 2, -122.142593, 37.987230, -122.142300, 37.984850 )
+Galleon                       Pl       (0, 2, -122.178053, 37.922300, -122.177578, 37.922520 )
+Busby                         Ave      (0, 2, -122.154500, 37.790000, -122.154983, 37.789160 )
+Fairview                      Ave      (0, 2, -122.038562, 37.762580, -122.037900, 37.762000 )
+Marlin                        Ct       (0, 2, -122.044157, 37.304060, -122.044602, 37.300520 )
+Lakeridge                     Ave      (0, 2, -122.048299, 37.843490, -122.048127, 37.834010 )
+D                             St       (0, 2, -122.056892, 37.798960, -122.056400, 37.800000 )
+Rosario                       Ct       (0, 2, -122.110662, 37.798470, -122.109931, 37.785960 )
+Via Enrico                             (0, 2, -122.139846, 37.813790, -122.140700, 37.810000 )
+Bartlett                      Lane     (0, 2, -122.111062, 37.717710, -122.109880, 37.702760 )
+Quinn                         Lane     (0, 2, -122.066405, 37.747940, -122.066039, 37.735570 )
+Walpert                       St       (0, 2, -122.074760, 37.689300, -122.073488, 37.694870 )
+Garwood Glenn                 Dr       (0, 2, -122.064060, 37.689460, -122.064269, 37.683360 )
+Sylvan Glen                            (0, 2, -122.065238, 37.685000, -122.065615, 37.678790 )
+Deer Trail                    Pl       (0, 2, -122.045500, 37.669000, -122.044472, 37.669380 )
+Durk                          Way      (0, 2, -122.097393, 37.579550, -122.097541, 37.581440 )
+Denton                        Ave      (0, 2, -122.111800, 37.467000, -122.108659, 37.476890 )
+Chandler                      Road     (0, 2, -122.108510, 37.481390, -122.108787, 37.486770 )
+Yosemite                      Way      (0, 2, -122.110800, 37.480300, -122.109445, 37.484150 )
+Myra                          Ct       (0, 2, -122.108083, 37.473750, -122.108278, 37.476770 )
+Eucalyptus                    Ct       (0, 2, -122.064324, 37.374250, -122.064548, 37.377330 )
+Colony                        Ct       (0, 2, -122.061894, 37.277730, -122.061805, 37.274970 )
+Foxfire                       Pl       (0, 2, -122.063987, 37.273490, -122.064027, 37.274590 )
+Medellion                     Dr       (0, 2, -122.050194, 37.957380, -122.050187, 37.964070 )
+San Jose                      Ct       (0, 2, -122.056300, 37.922000, -122.055864, 37.919210 )
+Jupiter                       Ct       (0, 2, -122.064459, 37.847410, -122.064255, 37.846260 )
+Palm                          Dr       (0, 2, -122.052051, 37.887510, -122.051900, 37.885000 )
+Timpanogas                    Cir      (0, 2, -121.963000, 37.752000, -121.962723, 37.749240 )
+Creekside                     Ter      (0, 2, -121.997958, 37.645930, -121.998047, 37.635040 )
+Sequoia                       Ter      (0, 2, -121.997978, 37.657100, -121.997403, 37.666960 )
+Reynolds                      Dr       (0, 2, -122.003600, 37.671000, -122.002954, 37.668090 )
+Cormorant                     Ter      (0, 2, -122.045468, 37.807530, -122.044829, 37.803920 )
+Bobwhite                      Ter      (0, 2, -122.046797, 37.802240, -122.046672, 37.801790 )
+Fulmar                        Ter      (0, 2, -122.045623, 37.802990, -122.045824, 37.801010 )
+Ganet                         Ter      (0, 2, -122.045424, 37.794780, -122.045330, 37.794180 )
+Eastpark                      Ter      (0, 2, -122.041182, 37.776860, -122.042271, 37.782260 )
+Welk Com                               (0, 2, -122.036356, 37.869140, -122.036265, 37.861800 )
+Mellow                        Way      (0, 2, -122.038700, 37.846000, -122.037700, 37.841000 )
+Browning                      Ct       (0, 2, -122.037289, 37.766000, -122.038366, 37.762280 )
+Norissa                       Cir      (0, 2, -122.048600, 37.763000, -122.048400, 37.770000 )
+Jaques                        Ct       (0, 2, -122.052072, 37.689980, -122.050458, 37.694740 )
+Paseo Padre                   Pkwy     (0, 2, -122.051153, 37.675860, -122.051700, 37.674000 )
+Siward                        Dr       (0, 2, -122.046199, 37.681580, -122.046000, 37.672000 )
+Ardenwood                     Blvd     (0, 2, -122.063701, 37.596530, -122.063302, 37.588150 )
+Winslow                       Ter      (0, 2, -122.059185, 37.539280, -122.058347, 37.536300 )
+Northland                     Ter      (0, 2, -122.059106, 37.555590, -122.059220, 37.554980 )
+Milburn                       Ter      (0, 2, -122.058937, 37.549310, -122.058337, 37.537300 )
+Heathrow                      Ter      (0, 2, -122.057185, 37.556990, -122.056911, 37.552230 )
+Vane Common                            (0, 2, -122.056732, 37.547970, -122.056633, 37.545430 )
+Kenwood                       Dr       (0, 2, -122.054303, 37.636140, -122.054133, 37.629810 )
+Diana Common                           (0, 2, -122.056116, 37.613860, -122.056337, 37.620650 )
+Shattuck                      Ave      (0, 2, -122.053676, 37.612230, -122.052619, 37.615910 )
+Gotubin Common                         (0, 2, -122.053546, 37.590370, -122.054013, 37.588870 )
+Quebec Common                          (0, 2, -122.051368, 37.607560, -122.052820, 37.601020 )
+Falls                         Ter      (0, 2, -122.048735, 37.616240, -122.048402, 37.609540 )
+Xavier Common                          (0, 2, -122.049697, 37.645090, -122.050346, 37.643130 )
+Zircon                        Ter      (0, 2, -122.051254, 37.640380, -122.051126, 37.636960 )
+Ridgewood                     Dr       (0, 2, -122.051305, 37.593860, -122.053772, 37.582630 )
+Creekwood                     Dr       (0, 2, -122.043309, 37.669110, -122.041422, 37.660990 )
+Conway                        Ter      (0, 2, -122.044884, 37.644410, -122.044660, 37.645140 )
+Woodhue                       Ter      (0, 2, -122.046987, 37.620700, -122.047212, 37.625290 )
+Jovan                         Ter      (0, 2, -122.046671, 37.622060, -122.046466, 37.618100 )
+Grange                        Ter      (0, 2, -122.045470, 37.628050, -122.045749, 37.626950 )
+Medacino                      Ter      (0, 2, -122.043321, 37.651210, -122.043914, 37.648810 )
+Siward                        Dr       (0, 2, -122.043286, 37.632820, -122.043581, 37.636390 )
+Spoonbill Common                       (0, 2, -122.043662, 37.665220, -122.043425, 37.660650 )
+Crockwood                     Ter      (0, 2, -122.045255, 37.665690, -122.045487, 37.662620 )
+Deep Creek                    Road     (0, 2, -122.049391, 37.640530, -122.049280, 37.638880 )
+Hilsadne                      Ter      (0, 2, -122.046919, 37.641830, -122.046990, 37.644300 )
+Core                          Ter      (0, 2, -122.047353, 37.653910, -122.047303, 37.651850 )
+Marshall                      Ter      (0, 2, -122.026173, 37.676480, -122.026375, 37.673460 )
+Sequoia                       Road     (0, 2, -122.001300, 37.617000, -122.001192, 37.613460 )
+Tacchella                     Way      (0, 2, -121.994785, 37.624960, -121.994311, 37.614170 )
+Heritage                      Ter      (0, 2, -121.997217, 37.550760, -121.997913, 37.543190 )
+Cherry                        Lane     (0, 2, -121.966799, 37.630850, -121.966874, 37.633730 )
+Ridgeview                     Ter      (0, 2, -121.970517, 37.635590, -121.970862, 37.633320 )
+Rosegate                      Ter      (0, 2, -121.969628, 37.632630, -121.970152, 37.638000 )
+Bell                          St       (0, 2, -121.991260, 37.491600, -121.991407, 37.492150 )
+Eugene                        St       (0, 2, -121.963937, 37.386280, -121.964200, 37.383000 )
+Morada                        Ct       (0, 2, -121.935410, 37.492480, -121.935229, 37.491550 )
+Walnut                        Ave      (0, 2, -121.983083, 37.441590, -121.983361, 37.438690 )
+Sundale                       Dr       (0, 2, -121.982028, 37.415950, -121.982310, 37.412540 )
+Stratton Common                        (0, 2, -121.983399, 37.432260, -121.983217, 37.430970 )
+Leslie                        St       (0, 2, -121.972900, 37.438000, -121.971866, 37.438470 )
+Hemlock                       Ter      (0, 2, -121.986875, 37.256490, -121.986744, 37.255710 )
+Fremont                       Blvd     (0, 2, -121.955914, 37.220710, -121.955600, 37.213000 )
+Rosewood Common                        (0, 2, -121.964615, 37.217890, -121.964292, 37.213000 )
+Ashwood Common                         (0, 2, -121.962832, 37.210860, -121.963052, 37.210670 )
+Drew                          Ter      (0, 2, -121.956729, 37.219230, -121.956991, 37.218080 )
+Coit                          Ave      (0, 2, -121.924500, 37.352000, -121.924594, 37.362000 )
+Sioux                         Ct       (0, 2, -121.926147, 37.143740, -121.924800, 37.122000 )
+Mission                       Blvd     (0, 2, -121.924901, 37.929120, -121.925400, 37.922000 )
+Hackamore Com                          (0, 2, -121.923300, 37.864000, -121.922542, 37.864110 )
+Sylvaner                      Way      (0, 2, -121.907301, 37.675960, -121.908111, 37.674190 )
+Severn                        Dr       (0, 2, -122.035900, 37.584000, -122.036800, 37.571000 )
+Dichondra                     Pl       (0, 2, -122.009872, 37.311280, -122.009205, 37.314380 )
+Foothill                      Road     (0, 2, -121.923245, 37.816890, -121.923600, 37.820000 )
+Arroyo                        Road     (0, 2, -121.766960, 37.711200, -121.766870, 37.709400 )
+Fenwick                       Way      (0, 2, -121.947144, 37.201160, -121.945100, 37.202000 )
+San Ramon                     Road     (0, 2, -121.937288, 37.091640, -121.937307, 37.092200 )
+Santo                         Ct       (0, 2, -121.946172, 37.108690, -121.947136, 37.103670 )
+Champagne                     Pl       (0, 2, -121.950378, 37.113760, -121.949579, 37.111740 )
+Rothchild                     Ct       (0, 2, -121.950257, 37.084520, -121.951086, 37.081150 )
+Inglewood                     Dr       (0, 2, -121.909484, 37.877620, -121.909900, 37.878000 )
+Alamo Canal                            (0, 2, -121.910434, 37.734760, -121.910100, 37.726000 )
+Parkside                      Dr       (0, 2, -121.886474, 37.833250, -121.886300, 37.834000 )
+Del Valle                     Pkwy     (0, 2, -121.875441, 37.660040, -121.874759, 37.657280 )
+Corte de Flores                        (0, 2, -121.908126, 37.710730, -121.909240, 37.713910 )
+Paseo Santa Cruz                       (0, 2, -121.906145, 37.661720, -121.905945, 37.658170 )
+Camino Segura                          (0, 2, -121.900094, 37.716470, -121.900200, 37.726000 )
+Calle de la Mesa                       (0, 2, -121.905106, 37.715320, -121.906643, 37.699770 )
+Calle Alegre                           (0, 2, -121.895178, 37.719750, -121.894600, 37.725000 )
+Trimingham                    Dr       (0, 2, -121.871338, 37.763960, -121.872239, 37.767360 )
+Vine                          St       (0, 2, -121.860300, 37.640360, -121.860100, 37.640440 )
+St Michael                    Cir      (0, 2, -121.853648, 37.625100, -121.853779, 37.617960 )
+Randicik                      Ct       (0, 2, -121.863424, 37.958180, -121.863445, 37.965400 )
+Guthrie                       St       (0, 2, -121.861539, 37.977960, -121.860503, 37.977810 )
+Ballentine                    Dr       (0, 2, -121.859765, 37.968250, -121.860477, 37.967840 )
+Arroyo Las Positas                     (0, 2, -121.858962, 37.949250, -121.858919, 37.958780 )
+Manchester                    St       (0, 2, -121.866725, 37.989730, -121.866734, 37.982550 )
+Kirkcaldy                     St       (0, 2, -121.859937, 37.974430, -121.860100, 37.974410 )
+Pyramid                       St       (0, 2, -121.768592, 37.561640, -121.768329, 37.561980 )
+Galloway Common                        (0, 2, -121.745900, 37.180000, -121.745647, 37.170490 )
+Cassiopia                     St       (0, 2, -121.735979, 37.183110, -121.735979, 37.190690 )
+Hermitage                     Ct       (0, 2, -121.729500, 37.263000, -121.729426, 37.268190 )
+Autumn Oak                    Dr       (0, 2, -121.749435, 37.091870, -121.749806, 37.100650 )
+Springtown                    Blvd     (0, 2, -121.743242, 37.075680, -121.745000, 37.088000 )
+Rhododendron                  Dr       (0, 2, -121.744190, 37.096450, -121.744500, 37.105000 )
+Hagemann                      Dr       (0, 2, -121.799530, 37.881180, -121.799500, 37.883000 )
+Pomona                        Way      (0, 2, -121.743614, 37.843570, -121.742700, 37.839000 )
+Pestana                       Pl       (0, 2, -121.757800, 37.840000, -121.757776, 37.846440 )
+Charlotte                     Way      (0, 2, -121.733871, 37.880440, -121.734295, 37.880510 )
+Pulsar                        Ave      (0, 2, -121.789200, 37.589550, -121.789200, 37.600110 )
+Shannon                       Ave      (0, 2, -121.942180, 37.141370, -121.941800, 37.142000 )
+Chardonnay                    Dr       (0, 2, -121.846993, 37.642010, -121.846448, 37.641460 )
+Mac Arthur                    Blvd     (0, 2, -122.252491, 37.174730, -122.253000, 37.182000 )
+Boar                          Cir      (0, 2, -121.912463, 37.086670, -121.912335, 37.090520 )
+A                             St       (0, 2, -122.103419, 37.667000, -122.103439, 37.667000 )
+A                             St       (0, 3, -122.103913, 37.666320, -122.104037, 37.666110 , -122.104051, 37.666090)
+Hayward                       Blvd     (0, 3, -122.050043, 37.585830, -122.048400, 37.574000 , -122.047000, 37.556000)
+Elk                           Ct       (0, 2, -121.912463, 37.086670, -121.913575, 37.091250 )
+Garin                         Ave      (0, 2, -122.033003, 37.284200, -122.032000, 37.282000 )
+Wp Railroad                            (0, 2, -121.813721, 37.756180, -121.804900, 37.765000 )
+Isabel                        Ave      (0, 2, -121.804700, 37.633010, -121.804700, 37.632480 )
+Lomitas                       Ave      (0, 3, -121.773262, 37.590410, -121.773080, 37.590420 , -121.772998, 37.590430)
+South Front                   Road     (0, 2, -121.723953, 37.079700, -121.722000, 37.062000 )
+Livermore                     Ave      (0, 2, -121.772719, 37.990850, -121.772800, 37.001000 )
+Las Positas                   Road     (0, 2, -121.764488, 37.991990, -121.755690, 37.020220 )
+Doolittle                     Dr       (0, 2, -122.193162, 37.217470, -122.193000, 37.216000 )
+Dublin Canyon                 Road     (0, 2, -121.944280, 37.953990, -121.943721, 37.955270 )
+Trenery                       Dr       (0, 2, -121.866449, 37.880450, -121.866515, 37.880440 )
+Foothill Knolls               Dr       (0, 2, -121.909407, 37.659990, -121.909400, 37.657000 )
+Happy Valley                  Road     (0, 2, -121.877541, 37.356520, -121.871788, 37.336360 )
+Amador Valley                 Ct       (0, 2, -121.936500, 37.068000, -121.937909, 37.063750 )
+Creekside                     Dr       (0, 2, -121.926024, 37.887740, -121.926337, 37.886630 )
+Dublin                        Blvd     (0, 2, -121.945852, 37.987120, -121.944625, 37.991870 )
+Rolling Hills                 Dr       (0, 2, -121.950806, 37.103040, -121.950378, 37.113760 )
+Las Positas                   Road     (0, 2, -121.754800, 37.025000, -121.750000, 37.017780 )
+Creekside                     Dr       (0, 2, -121.924308, 37.893850, -121.925368, 37.890080 )
+Wp Railroad                            (0, 2, -121.882149, 37.574580, -121.881300, 37.585000 )
+Old Bernal                    Ave      (0, 2, -121.879084, 37.580270, -121.878944, 37.579930 )
+Pleasanton                    Ave      (0, 2, -121.881900, 37.586000, -121.882761, 37.575300 )
+Eilene                        Dr       (0, 2, -121.869254, 37.870190, -121.869113, 37.872530 )
+Eilene                        Dr       (0, 2, -121.869618, 37.865920, -121.869389, 37.868610 )
+Castille                      Lane     (0, 2, -122.082600, 37.811000, -122.082655, 37.812400 )
+Kamp                          Dr       (0, 2, -121.867789, 37.823260, -121.867753, 37.829200 )
+Palmer                        Dr       (0, 2, -121.866010, 37.858730, -121.865793, 37.847780 )
+Cameron                       Ave      (0, 2, -121.867320, 37.843100, -121.865836, 37.843710 )
+Maple Leaf                    Dr       (0, 2, -121.864615, 37.812760, -121.863868, 37.792830 )
+Magnolia                      Cir      (0, 2, -121.868186, 37.810150, -121.866849, 37.806830 )
+Bernal                        Ave      (0, 2, -121.855600, 37.668000, -121.856260, 37.686560 )
+Sylvia                        Cir      (0, 2, -121.860125, 37.643500, -121.860300, 37.643250 )
+Capitan                       Dr       (0, 2, -121.844880, 37.656950, -121.844580, 37.651950 )
+Tioga                         Ct       (0, 2, -121.847804, 37.666700, -121.848197, 37.669780 )
+Sp Railroad                            (0, 2, -121.893564, 37.990090, -121.897000, 37.016000 )
+Tassajara Creek                        (0, 2, -121.878660, 37.988980, -121.878200, 37.015000 )
+Sp Railroad                            (0, 2, -121.879580, 37.886030, -121.880675, 37.893960 )
+Springhouse                   Dr       (0, 2, -121.883683, 37.895870, -121.880871, 37.891250 )
+Shasta                        St       (0, 2, -121.802842, 37.889000, -121.802801, 37.882600 )
+Inverness                     Way      (0, 2, -121.752134, 37.924120, -121.751744, 37.924130 )
+Murrieta                      Blvd     (0, 2, -121.788409, 37.809060, -121.788500, 37.815000 )
+Andrea                        Cir      (0, 2, -121.733218, 37.886410, -121.733286, 37.906170 )
+Chris Commons                          (0, 2, -121.736470, 37.894370, -121.736359, 37.886650 )
+Kimberly Commons                       (0, 2, -121.737774, 37.887690, -121.737673, 37.890240 )
+Beverly                       St       (0, 2, -121.736023, 37.851770, -121.737956, 37.846320 )
+Cindy                         Lane     (0, 2, -121.734600, 37.824400, -121.734673, 37.830420 )
+Jaquiline                     St       (0, 2, -121.725054, 37.805970, -121.723604, 37.804490 )
+Livermore Commons                      (0, 2, -121.926632, 37.309900, -121.926508, 37.301130 )
+De Brum Commons                        (0, 2, -121.924934, 37.308720, -121.924728, 37.300140 )
+Kaiser                        Dr       (0, 2, -122.067163, 37.478210, -122.060402, 37.519610 )
+Tupelo                        Ter      (0, 2, -122.059087, 37.611300, -122.057021, 37.599420 )
+Buckner                       Ter      (0, 2, -122.060105, 37.625040, -122.059743, 37.623260 )
+Lowry                         Road     (0, 3, -122.052627, 37.833390, -122.053100, 37.827000 , -122.053800, 37.818000)
+Princeton                     Ter      (0, 2, -121.977476, 37.611020, -121.977871, 37.610660 )
+Fontes                        Dr       (0, 2, -121.933352, 37.407680, -121.932510, 37.408870 )
+Hooper                        St       (0, 2, -121.955964, 37.280790, -121.955820, 37.275070 )
+Vineyard                      Ave      (0, 2, -121.853754, 37.648000, -121.853805, 37.648000 )
+Inglewood Common                       (0, 2, -121.955843, 37.367290, -121.955147, 37.369230 )
+France                        Road     (0, 2, -122.045563, 37.682900, -122.045452, 37.682920 )
+Blue Coral                             (0, 2, -121.965392, 37.695090, -121.965261, 37.701320 )
+Wilson                        Cir      (0, 2, -122.246849, 37.814630, -122.246800, 37.815000 )
+Corte Munras                           (0, 2, -121.900576, 37.744520, -121.900804, 37.748890 )
+Via de Los Cerros                      (0, 2, -121.901117, 37.721200, -121.900094, 37.716470 )
+Parish                        Ct       (0, 2, -122.236048, 37.333870, -122.235406, 37.330700 )
+Kilkenny                      Pl       (0, 2, -122.251002, 37.405350, -122.250800, 37.403000 )
+Niles Canyon                  Road     (0, 2, -121.881448, 37.928650, -121.881200, 37.929000 )
+Sheridan                      Road     (0, 2, -121.901114, 37.589140, -121.899000, 37.574000 )
+Pacific                       Ave      (0, 2, -121.754552, 37.761770, -121.753824, 37.761640 )
+Cerro Vista                   Pl       (0, 2, -121.734020, 37.740080, -121.735461, 37.739550 )
+Vasco                         Road     (0, 2, -121.717700, 37.706740, -121.717700, 37.682280 )
+Shady Creek                   Road     (0, 2, -121.913106, 37.247010, -121.914087, 37.244750 )
+Stagecoach                    Road     (0, 2, -121.921401, 37.270490, -121.921800, 37.277000 )
+Jade                          Cir      (0, 2, -121.919585, 37.256990, -121.918784, 37.244170 )
+Turquoise                     St       (0, 2, -121.918671, 37.203470, -121.918562, 37.201730 )
+Dorthea                       Ct       (0, 2, -121.906400, 37.519000, -121.908617, 37.523690 )
+Racoon Hallow                 Ct       (0, 2, -121.914577, 37.636030, -121.913452, 37.637380 )
+Crystal                       Lane     (0, 2, -121.868866, 37.507630, -121.870709, 37.510240 )
+Independence                  Ave      (0, 2, -121.865283, 37.461380, -121.863400, 37.446000 )
+Dolores                       Dr       (0, 2, -121.875651, 37.527050, -121.873840, 37.523240 )
+Happy Valley                  Road     (0, 2, -121.879849, 37.364610, -121.877975, 37.358040 )
+Rhine                         Way      (0, 2, -121.841120, 37.585180, -121.840944, 37.563630 )
+Altimirano                    Dr       (0, 2, -121.878100, 37.019300, -121.871300, 37.017070 )
+Alameda Creek                          (0, 2, -121.909502, 37.938920, -121.909000, 37.940000 )
+Foothill                      Lane     (0, 2, -121.905417, 37.523030, -121.905087, 37.516590 )
+Alisal                        St       (0, 2, -121.869250, 37.349790, -121.868500, 37.326000 )
+Abbie                         St       (0, 2, -121.867486, 37.542430, -121.868000, 37.545000 )
+Nelson                        Ct       (0, 2, -121.864163, 37.468400, -121.864360, 37.474500 )
+Constitution                  Dr       (0, 2, -121.816179, 37.011780, -121.816179, 37.010230 )
+Gray Fox                      Cir      (0, 2, -121.841220, 37.600720, -121.841235, 37.601610 )
+Vasco                         Road     (0, 2, -121.717311, 37.993160, -121.717400, 37.971000 )
+Preston                       Ct       (0, 2, -121.733536, 37.014470, -121.733204, 37.009550 )
+1st                           St       (0, 2, -121.755080, 37.892940, -121.753581, 37.900310 )
+Sherry                        Ct       (0, 2, -121.764200, 37.736000, -121.764370, 37.739580 )
+Claret                        Road     (0, 2, -121.757012, 37.725680, -121.757266, 37.713910 )
+Stadium                       Way      (0, 2, -121.758155, 37.667150, -121.757912, 37.667280 )
+Wildcat                       Ct       (0, 2, -121.946930, 37.087670, -121.947193, 37.082950 )
+Rollinghills                  Cir      (0, 2, -121.945273, 37.064040, -121.945249, 37.067100 )
+Rolling Hills                 Dr       (0, 2, -121.948386, 37.071260, -121.947082, 37.071030 )
+Claremont                     Ave      (0, 2, -122.243294, 37.593180, -122.243400, 37.590000 )
+Doolittle                     Dr       (0, 2, -122.224088, 37.448000, -122.222600, 37.448000 )
+Industrial                    Pkwy     (0, 2, -122.055708, 37.218920, -122.055900, 37.217000 )
+Hackamore                     Lane     (0, 2, -121.924324, 37.855940, -121.923900, 37.857000 )
+Hollyhock                     Dr       (0, 2, -122.131813, 37.996330, -122.130600, 37.997000 )
+Sp Railroad                            (0, 3, -122.178940, 37.323860, -122.179281, 37.318270 , -122.180700, 37.295000)
+Jessica                       Cir      (0, 2, -122.047200, 37.760000, -122.048800, 37.748000 )
+Briscoe                       Ter      (0, 2, -121.948491, 37.418400, -121.948634, 37.416450 )
+Paseo Padre                   Pkwy     (0, 2, -121.960768, 37.442480, -121.959900, 37.437000 )
+Clarke                        Lane     (0, 2, -122.236271, 37.292020, -122.236552, 37.289060 )
+Doolittle                     Dr       (0, 2, -122.174643, 37.983410, -122.173500, 37.965000 )
+Sunnybank                     Pl       (0, 2, -122.051879, 37.785030, -122.052000, 37.782000 )
+Polaris                       Ave      (0, 2, -122.064185, 37.845620, -122.064700, 37.840000 )
+Siward                        Dr       (0, 2, -122.047527, 37.717640, -122.047500, 37.716000 )
+Siward                        Dr       (0, 2, -122.044259, 37.646170, -122.044499, 37.649300 )
+Bowie Common                           (0, 2, -122.042847, 37.645320, -122.042808, 37.644840 )
+Ralston Com                            (0, 2, -121.977500, 37.428000, -121.977100, 37.432000 )
+Hackamore                     Lane     (0, 2, -121.922474, 37.862330, -121.922300, 37.863000 )
+Iglesia                       Dr       (0, 2, -121.946443, 37.094380, -121.946833, 37.092850 )
+Ballantyne                    Dr       (0, 2, -121.858907, 37.985000, -121.858500, 37.985000 )
+Delaware                      Way      (0, 2, -121.786362, 37.929590, -121.786300, 37.930000 )
+Stanton                       Ave      (0, 2, -122.100392, 37.069700, -122.099513, 37.060520 )
+Villareal                     Dr       (0, 2, -122.019699, 37.150040, -122.018295, 37.159120 )
+Railroad                      Ave      (0, 2, -121.771533, 37.824720, -121.771000, 37.826000 )
+Oakland                       Ave      (0, 2, -121.870300, 37.853050, -121.870300, 37.866000 )
+Old Santa Rita                Road     (0, 2, -121.877505, 37.942900, -121.877600, 37.960000 )
+Adelina Common                         (0, 2, -121.925847, 37.299850, -121.925765, 37.294930 )
+Harbor Bay                    Pkwy     (0, 3, -122.233076, 37.252250, -122.236357, 37.251500 , -122.238500, 37.251000)
+Almond                        Ave      (0, 2, -121.738700, 37.755270, -121.738700, 37.741180 )
+Crellin                       Road     (0, 3, -121.846446, 37.591890, -121.846000, 37.591000 , -121.845775, 37.590730)
+Dublin                        Road     (0, 2, -121.955087, 37.975290, -121.946646, 37.948580 )
+Jones                         St       (0, 2, -122.301984, 37.747550, -122.301500, 37.749000 )
+Embarcadero                            (0, 2, -122.252700, 37.894000, -122.246486, 37.869080 )
+Mtn House Creek                        (0, 3, -121.558979, 37.696720, -121.558765, 37.691230 , -121.564396, 37.650950)
+Sydney                        Cir      (0, 2, -122.098211, 37.076520, -122.097230, 37.065470 )
+Christensen                   Lane     (0, 2, -122.085026, 37.064630, -122.084400, 37.064000 )
+Redwood                       Road     (0, 2, -122.073600, 37.393000, -122.075736, 37.376340 )
+Villareal                     Dr       (0, 2, -122.022433, 37.102660, -122.023425, 37.092940 )
+Mount Hamilton                Ct       (0, 2, -122.030097, 37.113480, -122.030412, 37.110490 )
+Fall Creek                    Road     (0, 2, -121.910898, 37.270200, -121.909729, 37.255840 )
+Western Pacific Railroad               (0, 2, -121.628112, 37.314060, -121.628315, 37.315240 )
+South Front                   Road     (0, 2, -121.738379, 37.021830, -121.737947, 37.023350 )
+Depot                         Road     (0, 2, -122.127518, 37.380800, -122.128400, 37.380000 )
+Deer Oaks                     Dr       (0, 2, -121.909133, 37.540920, -121.907389, 37.545440 )
+Alameda Creek                          (0, 2, -121.930593, 37.937850, -121.930096, 37.940110 )
+Strong                        Way      (0, 2, -122.102446, 37.048850, -122.101424, 37.053370 )
+Greenhills                             (0, 2, -122.082437, 37.055510, -122.083750, 37.055570 )
+Angus                         Way      (0, 2, -122.098774, 37.865350, -122.098600, 37.861000 )
+B                             St       (0, 2, -121.892400, 37.951330, -121.892400, 37.952000 )
+3rd                           St       (0, 2, -121.892355, 37.957590, -121.890800, 37.957000 )
+Castro Valley                 Blvd     (0, 2, -122.049131, 37.960680, -122.048358, 37.963770 )
+Clausen                       Ct       (0, 2, -122.040846, 37.675800, -122.040845, 37.669130 )
+Eden Creek                             (0, 2, -122.022037, 37.006750, -122.022100, 37.998000 )
+Las Palmas                    Ct       (0, 2, -121.950103, 37.005820, -121.949498, 37.008350 )
+Mines                         Road     (0, 2, -121.536341, 37.000000, -121.536966, 37.001980 )
+San Lorenzo Creek                      (0, 2, -122.117293, 37.858680, -122.120139, 37.857390 )
+Coyote River                           (0, 2, -121.931582, 37.607070, -121.932309, 37.608240 )
+I- 205                                 (0, 9, -121.572819, 37.421070, -121.571705, 37.421680 , -121.563557, 37.426410, -121.560856, 37.428850, -121.559467, 37.429460, -121.559055, 37.429390, -121.558781, 37.429920, -121.556644, 37.432140, -121.555900, 37.434000)
+I- 205                                 (1, 3, -121.573292, 37.417260, -121.571644, 37.420000 , -121.563557, 37.426410)
+I- 580                                 (0, 4, -121.560856, 37.428850, -121.558430, 37.404060 , -121.557655, 37.399260, -121.556000, 37.389000)
+I- 880                                 (0, 6, -121.948000, 37.933000, -121.947100, 37.925000 , -121.946700, 37.923000, -121.946000, 37.918000, -121.945200, 37.912000, -121.937000, 37.852000)
+I- 880                        Ramp     (0, 3, -121.946000, 37.918000, -121.946300, 37.911000 , -121.945200, 37.912000)
+I- 880                        Ramp     (1, 3, -121.947700, 37.910000, -121.946900, 37.911000 , -121.946300, 37.911000)
+I- 880                                 (0, 2, -121.937000, 37.852000, -121.936800, 37.848000 )
+I- 880                        Ramp     (0, 6, -121.934300, 37.850000, -121.937000, 37.852000 , -121.937000, 37.836000, -121.936200, 37.835000, -121.935900, 37.826000, -121.934900, 37.813000)
+I- 880                        Ramp     (1, 2, -121.936800, 37.848000, -121.936200, 37.835000 )
+I- 880                        Ramp     (0, 4, -121.933500, 37.851000, -121.933900, 37.847000 , -121.935100, 37.835000, -121.935700, 37.830000)
+I- 880                        Ramp     (1, 2, -121.937600, 37.834000, -121.937000, 37.836000 )
+I- 880                        Ramp     (1, 2, -121.936000, 37.837000, -121.935100, 37.835000 )
+I- 880                                 (0, 10, -121.935700, 37.830000, -121.935600, 37.826000 , -121.935100, 37.819000, -121.934900, 37.813000, -121.933700, 37.788000, -121.932700, 37.767000, -121.923200, 37.570000, -121.922900, 37.563000, -121.922900, 37.561000, -121.922487, 37.554120)
+I- 880                        Ramp     (1, 3, -121.935100, 37.819000, -121.935000, 37.828000 , -121.933900, 37.847000)
+I- 880                        Ramp     (1, 2, -121.934900, 37.813000, -121.935000, 37.828000 )
+I- 680                        Ramp     (0, 8, -121.926500, 37.998000, -121.924900, 37.980000 , -121.922700, 37.963000, -121.922100, 37.959000, -121.921400, 37.954000, -121.920600, 37.947000, -121.920100, 37.944000, -121.918400, 37.934000)
+I- 680                        Ramp     (0, 7, -121.926500, 37.998000, -121.924200, 37.983000 , -121.922383, 37.978600, -121.919800, 37.975000, -121.919500, 37.954000, -121.918700, 37.941000, -121.918400, 37.934000)
+I- 680                        Ramp     (0, 3, -121.921000, 37.965000, -121.922000, 37.966000 , -121.921400, 37.961000)
+I- 680                        Ramp     (1, 5, -121.921000, 37.965000, -121.919800, 37.960000 , -121.920800, 37.957000, -121.919900, 37.951000, -121.918700, 37.941000)
+I- 680                        Ramp     (1, 3, -121.924700, 37.932000, -121.921100, 37.944000 , -121.920100, 37.944000)
+I- 680                                 (0, 4, -121.918400, 37.934000, -121.917000, 37.913000 , -121.912200, 37.830000, -121.905200, 37.702000)
+I- 880                        Ramp     (0, 3, -121.923200, 37.570000, -121.923400, 37.559000 , -121.922900, 37.561000)
+I- 880                        Ramp     (0, 3, -121.921500, 37.556000, -121.921790, 37.557450 , -121.922900, 37.563000)
+I- 680                        Ramp     (0, 3, -121.905200, 37.702000, -121.904700, 37.667000 , -121.903435, 37.658820)
+I- 680                        Ramp     (0, 3, -121.905000, 37.699000, -121.900400, 37.677000 , -121.901610, 37.648980)
+I- 680                        Ramp     (0, 3, -121.902700, 37.672000, -121.902847, 37.671500 , -121.901610, 37.648980)
+I- 680                                 (0, 2, -121.902447, 37.646950, -121.903435, 37.658820 )
+I- 580                                 (0, 3, -121.664341, 37.182200, -121.666965, 37.181660 , -121.669700, 37.185000)
+I- 580                        Ramp     (0, 5, -121.660816, 37.189520, -121.659428, 37.191050 , -121.658115, 37.192720, -121.657078, 37.196390, -121.654926, 37.205310)
+I- 580                        Ramp     (0, 6, -121.657688, 37.200890, -121.658176, 37.200890 , -121.659977, 37.197150, -121.660232, 37.196500, -121.661524, 37.189960, -121.662081, 37.186930)
+I- 580                                 (0, 5, -121.628757, 37.342870, -121.600742, 37.384070 , -121.588856, 37.401240, -121.580494, 37.414510, -121.572819, 37.421070)
+I- 580                                 (0, 6, -121.628757, 37.342870, -121.630009, 37.337910 , -121.657688, 37.200890, -121.658827, 37.195520, -121.659626, 37.193260, -121.660816, 37.189520)
+I- 580                                 (1, 7, -121.628147, 37.330890, -121.629246, 37.324640 , -121.644337, 37.241100, -121.654377, 37.207980, -121.654356, 37.207230, -121.654926, 37.205310, -121.658827, 37.195520)
+I- 580                                 (1, 7, -121.628147, 37.330890, -121.605091, 37.360500 , -121.589344, 37.394600, -121.581180, 37.410620, -121.573292, 37.417260, -121.571644, 37.417790, -121.558430, 37.404060)
+I- 580                        Ramp     (0, 4, -121.743800, 37.024000, -121.742961, 37.028960 , -121.741600, 37.037000, -121.740000, 37.039000)
+I- 580                        Ramp     (0, 5, -121.740000, 37.034000, -121.740900, 37.034000 , -121.740100, 37.029000, -121.738400, 37.032000, -121.735800, 37.034000)
+I- 580                        Ramp     (0, 3, -121.741100, 37.024000, -121.740100, 37.018000 , -121.740100, 37.024000)
+I- 580                        Ramp     (1, 3, -121.740000, 37.036000, -121.739300, 37.033000 , -121.738400, 37.032000)
+I- 580                        Ramp     (0, 2, -121.739000, 37.020000, -121.740200, 37.015000 )
+I- 580                        Ramp     (0, 2, -121.725500, 37.083000, -121.723200, 37.114000 )
+I- 580                        Ramp     (0, 2, -121.723200, 37.103000, -121.723400, 37.092000 )
+I- 580                        Ramp     (0, 2, -121.723100, 37.108000, -121.721100, 37.102000 )
+I- 580                                 (0, 22, -121.727000, 37.074000, -121.722900, 37.093000 , -121.722301, 37.095220, -121.721001, 37.100050, -121.719400, 37.106000, -121.718800, 37.109000, -121.716800, 37.120000, -121.716300, 37.123000, -121.714500, 37.127000, -121.709600, 37.148000, -121.707731, 37.156800, -121.705800, 37.166000, -121.705500, 37.168000, -121.704400, 37.174000, -121.703800, 37.172000, -121.703700, 37.172000, -121.702700, 37.175000, -121.700100, 37.181000, -121.695700, 37.191000, -121.694800, 37.192000, -121.689700, 37.204000, -121.669700, 37.185000)
+I- 580                        Ramp     (0, 3, -121.723200, 37.103000, -121.722200, 37.103000 , -121.721995, 37.098590)
+I- 580                                 (0, 3, -121.727000, 37.074000, -121.727500, 37.072000 , -121.733100, 37.046000)
+I- 580                                 (1, 8, -121.727000, 37.074000, -121.725500, 37.083000 , -121.723400, 37.092000, -121.723000, 37.095000, -121.721995, 37.098590, -121.721600, 37.100000, -121.721100, 37.102000, -121.718800, 37.109000)
+I- 580                        Ramp     (0, 7, -121.727000, 37.074000, -121.723700, 37.084000 , -121.723311, 37.082300, -121.722100, 37.077000, -121.721900, 37.081000, -121.720744, 37.092570, -121.719400, 37.106000)
+I- 80                                  (0, 11, -122.308000, 37.980000, -122.306600, 37.943000 , -122.305600, 37.912000, -122.305700, 37.908000, -122.305805, 37.905110, -122.306100, 37.897000, -122.306400, 37.893000, -122.307300, 37.881000, -122.307300, 37.877000, -122.307700, 37.861000, -122.307600, 37.831000)
+I- 80                         Ramp     (0, 2, -122.307800, 37.930000, -122.308100, 37.879000 )
+I- 80                         Ramp     (0, 2, -122.307200, 37.916000, -122.307400, 37.896000 )
+I- 80                         Ramp     (0, 2, -122.308800, 37.885000, -122.307600, 37.831000 )
+I- 80                         Ramp     (0, 2, -122.306900, 37.925000, -122.306500, 37.935000 )
+I- 80                                  (0, 4, -122.306500, 37.935000, -122.306500, 37.913000 , -122.306700, 37.905000, -122.307000, 37.902000)
+I- 80                         Ramp     (0, 2, -122.305100, 37.910000, -122.305600, 37.912000 )
+I- 80                                  (1, 5, -122.307000, 37.902000, -122.307400, 37.896000 , -122.307512, 37.893270, -122.308100, 37.879000, -122.307700, 37.861000)
+I- 80                         Ramp     (0, 2, -122.307000, 37.902000, -122.307300, 37.881000 )
+I- 80                         Ramp     (0, 2, -122.305100, 37.910000, -122.305700, 37.908000 )
+I- 80                                  (0, 2, -122.307500, 37.828000, -122.307600, 37.831000 )
+I- 80                                  (0, 5, -122.307500, 37.828000, -122.307700, 37.822000 , -122.307400, 37.817000, -122.306900, 37.801000, -122.306800, 37.795000)
+I- 80                         Ramp     (0, 4, -122.306000, 37.876000, -122.306600, 37.869000 , -122.306700, 37.843000, -122.306900, 37.818000)
+I- 80                         Ramp     (0, 3, -122.306800, 37.795000, -122.306900, 37.782000 , -122.306200, 37.774000)
+I- 80                         Ramp     (0, 3, -122.306800, 37.795000, -122.306000, 37.783000 , -122.306200, 37.774000)
+I- 80                                  (0, 2, -122.306200, 37.774000, -122.303800, 37.695000 )
+I- 80                         Ramp     (0, 5, -122.303800, 37.695000, -122.303600, 37.674000 , -122.303400, 37.667000, -122.303300, 37.661000, -122.302300, 37.644000)
+I- 80                         Ramp     (0, 4, -122.303800, 37.695000, -122.302100, 37.670000 , -122.302200, 37.662000, -122.302300, 37.644000)
+I- 80                         Ramp     (1, 2, -122.301700, 37.670000, -122.302200, 37.662000 )
+I- 80                                  (0, 2, -122.302300, 37.644000, -122.298800, 37.518000 )
+I- 80                         Ramp     (0, 5, -122.298800, 37.518000, -122.299000, 37.500000 , -122.299400, 37.488000, -122.299500, 37.477000, -122.297100, 37.452000)
+I- 80                         Ramp     (0, 7, -122.298400, 37.506000, -122.297700, 37.502000 , -122.297900, 37.499000, -122.295600, 37.489000, -122.296200, 37.483000, -122.296500, 37.478000, -122.297100, 37.452000)
+I- 80                         Ramp     (1, 2, -122.298300, 37.501000, -122.297900, 37.499000 )
+I- 80                                  (0, 5, -122.298100, 37.492000, -122.297800, 37.480000 , -122.297600, 37.473000, -122.297100, 37.452000, -122.296200, 37.413000)
+I- 80                         Ramp     (1, 3, -122.294400, 37.491000, -122.295600, 37.484000 , -122.296500, 37.478000)
+I- 80                         Ramp     (0, 3, -122.296200, 37.413000, -122.295900, 37.382000 , -122.295100, 37.372000)
+I- 80                         Ramp     (0, 3, -122.295700, 37.396000, -122.294600, 37.384000 , -122.294800, 37.367000)
+I- 80                                  (0, 3, -122.308300, 37.249000, -122.328100, 37.216000 , -122.348000, 37.175000)
+I- 80                         Ramp     (0, 2, -122.305100, 37.254000, -122.308300, 37.249000 )
+I- 80                         Ramp     (0, 2, -122.304400, 37.250000, -122.305100, 37.254000 )
+I- 80                         Ramp     (0, 2, -122.304400, 37.250000, -122.308300, 37.249000 )
+I- 80                                  (1, 4, -122.300400, 37.264000, -122.301600, 37.262000 , -122.305100, 37.254000, -122.308300, 37.249000)
+I- 80                                  (1, 2, -122.296200, 37.273000, -122.300400, 37.264000 )
+I- 80                                  (1, 2, -122.294900, 37.279000, -122.296200, 37.273000 )
+I- 80                                  (1, 2, -122.293100, 37.301000, -122.294900, 37.279000 )
+I- 580                                 (0, 2, -122.292800, 37.293000, -122.293100, 37.301000 )
+I- 80                                  (1, 2, -122.293700, 37.277000, -122.301600, 37.262000 )
+I- 80                                  (0, 2, -122.293200, 37.284000, -122.293400, 37.280000 )
+I- 80                         Ramp     (0, 2, -122.293400, 37.280000, -122.296200, 37.273000 )
+I- 580                                 (0, 6, -122.261300, 37.230000, -122.260100, 37.228330 , -122.261100, 37.231000, -122.263900, 37.238000, -122.264600, 37.241000, -122.265400, 37.242000)
+I- 80                                  (1, 3, -122.292700, 37.299000, -122.292800, 37.293000 , -122.293200, 37.284000)
+I- 80                         Ramp     (0, 3, -122.292300, 37.282000, -122.292600, 37.283000 , -122.293200, 37.284000)
+I- 80                         Ramp     (0, 2, -122.291800, 37.279000, -122.292600, 37.281000 )
+I- 580                        Ramp     (0, 2, -122.291700, 37.282000, -122.292100, 37.286000 )
+I- 580                        Ramp     (0, 2, -122.291300, 37.278000, -122.291700, 37.282000 )
+I- 580                                 (0, 3, -122.290900, 37.273000, -122.291800, 37.279000 , -122.292300, 37.282000)
+I- 880                        Ramp     (0, 3, -122.291600, 37.052000, -122.289800, 37.040000 , -122.288800, 37.038000)
+I- 580                                 (0, 4, -122.290100, 37.274000, -122.290500, 37.278000 , -122.292100, 37.286000, -122.292700, 37.299000)
+I- 580                        Ramp     (0, 3, -122.267700, 37.252000, -122.267508, 37.253680 , -122.267000, 37.261000)
+I- 580                                 (0, 3, -122.289300, 37.266000, -122.290400, 37.270000 , -122.290900, 37.273000)
+I- 80                         Ramp     (0, 2, -122.288500, 37.254000, -122.289300, 37.266000 )
+I- 80                         Ramp     (0, 5, -122.288300, 37.247000, -122.290400, 37.267000 , -122.290900, 37.273000, -122.291800, 37.275000, -122.293700, 37.277000)
+I- 80                         Ramp     (0, 2, -122.287100, 37.266000, -122.290100, 37.274000 )
+I- 80                         Ramp     (0, 2, -122.288500, 37.254000, -122.288300, 37.247000 )
+I- 580                                 (1, 7, -122.283700, 37.276000, -122.284900, 37.273000 , -122.286000, 37.270000, -122.286800, 37.269000, -122.287100, 37.266000, -122.288200, 37.265000, -122.289300, 37.266000)
+I- 580                        Ramp     (0, 4, -122.280600, 37.275000, -122.281800, 37.280000 , -122.282200, 37.281000, -122.283700, 37.276000)
+I- 580                        Ramp     (1, 3, -122.279600, 37.289000, -122.281800, 37.283000 , -122.282200, 37.281000)
+I- 580                        Ramp     (1, 5, -122.278600, 37.288000, -122.279800, 37.286000 , -122.280400, 37.285000, -122.281400, 37.282000, -122.281800, 37.280000)
+I- 580                                 (1, 9, -122.274400, 37.262000, -122.274600, 37.263000 , -122.277400, 37.270000, -122.278000, 37.271000, -122.279200, 37.274000, -122.280600, 37.275000, -122.281700, 37.276000, -122.282800, 37.276000, -122.283700, 37.276000)
+I- 580                                 (0, 6, -122.274400, 37.262000, -122.273400, 37.259000 , -122.269500, 37.247000, -122.268532, 37.244900, -122.268237, 37.244260, -122.268100, 37.244000)
+I- 580                        Ramp     (0, 2, -122.271600, 37.259000, -122.273400, 37.259000 )
+I- 580                        Ramp     (0, 2, -122.268600, 37.249000, -122.270100, 37.255000 )
+I- 580                        Ramp     (0, 3, -122.269500, 37.247000, -122.269200, 37.244000 , -122.268400, 37.234000)
+I- 580                        Ramp     (0, 3, -122.267600, 37.273000, -122.267900, 37.264000 , -122.267700, 37.252000)
+I- 580                        Ramp     (0, 3, -122.269500, 37.247000, -122.268600, 37.249000 , -122.267700, 37.252000)
+I- 580                        Ramp     (0, 2, -122.268600, 37.249000, -122.268532, 37.244900 )
+I- 580                        Ramp     (0, 3, -122.268600, 37.249000, -122.268200, 37.245000 , -122.268100, 37.244000)
+I- 580                                 (0, 2, -122.267900, 37.248000, -122.268100, 37.248000 )
+I- 580                        Ramp     (0, 2, -122.268300, 37.243000, -122.268500, 37.243000 )
+I- 580                        Ramp     (0, 2, -122.267700, 37.242000, -122.268100, 37.244000 )
+I- 580                        Ramp     (0, 2, -122.267700, 37.242000, -122.268300, 37.243000 )
+I- 980                                 (0, 3, -122.268400, 37.236000, -122.268300, 37.243000 , -122.268237, 37.244260)
+I- 980                                 (0, 2, -122.268100, 37.248000, -122.267700, 37.252000 )
+I- 580                        Ramp     (0, 3, -122.267300, 37.248000, -122.267600, 37.250000 , -122.267700, 37.252000)
+I- 580                        Ramp     (0, 2, -122.267100, 37.245000, -122.267300, 37.248000 )
+I- 580                                 (0, 3, -122.267500, 37.243000, -122.267700, 37.243000 , -122.268100, 37.244000)
+I- 580                        Ramp     (0, 3, -122.267700, 37.242000, -122.268100, 37.238000 , -122.267800, 37.233000)
+I- 980                                 (0, 2, -122.267500, 37.243000, -122.267400, 37.246000 )
+I- 580                        Ramp     (0, 2, -122.267700, 37.242000, -122.267500, 37.243000 )
+I- 580                                 (0, 2, -122.266400, 37.238000, -122.267500, 37.243000 )
+I- 580                        Ramp     (0, 2, -122.267000, 37.261000, -122.267100, 37.263000 )
+I- 580                        Ramp     (0, 2, -122.267100, 37.245000, -122.267500, 37.243000 )
+I- 580                        Ramp     (1, 3, -122.265400, 37.242000, -122.266000, 37.245000 , -122.267100, 37.245000)
+I- 980                        Ramp     (0, 3, -122.273900, 37.117000, -122.274500, 37.119000 , -122.273300, 37.129000)
+I- 980                        Ramp     (0, 7, -122.269100, 37.213000, -122.269438, 37.207090 , -122.269900, 37.199000, -122.270000, 37.194000, -122.270194, 37.183820, -122.270352, 37.175520, -122.270400, 37.173000)
+I- 980                        Ramp     (0, 3, -122.268800, 37.206000, -122.268800, 37.203000 , -122.268400, 37.215000)
+I- 980                                 (0, 3, -122.268800, 37.200000, -122.268800, 37.197000 , -122.268900, 37.191000)
+I- 980                        Ramp     (0, 2, -122.269300, 37.194000, -122.269100, 37.198000 )
+I- 980                        Ramp     (0, 3, -122.268800, 37.197000, -122.269100, 37.191000 , -122.269300, 37.180000)
+I- 980                                 (0, 7, -122.268400, 37.236000, -122.268400, 37.234000 , -122.268817, 37.224580, -122.269027, 37.215570, -122.269100, 37.213000, -122.269400, 37.199000, -122.269300, 37.194000)
+I- 980                                 (1, 3, -122.268000, 37.227000, -122.267800, 37.233000 , -122.267500, 37.243000)
+I- 980                                 (1, 3, -122.268800, 37.200000, -122.268400, 37.215000 , -122.268000, 37.227000)
+I- 580                        Ramp     (0, 5, -122.263900, 37.238000, -122.265900, 37.239000 , -122.266400, 37.238000, -122.267600, 37.231000, -122.268000, 37.227000)
+I- 980                                 (0, 4, -122.270100, 37.163000, -122.269900, 37.172000 , -122.269300, 37.192000, -122.269300, 37.194000)
+I- 980                        Ramp     (0, 2, -122.269900, 37.159000, -122.270100, 37.163000 )
+I- 980                                 (0, 5, -122.269900, 37.159000, -122.269700, 37.165000 , -122.269500, 37.170000, -122.269300, 37.180000, -122.268900, 37.191000)
+I- 880                                 (0, 5, -122.291000, 37.052000, -122.289800, 37.042000 , -122.288800, 37.038000, -122.287800, 37.036000, -122.286300, 37.032000)
+I- 880                        Ramp     (0, 5, -122.286300, 37.032000, -122.285451, 37.030940 , -122.284429, 37.029660, -122.283100, 37.028000, -122.282300, 37.031000)
+I- 880                        Ramp     (0, 3, -122.284900, 37.028000, -122.284500, 37.026000 , -122.283700, 37.021000)
+I- 980                                 (0, 12, -122.278900, 37.009000, -122.279500, 37.022000 , -122.279648, 37.027110, -122.278700, 37.055000, -122.278200, 37.062000, -122.277500, 37.075000, -122.277300, 37.078000, -122.276300, 37.094000, -122.275800, 37.100000, -122.274904, 37.112350, -122.274857, 37.113100, -122.274800, 37.114000)
+I- 980                                 (0, 7, -122.278300, 37.006000, -122.278700, 37.014000 , -122.279100, 37.024000, -122.279124, 37.025400, -122.278200, 37.053000, -122.277800, 37.061000, -122.277300, 37.068000)
+I- 980                        Ramp     (0, 2, -122.277300, 37.078000, -122.276600, 37.095000 )
+I- 980                        Ramp     (0, 2, -122.277300, 37.068000, -122.277200, 37.061000 )
+I- 880                        Ramp     (0, 2, -122.277500, 37.007000, -122.276900, 37.001000 )
+I- 880                        Ramp     (0, 2, -122.277100, 37.002000, -122.278000, 37.000000 )
+I- 880                        Ramp     (0, 2, -122.272900, 37.986000, -122.274400, 37.986000 )
+I- 880                        Ramp     (0, 2, -122.274000, 37.993000, -122.272900, 37.986000 )
+I- 880                                 (0, 17, -122.270700, 37.975000, -122.269300, 37.972000 , -122.268100, 37.966000, -122.267000, 37.962000, -122.265900, 37.957000, -122.264800, 37.952000, -122.263600, 37.946000, -122.262500, 37.935000, -122.261700, 37.927000, -122.260700, 37.921000, -122.259300, 37.916000, -122.258000, 37.911000, -122.253600, 37.898000, -122.243200, 37.858000, -122.240800, 37.845000, -122.238600, 37.827000, -122.237400, 37.811000)
+I- 880                        Ramp     (0, 2, -122.268000, 37.969000, -122.269300, 37.972000 )
+I- 880                        Ramp     (0, 3, -122.268500, 37.962000, -122.269500, 37.969000 , -122.270700, 37.975000)
+I- 580                        Ramp     (1, 3, -122.264600, 37.241000, -122.265300, 37.244000 , -122.266000, 37.245000)
+I- 580                                 (0, 5, -122.261300, 37.230000, -122.263448, 37.233070 , -122.264700, 37.234860, -122.265500, 37.236000, -122.266400, 37.238000)
+I- 580                        Ramp     (0, 2, -122.260400, 37.222000, -122.261300, 37.230000 )
+I- 580                        Ramp     (0, 3, -122.256400, 37.213000, -122.254400, 37.197000 , -122.253800, 37.190000)
+I- 580                        Ramp     (0, 3, -122.256400, 37.213000, -122.254200, 37.199000 , -122.253800, 37.190000)
+I- 580                        Ramp     (0, 4, -122.254700, 37.205000, -122.254000, 37.205000 , -122.253800, 37.202000, -122.253500, 37.196000)
+I- 580                        Ramp     (1, 2, -122.254100, 37.201000, -122.254000, 37.205000 )
+I- 580                                 (0, 2, -122.253900, 37.200000, -122.254100, 37.201000 )
+I- 580                                 (1, 2, -122.253500, 37.196000, -122.253900, 37.200000 )
+I- 580                        Ramp     (0, 2, -122.253500, 37.192000, -122.253900, 37.200000 )
+I- 580                                 (0, 2, -122.253500, 37.196000, -122.253300, 37.196000 )
+I- 580                        Ramp     (0, 2, -122.253500, 37.192000, -122.253000, 37.182000 )
+I- 580                        Ramp     (0, 3, -122.252900, 37.197000, -122.252500, 37.192000 , -122.251700, 37.174000)
+I- 580                        Ramp     (0, 3, -122.250100, 37.148000, -122.249485, 37.140410 , -122.248400, 37.127000)
+I- 580                        Ramp     (0, 2, -122.249400, 37.125000, -122.248900, 37.113000 )
+I- 580                        Ramp     (0, 2, -122.249100, 37.115000, -122.248900, 37.113000 )
+I- 580                        Ramp     (0, 2, -122.249000, 37.121000, -122.247600, 37.120000 )
+I- 880                        Ramp     (0, 2, -122.263600, 37.946000, -122.264600, 37.954000 )
+I- 880                        Ramp     (0, 3, -122.263600, 37.946000, -122.264900, 37.950000 , -122.266200, 37.954000)
+I- 880                        Ramp     (0, 2, -122.257700, 37.914000, -122.258000, 37.911000 )
+I- 880                        Ramp     (0, 2, -122.253600, 37.898000, -122.254000, 37.902000 )
+I- 580                        Ramp     (0, 2, -122.245300, 37.095000, -122.241400, 37.089000 )
+I- 580                        Ramp     (0, 4, -122.242800, 37.096000, -122.241400, 37.091000 , -122.241182, 37.090180, -122.240600, 37.088000)
+I- 580                        Ramp     (0, 3, -122.236500, 37.070000, -122.234600, 37.052000 , -122.233800, 37.044000)
+I- 580                        Ramp     (0, 2, -122.227800, 37.013000, -122.226100, 37.003000 )
+I- 580                        Ramp     (0, 2, -122.227100, 37.001000, -122.226100, 37.003000 )
+I- 580                                 (0, 12, -122.219700, 37.990000, -122.220000, 37.990000 , -122.222092, 37.995230, -122.223200, 37.998000, -122.224146, 37.999630, -122.226100, 37.003000, -122.227800, 37.007000, -122.230200, 37.026000, -122.232300, 37.043000, -122.234400, 37.059000, -122.235405, 37.064270, -122.236500, 37.070000)
+I- 580                        Ramp     (0, 6, -122.215800, 37.985000, -122.217300, 37.989000 , -122.217800, 37.991000, -122.218357, 37.990710, -122.219392, 37.990160, -122.219700, 37.990000)
+I- 580                        Ramp     (0, 4, -122.216100, 37.976000, -122.218000, 37.982000 , -122.218481, 37.984260, -122.219700, 37.990000)
+I- 580                        Ramp     (0, 2, -122.207900, 37.958000, -122.209900, 37.962000 )
+I- 580                        Ramp     (0, 2, -122.208500, 37.962000, -122.209000, 37.966000 )
+I- 580                                 (0, 6, -122.204300, 37.938000, -122.204734, 37.940740 , -122.206200, 37.950000, -122.207492, 37.956080, -122.207900, 37.958000, -122.208500, 37.962000)
+I- 580                                 (1, 3, -122.202900, 37.928000, -122.203600, 37.933000 , -122.204300, 37.938000)
+I- 580                        Ramp     (0, 3, -122.202700, 37.918000, -122.203700, 37.930000 , -122.204300, 37.938000)
+I- 580                        Ramp     (0, 2, -122.201700, 37.923000, -122.202900, 37.928000 )
+I- 580                        Ramp     (0, 5, -122.197500, 37.878000, -122.196458, 37.875160 , -122.196400, 37.875000, -122.195100, 37.871000, -122.194200, 37.869000)
+I- 580                        Ramp     (0, 2, -122.191700, 37.853000, -122.192700, 37.863000 )
+I- 880                        Ramp     (0, 2, -122.237400, 37.811000, -122.237400, 37.796000 )
+I- 880                        Ramp     (0, 4, -122.237200, 37.802000, -122.236380, 37.799600 , -122.236100, 37.801000, -122.235700, 37.801000)
+I- 880                        Ramp     (0, 5, -122.237000, 37.799000, -122.236000, 37.777000 , -122.236000, 37.768000, -122.235700, 37.764000, -122.235000, 37.754000)
+I- 880                        Ramp     (0, 2, -122.236100, 37.785000, -122.235600, 37.781000 )
+I- 880                                 (0, 13, -122.236000, 37.783000, -122.235200, 37.767000 , -122.234800, 37.764000, -122.233900, 37.758000, -122.232900, 37.752000, -122.232300, 37.748000, -122.231900, 37.746000, -122.231600, 37.746000, -122.231200, 37.744000, -122.230100, 37.743000, -122.226900, 37.730000, -122.226700, 37.730000, -122.222000, 37.713000)
+I- 880                        Ramp     (1, 3, -122.235200, 37.767000, -122.235600, 37.768000 , -122.236000, 37.768000)
+I- 880                        Ramp     (0, 4, -122.231600, 37.746000, -122.231600, 37.751000 , -122.232100, 37.752000, -122.232900, 37.752000)
+I- 880                        Ramp     (1, 2, -122.230100, 37.743000, -122.231600, 37.751000 )
+I- 880                        Ramp     (0, 2, -122.232500, 37.743000, -122.231900, 37.746000 )
+I- 880                        Ramp     (0, 2, -122.230100, 37.743000, -122.229400, 37.735000 )
+I- 880                        Ramp     (0, 6, -122.222000, 37.713000, -122.221200, 37.705000 , -122.220700, 37.697000, -122.220800, 37.694000, -122.219200, 37.680000, -122.218400, 37.672000)
+I- 880                                 (0, 13, -122.221400, 37.711000, -122.220200, 37.699000 , -122.219900, 37.695000, -122.219000, 37.682000, -122.218400, 37.672000, -122.217300, 37.652000, -122.215900, 37.638000, -122.214400, 37.616000, -122.213800, 37.612000, -122.213500, 37.609000, -122.212000, 37.592000, -122.211600, 37.586000, -122.211100, 37.581000)
+I- 880                        Ramp     (0, 4, -122.219900, 37.695000, -122.219400, 37.697000 , -122.219700, 37.700000, -122.220200, 37.699000)
+I- 880                        Ramp     (1, 3, -122.218700, 37.684000, -122.219000, 37.688000 , -122.219400, 37.697000)
+I- 880                        Ramp     (0, 3, -122.217700, 37.668000, -122.216800, 37.653000 , -122.215900, 37.638000)
+I- 880                        Ramp     (0, 2, -122.218000, 37.659000, -122.217300, 37.652000 )
+I- 880                        Ramp     (0, 2, -122.217600, 37.650000, -122.217300, 37.652000 )
+I- 580                        Ramp     (0, 3, -122.192400, 37.854000, -122.189775, 37.844250 , -122.188900, 37.841000)
+I- 580                        Ramp     (0, 3, -122.190900, 37.850000, -122.189200, 37.848000 , -122.187900, 37.844000)
+I- 880                        Ramp     (0, 2, -122.211100, 37.581000, -122.210800, 37.535000 )
+I- 880                        Ramp     (0, 3, -122.209600, 37.565000, -122.207900, 37.549000 , -122.206100, 37.541000)
+I- 880                        Ramp     (0, 4, -122.209200, 37.535000, -122.207400, 37.536000 , -122.207200, 37.535000, -122.206600, 37.530000)
+I- 880                        Ramp     (1, 3, -122.209100, 37.532000, -122.208900, 37.535000 , -122.207200, 37.535000)
+I- 880                        Ramp     (0, 5, -122.206500, 37.540000, -122.206600, 37.535000 , -122.206300, 37.531000, -122.205900, 37.526000, -122.206000, 37.522000)
+I- 880                        Ramp     (1, 3, -122.205400, 37.542000, -122.205700, 37.533000 , -122.205900, 37.526000)
+I- 880                                 (0, 6, -122.206800, 37.534000, -122.206600, 37.530000 , -122.206000, 37.522000, -122.200500, 37.460000, -122.196700, 37.418000, -122.195900, 37.407000)
+I- 880                        Ramp     (0, 2, -122.195200, 37.418000, -122.196700, 37.418000 )
+I- 880                        Ramp     (0, 4, -122.195900, 37.407000, -122.195800, 37.396000 , -122.195300, 37.396000, -122.194700, 37.394000)
+I- 880                        Ramp     (0, 2, -122.195400, 37.372000, -122.195700, 37.378000 )
+I- 880                        Ramp     (0, 3, -122.194600, 37.405000, -122.194200, 37.411000 , -122.195100, 37.411000)
+I- 880                        Ramp     (0, 2, -122.194600, 37.405000, -122.194700, 37.394000 )
+I- 880                                 (0, 14, -122.194700, 37.394000, -122.194000, 37.385000 , -122.193300, 37.378000, -122.188200, 37.320000, -122.187900, 37.317000, -122.187700, 37.315000, -122.187600, 37.312000, -122.187300, 37.309000, -122.182900, 37.260000, -122.181800, 37.249000, -122.180400, 37.234000, -122.180200, 37.231000, -122.177600, 37.206000, -122.176800, 37.197000)
+I- 580                        Ramp     (0, 2, -122.189500, 37.844000, -122.188600, 37.845000 )
+I- 580                        Ramp     (0, 5, -122.187300, 37.838000, -122.186800, 37.841000 , -122.186600, 37.844000, -122.185400, 37.841000, -122.184700, 37.840000)
+I- 580                        Ramp     (0, 5, -122.182300, 37.838000, -122.182011, 37.837540 , -122.180678, 37.835410, -122.179800, 37.834000, -122.179800, 37.838000)
+I- 580                                 (0, 2, -122.177000, 37.833000, -122.178900, 37.838000 )
+I- 580                        Ramp     (0, 2, -122.178900, 37.838000, -122.177300, 37.839000 )
+I- 580                                 (1, 2, -122.175200, 37.826000, -122.177000, 37.833000 )
+I- 580                        Ramp     (0, 2, -122.177300, 37.839000, -122.176400, 37.839000 )
+I- 580                        Ramp     (0, 3, -122.176400, 37.839000, -122.175600, 37.838000 , -122.174300, 37.833000)
+I- 580                        Ramp     (0, 3, -122.174200, 37.817000, -122.175200, 37.822000 , -122.177000, 37.833000)
+I- 580                                 (0, 3, -122.171600, 37.805000, -122.172800, 37.811000 , -122.174200, 37.817000)
+I- 580                        Ramp     (0, 3, -122.172900, 37.803000, -122.171600, 37.801000 , -122.170300, 37.799000)
+I- 580                        Ramp     (0, 2, -122.171600, 37.805000, -122.172500, 37.816000 )
+I- 580                                 (0, 8, -122.171600, 37.805000, -122.170300, 37.799000 , -122.170042, 37.797580, -122.169413, 37.794110, -122.167295, 37.782440, -122.166339, 37.777170, -122.165224, 37.771120, -122.164800, 37.769000)
+I- 580                        Ramp     (0, 3, -122.164800, 37.769000, -122.164720, 37.768200 , -122.163800, 37.759000)
+I- 880                        Ramp     (0, 5, -122.189300, 37.303000, -122.188800, 37.305000 , -122.188500, 37.307000, -122.187700, 37.309000, -122.187300, 37.309000)
+I- 880                        Ramp     (1, 2, -122.187600, 37.312000, -122.187700, 37.309000 )
+I- 880                        Ramp     (0, 5, -122.186800, 37.320000, -122.187000, 37.322000 , -122.187300, 37.321000, -122.187700, 37.319000, -122.187900, 37.317000)
+I- 880                        Ramp     (1, 2, -122.186300, 37.322000, -122.187000, 37.322000 )
+I- 880                        Ramp     (0, 5, -122.176800, 37.197000, -122.177400, 37.182000 , -122.176900, 37.180000, -122.175800, 37.181000, -122.174700, 37.178000)
+I- 880                        Ramp     (0, 6, -122.176300, 37.193000, -122.175400, 37.191000 , -122.174500, 37.194000, -122.174300, 37.192000, -122.173700, 37.196000, -122.172500, 37.198000)
+I- 880                        Ramp     (0, 2, -122.176300, 37.184000, -122.176100, 37.191000 )
+I- 880                                 (0, 2, -122.176100, 37.191000, -122.175700, 37.187000 )
+I- 880                        Ramp     (1, 2, -122.175700, 37.187000, -122.175400, 37.191000 )
+I- 880                                 (0, 19, -122.175500, 37.185000, -122.174700, 37.178000 , -122.174200, 37.173000, -122.169200, 37.126000, -122.167792, 37.115940, -122.167570, 37.114350, -122.167100, 37.111000, -122.165500, 37.100000, -122.165169, 37.098110, -122.164100, 37.092000, -122.159600, 37.061000, -122.158381, 37.052750, -122.155991, 37.036570, -122.153100, 37.017000, -122.147800, 37.980000, -122.140700, 37.932000, -122.139400, 37.924000, -122.138900, 37.920000, -122.137600, 37.910000)
+I- 880                        Ramp     (0, 2, -122.173000, 37.194000, -122.174200, 37.173000 )
+I- 880                        Ramp     (0, 5, -122.167500, 37.090000, -122.166600, 37.089000 , -122.165804, 37.089720, -122.165500, 37.090000, -122.164100, 37.092000)
+I- 880                        Ramp     (0, 2, -122.164600, 37.113000, -122.163000, 37.111000 )
+I- 880                        Ramp     (0, 3, -122.164000, 37.106000, -122.165300, 37.113000 , -122.165100, 37.101000)
+I- 580                        Ramp     (0, 2, -122.156700, 37.727000, -122.156000, 37.710000 )
+I- 580                        Ramp     (0, 2, -122.155300, 37.706000, -122.154300, 37.703000 )
+I- 580                        Ramp     (0, 2, -122.153900, 37.707000, -122.153500, 37.694000 )
+I- 580                                 (0, 21, -122.154300, 37.703000, -122.153500, 37.694000 , -122.151200, 37.655000, -122.147500, 37.603000, -122.146800, 37.583000, -122.147200, 37.569000, -122.149044, 37.548740, -122.149300, 37.546000, -122.150100, 37.532000, -122.150600, 37.509000, -122.149500, 37.482000, -122.148700, 37.467000, -122.147700, 37.447000, -122.141400, 37.383000, -122.140400, 37.376000, -122.139800, 37.372000, -122.139000, 37.356000, -122.138800, 37.353000, -122.138500, 37.340000, -122.138200, 37.330000, -122.137800, 37.316000)
+I- 580                        Ramp     (0, 3, -122.152100, 37.682000, -122.151000, 37.659000 , -122.151200, 37.655000)
+I- 580                        Ramp     (0, 3, -122.152100, 37.529000, -122.151400, 37.524000 , -122.150600, 37.509000)
+I- 580                        Ramp     (1, 2, -122.152200, 37.526000, -122.151400, 37.524000 )
+I- 580                        Ramp     (0, 2, -122.149300, 37.546000, -122.150800, 37.535000 )
+I- 580                        Ramp     (0, 2, -122.149500, 37.482000, -122.148900, 37.452000 )
+I- 580                        Ramp     (0, 2, -122.148700, 37.467000, -122.147600, 37.454000 )
+I- 580                        Ramp     (0, 4, -122.141400, 37.383000, -122.140700, 37.376000 , -122.140300, 37.372000, -122.139000, 37.356000)
+I- 580                        Ramp     (0, 2, -122.139100, 37.367000, -122.138800, 37.353000 )
+I- 580                        Ramp     (0, 4, -122.137800, 37.316000, -122.137400, 37.311000 , -122.137300, 37.307000, -122.136700, 37.296000)
+I- 580                        Ramp     (0, 2, -122.137900, 37.282000, -122.137100, 37.279000 )
+I- 880                        Ramp     (0, 4, -122.140700, 37.932000, -122.139700, 37.923000 , -122.138900, 37.917000, -122.138300, 37.913000)
+I- 880                        Ramp     (0, 8, -122.137900, 37.931000, -122.137597, 37.927360 , -122.137400, 37.925000, -122.137300, 37.924000, -122.136900, 37.914000, -122.135800, 37.905000, -122.136500, 37.908000, -122.135800, 37.898000)
+I- 880                        Ramp     (0, 4, -122.137600, 37.910000, -122.137900, 37.909000 , -122.137600, 37.907000, -122.136500, 37.902000)
+I- 880                                 (0, 2, -122.136500, 37.902000, -122.137600, 37.910000 )
+I- 880                        Ramp     (0, 3, -122.137900, 37.891000, -122.138300, 37.897000 , -122.137700, 37.902000)
+I- 580                        Ramp     (0, 2, -122.137100, 37.279000, -122.136700, 37.264000 )
+I- 580                        Ramp     (0, 2, -122.137100, 37.279000, -122.138100, 37.262000 )
+I- 580                        Ramp     (0, 2, -122.136200, 37.248000, -122.137400, 37.249000 )
+I- 580                        Ramp     (0, 2, -122.133800, 37.222000, -122.135000, 37.227000 )
+I- 580                        Ramp     (0, 2, -122.131100, 37.177000, -122.130600, 37.157000 )
+I- 580                                 (0, 13, -122.130600, 37.157000, -122.129800, 37.147000 , -122.127700, 37.122000, -122.126709, 37.114680, -122.125400, 37.105000, -122.125000, 37.103000, -122.123700, 37.096000, -122.123100, 37.093000, -122.121400, 37.082000, -122.118300, 37.066000, -122.116000, 37.052000, -122.115300, 37.048000, -122.110800, 37.023000)
+I- 580                        Ramp     (0, 3, -122.122600, 37.099000, -122.123800, 37.102000 , -122.125400, 37.105000)
+I- 580                        Ramp     (0, 2, -122.123700, 37.096000, -122.123400, 37.090000 )
+I- 580                        Ramp     (0, 3, -122.120600, 37.088000, -122.120300, 37.083000 , -122.118300, 37.066000)
+I- 880                        Ramp     (1, 2, -122.137800, 37.923000, -122.137400, 37.925000 )
+I- 880                                 (0, 17, -122.136500, 37.902000, -122.135800, 37.898000 , -122.133300, 37.881000, -122.132300, 37.874000, -122.131100, 37.866000, -122.130800, 37.865000, -122.130700, 37.864000, -122.128900, 37.851000, -122.127700, 37.843000, -122.126400, 37.834000, -122.123100, 37.812000, -122.116500, 37.766000, -122.110400, 37.720000, -122.109695, 37.710940, -122.109000, 37.702000, -122.108312, 37.691680, -122.107600, 37.681000)
+I- 880                        Ramp     (1, 2, -122.133500, 37.901000, -122.135800, 37.905000 )
+I- 880                        Ramp     (0, 3, -122.131800, 37.865000, -122.131900, 37.866000 , -122.133300, 37.881000)
+I- 880                        Ramp     (0, 3, -122.129600, 37.865000, -122.129900, 37.866000 , -122.132300, 37.874000)
+I- 880                        Ramp     (0, 3, -122.128900, 37.861000, -122.128400, 37.855000 , -122.127700, 37.843000)
+I- 880                        Ramp     (0, 3, -122.128700, 37.842000, -122.128300, 37.839000 , -122.126400, 37.834000)
+I- 880                        Ramp     (1, 2, -122.128600, 37.836000, -122.128300, 37.839000 )
+I- 580                        Ramp     (0, 2, -122.121500, 37.075000, -122.118300, 37.066000 )
+I- 580                        Ramp     (1, 2, -122.120200, 37.085000, -122.120300, 37.083000 )
+I- 580                                 (0, 14, -122.110800, 37.023000, -122.110100, 37.020000 , -122.108103, 37.007640, -122.108000, 37.007000, -122.106900, 37.998000, -122.106400, 37.994000, -122.105300, 37.982000, -122.104800, 37.977000, -122.103200, 37.958000, -122.102600, 37.953000, -122.101300, 37.938000, -122.098900, 37.911000, -122.098400, 37.910000, -122.098000, 37.908000)
+I- 580                        Ramp     (0, 4, -122.108600, 37.003000, -122.110300, 37.018000 , -122.110800, 37.023000, -122.109300, 37.020000)
+I- 580                        Ramp     (0, 2, -122.108000, 37.007000, -122.109300, 37.020000 )
+I- 580                        Ramp     (0, 4, -122.108600, 37.003000, -122.106800, 37.993000 , -122.106600, 37.992000, -122.105300, 37.982000)
+I- 580                        Ramp     (0, 3, -122.101000, 37.898000, -122.100500, 37.902000 , -122.098900, 37.911000)
+I- 580                                 (0, 7, -122.098000, 37.908000, -122.096500, 37.904000 , -122.095586, 37.903000, -122.095300, 37.903000, -122.094300, 37.902000, -122.093800, 37.903000, -122.093241, 37.903510)
+I- 580                        Ramp     (0, 3, -122.095300, 37.903000, -122.097100, 37.911000 , -122.094300, 37.902000)
+I- 580                        Ramp     (0, 2, -122.094100, 37.897000, -122.094300, 37.902000 )
+I- 580                        Ramp     (0, 3, -122.096000, 37.888000, -122.096200, 37.891000 , -122.096400, 37.900000)
+I- 580                        Ramp     (0, 3, -122.093400, 37.896000, -122.092570, 37.899610 , -122.091100, 37.906000)
+I- 580                                 (0, 9, -122.091100, 37.906000, -122.090000, 37.908000 , -122.088200, 37.908000, -122.085600, 37.909000, -122.078489, 37.909000, -122.072600, 37.909000, -122.071100, 37.910000, -122.068287, 37.911370, -122.064900, 37.914000)
+I- 580                        Ramp     (0, 2, -122.088400, 37.911000, -122.085600, 37.909000 )
+I- 580                        Ramp     (0, 3, -122.064900, 37.914000, -122.063900, 37.920000 , -122.063000, 37.923000)
+I- 580                        Ramp     (0, 3, -122.064900, 37.914000, -122.061800, 37.916000 , -122.060400, 37.920000)
+I- 580                        Ramp     (0, 2, -122.053100, 37.932000, -122.054400, 37.941000 )
+I- 580                        Ramp     (0, 3, -122.019500, 37.012000, -122.020300, 37.015000 , -122.021200, 37.020000)
+I- 580                        Ramp     (1, 2, -122.020600, 37.007000, -122.020300, 37.015000 )
+I- 580                        Ramp     (0, 3, -122.019500, 37.012000, -122.018400, 37.009000 , -122.018000, 37.019000)
+I- 580                                 (0, 5, -122.018000, 37.019000, -122.000900, 37.032000 , -121.978700, 37.983000, -121.958000, 37.984000, -121.957100, 37.986000)
+I- 680                        Ramp     (0, 4, -121.940100, 37.233000, -121.937800, 37.219000 , -121.937899, 37.227910, -121.938000, 37.237000)
+I- 580                        Ramp     (0, 2, -121.958000, 37.984000, -121.956500, 37.980000 )
+I- 580                        Ramp     (0, 2, -121.957100, 37.986000, -121.956500, 37.980000 )
+I- 580                        Ramp     (0, 2, -121.956200, 37.984000, -121.956500, 37.980000 )
+I- 580                        Ramp     (0, 4, -121.936400, 37.986000, -121.933800, 37.971000 , -121.933100, 37.979000, -121.932200, 37.989000)
+I- 580                        Ramp     (0, 8, -121.936800, 37.986000, -121.936483, 37.988320 , -121.935300, 37.997000, -121.935040, 37.000350, -121.934600, 37.006000, -121.933764, 37.000310, -121.933300, 37.997000, -121.932200, 37.989000)
+I- 580                        Ramp     (0, 3, -121.933500, 37.987000, -121.934100, 37.991000 , -121.934500, 37.985000)
+I- 580                        Ramp     (0, 2, -121.934500, 37.985000, -121.934100, 37.978000 )
+I- 580                        Ramp     (1, 2, -121.933500, 37.987000, -121.933100, 37.979000 )
+I- 680                                 (0, 10, -121.922900, 37.039000, -121.924300, 37.057000 , -121.928600, 37.106000, -121.929195, 37.113290, -121.929661, 37.119000, -121.932027, 37.148000, -121.933910, 37.171070, -121.935700, 37.193000, -121.936421, 37.201930, -121.937800, 37.219000)
+I- 580/I-680                  Ramp     (0, 5, -121.922500, 37.034000, -121.921800, 37.031000 , -121.921300, 37.026000, -121.919443, 37.020430, -121.921326, 37.017390)
+I- 580/I-680                  Ramp     (0, 6, -121.924300, 37.006000, -121.924389, 37.007790 , -121.923700, 37.017000, -121.923100, 37.022000, -121.922700, 37.029000, -121.922900, 37.039000)
+I- 580                                 (0, 3, -121.932200, 37.989000, -121.924300, 37.006000 , -121.921700, 37.014000)
+I- 580/I-680                  Ramp     (0, 4, -121.924300, 37.006000, -121.923800, 37.005000 , -121.922500, 37.008000, -121.921540, 37.010400)
+I- 580/I-680                  Ramp     (1, 2, -121.921800, 37.021000, -121.923700, 37.017000 )
+I- 680                                 (0, 2, -121.921700, 37.014000, -121.921800, 37.021000 )
+I- 580                                 (0, 2, -121.921400, 37.015000, -121.921700, 37.014000 )
+I- 580/I-680                  Ramp     (0, 4, -121.921300, 37.005000, -121.920300, 37.013000 , -121.919200, 37.016000, -121.918000, 37.020000)
+I- 580/I-680                  Ramp     (1, 2, -121.920700, 37.988000, -121.919200, 37.016000 )
+I- 580                                 (0, 6, -121.921400, 37.015000, -121.918900, 37.020000 , -121.918000, 37.020000, -121.908700, 37.017000, -121.906090, 37.017970, -121.906000, 37.018000)
+I- 580                        Ramp     (0, 3, -121.908700, 37.017000, -121.905600, 37.003000 , -121.905000, 37.004000)
+I- 580                        Ramp     (0, 4, -121.908700, 37.017000, -121.907194, 37.026190 , -121.907100, 37.029000, -121.907200, 37.033000)
+I- 580                        Ramp     (0, 3, -121.906800, 37.027000, -121.905900, 37.029000 , -121.907800, 37.040000)
+I- 580                        Ramp     (0, 2, -121.906000, 37.018000, -121.905600, 37.011000 )
+I- 580                        Ramp     (0, 3, -121.906000, 37.018000, -121.905635, 37.023900 , -121.906500, 37.023000)
+I- 580                        Ramp     (0, 4, -121.904300, 37.998000, -121.903600, 37.013000 , -121.902632, 37.017400, -121.902500, 37.018000)
+I- 580                        Ramp     (1, 3, -121.902500, 37.018000, -121.903700, 37.022000 , -121.905900, 37.029000)
+I- 580                        Ramp     (0, 3, -121.874300, 37.014000, -121.872200, 37.999000 , -121.871400, 37.999000)
+I- 580                        Ramp     (0, 4, -121.869500, 37.013000, -121.871200, 37.011000 , -121.871700, 37.001000, -121.871400, 37.001000)
+I- 580                                 (0, 3, -121.858500, 37.013000, -121.863393, 37.012130 , -121.864100, 37.012000)
+I- 580                                 (0, 12, -121.858500, 37.013000, -121.852100, 37.011000 , -121.848500, 37.011000, -121.846300, 37.011000, -121.845500, 37.011000, -121.841600, 37.011000, -121.841400, 37.011000, -121.834001, 37.010400, -121.829200, 37.010000, -121.828800, 37.009000, -121.823320, 37.008330, -121.820600, 37.008000)
+I- 580                        Ramp     (0, 5, -121.852100, 37.011000, -121.847900, 37.999000 , -121.847600, 37.999000, -121.845600, 37.010000, -121.845500, 37.011000)
+I- 580                        Ramp     (0, 2, -121.852100, 37.011000, -121.849600, 37.025000 )
+I- 580                        Ramp     (0, 2, -121.846300, 37.011000, -121.849600, 37.025000 )
+I- 580                        Ramp     (0, 2, -121.845400, 37.010000, -121.845500, 37.011000 )
+I- 580                        Ramp     (0, 3, -121.820600, 37.008000, -121.818700, 37.001000 , -121.817900, 37.005000)
+I- 580                        Ramp     (0, 3, -121.819500, 37.007000, -121.818700, 37.014000 , -121.817900, 37.015000)
+I- 580                        Ramp     (1, 2, -121.818000, 37.011000, -121.818700, 37.014000 )
+I- 580                        Ramp     (1, 2, -121.818500, 37.008000, -121.818700, 37.001000 )
+I- 580                        Ramp     (0, 5, -121.786500, 37.995000, -121.785200, 37.992000 , -121.784200, 37.985000, -121.783800, 37.966000, -121.783400, 37.957000)
+I- 580                        Ramp     (0, 4, -121.789100, 37.998000, -121.785400, 37.999000 , -121.784410, 37.996300, -121.784300, 37.996000)
+I- 580                        Ramp     (1, 3, -121.784300, 37.996000, -121.784294, 37.995390 , -121.784200, 37.985000)
+I- 580                        Ramp     (0, 3, -121.774300, 37.006000, -121.772900, 37.013000 , -121.770500, 37.013000)
+I- 580                        Ramp     (0, 3, -121.774300, 37.006000, -121.772900, 37.006000 , -121.770500, 37.013000)
+I- 580                                 (0, 18, -121.770500, 37.013000, -121.769039, 37.015040 , -121.758300, 37.030000, -121.755700, 37.030000, -121.754805, 37.029660, -121.750803, 37.028120, -121.750000, 37.027380, -121.748900, 37.026000, -121.745300, 37.024000, -121.743800, 37.024000, -121.741100, 37.024000, -121.740416, 37.025050, -121.739035, 37.027190, -121.737900, 37.028000, -121.736275, 37.032780, -121.736200, 37.033000, -121.735800, 37.034000, -121.733100, 37.046000)
+I- 880                        Ramp     (0, 5, -122.107600, 37.681000, -122.107021, 37.664600 , -122.107000, 37.664000, -122.106808, 37.661840, -122.104800, 37.638000)
+I- 880                        Ramp     (0, 6, -122.107600, 37.681000, -122.106243, 37.665490 , -122.106200, 37.665000, -122.106043, 37.661860, -122.105869, 37.658380, -122.104800, 37.638000)
+I- 880                                 (0, 5, -122.104800, 37.638000, -122.104500, 37.633000 , -122.104200, 37.630000, -122.103300, 37.617000, -122.102900, 37.610000)
+I- 880                        Ramp     (0, 3, -122.102900, 37.610000, -122.101300, 37.587000 , -122.099900, 37.569000)
+I- 880                        Ramp     (0, 3, -122.102200, 37.597000, -122.102055, 37.591780 , -122.101900, 37.562000)
+I- 880                        Ramp     (0, 3, -122.103200, 37.557000, -122.099700, 37.548000 , -122.097800, 37.528000)
+I- 880                        Ramp     (0, 2, -122.100500, 37.571000, -122.100900, 37.568000 )
+I- 880                        Ramp     (1, 3, -122.100500, 37.571000, -122.100300, 37.565000 , -122.099700, 37.548000)
+I- 880                        Ramp     (0, 2, -122.099300, 37.571000, -122.099900, 37.569000 )
+I- 880                        Ramp     (0, 3, -122.098500, 37.574000, -122.098600, 37.567000 , -122.097800, 37.528000)
+I- 880                                 (0, 7, -122.097800, 37.528000, -122.096000, 37.496000 , -122.093100, 37.453000, -122.092770, 37.449600, -122.090189, 37.414420, -122.089600, 37.405000, -122.085000, 37.340000)
+I- 880                        Ramp     (0, 5, -122.096000, 37.496000, -122.094100, 37.464000 , -122.094300, 37.421000, -122.091500, 37.427000, -122.089600, 37.405000)
+I- 880                        Ramp     (0, 4, -122.093100, 37.453000, -122.093700, 37.469000 , -122.093100, 37.461000, -122.092300, 37.457000)
+I- 880                        Ramp     (1, 2, -122.091900, 37.465000, -122.093100, 37.461000 )
+I- 880                        Ramp     (0, 2, -122.091800, 37.466000, -122.089600, 37.405000 )
+I- 880                        Ramp     (0, 2, -122.092000, 37.461000, -122.092300, 37.457000 )
+I- 880                        Ramp     (1, 3, -122.093200, 37.439000, -122.092200, 37.437000 , -122.091500, 37.427000)
+I- 880                        Ramp     (0, 3, -122.085000, 37.340000, -122.086600, 37.316000 , -122.081900, 37.296000)
+I- 880                        Ramp     (0, 3, -122.085000, 37.340000, -122.080100, 37.316000 , -122.081000, 37.285000)
+I- 880                        Ramp     (0, 2, -122.083700, 37.322000, -122.084300, 37.316000 )
+I- 880                        Ramp     (0, 3, -122.083700, 37.322000, -122.082200, 37.316000 , -122.083100, 37.312000)
+I- 880                                 (0, 10, -122.083100, 37.312000, -122.081900, 37.296000 , -122.081000, 37.285000, -122.078600, 37.248000, -122.078000, 37.240000, -122.077642, 37.234960, -122.076983, 37.225670, -122.076599, 37.220260, -122.076229, 37.215050, -122.075800, 37.209000)
+I- 880                        Ramp     (0, 4, -122.075800, 37.209000, -122.075697, 37.206300 , -122.075000, 37.188000, -122.074900, 37.184000)
+I- 880                        Ramp     (0, 4, -122.075800, 37.209000, -122.073269, 37.189110 , -122.073000, 37.187000, -122.072300, 37.183000)
+I- 880                                 (0, 6, -122.066600, 37.085000, -122.065600, 37.067000 , -122.065300, 37.062000, -122.064400, 37.049000, -122.063500, 37.036000, -122.061800, 37.011000)
+I- 880                        Ramp     (0, 3, -122.064200, 37.073000, -122.064800, 37.076000 , -122.066600, 37.085000)
+I- 880                        Ramp     (0, 2, -122.065300, 37.062000, -122.066300, 37.064000 )
+I- 880                        Ramp     (0, 3, -122.063500, 37.036000, -122.064000, 37.049000 , -122.064800, 37.069000)
+I- 880                        Ramp     (0, 3, -122.061800, 37.011000, -122.063100, 37.982000 , -122.058500, 37.967000)
+I- 880                                 (0, 12, -122.061200, 37.003000, -122.060400, 37.991000 , -122.059600, 37.982000, -122.058500, 37.967000, -122.058300, 37.961000, -122.055300, 37.918000, -122.053635, 37.894750, -122.050759, 37.854600, -122.050000, 37.844000, -122.048500, 37.817000, -122.048300, 37.813000, -122.048200, 37.811000)
+I- 880                        Ramp     (0, 3, -122.060400, 37.991000, -122.060000, 37.983000 , -122.060500, 37.983000)
+I- 880                        Ramp     (0, 3, -122.059000, 37.982000, -122.057700, 37.984000 , -122.061200, 37.003000)
+I- 880                        Ramp     (1, 5, -122.058500, 37.967000, -122.057700, 37.974000 , -122.054800, 37.966000, -122.055168, 37.968280, -122.057700, 37.984000)
+I- 880                        Ramp     (0, 6, -122.048500, 37.817000, -122.048400, 37.813000 , -122.048200, 37.800000, -122.048100, 37.794000, -122.047800, 37.781000, -122.046900, 37.774000)
+I- 880                        Ramp     (0, 4, -122.048200, 37.811000, -122.046100, 37.785000 , -122.045300, 37.778000, -122.045000, 37.775000)
+I- 880                        Ramp     (1, 2, -122.047700, 37.798000, -122.048200, 37.800000 )
+I- 880                        Ramp     (1, 3, -122.047700, 37.798000, -122.046600, 37.789000 , -122.046100, 37.785000)
+I- 880                                 (0, 9, -122.046900, 37.774000, -122.046600, 37.765000 , -122.046500, 37.761000, -122.045041, 37.722340, -122.044500, 37.708000, -122.042600, 37.686000, -122.041515, 37.674250, -122.041400, 37.673000, -122.039800, 37.656000)
+I- 880                        Ramp     (0, 5, -122.039200, 37.650000, -122.038900, 37.625000 , -122.039100, 37.617000, -122.036099, 37.616100, -122.035800, 37.616000)
+I- 880                        Ramp     (0, 2, -122.039800, 37.656000, -122.036100, 37.656000 )
+I- 880                        Ramp     (0, 5, -122.036400, 37.652000, -122.036400, 37.646000 , -122.036180, 37.634120, -122.036027, 37.625860, -122.035900, 37.619000)
+I- 880                        Ramp     (0, 4, -122.038300, 37.640000, -122.038700, 37.636000 , -122.038700, 37.633000, -122.038000, 37.632000)
+I- 880                        Ramp     (0, 2, -122.037400, 37.639000, -122.037500, 37.632000 )
+I- 880                                 (0, 12, -122.037500, 37.632000, -122.035900, 37.619000 , -122.035800, 37.616000, -122.034514, 37.604090, -122.031876, 37.579650, -122.031193, 37.573320, -122.030160, 37.563750, -122.029430, 37.556980, -122.028689, 37.549290, -122.027833, 37.539080, -122.025979, 37.516980, -122.023800, 37.491000)
+I- 880                        Ramp     (1, 2, -122.036900, 37.645000, -122.036400, 37.646000 )
+I- 880                        Ramp     (0, 4, -122.023800, 37.491000, -122.021500, 37.483000 , -122.021100, 37.477000, -122.020500, 37.447000)
+I- 880                        Ramp     (0, 5, -122.023600, 37.488000, -122.023100, 37.458000 , -122.022700, 37.458000, -122.022300, 37.452000, -122.020500, 37.447000)
+I- 880                        Ramp     (0, 3, -122.022600, 37.474000, -122.021400, 37.473000 , -122.021900, 37.466000)
+I- 880                                 (0, 9, -122.021900, 37.466000, -122.020500, 37.447000 , -122.020331, 37.444470, -122.020008, 37.439620, -122.019500, 37.432000, -122.019300, 37.429000, -122.016400, 37.393000, -122.010219, 37.347710, -122.004100, 37.313000)
+I- 880                        Ramp     (0, 5, -122.004100, 37.313000, -122.003800, 37.308000 , -122.003900, 37.284000, -122.001300, 37.287000, -121.999500, 37.289000)
+I- 880                        Ramp     (0, 6, -122.004100, 37.313000, -122.001800, 37.315000 , -122.000700, 37.315000, -122.000500, 37.313000, -122.000200, 37.308000, -121.999500, 37.289000)
+I- 880                        Ramp     (0, 2, -122.001900, 37.301000, -122.002000, 37.293000 )
+I- 880                        Ramp     (1, 2, -122.000800, 37.310000, -122.000200, 37.308000 )
+I- 880                        Ramp     (1, 2, -122.001300, 37.298000, -122.001300, 37.287000 )
+I- 880                                 (0, 17, -121.999500, 37.289000, -121.998758, 37.285580 , -121.998013, 37.282000, -121.996913, 37.276130, -121.992900, 37.255000, -121.991900, 37.252000, -121.991111, 37.247950, -121.990277, 37.243670, -121.989597, 37.240180, -121.988200, 37.233000, -121.987100, 37.229000, -121.986500, 37.226000, -121.984800, 37.216000, -121.982000, 37.196000, -121.980500, 37.186000, -121.975936, 37.147230, -121.971200, 37.107000)
+I- 880                        Ramp     (0, 2, -121.987100, 37.229000, -121.986300, 37.232000 )
+I- 880                        Ramp     (0, 3, -121.986000, 37.239000, -121.986500, 37.236000 , -121.986100, 37.234000)
+I- 880                        Ramp     (0, 2, -121.986500, 37.226000, -121.987200, 37.220000 )
+I- 880                        Ramp     (0, 2, -121.988500, 37.211000, -121.984800, 37.216000 )
+I- 880                        Ramp     (0, 3, -121.971200, 37.107000, -121.964300, 37.087000 , -121.962300, 37.085000)
+I- 880                        Ramp     (0, 3, -121.968800, 37.061000, -121.968600, 37.066000 , -121.966900, 37.075000)
+I- 880                        Ramp     (1, 2, -121.968200, 37.065000, -121.968600, 37.066000 )
+I- 880                                 (1, 6, -121.966900, 37.075000, -121.966300, 37.071000 , -121.965600, 37.065000, -121.961800, 37.037000, -121.956890, 37.000000, -121.948000, 37.933000)
+I- 880                        Ramp     (0, 3, -121.965600, 37.065000, -121.967800, 37.061000 , -121.967600, 37.065000)
+I- 880                        Ramp     (0, 5, -121.961800, 37.037000, -121.961998, 37.052050 , -121.962212, 37.068310, -121.962300, 37.075000, -121.961600, 37.087000)
+I- 880                        Ramp     (0, 3, -121.961200, 37.099000, -121.961500, 37.090000 , -121.961600, 37.087000)
+I- 680                                 (0, 7, -121.910100, 37.715000, -121.911269, 37.746820 , -121.911900, 37.764000, -121.912400, 37.776000, -121.917400, 37.905000, -121.919400, 37.957000, -121.920700, 37.988000)
+I- 680                        Ramp     (0, 3, -121.899400, 37.553000, -121.900800, 37.558000 , -121.901400, 37.565000)
+I- 680                        Ramp     (1, 2, -121.900700, 37.565000, -121.900800, 37.558000 )
+I- 680                        Ramp     (0, 5, -121.898500, 37.545000, -121.900100, 37.565000 , -121.899900, 37.571000, -121.900800, 37.572000, -121.903000, 37.586000)
+I- 680                                 (0, 8, -121.885200, 37.422000, -121.887400, 37.444000 , -121.890200, 37.470000, -121.890500, 37.472000, -121.890700, 37.474000, -121.898500, 37.545000, -121.899400, 37.553000, -121.900700, 37.565000)
+I- 680                        Ramp     (0, 3, -121.883900, 37.397000, -121.884700, 37.394000 , -121.884000, 37.399000)
+I- 680                        Ramp     (0, 5, -121.883300, 37.376000, -121.883300, 37.392000 , -121.883000, 37.400000, -121.883500, 37.402000, -121.885200, 37.422000)
+I- 680                                 (0, 3, -121.870900, 37.047000, -121.883100, 37.366000 , -121.883300, 37.376000)
+I- 680                                 (1, 2, -121.870600, 37.038000, -121.870900, 37.047000 )
+I- 680                        Ramp     (0, 2, -121.871200, 37.010000, -121.870900, 37.047000 )
+I- 680                        Ramp     (0, 2, -121.869800, 37.010000, -121.870600, 37.038000 )
+I- 680                        Ramp     (0, 3, -121.870300, 37.891000, -121.871200, 37.857000 , -121.867800, 37.875000)
+I- 680                        Ramp     (0, 2, -121.870100, 37.878000, -121.868100, 37.881000 )
+I- 680                        Ramp     (0, 2, -121.870900, 37.924000, -121.868600, 37.925000 )
+I- 680                        Ramp     (0, 6, -121.942600, 37.315000, -121.942300, 37.318000 , -121.941400, 37.320000, -121.939400, 37.324000, -121.938000, 37.320000, -121.936800, 37.313000)
+I- 680                        Ramp     (1, 2, -121.942200, 37.314000, -121.942300, 37.318000 )
+I- 680                        Ramp     (1, 2, -121.941800, 37.314000, -121.941400, 37.320000 )
+I- 680                        Ramp     (1, 2, -121.937200, 37.335000, -121.938000, 37.320000 )
+I- 680                                 (0, 10, -121.913900, 37.562000, -121.915000, 37.540000 , -121.915600, 37.532000, -121.916500, 37.519000, -121.917400, 37.504000, -121.918000, 37.493000, -121.920000, 37.438000, -121.920200, 37.435000, -121.923300, 37.404000, -121.923800, 37.402000)
+I- 680                                 (0, 2, -121.913900, 37.562000, -121.907700, 37.609000 )
+I- 680                        Ramp     (0, 4, -121.920000, 37.438000, -121.921800, 37.424000 , -121.923800, 37.408000, -121.925200, 37.392000)
+I- 680                        Ramp     (0, 3, -121.923800, 37.402000, -121.923400, 37.395000 , -121.923000, 37.399000)
+I- 680                        Ramp     (0, 3, -121.922600, 37.394000, -121.923200, 37.392000 , -121.925200, 37.392000)
+I- 680                        Ramp     (0, 2, -121.916700, 37.500000, -121.917400, 37.504000 )
+I- 680                                 (0, 2, -121.939000, 37.150000, -121.941800, 37.195000 )
+I- 680                        Ramp     (0, 3, -121.940600, 37.142000, -121.940000, 37.139000 , -121.937300, 37.125000)
+I- 680                        Ramp     (1, 2, -121.940200, 37.143000, -121.940000, 37.139000 )
+I- 680                        Ramp     (0, 2, -121.939400, 37.144000, -121.938700, 37.145000 )
+I- 680                                 (1, 16, -121.939000, 37.150000, -121.938700, 37.145000 , -121.937300, 37.125000, -121.934242, 37.076430, -121.933886, 37.070900, -121.933700, 37.068000, -121.933122, 37.061390, -121.932736, 37.056980, -121.932220, 37.051080, -121.931844, 37.046780, -121.930113, 37.027000, -121.926829, 37.000000, -121.926500, 37.998000, -121.921700, 37.960000, -121.920300, 37.949000, -121.918400, 37.934000)
+I- 680                        Ramp     (0, 2, -121.938700, 37.145000, -121.937600, 37.147000 )
+I- 680                        Ramp     (0, 3, -121.937300, 37.148000, -121.937000, 37.144000 , -121.937300, 37.125000)
+I- 680                        Ramp     (1, 2, -121.936800, 37.149000, -121.937000, 37.144000 )
+I- 680                        Ramp     (0, 2, -121.907700, 37.609000, -121.905300, 37.625000 )
+I- 680                        Ramp     (0, 2, -121.907700, 37.609000, -121.903600, 37.614000 )
+I- 680                                 (0, 5, -121.886700, 37.732000, -121.884500, 37.744000 , -121.881800, 37.756000, -121.876100, 37.781000, -121.871200, 37.857000)
+I- 680                        Ramp     (0, 3, -121.881800, 37.756000, -121.884200, 37.741000 , -121.886700, 37.732000)
+I- 680                                 (0, 11, -121.910100, 37.715000, -121.909801, 37.705190 , -121.909562, 37.697340, -121.909493, 37.695070, -121.909400, 37.692000, -121.909295, 37.688620, -121.909145, 37.683800, -121.909103, 37.682450, -121.908715, 37.669950, -121.908524, 37.663800, -121.908200, 37.653000)
+I- 580                                 (0, 4, -121.664341, 37.182200, -121.662081, 37.186930 , -121.661812, 37.187460, -121.660816, 37.189520)
+I- 80                         Ramp     (0, 2, -122.293400, 37.280000, -122.292600, 37.281000 )
+I- 580                        Ramp     (0, 2, -122.268532, 37.244900, -122.268500, 37.243000 )
+I- 980                                 (0, 2, -122.268237, 37.244260, -122.268200, 37.245000 )
+I- 580                        Ramp     (0, 3, -122.093241, 37.903510, -122.093640, 37.896340 , -122.093788, 37.892120)
+I- 580/I-680                  Ramp     (0, 2, -121.922017, 37.023170, -121.921200, 37.021000 )
+I- 580                        Ramp     (0, 4, -121.906000, 37.018000, -121.906044, 37.024160 , -121.906177, 37.023310, -121.906315, 37.021150)
+I- 80                         Ramp     (0, 2, -122.307624, 37.877810, -122.307512, 37.893270 )
+I- 580                        Ramp     (0, 3, -122.267600, 37.273000, -122.267231, 37.283490 , -122.266927, 37.292140)
+I- 880                        Ramp     (0, 5, -122.291454, 37.064000, -122.291088, 37.061030 , -122.290300, 37.050000, -122.289700, 37.044000, -122.288800, 37.038000)
+I- 880                        Ramp     (0, 2, -122.167500, 37.090000, -122.167570, 37.114350 )
+I- 880                        Ramp     (0, 2, -122.165757, 37.090590, -122.166350, 37.095590 )
+I- 880                        Ramp     (0, 2, -122.165757, 37.090590, -122.165169, 37.098110 )
+I- 880                        Ramp     (0, 2, -122.164188, 37.105140, -122.164100, 37.092000 )
+I- 880                        Ramp     (0, 3, -122.164600, 37.113000, -122.165458, 37.113790 , -122.167792, 37.115940)
+I- 580                        Ramp     (0, 2, -121.721800, 37.088000, -121.722301, 37.095220 )
+I- 580                        Ramp     (0, 4, -121.739000, 37.020000, -121.738298, 37.025060 , -121.737988, 37.026860, -121.737900, 37.028000)
+I- 680                                 (0, 2, -121.921540, 37.010400, -121.921700, 37.014000 )
+I- 580                        Ramp     (0, 2, -121.866951, 37.013850, -121.871381, 37.026080 )
+I- 580                        Ramp     (0, 2, -121.871300, 37.014000, -121.871352, 37.023210 )
+State Hwy 17                           (0, 4, -122.310700, 37.976000, -122.307800, 37.930000 , -122.307200, 37.916000, -122.307000, 37.902000)
+State Hwy 123                          (0, 13, -122.300400, 37.986000, -122.299800, 37.969000 , -122.299500, 37.962000, -122.299200, 37.952000, -122.299000, 37.942000, -122.298700, 37.935000, -122.298400, 37.924000, -122.298200, 37.920000, -122.297600, 37.904000, -122.297000, 37.880000, -122.296600, 37.869000, -122.295900, 37.848000, -122.296100, 37.843000)
+State Hwy 17                  Ramp     (0, 3, -122.291500, 37.083000, -122.291370, 37.070650 , -122.290561, 37.062240)
+State Hwy 17                  Ramp     (0, 5, -122.288200, 37.233000, -122.288100, 37.237000 , -122.288200, 37.242000, -122.288300, 37.247000, -122.288500, 37.254000)
+State Hwy 17                  Ramp     (1, 2, -122.287700, 37.234000, -122.288100, 37.237000 )
+State Hwy 17                  Ramp     (0, 3, -122.289900, 37.128000, -122.289800, 37.132000 , -122.289700, 37.142000)
+State Hwy 17                  Ramp     (0, 2, -122.291000, 37.113000, -122.290600, 37.118000 )
+State Hwy 24                  Ramp     (0, 2, -122.265700, 37.351000, -122.266500, 37.360000 )
+State Hwy 24                  Ramp     (0, 3, -122.266000, 37.348000, -122.266100, 37.350000 , -122.266800, 37.350000)
+State Hwy 24                  Ramp     (0, 5, -122.267700, 37.252000, -122.267400, 37.258000 , -122.267100, 37.263000, -122.267200, 37.267000, -122.267300, 37.272000)
+State Hwy 24                           (0, 3, -122.268100, 37.244000, -122.267900, 37.248000 , -122.267700, 37.252000)
+State Hwy 24                           (0, 15, -122.267400, 37.246000, -122.267300, 37.248000 , -122.267000, 37.261000, -122.266800, 37.271000, -122.266300, 37.298000, -122.265900, 37.315000, -122.265500, 37.336000, -122.265007, 37.358820, -122.264443, 37.372860, -122.264100, 37.381000, -122.263800, 37.388000, -122.263100, 37.396000, -122.261700, 37.405000, -122.261500, 37.407000, -122.260500, 37.412000)
+State Hwy 24                  Ramp     (0, 4, -122.262300, 37.409000, -122.261400, 37.411000 , -122.260282, 37.416260, -122.258000, 37.427000)
+State Hwy 24                  Ramp     (0, 4, -122.260500, 37.412000, -122.260100, 37.412000 , -122.259900, 37.412000, -122.258600, 37.413000)
+State Hwy 24                  Ramp     (0, 2, -122.257000, 37.433000, -122.258000, 37.427000 )
+State Hwy 24                  Ramp     (0, 6, -122.255100, 37.435000, -122.256800, 37.436000 , -122.258500, 37.428000, -122.259722, 37.424380, -122.260592, 37.421800, -122.261200, 37.420000)
+State Hwy 13                           (0, 6, -122.231900, 37.515000, -122.231600, 37.511000 , -122.230500, 37.498000, -122.229600, 37.489000, -122.228600, 37.478000, -122.224400, 37.427000)
+State Hwy 24                  Ramp     (0, 2, -122.227900, 37.486000, -122.230500, 37.498000 )
+State Hwy 13                  Ramp     (0, 2, -122.230700, 37.487000, -122.228600, 37.478000 )
+State Hwy 24                  Ramp     (0, 2, -122.228600, 37.478000, -122.228400, 37.485000 )
+State Hwy 24                  Ramp     (0, 2, -122.227900, 37.486000, -122.228400, 37.485000 )
+State Hwy 24                  Ramp     (0, 3, -122.221100, 37.542000, -122.221100, 37.533000 , -122.220900, 37.526000)
+State Hwy 24                  Ramp     (0, 3, -122.222400, 37.497000, -122.221900, 37.494000 , -122.221500, 37.492000)
+State Hwy 24                  Ramp     (0, 2, -122.221800, 37.517000, -122.221400, 37.507000 )
+State Hwy 24                  Ramp     (1, 2, -122.220400, 37.536000, -122.221100, 37.533000 )
+State Hwy 24                           (0, 7, -122.220600, 37.531000, -122.220400, 37.536000 , -122.219400, 37.541000, -122.217000, 37.546000, -122.215100, 37.551000, -122.213900, 37.561000, -122.212100, 37.577000)
+State Hwy 24                  Ramp     (0, 3, -122.219400, 37.541000, -122.219700, 37.535000 , -122.219700, 37.531000)
+State Hwy 24                  Ramp     (1, 2, -122.219100, 37.537000, -122.219700, 37.535000 )
+State Hwy 13                  Ramp     (0, 2, -122.224400, 37.427000, -122.224700, 37.422000 )
+State Hwy 13                  Ramp     (0, 4, -122.224400, 37.427000, -122.223000, 37.414000 , -122.221400, 37.396000, -122.221300, 37.388000)
+State Hwy 13                  Ramp     (0, 2, -122.223200, 37.410000, -122.224700, 37.422000 )
+State Hwy 13                           (0, 11, -122.221300, 37.388000, -122.218753, 37.364020 , -122.216800, 37.336000, -122.216300, 37.328000, -122.214400, 37.313000, -122.211744, 37.282210, -122.210000, 37.262000, -122.208700, 37.244000, -122.207000, 37.224000, -122.206500, 37.218000, -122.205800, 37.209000)
+State Hwy 13                  Ramp     (0, 2, -122.216800, 37.336000, -122.216800, 37.328000 )
+State Hwy 13                  Ramp     (0, 3, -122.216300, 37.328000, -122.214900, 37.321000 , -122.214400, 37.313000)
+State Hwy 13                  Ramp     (0, 2, -122.207300, 37.221000, -122.207000, 37.224000 )
+State Hwy 13                  Ramp     (0, 3, -122.206900, 37.213000, -122.207200, 37.216000 , -122.206500, 37.218000)
+State Hwy 13                  Ramp     (0, 2, -122.205800, 37.209000, -122.204900, 37.211000 )
+State Hwy 13                  Ramp     (0, 2, -122.205400, 37.205000, -122.204800, 37.209000 )
+State Hwy 13                  Ramp     (0, 2, -122.205500, 37.197000, -122.204900, 37.200000 )
+State Hwy 13                           (0, 5, -122.204900, 37.200000, -122.203280, 37.179750 , -122.198900, 37.125000, -122.198078, 37.116410, -122.197500, 37.110000)
+State Hwy 13                  Ramp     (0, 3, -122.198900, 37.125000, -122.198400, 37.117000 , -122.198100, 37.112000)
+State Hwy 13                  Ramp     (0, 2, -122.197500, 37.110000, -122.196400, 37.107000 )
+State Hwy 13                  Ramp     (0, 2, -122.194200, 37.084000, -122.196400, 37.107000 )
+State Hwy 13                  Ramp     (0, 2, -122.193700, 37.078000, -122.194300, 37.080000 )
+State Hwy 13                  Ramp     (0, 2, -122.185400, 37.996000, -122.186100, 37.986000 )
+State Hwy 13                  Ramp     (0, 2, -122.183500, 37.980000, -122.185200, 37.985000 )
+State Hwy 13                           (0, 2, -122.182800, 37.974000, -122.179900, 37.948000 )
+State Hwy 13                  Ramp     (0, 2, -122.181900, 37.978000, -122.182800, 37.974000 )
+State Hwy 13                  Ramp     (0, 2, -122.179900, 37.948000, -122.179700, 37.943000 )
+State Hwy 13                           (0, 9, -122.179700, 37.943000, -122.179871, 37.918490 , -122.180000, 37.900000, -122.179023, 37.866150, -122.178700, 37.862000, -122.178100, 37.851000, -122.177700, 37.845000, -122.177300, 37.839000, -122.177000, 37.833000)
+State Hwy 13                  Ramp     (0, 3, -122.178100, 37.851000, -122.178200, 37.847000 , -122.177700, 37.845000)
+State Hwy 238                 Ramp     (0, 3, -122.128800, 37.922000, -122.129400, 37.917000 , -122.129600, 37.906000)
+State Hwy 238                 Ramp     (1, 2, -122.128800, 37.913000, -122.129400, 37.917000 )
+State Hwy 238                 Ramp     (0, 3, -122.128800, 37.900000, -122.129300, 37.895000 , -122.129600, 37.906000)
+State Hwy 238                 Ramp     (1, 2, -122.128800, 37.892000, -122.129300, 37.895000 )
+State Hwy 238                 Ramp     (0, 3, -122.107300, 37.889000, -122.106700, 37.899000 , -122.105600, 37.899000)
+State Hwy 238                 Ramp     (1, 2, -122.106400, 37.890000, -122.106700, 37.899000 )
+State Hwy 238                 Ramp     (0, 4, -122.107300, 37.889000, -122.106800, 37.881000 , -122.106574, 37.883540, -122.106000, 37.890000)
+State Hwy 238                          (0, 4, -122.106000, 37.890000, -122.106100, 37.890000 , -122.106400, 37.890000, -122.107300, 37.889000)
+State Hwy 238                          (1, 8, -122.098000, 37.908000, -122.098300, 37.907000 , -122.099000, 37.905000, -122.101000, 37.898000, -122.101535, 37.897110, -122.103173, 37.894380, -122.104600, 37.892000, -122.106000, 37.890000)
+State Hwy 92                  Ramp     (0, 2, -122.120400, 37.267000, -122.123000, 37.271000 )
+State Hwy 92                  Ramp     (0, 3, -122.116700, 37.292000, -122.118500, 37.295000 , -122.119100, 37.298000)
+State Hwy 92                  Ramp     (0, 3, -122.118700, 37.285000, -122.118600, 37.292000 , -122.119300, 37.290000)
+State Hwy 92                  Ramp     (0, 3, -122.120000, 37.280000, -122.120700, 37.269000 , -122.120400, 37.270000)
+State Hwy 92                  Ramp     (0, 4, -122.109100, 37.328000, -122.110100, 37.332000 , -122.110100, 37.327000, -122.109500, 37.322000)
+State Hwy 92                  Ramp     (1, 2, -122.109100, 37.328000, -122.110100, 37.332000 )
+State Hwy 92                           (0, 9, -122.108500, 37.326000, -122.109500, 37.322000 , -122.111100, 37.316000, -122.111900, 37.313000, -122.112500, 37.311000, -122.113100, 37.308000, -122.116700, 37.292000, -122.118700, 37.285000, -122.120000, 37.280000)
+State Hwy 92                  Ramp     (0, 3, -122.108600, 37.321000, -122.108900, 37.315000 , -122.111100, 37.316000)
+State Hwy 92                  Ramp     (0, 2, -122.099000, 37.368000, -122.099400, 37.363000 )
+State Hwy 84                           (0, 4, -122.067100, 37.426000, -122.070000, 37.402000 , -122.074000, 37.370000, -122.077300, 37.338000)
+State Hwy 84                           (0, 2, -122.067100, 37.426000, -122.065800, 37.432000 )
+State Hwy 84                           (0, 3, -122.048400, 37.539000, -122.044300, 37.564000 , -122.042300, 37.577000)
+State Hwy 84                           (0, 4, -122.048400, 37.539000, -122.051400, 37.520000 , -122.051557, 37.519060, -122.054400, 37.502000)
+State Hwy 84                  Ramp     (0, 4, -122.054400, 37.502000, -122.052622, 37.509020 , -122.050600, 37.517000, -122.048400, 37.539000)
+State Hwy 84                           (0, 9, -121.972500, 37.749000, -121.972151, 37.751440 , -121.971500, 37.756000, -121.970600, 37.762000, -121.969200, 37.778000, -121.967300, 37.793000, -121.963700, 37.813000, -121.963700, 37.854000, -121.957600, 37.891000)
+State Hwy 84                           (0, 5, -121.956500, 37.898000, -121.956589, 37.899110 , -121.956900, 37.903000, -121.956000, 37.910000, -121.955300, 37.919000)
+State Hwy 84                           (0, 4, -121.767300, 37.820000, -121.766400, 37.828000 , -121.765400, 37.835000, -121.765000, 37.837000)
diff --git a/src/test/regress/data/stud_emp.data b/src/test/regress/data/stud_emp.data
new file mode 100644 (file)
index 0000000..315cefe
--- /dev/null
@@ -0,0 +1,3 @@
+jeff   23      (8,7.7) 600     sharon  3.50000000000000000e+00 
+cim    30      (10.5,4.7)      400             3.39999999999999990e+00 
+linda  19      (0.9,6.1)       100             2.89999999999999990e+00 
diff --git a/src/test/regress/data/student.data b/src/test/regress/data/student.data
new file mode 100644 (file)
index 0000000..f7e29e4
--- /dev/null
@@ -0,0 +1,2 @@
+fred   28      (3.1,-1.5)      3.70000000000000020e+00
+larry  60      (21.8,4.9)      3.10000000000000010e+00
diff --git a/src/test/regress/data/tenk.data b/src/test/regress/data/tenk.data
new file mode 100644 (file)
index 0000000..c9064c9
--- /dev/null
@@ -0,0 +1,10000 @@
+8800   0       0       0       0       0       0       800     800     3800    8800    0       1       MAAAAA  AAAAAA  AAAAxx
+1891   1       1       3       1       11      91      891     1891    1891    1891    182     183     TUAAAA  BAAAAA  HHHHxx
+3420   2       0       0       0       0       20      420     1420    3420    3420    40      41      OBAAAA  CAAAAA  OOOOxx
+9850   3       0       2       0       10      50      850     1850    4850    9850    100     101     WOAAAA  DAAAAA  VVVVxx
+7164   4       0       0       4       4       64      164     1164    2164    7164    128     129     OPAAAA  EAAAAA  AAAAxx
+8009   5       1       1       9       9       9       9       9       3009    8009    18      19      BWAAAA  FAAAAA  HHHHxx
+5057   6       1       1       7       17      57      57      1057    57      5057    114     115     NMAAAA  GAAAAA  OOOOxx
+6701   7       1       1       1       1       1       701     701     1701    6701    2       3       TXAAAA  HAAAAA  VVVVxx
+4321   8       1       1       1       1       21      321     321     4321    4321    42      43      FKAAAA  IAAAAA  AAAAxx
+3043   9       1       3       3       3       43      43      1043    3043    3043    86      87      BNAAAA  JAAAAA  HHHHxx
+1314   10      0       2       4       14      14      314     1314    1314    1314    28      29      OYAAAA  KAAAAA  OOOOxx
+1504   11      0       0       4       4       4       504     1504    1504    1504    8       9       WFAAAA  LAAAAA  VVVVxx
+5222   12      0       2       2       2       22      222     1222    222     5222    44      45      WSAAAA  MAAAAA  AAAAxx
+6243   13      1       3       3       3       43      243     243     1243    6243    86      87      DGAAAA  NAAAAA  HHHHxx
+5471   14      1       3       1       11      71      471     1471    471     5471    142     143     LCAAAA  OAAAAA  OOOOxx
+5006   15      0       2       6       6       6       6       1006    6       5006    12      13      OKAAAA  PAAAAA  VVVVxx
+5387   16      1       3       7       7       87      387     1387    387     5387    174     175     FZAAAA  QAAAAA  AAAAxx
+5785   17      1       1       5       5       85      785     1785    785     5785    170     171     NOAAAA  RAAAAA  HHHHxx
+6621   18      1       1       1       1       21      621     621     1621    6621    42      43      RUAAAA  SAAAAA  OOOOxx
+6969   19      1       1       9       9       69      969     969     1969    6969    138     139     BIAAAA  TAAAAA  VVVVxx
+9460   20      0       0       0       0       60      460     1460    4460    9460    120     121     WZAAAA  UAAAAA  AAAAxx
+59     21      1       3       9       19      59      59      59      59      59      118     119     HCAAAA  VAAAAA  HHHHxx
+8020   22      0       0       0       0       20      20      20      3020    8020    40      41      MWAAAA  WAAAAA  OOOOxx
+7695   23      1       3       5       15      95      695     1695    2695    7695    190     191     ZJAAAA  XAAAAA  VVVVxx
+3442   24      0       2       2       2       42      442     1442    3442    3442    84      85      KCAAAA  YAAAAA  AAAAxx
+5119   25      1       3       9       19      19      119     1119    119     5119    38      39      XOAAAA  ZAAAAA  HHHHxx
+646    26      0       2       6       6       46      646     646     646     646     92      93      WYAAAA  ABAAAA  OOOOxx
+9605   27      1       1       5       5       5       605     1605    4605    9605    10      11      LFAAAA  BBAAAA  VVVVxx
+263    28      1       3       3       3       63      263     263     263     263     126     127     DKAAAA  CBAAAA  AAAAxx
+3269   29      1       1       9       9       69      269     1269    3269    3269    138     139     TVAAAA  DBAAAA  HHHHxx
+1839   30      1       3       9       19      39      839     1839    1839    1839    78      79      TSAAAA  EBAAAA  OOOOxx
+9144   31      0       0       4       4       44      144     1144    4144    9144    88      89      SNAAAA  FBAAAA  VVVVxx
+2513   32      1       1       3       13      13      513     513     2513    2513    26      27      RSAAAA  GBAAAA  AAAAxx
+8850   33      0       2       0       10      50      850     850     3850    8850    100     101     KCAAAA  HBAAAA  HHHHxx
+236    34      0       0       6       16      36      236     236     236     236     72      73      CJAAAA  IBAAAA  OOOOxx
+3162   35      0       2       2       2       62      162     1162    3162    3162    124     125     QRAAAA  JBAAAA  VVVVxx
+4380   36      0       0       0       0       80      380     380     4380    4380    160     161     MMAAAA  KBAAAA  AAAAxx
+8095   37      1       3       5       15      95      95      95      3095    8095    190     191     JZAAAA  LBAAAA  HHHHxx
+209    38      1       1       9       9       9       209     209     209     209     18      19      BIAAAA  MBAAAA  OOOOxx
+3055   39      1       3       5       15      55      55      1055    3055    3055    110     111     NNAAAA  NBAAAA  VVVVxx
+6921   40      1       1       1       1       21      921     921     1921    6921    42      43      FGAAAA  OBAAAA  AAAAxx
+7046   41      0       2       6       6       46      46      1046    2046    7046    92      93      ALAAAA  PBAAAA  HHHHxx
+7912   42      0       0       2       12      12      912     1912    2912    7912    24      25      ISAAAA  QBAAAA  OOOOxx
+7267   43      1       3       7       7       67      267     1267    2267    7267    134     135     NTAAAA  RBAAAA  VVVVxx
+3599   44      1       3       9       19      99      599     1599    3599    3599    198     199     LIAAAA  SBAAAA  AAAAxx
+923    45      1       3       3       3       23      923     923     923     923     46      47      NJAAAA  TBAAAA  HHHHxx
+1437   46      1       1       7       17      37      437     1437    1437    1437    74      75      HDAAAA  UBAAAA  OOOOxx
+6439   47      1       3       9       19      39      439     439     1439    6439    78      79      RNAAAA  VBAAAA  VVVVxx
+6989   48      1       1       9       9       89      989     989     1989    6989    178     179     VIAAAA  WBAAAA  AAAAxx
+8798   49      0       2       8       18      98      798     798     3798    8798    196     197     KAAAAA  XBAAAA  HHHHxx
+5960   50      0       0       0       0       60      960     1960    960     5960    120     121     GVAAAA  YBAAAA  OOOOxx
+5832   51      0       0       2       12      32      832     1832    832     5832    64      65      IQAAAA  ZBAAAA  VVVVxx
+6066   52      0       2       6       6       66      66      66      1066    6066    132     133     IZAAAA  ACAAAA  AAAAxx
+322    53      0       2       2       2       22      322     322     322     322     44      45      KMAAAA  BCAAAA  HHHHxx
+8321   54      1       1       1       1       21      321     321     3321    8321    42      43      BIAAAA  CCAAAA  OOOOxx
+734    55      0       2       4       14      34      734     734     734     734     68      69      GCAAAA  DCAAAA  VVVVxx
+688    56      0       0       8       8       88      688     688     688     688     176     177     MAAAAA  ECAAAA  AAAAxx
+4212   57      0       0       2       12      12      212     212     4212    4212    24      25      AGAAAA  FCAAAA  HHHHxx
+9653   58      1       1       3       13      53      653     1653    4653    9653    106     107     HHAAAA  GCAAAA  OOOOxx
+2677   59      1       1       7       17      77      677     677     2677    2677    154     155     ZYAAAA  HCAAAA  VVVVxx
+5423   60      1       3       3       3       23      423     1423    423     5423    46      47      PAAAAA  ICAAAA  AAAAxx
+2592   61      0       0       2       12      92      592     592     2592    2592    184     185     SVAAAA  JCAAAA  HHHHxx
+3233   62      1       1       3       13      33      233     1233    3233    3233    66      67      JUAAAA  KCAAAA  OOOOxx
+5032   63      0       0       2       12      32      32      1032    32      5032    64      65      OLAAAA  LCAAAA  VVVVxx
+2525   64      1       1       5       5       25      525     525     2525    2525    50      51      DTAAAA  MCAAAA  AAAAxx
+4450   65      0       2       0       10      50      450     450     4450    4450    100     101     EPAAAA  NCAAAA  HHHHxx
+5778   66      0       2       8       18      78      778     1778    778     5778    156     157     GOAAAA  OCAAAA  OOOOxx
+5852   67      0       0       2       12      52      852     1852    852     5852    104     105     CRAAAA  PCAAAA  VVVVxx
+5404   68      0       0       4       4       4       404     1404    404     5404    8       9       WZAAAA  QCAAAA  AAAAxx
+6223   69      1       3       3       3       23      223     223     1223    6223    46      47      JFAAAA  RCAAAA  HHHHxx
+6133   70      1       1       3       13      33      133     133     1133    6133    66      67      XBAAAA  SCAAAA  OOOOxx
+9112   71      0       0       2       12      12      112     1112    4112    9112    24      25      MMAAAA  TCAAAA  VVVVxx
+7575   72      1       3       5       15      75      575     1575    2575    7575    150     151     JFAAAA  UCAAAA  AAAAxx
+7414   73      0       2       4       14      14      414     1414    2414    7414    28      29      EZAAAA  VCAAAA  HHHHxx
+9741   74      1       1       1       1       41      741     1741    4741    9741    82      83      RKAAAA  WCAAAA  OOOOxx
+3767   75      1       3       7       7       67      767     1767    3767    3767    134     135     XOAAAA  XCAAAA  VVVVxx
+9372   76      0       0       2       12      72      372     1372    4372    9372    144     145     MWAAAA  YCAAAA  AAAAxx
+8976   77      0       0       6       16      76      976     976     3976    8976    152     153     GHAAAA  ZCAAAA  HHHHxx
+4071   78      1       3       1       11      71      71      71      4071    4071    142     143     PAAAAA  ADAAAA  OOOOxx
+1311   79      1       3       1       11      11      311     1311    1311    1311    22      23      LYAAAA  BDAAAA  VVVVxx
+2604   80      0       0       4       4       4       604     604     2604    2604    8       9       EWAAAA  CDAAAA  AAAAxx
+8840   81      0       0       0       0       40      840     840     3840    8840    80      81      ACAAAA  DDAAAA  HHHHxx
+567    82      1       3       7       7       67      567     567     567     567     134     135     VVAAAA  EDAAAA  OOOOxx
+5215   83      1       3       5       15      15      215     1215    215     5215    30      31      PSAAAA  FDAAAA  VVVVxx
+5474   84      0       2       4       14      74      474     1474    474     5474    148     149     OCAAAA  GDAAAA  AAAAxx
+3906   85      0       2       6       6       6       906     1906    3906    3906    12      13      GUAAAA  HDAAAA  HHHHxx
+1769   86      1       1       9       9       69      769     1769    1769    1769    138     139     BQAAAA  IDAAAA  OOOOxx
+1454   87      0       2       4       14      54      454     1454    1454    1454    108     109     YDAAAA  JDAAAA  VVVVxx
+6877   88      1       1       7       17      77      877     877     1877    6877    154     155     NEAAAA  KDAAAA  AAAAxx
+6501   89      1       1       1       1       1       501     501     1501    6501    2       3       BQAAAA  LDAAAA  HHHHxx
+934    90      0       2       4       14      34      934     934     934     934     68      69      YJAAAA  MDAAAA  OOOOxx
+4075   91      1       3       5       15      75      75      75      4075    4075    150     151     TAAAAA  NDAAAA  VVVVxx
+3180   92      0       0       0       0       80      180     1180    3180    3180    160     161     ISAAAA  ODAAAA  AAAAxx
+7787   93      1       3       7       7       87      787     1787    2787    7787    174     175     NNAAAA  PDAAAA  HHHHxx
+6401   94      1       1       1       1       1       401     401     1401    6401    2       3       FMAAAA  QDAAAA  OOOOxx
+4244   95      0       0       4       4       44      244     244     4244    4244    88      89      GHAAAA  RDAAAA  VVVVxx
+4591   96      1       3       1       11      91      591     591     4591    4591    182     183     PUAAAA  SDAAAA  AAAAxx
+4113   97      1       1       3       13      13      113     113     4113    4113    26      27      FCAAAA  TDAAAA  HHHHxx
+5925   98      1       1       5       5       25      925     1925    925     5925    50      51      XTAAAA  UDAAAA  OOOOxx
+1987   99      1       3       7       7       87      987     1987    1987    1987    174     175     LYAAAA  VDAAAA  VVVVxx
+8248   100     0       0       8       8       48      248     248     3248    8248    96      97      GFAAAA  WDAAAA  AAAAxx
+4151   101     1       3       1       11      51      151     151     4151    4151    102     103     RDAAAA  XDAAAA  HHHHxx
+8670   102     0       2       0       10      70      670     670     3670    8670    140     141     MVAAAA  YDAAAA  OOOOxx
+6194   103     0       2       4       14      94      194     194     1194    6194    188     189     GEAAAA  ZDAAAA  VVVVxx
+88     104     0       0       8       8       88      88      88      88      88      176     177     KDAAAA  AEAAAA  AAAAxx
+4058   105     0       2       8       18      58      58      58      4058    4058    116     117     CAAAAA  BEAAAA  HHHHxx
+2742   106     0       2       2       2       42      742     742     2742    2742    84      85      MBAAAA  CEAAAA  OOOOxx
+8275   107     1       3       5       15      75      275     275     3275    8275    150     151     HGAAAA  DEAAAA  VVVVxx
+4258   108     0       2       8       18      58      258     258     4258    4258    116     117     UHAAAA  EEAAAA  AAAAxx
+6129   109     1       1       9       9       29      129     129     1129    6129    58      59      TBAAAA  FEAAAA  HHHHxx
+7243   110     1       3       3       3       43      243     1243    2243    7243    86      87      PSAAAA  GEAAAA  OOOOxx
+2392   111     0       0       2       12      92      392     392     2392    2392    184     185     AOAAAA  HEAAAA  VVVVxx
+9853   112     1       1       3       13      53      853     1853    4853    9853    106     107     ZOAAAA  IEAAAA  AAAAxx
+6064   113     0       0       4       4       64      64      64      1064    6064    128     129     GZAAAA  JEAAAA  HHHHxx
+4391   114     1       3       1       11      91      391     391     4391    4391    182     183     XMAAAA  KEAAAA  OOOOxx
+726    115     0       2       6       6       26      726     726     726     726     52      53      YBAAAA  LEAAAA  VVVVxx
+6957   116     1       1       7       17      57      957     957     1957    6957    114     115     PHAAAA  MEAAAA  AAAAxx
+3853   117     1       1       3       13      53      853     1853    3853    3853    106     107     FSAAAA  NEAAAA  HHHHxx
+4524   118     0       0       4       4       24      524     524     4524    4524    48      49      ASAAAA  OEAAAA  OOOOxx
+5330   119     0       2       0       10      30      330     1330    330     5330    60      61      AXAAAA  PEAAAA  VVVVxx
+6671   120     1       3       1       11      71      671     671     1671    6671    142     143     PWAAAA  QEAAAA  AAAAxx
+5314   121     0       2       4       14      14      314     1314    314     5314    28      29      KWAAAA  REAAAA  HHHHxx
+9202   122     0       2       2       2       2       202     1202    4202    9202    4       5       YPAAAA  SEAAAA  OOOOxx
+4596   123     0       0       6       16      96      596     596     4596    4596    192     193     UUAAAA  TEAAAA  VVVVxx
+8951   124     1       3       1       11      51      951     951     3951    8951    102     103     HGAAAA  UEAAAA  AAAAxx
+9902   125     0       2       2       2       2       902     1902    4902    9902    4       5       WQAAAA  VEAAAA  HHHHxx
+1440   126     0       0       0       0       40      440     1440    1440    1440    80      81      KDAAAA  WEAAAA  OOOOxx
+5339   127     1       3       9       19      39      339     1339    339     5339    78      79      JXAAAA  XEAAAA  VVVVxx
+3371   128     1       3       1       11      71      371     1371    3371    3371    142     143     RZAAAA  YEAAAA  AAAAxx
+4467   129     1       3       7       7       67      467     467     4467    4467    134     135     VPAAAA  ZEAAAA  HHHHxx
+6216   130     0       0       6       16      16      216     216     1216    6216    32      33      CFAAAA  AFAAAA  OOOOxx
+5364   131     0       0       4       4       64      364     1364    364     5364    128     129     IYAAAA  BFAAAA  VVVVxx
+7547   132     1       3       7       7       47      547     1547    2547    7547    94      95      HEAAAA  CFAAAA  AAAAxx
+4338   133     0       2       8       18      38      338     338     4338    4338    76      77      WKAAAA  DFAAAA  HHHHxx
+3481   134     1       1       1       1       81      481     1481    3481    3481    162     163     XDAAAA  EFAAAA  OOOOxx
+826    135     0       2       6       6       26      826     826     826     826     52      53      UFAAAA  FFAAAA  VVVVxx
+3647   136     1       3       7       7       47      647     1647    3647    3647    94      95      HKAAAA  GFAAAA  AAAAxx
+3337   137     1       1       7       17      37      337     1337    3337    3337    74      75      JYAAAA  HFAAAA  HHHHxx
+3591   138     1       3       1       11      91      591     1591    3591    3591    182     183     DIAAAA  IFAAAA  OOOOxx
+7192   139     0       0       2       12      92      192     1192    2192    7192    184     185     QQAAAA  JFAAAA  VVVVxx
+1078   140     0       2       8       18      78      78      1078    1078    1078    156     157     MPAAAA  KFAAAA  AAAAxx
+1310   141     0       2       0       10      10      310     1310    1310    1310    20      21      KYAAAA  LFAAAA  HHHHxx
+9642   142     0       2       2       2       42      642     1642    4642    9642    84      85      WGAAAA  MFAAAA  OOOOxx
+39     143     1       3       9       19      39      39      39      39      39      78      79      NBAAAA  NFAAAA  VVVVxx
+8682   144     0       2       2       2       82      682     682     3682    8682    164     165     YVAAAA  OFAAAA  AAAAxx
+1794   145     0       2       4       14      94      794     1794    1794    1794    188     189     ARAAAA  PFAAAA  HHHHxx
+5630   146     0       2       0       10      30      630     1630    630     5630    60      61      OIAAAA  QFAAAA  OOOOxx
+6748   147     0       0       8       8       48      748     748     1748    6748    96      97      OZAAAA  RFAAAA  VVVVxx
+3766   148     0       2       6       6       66      766     1766    3766    3766    132     133     WOAAAA  SFAAAA  AAAAxx
+6403   149     1       3       3       3       3       403     403     1403    6403    6       7       HMAAAA  TFAAAA  HHHHxx
+175    150     1       3       5       15      75      175     175     175     175     150     151     TGAAAA  UFAAAA  OOOOxx
+2179   151     1       3       9       19      79      179     179     2179    2179    158     159     VFAAAA  VFAAAA  VVVVxx
+7897   152     1       1       7       17      97      897     1897    2897    7897    194     195     TRAAAA  WFAAAA  AAAAxx
+2760   153     0       0       0       0       60      760     760     2760    2760    120     121     ECAAAA  XFAAAA  HHHHxx
+1675   154     1       3       5       15      75      675     1675    1675    1675    150     151     LMAAAA  YFAAAA  OOOOxx
+2564   155     0       0       4       4       64      564     564     2564    2564    128     129     QUAAAA  ZFAAAA  VVVVxx
+157    156     1       1       7       17      57      157     157     157     157     114     115     BGAAAA  AGAAAA  AAAAxx
+8779   157     1       3       9       19      79      779     779     3779    8779    158     159     RZAAAA  BGAAAA  HHHHxx
+9591   158     1       3       1       11      91      591     1591    4591    9591    182     183     XEAAAA  CGAAAA  OOOOxx
+8732   159     0       0       2       12      32      732     732     3732    8732    64      65      WXAAAA  DGAAAA  VVVVxx
+139    160     1       3       9       19      39      139     139     139     139     78      79      JFAAAA  EGAAAA  AAAAxx
+5372   161     0       0       2       12      72      372     1372    372     5372    144     145     QYAAAA  FGAAAA  HHHHxx
+1278   162     0       2       8       18      78      278     1278    1278    1278    156     157     EXAAAA  GGAAAA  OOOOxx
+4697   163     1       1       7       17      97      697     697     4697    4697    194     195     RYAAAA  HGAAAA  VVVVxx
+8610   164     0       2       0       10      10      610     610     3610    8610    20      21      ETAAAA  IGAAAA  AAAAxx
+8180   165     0       0       0       0       80      180     180     3180    8180    160     161     QCAAAA  JGAAAA  HHHHxx
+2399   166     1       3       9       19      99      399     399     2399    2399    198     199     HOAAAA  KGAAAA  OOOOxx
+615    167     1       3       5       15      15      615     615     615     615     30      31      RXAAAA  LGAAAA  VVVVxx
+7629   168     1       1       9       9       29      629     1629    2629    7629    58      59      LHAAAA  MGAAAA  AAAAxx
+7628   169     0       0       8       8       28      628     1628    2628    7628    56      57      KHAAAA  NGAAAA  HHHHxx
+4659   170     1       3       9       19      59      659     659     4659    4659    118     119     FXAAAA  OGAAAA  OOOOxx
+5865   171     1       1       5       5       65      865     1865    865     5865    130     131     PRAAAA  PGAAAA  VVVVxx
+3973   172     1       1       3       13      73      973     1973    3973    3973    146     147     VWAAAA  QGAAAA  AAAAxx
+552    173     0       0       2       12      52      552     552     552     552     104     105     GVAAAA  RGAAAA  HHHHxx
+708    174     0       0       8       8       8       708     708     708     708     16      17      GBAAAA  SGAAAA  OOOOxx
+3550   175     0       2       0       10      50      550     1550    3550    3550    100     101     OGAAAA  TGAAAA  VVVVxx
+5547   176     1       3       7       7       47      547     1547    547     5547    94      95      JFAAAA  UGAAAA  AAAAxx
+489    177     1       1       9       9       89      489     489     489     489     178     179     VSAAAA  VGAAAA  HHHHxx
+3794   178     0       2       4       14      94      794     1794    3794    3794    188     189     YPAAAA  WGAAAA  OOOOxx
+9479   179     1       3       9       19      79      479     1479    4479    9479    158     159     PAAAAA  XGAAAA  VVVVxx
+6435   180     1       3       5       15      35      435     435     1435    6435    70      71      NNAAAA  YGAAAA  AAAAxx
+5120   181     0       0       0       0       20      120     1120    120     5120    40      41      YOAAAA  ZGAAAA  HHHHxx
+3615   182     1       3       5       15      15      615     1615    3615    3615    30      31      BJAAAA  AHAAAA  OOOOxx
+8399   183     1       3       9       19      99      399     399     3399    8399    198     199     BLAAAA  BHAAAA  VVVVxx
+2155   184     1       3       5       15      55      155     155     2155    2155    110     111     XEAAAA  CHAAAA  AAAAxx
+6690   185     0       2       0       10      90      690     690     1690    6690    180     181     IXAAAA  DHAAAA  HHHHxx
+1683   186     1       3       3       3       83      683     1683    1683    1683    166     167     TMAAAA  EHAAAA  OOOOxx
+6302   187     0       2       2       2       2       302     302     1302    6302    4       5       KIAAAA  FHAAAA  VVVVxx
+516    188     0       0       6       16      16      516     516     516     516     32      33      WTAAAA  GHAAAA  AAAAxx
+3901   189     1       1       1       1       1       901     1901    3901    3901    2       3       BUAAAA  HHAAAA  HHHHxx
+6938   190     0       2       8       18      38      938     938     1938    6938    76      77      WGAAAA  IHAAAA  OOOOxx
+7484   191     0       0       4       4       84      484     1484    2484    7484    168     169     WBAAAA  JHAAAA  VVVVxx
+7424   192     0       0       4       4       24      424     1424    2424    7424    48      49      OZAAAA  KHAAAA  AAAAxx
+9410   193     0       2       0       10      10      410     1410    4410    9410    20      21      YXAAAA  LHAAAA  HHHHxx
+1714   194     0       2       4       14      14      714     1714    1714    1714    28      29      YNAAAA  MHAAAA  OOOOxx
+8278   195     0       2       8       18      78      278     278     3278    8278    156     157     KGAAAA  NHAAAA  VVVVxx
+3158   196     0       2       8       18      58      158     1158    3158    3158    116     117     MRAAAA  OHAAAA  AAAAxx
+2511   197     1       3       1       11      11      511     511     2511    2511    22      23      PSAAAA  PHAAAA  HHHHxx
+2912   198     0       0       2       12      12      912     912     2912    2912    24      25      AIAAAA  QHAAAA  OOOOxx
+2648   199     0       0       8       8       48      648     648     2648    2648    96      97      WXAAAA  RHAAAA  VVVVxx
+9385   200     1       1       5       5       85      385     1385    4385    9385    170     171     ZWAAAA  SHAAAA  AAAAxx
+7545   201     1       1       5       5       45      545     1545    2545    7545    90      91      FEAAAA  THAAAA  HHHHxx
+8407   202     1       3       7       7       7       407     407     3407    8407    14      15      JLAAAA  UHAAAA  OOOOxx
+5893   203     1       1       3       13      93      893     1893    893     5893    186     187     RSAAAA  VHAAAA  VVVVxx
+7049   204     1       1       9       9       49      49      1049    2049    7049    98      99      DLAAAA  WHAAAA  AAAAxx
+6812   205     0       0       2       12      12      812     812     1812    6812    24      25      ACAAAA  XHAAAA  HHHHxx
+3649   206     1       1       9       9       49      649     1649    3649    3649    98      99      JKAAAA  YHAAAA  OOOOxx
+9275   207     1       3       5       15      75      275     1275    4275    9275    150     151     TSAAAA  ZHAAAA  VVVVxx
+1179   208     1       3       9       19      79      179     1179    1179    1179    158     159     JTAAAA  AIAAAA  AAAAxx
+969    209     1       1       9       9       69      969     969     969     969     138     139     HLAAAA  BIAAAA  HHHHxx
+7920   210     0       0       0       0       20      920     1920    2920    7920    40      41      QSAAAA  CIAAAA  OOOOxx
+998    211     0       2       8       18      98      998     998     998     998     196     197     KMAAAA  DIAAAA  VVVVxx
+3958   212     0       2       8       18      58      958     1958    3958    3958    116     117     GWAAAA  EIAAAA  AAAAxx
+6052   213     0       0       2       12      52      52      52      1052    6052    104     105     UYAAAA  FIAAAA  HHHHxx
+8791   214     1       3       1       11      91      791     791     3791    8791    182     183     DAAAAA  GIAAAA  OOOOxx
+5191   215     1       3       1       11      91      191     1191    191     5191    182     183     RRAAAA  HIAAAA  VVVVxx
+4267   216     1       3       7       7       67      267     267     4267    4267    134     135     DIAAAA  IIAAAA  AAAAxx
+2829   217     1       1       9       9       29      829     829     2829    2829    58      59      VEAAAA  JIAAAA  HHHHxx
+6396   218     0       0       6       16      96      396     396     1396    6396    192     193     AMAAAA  KIAAAA  OOOOxx
+9413   219     1       1       3       13      13      413     1413    4413    9413    26      27      BYAAAA  LIAAAA  VVVVxx
+614    220     0       2       4       14      14      614     614     614     614     28      29      QXAAAA  MIAAAA  AAAAxx
+4660   221     0       0       0       0       60      660     660     4660    4660    120     121     GXAAAA  NIAAAA  HHHHxx
+8834   222     0       2       4       14      34      834     834     3834    8834    68      69      UBAAAA  OIAAAA  OOOOxx
+2767   223     1       3       7       7       67      767     767     2767    2767    134     135     LCAAAA  PIAAAA  VVVVxx
+2444   224     0       0       4       4       44      444     444     2444    2444    88      89      AQAAAA  QIAAAA  AAAAxx
+4129   225     1       1       9       9       29      129     129     4129    4129    58      59      VCAAAA  RIAAAA  HHHHxx
+3394   226     0       2       4       14      94      394     1394    3394    3394    188     189     OAAAAA  SIAAAA  OOOOxx
+2705   227     1       1       5       5       5       705     705     2705    2705    10      11      BAAAAA  TIAAAA  VVVVxx
+8499   228     1       3       9       19      99      499     499     3499    8499    198     199     XOAAAA  UIAAAA  AAAAxx
+8852   229     0       0       2       12      52      852     852     3852    8852    104     105     MCAAAA  VIAAAA  HHHHxx
+6174   230     0       2       4       14      74      174     174     1174    6174    148     149     MDAAAA  WIAAAA  OOOOxx
+750    231     0       2       0       10      50      750     750     750     750     100     101     WCAAAA  XIAAAA  VVVVxx
+8164   232     0       0       4       4       64      164     164     3164    8164    128     129     ACAAAA  YIAAAA  AAAAxx
+4930   233     0       2       0       10      30      930     930     4930    4930    60      61      QHAAAA  ZIAAAA  HHHHxx
+9904   234     0       0       4       4       4       904     1904    4904    9904    8       9       YQAAAA  AJAAAA  OOOOxx
+7378   235     0       2       8       18      78      378     1378    2378    7378    156     157     UXAAAA  BJAAAA  VVVVxx
+2927   236     1       3       7       7       27      927     927     2927    2927    54      55      PIAAAA  CJAAAA  AAAAxx
+7155   237     1       3       5       15      55      155     1155    2155    7155    110     111     FPAAAA  DJAAAA  HHHHxx
+1302   238     0       2       2       2       2       302     1302    1302    1302    4       5       CYAAAA  EJAAAA  OOOOxx
+5904   239     0       0       4       4       4       904     1904    904     5904    8       9       CTAAAA  FJAAAA  VVVVxx
+9687   240     1       3       7       7       87      687     1687    4687    9687    174     175     PIAAAA  GJAAAA  AAAAxx
+3553   241     1       1       3       13      53      553     1553    3553    3553    106     107     RGAAAA  HJAAAA  HHHHxx
+4447   242     1       3       7       7       47      447     447     4447    4447    94      95      BPAAAA  IJAAAA  OOOOxx
+6878   243     0       2       8       18      78      878     878     1878    6878    156     157     OEAAAA  JJAAAA  VVVVxx
+9470   244     0       2       0       10      70      470     1470    4470    9470    140     141     GAAAAA  KJAAAA  AAAAxx
+9735   245     1       3       5       15      35      735     1735    4735    9735    70      71      LKAAAA  LJAAAA  HHHHxx
+5967   246     1       3       7       7       67      967     1967    967     5967    134     135     NVAAAA  MJAAAA  OOOOxx
+6601   247     1       1       1       1       1       601     601     1601    6601    2       3       XTAAAA  NJAAAA  VVVVxx
+7631   248     1       3       1       11      31      631     1631    2631    7631    62      63      NHAAAA  OJAAAA  AAAAxx
+3559   249     1       3       9       19      59      559     1559    3559    3559    118     119     XGAAAA  PJAAAA  HHHHxx
+2247   250     1       3       7       7       47      247     247     2247    2247    94      95      LIAAAA  QJAAAA  OOOOxx
+9649   251     1       1       9       9       49      649     1649    4649    9649    98      99      DHAAAA  RJAAAA  VVVVxx
+808    252     0       0       8       8       8       808     808     808     808     16      17      CFAAAA  SJAAAA  AAAAxx
+240    253     0       0       0       0       40      240     240     240     240     80      81      GJAAAA  TJAAAA  HHHHxx
+5031   254     1       3       1       11      31      31      1031    31      5031    62      63      NLAAAA  UJAAAA  OOOOxx
+9563   255     1       3       3       3       63      563     1563    4563    9563    126     127     VDAAAA  VJAAAA  VVVVxx
+5656   256     0       0       6       16      56      656     1656    656     5656    112     113     OJAAAA  WJAAAA  AAAAxx
+3886   257     0       2       6       6       86      886     1886    3886    3886    172     173     MTAAAA  XJAAAA  HHHHxx
+2431   258     1       3       1       11      31      431     431     2431    2431    62      63      NPAAAA  YJAAAA  OOOOxx
+5560   259     0       0       0       0       60      560     1560    560     5560    120     121     WFAAAA  ZJAAAA  VVVVxx
+9065   260     1       1       5       5       65      65      1065    4065    9065    130     131     RKAAAA  AKAAAA  AAAAxx
+8130   261     0       2       0       10      30      130     130     3130    8130    60      61      SAAAAA  BKAAAA  HHHHxx
+4054   262     0       2       4       14      54      54      54      4054    4054    108     109     YZAAAA  CKAAAA  OOOOxx
+873    263     1       1       3       13      73      873     873     873     873     146     147     PHAAAA  DKAAAA  VVVVxx
+3092   264     0       0       2       12      92      92      1092    3092    3092    184     185     YOAAAA  EKAAAA  AAAAxx
+6697   265     1       1       7       17      97      697     697     1697    6697    194     195     PXAAAA  FKAAAA  HHHHxx
+2452   266     0       0       2       12      52      452     452     2452    2452    104     105     IQAAAA  GKAAAA  OOOOxx
+7867   267     1       3       7       7       67      867     1867    2867    7867    134     135     PQAAAA  HKAAAA  VVVVxx
+3753   268     1       1       3       13      53      753     1753    3753    3753    106     107     JOAAAA  IKAAAA  AAAAxx
+7834   269     0       2       4       14      34      834     1834    2834    7834    68      69      IPAAAA  JKAAAA  HHHHxx
+5846   270     0       2       6       6       46      846     1846    846     5846    92      93      WQAAAA  KKAAAA  OOOOxx
+7604   271     0       0       4       4       4       604     1604    2604    7604    8       9       MGAAAA  LKAAAA  VVVVxx
+3452   272     0       0       2       12      52      452     1452    3452    3452    104     105     UCAAAA  MKAAAA  AAAAxx
+4788   273     0       0       8       8       88      788     788     4788    4788    176     177     ECAAAA  NKAAAA  HHHHxx
+8600   274     0       0       0       0       0       600     600     3600    8600    0       1       USAAAA  OKAAAA  OOOOxx
+8511   275     1       3       1       11      11      511     511     3511    8511    22      23      JPAAAA  PKAAAA  VVVVxx
+4452   276     0       0       2       12      52      452     452     4452    4452    104     105     GPAAAA  QKAAAA  AAAAxx
+1709   277     1       1       9       9       9       709     1709    1709    1709    18      19      TNAAAA  RKAAAA  HHHHxx
+3440   278     0       0       0       0       40      440     1440    3440    3440    80      81      ICAAAA  SKAAAA  OOOOxx
+9188   279     0       0       8       8       88      188     1188    4188    9188    176     177     KPAAAA  TKAAAA  VVVVxx
+3058   280     0       2       8       18      58      58      1058    3058    3058    116     117     QNAAAA  UKAAAA  AAAAxx
+5821   281     1       1       1       1       21      821     1821    821     5821    42      43      XPAAAA  VKAAAA  HHHHxx
+3428   282     0       0       8       8       28      428     1428    3428    3428    56      57      WBAAAA  WKAAAA  OOOOxx
+3581   283     1       1       1       1       81      581     1581    3581    3581    162     163     THAAAA  XKAAAA  VVVVxx
+7523   284     1       3       3       3       23      523     1523    2523    7523    46      47      JDAAAA  YKAAAA  AAAAxx
+3131   285     1       3       1       11      31      131     1131    3131    3131    62      63      LQAAAA  ZKAAAA  HHHHxx
+2404   286     0       0       4       4       4       404     404     2404    2404    8       9       MOAAAA  ALAAAA  OOOOxx
+5453   287     1       1       3       13      53      453     1453    453     5453    106     107     TBAAAA  BLAAAA  VVVVxx
+1599   288     1       3       9       19      99      599     1599    1599    1599    198     199     NJAAAA  CLAAAA  AAAAxx
+7081   289     1       1       1       1       81      81      1081    2081    7081    162     163     JMAAAA  DLAAAA  HHHHxx
+1750   290     0       2       0       10      50      750     1750    1750    1750    100     101     IPAAAA  ELAAAA  OOOOxx
+5085   291     1       1       5       5       85      85      1085    85      5085    170     171     PNAAAA  FLAAAA  VVVVxx
+9777   292     1       1       7       17      77      777     1777    4777    9777    154     155     BMAAAA  GLAAAA  AAAAxx
+574    293     0       2       4       14      74      574     574     574     574     148     149     CWAAAA  HLAAAA  HHHHxx
+5984   294     0       0       4       4       84      984     1984    984     5984    168     169     EWAAAA  ILAAAA  OOOOxx
+7039   295     1       3       9       19      39      39      1039    2039    7039    78      79      TKAAAA  JLAAAA  VVVVxx
+7143   296     1       3       3       3       43      143     1143    2143    7143    86      87      TOAAAA  KLAAAA  AAAAxx
+5702   297     0       2       2       2       2       702     1702    702     5702    4       5       ILAAAA  LLAAAA  HHHHxx
+362    298     0       2       2       2       62      362     362     362     362     124     125     YNAAAA  MLAAAA  OOOOxx
+6997   299     1       1       7       17      97      997     997     1997    6997    194     195     DJAAAA  NLAAAA  VVVVxx
+2529   300     1       1       9       9       29      529     529     2529    2529    58      59      HTAAAA  OLAAAA  AAAAxx
+6319   301     1       3       9       19      19      319     319     1319    6319    38      39      BJAAAA  PLAAAA  HHHHxx
+954    302     0       2       4       14      54      954     954     954     954     108     109     SKAAAA  QLAAAA  OOOOxx
+3413   303     1       1       3       13      13      413     1413    3413    3413    26      27      HBAAAA  RLAAAA  VVVVxx
+9081   304     1       1       1       1       81      81      1081    4081    9081    162     163     HLAAAA  SLAAAA  AAAAxx
+5599   305     1       3       9       19      99      599     1599    599     5599    198     199     JHAAAA  TLAAAA  HHHHxx
+4772   306     0       0       2       12      72      772     772     4772    4772    144     145     OBAAAA  ULAAAA  OOOOxx
+1124   307     0       0       4       4       24      124     1124    1124    1124    48      49      GRAAAA  VLAAAA  VVVVxx
+7793   308     1       1       3       13      93      793     1793    2793    7793    186     187     TNAAAA  WLAAAA  AAAAxx
+4201   309     1       1       1       1       1       201     201     4201    4201    2       3       PFAAAA  XLAAAA  HHHHxx
+7015   310     1       3       5       15      15      15      1015    2015    7015    30      31      VJAAAA  YLAAAA  OOOOxx
+5936   311     0       0       6       16      36      936     1936    936     5936    72      73      IUAAAA  ZLAAAA  VVVVxx
+4625   312     1       1       5       5       25      625     625     4625    4625    50      51      XVAAAA  AMAAAA  AAAAxx
+4989   313     1       1       9       9       89      989     989     4989    4989    178     179     XJAAAA  BMAAAA  HHHHxx
+4949   314     1       1       9       9       49      949     949     4949    4949    98      99      JIAAAA  CMAAAA  OOOOxx
+6273   315     1       1       3       13      73      273     273     1273    6273    146     147     HHAAAA  DMAAAA  VVVVxx
+4478   316     0       2       8       18      78      478     478     4478    4478    156     157     GQAAAA  EMAAAA  AAAAxx
+8854   317     0       2       4       14      54      854     854     3854    8854    108     109     OCAAAA  FMAAAA  HHHHxx
+2105   318     1       1       5       5       5       105     105     2105    2105    10      11      ZCAAAA  GMAAAA  OOOOxx
+8345   319     1       1       5       5       45      345     345     3345    8345    90      91      ZIAAAA  HMAAAA  VVVVxx
+1941   320     1       1       1       1       41      941     1941    1941    1941    82      83      RWAAAA  IMAAAA  AAAAxx
+1765   321     1       1       5       5       65      765     1765    1765    1765    130     131     XPAAAA  JMAAAA  HHHHxx
+9592   322     0       0       2       12      92      592     1592    4592    9592    184     185     YEAAAA  KMAAAA  OOOOxx
+1694   323     0       2       4       14      94      694     1694    1694    1694    188     189     ENAAAA  LMAAAA  VVVVxx
+8940   324     0       0       0       0       40      940     940     3940    8940    80      81      WFAAAA  MMAAAA  AAAAxx
+7264   325     0       0       4       4       64      264     1264    2264    7264    128     129     KTAAAA  NMAAAA  HHHHxx
+4699   326     1       3       9       19      99      699     699     4699    4699    198     199     TYAAAA  OMAAAA  OOOOxx
+4541   327     1       1       1       1       41      541     541     4541    4541    82      83      RSAAAA  PMAAAA  VVVVxx
+5768   328     0       0       8       8       68      768     1768    768     5768    136     137     WNAAAA  QMAAAA  AAAAxx
+6183   329     1       3       3       3       83      183     183     1183    6183    166     167     VDAAAA  RMAAAA  HHHHxx
+7457   330     1       1       7       17      57      457     1457    2457    7457    114     115     VAAAAA  SMAAAA  OOOOxx
+7317   331     1       1       7       17      17      317     1317    2317    7317    34      35      LVAAAA  TMAAAA  VVVVxx
+1944   332     0       0       4       4       44      944     1944    1944    1944    88      89      UWAAAA  UMAAAA  AAAAxx
+665    333     1       1       5       5       65      665     665     665     665     130     131     PZAAAA  VMAAAA  HHHHxx
+5974   334     0       2       4       14      74      974     1974    974     5974    148     149     UVAAAA  WMAAAA  OOOOxx
+7370   335     0       2       0       10      70      370     1370    2370    7370    140     141     MXAAAA  XMAAAA  VVVVxx
+9196   336     0       0       6       16      96      196     1196    4196    9196    192     193     SPAAAA  YMAAAA  AAAAxx
+6796   337     0       0       6       16      96      796     796     1796    6796    192     193     KBAAAA  ZMAAAA  HHHHxx
+6180   338     0       0       0       0       80      180     180     1180    6180    160     161     SDAAAA  ANAAAA  OOOOxx
+8557   339     1       1       7       17      57      557     557     3557    8557    114     115     DRAAAA  BNAAAA  VVVVxx
+928    340     0       0       8       8       28      928     928     928     928     56      57      SJAAAA  CNAAAA  AAAAxx
+6275   341     1       3       5       15      75      275     275     1275    6275    150     151     JHAAAA  DNAAAA  HHHHxx
+409    342     1       1       9       9       9       409     409     409     409     18      19      TPAAAA  ENAAAA  OOOOxx
+6442   343     0       2       2       2       42      442     442     1442    6442    84      85      UNAAAA  FNAAAA  VVVVxx
+5889   344     1       1       9       9       89      889     1889    889     5889    178     179     NSAAAA  GNAAAA  AAAAxx
+5180   345     0       0       0       0       80      180     1180    180     5180    160     161     GRAAAA  HNAAAA  HHHHxx
+1629   346     1       1       9       9       29      629     1629    1629    1629    58      59      RKAAAA  INAAAA  OOOOxx
+6088   347     0       0       8       8       88      88      88      1088    6088    176     177     EAAAAA  JNAAAA  VVVVxx
+5598   348     0       2       8       18      98      598     1598    598     5598    196     197     IHAAAA  KNAAAA  AAAAxx
+1803   349     1       3       3       3       3       803     1803    1803    1803    6       7       JRAAAA  LNAAAA  HHHHxx
+2330   350     0       2       0       10      30      330     330     2330    2330    60      61      QLAAAA  MNAAAA  OOOOxx
+5901   351     1       1       1       1       1       901     1901    901     5901    2       3       ZSAAAA  NNAAAA  VVVVxx
+780    352     0       0       0       0       80      780     780     780     780     160     161     AEAAAA  ONAAAA  AAAAxx
+7171   353     1       3       1       11      71      171     1171    2171    7171    142     143     VPAAAA  PNAAAA  HHHHxx
+8778   354     0       2       8       18      78      778     778     3778    8778    156     157     QZAAAA  QNAAAA  OOOOxx
+6622   355     0       2       2       2       22      622     622     1622    6622    44      45      SUAAAA  RNAAAA  VVVVxx
+9938   356     0       2       8       18      38      938     1938    4938    9938    76      77      GSAAAA  SNAAAA  AAAAxx
+8254   357     0       2       4       14      54      254     254     3254    8254    108     109     MFAAAA  TNAAAA  HHHHxx
+1951   358     1       3       1       11      51      951     1951    1951    1951    102     103     BXAAAA  UNAAAA  OOOOxx
+1434   359     0       2       4       14      34      434     1434    1434    1434    68      69      EDAAAA  VNAAAA  VVVVxx
+7539   360     1       3       9       19      39      539     1539    2539    7539    78      79      ZDAAAA  WNAAAA  AAAAxx
+600    361     0       0       0       0       0       600     600     600     600     0       1       CXAAAA  XNAAAA  HHHHxx
+3122   362     0       2       2       2       22      122     1122    3122    3122    44      45      CQAAAA  YNAAAA  OOOOxx
+5704   363     0       0       4       4       4       704     1704    704     5704    8       9       KLAAAA  ZNAAAA  VVVVxx
+6300   364     0       0       0       0       0       300     300     1300    6300    0       1       IIAAAA  AOAAAA  AAAAxx
+4585   365     1       1       5       5       85      585     585     4585    4585    170     171     JUAAAA  BOAAAA  HHHHxx
+6313   366     1       1       3       13      13      313     313     1313    6313    26      27      VIAAAA  COAAAA  OOOOxx
+3154   367     0       2       4       14      54      154     1154    3154    3154    108     109     IRAAAA  DOAAAA  VVVVxx
+642    368     0       2       2       2       42      642     642     642     642     84      85      SYAAAA  EOAAAA  AAAAxx
+7736   369     0       0       6       16      36      736     1736    2736    7736    72      73      OLAAAA  FOAAAA  HHHHxx
+5087   370     1       3       7       7       87      87      1087    87      5087    174     175     RNAAAA  GOAAAA  OOOOxx
+5708   371     0       0       8       8       8       708     1708    708     5708    16      17      OLAAAA  HOAAAA  VVVVxx
+8169   372     1       1       9       9       69      169     169     3169    8169    138     139     FCAAAA  IOAAAA  AAAAxx
+9768   373     0       0       8       8       68      768     1768    4768    9768    136     137     SLAAAA  JOAAAA  HHHHxx
+3874   374     0       2       4       14      74      874     1874    3874    3874    148     149     ATAAAA  KOAAAA  OOOOxx
+6831   375     1       3       1       11      31      831     831     1831    6831    62      63      TCAAAA  LOAAAA  VVVVxx
+18     376     0       2       8       18      18      18      18      18      18      36      37      SAAAAA  MOAAAA  AAAAxx
+6375   377     1       3       5       15      75      375     375     1375    6375    150     151     FLAAAA  NOAAAA  HHHHxx
+7106   378     0       2       6       6       6       106     1106    2106    7106    12      13      INAAAA  OOAAAA  OOOOxx
+5926   379     0       2       6       6       26      926     1926    926     5926    52      53      YTAAAA  POAAAA  VVVVxx
+4956   380     0       0       6       16      56      956     956     4956    4956    112     113     QIAAAA  QOAAAA  AAAAxx
+7042   381     0       2       2       2       42      42      1042    2042    7042    84      85      WKAAAA  ROAAAA  HHHHxx
+6043   382     1       3       3       3       43      43      43      1043    6043    86      87      LYAAAA  SOAAAA  OOOOxx
+2084   383     0       0       4       4       84      84      84      2084    2084    168     169     ECAAAA  TOAAAA  VVVVxx
+6038   384     0       2       8       18      38      38      38      1038    6038    76      77      GYAAAA  UOAAAA  AAAAxx
+7253   385     1       1       3       13      53      253     1253    2253    7253    106     107     ZSAAAA  VOAAAA  HHHHxx
+2061   386     1       1       1       1       61      61      61      2061    2061    122     123     HBAAAA  WOAAAA  OOOOxx
+7800   387     0       0       0       0       0       800     1800    2800    7800    0       1       AOAAAA  XOAAAA  VVVVxx
+4970   388     0       2       0       10      70      970     970     4970    4970    140     141     EJAAAA  YOAAAA  AAAAxx
+8580   389     0       0       0       0       80      580     580     3580    8580    160     161     ASAAAA  ZOAAAA  HHHHxx
+9173   390     1       1       3       13      73      173     1173    4173    9173    146     147     VOAAAA  APAAAA  OOOOxx
+8558   391     0       2       8       18      58      558     558     3558    8558    116     117     ERAAAA  BPAAAA  VVVVxx
+3897   392     1       1       7       17      97      897     1897    3897    3897    194     195     XTAAAA  CPAAAA  AAAAxx
+5069   393     1       1       9       9       69      69      1069    69      5069    138     139     ZMAAAA  DPAAAA  HHHHxx
+2301   394     1       1       1       1       1       301     301     2301    2301    2       3       NKAAAA  EPAAAA  OOOOxx
+9863   395     1       3       3       3       63      863     1863    4863    9863    126     127     JPAAAA  FPAAAA  VVVVxx
+5733   396     1       1       3       13      33      733     1733    733     5733    66      67      NMAAAA  GPAAAA  AAAAxx
+2338   397     0       2       8       18      38      338     338     2338    2338    76      77      YLAAAA  HPAAAA  HHHHxx
+9639   398     1       3       9       19      39      639     1639    4639    9639    78      79      TGAAAA  IPAAAA  OOOOxx
+1139   399     1       3       9       19      39      139     1139    1139    1139    78      79      VRAAAA  JPAAAA  VVVVxx
+2293   400     1       1       3       13      93      293     293     2293    2293    186     187     FKAAAA  KPAAAA  AAAAxx
+6125   401     1       1       5       5       25      125     125     1125    6125    50      51      PBAAAA  LPAAAA  HHHHxx
+5374   402     0       2       4       14      74      374     1374    374     5374    148     149     SYAAAA  MPAAAA  OOOOxx
+7216   403     0       0       6       16      16      216     1216    2216    7216    32      33      ORAAAA  NPAAAA  VVVVxx
+2285   404     1       1       5       5       85      285     285     2285    2285    170     171     XJAAAA  OPAAAA  AAAAxx
+2387   405     1       3       7       7       87      387     387     2387    2387    174     175     VNAAAA  PPAAAA  HHHHxx
+5015   406     1       3       5       15      15      15      1015    15      5015    30      31      XKAAAA  QPAAAA  OOOOxx
+2087   407     1       3       7       7       87      87      87      2087    2087    174     175     HCAAAA  RPAAAA  VVVVxx
+4938   408     0       2       8       18      38      938     938     4938    4938    76      77      YHAAAA  SPAAAA  AAAAxx
+3635   409     1       3       5       15      35      635     1635    3635    3635    70      71      VJAAAA  TPAAAA  HHHHxx
+7737   410     1       1       7       17      37      737     1737    2737    7737    74      75      PLAAAA  UPAAAA  OOOOxx
+8056   411     0       0       6       16      56      56      56      3056    8056    112     113     WXAAAA  VPAAAA  VVVVxx
+4502   412     0       2       2       2       2       502     502     4502    4502    4       5       ERAAAA  WPAAAA  AAAAxx
+54     413     0       2       4       14      54      54      54      54      54      108     109     CCAAAA  XPAAAA  HHHHxx
+3182   414     0       2       2       2       82      182     1182    3182    3182    164     165     KSAAAA  YPAAAA  OOOOxx
+3718   415     0       2       8       18      18      718     1718    3718    3718    36      37      ANAAAA  ZPAAAA  VVVVxx
+3989   416     1       1       9       9       89      989     1989    3989    3989    178     179     LXAAAA  AQAAAA  AAAAxx
+8028   417     0       0       8       8       28      28      28      3028    8028    56      57      UWAAAA  BQAAAA  HHHHxx
+1426   418     0       2       6       6       26      426     1426    1426    1426    52      53      WCAAAA  CQAAAA  OOOOxx
+3801   419     1       1       1       1       1       801     1801    3801    3801    2       3       FQAAAA  DQAAAA  VVVVxx
+241    420     1       1       1       1       41      241     241     241     241     82      83      HJAAAA  EQAAAA  AAAAxx
+8000   421     0       0       0       0       0       0       0       3000    8000    0       1       SVAAAA  FQAAAA  HHHHxx
+8357   422     1       1       7       17      57      357     357     3357    8357    114     115     LJAAAA  GQAAAA  OOOOxx
+7548   423     0       0       8       8       48      548     1548    2548    7548    96      97      IEAAAA  HQAAAA  VVVVxx
+7307   424     1       3       7       7       7       307     1307    2307    7307    14      15      BVAAAA  IQAAAA  AAAAxx
+2275   425     1       3       5       15      75      275     275     2275    2275    150     151     NJAAAA  JQAAAA  HHHHxx
+2718   426     0       2       8       18      18      718     718     2718    2718    36      37      OAAAAA  KQAAAA  OOOOxx
+7068   427     0       0       8       8       68      68      1068    2068    7068    136     137     WLAAAA  LQAAAA  VVVVxx
+3181   428     1       1       1       1       81      181     1181    3181    3181    162     163     JSAAAA  MQAAAA  AAAAxx
+749    429     1       1       9       9       49      749     749     749     749     98      99      VCAAAA  NQAAAA  HHHHxx
+5195   430     1       3       5       15      95      195     1195    195     5195    190     191     VRAAAA  OQAAAA  OOOOxx
+6136   431     0       0       6       16      36      136     136     1136    6136    72      73      ACAAAA  PQAAAA  VVVVxx
+8012   432     0       0       2       12      12      12      12      3012    8012    24      25      EWAAAA  QQAAAA  AAAAxx
+3957   433     1       1       7       17      57      957     1957    3957    3957    114     115     FWAAAA  RQAAAA  HHHHxx
+3083   434     1       3       3       3       83      83      1083    3083    3083    166     167     POAAAA  SQAAAA  OOOOxx
+9997   435     1       1       7       17      97      997     1997    4997    9997    194     195     NUAAAA  TQAAAA  VVVVxx
+3299   436     1       3       9       19      99      299     1299    3299    3299    198     199     XWAAAA  UQAAAA  AAAAxx
+846    437     0       2       6       6       46      846     846     846     846     92      93      OGAAAA  VQAAAA  HHHHxx
+2985   438     1       1       5       5       85      985     985     2985    2985    170     171     VKAAAA  WQAAAA  OOOOxx
+9238   439     0       2       8       18      38      238     1238    4238    9238    76      77      IRAAAA  XQAAAA  VVVVxx
+1403   440     1       3       3       3       3       403     1403    1403    1403    6       7       ZBAAAA  YQAAAA  AAAAxx
+5563   441     1       3       3       3       63      563     1563    563     5563    126     127     ZFAAAA  ZQAAAA  HHHHxx
+7965   442     1       1       5       5       65      965     1965    2965    7965    130     131     JUAAAA  ARAAAA  OOOOxx
+4512   443     0       0       2       12      12      512     512     4512    4512    24      25      ORAAAA  BRAAAA  VVVVxx
+9730   444     0       2       0       10      30      730     1730    4730    9730    60      61      GKAAAA  CRAAAA  AAAAxx
+1129   445     1       1       9       9       29      129     1129    1129    1129    58      59      LRAAAA  DRAAAA  HHHHxx
+2624   446     0       0       4       4       24      624     624     2624    2624    48      49      YWAAAA  ERAAAA  OOOOxx
+8178   447     0       2       8       18      78      178     178     3178    8178    156     157     OCAAAA  FRAAAA  VVVVxx
+6468   448     0       0       8       8       68      468     468     1468    6468    136     137     UOAAAA  GRAAAA  AAAAxx
+3027   449     1       3       7       7       27      27      1027    3027    3027    54      55      LMAAAA  HRAAAA  HHHHxx
+3845   450     1       1       5       5       45      845     1845    3845    3845    90      91      XRAAAA  IRAAAA  OOOOxx
+786    451     0       2       6       6       86      786     786     786     786     172     173     GEAAAA  JRAAAA  VVVVxx
+4971   452     1       3       1       11      71      971     971     4971    4971    142     143     FJAAAA  KRAAAA  AAAAxx
+1542   453     0       2       2       2       42      542     1542    1542    1542    84      85      IHAAAA  LRAAAA  HHHHxx
+7967   454     1       3       7       7       67      967     1967    2967    7967    134     135     LUAAAA  MRAAAA  OOOOxx
+443    455     1       3       3       3       43      443     443     443     443     86      87      BRAAAA  NRAAAA  VVVVxx
+7318   456     0       2       8       18      18      318     1318    2318    7318    36      37      MVAAAA  ORAAAA  AAAAxx
+4913   457     1       1       3       13      13      913     913     4913    4913    26      27      ZGAAAA  PRAAAA  HHHHxx
+9466   458     0       2       6       6       66      466     1466    4466    9466    132     133     CAAAAA  QRAAAA  OOOOxx
+7866   459     0       2       6       6       66      866     1866    2866    7866    132     133     OQAAAA  RRAAAA  VVVVxx
+784    460     0       0       4       4       84      784     784     784     784     168     169     EEAAAA  SRAAAA  AAAAxx
+9040   461     0       0       0       0       40      40      1040    4040    9040    80      81      SJAAAA  TRAAAA  HHHHxx
+3954   462     0       2       4       14      54      954     1954    3954    3954    108     109     CWAAAA  URAAAA  OOOOxx
+4183   463     1       3       3       3       83      183     183     4183    4183    166     167     XEAAAA  VRAAAA  VVVVxx
+3608   464     0       0       8       8       8       608     1608    3608    3608    16      17      UIAAAA  WRAAAA  AAAAxx
+7630   465     0       2       0       10      30      630     1630    2630    7630    60      61      MHAAAA  XRAAAA  HHHHxx
+590    466     0       2       0       10      90      590     590     590     590     180     181     SWAAAA  YRAAAA  OOOOxx
+3453   467     1       1       3       13      53      453     1453    3453    3453    106     107     VCAAAA  ZRAAAA  VVVVxx
+7757   468     1       1       7       17      57      757     1757    2757    7757    114     115     JMAAAA  ASAAAA  AAAAxx
+7394   469     0       2       4       14      94      394     1394    2394    7394    188     189     KYAAAA  BSAAAA  HHHHxx
+396    470     0       0       6       16      96      396     396     396     396     192     193     GPAAAA  CSAAAA  OOOOxx
+7873   471     1       1       3       13      73      873     1873    2873    7873    146     147     VQAAAA  DSAAAA  VVVVxx
+1553   472     1       1       3       13      53      553     1553    1553    1553    106     107     THAAAA  ESAAAA  AAAAxx
+598    473     0       2       8       18      98      598     598     598     598     196     197     AXAAAA  FSAAAA  HHHHxx
+7191   474     1       3       1       11      91      191     1191    2191    7191    182     183     PQAAAA  GSAAAA  OOOOxx
+8116   475     0       0       6       16      16      116     116     3116    8116    32      33      EAAAAA  HSAAAA  VVVVxx
+2516   476     0       0       6       16      16      516     516     2516    2516    32      33      USAAAA  ISAAAA  AAAAxx
+7750   477     0       2       0       10      50      750     1750    2750    7750    100     101     CMAAAA  JSAAAA  HHHHxx
+6625   478     1       1       5       5       25      625     625     1625    6625    50      51      VUAAAA  KSAAAA  OOOOxx
+8838   479     0       2       8       18      38      838     838     3838    8838    76      77      YBAAAA  LSAAAA  VVVVxx
+4636   480     0       0       6       16      36      636     636     4636    4636    72      73      IWAAAA  MSAAAA  AAAAxx
+7627   481     1       3       7       7       27      627     1627    2627    7627    54      55      JHAAAA  NSAAAA  HHHHxx
+1690   482     0       2       0       10      90      690     1690    1690    1690    180     181     ANAAAA  OSAAAA  OOOOxx
+7071   483     1       3       1       11      71      71      1071    2071    7071    142     143     ZLAAAA  PSAAAA  VVVVxx
+2081   484     1       1       1       1       81      81      81      2081    2081    162     163     BCAAAA  QSAAAA  AAAAxx
+7138   485     0       2       8       18      38      138     1138    2138    7138    76      77      OOAAAA  RSAAAA  HHHHxx
+864    486     0       0       4       4       64      864     864     864     864     128     129     GHAAAA  SSAAAA  OOOOxx
+6392   487     0       0       2       12      92      392     392     1392    6392    184     185     WLAAAA  TSAAAA  VVVVxx
+7544   488     0       0       4       4       44      544     1544    2544    7544    88      89      EEAAAA  USAAAA  AAAAxx
+5438   489     0       2       8       18      38      438     1438    438     5438    76      77      EBAAAA  VSAAAA  HHHHxx
+7099   490     1       3       9       19      99      99      1099    2099    7099    198     199     BNAAAA  WSAAAA  OOOOxx
+5157   491     1       1       7       17      57      157     1157    157     5157    114     115     JQAAAA  XSAAAA  VVVVxx
+3391   492     1       3       1       11      91      391     1391    3391    3391    182     183     LAAAAA  YSAAAA  AAAAxx
+3805   493     1       1       5       5       5       805     1805    3805    3805    10      11      JQAAAA  ZSAAAA  HHHHxx
+2110   494     0       2       0       10      10      110     110     2110    2110    20      21      EDAAAA  ATAAAA  OOOOxx
+3176   495     0       0       6       16      76      176     1176    3176    3176    152     153     ESAAAA  BTAAAA  VVVVxx
+5918   496     0       2       8       18      18      918     1918    918     5918    36      37      QTAAAA  CTAAAA  AAAAxx
+1218   497     0       2       8       18      18      218     1218    1218    1218    36      37      WUAAAA  DTAAAA  HHHHxx
+6683   498     1       3       3       3       83      683     683     1683    6683    166     167     BXAAAA  ETAAAA  OOOOxx
+914    499     0       2       4       14      14      914     914     914     914     28      29      EJAAAA  FTAAAA  VVVVxx
+4737   500     1       1       7       17      37      737     737     4737    4737    74      75      FAAAAA  GTAAAA  AAAAxx
+7286   501     0       2       6       6       86      286     1286    2286    7286    172     173     GUAAAA  HTAAAA  HHHHxx
+9975   502     1       3       5       15      75      975     1975    4975    9975    150     151     RTAAAA  ITAAAA  OOOOxx
+8030   503     0       2       0       10      30      30      30      3030    8030    60      61      WWAAAA  JTAAAA  VVVVxx
+7364   504     0       0       4       4       64      364     1364    2364    7364    128     129     GXAAAA  KTAAAA  AAAAxx
+1389   505     1       1       9       9       89      389     1389    1389    1389    178     179     LBAAAA  LTAAAA  HHHHxx
+4025   506     1       1       5       5       25      25      25      4025    4025    50      51      VYAAAA  MTAAAA  OOOOxx
+4835   507     1       3       5       15      35      835     835     4835    4835    70      71      ZDAAAA  NTAAAA  VVVVxx
+8045   508     1       1       5       5       45      45      45      3045    8045    90      91      LXAAAA  OTAAAA  AAAAxx
+1864   509     0       0       4       4       64      864     1864    1864    1864    128     129     STAAAA  PTAAAA  HHHHxx
+3313   510     1       1       3       13      13      313     1313    3313    3313    26      27      LXAAAA  QTAAAA  OOOOxx
+2384   511     0       0       4       4       84      384     384     2384    2384    168     169     SNAAAA  RTAAAA  VVVVxx
+6115   512     1       3       5       15      15      115     115     1115    6115    30      31      FBAAAA  STAAAA  AAAAxx
+5705   513     1       1       5       5       5       705     1705    705     5705    10      11      LLAAAA  TTAAAA  HHHHxx
+9269   514     1       1       9       9       69      269     1269    4269    9269    138     139     NSAAAA  UTAAAA  OOOOxx
+3379   515     1       3       9       19      79      379     1379    3379    3379    158     159     ZZAAAA  VTAAAA  VVVVxx
+8205   516     1       1       5       5       5       205     205     3205    8205    10      11      PDAAAA  WTAAAA  AAAAxx
+6575   517     1       3       5       15      75      575     575     1575    6575    150     151     XSAAAA  XTAAAA  HHHHxx
+486    518     0       2       6       6       86      486     486     486     486     172     173     SSAAAA  YTAAAA  OOOOxx
+4894   519     0       2       4       14      94      894     894     4894    4894    188     189     GGAAAA  ZTAAAA  VVVVxx
+3090   520     0       2       0       10      90      90      1090    3090    3090    180     181     WOAAAA  AUAAAA  AAAAxx
+759    521     1       3       9       19      59      759     759     759     759     118     119     FDAAAA  BUAAAA  HHHHxx
+4864   522     0       0       4       4       64      864     864     4864    4864    128     129     CFAAAA  CUAAAA  OOOOxx
+4083   523     1       3       3       3       83      83      83      4083    4083    166     167     BBAAAA  DUAAAA  VVVVxx
+6918   524     0       2       8       18      18      918     918     1918    6918    36      37      CGAAAA  EUAAAA  AAAAxx
+8146   525     0       2       6       6       46      146     146     3146    8146    92      93      IBAAAA  FUAAAA  HHHHxx
+1523   526     1       3       3       3       23      523     1523    1523    1523    46      47      PGAAAA  GUAAAA  OOOOxx
+1591   527     1       3       1       11      91      591     1591    1591    1591    182     183     FJAAAA  HUAAAA  VVVVxx
+3343   528     1       3       3       3       43      343     1343    3343    3343    86      87      PYAAAA  IUAAAA  AAAAxx
+1391   529     1       3       1       11      91      391     1391    1391    1391    182     183     NBAAAA  JUAAAA  HHHHxx
+9963   530     1       3       3       3       63      963     1963    4963    9963    126     127     FTAAAA  KUAAAA  OOOOxx
+2423   531     1       3       3       3       23      423     423     2423    2423    46      47      FPAAAA  LUAAAA  VVVVxx
+1822   532     0       2       2       2       22      822     1822    1822    1822    44      45      CSAAAA  MUAAAA  AAAAxx
+8706   533     0       2       6       6       6       706     706     3706    8706    12      13      WWAAAA  NUAAAA  HHHHxx
+3001   534     1       1       1       1       1       1       1001    3001    3001    2       3       LLAAAA  OUAAAA  OOOOxx
+6707   535     1       3       7       7       7       707     707     1707    6707    14      15      ZXAAAA  PUAAAA  VVVVxx
+2121   536     1       1       1       1       21      121     121     2121    2121    42      43      PDAAAA  QUAAAA  AAAAxx
+5814   537     0       2       4       14      14      814     1814    814     5814    28      29      QPAAAA  RUAAAA  HHHHxx
+2659   538     1       3       9       19      59      659     659     2659    2659    118     119     HYAAAA  SUAAAA  OOOOxx
+2016   539     0       0       6       16      16      16      16      2016    2016    32      33      OZAAAA  TUAAAA  VVVVxx
+4286   540     0       2       6       6       86      286     286     4286    4286    172     173     WIAAAA  UUAAAA  AAAAxx
+9205   541     1       1       5       5       5       205     1205    4205    9205    10      11      BQAAAA  VUAAAA  HHHHxx
+3496   542     0       0       6       16      96      496     1496    3496    3496    192     193     MEAAAA  WUAAAA  OOOOxx
+5333   543     1       1       3       13      33      333     1333    333     5333    66      67      DXAAAA  XUAAAA  VVVVxx
+5571   544     1       3       1       11      71      571     1571    571     5571    142     143     HGAAAA  YUAAAA  AAAAxx
+1696   545     0       0       6       16      96      696     1696    1696    1696    192     193     GNAAAA  ZUAAAA  HHHHxx
+4871   546     1       3       1       11      71      871     871     4871    4871    142     143     JFAAAA  AVAAAA  OOOOxx
+4852   547     0       0       2       12      52      852     852     4852    4852    104     105     QEAAAA  BVAAAA  VVVVxx
+8483   548     1       3       3       3       83      483     483     3483    8483    166     167     HOAAAA  CVAAAA  AAAAxx
+1376   549     0       0       6       16      76      376     1376    1376    1376    152     153     YAAAAA  DVAAAA  HHHHxx
+5456   550     0       0       6       16      56      456     1456    456     5456    112     113     WBAAAA  EVAAAA  OOOOxx
+499    551     1       3       9       19      99      499     499     499     499     198     199     FTAAAA  FVAAAA  VVVVxx
+3463   552     1       3       3       3       63      463     1463    3463    3463    126     127     FDAAAA  GVAAAA  AAAAxx
+7426   553     0       2       6       6       26      426     1426    2426    7426    52      53      QZAAAA  HVAAAA  HHHHxx
+5341   554     1       1       1       1       41      341     1341    341     5341    82      83      LXAAAA  IVAAAA  OOOOxx
+9309   555     1       1       9       9       9       309     1309    4309    9309    18      19      BUAAAA  JVAAAA  VVVVxx
+2055   556     1       3       5       15      55      55      55      2055    2055    110     111     BBAAAA  KVAAAA  AAAAxx
+2199   557     1       3       9       19      99      199     199     2199    2199    198     199     PGAAAA  LVAAAA  HHHHxx
+7235   558     1       3       5       15      35      235     1235    2235    7235    70      71      HSAAAA  MVAAAA  OOOOxx
+8661   559     1       1       1       1       61      661     661     3661    8661    122     123     DVAAAA  NVAAAA  VVVVxx
+9494   560     0       2       4       14      94      494     1494    4494    9494    188     189     EBAAAA  OVAAAA  AAAAxx
+935    561     1       3       5       15      35      935     935     935     935     70      71      ZJAAAA  PVAAAA  HHHHxx
+7044   562     0       0       4       4       44      44      1044    2044    7044    88      89      YKAAAA  QVAAAA  OOOOxx
+1974   563     0       2       4       14      74      974     1974    1974    1974    148     149     YXAAAA  RVAAAA  VVVVxx
+9679   564     1       3       9       19      79      679     1679    4679    9679    158     159     HIAAAA  SVAAAA  AAAAxx
+9822   565     0       2       2       2       22      822     1822    4822    9822    44      45      UNAAAA  TVAAAA  HHHHxx
+4088   566     0       0       8       8       88      88      88      4088    4088    176     177     GBAAAA  UVAAAA  OOOOxx
+1749   567     1       1       9       9       49      749     1749    1749    1749    98      99      HPAAAA  VVAAAA  VVVVxx
+2116   568     0       0       6       16      16      116     116     2116    2116    32      33      KDAAAA  WVAAAA  AAAAxx
+976    569     0       0       6       16      76      976     976     976     976     152     153     OLAAAA  XVAAAA  HHHHxx
+8689   570     1       1       9       9       89      689     689     3689    8689    178     179     FWAAAA  YVAAAA  OOOOxx
+2563   571     1       3       3       3       63      563     563     2563    2563    126     127     PUAAAA  ZVAAAA  VVVVxx
+7195   572     1       3       5       15      95      195     1195    2195    7195    190     191     TQAAAA  AWAAAA  AAAAxx
+9985   573     1       1       5       5       85      985     1985    4985    9985    170     171     BUAAAA  BWAAAA  HHHHxx
+7699   574     1       3       9       19      99      699     1699    2699    7699    198     199     DKAAAA  CWAAAA  OOOOxx
+5311   575     1       3       1       11      11      311     1311    311     5311    22      23      HWAAAA  DWAAAA  VVVVxx
+295    576     1       3       5       15      95      295     295     295     295     190     191     JLAAAA  EWAAAA  AAAAxx
+8214   577     0       2       4       14      14      214     214     3214    8214    28      29      YDAAAA  FWAAAA  HHHHxx
+3275   578     1       3       5       15      75      275     1275    3275    3275    150     151     ZVAAAA  GWAAAA  OOOOxx
+9646   579     0       2       6       6       46      646     1646    4646    9646    92      93      AHAAAA  HWAAAA  VVVVxx
+1908   580     0       0       8       8       8       908     1908    1908    1908    16      17      KVAAAA  IWAAAA  AAAAxx
+3858   581     0       2       8       18      58      858     1858    3858    3858    116     117     KSAAAA  JWAAAA  HHHHxx
+9362   582     0       2       2       2       62      362     1362    4362    9362    124     125     CWAAAA  KWAAAA  OOOOxx
+9307   583     1       3       7       7       7       307     1307    4307    9307    14      15      ZTAAAA  LWAAAA  VVVVxx
+6124   584     0       0       4       4       24      124     124     1124    6124    48      49      OBAAAA  MWAAAA  AAAAxx
+2405   585     1       1       5       5       5       405     405     2405    2405    10      11      NOAAAA  NWAAAA  HHHHxx
+8422   586     0       2       2       2       22      422     422     3422    8422    44      45      YLAAAA  OWAAAA  OOOOxx
+393    587     1       1       3       13      93      393     393     393     393     186     187     DPAAAA  PWAAAA  VVVVxx
+8973   588     1       1       3       13      73      973     973     3973    8973    146     147     DHAAAA  QWAAAA  AAAAxx
+5171   589     1       3       1       11      71      171     1171    171     5171    142     143     XQAAAA  RWAAAA  HHHHxx
+4929   590     1       1       9       9       29      929     929     4929    4929    58      59      PHAAAA  SWAAAA  OOOOxx
+6935   591     1       3       5       15      35      935     935     1935    6935    70      71      TGAAAA  TWAAAA  VVVVxx
+8584   592     0       0       4       4       84      584     584     3584    8584    168     169     ESAAAA  UWAAAA  AAAAxx
+1035   593     1       3       5       15      35      35      1035    1035    1035    70      71      VNAAAA  VWAAAA  HHHHxx
+3734   594     0       2       4       14      34      734     1734    3734    3734    68      69      QNAAAA  WWAAAA  OOOOxx
+1458   595     0       2       8       18      58      458     1458    1458    1458    116     117     CEAAAA  XWAAAA  VVVVxx
+8746   596     0       2       6       6       46      746     746     3746    8746    92      93      KYAAAA  YWAAAA  AAAAxx
+1677   597     1       1       7       17      77      677     1677    1677    1677    154     155     NMAAAA  ZWAAAA  HHHHxx
+8502   598     0       2       2       2       2       502     502     3502    8502    4       5       APAAAA  AXAAAA  OOOOxx
+7752   599     0       0       2       12      52      752     1752    2752    7752    104     105     EMAAAA  BXAAAA  VVVVxx
+2556   600     0       0       6       16      56      556     556     2556    2556    112     113     IUAAAA  CXAAAA  AAAAxx
+6426   601     0       2       6       6       26      426     426     1426    6426    52      53      ENAAAA  DXAAAA  HHHHxx
+8420   602     0       0       0       0       20      420     420     3420    8420    40      41      WLAAAA  EXAAAA  OOOOxx
+4462   603     0       2       2       2       62      462     462     4462    4462    124     125     QPAAAA  FXAAAA  VVVVxx
+1378   604     0       2       8       18      78      378     1378    1378    1378    156     157     ABAAAA  GXAAAA  AAAAxx
+1387   605     1       3       7       7       87      387     1387    1387    1387    174     175     JBAAAA  HXAAAA  HHHHxx
+8094   606     0       2       4       14      94      94      94      3094    8094    188     189     IZAAAA  IXAAAA  OOOOxx
+7247   607     1       3       7       7       47      247     1247    2247    7247    94      95      TSAAAA  JXAAAA  VVVVxx
+4261   608     1       1       1       1       61      261     261     4261    4261    122     123     XHAAAA  KXAAAA  AAAAxx
+5029   609     1       1       9       9       29      29      1029    29      5029    58      59      LLAAAA  LXAAAA  HHHHxx
+3625   610     1       1       5       5       25      625     1625    3625    3625    50      51      LJAAAA  MXAAAA  OOOOxx
+8068   611     0       0       8       8       68      68      68      3068    8068    136     137     IYAAAA  NXAAAA  VVVVxx
+102    612     0       2       2       2       2       102     102     102     102     4       5       YDAAAA  OXAAAA  AAAAxx
+5596   613     0       0       6       16      96      596     1596    596     5596    192     193     GHAAAA  PXAAAA  HHHHxx
+5872   614     0       0       2       12      72      872     1872    872     5872    144     145     WRAAAA  QXAAAA  OOOOxx
+4742   615     0       2       2       2       42      742     742     4742    4742    84      85      KAAAAA  RXAAAA  VVVVxx
+2117   616     1       1       7       17      17      117     117     2117    2117    34      35      LDAAAA  SXAAAA  AAAAxx
+3945   617     1       1       5       5       45      945     1945    3945    3945    90      91      TVAAAA  TXAAAA  HHHHxx
+7483   618     1       3       3       3       83      483     1483    2483    7483    166     167     VBAAAA  UXAAAA  OOOOxx
+4455   619     1       3       5       15      55      455     455     4455    4455    110     111     JPAAAA  VXAAAA  VVVVxx
+609    620     1       1       9       9       9       609     609     609     609     18      19      LXAAAA  WXAAAA  AAAAxx
+9829   621     1       1       9       9       29      829     1829    4829    9829    58      59      BOAAAA  XXAAAA  HHHHxx
+4857   622     1       1       7       17      57      857     857     4857    4857    114     115     VEAAAA  YXAAAA  OOOOxx
+3314   623     0       2       4       14      14      314     1314    3314    3314    28      29      MXAAAA  ZXAAAA  VVVVxx
+5353   624     1       1       3       13      53      353     1353    353     5353    106     107     XXAAAA  AYAAAA  AAAAxx
+4909   625     1       1       9       9       9       909     909     4909    4909    18      19      VGAAAA  BYAAAA  HHHHxx
+7597   626     1       1       7       17      97      597     1597    2597    7597    194     195     FGAAAA  CYAAAA  OOOOxx
+2683   627     1       3       3       3       83      683     683     2683    2683    166     167     FZAAAA  DYAAAA  VVVVxx
+3223   628     1       3       3       3       23      223     1223    3223    3223    46      47      ZTAAAA  EYAAAA  AAAAxx
+5363   629     1       3       3       3       63      363     1363    363     5363    126     127     HYAAAA  FYAAAA  HHHHxx
+4578   630     0       2       8       18      78      578     578     4578    4578    156     157     CUAAAA  GYAAAA  OOOOxx
+5544   631     0       0       4       4       44      544     1544    544     5544    88      89      GFAAAA  HYAAAA  VVVVxx
+1589   632     1       1       9       9       89      589     1589    1589    1589    178     179     DJAAAA  IYAAAA  AAAAxx
+7412   633     0       0       2       12      12      412     1412    2412    7412    24      25      CZAAAA  JYAAAA  HHHHxx
+3803   634     1       3       3       3       3       803     1803    3803    3803    6       7       HQAAAA  KYAAAA  OOOOxx
+6179   635     1       3       9       19      79      179     179     1179    6179    158     159     RDAAAA  LYAAAA  VVVVxx
+5588   636     0       0       8       8       88      588     1588    588     5588    176     177     YGAAAA  MYAAAA  AAAAxx
+2134   637     0       2       4       14      34      134     134     2134    2134    68      69      CEAAAA  NYAAAA  HHHHxx
+4383   638     1       3       3       3       83      383     383     4383    4383    166     167     PMAAAA  OYAAAA  OOOOxx
+6995   639     1       3       5       15      95      995     995     1995    6995    190     191     BJAAAA  PYAAAA  VVVVxx
+6598   640     0       2       8       18      98      598     598     1598    6598    196     197     UTAAAA  QYAAAA  AAAAxx
+8731   641     1       3       1       11      31      731     731     3731    8731    62      63      VXAAAA  RYAAAA  HHHHxx
+7177   642     1       1       7       17      77      177     1177    2177    7177    154     155     BQAAAA  SYAAAA  OOOOxx
+6578   643     0       2       8       18      78      578     578     1578    6578    156     157     ATAAAA  TYAAAA  VVVVxx
+9393   644     1       1       3       13      93      393     1393    4393    9393    186     187     HXAAAA  UYAAAA  AAAAxx
+1276   645     0       0       6       16      76      276     1276    1276    1276    152     153     CXAAAA  VYAAAA  HHHHxx
+8766   646     0       2       6       6       66      766     766     3766    8766    132     133     EZAAAA  WYAAAA  OOOOxx
+1015   647     1       3       5       15      15      15      1015    1015    1015    30      31      BNAAAA  XYAAAA  VVVVxx
+4396   648     0       0       6       16      96      396     396     4396    4396    192     193     CNAAAA  YYAAAA  AAAAxx
+5564   649     0       0       4       4       64      564     1564    564     5564    128     129     AGAAAA  ZYAAAA  HHHHxx
+927    650     1       3       7       7       27      927     927     927     927     54      55      RJAAAA  AZAAAA  OOOOxx
+3306   651     0       2       6       6       6       306     1306    3306    3306    12      13      EXAAAA  BZAAAA  VVVVxx
+1615   652     1       3       5       15      15      615     1615    1615    1615    30      31      DKAAAA  CZAAAA  AAAAxx
+4550   653     0       2       0       10      50      550     550     4550    4550    100     101     ATAAAA  DZAAAA  HHHHxx
+2468   654     0       0       8       8       68      468     468     2468    2468    136     137     YQAAAA  EZAAAA  OOOOxx
+5336   655     0       0       6       16      36      336     1336    336     5336    72      73      GXAAAA  FZAAAA  VVVVxx
+4471   656     1       3       1       11      71      471     471     4471    4471    142     143     ZPAAAA  GZAAAA  AAAAxx
+8085   657     1       1       5       5       85      85      85      3085    8085    170     171     ZYAAAA  HZAAAA  HHHHxx
+540    658     0       0       0       0       40      540     540     540     540     80      81      UUAAAA  IZAAAA  OOOOxx
+5108   659     0       0       8       8       8       108     1108    108     5108    16      17      MOAAAA  JZAAAA  VVVVxx
+8015   660     1       3       5       15      15      15      15      3015    8015    30      31      HWAAAA  KZAAAA  AAAAxx
+2857   661     1       1       7       17      57      857     857     2857    2857    114     115     XFAAAA  LZAAAA  HHHHxx
+9472   662     0       0       2       12      72      472     1472    4472    9472    144     145     IAAAAA  MZAAAA  OOOOxx
+5666   663     0       2       6       6       66      666     1666    666     5666    132     133     YJAAAA  NZAAAA  VVVVxx
+3555   664     1       3       5       15      55      555     1555    3555    3555    110     111     TGAAAA  OZAAAA  AAAAxx
+378    665     0       2       8       18      78      378     378     378     378     156     157     OOAAAA  PZAAAA  HHHHxx
+4466   666     0       2       6       6       66      466     466     4466    4466    132     133     UPAAAA  QZAAAA  OOOOxx
+3247   667     1       3       7       7       47      247     1247    3247    3247    94      95      XUAAAA  RZAAAA  VVVVxx
+6570   668     0       2       0       10      70      570     570     1570    6570    140     141     SSAAAA  SZAAAA  AAAAxx
+5655   669     1       3       5       15      55      655     1655    655     5655    110     111     NJAAAA  TZAAAA  HHHHxx
+917    670     1       1       7       17      17      917     917     917     917     34      35      HJAAAA  UZAAAA  OOOOxx
+3637   671     1       1       7       17      37      637     1637    3637    3637    74      75      XJAAAA  VZAAAA  VVVVxx
+3668   672     0       0       8       8       68      668     1668    3668    3668    136     137     CLAAAA  WZAAAA  AAAAxx
+5644   673     0       0       4       4       44      644     1644    644     5644    88      89      CJAAAA  XZAAAA  HHHHxx
+8286   674     0       2       6       6       86      286     286     3286    8286    172     173     SGAAAA  YZAAAA  OOOOxx
+6896   675     0       0       6       16      96      896     896     1896    6896    192     193     GFAAAA  ZZAAAA  VVVVxx
+2870   676     0       2       0       10      70      870     870     2870    2870    140     141     KGAAAA  AABAAA  AAAAxx
+8041   677     1       1       1       1       41      41      41      3041    8041    82      83      HXAAAA  BABAAA  HHHHxx
+8137   678     1       1       7       17      37      137     137     3137    8137    74      75      ZAAAAA  CABAAA  OOOOxx
+4823   679     1       3       3       3       23      823     823     4823    4823    46      47      NDAAAA  DABAAA  VVVVxx
+2438   680     0       2       8       18      38      438     438     2438    2438    76      77      UPAAAA  EABAAA  AAAAxx
+6329   681     1       1       9       9       29      329     329     1329    6329    58      59      LJAAAA  FABAAA  HHHHxx
+623    682     1       3       3       3       23      623     623     623     623     46      47      ZXAAAA  GABAAA  OOOOxx
+1360   683     0       0       0       0       60      360     1360    1360    1360    120     121     IAAAAA  HABAAA  VVVVxx
+7987   684     1       3       7       7       87      987     1987    2987    7987    174     175     FVAAAA  IABAAA  AAAAxx
+9788   685     0       0       8       8       88      788     1788    4788    9788    176     177     MMAAAA  JABAAA  HHHHxx
+3212   686     0       0       2       12      12      212     1212    3212    3212    24      25      OTAAAA  KABAAA  OOOOxx
+2725   687     1       1       5       5       25      725     725     2725    2725    50      51      VAAAAA  LABAAA  VVVVxx
+7837   688     1       1       7       17      37      837     1837    2837    7837    74      75      LPAAAA  MABAAA  AAAAxx
+4746   689     0       2       6       6       46      746     746     4746    4746    92      93      OAAAAA  NABAAA  HHHHxx
+3986   690     0       2       6       6       86      986     1986    3986    3986    172     173     IXAAAA  OABAAA  OOOOxx
+9128   691     0       0       8       8       28      128     1128    4128    9128    56      57      CNAAAA  PABAAA  VVVVxx
+5044   692     0       0       4       4       44      44      1044    44      5044    88      89      AMAAAA  QABAAA  AAAAxx
+8132   693     0       0       2       12      32      132     132     3132    8132    64      65      UAAAAA  RABAAA  HHHHxx
+9992   694     0       0       2       12      92      992     1992    4992    9992    184     185     IUAAAA  SABAAA  OOOOxx
+8468   695     0       0       8       8       68      468     468     3468    8468    136     137     SNAAAA  TABAAA  VVVVxx
+6876   696     0       0       6       16      76      876     876     1876    6876    152     153     MEAAAA  UABAAA  AAAAxx
+3532   697     0       0       2       12      32      532     1532    3532    3532    64      65      WFAAAA  VABAAA  HHHHxx
+2140   698     0       0       0       0       40      140     140     2140    2140    80      81      IEAAAA  WABAAA  OOOOxx
+2183   699     1       3       3       3       83      183     183     2183    2183    166     167     ZFAAAA  XABAAA  VVVVxx
+9766   700     0       2       6       6       66      766     1766    4766    9766    132     133     QLAAAA  YABAAA  AAAAxx
+7943   701     1       3       3       3       43      943     1943    2943    7943    86      87      NTAAAA  ZABAAA  HHHHxx
+9243   702     1       3       3       3       43      243     1243    4243    9243    86      87      NRAAAA  ABBAAA  OOOOxx
+6241   703     1       1       1       1       41      241     241     1241    6241    82      83      BGAAAA  BBBAAA  VVVVxx
+9540   704     0       0       0       0       40      540     1540    4540    9540    80      81      YCAAAA  CBBAAA  AAAAxx
+7418   705     0       2       8       18      18      418     1418    2418    7418    36      37      IZAAAA  DBBAAA  HHHHxx
+1603   706     1       3       3       3       3       603     1603    1603    1603    6       7       RJAAAA  EBBAAA  OOOOxx
+8950   707     0       2       0       10      50      950     950     3950    8950    100     101     GGAAAA  FBBAAA  VVVVxx
+6933   708     1       1       3       13      33      933     933     1933    6933    66      67      RGAAAA  GBBAAA  AAAAxx
+2646   709     0       2       6       6       46      646     646     2646    2646    92      93      UXAAAA  HBBAAA  HHHHxx
+3447   710     1       3       7       7       47      447     1447    3447    3447    94      95      PCAAAA  IBBAAA  OOOOxx
+9957   711     1       1       7       17      57      957     1957    4957    9957    114     115     ZSAAAA  JBBAAA  VVVVxx
+4623   712     1       3       3       3       23      623     623     4623    4623    46      47      VVAAAA  KBBAAA  AAAAxx
+9058   713     0       2       8       18      58      58      1058    4058    9058    116     117     KKAAAA  LBBAAA  HHHHxx
+7361   714     1       1       1       1       61      361     1361    2361    7361    122     123     DXAAAA  MBBAAA  OOOOxx
+2489   715     1       1       9       9       89      489     489     2489    2489    178     179     TRAAAA  NBBAAA  VVVVxx
+7643   716     1       3       3       3       43      643     1643    2643    7643    86      87      ZHAAAA  OBBAAA  AAAAxx
+9166   717     0       2       6       6       66      166     1166    4166    9166    132     133     OOAAAA  PBBAAA  HHHHxx
+7789   718     1       1       9       9       89      789     1789    2789    7789    178     179     PNAAAA  QBBAAA  OOOOxx
+2332   719     0       0       2       12      32      332     332     2332    2332    64      65      SLAAAA  RBBAAA  VVVVxx
+1832   720     0       0       2       12      32      832     1832    1832    1832    64      65      MSAAAA  SBBAAA  AAAAxx
+8375   721     1       3       5       15      75      375     375     3375    8375    150     151     DKAAAA  TBBAAA  HHHHxx
+948    722     0       0       8       8       48      948     948     948     948     96      97      MKAAAA  UBBAAA  OOOOxx
+5613   723     1       1       3       13      13      613     1613    613     5613    26      27      XHAAAA  VBBAAA  VVVVxx
+6310   724     0       2       0       10      10      310     310     1310    6310    20      21      SIAAAA  WBBAAA  AAAAxx
+4254   725     0       2       4       14      54      254     254     4254    4254    108     109     QHAAAA  XBBAAA  HHHHxx
+4260   726     0       0       0       0       60      260     260     4260    4260    120     121     WHAAAA  YBBAAA  OOOOxx
+2060   727     0       0       0       0       60      60      60      2060    2060    120     121     GBAAAA  ZBBAAA  VVVVxx
+4831   728     1       3       1       11      31      831     831     4831    4831    62      63      VDAAAA  ACBAAA  AAAAxx
+6176   729     0       0       6       16      76      176     176     1176    6176    152     153     ODAAAA  BCBAAA  HHHHxx
+6688   730     0       0       8       8       88      688     688     1688    6688    176     177     GXAAAA  CCBAAA  OOOOxx
+5752   731     0       0       2       12      52      752     1752    752     5752    104     105     GNAAAA  DCBAAA  VVVVxx
+8714   732     0       2       4       14      14      714     714     3714    8714    28      29      EXAAAA  ECBAAA  AAAAxx
+6739   733     1       3       9       19      39      739     739     1739    6739    78      79      FZAAAA  FCBAAA  HHHHxx
+7066   734     0       2       6       6       66      66      1066    2066    7066    132     133     ULAAAA  GCBAAA  OOOOxx
+7250   735     0       2       0       10      50      250     1250    2250    7250    100     101     WSAAAA  HCBAAA  VVVVxx
+3161   736     1       1       1       1       61      161     1161    3161    3161    122     123     PRAAAA  ICBAAA  AAAAxx
+1411   737     1       3       1       11      11      411     1411    1411    1411    22      23      HCAAAA  JCBAAA  HHHHxx
+9301   738     1       1       1       1       1       301     1301    4301    9301    2       3       TTAAAA  KCBAAA  OOOOxx
+8324   739     0       0       4       4       24      324     324     3324    8324    48      49      EIAAAA  LCBAAA  VVVVxx
+9641   740     1       1       1       1       41      641     1641    4641    9641    82      83      VGAAAA  MCBAAA  AAAAxx
+7077   741     1       1       7       17      77      77      1077    2077    7077    154     155     FMAAAA  NCBAAA  HHHHxx
+9888   742     0       0       8       8       88      888     1888    4888    9888    176     177     IQAAAA  OCBAAA  OOOOxx
+9909   743     1       1       9       9       9       909     1909    4909    9909    18      19      DRAAAA  PCBAAA  VVVVxx
+2209   744     1       1       9       9       9       209     209     2209    2209    18      19      ZGAAAA  QCBAAA  AAAAxx
+6904   745     0       0       4       4       4       904     904     1904    6904    8       9       OFAAAA  RCBAAA  HHHHxx
+6608   746     0       0       8       8       8       608     608     1608    6608    16      17      EUAAAA  SCBAAA  OOOOxx
+8400   747     0       0       0       0       0       400     400     3400    8400    0       1       CLAAAA  TCBAAA  VVVVxx
+5124   748     0       0       4       4       24      124     1124    124     5124    48      49      CPAAAA  UCBAAA  AAAAxx
+5484   749     0       0       4       4       84      484     1484    484     5484    168     169     YCAAAA  VCBAAA  HHHHxx
+3575   750     1       3       5       15      75      575     1575    3575    3575    150     151     NHAAAA  WCBAAA  OOOOxx
+9723   751     1       3       3       3       23      723     1723    4723    9723    46      47      ZJAAAA  XCBAAA  VVVVxx
+360    752     0       0       0       0       60      360     360     360     360     120     121     WNAAAA  YCBAAA  AAAAxx
+1059   753     1       3       9       19      59      59      1059    1059    1059    118     119     TOAAAA  ZCBAAA  HHHHxx
+4941   754     1       1       1       1       41      941     941     4941    4941    82      83      BIAAAA  ADBAAA  OOOOxx
+2535   755     1       3       5       15      35      535     535     2535    2535    70      71      NTAAAA  BDBAAA  VVVVxx
+4119   756     1       3       9       19      19      119     119     4119    4119    38      39      LCAAAA  CDBAAA  AAAAxx
+3725   757     1       1       5       5       25      725     1725    3725    3725    50      51      HNAAAA  DDBAAA  HHHHxx
+4758   758     0       2       8       18      58      758     758     4758    4758    116     117     ABAAAA  EDBAAA  OOOOxx
+9593   759     1       1       3       13      93      593     1593    4593    9593    186     187     ZEAAAA  FDBAAA  VVVVxx
+4663   760     1       3       3       3       63      663     663     4663    4663    126     127     JXAAAA  GDBAAA  AAAAxx
+7734   761     0       2       4       14      34      734     1734    2734    7734    68      69      MLAAAA  HDBAAA  HHHHxx
+9156   762     0       0       6       16      56      156     1156    4156    9156    112     113     EOAAAA  IDBAAA  OOOOxx
+8120   763     0       0       0       0       20      120     120     3120    8120    40      41      IAAAAA  JDBAAA  VVVVxx
+4385   764     1       1       5       5       85      385     385     4385    4385    170     171     RMAAAA  KDBAAA  AAAAxx
+2926   765     0       2       6       6       26      926     926     2926    2926    52      53      OIAAAA  LDBAAA  HHHHxx
+4186   766     0       2       6       6       86      186     186     4186    4186    172     173     AFAAAA  MDBAAA  OOOOxx
+2508   767     0       0       8       8       8       508     508     2508    2508    16      17      MSAAAA  NDBAAA  VVVVxx
+4012   768     0       0       2       12      12      12      12      4012    4012    24      25      IYAAAA  ODBAAA  AAAAxx
+6266   769     0       2       6       6       66      266     266     1266    6266    132     133     AHAAAA  PDBAAA  HHHHxx
+3709   770     1       1       9       9       9       709     1709    3709    3709    18      19      RMAAAA  QDBAAA  OOOOxx
+7289   771     1       1       9       9       89      289     1289    2289    7289    178     179     JUAAAA  RDBAAA  VVVVxx
+8875   772     1       3       5       15      75      875     875     3875    8875    150     151     JDAAAA  SDBAAA  AAAAxx
+4412   773     0       0       2       12      12      412     412     4412    4412    24      25      SNAAAA  TDBAAA  HHHHxx
+3033   774     1       1       3       13      33      33      1033    3033    3033    66      67      RMAAAA  UDBAAA  OOOOxx
+1645   775     1       1       5       5       45      645     1645    1645    1645    90      91      HLAAAA  VDBAAA  VVVVxx
+3557   776     1       1       7       17      57      557     1557    3557    3557    114     115     VGAAAA  WDBAAA  AAAAxx
+6316   777     0       0       6       16      16      316     316     1316    6316    32      33      YIAAAA  XDBAAA  HHHHxx
+2054   778     0       2       4       14      54      54      54      2054    2054    108     109     ABAAAA  YDBAAA  OOOOxx
+7031   779     1       3       1       11      31      31      1031    2031    7031    62      63      LKAAAA  ZDBAAA  VVVVxx
+3405   780     1       1       5       5       5       405     1405    3405    3405    10      11      ZAAAAA  AEBAAA  AAAAxx
+5343   781     1       3       3       3       43      343     1343    343     5343    86      87      NXAAAA  BEBAAA  HHHHxx
+5240   782     0       0       0       0       40      240     1240    240     5240    80      81      OTAAAA  CEBAAA  OOOOxx
+9650   783     0       2       0       10      50      650     1650    4650    9650    100     101     EHAAAA  DEBAAA  VVVVxx
+3777   784     1       1       7       17      77      777     1777    3777    3777    154     155     HPAAAA  EEBAAA  AAAAxx
+9041   785     1       1       1       1       41      41      1041    4041    9041    82      83      TJAAAA  FEBAAA  HHHHxx
+6923   786     1       3       3       3       23      923     923     1923    6923    46      47      HGAAAA  GEBAAA  OOOOxx
+2977   787     1       1       7       17      77      977     977     2977    2977    154     155     NKAAAA  HEBAAA  VVVVxx
+5500   788     0       0       0       0       0       500     1500    500     5500    0       1       ODAAAA  IEBAAA  AAAAxx
+1044   789     0       0       4       4       44      44      1044    1044    1044    88      89      EOAAAA  JEBAAA  HHHHxx
+434    790     0       2       4       14      34      434     434     434     434     68      69      SQAAAA  KEBAAA  OOOOxx
+611    791     1       3       1       11      11      611     611     611     611     22      23      NXAAAA  LEBAAA  VVVVxx
+5760   792     0       0       0       0       60      760     1760    760     5760    120     121     ONAAAA  MEBAAA  AAAAxx
+2445   793     1       1       5       5       45      445     445     2445    2445    90      91      BQAAAA  NEBAAA  HHHHxx
+7098   794     0       2       8       18      98      98      1098    2098    7098    196     197     ANAAAA  OEBAAA  OOOOxx
+2188   795     0       0       8       8       88      188     188     2188    2188    176     177     EGAAAA  PEBAAA  VVVVxx
+4597   796     1       1       7       17      97      597     597     4597    4597    194     195     VUAAAA  QEBAAA  AAAAxx
+1913   797     1       1       3       13      13      913     1913    1913    1913    26      27      PVAAAA  REBAAA  HHHHxx
+8696   798     0       0       6       16      96      696     696     3696    8696    192     193     MWAAAA  SEBAAA  OOOOxx
+3332   799     0       0       2       12      32      332     1332    3332    3332    64      65      EYAAAA  TEBAAA  VVVVxx
+8760   800     0       0       0       0       60      760     760     3760    8760    120     121     YYAAAA  UEBAAA  AAAAxx
+3215   801     1       3       5       15      15      215     1215    3215    3215    30      31      RTAAAA  VEBAAA  HHHHxx
+1625   802     1       1       5       5       25      625     1625    1625    1625    50      51      NKAAAA  WEBAAA  OOOOxx
+4219   803     1       3       9       19      19      219     219     4219    4219    38      39      HGAAAA  XEBAAA  VVVVxx
+415    804     1       3       5       15      15      415     415     415     415     30      31      ZPAAAA  YEBAAA  AAAAxx
+4242   805     0       2       2       2       42      242     242     4242    4242    84      85      EHAAAA  ZEBAAA  HHHHxx
+8660   806     0       0       0       0       60      660     660     3660    8660    120     121     CVAAAA  AFBAAA  OOOOxx
+6525   807     1       1       5       5       25      525     525     1525    6525    50      51      ZQAAAA  BFBAAA  VVVVxx
+2141   808     1       1       1       1       41      141     141     2141    2141    82      83      JEAAAA  CFBAAA  AAAAxx
+5152   809     0       0       2       12      52      152     1152    152     5152    104     105     EQAAAA  DFBAAA  HHHHxx
+8560   810     0       0       0       0       60      560     560     3560    8560    120     121     GRAAAA  EFBAAA  OOOOxx
+9835   811     1       3       5       15      35      835     1835    4835    9835    70      71      HOAAAA  FFBAAA  VVVVxx
+2657   812     1       1       7       17      57      657     657     2657    2657    114     115     FYAAAA  GFBAAA  AAAAxx
+6085   813     1       1       5       5       85      85      85      1085    6085    170     171     BAAAAA  HFBAAA  HHHHxx
+6698   814     0       2       8       18      98      698     698     1698    6698    196     197     QXAAAA  IFBAAA  OOOOxx
+5421   815     1       1       1       1       21      421     1421    421     5421    42      43      NAAAAA  JFBAAA  VVVVxx
+6661   816     1       1       1       1       61      661     661     1661    6661    122     123     FWAAAA  KFBAAA  AAAAxx
+5645   817     1       1       5       5       45      645     1645    645     5645    90      91      DJAAAA  LFBAAA  HHHHxx
+1248   818     0       0       8       8       48      248     1248    1248    1248    96      97      AWAAAA  MFBAAA  OOOOxx
+5690   819     0       2       0       10      90      690     1690    690     5690    180     181     WKAAAA  NFBAAA  VVVVxx
+4762   820     0       2       2       2       62      762     762     4762    4762    124     125     EBAAAA  OFBAAA  AAAAxx
+1455   821     1       3       5       15      55      455     1455    1455    1455    110     111     ZDAAAA  PFBAAA  HHHHxx
+9846   822     0       2       6       6       46      846     1846    4846    9846    92      93      SOAAAA  QFBAAA  OOOOxx
+5295   823     1       3       5       15      95      295     1295    295     5295    190     191     RVAAAA  RFBAAA  VVVVxx
+2826   824     0       2       6       6       26      826     826     2826    2826    52      53      SEAAAA  SFBAAA  AAAAxx
+7496   825     0       0       6       16      96      496     1496    2496    7496    192     193     ICAAAA  TFBAAA  HHHHxx
+3024   826     0       0       4       4       24      24      1024    3024    3024    48      49      IMAAAA  UFBAAA  OOOOxx
+4945   827     1       1       5       5       45      945     945     4945    4945    90      91      FIAAAA  VFBAAA  VVVVxx
+4404   828     0       0       4       4       4       404     404     4404    4404    8       9       KNAAAA  WFBAAA  AAAAxx
+9302   829     0       2       2       2       2       302     1302    4302    9302    4       5       UTAAAA  XFBAAA  HHHHxx
+1286   830     0       2       6       6       86      286     1286    1286    1286    172     173     MXAAAA  YFBAAA  OOOOxx
+8435   831     1       3       5       15      35      435     435     3435    8435    70      71      LMAAAA  ZFBAAA  VVVVxx
+8969   832     1       1       9       9       69      969     969     3969    8969    138     139     ZGAAAA  AGBAAA  AAAAxx
+3302   833     0       2       2       2       2       302     1302    3302    3302    4       5       AXAAAA  BGBAAA  HHHHxx
+9753   834     1       1       3       13      53      753     1753    4753    9753    106     107     DLAAAA  CGBAAA  OOOOxx
+9374   835     0       2       4       14      74      374     1374    4374    9374    148     149     OWAAAA  DGBAAA  VVVVxx
+4907   836     1       3       7       7       7       907     907     4907    4907    14      15      TGAAAA  EGBAAA  AAAAxx
+1659   837     1       3       9       19      59      659     1659    1659    1659    118     119     VLAAAA  FGBAAA  HHHHxx
+5095   838     1       3       5       15      95      95      1095    95      5095    190     191     ZNAAAA  GGBAAA  OOOOxx
+9446   839     0       2       6       6       46      446     1446    4446    9446    92      93      IZAAAA  HGBAAA  VVVVxx
+8528   840     0       0       8       8       28      528     528     3528    8528    56      57      AQAAAA  IGBAAA  AAAAxx
+4890   841     0       2       0       10      90      890     890     4890    4890    180     181     CGAAAA  JGBAAA  HHHHxx
+1221   842     1       1       1       1       21      221     1221    1221    1221    42      43      ZUAAAA  KGBAAA  OOOOxx
+5583   843     1       3       3       3       83      583     1583    583     5583    166     167     TGAAAA  LGBAAA  VVVVxx
+7303   844     1       3       3       3       3       303     1303    2303    7303    6       7       XUAAAA  MGBAAA  AAAAxx
+406    845     0       2       6       6       6       406     406     406     406     12      13      QPAAAA  NGBAAA  HHHHxx
+7542   846     0       2       2       2       42      542     1542    2542    7542    84      85      CEAAAA  OGBAAA  OOOOxx
+9507   847     1       3       7       7       7       507     1507    4507    9507    14      15      RBAAAA  PGBAAA  VVVVxx
+9511   848     1       3       1       11      11      511     1511    4511    9511    22      23      VBAAAA  QGBAAA  AAAAxx
+1373   849     1       1       3       13      73      373     1373    1373    1373    146     147     VAAAAA  RGBAAA  HHHHxx
+6556   850     0       0       6       16      56      556     556     1556    6556    112     113     ESAAAA  SGBAAA  OOOOxx
+4117   851     1       1       7       17      17      117     117     4117    4117    34      35      JCAAAA  TGBAAA  VVVVxx
+7794   852     0       2       4       14      94      794     1794    2794    7794    188     189     UNAAAA  UGBAAA  AAAAxx
+7170   853     0       2       0       10      70      170     1170    2170    7170    140     141     UPAAAA  VGBAAA  HHHHxx
+5809   854     1       1       9       9       9       809     1809    809     5809    18      19      LPAAAA  WGBAAA  OOOOxx
+7828   855     0       0       8       8       28      828     1828    2828    7828    56      57      CPAAAA  XGBAAA  VVVVxx
+8046   856     0       2       6       6       46      46      46      3046    8046    92      93      MXAAAA  YGBAAA  AAAAxx
+4833   857     1       1       3       13      33      833     833     4833    4833    66      67      XDAAAA  ZGBAAA  HHHHxx
+2107   858     1       3       7       7       7       107     107     2107    2107    14      15      BDAAAA  AHBAAA  OOOOxx
+4276   859     0       0       6       16      76      276     276     4276    4276    152     153     MIAAAA  BHBAAA  VVVVxx
+9536   860     0       0       6       16      36      536     1536    4536    9536    72      73      UCAAAA  CHBAAA  AAAAxx
+5549   861     1       1       9       9       49      549     1549    549     5549    98      99      LFAAAA  DHBAAA  HHHHxx
+6427   862     1       3       7       7       27      427     427     1427    6427    54      55      FNAAAA  EHBAAA  OOOOxx
+1382   863     0       2       2       2       82      382     1382    1382    1382    164     165     EBAAAA  FHBAAA  VVVVxx
+3256   864     0       0       6       16      56      256     1256    3256    3256    112     113     GVAAAA  GHBAAA  AAAAxx
+3270   865     0       2       0       10      70      270     1270    3270    3270    140     141     UVAAAA  HHBAAA  HHHHxx
+4808   866     0       0       8       8       8       808     808     4808    4808    16      17      YCAAAA  IHBAAA  OOOOxx
+7938   867     0       2       8       18      38      938     1938    2938    7938    76      77      ITAAAA  JHBAAA  VVVVxx
+4405   868     1       1       5       5       5       405     405     4405    4405    10      11      LNAAAA  KHBAAA  AAAAxx
+2264   869     0       0       4       4       64      264     264     2264    2264    128     129     CJAAAA  LHBAAA  HHHHxx
+80     870     0       0       0       0       80      80      80      80      80      160     161     CDAAAA  MHBAAA  OOOOxx
+320    871     0       0       0       0       20      320     320     320     320     40      41      IMAAAA  NHBAAA  VVVVxx
+2383   872     1       3       3       3       83      383     383     2383    2383    166     167     RNAAAA  OHBAAA  AAAAxx
+3146   873     0       2       6       6       46      146     1146    3146    3146    92      93      ARAAAA  PHBAAA  HHHHxx
+6911   874     1       3       1       11      11      911     911     1911    6911    22      23      VFAAAA  QHBAAA  OOOOxx
+7377   875     1       1       7       17      77      377     1377    2377    7377    154     155     TXAAAA  RHBAAA  VVVVxx
+9965   876     1       1       5       5       65      965     1965    4965    9965    130     131     HTAAAA  SHBAAA  AAAAxx
+8361   877     1       1       1       1       61      361     361     3361    8361    122     123     PJAAAA  THBAAA  HHHHxx
+9417   878     1       1       7       17      17      417     1417    4417    9417    34      35      FYAAAA  UHBAAA  OOOOxx
+2483   879     1       3       3       3       83      483     483     2483    2483    166     167     NRAAAA  VHBAAA  VVVVxx
+9843   880     1       3       3       3       43      843     1843    4843    9843    86      87      POAAAA  WHBAAA  AAAAxx
+6395   881     1       3       5       15      95      395     395     1395    6395    190     191     ZLAAAA  XHBAAA  HHHHxx
+6444   882     0       0       4       4       44      444     444     1444    6444    88      89      WNAAAA  YHBAAA  OOOOxx
+1820   883     0       0       0       0       20      820     1820    1820    1820    40      41      ASAAAA  ZHBAAA  VVVVxx
+2768   884     0       0       8       8       68      768     768     2768    2768    136     137     MCAAAA  AIBAAA  AAAAxx
+5413   885     1       1       3       13      13      413     1413    413     5413    26      27      FAAAAA  BIBAAA  HHHHxx
+2923   886     1       3       3       3       23      923     923     2923    2923    46      47      LIAAAA  CIBAAA  OOOOxx
+5286   887     0       2       6       6       86      286     1286    286     5286    172     173     IVAAAA  DIBAAA  VVVVxx
+6126   888     0       2       6       6       26      126     126     1126    6126    52      53      QBAAAA  EIBAAA  AAAAxx
+8343   889     1       3       3       3       43      343     343     3343    8343    86      87      XIAAAA  FIBAAA  HHHHxx
+6010   890     0       2       0       10      10      10      10      1010    6010    20      21      EXAAAA  GIBAAA  OOOOxx
+4177   891     1       1       7       17      77      177     177     4177    4177    154     155     REAAAA  HIBAAA  VVVVxx
+5808   892     0       0       8       8       8       808     1808    808     5808    16      17      KPAAAA  IIBAAA  AAAAxx
+4859   893     1       3       9       19      59      859     859     4859    4859    118     119     XEAAAA  JIBAAA  HHHHxx
+9252   894     0       0       2       12      52      252     1252    4252    9252    104     105     WRAAAA  KIBAAA  OOOOxx
+2941   895     1       1       1       1       41      941     941     2941    2941    82      83      DJAAAA  LIBAAA  VVVVxx
+8693   896     1       1       3       13      93      693     693     3693    8693    186     187     JWAAAA  MIBAAA  AAAAxx
+4432   897     0       0       2       12      32      432     432     4432    4432    64      65      MOAAAA  NIBAAA  HHHHxx
+2371   898     1       3       1       11      71      371     371     2371    2371    142     143     FNAAAA  OIBAAA  OOOOxx
+7546   899     0       2       6       6       46      546     1546    2546    7546    92      93      GEAAAA  PIBAAA  VVVVxx
+1369   900     1       1       9       9       69      369     1369    1369    1369    138     139     RAAAAA  QIBAAA  AAAAxx
+4687   901     1       3       7       7       87      687     687     4687    4687    174     175     HYAAAA  RIBAAA  HHHHxx
+8941   902     1       1       1       1       41      941     941     3941    8941    82      83      XFAAAA  SIBAAA  OOOOxx
+226    903     0       2       6       6       26      226     226     226     226     52      53      SIAAAA  TIBAAA  VVVVxx
+3493   904     1       1       3       13      93      493     1493    3493    3493    186     187     JEAAAA  UIBAAA  AAAAxx
+6433   905     1       1       3       13      33      433     433     1433    6433    66      67      LNAAAA  VIBAAA  HHHHxx
+9189   906     1       1       9       9       89      189     1189    4189    9189    178     179     LPAAAA  WIBAAA  OOOOxx
+6027   907     1       3       7       7       27      27      27      1027    6027    54      55      VXAAAA  XIBAAA  VVVVxx
+4615   908     1       3       5       15      15      615     615     4615    4615    30      31      NVAAAA  YIBAAA  AAAAxx
+5320   909     0       0       0       0       20      320     1320    320     5320    40      41      QWAAAA  ZIBAAA  HHHHxx
+7002   910     0       2       2       2       2       2       1002    2002    7002    4       5       IJAAAA  AJBAAA  OOOOxx
+7367   911     1       3       7       7       67      367     1367    2367    7367    134     135     JXAAAA  BJBAAA  VVVVxx
+289    912     1       1       9       9       89      289     289     289     289     178     179     DLAAAA  CJBAAA  AAAAxx
+407    913     1       3       7       7       7       407     407     407     407     14      15      RPAAAA  DJBAAA  HHHHxx
+504    914     0       0       4       4       4       504     504     504     504     8       9       KTAAAA  EJBAAA  OOOOxx
+8301   915     1       1       1       1       1       301     301     3301    8301    2       3       HHAAAA  FJBAAA  VVVVxx
+1396   916     0       0       6       16      96      396     1396    1396    1396    192     193     SBAAAA  GJBAAA  AAAAxx
+4794   917     0       2       4       14      94      794     794     4794    4794    188     189     KCAAAA  HJBAAA  HHHHxx
+6400   918     0       0       0       0       0       400     400     1400    6400    0       1       EMAAAA  IJBAAA  OOOOxx
+1275   919     1       3       5       15      75      275     1275    1275    1275    150     151     BXAAAA  JJBAAA  VVVVxx
+5797   920     1       1       7       17      97      797     1797    797     5797    194     195     ZOAAAA  KJBAAA  AAAAxx
+2221   921     1       1       1       1       21      221     221     2221    2221    42      43      LHAAAA  LJBAAA  HHHHxx
+2504   922     0       0       4       4       4       504     504     2504    2504    8       9       ISAAAA  MJBAAA  OOOOxx
+2143   923     1       3       3       3       43      143     143     2143    2143    86      87      LEAAAA  NJBAAA  VVVVxx
+1083   924     1       3       3       3       83      83      1083    1083    1083    166     167     RPAAAA  OJBAAA  AAAAxx
+6148   925     0       0       8       8       48      148     148     1148    6148    96      97      MCAAAA  PJBAAA  HHHHxx
+3612   926     0       0       2       12      12      612     1612    3612    3612    24      25      YIAAAA  QJBAAA  OOOOxx
+9499   927     1       3       9       19      99      499     1499    4499    9499    198     199     JBAAAA  RJBAAA  VVVVxx
+5773   928     1       1       3       13      73      773     1773    773     5773    146     147     BOAAAA  SJBAAA  AAAAxx
+1014   929     0       2       4       14      14      14      1014    1014    1014    28      29      ANAAAA  TJBAAA  HHHHxx
+1427   930     1       3       7       7       27      427     1427    1427    1427    54      55      XCAAAA  UJBAAA  OOOOxx
+6770   931     0       2       0       10      70      770     770     1770    6770    140     141     KAAAAA  VJBAAA  VVVVxx
+9042   932     0       2       2       2       42      42      1042    4042    9042    84      85      UJAAAA  WJBAAA  AAAAxx
+9892   933     0       0       2       12      92      892     1892    4892    9892    184     185     MQAAAA  XJBAAA  HHHHxx
+1771   934     1       3       1       11      71      771     1771    1771    1771    142     143     DQAAAA  YJBAAA  OOOOxx
+7392   935     0       0       2       12      92      392     1392    2392    7392    184     185     IYAAAA  ZJBAAA  VVVVxx
+4465   936     1       1       5       5       65      465     465     4465    4465    130     131     TPAAAA  AKBAAA  AAAAxx
+278    937     0       2       8       18      78      278     278     278     278     156     157     SKAAAA  BKBAAA  HHHHxx
+7776   938     0       0       6       16      76      776     1776    2776    7776    152     153     CNAAAA  CKBAAA  OOOOxx
+3763   939     1       3       3       3       63      763     1763    3763    3763    126     127     TOAAAA  DKBAAA  VVVVxx
+7503   940     1       3       3       3       3       503     1503    2503    7503    6       7       PCAAAA  EKBAAA  AAAAxx
+3793   941     1       1       3       13      93      793     1793    3793    3793    186     187     XPAAAA  FKBAAA  HHHHxx
+6510   942     0       2       0       10      10      510     510     1510    6510    20      21      KQAAAA  GKBAAA  OOOOxx
+7641   943     1       1       1       1       41      641     1641    2641    7641    82      83      XHAAAA  HKBAAA  VVVVxx
+3228   944     0       0       8       8       28      228     1228    3228    3228    56      57      EUAAAA  IKBAAA  AAAAxx
+194    945     0       2       4       14      94      194     194     194     194     188     189     MHAAAA  JKBAAA  HHHHxx
+8555   946     1       3       5       15      55      555     555     3555    8555    110     111     BRAAAA  KKBAAA  OOOOxx
+4997   947     1       1       7       17      97      997     997     4997    4997    194     195     FKAAAA  LKBAAA  VVVVxx
+8687   948     1       3       7       7       87      687     687     3687    8687    174     175     DWAAAA  MKBAAA  AAAAxx
+6632   949     0       0       2       12      32      632     632     1632    6632    64      65      CVAAAA  NKBAAA  HHHHxx
+9607   950     1       3       7       7       7       607     1607    4607    9607    14      15      NFAAAA  OKBAAA  OOOOxx
+6201   951     1       1       1       1       1       201     201     1201    6201    2       3       NEAAAA  PKBAAA  VVVVxx
+857    952     1       1       7       17      57      857     857     857     857     114     115     ZGAAAA  QKBAAA  AAAAxx
+5623   953     1       3       3       3       23      623     1623    623     5623    46      47      HIAAAA  RKBAAA  HHHHxx
+5979   954     1       3       9       19      79      979     1979    979     5979    158     159     ZVAAAA  SKBAAA  OOOOxx
+2201   955     1       1       1       1       1       201     201     2201    2201    2       3       RGAAAA  TKBAAA  VVVVxx
+3166   956     0       2       6       6       66      166     1166    3166    3166    132     133     URAAAA  UKBAAA  AAAAxx
+6249   957     1       1       9       9       49      249     249     1249    6249    98      99      JGAAAA  VKBAAA  HHHHxx
+3271   958     1       3       1       11      71      271     1271    3271    3271    142     143     VVAAAA  WKBAAA  OOOOxx
+7777   959     1       1       7       17      77      777     1777    2777    7777    154     155     DNAAAA  XKBAAA  VVVVxx
+6732   960     0       0       2       12      32      732     732     1732    6732    64      65      YYAAAA  YKBAAA  AAAAxx
+6297   961     1       1       7       17      97      297     297     1297    6297    194     195     FIAAAA  ZKBAAA  HHHHxx
+5685   962     1       1       5       5       85      685     1685    685     5685    170     171     RKAAAA  ALBAAA  OOOOxx
+9931   963     1       3       1       11      31      931     1931    4931    9931    62      63      ZRAAAA  BLBAAA  VVVVxx
+7485   964     1       1       5       5       85      485     1485    2485    7485    170     171     XBAAAA  CLBAAA  AAAAxx
+386    965     0       2       6       6       86      386     386     386     386     172     173     WOAAAA  DLBAAA  HHHHxx
+8204   966     0       0       4       4       4       204     204     3204    8204    8       9       ODAAAA  ELBAAA  OOOOxx
+3606   967     0       2       6       6       6       606     1606    3606    3606    12      13      SIAAAA  FLBAAA  VVVVxx
+1692   968     0       0       2       12      92      692     1692    1692    1692    184     185     CNAAAA  GLBAAA  AAAAxx
+3002   969     0       2       2       2       2       2       1002    3002    3002    4       5       MLAAAA  HLBAAA  HHHHxx
+9676   970     0       0       6       16      76      676     1676    4676    9676    152     153     EIAAAA  ILBAAA  OOOOxx
+915    971     1       3       5       15      15      915     915     915     915     30      31      FJAAAA  JLBAAA  VVVVxx
+7706   972     0       2       6       6       6       706     1706    2706    7706    12      13      KKAAAA  KLBAAA  AAAAxx
+6080   973     0       0       0       0       80      80      80      1080    6080    160     161     WZAAAA  LLBAAA  HHHHxx
+1860   974     0       0       0       0       60      860     1860    1860    1860    120     121     OTAAAA  MLBAAA  OOOOxx
+1444   975     0       0       4       4       44      444     1444    1444    1444    88      89      ODAAAA  NLBAAA  VVVVxx
+7208   976     0       0       8       8       8       208     1208    2208    7208    16      17      GRAAAA  OLBAAA  AAAAxx
+8554   977     0       2       4       14      54      554     554     3554    8554    108     109     ARAAAA  PLBAAA  HHHHxx
+2028   978     0       0       8       8       28      28      28      2028    2028    56      57      AAAAAA  QLBAAA  OOOOxx
+9893   979     1       1       3       13      93      893     1893    4893    9893    186     187     NQAAAA  RLBAAA  VVVVxx
+4740   980     0       0       0       0       40      740     740     4740    4740    80      81      IAAAAA  SLBAAA  AAAAxx
+6186   981     0       2       6       6       86      186     186     1186    6186    172     173     YDAAAA  TLBAAA  HHHHxx
+6357   982     1       1       7       17      57      357     357     1357    6357    114     115     NKAAAA  ULBAAA  OOOOxx
+3699   983     1       3       9       19      99      699     1699    3699    3699    198     199     HMAAAA  VLBAAA  VVVVxx
+7620   984     0       0       0       0       20      620     1620    2620    7620    40      41      CHAAAA  WLBAAA  AAAAxx
+921    985     1       1       1       1       21      921     921     921     921     42      43      LJAAAA  XLBAAA  HHHHxx
+5506   986     0       2       6       6       6       506     1506    506     5506    12      13      UDAAAA  YLBAAA  OOOOxx
+8851   987     1       3       1       11      51      851     851     3851    8851    102     103     LCAAAA  ZLBAAA  VVVVxx
+3205   988     1       1       5       5       5       205     1205    3205    3205    10      11      HTAAAA  AMBAAA  AAAAxx
+1956   989     0       0       6       16      56      956     1956    1956    1956    112     113     GXAAAA  BMBAAA  HHHHxx
+6272   990     0       0       2       12      72      272     272     1272    6272    144     145     GHAAAA  CMBAAA  OOOOxx
+1509   991     1       1       9       9       9       509     1509    1509    1509    18      19      BGAAAA  DMBAAA  VVVVxx
+53     992     1       1       3       13      53      53      53      53      53      106     107     BCAAAA  EMBAAA  AAAAxx
+213    993     1       1       3       13      13      213     213     213     213     26      27      FIAAAA  FMBAAA  HHHHxx
+4924   994     0       0       4       4       24      924     924     4924    4924    48      49      KHAAAA  GMBAAA  OOOOxx
+2097   995     1       1       7       17      97      97      97      2097    2097    194     195     RCAAAA  HMBAAA  VVVVxx
+4607   996     1       3       7       7       7       607     607     4607    4607    14      15      FVAAAA  IMBAAA  AAAAxx
+1582   997     0       2       2       2       82      582     1582    1582    1582    164     165     WIAAAA  JMBAAA  HHHHxx
+6643   998     1       3       3       3       43      643     643     1643    6643    86      87      NVAAAA  KMBAAA  OOOOxx
+2238   999     0       2       8       18      38      238     238     2238    2238    76      77      CIAAAA  LMBAAA  VVVVxx
+2942   1000    0       2       2       2       42      942     942     2942    2942    84      85      EJAAAA  MMBAAA  AAAAxx
+1655   1001    1       3       5       15      55      655     1655    1655    1655    110     111     RLAAAA  NMBAAA  HHHHxx
+3226   1002    0       2       6       6       26      226     1226    3226    3226    52      53      CUAAAA  OMBAAA  OOOOxx
+4263   1003    1       3       3       3       63      263     263     4263    4263    126     127     ZHAAAA  PMBAAA  VVVVxx
+960    1004    0       0       0       0       60      960     960     960     960     120     121     YKAAAA  QMBAAA  AAAAxx
+1213   1005    1       1       3       13      13      213     1213    1213    1213    26      27      RUAAAA  RMBAAA  HHHHxx
+1845   1006    1       1       5       5       45      845     1845    1845    1845    90      91      ZSAAAA  SMBAAA  OOOOxx
+6944   1007    0       0       4       4       44      944     944     1944    6944    88      89      CHAAAA  TMBAAA  VVVVxx
+5284   1008    0       0       4       4       84      284     1284    284     5284    168     169     GVAAAA  UMBAAA  AAAAxx
+188    1009    0       0       8       8       88      188     188     188     188     176     177     GHAAAA  VMBAAA  HHHHxx
+748    1010    0       0       8       8       48      748     748     748     748     96      97      UCAAAA  WMBAAA  OOOOxx
+2226   1011    0       2       6       6       26      226     226     2226    2226    52      53      QHAAAA  XMBAAA  VVVVxx
+7342   1012    0       2       2       2       42      342     1342    2342    7342    84      85      KWAAAA  YMBAAA  AAAAxx
+6120   1013    0       0       0       0       20      120     120     1120    6120    40      41      KBAAAA  ZMBAAA  HHHHxx
+536    1014    0       0       6       16      36      536     536     536     536     72      73      QUAAAA  ANBAAA  OOOOxx
+3239   1015    1       3       9       19      39      239     1239    3239    3239    78      79      PUAAAA  BNBAAA  VVVVxx
+2832   1016    0       0       2       12      32      832     832     2832    2832    64      65      YEAAAA  CNBAAA  AAAAxx
+5296   1017    0       0       6       16      96      296     1296    296     5296    192     193     SVAAAA  DNBAAA  HHHHxx
+5795   1018    1       3       5       15      95      795     1795    795     5795    190     191     XOAAAA  ENBAAA  OOOOxx
+6290   1019    0       2       0       10      90      290     290     1290    6290    180     181     YHAAAA  FNBAAA  VVVVxx
+4916   1020    0       0       6       16      16      916     916     4916    4916    32      33      CHAAAA  GNBAAA  AAAAxx
+8366   1021    0       2       6       6       66      366     366     3366    8366    132     133     UJAAAA  HNBAAA  HHHHxx
+4248   1022    0       0       8       8       48      248     248     4248    4248    96      97      KHAAAA  INBAAA  OOOOxx
+6460   1023    0       0       0       0       60      460     460     1460    6460    120     121     MOAAAA  JNBAAA  VVVVxx
+9296   1024    0       0       6       16      96      296     1296    4296    9296    192     193     OTAAAA  KNBAAA  AAAAxx
+3486   1025    0       2       6       6       86      486     1486    3486    3486    172     173     CEAAAA  LNBAAA  HHHHxx
+5664   1026    0       0       4       4       64      664     1664    664     5664    128     129     WJAAAA  MNBAAA  OOOOxx
+7624   1027    0       0       4       4       24      624     1624    2624    7624    48      49      GHAAAA  NNBAAA  VVVVxx
+2790   1028    0       2       0       10      90      790     790     2790    2790    180     181     IDAAAA  ONBAAA  AAAAxx
+682    1029    0       2       2       2       82      682     682     682     682     164     165     GAAAAA  PNBAAA  HHHHxx
+6412   1030    0       0       2       12      12      412     412     1412    6412    24      25      QMAAAA  QNBAAA  OOOOxx
+6882   1031    0       2       2       2       82      882     882     1882    6882    164     165     SEAAAA  RNBAAA  VVVVxx
+1332   1032    0       0       2       12      32      332     1332    1332    1332    64      65      GZAAAA  SNBAAA  AAAAxx
+4911   1033    1       3       1       11      11      911     911     4911    4911    22      23      XGAAAA  TNBAAA  HHHHxx
+3528   1034    0       0       8       8       28      528     1528    3528    3528    56      57      SFAAAA  UNBAAA  OOOOxx
+271    1035    1       3       1       11      71      271     271     271     271     142     143     LKAAAA  VNBAAA  VVVVxx
+7007   1036    1       3       7       7       7       7       1007    2007    7007    14      15      NJAAAA  WNBAAA  AAAAxx
+2198   1037    0       2       8       18      98      198     198     2198    2198    196     197     OGAAAA  XNBAAA  HHHHxx
+4266   1038    0       2       6       6       66      266     266     4266    4266    132     133     CIAAAA  YNBAAA  OOOOxx
+9867   1039    1       3       7       7       67      867     1867    4867    9867    134     135     NPAAAA  ZNBAAA  VVVVxx
+7602   1040    0       2       2       2       2       602     1602    2602    7602    4       5       KGAAAA  AOBAAA  AAAAxx
+7521   1041    1       1       1       1       21      521     1521    2521    7521    42      43      HDAAAA  BOBAAA  HHHHxx
+7200   1042    0       0       0       0       0       200     1200    2200    7200    0       1       YQAAAA  COBAAA  OOOOxx
+4816   1043    0       0       6       16      16      816     816     4816    4816    32      33      GDAAAA  DOBAAA  VVVVxx
+1669   1044    1       1       9       9       69      669     1669    1669    1669    138     139     FMAAAA  EOBAAA  AAAAxx
+4764   1045    0       0       4       4       64      764     764     4764    4764    128     129     GBAAAA  FOBAAA  HHHHxx
+7393   1046    1       1       3       13      93      393     1393    2393    7393    186     187     JYAAAA  GOBAAA  OOOOxx
+7434   1047    0       2       4       14      34      434     1434    2434    7434    68      69      YZAAAA  HOBAAA  VVVVxx
+9079   1048    1       3       9       19      79      79      1079    4079    9079    158     159     FLAAAA  IOBAAA  AAAAxx
+9668   1049    0       0       8       8       68      668     1668    4668    9668    136     137     WHAAAA  JOBAAA  HHHHxx
+7184   1050    0       0       4       4       84      184     1184    2184    7184    168     169     IQAAAA  KOBAAA  OOOOxx
+7347   1051    1       3       7       7       47      347     1347    2347    7347    94      95      PWAAAA  LOBAAA  VVVVxx
+951    1052    1       3       1       11      51      951     951     951     951     102     103     PKAAAA  MOBAAA  AAAAxx
+4513   1053    1       1       3       13      13      513     513     4513    4513    26      27      PRAAAA  NOBAAA  HHHHxx
+2692   1054    0       0       2       12      92      692     692     2692    2692    184     185     OZAAAA  OOBAAA  OOOOxx
+9930   1055    0       2       0       10      30      930     1930    4930    9930    60      61      YRAAAA  POBAAA  VVVVxx
+4516   1056    0       0       6       16      16      516     516     4516    4516    32      33      SRAAAA  QOBAAA  AAAAxx
+1592   1057    0       0       2       12      92      592     1592    1592    1592    184     185     GJAAAA  ROBAAA  HHHHxx
+6312   1058    0       0       2       12      12      312     312     1312    6312    24      25      UIAAAA  SOBAAA  OOOOxx
+185    1059    1       1       5       5       85      185     185     185     185     170     171     DHAAAA  TOBAAA  VVVVxx
+1848   1060    0       0       8       8       48      848     1848    1848    1848    96      97      CTAAAA  UOBAAA  AAAAxx
+5844   1061    0       0       4       4       44      844     1844    844     5844    88      89      UQAAAA  VOBAAA  HHHHxx
+1666   1062    0       2       6       6       66      666     1666    1666    1666    132     133     CMAAAA  WOBAAA  OOOOxx
+5864   1063    0       0       4       4       64      864     1864    864     5864    128     129     ORAAAA  XOBAAA  VVVVxx
+1004   1064    0       0       4       4       4       4       1004    1004    1004    8       9       QMAAAA  YOBAAA  AAAAxx
+1758   1065    0       2       8       18      58      758     1758    1758    1758    116     117     QPAAAA  ZOBAAA  HHHHxx
+8823   1066    1       3       3       3       23      823     823     3823    8823    46      47      JBAAAA  APBAAA  OOOOxx
+129    1067    1       1       9       9       29      129     129     129     129     58      59      ZEAAAA  BPBAAA  VVVVxx
+5703   1068    1       3       3       3       3       703     1703    703     5703    6       7       JLAAAA  CPBAAA  AAAAxx
+3331   1069    1       3       1       11      31      331     1331    3331    3331    62      63      DYAAAA  DPBAAA  HHHHxx
+5791   1070    1       3       1       11      91      791     1791    791     5791    182     183     TOAAAA  EPBAAA  OOOOxx
+4421   1071    1       1       1       1       21      421     421     4421    4421    42      43      BOAAAA  FPBAAA  VVVVxx
+9740   1072    0       0       0       0       40      740     1740    4740    9740    80      81      QKAAAA  GPBAAA  AAAAxx
+798    1073    0       2       8       18      98      798     798     798     798     196     197     SEAAAA  HPBAAA  HHHHxx
+571    1074    1       3       1       11      71      571     571     571     571     142     143     ZVAAAA  IPBAAA  OOOOxx
+7084   1075    0       0       4       4       84      84      1084    2084    7084    168     169     MMAAAA  JPBAAA  VVVVxx
+650    1076    0       2       0       10      50      650     650     650     650     100     101     AZAAAA  KPBAAA  AAAAxx
+1467   1077    1       3       7       7       67      467     1467    1467    1467    134     135     LEAAAA  LPBAAA  HHHHxx
+5446   1078    0       2       6       6       46      446     1446    446     5446    92      93      MBAAAA  MPBAAA  OOOOxx
+830    1079    0       2       0       10      30      830     830     830     830     60      61      YFAAAA  NPBAAA  VVVVxx
+5516   1080    0       0       6       16      16      516     1516    516     5516    32      33      EEAAAA  OPBAAA  AAAAxx
+8520   1081    0       0       0       0       20      520     520     3520    8520    40      41      SPAAAA  PPBAAA  HHHHxx
+1152   1082    0       0       2       12      52      152     1152    1152    1152    104     105     ISAAAA  QPBAAA  OOOOxx
+862    1083    0       2       2       2       62      862     862     862     862     124     125     EHAAAA  RPBAAA  VVVVxx
+454    1084    0       2       4       14      54      454     454     454     454     108     109     MRAAAA  SPBAAA  AAAAxx
+9956   1085    0       0       6       16      56      956     1956    4956    9956    112     113     YSAAAA  TPBAAA  HHHHxx
+1654   1086    0       2       4       14      54      654     1654    1654    1654    108     109     QLAAAA  UPBAAA  OOOOxx
+257    1087    1       1       7       17      57      257     257     257     257     114     115     XJAAAA  VPBAAA  VVVVxx
+5469   1088    1       1       9       9       69      469     1469    469     5469    138     139     JCAAAA  WPBAAA  AAAAxx
+9075   1089    1       3       5       15      75      75      1075    4075    9075    150     151     BLAAAA  XPBAAA  HHHHxx
+7799   1090    1       3       9       19      99      799     1799    2799    7799    198     199     ZNAAAA  YPBAAA  OOOOxx
+2001   1091    1       1       1       1       1       1       1       2001    2001    2       3       ZYAAAA  ZPBAAA  VVVVxx
+9786   1092    0       2       6       6       86      786     1786    4786    9786    172     173     KMAAAA  AQBAAA  AAAAxx
+7281   1093    1       1       1       1       81      281     1281    2281    7281    162     163     BUAAAA  BQBAAA  HHHHxx
+5137   1094    1       1       7       17      37      137     1137    137     5137    74      75      PPAAAA  CQBAAA  OOOOxx
+4053   1095    1       1       3       13      53      53      53      4053    4053    106     107     XZAAAA  DQBAAA  VVVVxx
+7911   1096    1       3       1       11      11      911     1911    2911    7911    22      23      HSAAAA  EQBAAA  AAAAxx
+4298   1097    0       2       8       18      98      298     298     4298    4298    196     197     IJAAAA  FQBAAA  HHHHxx
+4805   1098    1       1       5       5       5       805     805     4805    4805    10      11      VCAAAA  GQBAAA  OOOOxx
+9038   1099    0       2       8       18      38      38      1038    4038    9038    76      77      QJAAAA  HQBAAA  VVVVxx
+8023   1100    1       3       3       3       23      23      23      3023    8023    46      47      PWAAAA  IQBAAA  AAAAxx
+6595   1101    1       3       5       15      95      595     595     1595    6595    190     191     RTAAAA  JQBAAA  HHHHxx
+9831   1102    1       3       1       11      31      831     1831    4831    9831    62      63      DOAAAA  KQBAAA  OOOOxx
+788    1103    0       0       8       8       88      788     788     788     788     176     177     IEAAAA  LQBAAA  VVVVxx
+902    1104    0       2       2       2       2       902     902     902     902     4       5       SIAAAA  MQBAAA  AAAAxx
+9137   1105    1       1       7       17      37      137     1137    4137    9137    74      75      LNAAAA  NQBAAA  HHHHxx
+1744   1106    0       0       4       4       44      744     1744    1744    1744    88      89      CPAAAA  OQBAAA  OOOOxx
+7285   1107    1       1       5       5       85      285     1285    2285    7285    170     171     FUAAAA  PQBAAA  VVVVxx
+7006   1108    0       2       6       6       6       6       1006    2006    7006    12      13      MJAAAA  QQBAAA  AAAAxx
+9236   1109    0       0       6       16      36      236     1236    4236    9236    72      73      GRAAAA  RQBAAA  HHHHxx
+5472   1110    0       0       2       12      72      472     1472    472     5472    144     145     MCAAAA  SQBAAA  OOOOxx
+7975   1111    1       3       5       15      75      975     1975    2975    7975    150     151     TUAAAA  TQBAAA  VVVVxx
+4181   1112    1       1       1       1       81      181     181     4181    4181    162     163     VEAAAA  UQBAAA  AAAAxx
+7677   1113    1       1       7       17      77      677     1677    2677    7677    154     155     HJAAAA  VQBAAA  HHHHxx
+35     1114    1       3       5       15      35      35      35      35      35      70      71      JBAAAA  WQBAAA  OOOOxx
+6813   1115    1       1       3       13      13      813     813     1813    6813    26      27      BCAAAA  XQBAAA  VVVVxx
+6618   1116    0       2       8       18      18      618     618     1618    6618    36      37      OUAAAA  YQBAAA  AAAAxx
+8069   1117    1       1       9       9       69      69      69      3069    8069    138     139     JYAAAA  ZQBAAA  HHHHxx
+3071   1118    1       3       1       11      71      71      1071    3071    3071    142     143     DOAAAA  ARBAAA  OOOOxx
+4390   1119    0       2       0       10      90      390     390     4390    4390    180     181     WMAAAA  BRBAAA  VVVVxx
+7764   1120    0       0       4       4       64      764     1764    2764    7764    128     129     QMAAAA  CRBAAA  AAAAxx
+8163   1121    1       3       3       3       63      163     163     3163    8163    126     127     ZBAAAA  DRBAAA  HHHHxx
+1961   1122    1       1       1       1       61      961     1961    1961    1961    122     123     LXAAAA  ERBAAA  OOOOxx
+1103   1123    1       3       3       3       3       103     1103    1103    1103    6       7       LQAAAA  FRBAAA  VVVVxx
+5486   1124    0       2       6       6       86      486     1486    486     5486    172     173     ADAAAA  GRBAAA  AAAAxx
+9513   1125    1       1       3       13      13      513     1513    4513    9513    26      27      XBAAAA  HRBAAA  HHHHxx
+7311   1126    1       3       1       11      11      311     1311    2311    7311    22      23      FVAAAA  IRBAAA  OOOOxx
+4144   1127    0       0       4       4       44      144     144     4144    4144    88      89      KDAAAA  JRBAAA  VVVVxx
+7901   1128    1       1       1       1       1       901     1901    2901    7901    2       3       XRAAAA  KRBAAA  AAAAxx
+4629   1129    1       1       9       9       29      629     629     4629    4629    58      59      BWAAAA  LRBAAA  HHHHxx
+6858   1130    0       2       8       18      58      858     858     1858    6858    116     117     UDAAAA  MRBAAA  OOOOxx
+125    1131    1       1       5       5       25      125     125     125     125     50      51      VEAAAA  NRBAAA  VVVVxx
+3834   1132    0       2       4       14      34      834     1834    3834    3834    68      69      MRAAAA  ORBAAA  AAAAxx
+8155   1133    1       3       5       15      55      155     155     3155    8155    110     111     RBAAAA  PRBAAA  HHHHxx
+8230   1134    0       2       0       10      30      230     230     3230    8230    60      61      OEAAAA  QRBAAA  OOOOxx
+744    1135    0       0       4       4       44      744     744     744     744     88      89      QCAAAA  RRBAAA  VVVVxx
+357    1136    1       1       7       17      57      357     357     357     357     114     115     TNAAAA  SRBAAA  AAAAxx
+2159   1137    1       3       9       19      59      159     159     2159    2159    118     119     BFAAAA  TRBAAA  HHHHxx
+8559   1138    1       3       9       19      59      559     559     3559    8559    118     119     FRAAAA  URBAAA  OOOOxx
+6866   1139    0       2       6       6       66      866     866     1866    6866    132     133     CEAAAA  VRBAAA  VVVVxx
+3863   1140    1       3       3       3       63      863     1863    3863    3863    126     127     PSAAAA  WRBAAA  AAAAxx
+4193   1141    1       1       3       13      93      193     193     4193    4193    186     187     HFAAAA  XRBAAA  HHHHxx
+3277   1142    1       1       7       17      77      277     1277    3277    3277    154     155     BWAAAA  YRBAAA  OOOOxx
+5577   1143    1       1       7       17      77      577     1577    577     5577    154     155     NGAAAA  ZRBAAA  VVVVxx
+9503   1144    1       3       3       3       3       503     1503    4503    9503    6       7       NBAAAA  ASBAAA  AAAAxx
+7642   1145    0       2       2       2       42      642     1642    2642    7642    84      85      YHAAAA  BSBAAA  HHHHxx
+6197   1146    1       1       7       17      97      197     197     1197    6197    194     195     JEAAAA  CSBAAA  OOOOxx
+8995   1147    1       3       5       15      95      995     995     3995    8995    190     191     ZHAAAA  DSBAAA  VVVVxx
+440    1148    0       0       0       0       40      440     440     440     440     80      81      YQAAAA  ESBAAA  AAAAxx
+8418   1149    0       2       8       18      18      418     418     3418    8418    36      37      ULAAAA  FSBAAA  HHHHxx
+8531   1150    1       3       1       11      31      531     531     3531    8531    62      63      DQAAAA  GSBAAA  OOOOxx
+3790   1151    0       2       0       10      90      790     1790    3790    3790    180     181     UPAAAA  HSBAAA  VVVVxx
+7610   1152    0       2       0       10      10      610     1610    2610    7610    20      21      SGAAAA  ISBAAA  AAAAxx
+1252   1153    0       0       2       12      52      252     1252    1252    1252    104     105     EWAAAA  JSBAAA  HHHHxx
+7559   1154    1       3       9       19      59      559     1559    2559    7559    118     119     TEAAAA  KSBAAA  OOOOxx
+9945   1155    1       1       5       5       45      945     1945    4945    9945    90      91      NSAAAA  LSBAAA  VVVVxx
+9023   1156    1       3       3       3       23      23      1023    4023    9023    46      47      BJAAAA  MSBAAA  AAAAxx
+3516   1157    0       0       6       16      16      516     1516    3516    3516    32      33      GFAAAA  NSBAAA  HHHHxx
+4671   1158    1       3       1       11      71      671     671     4671    4671    142     143     RXAAAA  OSBAAA  OOOOxx
+1465   1159    1       1       5       5       65      465     1465    1465    1465    130     131     JEAAAA  PSBAAA  VVVVxx
+9515   1160    1       3       5       15      15      515     1515    4515    9515    30      31      ZBAAAA  QSBAAA  AAAAxx
+3242   1161    0       2       2       2       42      242     1242    3242    3242    84      85      SUAAAA  RSBAAA  HHHHxx
+1732   1162    0       0       2       12      32      732     1732    1732    1732    64      65      QOAAAA  SSBAAA  OOOOxx
+1678   1163    0       2       8       18      78      678     1678    1678    1678    156     157     OMAAAA  TSBAAA  VVVVxx
+1464   1164    0       0       4       4       64      464     1464    1464    1464    128     129     IEAAAA  USBAAA  AAAAxx
+6546   1165    0       2       6       6       46      546     546     1546    6546    92      93      URAAAA  VSBAAA  HHHHxx
+4448   1166    0       0       8       8       48      448     448     4448    4448    96      97      CPAAAA  WSBAAA  OOOOxx
+9847   1167    1       3       7       7       47      847     1847    4847    9847    94      95      TOAAAA  XSBAAA  VVVVxx
+8264   1168    0       0       4       4       64      264     264     3264    8264    128     129     WFAAAA  YSBAAA  AAAAxx
+1620   1169    0       0       0       0       20      620     1620    1620    1620    40      41      IKAAAA  ZSBAAA  HHHHxx
+9388   1170    0       0       8       8       88      388     1388    4388    9388    176     177     CXAAAA  ATBAAA  OOOOxx
+6445   1171    1       1       5       5       45      445     445     1445    6445    90      91      XNAAAA  BTBAAA  VVVVxx
+4789   1172    1       1       9       9       89      789     789     4789    4789    178     179     FCAAAA  CTBAAA  AAAAxx
+1562   1173    0       2       2       2       62      562     1562    1562    1562    124     125     CIAAAA  DTBAAA  HHHHxx
+7305   1174    1       1       5       5       5       305     1305    2305    7305    10      11      ZUAAAA  ETBAAA  OOOOxx
+6344   1175    0       0       4       4       44      344     344     1344    6344    88      89      AKAAAA  FTBAAA  VVVVxx
+5130   1176    0       2       0       10      30      130     1130    130     5130    60      61      IPAAAA  GTBAAA  AAAAxx
+3284   1177    0       0       4       4       84      284     1284    3284    3284    168     169     IWAAAA  HTBAAA  HHHHxx
+6346   1178    0       2       6       6       46      346     346     1346    6346    92      93      CKAAAA  ITBAAA  OOOOxx
+1061   1179    1       1       1       1       61      61      1061    1061    1061    122     123     VOAAAA  JTBAAA  VVVVxx
+872    1180    0       0       2       12      72      872     872     872     872     144     145     OHAAAA  KTBAAA  AAAAxx
+123    1181    1       3       3       3       23      123     123     123     123     46      47      TEAAAA  LTBAAA  HHHHxx
+7903   1182    1       3       3       3       3       903     1903    2903    7903    6       7       ZRAAAA  MTBAAA  OOOOxx
+560    1183    0       0       0       0       60      560     560     560     560     120     121     OVAAAA  NTBAAA  VVVVxx
+4446   1184    0       2       6       6       46      446     446     4446    4446    92      93      APAAAA  OTBAAA  AAAAxx
+3909   1185    1       1       9       9       9       909     1909    3909    3909    18      19      JUAAAA  PTBAAA  HHHHxx
+669    1186    1       1       9       9       69      669     669     669     669     138     139     TZAAAA  QTBAAA  OOOOxx
+7843   1187    1       3       3       3       43      843     1843    2843    7843    86      87      RPAAAA  RTBAAA  VVVVxx
+2546   1188    0       2       6       6       46      546     546     2546    2546    92      93      YTAAAA  STBAAA  AAAAxx
+6757   1189    1       1       7       17      57      757     757     1757    6757    114     115     XZAAAA  TTBAAA  HHHHxx
+466    1190    0       2       6       6       66      466     466     466     466     132     133     YRAAAA  UTBAAA  OOOOxx
+5556   1191    0       0       6       16      56      556     1556    556     5556    112     113     SFAAAA  VTBAAA  VVVVxx
+7196   1192    0       0       6       16      96      196     1196    2196    7196    192     193     UQAAAA  WTBAAA  AAAAxx
+2947   1193    1       3       7       7       47      947     947     2947    2947    94      95      JJAAAA  XTBAAA  HHHHxx
+6493   1194    1       1       3       13      93      493     493     1493    6493    186     187     TPAAAA  YTBAAA  OOOOxx
+7203   1195    1       3       3       3       3       203     1203    2203    7203    6       7       BRAAAA  ZTBAAA  VVVVxx
+3716   1196    0       0       6       16      16      716     1716    3716    3716    32      33      YMAAAA  AUBAAA  AAAAxx
+8058   1197    0       2       8       18      58      58      58      3058    8058    116     117     YXAAAA  BUBAAA  HHHHxx
+433    1198    1       1       3       13      33      433     433     433     433     66      67      RQAAAA  CUBAAA  OOOOxx
+7649   1199    1       1       9       9       49      649     1649    2649    7649    98      99      FIAAAA  DUBAAA  VVVVxx
+6966   1200    0       2       6       6       66      966     966     1966    6966    132     133     YHAAAA  EUBAAA  AAAAxx
+553    1201    1       1       3       13      53      553     553     553     553     106     107     HVAAAA  FUBAAA  HHHHxx
+3677   1202    1       1       7       17      77      677     1677    3677    3677    154     155     LLAAAA  GUBAAA  OOOOxx
+2344   1203    0       0       4       4       44      344     344     2344    2344    88      89      EMAAAA  HUBAAA  VVVVxx
+7439   1204    1       3       9       19      39      439     1439    2439    7439    78      79      DAAAAA  IUBAAA  AAAAxx
+3910   1205    0       2       0       10      10      910     1910    3910    3910    20      21      KUAAAA  JUBAAA  HHHHxx
+3638   1206    0       2       8       18      38      638     1638    3638    3638    76      77      YJAAAA  KUBAAA  OOOOxx
+6637   1207    1       1       7       17      37      637     637     1637    6637    74      75      HVAAAA  LUBAAA  VVVVxx
+4438   1208    0       2       8       18      38      438     438     4438    4438    76      77      SOAAAA  MUBAAA  AAAAxx
+171    1209    1       3       1       11      71      171     171     171     171     142     143     PGAAAA  NUBAAA  HHHHxx
+310    1210    0       2       0       10      10      310     310     310     310     20      21      YLAAAA  OUBAAA  OOOOxx
+2714   1211    0       2       4       14      14      714     714     2714    2714    28      29      KAAAAA  PUBAAA  VVVVxx
+5199   1212    1       3       9       19      99      199     1199    199     5199    198     199     ZRAAAA  QUBAAA  AAAAxx
+8005   1213    1       1       5       5       5       5       5       3005    8005    10      11      XVAAAA  RUBAAA  HHHHxx
+3188   1214    0       0       8       8       88      188     1188    3188    3188    176     177     QSAAAA  SUBAAA  OOOOxx
+1518   1215    0       2       8       18      18      518     1518    1518    1518    36      37      KGAAAA  TUBAAA  VVVVxx
+6760   1216    0       0       0       0       60      760     760     1760    6760    120     121     AAAAAA  UUBAAA  AAAAxx
+9373   1217    1       1       3       13      73      373     1373    4373    9373    146     147     NWAAAA  VUBAAA  HHHHxx
+1938   1218    0       2       8       18      38      938     1938    1938    1938    76      77      OWAAAA  WUBAAA  OOOOxx
+2865   1219    1       1       5       5       65      865     865     2865    2865    130     131     FGAAAA  XUBAAA  VVVVxx
+3203   1220    1       3       3       3       3       203     1203    3203    3203    6       7       FTAAAA  YUBAAA  AAAAxx
+6025   1221    1       1       5       5       25      25      25      1025    6025    50      51      TXAAAA  ZUBAAA  HHHHxx
+8684   1222    0       0       4       4       84      684     684     3684    8684    168     169     AWAAAA  AVBAAA  OOOOxx
+7732   1223    0       0       2       12      32      732     1732    2732    7732    64      65      KLAAAA  BVBAAA  VVVVxx
+3218   1224    0       2       8       18      18      218     1218    3218    3218    36      37      UTAAAA  CVBAAA  AAAAxx
+525    1225    1       1       5       5       25      525     525     525     525     50      51      FUAAAA  DVBAAA  HHHHxx
+601    1226    1       1       1       1       1       601     601     601     601     2       3       DXAAAA  EVBAAA  OOOOxx
+6091   1227    1       3       1       11      91      91      91      1091    6091    182     183     HAAAAA  FVBAAA  VVVVxx
+4498   1228    0       2       8       18      98      498     498     4498    4498    196     197     ARAAAA  GVBAAA  AAAAxx
+8192   1229    0       0       2       12      92      192     192     3192    8192    184     185     CDAAAA  HVBAAA  HHHHxx
+8006   1230    0       2       6       6       6       6       6       3006    8006    12      13      YVAAAA  IVBAAA  OOOOxx
+6157   1231    1       1       7       17      57      157     157     1157    6157    114     115     VCAAAA  JVBAAA  VVVVxx
+312    1232    0       0       2       12      12      312     312     312     312     24      25      AMAAAA  KVBAAA  AAAAxx
+8652   1233    0       0       2       12      52      652     652     3652    8652    104     105     UUAAAA  LVBAAA  HHHHxx
+2787   1234    1       3       7       7       87      787     787     2787    2787    174     175     FDAAAA  MVBAAA  OOOOxx
+1782   1235    0       2       2       2       82      782     1782    1782    1782    164     165     OQAAAA  NVBAAA  VVVVxx
+23     1236    1       3       3       3       23      23      23      23      23      46      47      XAAAAA  OVBAAA  AAAAxx
+1206   1237    0       2       6       6       6       206     1206    1206    1206    12      13      KUAAAA  PVBAAA  HHHHxx
+1076   1238    0       0       6       16      76      76      1076    1076    1076    152     153     KPAAAA  QVBAAA  OOOOxx
+5379   1239    1       3       9       19      79      379     1379    379     5379    158     159     XYAAAA  RVBAAA  VVVVxx
+2047   1240    1       3       7       7       47      47      47      2047    2047    94      95      TAAAAA  SVBAAA  AAAAxx
+6262   1241    0       2       2       2       62      262     262     1262    6262    124     125     WGAAAA  TVBAAA  HHHHxx
+1840   1242    0       0       0       0       40      840     1840    1840    1840    80      81      USAAAA  UVBAAA  OOOOxx
+2106   1243    0       2       6       6       6       106     106     2106    2106    12      13      ADAAAA  VVBAAA  VVVVxx
+1307   1244    1       3       7       7       7       307     1307    1307    1307    14      15      HYAAAA  WVBAAA  AAAAxx
+735    1245    1       3       5       15      35      735     735     735     735     70      71      HCAAAA  XVBAAA  HHHHxx
+3657   1246    1       1       7       17      57      657     1657    3657    3657    114     115     RKAAAA  YVBAAA  OOOOxx
+3006   1247    0       2       6       6       6       6       1006    3006    3006    12      13      QLAAAA  ZVBAAA  VVVVxx
+1538   1248    0       2       8       18      38      538     1538    1538    1538    76      77      EHAAAA  AWBAAA  AAAAxx
+6098   1249    0       2       8       18      98      98      98      1098    6098    196     197     OAAAAA  BWBAAA  HHHHxx
+5267   1250    1       3       7       7       67      267     1267    267     5267    134     135     PUAAAA  CWBAAA  OOOOxx
+9757   1251    1       1       7       17      57      757     1757    4757    9757    114     115     HLAAAA  DWBAAA  VVVVxx
+1236   1252    0       0       6       16      36      236     1236    1236    1236    72      73      OVAAAA  EWBAAA  AAAAxx
+83     1253    1       3       3       3       83      83      83      83      83      166     167     FDAAAA  FWBAAA  HHHHxx
+9227   1254    1       3       7       7       27      227     1227    4227    9227    54      55      XQAAAA  GWBAAA  OOOOxx
+8772   1255    0       0       2       12      72      772     772     3772    8772    144     145     KZAAAA  HWBAAA  VVVVxx
+8822   1256    0       2       2       2       22      822     822     3822    8822    44      45      IBAAAA  IWBAAA  AAAAxx
+7167   1257    1       3       7       7       67      167     1167    2167    7167    134     135     RPAAAA  JWBAAA  HHHHxx
+6909   1258    1       1       9       9       9       909     909     1909    6909    18      19      TFAAAA  KWBAAA  OOOOxx
+1439   1259    1       3       9       19      39      439     1439    1439    1439    78      79      JDAAAA  LWBAAA  VVVVxx
+2370   1260    0       2       0       10      70      370     370     2370    2370    140     141     ENAAAA  MWBAAA  AAAAxx
+4577   1261    1       1       7       17      77      577     577     4577    4577    154     155     BUAAAA  NWBAAA  HHHHxx
+2575   1262    1       3       5       15      75      575     575     2575    2575    150     151     BVAAAA  OWBAAA  OOOOxx
+2795   1263    1       3       5       15      95      795     795     2795    2795    190     191     NDAAAA  PWBAAA  VVVVxx
+5520   1264    0       0       0       0       20      520     1520    520     5520    40      41      IEAAAA  QWBAAA  AAAAxx
+382    1265    0       2       2       2       82      382     382     382     382     164     165     SOAAAA  RWBAAA  HHHHxx
+6335   1266    1       3       5       15      35      335     335     1335    6335    70      71      RJAAAA  SWBAAA  OOOOxx
+8430   1267    0       2       0       10      30      430     430     3430    8430    60      61      GMAAAA  TWBAAA  VVVVxx
+4131   1268    1       3       1       11      31      131     131     4131    4131    62      63      XCAAAA  UWBAAA  AAAAxx
+9332   1269    0       0       2       12      32      332     1332    4332    9332    64      65      YUAAAA  VWBAAA  HHHHxx
+293    1270    1       1       3       13      93      293     293     293     293     186     187     HLAAAA  WWBAAA  OOOOxx
+2276   1271    0       0       6       16      76      276     276     2276    2276    152     153     OJAAAA  XWBAAA  VVVVxx
+5687   1272    1       3       7       7       87      687     1687    687     5687    174     175     TKAAAA  YWBAAA  AAAAxx
+5862   1273    0       2       2       2       62      862     1862    862     5862    124     125     MRAAAA  ZWBAAA  HHHHxx
+5073   1274    1       1       3       13      73      73      1073    73      5073    146     147     DNAAAA  AXBAAA  OOOOxx
+4170   1275    0       2       0       10      70      170     170     4170    4170    140     141     KEAAAA  BXBAAA  VVVVxx
+5039   1276    1       3       9       19      39      39      1039    39      5039    78      79      VLAAAA  CXBAAA  AAAAxx
+3294   1277    0       2       4       14      94      294     1294    3294    3294    188     189     SWAAAA  DXBAAA  HHHHxx
+6015   1278    1       3       5       15      15      15      15      1015    6015    30      31      JXAAAA  EXBAAA  OOOOxx
+9015   1279    1       3       5       15      15      15      1015    4015    9015    30      31      TIAAAA  FXBAAA  VVVVxx
+9785   1280    1       1       5       5       85      785     1785    4785    9785    170     171     JMAAAA  GXBAAA  AAAAxx
+4312   1281    0       0       2       12      12      312     312     4312    4312    24      25      WJAAAA  HXBAAA  HHHHxx
+6343   1282    1       3       3       3       43      343     343     1343    6343    86      87      ZJAAAA  IXBAAA  OOOOxx
+2161   1283    1       1       1       1       61      161     161     2161    2161    122     123     DFAAAA  JXBAAA  VVVVxx
+4490   1284    0       2       0       10      90      490     490     4490    4490    180     181     SQAAAA  KXBAAA  AAAAxx
+4454   1285    0       2       4       14      54      454     454     4454    4454    108     109     IPAAAA  LXBAAA  HHHHxx
+7647   1286    1       3       7       7       47      647     1647    2647    7647    94      95      DIAAAA  MXBAAA  OOOOxx
+1028   1287    0       0       8       8       28      28      1028    1028    1028    56      57      ONAAAA  NXBAAA  VVVVxx
+2965   1288    1       1       5       5       65      965     965     2965    2965    130     131     BKAAAA  OXBAAA  AAAAxx
+9900   1289    0       0       0       0       0       900     1900    4900    9900    0       1       UQAAAA  PXBAAA  HHHHxx
+5509   1290    1       1       9       9       9       509     1509    509     5509    18      19      XDAAAA  QXBAAA  OOOOxx
+7751   1291    1       3       1       11      51      751     1751    2751    7751    102     103     DMAAAA  RXBAAA  VVVVxx
+9594   1292    0       2       4       14      94      594     1594    4594    9594    188     189     AFAAAA  SXBAAA  AAAAxx
+7632   1293    0       0       2       12      32      632     1632    2632    7632    64      65      OHAAAA  TXBAAA  HHHHxx
+6528   1294    0       0       8       8       28      528     528     1528    6528    56      57      CRAAAA  UXBAAA  OOOOxx
+1041   1295    1       1       1       1       41      41      1041    1041    1041    82      83      BOAAAA  VXBAAA  VVVVxx
+1534   1296    0       2       4       14      34      534     1534    1534    1534    68      69      AHAAAA  WXBAAA  AAAAxx
+4229   1297    1       1       9       9       29      229     229     4229    4229    58      59      RGAAAA  XXBAAA  HHHHxx
+84     1298    0       0       4       4       84      84      84      84      84      168     169     GDAAAA  YXBAAA  OOOOxx
+2189   1299    1       1       9       9       89      189     189     2189    2189    178     179     FGAAAA  ZXBAAA  VVVVxx
+7566   1300    0       2       6       6       66      566     1566    2566    7566    132     133     AFAAAA  AYBAAA  AAAAxx
+707    1301    1       3       7       7       7       707     707     707     707     14      15      FBAAAA  BYBAAA  HHHHxx
+581    1302    1       1       1       1       81      581     581     581     581     162     163     JWAAAA  CYBAAA  OOOOxx
+6753   1303    1       1       3       13      53      753     753     1753    6753    106     107     TZAAAA  DYBAAA  VVVVxx
+8604   1304    0       0       4       4       4       604     604     3604    8604    8       9       YSAAAA  EYBAAA  AAAAxx
+373    1305    1       1       3       13      73      373     373     373     373     146     147     JOAAAA  FYBAAA  HHHHxx
+9635   1306    1       3       5       15      35      635     1635    4635    9635    70      71      PGAAAA  GYBAAA  OOOOxx
+9277   1307    1       1       7       17      77      277     1277    4277    9277    154     155     VSAAAA  HYBAAA  VVVVxx
+7117   1308    1       1       7       17      17      117     1117    2117    7117    34      35      TNAAAA  IYBAAA  AAAAxx
+8564   1309    0       0       4       4       64      564     564     3564    8564    128     129     KRAAAA  JYBAAA  HHHHxx
+1697   1310    1       1       7       17      97      697     1697    1697    1697    194     195     HNAAAA  KYBAAA  OOOOxx
+7840   1311    0       0       0       0       40      840     1840    2840    7840    80      81      OPAAAA  LYBAAA  VVVVxx
+3646   1312    0       2       6       6       46      646     1646    3646    3646    92      93      GKAAAA  MYBAAA  AAAAxx
+368    1313    0       0       8       8       68      368     368     368     368     136     137     EOAAAA  NYBAAA  HHHHxx
+4797   1314    1       1       7       17      97      797     797     4797    4797    194     195     NCAAAA  OYBAAA  OOOOxx
+5300   1315    0       0       0       0       0       300     1300    300     5300    0       1       WVAAAA  PYBAAA  VVVVxx
+7664   1316    0       0       4       4       64      664     1664    2664    7664    128     129     UIAAAA  QYBAAA  AAAAxx
+1466   1317    0       2       6       6       66      466     1466    1466    1466    132     133     KEAAAA  RYBAAA  HHHHxx
+2477   1318    1       1       7       17      77      477     477     2477    2477    154     155     HRAAAA  SYBAAA  OOOOxx
+2036   1319    0       0       6       16      36      36      36      2036    2036    72      73      IAAAAA  TYBAAA  VVVVxx
+3624   1320    0       0       4       4       24      624     1624    3624    3624    48      49      KJAAAA  UYBAAA  AAAAxx
+5099   1321    1       3       9       19      99      99      1099    99      5099    198     199     DOAAAA  VYBAAA  HHHHxx
+1308   1322    0       0       8       8       8       308     1308    1308    1308    16      17      IYAAAA  WYBAAA  OOOOxx
+3704   1323    0       0       4       4       4       704     1704    3704    3704    8       9       MMAAAA  XYBAAA  VVVVxx
+2451   1324    1       3       1       11      51      451     451     2451    2451    102     103     HQAAAA  YYBAAA  AAAAxx
+4898   1325    0       2       8       18      98      898     898     4898    4898    196     197     KGAAAA  ZYBAAA  HHHHxx
+4959   1326    1       3       9       19      59      959     959     4959    4959    118     119     TIAAAA  AZBAAA  OOOOxx
+5942   1327    0       2       2       2       42      942     1942    942     5942    84      85      OUAAAA  BZBAAA  VVVVxx
+2425   1328    1       1       5       5       25      425     425     2425    2425    50      51      HPAAAA  CZBAAA  AAAAxx
+7760   1329    0       0       0       0       60      760     1760    2760    7760    120     121     MMAAAA  DZBAAA  HHHHxx
+6294   1330    0       2       4       14      94      294     294     1294    6294    188     189     CIAAAA  EZBAAA  OOOOxx
+6785   1331    1       1       5       5       85      785     785     1785    6785    170     171     ZAAAAA  FZBAAA  VVVVxx
+3542   1332    0       2       2       2       42      542     1542    3542    3542    84      85      GGAAAA  GZBAAA  AAAAxx
+1809   1333    1       1       9       9       9       809     1809    1809    1809    18      19      PRAAAA  HZBAAA  HHHHxx
+130    1334    0       2       0       10      30      130     130     130     130     60      61      AFAAAA  IZBAAA  OOOOxx
+8672   1335    0       0       2       12      72      672     672     3672    8672    144     145     OVAAAA  JZBAAA  VVVVxx
+2125   1336    1       1       5       5       25      125     125     2125    2125    50      51      TDAAAA  KZBAAA  AAAAxx
+7683   1337    1       3       3       3       83      683     1683    2683    7683    166     167     NJAAAA  LZBAAA  HHHHxx
+7842   1338    0       2       2       2       42      842     1842    2842    7842    84      85      QPAAAA  MZBAAA  OOOOxx
+9584   1339    0       0       4       4       84      584     1584    4584    9584    168     169     QEAAAA  NZBAAA  VVVVxx
+7963   1340    1       3       3       3       63      963     1963    2963    7963    126     127     HUAAAA  OZBAAA  AAAAxx
+8581   1341    1       1       1       1       81      581     581     3581    8581    162     163     BSAAAA  PZBAAA  HHHHxx
+2135   1342    1       3       5       15      35      135     135     2135    2135    70      71      DEAAAA  QZBAAA  OOOOxx
+7352   1343    0       0       2       12      52      352     1352    2352    7352    104     105     UWAAAA  RZBAAA  VVVVxx
+5789   1344    1       1       9       9       89      789     1789    789     5789    178     179     ROAAAA  SZBAAA  AAAAxx
+8490   1345    0       2       0       10      90      490     490     3490    8490    180     181     OOAAAA  TZBAAA  HHHHxx
+2145   1346    1       1       5       5       45      145     145     2145    2145    90      91      NEAAAA  UZBAAA  OOOOxx
+7021   1347    1       1       1       1       21      21      1021    2021    7021    42      43      BKAAAA  VZBAAA  VVVVxx
+3736   1348    0       0       6       16      36      736     1736    3736    3736    72      73      SNAAAA  WZBAAA  AAAAxx
+7396   1349    0       0       6       16      96      396     1396    2396    7396    192     193     MYAAAA  XZBAAA  HHHHxx
+6334   1350    0       2       4       14      34      334     334     1334    6334    68      69      QJAAAA  YZBAAA  OOOOxx
+5461   1351    1       1       1       1       61      461     1461    461     5461    122     123     BCAAAA  ZZBAAA  VVVVxx
+5337   1352    1       1       7       17      37      337     1337    337     5337    74      75      HXAAAA  AACAAA  AAAAxx
+7440   1353    0       0       0       0       40      440     1440    2440    7440    80      81      EAAAAA  BACAAA  HHHHxx
+6879   1354    1       3       9       19      79      879     879     1879    6879    158     159     PEAAAA  CACAAA  OOOOxx
+2432   1355    0       0       2       12      32      432     432     2432    2432    64      65      OPAAAA  DACAAA  VVVVxx
+8529   1356    1       1       9       9       29      529     529     3529    8529    58      59      BQAAAA  EACAAA  AAAAxx
+7859   1357    1       3       9       19      59      859     1859    2859    7859    118     119     HQAAAA  FACAAA  HHHHxx
+15     1358    1       3       5       15      15      15      15      15      15      30      31      PAAAAA  GACAAA  OOOOxx
+7475   1359    1       3       5       15      75      475     1475    2475    7475    150     151     NBAAAA  HACAAA  VVVVxx
+717    1360    1       1       7       17      17      717     717     717     717     34      35      PBAAAA  IACAAA  AAAAxx
+250    1361    0       2       0       10      50      250     250     250     250     100     101     QJAAAA  JACAAA  HHHHxx
+4700   1362    0       0       0       0       0       700     700     4700    4700    0       1       UYAAAA  KACAAA  OOOOxx
+7510   1363    0       2       0       10      10      510     1510    2510    7510    20      21      WCAAAA  LACAAA  VVVVxx
+4562   1364    0       2       2       2       62      562     562     4562    4562    124     125     MTAAAA  MACAAA  AAAAxx
+8075   1365    1       3       5       15      75      75      75      3075    8075    150     151     PYAAAA  NACAAA  HHHHxx
+871    1366    1       3       1       11      71      871     871     871     871     142     143     NHAAAA  OACAAA  OOOOxx
+7161   1367    1       1       1       1       61      161     1161    2161    7161    122     123     LPAAAA  PACAAA  VVVVxx
+9109   1368    1       1       9       9       9       109     1109    4109    9109    18      19      JMAAAA  QACAAA  AAAAxx
+8675   1369    1       3       5       15      75      675     675     3675    8675    150     151     RVAAAA  RACAAA  HHHHxx
+1025   1370    1       1       5       5       25      25      1025    1025    1025    50      51      LNAAAA  SACAAA  OOOOxx
+4065   1371    1       1       5       5       65      65      65      4065    4065    130     131     JAAAAA  TACAAA  VVVVxx
+3511   1372    1       3       1       11      11      511     1511    3511    3511    22      23      BFAAAA  UACAAA  AAAAxx
+9840   1373    0       0       0       0       40      840     1840    4840    9840    80      81      MOAAAA  VACAAA  HHHHxx
+7495   1374    1       3       5       15      95      495     1495    2495    7495    190     191     HCAAAA  WACAAA  OOOOxx
+55     1375    1       3       5       15      55      55      55      55      55      110     111     DCAAAA  XACAAA  VVVVxx
+6151   1376    1       3       1       11      51      151     151     1151    6151    102     103     PCAAAA  YACAAA  AAAAxx
+2512   1377    0       0       2       12      12      512     512     2512    2512    24      25      QSAAAA  ZACAAA  HHHHxx
+5881   1378    1       1       1       1       81      881     1881    881     5881    162     163     FSAAAA  ABCAAA  OOOOxx
+1442   1379    0       2       2       2       42      442     1442    1442    1442    84      85      MDAAAA  BBCAAA  VVVVxx
+1270   1380    0       2       0       10      70      270     1270    1270    1270    140     141     WWAAAA  CBCAAA  AAAAxx
+959    1381    1       3       9       19      59      959     959     959     959     118     119     XKAAAA  DBCAAA  HHHHxx
+8251   1382    1       3       1       11      51      251     251     3251    8251    102     103     JFAAAA  EBCAAA  OOOOxx
+3051   1383    1       3       1       11      51      51      1051    3051    3051    102     103     JNAAAA  FBCAAA  VVVVxx
+5052   1384    0       0       2       12      52      52      1052    52      5052    104     105     IMAAAA  GBCAAA  AAAAxx
+1863   1385    1       3       3       3       63      863     1863    1863    1863    126     127     RTAAAA  HBCAAA  HHHHxx
+344    1386    0       0       4       4       44      344     344     344     344     88      89      GNAAAA  IBCAAA  OOOOxx
+3590   1387    0       2       0       10      90      590     1590    3590    3590    180     181     CIAAAA  JBCAAA  VVVVxx
+4223   1388    1       3       3       3       23      223     223     4223    4223    46      47      LGAAAA  KBCAAA  AAAAxx
+2284   1389    0       0       4       4       84      284     284     2284    2284    168     169     WJAAAA  LBCAAA  HHHHxx
+9425   1390    1       1       5       5       25      425     1425    4425    9425    50      51      NYAAAA  MBCAAA  OOOOxx
+6221   1391    1       1       1       1       21      221     221     1221    6221    42      43      HFAAAA  NBCAAA  VVVVxx
+195    1392    1       3       5       15      95      195     195     195     195     190     191     NHAAAA  OBCAAA  AAAAxx
+1517   1393    1       1       7       17      17      517     1517    1517    1517    34      35      JGAAAA  PBCAAA  HHHHxx
+3791   1394    1       3       1       11      91      791     1791    3791    3791    182     183     VPAAAA  QBCAAA  OOOOxx
+572    1395    0       0       2       12      72      572     572     572     572     144     145     AWAAAA  RBCAAA  VVVVxx
+46     1396    0       2       6       6       46      46      46      46      46      92      93      UBAAAA  SBCAAA  AAAAxx
+9451   1397    1       3       1       11      51      451     1451    4451    9451    102     103     NZAAAA  TBCAAA  HHHHxx
+3359   1398    1       3       9       19      59      359     1359    3359    3359    118     119     FZAAAA  UBCAAA  OOOOxx
+8867   1399    1       3       7       7       67      867     867     3867    8867    134     135     BDAAAA  VBCAAA  VVVVxx
+674    1400    0       2       4       14      74      674     674     674     674     148     149     YZAAAA  WBCAAA  AAAAxx
+2674   1401    0       2       4       14      74      674     674     2674    2674    148     149     WYAAAA  XBCAAA  HHHHxx
+6523   1402    1       3       3       3       23      523     523     1523    6523    46      47      XQAAAA  YBCAAA  OOOOxx
+6210   1403    0       2       0       10      10      210     210     1210    6210    20      21      WEAAAA  ZBCAAA  VVVVxx
+7564   1404    0       0       4       4       64      564     1564    2564    7564    128     129     YEAAAA  ACCAAA  AAAAxx
+4776   1405    0       0       6       16      76      776     776     4776    4776    152     153     SBAAAA  BCCAAA  HHHHxx
+2993   1406    1       1       3       13      93      993     993     2993    2993    186     187     DLAAAA  CCCAAA  OOOOxx
+2969   1407    1       1       9       9       69      969     969     2969    2969    138     139     FKAAAA  DCCAAA  VVVVxx
+1762   1408    0       2       2       2       62      762     1762    1762    1762    124     125     UPAAAA  ECCAAA  AAAAxx
+685    1409    1       1       5       5       85      685     685     685     685     170     171     JAAAAA  FCCAAA  HHHHxx
+5312   1410    0       0       2       12      12      312     1312    312     5312    24      25      IWAAAA  GCCAAA  OOOOxx
+3264   1411    0       0       4       4       64      264     1264    3264    3264    128     129     OVAAAA  HCCAAA  VVVVxx
+7008   1412    0       0       8       8       8       8       1008    2008    7008    16      17      OJAAAA  ICCAAA  AAAAxx
+5167   1413    1       3       7       7       67      167     1167    167     5167    134     135     TQAAAA  JCCAAA  HHHHxx
+3060   1414    0       0       0       0       60      60      1060    3060    3060    120     121     SNAAAA  KCCAAA  OOOOxx
+1752   1415    0       0       2       12      52      752     1752    1752    1752    104     105     KPAAAA  LCCAAA  VVVVxx
+1016   1416    0       0       6       16      16      16      1016    1016    1016    32      33      CNAAAA  MCCAAA  AAAAxx
+7365   1417    1       1       5       5       65      365     1365    2365    7365    130     131     HXAAAA  NCCAAA  HHHHxx
+4358   1418    0       2       8       18      58      358     358     4358    4358    116     117     QLAAAA  OCCAAA  OOOOxx
+2819   1419    1       3       9       19      19      819     819     2819    2819    38      39      LEAAAA  PCCAAA  VVVVxx
+6727   1420    1       3       7       7       27      727     727     1727    6727    54      55      TYAAAA  QCCAAA  AAAAxx
+1459   1421    1       3       9       19      59      459     1459    1459    1459    118     119     DEAAAA  RCCAAA  HHHHxx
+1708   1422    0       0       8       8       8       708     1708    1708    1708    16      17      SNAAAA  SCCAAA  OOOOxx
+471    1423    1       3       1       11      71      471     471     471     471     142     143     DSAAAA  TCCAAA  VVVVxx
+387    1424    1       3       7       7       87      387     387     387     387     174     175     XOAAAA  UCCAAA  AAAAxx
+1166   1425    0       2       6       6       66      166     1166    1166    1166    132     133     WSAAAA  VCCAAA  HHHHxx
+2400   1426    0       0       0       0       0       400     400     2400    2400    0       1       IOAAAA  WCCAAA  OOOOxx
+3584   1427    0       0       4       4       84      584     1584    3584    3584    168     169     WHAAAA  XCCAAA  VVVVxx
+6423   1428    1       3       3       3       23      423     423     1423    6423    46      47      BNAAAA  YCCAAA  AAAAxx
+9520   1429    0       0       0       0       20      520     1520    4520    9520    40      41      ECAAAA  ZCCAAA  HHHHxx
+8080   1430    0       0       0       0       80      80      80      3080    8080    160     161     UYAAAA  ADCAAA  OOOOxx
+5709   1431    1       1       9       9       9       709     1709    709     5709    18      19      PLAAAA  BDCAAA  VVVVxx
+1131   1432    1       3       1       11      31      131     1131    1131    1131    62      63      NRAAAA  CDCAAA  AAAAxx
+8562   1433    0       2       2       2       62      562     562     3562    8562    124     125     IRAAAA  DDCAAA  HHHHxx
+5766   1434    0       2       6       6       66      766     1766    766     5766    132     133     UNAAAA  EDCAAA  OOOOxx
+245    1435    1       1       5       5       45      245     245     245     245     90      91      LJAAAA  FDCAAA  VVVVxx
+9869   1436    1       1       9       9       69      869     1869    4869    9869    138     139     PPAAAA  GDCAAA  AAAAxx
+3533   1437    1       1       3       13      33      533     1533    3533    3533    66      67      XFAAAA  HDCAAA  HHHHxx
+5109   1438    1       1       9       9       9       109     1109    109     5109    18      19      NOAAAA  IDCAAA  OOOOxx
+977    1439    1       1       7       17      77      977     977     977     977     154     155     PLAAAA  JDCAAA  VVVVxx
+1651   1440    1       3       1       11      51      651     1651    1651    1651    102     103     NLAAAA  KDCAAA  AAAAxx
+1357   1441    1       1       7       17      57      357     1357    1357    1357    114     115     FAAAAA  LDCAAA  HHHHxx
+9087   1442    1       3       7       7       87      87      1087    4087    9087    174     175     NLAAAA  MDCAAA  OOOOxx
+3399   1443    1       3       9       19      99      399     1399    3399    3399    198     199     TAAAAA  NDCAAA  VVVVxx
+7543   1444    1       3       3       3       43      543     1543    2543    7543    86      87      DEAAAA  ODCAAA  AAAAxx
+2469   1445    1       1       9       9       69      469     469     2469    2469    138     139     ZQAAAA  PDCAAA  HHHHxx
+8305   1446    1       1       5       5       5       305     305     3305    8305    10      11      LHAAAA  QDCAAA  OOOOxx
+3265   1447    1       1       5       5       65      265     1265    3265    3265    130     131     PVAAAA  RDCAAA  VVVVxx
+9977   1448    1       1       7       17      77      977     1977    4977    9977    154     155     TTAAAA  SDCAAA  AAAAxx
+3961   1449    1       1       1       1       61      961     1961    3961    3961    122     123     JWAAAA  TDCAAA  HHHHxx
+4952   1450    0       0       2       12      52      952     952     4952    4952    104     105     MIAAAA  UDCAAA  OOOOxx
+5173   1451    1       1       3       13      73      173     1173    173     5173    146     147     ZQAAAA  VDCAAA  VVVVxx
+860    1452    0       0       0       0       60      860     860     860     860     120     121     CHAAAA  WDCAAA  AAAAxx
+4523   1453    1       3       3       3       23      523     523     4523    4523    46      47      ZRAAAA  XDCAAA  HHHHxx
+2361   1454    1       1       1       1       61      361     361     2361    2361    122     123     VMAAAA  YDCAAA  OOOOxx
+7877   1455    1       1       7       17      77      877     1877    2877    7877    154     155     ZQAAAA  ZDCAAA  VVVVxx
+3422   1456    0       2       2       2       22      422     1422    3422    3422    44      45      QBAAAA  AECAAA  AAAAxx
+5781   1457    1       1       1       1       81      781     1781    781     5781    162     163     JOAAAA  BECAAA  HHHHxx
+4752   1458    0       0       2       12      52      752     752     4752    4752    104     105     UAAAAA  CECAAA  OOOOxx
+1786   1459    0       2       6       6       86      786     1786    1786    1786    172     173     SQAAAA  DECAAA  VVVVxx
+1892   1460    0       0       2       12      92      892     1892    1892    1892    184     185     UUAAAA  EECAAA  AAAAxx
+6389   1461    1       1       9       9       89      389     389     1389    6389    178     179     TLAAAA  FECAAA  HHHHxx
+8644   1462    0       0       4       4       44      644     644     3644    8644    88      89      MUAAAA  GECAAA  OOOOxx
+9056   1463    0       0       6       16      56      56      1056    4056    9056    112     113     IKAAAA  HECAAA  VVVVxx
+1423   1464    1       3       3       3       23      423     1423    1423    1423    46      47      TCAAAA  IECAAA  AAAAxx
+4901   1465    1       1       1       1       1       901     901     4901    4901    2       3       NGAAAA  JECAAA  HHHHxx
+3859   1466    1       3       9       19      59      859     1859    3859    3859    118     119     LSAAAA  KECAAA  OOOOxx
+2324   1467    0       0       4       4       24      324     324     2324    2324    48      49      KLAAAA  LECAAA  VVVVxx
+8101   1468    1       1       1       1       1       101     101     3101    8101    2       3       PZAAAA  MECAAA  AAAAxx
+8016   1469    0       0       6       16      16      16      16      3016    8016    32      33      IWAAAA  NECAAA  HHHHxx
+5826   1470    0       2       6       6       26      826     1826    826     5826    52      53      CQAAAA  OECAAA  OOOOxx
+8266   1471    0       2       6       6       66      266     266     3266    8266    132     133     YFAAAA  PECAAA  VVVVxx
+7558   1472    0       2       8       18      58      558     1558    2558    7558    116     117     SEAAAA  QECAAA  AAAAxx
+6976   1473    0       0       6       16      76      976     976     1976    6976    152     153     IIAAAA  RECAAA  HHHHxx
+222    1474    0       2       2       2       22      222     222     222     222     44      45      OIAAAA  SECAAA  OOOOxx
+1624   1475    0       0       4       4       24      624     1624    1624    1624    48      49      MKAAAA  TECAAA  VVVVxx
+1250   1476    0       2       0       10      50      250     1250    1250    1250    100     101     CWAAAA  UECAAA  AAAAxx
+1621   1477    1       1       1       1       21      621     1621    1621    1621    42      43      JKAAAA  VECAAA  HHHHxx
+2350   1478    0       2       0       10      50      350     350     2350    2350    100     101     KMAAAA  WECAAA  OOOOxx
+5239   1479    1       3       9       19      39      239     1239    239     5239    78      79      NTAAAA  XECAAA  VVVVxx
+6681   1480    1       1       1       1       81      681     681     1681    6681    162     163     ZWAAAA  YECAAA  AAAAxx
+4983   1481    1       3       3       3       83      983     983     4983    4983    166     167     RJAAAA  ZECAAA  HHHHxx
+7149   1482    1       1       9       9       49      149     1149    2149    7149    98      99      ZOAAAA  AFCAAA  OOOOxx
+3502   1483    0       2       2       2       2       502     1502    3502    3502    4       5       SEAAAA  BFCAAA  VVVVxx
+3133   1484    1       1       3       13      33      133     1133    3133    3133    66      67      NQAAAA  CFCAAA  AAAAxx
+8342   1485    0       2       2       2       42      342     342     3342    8342    84      85      WIAAAA  DFCAAA  HHHHxx
+3041   1486    1       1       1       1       41      41      1041    3041    3041    82      83      ZMAAAA  EFCAAA  OOOOxx
+5383   1487    1       3       3       3       83      383     1383    383     5383    166     167     BZAAAA  FFCAAA  VVVVxx
+3916   1488    0       0       6       16      16      916     1916    3916    3916    32      33      QUAAAA  GFCAAA  AAAAxx
+1438   1489    0       2       8       18      38      438     1438    1438    1438    76      77      IDAAAA  HFCAAA  HHHHxx
+9408   1490    0       0       8       8       8       408     1408    4408    9408    16      17      WXAAAA  IFCAAA  OOOOxx
+5783   1491    1       3       3       3       83      783     1783    783     5783    166     167     LOAAAA  JFCAAA  VVVVxx
+683    1492    1       3       3       3       83      683     683     683     683     166     167     HAAAAA  KFCAAA  AAAAxx
+9381   1493    1       1       1       1       81      381     1381    4381    9381    162     163     VWAAAA  LFCAAA  HHHHxx
+5676   1494    0       0       6       16      76      676     1676    676     5676    152     153     IKAAAA  MFCAAA  OOOOxx
+3224   1495    0       0       4       4       24      224     1224    3224    3224    48      49      AUAAAA  NFCAAA  VVVVxx
+8332   1496    0       0       2       12      32      332     332     3332    8332    64      65      MIAAAA  OFCAAA  AAAAxx
+3372   1497    0       0       2       12      72      372     1372    3372    3372    144     145     SZAAAA  PFCAAA  HHHHxx
+7436   1498    0       0       6       16      36      436     1436    2436    7436    72      73      AAAAAA  QFCAAA  OOOOxx
+5010   1499    0       2       0       10      10      10      1010    10      5010    20      21      SKAAAA  RFCAAA  VVVVxx
+7256   1500    0       0       6       16      56      256     1256    2256    7256    112     113     CTAAAA  SFCAAA  AAAAxx
+961    1501    1       1       1       1       61      961     961     961     961     122     123     ZKAAAA  TFCAAA  HHHHxx
+4182   1502    0       2       2       2       82      182     182     4182    4182    164     165     WEAAAA  UFCAAA  OOOOxx
+639    1503    1       3       9       19      39      639     639     639     639     78      79      PYAAAA  VFCAAA  VVVVxx
+8836   1504    0       0       6       16      36      836     836     3836    8836    72      73      WBAAAA  WFCAAA  AAAAxx
+8705   1505    1       1       5       5       5       705     705     3705    8705    10      11      VWAAAA  XFCAAA  HHHHxx
+32     1506    0       0       2       12      32      32      32      32      32      64      65      GBAAAA  YFCAAA  OOOOxx
+7913   1507    1       1       3       13      13      913     1913    2913    7913    26      27      JSAAAA  ZFCAAA  VVVVxx
+229    1508    1       1       9       9       29      229     229     229     229     58      59      VIAAAA  AGCAAA  AAAAxx
+2393   1509    1       1       3       13      93      393     393     2393    2393    186     187     BOAAAA  BGCAAA  HHHHxx
+2815   1510    1       3       5       15      15      815     815     2815    2815    30      31      HEAAAA  CGCAAA  OOOOxx
+4858   1511    0       2       8       18      58      858     858     4858    4858    116     117     WEAAAA  DGCAAA  VVVVxx
+6283   1512    1       3       3       3       83      283     283     1283    6283    166     167     RHAAAA  EGCAAA  AAAAxx
+4147   1513    1       3       7       7       47      147     147     4147    4147    94      95      NDAAAA  FGCAAA  HHHHxx
+6801   1514    1       1       1       1       1       801     801     1801    6801    2       3       PBAAAA  GGCAAA  OOOOxx
+1011   1515    1       3       1       11      11      11      1011    1011    1011    22      23      XMAAAA  HGCAAA  VVVVxx
+2527   1516    1       3       7       7       27      527     527     2527    2527    54      55      FTAAAA  IGCAAA  AAAAxx
+381    1517    1       1       1       1       81      381     381     381     381     162     163     ROAAAA  JGCAAA  HHHHxx
+3366   1518    0       2       6       6       66      366     1366    3366    3366    132     133     MZAAAA  KGCAAA  OOOOxx
+9636   1519    0       0       6       16      36      636     1636    4636    9636    72      73      QGAAAA  LGCAAA  VVVVxx
+2239   1520    1       3       9       19      39      239     239     2239    2239    78      79      DIAAAA  MGCAAA  AAAAxx
+5911   1521    1       3       1       11      11      911     1911    911     5911    22      23      JTAAAA  NGCAAA  HHHHxx
+449    1522    1       1       9       9       49      449     449     449     449     98      99      HRAAAA  OGCAAA  OOOOxx
+5118   1523    0       2       8       18      18      118     1118    118     5118    36      37      WOAAAA  PGCAAA  VVVVxx
+7684   1524    0       0       4       4       84      684     1684    2684    7684    168     169     OJAAAA  QGCAAA  AAAAxx
+804    1525    0       0       4       4       4       804     804     804     804     8       9       YEAAAA  RGCAAA  HHHHxx
+8378   1526    0       2       8       18      78      378     378     3378    8378    156     157     GKAAAA  SGCAAA  OOOOxx
+9855   1527    1       3       5       15      55      855     1855    4855    9855    110     111     BPAAAA  TGCAAA  VVVVxx
+1995   1528    1       3       5       15      95      995     1995    1995    1995    190     191     TYAAAA  UGCAAA  AAAAxx
+1979   1529    1       3       9       19      79      979     1979    1979    1979    158     159     DYAAAA  VGCAAA  HHHHxx
+4510   1530    0       2       0       10      10      510     510     4510    4510    20      21      MRAAAA  WGCAAA  OOOOxx
+3792   1531    0       0       2       12      92      792     1792    3792    3792    184     185     WPAAAA  XGCAAA  VVVVxx
+3541   1532    1       1       1       1       41      541     1541    3541    3541    82      83      FGAAAA  YGCAAA  AAAAxx
+8847   1533    1       3       7       7       47      847     847     3847    8847    94      95      HCAAAA  ZGCAAA  HHHHxx
+1336   1534    0       0       6       16      36      336     1336    1336    1336    72      73      KZAAAA  AHCAAA  OOOOxx
+6780   1535    0       0       0       0       80      780     780     1780    6780    160     161     UAAAAA  BHCAAA  VVVVxx
+8711   1536    1       3       1       11      11      711     711     3711    8711    22      23      BXAAAA  CHCAAA  AAAAxx
+7839   1537    1       3       9       19      39      839     1839    2839    7839    78      79      NPAAAA  DHCAAA  HHHHxx
+677    1538    1       1       7       17      77      677     677     677     677     154     155     BAAAAA  EHCAAA  OOOOxx
+1574   1539    0       2       4       14      74      574     1574    1574    1574    148     149     OIAAAA  FHCAAA  VVVVxx
+2905   1540    1       1       5       5       5       905     905     2905    2905    10      11      THAAAA  GHCAAA  AAAAxx
+1879   1541    1       3       9       19      79      879     1879    1879    1879    158     159     HUAAAA  HHCAAA  HHHHxx
+7820   1542    0       0       0       0       20      820     1820    2820    7820    40      41      UOAAAA  IHCAAA  OOOOxx
+4308   1543    0       0       8       8       8       308     308     4308    4308    16      17      SJAAAA  JHCAAA  VVVVxx
+4474   1544    0       2       4       14      74      474     474     4474    4474    148     149     CQAAAA  KHCAAA  AAAAxx
+6985   1545    1       1       5       5       85      985     985     1985    6985    170     171     RIAAAA  LHCAAA  HHHHxx
+6929   1546    1       1       9       9       29      929     929     1929    6929    58      59      NGAAAA  MHCAAA  OOOOxx
+777    1547    1       1       7       17      77      777     777     777     777     154     155     XDAAAA  NHCAAA  VVVVxx
+8271   1548    1       3       1       11      71      271     271     3271    8271    142     143     DGAAAA  OHCAAA  AAAAxx
+2389   1549    1       1       9       9       89      389     389     2389    2389    178     179     XNAAAA  PHCAAA  HHHHxx
+946    1550    0       2       6       6       46      946     946     946     946     92      93      KKAAAA  QHCAAA  OOOOxx
+9682   1551    0       2       2       2       82      682     1682    4682    9682    164     165     KIAAAA  RHCAAA  VVVVxx
+8722   1552    0       2       2       2       22      722     722     3722    8722    44      45      MXAAAA  SHCAAA  AAAAxx
+470    1553    0       2       0       10      70      470     470     470     470     140     141     CSAAAA  THCAAA  HHHHxx
+7425   1554    1       1       5       5       25      425     1425    2425    7425    50      51      PZAAAA  UHCAAA  OOOOxx
+2372   1555    0       0       2       12      72      372     372     2372    2372    144     145     GNAAAA  VHCAAA  VVVVxx
+508    1556    0       0       8       8       8       508     508     508     508     16      17      OTAAAA  WHCAAA  AAAAxx
+163    1557    1       3       3       3       63      163     163     163     163     126     127     HGAAAA  XHCAAA  HHHHxx
+6579   1558    1       3       9       19      79      579     579     1579    6579    158     159     BTAAAA  YHCAAA  OOOOxx
+2355   1559    1       3       5       15      55      355     355     2355    2355    110     111     PMAAAA  ZHCAAA  VVVVxx
+70     1560    0       2       0       10      70      70      70      70      70      140     141     SCAAAA  AICAAA  AAAAxx
+651    1561    1       3       1       11      51      651     651     651     651     102     103     BZAAAA  BICAAA  HHHHxx
+4436   1562    0       0       6       16      36      436     436     4436    4436    72      73      QOAAAA  CICAAA  OOOOxx
+4240   1563    0       0       0       0       40      240     240     4240    4240    80      81      CHAAAA  DICAAA  VVVVxx
+2722   1564    0       2       2       2       22      722     722     2722    2722    44      45      SAAAAA  EICAAA  AAAAxx
+8937   1565    1       1       7       17      37      937     937     3937    8937    74      75      TFAAAA  FICAAA  HHHHxx
+8364   1566    0       0       4       4       64      364     364     3364    8364    128     129     SJAAAA  GICAAA  OOOOxx
+8317   1567    1       1       7       17      17      317     317     3317    8317    34      35      XHAAAA  HICAAA  VVVVxx
+8872   1568    0       0       2       12      72      872     872     3872    8872    144     145     GDAAAA  IICAAA  AAAAxx
+5512   1569    0       0       2       12      12      512     1512    512     5512    24      25      AEAAAA  JICAAA  HHHHxx
+6651   1570    1       3       1       11      51      651     651     1651    6651    102     103     VVAAAA  KICAAA  OOOOxx
+5976   1571    0       0       6       16      76      976     1976    976     5976    152     153     WVAAAA  LICAAA  VVVVxx
+3301   1572    1       1       1       1       1       301     1301    3301    3301    2       3       ZWAAAA  MICAAA  AAAAxx
+6784   1573    0       0       4       4       84      784     784     1784    6784    168     169     YAAAAA  NICAAA  HHHHxx
+573    1574    1       1       3       13      73      573     573     573     573     146     147     BWAAAA  OICAAA  OOOOxx
+3015   1575    1       3       5       15      15      15      1015    3015    3015    30      31      ZLAAAA  PICAAA  VVVVxx
+8245   1576    1       1       5       5       45      245     245     3245    8245    90      91      DFAAAA  QICAAA  AAAAxx
+5251   1577    1       3       1       11      51      251     1251    251     5251    102     103     ZTAAAA  RICAAA  HHHHxx
+2281   1578    1       1       1       1       81      281     281     2281    2281    162     163     TJAAAA  SICAAA  OOOOxx
+518    1579    0       2       8       18      18      518     518     518     518     36      37      YTAAAA  TICAAA  VVVVxx
+9839   1580    1       3       9       19      39      839     1839    4839    9839    78      79      LOAAAA  UICAAA  AAAAxx
+4526   1581    0       2       6       6       26      526     526     4526    4526    52      53      CSAAAA  VICAAA  HHHHxx
+1261   1582    1       1       1       1       61      261     1261    1261    1261    122     123     NWAAAA  WICAAA  OOOOxx
+4259   1583    1       3       9       19      59      259     259     4259    4259    118     119     VHAAAA  XICAAA  VVVVxx
+9098   1584    0       2       8       18      98      98      1098    4098    9098    196     197     YLAAAA  YICAAA  AAAAxx
+6037   1585    1       1       7       17      37      37      37      1037    6037    74      75      FYAAAA  ZICAAA  HHHHxx
+4284   1586    0       0       4       4       84      284     284     4284    4284    168     169     UIAAAA  AJCAAA  OOOOxx
+3267   1587    1       3       7       7       67      267     1267    3267    3267    134     135     RVAAAA  BJCAAA  VVVVxx
+5908   1588    0       0       8       8       8       908     1908    908     5908    16      17      GTAAAA  CJCAAA  AAAAxx
+1549   1589    1       1       9       9       49      549     1549    1549    1549    98      99      PHAAAA  DJCAAA  HHHHxx
+8736   1590    0       0       6       16      36      736     736     3736    8736    72      73      AYAAAA  EJCAAA  OOOOxx
+2008   1591    0       0       8       8       8       8       8       2008    2008    16      17      GZAAAA  FJCAAA  VVVVxx
+548    1592    0       0       8       8       48      548     548     548     548     96      97      CVAAAA  GJCAAA  AAAAxx
+8846   1593    0       2       6       6       46      846     846     3846    8846    92      93      GCAAAA  HJCAAA  HHHHxx
+8374   1594    0       2       4       14      74      374     374     3374    8374    148     149     CKAAAA  IJCAAA  OOOOxx
+7986   1595    0       2       6       6       86      986     1986    2986    7986    172     173     EVAAAA  JJCAAA  VVVVxx
+6819   1596    1       3       9       19      19      819     819     1819    6819    38      39      HCAAAA  KJCAAA  AAAAxx
+4418   1597    0       2       8       18      18      418     418     4418    4418    36      37      YNAAAA  LJCAAA  HHHHxx
+833    1598    1       1       3       13      33      833     833     833     833     66      67      BGAAAA  MJCAAA  OOOOxx
+4416   1599    0       0       6       16      16      416     416     4416    4416    32      33      WNAAAA  NJCAAA  VVVVxx
+4902   1600    0       2       2       2       2       902     902     4902    4902    4       5       OGAAAA  OJCAAA  AAAAxx
+6828   1601    0       0       8       8       28      828     828     1828    6828    56      57      QCAAAA  PJCAAA  HHHHxx
+1118   1602    0       2       8       18      18      118     1118    1118    1118    36      37      ARAAAA  QJCAAA  OOOOxx
+9993   1603    1       1       3       13      93      993     1993    4993    9993    186     187     JUAAAA  RJCAAA  VVVVxx
+1430   1604    0       2       0       10      30      430     1430    1430    1430    60      61      ADAAAA  SJCAAA  AAAAxx
+5670   1605    0       2       0       10      70      670     1670    670     5670    140     141     CKAAAA  TJCAAA  HHHHxx
+5424   1606    0       0       4       4       24      424     1424    424     5424    48      49      QAAAAA  UJCAAA  OOOOxx
+5561   1607    1       1       1       1       61      561     1561    561     5561    122     123     XFAAAA  VJCAAA  VVVVxx
+2027   1608    1       3       7       7       27      27      27      2027    2027    54      55      ZZAAAA  WJCAAA  AAAAxx
+6924   1609    0       0       4       4       24      924     924     1924    6924    48      49      IGAAAA  XJCAAA  HHHHxx
+5946   1610    0       2       6       6       46      946     1946    946     5946    92      93      SUAAAA  YJCAAA  OOOOxx
+4294   1611    0       2       4       14      94      294     294     4294    4294    188     189     EJAAAA  ZJCAAA  VVVVxx
+2936   1612    0       0       6       16      36      936     936     2936    2936    72      73      YIAAAA  AKCAAA  AAAAxx
+3855   1613    1       3       5       15      55      855     1855    3855    3855    110     111     HSAAAA  BKCAAA  HHHHxx
+455    1614    1       3       5       15      55      455     455     455     455     110     111     NRAAAA  CKCAAA  OOOOxx
+2918   1615    0       2       8       18      18      918     918     2918    2918    36      37      GIAAAA  DKCAAA  VVVVxx
+448    1616    0       0       8       8       48      448     448     448     448     96      97      GRAAAA  EKCAAA  AAAAxx
+2149   1617    1       1       9       9       49      149     149     2149    2149    98      99      REAAAA  FKCAAA  HHHHxx
+8890   1618    0       2       0       10      90      890     890     3890    8890    180     181     YDAAAA  GKCAAA  OOOOxx
+8919   1619    1       3       9       19      19      919     919     3919    8919    38      39      BFAAAA  HKCAAA  VVVVxx
+4957   1620    1       1       7       17      57      957     957     4957    4957    114     115     RIAAAA  IKCAAA  AAAAxx
+4      1621    0       0       4       4       4       4       4       4       4       8       9       EAAAAA  JKCAAA  HHHHxx
+4837   1622    1       1       7       17      37      837     837     4837    4837    74      75      BEAAAA  KKCAAA  OOOOxx
+3976   1623    0       0       6       16      76      976     1976    3976    3976    152     153     YWAAAA  LKCAAA  VVVVxx
+9459   1624    1       3       9       19      59      459     1459    4459    9459    118     119     VZAAAA  MKCAAA  AAAAxx
+7097   1625    1       1       7       17      97      97      1097    2097    7097    194     195     ZMAAAA  NKCAAA  HHHHxx
+9226   1626    0       2       6       6       26      226     1226    4226    9226    52      53      WQAAAA  OKCAAA  OOOOxx
+5803   1627    1       3       3       3       3       803     1803    803     5803    6       7       FPAAAA  PKCAAA  VVVVxx
+21     1628    1       1       1       1       21      21      21      21      21      42      43      VAAAAA  QKCAAA  AAAAxx
+5275   1629    1       3       5       15      75      275     1275    275     5275    150     151     XUAAAA  RKCAAA  HHHHxx
+3488   1630    0       0       8       8       88      488     1488    3488    3488    176     177     EEAAAA  SKCAAA  OOOOxx
+1595   1631    1       3       5       15      95      595     1595    1595    1595    190     191     JJAAAA  TKCAAA  VVVVxx
+5212   1632    0       0       2       12      12      212     1212    212     5212    24      25      MSAAAA  UKCAAA  AAAAxx
+6574   1633    0       2       4       14      74      574     574     1574    6574    148     149     WSAAAA  VKCAAA  HHHHxx
+7524   1634    0       0       4       4       24      524     1524    2524    7524    48      49      KDAAAA  WKCAAA  OOOOxx
+6100   1635    0       0       0       0       0       100     100     1100    6100    0       1       QAAAAA  XKCAAA  VVVVxx
+1198   1636    0       2       8       18      98      198     1198    1198    1198    196     197     CUAAAA  YKCAAA  AAAAxx
+7345   1637    1       1       5       5       45      345     1345    2345    7345    90      91      NWAAAA  ZKCAAA  HHHHxx
+5020   1638    0       0       0       0       20      20      1020    20      5020    40      41      CLAAAA  ALCAAA  OOOOxx
+6925   1639    1       1       5       5       25      925     925     1925    6925    50      51      JGAAAA  BLCAAA  VVVVxx
+8915   1640    1       3       5       15      15      915     915     3915    8915    30      31      XEAAAA  CLCAAA  AAAAxx
+3088   1641    0       0       8       8       88      88      1088    3088    3088    176     177     UOAAAA  DLCAAA  HHHHxx
+4828   1642    0       0       8       8       28      828     828     4828    4828    56      57      SDAAAA  ELCAAA  OOOOxx
+7276   1643    0       0       6       16      76      276     1276    2276    7276    152     153     WTAAAA  FLCAAA  VVVVxx
+299    1644    1       3       9       19      99      299     299     299     299     198     199     NLAAAA  GLCAAA  AAAAxx
+76     1645    0       0       6       16      76      76      76      76      76      152     153     YCAAAA  HLCAAA  HHHHxx
+8458   1646    0       2       8       18      58      458     458     3458    8458    116     117     INAAAA  ILCAAA  OOOOxx
+7207   1647    1       3       7       7       7       207     1207    2207    7207    14      15      FRAAAA  JLCAAA  VVVVxx
+5585   1648    1       1       5       5       85      585     1585    585     5585    170     171     VGAAAA  KLCAAA  AAAAxx
+3234   1649    0       2       4       14      34      234     1234    3234    3234    68      69      KUAAAA  LLCAAA  HHHHxx
+8001   1650    1       1       1       1       1       1       1       3001    8001    2       3       TVAAAA  MLCAAA  OOOOxx
+1319   1651    1       3       9       19      19      319     1319    1319    1319    38      39      TYAAAA  NLCAAA  VVVVxx
+6342   1652    0       2       2       2       42      342     342     1342    6342    84      85      YJAAAA  OLCAAA  AAAAxx
+9199   1653    1       3       9       19      99      199     1199    4199    9199    198     199     VPAAAA  PLCAAA  HHHHxx
+5696   1654    0       0       6       16      96      696     1696    696     5696    192     193     CLAAAA  QLCAAA  OOOOxx
+2562   1655    0       2       2       2       62      562     562     2562    2562    124     125     OUAAAA  RLCAAA  VVVVxx
+4226   1656    0       2       6       6       26      226     226     4226    4226    52      53      OGAAAA  SLCAAA  AAAAxx
+1184   1657    0       0       4       4       84      184     1184    1184    1184    168     169     OTAAAA  TLCAAA  HHHHxx
+5807   1658    1       3       7       7       7       807     1807    807     5807    14      15      JPAAAA  ULCAAA  OOOOxx
+1890   1659    0       2       0       10      90      890     1890    1890    1890    180     181     SUAAAA  VLCAAA  VVVVxx
+451    1660    1       3       1       11      51      451     451     451     451     102     103     JRAAAA  WLCAAA  AAAAxx
+1049   1661    1       1       9       9       49      49      1049    1049    1049    98      99      JOAAAA  XLCAAA  HHHHxx
+5272   1662    0       0       2       12      72      272     1272    272     5272    144     145     UUAAAA  YLCAAA  OOOOxx
+4588   1663    0       0       8       8       88      588     588     4588    4588    176     177     MUAAAA  ZLCAAA  VVVVxx
+5213   1664    1       1       3       13      13      213     1213    213     5213    26      27      NSAAAA  AMCAAA  AAAAxx
+9543   1665    1       3       3       3       43      543     1543    4543    9543    86      87      BDAAAA  BMCAAA  HHHHxx
+6318   1666    0       2       8       18      18      318     318     1318    6318    36      37      AJAAAA  CMCAAA  OOOOxx
+7992   1667    0       0       2       12      92      992     1992    2992    7992    184     185     KVAAAA  DMCAAA  VVVVxx
+4619   1668    1       3       9       19      19      619     619     4619    4619    38      39      RVAAAA  EMCAAA  AAAAxx
+7189   1669    1       1       9       9       89      189     1189    2189    7189    178     179     NQAAAA  FMCAAA  HHHHxx
+2178   1670    0       2       8       18      78      178     178     2178    2178    156     157     UFAAAA  GMCAAA  OOOOxx
+4928   1671    0       0       8       8       28      928     928     4928    4928    56      57      OHAAAA  HMCAAA  VVVVxx
+3966   1672    0       2       6       6       66      966     1966    3966    3966    132     133     OWAAAA  IMCAAA  AAAAxx
+9790   1673    0       2       0       10      90      790     1790    4790    9790    180     181     OMAAAA  JMCAAA  HHHHxx
+9150   1674    0       2       0       10      50      150     1150    4150    9150    100     101     YNAAAA  KMCAAA  OOOOxx
+313    1675    1       1       3       13      13      313     313     313     313     26      27      BMAAAA  LMCAAA  VVVVxx
+1614   1676    0       2       4       14      14      614     1614    1614    1614    28      29      CKAAAA  MMCAAA  AAAAxx
+1581   1677    1       1       1       1       81      581     1581    1581    1581    162     163     VIAAAA  NMCAAA  HHHHxx
+3674   1678    0       2       4       14      74      674     1674    3674    3674    148     149     ILAAAA  OMCAAA  OOOOxx
+3444   1679    0       0       4       4       44      444     1444    3444    3444    88      89      MCAAAA  PMCAAA  VVVVxx
+1050   1680    0       2       0       10      50      50      1050    1050    1050    100     101     KOAAAA  QMCAAA  AAAAxx
+8241   1681    1       1       1       1       41      241     241     3241    8241    82      83      ZEAAAA  RMCAAA  HHHHxx
+3382   1682    0       2       2       2       82      382     1382    3382    3382    164     165     CAAAAA  SMCAAA  OOOOxx
+7105   1683    1       1       5       5       5       105     1105    2105    7105    10      11      HNAAAA  TMCAAA  VVVVxx
+2957   1684    1       1       7       17      57      957     957     2957    2957    114     115     TJAAAA  UMCAAA  AAAAxx
+6162   1685    0       2       2       2       62      162     162     1162    6162    124     125     ADAAAA  VMCAAA  HHHHxx
+5150   1686    0       2       0       10      50      150     1150    150     5150    100     101     CQAAAA  WMCAAA  OOOOxx
+2622   1687    0       2       2       2       22      622     622     2622    2622    44      45      WWAAAA  XMCAAA  VVVVxx
+2240   1688    0       0       0       0       40      240     240     2240    2240    80      81      EIAAAA  YMCAAA  AAAAxx
+8880   1689    0       0       0       0       80      880     880     3880    8880    160     161     ODAAAA  ZMCAAA  HHHHxx
+9250   1690    0       2       0       10      50      250     1250    4250    9250    100     101     URAAAA  ANCAAA  OOOOxx
+7010   1691    0       2       0       10      10      10      1010    2010    7010    20      21      QJAAAA  BNCAAA  VVVVxx
+1098   1692    0       2       8       18      98      98      1098    1098    1098    196     197     GQAAAA  CNCAAA  AAAAxx
+648    1693    0       0       8       8       48      648     648     648     648     96      97      YYAAAA  DNCAAA  HHHHxx
+5536   1694    0       0       6       16      36      536     1536    536     5536    72      73      YEAAAA  ENCAAA  OOOOxx
+7858   1695    0       2       8       18      58      858     1858    2858    7858    116     117     GQAAAA  FNCAAA  VVVVxx
+7053   1696    1       1       3       13      53      53      1053    2053    7053    106     107     HLAAAA  GNCAAA  AAAAxx
+8681   1697    1       1       1       1       81      681     681     3681    8681    162     163     XVAAAA  HNCAAA  HHHHxx
+8832   1698    0       0       2       12      32      832     832     3832    8832    64      65      SBAAAA  INCAAA  OOOOxx
+6836   1699    0       0       6       16      36      836     836     1836    6836    72      73      YCAAAA  JNCAAA  VVVVxx
+4856   1700    0       0       6       16      56      856     856     4856    4856    112     113     UEAAAA  KNCAAA  AAAAxx
+345    1701    1       1       5       5       45      345     345     345     345     90      91      HNAAAA  LNCAAA  HHHHxx
+6559   1702    1       3       9       19      59      559     559     1559    6559    118     119     HSAAAA  MNCAAA  OOOOxx
+3017   1703    1       1       7       17      17      17      1017    3017    3017    34      35      BMAAAA  NNCAAA  VVVVxx
+4176   1704    0       0       6       16      76      176     176     4176    4176    152     153     QEAAAA  ONCAAA  AAAAxx
+2839   1705    1       3       9       19      39      839     839     2839    2839    78      79      FFAAAA  PNCAAA  HHHHxx
+6065   1706    1       1       5       5       65      65      65      1065    6065    130     131     HZAAAA  QNCAAA  OOOOxx
+7360   1707    0       0       0       0       60      360     1360    2360    7360    120     121     CXAAAA  RNCAAA  VVVVxx
+9527   1708    1       3       7       7       27      527     1527    4527    9527    54      55      LCAAAA  SNCAAA  AAAAxx
+8849   1709    1       1       9       9       49      849     849     3849    8849    98      99      JCAAAA  TNCAAA  HHHHxx
+7274   1710    0       2       4       14      74      274     1274    2274    7274    148     149     UTAAAA  UNCAAA  OOOOxx
+4368   1711    0       0       8       8       68      368     368     4368    4368    136     137     AMAAAA  VNCAAA  VVVVxx
+2488   1712    0       0       8       8       88      488     488     2488    2488    176     177     SRAAAA  WNCAAA  AAAAxx
+4674   1713    0       2       4       14      74      674     674     4674    4674    148     149     UXAAAA  XNCAAA  HHHHxx
+365    1714    1       1       5       5       65      365     365     365     365     130     131     BOAAAA  YNCAAA  OOOOxx
+5897   1715    1       1       7       17      97      897     1897    897     5897    194     195     VSAAAA  ZNCAAA  VVVVxx
+8918   1716    0       2       8       18      18      918     918     3918    8918    36      37      AFAAAA  AOCAAA  AAAAxx
+1988   1717    0       0       8       8       88      988     1988    1988    1988    176     177     MYAAAA  BOCAAA  HHHHxx
+1210   1718    0       2       0       10      10      210     1210    1210    1210    20      21      OUAAAA  COCAAA  OOOOxx
+2945   1719    1       1       5       5       45      945     945     2945    2945    90      91      HJAAAA  DOCAAA  VVVVxx
+555    1720    1       3       5       15      55      555     555     555     555     110     111     JVAAAA  EOCAAA  AAAAxx
+9615   1721    1       3       5       15      15      615     1615    4615    9615    30      31      VFAAAA  FOCAAA  HHHHxx
+9939   1722    1       3       9       19      39      939     1939    4939    9939    78      79      HSAAAA  GOCAAA  OOOOxx
+1216   1723    0       0       6       16      16      216     1216    1216    1216    32      33      UUAAAA  HOCAAA  VVVVxx
+745    1724    1       1       5       5       45      745     745     745     745     90      91      RCAAAA  IOCAAA  AAAAxx
+3326   1725    0       2       6       6       26      326     1326    3326    3326    52      53      YXAAAA  JOCAAA  HHHHxx
+953    1726    1       1       3       13      53      953     953     953     953     106     107     RKAAAA  KOCAAA  OOOOxx
+444    1727    0       0       4       4       44      444     444     444     444     88      89      CRAAAA  LOCAAA  VVVVxx
+280    1728    0       0       0       0       80      280     280     280     280     160     161     UKAAAA  MOCAAA  AAAAxx
+3707   1729    1       3       7       7       7       707     1707    3707    3707    14      15      PMAAAA  NOCAAA  HHHHxx
+1351   1730    1       3       1       11      51      351     1351    1351    1351    102     103     ZZAAAA  OOCAAA  OOOOxx
+1280   1731    0       0       0       0       80      280     1280    1280    1280    160     161     GXAAAA  POCAAA  VVVVxx
+628    1732    0       0       8       8       28      628     628     628     628     56      57      EYAAAA  QOCAAA  AAAAxx
+6198   1733    0       2       8       18      98      198     198     1198    6198    196     197     KEAAAA  ROCAAA  HHHHxx
+1957   1734    1       1       7       17      57      957     1957    1957    1957    114     115     HXAAAA  SOCAAA  OOOOxx
+9241   1735    1       1       1       1       41      241     1241    4241    9241    82      83      LRAAAA  TOCAAA  VVVVxx
+303    1736    1       3       3       3       3       303     303     303     303     6       7       RLAAAA  UOCAAA  AAAAxx
+1945   1737    1       1       5       5       45      945     1945    1945    1945    90      91      VWAAAA  VOCAAA  HHHHxx
+3634   1738    0       2       4       14      34      634     1634    3634    3634    68      69      UJAAAA  WOCAAA  OOOOxx
+4768   1739    0       0       8       8       68      768     768     4768    4768    136     137     KBAAAA  XOCAAA  VVVVxx
+9262   1740    0       2       2       2       62      262     1262    4262    9262    124     125     GSAAAA  YOCAAA  AAAAxx
+2610   1741    0       2       0       10      10      610     610     2610    2610    20      21      KWAAAA  ZOCAAA  HHHHxx
+6640   1742    0       0       0       0       40      640     640     1640    6640    80      81      KVAAAA  APCAAA  OOOOxx
+3338   1743    0       2       8       18      38      338     1338    3338    3338    76      77      KYAAAA  BPCAAA  VVVVxx
+6560   1744    0       0       0       0       60      560     560     1560    6560    120     121     ISAAAA  CPCAAA  AAAAxx
+5986   1745    0       2       6       6       86      986     1986    986     5986    172     173     GWAAAA  DPCAAA  HHHHxx
+2970   1746    0       2       0       10      70      970     970     2970    2970    140     141     GKAAAA  EPCAAA  OOOOxx
+4731   1747    1       3       1       11      31      731     731     4731    4731    62      63      ZZAAAA  FPCAAA  VVVVxx
+9486   1748    0       2       6       6       86      486     1486    4486    9486    172     173     WAAAAA  GPCAAA  AAAAxx
+7204   1749    0       0       4       4       4       204     1204    2204    7204    8       9       CRAAAA  HPCAAA  HHHHxx
+6685   1750    1       1       5       5       85      685     685     1685    6685    170     171     DXAAAA  IPCAAA  OOOOxx
+6852   1751    0       0       2       12      52      852     852     1852    6852    104     105     ODAAAA  JPCAAA  VVVVxx
+2325   1752    1       1       5       5       25      325     325     2325    2325    50      51      LLAAAA  KPCAAA  AAAAxx
+1063   1753    1       3       3       3       63      63      1063    1063    1063    126     127     XOAAAA  LPCAAA  HHHHxx
+6810   1754    0       2       0       10      10      810     810     1810    6810    20      21      YBAAAA  MPCAAA  OOOOxx
+7718   1755    0       2       8       18      18      718     1718    2718    7718    36      37      WKAAAA  NPCAAA  VVVVxx
+1680   1756    0       0       0       0       80      680     1680    1680    1680    160     161     QMAAAA  OPCAAA  AAAAxx
+7402   1757    0       2       2       2       2       402     1402    2402    7402    4       5       SYAAAA  PPCAAA  HHHHxx
+4134   1758    0       2       4       14      34      134     134     4134    4134    68      69      ADAAAA  QPCAAA  OOOOxx
+8232   1759    0       0       2       12      32      232     232     3232    8232    64      65      QEAAAA  RPCAAA  VVVVxx
+6682   1760    0       2       2       2       82      682     682     1682    6682    164     165     AXAAAA  SPCAAA  AAAAxx
+7952   1761    0       0       2       12      52      952     1952    2952    7952    104     105     WTAAAA  TPCAAA  HHHHxx
+5943   1762    1       3       3       3       43      943     1943    943     5943    86      87      PUAAAA  UPCAAA  OOOOxx
+5394   1763    0       2       4       14      94      394     1394    394     5394    188     189     MZAAAA  VPCAAA  VVVVxx
+6554   1764    0       2       4       14      54      554     554     1554    6554    108     109     CSAAAA  WPCAAA  AAAAxx
+8186   1765    0       2       6       6       86      186     186     3186    8186    172     173     WCAAAA  XPCAAA  HHHHxx
+199    1766    1       3       9       19      99      199     199     199     199     198     199     RHAAAA  YPCAAA  OOOOxx
+3386   1767    0       2       6       6       86      386     1386    3386    3386    172     173     GAAAAA  ZPCAAA  VVVVxx
+8974   1768    0       2       4       14      74      974     974     3974    8974    148     149     EHAAAA  AQCAAA  AAAAxx
+8140   1769    0       0       0       0       40      140     140     3140    8140    80      81      CBAAAA  BQCAAA  HHHHxx
+3723   1770    1       3       3       3       23      723     1723    3723    3723    46      47      FNAAAA  CQCAAA  OOOOxx
+8827   1771    1       3       7       7       27      827     827     3827    8827    54      55      NBAAAA  DQCAAA  VVVVxx
+1998   1772    0       2       8       18      98      998     1998    1998    1998    196     197     WYAAAA  EQCAAA  AAAAxx
+879    1773    1       3       9       19      79      879     879     879     879     158     159     VHAAAA  FQCAAA  HHHHxx
+892    1774    0       0       2       12      92      892     892     892     892     184     185     IIAAAA  GQCAAA  OOOOxx
+9468   1775    0       0       8       8       68      468     1468    4468    9468    136     137     EAAAAA  HQCAAA  VVVVxx
+3797   1776    1       1       7       17      97      797     1797    3797    3797    194     195     BQAAAA  IQCAAA  AAAAxx
+8379   1777    1       3       9       19      79      379     379     3379    8379    158     159     HKAAAA  JQCAAA  HHHHxx
+2817   1778    1       1       7       17      17      817     817     2817    2817    34      35      JEAAAA  KQCAAA  OOOOxx
+789    1779    1       1       9       9       89      789     789     789     789     178     179     JEAAAA  LQCAAA  VVVVxx
+3871   1780    1       3       1       11      71      871     1871    3871    3871    142     143     XSAAAA  MQCAAA  AAAAxx
+7931   1781    1       3       1       11      31      931     1931    2931    7931    62      63      BTAAAA  NQCAAA  HHHHxx
+3636   1782    0       0       6       16      36      636     1636    3636    3636    72      73      WJAAAA  OQCAAA  OOOOxx
+699    1783    1       3       9       19      99      699     699     699     699     198     199     XAAAAA  PQCAAA  VVVVxx
+6850   1784    0       2       0       10      50      850     850     1850    6850    100     101     MDAAAA  QQCAAA  AAAAxx
+6394   1785    0       2       4       14      94      394     394     1394    6394    188     189     YLAAAA  RQCAAA  HHHHxx
+3475   1786    1       3       5       15      75      475     1475    3475    3475    150     151     RDAAAA  SQCAAA  OOOOxx
+3026   1787    0       2       6       6       26      26      1026    3026    3026    52      53      KMAAAA  TQCAAA  VVVVxx
+876    1788    0       0       6       16      76      876     876     876     876     152     153     SHAAAA  UQCAAA  AAAAxx
+1992   1789    0       0       2       12      92      992     1992    1992    1992    184     185     QYAAAA  VQCAAA  HHHHxx
+3079   1790    1       3       9       19      79      79      1079    3079    3079    158     159     LOAAAA  WQCAAA  OOOOxx
+8128   1791    0       0       8       8       28      128     128     3128    8128    56      57      QAAAAA  XQCAAA  VVVVxx
+8123   1792    1       3       3       3       23      123     123     3123    8123    46      47      LAAAAA  YQCAAA  AAAAxx
+3285   1793    1       1       5       5       85      285     1285    3285    3285    170     171     JWAAAA  ZQCAAA  HHHHxx
+9315   1794    1       3       5       15      15      315     1315    4315    9315    30      31      HUAAAA  ARCAAA  OOOOxx
+9862   1795    0       2       2       2       62      862     1862    4862    9862    124     125     IPAAAA  BRCAAA  VVVVxx
+2764   1796    0       0       4       4       64      764     764     2764    2764    128     129     ICAAAA  CRCAAA  AAAAxx
+3544   1797    0       0       4       4       44      544     1544    3544    3544    88      89      IGAAAA  DRCAAA  HHHHxx
+7747   1798    1       3       7       7       47      747     1747    2747    7747    94      95      ZLAAAA  ERCAAA  OOOOxx
+7725   1799    1       1       5       5       25      725     1725    2725    7725    50      51      DLAAAA  FRCAAA  VVVVxx
+2449   1800    1       1       9       9       49      449     449     2449    2449    98      99      FQAAAA  GRCAAA  AAAAxx
+8967   1801    1       3       7       7       67      967     967     3967    8967    134     135     XGAAAA  HRCAAA  HHHHxx
+7371   1802    1       3       1       11      71      371     1371    2371    7371    142     143     NXAAAA  IRCAAA  OOOOxx
+2158   1803    0       2       8       18      58      158     158     2158    2158    116     117     AFAAAA  JRCAAA  VVVVxx
+5590   1804    0       2       0       10      90      590     1590    590     5590    180     181     AHAAAA  KRCAAA  AAAAxx
+8072   1805    0       0       2       12      72      72      72      3072    8072    144     145     MYAAAA  LRCAAA  HHHHxx
+1971   1806    1       3       1       11      71      971     1971    1971    1971    142     143     VXAAAA  MRCAAA  OOOOxx
+772    1807    0       0       2       12      72      772     772     772     772     144     145     SDAAAA  NRCAAA  VVVVxx
+3433   1808    1       1       3       13      33      433     1433    3433    3433    66      67      BCAAAA  ORCAAA  AAAAxx
+8419   1809    1       3       9       19      19      419     419     3419    8419    38      39      VLAAAA  PRCAAA  HHHHxx
+1493   1810    1       1       3       13      93      493     1493    1493    1493    186     187     LFAAAA  QRCAAA  OOOOxx
+2584   1811    0       0       4       4       84      584     584     2584    2584    168     169     KVAAAA  RRCAAA  VVVVxx
+9502   1812    0       2       2       2       2       502     1502    4502    9502    4       5       MBAAAA  SRCAAA  AAAAxx
+4673   1813    1       1       3       13      73      673     673     4673    4673    146     147     TXAAAA  TRCAAA  HHHHxx
+7403   1814    1       3       3       3       3       403     1403    2403    7403    6       7       TYAAAA  URCAAA  OOOOxx
+7103   1815    1       3       3       3       3       103     1103    2103    7103    6       7       FNAAAA  VRCAAA  VVVVxx
+7026   1816    0       2       6       6       26      26      1026    2026    7026    52      53      GKAAAA  WRCAAA  AAAAxx
+8574   1817    0       2       4       14      74      574     574     3574    8574    148     149     URAAAA  XRCAAA  HHHHxx
+1366   1818    0       2       6       6       66      366     1366    1366    1366    132     133     OAAAAA  YRCAAA  OOOOxx
+5787   1819    1       3       7       7       87      787     1787    787     5787    174     175     POAAAA  ZRCAAA  VVVVxx
+2552   1820    0       0       2       12      52      552     552     2552    2552    104     105     EUAAAA  ASCAAA  AAAAxx
+4557   1821    1       1       7       17      57      557     557     4557    4557    114     115     HTAAAA  BSCAAA  HHHHxx
+3237   1822    1       1       7       17      37      237     1237    3237    3237    74      75      NUAAAA  CSCAAA  OOOOxx
+6901   1823    1       1       1       1       1       901     901     1901    6901    2       3       LFAAAA  DSCAAA  VVVVxx
+7708   1824    0       0       8       8       8       708     1708    2708    7708    16      17      MKAAAA  ESCAAA  AAAAxx
+2011   1825    1       3       1       11      11      11      11      2011    2011    22      23      JZAAAA  FSCAAA  HHHHxx
+9455   1826    1       3       5       15      55      455     1455    4455    9455    110     111     RZAAAA  GSCAAA  OOOOxx
+5228   1827    0       0       8       8       28      228     1228    228     5228    56      57      CTAAAA  HSCAAA  VVVVxx
+4043   1828    1       3       3       3       43      43      43      4043    4043    86      87      NZAAAA  ISCAAA  AAAAxx
+8242   1829    0       2       2       2       42      242     242     3242    8242    84      85      AFAAAA  JSCAAA  HHHHxx
+6351   1830    1       3       1       11      51      351     351     1351    6351    102     103     HKAAAA  KSCAAA  OOOOxx
+5899   1831    1       3       9       19      99      899     1899    899     5899    198     199     XSAAAA  LSCAAA  VVVVxx
+4849   1832    1       1       9       9       49      849     849     4849    4849    98      99      NEAAAA  MSCAAA  AAAAxx
+9583   1833    1       3       3       3       83      583     1583    4583    9583    166     167     PEAAAA  NSCAAA  HHHHxx
+4994   1834    0       2       4       14      94      994     994     4994    4994    188     189     CKAAAA  OSCAAA  OOOOxx
+9787   1835    1       3       7       7       87      787     1787    4787    9787    174     175     LMAAAA  PSCAAA  VVVVxx
+243    1836    1       3       3       3       43      243     243     243     243     86      87      JJAAAA  QSCAAA  AAAAxx
+3931   1837    1       3       1       11      31      931     1931    3931    3931    62      63      FVAAAA  RSCAAA  HHHHxx
+5945   1838    1       1       5       5       45      945     1945    945     5945    90      91      RUAAAA  SSCAAA  OOOOxx
+1325   1839    1       1       5       5       25      325     1325    1325    1325    50      51      ZYAAAA  TSCAAA  VVVVxx
+4142   1840    0       2       2       2       42      142     142     4142    4142    84      85      IDAAAA  USCAAA  AAAAxx
+1963   1841    1       3       3       3       63      963     1963    1963    1963    126     127     NXAAAA  VSCAAA  HHHHxx
+7041   1842    1       1       1       1       41      41      1041    2041    7041    82      83      VKAAAA  WSCAAA  OOOOxx
+3074   1843    0       2       4       14      74      74      1074    3074    3074    148     149     GOAAAA  XSCAAA  VVVVxx
+3290   1844    0       2       0       10      90      290     1290    3290    3290    180     181     OWAAAA  YSCAAA  AAAAxx
+4146   1845    0       2       6       6       46      146     146     4146    4146    92      93      MDAAAA  ZSCAAA  HHHHxx
+3832   1846    0       0       2       12      32      832     1832    3832    3832    64      65      KRAAAA  ATCAAA  OOOOxx
+2217   1847    1       1       7       17      17      217     217     2217    2217    34      35      HHAAAA  BTCAAA  VVVVxx
+635    1848    1       3       5       15      35      635     635     635     635     70      71      LYAAAA  CTCAAA  AAAAxx
+6967   1849    1       3       7       7       67      967     967     1967    6967    134     135     ZHAAAA  DTCAAA  HHHHxx
+3522   1850    0       2       2       2       22      522     1522    3522    3522    44      45      MFAAAA  ETCAAA  OOOOxx
+2471   1851    1       3       1       11      71      471     471     2471    2471    142     143     BRAAAA  FTCAAA  VVVVxx
+4236   1852    0       0       6       16      36      236     236     4236    4236    72      73      YGAAAA  GTCAAA  AAAAxx
+853    1853    1       1       3       13      53      853     853     853     853     106     107     VGAAAA  HTCAAA  HHHHxx
+3754   1854    0       2       4       14      54      754     1754    3754    3754    108     109     KOAAAA  ITCAAA  OOOOxx
+796    1855    0       0       6       16      96      796     796     796     796     192     193     QEAAAA  JTCAAA  VVVVxx
+4640   1856    0       0       0       0       40      640     640     4640    4640    80      81      MWAAAA  KTCAAA  AAAAxx
+9496   1857    0       0       6       16      96      496     1496    4496    9496    192     193     GBAAAA  LTCAAA  HHHHxx
+6873   1858    1       1       3       13      73      873     873     1873    6873    146     147     JEAAAA  MTCAAA  OOOOxx
+4632   1859    0       0       2       12      32      632     632     4632    4632    64      65      EWAAAA  NTCAAA  VVVVxx
+5758   1860    0       2       8       18      58      758     1758    758     5758    116     117     MNAAAA  OTCAAA  AAAAxx
+6514   1861    0       2       4       14      14      514     514     1514    6514    28      29      OQAAAA  PTCAAA  HHHHxx
+9510   1862    0       2       0       10      10      510     1510    4510    9510    20      21      UBAAAA  QTCAAA  OOOOxx
+8411   1863    1       3       1       11      11      411     411     3411    8411    22      23      NLAAAA  RTCAAA  VVVVxx
+7762   1864    0       2       2       2       62      762     1762    2762    7762    124     125     OMAAAA  STCAAA  AAAAxx
+2225   1865    1       1       5       5       25      225     225     2225    2225    50      51      PHAAAA  TTCAAA  HHHHxx
+4373   1866    1       1       3       13      73      373     373     4373    4373    146     147     FMAAAA  UTCAAA  OOOOxx
+7326   1867    0       2       6       6       26      326     1326    2326    7326    52      53      UVAAAA  VTCAAA  VVVVxx
+8651   1868    1       3       1       11      51      651     651     3651    8651    102     103     TUAAAA  WTCAAA  AAAAxx
+9825   1869    1       1       5       5       25      825     1825    4825    9825    50      51      XNAAAA  XTCAAA  HHHHxx
+2988   1870    0       0       8       8       88      988     988     2988    2988    176     177     YKAAAA  YTCAAA  OOOOxx
+8138   1871    0       2       8       18      38      138     138     3138    8138    76      77      ABAAAA  ZTCAAA  VVVVxx
+7792   1872    0       0       2       12      92      792     1792    2792    7792    184     185     SNAAAA  AUCAAA  AAAAxx
+1232   1873    0       0       2       12      32      232     1232    1232    1232    64      65      KVAAAA  BUCAAA  HHHHxx
+8221   1874    1       1       1       1       21      221     221     3221    8221    42      43      FEAAAA  CUCAAA  OOOOxx
+4044   1875    0       0       4       4       44      44      44      4044    4044    88      89      OZAAAA  DUCAAA  VVVVxx
+1204   1876    0       0       4       4       4       204     1204    1204    1204    8       9       IUAAAA  EUCAAA  AAAAxx
+5145   1877    1       1       5       5       45      145     1145    145     5145    90      91      XPAAAA  FUCAAA  HHHHxx
+7791   1878    1       3       1       11      91      791     1791    2791    7791    182     183     RNAAAA  GUCAAA  OOOOxx
+8270   1879    0       2       0       10      70      270     270     3270    8270    140     141     CGAAAA  HUCAAA  VVVVxx
+9427   1880    1       3       7       7       27      427     1427    4427    9427    54      55      PYAAAA  IUCAAA  AAAAxx
+2152   1881    0       0       2       12      52      152     152     2152    2152    104     105     UEAAAA  JUCAAA  HHHHxx
+7790   1882    0       2       0       10      90      790     1790    2790    7790    180     181     QNAAAA  KUCAAA  OOOOxx
+5301   1883    1       1       1       1       1       301     1301    301     5301    2       3       XVAAAA  LUCAAA  VVVVxx
+626    1884    0       2       6       6       26      626     626     626     626     52      53      CYAAAA  MUCAAA  AAAAxx
+260    1885    0       0       0       0       60      260     260     260     260     120     121     AKAAAA  NUCAAA  HHHHxx
+4369   1886    1       1       9       9       69      369     369     4369    4369    138     139     BMAAAA  OUCAAA  OOOOxx
+5457   1887    1       1       7       17      57      457     1457    457     5457    114     115     XBAAAA  PUCAAA  VVVVxx
+3468   1888    0       0       8       8       68      468     1468    3468    3468    136     137     KDAAAA  QUCAAA  AAAAxx
+2257   1889    1       1       7       17      57      257     257     2257    2257    114     115     VIAAAA  RUCAAA  HHHHxx
+9318   1890    0       2       8       18      18      318     1318    4318    9318    36      37      KUAAAA  SUCAAA  OOOOxx
+8762   1891    0       2       2       2       62      762     762     3762    8762    124     125     AZAAAA  TUCAAA  VVVVxx
+9153   1892    1       1       3       13      53      153     1153    4153    9153    106     107     BOAAAA  UUCAAA  AAAAxx
+9220   1893    0       0       0       0       20      220     1220    4220    9220    40      41      QQAAAA  VUCAAA  HHHHxx
+8003   1894    1       3       3       3       3       3       3       3003    8003    6       7       VVAAAA  WUCAAA  OOOOxx
+7257   1895    1       1       7       17      57      257     1257    2257    7257    114     115     DTAAAA  XUCAAA  VVVVxx
+3930   1896    0       2       0       10      30      930     1930    3930    3930    60      61      EVAAAA  YUCAAA  AAAAxx
+2976   1897    0       0       6       16      76      976     976     2976    2976    152     153     MKAAAA  ZUCAAA  HHHHxx
+2531   1898    1       3       1       11      31      531     531     2531    2531    62      63      JTAAAA  AVCAAA  OOOOxx
+2250   1899    0       2       0       10      50      250     250     2250    2250    100     101     OIAAAA  BVCAAA  VVVVxx
+8549   1900    1       1       9       9       49      549     549     3549    8549    98      99      VQAAAA  CVCAAA  AAAAxx
+7197   1901    1       1       7       17      97      197     1197    2197    7197    194     195     VQAAAA  DVCAAA  HHHHxx
+5916   1902    0       0       6       16      16      916     1916    916     5916    32      33      OTAAAA  EVCAAA  OOOOxx
+5287   1903    1       3       7       7       87      287     1287    287     5287    174     175     JVAAAA  FVCAAA  VVVVxx
+9095   1904    1       3       5       15      95      95      1095    4095    9095    190     191     VLAAAA  GVCAAA  AAAAxx
+7137   1905    1       1       7       17      37      137     1137    2137    7137    74      75      NOAAAA  HVCAAA  HHHHxx
+7902   1906    0       2       2       2       2       902     1902    2902    7902    4       5       YRAAAA  IVCAAA  OOOOxx
+7598   1907    0       2       8       18      98      598     1598    2598    7598    196     197     GGAAAA  JVCAAA  VVVVxx
+5652   1908    0       0       2       12      52      652     1652    652     5652    104     105     KJAAAA  KVCAAA  AAAAxx
+2017   1909    1       1       7       17      17      17      17      2017    2017    34      35      PZAAAA  LVCAAA  HHHHxx
+7255   1910    1       3       5       15      55      255     1255    2255    7255    110     111     BTAAAA  MVCAAA  OOOOxx
+7999   1911    1       3       9       19      99      999     1999    2999    7999    198     199     RVAAAA  NVCAAA  VVVVxx
+5388   1912    0       0       8       8       88      388     1388    388     5388    176     177     GZAAAA  OVCAAA  AAAAxx
+8754   1913    0       2       4       14      54      754     754     3754    8754    108     109     SYAAAA  PVCAAA  HHHHxx
+5415   1914    1       3       5       15      15      415     1415    415     5415    30      31      HAAAAA  QVCAAA  OOOOxx
+8861   1915    1       1       1       1       61      861     861     3861    8861    122     123     VCAAAA  RVCAAA  VVVVxx
+2874   1916    0       2       4       14      74      874     874     2874    2874    148     149     OGAAAA  SVCAAA  AAAAxx
+9910   1917    0       2       0       10      10      910     1910    4910    9910    20      21      ERAAAA  TVCAAA  HHHHxx
+5178   1918    0       2       8       18      78      178     1178    178     5178    156     157     ERAAAA  UVCAAA  OOOOxx
+5698   1919    0       2       8       18      98      698     1698    698     5698    196     197     ELAAAA  VVCAAA  VVVVxx
+8500   1920    0       0       0       0       0       500     500     3500    8500    0       1       YOAAAA  WVCAAA  AAAAxx
+1814   1921    0       2       4       14      14      814     1814    1814    1814    28      29      URAAAA  XVCAAA  HHHHxx
+4968   1922    0       0       8       8       68      968     968     4968    4968    136     137     CJAAAA  YVCAAA  OOOOxx
+2642   1923    0       2       2       2       42      642     642     2642    2642    84      85      QXAAAA  ZVCAAA  VVVVxx
+1578   1924    0       2       8       18      78      578     1578    1578    1578    156     157     SIAAAA  AWCAAA  AAAAxx
+4774   1925    0       2       4       14      74      774     774     4774    4774    148     149     QBAAAA  BWCAAA  HHHHxx
+7062   1926    0       2       2       2       62      62      1062    2062    7062    124     125     QLAAAA  CWCAAA  OOOOxx
+5381   1927    1       1       1       1       81      381     1381    381     5381    162     163     ZYAAAA  DWCAAA  VVVVxx
+7985   1928    1       1       5       5       85      985     1985    2985    7985    170     171     DVAAAA  EWCAAA  AAAAxx
+3850   1929    0       2       0       10      50      850     1850    3850    3850    100     101     CSAAAA  FWCAAA  HHHHxx
+5624   1930    0       0       4       4       24      624     1624    624     5624    48      49      IIAAAA  GWCAAA  OOOOxx
+8948   1931    0       0       8       8       48      948     948     3948    8948    96      97      EGAAAA  HWCAAA  VVVVxx
+995    1932    1       3       5       15      95      995     995     995     995     190     191     HMAAAA  IWCAAA  AAAAxx
+5058   1933    0       2       8       18      58      58      1058    58      5058    116     117     OMAAAA  JWCAAA  HHHHxx
+9670   1934    0       2       0       10      70      670     1670    4670    9670    140     141     YHAAAA  KWCAAA  OOOOxx
+3115   1935    1       3       5       15      15      115     1115    3115    3115    30      31      VPAAAA  LWCAAA  VVVVxx
+4935   1936    1       3       5       15      35      935     935     4935    4935    70      71      VHAAAA  MWCAAA  AAAAxx
+4735   1937    1       3       5       15      35      735     735     4735    4735    70      71      DAAAAA  NWCAAA  HHHHxx
+1348   1938    0       0       8       8       48      348     1348    1348    1348    96      97      WZAAAA  OWCAAA  OOOOxx
+2380   1939    0       0       0       0       80      380     380     2380    2380    160     161     ONAAAA  PWCAAA  VVVVxx
+4246   1940    0       2       6       6       46      246     246     4246    4246    92      93      IHAAAA  QWCAAA  AAAAxx
+522    1941    0       2       2       2       22      522     522     522     522     44      45      CUAAAA  RWCAAA  HHHHxx
+1701   1942    1       1       1       1       1       701     1701    1701    1701    2       3       LNAAAA  SWCAAA  OOOOxx
+9709   1943    1       1       9       9       9       709     1709    4709    9709    18      19      LJAAAA  TWCAAA  VVVVxx
+8829   1944    1       1       9       9       29      829     829     3829    8829    58      59      PBAAAA  UWCAAA  AAAAxx
+7936   1945    0       0       6       16      36      936     1936    2936    7936    72      73      GTAAAA  VWCAAA  HHHHxx
+8474   1946    0       2       4       14      74      474     474     3474    8474    148     149     YNAAAA  WWCAAA  OOOOxx
+4676   1947    0       0       6       16      76      676     676     4676    4676    152     153     WXAAAA  XWCAAA  VVVVxx
+6303   1948    1       3       3       3       3       303     303     1303    6303    6       7       LIAAAA  YWCAAA  AAAAxx
+3485   1949    1       1       5       5       85      485     1485    3485    3485    170     171     BEAAAA  ZWCAAA  HHHHxx
+2695   1950    1       3       5       15      95      695     695     2695    2695    190     191     RZAAAA  AXCAAA  OOOOxx
+8830   1951    0       2       0       10      30      830     830     3830    8830    60      61      QBAAAA  BXCAAA  VVVVxx
+898    1952    0       2       8       18      98      898     898     898     898     196     197     OIAAAA  CXCAAA  AAAAxx
+7268   1953    0       0       8       8       68      268     1268    2268    7268    136     137     OTAAAA  DXCAAA  HHHHxx
+6568   1954    0       0       8       8       68      568     568     1568    6568    136     137     QSAAAA  EXCAAA  OOOOxx
+9724   1955    0       0       4       4       24      724     1724    4724    9724    48      49      AKAAAA  FXCAAA  VVVVxx
+3329   1956    1       1       9       9       29      329     1329    3329    3329    58      59      BYAAAA  GXCAAA  AAAAxx
+9860   1957    0       0       0       0       60      860     1860    4860    9860    120     121     GPAAAA  HXCAAA  HHHHxx
+6833   1958    1       1       3       13      33      833     833     1833    6833    66      67      VCAAAA  IXCAAA  OOOOxx
+5956   1959    0       0       6       16      56      956     1956    956     5956    112     113     CVAAAA  JXCAAA  VVVVxx
+3963   1960    1       3       3       3       63      963     1963    3963    3963    126     127     LWAAAA  KXCAAA  AAAAxx
+883    1961    1       3       3       3       83      883     883     883     883     166     167     ZHAAAA  LXCAAA  HHHHxx
+2761   1962    1       1       1       1       61      761     761     2761    2761    122     123     FCAAAA  MXCAAA  OOOOxx
+4644   1963    0       0       4       4       44      644     644     4644    4644    88      89      QWAAAA  NXCAAA  VVVVxx
+1358   1964    0       2       8       18      58      358     1358    1358    1358    116     117     GAAAAA  OXCAAA  AAAAxx
+2049   1965    1       1       9       9       49      49      49      2049    2049    98      99      VAAAAA  PXCAAA  HHHHxx
+2193   1966    1       1       3       13      93      193     193     2193    2193    186     187     JGAAAA  QXCAAA  OOOOxx
+9435   1967    1       3       5       15      35      435     1435    4435    9435    70      71      XYAAAA  RXCAAA  VVVVxx
+5890   1968    0       2       0       10      90      890     1890    890     5890    180     181     OSAAAA  SXCAAA  AAAAxx
+8149   1969    1       1       9       9       49      149     149     3149    8149    98      99      LBAAAA  TXCAAA  HHHHxx
+423    1970    1       3       3       3       23      423     423     423     423     46      47      HQAAAA  UXCAAA  OOOOxx
+7980   1971    0       0       0       0       80      980     1980    2980    7980    160     161     YUAAAA  VXCAAA  VVVVxx
+9019   1972    1       3       9       19      19      19      1019    4019    9019    38      39      XIAAAA  WXCAAA  AAAAxx
+1647   1973    1       3       7       7       47      647     1647    1647    1647    94      95      JLAAAA  XXCAAA  HHHHxx
+9495   1974    1       3       5       15      95      495     1495    4495    9495    190     191     FBAAAA  YXCAAA  OOOOxx
+3904   1975    0       0       4       4       4       904     1904    3904    3904    8       9       EUAAAA  ZXCAAA  VVVVxx
+5838   1976    0       2       8       18      38      838     1838    838     5838    76      77      OQAAAA  AYCAAA  AAAAxx
+3866   1977    0       2       6       6       66      866     1866    3866    3866    132     133     SSAAAA  BYCAAA  HHHHxx
+3093   1978    1       1       3       13      93      93      1093    3093    3093    186     187     ZOAAAA  CYCAAA  OOOOxx
+9666   1979    0       2       6       6       66      666     1666    4666    9666    132     133     UHAAAA  DYCAAA  VVVVxx
+1246   1980    0       2       6       6       46      246     1246    1246    1246    92      93      YVAAAA  EYCAAA  AAAAxx
+9759   1981    1       3       9       19      59      759     1759    4759    9759    118     119     JLAAAA  FYCAAA  HHHHxx
+7174   1982    0       2       4       14      74      174     1174    2174    7174    148     149     YPAAAA  GYCAAA  OOOOxx
+7678   1983    0       2       8       18      78      678     1678    2678    7678    156     157     IJAAAA  HYCAAA  VVVVxx
+3004   1984    0       0       4       4       4       4       1004    3004    3004    8       9       OLAAAA  IYCAAA  AAAAxx
+5607   1985    1       3       7       7       7       607     1607    607     5607    14      15      RHAAAA  JYCAAA  HHHHxx
+8510   1986    0       2       0       10      10      510     510     3510    8510    20      21      IPAAAA  KYCAAA  OOOOxx
+1483   1987    1       3       3       3       83      483     1483    1483    1483    166     167     BFAAAA  LYCAAA  VVVVxx
+2915   1988    1       3       5       15      15      915     915     2915    2915    30      31      DIAAAA  MYCAAA  AAAAxx
+1548   1989    0       0       8       8       48      548     1548    1548    1548    96      97      OHAAAA  NYCAAA  HHHHxx
+5767   1990    1       3       7       7       67      767     1767    767     5767    134     135     VNAAAA  OYCAAA  OOOOxx
+3214   1991    0       2       4       14      14      214     1214    3214    3214    28      29      QTAAAA  PYCAAA  VVVVxx
+8663   1992    1       3       3       3       63      663     663     3663    8663    126     127     FVAAAA  QYCAAA  AAAAxx
+5425   1993    1       1       5       5       25      425     1425    425     5425    50      51      RAAAAA  RYCAAA  HHHHxx
+8530   1994    0       2       0       10      30      530     530     3530    8530    60      61      CQAAAA  SYCAAA  OOOOxx
+821    1995    1       1       1       1       21      821     821     821     821     42      43      PFAAAA  TYCAAA  VVVVxx
+8816   1996    0       0       6       16      16      816     816     3816    8816    32      33      CBAAAA  UYCAAA  AAAAxx
+9367   1997    1       3       7       7       67      367     1367    4367    9367    134     135     HWAAAA  VYCAAA  HHHHxx
+4138   1998    0       2       8       18      38      138     138     4138    4138    76      77      EDAAAA  WYCAAA  OOOOxx
+94     1999    0       2       4       14      94      94      94      94      94      188     189     QDAAAA  XYCAAA  VVVVxx
+1858   2000    0       2       8       18      58      858     1858    1858    1858    116     117     MTAAAA  YYCAAA  AAAAxx
+5513   2001    1       1       3       13      13      513     1513    513     5513    26      27      BEAAAA  ZYCAAA  HHHHxx
+9620   2002    0       0       0       0       20      620     1620    4620    9620    40      41      AGAAAA  AZCAAA  OOOOxx
+4770   2003    0       2       0       10      70      770     770     4770    4770    140     141     MBAAAA  BZCAAA  VVVVxx
+5193   2004    1       1       3       13      93      193     1193    193     5193    186     187     TRAAAA  CZCAAA  AAAAxx
+198    2005    0       2       8       18      98      198     198     198     198     196     197     QHAAAA  DZCAAA  HHHHxx
+417    2006    1       1       7       17      17      417     417     417     417     34      35      BQAAAA  EZCAAA  OOOOxx
+173    2007    1       1       3       13      73      173     173     173     173     146     147     RGAAAA  FZCAAA  VVVVxx
+6248   2008    0       0       8       8       48      248     248     1248    6248    96      97      IGAAAA  GZCAAA  AAAAxx
+302    2009    0       2       2       2       2       302     302     302     302     4       5       QLAAAA  HZCAAA  HHHHxx
+8983   2010    1       3       3       3       83      983     983     3983    8983    166     167     NHAAAA  IZCAAA  OOOOxx
+4840   2011    0       0       0       0       40      840     840     4840    4840    80      81      EEAAAA  JZCAAA  VVVVxx
+2876   2012    0       0       6       16      76      876     876     2876    2876    152     153     QGAAAA  KZCAAA  AAAAxx
+5841   2013    1       1       1       1       41      841     1841    841     5841    82      83      RQAAAA  LZCAAA  HHHHxx
+2766   2014    0       2       6       6       66      766     766     2766    2766    132     133     KCAAAA  MZCAAA  OOOOxx
+9482   2015    0       2       2       2       82      482     1482    4482    9482    164     165     SAAAAA  NZCAAA  VVVVxx
+5335   2016    1       3       5       15      35      335     1335    335     5335    70      71      FXAAAA  OZCAAA  AAAAxx
+1502   2017    0       2       2       2       2       502     1502    1502    1502    4       5       UFAAAA  PZCAAA  HHHHxx
+9291   2018    1       3       1       11      91      291     1291    4291    9291    182     183     JTAAAA  QZCAAA  OOOOxx
+8655   2019    1       3       5       15      55      655     655     3655    8655    110     111     XUAAAA  RZCAAA  VVVVxx
+1687   2020    1       3       7       7       87      687     1687    1687    1687    174     175     XMAAAA  SZCAAA  AAAAxx
+8171   2021    1       3       1       11      71      171     171     3171    8171    142     143     HCAAAA  TZCAAA  HHHHxx
+5699   2022    1       3       9       19      99      699     1699    699     5699    198     199     FLAAAA  UZCAAA  OOOOxx
+1462   2023    0       2       2       2       62      462     1462    1462    1462    124     125     GEAAAA  VZCAAA  VVVVxx
+608    2024    0       0       8       8       8       608     608     608     608     16      17      KXAAAA  WZCAAA  AAAAxx
+6860   2025    0       0       0       0       60      860     860     1860    6860    120     121     WDAAAA  XZCAAA  HHHHxx
+6063   2026    1       3       3       3       63      63      63      1063    6063    126     127     FZAAAA  YZCAAA  OOOOxx
+1422   2027    0       2       2       2       22      422     1422    1422    1422    44      45      SCAAAA  ZZCAAA  VVVVxx
+1932   2028    0       0       2       12      32      932     1932    1932    1932    64      65      IWAAAA  AADAAA  AAAAxx
+5065   2029    1       1       5       5       65      65      1065    65      5065    130     131     VMAAAA  BADAAA  HHHHxx
+432    2030    0       0       2       12      32      432     432     432     432     64      65      QQAAAA  CADAAA  OOOOxx
+4680   2031    0       0       0       0       80      680     680     4680    4680    160     161     AYAAAA  DADAAA  VVVVxx
+8172   2032    0       0       2       12      72      172     172     3172    8172    144     145     ICAAAA  EADAAA  AAAAxx
+8668   2033    0       0       8       8       68      668     668     3668    8668    136     137     KVAAAA  FADAAA  HHHHxx
+256    2034    0       0       6       16      56      256     256     256     256     112     113     WJAAAA  GADAAA  OOOOxx
+2500   2035    0       0       0       0       0       500     500     2500    2500    0       1       ESAAAA  HADAAA  VVVVxx
+274    2036    0       2       4       14      74      274     274     274     274     148     149     OKAAAA  IADAAA  AAAAxx
+5907   2037    1       3       7       7       7       907     1907    907     5907    14      15      FTAAAA  JADAAA  HHHHxx
+8587   2038    1       3       7       7       87      587     587     3587    8587    174     175     HSAAAA  KADAAA  OOOOxx
+9942   2039    0       2       2       2       42      942     1942    4942    9942    84      85      KSAAAA  LADAAA  VVVVxx
+116    2040    0       0       6       16      16      116     116     116     116     32      33      MEAAAA  MADAAA  AAAAxx
+7134   2041    0       2       4       14      34      134     1134    2134    7134    68      69      KOAAAA  NADAAA  HHHHxx
+9002   2042    0       2       2       2       2       2       1002    4002    9002    4       5       GIAAAA  OADAAA  OOOOxx
+1209   2043    1       1       9       9       9       209     1209    1209    1209    18      19      NUAAAA  PADAAA  VVVVxx
+9983   2044    1       3       3       3       83      983     1983    4983    9983    166     167     ZTAAAA  QADAAA  AAAAxx
+1761   2045    1       1       1       1       61      761     1761    1761    1761    122     123     TPAAAA  RADAAA  HHHHxx
+7723   2046    1       3       3       3       23      723     1723    2723    7723    46      47      BLAAAA  SADAAA  OOOOxx
+6518   2047    0       2       8       18      18      518     518     1518    6518    36      37      SQAAAA  TADAAA  VVVVxx
+1372   2048    0       0       2       12      72      372     1372    1372    1372    144     145     UAAAAA  UADAAA  AAAAxx
+3587   2049    1       3       7       7       87      587     1587    3587    3587    174     175     ZHAAAA  VADAAA  HHHHxx
+5323   2050    1       3       3       3       23      323     1323    323     5323    46      47      TWAAAA  WADAAA  OOOOxx
+5902   2051    0       2       2       2       2       902     1902    902     5902    4       5       ATAAAA  XADAAA  VVVVxx
+3749   2052    1       1       9       9       49      749     1749    3749    3749    98      99      FOAAAA  YADAAA  AAAAxx
+5965   2053    1       1       5       5       65      965     1965    965     5965    130     131     LVAAAA  ZADAAA  HHHHxx
+663    2054    1       3       3       3       63      663     663     663     663     126     127     NZAAAA  ABDAAA  OOOOxx
+36     2055    0       0       6       16      36      36      36      36      36      72      73      KBAAAA  BBDAAA  VVVVxx
+9782   2056    0       2       2       2       82      782     1782    4782    9782    164     165     GMAAAA  CBDAAA  AAAAxx
+5412   2057    0       0       2       12      12      412     1412    412     5412    24      25      EAAAAA  DBDAAA  HHHHxx
+9961   2058    1       1       1       1       61      961     1961    4961    9961    122     123     DTAAAA  EBDAAA  OOOOxx
+6492   2059    0       0       2       12      92      492     492     1492    6492    184     185     SPAAAA  FBDAAA  VVVVxx
+4234   2060    0       2       4       14      34      234     234     4234    4234    68      69      WGAAAA  GBDAAA  AAAAxx
+4922   2061    0       2       2       2       22      922     922     4922    4922    44      45      IHAAAA  HBDAAA  HHHHxx
+6166   2062    0       2       6       6       66      166     166     1166    6166    132     133     EDAAAA  IBDAAA  OOOOxx
+7019   2063    1       3       9       19      19      19      1019    2019    7019    38      39      ZJAAAA  JBDAAA  VVVVxx
+7805   2064    1       1       5       5       5       805     1805    2805    7805    10      11      FOAAAA  KBDAAA  AAAAxx
+9808   2065    0       0       8       8       8       808     1808    4808    9808    16      17      GNAAAA  LBDAAA  HHHHxx
+2550   2066    0       2       0       10      50      550     550     2550    2550    100     101     CUAAAA  MBDAAA  OOOOxx
+8626   2067    0       2       6       6       26      626     626     3626    8626    52      53      UTAAAA  NBDAAA  VVVVxx
+5649   2068    1       1       9       9       49      649     1649    649     5649    98      99      HJAAAA  OBDAAA  AAAAxx
+3117   2069    1       1       7       17      17      117     1117    3117    3117    34      35      XPAAAA  PBDAAA  HHHHxx
+866    2070    0       2       6       6       66      866     866     866     866     132     133     IHAAAA  QBDAAA  OOOOxx
+2323   2071    1       3       3       3       23      323     323     2323    2323    46      47      JLAAAA  RBDAAA  VVVVxx
+5132   2072    0       0       2       12      32      132     1132    132     5132    64      65      KPAAAA  SBDAAA  AAAAxx
+9222   2073    0       2       2       2       22      222     1222    4222    9222    44      45      SQAAAA  TBDAAA  HHHHxx
+3934   2074    0       2       4       14      34      934     1934    3934    3934    68      69      IVAAAA  UBDAAA  OOOOxx
+4845   2075    1       1       5       5       45      845     845     4845    4845    90      91      JEAAAA  VBDAAA  VVVVxx
+7714   2076    0       2       4       14      14      714     1714    2714    7714    28      29      SKAAAA  WBDAAA  AAAAxx
+9818   2077    0       2       8       18      18      818     1818    4818    9818    36      37      QNAAAA  XBDAAA  HHHHxx
+2219   2078    1       3       9       19      19      219     219     2219    2219    38      39      JHAAAA  YBDAAA  OOOOxx
+6573   2079    1       1       3       13      73      573     573     1573    6573    146     147     VSAAAA  ZBDAAA  VVVVxx
+4555   2080    1       3       5       15      55      555     555     4555    4555    110     111     FTAAAA  ACDAAA  AAAAxx
+7306   2081    0       2       6       6       6       306     1306    2306    7306    12      13      AVAAAA  BCDAAA  HHHHxx
+9313   2082    1       1       3       13      13      313     1313    4313    9313    26      27      FUAAAA  CCDAAA  OOOOxx
+3924   2083    0       0       4       4       24      924     1924    3924    3924    48      49      YUAAAA  DCDAAA  VVVVxx
+5176   2084    0       0       6       16      76      176     1176    176     5176    152     153     CRAAAA  ECDAAA  AAAAxx
+9767   2085    1       3       7       7       67      767     1767    4767    9767    134     135     RLAAAA  FCDAAA  HHHHxx
+905    2086    1       1       5       5       5       905     905     905     905     10      11      VIAAAA  GCDAAA  OOOOxx
+8037   2087    1       1       7       17      37      37      37      3037    8037    74      75      DXAAAA  HCDAAA  VVVVxx
+8133   2088    1       1       3       13      33      133     133     3133    8133    66      67      VAAAAA  ICDAAA  AAAAxx
+2954   2089    0       2       4       14      54      954     954     2954    2954    108     109     QJAAAA  JCDAAA  HHHHxx
+7262   2090    0       2       2       2       62      262     1262    2262    7262    124     125     ITAAAA  KCDAAA  OOOOxx
+8768   2091    0       0       8       8       68      768     768     3768    8768    136     137     GZAAAA  LCDAAA  VVVVxx
+6953   2092    1       1       3       13      53      953     953     1953    6953    106     107     LHAAAA  MCDAAA  AAAAxx
+1984   2093    0       0       4       4       84      984     1984    1984    1984    168     169     IYAAAA  NCDAAA  HHHHxx
+9348   2094    0       0       8       8       48      348     1348    4348    9348    96      97      OVAAAA  OCDAAA  OOOOxx
+7769   2095    1       1       9       9       69      769     1769    2769    7769    138     139     VMAAAA  PCDAAA  VVVVxx
+2994   2096    0       2       4       14      94      994     994     2994    2994    188     189     ELAAAA  QCDAAA  AAAAxx
+5938   2097    0       2       8       18      38      938     1938    938     5938    76      77      KUAAAA  RCDAAA  HHHHxx
+556    2098    0       0       6       16      56      556     556     556     556     112     113     KVAAAA  SCDAAA  OOOOxx
+2577   2099    1       1       7       17      77      577     577     2577    2577    154     155     DVAAAA  TCDAAA  VVVVxx
+8733   2100    1       1       3       13      33      733     733     3733    8733    66      67      XXAAAA  UCDAAA  AAAAxx
+3108   2101    0       0       8       8       8       108     1108    3108    3108    16      17      OPAAAA  VCDAAA  HHHHxx
+4166   2102    0       2       6       6       66      166     166     4166    4166    132     133     GEAAAA  WCDAAA  OOOOxx
+3170   2103    0       2       0       10      70      170     1170    3170    3170    140     141     YRAAAA  XCDAAA  VVVVxx
+8118   2104    0       2       8       18      18      118     118     3118    8118    36      37      GAAAAA  YCDAAA  AAAAxx
+8454   2105    0       2       4       14      54      454     454     3454    8454    108     109     ENAAAA  ZCDAAA  HHHHxx
+5338   2106    0       2       8       18      38      338     1338    338     5338    76      77      IXAAAA  ADDAAA  OOOOxx
+402    2107    0       2       2       2       2       402     402     402     402     4       5       MPAAAA  BDDAAA  VVVVxx
+5673   2108    1       1       3       13      73      673     1673    673     5673    146     147     FKAAAA  CDDAAA  AAAAxx
+4324   2109    0       0       4       4       24      324     324     4324    4324    48      49      IKAAAA  DDDAAA  HHHHxx
+1943   2110    1       3       3       3       43      943     1943    1943    1943    86      87      TWAAAA  EDDAAA  OOOOxx
+7703   2111    1       3       3       3       3       703     1703    2703    7703    6       7       HKAAAA  FDDAAA  VVVVxx
+7180   2112    0       0       0       0       80      180     1180    2180    7180    160     161     EQAAAA  GDDAAA  AAAAxx
+5478   2113    0       2       8       18      78      478     1478    478     5478    156     157     SCAAAA  HDDAAA  HHHHxx
+5775   2114    1       3       5       15      75      775     1775    775     5775    150     151     DOAAAA  IDDAAA  OOOOxx
+6952   2115    0       0       2       12      52      952     952     1952    6952    104     105     KHAAAA  JDDAAA  VVVVxx
+9022   2116    0       2       2       2       22      22      1022    4022    9022    44      45      AJAAAA  KDDAAA  AAAAxx
+547    2117    1       3       7       7       47      547     547     547     547     94      95      BVAAAA  LDDAAA  HHHHxx
+5877   2118    1       1       7       17      77      877     1877    877     5877    154     155     BSAAAA  MDDAAA  OOOOxx
+9580   2119    0       0       0       0       80      580     1580    4580    9580    160     161     MEAAAA  NDDAAA  VVVVxx
+6094   2120    0       2       4       14      94      94      94      1094    6094    188     189     KAAAAA  ODDAAA  AAAAxx
+3398   2121    0       2       8       18      98      398     1398    3398    3398    196     197     SAAAAA  PDDAAA  HHHHxx
+4574   2122    0       2       4       14      74      574     574     4574    4574    148     149     YTAAAA  QDDAAA  OOOOxx
+3675   2123    1       3       5       15      75      675     1675    3675    3675    150     151     JLAAAA  RDDAAA  VVVVxx
+6413   2124    1       1       3       13      13      413     413     1413    6413    26      27      RMAAAA  SDDAAA  AAAAxx
+9851   2125    1       3       1       11      51      851     1851    4851    9851    102     103     XOAAAA  TDDAAA  HHHHxx
+126    2126    0       2       6       6       26      126     126     126     126     52      53      WEAAAA  UDDAAA  OOOOxx
+6803   2127    1       3       3       3       3       803     803     1803    6803    6       7       RBAAAA  VDDAAA  VVVVxx
+6949   2128    1       1       9       9       49      949     949     1949    6949    98      99      HHAAAA  WDDAAA  AAAAxx
+115    2129    1       3       5       15      15      115     115     115     115     30      31      LEAAAA  XDDAAA  HHHHxx
+4165   2130    1       1       5       5       65      165     165     4165    4165    130     131     FEAAAA  YDDAAA  OOOOxx
+201    2131    1       1       1       1       1       201     201     201     201     2       3       THAAAA  ZDDAAA  VVVVxx
+9324   2132    0       0       4       4       24      324     1324    4324    9324    48      49      QUAAAA  AEDAAA  AAAAxx
+6562   2133    0       2       2       2       62      562     562     1562    6562    124     125     KSAAAA  BEDAAA  HHHHxx
+1917   2134    1       1       7       17      17      917     1917    1917    1917    34      35      TVAAAA  CEDAAA  OOOOxx
+558    2135    0       2       8       18      58      558     558     558     558     116     117     MVAAAA  DEDAAA  VVVVxx
+8515   2136    1       3       5       15      15      515     515     3515    8515    30      31      NPAAAA  EEDAAA  AAAAxx
+6321   2137    1       1       1       1       21      321     321     1321    6321    42      43      DJAAAA  FEDAAA  HHHHxx
+6892   2138    0       0       2       12      92      892     892     1892    6892    184     185     CFAAAA  GEDAAA  OOOOxx
+1001   2139    1       1       1       1       1       1       1001    1001    1001    2       3       NMAAAA  HEDAAA  VVVVxx
+2858   2140    0       2       8       18      58      858     858     2858    2858    116     117     YFAAAA  IEDAAA  AAAAxx
+2434   2141    0       2       4       14      34      434     434     2434    2434    68      69      QPAAAA  JEDAAA  HHHHxx
+4460   2142    0       0       0       0       60      460     460     4460    4460    120     121     OPAAAA  KEDAAA  OOOOxx
+5447   2143    1       3       7       7       47      447     1447    447     5447    94      95      NBAAAA  LEDAAA  VVVVxx
+3799   2144    1       3       9       19      99      799     1799    3799    3799    198     199     DQAAAA  MEDAAA  AAAAxx
+4310   2145    0       2       0       10      10      310     310     4310    4310    20      21      UJAAAA  NEDAAA  HHHHxx
+405    2146    1       1       5       5       5       405     405     405     405     10      11      PPAAAA  OEDAAA  OOOOxx
+4573   2147    1       1       3       13      73      573     573     4573    4573    146     147     XTAAAA  PEDAAA  VVVVxx
+706    2148    0       2       6       6       6       706     706     706     706     12      13      EBAAAA  QEDAAA  AAAAxx
+7619   2149    1       3       9       19      19      619     1619    2619    7619    38      39      BHAAAA  REDAAA  HHHHxx
+7959   2150    1       3       9       19      59      959     1959    2959    7959    118     119     DUAAAA  SEDAAA  OOOOxx
+6712   2151    0       0       2       12      12      712     712     1712    6712    24      25      EYAAAA  TEDAAA  VVVVxx
+6959   2152    1       3       9       19      59      959     959     1959    6959    118     119     RHAAAA  UEDAAA  AAAAxx
+9791   2153    1       3       1       11      91      791     1791    4791    9791    182     183     PMAAAA  VEDAAA  HHHHxx
+2112   2154    0       0       2       12      12      112     112     2112    2112    24      25      GDAAAA  WEDAAA  OOOOxx
+9114   2155    0       2       4       14      14      114     1114    4114    9114    28      29      OMAAAA  XEDAAA  VVVVxx
+3506   2156    0       2       6       6       6       506     1506    3506    3506    12      13      WEAAAA  YEDAAA  AAAAxx
+5002   2157    0       2       2       2       2       2       1002    2       5002    4       5       KKAAAA  ZEDAAA  HHHHxx
+3518   2158    0       2       8       18      18      518     1518    3518    3518    36      37      IFAAAA  AFDAAA  OOOOxx
+602    2159    0       2       2       2       2       602     602     602     602     4       5       EXAAAA  BFDAAA  VVVVxx
+9060   2160    0       0       0       0       60      60      1060    4060    9060    120     121     MKAAAA  CFDAAA  AAAAxx
+3292   2161    0       0       2       12      92      292     1292    3292    3292    184     185     QWAAAA  DFDAAA  HHHHxx
+77     2162    1       1       7       17      77      77      77      77      77      154     155     ZCAAAA  EFDAAA  OOOOxx
+1420   2163    0       0       0       0       20      420     1420    1420    1420    40      41      QCAAAA  FFDAAA  VVVVxx
+6001   2164    1       1       1       1       1       1       1       1001    6001    2       3       VWAAAA  GFDAAA  AAAAxx
+7477   2165    1       1       7       17      77      477     1477    2477    7477    154     155     PBAAAA  HFDAAA  HHHHxx
+6655   2166    1       3       5       15      55      655     655     1655    6655    110     111     ZVAAAA  IFDAAA  OOOOxx
+7845   2167    1       1       5       5       45      845     1845    2845    7845    90      91      TPAAAA  JFDAAA  VVVVxx
+8484   2168    0       0       4       4       84      484     484     3484    8484    168     169     IOAAAA  KFDAAA  AAAAxx
+4345   2169    1       1       5       5       45      345     345     4345    4345    90      91      DLAAAA  LFDAAA  HHHHxx
+4250   2170    0       2       0       10      50      250     250     4250    4250    100     101     MHAAAA  MFDAAA  OOOOxx
+2391   2171    1       3       1       11      91      391     391     2391    2391    182     183     ZNAAAA  NFDAAA  VVVVxx
+6884   2172    0       0       4       4       84      884     884     1884    6884    168     169     UEAAAA  OFDAAA  AAAAxx
+7270   2173    0       2       0       10      70      270     1270    2270    7270    140     141     QTAAAA  PFDAAA  HHHHxx
+2499   2174    1       3       9       19      99      499     499     2499    2499    198     199     DSAAAA  QFDAAA  OOOOxx
+7312   2175    0       0       2       12      12      312     1312    2312    7312    24      25      GVAAAA  RFDAAA  VVVVxx
+7113   2176    1       1       3       13      13      113     1113    2113    7113    26      27      PNAAAA  SFDAAA  AAAAxx
+6695   2177    1       3       5       15      95      695     695     1695    6695    190     191     NXAAAA  TFDAAA  HHHHxx
+6521   2178    1       1       1       1       21      521     521     1521    6521    42      43      VQAAAA  UFDAAA  OOOOxx
+272    2179    0       0       2       12      72      272     272     272     272     144     145     MKAAAA  VFDAAA  VVVVxx
+9976   2180    0       0       6       16      76      976     1976    4976    9976    152     153     STAAAA  WFDAAA  AAAAxx
+992    2181    0       0       2       12      92      992     992     992     992     184     185     EMAAAA  XFDAAA  HHHHxx
+6158   2182    0       2       8       18      58      158     158     1158    6158    116     117     WCAAAA  YFDAAA  OOOOxx
+3281   2183    1       1       1       1       81      281     1281    3281    3281    162     163     FWAAAA  ZFDAAA  VVVVxx
+7446   2184    0       2       6       6       46      446     1446    2446    7446    92      93      KAAAAA  AGDAAA  AAAAxx
+4679   2185    1       3       9       19      79      679     679     4679    4679    158     159     ZXAAAA  BGDAAA  HHHHxx
+5203   2186    1       3       3       3       3       203     1203    203     5203    6       7       DSAAAA  CGDAAA  OOOOxx
+9874   2187    0       2       4       14      74      874     1874    4874    9874    148     149     UPAAAA  DGDAAA  VVVVxx
+8371   2188    1       3       1       11      71      371     371     3371    8371    142     143     ZJAAAA  EGDAAA  AAAAxx
+9086   2189    0       2       6       6       86      86      1086    4086    9086    172     173     MLAAAA  FGDAAA  HHHHxx
+430    2190    0       2       0       10      30      430     430     430     430     60      61      OQAAAA  GGDAAA  OOOOxx
+8749   2191    1       1       9       9       49      749     749     3749    8749    98      99      NYAAAA  HGDAAA  VVVVxx
+577    2192    1       1       7       17      77      577     577     577     577     154     155     FWAAAA  IGDAAA  AAAAxx
+4884   2193    0       0       4       4       84      884     884     4884    4884    168     169     WFAAAA  JGDAAA  HHHHxx
+3421   2194    1       1       1       1       21      421     1421    3421    3421    42      43      PBAAAA  KGDAAA  OOOOxx
+2812   2195    0       0       2       12      12      812     812     2812    2812    24      25      EEAAAA  LGDAAA  VVVVxx
+5958   2196    0       2       8       18      58      958     1958    958     5958    116     117     EVAAAA  MGDAAA  AAAAxx
+9901   2197    1       1       1       1       1       901     1901    4901    9901    2       3       VQAAAA  NGDAAA  HHHHxx
+8478   2198    0       2       8       18      78      478     478     3478    8478    156     157     COAAAA  OGDAAA  OOOOxx
+6545   2199    1       1       5       5       45      545     545     1545    6545    90      91      TRAAAA  PGDAAA  VVVVxx
+1479   2200    1       3       9       19      79      479     1479    1479    1479    158     159     XEAAAA  QGDAAA  AAAAxx
+1046   2201    0       2       6       6       46      46      1046    1046    1046    92      93      GOAAAA  RGDAAA  HHHHxx
+6372   2202    0       0       2       12      72      372     372     1372    6372    144     145     CLAAAA  SGDAAA  OOOOxx
+8206   2203    0       2       6       6       6       206     206     3206    8206    12      13      QDAAAA  TGDAAA  VVVVxx
+9544   2204    0       0       4       4       44      544     1544    4544    9544    88      89      CDAAAA  UGDAAA  AAAAxx
+9287   2205    1       3       7       7       87      287     1287    4287    9287    174     175     FTAAAA  VGDAAA  HHHHxx
+6786   2206    0       2       6       6       86      786     786     1786    6786    172     173     ABAAAA  WGDAAA  OOOOxx
+6511   2207    1       3       1       11      11      511     511     1511    6511    22      23      LQAAAA  XGDAAA  VVVVxx
+603    2208    1       3       3       3       3       603     603     603     603     6       7       FXAAAA  YGDAAA  AAAAxx
+2022   2209    0       2       2       2       22      22      22      2022    2022    44      45      UZAAAA  ZGDAAA  HHHHxx
+2086   2210    0       2       6       6       86      86      86      2086    2086    172     173     GCAAAA  AHDAAA  OOOOxx
+1969   2211    1       1       9       9       69      969     1969    1969    1969    138     139     TXAAAA  BHDAAA  VVVVxx
+4841   2212    1       1       1       1       41      841     841     4841    4841    82      83      FEAAAA  CHDAAA  AAAAxx
+5845   2213    1       1       5       5       45      845     1845    845     5845    90      91      VQAAAA  DHDAAA  HHHHxx
+4635   2214    1       3       5       15      35      635     635     4635    4635    70      71      HWAAAA  EHDAAA  OOOOxx
+4658   2215    0       2       8       18      58      658     658     4658    4658    116     117     EXAAAA  FHDAAA  VVVVxx
+2896   2216    0       0       6       16      96      896     896     2896    2896    192     193     KHAAAA  GHDAAA  AAAAxx
+5179   2217    1       3       9       19      79      179     1179    179     5179    158     159     FRAAAA  HHDAAA  HHHHxx
+8667   2218    1       3       7       7       67      667     667     3667    8667    134     135     JVAAAA  IHDAAA  OOOOxx
+7294   2219    0       2       4       14      94      294     1294    2294    7294    188     189     OUAAAA  JHDAAA  VVVVxx
+3706   2220    0       2       6       6       6       706     1706    3706    3706    12      13      OMAAAA  KHDAAA  AAAAxx
+8389   2221    1       1       9       9       89      389     389     3389    8389    178     179     RKAAAA  LHDAAA  HHHHxx
+2486   2222    0       2       6       6       86      486     486     2486    2486    172     173     QRAAAA  MHDAAA  OOOOxx
+8743   2223    1       3       3       3       43      743     743     3743    8743    86      87      HYAAAA  NHDAAA  VVVVxx
+2777   2224    1       1       7       17      77      777     777     2777    2777    154     155     VCAAAA  OHDAAA  AAAAxx
+2113   2225    1       1       3       13      13      113     113     2113    2113    26      27      HDAAAA  PHDAAA  HHHHxx
+2076   2226    0       0       6       16      76      76      76      2076    2076    152     153     WBAAAA  QHDAAA  OOOOxx
+2300   2227    0       0       0       0       0       300     300     2300    2300    0       1       MKAAAA  RHDAAA  VVVVxx
+6894   2228    0       2       4       14      94      894     894     1894    6894    188     189     EFAAAA  SHDAAA  AAAAxx
+6939   2229    1       3       9       19      39      939     939     1939    6939    78      79      XGAAAA  THDAAA  HHHHxx
+446    2230    0       2       6       6       46      446     446     446     446     92      93      ERAAAA  UHDAAA  OOOOxx
+6218   2231    0       2       8       18      18      218     218     1218    6218    36      37      EFAAAA  VHDAAA  VVVVxx
+1295   2232    1       3       5       15      95      295     1295    1295    1295    190     191     VXAAAA  WHDAAA  AAAAxx
+5135   2233    1       3       5       15      35      135     1135    135     5135    70      71      NPAAAA  XHDAAA  HHHHxx
+8122   2234    0       2       2       2       22      122     122     3122    8122    44      45      KAAAAA  YHDAAA  OOOOxx
+316    2235    0       0       6       16      16      316     316     316     316     32      33      EMAAAA  ZHDAAA  VVVVxx
+514    2236    0       2       4       14      14      514     514     514     514     28      29      UTAAAA  AIDAAA  AAAAxx
+7970   2237    0       2       0       10      70      970     1970    2970    7970    140     141     OUAAAA  BIDAAA  HHHHxx
+9350   2238    0       2       0       10      50      350     1350    4350    9350    100     101     QVAAAA  CIDAAA  OOOOxx
+3700   2239    0       0       0       0       0       700     1700    3700    3700    0       1       IMAAAA  DIDAAA  VVVVxx
+582    2240    0       2       2       2       82      582     582     582     582     164     165     KWAAAA  EIDAAA  AAAAxx
+9722   2241    0       2       2       2       22      722     1722    4722    9722    44      45      YJAAAA  FIDAAA  HHHHxx
+7398   2242    0       2       8       18      98      398     1398    2398    7398    196     197     OYAAAA  GIDAAA  OOOOxx
+2265   2243    1       1       5       5       65      265     265     2265    2265    130     131     DJAAAA  HIDAAA  VVVVxx
+3049   2244    1       1       9       9       49      49      1049    3049    3049    98      99      HNAAAA  IIDAAA  AAAAxx
+9121   2245    1       1       1       1       21      121     1121    4121    9121    42      43      VMAAAA  JIDAAA  HHHHxx
+4275   2246    1       3       5       15      75      275     275     4275    4275    150     151     LIAAAA  KIDAAA  OOOOxx
+6567   2247    1       3       7       7       67      567     567     1567    6567    134     135     PSAAAA  LIDAAA  VVVVxx
+6755   2248    1       3       5       15      55      755     755     1755    6755    110     111     VZAAAA  MIDAAA  AAAAxx
+4535   2249    1       3       5       15      35      535     535     4535    4535    70      71      LSAAAA  NIDAAA  HHHHxx
+7968   2250    0       0       8       8       68      968     1968    2968    7968    136     137     MUAAAA  OIDAAA  OOOOxx
+3412   2251    0       0       2       12      12      412     1412    3412    3412    24      25      GBAAAA  PIDAAA  VVVVxx
+6112   2252    0       0       2       12      12      112     112     1112    6112    24      25      CBAAAA  QIDAAA  AAAAxx
+6805   2253    1       1       5       5       5       805     805     1805    6805    10      11      TBAAAA  RIDAAA  HHHHxx
+2880   2254    0       0       0       0       80      880     880     2880    2880    160     161     UGAAAA  SIDAAA  OOOOxx
+7710   2255    0       2       0       10      10      710     1710    2710    7710    20      21      OKAAAA  TIDAAA  VVVVxx
+7949   2256    1       1       9       9       49      949     1949    2949    7949    98      99      TTAAAA  UIDAAA  AAAAxx
+7043   2257    1       3       3       3       43      43      1043    2043    7043    86      87      XKAAAA  VIDAAA  HHHHxx
+9012   2258    0       0       2       12      12      12      1012    4012    9012    24      25      QIAAAA  WIDAAA  OOOOxx
+878    2259    0       2       8       18      78      878     878     878     878     156     157     UHAAAA  XIDAAA  VVVVxx
+7930   2260    0       2       0       10      30      930     1930    2930    7930    60      61      ATAAAA  YIDAAA  AAAAxx
+667    2261    1       3       7       7       67      667     667     667     667     134     135     RZAAAA  ZIDAAA  HHHHxx
+1905   2262    1       1       5       5       5       905     1905    1905    1905    10      11      HVAAAA  AJDAAA  OOOOxx
+4958   2263    0       2       8       18      58      958     958     4958    4958    116     117     SIAAAA  BJDAAA  VVVVxx
+2973   2264    1       1       3       13      73      973     973     2973    2973    146     147     JKAAAA  CJDAAA  AAAAxx
+3631   2265    1       3       1       11      31      631     1631    3631    3631    62      63      RJAAAA  DJDAAA  HHHHxx
+5868   2266    0       0       8       8       68      868     1868    868     5868    136     137     SRAAAA  EJDAAA  OOOOxx
+2873   2267    1       1       3       13      73      873     873     2873    2873    146     147     NGAAAA  FJDAAA  VVVVxx
+6941   2268    1       1       1       1       41      941     941     1941    6941    82      83      ZGAAAA  GJDAAA  AAAAxx
+6384   2269    0       0       4       4       84      384     384     1384    6384    168     169     OLAAAA  HJDAAA  HHHHxx
+3806   2270    0       2       6       6       6       806     1806    3806    3806    12      13      KQAAAA  IJDAAA  OOOOxx
+5079   2271    1       3       9       19      79      79      1079    79      5079    158     159     JNAAAA  JJDAAA  VVVVxx
+1970   2272    0       2       0       10      70      970     1970    1970    1970    140     141     UXAAAA  KJDAAA  AAAAxx
+7810   2273    0       2       0       10      10      810     1810    2810    7810    20      21      KOAAAA  LJDAAA  HHHHxx
+4639   2274    1       3       9       19      39      639     639     4639    4639    78      79      LWAAAA  MJDAAA  OOOOxx
+6527   2275    1       3       7       7       27      527     527     1527    6527    54      55      BRAAAA  NJDAAA  VVVVxx
+8079   2276    1       3       9       19      79      79      79      3079    8079    158     159     TYAAAA  OJDAAA  AAAAxx
+2740   2277    0       0       0       0       40      740     740     2740    2740    80      81      KBAAAA  PJDAAA  HHHHxx
+2337   2278    1       1       7       17      37      337     337     2337    2337    74      75      XLAAAA  QJDAAA  OOOOxx
+6670   2279    0       2       0       10      70      670     670     1670    6670    140     141     OWAAAA  RJDAAA  VVVVxx
+2345   2280    1       1       5       5       45      345     345     2345    2345    90      91      FMAAAA  SJDAAA  AAAAxx
+401    2281    1       1       1       1       1       401     401     401     401     2       3       LPAAAA  TJDAAA  HHHHxx
+2704   2282    0       0       4       4       4       704     704     2704    2704    8       9       AAAAAA  UJDAAA  OOOOxx
+5530   2283    0       2       0       10      30      530     1530    530     5530    60      61      SEAAAA  VJDAAA  VVVVxx
+51     2284    1       3       1       11      51      51      51      51      51      102     103     ZBAAAA  WJDAAA  AAAAxx
+4282   2285    0       2       2       2       82      282     282     4282    4282    164     165     SIAAAA  XJDAAA  HHHHxx
+7336   2286    0       0       6       16      36      336     1336    2336    7336    72      73      EWAAAA  YJDAAA  OOOOxx
+8320   2287    0       0       0       0       20      320     320     3320    8320    40      41      AIAAAA  ZJDAAA  VVVVxx
+7772   2288    0       0       2       12      72      772     1772    2772    7772    144     145     YMAAAA  AKDAAA  AAAAxx
+1894   2289    0       2       4       14      94      894     1894    1894    1894    188     189     WUAAAA  BKDAAA  HHHHxx
+2320   2290    0       0       0       0       20      320     320     2320    2320    40      41      GLAAAA  CKDAAA  OOOOxx
+6232   2291    0       0       2       12      32      232     232     1232    6232    64      65      SFAAAA  DKDAAA  VVVVxx
+2833   2292    1       1       3       13      33      833     833     2833    2833    66      67      ZEAAAA  EKDAAA  AAAAxx
+8265   2293    1       1       5       5       65      265     265     3265    8265    130     131     XFAAAA  FKDAAA  HHHHxx
+4589   2294    1       1       9       9       89      589     589     4589    4589    178     179     NUAAAA  GKDAAA  OOOOxx
+8182   2295    0       2       2       2       82      182     182     3182    8182    164     165     SCAAAA  HKDAAA  VVVVxx
+8337   2296    1       1       7       17      37      337     337     3337    8337    74      75      RIAAAA  IKDAAA  AAAAxx
+8210   2297    0       2       0       10      10      210     210     3210    8210    20      21      UDAAAA  JKDAAA  HHHHxx
+1406   2298    0       2       6       6       6       406     1406    1406    1406    12      13      CCAAAA  KKDAAA  OOOOxx
+4463   2299    1       3       3       3       63      463     463     4463    4463    126     127     RPAAAA  LKDAAA  VVVVxx
+4347   2300    1       3       7       7       47      347     347     4347    4347    94      95      FLAAAA  MKDAAA  AAAAxx
+181    2301    1       1       1       1       81      181     181     181     181     162     163     ZGAAAA  NKDAAA  HHHHxx
+9986   2302    0       2       6       6       86      986     1986    4986    9986    172     173     CUAAAA  OKDAAA  OOOOxx
+661    2303    1       1       1       1       61      661     661     661     661     122     123     LZAAAA  PKDAAA  VVVVxx
+4105   2304    1       1       5       5       5       105     105     4105    4105    10      11      XBAAAA  QKDAAA  AAAAxx
+2187   2305    1       3       7       7       87      187     187     2187    2187    174     175     DGAAAA  RKDAAA  HHHHxx
+1628   2306    0       0       8       8       28      628     1628    1628    1628    56      57      QKAAAA  SKDAAA  OOOOxx
+3119   2307    1       3       9       19      19      119     1119    3119    3119    38      39      ZPAAAA  TKDAAA  VVVVxx
+6804   2308    0       0       4       4       4       804     804     1804    6804    8       9       SBAAAA  UKDAAA  AAAAxx
+9918   2309    0       2       8       18      18      918     1918    4918    9918    36      37      MRAAAA  VKDAAA  HHHHxx
+8916   2310    0       0       6       16      16      916     916     3916    8916    32      33      YEAAAA  WKDAAA  OOOOxx
+6057   2311    1       1       7       17      57      57      57      1057    6057    114     115     ZYAAAA  XKDAAA  VVVVxx
+3622   2312    0       2       2       2       22      622     1622    3622    3622    44      45      IJAAAA  YKDAAA  AAAAxx
+9168   2313    0       0       8       8       68      168     1168    4168    9168    136     137     QOAAAA  ZKDAAA  HHHHxx
+3720   2314    0       0       0       0       20      720     1720    3720    3720    40      41      CNAAAA  ALDAAA  OOOOxx
+9927   2315    1       3       7       7       27      927     1927    4927    9927    54      55      VRAAAA  BLDAAA  VVVVxx
+5616   2316    0       0       6       16      16      616     1616    616     5616    32      33      AIAAAA  CLDAAA  AAAAxx
+5210   2317    0       2       0       10      10      210     1210    210     5210    20      21      KSAAAA  DLDAAA  HHHHxx
+636    2318    0       0       6       16      36      636     636     636     636     72      73      MYAAAA  ELDAAA  OOOOxx
+9936   2319    0       0       6       16      36      936     1936    4936    9936    72      73      ESAAAA  FLDAAA  VVVVxx
+2316   2320    0       0       6       16      16      316     316     2316    2316    32      33      CLAAAA  GLDAAA  AAAAxx
+4363   2321    1       3       3       3       63      363     363     4363    4363    126     127     VLAAAA  HLDAAA  HHHHxx
+7657   2322    1       1       7       17      57      657     1657    2657    7657    114     115     NIAAAA  ILDAAA  OOOOxx
+697    2323    1       1       7       17      97      697     697     697     697     194     195     VAAAAA  JLDAAA  VVVVxx
+912    2324    0       0       2       12      12      912     912     912     912     24      25      CJAAAA  KLDAAA  AAAAxx
+8806   2325    0       2       6       6       6       806     806     3806    8806    12      13      SAAAAA  LLDAAA  HHHHxx
+9698   2326    0       2       8       18      98      698     1698    4698    9698    196     197     AJAAAA  MLDAAA  OOOOxx
+6191   2327    1       3       1       11      91      191     191     1191    6191    182     183     DEAAAA  NLDAAA  VVVVxx
+1188   2328    0       0       8       8       88      188     1188    1188    1188    176     177     STAAAA  OLDAAA  AAAAxx
+7676   2329    0       0       6       16      76      676     1676    2676    7676    152     153     GJAAAA  PLDAAA  HHHHxx
+7073   2330    1       1       3       13      73      73      1073    2073    7073    146     147     BMAAAA  QLDAAA  OOOOxx
+8019   2331    1       3       9       19      19      19      19      3019    8019    38      39      LWAAAA  RLDAAA  VVVVxx
+4726   2332    0       2       6       6       26      726     726     4726    4726    52      53      UZAAAA  SLDAAA  AAAAxx
+4648   2333    0       0       8       8       48      648     648     4648    4648    96      97      UWAAAA  TLDAAA  HHHHxx
+3227   2334    1       3       7       7       27      227     1227    3227    3227    54      55      DUAAAA  ULDAAA  OOOOxx
+7232   2335    0       0       2       12      32      232     1232    2232    7232    64      65      ESAAAA  VLDAAA  VVVVxx
+9761   2336    1       1       1       1       61      761     1761    4761    9761    122     123     LLAAAA  WLDAAA  AAAAxx
+3105   2337    1       1       5       5       5       105     1105    3105    3105    10      11      LPAAAA  XLDAAA  HHHHxx
+5266   2338    0       2       6       6       66      266     1266    266     5266    132     133     OUAAAA  YLDAAA  OOOOxx
+6788   2339    0       0       8       8       88      788     788     1788    6788    176     177     CBAAAA  ZLDAAA  VVVVxx
+2442   2340    0       2       2       2       42      442     442     2442    2442    84      85      YPAAAA  AMDAAA  AAAAxx
+8198   2341    0       2       8       18      98      198     198     3198    8198    196     197     IDAAAA  BMDAAA  HHHHxx
+5806   2342    0       2       6       6       6       806     1806    806     5806    12      13      IPAAAA  CMDAAA  OOOOxx
+8928   2343    0       0       8       8       28      928     928     3928    8928    56      57      KFAAAA  DMDAAA  VVVVxx
+1657   2344    1       1       7       17      57      657     1657    1657    1657    114     115     TLAAAA  EMDAAA  AAAAxx
+9164   2345    0       0       4       4       64      164     1164    4164    9164    128     129     MOAAAA  FMDAAA  HHHHxx
+1851   2346    1       3       1       11      51      851     1851    1851    1851    102     103     FTAAAA  GMDAAA  OOOOxx
+4744   2347    0       0       4       4       44      744     744     4744    4744    88      89      MAAAAA  HMDAAA  VVVVxx
+8055   2348    1       3       5       15      55      55      55      3055    8055    110     111     VXAAAA  IMDAAA  AAAAxx
+1533   2349    1       1       3       13      33      533     1533    1533    1533    66      67      ZGAAAA  JMDAAA  HHHHxx
+1260   2350    0       0       0       0       60      260     1260    1260    1260    120     121     MWAAAA  KMDAAA  OOOOxx
+1290   2351    0       2       0       10      90      290     1290    1290    1290    180     181     QXAAAA  LMDAAA  VVVVxx
+297    2352    1       1       7       17      97      297     297     297     297     194     195     LLAAAA  MMDAAA  AAAAxx
+4145   2353    1       1       5       5       45      145     145     4145    4145    90      91      LDAAAA  NMDAAA  HHHHxx
+863    2354    1       3       3       3       63      863     863     863     863     126     127     FHAAAA  OMDAAA  OOOOxx
+3423   2355    1       3       3       3       23      423     1423    3423    3423    46      47      RBAAAA  PMDAAA  VVVVxx
+8750   2356    0       2       0       10      50      750     750     3750    8750    100     101     OYAAAA  QMDAAA  AAAAxx
+3546   2357    0       2       6       6       46      546     1546    3546    3546    92      93      KGAAAA  RMDAAA  HHHHxx
+3678   2358    0       2       8       18      78      678     1678    3678    3678    156     157     MLAAAA  SMDAAA  OOOOxx
+5313   2359    1       1       3       13      13      313     1313    313     5313    26      27      JWAAAA  TMDAAA  VVVVxx
+6233   2360    1       1       3       13      33      233     233     1233    6233    66      67      TFAAAA  UMDAAA  AAAAxx
+5802   2361    0       2       2       2       2       802     1802    802     5802    4       5       EPAAAA  VMDAAA  HHHHxx
+7059   2362    1       3       9       19      59      59      1059    2059    7059    118     119     NLAAAA  WMDAAA  OOOOxx
+6481   2363    1       1       1       1       81      481     481     1481    6481    162     163     HPAAAA  XMDAAA  VVVVxx
+1596   2364    0       0       6       16      96      596     1596    1596    1596    192     193     KJAAAA  YMDAAA  AAAAxx
+8181   2365    1       1       1       1       81      181     181     3181    8181    162     163     RCAAAA  ZMDAAA  HHHHxx
+5368   2366    0       0       8       8       68      368     1368    368     5368    136     137     MYAAAA  ANDAAA  OOOOxx
+9416   2367    0       0       6       16      16      416     1416    4416    9416    32      33      EYAAAA  BNDAAA  VVVVxx
+9521   2368    1       1       1       1       21      521     1521    4521    9521    42      43      FCAAAA  CNDAAA  AAAAxx
+1042   2369    0       2       2       2       42      42      1042    1042    1042    84      85      COAAAA  DNDAAA  HHHHxx
+4503   2370    1       3       3       3       3       503     503     4503    4503    6       7       FRAAAA  ENDAAA  OOOOxx
+3023   2371    1       3       3       3       23      23      1023    3023    3023    46      47      HMAAAA  FNDAAA  VVVVxx
+1976   2372    0       0       6       16      76      976     1976    1976    1976    152     153     AYAAAA  GNDAAA  AAAAxx
+5610   2373    0       2       0       10      10      610     1610    610     5610    20      21      UHAAAA  HNDAAA  HHHHxx
+7410   2374    0       2       0       10      10      410     1410    2410    7410    20      21      AZAAAA  INDAAA  OOOOxx
+7872   2375    0       0       2       12      72      872     1872    2872    7872    144     145     UQAAAA  JNDAAA  VVVVxx
+8591   2376    1       3       1       11      91      591     591     3591    8591    182     183     LSAAAA  KNDAAA  AAAAxx
+1804   2377    0       0       4       4       4       804     1804    1804    1804    8       9       KRAAAA  LNDAAA  HHHHxx
+5299   2378    1       3       9       19      99      299     1299    299     5299    198     199     VVAAAA  MNDAAA  OOOOxx
+4695   2379    1       3       5       15      95      695     695     4695    4695    190     191     PYAAAA  NNDAAA  VVVVxx
+2672   2380    0       0       2       12      72      672     672     2672    2672    144     145     UYAAAA  ONDAAA  AAAAxx
+585    2381    1       1       5       5       85      585     585     585     585     170     171     NWAAAA  PNDAAA  HHHHxx
+8622   2382    0       2       2       2       22      622     622     3622    8622    44      45      QTAAAA  QNDAAA  OOOOxx
+3780   2383    0       0       0       0       80      780     1780    3780    3780    160     161     KPAAAA  RNDAAA  VVVVxx
+7941   2384    1       1       1       1       41      941     1941    2941    7941    82      83      LTAAAA  SNDAAA  AAAAxx
+3305   2385    1       1       5       5       5       305     1305    3305    3305    10      11      DXAAAA  TNDAAA  HHHHxx
+8653   2386    1       1       3       13      53      653     653     3653    8653    106     107     VUAAAA  UNDAAA  OOOOxx
+5756   2387    0       0       6       16      56      756     1756    756     5756    112     113     KNAAAA  VNDAAA  VVVVxx
+576    2388    0       0       6       16      76      576     576     576     576     152     153     EWAAAA  WNDAAA  AAAAxx
+1915   2389    1       3       5       15      15      915     1915    1915    1915    30      31      RVAAAA  XNDAAA  HHHHxx
+4627   2390    1       3       7       7       27      627     627     4627    4627    54      55      ZVAAAA  YNDAAA  OOOOxx
+920    2391    0       0       0       0       20      920     920     920     920     40      41      KJAAAA  ZNDAAA  VVVVxx
+2537   2392    1       1       7       17      37      537     537     2537    2537    74      75      PTAAAA  AODAAA  AAAAxx
+50     2393    0       2       0       10      50      50      50      50      50      100     101     YBAAAA  BODAAA  HHHHxx
+1313   2394    1       1       3       13      13      313     1313    1313    1313    26      27      NYAAAA  CODAAA  OOOOxx
+8542   2395    0       2       2       2       42      542     542     3542    8542    84      85      OQAAAA  DODAAA  VVVVxx
+6428   2396    0       0       8       8       28      428     428     1428    6428    56      57      GNAAAA  EODAAA  AAAAxx
+4351   2397    1       3       1       11      51      351     351     4351    4351    102     103     JLAAAA  FODAAA  HHHHxx
+2050   2398    0       2       0       10      50      50      50      2050    2050    100     101     WAAAAA  GODAAA  OOOOxx
+5162   2399    0       2       2       2       62      162     1162    162     5162    124     125     OQAAAA  HODAAA  VVVVxx
+8229   2400    1       1       9       9       29      229     229     3229    8229    58      59      NEAAAA  IODAAA  AAAAxx
+7782   2401    0       2       2       2       82      782     1782    2782    7782    164     165     INAAAA  JODAAA  HHHHxx
+1563   2402    1       3       3       3       63      563     1563    1563    1563    126     127     DIAAAA  KODAAA  OOOOxx
+267    2403    1       3       7       7       67      267     267     267     267     134     135     HKAAAA  LODAAA  VVVVxx
+5138   2404    0       2       8       18      38      138     1138    138     5138    76      77      QPAAAA  MODAAA  AAAAxx
+7022   2405    0       2       2       2       22      22      1022    2022    7022    44      45      CKAAAA  NODAAA  HHHHxx
+6705   2406    1       1       5       5       5       705     705     1705    6705    10      11      XXAAAA  OODAAA  OOOOxx
+6190   2407    0       2       0       10      90      190     190     1190    6190    180     181     CEAAAA  PODAAA  VVVVxx
+8226   2408    0       2       6       6       26      226     226     3226    8226    52      53      KEAAAA  QODAAA  AAAAxx
+8882   2409    0       2       2       2       82      882     882     3882    8882    164     165     QDAAAA  RODAAA  HHHHxx
+5181   2410    1       1       1       1       81      181     1181    181     5181    162     163     HRAAAA  SODAAA  OOOOxx
+4598   2411    0       2       8       18      98      598     598     4598    4598    196     197     WUAAAA  TODAAA  VVVVxx
+4882   2412    0       2       2       2       82      882     882     4882    4882    164     165     UFAAAA  UODAAA  AAAAxx
+7490   2413    0       2       0       10      90      490     1490    2490    7490    180     181     CCAAAA  VODAAA  HHHHxx
+5224   2414    0       0       4       4       24      224     1224    224     5224    48      49      YSAAAA  WODAAA  OOOOxx
+2174   2415    0       2       4       14      74      174     174     2174    2174    148     149     QFAAAA  XODAAA  VVVVxx
+3059   2416    1       3       9       19      59      59      1059    3059    3059    118     119     RNAAAA  YODAAA  AAAAxx
+8790   2417    0       2       0       10      90      790     790     3790    8790    180     181     CAAAAA  ZODAAA  HHHHxx
+2222   2418    0       2       2       2       22      222     222     2222    2222    44      45      MHAAAA  APDAAA  OOOOxx
+5473   2419    1       1       3       13      73      473     1473    473     5473    146     147     NCAAAA  BPDAAA  VVVVxx
+937    2420    1       1       7       17      37      937     937     937     937     74      75      BKAAAA  CPDAAA  AAAAxx
+2975   2421    1       3       5       15      75      975     975     2975    2975    150     151     LKAAAA  DPDAAA  HHHHxx
+9569   2422    1       1       9       9       69      569     1569    4569    9569    138     139     BEAAAA  EPDAAA  OOOOxx
+3456   2423    0       0       6       16      56      456     1456    3456    3456    112     113     YCAAAA  FPDAAA  VVVVxx
+6657   2424    1       1       7       17      57      657     657     1657    6657    114     115     BWAAAA  GPDAAA  AAAAxx
+3776   2425    0       0       6       16      76      776     1776    3776    3776    152     153     GPAAAA  HPDAAA  HHHHxx
+6072   2426    0       0       2       12      72      72      72      1072    6072    144     145     OZAAAA  IPDAAA  OOOOxx
+8129   2427    1       1       9       9       29      129     129     3129    8129    58      59      RAAAAA  JPDAAA  VVVVxx
+1085   2428    1       1       5       5       85      85      1085    1085    1085    170     171     TPAAAA  KPDAAA  AAAAxx
+2079   2429    1       3       9       19      79      79      79      2079    2079    158     159     ZBAAAA  LPDAAA  HHHHxx
+1200   2430    0       0       0       0       0       200     1200    1200    1200    0       1       EUAAAA  MPDAAA  OOOOxx
+3276   2431    0       0       6       16      76      276     1276    3276    3276    152     153     AWAAAA  NPDAAA  VVVVxx
+2608   2432    0       0       8       8       8       608     608     2608    2608    16      17      IWAAAA  OPDAAA  AAAAxx
+702    2433    0       2       2       2       2       702     702     702     702     4       5       ABAAAA  PPDAAA  HHHHxx
+5750   2434    0       2       0       10      50      750     1750    750     5750    100     101     ENAAAA  QPDAAA  OOOOxx
+2776   2435    0       0       6       16      76      776     776     2776    2776    152     153     UCAAAA  RPDAAA  VVVVxx
+9151   2436    1       3       1       11      51      151     1151    4151    9151    102     103     ZNAAAA  SPDAAA  AAAAxx
+3282   2437    0       2       2       2       82      282     1282    3282    3282    164     165     GWAAAA  TPDAAA  HHHHxx
+408    2438    0       0       8       8       8       408     408     408     408     16      17      SPAAAA  UPDAAA  OOOOxx
+3473   2439    1       1       3       13      73      473     1473    3473    3473    146     147     PDAAAA  VPDAAA  VVVVxx
+7095   2440    1       3       5       15      95      95      1095    2095    7095    190     191     XMAAAA  WPDAAA  AAAAxx
+3288   2441    0       0       8       8       88      288     1288    3288    3288    176     177     MWAAAA  XPDAAA  HHHHxx
+8215   2442    1       3       5       15      15      215     215     3215    8215    30      31      ZDAAAA  YPDAAA  OOOOxx
+6244   2443    0       0       4       4       44      244     244     1244    6244    88      89      EGAAAA  ZPDAAA  VVVVxx
+8440   2444    0       0       0       0       40      440     440     3440    8440    80      81      QMAAAA  AQDAAA  AAAAxx
+3800   2445    0       0       0       0       0       800     1800    3800    3800    0       1       EQAAAA  BQDAAA  HHHHxx
+7279   2446    1       3       9       19      79      279     1279    2279    7279    158     159     ZTAAAA  CQDAAA  OOOOxx
+9206   2447    0       2       6       6       6       206     1206    4206    9206    12      13      CQAAAA  DQDAAA  VVVVxx
+6465   2448    1       1       5       5       65      465     465     1465    6465    130     131     ROAAAA  EQDAAA  AAAAxx
+4127   2449    1       3       7       7       27      127     127     4127    4127    54      55      TCAAAA  FQDAAA  HHHHxx
+7463   2450    1       3       3       3       63      463     1463    2463    7463    126     127     BBAAAA  GQDAAA  OOOOxx
+5117   2451    1       1       7       17      17      117     1117    117     5117    34      35      VOAAAA  HQDAAA  VVVVxx
+4715   2452    1       3       5       15      15      715     715     4715    4715    30      31      JZAAAA  IQDAAA  AAAAxx
+2010   2453    0       2       0       10      10      10      10      2010    2010    20      21      IZAAAA  JQDAAA  HHHHxx
+6486   2454    0       2       6       6       86      486     486     1486    6486    172     173     MPAAAA  KQDAAA  OOOOxx
+6434   2455    0       2       4       14      34      434     434     1434    6434    68      69      MNAAAA  LQDAAA  VVVVxx
+2151   2456    1       3       1       11      51      151     151     2151    2151    102     103     TEAAAA  MQDAAA  AAAAxx
+4821   2457    1       1       1       1       21      821     821     4821    4821    42      43      LDAAAA  NQDAAA  HHHHxx
+6507   2458    1       3       7       7       7       507     507     1507    6507    14      15      HQAAAA  OQDAAA  OOOOxx
+8741   2459    1       1       1       1       41      741     741     3741    8741    82      83      FYAAAA  PQDAAA  VVVVxx
+6846   2460    0       2       6       6       46      846     846     1846    6846    92      93      IDAAAA  QQDAAA  AAAAxx
+4525   2461    1       1       5       5       25      525     525     4525    4525    50      51      BSAAAA  RQDAAA  HHHHxx
+8299   2462    1       3       9       19      99      299     299     3299    8299    198     199     FHAAAA  SQDAAA  OOOOxx
+5465   2463    1       1       5       5       65      465     1465    465     5465    130     131     FCAAAA  TQDAAA  VVVVxx
+7206   2464    0       2       6       6       6       206     1206    2206    7206    12      13      ERAAAA  UQDAAA  AAAAxx
+2616   2465    0       0       6       16      16      616     616     2616    2616    32      33      QWAAAA  VQDAAA  HHHHxx
+4440   2466    0       0       0       0       40      440     440     4440    4440    80      81      UOAAAA  WQDAAA  OOOOxx
+6109   2467    1       1       9       9       9       109     109     1109    6109    18      19      ZAAAAA  XQDAAA  VVVVxx
+7905   2468    1       1       5       5       5       905     1905    2905    7905    10      11      BSAAAA  YQDAAA  AAAAxx
+6498   2469    0       2       8       18      98      498     498     1498    6498    196     197     YPAAAA  ZQDAAA  HHHHxx
+2034   2470    0       2       4       14      34      34      34      2034    2034    68      69      GAAAAA  ARDAAA  OOOOxx
+7693   2471    1       1       3       13      93      693     1693    2693    7693    186     187     XJAAAA  BRDAAA  VVVVxx
+7511   2472    1       3       1       11      11      511     1511    2511    7511    22      23      XCAAAA  CRDAAA  AAAAxx
+7531   2473    1       3       1       11      31      531     1531    2531    7531    62      63      RDAAAA  DRDAAA  HHHHxx
+6869   2474    1       1       9       9       69      869     869     1869    6869    138     139     FEAAAA  ERDAAA  OOOOxx
+2763   2475    1       3       3       3       63      763     763     2763    2763    126     127     HCAAAA  FRDAAA  VVVVxx
+575    2476    1       3       5       15      75      575     575     575     575     150     151     DWAAAA  GRDAAA  AAAAxx
+8953   2477    1       1       3       13      53      953     953     3953    8953    106     107     JGAAAA  HRDAAA  HHHHxx
+5833   2478    1       1       3       13      33      833     1833    833     5833    66      67      JQAAAA  IRDAAA  OOOOxx
+9035   2479    1       3       5       15      35      35      1035    4035    9035    70      71      NJAAAA  JRDAAA  VVVVxx
+9123   2480    1       3       3       3       23      123     1123    4123    9123    46      47      XMAAAA  KRDAAA  AAAAxx
+206    2481    0       2       6       6       6       206     206     206     206     12      13      YHAAAA  LRDAAA  HHHHxx
+4155   2482    1       3       5       15      55      155     155     4155    4155    110     111     VDAAAA  MRDAAA  OOOOxx
+532    2483    0       0       2       12      32      532     532     532     532     64      65      MUAAAA  NRDAAA  VVVVxx
+1370   2484    0       2       0       10      70      370     1370    1370    1370    140     141     SAAAAA  ORDAAA  AAAAxx
+7656   2485    0       0       6       16      56      656     1656    2656    7656    112     113     MIAAAA  PRDAAA  HHHHxx
+7735   2486    1       3       5       15      35      735     1735    2735    7735    70      71      NLAAAA  QRDAAA  OOOOxx
+2118   2487    0       2       8       18      18      118     118     2118    2118    36      37      MDAAAA  RRDAAA  VVVVxx
+6914   2488    0       2       4       14      14      914     914     1914    6914    28      29      YFAAAA  SRDAAA  AAAAxx
+6277   2489    1       1       7       17      77      277     277     1277    6277    154     155     LHAAAA  TRDAAA  HHHHxx
+6347   2490    1       3       7       7       47      347     347     1347    6347    94      95      DKAAAA  URDAAA  OOOOxx
+4030   2491    0       2       0       10      30      30      30      4030    4030    60      61      AZAAAA  VRDAAA  VVVVxx
+9673   2492    1       1       3       13      73      673     1673    4673    9673    146     147     BIAAAA  WRDAAA  AAAAxx
+2015   2493    1       3       5       15      15      15      15      2015    2015    30      31      NZAAAA  XRDAAA  HHHHxx
+1317   2494    1       1       7       17      17      317     1317    1317    1317    34      35      RYAAAA  YRDAAA  OOOOxx
+404    2495    0       0       4       4       4       404     404     404     404     8       9       OPAAAA  ZRDAAA  VVVVxx
+1604   2496    0       0       4       4       4       604     1604    1604    1604    8       9       SJAAAA  ASDAAA  AAAAxx
+1912   2497    0       0       2       12      12      912     1912    1912    1912    24      25      OVAAAA  BSDAAA  HHHHxx
+5727   2498    1       3       7       7       27      727     1727    727     5727    54      55      HMAAAA  CSDAAA  OOOOxx
+4538   2499    0       2       8       18      38      538     538     4538    4538    76      77      OSAAAA  DSDAAA  VVVVxx
+6868   2500    0       0       8       8       68      868     868     1868    6868    136     137     EEAAAA  ESDAAA  AAAAxx
+9801   2501    1       1       1       1       1       801     1801    4801    9801    2       3       ZMAAAA  FSDAAA  HHHHxx
+1781   2502    1       1       1       1       81      781     1781    1781    1781    162     163     NQAAAA  GSDAAA  OOOOxx
+7061   2503    1       1       1       1       61      61      1061    2061    7061    122     123     PLAAAA  HSDAAA  VVVVxx
+2412   2504    0       0       2       12      12      412     412     2412    2412    24      25      UOAAAA  ISDAAA  AAAAxx
+9191   2505    1       3       1       11      91      191     1191    4191    9191    182     183     NPAAAA  JSDAAA  HHHHxx
+1958   2506    0       2       8       18      58      958     1958    1958    1958    116     117     IXAAAA  KSDAAA  OOOOxx
+2203   2507    1       3       3       3       3       203     203     2203    2203    6       7       TGAAAA  LSDAAA  VVVVxx
+9104   2508    0       0       4       4       4       104     1104    4104    9104    8       9       EMAAAA  MSDAAA  AAAAxx
+3837   2509    1       1       7       17      37      837     1837    3837    3837    74      75      PRAAAA  NSDAAA  HHHHxx
+7055   2510    1       3       5       15      55      55      1055    2055    7055    110     111     JLAAAA  OSDAAA  OOOOxx
+4612   2511    0       0       2       12      12      612     612     4612    4612    24      25      KVAAAA  PSDAAA  VVVVxx
+6420   2512    0       0       0       0       20      420     420     1420    6420    40      41      YMAAAA  QSDAAA  AAAAxx
+613    2513    1       1       3       13      13      613     613     613     613     26      27      PXAAAA  RSDAAA  HHHHxx
+1691   2514    1       3       1       11      91      691     1691    1691    1691    182     183     BNAAAA  SSDAAA  OOOOxx
+33     2515    1       1       3       13      33      33      33      33      33      66      67      HBAAAA  TSDAAA  VVVVxx
+875    2516    1       3       5       15      75      875     875     875     875     150     151     RHAAAA  USDAAA  AAAAxx
+9030   2517    0       2       0       10      30      30      1030    4030    9030    60      61      IJAAAA  VSDAAA  HHHHxx
+4285   2518    1       1       5       5       85      285     285     4285    4285    170     171     VIAAAA  WSDAAA  OOOOxx
+6236   2519    0       0       6       16      36      236     236     1236    6236    72      73      WFAAAA  XSDAAA  VVVVxx
+4702   2520    0       2       2       2       2       702     702     4702    4702    4       5       WYAAAA  YSDAAA  AAAAxx
+3441   2521    1       1       1       1       41      441     1441    3441    3441    82      83      JCAAAA  ZSDAAA  HHHHxx
+2150   2522    0       2       0       10      50      150     150     2150    2150    100     101     SEAAAA  ATDAAA  OOOOxx
+1852   2523    0       0       2       12      52      852     1852    1852    1852    104     105     GTAAAA  BTDAAA  VVVVxx
+7713   2524    1       1       3       13      13      713     1713    2713    7713    26      27      RKAAAA  CTDAAA  AAAAxx
+6849   2525    1       1       9       9       49      849     849     1849    6849    98      99      LDAAAA  DTDAAA  HHHHxx
+3425   2526    1       1       5       5       25      425     1425    3425    3425    50      51      TBAAAA  ETDAAA  OOOOxx
+4681   2527    1       1       1       1       81      681     681     4681    4681    162     163     BYAAAA  FTDAAA  VVVVxx
+1134   2528    0       2       4       14      34      134     1134    1134    1134    68      69      QRAAAA  GTDAAA  AAAAxx
+7462   2529    0       2       2       2       62      462     1462    2462    7462    124     125     ABAAAA  HTDAAA  HHHHxx
+2148   2530    0       0       8       8       48      148     148     2148    2148    96      97      QEAAAA  ITDAAA  OOOOxx
+5921   2531    1       1       1       1       21      921     1921    921     5921    42      43      TTAAAA  JTDAAA  VVVVxx
+118    2532    0       2       8       18      18      118     118     118     118     36      37      OEAAAA  KTDAAA  AAAAxx
+3065   2533    1       1       5       5       65      65      1065    3065    3065    130     131     XNAAAA  LTDAAA  HHHHxx
+6590   2534    0       2       0       10      90      590     590     1590    6590    180     181     MTAAAA  MTDAAA  OOOOxx
+4993   2535    1       1       3       13      93      993     993     4993    4993    186     187     BKAAAA  NTDAAA  VVVVxx
+6818   2536    0       2       8       18      18      818     818     1818    6818    36      37      GCAAAA  OTDAAA  AAAAxx
+1449   2537    1       1       9       9       49      449     1449    1449    1449    98      99      TDAAAA  PTDAAA  HHHHxx
+2039   2538    1       3       9       19      39      39      39      2039    2039    78      79      LAAAAA  QTDAAA  OOOOxx
+2524   2539    0       0       4       4       24      524     524     2524    2524    48      49      CTAAAA  RTDAAA  VVVVxx
+1481   2540    1       1       1       1       81      481     1481    1481    1481    162     163     ZEAAAA  STDAAA  AAAAxx
+6984   2541    0       0       4       4       84      984     984     1984    6984    168     169     QIAAAA  TTDAAA  HHHHxx
+3960   2542    0       0       0       0       60      960     1960    3960    3960    120     121     IWAAAA  UTDAAA  OOOOxx
+1983   2543    1       3       3       3       83      983     1983    1983    1983    166     167     HYAAAA  VTDAAA  VVVVxx
+6379   2544    1       3       9       19      79      379     379     1379    6379    158     159     JLAAAA  WTDAAA  AAAAxx
+8975   2545    1       3       5       15      75      975     975     3975    8975    150     151     FHAAAA  XTDAAA  HHHHxx
+1102   2546    0       2       2       2       2       102     1102    1102    1102    4       5       KQAAAA  YTDAAA  OOOOxx
+2517   2547    1       1       7       17      17      517     517     2517    2517    34      35      VSAAAA  ZTDAAA  VVVVxx
+712    2548    0       0       2       12      12      712     712     712     712     24      25      KBAAAA  AUDAAA  AAAAxx
+5419   2549    1       3       9       19      19      419     1419    419     5419    38      39      LAAAAA  BUDAAA  HHHHxx
+723    2550    1       3       3       3       23      723     723     723     723     46      47      VBAAAA  CUDAAA  OOOOxx
+8057   2551    1       1       7       17      57      57      57      3057    8057    114     115     XXAAAA  DUDAAA  VVVVxx
+7471   2552    1       3       1       11      71      471     1471    2471    7471    142     143     JBAAAA  EUDAAA  AAAAxx
+8855   2553    1       3       5       15      55      855     855     3855    8855    110     111     PCAAAA  FUDAAA  HHHHxx
+5074   2554    0       2       4       14      74      74      1074    74      5074    148     149     ENAAAA  GUDAAA  OOOOxx
+7139   2555    1       3       9       19      39      139     1139    2139    7139    78      79      POAAAA  HUDAAA  VVVVxx
+3833   2556    1       1       3       13      33      833     1833    3833    3833    66      67      LRAAAA  IUDAAA  AAAAxx
+5186   2557    0       2       6       6       86      186     1186    186     5186    172     173     MRAAAA  JUDAAA  HHHHxx
+9436   2558    0       0       6       16      36      436     1436    4436    9436    72      73      YYAAAA  KUDAAA  OOOOxx
+8859   2559    1       3       9       19      59      859     859     3859    8859    118     119     TCAAAA  LUDAAA  VVVVxx
+6943   2560    1       3       3       3       43      943     943     1943    6943    86      87      BHAAAA  MUDAAA  AAAAxx
+2315   2561    1       3       5       15      15      315     315     2315    2315    30      31      BLAAAA  NUDAAA  HHHHxx
+1394   2562    0       2       4       14      94      394     1394    1394    1394    188     189     QBAAAA  OUDAAA  OOOOxx
+8863   2563    1       3       3       3       63      863     863     3863    8863    126     127     XCAAAA  PUDAAA  VVVVxx
+8812   2564    0       0       2       12      12      812     812     3812    8812    24      25      YAAAAA  QUDAAA  AAAAxx
+7498   2565    0       2       8       18      98      498     1498    2498    7498    196     197     KCAAAA  RUDAAA  HHHHxx
+8962   2566    0       2       2       2       62      962     962     3962    8962    124     125     SGAAAA  SUDAAA  OOOOxx
+2533   2567    1       1       3       13      33      533     533     2533    2533    66      67      LTAAAA  TUDAAA  VVVVxx
+8188   2568    0       0       8       8       88      188     188     3188    8188    176     177     YCAAAA  UUDAAA  AAAAxx
+6137   2569    1       1       7       17      37      137     137     1137    6137    74      75      BCAAAA  VUDAAA  HHHHxx
+974    2570    0       2       4       14      74      974     974     974     974     148     149     MLAAAA  WUDAAA  OOOOxx
+2751   2571    1       3       1       11      51      751     751     2751    2751    102     103     VBAAAA  XUDAAA  VVVVxx
+4975   2572    1       3       5       15      75      975     975     4975    4975    150     151     JJAAAA  YUDAAA  AAAAxx
+3411   2573    1       3       1       11      11      411     1411    3411    3411    22      23      FBAAAA  ZUDAAA  HHHHxx
+3143   2574    1       3       3       3       43      143     1143    3143    3143    86      87      XQAAAA  AVDAAA  OOOOxx
+8011   2575    1       3       1       11      11      11      11      3011    8011    22      23      DWAAAA  BVDAAA  VVVVxx
+988    2576    0       0       8       8       88      988     988     988     988     176     177     AMAAAA  CVDAAA  AAAAxx
+4289   2577    1       1       9       9       89      289     289     4289    4289    178     179     ZIAAAA  DVDAAA  HHHHxx
+8105   2578    1       1       5       5       5       105     105     3105    8105    10      11      TZAAAA  EVDAAA  OOOOxx
+9885   2579    1       1       5       5       85      885     1885    4885    9885    170     171     FQAAAA  FVDAAA  VVVVxx
+1002   2580    0       2       2       2       2       2       1002    1002    1002    4       5       OMAAAA  GVDAAA  AAAAxx
+5827   2581    1       3       7       7       27      827     1827    827     5827    54      55      DQAAAA  HVDAAA  HHHHxx
+1228   2582    0       0       8       8       28      228     1228    1228    1228    56      57      GVAAAA  IVDAAA  OOOOxx
+6352   2583    0       0       2       12      52      352     352     1352    6352    104     105     IKAAAA  JVDAAA  VVVVxx
+8868   2584    0       0       8       8       68      868     868     3868    8868    136     137     CDAAAA  KVDAAA  AAAAxx
+3643   2585    1       3       3       3       43      643     1643    3643    3643    86      87      DKAAAA  LVDAAA  HHHHxx
+1468   2586    0       0       8       8       68      468     1468    1468    1468    136     137     MEAAAA  MVDAAA  OOOOxx
+8415   2587    1       3       5       15      15      415     415     3415    8415    30      31      RLAAAA  NVDAAA  VVVVxx
+9631   2588    1       3       1       11      31      631     1631    4631    9631    62      63      LGAAAA  OVDAAA  AAAAxx
+7408   2589    0       0       8       8       8       408     1408    2408    7408    16      17      YYAAAA  PVDAAA  HHHHxx
+1934   2590    0       2       4       14      34      934     1934    1934    1934    68      69      KWAAAA  QVDAAA  OOOOxx
+996    2591    0       0       6       16      96      996     996     996     996     192     193     IMAAAA  RVDAAA  VVVVxx
+8027   2592    1       3       7       7       27      27      27      3027    8027    54      55      TWAAAA  SVDAAA  AAAAxx
+8464   2593    0       0       4       4       64      464     464     3464    8464    128     129     ONAAAA  TVDAAA  HHHHxx
+5007   2594    1       3       7       7       7       7       1007    7       5007    14      15      PKAAAA  UVDAAA  OOOOxx
+8356   2595    0       0       6       16      56      356     356     3356    8356    112     113     KJAAAA  VVDAAA  VVVVxx
+4579   2596    1       3       9       19      79      579     579     4579    4579    158     159     DUAAAA  WVDAAA  AAAAxx
+8513   2597    1       1       3       13      13      513     513     3513    8513    26      27      LPAAAA  XVDAAA  HHHHxx
+383    2598    1       3       3       3       83      383     383     383     383     166     167     TOAAAA  YVDAAA  OOOOxx
+9304   2599    0       0       4       4       4       304     1304    4304    9304    8       9       WTAAAA  ZVDAAA  VVVVxx
+7224   2600    0       0       4       4       24      224     1224    2224    7224    48      49      WRAAAA  AWDAAA  AAAAxx
+6023   2601    1       3       3       3       23      23      23      1023    6023    46      47      RXAAAA  BWDAAA  HHHHxx
+2746   2602    0       2       6       6       46      746     746     2746    2746    92      93      QBAAAA  CWDAAA  OOOOxx
+137    2603    1       1       7       17      37      137     137     137     137     74      75      HFAAAA  DWDAAA  VVVVxx
+9441   2604    1       1       1       1       41      441     1441    4441    9441    82      83      DZAAAA  EWDAAA  AAAAxx
+3690   2605    0       2       0       10      90      690     1690    3690    3690    180     181     YLAAAA  FWDAAA  HHHHxx
+913    2606    1       1       3       13      13      913     913     913     913     26      27      DJAAAA  GWDAAA  OOOOxx
+1768   2607    0       0       8       8       68      768     1768    1768    1768    136     137     AQAAAA  HWDAAA  VVVVxx
+8492   2608    0       0       2       12      92      492     492     3492    8492    184     185     QOAAAA  IWDAAA  AAAAxx
+8083   2609    1       3       3       3       83      83      83      3083    8083    166     167     XYAAAA  JWDAAA  HHHHxx
+4609   2610    1       1       9       9       9       609     609     4609    4609    18      19      HVAAAA  KWDAAA  OOOOxx
+7520   2611    0       0       0       0       20      520     1520    2520    7520    40      41      GDAAAA  LWDAAA  VVVVxx
+4231   2612    1       3       1       11      31      231     231     4231    4231    62      63      TGAAAA  MWDAAA  AAAAxx
+6022   2613    0       2       2       2       22      22      22      1022    6022    44      45      QXAAAA  NWDAAA  HHHHxx
+9784   2614    0       0       4       4       84      784     1784    4784    9784    168     169     IMAAAA  OWDAAA  OOOOxx
+1343   2615    1       3       3       3       43      343     1343    1343    1343    86      87      RZAAAA  PWDAAA  VVVVxx
+7549   2616    1       1       9       9       49      549     1549    2549    7549    98      99      JEAAAA  QWDAAA  AAAAxx
+269    2617    1       1       9       9       69      269     269     269     269     138     139     JKAAAA  RWDAAA  HHHHxx
+1069   2618    1       1       9       9       69      69      1069    1069    1069    138     139     DPAAAA  SWDAAA  OOOOxx
+4610   2619    0       2       0       10      10      610     610     4610    4610    20      21      IVAAAA  TWDAAA  VVVVxx
+482    2620    0       2       2       2       82      482     482     482     482     164     165     OSAAAA  UWDAAA  AAAAxx
+3025   2621    1       1       5       5       25      25      1025    3025    3025    50      51      JMAAAA  VWDAAA  HHHHxx
+7914   2622    0       2       4       14      14      914     1914    2914    7914    28      29      KSAAAA  WWDAAA  OOOOxx
+3198   2623    0       2       8       18      98      198     1198    3198    3198    196     197     ATAAAA  XWDAAA  VVVVxx
+1187   2624    1       3       7       7       87      187     1187    1187    1187    174     175     RTAAAA  YWDAAA  AAAAxx
+4707   2625    1       3       7       7       7       707     707     4707    4707    14      15      BZAAAA  ZWDAAA  HHHHxx
+8279   2626    1       3       9       19      79      279     279     3279    8279    158     159     LGAAAA  AXDAAA  OOOOxx
+6127   2627    1       3       7       7       27      127     127     1127    6127    54      55      RBAAAA  BXDAAA  VVVVxx
+1305   2628    1       1       5       5       5       305     1305    1305    1305    10      11      FYAAAA  CXDAAA  AAAAxx
+4804   2629    0       0       4       4       4       804     804     4804    4804    8       9       UCAAAA  DXDAAA  HHHHxx
+6069   2630    1       1       9       9       69      69      69      1069    6069    138     139     LZAAAA  EXDAAA  OOOOxx
+9229   2631    1       1       9       9       29      229     1229    4229    9229    58      59      ZQAAAA  FXDAAA  VVVVxx
+4703   2632    1       3       3       3       3       703     703     4703    4703    6       7       XYAAAA  GXDAAA  AAAAxx
+6410   2633    0       2       0       10      10      410     410     1410    6410    20      21      OMAAAA  HXDAAA  HHHHxx
+944    2634    0       0       4       4       44      944     944     944     944     88      89      IKAAAA  IXDAAA  OOOOxx
+3744   2635    0       0       4       4       44      744     1744    3744    3744    88      89      AOAAAA  JXDAAA  VVVVxx
+1127   2636    1       3       7       7       27      127     1127    1127    1127    54      55      JRAAAA  KXDAAA  AAAAxx
+6693   2637    1       1       3       13      93      693     693     1693    6693    186     187     LXAAAA  LXDAAA  HHHHxx
+583    2638    1       3       3       3       83      583     583     583     583     166     167     LWAAAA  MXDAAA  OOOOxx
+2684   2639    0       0       4       4       84      684     684     2684    2684    168     169     GZAAAA  NXDAAA  VVVVxx
+6192   2640    0       0       2       12      92      192     192     1192    6192    184     185     EEAAAA  OXDAAA  AAAAxx
+4157   2641    1       1       7       17      57      157     157     4157    4157    114     115     XDAAAA  PXDAAA  HHHHxx
+6470   2642    0       2       0       10      70      470     470     1470    6470    140     141     WOAAAA  QXDAAA  OOOOxx
+8965   2643    1       1       5       5       65      965     965     3965    8965    130     131     VGAAAA  RXDAAA  VVVVxx
+1433   2644    1       1       3       13      33      433     1433    1433    1433    66      67      DDAAAA  SXDAAA  AAAAxx
+4570   2645    0       2       0       10      70      570     570     4570    4570    140     141     UTAAAA  TXDAAA  HHHHxx
+1806   2646    0       2       6       6       6       806     1806    1806    1806    12      13      MRAAAA  UXDAAA  OOOOxx
+1230   2647    0       2       0       10      30      230     1230    1230    1230    60      61      IVAAAA  VXDAAA  VVVVxx
+2283   2648    1       3       3       3       83      283     283     2283    2283    166     167     VJAAAA  WXDAAA  AAAAxx
+6456   2649    0       0       6       16      56      456     456     1456    6456    112     113     IOAAAA  XXDAAA  HHHHxx
+7427   2650    1       3       7       7       27      427     1427    2427    7427    54      55      RZAAAA  YXDAAA  OOOOxx
+8310   2651    0       2       0       10      10      310     310     3310    8310    20      21      QHAAAA  ZXDAAA  VVVVxx
+8103   2652    1       3       3       3       3       103     103     3103    8103    6       7       RZAAAA  AYDAAA  AAAAxx
+3947   2653    1       3       7       7       47      947     1947    3947    3947    94      95      VVAAAA  BYDAAA  HHHHxx
+3414   2654    0       2       4       14      14      414     1414    3414    3414    28      29      IBAAAA  CYDAAA  OOOOxx
+2043   2655    1       3       3       3       43      43      43      2043    2043    86      87      PAAAAA  DYDAAA  VVVVxx
+4393   2656    1       1       3       13      93      393     393     4393    4393    186     187     ZMAAAA  EYDAAA  AAAAxx
+6664   2657    0       0       4       4       64      664     664     1664    6664    128     129     IWAAAA  FYDAAA  HHHHxx
+4545   2658    1       1       5       5       45      545     545     4545    4545    90      91      VSAAAA  GYDAAA  OOOOxx
+7637   2659    1       1       7       17      37      637     1637    2637    7637    74      75      THAAAA  HYDAAA  VVVVxx
+1359   2660    1       3       9       19      59      359     1359    1359    1359    118     119     HAAAAA  IYDAAA  AAAAxx
+5018   2661    0       2       8       18      18      18      1018    18      5018    36      37      ALAAAA  JYDAAA  HHHHxx
+987    2662    1       3       7       7       87      987     987     987     987     174     175     ZLAAAA  KYDAAA  OOOOxx
+1320   2663    0       0       0       0       20      320     1320    1320    1320    40      41      UYAAAA  LYDAAA  VVVVxx
+9311   2664    1       3       1       11      11      311     1311    4311    9311    22      23      DUAAAA  MYDAAA  AAAAxx
+7993   2665    1       1       3       13      93      993     1993    2993    7993    186     187     LVAAAA  NYDAAA  HHHHxx
+7588   2666    0       0       8       8       88      588     1588    2588    7588    176     177     WFAAAA  OYDAAA  OOOOxx
+5983   2667    1       3       3       3       83      983     1983    983     5983    166     167     DWAAAA  PYDAAA  VVVVxx
+4070   2668    0       2       0       10      70      70      70      4070    4070    140     141     OAAAAA  QYDAAA  AAAAxx
+8349   2669    1       1       9       9       49      349     349     3349    8349    98      99      DJAAAA  RYDAAA  HHHHxx
+3810   2670    0       2       0       10      10      810     1810    3810    3810    20      21      OQAAAA  SYDAAA  OOOOxx
+6948   2671    0       0       8       8       48      948     948     1948    6948    96      97      GHAAAA  TYDAAA  VVVVxx
+7153   2672    1       1       3       13      53      153     1153    2153    7153    106     107     DPAAAA  UYDAAA  AAAAxx
+5371   2673    1       3       1       11      71      371     1371    371     5371    142     143     PYAAAA  VYDAAA  HHHHxx
+8316   2674    0       0       6       16      16      316     316     3316    8316    32      33      WHAAAA  WYDAAA  OOOOxx
+5903   2675    1       3       3       3       3       903     1903    903     5903    6       7       BTAAAA  XYDAAA  VVVVxx
+6718   2676    0       2       8       18      18      718     718     1718    6718    36      37      KYAAAA  YYDAAA  AAAAxx
+4759   2677    1       3       9       19      59      759     759     4759    4759    118     119     BBAAAA  ZYDAAA  HHHHxx
+2555   2678    1       3       5       15      55      555     555     2555    2555    110     111     HUAAAA  AZDAAA  OOOOxx
+3457   2679    1       1       7       17      57      457     1457    3457    3457    114     115     ZCAAAA  BZDAAA  VVVVxx
+9626   2680    0       2       6       6       26      626     1626    4626    9626    52      53      GGAAAA  CZDAAA  AAAAxx
+2570   2681    0       2       0       10      70      570     570     2570    2570    140     141     WUAAAA  DZDAAA  HHHHxx
+7964   2682    0       0       4       4       64      964     1964    2964    7964    128     129     IUAAAA  EZDAAA  OOOOxx
+1543   2683    1       3       3       3       43      543     1543    1543    1543    86      87      JHAAAA  FZDAAA  VVVVxx
+929    2684    1       1       9       9       29      929     929     929     929     58      59      TJAAAA  GZDAAA  AAAAxx
+9244   2685    0       0       4       4       44      244     1244    4244    9244    88      89      ORAAAA  HZDAAA  HHHHxx
+9210   2686    0       2       0       10      10      210     1210    4210    9210    20      21      GQAAAA  IZDAAA  OOOOxx
+8334   2687    0       2       4       14      34      334     334     3334    8334    68      69      OIAAAA  JZDAAA  VVVVxx
+9310   2688    0       2       0       10      10      310     1310    4310    9310    20      21      CUAAAA  KZDAAA  AAAAxx
+5024   2689    0       0       4       4       24      24      1024    24      5024    48      49      GLAAAA  LZDAAA  HHHHxx
+8794   2690    0       2       4       14      94      794     794     3794    8794    188     189     GAAAAA  MZDAAA  OOOOxx
+4091   2691    1       3       1       11      91      91      91      4091    4091    182     183     JBAAAA  NZDAAA  VVVVxx
+649    2692    1       1       9       9       49      649     649     649     649     98      99      ZYAAAA  OZDAAA  AAAAxx
+8505   2693    1       1       5       5       5       505     505     3505    8505    10      11      DPAAAA  PZDAAA  HHHHxx
+6652   2694    0       0       2       12      52      652     652     1652    6652    104     105     WVAAAA  QZDAAA  OOOOxx
+8945   2695    1       1       5       5       45      945     945     3945    8945    90      91      BGAAAA  RZDAAA  VVVVxx
+2095   2696    1       3       5       15      95      95      95      2095    2095    190     191     PCAAAA  SZDAAA  AAAAxx
+8676   2697    0       0       6       16      76      676     676     3676    8676    152     153     SVAAAA  TZDAAA  HHHHxx
+3994   2698    0       2       4       14      94      994     1994    3994    3994    188     189     QXAAAA  UZDAAA  OOOOxx
+2859   2699    1       3       9       19      59      859     859     2859    2859    118     119     ZFAAAA  VZDAAA  VVVVxx
+5403   2700    1       3       3       3       3       403     1403    403     5403    6       7       VZAAAA  WZDAAA  AAAAxx
+3254   2701    0       2       4       14      54      254     1254    3254    3254    108     109     EVAAAA  XZDAAA  HHHHxx
+7339   2702    1       3       9       19      39      339     1339    2339    7339    78      79      HWAAAA  YZDAAA  OOOOxx
+7220   2703    0       0       0       0       20      220     1220    2220    7220    40      41      SRAAAA  ZZDAAA  VVVVxx
+4154   2704    0       2       4       14      54      154     154     4154    4154    108     109     UDAAAA  AAEAAA  AAAAxx
+7570   2705    0       2       0       10      70      570     1570    2570    7570    140     141     EFAAAA  BAEAAA  HHHHxx
+2576   2706    0       0       6       16      76      576     576     2576    2576    152     153     CVAAAA  CAEAAA  OOOOxx
+5764   2707    0       0       4       4       64      764     1764    764     5764    128     129     SNAAAA  DAEAAA  VVVVxx
+4314   2708    0       2       4       14      14      314     314     4314    4314    28      29      YJAAAA  EAEAAA  AAAAxx
+2274   2709    0       2       4       14      74      274     274     2274    2274    148     149     MJAAAA  FAEAAA  HHHHxx
+9756   2710    0       0       6       16      56      756     1756    4756    9756    112     113     GLAAAA  GAEAAA  OOOOxx
+8274   2711    0       2       4       14      74      274     274     3274    8274    148     149     GGAAAA  HAEAAA  VVVVxx
+1289   2712    1       1       9       9       89      289     1289    1289    1289    178     179     PXAAAA  IAEAAA  AAAAxx
+7335   2713    1       3       5       15      35      335     1335    2335    7335    70      71      DWAAAA  JAEAAA  HHHHxx
+5351   2714    1       3       1       11      51      351     1351    351     5351    102     103     VXAAAA  KAEAAA  OOOOxx
+8978   2715    0       2       8       18      78      978     978     3978    8978    156     157     IHAAAA  LAEAAA  VVVVxx
+2      2716    0       2       2       2       2       2       2       2       2       4       5       CAAAAA  MAEAAA  AAAAxx
+8906   2717    0       2       6       6       6       906     906     3906    8906    12      13      OEAAAA  NAEAAA  HHHHxx
+6388   2718    0       0       8       8       88      388     388     1388    6388    176     177     SLAAAA  OAEAAA  OOOOxx
+5675   2719    1       3       5       15      75      675     1675    675     5675    150     151     HKAAAA  PAEAAA  VVVVxx
+255    2720    1       3       5       15      55      255     255     255     255     110     111     VJAAAA  QAEAAA  AAAAxx
+9538   2721    0       2       8       18      38      538     1538    4538    9538    76      77      WCAAAA  RAEAAA  HHHHxx
+1480   2722    0       0       0       0       80      480     1480    1480    1480    160     161     YEAAAA  SAEAAA  OOOOxx
+4015   2723    1       3       5       15      15      15      15      4015    4015    30      31      LYAAAA  TAEAAA  VVVVxx
+5166   2724    0       2       6       6       66      166     1166    166     5166    132     133     SQAAAA  UAEAAA  AAAAxx
+91     2725    1       3       1       11      91      91      91      91      91      182     183     NDAAAA  VAEAAA  HHHHxx
+2958   2726    0       2       8       18      58      958     958     2958    2958    116     117     UJAAAA  WAEAAA  OOOOxx
+9131   2727    1       3       1       11      31      131     1131    4131    9131    62      63      FNAAAA  XAEAAA  VVVVxx
+3944   2728    0       0       4       4       44      944     1944    3944    3944    88      89      SVAAAA  YAEAAA  AAAAxx
+4514   2729    0       2       4       14      14      514     514     4514    4514    28      29      QRAAAA  ZAEAAA  HHHHxx
+5661   2730    1       1       1       1       61      661     1661    661     5661    122     123     TJAAAA  ABEAAA  OOOOxx
+8724   2731    0       0       4       4       24      724     724     3724    8724    48      49      OXAAAA  BBEAAA  VVVVxx
+6408   2732    0       0       8       8       8       408     408     1408    6408    16      17      MMAAAA  CBEAAA  AAAAxx
+5013   2733    1       1       3       13      13      13      1013    13      5013    26      27      VKAAAA  DBEAAA  HHHHxx
+6156   2734    0       0       6       16      56      156     156     1156    6156    112     113     UCAAAA  EBEAAA  OOOOxx
+7350   2735    0       2       0       10      50      350     1350    2350    7350    100     101     SWAAAA  FBEAAA  VVVVxx
+9858   2736    0       2       8       18      58      858     1858    4858    9858    116     117     EPAAAA  GBEAAA  AAAAxx
+895    2737    1       3       5       15      95      895     895     895     895     190     191     LIAAAA  HBEAAA  HHHHxx
+8368   2738    0       0       8       8       68      368     368     3368    8368    136     137     WJAAAA  IBEAAA  OOOOxx
+179    2739    1       3       9       19      79      179     179     179     179     158     159     XGAAAA  JBEAAA  VVVVxx
+4048   2740    0       0       8       8       48      48      48      4048    4048    96      97      SZAAAA  KBEAAA  AAAAxx
+3073   2741    1       1       3       13      73      73      1073    3073    3073    146     147     FOAAAA  LBEAAA  HHHHxx
+321    2742    1       1       1       1       21      321     321     321     321     42      43      JMAAAA  MBEAAA  OOOOxx
+5352   2743    0       0       2       12      52      352     1352    352     5352    104     105     WXAAAA  NBEAAA  VVVVxx
+1940   2744    0       0       0       0       40      940     1940    1940    1940    80      81      QWAAAA  OBEAAA  AAAAxx
+8803   2745    1       3       3       3       3       803     803     3803    8803    6       7       PAAAAA  PBEAAA  HHHHxx
+791    2746    1       3       1       11      91      791     791     791     791     182     183     LEAAAA  QBEAAA  OOOOxx
+9809   2747    1       1       9       9       9       809     1809    4809    9809    18      19      HNAAAA  RBEAAA  VVVVxx
+5519   2748    1       3       9       19      19      519     1519    519     5519    38      39      HEAAAA  SBEAAA  AAAAxx
+7420   2749    0       0       0       0       20      420     1420    2420    7420    40      41      KZAAAA  TBEAAA  HHHHxx
+7541   2750    1       1       1       1       41      541     1541    2541    7541    82      83      BEAAAA  UBEAAA  OOOOxx
+6538   2751    0       2       8       18      38      538     538     1538    6538    76      77      MRAAAA  VBEAAA  VVVVxx
+710    2752    0       2       0       10      10      710     710     710     710     20      21      IBAAAA  WBEAAA  AAAAxx
+9488   2753    0       0       8       8       88      488     1488    4488    9488    176     177     YAAAAA  XBEAAA  HHHHxx
+3135   2754    1       3       5       15      35      135     1135    3135    3135    70      71      PQAAAA  YBEAAA  OOOOxx
+4273   2755    1       1       3       13      73      273     273     4273    4273    146     147     JIAAAA  ZBEAAA  VVVVxx
+629    2756    1       1       9       9       29      629     629     629     629     58      59      FYAAAA  ACEAAA  AAAAxx
+9167   2757    1       3       7       7       67      167     1167    4167    9167    134     135     POAAAA  BCEAAA  HHHHxx
+751    2758    1       3       1       11      51      751     751     751     751     102     103     XCAAAA  CCEAAA  OOOOxx
+1126   2759    0       2       6       6       26      126     1126    1126    1126    52      53      IRAAAA  DCEAAA  VVVVxx
+3724   2760    0       0       4       4       24      724     1724    3724    3724    48      49      GNAAAA  ECEAAA  AAAAxx
+1789   2761    1       1       9       9       89      789     1789    1789    1789    178     179     VQAAAA  FCEAAA  HHHHxx
+792    2762    0       0       2       12      92      792     792     792     792     184     185     MEAAAA  GCEAAA  OOOOxx
+2771   2763    1       3       1       11      71      771     771     2771    2771    142     143     PCAAAA  HCEAAA  VVVVxx
+4313   2764    1       1       3       13      13      313     313     4313    4313    26      27      XJAAAA  ICEAAA  AAAAxx
+9312   2765    0       0       2       12      12      312     1312    4312    9312    24      25      EUAAAA  JCEAAA  HHHHxx
+955    2766    1       3       5       15      55      955     955     955     955     110     111     TKAAAA  KCEAAA  OOOOxx
+6382   2767    0       2       2       2       82      382     382     1382    6382    164     165     MLAAAA  LCEAAA  VVVVxx
+7875   2768    1       3       5       15      75      875     1875    2875    7875    150     151     XQAAAA  MCEAAA  AAAAxx
+7491   2769    1       3       1       11      91      491     1491    2491    7491    182     183     DCAAAA  NCEAAA  HHHHxx
+8193   2770    1       1       3       13      93      193     193     3193    8193    186     187     DDAAAA  OCEAAA  OOOOxx
+968    2771    0       0       8       8       68      968     968     968     968     136     137     GLAAAA  PCEAAA  VVVVxx
+4951   2772    1       3       1       11      51      951     951     4951    4951    102     103     LIAAAA  QCEAAA  AAAAxx
+2204   2773    0       0       4       4       4       204     204     2204    2204    8       9       UGAAAA  RCEAAA  HHHHxx
+2066   2774    0       2       6       6       66      66      66      2066    2066    132     133     MBAAAA  SCEAAA  OOOOxx
+2631   2775    1       3       1       11      31      631     631     2631    2631    62      63      FXAAAA  TCEAAA  VVVVxx
+8947   2776    1       3       7       7       47      947     947     3947    8947    94      95      DGAAAA  UCEAAA  AAAAxx
+8033   2777    1       1       3       13      33      33      33      3033    8033    66      67      ZWAAAA  VCEAAA  HHHHxx
+6264   2778    0       0       4       4       64      264     264     1264    6264    128     129     YGAAAA  WCEAAA  OOOOxx
+7778   2779    0       2       8       18      78      778     1778    2778    7778    156     157     ENAAAA  XCEAAA  VVVVxx
+9701   2780    1       1       1       1       1       701     1701    4701    9701    2       3       DJAAAA  YCEAAA  AAAAxx
+5091   2781    1       3       1       11      91      91      1091    91      5091    182     183     VNAAAA  ZCEAAA  HHHHxx
+7577   2782    1       1       7       17      77      577     1577    2577    7577    154     155     LFAAAA  ADEAAA  OOOOxx
+3345   2783    1       1       5       5       45      345     1345    3345    3345    90      91      RYAAAA  BDEAAA  VVVVxx
+7329   2784    1       1       9       9       29      329     1329    2329    7329    58      59      XVAAAA  CDEAAA  AAAAxx
+7551   2785    1       3       1       11      51      551     1551    2551    7551    102     103     LEAAAA  DDEAAA  HHHHxx
+6207   2786    1       3       7       7       7       207     207     1207    6207    14      15      TEAAAA  EDEAAA  OOOOxx
+8664   2787    0       0       4       4       64      664     664     3664    8664    128     129     GVAAAA  FDEAAA  VVVVxx
+8394   2788    0       2       4       14      94      394     394     3394    8394    188     189     WKAAAA  GDEAAA  AAAAxx
+7324   2789    0       0       4       4       24      324     1324    2324    7324    48      49      SVAAAA  HDEAAA  HHHHxx
+2713   2790    1       1       3       13      13      713     713     2713    2713    26      27      JAAAAA  IDEAAA  OOOOxx
+2230   2791    0       2       0       10      30      230     230     2230    2230    60      61      UHAAAA  JDEAAA  VVVVxx
+9211   2792    1       3       1       11      11      211     1211    4211    9211    22      23      HQAAAA  KDEAAA  AAAAxx
+1296   2793    0       0       6       16      96      296     1296    1296    1296    192     193     WXAAAA  LDEAAA  HHHHxx
+8104   2794    0       0       4       4       4       104     104     3104    8104    8       9       SZAAAA  MDEAAA  OOOOxx
+6916   2795    0       0       6       16      16      916     916     1916    6916    32      33      AGAAAA  NDEAAA  VVVVxx
+2208   2796    0       0       8       8       8       208     208     2208    2208    16      17      YGAAAA  ODEAAA  AAAAxx
+3935   2797    1       3       5       15      35      935     1935    3935    3935    70      71      JVAAAA  PDEAAA  HHHHxx
+7814   2798    0       2       4       14      14      814     1814    2814    7814    28      29      OOAAAA  QDEAAA  OOOOxx
+6508   2799    0       0       8       8       8       508     508     1508    6508    16      17      IQAAAA  RDEAAA  VVVVxx
+1703   2800    1       3       3       3       3       703     1703    1703    1703    6       7       NNAAAA  SDEAAA  AAAAxx
+5640   2801    0       0       0       0       40      640     1640    640     5640    80      81      YIAAAA  TDEAAA  HHHHxx
+6417   2802    1       1       7       17      17      417     417     1417    6417    34      35      VMAAAA  UDEAAA  OOOOxx
+1713   2803    1       1       3       13      13      713     1713    1713    1713    26      27      XNAAAA  VDEAAA  VVVVxx
+5309   2804    1       1       9       9       9       309     1309    309     5309    18      19      FWAAAA  WDEAAA  AAAAxx
+4364   2805    0       0       4       4       64      364     364     4364    4364    128     129     WLAAAA  XDEAAA  HHHHxx
+619    2806    1       3       9       19      19      619     619     619     619     38      39      VXAAAA  YDEAAA  OOOOxx
+9498   2807    0       2       8       18      98      498     1498    4498    9498    196     197     IBAAAA  ZDEAAA  VVVVxx
+2804   2808    0       0       4       4       4       804     804     2804    2804    8       9       WDAAAA  AEEAAA  AAAAxx
+2220   2809    0       0       0       0       20      220     220     2220    2220    40      41      KHAAAA  BEEAAA  HHHHxx
+9542   2810    0       2       2       2       42      542     1542    4542    9542    84      85      ADAAAA  CEEAAA  OOOOxx
+3349   2811    1       1       9       9       49      349     1349    3349    3349    98      99      VYAAAA  DEEAAA  VVVVxx
+9198   2812    0       2       8       18      98      198     1198    4198    9198    196     197     UPAAAA  EEEAAA  AAAAxx
+2727   2813    1       3       7       7       27      727     727     2727    2727    54      55      XAAAAA  FEEAAA  HHHHxx
+3768   2814    0       0       8       8       68      768     1768    3768    3768    136     137     YOAAAA  GEEAAA  OOOOxx
+2334   2815    0       2       4       14      34      334     334     2334    2334    68      69      ULAAAA  HEEAAA  VVVVxx
+7770   2816    0       2       0       10      70      770     1770    2770    7770    140     141     WMAAAA  IEEAAA  AAAAxx
+5963   2817    1       3       3       3       63      963     1963    963     5963    126     127     JVAAAA  JEEAAA  HHHHxx
+4732   2818    0       0       2       12      32      732     732     4732    4732    64      65      AAAAAA  KEEAAA  OOOOxx
+2448   2819    0       0       8       8       48      448     448     2448    2448    96      97      EQAAAA  LEEAAA  VVVVxx
+5998   2820    0       2       8       18      98      998     1998    998     5998    196     197     SWAAAA  MEEAAA  AAAAxx
+8577   2821    1       1       7       17      77      577     577     3577    8577    154     155     XRAAAA  NEEAAA  HHHHxx
+266    2822    0       2       6       6       66      266     266     266     266     132     133     GKAAAA  OEEAAA  OOOOxx
+2169   2823    1       1       9       9       69      169     169     2169    2169    138     139     LFAAAA  PEEAAA  VVVVxx
+8228   2824    0       0       8       8       28      228     228     3228    8228    56      57      MEAAAA  QEEAAA  AAAAxx
+4813   2825    1       1       3       13      13      813     813     4813    4813    26      27      DDAAAA  REEAAA  HHHHxx
+2769   2826    1       1       9       9       69      769     769     2769    2769    138     139     NCAAAA  SEEAAA  OOOOxx
+8382   2827    0       2       2       2       82      382     382     3382    8382    164     165     KKAAAA  TEEAAA  VVVVxx
+1717   2828    1       1       7       17      17      717     1717    1717    1717    34      35      BOAAAA  UEEAAA  AAAAxx
+7178   2829    0       2       8       18      78      178     1178    2178    7178    156     157     CQAAAA  VEEAAA  HHHHxx
+9547   2830    1       3       7       7       47      547     1547    4547    9547    94      95      FDAAAA  WEEAAA  OOOOxx
+8187   2831    1       3       7       7       87      187     187     3187    8187    174     175     XCAAAA  XEEAAA  VVVVxx
+3168   2832    0       0       8       8       68      168     1168    3168    3168    136     137     WRAAAA  YEEAAA  AAAAxx
+2180   2833    0       0       0       0       80      180     180     2180    2180    160     161     WFAAAA  ZEEAAA  HHHHxx
+859    2834    1       3       9       19      59      859     859     859     859     118     119     BHAAAA  AFEAAA  OOOOxx
+1554   2835    0       2       4       14      54      554     1554    1554    1554    108     109     UHAAAA  BFEAAA  VVVVxx
+3567   2836    1       3       7       7       67      567     1567    3567    3567    134     135     FHAAAA  CFEAAA  AAAAxx
+5985   2837    1       1       5       5       85      985     1985    985     5985    170     171     FWAAAA  DFEAAA  HHHHxx
+1      2838    1       1       1       1       1       1       1       1       1       2       3       BAAAAA  EFEAAA  OOOOxx
+5937   2839    1       1       7       17      37      937     1937    937     5937    74      75      JUAAAA  FFEAAA  VVVVxx
+7594   2840    0       2       4       14      94      594     1594    2594    7594    188     189     CGAAAA  GFEAAA  AAAAxx
+3783   2841    1       3       3       3       83      783     1783    3783    3783    166     167     NPAAAA  HFEAAA  HHHHxx
+6841   2842    1       1       1       1       41      841     841     1841    6841    82      83      DDAAAA  IFEAAA  OOOOxx
+9694   2843    0       2       4       14      94      694     1694    4694    9694    188     189     WIAAAA  JFEAAA  VVVVxx
+4322   2844    0       2       2       2       22      322     322     4322    4322    44      45      GKAAAA  KFEAAA  AAAAxx
+6012   2845    0       0       2       12      12      12      12      1012    6012    24      25      GXAAAA  LFEAAA  HHHHxx
+108    2846    0       0       8       8       8       108     108     108     108     16      17      EEAAAA  MFEAAA  OOOOxx
+3396   2847    0       0       6       16      96      396     1396    3396    3396    192     193     QAAAAA  NFEAAA  VVVVxx
+8643   2848    1       3       3       3       43      643     643     3643    8643    86      87      LUAAAA  OFEAAA  AAAAxx
+6087   2849    1       3       7       7       87      87      87      1087    6087    174     175     DAAAAA  PFEAAA  HHHHxx
+2629   2850    1       1       9       9       29      629     629     2629    2629    58      59      DXAAAA  QFEAAA  OOOOxx
+3009   2851    1       1       9       9       9       9       1009    3009    3009    18      19      TLAAAA  RFEAAA  VVVVxx
+438    2852    0       2       8       18      38      438     438     438     438     76      77      WQAAAA  SFEAAA  AAAAxx
+2480   2853    0       0       0       0       80      480     480     2480    2480    160     161     KRAAAA  TFEAAA  HHHHxx
+936    2854    0       0       6       16      36      936     936     936     936     72      73      AKAAAA  UFEAAA  OOOOxx
+6      2855    0       2       6       6       6       6       6       6       6       12      13      GAAAAA  VFEAAA  VVVVxx
+768    2856    0       0       8       8       68      768     768     768     768     136     137     ODAAAA  WFEAAA  AAAAxx
+1564   2857    0       0       4       4       64      564     1564    1564    1564    128     129     EIAAAA  XFEAAA  HHHHxx
+3236   2858    0       0       6       16      36      236     1236    3236    3236    72      73      MUAAAA  YFEAAA  OOOOxx
+3932   2859    0       0       2       12      32      932     1932    3932    3932    64      65      GVAAAA  ZFEAAA  VVVVxx
+8914   2860    0       2       4       14      14      914     914     3914    8914    28      29      WEAAAA  AGEAAA  AAAAxx
+119    2861    1       3       9       19      19      119     119     119     119     38      39      PEAAAA  BGEAAA  HHHHxx
+6034   2862    0       2       4       14      34      34      34      1034    6034    68      69      CYAAAA  CGEAAA  OOOOxx
+5384   2863    0       0       4       4       84      384     1384    384     5384    168     169     CZAAAA  DGEAAA  VVVVxx
+6885   2864    1       1       5       5       85      885     885     1885    6885    170     171     VEAAAA  EGEAAA  AAAAxx
+232    2865    0       0       2       12      32      232     232     232     232     64      65      YIAAAA  FGEAAA  HHHHxx
+1293   2866    1       1       3       13      93      293     1293    1293    1293    186     187     TXAAAA  GGEAAA  OOOOxx
+9204   2867    0       0       4       4       4       204     1204    4204    9204    8       9       AQAAAA  HGEAAA  VVVVxx
+527    2868    1       3       7       7       27      527     527     527     527     54      55      HUAAAA  IGEAAA  AAAAxx
+6539   2869    1       3       9       19      39      539     539     1539    6539    78      79      NRAAAA  JGEAAA  HHHHxx
+3679   2870    1       3       9       19      79      679     1679    3679    3679    158     159     NLAAAA  KGEAAA  OOOOxx
+8282   2871    0       2       2       2       82      282     282     3282    8282    164     165     OGAAAA  LGEAAA  VVVVxx
+5027   2872    1       3       7       7       27      27      1027    27      5027    54      55      JLAAAA  MGEAAA  AAAAxx
+7694   2873    0       2       4       14      94      694     1694    2694    7694    188     189     YJAAAA  NGEAAA  HHHHxx
+473    2874    1       1       3       13      73      473     473     473     473     146     147     FSAAAA  OGEAAA  OOOOxx
+6325   2875    1       1       5       5       25      325     325     1325    6325    50      51      HJAAAA  PGEAAA  VVVVxx
+8761   2876    1       1       1       1       61      761     761     3761    8761    122     123     ZYAAAA  QGEAAA  AAAAxx
+6184   2877    0       0       4       4       84      184     184     1184    6184    168     169     WDAAAA  RGEAAA  HHHHxx
+419    2878    1       3       9       19      19      419     419     419     419     38      39      DQAAAA  SGEAAA  OOOOxx
+6111   2879    1       3       1       11      11      111     111     1111    6111    22      23      BBAAAA  TGEAAA  VVVVxx
+3836   2880    0       0       6       16      36      836     1836    3836    3836    72      73      ORAAAA  UGEAAA  AAAAxx
+4086   2881    0       2       6       6       86      86      86      4086    4086    172     173     EBAAAA  VGEAAA  HHHHxx
+5818   2882    0       2       8       18      18      818     1818    818     5818    36      37      UPAAAA  WGEAAA  OOOOxx
+4528   2883    0       0       8       8       28      528     528     4528    4528    56      57      ESAAAA  XGEAAA  VVVVxx
+7199   2884    1       3       9       19      99      199     1199    2199    7199    198     199     XQAAAA  YGEAAA  AAAAxx
+1847   2885    1       3       7       7       47      847     1847    1847    1847    94      95      BTAAAA  ZGEAAA  HHHHxx
+2875   2886    1       3       5       15      75      875     875     2875    2875    150     151     PGAAAA  AHEAAA  OOOOxx
+2872   2887    0       0       2       12      72      872     872     2872    2872    144     145     MGAAAA  BHEAAA  VVVVxx
+3972   2888    0       0       2       12      72      972     1972    3972    3972    144     145     UWAAAA  CHEAAA  AAAAxx
+7590   2889    0       2       0       10      90      590     1590    2590    7590    180     181     YFAAAA  DHEAAA  HHHHxx
+1914   2890    0       2       4       14      14      914     1914    1914    1914    28      29      QVAAAA  EHEAAA  OOOOxx
+1658   2891    0       2       8       18      58      658     1658    1658    1658    116     117     ULAAAA  FHEAAA  VVVVxx
+2126   2892    0       2       6       6       26      126     126     2126    2126    52      53      UDAAAA  GHEAAA  AAAAxx
+645    2893    1       1       5       5       45      645     645     645     645     90      91      VYAAAA  HHEAAA  HHHHxx
+6636   2894    0       0       6       16      36      636     636     1636    6636    72      73      GVAAAA  IHEAAA  OOOOxx
+1469   2895    1       1       9       9       69      469     1469    1469    1469    138     139     NEAAAA  JHEAAA  VVVVxx
+1377   2896    1       1       7       17      77      377     1377    1377    1377    154     155     ZAAAAA  KHEAAA  AAAAxx
+8425   2897    1       1       5       5       25      425     425     3425    8425    50      51      BMAAAA  LHEAAA  HHHHxx
+9300   2898    0       0       0       0       0       300     1300    4300    9300    0       1       STAAAA  MHEAAA  OOOOxx
+5355   2899    1       3       5       15      55      355     1355    355     5355    110     111     ZXAAAA  NHEAAA  VVVVxx
+840    2900    0       0       0       0       40      840     840     840     840     80      81      IGAAAA  OHEAAA  AAAAxx
+5185   2901    1       1       5       5       85      185     1185    185     5185    170     171     LRAAAA  PHEAAA  HHHHxx
+6467   2902    1       3       7       7       67      467     467     1467    6467    134     135     TOAAAA  QHEAAA  OOOOxx
+58     2903    0       2       8       18      58      58      58      58      58      116     117     GCAAAA  RHEAAA  VVVVxx
+5051   2904    1       3       1       11      51      51      1051    51      5051    102     103     HMAAAA  SHEAAA  AAAAxx
+8901   2905    1       1       1       1       1       901     901     3901    8901    2       3       JEAAAA  THEAAA  HHHHxx
+1550   2906    0       2       0       10      50      550     1550    1550    1550    100     101     QHAAAA  UHEAAA  OOOOxx
+1698   2907    0       2       8       18      98      698     1698    1698    1698    196     197     INAAAA  VHEAAA  VVVVxx
+802    2908    0       2       2       2       2       802     802     802     802     4       5       WEAAAA  WHEAAA  AAAAxx
+2440   2909    0       0       0       0       40      440     440     2440    2440    80      81      WPAAAA  XHEAAA  HHHHxx
+2260   2910    0       0       0       0       60      260     260     2260    2260    120     121     YIAAAA  YHEAAA  OOOOxx
+8218   2911    0       2       8       18      18      218     218     3218    8218    36      37      CEAAAA  ZHEAAA  VVVVxx
+5144   2912    0       0       4       4       44      144     1144    144     5144    88      89      WPAAAA  AIEAAA  AAAAxx
+4822   2913    0       2       2       2       22      822     822     4822    4822    44      45      MDAAAA  BIEAAA  HHHHxx
+9476   2914    0       0       6       16      76      476     1476    4476    9476    152     153     MAAAAA  CIEAAA  OOOOxx
+7535   2915    1       3       5       15      35      535     1535    2535    7535    70      71      VDAAAA  DIEAAA  VVVVxx
+8738   2916    0       2       8       18      38      738     738     3738    8738    76      77      CYAAAA  EIEAAA  AAAAxx
+7946   2917    0       2       6       6       46      946     1946    2946    7946    92      93      QTAAAA  FIEAAA  HHHHxx
+8143   2918    1       3       3       3       43      143     143     3143    8143    86      87      FBAAAA  GIEAAA  OOOOxx
+2623   2919    1       3       3       3       23      623     623     2623    2623    46      47      XWAAAA  HIEAAA  VVVVxx
+5209   2920    1       1       9       9       9       209     1209    209     5209    18      19      JSAAAA  IIEAAA  AAAAxx
+7674   2921    0       2       4       14      74      674     1674    2674    7674    148     149     EJAAAA  JIEAAA  HHHHxx
+1135   2922    1       3       5       15      35      135     1135    1135    1135    70      71      RRAAAA  KIEAAA  OOOOxx
+424    2923    0       0       4       4       24      424     424     424     424     48      49      IQAAAA  LIEAAA  VVVVxx
+942    2924    0       2       2       2       42      942     942     942     942     84      85      GKAAAA  MIEAAA  AAAAxx
+7813   2925    1       1       3       13      13      813     1813    2813    7813    26      27      NOAAAA  NIEAAA  HHHHxx
+3539   2926    1       3       9       19      39      539     1539    3539    3539    78      79      DGAAAA  OIEAAA  OOOOxx
+2909   2927    1       1       9       9       9       909     909     2909    2909    18      19      XHAAAA  PIEAAA  VVVVxx
+3748   2928    0       0       8       8       48      748     1748    3748    3748    96      97      EOAAAA  QIEAAA  AAAAxx
+2996   2929    0       0       6       16      96      996     996     2996    2996    192     193     GLAAAA  RIEAAA  HHHHxx
+1869   2930    1       1       9       9       69      869     1869    1869    1869    138     139     XTAAAA  SIEAAA  OOOOxx
+8151   2931    1       3       1       11      51      151     151     3151    8151    102     103     NBAAAA  TIEAAA  VVVVxx
+6361   2932    1       1       1       1       61      361     361     1361    6361    122     123     RKAAAA  UIEAAA  AAAAxx
+5568   2933    0       0       8       8       68      568     1568    568     5568    136     137     EGAAAA  VIEAAA  HHHHxx
+2796   2934    0       0       6       16      96      796     796     2796    2796    192     193     ODAAAA  WIEAAA  OOOOxx
+8489   2935    1       1       9       9       89      489     489     3489    8489    178     179     NOAAAA  XIEAAA  VVVVxx
+9183   2936    1       3       3       3       83      183     1183    4183    9183    166     167     FPAAAA  YIEAAA  AAAAxx
+8227   2937    1       3       7       7       27      227     227     3227    8227    54      55      LEAAAA  ZIEAAA  HHHHxx
+1844   2938    0       0       4       4       44      844     1844    1844    1844    88      89      YSAAAA  AJEAAA  OOOOxx
+3975   2939    1       3       5       15      75      975     1975    3975    3975    150     151     XWAAAA  BJEAAA  VVVVxx
+6490   2940    0       2       0       10      90      490     490     1490    6490    180     181     QPAAAA  CJEAAA  AAAAxx
+8303   2941    1       3       3       3       3       303     303     3303    8303    6       7       JHAAAA  DJEAAA  HHHHxx
+7334   2942    0       2       4       14      34      334     1334    2334    7334    68      69      CWAAAA  EJEAAA  OOOOxx
+2382   2943    0       2       2       2       82      382     382     2382    2382    164     165     QNAAAA  FJEAAA  VVVVxx
+177    2944    1       1       7       17      77      177     177     177     177     154     155     VGAAAA  GJEAAA  AAAAxx
+8117   2945    1       1       7       17      17      117     117     3117    8117    34      35      FAAAAA  HJEAAA  HHHHxx
+5485   2946    1       1       5       5       85      485     1485    485     5485    170     171     ZCAAAA  IJEAAA  OOOOxx
+6544   2947    0       0       4       4       44      544     544     1544    6544    88      89      SRAAAA  JJEAAA  VVVVxx
+8517   2948    1       1       7       17      17      517     517     3517    8517    34      35      PPAAAA  KJEAAA  AAAAxx
+2252   2949    0       0       2       12      52      252     252     2252    2252    104     105     QIAAAA  LJEAAA  HHHHxx
+4480   2950    0       0       0       0       80      480     480     4480    4480    160     161     IQAAAA  MJEAAA  OOOOxx
+4785   2951    1       1       5       5       85      785     785     4785    4785    170     171     BCAAAA  NJEAAA  VVVVxx
+9700   2952    0       0       0       0       0       700     1700    4700    9700    0       1       CJAAAA  OJEAAA  AAAAxx
+2122   2953    0       2       2       2       22      122     122     2122    2122    44      45      QDAAAA  PJEAAA  HHHHxx
+8783   2954    1       3       3       3       83      783     783     3783    8783    166     167     VZAAAA  QJEAAA  OOOOxx
+1453   2955    1       1       3       13      53      453     1453    1453    1453    106     107     XDAAAA  RJEAAA  VVVVxx
+3908   2956    0       0       8       8       8       908     1908    3908    3908    16      17      IUAAAA  SJEAAA  AAAAxx
+7707   2957    1       3       7       7       7       707     1707    2707    7707    14      15      LKAAAA  TJEAAA  HHHHxx
+9049   2958    1       1       9       9       49      49      1049    4049    9049    98      99      BKAAAA  UJEAAA  OOOOxx
+654    2959    0       2       4       14      54      654     654     654     654     108     109     EZAAAA  VJEAAA  VVVVxx
+3336   2960    0       0       6       16      36      336     1336    3336    3336    72      73      IYAAAA  WJEAAA  AAAAxx
+622    2961    0       2       2       2       22      622     622     622     622     44      45      YXAAAA  XJEAAA  HHHHxx
+8398   2962    0       2       8       18      98      398     398     3398    8398    196     197     ALAAAA  YJEAAA  OOOOxx
+9193   2963    1       1       3       13      93      193     1193    4193    9193    186     187     PPAAAA  ZJEAAA  VVVVxx
+7896   2964    0       0       6       16      96      896     1896    2896    7896    192     193     SRAAAA  AKEAAA  AAAAxx
+9798   2965    0       2       8       18      98      798     1798    4798    9798    196     197     WMAAAA  BKEAAA  HHHHxx
+2881   2966    1       1       1       1       81      881     881     2881    2881    162     163     VGAAAA  CKEAAA  OOOOxx
+672    2967    0       0       2       12      72      672     672     672     672     144     145     WZAAAA  DKEAAA  VVVVxx
+6743   2968    1       3       3       3       43      743     743     1743    6743    86      87      JZAAAA  EKEAAA  AAAAxx
+8935   2969    1       3       5       15      35      935     935     3935    8935    70      71      RFAAAA  FKEAAA  HHHHxx
+2426   2970    0       2       6       6       26      426     426     2426    2426    52      53      IPAAAA  GKEAAA  OOOOxx
+722    2971    0       2       2       2       22      722     722     722     722     44      45      UBAAAA  HKEAAA  VVVVxx
+5088   2972    0       0       8       8       88      88      1088    88      5088    176     177     SNAAAA  IKEAAA  AAAAxx
+8677   2973    1       1       7       17      77      677     677     3677    8677    154     155     TVAAAA  JKEAAA  HHHHxx
+6963   2974    1       3       3       3       63      963     963     1963    6963    126     127     VHAAAA  KKEAAA  OOOOxx
+1653   2975    1       1       3       13      53      653     1653    1653    1653    106     107     PLAAAA  LKEAAA  VVVVxx
+7295   2976    1       3       5       15      95      295     1295    2295    7295    190     191     PUAAAA  MKEAAA  AAAAxx
+6675   2977    1       3       5       15      75      675     675     1675    6675    150     151     TWAAAA  NKEAAA  HHHHxx
+7183   2978    1       3       3       3       83      183     1183    2183    7183    166     167     HQAAAA  OKEAAA  OOOOxx
+4378   2979    0       2       8       18      78      378     378     4378    4378    156     157     KMAAAA  PKEAAA  VVVVxx
+2157   2980    1       1       7       17      57      157     157     2157    2157    114     115     ZEAAAA  QKEAAA  AAAAxx
+2621   2981    1       1       1       1       21      621     621     2621    2621    42      43      VWAAAA  RKEAAA  HHHHxx
+9278   2982    0       2       8       18      78      278     1278    4278    9278    156     157     WSAAAA  SKEAAA  OOOOxx
+79     2983    1       3       9       19      79      79      79      79      79      158     159     BDAAAA  TKEAAA  VVVVxx
+7358   2984    0       2       8       18      58      358     1358    2358    7358    116     117     AXAAAA  UKEAAA  AAAAxx
+3589   2985    1       1       9       9       89      589     1589    3589    3589    178     179     BIAAAA  VKEAAA  HHHHxx
+1254   2986    0       2       4       14      54      254     1254    1254    1254    108     109     GWAAAA  WKEAAA  OOOOxx
+3490   2987    0       2       0       10      90      490     1490    3490    3490    180     181     GEAAAA  XKEAAA  VVVVxx
+7533   2988    1       1       3       13      33      533     1533    2533    7533    66      67      TDAAAA  YKEAAA  AAAAxx
+2800   2989    0       0       0       0       0       800     800     2800    2800    0       1       SDAAAA  ZKEAAA  HHHHxx
+351    2990    1       3       1       11      51      351     351     351     351     102     103     NNAAAA  ALEAAA  OOOOxx
+4359   2991    1       3       9       19      59      359     359     4359    4359    118     119     RLAAAA  BLEAAA  VVVVxx
+5788   2992    0       0       8       8       88      788     1788    788     5788    176     177     QOAAAA  CLEAAA  AAAAxx
+5521   2993    1       1       1       1       21      521     1521    521     5521    42      43      JEAAAA  DLEAAA  HHHHxx
+3351   2994    1       3       1       11      51      351     1351    3351    3351    102     103     XYAAAA  ELEAAA  OOOOxx
+5129   2995    1       1       9       9       29      129     1129    129     5129    58      59      HPAAAA  FLEAAA  VVVVxx
+315    2996    1       3       5       15      15      315     315     315     315     30      31      DMAAAA  GLEAAA  AAAAxx
+7552   2997    0       0       2       12      52      552     1552    2552    7552    104     105     MEAAAA  HLEAAA  HHHHxx
+9176   2998    0       0       6       16      76      176     1176    4176    9176    152     153     YOAAAA  ILEAAA  OOOOxx
+7458   2999    0       2       8       18      58      458     1458    2458    7458    116     117     WAAAAA  JLEAAA  VVVVxx
+279    3000    1       3       9       19      79      279     279     279     279     158     159     TKAAAA  KLEAAA  AAAAxx
+738    3001    0       2       8       18      38      738     738     738     738     76      77      KCAAAA  LLEAAA  HHHHxx
+2557   3002    1       1       7       17      57      557     557     2557    2557    114     115     JUAAAA  MLEAAA  OOOOxx
+9395   3003    1       3       5       15      95      395     1395    4395    9395    190     191     JXAAAA  NLEAAA  VVVVxx
+7214   3004    0       2       4       14      14      214     1214    2214    7214    28      29      MRAAAA  OLEAAA  AAAAxx
+6354   3005    0       2       4       14      54      354     354     1354    6354    108     109     KKAAAA  PLEAAA  HHHHxx
+4799   3006    1       3       9       19      99      799     799     4799    4799    198     199     PCAAAA  QLEAAA  OOOOxx
+1231   3007    1       3       1       11      31      231     1231    1231    1231    62      63      JVAAAA  RLEAAA  VVVVxx
+5252   3008    0       0       2       12      52      252     1252    252     5252    104     105     AUAAAA  SLEAAA  AAAAxx
+5250   3009    0       2       0       10      50      250     1250    250     5250    100     101     YTAAAA  TLEAAA  HHHHxx
+9319   3010    1       3       9       19      19      319     1319    4319    9319    38      39      LUAAAA  ULEAAA  OOOOxx
+1724   3011    0       0       4       4       24      724     1724    1724    1724    48      49      IOAAAA  VLEAAA  VVVVxx
+7947   3012    1       3       7       7       47      947     1947    2947    7947    94      95      RTAAAA  WLEAAA  AAAAxx
+1105   3013    1       1       5       5       5       105     1105    1105    1105    10      11      NQAAAA  XLEAAA  HHHHxx
+1417   3014    1       1       7       17      17      417     1417    1417    1417    34      35      NCAAAA  YLEAAA  OOOOxx
+7101   3015    1       1       1       1       1       101     1101    2101    7101    2       3       DNAAAA  ZLEAAA  VVVVxx
+1088   3016    0       0       8       8       88      88      1088    1088    1088    176     177     WPAAAA  AMEAAA  AAAAxx
+979    3017    1       3       9       19      79      979     979     979     979     158     159     RLAAAA  BMEAAA  HHHHxx
+7589   3018    1       1       9       9       89      589     1589    2589    7589    178     179     XFAAAA  CMEAAA  OOOOxx
+8952   3019    0       0       2       12      52      952     952     3952    8952    104     105     IGAAAA  DMEAAA  VVVVxx
+2864   3020    0       0       4       4       64      864     864     2864    2864    128     129     EGAAAA  EMEAAA  AAAAxx
+234    3021    0       2       4       14      34      234     234     234     234     68      69      AJAAAA  FMEAAA  HHHHxx
+7231   3022    1       3       1       11      31      231     1231    2231    7231    62      63      DSAAAA  GMEAAA  OOOOxx
+6792   3023    0       0       2       12      92      792     792     1792    6792    184     185     GBAAAA  HMEAAA  VVVVxx
+4311   3024    1       3       1       11      11      311     311     4311    4311    22      23      VJAAAA  IMEAAA  AAAAxx
+3374   3025    0       2       4       14      74      374     1374    3374    3374    148     149     UZAAAA  JMEAAA  HHHHxx
+3367   3026    1       3       7       7       67      367     1367    3367    3367    134     135     NZAAAA  KMEAAA  OOOOxx
+2598   3027    0       2       8       18      98      598     598     2598    2598    196     197     YVAAAA  LMEAAA  VVVVxx
+1033   3028    1       1       3       13      33      33      1033    1033    1033    66      67      TNAAAA  MMEAAA  AAAAxx
+7803   3029    1       3       3       3       3       803     1803    2803    7803    6       7       DOAAAA  NMEAAA  HHHHxx
+3870   3030    0       2       0       10      70      870     1870    3870    3870    140     141     WSAAAA  OMEAAA  OOOOxx
+4962   3031    0       2       2       2       62      962     962     4962    4962    124     125     WIAAAA  PMEAAA  VVVVxx
+4842   3032    0       2       2       2       42      842     842     4842    4842    84      85      GEAAAA  QMEAAA  AAAAxx
+8814   3033    0       2       4       14      14      814     814     3814    8814    28      29      ABAAAA  RMEAAA  HHHHxx
+3429   3034    1       1       9       9       29      429     1429    3429    3429    58      59      XBAAAA  SMEAAA  OOOOxx
+6550   3035    0       2       0       10      50      550     550     1550    6550    100     101     YRAAAA  TMEAAA  VVVVxx
+6317   3036    1       1       7       17      17      317     317     1317    6317    34      35      ZIAAAA  UMEAAA  AAAAxx
+5023   3037    1       3       3       3       23      23      1023    23      5023    46      47      FLAAAA  VMEAAA  HHHHxx
+5825   3038    1       1       5       5       25      825     1825    825     5825    50      51      BQAAAA  WMEAAA  OOOOxx
+5297   3039    1       1       7       17      97      297     1297    297     5297    194     195     TVAAAA  XMEAAA  VVVVxx
+8764   3040    0       0       4       4       64      764     764     3764    8764    128     129     CZAAAA  YMEAAA  AAAAxx
+5084   3041    0       0       4       4       84      84      1084    84      5084    168     169     ONAAAA  ZMEAAA  HHHHxx
+6808   3042    0       0       8       8       8       808     808     1808    6808    16      17      WBAAAA  ANEAAA  OOOOxx
+1780   3043    0       0       0       0       80      780     1780    1780    1780    160     161     MQAAAA  BNEAAA  VVVVxx
+4092   3044    0       0       2       12      92      92      92      4092    4092    184     185     KBAAAA  CNEAAA  AAAAxx
+3618   3045    0       2       8       18      18      618     1618    3618    3618    36      37      EJAAAA  DNEAAA  HHHHxx
+7299   3046    1       3       9       19      99      299     1299    2299    7299    198     199     TUAAAA  ENEAAA  OOOOxx
+8544   3047    0       0       4       4       44      544     544     3544    8544    88      89      QQAAAA  FNEAAA  VVVVxx
+2359   3048    1       3       9       19      59      359     359     2359    2359    118     119     TMAAAA  GNEAAA  AAAAxx
+1939   3049    1       3       9       19      39      939     1939    1939    1939    78      79      PWAAAA  HNEAAA  HHHHxx
+5834   3050    0       2       4       14      34      834     1834    834     5834    68      69      KQAAAA  INEAAA  OOOOxx
+1997   3051    1       1       7       17      97      997     1997    1997    1997    194     195     VYAAAA  JNEAAA  VVVVxx
+7917   3052    1       1       7       17      17      917     1917    2917    7917    34      35      NSAAAA  KNEAAA  AAAAxx
+2098   3053    0       2       8       18      98      98      98      2098    2098    196     197     SCAAAA  LNEAAA  HHHHxx
+7576   3054    0       0       6       16      76      576     1576    2576    7576    152     153     KFAAAA  MNEAAA  OOOOxx
+376    3055    0       0       6       16      76      376     376     376     376     152     153     MOAAAA  NNEAAA  VVVVxx
+8535   3056    1       3       5       15      35      535     535     3535    8535    70      71      HQAAAA  ONEAAA  AAAAxx
+5659   3057    1       3       9       19      59      659     1659    659     5659    118     119     RJAAAA  PNEAAA  HHHHxx
+2786   3058    0       2       6       6       86      786     786     2786    2786    172     173     EDAAAA  QNEAAA  OOOOxx
+8820   3059    0       0       0       0       20      820     820     3820    8820    40      41      GBAAAA  RNEAAA  VVVVxx
+1229   3060    1       1       9       9       29      229     1229    1229    1229    58      59      HVAAAA  SNEAAA  AAAAxx
+9321   3061    1       1       1       1       21      321     1321    4321    9321    42      43      NUAAAA  TNEAAA  HHHHxx
+7662   3062    0       2       2       2       62      662     1662    2662    7662    124     125     SIAAAA  UNEAAA  OOOOxx
+5535   3063    1       3       5       15      35      535     1535    535     5535    70      71      XEAAAA  VNEAAA  VVVVxx
+4889   3064    1       1       9       9       89      889     889     4889    4889    178     179     BGAAAA  WNEAAA  AAAAxx
+8259   3065    1       3       9       19      59      259     259     3259    8259    118     119     RFAAAA  XNEAAA  HHHHxx
+6789   3066    1       1       9       9       89      789     789     1789    6789    178     179     DBAAAA  YNEAAA  OOOOxx
+5411   3067    1       3       1       11      11      411     1411    411     5411    22      23      DAAAAA  ZNEAAA  VVVVxx
+6992   3068    0       0       2       12      92      992     992     1992    6992    184     185     YIAAAA  AOEAAA  AAAAxx
+7698   3069    0       2       8       18      98      698     1698    2698    7698    196     197     CKAAAA  BOEAAA  HHHHxx
+2342   3070    0       2       2       2       42      342     342     2342    2342    84      85      CMAAAA  COEAAA  OOOOxx
+1501   3071    1       1       1       1       1       501     1501    1501    1501    2       3       TFAAAA  DOEAAA  VVVVxx
+6322   3072    0       2       2       2       22      322     322     1322    6322    44      45      EJAAAA  EOEAAA  AAAAxx
+9861   3073    1       1       1       1       61      861     1861    4861    9861    122     123     HPAAAA  FOEAAA  HHHHxx
+9802   3074    0       2       2       2       2       802     1802    4802    9802    4       5       ANAAAA  GOEAAA  OOOOxx
+4750   3075    0       2       0       10      50      750     750     4750    4750    100     101     SAAAAA  HOEAAA  VVVVxx
+5855   3076    1       3       5       15      55      855     1855    855     5855    110     111     FRAAAA  IOEAAA  AAAAxx
+4304   3077    0       0       4       4       4       304     304     4304    4304    8       9       OJAAAA  JOEAAA  HHHHxx
+2605   3078    1       1       5       5       5       605     605     2605    2605    10      11      FWAAAA  KOEAAA  OOOOxx
+1802   3079    0       2       2       2       2       802     1802    1802    1802    4       5       IRAAAA  LOEAAA  VVVVxx
+9368   3080    0       0       8       8       68      368     1368    4368    9368    136     137     IWAAAA  MOEAAA  AAAAxx
+7107   3081    1       3       7       7       7       107     1107    2107    7107    14      15      JNAAAA  NOEAAA  HHHHxx
+8895   3082    1       3       5       15      95      895     895     3895    8895    190     191     DEAAAA  OOEAAA  OOOOxx
+3750   3083    0       2       0       10      50      750     1750    3750    3750    100     101     GOAAAA  POEAAA  VVVVxx
+8934   3084    0       2       4       14      34      934     934     3934    8934    68      69      QFAAAA  QOEAAA  AAAAxx
+9464   3085    0       0       4       4       64      464     1464    4464    9464    128     129     AAAAAA  ROEAAA  HHHHxx
+1928   3086    0       0       8       8       28      928     1928    1928    1928    56      57      EWAAAA  SOEAAA  OOOOxx
+3196   3087    0       0       6       16      96      196     1196    3196    3196    192     193     YSAAAA  TOEAAA  VVVVxx
+5256   3088    0       0       6       16      56      256     1256    256     5256    112     113     EUAAAA  UOEAAA  AAAAxx
+7119   3089    1       3       9       19      19      119     1119    2119    7119    38      39      VNAAAA  VOEAAA  HHHHxx
+4495   3090    1       3       5       15      95      495     495     4495    4495    190     191     XQAAAA  WOEAAA  OOOOxx
+9292   3091    0       0       2       12      92      292     1292    4292    9292    184     185     KTAAAA  XOEAAA  VVVVxx
+1617   3092    1       1       7       17      17      617     1617    1617    1617    34      35      FKAAAA  YOEAAA  AAAAxx
+481    3093    1       1       1       1       81      481     481     481     481     162     163     NSAAAA  ZOEAAA  HHHHxx
+56     3094    0       0       6       16      56      56      56      56      56      112     113     ECAAAA  APEAAA  OOOOxx
+9120   3095    0       0       0       0       20      120     1120    4120    9120    40      41      UMAAAA  BPEAAA  VVVVxx
+1306   3096    0       2       6       6       6       306     1306    1306    1306    12      13      GYAAAA  CPEAAA  AAAAxx
+7773   3097    1       1       3       13      73      773     1773    2773    7773    146     147     ZMAAAA  DPEAAA  HHHHxx
+4863   3098    1       3       3       3       63      863     863     4863    4863    126     127     BFAAAA  EPEAAA  OOOOxx
+1114   3099    0       2       4       14      14      114     1114    1114    1114    28      29      WQAAAA  FPEAAA  VVVVxx
+8124   3100    0       0       4       4       24      124     124     3124    8124    48      49      MAAAAA  GPEAAA  AAAAxx
+6254   3101    0       2       4       14      54      254     254     1254    6254    108     109     OGAAAA  HPEAAA  HHHHxx
+8109   3102    1       1       9       9       9       109     109     3109    8109    18      19      XZAAAA  IPEAAA  OOOOxx
+1747   3103    1       3       7       7       47      747     1747    1747    1747    94      95      FPAAAA  JPEAAA  VVVVxx
+6185   3104    1       1       5       5       85      185     185     1185    6185    170     171     XDAAAA  KPEAAA  AAAAxx
+3388   3105    0       0       8       8       88      388     1388    3388    3388    176     177     IAAAAA  LPEAAA  HHHHxx
+4905   3106    1       1       5       5       5       905     905     4905    4905    10      11      RGAAAA  MPEAAA  OOOOxx
+5728   3107    0       0       8       8       28      728     1728    728     5728    56      57      IMAAAA  NPEAAA  VVVVxx
+7507   3108    1       3       7       7       7       507     1507    2507    7507    14      15      TCAAAA  OPEAAA  AAAAxx
+5662   3109    0       2       2       2       62      662     1662    662     5662    124     125     UJAAAA  PPEAAA  HHHHxx
+1686   3110    0       2       6       6       86      686     1686    1686    1686    172     173     WMAAAA  QPEAAA  OOOOxx
+5202   3111    0       2       2       2       2       202     1202    202     5202    4       5       CSAAAA  RPEAAA  VVVVxx
+6905   3112    1       1       5       5       5       905     905     1905    6905    10      11      PFAAAA  SPEAAA  AAAAxx
+9577   3113    1       1       7       17      77      577     1577    4577    9577    154     155     JEAAAA  TPEAAA  HHHHxx
+7194   3114    0       2       4       14      94      194     1194    2194    7194    188     189     SQAAAA  UPEAAA  OOOOxx
+7016   3115    0       0       6       16      16      16      1016    2016    7016    32      33      WJAAAA  VPEAAA  VVVVxx
+8905   3116    1       1       5       5       5       905     905     3905    8905    10      11      NEAAAA  WPEAAA  AAAAxx
+3419   3117    1       3       9       19      19      419     1419    3419    3419    38      39      NBAAAA  XPEAAA  HHHHxx
+6881   3118    1       1       1       1       81      881     881     1881    6881    162     163     REAAAA  YPEAAA  OOOOxx
+8370   3119    0       2       0       10      70      370     370     3370    8370    140     141     YJAAAA  ZPEAAA  VVVVxx
+6117   3120    1       1       7       17      17      117     117     1117    6117    34      35      HBAAAA  AQEAAA  AAAAxx
+1636   3121    0       0       6       16      36      636     1636    1636    1636    72      73      YKAAAA  BQEAAA  HHHHxx
+6857   3122    1       1       7       17      57      857     857     1857    6857    114     115     TDAAAA  CQEAAA  OOOOxx
+7163   3123    1       3       3       3       63      163     1163    2163    7163    126     127     NPAAAA  DQEAAA  VVVVxx
+5040   3124    0       0       0       0       40      40      1040    40      5040    80      81      WLAAAA  EQEAAA  AAAAxx
+6263   3125    1       3       3       3       63      263     263     1263    6263    126     127     XGAAAA  FQEAAA  HHHHxx
+4809   3126    1       1       9       9       9       809     809     4809    4809    18      19      ZCAAAA  GQEAAA  OOOOxx
+900    3127    0       0       0       0       0       900     900     900     900     0       1       QIAAAA  HQEAAA  VVVVxx
+3199   3128    1       3       9       19      99      199     1199    3199    3199    198     199     BTAAAA  IQEAAA  AAAAxx
+4156   3129    0       0       6       16      56      156     156     4156    4156    112     113     WDAAAA  JQEAAA  HHHHxx
+3501   3130    1       1       1       1       1       501     1501    3501    3501    2       3       REAAAA  KQEAAA  OOOOxx
+164    3131    0       0       4       4       64      164     164     164     164     128     129     IGAAAA  LQEAAA  VVVVxx
+9548   3132    0       0       8       8       48      548     1548    4548    9548    96      97      GDAAAA  MQEAAA  AAAAxx
+1149   3133    1       1       9       9       49      149     1149    1149    1149    98      99      FSAAAA  NQEAAA  HHHHxx
+1962   3134    0       2       2       2       62      962     1962    1962    1962    124     125     MXAAAA  OQEAAA  OOOOxx
+4072   3135    0       0       2       12      72      72      72      4072    4072    144     145     QAAAAA  PQEAAA  VVVVxx
+4280   3136    0       0       0       0       80      280     280     4280    4280    160     161     QIAAAA  QQEAAA  AAAAxx
+1398   3137    0       2       8       18      98      398     1398    1398    1398    196     197     UBAAAA  RQEAAA  HHHHxx
+725    3138    1       1       5       5       25      725     725     725     725     50      51      XBAAAA  SQEAAA  OOOOxx
+3988   3139    0       0       8       8       88      988     1988    3988    3988    176     177     KXAAAA  TQEAAA  VVVVxx
+5059   3140    1       3       9       19      59      59      1059    59      5059    118     119     PMAAAA  UQEAAA  AAAAxx
+2632   3141    0       0       2       12      32      632     632     2632    2632    64      65      GXAAAA  VQEAAA  HHHHxx
+1909   3142    1       1       9       9       9       909     1909    1909    1909    18      19      LVAAAA  WQEAAA  OOOOxx
+6827   3143    1       3       7       7       27      827     827     1827    6827    54      55      PCAAAA  XQEAAA  VVVVxx
+8156   3144    0       0       6       16      56      156     156     3156    8156    112     113     SBAAAA  YQEAAA  AAAAxx
+1192   3145    0       0       2       12      92      192     1192    1192    1192    184     185     WTAAAA  ZQEAAA  HHHHxx
+9545   3146    1       1       5       5       45      545     1545    4545    9545    90      91      DDAAAA  AREAAA  OOOOxx
+2249   3147    1       1       9       9       49      249     249     2249    2249    98      99      NIAAAA  BREAAA  VVVVxx
+5580   3148    0       0       0       0       80      580     1580    580     5580    160     161     QGAAAA  CREAAA  AAAAxx
+8403   3149    1       3       3       3       3       403     403     3403    8403    6       7       FLAAAA  DREAAA  HHHHxx
+4024   3150    0       0       4       4       24      24      24      4024    4024    48      49      UYAAAA  EREAAA  OOOOxx
+1866   3151    0       2       6       6       66      866     1866    1866    1866    132     133     UTAAAA  FREAAA  VVVVxx
+9251   3152    1       3       1       11      51      251     1251    4251    9251    102     103     VRAAAA  GREAAA  AAAAxx
+9979   3153    1       3       9       19      79      979     1979    4979    9979    158     159     VTAAAA  HREAAA  HHHHxx
+9899   3154    1       3       9       19      99      899     1899    4899    9899    198     199     TQAAAA  IREAAA  OOOOxx
+2540   3155    0       0       0       0       40      540     540     2540    2540    80      81      STAAAA  JREAAA  VVVVxx
+8957   3156    1       1       7       17      57      957     957     3957    8957    114     115     NGAAAA  KREAAA  AAAAxx
+7702   3157    0       2       2       2       2       702     1702    2702    7702    4       5       GKAAAA  LREAAA  HHHHxx
+4211   3158    1       3       1       11      11      211     211     4211    4211    22      23      ZFAAAA  MREAAA  OOOOxx
+6684   3159    0       0       4       4       84      684     684     1684    6684    168     169     CXAAAA  NREAAA  VVVVxx
+3883   3160    1       3       3       3       83      883     1883    3883    3883    166     167     JTAAAA  OREAAA  AAAAxx
+3531   3161    1       3       1       11      31      531     1531    3531    3531    62      63      VFAAAA  PREAAA  HHHHxx
+9178   3162    0       2       8       18      78      178     1178    4178    9178    156     157     APAAAA  QREAAA  OOOOxx
+3389   3163    1       1       9       9       89      389     1389    3389    3389    178     179     JAAAAA  RREAAA  VVVVxx
+7874   3164    0       2       4       14      74      874     1874    2874    7874    148     149     WQAAAA  SREAAA  AAAAxx
+4522   3165    0       2       2       2       22      522     522     4522    4522    44      45      YRAAAA  TREAAA  HHHHxx
+9399   3166    1       3       9       19      99      399     1399    4399    9399    198     199     NXAAAA  UREAAA  OOOOxx
+9083   3167    1       3       3       3       83      83      1083    4083    9083    166     167     JLAAAA  VREAAA  VVVVxx
+1530   3168    0       2       0       10      30      530     1530    1530    1530    60      61      WGAAAA  WREAAA  AAAAxx
+2360   3169    0       0       0       0       60      360     360     2360    2360    120     121     UMAAAA  XREAAA  HHHHxx
+4908   3170    0       0       8       8       8       908     908     4908    4908    16      17      UGAAAA  YREAAA  OOOOxx
+4628   3171    0       0       8       8       28      628     628     4628    4628    56      57      AWAAAA  ZREAAA  VVVVxx
+3889   3172    1       1       9       9       89      889     1889    3889    3889    178     179     PTAAAA  ASEAAA  AAAAxx
+1331   3173    1       3       1       11      31      331     1331    1331    1331    62      63      FZAAAA  BSEAAA  HHHHxx
+1942   3174    0       2       2       2       42      942     1942    1942    1942    84      85      SWAAAA  CSEAAA  OOOOxx
+4734   3175    0       2       4       14      34      734     734     4734    4734    68      69      CAAAAA  DSEAAA  VVVVxx
+8386   3176    0       2       6       6       86      386     386     3386    8386    172     173     OKAAAA  ESEAAA  AAAAxx
+3586   3177    0       2       6       6       86      586     1586    3586    3586    172     173     YHAAAA  FSEAAA  HHHHxx
+2354   3178    0       2       4       14      54      354     354     2354    2354    108     109     OMAAAA  GSEAAA  OOOOxx
+7108   3179    0       0       8       8       8       108     1108    2108    7108    16      17      KNAAAA  HSEAAA  VVVVxx
+1857   3180    1       1       7       17      57      857     1857    1857    1857    114     115     LTAAAA  ISEAAA  AAAAxx
+2544   3181    0       0       4       4       44      544     544     2544    2544    88      89      WTAAAA  JSEAAA  HHHHxx
+819    3182    1       3       9       19      19      819     819     819     819     38      39      NFAAAA  KSEAAA  OOOOxx
+2878   3183    0       2       8       18      78      878     878     2878    2878    156     157     SGAAAA  LSEAAA  VVVVxx
+1772   3184    0       0       2       12      72      772     1772    1772    1772    144     145     EQAAAA  MSEAAA  AAAAxx
+354    3185    0       2       4       14      54      354     354     354     354     108     109     QNAAAA  NSEAAA  HHHHxx
+3259   3186    1       3       9       19      59      259     1259    3259    3259    118     119     JVAAAA  OSEAAA  OOOOxx
+2170   3187    0       2       0       10      70      170     170     2170    2170    140     141     MFAAAA  PSEAAA  VVVVxx
+1190   3188    0       2       0       10      90      190     1190    1190    1190    180     181     UTAAAA  QSEAAA  AAAAxx
+3607   3189    1       3       7       7       7       607     1607    3607    3607    14      15      TIAAAA  RSEAAA  HHHHxx
+4661   3190    1       1       1       1       61      661     661     4661    4661    122     123     HXAAAA  SSEAAA  OOOOxx
+1796   3191    0       0       6       16      96      796     1796    1796    1796    192     193     CRAAAA  TSEAAA  VVVVxx
+1561   3192    1       1       1       1       61      561     1561    1561    1561    122     123     BIAAAA  USEAAA  AAAAxx
+4336   3193    0       0       6       16      36      336     336     4336    4336    72      73      UKAAAA  VSEAAA  HHHHxx
+7550   3194    0       2       0       10      50      550     1550    2550    7550    100     101     KEAAAA  WSEAAA  OOOOxx
+3238   3195    0       2       8       18      38      238     1238    3238    3238    76      77      OUAAAA  XSEAAA  VVVVxx
+9870   3196    0       2       0       10      70      870     1870    4870    9870    140     141     QPAAAA  YSEAAA  AAAAxx
+6502   3197    0       2       2       2       2       502     502     1502    6502    4       5       CQAAAA  ZSEAAA  HHHHxx
+3903   3198    1       3       3       3       3       903     1903    3903    3903    6       7       DUAAAA  ATEAAA  OOOOxx
+2869   3199    1       1       9       9       69      869     869     2869    2869    138     139     JGAAAA  BTEAAA  VVVVxx
+5072   3200    0       0       2       12      72      72      1072    72      5072    144     145     CNAAAA  CTEAAA  AAAAxx
+1201   3201    1       1       1       1       1       201     1201    1201    1201    2       3       FUAAAA  DTEAAA  HHHHxx
+6245   3202    1       1       5       5       45      245     245     1245    6245    90      91      FGAAAA  ETEAAA  OOOOxx
+1402   3203    0       2       2       2       2       402     1402    1402    1402    4       5       YBAAAA  FTEAAA  VVVVxx
+2594   3204    0       2       4       14      94      594     594     2594    2594    188     189     UVAAAA  GTEAAA  AAAAxx
+9171   3205    1       3       1       11      71      171     1171    4171    9171    142     143     TOAAAA  HTEAAA  HHHHxx
+2620   3206    0       0       0       0       20      620     620     2620    2620    40      41      UWAAAA  ITEAAA  OOOOxx
+6309   3207    1       1       9       9       9       309     309     1309    6309    18      19      RIAAAA  JTEAAA  VVVVxx
+1285   3208    1       1       5       5       85      285     1285    1285    1285    170     171     LXAAAA  KTEAAA  AAAAxx
+5466   3209    0       2       6       6       66      466     1466    466     5466    132     133     GCAAAA  LTEAAA  HHHHxx
+168    3210    0       0       8       8       68      168     168     168     168     136     137     MGAAAA  MTEAAA  OOOOxx
+1410   3211    0       2       0       10      10      410     1410    1410    1410    20      21      GCAAAA  NTEAAA  VVVVxx
+6332   3212    0       0       2       12      32      332     332     1332    6332    64      65      OJAAAA  OTEAAA  AAAAxx
+9530   3213    0       2       0       10      30      530     1530    4530    9530    60      61      OCAAAA  PTEAAA  HHHHxx
+7749   3214    1       1       9       9       49      749     1749    2749    7749    98      99      BMAAAA  QTEAAA  OOOOxx
+3656   3215    0       0       6       16      56      656     1656    3656    3656    112     113     QKAAAA  RTEAAA  VVVVxx
+37     3216    1       1       7       17      37      37      37      37      37      74      75      LBAAAA  STEAAA  AAAAxx
+2744   3217    0       0       4       4       44      744     744     2744    2744    88      89      OBAAAA  TTEAAA  HHHHxx
+4206   3218    0       2       6       6       6       206     206     4206    4206    12      13      UFAAAA  UTEAAA  OOOOxx
+1846   3219    0       2       6       6       46      846     1846    1846    1846    92      93      ATAAAA  VTEAAA  VVVVxx
+9913   3220    1       1       3       13      13      913     1913    4913    9913    26      27      HRAAAA  WTEAAA  AAAAxx
+4078   3221    0       2       8       18      78      78      78      4078    4078    156     157     WAAAAA  XTEAAA  HHHHxx
+2080   3222    0       0       0       0       80      80      80      2080    2080    160     161     ACAAAA  YTEAAA  OOOOxx
+4169   3223    1       1       9       9       69      169     169     4169    4169    138     139     JEAAAA  ZTEAAA  VVVVxx
+2070   3224    0       2       0       10      70      70      70      2070    2070    140     141     QBAAAA  AUEAAA  AAAAxx
+4500   3225    0       0       0       0       0       500     500     4500    4500    0       1       CRAAAA  BUEAAA  HHHHxx
+4123   3226    1       3       3       3       23      123     123     4123    4123    46      47      PCAAAA  CUEAAA  OOOOxx
+5594   3227    0       2       4       14      94      594     1594    594     5594    188     189     EHAAAA  DUEAAA  VVVVxx
+9941   3228    1       1       1       1       41      941     1941    4941    9941    82      83      JSAAAA  EUEAAA  AAAAxx
+7154   3229    0       2       4       14      54      154     1154    2154    7154    108     109     EPAAAA  FUEAAA  HHHHxx
+8340   3230    0       0       0       0       40      340     340     3340    8340    80      81      UIAAAA  GUEAAA  OOOOxx
+7110   3231    0       2       0       10      10      110     1110    2110    7110    20      21      MNAAAA  HUEAAA  VVVVxx
+7795   3232    1       3       5       15      95      795     1795    2795    7795    190     191     VNAAAA  IUEAAA  AAAAxx
+132    3233    0       0       2       12      32      132     132     132     132     64      65      CFAAAA  JUEAAA  HHHHxx
+4603   3234    1       3       3       3       3       603     603     4603    4603    6       7       BVAAAA  KUEAAA  OOOOxx
+9720   3235    0       0       0       0       20      720     1720    4720    9720    40      41      WJAAAA  LUEAAA  VVVVxx
+1460   3236    0       0       0       0       60      460     1460    1460    1460    120     121     EEAAAA  MUEAAA  AAAAxx
+4677   3237    1       1       7       17      77      677     677     4677    4677    154     155     XXAAAA  NUEAAA  HHHHxx
+9272   3238    0       0       2       12      72      272     1272    4272    9272    144     145     QSAAAA  OUEAAA  OOOOxx
+2279   3239    1       3       9       19      79      279     279     2279    2279    158     159     RJAAAA  PUEAAA  VVVVxx
+4587   3240    1       3       7       7       87      587     587     4587    4587    174     175     LUAAAA  QUEAAA  AAAAxx
+2244   3241    0       0       4       4       44      244     244     2244    2244    88      89      IIAAAA  RUEAAA  HHHHxx
+742    3242    0       2       2       2       42      742     742     742     742     84      85      OCAAAA  SUEAAA  OOOOxx
+4426   3243    0       2       6       6       26      426     426     4426    4426    52      53      GOAAAA  TUEAAA  VVVVxx
+4571   3244    1       3       1       11      71      571     571     4571    4571    142     143     VTAAAA  UUEAAA  AAAAxx
+4775   3245    1       3       5       15      75      775     775     4775    4775    150     151     RBAAAA  VUEAAA  HHHHxx
+24     3246    0       0       4       4       24      24      24      24      24      48      49      YAAAAA  WUEAAA  OOOOxx
+4175   3247    1       3       5       15      75      175     175     4175    4175    150     151     PEAAAA  XUEAAA  VVVVxx
+9877   3248    1       1       7       17      77      877     1877    4877    9877    154     155     XPAAAA  YUEAAA  AAAAxx
+7271   3249    1       3       1       11      71      271     1271    2271    7271    142     143     RTAAAA  ZUEAAA  HHHHxx
+5468   3250    0       0       8       8       68      468     1468    468     5468    136     137     ICAAAA  AVEAAA  OOOOxx
+6106   3251    0       2       6       6       6       106     106     1106    6106    12      13      WAAAAA  BVEAAA  VVVVxx
+9005   3252    1       1       5       5       5       5       1005    4005    9005    10      11      JIAAAA  CVEAAA  AAAAxx
+109    3253    1       1       9       9       9       109     109     109     109     18      19      FEAAAA  DVEAAA  HHHHxx
+6365   3254    1       1       5       5       65      365     365     1365    6365    130     131     VKAAAA  EVEAAA  OOOOxx
+7437   3255    1       1       7       17      37      437     1437    2437    7437    74      75      BAAAAA  FVEAAA  VVVVxx
+7979   3256    1       3       9       19      79      979     1979    2979    7979    158     159     XUAAAA  GVEAAA  AAAAxx
+6050   3257    0       2       0       10      50      50      50      1050    6050    100     101     SYAAAA  HVEAAA  HHHHxx
+2853   3258    1       1       3       13      53      853     853     2853    2853    106     107     TFAAAA  IVEAAA  OOOOxx
+7603   3259    1       3       3       3       3       603     1603    2603    7603    6       7       LGAAAA  JVEAAA  VVVVxx
+483    3260    1       3       3       3       83      483     483     483     483     166     167     PSAAAA  KVEAAA  AAAAxx
+5994   3261    0       2       4       14      94      994     1994    994     5994    188     189     OWAAAA  LVEAAA  HHHHxx
+6708   3262    0       0       8       8       8       708     708     1708    6708    16      17      AYAAAA  MVEAAA  OOOOxx
+5090   3263    0       2       0       10      90      90      1090    90      5090    180     181     UNAAAA  NVEAAA  VVVVxx
+4608   3264    0       0       8       8       8       608     608     4608    4608    16      17      GVAAAA  OVEAAA  AAAAxx
+4551   3265    1       3       1       11      51      551     551     4551    4551    102     103     BTAAAA  PVEAAA  HHHHxx
+5437   3266    1       1       7       17      37      437     1437    437     5437    74      75      DBAAAA  QVEAAA  OOOOxx
+4130   3267    0       2       0       10      30      130     130     4130    4130    60      61      WCAAAA  RVEAAA  VVVVxx
+6363   3268    1       3       3       3       63      363     363     1363    6363    126     127     TKAAAA  SVEAAA  AAAAxx
+1499   3269    1       3       9       19      99      499     1499    1499    1499    198     199     RFAAAA  TVEAAA  HHHHxx
+384    3270    0       0       4       4       84      384     384     384     384     168     169     UOAAAA  UVEAAA  OOOOxx
+2266   3271    0       2       6       6       66      266     266     2266    2266    132     133     EJAAAA  VVEAAA  VVVVxx
+6018   3272    0       2       8       18      18      18      18      1018    6018    36      37      MXAAAA  WVEAAA  AAAAxx
+7915   3273    1       3       5       15      15      915     1915    2915    7915    30      31      LSAAAA  XVEAAA  HHHHxx
+6167   3274    1       3       7       7       67      167     167     1167    6167    134     135     FDAAAA  YVEAAA  OOOOxx
+9988   3275    0       0       8       8       88      988     1988    4988    9988    176     177     EUAAAA  ZVEAAA  VVVVxx
+6599   3276    1       3       9       19      99      599     599     1599    6599    198     199     VTAAAA  AWEAAA  AAAAxx
+1693   3277    1       1       3       13      93      693     1693    1693    1693    186     187     DNAAAA  BWEAAA  HHHHxx
+5971   3278    1       3       1       11      71      971     1971    971     5971    142     143     RVAAAA  CWEAAA  OOOOxx
+8470   3279    0       2       0       10      70      470     470     3470    8470    140     141     UNAAAA  DWEAAA  VVVVxx
+2807   3280    1       3       7       7       7       807     807     2807    2807    14      15      ZDAAAA  EWEAAA  AAAAxx
+1120   3281    0       0       0       0       20      120     1120    1120    1120    40      41      CRAAAA  FWEAAA  HHHHxx
+5924   3282    0       0       4       4       24      924     1924    924     5924    48      49      WTAAAA  GWEAAA  OOOOxx
+9025   3283    1       1       5       5       25      25      1025    4025    9025    50      51      DJAAAA  HWEAAA  VVVVxx
+9454   3284    0       2       4       14      54      454     1454    4454    9454    108     109     QZAAAA  IWEAAA  AAAAxx
+2259   3285    1       3       9       19      59      259     259     2259    2259    118     119     XIAAAA  JWEAAA  HHHHxx
+5249   3286    1       1       9       9       49      249     1249    249     5249    98      99      XTAAAA  KWEAAA  OOOOxx
+6350   3287    0       2       0       10      50      350     350     1350    6350    100     101     GKAAAA  LWEAAA  VVVVxx
+2930   3288    0       2       0       10      30      930     930     2930    2930    60      61      SIAAAA  MWEAAA  AAAAxx
+6055   3289    1       3       5       15      55      55      55      1055    6055    110     111     XYAAAA  NWEAAA  HHHHxx
+7691   3290    1       3       1       11      91      691     1691    2691    7691    182     183     VJAAAA  OWEAAA  OOOOxx
+1573   3291    1       1       3       13      73      573     1573    1573    1573    146     147     NIAAAA  PWEAAA  VVVVxx
+9943   3292    1       3       3       3       43      943     1943    4943    9943    86      87      LSAAAA  QWEAAA  AAAAxx
+3085   3293    1       1       5       5       85      85      1085    3085    3085    170     171     ROAAAA  RWEAAA  HHHHxx
+5928   3294    0       0       8       8       28      928     1928    928     5928    56      57      AUAAAA  SWEAAA  OOOOxx
+887    3295    1       3       7       7       87      887     887     887     887     174     175     DIAAAA  TWEAAA  VVVVxx
+4630   3296    0       2       0       10      30      630     630     4630    4630    60      61      CWAAAA  UWEAAA  AAAAxx
+9827   3297    1       3       7       7       27      827     1827    4827    9827    54      55      ZNAAAA  VWEAAA  HHHHxx
+8926   3298    0       2       6       6       26      926     926     3926    8926    52      53      IFAAAA  WWEAAA  OOOOxx
+5726   3299    0       2       6       6       26      726     1726    726     5726    52      53      GMAAAA  XWEAAA  VVVVxx
+1569   3300    1       1       9       9       69      569     1569    1569    1569    138     139     JIAAAA  YWEAAA  AAAAxx
+8074   3301    0       2       4       14      74      74      74      3074    8074    148     149     OYAAAA  ZWEAAA  HHHHxx
+7909   3302    1       1       9       9       9       909     1909    2909    7909    18      19      FSAAAA  AXEAAA  OOOOxx
+8367   3303    1       3       7       7       67      367     367     3367    8367    134     135     VJAAAA  BXEAAA  VVVVxx
+7217   3304    1       1       7       17      17      217     1217    2217    7217    34      35      PRAAAA  CXEAAA  AAAAxx
+5254   3305    0       2       4       14      54      254     1254    254     5254    108     109     CUAAAA  DXEAAA  HHHHxx
+1181   3306    1       1       1       1       81      181     1181    1181    1181    162     163     LTAAAA  EXEAAA  OOOOxx
+6907   3307    1       3       7       7       7       907     907     1907    6907    14      15      RFAAAA  FXEAAA  VVVVxx
+5508   3308    0       0       8       8       8       508     1508    508     5508    16      17      WDAAAA  GXEAAA  AAAAxx
+4782   3309    0       2       2       2       82      782     782     4782    4782    164     165     YBAAAA  HXEAAA  HHHHxx
+793    3310    1       1       3       13      93      793     793     793     793     186     187     NEAAAA  IXEAAA  OOOOxx
+5740   3311    0       0       0       0       40      740     1740    740     5740    80      81      UMAAAA  JXEAAA  VVVVxx
+3107   3312    1       3       7       7       7       107     1107    3107    3107    14      15      NPAAAA  KXEAAA  AAAAxx
+1197   3313    1       1       7       17      97      197     1197    1197    1197    194     195     BUAAAA  LXEAAA  HHHHxx
+4376   3314    0       0       6       16      76      376     376     4376    4376    152     153     IMAAAA  MXEAAA  OOOOxx
+6226   3315    0       2       6       6       26      226     226     1226    6226    52      53      MFAAAA  NXEAAA  VVVVxx
+5033   3316    1       1       3       13      33      33      1033    33      5033    66      67      PLAAAA  OXEAAA  AAAAxx
+5494   3317    0       2       4       14      94      494     1494    494     5494    188     189     IDAAAA  PXEAAA  HHHHxx
+3244   3318    0       0       4       4       44      244     1244    3244    3244    88      89      UUAAAA  QXEAAA  OOOOxx
+7670   3319    0       2       0       10      70      670     1670    2670    7670    140     141     AJAAAA  RXEAAA  VVVVxx
+9273   3320    1       1       3       13      73      273     1273    4273    9273    146     147     RSAAAA  SXEAAA  AAAAxx
+5248   3321    0       0       8       8       48      248     1248    248     5248    96      97      WTAAAA  TXEAAA  HHHHxx
+3381   3322    1       1       1       1       81      381     1381    3381    3381    162     163     BAAAAA  UXEAAA  OOOOxx
+4136   3323    0       0       6       16      36      136     136     4136    4136    72      73      CDAAAA  VXEAAA  VVVVxx
+4163   3324    1       3       3       3       63      163     163     4163    4163    126     127     DEAAAA  WXEAAA  AAAAxx
+4270   3325    0       2       0       10      70      270     270     4270    4270    140     141     GIAAAA  XXEAAA  HHHHxx
+1729   3326    1       1       9       9       29      729     1729    1729    1729    58      59      NOAAAA  YXEAAA  OOOOxx
+2778   3327    0       2       8       18      78      778     778     2778    2778    156     157     WCAAAA  ZXEAAA  VVVVxx
+5082   3328    0       2       2       2       82      82      1082    82      5082    164     165     MNAAAA  AYEAAA  AAAAxx
+870    3329    0       2       0       10      70      870     870     870     870     140     141     MHAAAA  BYEAAA  HHHHxx
+4192   3330    0       0       2       12      92      192     192     4192    4192    184     185     GFAAAA  CYEAAA  OOOOxx
+308    3331    0       0       8       8       8       308     308     308     308     16      17      WLAAAA  DYEAAA  VVVVxx
+6783   3332    1       3       3       3       83      783     783     1783    6783    166     167     XAAAAA  EYEAAA  AAAAxx
+7611   3333    1       3       1       11      11      611     1611    2611    7611    22      23      TGAAAA  FYEAAA  HHHHxx
+4221   3334    1       1       1       1       21      221     221     4221    4221    42      43      JGAAAA  GYEAAA  OOOOxx
+6353   3335    1       1       3       13      53      353     353     1353    6353    106     107     JKAAAA  HYEAAA  VVVVxx
+1830   3336    0       2       0       10      30      830     1830    1830    1830    60      61      KSAAAA  IYEAAA  AAAAxx
+2437   3337    1       1       7       17      37      437     437     2437    2437    74      75      TPAAAA  JYEAAA  HHHHxx
+3360   3338    0       0       0       0       60      360     1360    3360    3360    120     121     GZAAAA  KYEAAA  OOOOxx
+1829   3339    1       1       9       9       29      829     1829    1829    1829    58      59      JSAAAA  LYEAAA  VVVVxx
+9475   3340    1       3       5       15      75      475     1475    4475    9475    150     151     LAAAAA  MYEAAA  AAAAxx
+4566   3341    0       2       6       6       66      566     566     4566    4566    132     133     QTAAAA  NYEAAA  HHHHxx
+9944   3342    0       0       4       4       44      944     1944    4944    9944    88      89      MSAAAA  OYEAAA  OOOOxx
+6054   3343    0       2       4       14      54      54      54      1054    6054    108     109     WYAAAA  PYEAAA  VVVVxx
+4722   3344    0       2       2       2       22      722     722     4722    4722    44      45      QZAAAA  QYEAAA  AAAAxx
+2779   3345    1       3       9       19      79      779     779     2779    2779    158     159     XCAAAA  RYEAAA  HHHHxx
+8051   3346    1       3       1       11      51      51      51      3051    8051    102     103     RXAAAA  SYEAAA  OOOOxx
+9671   3347    1       3       1       11      71      671     1671    4671    9671    142     143     ZHAAAA  TYEAAA  VVVVxx
+6084   3348    0       0       4       4       84      84      84      1084    6084    168     169     AAAAAA  UYEAAA  AAAAxx
+3729   3349    1       1       9       9       29      729     1729    3729    3729    58      59      LNAAAA  VYEAAA  HHHHxx
+6627   3350    1       3       7       7       27      627     627     1627    6627    54      55      XUAAAA  WYEAAA  OOOOxx
+4769   3351    1       1       9       9       69      769     769     4769    4769    138     139     LBAAAA  XYEAAA  VVVVxx
+2224   3352    0       0       4       4       24      224     224     2224    2224    48      49      OHAAAA  YYEAAA  AAAAxx
+1404   3353    0       0       4       4       4       404     1404    1404    1404    8       9       ACAAAA  ZYEAAA  HHHHxx
+8532   3354    0       0       2       12      32      532     532     3532    8532    64      65      EQAAAA  AZEAAA  OOOOxx
+6759   3355    1       3       9       19      59      759     759     1759    6759    118     119     ZZAAAA  BZEAAA  VVVVxx
+6404   3356    0       0       4       4       4       404     404     1404    6404    8       9       IMAAAA  CZEAAA  AAAAxx
+3144   3357    0       0       4       4       44      144     1144    3144    3144    88      89      YQAAAA  DZEAAA  HHHHxx
+973    3358    1       1       3       13      73      973     973     973     973     146     147     LLAAAA  EZEAAA  OOOOxx
+9789   3359    1       1       9       9       89      789     1789    4789    9789    178     179     NMAAAA  FZEAAA  VVVVxx
+6181   3360    1       1       1       1       81      181     181     1181    6181    162     163     TDAAAA  GZEAAA  AAAAxx
+1519   3361    1       3       9       19      19      519     1519    1519    1519    38      39      LGAAAA  HZEAAA  HHHHxx
+9729   3362    1       1       9       9       29      729     1729    4729    9729    58      59      FKAAAA  IZEAAA  OOOOxx
+8167   3363    1       3       7       7       67      167     167     3167    8167    134     135     DCAAAA  JZEAAA  VVVVxx
+3830   3364    0       2       0       10      30      830     1830    3830    3830    60      61      IRAAAA  KZEAAA  AAAAxx
+6286   3365    0       2       6       6       86      286     286     1286    6286    172     173     UHAAAA  LZEAAA  HHHHxx
+3047   3366    1       3       7       7       47      47      1047    3047    3047    94      95      FNAAAA  MZEAAA  OOOOxx
+3183   3367    1       3       3       3       83      183     1183    3183    3183    166     167     LSAAAA  NZEAAA  VVVVxx
+6687   3368    1       3       7       7       87      687     687     1687    6687    174     175     FXAAAA  OZEAAA  AAAAxx
+2783   3369    1       3       3       3       83      783     783     2783    2783    166     167     BDAAAA  PZEAAA  HHHHxx
+9920   3370    0       0       0       0       20      920     1920    4920    9920    40      41      ORAAAA  QZEAAA  OOOOxx
+4847   3371    1       3       7       7       47      847     847     4847    4847    94      95      LEAAAA  RZEAAA  VVVVxx
+3645   3372    1       1       5       5       45      645     1645    3645    3645    90      91      FKAAAA  SZEAAA  AAAAxx
+7406   3373    0       2       6       6       6       406     1406    2406    7406    12      13      WYAAAA  TZEAAA  HHHHxx
+6003   3374    1       3       3       3       3       3       3       1003    6003    6       7       XWAAAA  UZEAAA  OOOOxx
+3408   3375    0       0       8       8       8       408     1408    3408    3408    16      17      CBAAAA  VZEAAA  VVVVxx
+4243   3376    1       3       3       3       43      243     243     4243    4243    86      87      FHAAAA  WZEAAA  AAAAxx
+1622   3377    0       2       2       2       22      622     1622    1622    1622    44      45      KKAAAA  XZEAAA  HHHHxx
+5319   3378    1       3       9       19      19      319     1319    319     5319    38      39      PWAAAA  YZEAAA  OOOOxx
+4033   3379    1       1       3       13      33      33      33      4033    4033    66      67      DZAAAA  ZZEAAA  VVVVxx
+8573   3380    1       1       3       13      73      573     573     3573    8573    146     147     TRAAAA  AAFAAA  AAAAxx
+8404   3381    0       0       4       4       4       404     404     3404    8404    8       9       GLAAAA  BAFAAA  HHHHxx
+6993   3382    1       1       3       13      93      993     993     1993    6993    186     187     ZIAAAA  CAFAAA  OOOOxx
+660    3383    0       0       0       0       60      660     660     660     660     120     121     KZAAAA  DAFAAA  VVVVxx
+1136   3384    0       0       6       16      36      136     1136    1136    1136    72      73      SRAAAA  EAFAAA  AAAAxx
+3393   3385    1       1       3       13      93      393     1393    3393    3393    186     187     NAAAAA  FAFAAA  HHHHxx
+9743   3386    1       3       3       3       43      743     1743    4743    9743    86      87      TKAAAA  GAFAAA  OOOOxx
+9705   3387    1       1       5       5       5       705     1705    4705    9705    10      11      HJAAAA  HAFAAA  VVVVxx
+6960   3388    0       0       0       0       60      960     960     1960    6960    120     121     SHAAAA  IAFAAA  AAAAxx
+2753   3389    1       1       3       13      53      753     753     2753    2753    106     107     XBAAAA  JAFAAA  HHHHxx
+906    3390    0       2       6       6       6       906     906     906     906     12      13      WIAAAA  KAFAAA  OOOOxx
+999    3391    1       3       9       19      99      999     999     999     999     198     199     LMAAAA  LAFAAA  VVVVxx
+6927   3392    1       3       7       7       27      927     927     1927    6927    54      55      LGAAAA  MAFAAA  AAAAxx
+4846   3393    0       2       6       6       46      846     846     4846    4846    92      93      KEAAAA  NAFAAA  HHHHxx
+676    3394    0       0       6       16      76      676     676     676     676     152     153     AAAAAA  OAFAAA  OOOOxx
+8612   3395    0       0       2       12      12      612     612     3612    8612    24      25      GTAAAA  PAFAAA  VVVVxx
+4111   3396    1       3       1       11      11      111     111     4111    4111    22      23      DCAAAA  QAFAAA  AAAAxx
+9994   3397    0       2       4       14      94      994     1994    4994    9994    188     189     KUAAAA  RAFAAA  HHHHxx
+4399   3398    1       3       9       19      99      399     399     4399    4399    198     199     FNAAAA  SAFAAA  OOOOxx
+4464   3399    0       0       4       4       64      464     464     4464    4464    128     129     SPAAAA  TAFAAA  VVVVxx
+7316   3400    0       0       6       16      16      316     1316    2316    7316    32      33      KVAAAA  UAFAAA  AAAAxx
+8982   3401    0       2       2       2       82      982     982     3982    8982    164     165     MHAAAA  VAFAAA  HHHHxx
+1871   3402    1       3       1       11      71      871     1871    1871    1871    142     143     ZTAAAA  WAFAAA  OOOOxx
+4082   3403    0       2       2       2       82      82      82      4082    4082    164     165     ABAAAA  XAFAAA  VVVVxx
+3949   3404    1       1       9       9       49      949     1949    3949    3949    98      99      XVAAAA  YAFAAA  AAAAxx
+9352   3405    0       0       2       12      52      352     1352    4352    9352    104     105     SVAAAA  ZAFAAA  HHHHxx
+9638   3406    0       2       8       18      38      638     1638    4638    9638    76      77      SGAAAA  ABFAAA  OOOOxx
+8177   3407    1       1       7       17      77      177     177     3177    8177    154     155     NCAAAA  BBFAAA  VVVVxx
+3499   3408    1       3       9       19      99      499     1499    3499    3499    198     199     PEAAAA  CBFAAA  AAAAxx
+4233   3409    1       1       3       13      33      233     233     4233    4233    66      67      VGAAAA  DBFAAA  HHHHxx
+1953   3410    1       1       3       13      53      953     1953    1953    1953    106     107     DXAAAA  EBFAAA  OOOOxx
+7372   3411    0       0       2       12      72      372     1372    2372    7372    144     145     OXAAAA  FBFAAA  VVVVxx
+5127   3412    1       3       7       7       27      127     1127    127     5127    54      55      FPAAAA  GBFAAA  AAAAxx
+4384   3413    0       0       4       4       84      384     384     4384    4384    168     169     QMAAAA  HBFAAA  HHHHxx
+9964   3414    0       0       4       4       64      964     1964    4964    9964    128     129     GTAAAA  IBFAAA  OOOOxx
+5392   3415    0       0       2       12      92      392     1392    392     5392    184     185     KZAAAA  JBFAAA  VVVVxx
+616    3416    0       0       6       16      16      616     616     616     616     32      33      SXAAAA  KBFAAA  AAAAxx
+591    3417    1       3       1       11      91      591     591     591     591     182     183     TWAAAA  LBFAAA  HHHHxx
+6422   3418    0       2       2       2       22      422     422     1422    6422    44      45      ANAAAA  MBFAAA  OOOOxx
+6551   3419    1       3       1       11      51      551     551     1551    6551    102     103     ZRAAAA  NBFAAA  VVVVxx
+9286   3420    0       2       6       6       86      286     1286    4286    9286    172     173     ETAAAA  OBFAAA  AAAAxx
+3817   3421    1       1       7       17      17      817     1817    3817    3817    34      35      VQAAAA  PBFAAA  HHHHxx
+7717   3422    1       1       7       17      17      717     1717    2717    7717    34      35      VKAAAA  QBFAAA  OOOOxx
+8718   3423    0       2       8       18      18      718     718     3718    8718    36      37      IXAAAA  RBFAAA  VVVVxx
+8608   3424    0       0       8       8       8       608     608     3608    8608    16      17      CTAAAA  SBFAAA  AAAAxx
+2242   3425    0       2       2       2       42      242     242     2242    2242    84      85      GIAAAA  TBFAAA  HHHHxx
+4811   3426    1       3       1       11      11      811     811     4811    4811    22      23      BDAAAA  UBFAAA  OOOOxx
+6838   3427    0       2       8       18      38      838     838     1838    6838    76      77      ADAAAA  VBFAAA  VVVVxx
+787    3428    1       3       7       7       87      787     787     787     787     174     175     HEAAAA  WBFAAA  AAAAxx
+7940   3429    0       0       0       0       40      940     1940    2940    7940    80      81      KTAAAA  XBFAAA  HHHHxx
+336    3430    0       0       6       16      36      336     336     336     336     72      73      YMAAAA  YBFAAA  OOOOxx
+9859   3431    1       3       9       19      59      859     1859    4859    9859    118     119     FPAAAA  ZBFAAA  VVVVxx
+3864   3432    0       0       4       4       64      864     1864    3864    3864    128     129     QSAAAA  ACFAAA  AAAAxx
+7162   3433    0       2       2       2       62      162     1162    2162    7162    124     125     MPAAAA  BCFAAA  HHHHxx
+2071   3434    1       3       1       11      71      71      71      2071    2071    142     143     RBAAAA  CCFAAA  OOOOxx
+7469   3435    1       1       9       9       69      469     1469    2469    7469    138     139     HBAAAA  DCFAAA  VVVVxx
+2917   3436    1       1       7       17      17      917     917     2917    2917    34      35      FIAAAA  ECFAAA  AAAAxx
+7486   3437    0       2       6       6       86      486     1486    2486    7486    172     173     YBAAAA  FCFAAA  HHHHxx
+3355   3438    1       3       5       15      55      355     1355    3355    3355    110     111     BZAAAA  GCFAAA  OOOOxx
+6998   3439    0       2       8       18      98      998     998     1998    6998    196     197     EJAAAA  HCFAAA  VVVVxx
+5498   3440    0       2       8       18      98      498     1498    498     5498    196     197     MDAAAA  ICFAAA  AAAAxx
+5113   3441    1       1       3       13      13      113     1113    113     5113    26      27      ROAAAA  JCFAAA  HHHHxx
+2846   3442    0       2       6       6       46      846     846     2846    2846    92      93      MFAAAA  KCFAAA  OOOOxx
+6834   3443    0       2       4       14      34      834     834     1834    6834    68      69      WCAAAA  LCFAAA  VVVVxx
+8925   3444    1       1       5       5       25      925     925     3925    8925    50      51      HFAAAA  MCFAAA  AAAAxx
+2757   3445    1       1       7       17      57      757     757     2757    2757    114     115     BCAAAA  NCFAAA  HHHHxx
+2775   3446    1       3       5       15      75      775     775     2775    2775    150     151     TCAAAA  OCFAAA  OOOOxx
+6182   3447    0       2       2       2       82      182     182     1182    6182    164     165     UDAAAA  PCFAAA  VVVVxx
+4488   3448    0       0       8       8       88      488     488     4488    4488    176     177     QQAAAA  QCFAAA  AAAAxx
+8523   3449    1       3       3       3       23      523     523     3523    8523    46      47      VPAAAA  RCFAAA  HHHHxx
+52     3450    0       0       2       12      52      52      52      52      52      104     105     ACAAAA  SCFAAA  OOOOxx
+7251   3451    1       3       1       11      51      251     1251    2251    7251    102     103     XSAAAA  TCFAAA  VVVVxx
+6130   3452    0       2       0       10      30      130     130     1130    6130    60      61      UBAAAA  UCFAAA  AAAAxx
+205    3453    1       1       5       5       5       205     205     205     205     10      11      XHAAAA  VCFAAA  HHHHxx
+1186   3454    0       2       6       6       86      186     1186    1186    1186    172     173     QTAAAA  WCFAAA  OOOOxx
+1738   3455    0       2       8       18      38      738     1738    1738    1738    76      77      WOAAAA  XCFAAA  VVVVxx
+9485   3456    1       1       5       5       85      485     1485    4485    9485    170     171     VAAAAA  YCFAAA  AAAAxx
+4235   3457    1       3       5       15      35      235     235     4235    4235    70      71      XGAAAA  ZCFAAA  HHHHxx
+7891   3458    1       3       1       11      91      891     1891    2891    7891    182     183     NRAAAA  ADFAAA  OOOOxx
+4960   3459    0       0       0       0       60      960     960     4960    4960    120     121     UIAAAA  BDFAAA  VVVVxx
+8911   3460    1       3       1       11      11      911     911     3911    8911    22      23      TEAAAA  CDFAAA  AAAAxx
+1219   3461    1       3       9       19      19      219     1219    1219    1219    38      39      XUAAAA  DDFAAA  HHHHxx
+9652   3462    0       0       2       12      52      652     1652    4652    9652    104     105     GHAAAA  EDFAAA  OOOOxx
+9715   3463    1       3       5       15      15      715     1715    4715    9715    30      31      RJAAAA  FDFAAA  VVVVxx
+6629   3464    1       1       9       9       29      629     629     1629    6629    58      59      ZUAAAA  GDFAAA  AAAAxx
+700    3465    0       0       0       0       0       700     700     700     700     0       1       YAAAAA  HDFAAA  HHHHxx
+9819   3466    1       3       9       19      19      819     1819    4819    9819    38      39      RNAAAA  IDFAAA  OOOOxx
+5188   3467    0       0       8       8       88      188     1188    188     5188    176     177     ORAAAA  JDFAAA  VVVVxx
+5367   3468    1       3       7       7       67      367     1367    367     5367    134     135     LYAAAA  KDFAAA  AAAAxx
+6447   3469    1       3       7       7       47      447     447     1447    6447    94      95      ZNAAAA  LDFAAA  HHHHxx
+720    3470    0       0       0       0       20      720     720     720     720     40      41      SBAAAA  MDFAAA  OOOOxx
+9157   3471    1       1       7       17      57      157     1157    4157    9157    114     115     FOAAAA  NDFAAA  VVVVxx
+1082   3472    0       2       2       2       82      82      1082    1082    1082    164     165     QPAAAA  ODFAAA  AAAAxx
+3179   3473    1       3       9       19      79      179     1179    3179    3179    158     159     HSAAAA  PDFAAA  HHHHxx
+4818   3474    0       2       8       18      18      818     818     4818    4818    36      37      IDAAAA  QDFAAA  OOOOxx
+7607   3475    1       3       7       7       7       607     1607    2607    7607    14      15      PGAAAA  RDFAAA  VVVVxx
+2352   3476    0       0       2       12      52      352     352     2352    2352    104     105     MMAAAA  SDFAAA  AAAAxx
+1170   3477    0       2       0       10      70      170     1170    1170    1170    140     141     ATAAAA  TDFAAA  HHHHxx
+4269   3478    1       1       9       9       69      269     269     4269    4269    138     139     FIAAAA  UDFAAA  OOOOxx
+8767   3479    1       3       7       7       67      767     767     3767    8767    134     135     FZAAAA  VDFAAA  VVVVxx
+3984   3480    0       0       4       4       84      984     1984    3984    3984    168     169     GXAAAA  WDFAAA  AAAAxx
+3190   3481    0       2       0       10      90      190     1190    3190    3190    180     181     SSAAAA  XDFAAA  HHHHxx
+7456   3482    0       0       6       16      56      456     1456    2456    7456    112     113     UAAAAA  YDFAAA  OOOOxx
+4348   3483    0       0       8       8       48      348     348     4348    4348    96      97      GLAAAA  ZDFAAA  VVVVxx
+3150   3484    0       2       0       10      50      150     1150    3150    3150    100     101     ERAAAA  AEFAAA  AAAAxx
+8780   3485    0       0       0       0       80      780     780     3780    8780    160     161     SZAAAA  BEFAAA  HHHHxx
+2553   3486    1       1       3       13      53      553     553     2553    2553    106     107     FUAAAA  CEFAAA  OOOOxx
+7526   3487    0       2       6       6       26      526     1526    2526    7526    52      53      MDAAAA  DEFAAA  VVVVxx
+2031   3488    1       3       1       11      31      31      31      2031    2031    62      63      DAAAAA  EEFAAA  AAAAxx
+8793   3489    1       1       3       13      93      793     793     3793    8793    186     187     FAAAAA  FEFAAA  HHHHxx
+1122   3490    0       2       2       2       22      122     1122    1122    1122    44      45      ERAAAA  GEFAAA  OOOOxx
+1855   3491    1       3       5       15      55      855     1855    1855    1855    110     111     JTAAAA  HEFAAA  VVVVxx
+6613   3492    1       1       3       13      13      613     613     1613    6613    26      27      JUAAAA  IEFAAA  AAAAxx
+3231   3493    1       3       1       11      31      231     1231    3231    3231    62      63      HUAAAA  JEFAAA  HHHHxx
+9101   3494    1       1       1       1       1       101     1101    4101    9101    2       3       BMAAAA  KEFAAA  OOOOxx
+4937   3495    1       1       7       17      37      937     937     4937    4937    74      75      XHAAAA  LEFAAA  VVVVxx
+666    3496    0       2       6       6       66      666     666     666     666     132     133     QZAAAA  MEFAAA  AAAAxx
+8943   3497    1       3       3       3       43      943     943     3943    8943    86      87      ZFAAAA  NEFAAA  HHHHxx
+6164   3498    0       0       4       4       64      164     164     1164    6164    128     129     CDAAAA  OEFAAA  OOOOxx
+1081   3499    1       1       1       1       81      81      1081    1081    1081    162     163     PPAAAA  PEFAAA  VVVVxx
+210    3500    0       2       0       10      10      210     210     210     210     20      21      CIAAAA  QEFAAA  AAAAxx
+6024   3501    0       0       4       4       24      24      24      1024    6024    48      49      SXAAAA  REFAAA  HHHHxx
+5715   3502    1       3       5       15      15      715     1715    715     5715    30      31      VLAAAA  SEFAAA  OOOOxx
+8938   3503    0       2       8       18      38      938     938     3938    8938    76      77      UFAAAA  TEFAAA  VVVVxx
+1326   3504    0       2       6       6       26      326     1326    1326    1326    52      53      AZAAAA  UEFAAA  AAAAxx
+7111   3505    1       3       1       11      11      111     1111    2111    7111    22      23      NNAAAA  VEFAAA  HHHHxx
+757    3506    1       1       7       17      57      757     757     757     757     114     115     DDAAAA  WEFAAA  OOOOxx
+8933   3507    1       1       3       13      33      933     933     3933    8933    66      67      PFAAAA  XEFAAA  VVVVxx
+6495   3508    1       3       5       15      95      495     495     1495    6495    190     191     VPAAAA  YEFAAA  AAAAxx
+3134   3509    0       2       4       14      34      134     1134    3134    3134    68      69      OQAAAA  ZEFAAA  HHHHxx
+1304   3510    0       0       4       4       4       304     1304    1304    1304    8       9       EYAAAA  AFFAAA  OOOOxx
+1835   3511    1       3       5       15      35      835     1835    1835    1835    70      71      PSAAAA  BFFAAA  VVVVxx
+7275   3512    1       3       5       15      75      275     1275    2275    7275    150     151     VTAAAA  CFFAAA  AAAAxx
+7337   3513    1       1       7       17      37      337     1337    2337    7337    74      75      FWAAAA  DFFAAA  HHHHxx
+1282   3514    0       2       2       2       82      282     1282    1282    1282    164     165     IXAAAA  EFFAAA  OOOOxx
+6566   3515    0       2       6       6       66      566     566     1566    6566    132     133     OSAAAA  FFFAAA  VVVVxx
+3786   3516    0       2       6       6       86      786     1786    3786    3786    172     173     QPAAAA  GFFAAA  AAAAxx
+5741   3517    1       1       1       1       41      741     1741    741     5741    82      83      VMAAAA  HFFAAA  HHHHxx
+6076   3518    0       0       6       16      76      76      76      1076    6076    152     153     SZAAAA  IFFAAA  OOOOxx
+9998   3519    0       2       8       18      98      998     1998    4998    9998    196     197     OUAAAA  JFFAAA  VVVVxx
+6268   3520    0       0       8       8       68      268     268     1268    6268    136     137     CHAAAA  KFFAAA  AAAAxx
+9647   3521    1       3       7       7       47      647     1647    4647    9647    94      95      BHAAAA  LFFAAA  HHHHxx
+4877   3522    1       1       7       17      77      877     877     4877    4877    154     155     PFAAAA  MFFAAA  OOOOxx
+2652   3523    0       0       2       12      52      652     652     2652    2652    104     105     AYAAAA  NFFAAA  VVVVxx
+1247   3524    1       3       7       7       47      247     1247    1247    1247    94      95      ZVAAAA  OFFAAA  AAAAxx
+2721   3525    1       1       1       1       21      721     721     2721    2721    42      43      RAAAAA  PFFAAA  HHHHxx
+5968   3526    0       0       8       8       68      968     1968    968     5968    136     137     OVAAAA  QFFAAA  OOOOxx
+9570   3527    0       2       0       10      70      570     1570    4570    9570    140     141     CEAAAA  RFFAAA  VVVVxx
+6425   3528    1       1       5       5       25      425     425     1425    6425    50      51      DNAAAA  SFFAAA  AAAAxx
+5451   3529    1       3       1       11      51      451     1451    451     5451    102     103     RBAAAA  TFFAAA  HHHHxx
+5668   3530    0       0       8       8       68      668     1668    668     5668    136     137     AKAAAA  UFFAAA  OOOOxx
+9493   3531    1       1       3       13      93      493     1493    4493    9493    186     187     DBAAAA  VFFAAA  VVVVxx
+7973   3532    1       1       3       13      73      973     1973    2973    7973    146     147     RUAAAA  WFFAAA  AAAAxx
+8250   3533    0       2       0       10      50      250     250     3250    8250    100     101     IFAAAA  XFFAAA  HHHHxx
+82     3534    0       2       2       2       82      82      82      82      82      164     165     EDAAAA  YFFAAA  OOOOxx
+6258   3535    0       2       8       18      58      258     258     1258    6258    116     117     SGAAAA  ZFFAAA  VVVVxx
+9978   3536    0       2       8       18      78      978     1978    4978    9978    156     157     UTAAAA  AGFAAA  AAAAxx
+6930   3537    0       2       0       10      30      930     930     1930    6930    60      61      OGAAAA  BGFAAA  HHHHxx
+3746   3538    0       2       6       6       46      746     1746    3746    3746    92      93      COAAAA  CGFAAA  OOOOxx
+7065   3539    1       1       5       5       65      65      1065    2065    7065    130     131     TLAAAA  DGFAAA  VVVVxx
+4281   3540    1       1       1       1       81      281     281     4281    4281    162     163     RIAAAA  EGFAAA  AAAAxx
+4367   3541    1       3       7       7       67      367     367     4367    4367    134     135     ZLAAAA  FGFAAA  HHHHxx
+9526   3542    0       2       6       6       26      526     1526    4526    9526    52      53      KCAAAA  GGFAAA  OOOOxx
+5880   3543    0       0       0       0       80      880     1880    880     5880    160     161     ESAAAA  HGFAAA  VVVVxx
+8480   3544    0       0       0       0       80      480     480     3480    8480    160     161     EOAAAA  IGFAAA  AAAAxx
+2476   3545    0       0       6       16      76      476     476     2476    2476    152     153     GRAAAA  JGFAAA  HHHHxx
+9074   3546    0       2       4       14      74      74      1074    4074    9074    148     149     ALAAAA  KGFAAA  OOOOxx
+4830   3547    0       2       0       10      30      830     830     4830    4830    60      61      UDAAAA  LGFAAA  VVVVxx
+3207   3548    1       3       7       7       7       207     1207    3207    3207    14      15      JTAAAA  MGFAAA  AAAAxx
+7894   3549    0       2       4       14      94      894     1894    2894    7894    188     189     QRAAAA  NGFAAA  HHHHxx
+3860   3550    0       0       0       0       60      860     1860    3860    3860    120     121     MSAAAA  OGFAAA  OOOOxx
+5293   3551    1       1       3       13      93      293     1293    293     5293    186     187     PVAAAA  PGFAAA  VVVVxx
+6895   3552    1       3       5       15      95      895     895     1895    6895    190     191     FFAAAA  QGFAAA  AAAAxx
+9908   3553    0       0       8       8       8       908     1908    4908    9908    16      17      CRAAAA  RGFAAA  HHHHxx
+9247   3554    1       3       7       7       47      247     1247    4247    9247    94      95      RRAAAA  SGFAAA  OOOOxx
+8110   3555    0       2       0       10      10      110     110     3110    8110    20      21      YZAAAA  TGFAAA  VVVVxx
+4716   3556    0       0       6       16      16      716     716     4716    4716    32      33      KZAAAA  UGFAAA  AAAAxx
+4979   3557    1       3       9       19      79      979     979     4979    4979    158     159     NJAAAA  VGFAAA  HHHHxx
+5280   3558    0       0       0       0       80      280     1280    280     5280    160     161     CVAAAA  WGFAAA  OOOOxx
+8326   3559    0       2       6       6       26      326     326     3326    8326    52      53      GIAAAA  XGFAAA  VVVVxx
+5572   3560    0       0       2       12      72      572     1572    572     5572    144     145     IGAAAA  YGFAAA  AAAAxx
+4665   3561    1       1       5       5       65      665     665     4665    4665    130     131     LXAAAA  ZGFAAA  HHHHxx
+3665   3562    1       1       5       5       65      665     1665    3665    3665    130     131     ZKAAAA  AHFAAA  OOOOxx
+6744   3563    0       0       4       4       44      744     744     1744    6744    88      89      KZAAAA  BHFAAA  VVVVxx
+1897   3564    1       1       7       17      97      897     1897    1897    1897    194     195     ZUAAAA  CHFAAA  AAAAxx
+1220   3565    0       0       0       0       20      220     1220    1220    1220    40      41      YUAAAA  DHFAAA  HHHHxx
+2614   3566    0       2       4       14      14      614     614     2614    2614    28      29      OWAAAA  EHFAAA  OOOOxx
+8509   3567    1       1       9       9       9       509     509     3509    8509    18      19      HPAAAA  FHFAAA  VVVVxx
+8521   3568    1       1       1       1       21      521     521     3521    8521    42      43      TPAAAA  GHFAAA  AAAAxx
+4121   3569    1       1       1       1       21      121     121     4121    4121    42      43      NCAAAA  HHFAAA  HHHHxx
+9663   3570    1       3       3       3       63      663     1663    4663    9663    126     127     RHAAAA  IHFAAA  OOOOxx
+2346   3571    0       2       6       6       46      346     346     2346    2346    92      93      GMAAAA  JHFAAA  VVVVxx
+3370   3572    0       2       0       10      70      370     1370    3370    3370    140     141     QZAAAA  KHFAAA  AAAAxx
+1498   3573    0       2       8       18      98      498     1498    1498    1498    196     197     QFAAAA  LHFAAA  HHHHxx
+7422   3574    0       2       2       2       22      422     1422    2422    7422    44      45      MZAAAA  MHFAAA  OOOOxx
+3472   3575    0       0       2       12      72      472     1472    3472    3472    144     145     ODAAAA  NHFAAA  VVVVxx
+4126   3576    0       2       6       6       26      126     126     4126    4126    52      53      SCAAAA  OHFAAA  AAAAxx
+4494   3577    0       2       4       14      94      494     494     4494    4494    188     189     WQAAAA  PHFAAA  HHHHxx
+6323   3578    1       3       3       3       23      323     323     1323    6323    46      47      FJAAAA  QHFAAA  OOOOxx
+2823   3579    1       3       3       3       23      823     823     2823    2823    46      47      PEAAAA  RHFAAA  VVVVxx
+8596   3580    0       0       6       16      96      596     596     3596    8596    192     193     QSAAAA  SHFAAA  AAAAxx
+6642   3581    0       2       2       2       42      642     642     1642    6642    84      85      MVAAAA  THFAAA  HHHHxx
+9276   3582    0       0       6       16      76      276     1276    4276    9276    152     153     USAAAA  UHFAAA  OOOOxx
+4148   3583    0       0       8       8       48      148     148     4148    4148    96      97      ODAAAA  VHFAAA  VVVVxx
+9770   3584    0       2       0       10      70      770     1770    4770    9770    140     141     ULAAAA  WHFAAA  AAAAxx
+9812   3585    0       0       2       12      12      812     1812    4812    9812    24      25      KNAAAA  XHFAAA  HHHHxx
+4419   3586    1       3       9       19      19      419     419     4419    4419    38      39      ZNAAAA  YHFAAA  OOOOxx
+3802   3587    0       2       2       2       2       802     1802    3802    3802    4       5       GQAAAA  ZHFAAA  VVVVxx
+3210   3588    0       2       0       10      10      210     1210    3210    3210    20      21      MTAAAA  AIFAAA  AAAAxx
+6794   3589    0       2       4       14      94      794     794     1794    6794    188     189     IBAAAA  BIFAAA  HHHHxx
+242    3590    0       2       2       2       42      242     242     242     242     84      85      IJAAAA  CIFAAA  OOOOxx
+962    3591    0       2       2       2       62      962     962     962     962     124     125     ALAAAA  DIFAAA  VVVVxx
+7151   3592    1       3       1       11      51      151     1151    2151    7151    102     103     BPAAAA  EIFAAA  AAAAxx
+9440   3593    0       0       0       0       40      440     1440    4440    9440    80      81      CZAAAA  FIFAAA  HHHHxx
+721    3594    1       1       1       1       21      721     721     721     721     42      43      TBAAAA  GIFAAA  OOOOxx
+2119   3595    1       3       9       19      19      119     119     2119    2119    38      39      NDAAAA  HIFAAA  VVVVxx
+9883   3596    1       3       3       3       83      883     1883    4883    9883    166     167     DQAAAA  IIFAAA  AAAAxx
+5071   3597    1       3       1       11      71      71      1071    71      5071    142     143     BNAAAA  JIFAAA  HHHHxx
+8239   3598    1       3       9       19      39      239     239     3239    8239    78      79      XEAAAA  KIFAAA  OOOOxx
+7451   3599    1       3       1       11      51      451     1451    2451    7451    102     103     PAAAAA  LIFAAA  VVVVxx
+9517   3600    1       1       7       17      17      517     1517    4517    9517    34      35      BCAAAA  MIFAAA  AAAAxx
+9180   3601    0       0       0       0       80      180     1180    4180    9180    160     161     CPAAAA  NIFAAA  HHHHxx
+9327   3602    1       3       7       7       27      327     1327    4327    9327    54      55      TUAAAA  OIFAAA  OOOOxx
+5462   3603    0       2       2       2       62      462     1462    462     5462    124     125     CCAAAA  PIFAAA  VVVVxx
+8306   3604    0       2       6       6       6       306     306     3306    8306    12      13      MHAAAA  QIFAAA  AAAAxx
+6234   3605    0       2       4       14      34      234     234     1234    6234    68      69      UFAAAA  RIFAAA  HHHHxx
+8771   3606    1       3       1       11      71      771     771     3771    8771    142     143     JZAAAA  SIFAAA  OOOOxx
+5853   3607    1       1       3       13      53      853     1853    853     5853    106     107     DRAAAA  TIFAAA  VVVVxx
+8373   3608    1       1       3       13      73      373     373     3373    8373    146     147     BKAAAA  UIFAAA  AAAAxx
+5017   3609    1       1       7       17      17      17      1017    17      5017    34      35      ZKAAAA  VIFAAA  HHHHxx
+8025   3610    1       1       5       5       25      25      25      3025    8025    50      51      RWAAAA  WIFAAA  OOOOxx
+2526   3611    0       2       6       6       26      526     526     2526    2526    52      53      ETAAAA  XIFAAA  VVVVxx
+7419   3612    1       3       9       19      19      419     1419    2419    7419    38      39      JZAAAA  YIFAAA  AAAAxx
+4572   3613    0       0       2       12      72      572     572     4572    4572    144     145     WTAAAA  ZIFAAA  HHHHxx
+7744   3614    0       0       4       4       44      744     1744    2744    7744    88      89      WLAAAA  AJFAAA  OOOOxx
+8825   3615    1       1       5       5       25      825     825     3825    8825    50      51      LBAAAA  BJFAAA  VVVVxx
+6067   3616    1       3       7       7       67      67      67      1067    6067    134     135     JZAAAA  CJFAAA  AAAAxx
+3291   3617    1       3       1       11      91      291     1291    3291    3291    182     183     PWAAAA  DJFAAA  HHHHxx
+7115   3618    1       3       5       15      15      115     1115    2115    7115    30      31      RNAAAA  EJFAAA  OOOOxx
+2626   3619    0       2       6       6       26      626     626     2626    2626    52      53      AXAAAA  FJFAAA  VVVVxx
+4109   3620    1       1       9       9       9       109     109     4109    4109    18      19      BCAAAA  GJFAAA  AAAAxx
+4056   3621    0       0       6       16      56      56      56      4056    4056    112     113     AAAAAA  HJFAAA  HHHHxx
+6811   3622    1       3       1       11      11      811     811     1811    6811    22      23      ZBAAAA  IJFAAA  OOOOxx
+680    3623    0       0       0       0       80      680     680     680     680     160     161     EAAAAA  JJFAAA  VVVVxx
+474    3624    0       2       4       14      74      474     474     474     474     148     149     GSAAAA  KJFAAA  AAAAxx
+9294   3625    0       2       4       14      94      294     1294    4294    9294    188     189     MTAAAA  LJFAAA  HHHHxx
+7555   3626    1       3       5       15      55      555     1555    2555    7555    110     111     PEAAAA  MJFAAA  OOOOxx
+8076   3627    0       0       6       16      76      76      76      3076    8076    152     153     QYAAAA  NJFAAA  VVVVxx
+3840   3628    0       0       0       0       40      840     1840    3840    3840    80      81      SRAAAA  OJFAAA  AAAAxx
+5955   3629    1       3       5       15      55      955     1955    955     5955    110     111     BVAAAA  PJFAAA  HHHHxx
+994    3630    0       2       4       14      94      994     994     994     994     188     189     GMAAAA  QJFAAA  OOOOxx
+2089   3631    1       1       9       9       89      89      89      2089    2089    178     179     JCAAAA  RJFAAA  VVVVxx
+869    3632    1       1       9       9       69      869     869     869     869     138     139     LHAAAA  SJFAAA  AAAAxx
+1223   3633    1       3       3       3       23      223     1223    1223    1223    46      47      BVAAAA  TJFAAA  HHHHxx
+1514   3634    0       2       4       14      14      514     1514    1514    1514    28      29      GGAAAA  UJFAAA  OOOOxx
+4891   3635    1       3       1       11      91      891     891     4891    4891    182     183     DGAAAA  VJFAAA  VVVVxx
+4190   3636    0       2       0       10      90      190     190     4190    4190    180     181     EFAAAA  WJFAAA  AAAAxx
+4377   3637    1       1       7       17      77      377     377     4377    4377    154     155     JMAAAA  XJFAAA  HHHHxx
+9195   3638    1       3       5       15      95      195     1195    4195    9195    190     191     RPAAAA  YJFAAA  OOOOxx
+3827   3639    1       3       7       7       27      827     1827    3827    3827    54      55      FRAAAA  ZJFAAA  VVVVxx
+7386   3640    0       2       6       6       86      386     1386    2386    7386    172     173     CYAAAA  AKFAAA  AAAAxx
+6665   3641    1       1       5       5       65      665     665     1665    6665    130     131     JWAAAA  BKFAAA  HHHHxx
+7514   3642    0       2       4       14      14      514     1514    2514    7514    28      29      ADAAAA  CKFAAA  OOOOxx
+6431   3643    1       3       1       11      31      431     431     1431    6431    62      63      JNAAAA  DKFAAA  VVVVxx
+3251   3644    1       3       1       11      51      251     1251    3251    3251    102     103     BVAAAA  EKFAAA  AAAAxx
+8439   3645    1       3       9       19      39      439     439     3439    8439    78      79      PMAAAA  FKFAAA  HHHHxx
+831    3646    1       3       1       11      31      831     831     831     831     62      63      ZFAAAA  GKFAAA  OOOOxx
+8485   3647    1       1       5       5       85      485     485     3485    8485    170     171     JOAAAA  HKFAAA  VVVVxx
+7314   3648    0       2       4       14      14      314     1314    2314    7314    28      29      IVAAAA  IKFAAA  AAAAxx
+3044   3649    0       0       4       4       44      44      1044    3044    3044    88      89      CNAAAA  JKFAAA  HHHHxx
+4283   3650    1       3       3       3       83      283     283     4283    4283    166     167     TIAAAA  KKFAAA  OOOOxx
+298    3651    0       2       8       18      98      298     298     298     298     196     197     MLAAAA  LKFAAA  VVVVxx
+7114   3652    0       2       4       14      14      114     1114    2114    7114    28      29      QNAAAA  MKFAAA  AAAAxx
+9664   3653    0       0       4       4       64      664     1664    4664    9664    128     129     SHAAAA  NKFAAA  HHHHxx
+5315   3654    1       3       5       15      15      315     1315    315     5315    30      31      LWAAAA  OKFAAA  OOOOxx
+2164   3655    0       0       4       4       64      164     164     2164    2164    128     129     GFAAAA  PKFAAA  VVVVxx
+3390   3656    0       2       0       10      90      390     1390    3390    3390    180     181     KAAAAA  QKFAAA  AAAAxx
+836    3657    0       0       6       16      36      836     836     836     836     72      73      EGAAAA  RKFAAA  HHHHxx
+3316   3658    0       0       6       16      16      316     1316    3316    3316    32      33      OXAAAA  SKFAAA  OOOOxx
+1284   3659    0       0       4       4       84      284     1284    1284    1284    168     169     KXAAAA  TKFAAA  VVVVxx
+2497   3660    1       1       7       17      97      497     497     2497    2497    194     195     BSAAAA  UKFAAA  AAAAxx
+1374   3661    0       2       4       14      74      374     1374    1374    1374    148     149     WAAAAA  VKFAAA  HHHHxx
+9525   3662    1       1       5       5       25      525     1525    4525    9525    50      51      JCAAAA  WKFAAA  OOOOxx
+2911   3663    1       3       1       11      11      911     911     2911    2911    22      23      ZHAAAA  XKFAAA  VVVVxx
+9686   3664    0       2       6       6       86      686     1686    4686    9686    172     173     OIAAAA  YKFAAA  AAAAxx
+584    3665    0       0       4       4       84      584     584     584     584     168     169     MWAAAA  ZKFAAA  HHHHxx
+5653   3666    1       1       3       13      53      653     1653    653     5653    106     107     LJAAAA  ALFAAA  OOOOxx
+4986   3667    0       2       6       6       86      986     986     4986    4986    172     173     UJAAAA  BLFAAA  VVVVxx
+6049   3668    1       1       9       9       49      49      49      1049    6049    98      99      RYAAAA  CLFAAA  AAAAxx
+9891   3669    1       3       1       11      91      891     1891    4891    9891    182     183     LQAAAA  DLFAAA  HHHHxx
+8809   3670    1       1       9       9       9       809     809     3809    8809    18      19      VAAAAA  ELFAAA  OOOOxx
+8598   3671    0       2       8       18      98      598     598     3598    8598    196     197     SSAAAA  FLFAAA  VVVVxx
+2573   3672    1       1       3       13      73      573     573     2573    2573    146     147     ZUAAAA  GLFAAA  AAAAxx
+6864   3673    0       0       4       4       64      864     864     1864    6864    128     129     AEAAAA  HLFAAA  HHHHxx
+7932   3674    0       0       2       12      32      932     1932    2932    7932    64      65      CTAAAA  ILFAAA  OOOOxx
+6605   3675    1       1       5       5       5       605     605     1605    6605    10      11      BUAAAA  JLFAAA  VVVVxx
+9500   3676    0       0       0       0       0       500     1500    4500    9500    0       1       KBAAAA  KLFAAA  AAAAxx
+8742   3677    0       2       2       2       42      742     742     3742    8742    84      85      GYAAAA  LLFAAA  HHHHxx
+9815   3678    1       3       5       15      15      815     1815    4815    9815    30      31      NNAAAA  MLFAAA  OOOOxx
+3319   3679    1       3       9       19      19      319     1319    3319    3319    38      39      RXAAAA  NLFAAA  VVVVxx
+184    3680    0       0       4       4       84      184     184     184     184     168     169     CHAAAA  OLFAAA  AAAAxx
+8886   3681    0       2       6       6       86      886     886     3886    8886    172     173     UDAAAA  PLFAAA  HHHHxx
+7050   3682    0       2       0       10      50      50      1050    2050    7050    100     101     ELAAAA  QLFAAA  OOOOxx
+9781   3683    1       1       1       1       81      781     1781    4781    9781    162     163     FMAAAA  RLFAAA  VVVVxx
+2443   3684    1       3       3       3       43      443     443     2443    2443    86      87      ZPAAAA  SLFAAA  AAAAxx
+1160   3685    0       0       0       0       60      160     1160    1160    1160    120     121     QSAAAA  TLFAAA  HHHHxx
+4600   3686    0       0       0       0       0       600     600     4600    4600    0       1       YUAAAA  ULFAAA  OOOOxx
+813    3687    1       1       3       13      13      813     813     813     813     26      27      HFAAAA  VLFAAA  VVVVxx
+5078   3688    0       2       8       18      78      78      1078    78      5078    156     157     INAAAA  WLFAAA  AAAAxx
+9008   3689    0       0       8       8       8       8       1008    4008    9008    16      17      MIAAAA  XLFAAA  HHHHxx
+9016   3690    0       0       6       16      16      16      1016    4016    9016    32      33      UIAAAA  YLFAAA  OOOOxx
+2747   3691    1       3       7       7       47      747     747     2747    2747    94      95      RBAAAA  ZLFAAA  VVVVxx
+3106   3692    0       2       6       6       6       106     1106    3106    3106    12      13      MPAAAA  AMFAAA  AAAAxx
+8235   3693    1       3       5       15      35      235     235     3235    8235    70      71      TEAAAA  BMFAAA  HHHHxx
+5582   3694    0       2       2       2       82      582     1582    582     5582    164     165     SGAAAA  CMFAAA  OOOOxx
+4334   3695    0       2       4       14      34      334     334     4334    4334    68      69      SKAAAA  DMFAAA  VVVVxx
+1612   3696    0       0       2       12      12      612     1612    1612    1612    24      25      AKAAAA  EMFAAA  AAAAxx
+5650   3697    0       2       0       10      50      650     1650    650     5650    100     101     IJAAAA  FMFAAA  HHHHxx
+6086   3698    0       2       6       6       86      86      86      1086    6086    172     173     CAAAAA  GMFAAA  OOOOxx
+9667   3699    1       3       7       7       67      667     1667    4667    9667    134     135     VHAAAA  HMFAAA  VVVVxx
+4215   3700    1       3       5       15      15      215     215     4215    4215    30      31      DGAAAA  IMFAAA  AAAAxx
+8553   3701    1       1       3       13      53      553     553     3553    8553    106     107     ZQAAAA  JMFAAA  HHHHxx
+9066   3702    0       2       6       6       66      66      1066    4066    9066    132     133     SKAAAA  KMFAAA  OOOOxx
+1092   3703    0       0       2       12      92      92      1092    1092    1092    184     185     AQAAAA  LMFAAA  VVVVxx
+2848   3704    0       0       8       8       48      848     848     2848    2848    96      97      OFAAAA  MMFAAA  AAAAxx
+2765   3705    1       1       5       5       65      765     765     2765    2765    130     131     JCAAAA  NMFAAA  HHHHxx
+6513   3706    1       1       3       13      13      513     513     1513    6513    26      27      NQAAAA  OMFAAA  OOOOxx
+6541   3707    1       1       1       1       41      541     541     1541    6541    82      83      PRAAAA  PMFAAA  VVVVxx
+9617   3708    1       1       7       17      17      617     1617    4617    9617    34      35      XFAAAA  QMFAAA  AAAAxx
+5870   3709    0       2       0       10      70      870     1870    870     5870    140     141     URAAAA  RMFAAA  HHHHxx
+8811   3710    1       3       1       11      11      811     811     3811    8811    22      23      XAAAAA  SMFAAA  OOOOxx
+4529   3711    1       1       9       9       29      529     529     4529    4529    58      59      FSAAAA  TMFAAA  VVVVxx
+161    3712    1       1       1       1       61      161     161     161     161     122     123     FGAAAA  UMFAAA  AAAAxx
+641    3713    1       1       1       1       41      641     641     641     641     82      83      RYAAAA  VMFAAA  HHHHxx
+4767   3714    1       3       7       7       67      767     767     4767    4767    134     135     JBAAAA  WMFAAA  OOOOxx
+6293   3715    1       1       3       13      93      293     293     1293    6293    186     187     BIAAAA  XMFAAA  VVVVxx
+3816   3716    0       0       6       16      16      816     1816    3816    3816    32      33      UQAAAA  YMFAAA  AAAAxx
+4748   3717    0       0       8       8       48      748     748     4748    4748    96      97      QAAAAA  ZMFAAA  HHHHxx
+9924   3718    0       0       4       4       24      924     1924    4924    9924    48      49      SRAAAA  ANFAAA  OOOOxx
+6716   3719    0       0       6       16      16      716     716     1716    6716    32      33      IYAAAA  BNFAAA  VVVVxx
+8828   3720    0       0       8       8       28      828     828     3828    8828    56      57      OBAAAA  CNFAAA  AAAAxx
+4967   3721    1       3       7       7       67      967     967     4967    4967    134     135     BJAAAA  DNFAAA  HHHHxx
+9680   3722    0       0       0       0       80      680     1680    4680    9680    160     161     IIAAAA  ENFAAA  OOOOxx
+2784   3723    0       0       4       4       84      784     784     2784    2784    168     169     CDAAAA  FNFAAA  VVVVxx
+2882   3724    0       2       2       2       82      882     882     2882    2882    164     165     WGAAAA  GNFAAA  AAAAxx
+3641   3725    1       1       1       1       41      641     1641    3641    3641    82      83      BKAAAA  HNFAAA  HHHHxx
+5537   3726    1       1       7       17      37      537     1537    537     5537    74      75      ZEAAAA  INFAAA  OOOOxx
+820    3727    0       0       0       0       20      820     820     820     820     40      41      OFAAAA  JNFAAA  VVVVxx
+5847   3728    1       3       7       7       47      847     1847    847     5847    94      95      XQAAAA  KNFAAA  AAAAxx
+566    3729    0       2       6       6       66      566     566     566     566     132     133     UVAAAA  LNFAAA  HHHHxx
+2246   3730    0       2       6       6       46      246     246     2246    2246    92      93      KIAAAA  MNFAAA  OOOOxx
+6680   3731    0       0       0       0       80      680     680     1680    6680    160     161     YWAAAA  NNFAAA  VVVVxx
+2014   3732    0       2       4       14      14      14      14      2014    2014    28      29      MZAAAA  ONFAAA  AAAAxx
+8355   3733    1       3       5       15      55      355     355     3355    8355    110     111     JJAAAA  PNFAAA  HHHHxx
+1610   3734    0       2       0       10      10      610     1610    1610    1610    20      21      YJAAAA  QNFAAA  OOOOxx
+9719   3735    1       3       9       19      19      719     1719    4719    9719    38      39      VJAAAA  RNFAAA  VVVVxx
+8498   3736    0       2       8       18      98      498     498     3498    8498    196     197     WOAAAA  SNFAAA  AAAAxx
+5883   3737    1       3       3       3       83      883     1883    883     5883    166     167     HSAAAA  TNFAAA  HHHHxx
+7380   3738    0       0       0       0       80      380     1380    2380    7380    160     161     WXAAAA  UNFAAA  OOOOxx
+8865   3739    1       1       5       5       65      865     865     3865    8865    130     131     ZCAAAA  VNFAAA  VVVVxx
+4743   3740    1       3       3       3       43      743     743     4743    4743    86      87      LAAAAA  WNFAAA  AAAAxx
+5086   3741    0       2       6       6       86      86      1086    86      5086    172     173     QNAAAA  XNFAAA  HHHHxx
+2739   3742    1       3       9       19      39      739     739     2739    2739    78      79      JBAAAA  YNFAAA  OOOOxx
+9375   3743    1       3       5       15      75      375     1375    4375    9375    150     151     PWAAAA  ZNFAAA  VVVVxx
+7876   3744    0       0       6       16      76      876     1876    2876    7876    152     153     YQAAAA  AOFAAA  AAAAxx
+453    3745    1       1       3       13      53      453     453     453     453     106     107     LRAAAA  BOFAAA  HHHHxx
+6987   3746    1       3       7       7       87      987     987     1987    6987    174     175     TIAAAA  COFAAA  OOOOxx
+2860   3747    0       0       0       0       60      860     860     2860    2860    120     121     AGAAAA  DOFAAA  VVVVxx
+8372   3748    0       0       2       12      72      372     372     3372    8372    144     145     AKAAAA  EOFAAA  AAAAxx
+2048   3749    0       0       8       8       48      48      48      2048    2048    96      97      UAAAAA  FOFAAA  HHHHxx
+9231   3750    1       3       1       11      31      231     1231    4231    9231    62      63      BRAAAA  GOFAAA  OOOOxx
+634    3751    0       2       4       14      34      634     634     634     634     68      69      KYAAAA  HOFAAA  VVVVxx
+3998   3752    0       2       8       18      98      998     1998    3998    3998    196     197     UXAAAA  IOFAAA  AAAAxx
+4728   3753    0       0       8       8       28      728     728     4728    4728    56      57      WZAAAA  JOFAAA  HHHHxx
+579    3754    1       3       9       19      79      579     579     579     579     158     159     HWAAAA  KOFAAA  OOOOxx
+815    3755    1       3       5       15      15      815     815     815     815     30      31      JFAAAA  LOFAAA  VVVVxx
+1009   3756    1       1       9       9       9       9       1009    1009    1009    18      19      VMAAAA  MOFAAA  AAAAxx
+6596   3757    0       0       6       16      96      596     596     1596    6596    192     193     STAAAA  NOFAAA  HHHHxx
+2793   3758    1       1       3       13      93      793     793     2793    2793    186     187     LDAAAA  OOFAAA  OOOOxx
+9589   3759    1       1       9       9       89      589     1589    4589    9589    178     179     VEAAAA  POFAAA  VVVVxx
+2794   3760    0       2       4       14      94      794     794     2794    2794    188     189     MDAAAA  QOFAAA  AAAAxx
+2551   3761    1       3       1       11      51      551     551     2551    2551    102     103     DUAAAA  ROFAAA  HHHHxx
+1588   3762    0       0       8       8       88      588     1588    1588    1588    176     177     CJAAAA  SOFAAA  OOOOxx
+4443   3763    1       3       3       3       43      443     443     4443    4443    86      87      XOAAAA  TOFAAA  VVVVxx
+5009   3764    1       1       9       9       9       9       1009    9       5009    18      19      RKAAAA  UOFAAA  AAAAxx
+4287   3765    1       3       7       7       87      287     287     4287    4287    174     175     XIAAAA  VOFAAA  HHHHxx
+2167   3766    1       3       7       7       67      167     167     2167    2167    134     135     JFAAAA  WOFAAA  OOOOxx
+2290   3767    0       2       0       10      90      290     290     2290    2290    180     181     CKAAAA  XOFAAA  VVVVxx
+7225   3768    1       1       5       5       25      225     1225    2225    7225    50      51      XRAAAA  YOFAAA  AAAAxx
+8992   3769    0       0       2       12      92      992     992     3992    8992    184     185     WHAAAA  ZOFAAA  HHHHxx
+1540   3770    0       0       0       0       40      540     1540    1540    1540    80      81      GHAAAA  APFAAA  OOOOxx
+2029   3771    1       1       9       9       29      29      29      2029    2029    58      59      BAAAAA  BPFAAA  VVVVxx
+2855   3772    1       3       5       15      55      855     855     2855    2855    110     111     VFAAAA  CPFAAA  AAAAxx
+3534   3773    0       2       4       14      34      534     1534    3534    3534    68      69      YFAAAA  DPFAAA  HHHHxx
+8078   3774    0       2       8       18      78      78      78      3078    8078    156     157     SYAAAA  EPFAAA  OOOOxx
+9778   3775    0       2       8       18      78      778     1778    4778    9778    156     157     CMAAAA  FPFAAA  VVVVxx
+3543   3776    1       3       3       3       43      543     1543    3543    3543    86      87      HGAAAA  GPFAAA  AAAAxx
+4778   3777    0       2       8       18      78      778     778     4778    4778    156     157     UBAAAA  HPFAAA  HHHHxx
+8931   3778    1       3       1       11      31      931     931     3931    8931    62      63      NFAAAA  IPFAAA  OOOOxx
+557    3779    1       1       7       17      57      557     557     557     557     114     115     LVAAAA  JPFAAA  VVVVxx
+5546   3780    0       2       6       6       46      546     1546    546     5546    92      93      IFAAAA  KPFAAA  AAAAxx
+7527   3781    1       3       7       7       27      527     1527    2527    7527    54      55      NDAAAA  LPFAAA  HHHHxx
+5000   3782    0       0       0       0       0       0       1000    0       5000    0       1       IKAAAA  MPFAAA  OOOOxx
+7587   3783    1       3       7       7       87      587     1587    2587    7587    174     175     VFAAAA  NPFAAA  VVVVxx
+3014   3784    0       2       4       14      14      14      1014    3014    3014    28      29      YLAAAA  OPFAAA  AAAAxx
+5276   3785    0       0       6       16      76      276     1276    276     5276    152     153     YUAAAA  PPFAAA  HHHHxx
+6457   3786    1       1       7       17      57      457     457     1457    6457    114     115     JOAAAA  QPFAAA  OOOOxx
+389    3787    1       1       9       9       89      389     389     389     389     178     179     ZOAAAA  RPFAAA  VVVVxx
+7104   3788    0       0       4       4       4       104     1104    2104    7104    8       9       GNAAAA  SPFAAA  AAAAxx
+9995   3789    1       3       5       15      95      995     1995    4995    9995    190     191     LUAAAA  TPFAAA  HHHHxx
+7368   3790    0       0       8       8       68      368     1368    2368    7368    136     137     KXAAAA  UPFAAA  OOOOxx
+3258   3791    0       2       8       18      58      258     1258    3258    3258    116     117     IVAAAA  VPFAAA  VVVVxx
+9208   3792    0       0       8       8       8       208     1208    4208    9208    16      17      EQAAAA  WPFAAA  AAAAxx
+2396   3793    0       0       6       16      96      396     396     2396    2396    192     193     EOAAAA  XPFAAA  HHHHxx
+1715   3794    1       3       5       15      15      715     1715    1715    1715    30      31      ZNAAAA  YPFAAA  OOOOxx
+1240   3795    0       0       0       0       40      240     1240    1240    1240    80      81      SVAAAA  ZPFAAA  VVVVxx
+1952   3796    0       0       2       12      52      952     1952    1952    1952    104     105     CXAAAA  AQFAAA  AAAAxx
+4403   3797    1       3       3       3       3       403     403     4403    4403    6       7       JNAAAA  BQFAAA  HHHHxx
+6333   3798    1       1       3       13      33      333     333     1333    6333    66      67      PJAAAA  CQFAAA  OOOOxx
+2492   3799    0       0       2       12      92      492     492     2492    2492    184     185     WRAAAA  DQFAAA  VVVVxx
+6543   3800    1       3       3       3       43      543     543     1543    6543    86      87      RRAAAA  EQFAAA  AAAAxx
+5548   3801    0       0       8       8       48      548     1548    548     5548    96      97      KFAAAA  FQFAAA  HHHHxx
+3458   3802    0       2       8       18      58      458     1458    3458    3458    116     117     ADAAAA  GQFAAA  OOOOxx
+2588   3803    0       0       8       8       88      588     588     2588    2588    176     177     OVAAAA  HQFAAA  VVVVxx
+1364   3804    0       0       4       4       64      364     1364    1364    1364    128     129     MAAAAA  IQFAAA  AAAAxx
+9856   3805    0       0       6       16      56      856     1856    4856    9856    112     113     CPAAAA  JQFAAA  HHHHxx
+4964   3806    0       0       4       4       64      964     964     4964    4964    128     129     YIAAAA  KQFAAA  OOOOxx
+773    3807    1       1       3       13      73      773     773     773     773     146     147     TDAAAA  LQFAAA  VVVVxx
+6402   3808    0       2       2       2       2       402     402     1402    6402    4       5       GMAAAA  MQFAAA  AAAAxx
+7213   3809    1       1       3       13      13      213     1213    2213    7213    26      27      LRAAAA  NQFAAA  HHHHxx
+3385   3810    1       1       5       5       85      385     1385    3385    3385    170     171     FAAAAA  OQFAAA  OOOOxx
+6005   3811    1       1       5       5       5       5       5       1005    6005    10      11      ZWAAAA  PQFAAA  VVVVxx
+9346   3812    0       2       6       6       46      346     1346    4346    9346    92      93      MVAAAA  QQFAAA  AAAAxx
+1831   3813    1       3       1       11      31      831     1831    1831    1831    62      63      LSAAAA  RQFAAA  HHHHxx
+5406   3814    0       2       6       6       6       406     1406    406     5406    12      13      YZAAAA  SQFAAA  OOOOxx
+2154   3815    0       2       4       14      54      154     154     2154    2154    108     109     WEAAAA  TQFAAA  VVVVxx
+3721   3816    1       1       1       1       21      721     1721    3721    3721    42      43      DNAAAA  UQFAAA  AAAAxx
+2889   3817    1       1       9       9       89      889     889     2889    2889    178     179     DHAAAA  VQFAAA  HHHHxx
+4410   3818    0       2       0       10      10      410     410     4410    4410    20      21      QNAAAA  WQFAAA  OOOOxx
+7102   3819    0       2       2       2       2       102     1102    2102    7102    4       5       ENAAAA  XQFAAA  VVVVxx
+4057   3820    1       1       7       17      57      57      57      4057    4057    114     115     BAAAAA  YQFAAA  AAAAxx
+9780   3821    0       0       0       0       80      780     1780    4780    9780    160     161     EMAAAA  ZQFAAA  HHHHxx
+9481   3822    1       1       1       1       81      481     1481    4481    9481    162     163     RAAAAA  ARFAAA  OOOOxx
+2366   3823    0       2       6       6       66      366     366     2366    2366    132     133     ANAAAA  BRFAAA  VVVVxx
+2708   3824    0       0       8       8       8       708     708     2708    2708    16      17      EAAAAA  CRFAAA  AAAAxx
+7399   3825    1       3       9       19      99      399     1399    2399    7399    198     199     PYAAAA  DRFAAA  HHHHxx
+5234   3826    0       2       4       14      34      234     1234    234     5234    68      69      ITAAAA  ERFAAA  OOOOxx
+1843   3827    1       3       3       3       43      843     1843    1843    1843    86      87      XSAAAA  FRFAAA  VVVVxx
+1006   3828    0       2       6       6       6       6       1006    1006    1006    12      13      SMAAAA  GRFAAA  AAAAxx
+7696   3829    0       0       6       16      96      696     1696    2696    7696    192     193     AKAAAA  HRFAAA  HHHHxx
+6411   3830    1       3       1       11      11      411     411     1411    6411    22      23      PMAAAA  IRFAAA  OOOOxx
+3913   3831    1       1       3       13      13      913     1913    3913    3913    26      27      NUAAAA  JRFAAA  VVVVxx
+2538   3832    0       2       8       18      38      538     538     2538    2538    76      77      QTAAAA  KRFAAA  AAAAxx
+3019   3833    1       3       9       19      19      19      1019    3019    3019    38      39      DMAAAA  LRFAAA  HHHHxx
+107    3834    1       3       7       7       7       107     107     107     107     14      15      DEAAAA  MRFAAA  OOOOxx
+427    3835    1       3       7       7       27      427     427     427     427     54      55      LQAAAA  NRFAAA  VVVVxx
+9849   3836    1       1       9       9       49      849     1849    4849    9849    98      99      VOAAAA  ORFAAA  AAAAxx
+4195   3837    1       3       5       15      95      195     195     4195    4195    190     191     JFAAAA  PRFAAA  HHHHxx
+9215   3838    1       3       5       15      15      215     1215    4215    9215    30      31      LQAAAA  QRFAAA  OOOOxx
+3165   3839    1       1       5       5       65      165     1165    3165    3165    130     131     TRAAAA  RRFAAA  VVVVxx
+3280   3840    0       0       0       0       80      280     1280    3280    3280    160     161     EWAAAA  SRFAAA  AAAAxx
+4477   3841    1       1       7       17      77      477     477     4477    4477    154     155     FQAAAA  TRFAAA  HHHHxx
+5885   3842    1       1       5       5       85      885     1885    885     5885    170     171     JSAAAA  URFAAA  OOOOxx
+3311   3843    1       3       1       11      11      311     1311    3311    3311    22      23      JXAAAA  VRFAAA  VVVVxx
+6453   3844    1       1       3       13      53      453     453     1453    6453    106     107     FOAAAA  WRFAAA  AAAAxx
+8527   3845    1       3       7       7       27      527     527     3527    8527    54      55      ZPAAAA  XRFAAA  HHHHxx
+1921   3846    1       1       1       1       21      921     1921    1921    1921    42      43      XVAAAA  YRFAAA  OOOOxx
+2427   3847    1       3       7       7       27      427     427     2427    2427    54      55      JPAAAA  ZRFAAA  VVVVxx
+3691   3848    1       3       1       11      91      691     1691    3691    3691    182     183     ZLAAAA  ASFAAA  AAAAxx
+3882   3849    0       2       2       2       82      882     1882    3882    3882    164     165     ITAAAA  BSFAAA  HHHHxx
+562    3850    0       2       2       2       62      562     562     562     562     124     125     QVAAAA  CSFAAA  OOOOxx
+377    3851    1       1       7       17      77      377     377     377     377     154     155     NOAAAA  DSFAAA  VVVVxx
+1497   3852    1       1       7       17      97      497     1497    1497    1497    194     195     PFAAAA  ESFAAA  AAAAxx
+4453   3853    1       1       3       13      53      453     453     4453    4453    106     107     HPAAAA  FSFAAA  HHHHxx
+4678   3854    0       2       8       18      78      678     678     4678    4678    156     157     YXAAAA  GSFAAA  OOOOxx
+2234   3855    0       2       4       14      34      234     234     2234    2234    68      69      YHAAAA  HSFAAA  VVVVxx
+1073   3856    1       1       3       13      73      73      1073    1073    1073    146     147     HPAAAA  ISFAAA  AAAAxx
+6479   3857    1       3       9       19      79      479     479     1479    6479    158     159     FPAAAA  JSFAAA  HHHHxx
+5665   3858    1       1       5       5       65      665     1665    665     5665    130     131     XJAAAA  KSFAAA  OOOOxx
+586    3859    0       2       6       6       86      586     586     586     586     172     173     OWAAAA  LSFAAA  VVVVxx
+1584   3860    0       0       4       4       84      584     1584    1584    1584    168     169     YIAAAA  MSFAAA  AAAAxx
+2574   3861    0       2       4       14      74      574     574     2574    2574    148     149     AVAAAA  NSFAAA  HHHHxx
+9833   3862    1       1       3       13      33      833     1833    4833    9833    66      67      FOAAAA  OSFAAA  OOOOxx
+6726   3863    0       2       6       6       26      726     726     1726    6726    52      53      SYAAAA  PSFAAA  VVVVxx
+8497   3864    1       1       7       17      97      497     497     3497    8497    194     195     VOAAAA  QSFAAA  AAAAxx
+2914   3865    0       2       4       14      14      914     914     2914    2914    28      29      CIAAAA  RSFAAA  HHHHxx
+8586   3866    0       2       6       6       86      586     586     3586    8586    172     173     GSAAAA  SSFAAA  OOOOxx
+6973   3867    1       1       3       13      73      973     973     1973    6973    146     147     FIAAAA  TSFAAA  VVVVxx
+1322   3868    0       2       2       2       22      322     1322    1322    1322    44      45      WYAAAA  USFAAA  AAAAxx
+5242   3869    0       2       2       2       42      242     1242    242     5242    84      85      QTAAAA  VSFAAA  HHHHxx
+5581   3870    1       1       1       1       81      581     1581    581     5581    162     163     RGAAAA  WSFAAA  OOOOxx
+1365   3871    1       1       5       5       65      365     1365    1365    1365    130     131     NAAAAA  XSFAAA  VVVVxx
+2818   3872    0       2       8       18      18      818     818     2818    2818    36      37      KEAAAA  YSFAAA  AAAAxx
+3758   3873    0       2       8       18      58      758     1758    3758    3758    116     117     OOAAAA  ZSFAAA  HHHHxx
+2665   3874    1       1       5       5       65      665     665     2665    2665    130     131     NYAAAA  ATFAAA  OOOOxx
+9823   3875    1       3       3       3       23      823     1823    4823    9823    46      47      VNAAAA  BTFAAA  VVVVxx
+7057   3876    1       1       7       17      57      57      1057    2057    7057    114     115     LLAAAA  CTFAAA  AAAAxx
+543    3877    1       3       3       3       43      543     543     543     543     86      87      XUAAAA  DTFAAA  HHHHxx
+4008   3878    0       0       8       8       8       8       8       4008    4008    16      17      EYAAAA  ETFAAA  OOOOxx
+4397   3879    1       1       7       17      97      397     397     4397    4397    194     195     DNAAAA  FTFAAA  VVVVxx
+8533   3880    1       1       3       13      33      533     533     3533    8533    66      67      FQAAAA  GTFAAA  AAAAxx
+9728   3881    0       0       8       8       28      728     1728    4728    9728    56      57      EKAAAA  HTFAAA  HHHHxx
+5198   3882    0       2       8       18      98      198     1198    198     5198    196     197     YRAAAA  ITFAAA  OOOOxx
+5036   3883    0       0       6       16      36      36      1036    36      5036    72      73      SLAAAA  JTFAAA  VVVVxx
+4394   3884    0       2       4       14      94      394     394     4394    4394    188     189     ANAAAA  KTFAAA  AAAAxx
+9633   3885    1       1       3       13      33      633     1633    4633    9633    66      67      NGAAAA  LTFAAA  HHHHxx
+3339   3886    1       3       9       19      39      339     1339    3339    3339    78      79      LYAAAA  MTFAAA  OOOOxx
+9529   3887    1       1       9       9       29      529     1529    4529    9529    58      59      NCAAAA  NTFAAA  VVVVxx
+4780   3888    0       0       0       0       80      780     780     4780    4780    160     161     WBAAAA  OTFAAA  AAAAxx
+4862   3889    0       2       2       2       62      862     862     4862    4862    124     125     AFAAAA  PTFAAA  HHHHxx
+8152   3890    0       0       2       12      52      152     152     3152    8152    104     105     OBAAAA  QTFAAA  OOOOxx
+9330   3891    0       2       0       10      30      330     1330    4330    9330    60      61      WUAAAA  RTFAAA  VVVVxx
+4362   3892    0       2       2       2       62      362     362     4362    4362    124     125     ULAAAA  STFAAA  AAAAxx
+4688   3893    0       0       8       8       88      688     688     4688    4688    176     177     IYAAAA  TTFAAA  HHHHxx
+1903   3894    1       3       3       3       3       903     1903    1903    1903    6       7       FVAAAA  UTFAAA  OOOOxx
+9027   3895    1       3       7       7       27      27      1027    4027    9027    54      55      FJAAAA  VTFAAA  VVVVxx
+5385   3896    1       1       5       5       85      385     1385    385     5385    170     171     DZAAAA  WTFAAA  AAAAxx
+9854   3897    0       2       4       14      54      854     1854    4854    9854    108     109     APAAAA  XTFAAA  HHHHxx
+9033   3898    1       1       3       13      33      33      1033    4033    9033    66      67      LJAAAA  YTFAAA  OOOOxx
+3185   3899    1       1       5       5       85      185     1185    3185    3185    170     171     NSAAAA  ZTFAAA  VVVVxx
+2618   3900    0       2       8       18      18      618     618     2618    2618    36      37      SWAAAA  AUFAAA  AAAAxx
+371    3901    1       3       1       11      71      371     371     371     371     142     143     HOAAAA  BUFAAA  HHHHxx
+3697   3902    1       1       7       17      97      697     1697    3697    3697    194     195     FMAAAA  CUFAAA  OOOOxx
+1682   3903    0       2       2       2       82      682     1682    1682    1682    164     165     SMAAAA  DUFAAA  VVVVxx
+3333   3904    1       1       3       13      33      333     1333    3333    3333    66      67      FYAAAA  EUFAAA  AAAAxx
+1722   3905    0       2       2       2       22      722     1722    1722    1722    44      45      GOAAAA  FUFAAA  HHHHxx
+2009   3906    1       1       9       9       9       9       9       2009    2009    18      19      HZAAAA  GUFAAA  OOOOxx
+3517   3907    1       1       7       17      17      517     1517    3517    3517    34      35      HFAAAA  HUFAAA  VVVVxx
+7640   3908    0       0       0       0       40      640     1640    2640    7640    80      81      WHAAAA  IUFAAA  AAAAxx
+259    3909    1       3       9       19      59      259     259     259     259     118     119     ZJAAAA  JUFAAA  HHHHxx
+1400   3910    0       0       0       0       0       400     1400    1400    1400    0       1       WBAAAA  KUFAAA  OOOOxx
+6663   3911    1       3       3       3       63      663     663     1663    6663    126     127     HWAAAA  LUFAAA  VVVVxx
+1576   3912    0       0       6       16      76      576     1576    1576    1576    152     153     QIAAAA  MUFAAA  AAAAxx
+8843   3913    1       3       3       3       43      843     843     3843    8843    86      87      DCAAAA  NUFAAA  HHHHxx
+9474   3914    0       2       4       14      74      474     1474    4474    9474    148     149     KAAAAA  OUFAAA  OOOOxx
+1597   3915    1       1       7       17      97      597     1597    1597    1597    194     195     LJAAAA  PUFAAA  VVVVxx
+1143   3916    1       3       3       3       43      143     1143    1143    1143    86      87      ZRAAAA  QUFAAA  AAAAxx
+4162   3917    0       2       2       2       62      162     162     4162    4162    124     125     CEAAAA  RUFAAA  HHHHxx
+1301   3918    1       1       1       1       1       301     1301    1301    1301    2       3       BYAAAA  SUFAAA  OOOOxx
+2935   3919    1       3       5       15      35      935     935     2935    2935    70      71      XIAAAA  TUFAAA  VVVVxx
+886    3920    0       2       6       6       86      886     886     886     886     172     173     CIAAAA  UUFAAA  AAAAxx
+1661   3921    1       1       1       1       61      661     1661    1661    1661    122     123     XLAAAA  VUFAAA  HHHHxx
+1026   3922    0       2       6       6       26      26      1026    1026    1026    52      53      MNAAAA  WUFAAA  OOOOxx
+7034   3923    0       2       4       14      34      34      1034    2034    7034    68      69      OKAAAA  XUFAAA  VVVVxx
+2305   3924    1       1       5       5       5       305     305     2305    2305    10      11      RKAAAA  YUFAAA  AAAAxx
+1725   3925    1       1       5       5       25      725     1725    1725    1725    50      51      JOAAAA  ZUFAAA  HHHHxx
+909    3926    1       1       9       9       9       909     909     909     909     18      19      ZIAAAA  AVFAAA  OOOOxx
+9906   3927    0       2       6       6       6       906     1906    4906    9906    12      13      ARAAAA  BVFAAA  VVVVxx
+3309   3928    1       1       9       9       9       309     1309    3309    3309    18      19      HXAAAA  CVFAAA  AAAAxx
+515    3929    1       3       5       15      15      515     515     515     515     30      31      VTAAAA  DVFAAA  HHHHxx
+932    3930    0       0       2       12      32      932     932     932     932     64      65      WJAAAA  EVFAAA  OOOOxx
+8144   3931    0       0       4       4       44      144     144     3144    8144    88      89      GBAAAA  FVFAAA  VVVVxx
+5592   3932    0       0       2       12      92      592     1592    592     5592    184     185     CHAAAA  GVFAAA  AAAAxx
+4003   3933    1       3       3       3       3       3       3       4003    4003    6       7       ZXAAAA  HVFAAA  HHHHxx
+9566   3934    0       2       6       6       66      566     1566    4566    9566    132     133     YDAAAA  IVFAAA  OOOOxx
+4556   3935    0       0       6       16      56      556     556     4556    4556    112     113     GTAAAA  JVFAAA  VVVVxx
+268    3936    0       0       8       8       68      268     268     268     268     136     137     IKAAAA  KVFAAA  AAAAxx
+8107   3937    1       3       7       7       7       107     107     3107    8107    14      15      VZAAAA  LVFAAA  HHHHxx
+5816   3938    0       0       6       16      16      816     1816    816     5816    32      33      SPAAAA  MVFAAA  OOOOxx
+8597   3939    1       1       7       17      97      597     597     3597    8597    194     195     RSAAAA  NVFAAA  VVVVxx
+9611   3940    1       3       1       11      11      611     1611    4611    9611    22      23      RFAAAA  OVFAAA  AAAAxx
+8070   3941    0       2       0       10      70      70      70      3070    8070    140     141     KYAAAA  PVFAAA  HHHHxx
+6040   3942    0       0       0       0       40      40      40      1040    6040    80      81      IYAAAA  QVFAAA  OOOOxx
+3184   3943    0       0       4       4       84      184     1184    3184    3184    168     169     MSAAAA  RVFAAA  VVVVxx
+9656   3944    0       0       6       16      56      656     1656    4656    9656    112     113     KHAAAA  SVFAAA  AAAAxx
+1577   3945    1       1       7       17      77      577     1577    1577    1577    154     155     RIAAAA  TVFAAA  HHHHxx
+1805   3946    1       1       5       5       5       805     1805    1805    1805    10      11      LRAAAA  UVFAAA  OOOOxx
+8268   3947    0       0       8       8       68      268     268     3268    8268    136     137     AGAAAA  VVFAAA  VVVVxx
+3489   3948    1       1       9       9       89      489     1489    3489    3489    178     179     FEAAAA  WVFAAA  AAAAxx
+4564   3949    0       0       4       4       64      564     564     4564    4564    128     129     OTAAAA  XVFAAA  HHHHxx
+4006   3950    0       2       6       6       6       6       6       4006    4006    12      13      CYAAAA  YVFAAA  OOOOxx
+8466   3951    0       2       6       6       66      466     466     3466    8466    132     133     QNAAAA  ZVFAAA  VVVVxx
+938    3952    0       2       8       18      38      938     938     938     938     76      77      CKAAAA  AWFAAA  AAAAxx
+5944   3953    0       0       4       4       44      944     1944    944     5944    88      89      QUAAAA  BWFAAA  HHHHxx
+8363   3954    1       3       3       3       63      363     363     3363    8363    126     127     RJAAAA  CWFAAA  OOOOxx
+5348   3955    0       0       8       8       48      348     1348    348     5348    96      97      SXAAAA  DWFAAA  VVVVxx
+71     3956    1       3       1       11      71      71      71      71      71      142     143     TCAAAA  EWFAAA  AAAAxx
+3620   3957    0       0       0       0       20      620     1620    3620    3620    40      41      GJAAAA  FWFAAA  HHHHxx
+3230   3958    0       2       0       10      30      230     1230    3230    3230    60      61      GUAAAA  GWFAAA  OOOOxx
+6132   3959    0       0       2       12      32      132     132     1132    6132    64      65      WBAAAA  HWFAAA  VVVVxx
+6143   3960    1       3       3       3       43      143     143     1143    6143    86      87      HCAAAA  IWFAAA  AAAAxx
+8781   3961    1       1       1       1       81      781     781     3781    8781    162     163     TZAAAA  JWFAAA  HHHHxx
+5522   3962    0       2       2       2       22      522     1522    522     5522    44      45      KEAAAA  KWFAAA  OOOOxx
+6320   3963    0       0       0       0       20      320     320     1320    6320    40      41      CJAAAA  LWFAAA  VVVVxx
+3923   3964    1       3       3       3       23      923     1923    3923    3923    46      47      XUAAAA  MWFAAA  AAAAxx
+2207   3965    1       3       7       7       7       207     207     2207    2207    14      15      XGAAAA  NWFAAA  HHHHxx
+966    3966    0       2       6       6       66      966     966     966     966     132     133     ELAAAA  OWFAAA  OOOOxx
+9020   3967    0       0       0       0       20      20      1020    4020    9020    40      41      YIAAAA  PWFAAA  VVVVxx
+4616   3968    0       0       6       16      16      616     616     4616    4616    32      33      OVAAAA  QWFAAA  AAAAxx
+8289   3969    1       1       9       9       89      289     289     3289    8289    178     179     VGAAAA  RWFAAA  HHHHxx
+5796   3970    0       0       6       16      96      796     1796    796     5796    192     193     YOAAAA  SWFAAA  OOOOxx
+9259   3971    1       3       9       19      59      259     1259    4259    9259    118     119     DSAAAA  TWFAAA  VVVVxx
+3710   3972    0       2       0       10      10      710     1710    3710    3710    20      21      SMAAAA  UWFAAA  AAAAxx
+251    3973    1       3       1       11      51      251     251     251     251     102     103     RJAAAA  VWFAAA  HHHHxx
+7669   3974    1       1       9       9       69      669     1669    2669    7669    138     139     ZIAAAA  WWFAAA  OOOOxx
+6304   3975    0       0       4       4       4       304     304     1304    6304    8       9       MIAAAA  XWFAAA  VVVVxx
+6454   3976    0       2       4       14      54      454     454     1454    6454    108     109     GOAAAA  YWFAAA  AAAAxx
+1489   3977    1       1       9       9       89      489     1489    1489    1489    178     179     HFAAAA  ZWFAAA  HHHHxx
+715    3978    1       3       5       15      15      715     715     715     715     30      31      NBAAAA  AXFAAA  OOOOxx
+4319   3979    1       3       9       19      19      319     319     4319    4319    38      39      DKAAAA  BXFAAA  VVVVxx
+7112   3980    0       0       2       12      12      112     1112    2112    7112    24      25      ONAAAA  CXFAAA  AAAAxx
+3726   3981    0       2       6       6       26      726     1726    3726    3726    52      53      INAAAA  DXFAAA  HHHHxx
+7727   3982    1       3       7       7       27      727     1727    2727    7727    54      55      FLAAAA  EXFAAA  OOOOxx
+8387   3983    1       3       7       7       87      387     387     3387    8387    174     175     PKAAAA  FXFAAA  VVVVxx
+6555   3984    1       3       5       15      55      555     555     1555    6555    110     111     DSAAAA  GXFAAA  AAAAxx
+1148   3985    0       0       8       8       48      148     1148    1148    1148    96      97      ESAAAA  HXFAAA  HHHHxx
+9000   3986    0       0       0       0       0       0       1000    4000    9000    0       1       EIAAAA  IXFAAA  OOOOxx
+5278   3987    0       2       8       18      78      278     1278    278     5278    156     157     AVAAAA  JXFAAA  VVVVxx
+2388   3988    0       0       8       8       88      388     388     2388    2388    176     177     WNAAAA  KXFAAA  AAAAxx
+7984   3989    0       0       4       4       84      984     1984    2984    7984    168     169     CVAAAA  LXFAAA  HHHHxx
+881    3990    1       1       1       1       81      881     881     881     881     162     163     XHAAAA  MXFAAA  OOOOxx
+6830   3991    0       2       0       10      30      830     830     1830    6830    60      61      SCAAAA  NXFAAA  VVVVxx
+7056   3992    0       0       6       16      56      56      1056    2056    7056    112     113     KLAAAA  OXFAAA  AAAAxx
+7581   3993    1       1       1       1       81      581     1581    2581    7581    162     163     PFAAAA  PXFAAA  HHHHxx
+5214   3994    0       2       4       14      14      214     1214    214     5214    28      29      OSAAAA  QXFAAA  OOOOxx
+2505   3995    1       1       5       5       5       505     505     2505    2505    10      11      JSAAAA  RXFAAA  VVVVxx
+5112   3996    0       0       2       12      12      112     1112    112     5112    24      25      QOAAAA  SXFAAA  AAAAxx
+9884   3997    0       0       4       4       84      884     1884    4884    9884    168     169     EQAAAA  TXFAAA  HHHHxx
+8040   3998    0       0       0       0       40      40      40      3040    8040    80      81      GXAAAA  UXFAAA  OOOOxx
+7033   3999    1       1       3       13      33      33      1033    2033    7033    66      67      NKAAAA  VXFAAA  VVVVxx
+9343   4000    1       3       3       3       43      343     1343    4343    9343    86      87      JVAAAA  WXFAAA  AAAAxx
+2931   4001    1       3       1       11      31      931     931     2931    2931    62      63      TIAAAA  XXFAAA  HHHHxx
+9024   4002    0       0       4       4       24      24      1024    4024    9024    48      49      CJAAAA  YXFAAA  OOOOxx
+6485   4003    1       1       5       5       85      485     485     1485    6485    170     171     LPAAAA  ZXFAAA  VVVVxx
+3465   4004    1       1       5       5       65      465     1465    3465    3465    130     131     HDAAAA  AYFAAA  AAAAxx
+3357   4005    1       1       7       17      57      357     1357    3357    3357    114     115     DZAAAA  BYFAAA  HHHHxx
+2929   4006    1       1       9       9       29      929     929     2929    2929    58      59      RIAAAA  CYFAAA  OOOOxx
+3086   4007    0       2       6       6       86      86      1086    3086    3086    172     173     SOAAAA  DYFAAA  VVVVxx
+8897   4008    1       1       7       17      97      897     897     3897    8897    194     195     FEAAAA  EYFAAA  AAAAxx
+9688   4009    0       0       8       8       88      688     1688    4688    9688    176     177     QIAAAA  FYFAAA  HHHHxx
+6522   4010    0       2       2       2       22      522     522     1522    6522    44      45      WQAAAA  GYFAAA  OOOOxx
+3241   4011    1       1       1       1       41      241     1241    3241    3241    82      83      RUAAAA  HYFAAA  VVVVxx
+8770   4012    0       2       0       10      70      770     770     3770    8770    140     141     IZAAAA  IYFAAA  AAAAxx
+2884   4013    0       0       4       4       84      884     884     2884    2884    168     169     YGAAAA  JYFAAA  HHHHxx
+9579   4014    1       3       9       19      79      579     1579    4579    9579    158     159     LEAAAA  KYFAAA  OOOOxx
+3125   4015    1       1       5       5       25      125     1125    3125    3125    50      51      FQAAAA  LYFAAA  VVVVxx
+4604   4016    0       0       4       4       4       604     604     4604    4604    8       9       CVAAAA  MYFAAA  AAAAxx
+2682   4017    0       2       2       2       82      682     682     2682    2682    164     165     EZAAAA  NYFAAA  HHHHxx
+254    4018    0       2       4       14      54      254     254     254     254     108     109     UJAAAA  OYFAAA  OOOOxx
+6569   4019    1       1       9       9       69      569     569     1569    6569    138     139     RSAAAA  PYFAAA  VVVVxx
+2686   4020    0       2       6       6       86      686     686     2686    2686    172     173     IZAAAA  QYFAAA  AAAAxx
+2123   4021    1       3       3       3       23      123     123     2123    2123    46      47      RDAAAA  RYFAAA  HHHHxx
+1745   4022    1       1       5       5       45      745     1745    1745    1745    90      91      DPAAAA  SYFAAA  OOOOxx
+247    4023    1       3       7       7       47      247     247     247     247     94      95      NJAAAA  TYFAAA  VVVVxx
+5800   4024    0       0       0       0       0       800     1800    800     5800    0       1       CPAAAA  UYFAAA  AAAAxx
+1121   4025    1       1       1       1       21      121     1121    1121    1121    42      43      DRAAAA  VYFAAA  HHHHxx
+8893   4026    1       1       3       13      93      893     893     3893    8893    186     187     BEAAAA  WYFAAA  OOOOxx
+7819   4027    1       3       9       19      19      819     1819    2819    7819    38      39      TOAAAA  XYFAAA  VVVVxx
+1339   4028    1       3       9       19      39      339     1339    1339    1339    78      79      NZAAAA  YYFAAA  AAAAxx
+5680   4029    0       0       0       0       80      680     1680    680     5680    160     161     MKAAAA  ZYFAAA  HHHHxx
+5093   4030    1       1       3       13      93      93      1093    93      5093    186     187     XNAAAA  AZFAAA  OOOOxx
+3508   4031    0       0       8       8       8       508     1508    3508    3508    16      17      YEAAAA  BZFAAA  VVVVxx
+933    4032    1       1       3       13      33      933     933     933     933     66      67      XJAAAA  CZFAAA  AAAAxx
+1106   4033    0       2       6       6       6       106     1106    1106    1106    12      13      OQAAAA  DZFAAA  HHHHxx
+4386   4034    0       2       6       6       86      386     386     4386    4386    172     173     SMAAAA  EZFAAA  OOOOxx
+5895   4035    1       3       5       15      95      895     1895    895     5895    190     191     TSAAAA  FZFAAA  VVVVxx
+2980   4036    0       0       0       0       80      980     980     2980    2980    160     161     QKAAAA  GZFAAA  AAAAxx
+4400   4037    0       0       0       0       0       400     400     4400    4400    0       1       GNAAAA  HZFAAA  HHHHxx
+7433   4038    1       1       3       13      33      433     1433    2433    7433    66      67      XZAAAA  IZFAAA  OOOOxx
+6110   4039    0       2       0       10      10      110     110     1110    6110    20      21      ABAAAA  JZFAAA  VVVVxx
+867    4040    1       3       7       7       67      867     867     867     867     134     135     JHAAAA  KZFAAA  AAAAxx
+5292   4041    0       0       2       12      92      292     1292    292     5292    184     185     OVAAAA  LZFAAA  HHHHxx
+3926   4042    0       2       6       6       26      926     1926    3926    3926    52      53      AVAAAA  MZFAAA  OOOOxx
+1107   4043    1       3       7       7       7       107     1107    1107    1107    14      15      PQAAAA  NZFAAA  VVVVxx
+7355   4044    1       3       5       15      55      355     1355    2355    7355    110     111     XWAAAA  OZFAAA  AAAAxx
+4689   4045    1       1       9       9       89      689     689     4689    4689    178     179     JYAAAA  PZFAAA  HHHHxx
+4872   4046    0       0       2       12      72      872     872     4872    4872    144     145     KFAAAA  QZFAAA  OOOOxx
+7821   4047    1       1       1       1       21      821     1821    2821    7821    42      43      VOAAAA  RZFAAA  VVVVxx
+7277   4048    1       1       7       17      77      277     1277    2277    7277    154     155     XTAAAA  SZFAAA  AAAAxx
+3268   4049    0       0       8       8       68      268     1268    3268    3268    136     137     SVAAAA  TZFAAA  HHHHxx
+8877   4050    1       1       7       17      77      877     877     3877    8877    154     155     LDAAAA  UZFAAA  OOOOxx
+343    4051    1       3       3       3       43      343     343     343     343     86      87      FNAAAA  VZFAAA  VVVVxx
+621    4052    1       1       1       1       21      621     621     621     621     42      43      XXAAAA  WZFAAA  AAAAxx
+5429   4053    1       1       9       9       29      429     1429    429     5429    58      59      VAAAAA  XZFAAA  HHHHxx
+392    4054    0       0       2       12      92      392     392     392     392     184     185     CPAAAA  YZFAAA  OOOOxx
+6004   4055    0       0       4       4       4       4       4       1004    6004    8       9       YWAAAA  ZZFAAA  VVVVxx
+6377   4056    1       1       7       17      77      377     377     1377    6377    154     155     HLAAAA  AAGAAA  AAAAxx
+3037   4057    1       1       7       17      37      37      1037    3037    3037    74      75      VMAAAA  BAGAAA  HHHHxx
+3514   4058    0       2       4       14      14      514     1514    3514    3514    28      29      EFAAAA  CAGAAA  OOOOxx
+8740   4059    0       0       0       0       40      740     740     3740    8740    80      81      EYAAAA  DAGAAA  VVVVxx
+3877   4060    1       1       7       17      77      877     1877    3877    3877    154     155     DTAAAA  EAGAAA  AAAAxx
+5731   4061    1       3       1       11      31      731     1731    731     5731    62      63      LMAAAA  FAGAAA  HHHHxx
+6407   4062    1       3       7       7       7       407     407     1407    6407    14      15      LMAAAA  GAGAAA  OOOOxx
+2044   4063    0       0       4       4       44      44      44      2044    2044    88      89      QAAAAA  HAGAAA  VVVVxx
+7362   4064    0       2       2       2       62      362     1362    2362    7362    124     125     EXAAAA  IAGAAA  AAAAxx
+5458   4065    0       2       8       18      58      458     1458    458     5458    116     117     YBAAAA  JAGAAA  HHHHxx
+6437   4066    1       1       7       17      37      437     437     1437    6437    74      75      PNAAAA  KAGAAA  OOOOxx
+1051   4067    1       3       1       11      51      51      1051    1051    1051    102     103     LOAAAA  LAGAAA  VVVVxx
+1203   4068    1       3       3       3       3       203     1203    1203    1203    6       7       HUAAAA  MAGAAA  AAAAxx
+2176   4069    0       0       6       16      76      176     176     2176    2176    152     153     SFAAAA  NAGAAA  HHHHxx
+8997   4070    1       1       7       17      97      997     997     3997    8997    194     195     BIAAAA  OAGAAA  OOOOxx
+6378   4071    0       2       8       18      78      378     378     1378    6378    156     157     ILAAAA  PAGAAA  VVVVxx
+6006   4072    0       2       6       6       6       6       6       1006    6006    12      13      AXAAAA  QAGAAA  AAAAxx
+2308   4073    0       0       8       8       8       308     308     2308    2308    16      17      UKAAAA  RAGAAA  HHHHxx
+625    4074    1       1       5       5       25      625     625     625     625     50      51      BYAAAA  SAGAAA  OOOOxx
+7298   4075    0       2       8       18      98      298     1298    2298    7298    196     197     SUAAAA  TAGAAA  VVVVxx
+5575   4076    1       3       5       15      75      575     1575    575     5575    150     151     LGAAAA  UAGAAA  AAAAxx
+3565   4077    1       1       5       5       65      565     1565    3565    3565    130     131     DHAAAA  VAGAAA  HHHHxx
+47     4078    1       3       7       7       47      47      47      47      47      94      95      VBAAAA  WAGAAA  OOOOxx
+2413   4079    1       1       3       13      13      413     413     2413    2413    26      27      VOAAAA  XAGAAA  VVVVxx
+2153   4080    1       1       3       13      53      153     153     2153    2153    106     107     VEAAAA  YAGAAA  AAAAxx
+752    4081    0       0       2       12      52      752     752     752     752     104     105     YCAAAA  ZAGAAA  HHHHxx
+4095   4082    1       3       5       15      95      95      95      4095    4095    190     191     NBAAAA  ABGAAA  OOOOxx
+2518   4083    0       2       8       18      18      518     518     2518    2518    36      37      WSAAAA  BBGAAA  VVVVxx
+3681   4084    1       1       1       1       81      681     1681    3681    3681    162     163     PLAAAA  CBGAAA  AAAAxx
+4213   4085    1       1       3       13      13      213     213     4213    4213    26      27      BGAAAA  DBGAAA  HHHHxx
+2615   4086    1       3       5       15      15      615     615     2615    2615    30      31      PWAAAA  EBGAAA  OOOOxx
+1471   4087    1       3       1       11      71      471     1471    1471    1471    142     143     PEAAAA  FBGAAA  VVVVxx
+7315   4088    1       3       5       15      15      315     1315    2315    7315    30      31      JVAAAA  GBGAAA  AAAAxx
+6013   4089    1       1       3       13      13      13      13      1013    6013    26      27      HXAAAA  HBGAAA  HHHHxx
+3077   4090    1       1       7       17      77      77      1077    3077    3077    154     155     JOAAAA  IBGAAA  OOOOxx
+2190   4091    0       2       0       10      90      190     190     2190    2190    180     181     GGAAAA  JBGAAA  VVVVxx
+528    4092    0       0       8       8       28      528     528     528     528     56      57      IUAAAA  KBGAAA  AAAAxx
+9508   4093    0       0       8       8       8       508     1508    4508    9508    16      17      SBAAAA  LBGAAA  HHHHxx
+2473   4094    1       1       3       13      73      473     473     2473    2473    146     147     DRAAAA  MBGAAA  OOOOxx
+167    4095    1       3       7       7       67      167     167     167     167     134     135     LGAAAA  NBGAAA  VVVVxx
+8448   4096    0       0       8       8       48      448     448     3448    8448    96      97      YMAAAA  OBGAAA  AAAAxx
+7538   4097    0       2       8       18      38      538     1538    2538    7538    76      77      YDAAAA  PBGAAA  HHHHxx
+7638   4098    0       2       8       18      38      638     1638    2638    7638    76      77      UHAAAA  QBGAAA  OOOOxx
+4328   4099    0       0       8       8       28      328     328     4328    4328    56      57      MKAAAA  RBGAAA  VVVVxx
+3812   4100    0       0       2       12      12      812     1812    3812    3812    24      25      QQAAAA  SBGAAA  AAAAxx
+2879   4101    1       3       9       19      79      879     879     2879    2879    158     159     TGAAAA  TBGAAA  HHHHxx
+4741   4102    1       1       1       1       41      741     741     4741    4741    82      83      JAAAAA  UBGAAA  OOOOxx
+9155   4103    1       3       5       15      55      155     1155    4155    9155    110     111     DOAAAA  VBGAAA  VVVVxx
+5151   4104    1       3       1       11      51      151     1151    151     5151    102     103     DQAAAA  WBGAAA  AAAAxx
+5591   4105    1       3       1       11      91      591     1591    591     5591    182     183     BHAAAA  XBGAAA  HHHHxx
+1034   4106    0       2       4       14      34      34      1034    1034    1034    68      69      UNAAAA  YBGAAA  OOOOxx
+765    4107    1       1       5       5       65      765     765     765     765     130     131     LDAAAA  ZBGAAA  VVVVxx
+2664   4108    0       0       4       4       64      664     664     2664    2664    128     129     MYAAAA  ACGAAA  AAAAxx
+6854   4109    0       2       4       14      54      854     854     1854    6854    108     109     QDAAAA  BCGAAA  HHHHxx
+8263   4110    1       3       3       3       63      263     263     3263    8263    126     127     VFAAAA  CCGAAA  OOOOxx
+8658   4111    0       2       8       18      58      658     658     3658    8658    116     117     AVAAAA  DCGAAA  VVVVxx
+587    4112    1       3       7       7       87      587     587     587     587     174     175     PWAAAA  ECGAAA  AAAAxx
+4553   4113    1       1       3       13      53      553     553     4553    4553    106     107     DTAAAA  FCGAAA  HHHHxx
+1368   4114    0       0       8       8       68      368     1368    1368    1368    136     137     QAAAAA  GCGAAA  OOOOxx
+1718   4115    0       2       8       18      18      718     1718    1718    1718    36      37      COAAAA  HCGAAA  VVVVxx
+140    4116    0       0       0       0       40      140     140     140     140     80      81      KFAAAA  ICGAAA  AAAAxx
+8341   4117    1       1       1       1       41      341     341     3341    8341    82      83      VIAAAA  JCGAAA  HHHHxx
+72     4118    0       0       2       12      72      72      72      72      72      144     145     UCAAAA  KCGAAA  OOOOxx
+6589   4119    1       1       9       9       89      589     589     1589    6589    178     179     LTAAAA  LCGAAA  VVVVxx
+2024   4120    0       0       4       4       24      24      24      2024    2024    48      49      WZAAAA  MCGAAA  AAAAxx
+8024   4121    0       0       4       4       24      24      24      3024    8024    48      49      QWAAAA  NCGAAA  HHHHxx
+9564   4122    0       0       4       4       64      564     1564    4564    9564    128     129     WDAAAA  OCGAAA  OOOOxx
+8625   4123    1       1       5       5       25      625     625     3625    8625    50      51      TTAAAA  PCGAAA  VVVVxx
+2680   4124    0       0       0       0       80      680     680     2680    2680    160     161     CZAAAA  QCGAAA  AAAAxx
+4323   4125    1       3       3       3       23      323     323     4323    4323    46      47      HKAAAA  RCGAAA  HHHHxx
+8981   4126    1       1       1       1       81      981     981     3981    8981    162     163     LHAAAA  SCGAAA  OOOOxx
+8909   4127    1       1       9       9       9       909     909     3909    8909    18      19      REAAAA  TCGAAA  VVVVxx
+5288   4128    0       0       8       8       88      288     1288    288     5288    176     177     KVAAAA  UCGAAA  AAAAxx
+2057   4129    1       1       7       17      57      57      57      2057    2057    114     115     DBAAAA  VCGAAA  HHHHxx
+5931   4130    1       3       1       11      31      931     1931    931     5931    62      63      DUAAAA  WCGAAA  OOOOxx
+9794   4131    0       2       4       14      94      794     1794    4794    9794    188     189     SMAAAA  XCGAAA  VVVVxx
+1012   4132    0       0       2       12      12      12      1012    1012    1012    24      25      YMAAAA  YCGAAA  AAAAxx
+5496   4133    0       0       6       16      96      496     1496    496     5496    192     193     KDAAAA  ZCGAAA  HHHHxx
+9182   4134    0       2       2       2       82      182     1182    4182    9182    164     165     EPAAAA  ADGAAA  OOOOxx
+5258   4135    0       2       8       18      58      258     1258    258     5258    116     117     GUAAAA  BDGAAA  VVVVxx
+3050   4136    0       2       0       10      50      50      1050    3050    3050    100     101     INAAAA  CDGAAA  AAAAxx
+2083   4137    1       3       3       3       83      83      83      2083    2083    166     167     DCAAAA  DDGAAA  HHHHxx
+3069   4138    1       1       9       9       69      69      1069    3069    3069    138     139     BOAAAA  EDGAAA  OOOOxx
+8459   4139    1       3       9       19      59      459     459     3459    8459    118     119     JNAAAA  FDGAAA  VVVVxx
+169    4140    1       1       9       9       69      169     169     169     169     138     139     NGAAAA  GDGAAA  AAAAxx
+4379   4141    1       3       9       19      79      379     379     4379    4379    158     159     LMAAAA  HDGAAA  HHHHxx
+5126   4142    0       2       6       6       26      126     1126    126     5126    52      53      EPAAAA  IDGAAA  OOOOxx
+1415   4143    1       3       5       15      15      415     1415    1415    1415    30      31      LCAAAA  JDGAAA  VVVVxx
+1163   4144    1       3       3       3       63      163     1163    1163    1163    126     127     TSAAAA  KDGAAA  AAAAxx
+3500   4145    0       0       0       0       0       500     1500    3500    3500    0       1       QEAAAA  LDGAAA  HHHHxx
+7202   4146    0       2       2       2       2       202     1202    2202    7202    4       5       ARAAAA  MDGAAA  OOOOxx
+747    4147    1       3       7       7       47      747     747     747     747     94      95      TCAAAA  NDGAAA  VVVVxx
+9264   4148    0       0       4       4       64      264     1264    4264    9264    128     129     ISAAAA  ODGAAA  AAAAxx
+8548   4149    0       0       8       8       48      548     548     3548    8548    96      97      UQAAAA  PDGAAA  HHHHxx
+4228   4150    0       0       8       8       28      228     228     4228    4228    56      57      QGAAAA  QDGAAA  OOOOxx
+7122   4151    0       2       2       2       22      122     1122    2122    7122    44      45      YNAAAA  RDGAAA  VVVVxx
+3395   4152    1       3       5       15      95      395     1395    3395    3395    190     191     PAAAAA  SDGAAA  AAAAxx
+5674   4153    0       2       4       14      74      674     1674    674     5674    148     149     GKAAAA  TDGAAA  HHHHxx
+7293   4154    1       1       3       13      93      293     1293    2293    7293    186     187     NUAAAA  UDGAAA  OOOOxx
+737    4155    1       1       7       17      37      737     737     737     737     74      75      JCAAAA  VDGAAA  VVVVxx
+9595   4156    1       3       5       15      95      595     1595    4595    9595    190     191     BFAAAA  WDGAAA  AAAAxx
+594    4157    0       2       4       14      94      594     594     594     594     188     189     WWAAAA  XDGAAA  HHHHxx
+5322   4158    0       2       2       2       22      322     1322    322     5322    44      45      SWAAAA  YDGAAA  OOOOxx
+2933   4159    1       1       3       13      33      933     933     2933    2933    66      67      VIAAAA  ZDGAAA  VVVVxx
+4955   4160    1       3       5       15      55      955     955     4955    4955    110     111     PIAAAA  AEGAAA  AAAAxx
+4073   4161    1       1       3       13      73      73      73      4073    4073    146     147     RAAAAA  BEGAAA  HHHHxx
+7249   4162    1       1       9       9       49      249     1249    2249    7249    98      99      VSAAAA  CEGAAA  OOOOxx
+192    4163    0       0       2       12      92      192     192     192     192     184     185     KHAAAA  DEGAAA  VVVVxx
+2617   4164    1       1       7       17      17      617     617     2617    2617    34      35      RWAAAA  EEGAAA  AAAAxx
+7409   4165    1       1       9       9       9       409     1409    2409    7409    18      19      ZYAAAA  FEGAAA  HHHHxx
+4903   4166    1       3       3       3       3       903     903     4903    4903    6       7       PGAAAA  GEGAAA  OOOOxx
+9797   4167    1       1       7       17      97      797     1797    4797    9797    194     195     VMAAAA  HEGAAA  VVVVxx
+9919   4168    1       3       9       19      19      919     1919    4919    9919    38      39      NRAAAA  IEGAAA  AAAAxx
+1878   4169    0       2       8       18      78      878     1878    1878    1878    156     157     GUAAAA  JEGAAA  HHHHxx
+4851   4170    1       3       1       11      51      851     851     4851    4851    102     103     PEAAAA  KEGAAA  OOOOxx
+5514   4171    0       2       4       14      14      514     1514    514     5514    28      29      CEAAAA  LEGAAA  VVVVxx
+2582   4172    0       2       2       2       82      582     582     2582    2582    164     165     IVAAAA  MEGAAA  AAAAxx
+3564   4173    0       0       4       4       64      564     1564    3564    3564    128     129     CHAAAA  NEGAAA  HHHHxx
+7085   4174    1       1       5       5       85      85      1085    2085    7085    170     171     NMAAAA  OEGAAA  OOOOxx
+3619   4175    1       3       9       19      19      619     1619    3619    3619    38      39      FJAAAA  PEGAAA  VVVVxx
+261    4176    1       1       1       1       61      261     261     261     261     122     123     BKAAAA  QEGAAA  AAAAxx
+7338   4177    0       2       8       18      38      338     1338    2338    7338    76      77      GWAAAA  REGAAA  HHHHxx
+4251   4178    1       3       1       11      51      251     251     4251    4251    102     103     NHAAAA  SEGAAA  OOOOxx
+5360   4179    0       0       0       0       60      360     1360    360     5360    120     121     EYAAAA  TEGAAA  VVVVxx
+5678   4180    0       2       8       18      78      678     1678    678     5678    156     157     KKAAAA  UEGAAA  AAAAxx
+9162   4181    0       2       2       2       62      162     1162    4162    9162    124     125     KOAAAA  VEGAAA  HHHHxx
+5920   4182    0       0       0       0       20      920     1920    920     5920    40      41      STAAAA  WEGAAA  OOOOxx
+7156   4183    0       0       6       16      56      156     1156    2156    7156    112     113     GPAAAA  XEGAAA  VVVVxx
+4271   4184    1       3       1       11      71      271     271     4271    4271    142     143     HIAAAA  YEGAAA  AAAAxx
+4698   4185    0       2       8       18      98      698     698     4698    4698    196     197     SYAAAA  ZEGAAA  HHHHxx
+1572   4186    0       0       2       12      72      572     1572    1572    1572    144     145     MIAAAA  AFGAAA  OOOOxx
+6974   4187    0       2       4       14      74      974     974     1974    6974    148     149     GIAAAA  BFGAAA  VVVVxx
+4291   4188    1       3       1       11      91      291     291     4291    4291    182     183     BJAAAA  CFGAAA  AAAAxx
+4036   4189    0       0       6       16      36      36      36      4036    4036    72      73      GZAAAA  DFGAAA  HHHHxx
+7473   4190    1       1       3       13      73      473     1473    2473    7473    146     147     LBAAAA  EFGAAA  OOOOxx
+4786   4191    0       2       6       6       86      786     786     4786    4786    172     173     CCAAAA  FFGAAA  VVVVxx
+2662   4192    0       2       2       2       62      662     662     2662    2662    124     125     KYAAAA  GFGAAA  AAAAxx
+916    4193    0       0       6       16      16      916     916     916     916     32      33      GJAAAA  HFGAAA  HHHHxx
+668    4194    0       0       8       8       68      668     668     668     668     136     137     SZAAAA  IFGAAA  OOOOxx
+4874   4195    0       2       4       14      74      874     874     4874    4874    148     149     MFAAAA  JFGAAA  VVVVxx
+3752   4196    0       0       2       12      52      752     1752    3752    3752    104     105     IOAAAA  KFGAAA  AAAAxx
+4865   4197    1       1       5       5       65      865     865     4865    4865    130     131     DFAAAA  LFGAAA  HHHHxx
+7052   4198    0       0       2       12      52      52      1052    2052    7052    104     105     GLAAAA  MFGAAA  OOOOxx
+5712   4199    0       0       2       12      12      712     1712    712     5712    24      25      SLAAAA  NFGAAA  VVVVxx
+31     4200    1       3       1       11      31      31      31      31      31      62      63      FBAAAA  OFGAAA  AAAAxx
+4944   4201    0       0       4       4       44      944     944     4944    4944    88      89      EIAAAA  PFGAAA  HHHHxx
+1435   4202    1       3       5       15      35      435     1435    1435    1435    70      71      FDAAAA  QFGAAA  OOOOxx
+501    4203    1       1       1       1       1       501     501     501     501     2       3       HTAAAA  RFGAAA  VVVVxx
+9401   4204    1       1       1       1       1       401     1401    4401    9401    2       3       PXAAAA  SFGAAA  AAAAxx
+5014   4205    0       2       4       14      14      14      1014    14      5014    28      29      WKAAAA  TFGAAA  HHHHxx
+9125   4206    1       1       5       5       25      125     1125    4125    9125    50      51      ZMAAAA  UFGAAA  OOOOxx
+6144   4207    0       0       4       4       44      144     144     1144    6144    88      89      ICAAAA  VFGAAA  VVVVxx
+1743   4208    1       3       3       3       43      743     1743    1743    1743    86      87      BPAAAA  WFGAAA  AAAAxx
+4316   4209    0       0       6       16      16      316     316     4316    4316    32      33      AKAAAA  XFGAAA  HHHHxx
+8212   4210    0       0       2       12      12      212     212     3212    8212    24      25      WDAAAA  YFGAAA  OOOOxx
+7344   4211    0       0       4       4       44      344     1344    2344    7344    88      89      MWAAAA  ZFGAAA  VVVVxx
+2051   4212    1       3       1       11      51      51      51      2051    2051    102     103     XAAAAA  AGGAAA  AAAAxx
+8131   4213    1       3       1       11      31      131     131     3131    8131    62      63      TAAAAA  BGGAAA  HHHHxx
+7023   4214    1       3       3       3       23      23      1023    2023    7023    46      47      DKAAAA  CGGAAA  OOOOxx
+9674   4215    0       2       4       14      74      674     1674    4674    9674    148     149     CIAAAA  DGGAAA  VVVVxx
+4984   4216    0       0       4       4       84      984     984     4984    4984    168     169     SJAAAA  EGGAAA  AAAAxx
+111    4217    1       3       1       11      11      111     111     111     111     22      23      HEAAAA  FGGAAA  HHHHxx
+2296   4218    0       0       6       16      96      296     296     2296    2296    192     193     IKAAAA  GGGAAA  OOOOxx
+5025   4219    1       1       5       5       25      25      1025    25      5025    50      51      HLAAAA  HGGAAA  VVVVxx
+1756   4220    0       0       6       16      56      756     1756    1756    1756    112     113     OPAAAA  IGGAAA  AAAAxx
+2885   4221    1       1       5       5       85      885     885     2885    2885    170     171     ZGAAAA  JGGAAA  HHHHxx
+2541   4222    1       1       1       1       41      541     541     2541    2541    82      83      TTAAAA  KGGAAA  OOOOxx
+1919   4223    1       3       9       19      19      919     1919    1919    1919    38      39      VVAAAA  LGGAAA  VVVVxx
+6496   4224    0       0       6       16      96      496     496     1496    6496    192     193     WPAAAA  MGGAAA  AAAAxx
+6103   4225    1       3       3       3       3       103     103     1103    6103    6       7       TAAAAA  NGGAAA  HHHHxx
+98     4226    0       2       8       18      98      98      98      98      98      196     197     UDAAAA  OGGAAA  OOOOxx
+3727   4227    1       3       7       7       27      727     1727    3727    3727    54      55      JNAAAA  PGGAAA  VVVVxx
+689    4228    1       1       9       9       89      689     689     689     689     178     179     NAAAAA  QGGAAA  AAAAxx
+7181   4229    1       1       1       1       81      181     1181    2181    7181    162     163     FQAAAA  RGGAAA  HHHHxx
+8447   4230    1       3       7       7       47      447     447     3447    8447    94      95      XMAAAA  SGGAAA  OOOOxx
+4569   4231    1       1       9       9       69      569     569     4569    4569    138     139     TTAAAA  TGGAAA  VVVVxx
+8844   4232    0       0       4       4       44      844     844     3844    8844    88      89      ECAAAA  UGGAAA  AAAAxx
+2436   4233    0       0       6       16      36      436     436     2436    2436    72      73      SPAAAA  VGGAAA  HHHHxx
+391    4234    1       3       1       11      91      391     391     391     391     182     183     BPAAAA  WGGAAA  OOOOxx
+3035   4235    1       3       5       15      35      35      1035    3035    3035    70      71      TMAAAA  XGGAAA  VVVVxx
+7583   4236    1       3       3       3       83      583     1583    2583    7583    166     167     RFAAAA  YGGAAA  AAAAxx
+1145   4237    1       1       5       5       45      145     1145    1145    1145    90      91      BSAAAA  ZGGAAA  HHHHxx
+93     4238    1       1       3       13      93      93      93      93      93      186     187     PDAAAA  AHGAAA  OOOOxx
+8896   4239    0       0       6       16      96      896     896     3896    8896    192     193     EEAAAA  BHGAAA  VVVVxx
+6719   4240    1       3       9       19      19      719     719     1719    6719    38      39      LYAAAA  CHGAAA  AAAAxx
+7728   4241    0       0       8       8       28      728     1728    2728    7728    56      57      GLAAAA  DHGAAA  HHHHxx
+1349   4242    1       1       9       9       49      349     1349    1349    1349    98      99      XZAAAA  EHGAAA  OOOOxx
+5349   4243    1       1       9       9       49      349     1349    349     5349    98      99      TXAAAA  FHGAAA  VVVVxx
+3040   4244    0       0       0       0       40      40      1040    3040    3040    80      81      YMAAAA  GHGAAA  AAAAxx
+2414   4245    0       2       4       14      14      414     414     2414    2414    28      29      WOAAAA  HHGAAA  HHHHxx
+5122   4246    0       2       2       2       22      122     1122    122     5122    44      45      APAAAA  IHGAAA  OOOOxx
+9553   4247    1       1       3       13      53      553     1553    4553    9553    106     107     LDAAAA  JHGAAA  VVVVxx
+5987   4248    1       3       7       7       87      987     1987    987     5987    174     175     HWAAAA  KHGAAA  AAAAxx
+5939   4249    1       3       9       19      39      939     1939    939     5939    78      79      LUAAAA  LHGAAA  HHHHxx
+3525   4250    1       1       5       5       25      525     1525    3525    3525    50      51      PFAAAA  MHGAAA  OOOOxx
+1371   4251    1       3       1       11      71      371     1371    1371    1371    142     143     TAAAAA  NHGAAA  VVVVxx
+618    4252    0       2       8       18      18      618     618     618     618     36      37      UXAAAA  OHGAAA  AAAAxx
+6529   4253    1       1       9       9       29      529     529     1529    6529    58      59      DRAAAA  PHGAAA  HHHHxx
+4010   4254    0       2       0       10      10      10      10      4010    4010    20      21      GYAAAA  QHGAAA  OOOOxx
+328    4255    0       0       8       8       28      328     328     328     328     56      57      QMAAAA  RHGAAA  VVVVxx
+6121   4256    1       1       1       1       21      121     121     1121    6121    42      43      LBAAAA  SHGAAA  AAAAxx
+3505   4257    1       1       5       5       5       505     1505    3505    3505    10      11      VEAAAA  THGAAA  HHHHxx
+2033   4258    1       1       3       13      33      33      33      2033    2033    66      67      FAAAAA  UHGAAA  OOOOxx
+4724   4259    0       0       4       4       24      724     724     4724    4724    48      49      SZAAAA  VHGAAA  VVVVxx
+8717   4260    1       1       7       17      17      717     717     3717    8717    34      35      HXAAAA  WHGAAA  AAAAxx
+5639   4261    1       3       9       19      39      639     1639    639     5639    78      79      XIAAAA  XHGAAA  HHHHxx
+3448   4262    0       0       8       8       48      448     1448    3448    3448    96      97      QCAAAA  YHGAAA  OOOOxx
+2919   4263    1       3       9       19      19      919     919     2919    2919    38      39      HIAAAA  ZHGAAA  VVVVxx
+3417   4264    1       1       7       17      17      417     1417    3417    3417    34      35      LBAAAA  AIGAAA  AAAAxx
+943    4265    1       3       3       3       43      943     943     943     943     86      87      HKAAAA  BIGAAA  HHHHxx
+775    4266    1       3       5       15      75      775     775     775     775     150     151     VDAAAA  CIGAAA  OOOOxx
+2333   4267    1       1       3       13      33      333     333     2333    2333    66      67      TLAAAA  DIGAAA  VVVVxx
+4801   4268    1       1       1       1       1       801     801     4801    4801    2       3       RCAAAA  EIGAAA  AAAAxx
+7169   4269    1       1       9       9       69      169     1169    2169    7169    138     139     TPAAAA  FIGAAA  HHHHxx
+2840   4270    0       0       0       0       40      840     840     2840    2840    80      81      GFAAAA  GIGAAA  OOOOxx
+9034   4271    0       2       4       14      34      34      1034    4034    9034    68      69      MJAAAA  HIGAAA  VVVVxx
+6154   4272    0       2       4       14      54      154     154     1154    6154    108     109     SCAAAA  IIGAAA  AAAAxx
+1412   4273    0       0       2       12      12      412     1412    1412    1412    24      25      ICAAAA  JIGAAA  HHHHxx
+2263   4274    1       3       3       3       63      263     263     2263    2263    126     127     BJAAAA  KIGAAA  OOOOxx
+7118   4275    0       2       8       18      18      118     1118    2118    7118    36      37      UNAAAA  LIGAAA  VVVVxx
+1526   4276    0       2       6       6       26      526     1526    1526    1526    52      53      SGAAAA  MIGAAA  AAAAxx
+491    4277    1       3       1       11      91      491     491     491     491     182     183     XSAAAA  NIGAAA  HHHHxx
+9732   4278    0       0       2       12      32      732     1732    4732    9732    64      65      IKAAAA  OIGAAA  OOOOxx
+7067   4279    1       3       7       7       67      67      1067    2067    7067    134     135     VLAAAA  PIGAAA  VVVVxx
+212    4280    0       0       2       12      12      212     212     212     212     24      25      EIAAAA  QIGAAA  AAAAxx
+1955   4281    1       3       5       15      55      955     1955    1955    1955    110     111     FXAAAA  RIGAAA  HHHHxx
+3303   4282    1       3       3       3       3       303     1303    3303    3303    6       7       BXAAAA  SIGAAA  OOOOxx
+2715   4283    1       3       5       15      15      715     715     2715    2715    30      31      LAAAAA  TIGAAA  VVVVxx
+8168   4284    0       0       8       8       68      168     168     3168    8168    136     137     ECAAAA  UIGAAA  AAAAxx
+6799   4285    1       3       9       19      99      799     799     1799    6799    198     199     NBAAAA  VIGAAA  HHHHxx
+5080   4286    0       0       0       0       80      80      1080    80      5080    160     161     KNAAAA  WIGAAA  OOOOxx
+4939   4287    1       3       9       19      39      939     939     4939    4939    78      79      ZHAAAA  XIGAAA  VVVVxx
+6604   4288    0       0       4       4       4       604     604     1604    6604    8       9       AUAAAA  YIGAAA  AAAAxx
+6531   4289    1       3       1       11      31      531     531     1531    6531    62      63      FRAAAA  ZIGAAA  HHHHxx
+9948   4290    0       0       8       8       48      948     1948    4948    9948    96      97      QSAAAA  AJGAAA  OOOOxx
+7923   4291    1       3       3       3       23      923     1923    2923    7923    46      47      TSAAAA  BJGAAA  VVVVxx
+9905   4292    1       1       5       5       5       905     1905    4905    9905    10      11      ZQAAAA  CJGAAA  AAAAxx
+340    4293    0       0       0       0       40      340     340     340     340     80      81      CNAAAA  DJGAAA  HHHHxx
+1721   4294    1       1       1       1       21      721     1721    1721    1721    42      43      FOAAAA  EJGAAA  OOOOxx
+9047   4295    1       3       7       7       47      47      1047    4047    9047    94      95      ZJAAAA  FJGAAA  VVVVxx
+4723   4296    1       3       3       3       23      723     723     4723    4723    46      47      RZAAAA  GJGAAA  AAAAxx
+5748   4297    0       0       8       8       48      748     1748    748     5748    96      97      CNAAAA  HJGAAA  HHHHxx
+6845   4298    1       1       5       5       45      845     845     1845    6845    90      91      HDAAAA  IJGAAA  OOOOxx
+1556   4299    0       0       6       16      56      556     1556    1556    1556    112     113     WHAAAA  JJGAAA  VVVVxx
+9505   4300    1       1       5       5       5       505     1505    4505    9505    10      11      PBAAAA  KJGAAA  AAAAxx
+3573   4301    1       1       3       13      73      573     1573    3573    3573    146     147     LHAAAA  LJGAAA  HHHHxx
+3785   4302    1       1       5       5       85      785     1785    3785    3785    170     171     PPAAAA  MJGAAA  OOOOxx
+2772   4303    0       0       2       12      72      772     772     2772    2772    144     145     QCAAAA  NJGAAA  VVVVxx
+7282   4304    0       2       2       2       82      282     1282    2282    7282    164     165     CUAAAA  OJGAAA  AAAAxx
+8106   4305    0       2       6       6       6       106     106     3106    8106    12      13      UZAAAA  PJGAAA  HHHHxx
+2847   4306    1       3       7       7       47      847     847     2847    2847    94      95      NFAAAA  QJGAAA  OOOOxx
+9803   4307    1       3       3       3       3       803     1803    4803    9803    6       7       BNAAAA  RJGAAA  VVVVxx
+7719   4308    1       3       9       19      19      719     1719    2719    7719    38      39      XKAAAA  SJGAAA  AAAAxx
+4649   4309    1       1       9       9       49      649     649     4649    4649    98      99      VWAAAA  TJGAAA  HHHHxx
+6196   4310    0       0       6       16      96      196     196     1196    6196    192     193     IEAAAA  UJGAAA  OOOOxx
+6026   4311    0       2       6       6       26      26      26      1026    6026    52      53      UXAAAA  VJGAAA  VVVVxx
+1646   4312    0       2       6       6       46      646     1646    1646    1646    92      93      ILAAAA  WJGAAA  AAAAxx
+6526   4313    0       2       6       6       26      526     526     1526    6526    52      53      ARAAAA  XJGAAA  HHHHxx
+5110   4314    0       2       0       10      10      110     1110    110     5110    20      21      OOAAAA  YJGAAA  OOOOxx
+3946   4315    0       2       6       6       46      946     1946    3946    3946    92      93      UVAAAA  ZJGAAA  VVVVxx
+445    4316    1       1       5       5       45      445     445     445     445     90      91      DRAAAA  AKGAAA  AAAAxx
+3249   4317    1       1       9       9       49      249     1249    3249    3249    98      99      ZUAAAA  BKGAAA  HHHHxx
+2501   4318    1       1       1       1       1       501     501     2501    2501    2       3       FSAAAA  CKGAAA  OOOOxx
+3243   4319    1       3       3       3       43      243     1243    3243    3243    86      87      TUAAAA  DKGAAA  VVVVxx
+4701   4320    1       1       1       1       1       701     701     4701    4701    2       3       VYAAAA  EKGAAA  AAAAxx
+472    4321    0       0       2       12      72      472     472     472     472     144     145     ESAAAA  FKGAAA  HHHHxx
+3356   4322    0       0       6       16      56      356     1356    3356    3356    112     113     CZAAAA  GKGAAA  OOOOxx
+9967   4323    1       3       7       7       67      967     1967    4967    9967    134     135     JTAAAA  HKGAAA  VVVVxx
+4292   4324    0       0       2       12      92      292     292     4292    4292    184     185     CJAAAA  IKGAAA  AAAAxx
+7005   4325    1       1       5       5       5       5       1005    2005    7005    10      11      LJAAAA  JKGAAA  HHHHxx
+6267   4326    1       3       7       7       67      267     267     1267    6267    134     135     BHAAAA  KKGAAA  OOOOxx
+6678   4327    0       2       8       18      78      678     678     1678    6678    156     157     WWAAAA  LKGAAA  VVVVxx
+6083   4328    1       3       3       3       83      83      83      1083    6083    166     167     ZZAAAA  MKGAAA  AAAAxx
+760    4329    0       0       0       0       60      760     760     760     760     120     121     GDAAAA  NKGAAA  HHHHxx
+7833   4330    1       1       3       13      33      833     1833    2833    7833    66      67      HPAAAA  OKGAAA  OOOOxx
+2877   4331    1       1       7       17      77      877     877     2877    2877    154     155     RGAAAA  PKGAAA  VVVVxx
+8810   4332    0       2       0       10      10      810     810     3810    8810    20      21      WAAAAA  QKGAAA  AAAAxx
+1560   4333    0       0       0       0       60      560     1560    1560    1560    120     121     AIAAAA  RKGAAA  HHHHxx
+1367   4334    1       3       7       7       67      367     1367    1367    1367    134     135     PAAAAA  SKGAAA  OOOOxx
+8756   4335    0       0       6       16      56      756     756     3756    8756    112     113     UYAAAA  TKGAAA  VVVVxx
+1346   4336    0       2       6       6       46      346     1346    1346    1346    92      93      UZAAAA  UKGAAA  AAAAxx
+6449   4337    1       1       9       9       49      449     449     1449    6449    98      99      BOAAAA  VKGAAA  HHHHxx
+6658   4338    0       2       8       18      58      658     658     1658    6658    116     117     CWAAAA  WKGAAA  OOOOxx
+6745   4339    1       1       5       5       45      745     745     1745    6745    90      91      LZAAAA  XKGAAA  VVVVxx
+4866   4340    0       2       6       6       66      866     866     4866    4866    132     133     EFAAAA  YKGAAA  AAAAxx
+14     4341    0       2       4       14      14      14      14      14      14      28      29      OAAAAA  ZKGAAA  HHHHxx
+4506   4342    0       2       6       6       6       506     506     4506    4506    12      13      IRAAAA  ALGAAA  OOOOxx
+1923   4343    1       3       3       3       23      923     1923    1923    1923    46      47      ZVAAAA  BLGAAA  VVVVxx
+8365   4344    1       1       5       5       65      365     365     3365    8365    130     131     TJAAAA  CLGAAA  AAAAxx
+1279   4345    1       3       9       19      79      279     1279    1279    1279    158     159     FXAAAA  DLGAAA  HHHHxx
+7666   4346    0       2       6       6       66      666     1666    2666    7666    132     133     WIAAAA  ELGAAA  OOOOxx
+7404   4347    0       0       4       4       4       404     1404    2404    7404    8       9       UYAAAA  FLGAAA  VVVVxx
+65     4348    1       1       5       5       65      65      65      65      65      130     131     NCAAAA  GLGAAA  AAAAxx
+5820   4349    0       0       0       0       20      820     1820    820     5820    40      41      WPAAAA  HLGAAA  HHHHxx
+459    4350    1       3       9       19      59      459     459     459     459     118     119     RRAAAA  ILGAAA  OOOOxx
+4787   4351    1       3       7       7       87      787     787     4787    4787    174     175     DCAAAA  JLGAAA  VVVVxx
+5631   4352    1       3       1       11      31      631     1631    631     5631    62      63      PIAAAA  KLGAAA  AAAAxx
+9717   4353    1       1       7       17      17      717     1717    4717    9717    34      35      TJAAAA  LLGAAA  HHHHxx
+2560   4354    0       0       0       0       60      560     560     2560    2560    120     121     MUAAAA  MLGAAA  OOOOxx
+8295   4355    1       3       5       15      95      295     295     3295    8295    190     191     BHAAAA  NLGAAA  VVVVxx
+3596   4356    0       0       6       16      96      596     1596    3596    3596    192     193     IIAAAA  OLGAAA  AAAAxx
+2023   4357    1       3       3       3       23      23      23      2023    2023    46      47      VZAAAA  PLGAAA  HHHHxx
+5055   4358    1       3       5       15      55      55      1055    55      5055    110     111     LMAAAA  QLGAAA  OOOOxx
+763    4359    1       3       3       3       63      763     763     763     763     126     127     JDAAAA  RLGAAA  VVVVxx
+6733   4360    1       1       3       13      33      733     733     1733    6733    66      67      ZYAAAA  SLGAAA  AAAAxx
+9266   4361    0       2       6       6       66      266     1266    4266    9266    132     133     KSAAAA  TLGAAA  HHHHxx
+4479   4362    1       3       9       19      79      479     479     4479    4479    158     159     HQAAAA  ULGAAA  OOOOxx
+1816   4363    0       0       6       16      16      816     1816    1816    1816    32      33      WRAAAA  VLGAAA  VVVVxx
+899    4364    1       3       9       19      99      899     899     899     899     198     199     PIAAAA  WLGAAA  AAAAxx
+230    4365    0       2       0       10      30      230     230     230     230     60      61      WIAAAA  XLGAAA  HHHHxx
+5362   4366    0       2       2       2       62      362     1362    362     5362    124     125     GYAAAA  YLGAAA  OOOOxx
+1609   4367    1       1       9       9       9       609     1609    1609    1609    18      19      XJAAAA  ZLGAAA  VVVVxx
+6750   4368    0       2       0       10      50      750     750     1750    6750    100     101     QZAAAA  AMGAAA  AAAAxx
+9704   4369    0       0       4       4       4       704     1704    4704    9704    8       9       GJAAAA  BMGAAA  HHHHxx
+3991   4370    1       3       1       11      91      991     1991    3991    3991    182     183     NXAAAA  CMGAAA  OOOOxx
+3959   4371    1       3       9       19      59      959     1959    3959    3959    118     119     HWAAAA  DMGAAA  VVVVxx
+9021   4372    1       1       1       1       21      21      1021    4021    9021    42      43      ZIAAAA  EMGAAA  AAAAxx
+7585   4373    1       1       5       5       85      585     1585    2585    7585    170     171     TFAAAA  FMGAAA  HHHHxx
+7083   4374    1       3       3       3       83      83      1083    2083    7083    166     167     LMAAAA  GMGAAA  OOOOxx
+7688   4375    0       0       8       8       88      688     1688    2688    7688    176     177     SJAAAA  HMGAAA  VVVVxx
+2673   4376    1       1       3       13      73      673     673     2673    2673    146     147     VYAAAA  IMGAAA  AAAAxx
+3554   4377    0       2       4       14      54      554     1554    3554    3554    108     109     SGAAAA  JMGAAA  HHHHxx
+7416   4378    0       0       6       16      16      416     1416    2416    7416    32      33      GZAAAA  KMGAAA  OOOOxx
+5672   4379    0       0       2       12      72      672     1672    672     5672    144     145     EKAAAA  LMGAAA  VVVVxx
+1355   4380    1       3       5       15      55      355     1355    1355    1355    110     111     DAAAAA  MMGAAA  AAAAxx
+3149   4381    1       1       9       9       49      149     1149    3149    3149    98      99      DRAAAA  NMGAAA  HHHHxx
+5811   4382    1       3       1       11      11      811     1811    811     5811    22      23      NPAAAA  OMGAAA  OOOOxx
+3759   4383    1       3       9       19      59      759     1759    3759    3759    118     119     POAAAA  PMGAAA  VVVVxx
+5634   4384    0       2       4       14      34      634     1634    634     5634    68      69      SIAAAA  QMGAAA  AAAAxx
+8617   4385    1       1       7       17      17      617     617     3617    8617    34      35      LTAAAA  RMGAAA  HHHHxx
+8949   4386    1       1       9       9       49      949     949     3949    8949    98      99      FGAAAA  SMGAAA  OOOOxx
+3964   4387    0       0       4       4       64      964     1964    3964    3964    128     129     MWAAAA  TMGAAA  VVVVxx
+3852   4388    0       0       2       12      52      852     1852    3852    3852    104     105     ESAAAA  UMGAAA  AAAAxx
+1555   4389    1       3       5       15      55      555     1555    1555    1555    110     111     VHAAAA  VMGAAA  HHHHxx
+6536   4390    0       0       6       16      36      536     536     1536    6536    72      73      KRAAAA  WMGAAA  OOOOxx
+4779   4391    1       3       9       19      79      779     779     4779    4779    158     159     VBAAAA  XMGAAA  VVVVxx
+1893   4392    1       1       3       13      93      893     1893    1893    1893    186     187     VUAAAA  YMGAAA  AAAAxx
+9358   4393    0       2       8       18      58      358     1358    4358    9358    116     117     YVAAAA  ZMGAAA  HHHHxx
+7438   4394    0       2       8       18      38      438     1438    2438    7438    76      77      CAAAAA  ANGAAA  OOOOxx
+941    4395    1       1       1       1       41      941     941     941     941     82      83      FKAAAA  BNGAAA  VVVVxx
+4844   4396    0       0       4       4       44      844     844     4844    4844    88      89      IEAAAA  CNGAAA  AAAAxx
+4745   4397    1       1       5       5       45      745     745     4745    4745    90      91      NAAAAA  DNGAAA  HHHHxx
+1017   4398    1       1       7       17      17      17      1017    1017    1017    34      35      DNAAAA  ENGAAA  OOOOxx
+327    4399    1       3       7       7       27      327     327     327     327     54      55      PMAAAA  FNGAAA  VVVVxx
+3152   4400    0       0       2       12      52      152     1152    3152    3152    104     105     GRAAAA  GNGAAA  AAAAxx
+4711   4401    1       3       1       11      11      711     711     4711    4711    22      23      FZAAAA  HNGAAA  HHHHxx
+141    4402    1       1       1       1       41      141     141     141     141     82      83      LFAAAA  INGAAA  OOOOxx
+1303   4403    1       3       3       3       3       303     1303    1303    1303    6       7       DYAAAA  JNGAAA  VVVVxx
+8873   4404    1       1       3       13      73      873     873     3873    8873    146     147     HDAAAA  KNGAAA  AAAAxx
+8481   4405    1       1       1       1       81      481     481     3481    8481    162     163     FOAAAA  LNGAAA  HHHHxx
+5445   4406    1       1       5       5       45      445     1445    445     5445    90      91      LBAAAA  MNGAAA  OOOOxx
+7868   4407    0       0       8       8       68      868     1868    2868    7868    136     137     QQAAAA  NNGAAA  VVVVxx
+6722   4408    0       2       2       2       22      722     722     1722    6722    44      45      OYAAAA  ONGAAA  AAAAxx
+6628   4409    0       0       8       8       28      628     628     1628    6628    56      57      YUAAAA  PNGAAA  HHHHxx
+7738   4410    0       2       8       18      38      738     1738    2738    7738    76      77      QLAAAA  QNGAAA  OOOOxx
+1018   4411    0       2       8       18      18      18      1018    1018    1018    36      37      ENAAAA  RNGAAA  VVVVxx
+3296   4412    0       0       6       16      96      296     1296    3296    3296    192     193     UWAAAA  SNGAAA  AAAAxx
+1946   4413    0       2       6       6       46      946     1946    1946    1946    92      93      WWAAAA  TNGAAA  HHHHxx
+6603   4414    1       3       3       3       3       603     603     1603    6603    6       7       ZTAAAA  UNGAAA  OOOOxx
+3562   4415    0       2       2       2       62      562     1562    3562    3562    124     125     AHAAAA  VNGAAA  VVVVxx
+1147   4416    1       3       7       7       47      147     1147    1147    1147    94      95      DSAAAA  WNGAAA  AAAAxx
+6031   4417    1       3       1       11      31      31      31      1031    6031    62      63      ZXAAAA  XNGAAA  HHHHxx
+6484   4418    0       0       4       4       84      484     484     1484    6484    168     169     KPAAAA  YNGAAA  OOOOxx
+496    4419    0       0       6       16      96      496     496     496     496     192     193     CTAAAA  ZNGAAA  VVVVxx
+4563   4420    1       3       3       3       63      563     563     4563    4563    126     127     NTAAAA  AOGAAA  AAAAxx
+1037   4421    1       1       7       17      37      37      1037    1037    1037    74      75      XNAAAA  BOGAAA  HHHHxx
+9672   4422    0       0       2       12      72      672     1672    4672    9672    144     145     AIAAAA  COGAAA  OOOOxx
+9053   4423    1       1       3       13      53      53      1053    4053    9053    106     107     FKAAAA  DOGAAA  VVVVxx
+2523   4424    1       3       3       3       23      523     523     2523    2523    46      47      BTAAAA  EOGAAA  AAAAxx
+8519   4425    1       3       9       19      19      519     519     3519    8519    38      39      RPAAAA  FOGAAA  HHHHxx
+8190   4426    0       2       0       10      90      190     190     3190    8190    180     181     ADAAAA  GOGAAA  OOOOxx
+2068   4427    0       0       8       8       68      68      68      2068    2068    136     137     OBAAAA  HOGAAA  VVVVxx
+8569   4428    1       1       9       9       69      569     569     3569    8569    138     139     PRAAAA  IOGAAA  AAAAxx
+6535   4429    1       3       5       15      35      535     535     1535    6535    70      71      JRAAAA  JOGAAA  HHHHxx
+1810   4430    0       2       0       10      10      810     1810    1810    1810    20      21      QRAAAA  KOGAAA  OOOOxx
+3099   4431    1       3       9       19      99      99      1099    3099    3099    198     199     FPAAAA  LOGAAA  VVVVxx
+7466   4432    0       2       6       6       66      466     1466    2466    7466    132     133     EBAAAA  MOGAAA  AAAAxx
+4017   4433    1       1       7       17      17      17      17      4017    4017    34      35      NYAAAA  NOGAAA  HHHHxx
+1097   4434    1       1       7       17      97      97      1097    1097    1097    194     195     FQAAAA  OOGAAA  OOOOxx
+7686   4435    0       2       6       6       86      686     1686    2686    7686    172     173     QJAAAA  POGAAA  VVVVxx
+6742   4436    0       2       2       2       42      742     742     1742    6742    84      85      IZAAAA  QOGAAA  AAAAxx
+5966   4437    0       2       6       6       66      966     1966    966     5966    132     133     MVAAAA  ROGAAA  HHHHxx
+3632   4438    0       0       2       12      32      632     1632    3632    3632    64      65      SJAAAA  SOGAAA  OOOOxx
+8837   4439    1       1       7       17      37      837     837     3837    8837    74      75      XBAAAA  TOGAAA  VVVVxx
+1667   4440    1       3       7       7       67      667     1667    1667    1667    134     135     DMAAAA  UOGAAA  AAAAxx
+8833   4441    1       1       3       13      33      833     833     3833    8833    66      67      TBAAAA  VOGAAA  HHHHxx
+9805   4442    1       1       5       5       5       805     1805    4805    9805    10      11      DNAAAA  WOGAAA  OOOOxx
+3650   4443    0       2       0       10      50      650     1650    3650    3650    100     101     KKAAAA  XOGAAA  VVVVxx
+2237   4444    1       1       7       17      37      237     237     2237    2237    74      75      BIAAAA  YOGAAA  AAAAxx
+9980   4445    0       0       0       0       80      980     1980    4980    9980    160     161     WTAAAA  ZOGAAA  HHHHxx
+2861   4446    1       1       1       1       61      861     861     2861    2861    122     123     BGAAAA  APGAAA  OOOOxx
+1334   4447    0       2       4       14      34      334     1334    1334    1334    68      69      IZAAAA  BPGAAA  VVVVxx
+842    4448    0       2       2       2       42      842     842     842     842     84      85      KGAAAA  CPGAAA  AAAAxx
+1116   4449    0       0       6       16      16      116     1116    1116    1116    32      33      YQAAAA  DPGAAA  HHHHxx
+4055   4450    1       3       5       15      55      55      55      4055    4055    110     111     ZZAAAA  EPGAAA  OOOOxx
+3842   4451    0       2       2       2       42      842     1842    3842    3842    84      85      URAAAA  FPGAAA  VVVVxx
+1886   4452    0       2       6       6       86      886     1886    1886    1886    172     173     OUAAAA  GPGAAA  AAAAxx
+8589   4453    1       1       9       9       89      589     589     3589    8589    178     179     JSAAAA  HPGAAA  HHHHxx
+5873   4454    1       1       3       13      73      873     1873    873     5873    146     147     XRAAAA  IPGAAA  OOOOxx
+7711   4455    1       3       1       11      11      711     1711    2711    7711    22      23      PKAAAA  JPGAAA  VVVVxx
+911    4456    1       3       1       11      11      911     911     911     911     22      23      BJAAAA  KPGAAA  AAAAxx
+5837   4457    1       1       7       17      37      837     1837    837     5837    74      75      NQAAAA  LPGAAA  HHHHxx
+897    4458    1       1       7       17      97      897     897     897     897     194     195     NIAAAA  MPGAAA  OOOOxx
+4299   4459    1       3       9       19      99      299     299     4299    4299    198     199     JJAAAA  NPGAAA  VVVVxx
+7774   4460    0       2       4       14      74      774     1774    2774    7774    148     149     ANAAAA  OPGAAA  AAAAxx
+7832   4461    0       0       2       12      32      832     1832    2832    7832    64      65      GPAAAA  PPGAAA  HHHHxx
+9915   4462    1       3       5       15      15      915     1915    4915    9915    30      31      JRAAAA  QPGAAA  OOOOxx
+9      4463    1       1       9       9       9       9       9       9       9       18      19      JAAAAA  RPGAAA  VVVVxx
+9675   4464    1       3       5       15      75      675     1675    4675    9675    150     151     DIAAAA  SPGAAA  AAAAxx
+7953   4465    1       1       3       13      53      953     1953    2953    7953    106     107     XTAAAA  TPGAAA  HHHHxx
+8912   4466    0       0       2       12      12      912     912     3912    8912    24      25      UEAAAA  UPGAAA  OOOOxx
+4188   4467    0       0       8       8       88      188     188     4188    4188    176     177     CFAAAA  VPGAAA  VVVVxx
+8446   4468    0       2       6       6       46      446     446     3446    8446    92      93      WMAAAA  WPGAAA  AAAAxx
+1600   4469    0       0       0       0       0       600     1600    1600    1600    0       1       OJAAAA  XPGAAA  HHHHxx
+43     4470    1       3       3       3       43      43      43      43      43      86      87      RBAAAA  YPGAAA  OOOOxx
+544    4471    0       0       4       4       44      544     544     544     544     88      89      YUAAAA  ZPGAAA  VVVVxx
+6977   4472    1       1       7       17      77      977     977     1977    6977    154     155     JIAAAA  AQGAAA  AAAAxx
+3191   4473    1       3       1       11      91      191     1191    3191    3191    182     183     TSAAAA  BQGAAA  HHHHxx
+418    4474    0       2       8       18      18      418     418     418     418     36      37      CQAAAA  CQGAAA  OOOOxx
+3142   4475    0       2       2       2       42      142     1142    3142    3142    84      85      WQAAAA  DQGAAA  VVVVxx
+5042   4476    0       2       2       2       42      42      1042    42      5042    84      85      YLAAAA  EQGAAA  AAAAxx
+2194   4477    0       2       4       14      94      194     194     2194    2194    188     189     KGAAAA  FQGAAA  HHHHxx
+2397   4478    1       1       7       17      97      397     397     2397    2397    194     195     FOAAAA  GQGAAA  OOOOxx
+4684   4479    0       0       4       4       84      684     684     4684    4684    168     169     EYAAAA  HQGAAA  VVVVxx
+34     4480    0       2       4       14      34      34      34      34      34      68      69      IBAAAA  IQGAAA  AAAAxx
+3844   4481    0       0       4       4       44      844     1844    3844    3844    88      89      WRAAAA  JQGAAA  HHHHxx
+7824   4482    0       0       4       4       24      824     1824    2824    7824    48      49      YOAAAA  KQGAAA  OOOOxx
+6177   4483    1       1       7       17      77      177     177     1177    6177    154     155     PDAAAA  LQGAAA  VVVVxx
+9657   4484    1       1       7       17      57      657     1657    4657    9657    114     115     LHAAAA  MQGAAA  AAAAxx
+4546   4485    0       2       6       6       46      546     546     4546    4546    92      93      WSAAAA  NQGAAA  HHHHxx
+599    4486    1       3       9       19      99      599     599     599     599     198     199     BXAAAA  OQGAAA  OOOOxx
+153    4487    1       1       3       13      53      153     153     153     153     106     107     XFAAAA  PQGAAA  VVVVxx
+6910   4488    0       2       0       10      10      910     910     1910    6910    20      21      UFAAAA  QQGAAA  AAAAxx
+4408   4489    0       0       8       8       8       408     408     4408    4408    16      17      ONAAAA  RQGAAA  HHHHxx
+1164   4490    0       0       4       4       64      164     1164    1164    1164    128     129     USAAAA  SQGAAA  OOOOxx
+6469   4491    1       1       9       9       69      469     469     1469    6469    138     139     VOAAAA  TQGAAA  VVVVxx
+5996   4492    0       0       6       16      96      996     1996    996     5996    192     193     QWAAAA  UQGAAA  AAAAxx
+2639   4493    1       3       9       19      39      639     639     2639    2639    78      79      NXAAAA  VQGAAA  HHHHxx
+2678   4494    0       2       8       18      78      678     678     2678    2678    156     157     AZAAAA  WQGAAA  OOOOxx
+8392   4495    0       0       2       12      92      392     392     3392    8392    184     185     UKAAAA  XQGAAA  VVVVxx
+1386   4496    0       2       6       6       86      386     1386    1386    1386    172     173     IBAAAA  YQGAAA  AAAAxx
+5125   4497    1       1       5       5       25      125     1125    125     5125    50      51      DPAAAA  ZQGAAA  HHHHxx
+8453   4498    1       1       3       13      53      453     453     3453    8453    106     107     DNAAAA  ARGAAA  OOOOxx
+2369   4499    1       1       9       9       69      369     369     2369    2369    138     139     DNAAAA  BRGAAA  VVVVxx
+1608   4500    0       0       8       8       8       608     1608    1608    1608    16      17      WJAAAA  CRGAAA  AAAAxx
+3781   4501    1       1       1       1       81      781     1781    3781    3781    162     163     LPAAAA  DRGAAA  HHHHxx
+903    4502    1       3       3       3       3       903     903     903     903     6       7       TIAAAA  ERGAAA  OOOOxx
+2099   4503    1       3       9       19      99      99      99      2099    2099    198     199     TCAAAA  FRGAAA  VVVVxx
+538    4504    0       2       8       18      38      538     538     538     538     76      77      SUAAAA  GRGAAA  AAAAxx
+9177   4505    1       1       7       17      77      177     1177    4177    9177    154     155     ZOAAAA  HRGAAA  HHHHxx
+420    4506    0       0       0       0       20      420     420     420     420     40      41      EQAAAA  IRGAAA  OOOOxx
+9080   4507    0       0       0       0       80      80      1080    4080    9080    160     161     GLAAAA  JRGAAA  VVVVxx
+2630   4508    0       2       0       10      30      630     630     2630    2630    60      61      EXAAAA  KRGAAA  AAAAxx
+5978   4509    0       2       8       18      78      978     1978    978     5978    156     157     YVAAAA  LRGAAA  HHHHxx
+9239   4510    1       3       9       19      39      239     1239    4239    9239    78      79      JRAAAA  MRGAAA  OOOOxx
+4372   4511    0       0       2       12      72      372     372     4372    4372    144     145     EMAAAA  NRGAAA  VVVVxx
+4357   4512    1       1       7       17      57      357     357     4357    4357    114     115     PLAAAA  ORGAAA  AAAAxx
+9857   4513    1       1       7       17      57      857     1857    4857    9857    114     115     DPAAAA  PRGAAA  HHHHxx
+7933   4514    1       1       3       13      33      933     1933    2933    7933    66      67      DTAAAA  QRGAAA  OOOOxx
+9574   4515    0       2       4       14      74      574     1574    4574    9574    148     149     GEAAAA  RRGAAA  VVVVxx
+8294   4516    0       2       4       14      94      294     294     3294    8294    188     189     AHAAAA  SRGAAA  AAAAxx
+627    4517    1       3       7       7       27      627     627     627     627     54      55      DYAAAA  TRGAAA  HHHHxx
+3229   4518    1       1       9       9       29      229     1229    3229    3229    58      59      FUAAAA  URGAAA  OOOOxx
+3163   4519    1       3       3       3       63      163     1163    3163    3163    126     127     RRAAAA  VRGAAA  VVVVxx
+7349   4520    1       1       9       9       49      349     1349    2349    7349    98      99      RWAAAA  WRGAAA  AAAAxx
+6889   4521    1       1       9       9       89      889     889     1889    6889    178     179     ZEAAAA  XRGAAA  HHHHxx
+2101   4522    1       1       1       1       1       101     101     2101    2101    2       3       VCAAAA  YRGAAA  OOOOxx
+6476   4523    0       0       6       16      76      476     476     1476    6476    152     153     CPAAAA  ZRGAAA  VVVVxx
+6765   4524    1       1       5       5       65      765     765     1765    6765    130     131     FAAAAA  ASGAAA  AAAAxx
+4204   4525    0       0       4       4       4       204     204     4204    4204    8       9       SFAAAA  BSGAAA  HHHHxx
+5915   4526    1       3       5       15      15      915     1915    915     5915    30      31      NTAAAA  CSGAAA  OOOOxx
+2318   4527    0       2       8       18      18      318     318     2318    2318    36      37      ELAAAA  DSGAAA  VVVVxx
+294    4528    0       2       4       14      94      294     294     294     294     188     189     ILAAAA  ESGAAA  AAAAxx
+5245   4529    1       1       5       5       45      245     1245    245     5245    90      91      TTAAAA  FSGAAA  HHHHxx
+4481   4530    1       1       1       1       81      481     481     4481    4481    162     163     JQAAAA  GSGAAA  OOOOxx
+7754   4531    0       2       4       14      54      754     1754    2754    7754    108     109     GMAAAA  HSGAAA  VVVVxx
+8494   4532    0       2       4       14      94      494     494     3494    8494    188     189     SOAAAA  ISGAAA  AAAAxx
+4014   4533    0       2       4       14      14      14      14      4014    4014    28      29      KYAAAA  JSGAAA  HHHHxx
+2197   4534    1       1       7       17      97      197     197     2197    2197    194     195     NGAAAA  KSGAAA  OOOOxx
+1297   4535    1       1       7       17      97      297     1297    1297    1297    194     195     XXAAAA  LSGAAA  VVVVxx
+1066   4536    0       2       6       6       66      66      1066    1066    1066    132     133     APAAAA  MSGAAA  AAAAxx
+5710   4537    0       2       0       10      10      710     1710    710     5710    20      21      QLAAAA  NSGAAA  HHHHxx
+4100   4538    0       0       0       0       0       100     100     4100    4100    0       1       SBAAAA  OSGAAA  OOOOxx
+7356   4539    0       0       6       16      56      356     1356    2356    7356    112     113     YWAAAA  PSGAAA  VVVVxx
+7658   4540    0       2       8       18      58      658     1658    2658    7658    116     117     OIAAAA  QSGAAA  AAAAxx
+3666   4541    0       2       6       6       66      666     1666    3666    3666    132     133     ALAAAA  RSGAAA  HHHHxx
+9713   4542    1       1       3       13      13      713     1713    4713    9713    26      27      PJAAAA  SSGAAA  OOOOxx
+691    4543    1       3       1       11      91      691     691     691     691     182     183     PAAAAA  TSGAAA  VVVVxx
+3112   4544    0       0       2       12      12      112     1112    3112    3112    24      25      SPAAAA  USGAAA  AAAAxx
+6035   4545    1       3       5       15      35      35      35      1035    6035    70      71      DYAAAA  VSGAAA  HHHHxx
+8353   4546    1       1       3       13      53      353     353     3353    8353    106     107     HJAAAA  WSGAAA  OOOOxx
+5679   4547    1       3       9       19      79      679     1679    679     5679    158     159     LKAAAA  XSGAAA  VVVVxx
+2124   4548    0       0       4       4       24      124     124     2124    2124    48      49      SDAAAA  YSGAAA  AAAAxx
+4714   4549    0       2       4       14      14      714     714     4714    4714    28      29      IZAAAA  ZSGAAA  HHHHxx
+9048   4550    0       0       8       8       48      48      1048    4048    9048    96      97      AKAAAA  ATGAAA  OOOOxx
+7692   4551    0       0       2       12      92      692     1692    2692    7692    184     185     WJAAAA  BTGAAA  VVVVxx
+4542   4552    0       2       2       2       42      542     542     4542    4542    84      85      SSAAAA  CTGAAA  AAAAxx
+8737   4553    1       1       7       17      37      737     737     3737    8737    74      75      BYAAAA  DTGAAA  HHHHxx
+4977   4554    1       1       7       17      77      977     977     4977    4977    154     155     LJAAAA  ETGAAA  OOOOxx
+9349   4555    1       1       9       9       49      349     1349    4349    9349    98      99      PVAAAA  FTGAAA  VVVVxx
+731    4556    1       3       1       11      31      731     731     731     731     62      63      DCAAAA  GTGAAA  AAAAxx
+1788   4557    0       0       8       8       88      788     1788    1788    1788    176     177     UQAAAA  HTGAAA  HHHHxx
+7830   4558    0       2       0       10      30      830     1830    2830    7830    60      61      EPAAAA  ITGAAA  OOOOxx
+3977   4559    1       1       7       17      77      977     1977    3977    3977    154     155     ZWAAAA  JTGAAA  VVVVxx
+2421   4560    1       1       1       1       21      421     421     2421    2421    42      43      DPAAAA  KTGAAA  AAAAxx
+5891   4561    1       3       1       11      91      891     1891    891     5891    182     183     PSAAAA  LTGAAA  HHHHxx
+1111   4562    1       3       1       11      11      111     1111    1111    1111    22      23      TQAAAA  MTGAAA  OOOOxx
+9224   4563    0       0       4       4       24      224     1224    4224    9224    48      49      UQAAAA  NTGAAA  VVVVxx
+9872   4564    0       0       2       12      72      872     1872    4872    9872    144     145     SPAAAA  OTGAAA  AAAAxx
+2433   4565    1       1       3       13      33      433     433     2433    2433    66      67      PPAAAA  PTGAAA  HHHHxx
+1491   4566    1       3       1       11      91      491     1491    1491    1491    182     183     JFAAAA  QTGAAA  OOOOxx
+6653   4567    1       1       3       13      53      653     653     1653    6653    106     107     XVAAAA  RTGAAA  VVVVxx
+1907   4568    1       3       7       7       7       907     1907    1907    1907    14      15      JVAAAA  STGAAA  AAAAxx
+889    4569    1       1       9       9       89      889     889     889     889     178     179     FIAAAA  TTGAAA  HHHHxx
+561    4570    1       1       1       1       61      561     561     561     561     122     123     PVAAAA  UTGAAA  OOOOxx
+7415   4571    1       3       5       15      15      415     1415    2415    7415    30      31      FZAAAA  VTGAAA  VVVVxx
+2703   4572    1       3       3       3       3       703     703     2703    2703    6       7       ZZAAAA  WTGAAA  AAAAxx
+2561   4573    1       1       1       1       61      561     561     2561    2561    122     123     NUAAAA  XTGAAA  HHHHxx
+1257   4574    1       1       7       17      57      257     1257    1257    1257    114     115     JWAAAA  YTGAAA  OOOOxx
+2390   4575    0       2       0       10      90      390     390     2390    2390    180     181     YNAAAA  ZTGAAA  VVVVxx
+3915   4576    1       3       5       15      15      915     1915    3915    3915    30      31      PUAAAA  AUGAAA  AAAAxx
+8476   4577    0       0       6       16      76      476     476     3476    8476    152     153     AOAAAA  BUGAAA  HHHHxx
+607    4578    1       3       7       7       7       607     607     607     607     14      15      JXAAAA  CUGAAA  OOOOxx
+3891   4579    1       3       1       11      91      891     1891    3891    3891    182     183     RTAAAA  DUGAAA  VVVVxx
+7269   4580    1       1       9       9       69      269     1269    2269    7269    138     139     PTAAAA  EUGAAA  AAAAxx
+9537   4581    1       1       7       17      37      537     1537    4537    9537    74      75      VCAAAA  FUGAAA  HHHHxx
+8518   4582    0       2       8       18      18      518     518     3518    8518    36      37      QPAAAA  GUGAAA  OOOOxx
+5221   4583    1       1       1       1       21      221     1221    221     5221    42      43      VSAAAA  HUGAAA  VVVVxx
+3274   4584    0       2       4       14      74      274     1274    3274    3274    148     149     YVAAAA  IUGAAA  AAAAxx
+6677   4585    1       1       7       17      77      677     677     1677    6677    154     155     VWAAAA  JUGAAA  HHHHxx
+3114   4586    0       2       4       14      14      114     1114    3114    3114    28      29      UPAAAA  KUGAAA  OOOOxx
+1966   4587    0       2       6       6       66      966     1966    1966    1966    132     133     QXAAAA  LUGAAA  VVVVxx
+5941   4588    1       1       1       1       41      941     1941    941     5941    82      83      NUAAAA  MUGAAA  AAAAxx
+9463   4589    1       3       3       3       63      463     1463    4463    9463    126     127     ZZAAAA  NUGAAA  HHHHxx
+8966   4590    0       2       6       6       66      966     966     3966    8966    132     133     WGAAAA  OUGAAA  OOOOxx
+4402   4591    0       2       2       2       2       402     402     4402    4402    4       5       INAAAA  PUGAAA  VVVVxx
+3364   4592    0       0       4       4       64      364     1364    3364    3364    128     129     KZAAAA  QUGAAA  AAAAxx
+3698   4593    0       2       8       18      98      698     1698    3698    3698    196     197     GMAAAA  RUGAAA  HHHHxx
+4651   4594    1       3       1       11      51      651     651     4651    4651    102     103     XWAAAA  SUGAAA  OOOOxx
+2127   4595    1       3       7       7       27      127     127     2127    2127    54      55      VDAAAA  TUGAAA  VVVVxx
+3614   4596    0       2       4       14      14      614     1614    3614    3614    28      29      AJAAAA  UUGAAA  AAAAxx
+5430   4597    0       2       0       10      30      430     1430    430     5430    60      61      WAAAAA  VUGAAA  HHHHxx
+3361   4598    1       1       1       1       61      361     1361    3361    3361    122     123     HZAAAA  WUGAAA  OOOOxx
+4798   4599    0       2       8       18      98      798     798     4798    4798    196     197     OCAAAA  XUGAAA  VVVVxx
+8269   4600    1       1       9       9       69      269     269     3269    8269    138     139     BGAAAA  YUGAAA  AAAAxx
+6458   4601    0       2       8       18      58      458     458     1458    6458    116     117     KOAAAA  ZUGAAA  HHHHxx
+3358   4602    0       2       8       18      58      358     1358    3358    3358    116     117     EZAAAA  AVGAAA  OOOOxx
+5898   4603    0       2       8       18      98      898     1898    898     5898    196     197     WSAAAA  BVGAAA  VVVVxx
+1880   4604    0       0       0       0       80      880     1880    1880    1880    160     161     IUAAAA  CVGAAA  AAAAxx
+782    4605    0       2       2       2       82      782     782     782     782     164     165     CEAAAA  DVGAAA  HHHHxx
+3102   4606    0       2       2       2       2       102     1102    3102    3102    4       5       IPAAAA  EVGAAA  OOOOxx
+6366   4607    0       2       6       6       66      366     366     1366    6366    132     133     WKAAAA  FVGAAA  VVVVxx
+399    4608    1       3       9       19      99      399     399     399     399     198     199     JPAAAA  GVGAAA  AAAAxx
+6773   4609    1       1       3       13      73      773     773     1773    6773    146     147     NAAAAA  HVGAAA  HHHHxx
+7942   4610    0       2       2       2       42      942     1942    2942    7942    84      85      MTAAAA  IVGAAA  OOOOxx
+6274   4611    0       2       4       14      74      274     274     1274    6274    148     149     IHAAAA  JVGAAA  VVVVxx
+7447   4612    1       3       7       7       47      447     1447    2447    7447    94      95      LAAAAA  KVGAAA  AAAAxx
+7648   4613    0       0       8       8       48      648     1648    2648    7648    96      97      EIAAAA  LVGAAA  HHHHxx
+3997   4614    1       1       7       17      97      997     1997    3997    3997    194     195     TXAAAA  MVGAAA  OOOOxx
+1759   4615    1       3       9       19      59      759     1759    1759    1759    118     119     RPAAAA  NVGAAA  VVVVxx
+1785   4616    1       1       5       5       85      785     1785    1785    1785    170     171     RQAAAA  OVGAAA  AAAAxx
+8930   4617    0       2       0       10      30      930     930     3930    8930    60      61      MFAAAA  PVGAAA  HHHHxx
+7595   4618    1       3       5       15      95      595     1595    2595    7595    190     191     DGAAAA  QVGAAA  OOOOxx
+6752   4619    0       0       2       12      52      752     752     1752    6752    104     105     SZAAAA  RVGAAA  VVVVxx
+5635   4620    1       3       5       15      35      635     1635    635     5635    70      71      TIAAAA  SVGAAA  AAAAxx
+1579   4621    1       3       9       19      79      579     1579    1579    1579    158     159     TIAAAA  TVGAAA  HHHHxx
+7743   4622    1       3       3       3       43      743     1743    2743    7743    86      87      VLAAAA  UVGAAA  OOOOxx
+5856   4623    0       0       6       16      56      856     1856    856     5856    112     113     GRAAAA  VVGAAA  VVVVxx
+7273   4624    1       1       3       13      73      273     1273    2273    7273    146     147     TTAAAA  WVGAAA  AAAAxx
+1399   4625    1       3       9       19      99      399     1399    1399    1399    198     199     VBAAAA  XVGAAA  HHHHxx
+3694   4626    0       2       4       14      94      694     1694    3694    3694    188     189     CMAAAA  YVGAAA  OOOOxx
+2782   4627    0       2       2       2       82      782     782     2782    2782    164     165     ADAAAA  ZVGAAA  VVVVxx
+6951   4628    1       3       1       11      51      951     951     1951    6951    102     103     JHAAAA  AWGAAA  AAAAxx
+6053   4629    1       1       3       13      53      53      53      1053    6053    106     107     VYAAAA  BWGAAA  HHHHxx
+1753   4630    1       1       3       13      53      753     1753    1753    1753    106     107     LPAAAA  CWGAAA  OOOOxx
+3985   4631    1       1       5       5       85      985     1985    3985    3985    170     171     HXAAAA  DWGAAA  VVVVxx
+6159   4632    1       3       9       19      59      159     159     1159    6159    118     119     XCAAAA  EWGAAA  AAAAxx
+6250   4633    0       2       0       10      50      250     250     1250    6250    100     101     KGAAAA  FWGAAA  HHHHxx
+6240   4634    0       0       0       0       40      240     240     1240    6240    80      81      AGAAAA  GWGAAA  OOOOxx
+6571   4635    1       3       1       11      71      571     571     1571    6571    142     143     TSAAAA  HWGAAA  VVVVxx
+8624   4636    0       0       4       4       24      624     624     3624    8624    48      49      STAAAA  IWGAAA  AAAAxx
+9718   4637    0       2       8       18      18      718     1718    4718    9718    36      37      UJAAAA  JWGAAA  HHHHxx
+5529   4638    1       1       9       9       29      529     1529    529     5529    58      59      REAAAA  KWGAAA  OOOOxx
+7089   4639    1       1       9       9       89      89      1089    2089    7089    178     179     RMAAAA  LWGAAA  VVVVxx
+5488   4640    0       0       8       8       88      488     1488    488     5488    176     177     CDAAAA  MWGAAA  AAAAxx
+5444   4641    0       0       4       4       44      444     1444    444     5444    88      89      KBAAAA  NWGAAA  HHHHxx
+4899   4642    1       3       9       19      99      899     899     4899    4899    198     199     LGAAAA  OWGAAA  OOOOxx
+7928   4643    0       0       8       8       28      928     1928    2928    7928    56      57      YSAAAA  PWGAAA  VVVVxx
+4736   4644    0       0       6       16      36      736     736     4736    4736    72      73      EAAAAA  QWGAAA  AAAAxx
+4317   4645    1       1       7       17      17      317     317     4317    4317    34      35      BKAAAA  RWGAAA  HHHHxx
+1174   4646    0       2       4       14      74      174     1174    1174    1174    148     149     ETAAAA  SWGAAA  OOOOxx
+6138   4647    0       2       8       18      38      138     138     1138    6138    76      77      CCAAAA  TWGAAA  VVVVxx
+3943   4648    1       3       3       3       43      943     1943    3943    3943    86      87      RVAAAA  UWGAAA  AAAAxx
+1545   4649    1       1       5       5       45      545     1545    1545    1545    90      91      LHAAAA  VWGAAA  HHHHxx
+6867   4650    1       3       7       7       67      867     867     1867    6867    134     135     DEAAAA  WWGAAA  OOOOxx
+6832   4651    0       0       2       12      32      832     832     1832    6832    64      65      UCAAAA  XWGAAA  VVVVxx
+2987   4652    1       3       7       7       87      987     987     2987    2987    174     175     XKAAAA  YWGAAA  AAAAxx
+5169   4653    1       1       9       9       69      169     1169    169     5169    138     139     VQAAAA  ZWGAAA  HHHHxx
+8998   4654    0       2       8       18      98      998     998     3998    8998    196     197     CIAAAA  AXGAAA  OOOOxx
+9347   4655    1       3       7       7       47      347     1347    4347    9347    94      95      NVAAAA  BXGAAA  VVVVxx
+4800   4656    0       0       0       0       0       800     800     4800    4800    0       1       QCAAAA  CXGAAA  AAAAxx
+4200   4657    0       0       0       0       0       200     200     4200    4200    0       1       OFAAAA  DXGAAA  HHHHxx
+4046   4658    0       2       6       6       46      46      46      4046    4046    92      93      QZAAAA  EXGAAA  OOOOxx
+7142   4659    0       2       2       2       42      142     1142    2142    7142    84      85      SOAAAA  FXGAAA  VVVVxx
+2733   4660    1       1       3       13      33      733     733     2733    2733    66      67      DBAAAA  GXGAAA  AAAAxx
+1568   4661    0       0       8       8       68      568     1568    1568    1568    136     137     IIAAAA  HXGAAA  HHHHxx
+5105   4662    1       1       5       5       5       105     1105    105     5105    10      11      JOAAAA  IXGAAA  OOOOxx
+9115   4663    1       3       5       15      15      115     1115    4115    9115    30      31      PMAAAA  JXGAAA  VVVVxx
+6475   4664    1       3       5       15      75      475     475     1475    6475    150     151     BPAAAA  KXGAAA  AAAAxx
+3796   4665    0       0       6       16      96      796     1796    3796    3796    192     193     AQAAAA  LXGAAA  HHHHxx
+5410   4666    0       2       0       10      10      410     1410    410     5410    20      21      CAAAAA  MXGAAA  OOOOxx
+4023   4667    1       3       3       3       23      23      23      4023    4023    46      47      TYAAAA  NXGAAA  VVVVxx
+8904   4668    0       0       4       4       4       904     904     3904    8904    8       9       MEAAAA  OXGAAA  AAAAxx
+450    4669    0       2       0       10      50      450     450     450     450     100     101     IRAAAA  PXGAAA  HHHHxx
+8087   4670    1       3       7       7       87      87      87      3087    8087    174     175     BZAAAA  QXGAAA  OOOOxx
+6478   4671    0       2       8       18      78      478     478     1478    6478    156     157     EPAAAA  RXGAAA  VVVVxx
+2696   4672    0       0       6       16      96      696     696     2696    2696    192     193     SZAAAA  SXGAAA  AAAAxx
+1792   4673    0       0       2       12      92      792     1792    1792    1792    184     185     YQAAAA  TXGAAA  HHHHxx
+9699   4674    1       3       9       19      99      699     1699    4699    9699    198     199     BJAAAA  UXGAAA  OOOOxx
+9160   4675    0       0       0       0       60      160     1160    4160    9160    120     121     IOAAAA  VXGAAA  VVVVxx
+9989   4676    1       1       9       9       89      989     1989    4989    9989    178     179     FUAAAA  WXGAAA  AAAAxx
+9568   4677    0       0       8       8       68      568     1568    4568    9568    136     137     AEAAAA  XXGAAA  HHHHxx
+487    4678    1       3       7       7       87      487     487     487     487     174     175     TSAAAA  YXGAAA  OOOOxx
+7863   4679    1       3       3       3       63      863     1863    2863    7863    126     127     LQAAAA  ZXGAAA  VVVVxx
+1884   4680    0       0       4       4       84      884     1884    1884    1884    168     169     MUAAAA  AYGAAA  AAAAxx
+2651   4681    1       3       1       11      51      651     651     2651    2651    102     103     ZXAAAA  BYGAAA  HHHHxx
+8285   4682    1       1       5       5       85      285     285     3285    8285    170     171     RGAAAA  CYGAAA  OOOOxx
+3927   4683    1       3       7       7       27      927     1927    3927    3927    54      55      BVAAAA  DYGAAA  VVVVxx
+4076   4684    0       0       6       16      76      76      76      4076    4076    152     153     UAAAAA  EYGAAA  AAAAxx
+6149   4685    1       1       9       9       49      149     149     1149    6149    98      99      NCAAAA  FYGAAA  HHHHxx
+6581   4686    1       1       1       1       81      581     581     1581    6581    162     163     DTAAAA  GYGAAA  OOOOxx
+8293   4687    1       1       3       13      93      293     293     3293    8293    186     187     ZGAAAA  HYGAAA  VVVVxx
+7665   4688    1       1       5       5       65      665     1665    2665    7665    130     131     VIAAAA  IYGAAA  AAAAxx
+4435   4689    1       3       5       15      35      435     435     4435    4435    70      71      POAAAA  JYGAAA  HHHHxx
+1271   4690    1       3       1       11      71      271     1271    1271    1271    142     143     XWAAAA  KYGAAA  OOOOxx
+3928   4691    0       0       8       8       28      928     1928    3928    3928    56      57      CVAAAA  LYGAAA  VVVVxx
+7045   4692    1       1       5       5       45      45      1045    2045    7045    90      91      ZKAAAA  MYGAAA  AAAAxx
+4943   4693    1       3       3       3       43      943     943     4943    4943    86      87      DIAAAA  NYGAAA  HHHHxx
+8473   4694    1       1       3       13      73      473     473     3473    8473    146     147     XNAAAA  OYGAAA  OOOOxx
+1707   4695    1       3       7       7       7       707     1707    1707    1707    14      15      RNAAAA  PYGAAA  VVVVxx
+7509   4696    1       1       9       9       9       509     1509    2509    7509    18      19      VCAAAA  QYGAAA  AAAAxx
+1593   4697    1       1       3       13      93      593     1593    1593    1593    186     187     HJAAAA  RYGAAA  HHHHxx
+9281   4698    1       1       1       1       81      281     1281    4281    9281    162     163     ZSAAAA  SYGAAA  OOOOxx
+8986   4699    0       2       6       6       86      986     986     3986    8986    172     173     QHAAAA  TYGAAA  VVVVxx
+3740   4700    0       0       0       0       40      740     1740    3740    3740    80      81      WNAAAA  UYGAAA  AAAAxx
+9265   4701    1       1       5       5       65      265     1265    4265    9265    130     131     JSAAAA  VYGAAA  HHHHxx
+1510   4702    0       2       0       10      10      510     1510    1510    1510    20      21      CGAAAA  WYGAAA  OOOOxx
+3022   4703    0       2       2       2       22      22      1022    3022    3022    44      45      GMAAAA  XYGAAA  VVVVxx
+9014   4704    0       2       4       14      14      14      1014    4014    9014    28      29      SIAAAA  YYGAAA  AAAAxx
+6816   4705    0       0       6       16      16      816     816     1816    6816    32      33      ECAAAA  ZYGAAA  HHHHxx
+5518   4706    0       2       8       18      18      518     1518    518     5518    36      37      GEAAAA  AZGAAA  OOOOxx
+4451   4707    1       3       1       11      51      451     451     4451    4451    102     103     FPAAAA  BZGAAA  VVVVxx
+8747   4708    1       3       7       7       47      747     747     3747    8747    94      95      LYAAAA  CZGAAA  AAAAxx
+4646   4709    0       2       6       6       46      646     646     4646    4646    92      93      SWAAAA  DZGAAA  HHHHxx
+7296   4710    0       0       6       16      96      296     1296    2296    7296    192     193     QUAAAA  EZGAAA  OOOOxx
+9644   4711    0       0       4       4       44      644     1644    4644    9644    88      89      YGAAAA  FZGAAA  VVVVxx
+5977   4712    1       1       7       17      77      977     1977    977     5977    154     155     XVAAAA  GZGAAA  AAAAxx
+6270   4713    0       2       0       10      70      270     270     1270    6270    140     141     EHAAAA  HZGAAA  HHHHxx
+5578   4714    0       2       8       18      78      578     1578    578     5578    156     157     OGAAAA  IZGAAA  OOOOxx
+2465   4715    1       1       5       5       65      465     465     2465    2465    130     131     VQAAAA  JZGAAA  VVVVxx
+6436   4716    0       0       6       16      36      436     436     1436    6436    72      73      ONAAAA  KZGAAA  AAAAxx
+8089   4717    1       1       9       9       89      89      89      3089    8089    178     179     DZAAAA  LZGAAA  HHHHxx
+2409   4718    1       1       9       9       9       409     409     2409    2409    18      19      ROAAAA  MZGAAA  OOOOxx
+284    4719    0       0       4       4       84      284     284     284     284     168     169     YKAAAA  NZGAAA  VVVVxx
+5576   4720    0       0       6       16      76      576     1576    576     5576    152     153     MGAAAA  OZGAAA  AAAAxx
+6534   4721    0       2       4       14      34      534     534     1534    6534    68      69      IRAAAA  PZGAAA  HHHHxx
+8848   4722    0       0       8       8       48      848     848     3848    8848    96      97      ICAAAA  QZGAAA  OOOOxx
+4305   4723    1       1       5       5       5       305     305     4305    4305    10      11      PJAAAA  RZGAAA  VVVVxx
+5574   4724    0       2       4       14      74      574     1574    574     5574    148     149     KGAAAA  SZGAAA  AAAAxx
+596    4725    0       0       6       16      96      596     596     596     596     192     193     YWAAAA  TZGAAA  HHHHxx
+1253   4726    1       1       3       13      53      253     1253    1253    1253    106     107     FWAAAA  UZGAAA  OOOOxx
+521    4727    1       1       1       1       21      521     521     521     521     42      43      BUAAAA  VZGAAA  VVVVxx
+8739   4728    1       3       9       19      39      739     739     3739    8739    78      79      DYAAAA  WZGAAA  AAAAxx
+908    4729    0       0       8       8       8       908     908     908     908     16      17      YIAAAA  XZGAAA  HHHHxx
+6937   4730    1       1       7       17      37      937     937     1937    6937    74      75      VGAAAA  YZGAAA  OOOOxx
+4515   4731    1       3       5       15      15      515     515     4515    4515    30      31      RRAAAA  ZZGAAA  VVVVxx
+8630   4732    0       2       0       10      30      630     630     3630    8630    60      61      YTAAAA  AAHAAA  AAAAxx
+7518   4733    0       2       8       18      18      518     1518    2518    7518    36      37      EDAAAA  BAHAAA  HHHHxx
+8300   4734    0       0       0       0       0       300     300     3300    8300    0       1       GHAAAA  CAHAAA  OOOOxx
+8434   4735    0       2       4       14      34      434     434     3434    8434    68      69      KMAAAA  DAHAAA  VVVVxx
+6000   4736    0       0       0       0       0       0       0       1000    6000    0       1       UWAAAA  EAHAAA  AAAAxx
+4508   4737    0       0       8       8       8       508     508     4508    4508    16      17      KRAAAA  FAHAAA  HHHHxx
+7861   4738    1       1       1       1       61      861     1861    2861    7861    122     123     JQAAAA  GAHAAA  OOOOxx
+5953   4739    1       1       3       13      53      953     1953    953     5953    106     107     ZUAAAA  HAHAAA  VVVVxx
+5063   4740    1       3       3       3       63      63      1063    63      5063    126     127     TMAAAA  IAHAAA  AAAAxx
+4501   4741    1       1       1       1       1       501     501     4501    4501    2       3       DRAAAA  JAHAAA  HHHHxx
+7092   4742    0       0       2       12      92      92      1092    2092    7092    184     185     UMAAAA  KAHAAA  OOOOxx
+4388   4743    0       0       8       8       88      388     388     4388    4388    176     177     UMAAAA  LAHAAA  VVVVxx
+1826   4744    0       2       6       6       26      826     1826    1826    1826    52      53      GSAAAA  MAHAAA  AAAAxx
+568    4745    0       0       8       8       68      568     568     568     568     136     137     WVAAAA  NAHAAA  HHHHxx
+8184   4746    0       0       4       4       84      184     184     3184    8184    168     169     UCAAAA  OAHAAA  OOOOxx
+4268   4747    0       0       8       8       68      268     268     4268    4268    136     137     EIAAAA  PAHAAA  VVVVxx
+5798   4748    0       2       8       18      98      798     1798    798     5798    196     197     APAAAA  QAHAAA  AAAAxx
+5190   4749    0       2       0       10      90      190     1190    190     5190    180     181     QRAAAA  RAHAAA  HHHHxx
+1298   4750    0       2       8       18      98      298     1298    1298    1298    196     197     YXAAAA  SAHAAA  OOOOxx
+4035   4751    1       3       5       15      35      35      35      4035    4035    70      71      FZAAAA  TAHAAA  VVVVxx
+4504   4752    0       0       4       4       4       504     504     4504    4504    8       9       GRAAAA  UAHAAA  AAAAxx
+5992   4753    0       0       2       12      92      992     1992    992     5992    184     185     MWAAAA  VAHAAA  HHHHxx
+770    4754    0       2       0       10      70      770     770     770     770     140     141     QDAAAA  WAHAAA  OOOOxx
+7502   4755    0       2       2       2       2       502     1502    2502    7502    4       5       OCAAAA  XAHAAA  VVVVxx
+824    4756    0       0       4       4       24      824     824     824     824     48      49      SFAAAA  YAHAAA  AAAAxx
+7716   4757    0       0       6       16      16      716     1716    2716    7716    32      33      UKAAAA  ZAHAAA  HHHHxx
+5749   4758    1       1       9       9       49      749     1749    749     5749    98      99      DNAAAA  ABHAAA  OOOOxx
+9814   4759    0       2       4       14      14      814     1814    4814    9814    28      29      MNAAAA  BBHAAA  VVVVxx
+350    4760    0       2       0       10      50      350     350     350     350     100     101     MNAAAA  CBHAAA  AAAAxx
+1390   4761    0       2       0       10      90      390     1390    1390    1390    180     181     MBAAAA  DBHAAA  HHHHxx
+6994   4762    0       2       4       14      94      994     994     1994    6994    188     189     AJAAAA  EBHAAA  OOOOxx
+3629   4763    1       1       9       9       29      629     1629    3629    3629    58      59      PJAAAA  FBHAAA  VVVVxx
+9937   4764    1       1       7       17      37      937     1937    4937    9937    74      75      FSAAAA  GBHAAA  AAAAxx
+5285   4765    1       1       5       5       85      285     1285    285     5285    170     171     HVAAAA  HBHAAA  HHHHxx
+3157   4766    1       1       7       17      57      157     1157    3157    3157    114     115     LRAAAA  IBHAAA  OOOOxx
+9549   4767    1       1       9       9       49      549     1549    4549    9549    98      99      HDAAAA  JBHAAA  VVVVxx
+4118   4768    0       2       8       18      18      118     118     4118    4118    36      37      KCAAAA  KBHAAA  AAAAxx
+756    4769    0       0       6       16      56      756     756     756     756     112     113     CDAAAA  LBHAAA  HHHHxx
+5964   4770    0       0       4       4       64      964     1964    964     5964    128     129     KVAAAA  MBHAAA  OOOOxx
+7701   4771    1       1       1       1       1       701     1701    2701    7701    2       3       FKAAAA  NBHAAA  VVVVxx
+1242   4772    0       2       2       2       42      242     1242    1242    1242    84      85      UVAAAA  OBHAAA  AAAAxx
+7890   4773    0       2       0       10      90      890     1890    2890    7890    180     181     MRAAAA  PBHAAA  HHHHxx
+1991   4774    1       3       1       11      91      991     1991    1991    1991    182     183     PYAAAA  QBHAAA  OOOOxx
+110    4775    0       2       0       10      10      110     110     110     110     20      21      GEAAAA  RBHAAA  VVVVxx
+9334   4776    0       2       4       14      34      334     1334    4334    9334    68      69      AVAAAA  SBHAAA  AAAAxx
+6231   4777    1       3       1       11      31      231     231     1231    6231    62      63      RFAAAA  TBHAAA  HHHHxx
+9871   4778    1       3       1       11      71      871     1871    4871    9871    142     143     RPAAAA  UBHAAA  OOOOxx
+9471   4779    1       3       1       11      71      471     1471    4471    9471    142     143     HAAAAA  VBHAAA  VVVVxx
+2697   4780    1       1       7       17      97      697     697     2697    2697    194     195     TZAAAA  WBHAAA  AAAAxx
+4761   4781    1       1       1       1       61      761     761     4761    4761    122     123     DBAAAA  XBHAAA  HHHHxx
+8493   4782    1       1       3       13      93      493     493     3493    8493    186     187     ROAAAA  YBHAAA  OOOOxx
+1045   4783    1       1       5       5       45      45      1045    1045    1045    90      91      FOAAAA  ZBHAAA  VVVVxx
+3403   4784    1       3       3       3       3       403     1403    3403    3403    6       7       XAAAAA  ACHAAA  AAAAxx
+9412   4785    0       0       2       12      12      412     1412    4412    9412    24      25      AYAAAA  BCHAAA  HHHHxx
+7652   4786    0       0       2       12      52      652     1652    2652    7652    104     105     IIAAAA  CCHAAA  OOOOxx
+5866   4787    0       2       6       6       66      866     1866    866     5866    132     133     QRAAAA  DCHAAA  VVVVxx
+6942   4788    0       2       2       2       42      942     942     1942    6942    84      85      AHAAAA  ECHAAA  AAAAxx
+9353   4789    1       1       3       13      53      353     1353    4353    9353    106     107     TVAAAA  FCHAAA  HHHHxx
+2600   4790    0       0       0       0       0       600     600     2600    2600    0       1       AWAAAA  GCHAAA  OOOOxx
+6971   4791    1       3       1       11      71      971     971     1971    6971    142     143     DIAAAA  HCHAAA  VVVVxx
+5391   4792    1       3       1       11      91      391     1391    391     5391    182     183     JZAAAA  ICHAAA  AAAAxx
+7654   4793    0       2       4       14      54      654     1654    2654    7654    108     109     KIAAAA  JCHAAA  HHHHxx
+1797   4794    1       1       7       17      97      797     1797    1797    1797    194     195     DRAAAA  KCHAAA  OOOOxx
+4530   4795    0       2       0       10      30      530     530     4530    4530    60      61      GSAAAA  LCHAAA  VVVVxx
+3130   4796    0       2       0       10      30      130     1130    3130    3130    60      61      KQAAAA  MCHAAA  AAAAxx
+9442   4797    0       2       2       2       42      442     1442    4442    9442    84      85      EZAAAA  NCHAAA  HHHHxx
+6659   4798    1       3       9       19      59      659     659     1659    6659    118     119     DWAAAA  OCHAAA  OOOOxx
+9714   4799    0       2       4       14      14      714     1714    4714    9714    28      29      QJAAAA  PCHAAA  VVVVxx
+3660   4800    0       0       0       0       60      660     1660    3660    3660    120     121     UKAAAA  QCHAAA  AAAAxx
+1906   4801    0       2       6       6       6       906     1906    1906    1906    12      13      IVAAAA  RCHAAA  HHHHxx
+7927   4802    1       3       7       7       27      927     1927    2927    7927    54      55      XSAAAA  SCHAAA  OOOOxx
+1767   4803    1       3       7       7       67      767     1767    1767    1767    134     135     ZPAAAA  TCHAAA  VVVVxx
+5523   4804    1       3       3       3       23      523     1523    523     5523    46      47      LEAAAA  UCHAAA  AAAAxx
+9289   4805    1       1       9       9       89      289     1289    4289    9289    178     179     HTAAAA  VCHAAA  HHHHxx
+2717   4806    1       1       7       17      17      717     717     2717    2717    34      35      NAAAAA  WCHAAA  OOOOxx
+4099   4807    1       3       9       19      99      99      99      4099    4099    198     199     RBAAAA  XCHAAA  VVVVxx
+4387   4808    1       3       7       7       87      387     387     4387    4387    174     175     TMAAAA  YCHAAA  AAAAxx
+8864   4809    0       0       4       4       64      864     864     3864    8864    128     129     YCAAAA  ZCHAAA  HHHHxx
+1774   4810    0       2       4       14      74      774     1774    1774    1774    148     149     GQAAAA  ADHAAA  OOOOxx
+6292   4811    0       0       2       12      92      292     292     1292    6292    184     185     AIAAAA  BDHAAA  VVVVxx
+847    4812    1       3       7       7       47      847     847     847     847     94      95      PGAAAA  CDHAAA  AAAAxx
+5954   4813    0       2       4       14      54      954     1954    954     5954    108     109     AVAAAA  DDHAAA  HHHHxx
+8032   4814    0       0       2       12      32      32      32      3032    8032    64      65      YWAAAA  EDHAAA  OOOOxx
+3295   4815    1       3       5       15      95      295     1295    3295    3295    190     191     TWAAAA  FDHAAA  VVVVxx
+8984   4816    0       0       4       4       84      984     984     3984    8984    168     169     OHAAAA  GDHAAA  AAAAxx
+7809   4817    1       1       9       9       9       809     1809    2809    7809    18      19      JOAAAA  HDHAAA  HHHHxx
+1670   4818    0       2       0       10      70      670     1670    1670    1670    140     141     GMAAAA  IDHAAA  OOOOxx
+7733   4819    1       1       3       13      33      733     1733    2733    7733    66      67      LLAAAA  JDHAAA  VVVVxx
+6187   4820    1       3       7       7       87      187     187     1187    6187    174     175     ZDAAAA  KDHAAA  AAAAxx
+9326   4821    0       2       6       6       26      326     1326    4326    9326    52      53      SUAAAA  LDHAAA  HHHHxx
+2493   4822    1       1       3       13      93      493     493     2493    2493    186     187     XRAAAA  MDHAAA  OOOOxx
+9512   4823    0       0       2       12      12      512     1512    4512    9512    24      25      WBAAAA  NDHAAA  VVVVxx
+4342   4824    0       2       2       2       42      342     342     4342    4342    84      85      ALAAAA  ODHAAA  AAAAxx
+5350   4825    0       2       0       10      50      350     1350    350     5350    100     101     UXAAAA  PDHAAA  HHHHxx
+6009   4826    1       1       9       9       9       9       9       1009    6009    18      19      DXAAAA  QDHAAA  OOOOxx
+1208   4827    0       0       8       8       8       208     1208    1208    1208    16      17      MUAAAA  RDHAAA  VVVVxx
+7014   4828    0       2       4       14      14      14      1014    2014    7014    28      29      UJAAAA  SDHAAA  AAAAxx
+2967   4829    1       3       7       7       67      967     967     2967    2967    134     135     DKAAAA  TDHAAA  HHHHxx
+5831   4830    1       3       1       11      31      831     1831    831     5831    62      63      HQAAAA  UDHAAA  OOOOxx
+3097   4831    1       1       7       17      97      97      1097    3097    3097    194     195     DPAAAA  VDHAAA  VVVVxx
+1528   4832    0       0       8       8       28      528     1528    1528    1528    56      57      UGAAAA  WDHAAA  AAAAxx
+6429   4833    1       1       9       9       29      429     429     1429    6429    58      59      HNAAAA  XDHAAA  HHHHxx
+7320   4834    0       0       0       0       20      320     1320    2320    7320    40      41      OVAAAA  YDHAAA  OOOOxx
+844    4835    0       0       4       4       44      844     844     844     844     88      89      MGAAAA  ZDHAAA  VVVVxx
+7054   4836    0       2       4       14      54      54      1054    2054    7054    108     109     ILAAAA  AEHAAA  AAAAxx
+1643   4837    1       3       3       3       43      643     1643    1643    1643    86      87      FLAAAA  BEHAAA  HHHHxx
+7626   4838    0       2       6       6       26      626     1626    2626    7626    52      53      IHAAAA  CEHAAA  OOOOxx
+8728   4839    0       0       8       8       28      728     728     3728    8728    56      57      SXAAAA  DEHAAA  VVVVxx
+8277   4840    1       1       7       17      77      277     277     3277    8277    154     155     JGAAAA  EEHAAA  AAAAxx
+189    4841    1       1       9       9       89      189     189     189     189     178     179     HHAAAA  FEHAAA  HHHHxx
+3717   4842    1       1       7       17      17      717     1717    3717    3717    34      35      ZMAAAA  GEHAAA  OOOOxx
+1020   4843    0       0       0       0       20      20      1020    1020    1020    40      41      GNAAAA  HEHAAA  VVVVxx
+9234   4844    0       2       4       14      34      234     1234    4234    9234    68      69      ERAAAA  IEHAAA  AAAAxx
+9541   4845    1       1       1       1       41      541     1541    4541    9541    82      83      ZCAAAA  JEHAAA  HHHHxx
+380    4846    0       0       0       0       80      380     380     380     380     160     161     QOAAAA  KEHAAA  OOOOxx
+397    4847    1       1       7       17      97      397     397     397     397     194     195     HPAAAA  LEHAAA  VVVVxx
+835    4848    1       3       5       15      35      835     835     835     835     70      71      DGAAAA  MEHAAA  AAAAxx
+347    4849    1       3       7       7       47      347     347     347     347     94      95      JNAAAA  NEHAAA  HHHHxx
+2490   4850    0       2       0       10      90      490     490     2490    2490    180     181     URAAAA  OEHAAA  OOOOxx
+605    4851    1       1       5       5       5       605     605     605     605     10      11      HXAAAA  PEHAAA  VVVVxx
+7960   4852    0       0       0       0       60      960     1960    2960    7960    120     121     EUAAAA  QEHAAA  AAAAxx
+9681   4853    1       1       1       1       81      681     1681    4681    9681    162     163     JIAAAA  REHAAA  HHHHxx
+5753   4854    1       1       3       13      53      753     1753    753     5753    106     107     HNAAAA  SEHAAA  OOOOxx
+1676   4855    0       0       6       16      76      676     1676    1676    1676    152     153     MMAAAA  TEHAAA  VVVVxx
+5533   4856    1       1       3       13      33      533     1533    533     5533    66      67      VEAAAA  UEHAAA  AAAAxx
+8958   4857    0       2       8       18      58      958     958     3958    8958    116     117     OGAAAA  VEHAAA  HHHHxx
+664    4858    0       0       4       4       64      664     664     664     664     128     129     OZAAAA  WEHAAA  OOOOxx
+3005   4859    1       1       5       5       5       5       1005    3005    3005    10      11      PLAAAA  XEHAAA  VVVVxx
+8576   4860    0       0       6       16      76      576     576     3576    8576    152     153     WRAAAA  YEHAAA  AAAAxx
+7304   4861    0       0       4       4       4       304     1304    2304    7304    8       9       YUAAAA  ZEHAAA  HHHHxx
+3375   4862    1       3       5       15      75      375     1375    3375    3375    150     151     VZAAAA  AFHAAA  OOOOxx
+6336   4863    0       0       6       16      36      336     336     1336    6336    72      73      SJAAAA  BFHAAA  VVVVxx
+1392   4864    0       0       2       12      92      392     1392    1392    1392    184     185     OBAAAA  CFHAAA  AAAAxx
+2925   4865    1       1       5       5       25      925     925     2925    2925    50      51      NIAAAA  DFHAAA  HHHHxx
+1217   4866    1       1       7       17      17      217     1217    1217    1217    34      35      VUAAAA  EFHAAA  OOOOxx
+3714   4867    0       2       4       14      14      714     1714    3714    3714    28      29      WMAAAA  FFHAAA  VVVVxx
+2120   4868    0       0       0       0       20      120     120     2120    2120    40      41      ODAAAA  GFHAAA  AAAAxx
+2845   4869    1       1       5       5       45      845     845     2845    2845    90      91      LFAAAA  HFHAAA  HHHHxx
+3865   4870    1       1       5       5       65      865     1865    3865    3865    130     131     RSAAAA  IFHAAA  OOOOxx
+124    4871    0       0       4       4       24      124     124     124     124     48      49      UEAAAA  JFHAAA  VVVVxx
+865    4872    1       1       5       5       65      865     865     865     865     130     131     HHAAAA  KFHAAA  AAAAxx
+9361   4873    1       1       1       1       61      361     1361    4361    9361    122     123     BWAAAA  LFHAAA  HHHHxx
+6338   4874    0       2       8       18      38      338     338     1338    6338    76      77      UJAAAA  MFHAAA  OOOOxx
+7330   4875    0       2       0       10      30      330     1330    2330    7330    60      61      YVAAAA  NFHAAA  VVVVxx
+513    4876    1       1       3       13      13      513     513     513     513     26      27      TTAAAA  OFHAAA  AAAAxx
+5001   4877    1       1       1       1       1       1       1001    1       5001    2       3       JKAAAA  PFHAAA  HHHHxx
+549    4878    1       1       9       9       49      549     549     549     549     98      99      DVAAAA  QFHAAA  OOOOxx
+1808   4879    0       0       8       8       8       808     1808    1808    1808    16      17      ORAAAA  RFHAAA  VVVVxx
+7168   4880    0       0       8       8       68      168     1168    2168    7168    136     137     SPAAAA  SFHAAA  AAAAxx
+9878   4881    0       2       8       18      78      878     1878    4878    9878    156     157     YPAAAA  TFHAAA  HHHHxx
+233    4882    1       1       3       13      33      233     233     233     233     66      67      ZIAAAA  UFHAAA  OOOOxx
+4262   4883    0       2       2       2       62      262     262     4262    4262    124     125     YHAAAA  VFHAAA  VVVVxx
+7998   4884    0       2       8       18      98      998     1998    2998    7998    196     197     QVAAAA  WFHAAA  AAAAxx
+2419   4885    1       3       9       19      19      419     419     2419    2419    38      39      BPAAAA  XFHAAA  HHHHxx
+9960   4886    0       0       0       0       60      960     1960    4960    9960    120     121     CTAAAA  YFHAAA  OOOOxx
+3523   4887    1       3       3       3       23      523     1523    3523    3523    46      47      NFAAAA  ZFHAAA  VVVVxx
+5440   4888    0       0       0       0       40      440     1440    440     5440    80      81      GBAAAA  AGHAAA  AAAAxx
+3030   4889    0       2       0       10      30      30      1030    3030    3030    60      61      OMAAAA  BGHAAA  HHHHxx
+2745   4890    1       1       5       5       45      745     745     2745    2745    90      91      PBAAAA  CGHAAA  OOOOxx
+7175   4891    1       3       5       15      75      175     1175    2175    7175    150     151     ZPAAAA  DGHAAA  VVVVxx
+640    4892    0       0       0       0       40      640     640     640     640     80      81      QYAAAA  EGHAAA  AAAAxx
+1798   4893    0       2       8       18      98      798     1798    1798    1798    196     197     ERAAAA  FGHAAA  HHHHxx
+7499   4894    1       3       9       19      99      499     1499    2499    7499    198     199     LCAAAA  GGHAAA  OOOOxx
+1924   4895    0       0       4       4       24      924     1924    1924    1924    48      49      AWAAAA  HGHAAA  VVVVxx
+1327   4896    1       3       7       7       27      327     1327    1327    1327    54      55      BZAAAA  IGHAAA  AAAAxx
+73     4897    1       1       3       13      73      73      73      73      73      146     147     VCAAAA  JGHAAA  HHHHxx
+9558   4898    0       2       8       18      58      558     1558    4558    9558    116     117     QDAAAA  KGHAAA  OOOOxx
+818    4899    0       2       8       18      18      818     818     818     818     36      37      MFAAAA  LGHAAA  VVVVxx
+9916   4900    0       0       6       16      16      916     1916    4916    9916    32      33      KRAAAA  MGHAAA  AAAAxx
+2978   4901    0       2       8       18      78      978     978     2978    2978    156     157     OKAAAA  NGHAAA  HHHHxx
+8469   4902    1       1       9       9       69      469     469     3469    8469    138     139     TNAAAA  OGHAAA  OOOOxx
+9845   4903    1       1       5       5       45      845     1845    4845    9845    90      91      ROAAAA  PGHAAA  VVVVxx
+2326   4904    0       2       6       6       26      326     326     2326    2326    52      53      MLAAAA  QGHAAA  AAAAxx
+4032   4905    0       0       2       12      32      32      32      4032    4032    64      65      CZAAAA  RGHAAA  HHHHxx
+5604   4906    0       0       4       4       4       604     1604    604     5604    8       9       OHAAAA  SGHAAA  OOOOxx
+9610   4907    0       2       0       10      10      610     1610    4610    9610    20      21      QFAAAA  TGHAAA  VVVVxx
+5101   4908    1       1       1       1       1       101     1101    101     5101    2       3       FOAAAA  UGHAAA  AAAAxx
+7246   4909    0       2       6       6       46      246     1246    2246    7246    92      93      SSAAAA  VGHAAA  HHHHxx
+1292   4910    0       0       2       12      92      292     1292    1292    1292    184     185     SXAAAA  WGHAAA  OOOOxx
+6235   4911    1       3       5       15      35      235     235     1235    6235    70      71      VFAAAA  XGHAAA  VVVVxx
+1733   4912    1       1       3       13      33      733     1733    1733    1733    66      67      ROAAAA  YGHAAA  AAAAxx
+4647   4913    1       3       7       7       47      647     647     4647    4647    94      95      TWAAAA  ZGHAAA  HHHHxx
+258    4914    0       2       8       18      58      258     258     258     258     116     117     YJAAAA  AHHAAA  OOOOxx
+8438   4915    0       2       8       18      38      438     438     3438    8438    76      77      OMAAAA  BHHAAA  VVVVxx
+7869   4916    1       1       9       9       69      869     1869    2869    7869    138     139     RQAAAA  CHHAAA  AAAAxx
+9691   4917    1       3       1       11      91      691     1691    4691    9691    182     183     TIAAAA  DHHAAA  HHHHxx
+5422   4918    0       2       2       2       22      422     1422    422     5422    44      45      OAAAAA  EHHAAA  OOOOxx
+9630   4919    0       2       0       10      30      630     1630    4630    9630    60      61      KGAAAA  FHHAAA  VVVVxx
+4439   4920    1       3       9       19      39      439     439     4439    4439    78      79      TOAAAA  GHHAAA  AAAAxx
+3140   4921    0       0       0       0       40      140     1140    3140    3140    80      81      UQAAAA  HHHAAA  HHHHxx
+9111   4922    1       3       1       11      11      111     1111    4111    9111    22      23      LMAAAA  IHHAAA  OOOOxx
+4606   4923    0       2       6       6       6       606     606     4606    4606    12      13      EVAAAA  JHHAAA  VVVVxx
+8620   4924    0       0       0       0       20      620     620     3620    8620    40      41      OTAAAA  KHHAAA  AAAAxx
+7849   4925    1       1       9       9       49      849     1849    2849    7849    98      99      XPAAAA  LHHAAA  HHHHxx
+346    4926    0       2       6       6       46      346     346     346     346     92      93      INAAAA  MHHAAA  OOOOxx
+9528   4927    0       0       8       8       28      528     1528    4528    9528    56      57      MCAAAA  NHHAAA  VVVVxx
+1811   4928    1       3       1       11      11      811     1811    1811    1811    22      23      RRAAAA  OHHAAA  AAAAxx
+6068   4929    0       0       8       8       68      68      68      1068    6068    136     137     KZAAAA  PHHAAA  HHHHxx
+6260   4930    0       0       0       0       60      260     260     1260    6260    120     121     UGAAAA  QHHAAA  OOOOxx
+5909   4931    1       1       9       9       9       909     1909    909     5909    18      19      HTAAAA  RHHAAA  VVVVxx
+4518   4932    0       2       8       18      18      518     518     4518    4518    36      37      URAAAA  SHHAAA  AAAAxx
+7530   4933    0       2       0       10      30      530     1530    2530    7530    60      61      QDAAAA  THHAAA  HHHHxx
+3900   4934    0       0       0       0       0       900     1900    3900    3900    0       1       AUAAAA  UHHAAA  OOOOxx
+3969   4935    1       1       9       9       69      969     1969    3969    3969    138     139     RWAAAA  VHHAAA  VVVVxx
+8690   4936    0       2       0       10      90      690     690     3690    8690    180     181     GWAAAA  WHHAAA  AAAAxx
+5532   4937    0       0       2       12      32      532     1532    532     5532    64      65      UEAAAA  XHHAAA  HHHHxx
+5989   4938    1       1       9       9       89      989     1989    989     5989    178     179     JWAAAA  YHHAAA  OOOOxx
+1870   4939    0       2       0       10      70      870     1870    1870    1870    140     141     YTAAAA  ZHHAAA  VVVVxx
+1113   4940    1       1       3       13      13      113     1113    1113    1113    26      27      VQAAAA  AIHAAA  AAAAxx
+5155   4941    1       3       5       15      55      155     1155    155     5155    110     111     HQAAAA  BIHAAA  HHHHxx
+7460   4942    0       0       0       0       60      460     1460    2460    7460    120     121     YAAAAA  CIHAAA  OOOOxx
+6217   4943    1       1       7       17      17      217     217     1217    6217    34      35      DFAAAA  DIHAAA  VVVVxx
+8333   4944    1       1       3       13      33      333     333     3333    8333    66      67      NIAAAA  EIHAAA  AAAAxx
+6341   4945    1       1       1       1       41      341     341     1341    6341    82      83      XJAAAA  FIHAAA  HHHHxx
+6230   4946    0       2       0       10      30      230     230     1230    6230    60      61      QFAAAA  GIHAAA  OOOOxx
+6902   4947    0       2       2       2       2       902     902     1902    6902    4       5       MFAAAA  HIHAAA  VVVVxx
+670    4948    0       2       0       10      70      670     670     670     670     140     141     UZAAAA  IIHAAA  AAAAxx
+805    4949    1       1       5       5       5       805     805     805     805     10      11      ZEAAAA  JIHAAA  HHHHxx
+1340   4950    0       0       0       0       40      340     1340    1340    1340    80      81      OZAAAA  KIHAAA  OOOOxx
+8649   4951    1       1       9       9       49      649     649     3649    8649    98      99      RUAAAA  LIHAAA  VVVVxx
+3887   4952    1       3       7       7       87      887     1887    3887    3887    174     175     NTAAAA  MIHAAA  AAAAxx
+5400   4953    0       0       0       0       0       400     1400    400     5400    0       1       SZAAAA  NIHAAA  HHHHxx
+4354   4954    0       2       4       14      54      354     354     4354    4354    108     109     MLAAAA  OIHAAA  OOOOxx
+950    4955    0       2       0       10      50      950     950     950     950     100     101     OKAAAA  PIHAAA  VVVVxx
+1544   4956    0       0       4       4       44      544     1544    1544    1544    88      89      KHAAAA  QIHAAA  AAAAxx
+3898   4957    0       2       8       18      98      898     1898    3898    3898    196     197     YTAAAA  RIHAAA  HHHHxx
+8038   4958    0       2       8       18      38      38      38      3038    8038    76      77      EXAAAA  SIHAAA  OOOOxx
+1095   4959    1       3       5       15      95      95      1095    1095    1095    190     191     DQAAAA  TIHAAA  VVVVxx
+1748   4960    0       0       8       8       48      748     1748    1748    1748    96      97      GPAAAA  UIHAAA  AAAAxx
+9154   4961    0       2       4       14      54      154     1154    4154    9154    108     109     COAAAA  VIHAAA  HHHHxx
+2182   4962    0       2       2       2       82      182     182     2182    2182    164     165     YFAAAA  WIHAAA  OOOOxx
+6797   4963    1       1       7       17      97      797     797     1797    6797    194     195     LBAAAA  XIHAAA  VVVVxx
+9149   4964    1       1       9       9       49      149     1149    4149    9149    98      99      XNAAAA  YIHAAA  AAAAxx
+7351   4965    1       3       1       11      51      351     1351    2351    7351    102     103     TWAAAA  ZIHAAA  HHHHxx
+2820   4966    0       0       0       0       20      820     820     2820    2820    40      41      MEAAAA  AJHAAA  OOOOxx
+9696   4967    0       0       6       16      96      696     1696    4696    9696    192     193     YIAAAA  BJHAAA  VVVVxx
+253    4968    1       1       3       13      53      253     253     253     253     106     107     TJAAAA  CJHAAA  AAAAxx
+3600   4969    0       0       0       0       0       600     1600    3600    3600    0       1       MIAAAA  DJHAAA  HHHHxx
+3892   4970    0       0       2       12      92      892     1892    3892    3892    184     185     STAAAA  EJHAAA  OOOOxx
+231    4971    1       3       1       11      31      231     231     231     231     62      63      XIAAAA  FJHAAA  VVVVxx
+8331   4972    1       3       1       11      31      331     331     3331    8331    62      63      LIAAAA  GJHAAA  AAAAxx
+403    4973    1       3       3       3       3       403     403     403     403     6       7       NPAAAA  HJHAAA  HHHHxx
+8642   4974    0       2       2       2       42      642     642     3642    8642    84      85      KUAAAA  IJHAAA  OOOOxx
+3118   4975    0       2       8       18      18      118     1118    3118    3118    36      37      YPAAAA  JJHAAA  VVVVxx
+3835   4976    1       3       5       15      35      835     1835    3835    3835    70      71      NRAAAA  KJHAAA  AAAAxx
+1117   4977    1       1       7       17      17      117     1117    1117    1117    34      35      ZQAAAA  LJHAAA  HHHHxx
+7024   4978    0       0       4       4       24      24      1024    2024    7024    48      49      EKAAAA  MJHAAA  OOOOxx
+2636   4979    0       0       6       16      36      636     636     2636    2636    72      73      KXAAAA  NJHAAA  VVVVxx
+3778   4980    0       2       8       18      78      778     1778    3778    3778    156     157     IPAAAA  OJHAAA  AAAAxx
+2003   4981    1       3       3       3       3       3       3       2003    2003    6       7       BZAAAA  PJHAAA  HHHHxx
+5717   4982    1       1       7       17      17      717     1717    717     5717    34      35      XLAAAA  QJHAAA  OOOOxx
+4869   4983    1       1       9       9       69      869     869     4869    4869    138     139     HFAAAA  RJHAAA  VVVVxx
+8921   4984    1       1       1       1       21      921     921     3921    8921    42      43      DFAAAA  SJHAAA  AAAAxx
+888    4985    0       0       8       8       88      888     888     888     888     176     177     EIAAAA  TJHAAA  HHHHxx
+7599   4986    1       3       9       19      99      599     1599    2599    7599    198     199     HGAAAA  UJHAAA  OOOOxx
+8621   4987    1       1       1       1       21      621     621     3621    8621    42      43      PTAAAA  VJHAAA  VVVVxx
+811    4988    1       3       1       11      11      811     811     811     811     22      23      FFAAAA  WJHAAA  AAAAxx
+9147   4989    1       3       7       7       47      147     1147    4147    9147    94      95      VNAAAA  XJHAAA  HHHHxx
+1413   4990    1       1       3       13      13      413     1413    1413    1413    26      27      JCAAAA  YJHAAA  OOOOxx
+5232   4991    0       0       2       12      32      232     1232    232     5232    64      65      GTAAAA  ZJHAAA  VVVVxx
+5912   4992    0       0       2       12      12      912     1912    912     5912    24      25      KTAAAA  AKHAAA  AAAAxx
+3418   4993    0       2       8       18      18      418     1418    3418    3418    36      37      MBAAAA  BKHAAA  HHHHxx
+3912   4994    0       0       2       12      12      912     1912    3912    3912    24      25      MUAAAA  CKHAAA  OOOOxx
+9576   4995    0       0       6       16      76      576     1576    4576    9576    152     153     IEAAAA  DKHAAA  VVVVxx
+4225   4996    1       1       5       5       25      225     225     4225    4225    50      51      NGAAAA  EKHAAA  AAAAxx
+8222   4997    0       2       2       2       22      222     222     3222    8222    44      45      GEAAAA  FKHAAA  HHHHxx
+7013   4998    1       1       3       13      13      13      1013    2013    7013    26      27      TJAAAA  GKHAAA  OOOOxx
+7037   4999    1       1       7       17      37      37      1037    2037    7037    74      75      RKAAAA  HKHAAA  VVVVxx
+1205   5000    1       1       5       5       5       205     1205    1205    1205    10      11      JUAAAA  IKHAAA  AAAAxx
+8114   5001    0       2       4       14      14      114     114     3114    8114    28      29      CAAAAA  JKHAAA  HHHHxx
+6585   5002    1       1       5       5       85      585     585     1585    6585    170     171     HTAAAA  KKHAAA  OOOOxx
+155    5003    1       3       5       15      55      155     155     155     155     110     111     ZFAAAA  LKHAAA  VVVVxx
+2841   5004    1       1       1       1       41      841     841     2841    2841    82      83      HFAAAA  MKHAAA  AAAAxx
+1996   5005    0       0       6       16      96      996     1996    1996    1996    192     193     UYAAAA  NKHAAA  HHHHxx
+4948   5006    0       0       8       8       48      948     948     4948    4948    96      97      IIAAAA  OKHAAA  OOOOxx
+3304   5007    0       0       4       4       4       304     1304    3304    3304    8       9       CXAAAA  PKHAAA  VVVVxx
+5684   5008    0       0       4       4       84      684     1684    684     5684    168     169     QKAAAA  QKHAAA  AAAAxx
+6962   5009    0       2       2       2       62      962     962     1962    6962    124     125     UHAAAA  RKHAAA  HHHHxx
+8691   5010    1       3       1       11      91      691     691     3691    8691    182     183     HWAAAA  SKHAAA  OOOOxx
+8501   5011    1       1       1       1       1       501     501     3501    8501    2       3       ZOAAAA  TKHAAA  VVVVxx
+4783   5012    1       3       3       3       83      783     783     4783    4783    166     167     ZBAAAA  UKHAAA  AAAAxx
+3762   5013    0       2       2       2       62      762     1762    3762    3762    124     125     SOAAAA  VKHAAA  HHHHxx
+4534   5014    0       2       4       14      34      534     534     4534    4534    68      69      KSAAAA  WKHAAA  OOOOxx
+4999   5015    1       3       9       19      99      999     999     4999    4999    198     199     HKAAAA  XKHAAA  VVVVxx
+4618   5016    0       2       8       18      18      618     618     4618    4618    36      37      QVAAAA  YKHAAA  AAAAxx
+4220   5017    0       0       0       0       20      220     220     4220    4220    40      41      IGAAAA  ZKHAAA  HHHHxx
+3384   5018    0       0       4       4       84      384     1384    3384    3384    168     169     EAAAAA  ALHAAA  OOOOxx
+3036   5019    0       0       6       16      36      36      1036    3036    3036    72      73      UMAAAA  BLHAAA  VVVVxx
+545    5020    1       1       5       5       45      545     545     545     545     90      91      ZUAAAA  CLHAAA  AAAAxx
+9946   5021    0       2       6       6       46      946     1946    4946    9946    92      93      OSAAAA  DLHAAA  HHHHxx
+1985   5022    1       1       5       5       85      985     1985    1985    1985    170     171     JYAAAA  ELHAAA  OOOOxx
+2310   5023    0       2       0       10      10      310     310     2310    2310    20      21      WKAAAA  FLHAAA  VVVVxx
+6563   5024    1       3       3       3       63      563     563     1563    6563    126     127     LSAAAA  GLHAAA  AAAAxx
+4886   5025    0       2       6       6       86      886     886     4886    4886    172     173     YFAAAA  HLHAAA  HHHHxx
+9359   5026    1       3       9       19      59      359     1359    4359    9359    118     119     ZVAAAA  ILHAAA  OOOOxx
+400    5027    0       0       0       0       0       400     400     400     400     0       1       KPAAAA  JLHAAA  VVVVxx
+9742   5028    0       2       2       2       42      742     1742    4742    9742    84      85      SKAAAA  KLHAAA  AAAAxx
+6736   5029    0       0       6       16      36      736     736     1736    6736    72      73      CZAAAA  LLHAAA  HHHHxx
+8166   5030    0       2       6       6       66      166     166     3166    8166    132     133     CCAAAA  MLHAAA  OOOOxx
+861    5031    1       1       1       1       61      861     861     861     861     122     123     DHAAAA  NLHAAA  VVVVxx
+7492   5032    0       0       2       12      92      492     1492    2492    7492    184     185     ECAAAA  OLHAAA  AAAAxx
+1155   5033    1       3       5       15      55      155     1155    1155    1155    110     111     LSAAAA  PLHAAA  HHHHxx
+9769   5034    1       1       9       9       69      769     1769    4769    9769    138     139     TLAAAA  QLHAAA  OOOOxx
+6843   5035    1       3       3       3       43      843     843     1843    6843    86      87      FDAAAA  RLHAAA  VVVVxx
+5625   5036    1       1       5       5       25      625     1625    625     5625    50      51      JIAAAA  SLHAAA  AAAAxx
+1910   5037    0       2       0       10      10      910     1910    1910    1910    20      21      MVAAAA  TLHAAA  HHHHxx
+9796   5038    0       0       6       16      96      796     1796    4796    9796    192     193     UMAAAA  ULHAAA  OOOOxx
+6950   5039    0       2       0       10      50      950     950     1950    6950    100     101     IHAAAA  VLHAAA  VVVVxx
+3084   5040    0       0       4       4       84      84      1084    3084    3084    168     169     QOAAAA  WLHAAA  AAAAxx
+2959   5041    1       3       9       19      59      959     959     2959    2959    118     119     VJAAAA  XLHAAA  HHHHxx
+2093   5042    1       1       3       13      93      93      93      2093    2093    186     187     NCAAAA  YLHAAA  OOOOxx
+2738   5043    0       2       8       18      38      738     738     2738    2738    76      77      IBAAAA  ZLHAAA  VVVVxx
+6406   5044    0       2       6       6       6       406     406     1406    6406    12      13      KMAAAA  AMHAAA  AAAAxx
+9082   5045    0       2       2       2       82      82      1082    4082    9082    164     165     ILAAAA  BMHAAA  HHHHxx
+8568   5046    0       0       8       8       68      568     568     3568    8568    136     137     ORAAAA  CMHAAA  OOOOxx
+3566   5047    0       2       6       6       66      566     1566    3566    3566    132     133     EHAAAA  DMHAAA  VVVVxx
+3016   5048    0       0       6       16      16      16      1016    3016    3016    32      33      AMAAAA  EMHAAA  AAAAxx
+1207   5049    1       3       7       7       7       207     1207    1207    1207    14      15      LUAAAA  FMHAAA  HHHHxx
+4045   5050    1       1       5       5       45      45      45      4045    4045    90      91      PZAAAA  GMHAAA  OOOOxx
+4173   5051    1       1       3       13      73      173     173     4173    4173    146     147     NEAAAA  HMHAAA  VVVVxx
+3939   5052    1       3       9       19      39      939     1939    3939    3939    78      79      NVAAAA  IMHAAA  AAAAxx
+9683   5053    1       3       3       3       83      683     1683    4683    9683    166     167     LIAAAA  JMHAAA  HHHHxx
+1684   5054    0       0       4       4       84      684     1684    1684    1684    168     169     UMAAAA  KMHAAA  OOOOxx
+9271   5055    1       3       1       11      71      271     1271    4271    9271    142     143     PSAAAA  LMHAAA  VVVVxx
+9317   5056    1       1       7       17      17      317     1317    4317    9317    34      35      JUAAAA  MMHAAA  AAAAxx
+5793   5057    1       1       3       13      93      793     1793    793     5793    186     187     VOAAAA  NMHAAA  HHHHxx
+352    5058    0       0       2       12      52      352     352     352     352     104     105     ONAAAA  OMHAAA  OOOOxx
+7328   5059    0       0       8       8       28      328     1328    2328    7328    56      57      WVAAAA  PMHAAA  VVVVxx
+4582   5060    0       2       2       2       82      582     582     4582    4582    164     165     GUAAAA  QMHAAA  AAAAxx
+7413   5061    1       1       3       13      13      413     1413    2413    7413    26      27      DZAAAA  RMHAAA  HHHHxx
+6772   5062    0       0       2       12      72      772     772     1772    6772    144     145     MAAAAA  SMHAAA  OOOOxx
+4973   5063    1       1       3       13      73      973     973     4973    4973    146     147     HJAAAA  TMHAAA  VVVVxx
+7480   5064    0       0       0       0       80      480     1480    2480    7480    160     161     SBAAAA  UMHAAA  AAAAxx
+5555   5065    1       3       5       15      55      555     1555    555     5555    110     111     RFAAAA  VMHAAA  HHHHxx
+4227   5066    1       3       7       7       27      227     227     4227    4227    54      55      PGAAAA  WMHAAA  OOOOxx
+4153   5067    1       1       3       13      53      153     153     4153    4153    106     107     TDAAAA  XMHAAA  VVVVxx
+4601   5068    1       1       1       1       1       601     601     4601    4601    2       3       ZUAAAA  YMHAAA  AAAAxx
+3782   5069    0       2       2       2       82      782     1782    3782    3782    164     165     MPAAAA  ZMHAAA  HHHHxx
+3872   5070    0       0       2       12      72      872     1872    3872    3872    144     145     YSAAAA  ANHAAA  OOOOxx
+893    5071    1       1       3       13      93      893     893     893     893     186     187     JIAAAA  BNHAAA  VVVVxx
+2430   5072    0       2       0       10      30      430     430     2430    2430    60      61      MPAAAA  CNHAAA  AAAAxx
+2591   5073    1       3       1       11      91      591     591     2591    2591    182     183     RVAAAA  DNHAAA  HHHHxx
+264    5074    0       0       4       4       64      264     264     264     264     128     129     EKAAAA  ENHAAA  OOOOxx
+6238   5075    0       2       8       18      38      238     238     1238    6238    76      77      YFAAAA  FNHAAA  VVVVxx
+633    5076    1       1       3       13      33      633     633     633     633     66      67      JYAAAA  GNHAAA  AAAAxx
+1029   5077    1       1       9       9       29      29      1029    1029    1029    58      59      PNAAAA  HNHAAA  HHHHxx
+5934   5078    0       2       4       14      34      934     1934    934     5934    68      69      GUAAAA  INHAAA  OOOOxx
+8694   5079    0       2       4       14      94      694     694     3694    8694    188     189     KWAAAA  JNHAAA  VVVVxx
+7401   5080    1       1       1       1       1       401     1401    2401    7401    2       3       RYAAAA  KNHAAA  AAAAxx
+1165   5081    1       1       5       5       65      165     1165    1165    1165    130     131     VSAAAA  LNHAAA  HHHHxx
+9438   5082    0       2       8       18      38      438     1438    4438    9438    76      77      AZAAAA  MNHAAA  OOOOxx
+4790   5083    0       2       0       10      90      790     790     4790    4790    180     181     GCAAAA  NNHAAA  VVVVxx
+4531   5084    1       3       1       11      31      531     531     4531    4531    62      63      HSAAAA  ONHAAA  AAAAxx
+6099   5085    1       3       9       19      99      99      99      1099    6099    198     199     PAAAAA  PNHAAA  HHHHxx
+8236   5086    0       0       6       16      36      236     236     3236    8236    72      73      UEAAAA  QNHAAA  OOOOxx
+8551   5087    1       3       1       11      51      551     551     3551    8551    102     103     XQAAAA  RNHAAA  VVVVxx
+3128   5088    0       0       8       8       28      128     1128    3128    3128    56      57      IQAAAA  SNHAAA  AAAAxx
+3504   5089    0       0       4       4       4       504     1504    3504    3504    8       9       UEAAAA  TNHAAA  HHHHxx
+9071   5090    1       3       1       11      71      71      1071    4071    9071    142     143     XKAAAA  UNHAAA  OOOOxx
+5930   5091    0       2       0       10      30      930     1930    930     5930    60      61      CUAAAA  VNHAAA  VVVVxx
+6825   5092    1       1       5       5       25      825     825     1825    6825    50      51      NCAAAA  WNHAAA  AAAAxx
+2218   5093    0       2       8       18      18      218     218     2218    2218    36      37      IHAAAA  XNHAAA  HHHHxx
+3604   5094    0       0       4       4       4       604     1604    3604    3604    8       9       QIAAAA  YNHAAA  OOOOxx
+5761   5095    1       1       1       1       61      761     1761    761     5761    122     123     PNAAAA  ZNHAAA  VVVVxx
+5414   5096    0       2       4       14      14      414     1414    414     5414    28      29      GAAAAA  AOHAAA  AAAAxx
+5892   5097    0       0       2       12      92      892     1892    892     5892    184     185     QSAAAA  BOHAAA  HHHHxx
+4080   5098    0       0       0       0       80      80      80      4080    4080    160     161     YAAAAA  COHAAA  OOOOxx
+8018   5099    0       2       8       18      18      18      18      3018    8018    36      37      KWAAAA  DOHAAA  VVVVxx
+1757   5100    1       1       7       17      57      757     1757    1757    1757    114     115     PPAAAA  EOHAAA  AAAAxx
+5854   5101    0       2       4       14      54      854     1854    854     5854    108     109     ERAAAA  FOHAAA  HHHHxx
+1335   5102    1       3       5       15      35      335     1335    1335    1335    70      71      JZAAAA  GOHAAA  OOOOxx
+3811   5103    1       3       1       11      11      811     1811    3811    3811    22      23      PQAAAA  HOHAAA  VVVVxx
+9917   5104    1       1       7       17      17      917     1917    4917    9917    34      35      LRAAAA  IOHAAA  AAAAxx
+5947   5105    1       3       7       7       47      947     1947    947     5947    94      95      TUAAAA  JOHAAA  HHHHxx
+7263   5106    1       3       3       3       63      263     1263    2263    7263    126     127     JTAAAA  KOHAAA  OOOOxx
+1730   5107    0       2       0       10      30      730     1730    1730    1730    60      61      OOAAAA  LOHAAA  VVVVxx
+5747   5108    1       3       7       7       47      747     1747    747     5747    94      95      BNAAAA  MOHAAA  AAAAxx
+3876   5109    0       0       6       16      76      876     1876    3876    3876    152     153     CTAAAA  NOHAAA  HHHHxx
+2762   5110    0       2       2       2       62      762     762     2762    2762    124     125     GCAAAA  OOHAAA  OOOOxx
+7613   5111    1       1       3       13      13      613     1613    2613    7613    26      27      VGAAAA  POHAAA  VVVVxx
+152    5112    0       0       2       12      52      152     152     152     152     104     105     WFAAAA  QOHAAA  AAAAxx
+3941   5113    1       1       1       1       41      941     1941    3941    3941    82      83      PVAAAA  ROHAAA  HHHHxx
+5614   5114    0       2       4       14      14      614     1614    614     5614    28      29      YHAAAA  SOHAAA  OOOOxx
+9279   5115    1       3       9       19      79      279     1279    4279    9279    158     159     XSAAAA  TOHAAA  VVVVxx
+3048   5116    0       0       8       8       48      48      1048    3048    3048    96      97      GNAAAA  UOHAAA  AAAAxx
+6152   5117    0       0       2       12      52      152     152     1152    6152    104     105     QCAAAA  VOHAAA  HHHHxx
+5481   5118    1       1       1       1       81      481     1481    481     5481    162     163     VCAAAA  WOHAAA  OOOOxx
+4675   5119    1       3       5       15      75      675     675     4675    4675    150     151     VXAAAA  XOHAAA  VVVVxx
+3334   5120    0       2       4       14      34      334     1334    3334    3334    68      69      GYAAAA  YOHAAA  AAAAxx
+4691   5121    1       3       1       11      91      691     691     4691    4691    182     183     LYAAAA  ZOHAAA  HHHHxx
+803    5122    1       3       3       3       3       803     803     803     803     6       7       XEAAAA  APHAAA  OOOOxx
+5409   5123    1       1       9       9       9       409     1409    409     5409    18      19      BAAAAA  BPHAAA  VVVVxx
+1054   5124    0       2       4       14      54      54      1054    1054    1054    108     109     OOAAAA  CPHAAA  AAAAxx
+103    5125    1       3       3       3       3       103     103     103     103     6       7       ZDAAAA  DPHAAA  HHHHxx
+8565   5126    1       1       5       5       65      565     565     3565    8565    130     131     LRAAAA  EPHAAA  OOOOxx
+4666   5127    0       2       6       6       66      666     666     4666    4666    132     133     MXAAAA  FPHAAA  VVVVxx
+6634   5128    0       2       4       14      34      634     634     1634    6634    68      69      EVAAAA  GPHAAA  AAAAxx
+5538   5129    0       2       8       18      38      538     1538    538     5538    76      77      AFAAAA  HPHAAA  HHHHxx
+3789   5130    1       1       9       9       89      789     1789    3789    3789    178     179     TPAAAA  IPHAAA  OOOOxx
+4641   5131    1       1       1       1       41      641     641     4641    4641    82      83      NWAAAA  JPHAAA  VVVVxx
+2458   5132    0       2       8       18      58      458     458     2458    2458    116     117     OQAAAA  KPHAAA  AAAAxx
+5667   5133    1       3       7       7       67      667     1667    667     5667    134     135     ZJAAAA  LPHAAA  HHHHxx
+6524   5134    0       0       4       4       24      524     524     1524    6524    48      49      YQAAAA  MPHAAA  OOOOxx
+9179   5135    1       3       9       19      79      179     1179    4179    9179    158     159     BPAAAA  NPHAAA  VVVVxx
+6358   5136    0       2       8       18      58      358     358     1358    6358    116     117     OKAAAA  OPHAAA  AAAAxx
+6668   5137    0       0       8       8       68      668     668     1668    6668    136     137     MWAAAA  PPHAAA  HHHHxx
+6414   5138    0       2       4       14      14      414     414     1414    6414    28      29      SMAAAA  QPHAAA  OOOOxx
+2813   5139    1       1       3       13      13      813     813     2813    2813    26      27      FEAAAA  RPHAAA  VVVVxx
+8927   5140    1       3       7       7       27      927     927     3927    8927    54      55      JFAAAA  SPHAAA  AAAAxx
+8695   5141    1       3       5       15      95      695     695     3695    8695    190     191     LWAAAA  TPHAAA  HHHHxx
+363    5142    1       3       3       3       63      363     363     363     363     126     127     ZNAAAA  UPHAAA  OOOOxx
+9966   5143    0       2       6       6       66      966     1966    4966    9966    132     133     ITAAAA  VPHAAA  VVVVxx
+1323   5144    1       3       3       3       23      323     1323    1323    1323    46      47      XYAAAA  WPHAAA  AAAAxx
+8211   5145    1       3       1       11      11      211     211     3211    8211    22      23      VDAAAA  XPHAAA  HHHHxx
+4375   5146    1       3       5       15      75      375     375     4375    4375    150     151     HMAAAA  YPHAAA  OOOOxx
+3257   5147    1       1       7       17      57      257     1257    3257    3257    114     115     HVAAAA  ZPHAAA  VVVVxx
+6239   5148    1       3       9       19      39      239     239     1239    6239    78      79      ZFAAAA  AQHAAA  AAAAxx
+3602   5149    0       2       2       2       2       602     1602    3602    3602    4       5       OIAAAA  BQHAAA  HHHHxx
+9830   5150    0       2       0       10      30      830     1830    4830    9830    60      61      COAAAA  CQHAAA  OOOOxx
+7826   5151    0       2       6       6       26      826     1826    2826    7826    52      53      APAAAA  DQHAAA  VVVVxx
+2108   5152    0       0       8       8       8       108     108     2108    2108    16      17      CDAAAA  EQHAAA  AAAAxx
+7245   5153    1       1       5       5       45      245     1245    2245    7245    90      91      RSAAAA  FQHAAA  HHHHxx
+8330   5154    0       2       0       10      30      330     330     3330    8330    60      61      KIAAAA  GQHAAA  OOOOxx
+7441   5155    1       1       1       1       41      441     1441    2441    7441    82      83      FAAAAA  HQHAAA  VVVVxx
+9848   5156    0       0       8       8       48      848     1848    4848    9848    96      97      UOAAAA  IQHAAA  AAAAxx
+1226   5157    0       2       6       6       26      226     1226    1226    1226    52      53      EVAAAA  JQHAAA  HHHHxx
+414    5158    0       2       4       14      14      414     414     414     414     28      29      YPAAAA  KQHAAA  OOOOxx
+1273   5159    1       1       3       13      73      273     1273    1273    1273    146     147     ZWAAAA  LQHAAA  VVVVxx
+9866   5160    0       2       6       6       66      866     1866    4866    9866    132     133     MPAAAA  MQHAAA  AAAAxx
+4633   5161    1       1       3       13      33      633     633     4633    4633    66      67      FWAAAA  NQHAAA  HHHHxx
+8727   5162    1       3       7       7       27      727     727     3727    8727    54      55      RXAAAA  OQHAAA  OOOOxx
+5308   5163    0       0       8       8       8       308     1308    308     5308    16      17      EWAAAA  PQHAAA  VVVVxx
+1395   5164    1       3       5       15      95      395     1395    1395    1395    190     191     RBAAAA  QQHAAA  AAAAxx
+1825   5165    1       1       5       5       25      825     1825    1825    1825    50      51      FSAAAA  RQHAAA  HHHHxx
+7606   5166    0       2       6       6       6       606     1606    2606    7606    12      13      OGAAAA  SQHAAA  OOOOxx
+9390   5167    0       2       0       10      90      390     1390    4390    9390    180     181     EXAAAA  TQHAAA  VVVVxx
+2376   5168    0       0       6       16      76      376     376     2376    2376    152     153     KNAAAA  UQHAAA  AAAAxx
+2377   5169    1       1       7       17      77      377     377     2377    2377    154     155     LNAAAA  VQHAAA  HHHHxx
+5346   5170    0       2       6       6       46      346     1346    346     5346    92      93      QXAAAA  WQHAAA  OOOOxx
+4140   5171    0       0       0       0       40      140     140     4140    4140    80      81      GDAAAA  XQHAAA  VVVVxx
+6032   5172    0       0       2       12      32      32      32      1032    6032    64      65      AYAAAA  YQHAAA  AAAAxx
+9453   5173    1       1       3       13      53      453     1453    4453    9453    106     107     PZAAAA  ZQHAAA  HHHHxx
+9297   5174    1       1       7       17      97      297     1297    4297    9297    194     195     PTAAAA  ARHAAA  OOOOxx
+6455   5175    1       3       5       15      55      455     455     1455    6455    110     111     HOAAAA  BRHAAA  VVVVxx
+4458   5176    0       2       8       18      58      458     458     4458    4458    116     117     MPAAAA  CRHAAA  AAAAxx
+9516   5177    0       0       6       16      16      516     1516    4516    9516    32      33      ACAAAA  DRHAAA  HHHHxx
+6211   5178    1       3       1       11      11      211     211     1211    6211    22      23      XEAAAA  ERHAAA  OOOOxx
+526    5179    0       2       6       6       26      526     526     526     526     52      53      GUAAAA  FRHAAA  VVVVxx
+3570   5180    0       2       0       10      70      570     1570    3570    3570    140     141     IHAAAA  GRHAAA  AAAAxx
+4885   5181    1       1       5       5       85      885     885     4885    4885    170     171     XFAAAA  HRHAAA  HHHHxx
+6390   5182    0       2       0       10      90      390     390     1390    6390    180     181     ULAAAA  IRHAAA  OOOOxx
+1606   5183    0       2       6       6       6       606     1606    1606    1606    12      13      UJAAAA  JRHAAA  VVVVxx
+7850   5184    0       2       0       10      50      850     1850    2850    7850    100     101     YPAAAA  KRHAAA  AAAAxx
+3315   5185    1       3       5       15      15      315     1315    3315    3315    30      31      NXAAAA  LRHAAA  HHHHxx
+8322   5186    0       2       2       2       22      322     322     3322    8322    44      45      CIAAAA  MRHAAA  OOOOxx
+3703   5187    1       3       3       3       3       703     1703    3703    3703    6       7       LMAAAA  NRHAAA  VVVVxx
+9489   5188    1       1       9       9       89      489     1489    4489    9489    178     179     ZAAAAA  ORHAAA  AAAAxx
+6104   5189    0       0       4       4       4       104     104     1104    6104    8       9       UAAAAA  PRHAAA  HHHHxx
+3067   5190    1       3       7       7       67      67      1067    3067    3067    134     135     ZNAAAA  QRHAAA  OOOOxx
+2521   5191    1       1       1       1       21      521     521     2521    2521    42      43      ZSAAAA  RRHAAA  VVVVxx
+2581   5192    1       1       1       1       81      581     581     2581    2581    162     163     HVAAAA  SRHAAA  AAAAxx
+595    5193    1       3       5       15      95      595     595     595     595     190     191     XWAAAA  TRHAAA  HHHHxx
+8291   5194    1       3       1       11      91      291     291     3291    8291    182     183     XGAAAA  URHAAA  OOOOxx
+1727   5195    1       3       7       7       27      727     1727    1727    1727    54      55      LOAAAA  VRHAAA  VVVVxx
+6847   5196    1       3       7       7       47      847     847     1847    6847    94      95      JDAAAA  WRHAAA  AAAAxx
+7494   5197    0       2       4       14      94      494     1494    2494    7494    188     189     GCAAAA  XRHAAA  HHHHxx
+7093   5198    1       1       3       13      93      93      1093    2093    7093    186     187     VMAAAA  YRHAAA  OOOOxx
+7357   5199    1       1       7       17      57      357     1357    2357    7357    114     115     ZWAAAA  ZRHAAA  VVVVxx
+620    5200    0       0       0       0       20      620     620     620     620     40      41      WXAAAA  ASHAAA  AAAAxx
+2460   5201    0       0       0       0       60      460     460     2460    2460    120     121     QQAAAA  BSHAAA  HHHHxx
+1598   5202    0       2       8       18      98      598     1598    1598    1598    196     197     MJAAAA  CSHAAA  OOOOxx
+4112   5203    0       0       2       12      12      112     112     4112    4112    24      25      ECAAAA  DSHAAA  VVVVxx
+2956   5204    0       0       6       16      56      956     956     2956    2956    112     113     SJAAAA  ESHAAA  AAAAxx
+3193   5205    1       1       3       13      93      193     1193    3193    3193    186     187     VSAAAA  FSHAAA  HHHHxx
+6356   5206    0       0       6       16      56      356     356     1356    6356    112     113     MKAAAA  GSHAAA  OOOOxx
+730    5207    0       2       0       10      30      730     730     730     730     60      61      CCAAAA  HSHAAA  VVVVxx
+8826   5208    0       2       6       6       26      826     826     3826    8826    52      53      MBAAAA  ISHAAA  AAAAxx
+9036   5209    0       0       6       16      36      36      1036    4036    9036    72      73      OJAAAA  JSHAAA  HHHHxx
+2085   5210    1       1       5       5       85      85      85      2085    2085    170     171     FCAAAA  KSHAAA  OOOOxx
+9007   5211    1       3       7       7       7       7       1007    4007    9007    14      15      LIAAAA  LSHAAA  VVVVxx
+6047   5212    1       3       7       7       47      47      47      1047    6047    94      95      PYAAAA  MSHAAA  AAAAxx
+3953   5213    1       1       3       13      53      953     1953    3953    3953    106     107     BWAAAA  NSHAAA  HHHHxx
+1214   5214    0       2       4       14      14      214     1214    1214    1214    28      29      SUAAAA  OSHAAA  OOOOxx
+4814   5215    0       2       4       14      14      814     814     4814    4814    28      29      EDAAAA  PSHAAA  VVVVxx
+5738   5216    0       2       8       18      38      738     1738    738     5738    76      77      SMAAAA  QSHAAA  AAAAxx
+7176   5217    0       0       6       16      76      176     1176    2176    7176    152     153     AQAAAA  RSHAAA  HHHHxx
+3609   5218    1       1       9       9       9       609     1609    3609    3609    18      19      VIAAAA  SSHAAA  OOOOxx
+592    5219    0       0       2       12      92      592     592     592     592     184     185     UWAAAA  TSHAAA  VVVVxx
+9391   5220    1       3       1       11      91      391     1391    4391    9391    182     183     FXAAAA  USHAAA  AAAAxx
+5345   5221    1       1       5       5       45      345     1345    345     5345    90      91      PXAAAA  VSHAAA  HHHHxx
+1171   5222    1       3       1       11      71      171     1171    1171    1171    142     143     BTAAAA  WSHAAA  OOOOxx
+7238   5223    0       2       8       18      38      238     1238    2238    7238    76      77      KSAAAA  XSHAAA  VVVVxx
+7561   5224    1       1       1       1       61      561     1561    2561    7561    122     123     VEAAAA  YSHAAA  AAAAxx
+5876   5225    0       0       6       16      76      876     1876    876     5876    152     153     ASAAAA  ZSHAAA  HHHHxx
+6611   5226    1       3       1       11      11      611     611     1611    6611    22      23      HUAAAA  ATHAAA  OOOOxx
+7300   5227    0       0       0       0       0       300     1300    2300    7300    0       1       UUAAAA  BTHAAA  VVVVxx
+1506   5228    0       2       6       6       6       506     1506    1506    1506    12      13      YFAAAA  CTHAAA  AAAAxx
+1153   5229    1       1       3       13      53      153     1153    1153    1153    106     107     JSAAAA  DTHAAA  HHHHxx
+3831   5230    1       3       1       11      31      831     1831    3831    3831    62      63      JRAAAA  ETHAAA  OOOOxx
+9255   5231    1       3       5       15      55      255     1255    4255    9255    110     111     ZRAAAA  FTHAAA  VVVVxx
+1841   5232    1       1       1       1       41      841     1841    1841    1841    82      83      VSAAAA  GTHAAA  AAAAxx
+5075   5233    1       3       5       15      75      75      1075    75      5075    150     151     FNAAAA  HTHAAA  HHHHxx
+101    5234    1       1       1       1       1       101     101     101     101     2       3       XDAAAA  ITHAAA  OOOOxx
+2627   5235    1       3       7       7       27      627     627     2627    2627    54      55      BXAAAA  JTHAAA  VVVVxx
+7078   5236    0       2       8       18      78      78      1078    2078    7078    156     157     GMAAAA  KTHAAA  AAAAxx
+2850   5237    0       2       0       10      50      850     850     2850    2850    100     101     QFAAAA  LTHAAA  HHHHxx
+8703   5238    1       3       3       3       3       703     703     3703    8703    6       7       TWAAAA  MTHAAA  OOOOxx
+4101   5239    1       1       1       1       1       101     101     4101    4101    2       3       TBAAAA  NTHAAA  VVVVxx
+318    5240    0       2       8       18      18      318     318     318     318     36      37      GMAAAA  OTHAAA  AAAAxx
+6452   5241    0       0       2       12      52      452     452     1452    6452    104     105     EOAAAA  PTHAAA  HHHHxx
+5558   5242    0       2       8       18      58      558     1558    558     5558    116     117     UFAAAA  QTHAAA  OOOOxx
+3127   5243    1       3       7       7       27      127     1127    3127    3127    54      55      HQAAAA  RTHAAA  VVVVxx
+535    5244    1       3       5       15      35      535     535     535     535     70      71      PUAAAA  STHAAA  AAAAxx
+270    5245    0       2       0       10      70      270     270     270     270     140     141     KKAAAA  TTHAAA  HHHHxx
+4038   5246    0       2       8       18      38      38      38      4038    4038    76      77      IZAAAA  UTHAAA  OOOOxx
+3404   5247    0       0       4       4       4       404     1404    3404    3404    8       9       YAAAAA  VTHAAA  VVVVxx
+2374   5248    0       2       4       14      74      374     374     2374    2374    148     149     INAAAA  WTHAAA  AAAAxx
+6446   5249    0       2       6       6       46      446     446     1446    6446    92      93      YNAAAA  XTHAAA  HHHHxx
+7758   5250    0       2       8       18      58      758     1758    2758    7758    116     117     KMAAAA  YTHAAA  OOOOxx
+356    5251    0       0       6       16      56      356     356     356     356     112     113     SNAAAA  ZTHAAA  VVVVxx
+9197   5252    1       1       7       17      97      197     1197    4197    9197    194     195     TPAAAA  AUHAAA  AAAAxx
+9765   5253    1       1       5       5       65      765     1765    4765    9765    130     131     PLAAAA  BUHAAA  HHHHxx
+4974   5254    0       2       4       14      74      974     974     4974    4974    148     149     IJAAAA  CUHAAA  OOOOxx
+442    5255    0       2       2       2       42      442     442     442     442     84      85      ARAAAA  DUHAAA  VVVVxx
+4349   5256    1       1       9       9       49      349     349     4349    4349    98      99      HLAAAA  EUHAAA  AAAAxx
+6119   5257    1       3       9       19      19      119     119     1119    6119    38      39      JBAAAA  FUHAAA  HHHHxx
+7574   5258    0       2       4       14      74      574     1574    2574    7574    148     149     IFAAAA  GUHAAA  OOOOxx
+4445   5259    1       1       5       5       45      445     445     4445    4445    90      91      ZOAAAA  HUHAAA  VVVVxx
+940    5260    0       0       0       0       40      940     940     940     940     80      81      EKAAAA  IUHAAA  AAAAxx
+1875   5261    1       3       5       15      75      875     1875    1875    1875    150     151     DUAAAA  JUHAAA  HHHHxx
+5951   5262    1       3       1       11      51      951     1951    951     5951    102     103     XUAAAA  KUHAAA  OOOOxx
+9132   5263    0       0       2       12      32      132     1132    4132    9132    64      65      GNAAAA  LUHAAA  VVVVxx
+6913   5264    1       1       3       13      13      913     913     1913    6913    26      27      XFAAAA  MUHAAA  AAAAxx
+3308   5265    0       0       8       8       8       308     1308    3308    3308    16      17      GXAAAA  NUHAAA  HHHHxx
+7553   5266    1       1       3       13      53      553     1553    2553    7553    106     107     NEAAAA  OUHAAA  OOOOxx
+2138   5267    0       2       8       18      38      138     138     2138    2138    76      77      GEAAAA  PUHAAA  VVVVxx
+6252   5268    0       0       2       12      52      252     252     1252    6252    104     105     MGAAAA  QUHAAA  AAAAxx
+2171   5269    1       3       1       11      71      171     171     2171    2171    142     143     NFAAAA  RUHAAA  HHHHxx
+4159   5270    1       3       9       19      59      159     159     4159    4159    118     119     ZDAAAA  SUHAAA  OOOOxx
+2401   5271    1       1       1       1       1       401     401     2401    2401    2       3       JOAAAA  TUHAAA  VVVVxx
+6553   5272    1       1       3       13      53      553     553     1553    6553    106     107     BSAAAA  UUHAAA  AAAAxx
+5217   5273    1       1       7       17      17      217     1217    217     5217    34      35      RSAAAA  VUHAAA  HHHHxx
+1405   5274    1       1       5       5       5       405     1405    1405    1405    10      11      BCAAAA  WUHAAA  OOOOxx
+1494   5275    0       2       4       14      94      494     1494    1494    1494    188     189     MFAAAA  XUHAAA  VVVVxx
+5553   5276    1       1       3       13      53      553     1553    553     5553    106     107     PFAAAA  YUHAAA  AAAAxx
+8296   5277    0       0       6       16      96      296     296     3296    8296    192     193     CHAAAA  ZUHAAA  HHHHxx
+6565   5278    1       1       5       5       65      565     565     1565    6565    130     131     NSAAAA  AVHAAA  OOOOxx
+817    5279    1       1       7       17      17      817     817     817     817     34      35      LFAAAA  BVHAAA  VVVVxx
+6947   5280    1       3       7       7       47      947     947     1947    6947    94      95      FHAAAA  CVHAAA  AAAAxx
+4184   5281    0       0       4       4       84      184     184     4184    4184    168     169     YEAAAA  DVHAAA  HHHHxx
+6577   5282    1       1       7       17      77      577     577     1577    6577    154     155     ZSAAAA  EVHAAA  OOOOxx
+6424   5283    0       0       4       4       24      424     424     1424    6424    48      49      CNAAAA  FVHAAA  VVVVxx
+2482   5284    0       2       2       2       82      482     482     2482    2482    164     165     MRAAAA  GVHAAA  AAAAxx
+6874   5285    0       2       4       14      74      874     874     1874    6874    148     149     KEAAAA  HVHAAA  HHHHxx
+7601   5286    1       1       1       1       1       601     1601    2601    7601    2       3       JGAAAA  IVHAAA  OOOOxx
+4552   5287    0       0       2       12      52      552     552     4552    4552    104     105     CTAAAA  JVHAAA  VVVVxx
+8406   5288    0       2       6       6       6       406     406     3406    8406    12      13      ILAAAA  KVHAAA  AAAAxx
+2924   5289    0       0       4       4       24      924     924     2924    2924    48      49      MIAAAA  LVHAAA  HHHHxx
+8255   5290    1       3       5       15      55      255     255     3255    8255    110     111     NFAAAA  MVHAAA  OOOOxx
+4920   5291    0       0       0       0       20      920     920     4920    4920    40      41      GHAAAA  NVHAAA  VVVVxx
+228    5292    0       0       8       8       28      228     228     228     228     56      57      UIAAAA  OVHAAA  AAAAxx
+9431   5293    1       3       1       11      31      431     1431    4431    9431    62      63      TYAAAA  PVHAAA  HHHHxx
+4021   5294    1       1       1       1       21      21      21      4021    4021    42      43      RYAAAA  QVHAAA  OOOOxx
+2966   5295    0       2       6       6       66      966     966     2966    2966    132     133     CKAAAA  RVHAAA  VVVVxx
+2862   5296    0       2       2       2       62      862     862     2862    2862    124     125     CGAAAA  SVHAAA  AAAAxx
+4303   5297    1       3       3       3       3       303     303     4303    4303    6       7       NJAAAA  TVHAAA  HHHHxx
+9643   5298    1       3       3       3       43      643     1643    4643    9643    86      87      XGAAAA  UVHAAA  OOOOxx
+3008   5299    0       0       8       8       8       8       1008    3008    3008    16      17      SLAAAA  VVHAAA  VVVVxx
+7476   5300    0       0       6       16      76      476     1476    2476    7476    152     153     OBAAAA  WVHAAA  AAAAxx
+3686   5301    0       2       6       6       86      686     1686    3686    3686    172     173     ULAAAA  XVHAAA  HHHHxx
+9051   5302    1       3       1       11      51      51      1051    4051    9051    102     103     DKAAAA  YVHAAA  OOOOxx
+6592   5303    0       0       2       12      92      592     592     1592    6592    184     185     OTAAAA  ZVHAAA  VVVVxx
+924    5304    0       0       4       4       24      924     924     924     924     48      49      OJAAAA  AWHAAA  AAAAxx
+4406   5305    0       2       6       6       6       406     406     4406    4406    12      13      MNAAAA  BWHAAA  HHHHxx
+5233   5306    1       1       3       13      33      233     1233    233     5233    66      67      HTAAAA  CWHAAA  OOOOxx
+8881   5307    1       1       1       1       81      881     881     3881    8881    162     163     PDAAAA  DWHAAA  VVVVxx
+2212   5308    0       0       2       12      12      212     212     2212    2212    24      25      CHAAAA  EWHAAA  AAAAxx
+5804   5309    0       0       4       4       4       804     1804    804     5804    8       9       GPAAAA  FWHAAA  HHHHxx
+2990   5310    0       2       0       10      90      990     990     2990    2990    180     181     ALAAAA  GWHAAA  OOOOxx
+4069   5311    1       1       9       9       69      69      69      4069    4069    138     139     NAAAAA  HWHAAA  VVVVxx
+5380   5312    0       0       0       0       80      380     1380    380     5380    160     161     YYAAAA  IWHAAA  AAAAxx
+5016   5313    0       0       6       16      16      16      1016    16      5016    32      33      YKAAAA  JWHAAA  HHHHxx
+5056   5314    0       0       6       16      56      56      1056    56      5056    112     113     MMAAAA  KWHAAA  OOOOxx
+3732   5315    0       0       2       12      32      732     1732    3732    3732    64      65      ONAAAA  LWHAAA  VVVVxx
+5527   5316    1       3       7       7       27      527     1527    527     5527    54      55      PEAAAA  MWHAAA  AAAAxx
+1151   5317    1       3       1       11      51      151     1151    1151    1151    102     103     HSAAAA  NWHAAA  HHHHxx
+7900   5318    0       0       0       0       0       900     1900    2900    7900    0       1       WRAAAA  OWHAAA  OOOOxx
+1660   5319    0       0       0       0       60      660     1660    1660    1660    120     121     WLAAAA  PWHAAA  VVVVxx
+8064   5320    0       0       4       4       64      64      64      3064    8064    128     129     EYAAAA  QWHAAA  AAAAxx
+8240   5321    0       0       0       0       40      240     240     3240    8240    80      81      YEAAAA  RWHAAA  HHHHxx
+413    5322    1       1       3       13      13      413     413     413     413     26      27      XPAAAA  SWHAAA  OOOOxx
+8311   5323    1       3       1       11      11      311     311     3311    8311    22      23      RHAAAA  TWHAAA  VVVVxx
+1065   5324    1       1       5       5       65      65      1065    1065    1065    130     131     ZOAAAA  UWHAAA  AAAAxx
+2741   5325    1       1       1       1       41      741     741     2741    2741    82      83      LBAAAA  VWHAAA  HHHHxx
+5306   5326    0       2       6       6       6       306     1306    306     5306    12      13      CWAAAA  WWHAAA  OOOOxx
+5464   5327    0       0       4       4       64      464     1464    464     5464    128     129     ECAAAA  XWHAAA  VVVVxx
+4237   5328    1       1       7       17      37      237     237     4237    4237    74      75      ZGAAAA  YWHAAA  AAAAxx
+3822   5329    0       2       2       2       22      822     1822    3822    3822    44      45      ARAAAA  ZWHAAA  HHHHxx
+2548   5330    0       0       8       8       48      548     548     2548    2548    96      97      AUAAAA  AXHAAA  OOOOxx
+2688   5331    0       0       8       8       88      688     688     2688    2688    176     177     KZAAAA  BXHAAA  VVVVxx
+8061   5332    1       1       1       1       61      61      61      3061    8061    122     123     BYAAAA  CXHAAA  AAAAxx
+9340   5333    0       0       0       0       40      340     1340    4340    9340    80      81      GVAAAA  DXHAAA  HHHHxx
+4031   5334    1       3       1       11      31      31      31      4031    4031    62      63      BZAAAA  EXHAAA  OOOOxx
+2635   5335    1       3       5       15      35      635     635     2635    2635    70      71      JXAAAA  FXHAAA  VVVVxx
+809    5336    1       1       9       9       9       809     809     809     809     18      19      DFAAAA  GXHAAA  AAAAxx
+3209   5337    1       1       9       9       9       209     1209    3209    3209    18      19      LTAAAA  HXHAAA  HHHHxx
+3825   5338    1       1       5       5       25      825     1825    3825    3825    50      51      DRAAAA  IXHAAA  OOOOxx
+1448   5339    0       0       8       8       48      448     1448    1448    1448    96      97      SDAAAA  JXHAAA  VVVVxx
+9077   5340    1       1       7       17      77      77      1077    4077    9077    154     155     DLAAAA  KXHAAA  AAAAxx
+3730   5341    0       2       0       10      30      730     1730    3730    3730    60      61      MNAAAA  LXHAAA  HHHHxx
+9596   5342    0       0       6       16      96      596     1596    4596    9596    192     193     CFAAAA  MXHAAA  OOOOxx
+3563   5343    1       3       3       3       63      563     1563    3563    3563    126     127     BHAAAA  NXHAAA  VVVVxx
+4116   5344    0       0       6       16      16      116     116     4116    4116    32      33      ICAAAA  OXHAAA  AAAAxx
+4825   5345    1       1       5       5       25      825     825     4825    4825    50      51      PDAAAA  PXHAAA  HHHHxx
+8376   5346    0       0       6       16      76      376     376     3376    8376    152     153     EKAAAA  QXHAAA  OOOOxx
+3917   5347    1       1       7       17      17      917     1917    3917    3917    34      35      RUAAAA  RXHAAA  VVVVxx
+4407   5348    1       3       7       7       7       407     407     4407    4407    14      15      NNAAAA  SXHAAA  AAAAxx
+8202   5349    0       2       2       2       2       202     202     3202    8202    4       5       MDAAAA  TXHAAA  HHHHxx
+7675   5350    1       3       5       15      75      675     1675    2675    7675    150     151     FJAAAA  UXHAAA  OOOOxx
+4104   5351    0       0       4       4       4       104     104     4104    4104    8       9       WBAAAA  VXHAAA  VVVVxx
+9225   5352    1       1       5       5       25      225     1225    4225    9225    50      51      VQAAAA  WXHAAA  AAAAxx
+2834   5353    0       2       4       14      34      834     834     2834    2834    68      69      AFAAAA  XXHAAA  HHHHxx
+1227   5354    1       3       7       7       27      227     1227    1227    1227    54      55      FVAAAA  YXHAAA  OOOOxx
+3383   5355    1       3       3       3       83      383     1383    3383    3383    166     167     DAAAAA  ZXHAAA  VVVVxx
+67     5356    1       3       7       7       67      67      67      67      67      134     135     PCAAAA  AYHAAA  AAAAxx
+1751   5357    1       3       1       11      51      751     1751    1751    1751    102     103     JPAAAA  BYHAAA  HHHHxx
+8054   5358    0       2       4       14      54      54      54      3054    8054    108     109     UXAAAA  CYHAAA  OOOOxx
+8571   5359    1       3       1       11      71      571     571     3571    8571    142     143     RRAAAA  DYHAAA  VVVVxx
+2466   5360    0       2       6       6       66      466     466     2466    2466    132     133     WQAAAA  EYHAAA  AAAAxx
+9405   5361    1       1       5       5       5       405     1405    4405    9405    10      11      TXAAAA  FYHAAA  HHHHxx
+6883   5362    1       3       3       3       83      883     883     1883    6883    166     167     TEAAAA  GYHAAA  OOOOxx
+4301   5363    1       1       1       1       1       301     301     4301    4301    2       3       LJAAAA  HYHAAA  VVVVxx
+3705   5364    1       1       5       5       5       705     1705    3705    3705    10      11      NMAAAA  IYHAAA  AAAAxx
+5420   5365    0       0       0       0       20      420     1420    420     5420    40      41      MAAAAA  JYHAAA  HHHHxx
+3692   5366    0       0       2       12      92      692     1692    3692    3692    184     185     AMAAAA  KYHAAA  OOOOxx
+6851   5367    1       3       1       11      51      851     851     1851    6851    102     103     NDAAAA  LYHAAA  VVVVxx
+9363   5368    1       3       3       3       63      363     1363    4363    9363    126     127     DWAAAA  MYHAAA  AAAAxx
+2269   5369    1       1       9       9       69      269     269     2269    2269    138     139     HJAAAA  NYHAAA  HHHHxx
+4918   5370    0       2       8       18      18      918     918     4918    4918    36      37      EHAAAA  OYHAAA  OOOOxx
+4297   5371    1       1       7       17      97      297     297     4297    4297    194     195     HJAAAA  PYHAAA  VVVVxx
+1836   5372    0       0       6       16      36      836     1836    1836    1836    72      73      QSAAAA  QYHAAA  AAAAxx
+237    5373    1       1       7       17      37      237     237     237     237     74      75      DJAAAA  RYHAAA  HHHHxx
+6131   5374    1       3       1       11      31      131     131     1131    6131    62      63      VBAAAA  SYHAAA  OOOOxx
+3174   5375    0       2       4       14      74      174     1174    3174    3174    148     149     CSAAAA  TYHAAA  VVVVxx
+9987   5376    1       3       7       7       87      987     1987    4987    9987    174     175     DUAAAA  UYHAAA  AAAAxx
+3630   5377    0       2       0       10      30      630     1630    3630    3630    60      61      QJAAAA  VYHAAA  HHHHxx
+2899   5378    1       3       9       19      99      899     899     2899    2899    198     199     NHAAAA  WYHAAA  OOOOxx
+4079   5379    1       3       9       19      79      79      79      4079    4079    158     159     XAAAAA  XYHAAA  VVVVxx
+5049   5380    1       1       9       9       49      49      1049    49      5049    98      99      FMAAAA  YYHAAA  AAAAxx
+2963   5381    1       3       3       3       63      963     963     2963    2963    126     127     ZJAAAA  ZYHAAA  HHHHxx
+3962   5382    0       2       2       2       62      962     1962    3962    3962    124     125     KWAAAA  AZHAAA  OOOOxx
+7921   5383    1       1       1       1       21      921     1921    2921    7921    42      43      RSAAAA  BZHAAA  VVVVxx
+3967   5384    1       3       7       7       67      967     1967    3967    3967    134     135     PWAAAA  CZHAAA  AAAAxx
+2752   5385    0       0       2       12      52      752     752     2752    2752    104     105     WBAAAA  DZHAAA  HHHHxx
+7944   5386    0       0       4       4       44      944     1944    2944    7944    88      89      OTAAAA  EZHAAA  OOOOxx
+2205   5387    1       1       5       5       5       205     205     2205    2205    10      11      VGAAAA  FZHAAA  VVVVxx
+5035   5388    1       3       5       15      35      35      1035    35      5035    70      71      RLAAAA  GZHAAA  AAAAxx
+1425   5389    1       1       5       5       25      425     1425    1425    1425    50      51      VCAAAA  HZHAAA  HHHHxx
+832    5390    0       0       2       12      32      832     832     832     832     64      65      AGAAAA  IZHAAA  OOOOxx
+1447   5391    1       3       7       7       47      447     1447    1447    1447    94      95      RDAAAA  JZHAAA  VVVVxx
+6108   5392    0       0       8       8       8       108     108     1108    6108    16      17      YAAAAA  KZHAAA  AAAAxx
+4936   5393    0       0       6       16      36      936     936     4936    4936    72      73      WHAAAA  LZHAAA  HHHHxx
+7704   5394    0       0       4       4       4       704     1704    2704    7704    8       9       IKAAAA  MZHAAA  OOOOxx
+142    5395    0       2       2       2       42      142     142     142     142     84      85      MFAAAA  NZHAAA  VVVVxx
+4272   5396    0       0       2       12      72      272     272     4272    4272    144     145     IIAAAA  OZHAAA  AAAAxx
+7667   5397    1       3       7       7       67      667     1667    2667    7667    134     135     XIAAAA  PZHAAA  HHHHxx
+366    5398    0       2       6       6       66      366     366     366     366     132     133     COAAAA  QZHAAA  OOOOxx
+8866   5399    0       2       6       6       66      866     866     3866    8866    132     133     ADAAAA  RZHAAA  VVVVxx
+7712   5400    0       0       2       12      12      712     1712    2712    7712    24      25      QKAAAA  SZHAAA  AAAAxx
+3880   5401    0       0       0       0       80      880     1880    3880    3880    160     161     GTAAAA  TZHAAA  HHHHxx
+4631   5402    1       3       1       11      31      631     631     4631    4631    62      63      DWAAAA  UZHAAA  OOOOxx
+2789   5403    1       1       9       9       89      789     789     2789    2789    178     179     HDAAAA  VZHAAA  VVVVxx
+7720   5404    0       0       0       0       20      720     1720    2720    7720    40      41      YKAAAA  WZHAAA  AAAAxx
+7618   5405    0       2       8       18      18      618     1618    2618    7618    36      37      AHAAAA  XZHAAA  HHHHxx
+4990   5406    0       2       0       10      90      990     990     4990    4990    180     181     YJAAAA  YZHAAA  OOOOxx
+7918   5407    0       2       8       18      18      918     1918    2918    7918    36      37      OSAAAA  ZZHAAA  VVVVxx
+5067   5408    1       3       7       7       67      67      1067    67      5067    134     135     XMAAAA  AAIAAA  AAAAxx
+6370   5409    0       2       0       10      70      370     370     1370    6370    140     141     ALAAAA  BAIAAA  HHHHxx
+2268   5410    0       0       8       8       68      268     268     2268    2268    136     137     GJAAAA  CAIAAA  OOOOxx
+1949   5411    1       1       9       9       49      949     1949    1949    1949    98      99      ZWAAAA  DAIAAA  VVVVxx
+5503   5412    1       3       3       3       3       503     1503    503     5503    6       7       RDAAAA  EAIAAA  AAAAxx
+9951   5413    1       3       1       11      51      951     1951    4951    9951    102     103     TSAAAA  FAIAAA  HHHHxx
+6823   5414    1       3       3       3       23      823     823     1823    6823    46      47      LCAAAA  GAIAAA  OOOOxx
+6287   5415    1       3       7       7       87      287     287     1287    6287    174     175     VHAAAA  HAIAAA  VVVVxx
+6016   5416    0       0       6       16      16      16      16      1016    6016    32      33      KXAAAA  IAIAAA  AAAAxx
+1977   5417    1       1       7       17      77      977     1977    1977    1977    154     155     BYAAAA  JAIAAA  HHHHxx
+8579   5418    1       3       9       19      79      579     579     3579    8579    158     159     ZRAAAA  KAIAAA  OOOOxx
+6204   5419    0       0       4       4       4       204     204     1204    6204    8       9       QEAAAA  LAIAAA  VVVVxx
+9764   5420    0       0       4       4       64      764     1764    4764    9764    128     129     OLAAAA  MAIAAA  AAAAxx
+2005   5421    1       1       5       5       5       5       5       2005    2005    10      11      DZAAAA  NAIAAA  HHHHxx
+1648   5422    0       0       8       8       48      648     1648    1648    1648    96      97      KLAAAA  OAIAAA  OOOOxx
+2457   5423    1       1       7       17      57      457     457     2457    2457    114     115     NQAAAA  PAIAAA  VVVVxx
+2698   5424    0       2       8       18      98      698     698     2698    2698    196     197     UZAAAA  QAIAAA  AAAAxx
+7730   5425    0       2       0       10      30      730     1730    2730    7730    60      61      ILAAAA  RAIAAA  HHHHxx
+7287   5426    1       3       7       7       87      287     1287    2287    7287    174     175     HUAAAA  SAIAAA  OOOOxx
+2937   5427    1       1       7       17      37      937     937     2937    2937    74      75      ZIAAAA  TAIAAA  VVVVxx
+6824   5428    0       0       4       4       24      824     824     1824    6824    48      49      MCAAAA  UAIAAA  AAAAxx
+9256   5429    0       0       6       16      56      256     1256    4256    9256    112     113     ASAAAA  VAIAAA  HHHHxx
+4810   5430    0       2       0       10      10      810     810     4810    4810    20      21      ADAAAA  WAIAAA  OOOOxx
+3869   5431    1       1       9       9       69      869     1869    3869    3869    138     139     VSAAAA  XAIAAA  VVVVxx
+1993   5432    1       1       3       13      93      993     1993    1993    1993    186     187     RYAAAA  YAIAAA  AAAAxx
+6048   5433    0       0       8       8       48      48      48      1048    6048    96      97      QYAAAA  ZAIAAA  HHHHxx
+6922   5434    0       2       2       2       22      922     922     1922    6922    44      45      GGAAAA  ABIAAA  OOOOxx
+8      5435    0       0       8       8       8       8       8       8       8       16      17      IAAAAA  BBIAAA  VVVVxx
+6706   5436    0       2       6       6       6       706     706     1706    6706    12      13      YXAAAA  CBIAAA  AAAAxx
+9159   5437    1       3       9       19      59      159     1159    4159    9159    118     119     HOAAAA  DBIAAA  HHHHxx
+7020   5438    0       0       0       0       20      20      1020    2020    7020    40      41      AKAAAA  EBIAAA  OOOOxx
+767    5439    1       3       7       7       67      767     767     767     767     134     135     NDAAAA  FBIAAA  VVVVxx
+8602   5440    0       2       2       2       2       602     602     3602    8602    4       5       WSAAAA  GBIAAA  AAAAxx
+4442   5441    0       2       2       2       42      442     442     4442    4442    84      85      WOAAAA  HBIAAA  HHHHxx
+2040   5442    0       0       0       0       40      40      40      2040    2040    80      81      MAAAAA  IBIAAA  OOOOxx
+5493   5443    1       1       3       13      93      493     1493    493     5493    186     187     HDAAAA  JBIAAA  VVVVxx
+275    5444    1       3       5       15      75      275     275     275     275     150     151     PKAAAA  KBIAAA  AAAAxx
+8876   5445    0       0       6       16      76      876     876     3876    8876    152     153     KDAAAA  LBIAAA  HHHHxx
+7381   5446    1       1       1       1       81      381     1381    2381    7381    162     163     XXAAAA  MBIAAA  OOOOxx
+1827   5447    1       3       7       7       27      827     1827    1827    1827    54      55      HSAAAA  NBIAAA  VVVVxx
+3537   5448    1       1       7       17      37      537     1537    3537    3537    74      75      BGAAAA  OBIAAA  AAAAxx
+6978   5449    0       2       8       18      78      978     978     1978    6978    156     157     KIAAAA  PBIAAA  HHHHxx
+6160   5450    0       0       0       0       60      160     160     1160    6160    120     121     YCAAAA  QBIAAA  OOOOxx
+9219   5451    1       3       9       19      19      219     1219    4219    9219    38      39      PQAAAA  RBIAAA  VVVVxx
+5034   5452    0       2       4       14      34      34      1034    34      5034    68      69      QLAAAA  SBIAAA  AAAAxx
+8463   5453    1       3       3       3       63      463     463     3463    8463    126     127     NNAAAA  TBIAAA  HHHHxx
+2038   5454    0       2       8       18      38      38      38      2038    2038    76      77      KAAAAA  UBIAAA  OOOOxx
+9562   5455    0       2       2       2       62      562     1562    4562    9562    124     125     UDAAAA  VBIAAA  VVVVxx
+2687   5456    1       3       7       7       87      687     687     2687    2687    174     175     JZAAAA  WBIAAA  AAAAxx
+5092   5457    0       0       2       12      92      92      1092    92      5092    184     185     WNAAAA  XBIAAA  HHHHxx
+539    5458    1       3       9       19      39      539     539     539     539     78      79      TUAAAA  YBIAAA  OOOOxx
+2139   5459    1       3       9       19      39      139     139     2139    2139    78      79      HEAAAA  ZBIAAA  VVVVxx
+9221   5460    1       1       1       1       21      221     1221    4221    9221    42      43      RQAAAA  ACIAAA  AAAAxx
+965    5461    1       1       5       5       65      965     965     965     965     130     131     DLAAAA  BCIAAA  HHHHxx
+6051   5462    1       3       1       11      51      51      51      1051    6051    102     103     TYAAAA  CCIAAA  OOOOxx
+5822   5463    0       2       2       2       22      822     1822    822     5822    44      45      YPAAAA  DCIAAA  VVVVxx
+6397   5464    1       1       7       17      97      397     397     1397    6397    194     195     BMAAAA  ECIAAA  AAAAxx
+2375   5465    1       3       5       15      75      375     375     2375    2375    150     151     JNAAAA  FCIAAA  HHHHxx
+9415   5466    1       3       5       15      15      415     1415    4415    9415    30      31      DYAAAA  GCIAAA  OOOOxx
+6552   5467    0       0       2       12      52      552     552     1552    6552    104     105     ASAAAA  HCIAAA  VVVVxx
+2248   5468    0       0       8       8       48      248     248     2248    2248    96      97      MIAAAA  ICIAAA  AAAAxx
+2611   5469    1       3       1       11      11      611     611     2611    2611    22      23      LWAAAA  JCIAAA  HHHHxx
+9609   5470    1       1       9       9       9       609     1609    4609    9609    18      19      PFAAAA  KCIAAA  OOOOxx
+2132   5471    0       0       2       12      32      132     132     2132    2132    64      65      AEAAAA  LCIAAA  VVVVxx
+8452   5472    0       0       2       12      52      452     452     3452    8452    104     105     CNAAAA  MCIAAA  AAAAxx
+9407   5473    1       3       7       7       7       407     1407    4407    9407    14      15      VXAAAA  NCIAAA  HHHHxx
+2814   5474    0       2       4       14      14      814     814     2814    2814    28      29      GEAAAA  OCIAAA  OOOOxx
+1889   5475    1       1       9       9       89      889     1889    1889    1889    178     179     RUAAAA  PCIAAA  VVVVxx
+7489   5476    1       1       9       9       89      489     1489    2489    7489    178     179     BCAAAA  QCIAAA  AAAAxx
+2255   5477    1       3       5       15      55      255     255     2255    2255    110     111     TIAAAA  RCIAAA  HHHHxx
+3380   5478    0       0       0       0       80      380     1380    3380    3380    160     161     AAAAAA  SCIAAA  OOOOxx
+1167   5479    1       3       7       7       67      167     1167    1167    1167    134     135     XSAAAA  TCIAAA  VVVVxx
+5369   5480    1       1       9       9       69      369     1369    369     5369    138     139     NYAAAA  UCIAAA  AAAAxx
+2378   5481    0       2       8       18      78      378     378     2378    2378    156     157     MNAAAA  VCIAAA  HHHHxx
+8315   5482    1       3       5       15      15      315     315     3315    8315    30      31      VHAAAA  WCIAAA  OOOOxx
+2934   5483    0       2       4       14      34      934     934     2934    2934    68      69      WIAAAA  XCIAAA  VVVVxx
+7924   5484    0       0       4       4       24      924     1924    2924    7924    48      49      USAAAA  YCIAAA  AAAAxx
+2867   5485    1       3       7       7       67      867     867     2867    2867    134     135     HGAAAA  ZCIAAA  HHHHxx
+9141   5486    1       1       1       1       41      141     1141    4141    9141    82      83      PNAAAA  ADIAAA  OOOOxx
+3613   5487    1       1       3       13      13      613     1613    3613    3613    26      27      ZIAAAA  BDIAAA  VVVVxx
+2461   5488    1       1       1       1       61      461     461     2461    2461    122     123     RQAAAA  CDIAAA  AAAAxx
+4567   5489    1       3       7       7       67      567     567     4567    4567    134     135     RTAAAA  DDIAAA  HHHHxx
+2906   5490    0       2       6       6       6       906     906     2906    2906    12      13      UHAAAA  EDIAAA  OOOOxx
+4848   5491    0       0       8       8       48      848     848     4848    4848    96      97      MEAAAA  FDIAAA  VVVVxx
+6614   5492    0       2       4       14      14      614     614     1614    6614    28      29      KUAAAA  GDIAAA  AAAAxx
+6200   5493    0       0       0       0       0       200     200     1200    6200    0       1       MEAAAA  HDIAAA  HHHHxx
+7895   5494    1       3       5       15      95      895     1895    2895    7895    190     191     RRAAAA  IDIAAA  OOOOxx
+6829   5495    1       1       9       9       29      829     829     1829    6829    58      59      RCAAAA  JDIAAA  VVVVxx
+4087   5496    1       3       7       7       87      87      87      4087    4087    174     175     FBAAAA  KDIAAA  AAAAxx
+8787   5497    1       3       7       7       87      787     787     3787    8787    174     175     ZZAAAA  LDIAAA  HHHHxx
+3322   5498    0       2       2       2       22      322     1322    3322    3322    44      45      UXAAAA  MDIAAA  OOOOxx
+9091   5499    1       3       1       11      91      91      1091    4091    9091    182     183     RLAAAA  NDIAAA  VVVVxx
+5268   5500    0       0       8       8       68      268     1268    268     5268    136     137     QUAAAA  ODIAAA  AAAAxx
+2719   5501    1       3       9       19      19      719     719     2719    2719    38      39      PAAAAA  PDIAAA  HHHHxx
+30     5502    0       2       0       10      30      30      30      30      30      60      61      EBAAAA  QDIAAA  OOOOxx
+1975   5503    1       3       5       15      75      975     1975    1975    1975    150     151     ZXAAAA  RDIAAA  VVVVxx
+2641   5504    1       1       1       1       41      641     641     2641    2641    82      83      PXAAAA  SDIAAA  AAAAxx
+8616   5505    0       0       6       16      16      616     616     3616    8616    32      33      KTAAAA  TDIAAA  HHHHxx
+5980   5506    0       0       0       0       80      980     1980    980     5980    160     161     AWAAAA  UDIAAA  OOOOxx
+5170   5507    0       2       0       10      70      170     1170    170     5170    140     141     WQAAAA  VDIAAA  VVVVxx
+1960   5508    0       0       0       0       60      960     1960    1960    1960    120     121     KXAAAA  WDIAAA  AAAAxx
+8141   5509    1       1       1       1       41      141     141     3141    8141    82      83      DBAAAA  XDIAAA  HHHHxx
+6692   5510    0       0       2       12      92      692     692     1692    6692    184     185     KXAAAA  YDIAAA  OOOOxx
+7621   5511    1       1       1       1       21      621     1621    2621    7621    42      43      DHAAAA  ZDIAAA  VVVVxx
+3890   5512    0       2       0       10      90      890     1890    3890    3890    180     181     QTAAAA  AEIAAA  AAAAxx
+4300   5513    0       0       0       0       0       300     300     4300    4300    0       1       KJAAAA  BEIAAA  HHHHxx
+736    5514    0       0       6       16      36      736     736     736     736     72      73      ICAAAA  CEIAAA  OOOOxx
+6626   5515    0       2       6       6       26      626     626     1626    6626    52      53      WUAAAA  DEIAAA  VVVVxx
+1800   5516    0       0       0       0       0       800     1800    1800    1800    0       1       GRAAAA  EEIAAA  AAAAxx
+3430   5517    0       2       0       10      30      430     1430    3430    3430    60      61      YBAAAA  FEIAAA  HHHHxx
+9519   5518    1       3       9       19      19      519     1519    4519    9519    38      39      DCAAAA  GEIAAA  OOOOxx
+5111   5519    1       3       1       11      11      111     1111    111     5111    22      23      POAAAA  HEIAAA  VVVVxx
+6915   5520    1       3       5       15      15      915     915     1915    6915    30      31      ZFAAAA  IEIAAA  AAAAxx
+9246   5521    0       2       6       6       46      246     1246    4246    9246    92      93      QRAAAA  JEIAAA  HHHHxx
+5141   5522    1       1       1       1       41      141     1141    141     5141    82      83      TPAAAA  KEIAAA  OOOOxx
+5922   5523    0       2       2       2       22      922     1922    922     5922    44      45      UTAAAA  LEIAAA  VVVVxx
+3087   5524    1       3       7       7       87      87      1087    3087    3087    174     175     TOAAAA  MEIAAA  AAAAxx
+1859   5525    1       3       9       19      59      859     1859    1859    1859    118     119     NTAAAA  NEIAAA  HHHHxx
+8482   5526    0       2       2       2       82      482     482     3482    8482    164     165     GOAAAA  OEIAAA  OOOOxx
+8414   5527    0       2       4       14      14      414     414     3414    8414    28      29      QLAAAA  PEIAAA  VVVVxx
+6662   5528    0       2       2       2       62      662     662     1662    6662    124     125     GWAAAA  QEIAAA  AAAAxx
+8614   5529    0       2       4       14      14      614     614     3614    8614    28      29      ITAAAA  REIAAA  HHHHxx
+42     5530    0       2       2       2       42      42      42      42      42      84      85      QBAAAA  SEIAAA  OOOOxx
+7582   5531    0       2       2       2       82      582     1582    2582    7582    164     165     QFAAAA  TEIAAA  VVVVxx
+8183   5532    1       3       3       3       83      183     183     3183    8183    166     167     TCAAAA  UEIAAA  AAAAxx
+1299   5533    1       3       9       19      99      299     1299    1299    1299    198     199     ZXAAAA  VEIAAA  HHHHxx
+7004   5534    0       0       4       4       4       4       1004    2004    7004    8       9       KJAAAA  WEIAAA  OOOOxx
+3298   5535    0       2       8       18      98      298     1298    3298    3298    196     197     WWAAAA  XEIAAA  VVVVxx
+7884   5536    0       0       4       4       84      884     1884    2884    7884    168     169     GRAAAA  YEIAAA  AAAAxx
+4191   5537    1       3       1       11      91      191     191     4191    4191    182     183     FFAAAA  ZEIAAA  HHHHxx
+7346   5538    0       2       6       6       46      346     1346    2346    7346    92      93      OWAAAA  AFIAAA  OOOOxx
+7989   5539    1       1       9       9       89      989     1989    2989    7989    178     179     HVAAAA  BFIAAA  VVVVxx
+5719   5540    1       3       9       19      19      719     1719    719     5719    38      39      ZLAAAA  CFIAAA  AAAAxx
+800    5541    0       0       0       0       0       800     800     800     800     0       1       UEAAAA  DFIAAA  HHHHxx
+6509   5542    1       1       9       9       9       509     509     1509    6509    18      19      JQAAAA  EFIAAA  OOOOxx
+4672   5543    0       0       2       12      72      672     672     4672    4672    144     145     SXAAAA  FFIAAA  VVVVxx
+4434   5544    0       2       4       14      34      434     434     4434    4434    68      69      OOAAAA  GFIAAA  AAAAxx
+8309   5545    1       1       9       9       9       309     309     3309    8309    18      19      PHAAAA  HFIAAA  HHHHxx
+5134   5546    0       2       4       14      34      134     1134    134     5134    68      69      MPAAAA  IFIAAA  OOOOxx
+5153   5547    1       1       3       13      53      153     1153    153     5153    106     107     FQAAAA  JFIAAA  VVVVxx
+1522   5548    0       2       2       2       22      522     1522    1522    1522    44      45      OGAAAA  KFIAAA  AAAAxx
+8629   5549    1       1       9       9       29      629     629     3629    8629    58      59      XTAAAA  LFIAAA  HHHHxx
+4549   5550    1       1       9       9       49      549     549     4549    4549    98      99      ZSAAAA  MFIAAA  OOOOxx
+9506   5551    0       2       6       6       6       506     1506    4506    9506    12      13      QBAAAA  NFIAAA  VVVVxx
+6542   5552    0       2       2       2       42      542     542     1542    6542    84      85      QRAAAA  OFIAAA  AAAAxx
+2579   5553    1       3       9       19      79      579     579     2579    2579    158     159     FVAAAA  PFIAAA  HHHHxx
+4664   5554    0       0       4       4       64      664     664     4664    4664    128     129     KXAAAA  QFIAAA  OOOOxx
+696    5555    0       0       6       16      96      696     696     696     696     192     193     UAAAAA  RFIAAA  VVVVxx
+7950   5556    0       2       0       10      50      950     1950    2950    7950    100     101     UTAAAA  SFIAAA  AAAAxx
+5      5557    1       1       5       5       5       5       5       5       5       10      11      FAAAAA  TFIAAA  HHHHxx
+7806   5558    0       2       6       6       6       806     1806    2806    7806    12      13      GOAAAA  UFIAAA  OOOOxx
+2770   5559    0       2       0       10      70      770     770     2770    2770    140     141     OCAAAA  VFIAAA  VVVVxx
+1344   5560    0       0       4       4       44      344     1344    1344    1344    88      89      SZAAAA  WFIAAA  AAAAxx
+511    5561    1       3       1       11      11      511     511     511     511     22      23      RTAAAA  XFIAAA  HHHHxx
+9070   5562    0       2       0       10      70      70      1070    4070    9070    140     141     WKAAAA  YFIAAA  OOOOxx
+2961   5563    1       1       1       1       61      961     961     2961    2961    122     123     XJAAAA  ZFIAAA  VVVVxx
+8031   5564    1       3       1       11      31      31      31      3031    8031    62      63      XWAAAA  AGIAAA  AAAAxx
+326    5565    0       2       6       6       26      326     326     326     326     52      53      OMAAAA  BGIAAA  HHHHxx
+183    5566    1       3       3       3       83      183     183     183     183     166     167     BHAAAA  CGIAAA  OOOOxx
+5917   5567    1       1       7       17      17      917     1917    917     5917    34      35      PTAAAA  DGIAAA  VVVVxx
+8256   5568    0       0       6       16      56      256     256     3256    8256    112     113     OFAAAA  EGIAAA  AAAAxx
+7889   5569    1       1       9       9       89      889     1889    2889    7889    178     179     LRAAAA  FGIAAA  HHHHxx
+9029   5570    1       1       9       9       29      29      1029    4029    9029    58      59      HJAAAA  GGIAAA  OOOOxx
+1316   5571    0       0       6       16      16      316     1316    1316    1316    32      33      QYAAAA  HGIAAA  VVVVxx
+7442   5572    0       2       2       2       42      442     1442    2442    7442    84      85      GAAAAA  IGIAAA  AAAAxx
+2810   5573    0       2       0       10      10      810     810     2810    2810    20      21      CEAAAA  JGIAAA  HHHHxx
+20     5574    0       0       0       0       20      20      20      20      20      40      41      UAAAAA  KGIAAA  OOOOxx
+2306   5575    0       2       6       6       6       306     306     2306    2306    12      13      SKAAAA  LGIAAA  VVVVxx
+4694   5576    0       2       4       14      94      694     694     4694    4694    188     189     OYAAAA  MGIAAA  AAAAxx
+9710   5577    0       2       0       10      10      710     1710    4710    9710    20      21      MJAAAA  NGIAAA  HHHHxx
+1791   5578    1       3       1       11      91      791     1791    1791    1791    182     183     XQAAAA  OGIAAA  OOOOxx
+6730   5579    0       2       0       10      30      730     730     1730    6730    60      61      WYAAAA  PGIAAA  VVVVxx
+359    5580    1       3       9       19      59      359     359     359     359     118     119     VNAAAA  QGIAAA  AAAAxx
+8097   5581    1       1       7       17      97      97      97      3097    8097    194     195     LZAAAA  RGIAAA  HHHHxx
+6147   5582    1       3       7       7       47      147     147     1147    6147    94      95      LCAAAA  SGIAAA  OOOOxx
+643    5583    1       3       3       3       43      643     643     643     643     86      87      TYAAAA  TGIAAA  VVVVxx
+698    5584    0       2       8       18      98      698     698     698     698     196     197     WAAAAA  UGIAAA  AAAAxx
+3881   5585    1       1       1       1       81      881     1881    3881    3881    162     163     HTAAAA  VGIAAA  HHHHxx
+7600   5586    0       0       0       0       0       600     1600    2600    7600    0       1       IGAAAA  WGIAAA  OOOOxx
+1583   5587    1       3       3       3       83      583     1583    1583    1583    166     167     XIAAAA  XGIAAA  VVVVxx
+9612   5588    0       0       2       12      12      612     1612    4612    9612    24      25      SFAAAA  YGIAAA  AAAAxx
+1032   5589    0       0       2       12      32      32      1032    1032    1032    64      65      SNAAAA  ZGIAAA  HHHHxx
+4834   5590    0       2       4       14      34      834     834     4834    4834    68      69      YDAAAA  AHIAAA  OOOOxx
+5076   5591    0       0       6       16      76      76      1076    76      5076    152     153     GNAAAA  BHIAAA  VVVVxx
+3070   5592    0       2       0       10      70      70      1070    3070    3070    140     141     COAAAA  CHIAAA  AAAAxx
+1421   5593    1       1       1       1       21      421     1421    1421    1421    42      43      RCAAAA  DHIAAA  HHHHxx
+8970   5594    0       2       0       10      70      970     970     3970    8970    140     141     AHAAAA  EHIAAA  OOOOxx
+6271   5595    1       3       1       11      71      271     271     1271    6271    142     143     FHAAAA  FHIAAA  VVVVxx
+8547   5596    1       3       7       7       47      547     547     3547    8547    94      95      TQAAAA  GHIAAA  AAAAxx
+1259   5597    1       3       9       19      59      259     1259    1259    1259    118     119     LWAAAA  HHIAAA  HHHHxx
+8328   5598    0       0       8       8       28      328     328     3328    8328    56      57      IIAAAA  IHIAAA  OOOOxx
+1503   5599    1       3       3       3       3       503     1503    1503    1503    6       7       VFAAAA  JHIAAA  VVVVxx
+2253   5600    1       1       3       13      53      253     253     2253    2253    106     107     RIAAAA  KHIAAA  AAAAxx
+7449   5601    1       1       9       9       49      449     1449    2449    7449    98      99      NAAAAA  LHIAAA  HHHHxx
+3579   5602    1       3       9       19      79      579     1579    3579    3579    158     159     RHAAAA  MHIAAA  OOOOxx
+1585   5603    1       1       5       5       85      585     1585    1585    1585    170     171     ZIAAAA  NHIAAA  VVVVxx
+5543   5604    1       3       3       3       43      543     1543    543     5543    86      87      FFAAAA  OHIAAA  AAAAxx
+8627   5605    1       3       7       7       27      627     627     3627    8627    54      55      VTAAAA  PHIAAA  HHHHxx
+8618   5606    0       2       8       18      18      618     618     3618    8618    36      37      MTAAAA  QHIAAA  OOOOxx
+1911   5607    1       3       1       11      11      911     1911    1911    1911    22      23      NVAAAA  RHIAAA  VVVVxx
+2758   5608    0       2       8       18      58      758     758     2758    2758    116     117     CCAAAA  SHIAAA  AAAAxx
+5744   5609    0       0       4       4       44      744     1744    744     5744    88      89      YMAAAA  THIAAA  HHHHxx
+4976   5610    0       0       6       16      76      976     976     4976    4976    152     153     KJAAAA  UHIAAA  OOOOxx
+6380   5611    0       0       0       0       80      380     380     1380    6380    160     161     KLAAAA  VHIAAA  VVVVxx
+1937   5612    1       1       7       17      37      937     1937    1937    1937    74      75      NWAAAA  WHIAAA  AAAAxx
+9903   5613    1       3       3       3       3       903     1903    4903    9903    6       7       XQAAAA  XHIAAA  HHHHxx
+4409   5614    1       1       9       9       9       409     409     4409    4409    18      19      PNAAAA  YHIAAA  OOOOxx
+4133   5615    1       1       3       13      33      133     133     4133    4133    66      67      ZCAAAA  ZHIAAA  VVVVxx
+5263   5616    1       3       3       3       63      263     1263    263     5263    126     127     LUAAAA  AIIAAA  AAAAxx
+7888   5617    0       0       8       8       88      888     1888    2888    7888    176     177     KRAAAA  BIIAAA  HHHHxx
+6060   5618    0       0       0       0       60      60      60      1060    6060    120     121     CZAAAA  CIIAAA  OOOOxx
+2522   5619    0       2       2       2       22      522     522     2522    2522    44      45      ATAAAA  DIIAAA  VVVVxx
+5550   5620    0       2       0       10      50      550     1550    550     5550    100     101     MFAAAA  EIIAAA  AAAAxx
+9396   5621    0       0       6       16      96      396     1396    4396    9396    192     193     KXAAAA  FIIAAA  HHHHxx
+176    5622    0       0       6       16      76      176     176     176     176     152     153     UGAAAA  GIIAAA  OOOOxx
+5148   5623    0       0       8       8       48      148     1148    148     5148    96      97      AQAAAA  HIIAAA  VVVVxx
+6691   5624    1       3       1       11      91      691     691     1691    6691    182     183     JXAAAA  IIIAAA  AAAAxx
+4652   5625    0       0       2       12      52      652     652     4652    4652    104     105     YWAAAA  JIIAAA  HHHHxx
+5096   5626    0       0       6       16      96      96      1096    96      5096    192     193     AOAAAA  KIIAAA  OOOOxx
+2408   5627    0       0       8       8       8       408     408     2408    2408    16      17      QOAAAA  LIIAAA  VVVVxx
+7322   5628    0       2       2       2       22      322     1322    2322    7322    44      45      QVAAAA  MIIAAA  AAAAxx
+6782   5629    0       2       2       2       82      782     782     1782    6782    164     165     WAAAAA  NIIAAA  HHHHxx
+4642   5630    0       2       2       2       42      642     642     4642    4642    84      85      OWAAAA  OIIAAA  OOOOxx
+5427   5631    1       3       7       7       27      427     1427    427     5427    54      55      TAAAAA  PIIAAA  VVVVxx
+4461   5632    1       1       1       1       61      461     461     4461    4461    122     123     PPAAAA  QIIAAA  AAAAxx
+8416   5633    0       0       6       16      16      416     416     3416    8416    32      33      SLAAAA  RIIAAA  HHHHxx
+2593   5634    1       1       3       13      93      593     593     2593    2593    186     187     TVAAAA  SIIAAA  OOOOxx
+6202   5635    0       2       2       2       2       202     202     1202    6202    4       5       OEAAAA  TIIAAA  VVVVxx
+3826   5636    0       2       6       6       26      826     1826    3826    3826    52      53      ERAAAA  UIIAAA  AAAAxx
+4417   5637    1       1       7       17      17      417     417     4417    4417    34      35      XNAAAA  VIIAAA  HHHHxx
+7871   5638    1       3       1       11      71      871     1871    2871    7871    142     143     TQAAAA  WIIAAA  OOOOxx
+5622   5639    0       2       2       2       22      622     1622    622     5622    44      45      GIAAAA  XIIAAA  VVVVxx
+3010   5640    0       2       0       10      10      10      1010    3010    3010    20      21      ULAAAA  YIIAAA  AAAAxx
+3407   5641    1       3       7       7       7       407     1407    3407    3407    14      15      BBAAAA  ZIIAAA  HHHHxx
+1274   5642    0       2       4       14      74      274     1274    1274    1274    148     149     AXAAAA  AJIAAA  OOOOxx
+2828   5643    0       0       8       8       28      828     828     2828    2828    56      57      UEAAAA  BJIAAA  VVVVxx
+3427   5644    1       3       7       7       27      427     1427    3427    3427    54      55      VBAAAA  CJIAAA  AAAAxx
+612    5645    0       0       2       12      12      612     612     612     612     24      25      OXAAAA  DJIAAA  HHHHxx
+8729   5646    1       1       9       9       29      729     729     3729    8729    58      59      TXAAAA  EJIAAA  OOOOxx
+1239   5647    1       3       9       19      39      239     1239    1239    1239    78      79      RVAAAA  FJIAAA  VVVVxx
+8990   5648    0       2       0       10      90      990     990     3990    8990    180     181     UHAAAA  GJIAAA  AAAAxx
+5609   5649    1       1       9       9       9       609     1609    609     5609    18      19      THAAAA  HJIAAA  HHHHxx
+4441   5650    1       1       1       1       41      441     441     4441    4441    82      83      VOAAAA  IJIAAA  OOOOxx
+9078   5651    0       2       8       18      78      78      1078    4078    9078    156     157     ELAAAA  JJIAAA  VVVVxx
+6699   5652    1       3       9       19      99      699     699     1699    6699    198     199     RXAAAA  KJIAAA  AAAAxx
+8390   5653    0       2       0       10      90      390     390     3390    8390    180     181     SKAAAA  LJIAAA  HHHHxx
+5455   5654    1       3       5       15      55      455     1455    455     5455    110     111     VBAAAA  MJIAAA  OOOOxx
+7537   5655    1       1       7       17      37      537     1537    2537    7537    74      75      XDAAAA  NJIAAA  VVVVxx
+4669   5656    1       1       9       9       69      669     669     4669    4669    138     139     PXAAAA  OJIAAA  AAAAxx
+5534   5657    0       2       4       14      34      534     1534    534     5534    68      69      WEAAAA  PJIAAA  HHHHxx
+1920   5658    0       0       0       0       20      920     1920    1920    1920    40      41      WVAAAA  QJIAAA  OOOOxx
+9465   5659    1       1       5       5       65      465     1465    4465    9465    130     131     BAAAAA  RJIAAA  VVVVxx
+4897   5660    1       1       7       17      97      897     897     4897    4897    194     195     JGAAAA  SJIAAA  AAAAxx
+1990   5661    0       2       0       10      90      990     1990    1990    1990    180     181     OYAAAA  TJIAAA  HHHHxx
+7148   5662    0       0       8       8       48      148     1148    2148    7148    96      97      YOAAAA  UJIAAA  OOOOxx
+533    5663    1       1       3       13      33      533     533     533     533     66      67      NUAAAA  VJIAAA  VVVVxx
+4339   5664    1       3       9       19      39      339     339     4339    4339    78      79      XKAAAA  WJIAAA  AAAAxx
+6450   5665    0       2       0       10      50      450     450     1450    6450    100     101     COAAAA  XJIAAA  HHHHxx
+9627   5666    1       3       7       7       27      627     1627    4627    9627    54      55      HGAAAA  YJIAAA  OOOOxx
+5539   5667    1       3       9       19      39      539     1539    539     5539    78      79      BFAAAA  ZJIAAA  VVVVxx
+6758   5668    0       2       8       18      58      758     758     1758    6758    116     117     YZAAAA  AKIAAA  AAAAxx
+3435   5669    1       3       5       15      35      435     1435    3435    3435    70      71      DCAAAA  BKIAAA  HHHHxx
+4350   5670    0       2       0       10      50      350     350     4350    4350    100     101     ILAAAA  CKIAAA  OOOOxx
+9088   5671    0       0       8       8       88      88      1088    4088    9088    176     177     OLAAAA  DKIAAA  VVVVxx
+6368   5672    0       0       8       8       68      368     368     1368    6368    136     137     YKAAAA  EKIAAA  AAAAxx
+6337   5673    1       1       7       17      37      337     337     1337    6337    74      75      TJAAAA  FKIAAA  HHHHxx
+4361   5674    1       1       1       1       61      361     361     4361    4361    122     123     TLAAAA  GKIAAA  OOOOxx
+1719   5675    1       3       9       19      19      719     1719    1719    1719    38      39      DOAAAA  HKIAAA  VVVVxx
+3109   5676    1       1       9       9       9       109     1109    3109    3109    18      19      PPAAAA  IKIAAA  AAAAxx
+7135   5677    1       3       5       15      35      135     1135    2135    7135    70      71      LOAAAA  JKIAAA  HHHHxx
+1964   5678    0       0       4       4       64      964     1964    1964    1964    128     129     OXAAAA  KKIAAA  OOOOxx
+3      5679    1       3       3       3       3       3       3       3       3       6       7       DAAAAA  LKIAAA  VVVVxx
+1868   5680    0       0       8       8       68      868     1868    1868    1868    136     137     WTAAAA  MKIAAA  AAAAxx
+5182   5681    0       2       2       2       82      182     1182    182     5182    164     165     IRAAAA  NKIAAA  HHHHxx
+7567   5682    1       3       7       7       67      567     1567    2567    7567    134     135     BFAAAA  OKIAAA  OOOOxx
+3676   5683    0       0       6       16      76      676     1676    3676    3676    152     153     KLAAAA  PKIAAA  VVVVxx
+9382   5684    0       2       2       2       82      382     1382    4382    9382    164     165     WWAAAA  QKIAAA  AAAAxx
+8645   5685    1       1       5       5       45      645     645     3645    8645    90      91      NUAAAA  RKIAAA  HHHHxx
+2018   5686    0       2       8       18      18      18      18      2018    2018    36      37      QZAAAA  SKIAAA  OOOOxx
+217    5687    1       1       7       17      17      217     217     217     217     34      35      JIAAAA  TKIAAA  VVVVxx
+6793   5688    1       1       3       13      93      793     793     1793    6793    186     187     HBAAAA  UKIAAA  AAAAxx
+7280   5689    0       0       0       0       80      280     1280    2280    7280    160     161     AUAAAA  VKIAAA  HHHHxx
+2168   5690    0       0       8       8       68      168     168     2168    2168    136     137     KFAAAA  WKIAAA  OOOOxx
+5259   5691    1       3       9       19      59      259     1259    259     5259    118     119     HUAAAA  XKIAAA  VVVVxx
+6019   5692    1       3       9       19      19      19      19      1019    6019    38      39      NXAAAA  YKIAAA  AAAAxx
+877    5693    1       1       7       17      77      877     877     877     877     154     155     THAAAA  ZKIAAA  HHHHxx
+4961   5694    1       1       1       1       61      961     961     4961    4961    122     123     VIAAAA  ALIAAA  OOOOxx
+1873   5695    1       1       3       13      73      873     1873    1873    1873    146     147     BUAAAA  BLIAAA  VVVVxx
+13     5696    1       1       3       13      13      13      13      13      13      26      27      NAAAAA  CLIAAA  AAAAxx
+1537   5697    1       1       7       17      37      537     1537    1537    1537    74      75      DHAAAA  DLIAAA  HHHHxx
+3129   5698    1       1       9       9       29      129     1129    3129    3129    58      59      JQAAAA  ELIAAA  OOOOxx
+6473   5699    1       1       3       13      73      473     473     1473    6473    146     147     ZOAAAA  FLIAAA  VVVVxx
+7865   5700    1       1       5       5       65      865     1865    2865    7865    130     131     NQAAAA  GLIAAA  AAAAxx
+7822   5701    0       2       2       2       22      822     1822    2822    7822    44      45      WOAAAA  HLIAAA  HHHHxx
+239    5702    1       3       9       19      39      239     239     239     239     78      79      FJAAAA  ILIAAA  OOOOxx
+2062   5703    0       2       2       2       62      62      62      2062    2062    124     125     IBAAAA  JLIAAA  VVVVxx
+762    5704    0       2       2       2       62      762     762     762     762     124     125     IDAAAA  KLIAAA  AAAAxx
+3764   5705    0       0       4       4       64      764     1764    3764    3764    128     129     UOAAAA  LLIAAA  HHHHxx
+465    5706    1       1       5       5       65      465     465     465     465     130     131     XRAAAA  MLIAAA  OOOOxx
+2587   5707    1       3       7       7       87      587     587     2587    2587    174     175     NVAAAA  NLIAAA  VVVVxx
+8402   5708    0       2       2       2       2       402     402     3402    8402    4       5       ELAAAA  OLIAAA  AAAAxx
+1055   5709    1       3       5       15      55      55      1055    1055    1055    110     111     POAAAA  PLIAAA  HHHHxx
+3072   5710    0       0       2       12      72      72      1072    3072    3072    144     145     EOAAAA  QLIAAA  OOOOxx
+7359   5711    1       3       9       19      59      359     1359    2359    7359    118     119     BXAAAA  RLIAAA  VVVVxx
+6558   5712    0       2       8       18      58      558     558     1558    6558    116     117     GSAAAA  SLIAAA  AAAAxx
+48     5713    0       0       8       8       48      48      48      48      48      96      97      WBAAAA  TLIAAA  HHHHxx
+5382   5714    0       2       2       2       82      382     1382    382     5382    164     165     AZAAAA  ULIAAA  OOOOxx
+947    5715    1       3       7       7       47      947     947     947     947     94      95      LKAAAA  VLIAAA  VVVVxx
+2644   5716    0       0       4       4       44      644     644     2644    2644    88      89      SXAAAA  WLIAAA  AAAAxx
+7516   5717    0       0       6       16      16      516     1516    2516    7516    32      33      CDAAAA  XLIAAA  HHHHxx
+2362   5718    0       2       2       2       62      362     362     2362    2362    124     125     WMAAAA  YLIAAA  OOOOxx
+839    5719    1       3       9       19      39      839     839     839     839     78      79      HGAAAA  ZLIAAA  VVVVxx
+2216   5720    0       0       6       16      16      216     216     2216    2216    32      33      GHAAAA  AMIAAA  AAAAxx
+7673   5721    1       1       3       13      73      673     1673    2673    7673    146     147     DJAAAA  BMIAAA  HHHHxx
+8173   5722    1       1       3       13      73      173     173     3173    8173    146     147     JCAAAA  CMIAAA  OOOOxx
+1630   5723    0       2       0       10      30      630     1630    1630    1630    60      61      SKAAAA  DMIAAA  VVVVxx
+9057   5724    1       1       7       17      57      57      1057    4057    9057    114     115     JKAAAA  EMIAAA  AAAAxx
+4392   5725    0       0       2       12      92      392     392     4392    4392    184     185     YMAAAA  FMIAAA  HHHHxx
+3695   5726    1       3       5       15      95      695     1695    3695    3695    190     191     DMAAAA  GMIAAA  OOOOxx
+5751   5727    1       3       1       11      51      751     1751    751     5751    102     103     FNAAAA  HMIAAA  VVVVxx
+5745   5728    1       1       5       5       45      745     1745    745     5745    90      91      ZMAAAA  IMIAAA  AAAAxx
+7945   5729    1       1       5       5       45      945     1945    2945    7945    90      91      PTAAAA  JMIAAA  HHHHxx
+5174   5730    0       2       4       14      74      174     1174    174     5174    148     149     ARAAAA  KMIAAA  OOOOxx
+3829   5731    1       1       9       9       29      829     1829    3829    3829    58      59      HRAAAA  LMIAAA  VVVVxx
+3317   5732    1       1       7       17      17      317     1317    3317    3317    34      35      PXAAAA  MMIAAA  AAAAxx
+4253   5733    1       1       3       13      53      253     253     4253    4253    106     107     PHAAAA  NMIAAA  HHHHxx
+1291   5734    1       3       1       11      91      291     1291    1291    1291    182     183     RXAAAA  OMIAAA  OOOOxx
+3266   5735    0       2       6       6       66      266     1266    3266    3266    132     133     QVAAAA  PMIAAA  VVVVxx
+2939   5736    1       3       9       19      39      939     939     2939    2939    78      79      BJAAAA  QMIAAA  AAAAxx
+2755   5737    1       3       5       15      55      755     755     2755    2755    110     111     ZBAAAA  RMIAAA  HHHHxx
+6844   5738    0       0       4       4       44      844     844     1844    6844    88      89      GDAAAA  SMIAAA  OOOOxx
+8594   5739    0       2       4       14      94      594     594     3594    8594    188     189     OSAAAA  TMIAAA  VVVVxx
+704    5740    0       0       4       4       4       704     704     704     704     8       9       CBAAAA  UMIAAA  AAAAxx
+1681   5741    1       1       1       1       81      681     1681    1681    1681    162     163     RMAAAA  VMIAAA  HHHHxx
+364    5742    0       0       4       4       64      364     364     364     364     128     129     AOAAAA  WMIAAA  OOOOxx
+2928   5743    0       0       8       8       28      928     928     2928    2928    56      57      QIAAAA  XMIAAA  VVVVxx
+117    5744    1       1       7       17      17      117     117     117     117     34      35      NEAAAA  YMIAAA  AAAAxx
+96     5745    0       0       6       16      96      96      96      96      96      192     193     SDAAAA  ZMIAAA  HHHHxx
+7796   5746    0       0       6       16      96      796     1796    2796    7796    192     193     WNAAAA  ANIAAA  OOOOxx
+3101   5747    1       1       1       1       1       101     1101    3101    3101    2       3       HPAAAA  BNIAAA  VVVVxx
+3397   5748    1       1       7       17      97      397     1397    3397    3397    194     195     RAAAAA  CNIAAA  AAAAxx
+1605   5749    1       1       5       5       5       605     1605    1605    1605    10      11      TJAAAA  DNIAAA  HHHHxx
+4881   5750    1       1       1       1       81      881     881     4881    4881    162     163     TFAAAA  ENIAAA  OOOOxx
+4521   5751    1       1       1       1       21      521     521     4521    4521    42      43      XRAAAA  FNIAAA  VVVVxx
+6430   5752    0       2       0       10      30      430     430     1430    6430    60      61      INAAAA  GNIAAA  AAAAxx
+282    5753    0       2       2       2       82      282     282     282     282     164     165     WKAAAA  HNIAAA  HHHHxx
+9645   5754    1       1       5       5       45      645     1645    4645    9645    90      91      ZGAAAA  INIAAA  OOOOxx
+8946   5755    0       2       6       6       46      946     946     3946    8946    92      93      CGAAAA  JNIAAA  VVVVxx
+5064   5756    0       0       4       4       64      64      1064    64      5064    128     129     UMAAAA  KNIAAA  AAAAxx
+7470   5757    0       2       0       10      70      470     1470    2470    7470    140     141     IBAAAA  LNIAAA  HHHHxx
+5886   5758    0       2       6       6       86      886     1886    886     5886    172     173     KSAAAA  MNIAAA  OOOOxx
+6280   5759    0       0       0       0       80      280     280     1280    6280    160     161     OHAAAA  NNIAAA  VVVVxx
+5247   5760    1       3       7       7       47      247     1247    247     5247    94      95      VTAAAA  ONIAAA  AAAAxx
+412    5761    0       0       2       12      12      412     412     412     412     24      25      WPAAAA  PNIAAA  HHHHxx
+5342   5762    0       2       2       2       42      342     1342    342     5342    84      85      MXAAAA  QNIAAA  OOOOxx
+2271   5763    1       3       1       11      71      271     271     2271    2271    142     143     JJAAAA  RNIAAA  VVVVxx
+849    5764    1       1       9       9       49      849     849     849     849     98      99      RGAAAA  SNIAAA  AAAAxx
+1885   5765    1       1       5       5       85      885     1885    1885    1885    170     171     NUAAAA  TNIAAA  HHHHxx
+5620   5766    0       0       0       0       20      620     1620    620     5620    40      41      EIAAAA  UNIAAA  OOOOxx
+7079   5767    1       3       9       19      79      79      1079    2079    7079    158     159     HMAAAA  VNIAAA  VVVVxx
+5819   5768    1       3       9       19      19      819     1819    819     5819    38      39      VPAAAA  WNIAAA  AAAAxx
+7497   5769    1       1       7       17      97      497     1497    2497    7497    194     195     JCAAAA  XNIAAA  HHHHxx
+5993   5770    1       1       3       13      93      993     1993    993     5993    186     187     NWAAAA  YNIAAA  OOOOxx
+3739   5771    1       3       9       19      39      739     1739    3739    3739    78      79      VNAAAA  ZNIAAA  VVVVxx
+6296   5772    0       0       6       16      96      296     296     1296    6296    192     193     EIAAAA  AOIAAA  AAAAxx
+2716   5773    0       0       6       16      16      716     716     2716    2716    32      33      MAAAAA  BOIAAA  HHHHxx
+1130   5774    0       2       0       10      30      130     1130    1130    1130    60      61      MRAAAA  COIAAA  OOOOxx
+5593   5775    1       1       3       13      93      593     1593    593     5593    186     187     DHAAAA  DOIAAA  VVVVxx
+6972   5776    0       0       2       12      72      972     972     1972    6972    144     145     EIAAAA  EOIAAA  AAAAxx
+8360   5777    0       0       0       0       60      360     360     3360    8360    120     121     OJAAAA  FOIAAA  HHHHxx
+6448   5778    0       0       8       8       48      448     448     1448    6448    96      97      AOAAAA  GOIAAA  OOOOxx
+3689   5779    1       1       9       9       89      689     1689    3689    3689    178     179     XLAAAA  HOIAAA  VVVVxx
+7951   5780    1       3       1       11      51      951     1951    2951    7951    102     103     VTAAAA  IOIAAA  AAAAxx
+2974   5781    0       2       4       14      74      974     974     2974    2974    148     149     KKAAAA  JOIAAA  HHHHxx
+6600   5782    0       0       0       0       0       600     600     1600    6600    0       1       WTAAAA  KOIAAA  OOOOxx
+4662   5783    0       2       2       2       62      662     662     4662    4662    124     125     IXAAAA  LOIAAA  VVVVxx
+4765   5784    1       1       5       5       65      765     765     4765    4765    130     131     HBAAAA  MOIAAA  AAAAxx
+355    5785    1       3       5       15      55      355     355     355     355     110     111     RNAAAA  NOIAAA  HHHHxx
+6228   5786    0       0       8       8       28      228     228     1228    6228    56      57      OFAAAA  OOIAAA  OOOOxx
+964    5787    0       0       4       4       64      964     964     964     964     128     129     CLAAAA  POIAAA  VVVVxx
+3082   5788    0       2       2       2       82      82      1082    3082    3082    164     165     OOAAAA  QOIAAA  AAAAxx
+7028   5789    0       0       8       8       28      28      1028    2028    7028    56      57      IKAAAA  ROIAAA  HHHHxx
+4505   5790    1       1       5       5       5       505     505     4505    4505    10      11      HRAAAA  SOIAAA  OOOOxx
+8961   5791    1       1       1       1       61      961     961     3961    8961    122     123     RGAAAA  TOIAAA  VVVVxx
+9571   5792    1       3       1       11      71      571     1571    4571    9571    142     143     DEAAAA  UOIAAA  AAAAxx
+9394   5793    0       2       4       14      94      394     1394    4394    9394    188     189     IXAAAA  VOIAAA  HHHHxx
+4245   5794    1       1       5       5       45      245     245     4245    4245    90      91      HHAAAA  WOIAAA  OOOOxx
+7560   5795    0       0       0       0       60      560     1560    2560    7560    120     121     UEAAAA  XOIAAA  VVVVxx
+2907   5796    1       3       7       7       7       907     907     2907    2907    14      15      VHAAAA  YOIAAA  AAAAxx
+7817   5797    1       1       7       17      17      817     1817    2817    7817    34      35      ROAAAA  ZOIAAA  HHHHxx
+5408   5798    0       0       8       8       8       408     1408    408     5408    16      17      AAAAAA  APIAAA  OOOOxx
+8092   5799    0       0       2       12      92      92      92      3092    8092    184     185     GZAAAA  BPIAAA  VVVVxx
+1309   5800    1       1       9       9       9       309     1309    1309    1309    18      19      JYAAAA  CPIAAA  AAAAxx
+6673   5801    1       1       3       13      73      673     673     1673    6673    146     147     RWAAAA  DPIAAA  HHHHxx
+1245   5802    1       1       5       5       45      245     1245    1245    1245    90      91      XVAAAA  EPIAAA  OOOOxx
+6790   5803    0       2       0       10      90      790     790     1790    6790    180     181     EBAAAA  FPIAAA  VVVVxx
+8380   5804    0       0       0       0       80      380     380     3380    8380    160     161     IKAAAA  GPIAAA  AAAAxx
+5786   5805    0       2       6       6       86      786     1786    786     5786    172     173     OOAAAA  HPIAAA  HHHHxx
+9590   5806    0       2       0       10      90      590     1590    4590    9590    180     181     WEAAAA  IPIAAA  OOOOxx
+5763   5807    1       3       3       3       63      763     1763    763     5763    126     127     RNAAAA  JPIAAA  VVVVxx
+1345   5808    1       1       5       5       45      345     1345    1345    1345    90      91      TZAAAA  KPIAAA  AAAAxx
+3480   5809    0       0       0       0       80      480     1480    3480    3480    160     161     WDAAAA  LPIAAA  HHHHxx
+7864   5810    0       0       4       4       64      864     1864    2864    7864    128     129     MQAAAA  MPIAAA  OOOOxx
+4853   5811    1       1       3       13      53      853     853     4853    4853    106     107     REAAAA  NPIAAA  VVVVxx
+1445   5812    1       1       5       5       45      445     1445    1445    1445    90      91      PDAAAA  OPIAAA  AAAAxx
+170    5813    0       2       0       10      70      170     170     170     170     140     141     OGAAAA  PPIAAA  HHHHxx
+7348   5814    0       0       8       8       48      348     1348    2348    7348    96      97      QWAAAA  QPIAAA  OOOOxx
+3920   5815    0       0       0       0       20      920     1920    3920    3920    40      41      UUAAAA  RPIAAA  VVVVxx
+3307   5816    1       3       7       7       7       307     1307    3307    3307    14      15      FXAAAA  SPIAAA  AAAAxx
+4584   5817    0       0       4       4       84      584     584     4584    4584    168     169     IUAAAA  TPIAAA  HHHHxx
+3344   5818    0       0       4       4       44      344     1344    3344    3344    88      89      QYAAAA  UPIAAA  OOOOxx
+4360   5819    0       0       0       0       60      360     360     4360    4360    120     121     SLAAAA  VPIAAA  VVVVxx
+8757   5820    1       1       7       17      57      757     757     3757    8757    114     115     VYAAAA  WPIAAA  AAAAxx
+4315   5821    1       3       5       15      15      315     315     4315    4315    30      31      ZJAAAA  XPIAAA  HHHHxx
+5243   5822    1       3       3       3       43      243     1243    243     5243    86      87      RTAAAA  YPIAAA  OOOOxx
+8550   5823    0       2       0       10      50      550     550     3550    8550    100     101     WQAAAA  ZPIAAA  VVVVxx
+159    5824    1       3       9       19      59      159     159     159     159     118     119     DGAAAA  AQIAAA  AAAAxx
+4710   5825    0       2       0       10      10      710     710     4710    4710    20      21      EZAAAA  BQIAAA  HHHHxx
+7179   5826    1       3       9       19      79      179     1179    2179    7179    158     159     DQAAAA  CQIAAA  OOOOxx
+2509   5827    1       1       9       9       9       509     509     2509    2509    18      19      NSAAAA  DQIAAA  VVVVxx
+6981   5828    1       1       1       1       81      981     981     1981    6981    162     163     NIAAAA  EQIAAA  AAAAxx
+5060   5829    0       0       0       0       60      60      1060    60      5060    120     121     QMAAAA  FQIAAA  HHHHxx
+5601   5830    1       1       1       1       1       601     1601    601     5601    2       3       LHAAAA  GQIAAA  OOOOxx
+703    5831    1       3       3       3       3       703     703     703     703     6       7       BBAAAA  HQIAAA  VVVVxx
+8719   5832    1       3       9       19      19      719     719     3719    8719    38      39      JXAAAA  IQIAAA  AAAAxx
+1570   5833    0       2       0       10      70      570     1570    1570    1570    140     141     KIAAAA  JQIAAA  HHHHxx
+1036   5834    0       0       6       16      36      36      1036    1036    1036    72      73      WNAAAA  KQIAAA  OOOOxx
+6703   5835    1       3       3       3       3       703     703     1703    6703    6       7       VXAAAA  LQIAAA  VVVVxx
+252    5836    0       0       2       12      52      252     252     252     252     104     105     SJAAAA  MQIAAA  AAAAxx
+631    5837    1       3       1       11      31      631     631     631     631     62      63      HYAAAA  NQIAAA  HHHHxx
+5098   5838    0       2       8       18      98      98      1098    98      5098    196     197     COAAAA  OQIAAA  OOOOxx
+8346   5839    0       2       6       6       46      346     346     3346    8346    92      93      AJAAAA  PQIAAA  VVVVxx
+4910   5840    0       2       0       10      10      910     910     4910    4910    20      21      WGAAAA  QQIAAA  AAAAxx
+559    5841    1       3       9       19      59      559     559     559     559     118     119     NVAAAA  RQIAAA  HHHHxx
+1477   5842    1       1       7       17      77      477     1477    1477    1477    154     155     VEAAAA  SQIAAA  OOOOxx
+5115   5843    1       3       5       15      15      115     1115    115     5115    30      31      TOAAAA  TQIAAA  VVVVxx
+8784   5844    0       0       4       4       84      784     784     3784    8784    168     169     WZAAAA  UQIAAA  AAAAxx
+4422   5845    0       2       2       2       22      422     422     4422    4422    44      45      COAAAA  VQIAAA  HHHHxx
+2702   5846    0       2       2       2       2       702     702     2702    2702    4       5       YZAAAA  WQIAAA  OOOOxx
+9599   5847    1       3       9       19      99      599     1599    4599    9599    198     199     FFAAAA  XQIAAA  VVVVxx
+2463   5848    1       3       3       3       63      463     463     2463    2463    126     127     TQAAAA  YQIAAA  AAAAxx
+498    5849    0       2       8       18      98      498     498     498     498     196     197     ETAAAA  ZQIAAA  HHHHxx
+494    5850    0       2       4       14      94      494     494     494     494     188     189     ATAAAA  ARIAAA  OOOOxx
+8632   5851    0       0       2       12      32      632     632     3632    8632    64      65      AUAAAA  BRIAAA  VVVVxx
+3449   5852    1       1       9       9       49      449     1449    3449    3449    98      99      RCAAAA  CRIAAA  AAAAxx
+5888   5853    0       0       8       8       88      888     1888    888     5888    176     177     MSAAAA  DRIAAA  HHHHxx
+2211   5854    1       3       1       11      11      211     211     2211    2211    22      23      BHAAAA  ERIAAA  OOOOxx
+2835   5855    1       3       5       15      35      835     835     2835    2835    70      71      BFAAAA  FRIAAA  VVVVxx
+4196   5856    0       0       6       16      96      196     196     4196    4196    192     193     KFAAAA  GRIAAA  AAAAxx
+2177   5857    1       1       7       17      77      177     177     2177    2177    154     155     TFAAAA  HRIAAA  HHHHxx
+1959   5858    1       3       9       19      59      959     1959    1959    1959    118     119     JXAAAA  IRIAAA  OOOOxx
+5172   5859    0       0       2       12      72      172     1172    172     5172    144     145     YQAAAA  JRIAAA  VVVVxx
+7898   5860    0       2       8       18      98      898     1898    2898    7898    196     197     URAAAA  KRIAAA  AAAAxx
+5729   5861    1       1       9       9       29      729     1729    729     5729    58      59      JMAAAA  LRIAAA  HHHHxx
+469    5862    1       1       9       9       69      469     469     469     469     138     139     BSAAAA  MRIAAA  OOOOxx
+4456   5863    0       0       6       16      56      456     456     4456    4456    112     113     KPAAAA  NRIAAA  VVVVxx
+3578   5864    0       2       8       18      78      578     1578    3578    3578    156     157     QHAAAA  ORIAAA  AAAAxx
+8623   5865    1       3       3       3       23      623     623     3623    8623    46      47      RTAAAA  PRIAAA  HHHHxx
+6749   5866    1       1       9       9       49      749     749     1749    6749    98      99      PZAAAA  QRIAAA  OOOOxx
+6735   5867    1       3       5       15      35      735     735     1735    6735    70      71      BZAAAA  RRIAAA  VVVVxx
+5197   5868    1       1       7       17      97      197     1197    197     5197    194     195     XRAAAA  SRIAAA  AAAAxx
+2067   5869    1       3       7       7       67      67      67      2067    2067    134     135     NBAAAA  TRIAAA  HHHHxx
+5600   5870    0       0       0       0       0       600     1600    600     5600    0       1       KHAAAA  URIAAA  OOOOxx
+7741   5871    1       1       1       1       41      741     1741    2741    7741    82      83      TLAAAA  VRIAAA  VVVVxx
+9925   5872    1       1       5       5       25      925     1925    4925    9925    50      51      TRAAAA  WRIAAA  AAAAxx
+9685   5873    1       1       5       5       85      685     1685    4685    9685    170     171     NIAAAA  XRIAAA  HHHHxx
+7622   5874    0       2       2       2       22      622     1622    2622    7622    44      45      EHAAAA  YRIAAA  OOOOxx
+6859   5875    1       3       9       19      59      859     859     1859    6859    118     119     VDAAAA  ZRIAAA  VVVVxx
+3094   5876    0       2       4       14      94      94      1094    3094    3094    188     189     APAAAA  ASIAAA  AAAAxx
+2628   5877    0       0       8       8       28      628     628     2628    2628    56      57      CXAAAA  BSIAAA  HHHHxx
+40     5878    0       0       0       0       40      40      40      40      40      80      81      OBAAAA  CSIAAA  OOOOxx
+1644   5879    0       0       4       4       44      644     1644    1644    1644    88      89      GLAAAA  DSIAAA  VVVVxx
+588    5880    0       0       8       8       88      588     588     588     588     176     177     QWAAAA  ESIAAA  AAAAxx
+7522   5881    0       2       2       2       22      522     1522    2522    7522    44      45      IDAAAA  FSIAAA  HHHHxx
+162    5882    0       2       2       2       62      162     162     162     162     124     125     GGAAAA  GSIAAA  OOOOxx
+3610   5883    0       2       0       10      10      610     1610    3610    3610    20      21      WIAAAA  HSIAAA  VVVVxx
+3561   5884    1       1       1       1       61      561     1561    3561    3561    122     123     ZGAAAA  ISIAAA  AAAAxx
+8185   5885    1       1       5       5       85      185     185     3185    8185    170     171     VCAAAA  JSIAAA  HHHHxx
+7237   5886    1       1       7       17      37      237     1237    2237    7237    74      75      JSAAAA  KSIAAA  OOOOxx
+4592   5887    0       0       2       12      92      592     592     4592    4592    184     185     QUAAAA  LSIAAA  VVVVxx
+7082   5888    0       2       2       2       82      82      1082    2082    7082    164     165     KMAAAA  MSIAAA  AAAAxx
+4719   5889    1       3       9       19      19      719     719     4719    4719    38      39      NZAAAA  NSIAAA  HHHHxx
+3879   5890    1       3       9       19      79      879     1879    3879    3879    158     159     FTAAAA  OSIAAA  OOOOxx
+1662   5891    0       2       2       2       62      662     1662    1662    1662    124     125     YLAAAA  PSIAAA  VVVVxx
+3995   5892    1       3       5       15      95      995     1995    3995    3995    190     191     RXAAAA  QSIAAA  AAAAxx
+5828   5893    0       0       8       8       28      828     1828    828     5828    56      57      EQAAAA  RSIAAA  HHHHxx
+4197   5894    1       1       7       17      97      197     197     4197    4197    194     195     LFAAAA  SSIAAA  OOOOxx
+5146   5895    0       2       6       6       46      146     1146    146     5146    92      93      YPAAAA  TSIAAA  VVVVxx
+753    5896    1       1       3       13      53      753     753     753     753     106     107     ZCAAAA  USIAAA  AAAAxx
+7064   5897    0       0       4       4       64      64      1064    2064    7064    128     129     SLAAAA  VSIAAA  HHHHxx
+1312   5898    0       0       2       12      12      312     1312    1312    1312    24      25      MYAAAA  WSIAAA  OOOOxx
+5573   5899    1       1       3       13      73      573     1573    573     5573    146     147     JGAAAA  XSIAAA  VVVVxx
+7634   5900    0       2       4       14      34      634     1634    2634    7634    68      69      QHAAAA  YSIAAA  AAAAxx
+2459   5901    1       3       9       19      59      459     459     2459    2459    118     119     PQAAAA  ZSIAAA  HHHHxx
+8636   5902    0       0       6       16      36      636     636     3636    8636    72      73      EUAAAA  ATIAAA  OOOOxx
+5318   5903    0       2       8       18      18      318     1318    318     5318    36      37      OWAAAA  BTIAAA  VVVVxx
+1064   5904    0       0       4       4       64      64      1064    1064    1064    128     129     YOAAAA  CTIAAA  AAAAxx
+9779   5905    1       3       9       19      79      779     1779    4779    9779    158     159     DMAAAA  DTIAAA  HHHHxx
+6512   5906    0       0       2       12      12      512     512     1512    6512    24      25      MQAAAA  ETIAAA  OOOOxx
+3572   5907    0       0       2       12      72      572     1572    3572    3572    144     145     KHAAAA  FTIAAA  VVVVxx
+816    5908    0       0       6       16      16      816     816     816     816     32      33      KFAAAA  GTIAAA  AAAAxx
+3978   5909    0       2       8       18      78      978     1978    3978    3978    156     157     AXAAAA  HTIAAA  HHHHxx
+5390   5910    0       2       0       10      90      390     1390    390     5390    180     181     IZAAAA  ITIAAA  OOOOxx
+4685   5911    1       1       5       5       85      685     685     4685    4685    170     171     FYAAAA  JTIAAA  VVVVxx
+3003   5912    1       3       3       3       3       3       1003    3003    3003    6       7       NLAAAA  KTIAAA  AAAAxx
+2638   5913    0       2       8       18      38      638     638     2638    2638    76      77      MXAAAA  LTIAAA  HHHHxx
+9716   5914    0       0       6       16      16      716     1716    4716    9716    32      33      SJAAAA  MTIAAA  OOOOxx
+9598   5915    0       2       8       18      98      598     1598    4598    9598    196     197     EFAAAA  NTIAAA  VVVVxx
+9501   5916    1       1       1       1       1       501     1501    4501    9501    2       3       LBAAAA  OTIAAA  AAAAxx
+1704   5917    0       0       4       4       4       704     1704    1704    1704    8       9       ONAAAA  PTIAAA  HHHHxx
+8609   5918    1       1       9       9       9       609     609     3609    8609    18      19      DTAAAA  QTIAAA  OOOOxx
+5211   5919    1       3       1       11      11      211     1211    211     5211    22      23      LSAAAA  RTIAAA  VVVVxx
+3605   5920    1       1       5       5       5       605     1605    3605    3605    10      11      RIAAAA  STIAAA  AAAAxx
+8730   5921    0       2       0       10      30      730     730     3730    8730    60      61      UXAAAA  TTIAAA  HHHHxx
+4208   5922    0       0       8       8       8       208     208     4208    4208    16      17      WFAAAA  UTIAAA  OOOOxx
+7784   5923    0       0       4       4       84      784     1784    2784    7784    168     169     KNAAAA  VTIAAA  VVVVxx
+7501   5924    1       1       1       1       1       501     1501    2501    7501    2       3       NCAAAA  WTIAAA  AAAAxx
+7862   5925    0       2       2       2       62      862     1862    2862    7862    124     125     KQAAAA  XTIAAA  HHHHxx
+8922   5926    0       2       2       2       22      922     922     3922    8922    44      45      EFAAAA  YTIAAA  OOOOxx
+3857   5927    1       1       7       17      57      857     1857    3857    3857    114     115     JSAAAA  ZTIAAA  VVVVxx
+6393   5928    1       1       3       13      93      393     393     1393    6393    186     187     XLAAAA  AUIAAA  AAAAxx
+506    5929    0       2       6       6       6       506     506     506     506     12      13      MTAAAA  BUIAAA  HHHHxx
+4232   5930    0       0       2       12      32      232     232     4232    4232    64      65      UGAAAA  CUIAAA  OOOOxx
+8991   5931    1       3       1       11      91      991     991     3991    8991    182     183     VHAAAA  DUIAAA  VVVVxx
+8578   5932    0       2       8       18      78      578     578     3578    8578    156     157     YRAAAA  EUIAAA  AAAAxx
+3235   5933    1       3       5       15      35      235     1235    3235    3235    70      71      LUAAAA  FUIAAA  HHHHxx
+963    5934    1       3       3       3       63      963     963     963     963     126     127     BLAAAA  GUIAAA  OOOOxx
+113    5935    1       1       3       13      13      113     113     113     113     26      27      JEAAAA  HUIAAA  VVVVxx
+8234   5936    0       2       4       14      34      234     234     3234    8234    68      69      SEAAAA  IUIAAA  AAAAxx
+2613   5937    1       1       3       13      13      613     613     2613    2613    26      27      NWAAAA  JUIAAA  HHHHxx
+5540   5938    0       0       0       0       40      540     1540    540     5540    80      81      CFAAAA  KUIAAA  OOOOxx
+9727   5939    1       3       7       7       27      727     1727    4727    9727    54      55      DKAAAA  LUIAAA  VVVVxx
+2229   5940    1       1       9       9       29      229     229     2229    2229    58      59      THAAAA  MUIAAA  AAAAxx
+6242   5941    0       2       2       2       42      242     242     1242    6242    84      85      CGAAAA  NUIAAA  HHHHxx
+2502   5942    0       2       2       2       2       502     502     2502    2502    4       5       GSAAAA  OUIAAA  OOOOxx
+6212   5943    0       0       2       12      12      212     212     1212    6212    24      25      YEAAAA  PUIAAA  VVVVxx
+3495   5944    1       3       5       15      95      495     1495    3495    3495    190     191     LEAAAA  QUIAAA  AAAAxx
+2364   5945    0       0       4       4       64      364     364     2364    2364    128     129     YMAAAA  RUIAAA  HHHHxx
+6777   5946    1       1       7       17      77      777     777     1777    6777    154     155     RAAAAA  SUIAAA  OOOOxx
+9811   5947    1       3       1       11      11      811     1811    4811    9811    22      23      JNAAAA  TUIAAA  VVVVxx
+1450   5948    0       2       0       10      50      450     1450    1450    1450    100     101     UDAAAA  UUIAAA  AAAAxx
+5008   5949    0       0       8       8       8       8       1008    8       5008    16      17      QKAAAA  VUIAAA  HHHHxx
+1318   5950    0       2       8       18      18      318     1318    1318    1318    36      37      SYAAAA  WUIAAA  OOOOxx
+3373   5951    1       1       3       13      73      373     1373    3373    3373    146     147     TZAAAA  XUIAAA  VVVVxx
+398    5952    0       2       8       18      98      398     398     398     398     196     197     IPAAAA  YUIAAA  AAAAxx
+3804   5953    0       0       4       4       4       804     1804    3804    3804    8       9       IQAAAA  ZUIAAA  HHHHxx
+9148   5954    0       0       8       8       48      148     1148    4148    9148    96      97      WNAAAA  AVIAAA  OOOOxx
+4382   5955    0       2       2       2       82      382     382     4382    4382    164     165     OMAAAA  BVIAAA  VVVVxx
+4026   5956    0       2       6       6       26      26      26      4026    4026    52      53      WYAAAA  CVIAAA  AAAAxx
+7804   5957    0       0       4       4       4       804     1804    2804    7804    8       9       EOAAAA  DVIAAA  HHHHxx
+6839   5958    1       3       9       19      39      839     839     1839    6839    78      79      BDAAAA  EVIAAA  OOOOxx
+3756   5959    0       0       6       16      56      756     1756    3756    3756    112     113     MOAAAA  FVIAAA  VVVVxx
+6734   5960    0       2       4       14      34      734     734     1734    6734    68      69      AZAAAA  GVIAAA  AAAAxx
+2228   5961    0       0       8       8       28      228     228     2228    2228    56      57      SHAAAA  HVIAAA  HHHHxx
+3273   5962    1       1       3       13      73      273     1273    3273    3273    146     147     XVAAAA  IVIAAA  OOOOxx
+3708   5963    0       0       8       8       8       708     1708    3708    3708    16      17      QMAAAA  JVIAAA  VVVVxx
+4320   5964    0       0       0       0       20      320     320     4320    4320    40      41      EKAAAA  KVIAAA  AAAAxx
+74     5965    0       2       4       14      74      74      74      74      74      148     149     WCAAAA  LVIAAA  HHHHxx
+2520   5966    0       0       0       0       20      520     520     2520    2520    40      41      YSAAAA  MVIAAA  OOOOxx
+9619   5967    1       3       9       19      19      619     1619    4619    9619    38      39      ZFAAAA  NVIAAA  VVVVxx
+1801   5968    1       1       1       1       1       801     1801    1801    1801    2       3       HRAAAA  OVIAAA  AAAAxx
+6399   5969    1       3       9       19      99      399     399     1399    6399    198     199     DMAAAA  PVIAAA  HHHHxx
+8313   5970    1       1       3       13      13      313     313     3313    8313    26      27      THAAAA  QVIAAA  OOOOxx
+7003   5971    1       3       3       3       3       3       1003    2003    7003    6       7       JJAAAA  RVIAAA  VVVVxx
+329    5972    1       1       9       9       29      329     329     329     329     58      59      RMAAAA  SVIAAA  AAAAxx
+9090   5973    0       2       0       10      90      90      1090    4090    9090    180     181     QLAAAA  TVIAAA  HHHHxx
+2299   5974    1       3       9       19      99      299     299     2299    2299    198     199     LKAAAA  UVIAAA  OOOOxx
+3925   5975    1       1       5       5       25      925     1925    3925    3925    50      51      ZUAAAA  VVIAAA  VVVVxx
+8145   5976    1       1       5       5       45      145     145     3145    8145    90      91      HBAAAA  WVIAAA  AAAAxx
+8561   5977    1       1       1       1       61      561     561     3561    8561    122     123     HRAAAA  XVIAAA  HHHHxx
+2797   5978    1       1       7       17      97      797     797     2797    2797    194     195     PDAAAA  YVIAAA  OOOOxx
+1451   5979    1       3       1       11      51      451     1451    1451    1451    102     103     VDAAAA  ZVIAAA  VVVVxx
+7977   5980    1       1       7       17      77      977     1977    2977    7977    154     155     VUAAAA  AWIAAA  AAAAxx
+112    5981    0       0       2       12      12      112     112     112     112     24      25      IEAAAA  BWIAAA  HHHHxx
+5265   5982    1       1       5       5       65      265     1265    265     5265    130     131     NUAAAA  CWIAAA  OOOOxx
+3819   5983    1       3       9       19      19      819     1819    3819    3819    38      39      XQAAAA  DWIAAA  VVVVxx
+3648   5984    0       0       8       8       48      648     1648    3648    3648    96      97      IKAAAA  EWIAAA  AAAAxx
+6306   5985    0       2       6       6       6       306     306     1306    6306    12      13      OIAAAA  FWIAAA  HHHHxx
+2385   5986    1       1       5       5       85      385     385     2385    2385    170     171     TNAAAA  GWIAAA  OOOOxx
+9084   5987    0       0       4       4       84      84      1084    4084    9084    168     169     KLAAAA  HWIAAA  VVVVxx
+4499   5988    1       3       9       19      99      499     499     4499    4499    198     199     BRAAAA  IWIAAA  AAAAxx
+1154   5989    0       2       4       14      54      154     1154    1154    1154    108     109     KSAAAA  JWIAAA  HHHHxx
+6800   5990    0       0       0       0       0       800     800     1800    6800    0       1       OBAAAA  KWIAAA  OOOOxx
+8049   5991    1       1       9       9       49      49      49      3049    8049    98      99      PXAAAA  LWIAAA  VVVVxx
+3733   5992    1       1       3       13      33      733     1733    3733    3733    66      67      PNAAAA  MWIAAA  AAAAxx
+8496   5993    0       0       6       16      96      496     496     3496    8496    192     193     UOAAAA  NWIAAA  HHHHxx
+9952   5994    0       0       2       12      52      952     1952    4952    9952    104     105     USAAAA  OWIAAA  OOOOxx
+9792   5995    0       0       2       12      92      792     1792    4792    9792    184     185     QMAAAA  PWIAAA  VVVVxx
+5081   5996    1       1       1       1       81      81      1081    81      5081    162     163     LNAAAA  QWIAAA  AAAAxx
+7908   5997    0       0       8       8       8       908     1908    2908    7908    16      17      ESAAAA  RWIAAA  HHHHxx
+5398   5998    0       2       8       18      98      398     1398    398     5398    196     197     QZAAAA  SWIAAA  OOOOxx
+8423   5999    1       3       3       3       23      423     423     3423    8423    46      47      ZLAAAA  TWIAAA  VVVVxx
+3362   6000    0       2       2       2       62      362     1362    3362    3362    124     125     IZAAAA  UWIAAA  AAAAxx
+7767   6001    1       3       7       7       67      767     1767    2767    7767    134     135     TMAAAA  VWIAAA  HHHHxx
+7063   6002    1       3       3       3       63      63      1063    2063    7063    126     127     RLAAAA  WWIAAA  OOOOxx
+8350   6003    0       2       0       10      50      350     350     3350    8350    100     101     EJAAAA  XWIAAA  VVVVxx
+6779   6004    1       3       9       19      79      779     779     1779    6779    158     159     TAAAAA  YWIAAA  AAAAxx
+5742   6005    0       2       2       2       42      742     1742    742     5742    84      85      WMAAAA  ZWIAAA  HHHHxx
+9045   6006    1       1       5       5       45      45      1045    4045    9045    90      91      XJAAAA  AXIAAA  OOOOxx
+8792   6007    0       0       2       12      92      792     792     3792    8792    184     185     EAAAAA  BXIAAA  VVVVxx
+8160   6008    0       0       0       0       60      160     160     3160    8160    120     121     WBAAAA  CXIAAA  AAAAxx
+3061   6009    1       1       1       1       61      61      1061    3061    3061    122     123     TNAAAA  DXIAAA  HHHHxx
+4721   6010    1       1       1       1       21      721     721     4721    4721    42      43      PZAAAA  EXIAAA  OOOOxx
+9817   6011    1       1       7       17      17      817     1817    4817    9817    34      35      PNAAAA  FXIAAA  VVVVxx
+9257   6012    1       1       7       17      57      257     1257    4257    9257    114     115     BSAAAA  GXIAAA  AAAAxx
+7779   6013    1       3       9       19      79      779     1779    2779    7779    158     159     FNAAAA  HXIAAA  HHHHxx
+2663   6014    1       3       3       3       63      663     663     2663    2663    126     127     LYAAAA  IXIAAA  OOOOxx
+3885   6015    1       1       5       5       85      885     1885    3885    3885    170     171     LTAAAA  JXIAAA  VVVVxx
+9469   6016    1       1       9       9       69      469     1469    4469    9469    138     139     FAAAAA  KXIAAA  AAAAxx
+6766   6017    0       2       6       6       66      766     766     1766    6766    132     133     GAAAAA  LXIAAA  HHHHxx
+7173   6018    1       1       3       13      73      173     1173    2173    7173    146     147     XPAAAA  MXIAAA  OOOOxx
+4709   6019    1       1       9       9       9       709     709     4709    4709    18      19      DZAAAA  NXIAAA  VVVVxx
+4210   6020    0       2       0       10      10      210     210     4210    4210    20      21      YFAAAA  OXIAAA  AAAAxx
+3715   6021    1       3       5       15      15      715     1715    3715    3715    30      31      XMAAAA  PXIAAA  HHHHxx
+5089   6022    1       1       9       9       89      89      1089    89      5089    178     179     TNAAAA  QXIAAA  OOOOxx
+1639   6023    1       3       9       19      39      639     1639    1639    1639    78      79      BLAAAA  RXIAAA  VVVVxx
+5757   6024    1       1       7       17      57      757     1757    757     5757    114     115     LNAAAA  SXIAAA  AAAAxx
+3545   6025    1       1       5       5       45      545     1545    3545    3545    90      91      JGAAAA  TXIAAA  HHHHxx
+709    6026    1       1       9       9       9       709     709     709     709     18      19      HBAAAA  UXIAAA  OOOOxx
+6519   6027    1       3       9       19      19      519     519     1519    6519    38      39      TQAAAA  VXIAAA  VVVVxx
+4341   6028    1       1       1       1       41      341     341     4341    4341    82      83      ZKAAAA  WXIAAA  AAAAxx
+2381   6029    1       1       1       1       81      381     381     2381    2381    162     163     PNAAAA  XXIAAA  HHHHxx
+7215   6030    1       3       5       15      15      215     1215    2215    7215    30      31      NRAAAA  YXIAAA  OOOOxx
+9323   6031    1       3       3       3       23      323     1323    4323    9323    46      47      PUAAAA  ZXIAAA  VVVVxx
+3593   6032    1       1       3       13      93      593     1593    3593    3593    186     187     FIAAAA  AYIAAA  AAAAxx
+3123   6033    1       3       3       3       23      123     1123    3123    3123    46      47      DQAAAA  BYIAAA  HHHHxx
+8673   6034    1       1       3       13      73      673     673     3673    8673    146     147     PVAAAA  CYIAAA  OOOOxx
+5094   6035    0       2       4       14      94      94      1094    94      5094    188     189     YNAAAA  DYIAAA  VVVVxx
+6477   6036    1       1       7       17      77      477     477     1477    6477    154     155     DPAAAA  EYIAAA  AAAAxx
+9734   6037    0       2       4       14      34      734     1734    4734    9734    68      69      KKAAAA  FYIAAA  HHHHxx
+2998   6038    0       2       8       18      98      998     998     2998    2998    196     197     ILAAAA  GYIAAA  OOOOxx
+7807   6039    1       3       7       7       7       807     1807    2807    7807    14      15      HOAAAA  HYIAAA  VVVVxx
+5739   6040    1       3       9       19      39      739     1739    739     5739    78      79      TMAAAA  IYIAAA  AAAAxx
+138    6041    0       2       8       18      38      138     138     138     138     76      77      IFAAAA  JYIAAA  HHHHxx
+2403   6042    1       3       3       3       3       403     403     2403    2403    6       7       LOAAAA  KYIAAA  OOOOxx
+2484   6043    0       0       4       4       84      484     484     2484    2484    168     169     ORAAAA  LYIAAA  VVVVxx
+2805   6044    1       1       5       5       5       805     805     2805    2805    10      11      XDAAAA  MYIAAA  AAAAxx
+5189   6045    1       1       9       9       89      189     1189    189     5189    178     179     PRAAAA  NYIAAA  HHHHxx
+8336   6046    0       0       6       16      36      336     336     3336    8336    72      73      QIAAAA  OYIAAA  OOOOxx
+5241   6047    1       1       1       1       41      241     1241    241     5241    82      83      PTAAAA  PYIAAA  VVVVxx
+2612   6048    0       0       2       12      12      612     612     2612    2612    24      25      MWAAAA  QYIAAA  AAAAxx
+2571   6049    1       3       1       11      71      571     571     2571    2571    142     143     XUAAAA  RYIAAA  HHHHxx
+926    6050    0       2       6       6       26      926     926     926     926     52      53      QJAAAA  SYIAAA  OOOOxx
+337    6051    1       1       7       17      37      337     337     337     337     74      75      ZMAAAA  TYIAAA  VVVVxx
+2821   6052    1       1       1       1       21      821     821     2821    2821    42      43      NEAAAA  UYIAAA  AAAAxx
+2658   6053    0       2       8       18      58      658     658     2658    2658    116     117     GYAAAA  VYIAAA  HHHHxx
+9054   6054    0       2       4       14      54      54      1054    4054    9054    108     109     GKAAAA  WYIAAA  OOOOxx
+5492   6055    0       0       2       12      92      492     1492    492     5492    184     185     GDAAAA  XYIAAA  VVVVxx
+7313   6056    1       1       3       13      13      313     1313    2313    7313    26      27      HVAAAA  YYIAAA  AAAAxx
+75     6057    1       3       5       15      75      75      75      75      75      150     151     XCAAAA  ZYIAAA  HHHHxx
+5489   6058    1       1       9       9       89      489     1489    489     5489    178     179     DDAAAA  AZIAAA  OOOOxx
+8413   6059    1       1       3       13      13      413     413     3413    8413    26      27      PLAAAA  BZIAAA  VVVVxx
+3693   6060    1       1       3       13      93      693     1693    3693    3693    186     187     BMAAAA  CZIAAA  AAAAxx
+9820   6061    0       0       0       0       20      820     1820    4820    9820    40      41      SNAAAA  DZIAAA  HHHHxx
+8157   6062    1       1       7       17      57      157     157     3157    8157    114     115     TBAAAA  EZIAAA  OOOOxx
+4161   6063    1       1       1       1       61      161     161     4161    4161    122     123     BEAAAA  FZIAAA  VVVVxx
+8339   6064    1       3       9       19      39      339     339     3339    8339    78      79      TIAAAA  GZIAAA  AAAAxx
+4141   6065    1       1       1       1       41      141     141     4141    4141    82      83      HDAAAA  HZIAAA  HHHHxx
+9001   6066    1       1       1       1       1       1       1001    4001    9001    2       3       FIAAAA  IZIAAA  OOOOxx
+8247   6067    1       3       7       7       47      247     247     3247    8247    94      95      FFAAAA  JZIAAA  VVVVxx
+1182   6068    0       2       2       2       82      182     1182    1182    1182    164     165     MTAAAA  KZIAAA  AAAAxx
+9876   6069    0       0       6       16      76      876     1876    4876    9876    152     153     WPAAAA  LZIAAA  HHHHxx
+4302   6070    0       2       2       2       2       302     302     4302    4302    4       5       MJAAAA  MZIAAA  OOOOxx
+6674   6071    0       2       4       14      74      674     674     1674    6674    148     149     SWAAAA  NZIAAA  VVVVxx
+4214   6072    0       2       4       14      14      214     214     4214    4214    28      29      CGAAAA  OZIAAA  AAAAxx
+5584   6073    0       0       4       4       84      584     1584    584     5584    168     169     UGAAAA  PZIAAA  HHHHxx
+265    6074    1       1       5       5       65      265     265     265     265     130     131     FKAAAA  QZIAAA  OOOOxx
+9207   6075    1       3       7       7       7       207     1207    4207    9207    14      15      DQAAAA  RZIAAA  VVVVxx
+9434   6076    0       2       4       14      34      434     1434    4434    9434    68      69      WYAAAA  SZIAAA  AAAAxx
+2921   6077    1       1       1       1       21      921     921     2921    2921    42      43      JIAAAA  TZIAAA  HHHHxx
+9355   6078    1       3       5       15      55      355     1355    4355    9355    110     111     VVAAAA  UZIAAA  OOOOxx
+8538   6079    0       2       8       18      38      538     538     3538    8538    76      77      KQAAAA  VZIAAA  VVVVxx
+4559   6080    1       3       9       19      59      559     559     4559    4559    118     119     JTAAAA  WZIAAA  AAAAxx
+9175   6081    1       3       5       15      75      175     1175    4175    9175    150     151     XOAAAA  XZIAAA  HHHHxx
+4489   6082    1       1       9       9       89      489     489     4489    4489    178     179     RQAAAA  YZIAAA  OOOOxx
+1485   6083    1       1       5       5       85      485     1485    1485    1485    170     171     DFAAAA  ZZIAAA  VVVVxx
+8853   6084    1       1       3       13      53      853     853     3853    8853    106     107     NCAAAA  AAJAAA  AAAAxx
+9143   6085    1       3       3       3       43      143     1143    4143    9143    86      87      RNAAAA  BAJAAA  HHHHxx
+9551   6086    1       3       1       11      51      551     1551    4551    9551    102     103     JDAAAA  CAJAAA  OOOOxx
+49     6087    1       1       9       9       49      49      49      49      49      98      99      XBAAAA  DAJAAA  VVVVxx
+8351   6088    1       3       1       11      51      351     351     3351    8351    102     103     FJAAAA  EAJAAA  AAAAxx
+9748   6089    0       0       8       8       48      748     1748    4748    9748    96      97      YKAAAA  FAJAAA  HHHHxx
+4536   6090    0       0       6       16      36      536     536     4536    4536    72      73      MSAAAA  GAJAAA  OOOOxx
+930    6091    0       2       0       10      30      930     930     930     930     60      61      UJAAAA  HAJAAA  VVVVxx
+2206   6092    0       2       6       6       6       206     206     2206    2206    12      13      WGAAAA  IAJAAA  AAAAxx
+8004   6093    0       0       4       4       4       4       4       3004    8004    8       9       WVAAAA  JAJAAA  HHHHxx
+219    6094    1       3       9       19      19      219     219     219     219     38      39      LIAAAA  KAJAAA  OOOOxx
+2724   6095    0       0       4       4       24      724     724     2724    2724    48      49      UAAAAA  LAJAAA  VVVVxx
+4868   6096    0       0       8       8       68      868     868     4868    4868    136     137     GFAAAA  MAJAAA  AAAAxx
+5952   6097    0       0       2       12      52      952     1952    952     5952    104     105     YUAAAA  NAJAAA  HHHHxx
+2094   6098    0       2       4       14      94      94      94      2094    2094    188     189     OCAAAA  OAJAAA  OOOOxx
+5707   6099    1       3       7       7       7       707     1707    707     5707    14      15      NLAAAA  PAJAAA  VVVVxx
+5200   6100    0       0       0       0       0       200     1200    200     5200    0       1       ASAAAA  QAJAAA  AAAAxx
+967    6101    1       3       7       7       67      967     967     967     967     134     135     FLAAAA  RAJAAA  HHHHxx
+1982   6102    0       2       2       2       82      982     1982    1982    1982    164     165     GYAAAA  SAJAAA  OOOOxx
+3410   6103    0       2       0       10      10      410     1410    3410    3410    20      21      EBAAAA  TAJAAA  VVVVxx
+174    6104    0       2       4       14      74      174     174     174     174     148     149     SGAAAA  UAJAAA  AAAAxx
+9217   6105    1       1       7       17      17      217     1217    4217    9217    34      35      NQAAAA  VAJAAA  HHHHxx
+9103   6106    1       3       3       3       3       103     1103    4103    9103    6       7       DMAAAA  WAJAAA  OOOOxx
+868    6107    0       0       8       8       68      868     868     868     868     136     137     KHAAAA  XAJAAA  VVVVxx
+8261   6108    1       1       1       1       61      261     261     3261    8261    122     123     TFAAAA  YAJAAA  AAAAxx
+2720   6109    0       0       0       0       20      720     720     2720    2720    40      41      QAAAAA  ZAJAAA  HHHHxx
+2999   6110    1       3       9       19      99      999     999     2999    2999    198     199     JLAAAA  ABJAAA  OOOOxx
+769    6111    1       1       9       9       69      769     769     769     769     138     139     PDAAAA  BBJAAA  VVVVxx
+4533   6112    1       1       3       13      33      533     533     4533    4533    66      67      JSAAAA  CBJAAA  AAAAxx
+2030   6113    0       2       0       10      30      30      30      2030    2030    60      61      CAAAAA  DBJAAA  HHHHxx
+5824   6114    0       0       4       4       24      824     1824    824     5824    48      49      AQAAAA  EBJAAA  OOOOxx
+2328   6115    0       0       8       8       28      328     328     2328    2328    56      57      OLAAAA  FBJAAA  VVVVxx
+9970   6116    0       2       0       10      70      970     1970    4970    9970    140     141     MTAAAA  GBJAAA  AAAAxx
+3192   6117    0       0       2       12      92      192     1192    3192    3192    184     185     USAAAA  HBJAAA  HHHHxx
+3387   6118    1       3       7       7       87      387     1387    3387    3387    174     175     HAAAAA  IBJAAA  OOOOxx
+1936   6119    0       0       6       16      36      936     1936    1936    1936    72      73      MWAAAA  JBJAAA  VVVVxx
+6934   6120    0       2       4       14      34      934     934     1934    6934    68      69      SGAAAA  KBJAAA  AAAAxx
+5615   6121    1       3       5       15      15      615     1615    615     5615    30      31      ZHAAAA  LBJAAA  HHHHxx
+2241   6122    1       1       1       1       41      241     241     2241    2241    82      83      FIAAAA  MBJAAA  OOOOxx
+1842   6123    0       2       2       2       42      842     1842    1842    1842    84      85      WSAAAA  NBJAAA  VVVVxx
+8044   6124    0       0       4       4       44      44      44      3044    8044    88      89      KXAAAA  OBJAAA  AAAAxx
+8902   6125    0       2       2       2       2       902     902     3902    8902    4       5       KEAAAA  PBJAAA  HHHHxx
+4519   6126    1       3       9       19      19      519     519     4519    4519    38      39      VRAAAA  QBJAAA  OOOOxx
+492    6127    0       0       2       12      92      492     492     492     492     184     185     YSAAAA  RBJAAA  VVVVxx
+2694   6128    0       2       4       14      94      694     694     2694    2694    188     189     QZAAAA  SBJAAA  AAAAxx
+5861   6129    1       1       1       1       61      861     1861    861     5861    122     123     LRAAAA  TBJAAA  HHHHxx
+2104   6130    0       0       4       4       4       104     104     2104    2104    8       9       YCAAAA  UBJAAA  OOOOxx
+5376   6131    0       0       6       16      76      376     1376    376     5376    152     153     UYAAAA  VBJAAA  VVVVxx
+3147   6132    1       3       7       7       47      147     1147    3147    3147    94      95      BRAAAA  WBJAAA  AAAAxx
+9880   6133    0       0       0       0       80      880     1880    4880    9880    160     161     AQAAAA  XBJAAA  HHHHxx
+6171   6134    1       3       1       11      71      171     171     1171    6171    142     143     JDAAAA  YBJAAA  OOOOxx
+1850   6135    0       2       0       10      50      850     1850    1850    1850    100     101     ETAAAA  ZBJAAA  VVVVxx
+1775   6136    1       3       5       15      75      775     1775    1775    1775    150     151     HQAAAA  ACJAAA  AAAAxx
+9261   6137    1       1       1       1       61      261     1261    4261    9261    122     123     FSAAAA  BCJAAA  HHHHxx
+9648   6138    0       0       8       8       48      648     1648    4648    9648    96      97      CHAAAA  CCJAAA  OOOOxx
+7846   6139    0       2       6       6       46      846     1846    2846    7846    92      93      UPAAAA  DCJAAA  VVVVxx
+1446   6140    0       2       6       6       46      446     1446    1446    1446    92      93      QDAAAA  ECJAAA  AAAAxx
+3139   6141    1       3       9       19      39      139     1139    3139    3139    78      79      TQAAAA  FCJAAA  HHHHxx
+6142   6142    0       2       2       2       42      142     142     1142    6142    84      85      GCAAAA  GCJAAA  OOOOxx
+5812   6143    0       0       2       12      12      812     1812    812     5812    24      25      OPAAAA  HCJAAA  VVVVxx
+6728   6144    0       0       8       8       28      728     728     1728    6728    56      57      UYAAAA  ICJAAA  AAAAxx
+4428   6145    0       0       8       8       28      428     428     4428    4428    56      57      IOAAAA  JCJAAA  HHHHxx
+502    6146    0       2       2       2       2       502     502     502     502     4       5       ITAAAA  KCJAAA  OOOOxx
+2363   6147    1       3       3       3       63      363     363     2363    2363    126     127     XMAAAA  LCJAAA  VVVVxx
+3808   6148    0       0       8       8       8       808     1808    3808    3808    16      17      MQAAAA  MCJAAA  AAAAxx
+1010   6149    0       2       0       10      10      10      1010    1010    1010    20      21      WMAAAA  NCJAAA  HHHHxx
+9565   6150    1       1       5       5       65      565     1565    4565    9565    130     131     XDAAAA  OCJAAA  OOOOxx
+1587   6151    1       3       7       7       87      587     1587    1587    1587    174     175     BJAAAA  PCJAAA  VVVVxx
+1474   6152    0       2       4       14      74      474     1474    1474    1474    148     149     SEAAAA  QCJAAA  AAAAxx
+6215   6153    1       3       5       15      15      215     215     1215    6215    30      31      BFAAAA  RCJAAA  HHHHxx
+2395   6154    1       3       5       15      95      395     395     2395    2395    190     191     DOAAAA  SCJAAA  OOOOxx
+8753   6155    1       1       3       13      53      753     753     3753    8753    106     107     RYAAAA  TCJAAA  VVVVxx
+2446   6156    0       2       6       6       46      446     446     2446    2446    92      93      CQAAAA  UCJAAA  AAAAxx
+60     6157    0       0       0       0       60      60      60      60      60      120     121     ICAAAA  VCJAAA  HHHHxx
+982    6158    0       2       2       2       82      982     982     982     982     164     165     ULAAAA  WCJAAA  OOOOxx
+6489   6159    1       1       9       9       89      489     489     1489    6489    178     179     PPAAAA  XCJAAA  VVVVxx
+5334   6160    0       2       4       14      34      334     1334    334     5334    68      69      EXAAAA  YCJAAA  AAAAxx
+8540   6161    0       0       0       0       40      540     540     3540    8540    80      81      MQAAAA  ZCJAAA  HHHHxx
+490    6162    0       2       0       10      90      490     490     490     490     180     181     WSAAAA  ADJAAA  OOOOxx
+6763   6163    1       3       3       3       63      763     763     1763    6763    126     127     DAAAAA  BDJAAA  VVVVxx
+8273   6164    1       1       3       13      73      273     273     3273    8273    146     147     FGAAAA  CDJAAA  AAAAxx
+8327   6165    1       3       7       7       27      327     327     3327    8327    54      55      HIAAAA  DDJAAA  HHHHxx
+8541   6166    1       1       1       1       41      541     541     3541    8541    82      83      NQAAAA  EDJAAA  OOOOxx
+3459   6167    1       3       9       19      59      459     1459    3459    3459    118     119     BDAAAA  FDJAAA  VVVVxx
+5557   6168    1       1       7       17      57      557     1557    557     5557    114     115     TFAAAA  GDJAAA  AAAAxx
+158    6169    0       2       8       18      58      158     158     158     158     116     117     CGAAAA  HDJAAA  HHHHxx
+1741   6170    1       1       1       1       41      741     1741    1741    1741    82      83      ZOAAAA  IDJAAA  OOOOxx
+8385   6171    1       1       5       5       85      385     385     3385    8385    170     171     NKAAAA  JDJAAA  VVVVxx
+617    6172    1       1       7       17      17      617     617     617     617     34      35      TXAAAA  KDJAAA  AAAAxx
+3560   6173    0       0       0       0       60      560     1560    3560    3560    120     121     YGAAAA  LDJAAA  HHHHxx
+5216   6174    0       0       6       16      16      216     1216    216     5216    32      33      QSAAAA  MDJAAA  OOOOxx
+8443   6175    1       3       3       3       43      443     443     3443    8443    86      87      TMAAAA  NDJAAA  VVVVxx
+2700   6176    0       0       0       0       0       700     700     2700    2700    0       1       WZAAAA  ODJAAA  AAAAxx
+3661   6177    1       1       1       1       61      661     1661    3661    3661    122     123     VKAAAA  PDJAAA  HHHHxx
+4875   6178    1       3       5       15      75      875     875     4875    4875    150     151     NFAAAA  QDJAAA  OOOOxx
+6721   6179    1       1       1       1       21      721     721     1721    6721    42      43      NYAAAA  RDJAAA  VVVVxx
+3659   6180    1       3       9       19      59      659     1659    3659    3659    118     119     TKAAAA  SDJAAA  AAAAxx
+8944   6181    0       0       4       4       44      944     944     3944    8944    88      89      AGAAAA  TDJAAA  HHHHxx
+9133   6182    1       1       3       13      33      133     1133    4133    9133    66      67      HNAAAA  UDJAAA  OOOOxx
+9882   6183    0       2       2       2       82      882     1882    4882    9882    164     165     CQAAAA  VDJAAA  VVVVxx
+2102   6184    0       2       2       2       2       102     102     2102    2102    4       5       WCAAAA  WDJAAA  AAAAxx
+9445   6185    1       1       5       5       45      445     1445    4445    9445    90      91      HZAAAA  XDJAAA  HHHHxx
+5559   6186    1       3       9       19      59      559     1559    559     5559    118     119     VFAAAA  YDJAAA  OOOOxx
+6096   6187    0       0       6       16      96      96      96      1096    6096    192     193     MAAAAA  ZDJAAA  VVVVxx
+9336   6188    0       0       6       16      36      336     1336    4336    9336    72      73      CVAAAA  AEJAAA  AAAAxx
+2162   6189    0       2       2       2       62      162     162     2162    2162    124     125     EFAAAA  BEJAAA  HHHHxx
+7459   6190    1       3       9       19      59      459     1459    2459    7459    118     119     XAAAAA  CEJAAA  OOOOxx
+3248   6191    0       0       8       8       48      248     1248    3248    3248    96      97      YUAAAA  DEJAAA  VVVVxx
+9539   6192    1       3       9       19      39      539     1539    4539    9539    78      79      XCAAAA  EEJAAA  AAAAxx
+4449   6193    1       1       9       9       49      449     449     4449    4449    98      99      DPAAAA  FEJAAA  HHHHxx
+2809   6194    1       1       9       9       9       809     809     2809    2809    18      19      BEAAAA  GEJAAA  OOOOxx
+7058   6195    0       2       8       18      58      58      1058    2058    7058    116     117     MLAAAA  HEJAAA  VVVVxx
+3512   6196    0       0       2       12      12      512     1512    3512    3512    24      25      CFAAAA  IEJAAA  AAAAxx
+2802   6197    0       2       2       2       2       802     802     2802    2802    4       5       UDAAAA  JEJAAA  HHHHxx
+6289   6198    1       1       9       9       89      289     289     1289    6289    178     179     XHAAAA  KEJAAA  OOOOxx
+1947   6199    1       3       7       7       47      947     1947    1947    1947    94      95      XWAAAA  LEJAAA  VVVVxx
+9572   6200    0       0       2       12      72      572     1572    4572    9572    144     145     EEAAAA  MEJAAA  AAAAxx
+2356   6201    0       0       6       16      56      356     356     2356    2356    112     113     QMAAAA  NEJAAA  HHHHxx
+3039   6202    1       3       9       19      39      39      1039    3039    3039    78      79      XMAAAA  OEJAAA  OOOOxx
+9452   6203    0       0       2       12      52      452     1452    4452    9452    104     105     OZAAAA  PEJAAA  VVVVxx
+6328   6204    0       0       8       8       28      328     328     1328    6328    56      57      KJAAAA  QEJAAA  AAAAxx
+7661   6205    1       1       1       1       61      661     1661    2661    7661    122     123     RIAAAA  REJAAA  HHHHxx
+2566   6206    0       2       6       6       66      566     566     2566    2566    132     133     SUAAAA  SEJAAA  OOOOxx
+6095   6207    1       3       5       15      95      95      95      1095    6095    190     191     LAAAAA  TEJAAA  VVVVxx
+6367   6208    1       3       7       7       67      367     367     1367    6367    134     135     XKAAAA  UEJAAA  AAAAxx
+3368   6209    0       0       8       8       68      368     1368    3368    3368    136     137     OZAAAA  VEJAAA  HHHHxx
+5567   6210    1       3       7       7       67      567     1567    567     5567    134     135     DGAAAA  WEJAAA  OOOOxx
+9834   6211    0       2       4       14      34      834     1834    4834    9834    68      69      GOAAAA  XEJAAA  VVVVxx
+9695   6212    1       3       5       15      95      695     1695    4695    9695    190     191     XIAAAA  YEJAAA  AAAAxx
+7291   6213    1       3       1       11      91      291     1291    2291    7291    182     183     LUAAAA  ZEJAAA  HHHHxx
+4806   6214    0       2       6       6       6       806     806     4806    4806    12      13      WCAAAA  AFJAAA  OOOOxx
+2000   6215    0       0       0       0       0       0       0       2000    2000    0       1       YYAAAA  BFJAAA  VVVVxx
+6817   6216    1       1       7       17      17      817     817     1817    6817    34      35      FCAAAA  CFJAAA  AAAAxx
+8487   6217    1       3       7       7       87      487     487     3487    8487    174     175     LOAAAA  DFJAAA  HHHHxx
+3245   6218    1       1       5       5       45      245     1245    3245    3245    90      91      VUAAAA  EFJAAA  OOOOxx
+632    6219    0       0       2       12      32      632     632     632     632     64      65      IYAAAA  FFJAAA  VVVVxx
+8067   6220    1       3       7       7       67      67      67      3067    8067    134     135     HYAAAA  GFJAAA  AAAAxx
+7140   6221    0       0       0       0       40      140     1140    2140    7140    80      81      QOAAAA  HFJAAA  HHHHxx
+6802   6222    0       2       2       2       2       802     802     1802    6802    4       5       QBAAAA  IFJAAA  OOOOxx
+3980   6223    0       0       0       0       80      980     1980    3980    3980    160     161     CXAAAA  JFJAAA  VVVVxx
+1321   6224    1       1       1       1       21      321     1321    1321    1321    42      43      VYAAAA  KFJAAA  AAAAxx
+2273   6225    1       1       3       13      73      273     273     2273    2273    146     147     LJAAAA  LFJAAA  HHHHxx
+6787   6226    1       3       7       7       87      787     787     1787    6787    174     175     BBAAAA  MFJAAA  OOOOxx
+9480   6227    0       0       0       0       80      480     1480    4480    9480    160     161     QAAAAA  NFJAAA  VVVVxx
+9404   6228    0       0       4       4       4       404     1404    4404    9404    8       9       SXAAAA  OFJAAA  AAAAxx
+3914   6229    0       2       4       14      14      914     1914    3914    3914    28      29      OUAAAA  PFJAAA  HHHHxx
+5507   6230    1       3       7       7       7       507     1507    507     5507    14      15      VDAAAA  QFJAAA  OOOOxx
+1813   6231    1       1       3       13      13      813     1813    1813    1813    26      27      TRAAAA  RFJAAA  VVVVxx
+1999   6232    1       3       9       19      99      999     1999    1999    1999    198     199     XYAAAA  SFJAAA  AAAAxx
+3848   6233    0       0       8       8       48      848     1848    3848    3848    96      97      ASAAAA  TFJAAA  HHHHxx
+9693   6234    1       1       3       13      93      693     1693    4693    9693    186     187     VIAAAA  UFJAAA  OOOOxx
+1353   6235    1       1       3       13      53      353     1353    1353    1353    106     107     BAAAAA  VFJAAA  VVVVxx
+7218   6236    0       2       8       18      18      218     1218    2218    7218    36      37      QRAAAA  WFJAAA  AAAAxx
+8223   6237    1       3       3       3       23      223     223     3223    8223    46      47      HEAAAA  XFJAAA  HHHHxx
+9982   6238    0       2       2       2       82      982     1982    4982    9982    164     165     YTAAAA  YFJAAA  OOOOxx
+8799   6239    1       3       9       19      99      799     799     3799    8799    198     199     LAAAAA  ZFJAAA  VVVVxx
+8929   6240    1       1       9       9       29      929     929     3929    8929    58      59      LFAAAA  AGJAAA  AAAAxx
+4626   6241    0       2       6       6       26      626     626     4626    4626    52      53      YVAAAA  BGJAAA  HHHHxx
+7958   6242    0       2       8       18      58      958     1958    2958    7958    116     117     CUAAAA  CGJAAA  OOOOxx
+3743   6243    1       3       3       3       43      743     1743    3743    3743    86      87      ZNAAAA  DGJAAA  VVVVxx
+8165   6244    1       1       5       5       65      165     165     3165    8165    130     131     BCAAAA  EGJAAA  AAAAxx
+7899   6245    1       3       9       19      99      899     1899    2899    7899    198     199     VRAAAA  FGJAAA  HHHHxx
+8698   6246    0       2       8       18      98      698     698     3698    8698    196     197     OWAAAA  GGJAAA  OOOOxx
+9270   6247    0       2       0       10      70      270     1270    4270    9270    140     141     OSAAAA  HGJAAA  VVVVxx
+6348   6248    0       0       8       8       48      348     348     1348    6348    96      97      EKAAAA  IGJAAA  AAAAxx
+6999   6249    1       3       9       19      99      999     999     1999    6999    198     199     FJAAAA  JGJAAA  HHHHxx
+8467   6250    1       3       7       7       67      467     467     3467    8467    134     135     RNAAAA  KGJAAA  OOOOxx
+3907   6251    1       3       7       7       7       907     1907    3907    3907    14      15      HUAAAA  LGJAAA  VVVVxx
+4738   6252    0       2       8       18      38      738     738     4738    4738    76      77      GAAAAA  MGJAAA  AAAAxx
+248    6253    0       0       8       8       48      248     248     248     248     96      97      OJAAAA  NGJAAA  HHHHxx
+8769   6254    1       1       9       9       69      769     769     3769    8769    138     139     HZAAAA  OGJAAA  OOOOxx
+9922   6255    0       2       2       2       22      922     1922    4922    9922    44      45      QRAAAA  PGJAAA  VVVVxx
+778    6256    0       2       8       18      78      778     778     778     778     156     157     YDAAAA  QGJAAA  AAAAxx
+1233   6257    1       1       3       13      33      233     1233    1233    1233    66      67      LVAAAA  RGJAAA  HHHHxx
+1183   6258    1       3       3       3       83      183     1183    1183    1183    166     167     NTAAAA  SGJAAA  OOOOxx
+2838   6259    0       2       8       18      38      838     838     2838    2838    76      77      EFAAAA  TGJAAA  VVVVxx
+3096   6260    0       0       6       16      96      96      1096    3096    3096    192     193     CPAAAA  UGJAAA  AAAAxx
+8566   6261    0       2       6       6       66      566     566     3566    8566    132     133     MRAAAA  VGJAAA  HHHHxx
+7635   6262    1       3       5       15      35      635     1635    2635    7635    70      71      RHAAAA  WGJAAA  OOOOxx
+5428   6263    0       0       8       8       28      428     1428    428     5428    56      57      UAAAAA  XGJAAA  VVVVxx
+7430   6264    0       2       0       10      30      430     1430    2430    7430    60      61      UZAAAA  YGJAAA  AAAAxx
+7210   6265    0       2       0       10      10      210     1210    2210    7210    20      21      IRAAAA  ZGJAAA  HHHHxx
+4485   6266    1       1       5       5       85      485     485     4485    4485    170     171     NQAAAA  AHJAAA  OOOOxx
+9623   6267    1       3       3       3       23      623     1623    4623    9623    46      47      DGAAAA  BHJAAA  VVVVxx
+3670   6268    0       2       0       10      70      670     1670    3670    3670    140     141     ELAAAA  CHJAAA  AAAAxx
+1575   6269    1       3       5       15      75      575     1575    1575    1575    150     151     PIAAAA  DHJAAA  HHHHxx
+5874   6270    0       2       4       14      74      874     1874    874     5874    148     149     YRAAAA  EHJAAA  OOOOxx
+673    6271    1       1       3       13      73      673     673     673     673     146     147     XZAAAA  FHJAAA  VVVVxx
+9712   6272    0       0       2       12      12      712     1712    4712    9712    24      25      OJAAAA  GHJAAA  AAAAxx
+7729   6273    1       1       9       9       29      729     1729    2729    7729    58      59      HLAAAA  HHJAAA  HHHHxx
+4318   6274    0       2       8       18      18      318     318     4318    4318    36      37      CKAAAA  IHJAAA  OOOOxx
+4143   6275    1       3       3       3       43      143     143     4143    4143    86      87      JDAAAA  JHJAAA  VVVVxx
+4932   6276    0       0       2       12      32      932     932     4932    4932    64      65      SHAAAA  KHJAAA  AAAAxx
+5835   6277    1       3       5       15      35      835     1835    835     5835    70      71      LQAAAA  LHJAAA  HHHHxx
+4966   6278    0       2       6       6       66      966     966     4966    4966    132     133     AJAAAA  MHJAAA  OOOOxx
+6711   6279    1       3       1       11      11      711     711     1711    6711    22      23      DYAAAA  NHJAAA  VVVVxx
+3990   6280    0       2       0       10      90      990     1990    3990    3990    180     181     MXAAAA  OHJAAA  AAAAxx
+990    6281    0       2       0       10      90      990     990     990     990     180     181     CMAAAA  PHJAAA  HHHHxx
+220    6282    0       0       0       0       20      220     220     220     220     40      41      MIAAAA  QHJAAA  OOOOxx
+5693   6283    1       1       3       13      93      693     1693    693     5693    186     187     ZKAAAA  RHJAAA  VVVVxx
+3662   6284    0       2       2       2       62      662     1662    3662    3662    124     125     WKAAAA  SHJAAA  AAAAxx
+7844   6285    0       0       4       4       44      844     1844    2844    7844    88      89      SPAAAA  THJAAA  HHHHxx
+5515   6286    1       3       5       15      15      515     1515    515     5515    30      31      DEAAAA  UHJAAA  OOOOxx
+5551   6287    1       3       1       11      51      551     1551    551     5551    102     103     NFAAAA  VHJAAA  VVVVxx
+2358   6288    0       2       8       18      58      358     358     2358    2358    116     117     SMAAAA  WHJAAA  AAAAxx
+8977   6289    1       1       7       17      77      977     977     3977    8977    154     155     HHAAAA  XHJAAA  HHHHxx
+7040   6290    0       0       0       0       40      40      1040    2040    7040    80      81      UKAAAA  YHJAAA  OOOOxx
+105    6291    1       1       5       5       5       105     105     105     105     10      11      BEAAAA  ZHJAAA  VVVVxx
+4496   6292    0       0       6       16      96      496     496     4496    4496    192     193     YQAAAA  AIJAAA  AAAAxx
+2254   6293    0       2       4       14      54      254     254     2254    2254    108     109     SIAAAA  BIJAAA  HHHHxx
+411    6294    1       3       1       11      11      411     411     411     411     22      23      VPAAAA  CIJAAA  OOOOxx
+2373   6295    1       1       3       13      73      373     373     2373    2373    146     147     HNAAAA  DIJAAA  VVVVxx
+3477   6296    1       1       7       17      77      477     1477    3477    3477    154     155     TDAAAA  EIJAAA  AAAAxx
+8964   6297    0       0       4       4       64      964     964     3964    8964    128     129     UGAAAA  FIJAAA  HHHHxx
+8471   6298    1       3       1       11      71      471     471     3471    8471    142     143     VNAAAA  GIJAAA  OOOOxx
+5776   6299    0       0       6       16      76      776     1776    776     5776    152     153     EOAAAA  HIJAAA  VVVVxx
+9921   6300    1       1       1       1       21      921     1921    4921    9921    42      43      PRAAAA  IIJAAA  AAAAxx
+7816   6301    0       0       6       16      16      816     1816    2816    7816    32      33      QOAAAA  JIJAAA  HHHHxx
+2439   6302    1       3       9       19      39      439     439     2439    2439    78      79      VPAAAA  KIJAAA  OOOOxx
+9298   6303    0       2       8       18      98      298     1298    4298    9298    196     197     QTAAAA  LIJAAA  VVVVxx
+9424   6304    0       0       4       4       24      424     1424    4424    9424    48      49      MYAAAA  MIJAAA  AAAAxx
+3252   6305    0       0       2       12      52      252     1252    3252    3252    104     105     CVAAAA  NIJAAA  HHHHxx
+1401   6306    1       1       1       1       1       401     1401    1401    1401    2       3       XBAAAA  OIJAAA  OOOOxx
+9632   6307    0       0       2       12      32      632     1632    4632    9632    64      65      MGAAAA  PIJAAA  VVVVxx
+370    6308    0       2       0       10      70      370     370     370     370     140     141     GOAAAA  QIJAAA  AAAAxx
+728    6309    0       0       8       8       28      728     728     728     728     56      57      ACAAAA  RIJAAA  HHHHxx
+2888   6310    0       0       8       8       88      888     888     2888    2888    176     177     CHAAAA  SIJAAA  OOOOxx
+1441   6311    1       1       1       1       41      441     1441    1441    1441    82      83      LDAAAA  TIJAAA  VVVVxx
+8308   6312    0       0       8       8       8       308     308     3308    8308    16      17      OHAAAA  UIJAAA  AAAAxx
+2165   6313    1       1       5       5       65      165     165     2165    2165    130     131     HFAAAA  VIJAAA  HHHHxx
+6359   6314    1       3       9       19      59      359     359     1359    6359    118     119     PKAAAA  WIJAAA  OOOOxx
+9637   6315    1       1       7       17      37      637     1637    4637    9637    74      75      RGAAAA  XIJAAA  VVVVxx
+5208   6316    0       0       8       8       8       208     1208    208     5208    16      17      ISAAAA  YIJAAA  AAAAxx
+4705   6317    1       1       5       5       5       705     705     4705    4705    10      11      ZYAAAA  ZIJAAA  HHHHxx
+2341   6318    1       1       1       1       41      341     341     2341    2341    82      83      BMAAAA  AJJAAA  OOOOxx
+8539   6319    1       3       9       19      39      539     539     3539    8539    78      79      LQAAAA  BJJAAA  VVVVxx
+7528   6320    0       0       8       8       28      528     1528    2528    7528    56      57      ODAAAA  CJJAAA  AAAAxx
+7969   6321    1       1       9       9       69      969     1969    2969    7969    138     139     NUAAAA  DJJAAA  HHHHxx
+6381   6322    1       1       1       1       81      381     381     1381    6381    162     163     LLAAAA  EJJAAA  OOOOxx
+4906   6323    0       2       6       6       6       906     906     4906    4906    12      13      SGAAAA  FJJAAA  VVVVxx
+8697   6324    1       1       7       17      97      697     697     3697    8697    194     195     NWAAAA  GJJAAA  AAAAxx
+6301   6325    1       1       1       1       1       301     301     1301    6301    2       3       JIAAAA  HJJAAA  HHHHxx
+7554   6326    0       2       4       14      54      554     1554    2554    7554    108     109     OEAAAA  IJJAAA  OOOOxx
+5107   6327    1       3       7       7       7       107     1107    107     5107    14      15      LOAAAA  JJJAAA  VVVVxx
+5046   6328    0       2       6       6       46      46      1046    46      5046    92      93      CMAAAA  KJJAAA  AAAAxx
+4063   6329    1       3       3       3       63      63      63      4063    4063    126     127     HAAAAA  LJJAAA  HHHHxx
+7580   6330    0       0       0       0       80      580     1580    2580    7580    160     161     OFAAAA  MJJAAA  OOOOxx
+2245   6331    1       1       5       5       45      245     245     2245    2245    90      91      JIAAAA  NJJAAA  VVVVxx
+3711   6332    1       3       1       11      11      711     1711    3711    3711    22      23      TMAAAA  OJJAAA  AAAAxx
+3220   6333    0       0       0       0       20      220     1220    3220    3220    40      41      WTAAAA  PJJAAA  HHHHxx
+6463   6334    1       3       3       3       63      463     463     1463    6463    126     127     POAAAA  QJJAAA  OOOOxx
+8196   6335    0       0       6       16      96      196     196     3196    8196    192     193     GDAAAA  RJJAAA  VVVVxx
+9875   6336    1       3       5       15      75      875     1875    4875    9875    150     151     VPAAAA  SJJAAA  AAAAxx
+1333   6337    1       1       3       13      33      333     1333    1333    1333    66      67      HZAAAA  TJJAAA  HHHHxx
+7880   6338    0       0       0       0       80      880     1880    2880    7880    160     161     CRAAAA  UJJAAA  OOOOxx
+2322   6339    0       2       2       2       22      322     322     2322    2322    44      45      ILAAAA  VJJAAA  VVVVxx
+2163   6340    1       3       3       3       63      163     163     2163    2163    126     127     FFAAAA  WJJAAA  AAAAxx
+421    6341    1       1       1       1       21      421     421     421     421     42      43      FQAAAA  XJJAAA  HHHHxx
+2042   6342    0       2       2       2       42      42      42      2042    2042    84      85      OAAAAA  YJJAAA  OOOOxx
+1424   6343    0       0       4       4       24      424     1424    1424    1424    48      49      UCAAAA  ZJJAAA  VVVVxx
+7870   6344    0       2       0       10      70      870     1870    2870    7870    140     141     SQAAAA  AKJAAA  AAAAxx
+2653   6345    1       1       3       13      53      653     653     2653    2653    106     107     BYAAAA  BKJAAA  HHHHxx
+4216   6346    0       0       6       16      16      216     216     4216    4216    32      33      EGAAAA  CKJAAA  OOOOxx
+1515   6347    1       3       5       15      15      515     1515    1515    1515    30      31      HGAAAA  DKJAAA  VVVVxx
+7860   6348    0       0       0       0       60      860     1860    2860    7860    120     121     IQAAAA  EKJAAA  AAAAxx
+2984   6349    0       0       4       4       84      984     984     2984    2984    168     169     UKAAAA  FKJAAA  HHHHxx
+6269   6350    1       1       9       9       69      269     269     1269    6269    138     139     DHAAAA  GKJAAA  OOOOxx
+2609   6351    1       1       9       9       9       609     609     2609    2609    18      19      JWAAAA  HKJAAA  VVVVxx
+3671   6352    1       3       1       11      71      671     1671    3671    3671    142     143     FLAAAA  IKJAAA  AAAAxx
+4544   6353    0       0       4       4       44      544     544     4544    4544    88      89      USAAAA  JKJAAA  HHHHxx
+4668   6354    0       0       8       8       68      668     668     4668    4668    136     137     OXAAAA  KKJAAA  OOOOxx
+2565   6355    1       1       5       5       65      565     565     2565    2565    130     131     RUAAAA  LKJAAA  VVVVxx
+3126   6356    0       2       6       6       26      126     1126    3126    3126    52      53      GQAAAA  MKJAAA  AAAAxx
+7573   6357    1       1       3       13      73      573     1573    2573    7573    146     147     HFAAAA  NKJAAA  HHHHxx
+1476   6358    0       0       6       16      76      476     1476    1476    1476    152     153     UEAAAA  OKJAAA  OOOOxx
+2146   6359    0       2       6       6       46      146     146     2146    2146    92      93      OEAAAA  PKJAAA  VVVVxx
+9990   6360    0       2       0       10      90      990     1990    4990    9990    180     181     GUAAAA  QKJAAA  AAAAxx
+2530   6361    0       2       0       10      30      530     530     2530    2530    60      61      ITAAAA  RKJAAA  HHHHxx
+9288   6362    0       0       8       8       88      288     1288    4288    9288    176     177     GTAAAA  SKJAAA  OOOOxx
+9755   6363    1       3       5       15      55      755     1755    4755    9755    110     111     FLAAAA  TKJAAA  VVVVxx
+5305   6364    1       1       5       5       5       305     1305    305     5305    10      11      BWAAAA  UKJAAA  AAAAxx
+2495   6365    1       3       5       15      95      495     495     2495    2495    190     191     ZRAAAA  VKJAAA  HHHHxx
+5443   6366    1       3       3       3       43      443     1443    443     5443    86      87      JBAAAA  WKJAAA  OOOOxx
+1930   6367    0       2       0       10      30      930     1930    1930    1930    60      61      GWAAAA  XKJAAA  VVVVxx
+9134   6368    0       2       4       14      34      134     1134    4134    9134    68      69      INAAAA  YKJAAA  AAAAxx
+2844   6369    0       0       4       4       44      844     844     2844    2844    88      89      KFAAAA  ZKJAAA  HHHHxx
+896    6370    0       0       6       16      96      896     896     896     896     192     193     MIAAAA  ALJAAA  OOOOxx
+1330   6371    0       2       0       10      30      330     1330    1330    1330    60      61      EZAAAA  BLJAAA  VVVVxx
+8980   6372    0       0       0       0       80      980     980     3980    8980    160     161     KHAAAA  CLJAAA  AAAAxx
+5940   6373    0       0       0       0       40      940     1940    940     5940    80      81      MUAAAA  DLJAAA  HHHHxx
+6494   6374    0       2       4       14      94      494     494     1494    6494    188     189     UPAAAA  ELJAAA  OOOOxx
+165    6375    1       1       5       5       65      165     165     165     165     130     131     JGAAAA  FLJAAA  VVVVxx
+2510   6376    0       2       0       10      10      510     510     2510    2510    20      21      OSAAAA  GLJAAA  AAAAxx
+9950   6377    0       2       0       10      50      950     1950    4950    9950    100     101     SSAAAA  HLJAAA  HHHHxx
+3854   6378    0       2       4       14      54      854     1854    3854    3854    108     109     GSAAAA  ILJAAA  OOOOxx
+7493   6379    1       1       3       13      93      493     1493    2493    7493    186     187     FCAAAA  JLJAAA  VVVVxx
+4124   6380    0       0       4       4       24      124     124     4124    4124    48      49      QCAAAA  KLJAAA  AAAAxx
+8563   6381    1       3       3       3       63      563     563     3563    8563    126     127     JRAAAA  LLJAAA  HHHHxx
+8735   6382    1       3       5       15      35      735     735     3735    8735    70      71      ZXAAAA  MLJAAA  OOOOxx
+9046   6383    0       2       6       6       46      46      1046    4046    9046    92      93      YJAAAA  NLJAAA  VVVVxx
+1754   6384    0       2       4       14      54      754     1754    1754    1754    108     109     MPAAAA  OLJAAA  AAAAxx
+6954   6385    0       2       4       14      54      954     954     1954    6954    108     109     MHAAAA  PLJAAA  HHHHxx
+4953   6386    1       1       3       13      53      953     953     4953    4953    106     107     NIAAAA  QLJAAA  OOOOxx
+8142   6387    0       2       2       2       42      142     142     3142    8142    84      85      EBAAAA  RLJAAA  VVVVxx
+9661   6388    1       1       1       1       61      661     1661    4661    9661    122     123     PHAAAA  SLJAAA  AAAAxx
+6415   6389    1       3       5       15      15      415     415     1415    6415    30      31      TMAAAA  TLJAAA  HHHHxx
+5782   6390    0       2       2       2       82      782     1782    782     5782    164     165     KOAAAA  ULJAAA  OOOOxx
+7721   6391    1       1       1       1       21      721     1721    2721    7721    42      43      ZKAAAA  VLJAAA  VVVVxx
+580    6392    0       0       0       0       80      580     580     580     580     160     161     IWAAAA  WLJAAA  AAAAxx
+3784   6393    0       0       4       4       84      784     1784    3784    3784    168     169     OPAAAA  XLJAAA  HHHHxx
+9810   6394    0       2       0       10      10      810     1810    4810    9810    20      21      INAAAA  YLJAAA  OOOOxx
+8488   6395    0       0       8       8       88      488     488     3488    8488    176     177     MOAAAA  ZLJAAA  VVVVxx
+6214   6396    0       2       4       14      14      214     214     1214    6214    28      29      AFAAAA  AMJAAA  AAAAxx
+9433   6397    1       1       3       13      33      433     1433    4433    9433    66      67      VYAAAA  BMJAAA  HHHHxx
+9959   6398    1       3       9       19      59      959     1959    4959    9959    118     119     BTAAAA  CMJAAA  OOOOxx
+554    6399    0       2       4       14      54      554     554     554     554     108     109     IVAAAA  DMJAAA  VVVVxx
+6646   6400    0       2       6       6       46      646     646     1646    6646    92      93      QVAAAA  EMJAAA  AAAAxx
+1138   6401    0       2       8       18      38      138     1138    1138    1138    76      77      URAAAA  FMJAAA  HHHHxx
+9331   6402    1       3       1       11      31      331     1331    4331    9331    62      63      XUAAAA  GMJAAA  OOOOxx
+7331   6403    1       3       1       11      31      331     1331    2331    7331    62      63      ZVAAAA  HMJAAA  VVVVxx
+3482   6404    0       2       2       2       82      482     1482    3482    3482    164     165     YDAAAA  IMJAAA  AAAAxx
+3795   6405    1       3       5       15      95      795     1795    3795    3795    190     191     ZPAAAA  JMJAAA  HHHHxx
+2441   6406    1       1       1       1       41      441     441     2441    2441    82      83      XPAAAA  KMJAAA  OOOOxx
+5229   6407    1       1       9       9       29      229     1229    229     5229    58      59      DTAAAA  LMJAAA  VVVVxx
+7012   6408    0       0       2       12      12      12      1012    2012    7012    24      25      SJAAAA  MMJAAA  AAAAxx
+7036   6409    0       0       6       16      36      36      1036    2036    7036    72      73      QKAAAA  NMJAAA  HHHHxx
+8243   6410    1       3       3       3       43      243     243     3243    8243    86      87      BFAAAA  OMJAAA  OOOOxx
+9320   6411    0       0       0       0       20      320     1320    4320    9320    40      41      MUAAAA  PMJAAA  VVVVxx
+4693   6412    1       1       3       13      93      693     693     4693    4693    186     187     NYAAAA  QMJAAA  AAAAxx
+6741   6413    1       1       1       1       41      741     741     1741    6741    82      83      HZAAAA  RMJAAA  HHHHxx
+2997   6414    1       1       7       17      97      997     997     2997    2997    194     195     HLAAAA  SMJAAA  OOOOxx
+4838   6415    0       2       8       18      38      838     838     4838    4838    76      77      CEAAAA  TMJAAA  VVVVxx
+6945   6416    1       1       5       5       45      945     945     1945    6945    90      91      DHAAAA  UMJAAA  AAAAxx
+8253   6417    1       1       3       13      53      253     253     3253    8253    106     107     LFAAAA  VMJAAA  HHHHxx
+8989   6418    1       1       9       9       89      989     989     3989    8989    178     179     THAAAA  WMJAAA  OOOOxx
+2640   6419    0       0       0       0       40      640     640     2640    2640    80      81      OXAAAA  XMJAAA  VVVVxx
+5647   6420    1       3       7       7       47      647     1647    647     5647    94      95      FJAAAA  YMJAAA  AAAAxx
+7186   6421    0       2       6       6       86      186     1186    2186    7186    172     173     KQAAAA  ZMJAAA  HHHHxx
+3278   6422    0       2       8       18      78      278     1278    3278    3278    156     157     CWAAAA  ANJAAA  OOOOxx
+8546   6423    0       2       6       6       46      546     546     3546    8546    92      93      SQAAAA  BNJAAA  VVVVxx
+8297   6424    1       1       7       17      97      297     297     3297    8297    194     195     DHAAAA  CNJAAA  AAAAxx
+9534   6425    0       2       4       14      34      534     1534    4534    9534    68      69      SCAAAA  DNJAAA  HHHHxx
+9618   6426    0       2       8       18      18      618     1618    4618    9618    36      37      YFAAAA  ENJAAA  OOOOxx
+8839   6427    1       3       9       19      39      839     839     3839    8839    78      79      ZBAAAA  FNJAAA  VVVVxx
+7605   6428    1       1       5       5       5       605     1605    2605    7605    10      11      NGAAAA  GNJAAA  AAAAxx
+6421   6429    1       1       1       1       21      421     421     1421    6421    42      43      ZMAAAA  HNJAAA  HHHHxx
+3582   6430    0       2       2       2       82      582     1582    3582    3582    164     165     UHAAAA  INJAAA  OOOOxx
+485    6431    1       1       5       5       85      485     485     485     485     170     171     RSAAAA  JNJAAA  VVVVxx
+1925   6432    1       1       5       5       25      925     1925    1925    1925    50      51      BWAAAA  KNJAAA  AAAAxx
+4296   6433    0       0       6       16      96      296     296     4296    4296    192     193     GJAAAA  LNJAAA  HHHHxx
+8874   6434    0       2       4       14      74      874     874     3874    8874    148     149     IDAAAA  MNJAAA  OOOOxx
+1443   6435    1       3       3       3       43      443     1443    1443    1443    86      87      NDAAAA  NNJAAA  VVVVxx
+4239   6436    1       3       9       19      39      239     239     4239    4239    78      79      BHAAAA  ONJAAA  AAAAxx
+9760   6437    0       0       0       0       60      760     1760    4760    9760    120     121     KLAAAA  PNJAAA  HHHHxx
+136    6438    0       0       6       16      36      136     136     136     136     72      73      GFAAAA  QNJAAA  OOOOxx
+6472   6439    0       0       2       12      72      472     472     1472    6472    144     145     YOAAAA  RNJAAA  VVVVxx
+4896   6440    0       0       6       16      96      896     896     4896    4896    192     193     IGAAAA  SNJAAA  AAAAxx
+9028   6441    0       0       8       8       28      28      1028    4028    9028    56      57      GJAAAA  TNJAAA  HHHHxx
+8354   6442    0       2       4       14      54      354     354     3354    8354    108     109     IJAAAA  UNJAAA  OOOOxx
+8648   6443    0       0       8       8       48      648     648     3648    8648    96      97      QUAAAA  VNJAAA  VVVVxx
+918    6444    0       2       8       18      18      918     918     918     918     36      37      IJAAAA  WNJAAA  AAAAxx
+6606   6445    0       2       6       6       6       606     606     1606    6606    12      13      CUAAAA  XNJAAA  HHHHxx
+2462   6446    0       2       2       2       62      462     462     2462    2462    124     125     SQAAAA  YNJAAA  OOOOxx
+7536   6447    0       0       6       16      36      536     1536    2536    7536    72      73      WDAAAA  ZNJAAA  VVVVxx
+1700   6448    0       0       0       0       0       700     1700    1700    1700    0       1       KNAAAA  AOJAAA  AAAAxx
+6740   6449    0       0       0       0       40      740     740     1740    6740    80      81      GZAAAA  BOJAAA  HHHHxx
+28     6450    0       0       8       8       28      28      28      28      28      56      57      CBAAAA  COJAAA  OOOOxx
+6044   6451    0       0       4       4       44      44      44      1044    6044    88      89      MYAAAA  DOJAAA  VVVVxx
+5053   6452    1       1       3       13      53      53      1053    53      5053    106     107     JMAAAA  EOJAAA  AAAAxx
+4832   6453    0       0       2       12      32      832     832     4832    4832    64      65      WDAAAA  FOJAAA  HHHHxx
+9145   6454    1       1       5       5       45      145     1145    4145    9145    90      91      TNAAAA  GOJAAA  OOOOxx
+5482   6455    0       2       2       2       82      482     1482    482     5482    164     165     WCAAAA  HOJAAA  VVVVxx
+7644   6456    0       0       4       4       44      644     1644    2644    7644    88      89      AIAAAA  IOJAAA  AAAAxx
+2128   6457    0       0       8       8       28      128     128     2128    2128    56      57      WDAAAA  JOJAAA  HHHHxx
+6583   6458    1       3       3       3       83      583     583     1583    6583    166     167     FTAAAA  KOJAAA  OOOOxx
+4224   6459    0       0       4       4       24      224     224     4224    4224    48      49      MGAAAA  LOJAAA  VVVVxx
+5253   6460    1       1       3       13      53      253     1253    253     5253    106     107     BUAAAA  MOJAAA  AAAAxx
+8219   6461    1       3       9       19      19      219     219     3219    8219    38      39      DEAAAA  NOJAAA  HHHHxx
+8113   6462    1       1       3       13      13      113     113     3113    8113    26      27      BAAAAA  OOJAAA  OOOOxx
+3616   6463    0       0       6       16      16      616     1616    3616    3616    32      33      CJAAAA  POJAAA  VVVVxx
+1361   6464    1       1       1       1       61      361     1361    1361    1361    122     123     JAAAAA  QOJAAA  AAAAxx
+949    6465    1       1       9       9       49      949     949     949     949     98      99      NKAAAA  ROJAAA  HHHHxx
+8582   6466    0       2       2       2       82      582     582     3582    8582    164     165     CSAAAA  SOJAAA  OOOOxx
+5104   6467    0       0       4       4       4       104     1104    104     5104    8       9       IOAAAA  TOJAAA  VVVVxx
+6146   6468    0       2       6       6       46      146     146     1146    6146    92      93      KCAAAA  UOJAAA  AAAAxx
+7681   6469    1       1       1       1       81      681     1681    2681    7681    162     163     LJAAAA  VOJAAA  HHHHxx
+1904   6470    0       0       4       4       4       904     1904    1904    1904    8       9       GVAAAA  WOJAAA  OOOOxx
+1989   6471    1       1       9       9       89      989     1989    1989    1989    178     179     NYAAAA  XOJAAA  VVVVxx
+4179   6472    1       3       9       19      79      179     179     4179    4179    158     159     TEAAAA  YOJAAA  AAAAxx
+1739   6473    1       3       9       19      39      739     1739    1739    1739    78      79      XOAAAA  ZOJAAA  HHHHxx
+2447   6474    1       3       7       7       47      447     447     2447    2447    94      95      DQAAAA  APJAAA  OOOOxx
+3029   6475    1       1       9       9       29      29      1029    3029    3029    58      59      NMAAAA  BPJAAA  VVVVxx
+9783   6476    1       3       3       3       83      783     1783    4783    9783    166     167     HMAAAA  CPJAAA  AAAAxx
+8381   6477    1       1       1       1       81      381     381     3381    8381    162     163     JKAAAA  DPJAAA  HHHHxx
+8755   6478    1       3       5       15      55      755     755     3755    8755    110     111     TYAAAA  EPJAAA  OOOOxx
+8384   6479    0       0       4       4       84      384     384     3384    8384    168     169     MKAAAA  FPJAAA  VVVVxx
+7655   6480    1       3       5       15      55      655     1655    2655    7655    110     111     LIAAAA  GPJAAA  AAAAxx
+4766   6481    0       2       6       6       66      766     766     4766    4766    132     133     IBAAAA  HPJAAA  HHHHxx
+3324   6482    0       0       4       4       24      324     1324    3324    3324    48      49      WXAAAA  IPJAAA  OOOOxx
+5022   6483    0       2       2       2       22      22      1022    22      5022    44      45      ELAAAA  JPJAAA  VVVVxx
+2856   6484    0       0       6       16      56      856     856     2856    2856    112     113     WFAAAA  KPJAAA  AAAAxx
+6503   6485    1       3       3       3       3       503     503     1503    6503    6       7       DQAAAA  LPJAAA  HHHHxx
+6872   6486    0       0       2       12      72      872     872     1872    6872    144     145     IEAAAA  MPJAAA  OOOOxx
+1663   6487    1       3       3       3       63      663     1663    1663    1663    126     127     ZLAAAA  NPJAAA  VVVVxx
+6964   6488    0       0       4       4       64      964     964     1964    6964    128     129     WHAAAA  OPJAAA  AAAAxx
+4622   6489    0       2       2       2       22      622     622     4622    4622    44      45      UVAAAA  PPJAAA  HHHHxx
+6089   6490    1       1       9       9       89      89      89      1089    6089    178     179     FAAAAA  QPJAAA  OOOOxx
+8567   6491    1       3       7       7       67      567     567     3567    8567    134     135     NRAAAA  RPJAAA  VVVVxx
+597    6492    1       1       7       17      97      597     597     597     597     194     195     ZWAAAA  SPJAAA  AAAAxx
+4222   6493    0       2       2       2       22      222     222     4222    4222    44      45      KGAAAA  TPJAAA  HHHHxx
+9322   6494    0       2       2       2       22      322     1322    4322    9322    44      45      OUAAAA  UPJAAA  OOOOxx
+624    6495    0       0       4       4       24      624     624     624     624     48      49      AYAAAA  VPJAAA  VVVVxx
+4329   6496    1       1       9       9       29      329     329     4329    4329    58      59      NKAAAA  WPJAAA  AAAAxx
+6781   6497    1       1       1       1       81      781     781     1781    6781    162     163     VAAAAA  XPJAAA  HHHHxx
+1673   6498    1       1       3       13      73      673     1673    1673    1673    146     147     JMAAAA  YPJAAA  OOOOxx
+6633   6499    1       1       3       13      33      633     633     1633    6633    66      67      DVAAAA  ZPJAAA  VVVVxx
+2569   6500    1       1       9       9       69      569     569     2569    2569    138     139     VUAAAA  AQJAAA  AAAAxx
+4995   6501    1       3       5       15      95      995     995     4995    4995    190     191     DKAAAA  BQJAAA  HHHHxx
+2749   6502    1       1       9       9       49      749     749     2749    2749    98      99      TBAAAA  CQJAAA  OOOOxx
+9044   6503    0       0       4       4       44      44      1044    4044    9044    88      89      WJAAAA  DQJAAA  VVVVxx
+5823   6504    1       3       3       3       23      823     1823    823     5823    46      47      ZPAAAA  EQJAAA  AAAAxx
+9366   6505    0       2       6       6       66      366     1366    4366    9366    132     133     GWAAAA  FQJAAA  HHHHxx
+1169   6506    1       1       9       9       69      169     1169    1169    1169    138     139     ZSAAAA  GQJAAA  OOOOxx
+1300   6507    0       0       0       0       0       300     1300    1300    1300    0       1       AYAAAA  HQJAAA  VVVVxx
+9973   6508    1       1       3       13      73      973     1973    4973    9973    146     147     PTAAAA  IQJAAA  AAAAxx
+2092   6509    0       0       2       12      92      92      92      2092    2092    184     185     MCAAAA  JQJAAA  HHHHxx
+9776   6510    0       0       6       16      76      776     1776    4776    9776    152     153     AMAAAA  KQJAAA  OOOOxx
+7612   6511    0       0       2       12      12      612     1612    2612    7612    24      25      UGAAAA  LQJAAA  VVVVxx
+7190   6512    0       2       0       10      90      190     1190    2190    7190    180     181     OQAAAA  MQJAAA  AAAAxx
+5147   6513    1       3       7       7       47      147     1147    147     5147    94      95      ZPAAAA  NQJAAA  HHHHxx
+3722   6514    0       2       2       2       22      722     1722    3722    3722    44      45      ENAAAA  OQJAAA  OOOOxx
+5858   6515    0       2       8       18      58      858     1858    858     5858    116     117     IRAAAA  PQJAAA  VVVVxx
+3204   6516    0       0       4       4       4       204     1204    3204    3204    8       9       GTAAAA  QQJAAA  AAAAxx
+8994   6517    0       2       4       14      94      994     994     3994    8994    188     189     YHAAAA  RQJAAA  HHHHxx
+7478   6518    0       2       8       18      78      478     1478    2478    7478    156     157     QBAAAA  SQJAAA  OOOOxx
+9624   6519    0       0       4       4       24      624     1624    4624    9624    48      49      EGAAAA  TQJAAA  VVVVxx
+6639   6520    1       3       9       19      39      639     639     1639    6639    78      79      JVAAAA  UQJAAA  AAAAxx
+369    6521    1       1       9       9       69      369     369     369     369     138     139     FOAAAA  VQJAAA  HHHHxx
+7766   6522    0       2       6       6       66      766     1766    2766    7766    132     133     SMAAAA  WQJAAA  OOOOxx
+4094   6523    0       2       4       14      94      94      94      4094    4094    188     189     MBAAAA  XQJAAA  VVVVxx
+9556   6524    0       0       6       16      56      556     1556    4556    9556    112     113     ODAAAA  YQJAAA  AAAAxx
+4887   6525    1       3       7       7       87      887     887     4887    4887    174     175     ZFAAAA  ZQJAAA  HHHHxx
+2321   6526    1       1       1       1       21      321     321     2321    2321    42      43      HLAAAA  ARJAAA  OOOOxx
+9201   6527    1       1       1       1       1       201     1201    4201    9201    2       3       XPAAAA  BRJAAA  VVVVxx
+1627   6528    1       3       7       7       27      627     1627    1627    1627    54      55      PKAAAA  CRJAAA  AAAAxx
+150    6529    0       2       0       10      50      150     150     150     150     100     101     UFAAAA  DRJAAA  HHHHxx
+8010   6530    0       2       0       10      10      10      10      3010    8010    20      21      CWAAAA  ERJAAA  OOOOxx
+8026   6531    0       2       6       6       26      26      26      3026    8026    52      53      SWAAAA  FRJAAA  VVVVxx
+5495   6532    1       3       5       15      95      495     1495    495     5495    190     191     JDAAAA  GRJAAA  AAAAxx
+6213   6533    1       1       3       13      13      213     213     1213    6213    26      27      ZEAAAA  HRJAAA  HHHHxx
+6464   6534    0       0       4       4       64      464     464     1464    6464    128     129     QOAAAA  IRJAAA  OOOOxx
+1158   6535    0       2       8       18      58      158     1158    1158    1158    116     117     OSAAAA  JRJAAA  VVVVxx
+8669   6536    1       1       9       9       69      669     669     3669    8669    138     139     LVAAAA  KRJAAA  AAAAxx
+3225   6537    1       1       5       5       25      225     1225    3225    3225    50      51      BUAAAA  LRJAAA  HHHHxx
+1294   6538    0       2       4       14      94      294     1294    1294    1294    188     189     UXAAAA  MRJAAA  OOOOxx
+2166   6539    0       2       6       6       66      166     166     2166    2166    132     133     IFAAAA  NRJAAA  VVVVxx
+9328   6540    0       0       8       8       28      328     1328    4328    9328    56      57      UUAAAA  ORJAAA  AAAAxx
+8431   6541    1       3       1       11      31      431     431     3431    8431    62      63      HMAAAA  PRJAAA  HHHHxx
+7100   6542    0       0       0       0       0       100     1100    2100    7100    0       1       CNAAAA  QRJAAA  OOOOxx
+8126   6543    0       2       6       6       26      126     126     3126    8126    52      53      OAAAAA  RRJAAA  VVVVxx
+2185   6544    1       1       5       5       85      185     185     2185    2185    170     171     BGAAAA  SRJAAA  AAAAxx
+5697   6545    1       1       7       17      97      697     1697    697     5697    194     195     DLAAAA  TRJAAA  HHHHxx
+5531   6546    1       3       1       11      31      531     1531    531     5531    62      63      TEAAAA  URJAAA  OOOOxx
+3020   6547    0       0       0       0       20      20      1020    3020    3020    40      41      EMAAAA  VRJAAA  VVVVxx
+3076   6548    0       0       6       16      76      76      1076    3076    3076    152     153     IOAAAA  WRJAAA  AAAAxx
+9228   6549    0       0       8       8       28      228     1228    4228    9228    56      57      YQAAAA  XRJAAA  HHHHxx
+1734   6550    0       2       4       14      34      734     1734    1734    1734    68      69      SOAAAA  YRJAAA  OOOOxx
+7616   6551    0       0       6       16      16      616     1616    2616    7616    32      33      YGAAAA  ZRJAAA  VVVVxx
+9059   6552    1       3       9       19      59      59      1059    4059    9059    118     119     LKAAAA  ASJAAA  AAAAxx
+323    6553    1       3       3       3       23      323     323     323     323     46      47      LMAAAA  BSJAAA  HHHHxx
+1283   6554    1       3       3       3       83      283     1283    1283    1283    166     167     JXAAAA  CSJAAA  OOOOxx
+9535   6555    1       3       5       15      35      535     1535    4535    9535    70      71      TCAAAA  DSJAAA  VVVVxx
+2580   6556    0       0       0       0       80      580     580     2580    2580    160     161     GVAAAA  ESJAAA  AAAAxx
+7633   6557    1       1       3       13      33      633     1633    2633    7633    66      67      PHAAAA  FSJAAA  HHHHxx
+9497   6558    1       1       7       17      97      497     1497    4497    9497    194     195     HBAAAA  GSJAAA  OOOOxx
+9842   6559    0       2       2       2       42      842     1842    4842    9842    84      85      OOAAAA  HSJAAA  VVVVxx
+3426   6560    0       2       6       6       26      426     1426    3426    3426    52      53      UBAAAA  ISJAAA  AAAAxx
+7650   6561    0       2       0       10      50      650     1650    2650    7650    100     101     GIAAAA  JSJAAA  HHHHxx
+9935   6562    1       3       5       15      35      935     1935    4935    9935    70      71      DSAAAA  KSJAAA  OOOOxx
+9354   6563    0       2       4       14      54      354     1354    4354    9354    108     109     UVAAAA  LSJAAA  VVVVxx
+5569   6564    1       1       9       9       69      569     1569    569     5569    138     139     FGAAAA  MSJAAA  AAAAxx
+5765   6565    1       1       5       5       65      765     1765    765     5765    130     131     TNAAAA  NSJAAA  HHHHxx
+7283   6566    1       3       3       3       83      283     1283    2283    7283    166     167     DUAAAA  OSJAAA  OOOOxx
+1068   6567    0       0       8       8       68      68      1068    1068    1068    136     137     CPAAAA  PSJAAA  VVVVxx
+1641   6568    1       1       1       1       41      641     1641    1641    1641    82      83      DLAAAA  QSJAAA  AAAAxx
+1688   6569    0       0       8       8       88      688     1688    1688    1688    176     177     YMAAAA  RSJAAA  HHHHxx
+1133   6570    1       1       3       13      33      133     1133    1133    1133    66      67      PRAAAA  SSJAAA  OOOOxx
+4493   6571    1       1       3       13      93      493     493     4493    4493    186     187     VQAAAA  TSJAAA  VVVVxx
+3354   6572    0       2       4       14      54      354     1354    3354    3354    108     109     AZAAAA  USJAAA  AAAAxx
+4029   6573    1       1       9       9       29      29      29      4029    4029    58      59      ZYAAAA  VSJAAA  HHHHxx
+6704   6574    0       0       4       4       4       704     704     1704    6704    8       9       WXAAAA  WSJAAA  OOOOxx
+3221   6575    1       1       1       1       21      221     1221    3221    3221    42      43      XTAAAA  XSJAAA  VVVVxx
+9432   6576    0       0       2       12      32      432     1432    4432    9432    64      65      UYAAAA  YSJAAA  AAAAxx
+6990   6577    0       2       0       10      90      990     990     1990    6990    180     181     WIAAAA  ZSJAAA  HHHHxx
+1760   6578    0       0       0       0       60      760     1760    1760    1760    120     121     SPAAAA  ATJAAA  OOOOxx
+4754   6579    0       2       4       14      54      754     754     4754    4754    108     109     WAAAAA  BTJAAA  VVVVxx
+7724   6580    0       0       4       4       24      724     1724    2724    7724    48      49      CLAAAA  CTJAAA  AAAAxx
+9487   6581    1       3       7       7       87      487     1487    4487    9487    174     175     XAAAAA  DTJAAA  HHHHxx
+166    6582    0       2       6       6       66      166     166     166     166     132     133     KGAAAA  ETJAAA  OOOOxx
+5479   6583    1       3       9       19      79      479     1479    479     5479    158     159     TCAAAA  FTJAAA  VVVVxx
+8744   6584    0       0       4       4       44      744     744     3744    8744    88      89      IYAAAA  GTJAAA  AAAAxx
+5746   6585    0       2       6       6       46      746     1746    746     5746    92      93      ANAAAA  HTJAAA  HHHHxx
+907    6586    1       3       7       7       7       907     907     907     907     14      15      XIAAAA  ITJAAA  OOOOxx
+3968   6587    0       0       8       8       68      968     1968    3968    3968    136     137     QWAAAA  JTJAAA  VVVVxx
+5721   6588    1       1       1       1       21      721     1721    721     5721    42      43      BMAAAA  KTJAAA  AAAAxx
+6738   6589    0       2       8       18      38      738     738     1738    6738    76      77      EZAAAA  LTJAAA  HHHHxx
+4097   6590    1       1       7       17      97      97      97      4097    4097    194     195     PBAAAA  MTJAAA  OOOOxx
+8456   6591    0       0       6       16      56      456     456     3456    8456    112     113     GNAAAA  NTJAAA  VVVVxx
+1269   6592    1       1       9       9       69      269     1269    1269    1269    138     139     VWAAAA  OTJAAA  AAAAxx
+7997   6593    1       1       7       17      97      997     1997    2997    7997    194     195     PVAAAA  PTJAAA  HHHHxx
+9457   6594    1       1       7       17      57      457     1457    4457    9457    114     115     TZAAAA  QTJAAA  OOOOxx
+1159   6595    1       3       9       19      59      159     1159    1159    1159    118     119     PSAAAA  RTJAAA  VVVVxx
+1631   6596    1       3       1       11      31      631     1631    1631    1631    62      63      TKAAAA  STJAAA  AAAAxx
+2019   6597    1       3       9       19      19      19      19      2019    2019    38      39      RZAAAA  TTJAAA  HHHHxx
+3186   6598    0       2       6       6       86      186     1186    3186    3186    172     173     OSAAAA  UTJAAA  OOOOxx
+5587   6599    1       3       7       7       87      587     1587    587     5587    174     175     XGAAAA  VTJAAA  VVVVxx
+9172   6600    0       0       2       12      72      172     1172    4172    9172    144     145     UOAAAA  WTJAAA  AAAAxx
+5589   6601    1       1       9       9       89      589     1589    589     5589    178     179     ZGAAAA  XTJAAA  HHHHxx
+5103   6602    1       3       3       3       3       103     1103    103     5103    6       7       HOAAAA  YTJAAA  OOOOxx
+3177   6603    1       1       7       17      77      177     1177    3177    3177    154     155     FSAAAA  ZTJAAA  VVVVxx
+8887   6604    1       3       7       7       87      887     887     3887    8887    174     175     VDAAAA  AUJAAA  AAAAxx
+12     6605    0       0       2       12      12      12      12      12      12      24      25      MAAAAA  BUJAAA  HHHHxx
+8575   6606    1       3       5       15      75      575     575     3575    8575    150     151     VRAAAA  CUJAAA  OOOOxx
+4335   6607    1       3       5       15      35      335     335     4335    4335    70      71      TKAAAA  DUJAAA  VVVVxx
+4581   6608    1       1       1       1       81      581     581     4581    4581    162     163     FUAAAA  EUJAAA  AAAAxx
+4444   6609    0       0       4       4       44      444     444     4444    4444    88      89      YOAAAA  FUJAAA  HHHHxx
+7978   6610    0       2       8       18      78      978     1978    2978    7978    156     157     WUAAAA  GUJAAA  OOOOxx
+3081   6611    1       1       1       1       81      81      1081    3081    3081    162     163     NOAAAA  HUJAAA  VVVVxx
+4059   6612    1       3       9       19      59      59      59      4059    4059    118     119     DAAAAA  IUJAAA  AAAAxx
+5711   6613    1       3       1       11      11      711     1711    711     5711    22      23      RLAAAA  JUJAAA  HHHHxx
+7069   6614    1       1       9       9       69      69      1069    2069    7069    138     139     XLAAAA  KUJAAA  OOOOxx
+6150   6615    0       2       0       10      50      150     150     1150    6150    100     101     OCAAAA  LUJAAA  VVVVxx
+9550   6616    0       2       0       10      50      550     1550    4550    9550    100     101     IDAAAA  MUJAAA  AAAAxx
+7087   6617    1       3       7       7       87      87      1087    2087    7087    174     175     PMAAAA  NUJAAA  HHHHxx
+9557   6618    1       1       7       17      57      557     1557    4557    9557    114     115     PDAAAA  OUJAAA  OOOOxx
+7856   6619    0       0       6       16      56      856     1856    2856    7856    112     113     EQAAAA  PUJAAA  VVVVxx
+1115   6620    1       3       5       15      15      115     1115    1115    1115    30      31      XQAAAA  QUJAAA  AAAAxx
+1086   6621    0       2       6       6       86      86      1086    1086    1086    172     173     UPAAAA  RUJAAA  HHHHxx
+5048   6622    0       0       8       8       48      48      1048    48      5048    96      97      EMAAAA  SUJAAA  OOOOxx
+5168   6623    0       0       8       8       68      168     1168    168     5168    136     137     UQAAAA  TUJAAA  VVVVxx
+6029   6624    1       1       9       9       29      29      29      1029    6029    58      59      XXAAAA  UUJAAA  AAAAxx
+546    6625    0       2       6       6       46      546     546     546     546     92      93      AVAAAA  VUJAAA  HHHHxx
+2908   6626    0       0       8       8       8       908     908     2908    2908    16      17      WHAAAA  WUJAAA  OOOOxx
+779    6627    1       3       9       19      79      779     779     779     779     158     159     ZDAAAA  XUJAAA  VVVVxx
+4202   6628    0       2       2       2       2       202     202     4202    4202    4       5       QFAAAA  YUJAAA  AAAAxx
+9984   6629    0       0       4       4       84      984     1984    4984    9984    168     169     AUAAAA  ZUJAAA  HHHHxx
+4730   6630    0       2       0       10      30      730     730     4730    4730    60      61      YZAAAA  AVJAAA  OOOOxx
+6517   6631    1       1       7       17      17      517     517     1517    6517    34      35      RQAAAA  BVJAAA  VVVVxx
+8410   6632    0       2       0       10      10      410     410     3410    8410    20      21      MLAAAA  CVJAAA  AAAAxx
+4793   6633    1       1       3       13      93      793     793     4793    4793    186     187     JCAAAA  DVJAAA  HHHHxx
+3431   6634    1       3       1       11      31      431     1431    3431    3431    62      63      ZBAAAA  EVJAAA  OOOOxx
+2481   6635    1       1       1       1       81      481     481     2481    2481    162     163     LRAAAA  FVJAAA  VVVVxx
+3905   6636    1       1       5       5       5       905     1905    3905    3905    10      11      FUAAAA  GVJAAA  AAAAxx
+8807   6637    1       3       7       7       7       807     807     3807    8807    14      15      TAAAAA  HVJAAA  HHHHxx
+2660   6638    0       0       0       0       60      660     660     2660    2660    120     121     IYAAAA  IVJAAA  OOOOxx
+4985   6639    1       1       5       5       85      985     985     4985    4985    170     171     TJAAAA  JVJAAA  VVVVxx
+3080   6640    0       0       0       0       80      80      1080    3080    3080    160     161     MOAAAA  KVJAAA  AAAAxx
+1090   6641    0       2       0       10      90      90      1090    1090    1090    180     181     YPAAAA  LVJAAA  HHHHxx
+6917   6642    1       1       7       17      17      917     917     1917    6917    34      35      BGAAAA  MVJAAA  OOOOxx
+5177   6643    1       1       7       17      77      177     1177    177     5177    154     155     DRAAAA  NVJAAA  VVVVxx
+2729   6644    1       1       9       9       29      729     729     2729    2729    58      59      ZAAAAA  OVJAAA  AAAAxx
+9706   6645    0       2       6       6       6       706     1706    4706    9706    12      13      IJAAAA  PVJAAA  HHHHxx
+9929   6646    1       1       9       9       29      929     1929    4929    9929    58      59      XRAAAA  QVJAAA  OOOOxx
+1547   6647    1       3       7       7       47      547     1547    1547    1547    94      95      NHAAAA  RVJAAA  VVVVxx
+2798   6648    0       2       8       18      98      798     798     2798    2798    196     197     QDAAAA  SVJAAA  AAAAxx
+4420   6649    0       0       0       0       20      420     420     4420    4420    40      41      AOAAAA  TVJAAA  HHHHxx
+6771   6650    1       3       1       11      71      771     771     1771    6771    142     143     LAAAAA  UVJAAA  OOOOxx
+2004   6651    0       0       4       4       4       4       4       2004    2004    8       9       CZAAAA  VVJAAA  VVVVxx
+8686   6652    0       2       6       6       86      686     686     3686    8686    172     173     CWAAAA  WVJAAA  AAAAxx
+3663   6653    1       3       3       3       63      663     1663    3663    3663    126     127     XKAAAA  XVJAAA  HHHHxx
+806    6654    0       2       6       6       6       806     806     806     806     12      13      AFAAAA  YVJAAA  OOOOxx
+4309   6655    1       1       9       9       9       309     309     4309    4309    18      19      TJAAAA  ZVJAAA  VVVVxx
+7443   6656    1       3       3       3       43      443     1443    2443    7443    86      87      HAAAAA  AWJAAA  AAAAxx
+5779   6657    1       3       9       19      79      779     1779    779     5779    158     159     HOAAAA  BWJAAA  HHHHxx
+8821   6658    1       1       1       1       21      821     821     3821    8821    42      43      HBAAAA  CWJAAA  OOOOxx
+4198   6659    0       2       8       18      98      198     198     4198    4198    196     197     MFAAAA  DWJAAA  VVVVxx
+8115   6660    1       3       5       15      15      115     115     3115    8115    30      31      DAAAAA  EWJAAA  AAAAxx
+9554   6661    0       2       4       14      54      554     1554    4554    9554    108     109     MDAAAA  FWJAAA  HHHHxx
+8956   6662    0       0       6       16      56      956     956     3956    8956    112     113     MGAAAA  GWJAAA  OOOOxx
+4733   6663    1       1       3       13      33      733     733     4733    4733    66      67      BAAAAA  HWJAAA  VVVVxx
+5417   6664    1       1       7       17      17      417     1417    417     5417    34      35      JAAAAA  IWJAAA  AAAAxx
+4792   6665    0       0       2       12      92      792     792     4792    4792    184     185     ICAAAA  JWJAAA  HHHHxx
+462    6666    0       2       2       2       62      462     462     462     462     124     125     URAAAA  KWJAAA  OOOOxx
+3687   6667    1       3       7       7       87      687     1687    3687    3687    174     175     VLAAAA  LWJAAA  VVVVxx
+2013   6668    1       1       3       13      13      13      13      2013    2013    26      27      LZAAAA  MWJAAA  AAAAxx
+5386   6669    0       2       6       6       86      386     1386    386     5386    172     173     EZAAAA  NWJAAA  HHHHxx
+2816   6670    0       0       6       16      16      816     816     2816    2816    32      33      IEAAAA  OWJAAA  OOOOxx
+7827   6671    1       3       7       7       27      827     1827    2827    7827    54      55      BPAAAA  PWJAAA  VVVVxx
+5077   6672    1       1       7       17      77      77      1077    77      5077    154     155     HNAAAA  QWJAAA  AAAAxx
+6039   6673    1       3       9       19      39      39      39      1039    6039    78      79      HYAAAA  RWJAAA  HHHHxx
+215    6674    1       3       5       15      15      215     215     215     215     30      31      HIAAAA  SWJAAA  OOOOxx
+855    6675    1       3       5       15      55      855     855     855     855     110     111     XGAAAA  TWJAAA  VVVVxx
+9692   6676    0       0       2       12      92      692     1692    4692    9692    184     185     UIAAAA  UWJAAA  AAAAxx
+8391   6677    1       3       1       11      91      391     391     3391    8391    182     183     TKAAAA  VWJAAA  HHHHxx
+8424   6678    0       0       4       4       24      424     424     3424    8424    48      49      AMAAAA  WWJAAA  OOOOxx
+6331   6679    1       3       1       11      31      331     331     1331    6331    62      63      NJAAAA  XWJAAA  VVVVxx
+6561   6680    1       1       1       1       61      561     561     1561    6561    122     123     JSAAAA  YWJAAA  AAAAxx
+8955   6681    1       3       5       15      55      955     955     3955    8955    110     111     LGAAAA  ZWJAAA  HHHHxx
+1764   6682    0       0       4       4       64      764     1764    1764    1764    128     129     WPAAAA  AXJAAA  OOOOxx
+6623   6683    1       3       3       3       23      623     623     1623    6623    46      47      TUAAAA  BXJAAA  VVVVxx
+2900   6684    0       0       0       0       0       900     900     2900    2900    0       1       OHAAAA  CXJAAA  AAAAxx
+7048   6685    0       0       8       8       48      48      1048    2048    7048    96      97      CLAAAA  DXJAAA  HHHHxx
+3843   6686    1       3       3       3       43      843     1843    3843    3843    86      87      VRAAAA  EXJAAA  OOOOxx
+4855   6687    1       3       5       15      55      855     855     4855    4855    110     111     TEAAAA  FXJAAA  VVVVxx
+7383   6688    1       3       3       3       83      383     1383    2383    7383    166     167     ZXAAAA  GXJAAA  AAAAxx
+7765   6689    1       1       5       5       65      765     1765    2765    7765    130     131     RMAAAA  HXJAAA  HHHHxx
+1125   6690    1       1       5       5       25      125     1125    1125    1125    50      51      HRAAAA  IXJAAA  OOOOxx
+755    6691    1       3       5       15      55      755     755     755     755     110     111     BDAAAA  JXJAAA  VVVVxx
+2995   6692    1       3       5       15      95      995     995     2995    2995    190     191     FLAAAA  KXJAAA  AAAAxx
+8907   6693    1       3       7       7       7       907     907     3907    8907    14      15      PEAAAA  LXJAAA  HHHHxx
+9357   6694    1       1       7       17      57      357     1357    4357    9357    114     115     XVAAAA  MXJAAA  OOOOxx
+4469   6695    1       1       9       9       69      469     469     4469    4469    138     139     XPAAAA  NXJAAA  VVVVxx
+2147   6696    1       3       7       7       47      147     147     2147    2147    94      95      PEAAAA  OXJAAA  AAAAxx
+2952   6697    0       0       2       12      52      952     952     2952    2952    104     105     OJAAAA  PXJAAA  HHHHxx
+1324   6698    0       0       4       4       24      324     1324    1324    1324    48      49      YYAAAA  QXJAAA  OOOOxx
+1173   6699    1       1       3       13      73      173     1173    1173    1173    146     147     DTAAAA  RXJAAA  VVVVxx
+3169   6700    1       1       9       9       69      169     1169    3169    3169    138     139     XRAAAA  SXJAAA  AAAAxx
+5149   6701    1       1       9       9       49      149     1149    149     5149    98      99      BQAAAA  TXJAAA  HHHHxx
+9660   6702    0       0       0       0       60      660     1660    4660    9660    120     121     OHAAAA  UXJAAA  OOOOxx
+3446   6703    0       2       6       6       46      446     1446    3446    3446    92      93      OCAAAA  VXJAAA  VVVVxx
+6988   6704    0       0       8       8       88      988     988     1988    6988    176     177     UIAAAA  WXJAAA  AAAAxx
+5829   6705    1       1       9       9       29      829     1829    829     5829    58      59      FQAAAA  XXJAAA  HHHHxx
+7166   6706    0       2       6       6       66      166     1166    2166    7166    132     133     QPAAAA  YXJAAA  OOOOxx
+3940   6707    0       0       0       0       40      940     1940    3940    3940    80      81      OVAAAA  ZXJAAA  VVVVxx
+2645   6708    1       1       5       5       45      645     645     2645    2645    90      91      TXAAAA  AYJAAA  AAAAxx
+478    6709    0       2       8       18      78      478     478     478     478     156     157     KSAAAA  BYJAAA  HHHHxx
+1156   6710    0       0       6       16      56      156     1156    1156    1156    112     113     MSAAAA  CYJAAA  OOOOxx
+2731   6711    1       3       1       11      31      731     731     2731    2731    62      63      BBAAAA  DYJAAA  VVVVxx
+5637   6712    1       1       7       17      37      637     1637    637     5637    74      75      VIAAAA  EYJAAA  AAAAxx
+7517   6713    1       1       7       17      17      517     1517    2517    7517    34      35      DDAAAA  FYJAAA  HHHHxx
+5331   6714    1       3       1       11      31      331     1331    331     5331    62      63      BXAAAA  GYJAAA  OOOOxx
+9640   6715    0       0       0       0       40      640     1640    4640    9640    80      81      UGAAAA  HYJAAA  VVVVxx
+4108   6716    0       0       8       8       8       108     108     4108    4108    16      17      ACAAAA  IYJAAA  AAAAxx
+1087   6717    1       3       7       7       87      87      1087    1087    1087    174     175     VPAAAA  JYJAAA  HHHHxx
+8017   6718    1       1       7       17      17      17      17      3017    8017    34      35      JWAAAA  KYJAAA  OOOOxx
+8795   6719    1       3       5       15      95      795     795     3795    8795    190     191     HAAAAA  LYJAAA  VVVVxx
+7060   6720    0       0       0       0       60      60      1060    2060    7060    120     121     OLAAAA  MYJAAA  AAAAxx
+9450   6721    0       2       0       10      50      450     1450    4450    9450    100     101     MZAAAA  NYJAAA  HHHHxx
+390    6722    0       2       0       10      90      390     390     390     390     180     181     APAAAA  OYJAAA  OOOOxx
+66     6723    0       2       6       6       66      66      66      66      66      132     133     OCAAAA  PYJAAA  VVVVxx
+8789   6724    1       1       9       9       89      789     789     3789    8789    178     179     BAAAAA  QYJAAA  AAAAxx
+9260   6725    0       0       0       0       60      260     1260    4260    9260    120     121     ESAAAA  RYJAAA  HHHHxx
+6679   6726    1       3       9       19      79      679     679     1679    6679    158     159     XWAAAA  SYJAAA  OOOOxx
+9052   6727    0       0       2       12      52      52      1052    4052    9052    104     105     EKAAAA  TYJAAA  VVVVxx
+9561   6728    1       1       1       1       61      561     1561    4561    9561    122     123     TDAAAA  UYJAAA  AAAAxx
+9725   6729    1       1       5       5       25      725     1725    4725    9725    50      51      BKAAAA  VYJAAA  HHHHxx
+6298   6730    0       2       8       18      98      298     298     1298    6298    196     197     GIAAAA  WYJAAA  OOOOxx
+8654   6731    0       2       4       14      54      654     654     3654    8654    108     109     WUAAAA  XYJAAA  VVVVxx
+8725   6732    1       1       5       5       25      725     725     3725    8725    50      51      PXAAAA  YYJAAA  AAAAxx
+9377   6733    1       1       7       17      77      377     1377    4377    9377    154     155     RWAAAA  ZYJAAA  HHHHxx
+3807   6734    1       3       7       7       7       807     1807    3807    3807    14      15      LQAAAA  AZJAAA  OOOOxx
+8048   6735    0       0       8       8       48      48      48      3048    8048    96      97      OXAAAA  BZJAAA  VVVVxx
+764    6736    0       0       4       4       64      764     764     764     764     128     129     KDAAAA  CZJAAA  AAAAxx
+9702   6737    0       2       2       2       2       702     1702    4702    9702    4       5       EJAAAA  DZJAAA  HHHHxx
+8060   6738    0       0       0       0       60      60      60      3060    8060    120     121     AYAAAA  EZJAAA  OOOOxx
+6371   6739    1       3       1       11      71      371     371     1371    6371    142     143     BLAAAA  FZJAAA  VVVVxx
+5237   6740    1       1       7       17      37      237     1237    237     5237    74      75      LTAAAA  GZJAAA  AAAAxx
+743    6741    1       3       3       3       43      743     743     743     743     86      87      PCAAAA  HZJAAA  HHHHxx
+7395   6742    1       3       5       15      95      395     1395    2395    7395    190     191     LYAAAA  IZJAAA  OOOOxx
+3365   6743    1       1       5       5       65      365     1365    3365    3365    130     131     LZAAAA  JZJAAA  VVVVxx
+6667   6744    1       3       7       7       67      667     667     1667    6667    134     135     LWAAAA  KZJAAA  AAAAxx
+3445   6745    1       1       5       5       45      445     1445    3445    3445    90      91      NCAAAA  LZJAAA  HHHHxx
+4019   6746    1       3       9       19      19      19      19      4019    4019    38      39      PYAAAA  MZJAAA  OOOOxx
+7035   6747    1       3       5       15      35      35      1035    2035    7035    70      71      PKAAAA  NZJAAA  VVVVxx
+5274   6748    0       2       4       14      74      274     1274    274     5274    148     149     WUAAAA  OZJAAA  AAAAxx
+519    6749    1       3       9       19      19      519     519     519     519     38      39      ZTAAAA  PZJAAA  HHHHxx
+2801   6750    1       1       1       1       1       801     801     2801    2801    2       3       TDAAAA  QZJAAA  OOOOxx
+3320   6751    0       0       0       0       20      320     1320    3320    3320    40      41      SXAAAA  RZJAAA  VVVVxx
+3153   6752    1       1       3       13      53      153     1153    3153    3153    106     107     HRAAAA  SZJAAA  AAAAxx
+7680   6753    0       0       0       0       80      680     1680    2680    7680    160     161     KJAAAA  TZJAAA  HHHHxx
+8942   6754    0       2       2       2       42      942     942     3942    8942    84      85      YFAAAA  UZJAAA  OOOOxx
+3195   6755    1       3       5       15      95      195     1195    3195    3195    190     191     XSAAAA  VZJAAA  VVVVxx
+2287   6756    1       3       7       7       87      287     287     2287    2287    174     175     ZJAAAA  WZJAAA  AAAAxx
+8325   6757    1       1       5       5       25      325     325     3325    8325    50      51      FIAAAA  XZJAAA  HHHHxx
+2603   6758    1       3       3       3       3       603     603     2603    2603    6       7       DWAAAA  YZJAAA  OOOOxx
+5871   6759    1       3       1       11      71      871     1871    871     5871    142     143     VRAAAA  ZZJAAA  VVVVxx
+1773   6760    1       1       3       13      73      773     1773    1773    1773    146     147     FQAAAA  AAKAAA  AAAAxx
+3323   6761    1       3       3       3       23      323     1323    3323    3323    46      47      VXAAAA  BAKAAA  HHHHxx
+2053   6762    1       1       3       13      53      53      53      2053    2053    106     107     ZAAAAA  CAKAAA  OOOOxx
+4062   6763    0       2       2       2       62      62      62      4062    4062    124     125     GAAAAA  DAKAAA  VVVVxx
+4611   6764    1       3       1       11      11      611     611     4611    4611    22      23      JVAAAA  EAKAAA  AAAAxx
+3451   6765    1       3       1       11      51      451     1451    3451    3451    102     103     TCAAAA  FAKAAA  HHHHxx
+1819   6766    1       3       9       19      19      819     1819    1819    1819    38      39      ZRAAAA  GAKAAA  OOOOxx
+9806   6767    0       2       6       6       6       806     1806    4806    9806    12      13      ENAAAA  HAKAAA  VVVVxx
+6619   6768    1       3       9       19      19      619     619     1619    6619    38      39      PUAAAA  IAKAAA  AAAAxx
+1031   6769    1       3       1       11      31      31      1031    1031    1031    62      63      RNAAAA  JAKAAA  HHHHxx
+1865   6770    1       1       5       5       65      865     1865    1865    1865    130     131     TTAAAA  KAKAAA  OOOOxx
+6282   6771    0       2       2       2       82      282     282     1282    6282    164     165     QHAAAA  LAKAAA  VVVVxx
+1178   6772    0       2       8       18      78      178     1178    1178    1178    156     157     ITAAAA  MAKAAA  AAAAxx
+8007   6773    1       3       7       7       7       7       7       3007    8007    14      15      ZVAAAA  NAKAAA  HHHHxx
+9126   6774    0       2       6       6       26      126     1126    4126    9126    52      53      ANAAAA  OAKAAA  OOOOxx
+9113   6775    1       1       3       13      13      113     1113    4113    9113    26      27      NMAAAA  PAKAAA  VVVVxx
+537    6776    1       1       7       17      37      537     537     537     537     74      75      RUAAAA  QAKAAA  AAAAxx
+6208   6777    0       0       8       8       8       208     208     1208    6208    16      17      UEAAAA  RAKAAA  HHHHxx
+1626   6778    0       2       6       6       26      626     1626    1626    1626    52      53      OKAAAA  SAKAAA  OOOOxx
+7188   6779    0       0       8       8       88      188     1188    2188    7188    176     177     MQAAAA  TAKAAA  VVVVxx
+9216   6780    0       0       6       16      16      216     1216    4216    9216    32      33      MQAAAA  UAKAAA  AAAAxx
+6134   6781    0       2       4       14      34      134     134     1134    6134    68      69      YBAAAA  VAKAAA  HHHHxx
+2074   6782    0       2       4       14      74      74      74      2074    2074    148     149     UBAAAA  WAKAAA  OOOOxx
+6369   6783    1       1       9       9       69      369     369     1369    6369    138     139     ZKAAAA  XAKAAA  VVVVxx
+9306   6784    0       2       6       6       6       306     1306    4306    9306    12      13      YTAAAA  YAKAAA  AAAAxx
+3155   6785    1       3       5       15      55      155     1155    3155    3155    110     111     JRAAAA  ZAKAAA  HHHHxx
+3611   6786    1       3       1       11      11      611     1611    3611    3611    22      23      XIAAAA  ABKAAA  OOOOxx
+6530   6787    0       2       0       10      30      530     530     1530    6530    60      61      ERAAAA  BBKAAA  VVVVxx
+6979   6788    1       3       9       19      79      979     979     1979    6979    158     159     LIAAAA  CBKAAA  AAAAxx
+9129   6789    1       1       9       9       29      129     1129    4129    9129    58      59      DNAAAA  DBKAAA  HHHHxx
+8013   6790    1       1       3       13      13      13      13      3013    8013    26      27      FWAAAA  EBKAAA  OOOOxx
+6926   6791    0       2       6       6       26      926     926     1926    6926    52      53      KGAAAA  FBKAAA  VVVVxx
+1877   6792    1       1       7       17      77      877     1877    1877    1877    154     155     FUAAAA  GBKAAA  AAAAxx
+1882   6793    0       2       2       2       82      882     1882    1882    1882    164     165     KUAAAA  HBKAAA  HHHHxx
+6720   6794    0       0       0       0       20      720     720     1720    6720    40      41      MYAAAA  IBKAAA  OOOOxx
+690    6795    0       2       0       10      90      690     690     690     690     180     181     OAAAAA  JBKAAA  VVVVxx
+143    6796    1       3       3       3       43      143     143     143     143     86      87      NFAAAA  KBKAAA  AAAAxx
+7241   6797    1       1       1       1       41      241     1241    2241    7241    82      83      NSAAAA  LBKAAA  HHHHxx
+6461   6798    1       1       1       1       61      461     461     1461    6461    122     123     NOAAAA  MBKAAA  OOOOxx
+2258   6799    0       2       8       18      58      258     258     2258    2258    116     117     WIAAAA  NBKAAA  VVVVxx
+2280   6800    0       0       0       0       80      280     280     2280    2280    160     161     SJAAAA  OBKAAA  AAAAxx
+7556   6801    0       0       6       16      56      556     1556    2556    7556    112     113     QEAAAA  PBKAAA  HHHHxx
+1038   6802    0       2       8       18      38      38      1038    1038    1038    76      77      YNAAAA  QBKAAA  OOOOxx
+2634   6803    0       2       4       14      34      634     634     2634    2634    68      69      IXAAAA  RBKAAA  VVVVxx
+7847   6804    1       3       7       7       47      847     1847    2847    7847    94      95      VPAAAA  SBKAAA  AAAAxx
+4415   6805    1       3       5       15      15      415     415     4415    4415    30      31      VNAAAA  TBKAAA  HHHHxx
+1933   6806    1       1       3       13      33      933     1933    1933    1933    66      67      JWAAAA  UBKAAA  OOOOxx
+8034   6807    0       2       4       14      34      34      34      3034    8034    68      69      AXAAAA  VBKAAA  VVVVxx
+9233   6808    1       1       3       13      33      233     1233    4233    9233    66      67      DRAAAA  WBKAAA  AAAAxx
+6572   6809    0       0       2       12      72      572     572     1572    6572    144     145     USAAAA  XBKAAA  HHHHxx
+1586   6810    0       2       6       6       86      586     1586    1586    1586    172     173     AJAAAA  YBKAAA  OOOOxx
+8512   6811    0       0       2       12      12      512     512     3512    8512    24      25      KPAAAA  ZBKAAA  VVVVxx
+7421   6812    1       1       1       1       21      421     1421    2421    7421    42      43      LZAAAA  ACKAAA  AAAAxx
+503    6813    1       3       3       3       3       503     503     503     503     6       7       JTAAAA  BCKAAA  HHHHxx
+5332   6814    0       0       2       12      32      332     1332    332     5332    64      65      CXAAAA  CCKAAA  OOOOxx
+2602   6815    0       2       2       2       2       602     602     2602    2602    4       5       CWAAAA  DCKAAA  VVVVxx
+2902   6816    0       2       2       2       2       902     902     2902    2902    4       5       QHAAAA  ECKAAA  AAAAxx
+2979   6817    1       3       9       19      79      979     979     2979    2979    158     159     PKAAAA  FCKAAA  HHHHxx
+1431   6818    1       3       1       11      31      431     1431    1431    1431    62      63      BDAAAA  GCKAAA  OOOOxx
+8639   6819    1       3       9       19      39      639     639     3639    8639    78      79      HUAAAA  HCKAAA  VVVVxx
+4218   6820    0       2       8       18      18      218     218     4218    4218    36      37      GGAAAA  ICKAAA  AAAAxx
+7453   6821    1       1       3       13      53      453     1453    2453    7453    106     107     RAAAAA  JCKAAA  HHHHxx
+5448   6822    0       0       8       8       48      448     1448    448     5448    96      97      OBAAAA  KCKAAA  OOOOxx
+6768   6823    0       0       8       8       68      768     768     1768    6768    136     137     IAAAAA  LCKAAA  VVVVxx
+3104   6824    0       0       4       4       4       104     1104    3104    3104    8       9       KPAAAA  MCKAAA  AAAAxx
+2297   6825    1       1       7       17      97      297     297     2297    2297    194     195     JKAAAA  NCKAAA  HHHHxx
+7994   6826    0       2       4       14      94      994     1994    2994    7994    188     189     MVAAAA  OCKAAA  OOOOxx
+550    6827    0       2       0       10      50      550     550     550     550     100     101     EVAAAA  PCKAAA  VVVVxx
+4777   6828    1       1       7       17      77      777     777     4777    4777    154     155     TBAAAA  QCKAAA  AAAAxx
+5962   6829    0       2       2       2       62      962     1962    962     5962    124     125     IVAAAA  RCKAAA  HHHHxx
+1763   6830    1       3       3       3       63      763     1763    1763    1763    126     127     VPAAAA  SCKAAA  OOOOxx
+3654   6831    0       2       4       14      54      654     1654    3654    3654    108     109     OKAAAA  TCKAAA  VVVVxx
+4106   6832    0       2       6       6       6       106     106     4106    4106    12      13      YBAAAA  UCKAAA  AAAAxx
+5156   6833    0       0       6       16      56      156     1156    156     5156    112     113     IQAAAA  VCKAAA  HHHHxx
+422    6834    0       2       2       2       22      422     422     422     422     44      45      GQAAAA  WCKAAA  OOOOxx
+5011   6835    1       3       1       11      11      11      1011    11      5011    22      23      TKAAAA  XCKAAA  VVVVxx
+218    6836    0       2       8       18      18      218     218     218     218     36      37      KIAAAA  YCKAAA  AAAAxx
+9762   6837    0       2       2       2       62      762     1762    4762    9762    124     125     MLAAAA  ZCKAAA  HHHHxx
+6074   6838    0       2       4       14      74      74      74      1074    6074    148     149     QZAAAA  ADKAAA  OOOOxx
+4060   6839    0       0       0       0       60      60      60      4060    4060    120     121     EAAAAA  BDKAAA  VVVVxx
+8680   6840    0       0       0       0       80      680     680     3680    8680    160     161     WVAAAA  CDKAAA  AAAAxx
+5863   6841    1       3       3       3       63      863     1863    863     5863    126     127     NRAAAA  DDKAAA  HHHHxx
+8042   6842    0       2       2       2       42      42      42      3042    8042    84      85      IXAAAA  EDKAAA  OOOOxx
+2964   6843    0       0       4       4       64      964     964     2964    2964    128     129     AKAAAA  FDKAAA  VVVVxx
+6931   6844    1       3       1       11      31      931     931     1931    6931    62      63      PGAAAA  GDKAAA  AAAAxx
+6715   6845    1       3       5       15      15      715     715     1715    6715    30      31      HYAAAA  HDKAAA  HHHHxx
+5859   6846    1       3       9       19      59      859     1859    859     5859    118     119     JRAAAA  IDKAAA  OOOOxx
+6173   6847    1       1       3       13      73      173     173     1173    6173    146     147     LDAAAA  JDKAAA  VVVVxx
+7788   6848    0       0       8       8       88      788     1788    2788    7788    176     177     ONAAAA  KDKAAA  AAAAxx
+9370   6849    0       2       0       10      70      370     1370    4370    9370    140     141     KWAAAA  LDKAAA  HHHHxx
+3038   6850    0       2       8       18      38      38      1038    3038    3038    76      77      WMAAAA  MDKAAA  OOOOxx
+6483   6851    1       3       3       3       83      483     483     1483    6483    166     167     JPAAAA  NDKAAA  VVVVxx
+7534   6852    0       2       4       14      34      534     1534    2534    7534    68      69      UDAAAA  ODKAAA  AAAAxx
+5769   6853    1       1       9       9       69      769     1769    769     5769    138     139     XNAAAA  PDKAAA  HHHHxx
+9152   6854    0       0       2       12      52      152     1152    4152    9152    104     105     AOAAAA  QDKAAA  OOOOxx
+6251   6855    1       3       1       11      51      251     251     1251    6251    102     103     LGAAAA  RDKAAA  VVVVxx
+9209   6856    1       1       9       9       9       209     1209    4209    9209    18      19      FQAAAA  SDKAAA  AAAAxx
+5365   6857    1       1       5       5       65      365     1365    365     5365    130     131     JYAAAA  TDKAAA  HHHHxx
+509    6858    1       1       9       9       9       509     509     509     509     18      19      PTAAAA  UDKAAA  OOOOxx
+3132   6859    0       0       2       12      32      132     1132    3132    3132    64      65      MQAAAA  VDKAAA  VVVVxx
+5373   6860    1       1       3       13      73      373     1373    373     5373    146     147     RYAAAA  WDKAAA  AAAAxx
+4247   6861    1       3       7       7       47      247     247     4247    4247    94      95      JHAAAA  XDKAAA  HHHHxx
+3491   6862    1       3       1       11      91      491     1491    3491    3491    182     183     HEAAAA  YDKAAA  OOOOxx
+495    6863    1       3       5       15      95      495     495     495     495     190     191     BTAAAA  ZDKAAA  VVVVxx
+1594   6864    0       2       4       14      94      594     1594    1594    1594    188     189     IJAAAA  AEKAAA  AAAAxx
+2243   6865    1       3       3       3       43      243     243     2243    2243    86      87      HIAAAA  BEKAAA  HHHHxx
+7780   6866    0       0       0       0       80      780     1780    2780    7780    160     161     GNAAAA  CEKAAA  OOOOxx
+5632   6867    0       0       2       12      32      632     1632    632     5632    64      65      QIAAAA  DEKAAA  VVVVxx
+2679   6868    1       3       9       19      79      679     679     2679    2679    158     159     BZAAAA  EEKAAA  AAAAxx
+1354   6869    0       2       4       14      54      354     1354    1354    1354    108     109     CAAAAA  FEKAAA  HHHHxx
+180    6870    0       0       0       0       80      180     180     180     180     160     161     YGAAAA  GEKAAA  OOOOxx
+7017   6871    1       1       7       17      17      17      1017    2017    7017    34      35      XJAAAA  HEKAAA  VVVVxx
+1867   6872    1       3       7       7       67      867     1867    1867    1867    134     135     VTAAAA  IEKAAA  AAAAxx
+2213   6873    1       1       3       13      13      213     213     2213    2213    26      27      DHAAAA  JEKAAA  HHHHxx
+8773   6874    1       1       3       13      73      773     773     3773    8773    146     147     LZAAAA  KEKAAA  OOOOxx
+1784   6875    0       0       4       4       84      784     1784    1784    1784    168     169     QQAAAA  LEKAAA  VVVVxx
+5961   6876    1       1       1       1       61      961     1961    961     5961    122     123     HVAAAA  MEKAAA  AAAAxx
+8801   6877    1       1       1       1       1       801     801     3801    8801    2       3       NAAAAA  NEKAAA  HHHHxx
+4860   6878    0       0       0       0       60      860     860     4860    4860    120     121     YEAAAA  OEKAAA  OOOOxx
+2214   6879    0       2       4       14      14      214     214     2214    2214    28      29      EHAAAA  PEKAAA  VVVVxx
+1735   6880    1       3       5       15      35      735     1735    1735    1735    70      71      TOAAAA  QEKAAA  AAAAxx
+578    6881    0       2       8       18      78      578     578     578     578     156     157     GWAAAA  REKAAA  HHHHxx
+7853   6882    1       1       3       13      53      853     1853    2853    7853    106     107     BQAAAA  SEKAAA  OOOOxx
+2215   6883    1       3       5       15      15      215     215     2215    2215    30      31      FHAAAA  TEKAAA  VVVVxx
+4704   6884    0       0       4       4       4       704     704     4704    4704    8       9       YYAAAA  UEKAAA  AAAAxx
+9379   6885    1       3       9       19      79      379     1379    4379    9379    158     159     TWAAAA  VEKAAA  HHHHxx
+9745   6886    1       1       5       5       45      745     1745    4745    9745    90      91      VKAAAA  WEKAAA  OOOOxx
+5636   6887    0       0       6       16      36      636     1636    636     5636    72      73      UIAAAA  XEKAAA  VVVVxx
+4548   6888    0       0       8       8       48      548     548     4548    4548    96      97      YSAAAA  YEKAAA  AAAAxx
+6537   6889    1       1       7       17      37      537     537     1537    6537    74      75      LRAAAA  ZEKAAA  HHHHxx
+7748   6890    0       0       8       8       48      748     1748    2748    7748    96      97      AMAAAA  AFKAAA  OOOOxx
+687    6891    1       3       7       7       87      687     687     687     687     174     175     LAAAAA  BFKAAA  VVVVxx
+1243   6892    1       3       3       3       43      243     1243    1243    1243    86      87      VVAAAA  CFKAAA  AAAAxx
+852    6893    0       0       2       12      52      852     852     852     852     104     105     UGAAAA  DFKAAA  HHHHxx
+785    6894    1       1       5       5       85      785     785     785     785     170     171     FEAAAA  EFKAAA  OOOOxx
+2002   6895    0       2       2       2       2       2       2       2002    2002    4       5       AZAAAA  FFKAAA  VVVVxx
+2748   6896    0       0       8       8       48      748     748     2748    2748    96      97      SBAAAA  GFKAAA  AAAAxx
+6075   6897    1       3       5       15      75      75      75      1075    6075    150     151     RZAAAA  HFKAAA  HHHHxx
+7029   6898    1       1       9       9       29      29      1029    2029    7029    58      59      JKAAAA  IFKAAA  OOOOxx
+7474   6899    0       2       4       14      74      474     1474    2474    7474    148     149     MBAAAA  JFKAAA  VVVVxx
+7755   6900    1       3       5       15      55      755     1755    2755    7755    110     111     HMAAAA  KFKAAA  AAAAxx
+1456   6901    0       0       6       16      56      456     1456    1456    1456    112     113     AEAAAA  LFKAAA  HHHHxx
+2808   6902    0       0       8       8       8       808     808     2808    2808    16      17      AEAAAA  MFKAAA  OOOOxx
+4089   6903    1       1       9       9       89      89      89      4089    4089    178     179     HBAAAA  NFKAAA  VVVVxx
+4718   6904    0       2       8       18      18      718     718     4718    4718    36      37      MZAAAA  OFKAAA  AAAAxx
+910    6905    0       2       0       10      10      910     910     910     910     20      21      AJAAAA  PFKAAA  HHHHxx
+2868   6906    0       0       8       8       68      868     868     2868    2868    136     137     IGAAAA  QFKAAA  OOOOxx
+2103   6907    1       3       3       3       3       103     103     2103    2103    6       7       XCAAAA  RFKAAA  VVVVxx
+2407   6908    1       3       7       7       7       407     407     2407    2407    14      15      POAAAA  SFKAAA  AAAAxx
+4353   6909    1       1       3       13      53      353     353     4353    4353    106     107     LLAAAA  TFKAAA  HHHHxx
+7988   6910    0       0       8       8       88      988     1988    2988    7988    176     177     GVAAAA  UFKAAA  OOOOxx
+2750   6911    0       2       0       10      50      750     750     2750    2750    100     101     UBAAAA  VFKAAA  VVVVxx
+2006   6912    0       2       6       6       6       6       6       2006    2006    12      13      EZAAAA  WFKAAA  AAAAxx
+4617   6913    1       1       7       17      17      617     617     4617    4617    34      35      PVAAAA  XFKAAA  HHHHxx
+1251   6914    1       3       1       11      51      251     1251    1251    1251    102     103     DWAAAA  YFKAAA  OOOOxx
+4590   6915    0       2       0       10      90      590     590     4590    4590    180     181     OUAAAA  ZFKAAA  VVVVxx
+1144   6916    0       0       4       4       44      144     1144    1144    1144    88      89      ASAAAA  AGKAAA  AAAAxx
+7131   6917    1       3       1       11      31      131     1131    2131    7131    62      63      HOAAAA  BGKAAA  HHHHxx
+95     6918    1       3       5       15      95      95      95      95      95      190     191     RDAAAA  CGKAAA  OOOOxx
+4827   6919    1       3       7       7       27      827     827     4827    4827    54      55      RDAAAA  DGKAAA  VVVVxx
+4307   6920    1       3       7       7       7       307     307     4307    4307    14      15      RJAAAA  EGKAAA  AAAAxx
+1505   6921    1       1       5       5       5       505     1505    1505    1505    10      11      XFAAAA  FGKAAA  HHHHxx
+8191   6922    1       3       1       11      91      191     191     3191    8191    182     183     BDAAAA  GGKAAA  OOOOxx
+5037   6923    1       1       7       17      37      37      1037    37      5037    74      75      TLAAAA  HGKAAA  VVVVxx
+7363   6924    1       3       3       3       63      363     1363    2363    7363    126     127     FXAAAA  IGKAAA  AAAAxx
+8427   6925    1       3       7       7       27      427     427     3427    8427    54      55      DMAAAA  JGKAAA  HHHHxx
+5231   6926    1       3       1       11      31      231     1231    231     5231    62      63      FTAAAA  KGKAAA  OOOOxx
+2943   6927    1       3       3       3       43      943     943     2943    2943    86      87      FJAAAA  LGKAAA  VVVVxx
+4624   6928    0       0       4       4       24      624     624     4624    4624    48      49      WVAAAA  MGKAAA  AAAAxx
+2020   6929    0       0       0       0       20      20      20      2020    2020    40      41      SZAAAA  NGKAAA  HHHHxx
+6155   6930    1       3       5       15      55      155     155     1155    6155    110     111     TCAAAA  OGKAAA  OOOOxx
+4381   6931    1       1       1       1       81      381     381     4381    4381    162     163     NMAAAA  PGKAAA  VVVVxx
+1057   6932    1       1       7       17      57      57      1057    1057    1057    114     115     ROAAAA  QGKAAA  AAAAxx
+9010   6933    0       2       0       10      10      10      1010    4010    9010    20      21      OIAAAA  RGKAAA  HHHHxx
+4947   6934    1       3       7       7       47      947     947     4947    4947    94      95      HIAAAA  SGKAAA  OOOOxx
+335    6935    1       3       5       15      35      335     335     335     335     70      71      XMAAAA  TGKAAA  VVVVxx
+6890   6936    0       2       0       10      90      890     890     1890    6890    180     181     AFAAAA  UGKAAA  AAAAxx
+5070   6937    0       2       0       10      70      70      1070    70      5070    140     141     ANAAAA  VGKAAA  HHHHxx
+5270   6938    0       2       0       10      70      270     1270    270     5270    140     141     SUAAAA  WGKAAA  OOOOxx
+8657   6939    1       1       7       17      57      657     657     3657    8657    114     115     ZUAAAA  XGKAAA  VVVVxx
+7625   6940    1       1       5       5       25      625     1625    2625    7625    50      51      HHAAAA  YGKAAA  AAAAxx
+5759   6941    1       3       9       19      59      759     1759    759     5759    118     119     NNAAAA  ZGKAAA  HHHHxx
+9483   6942    1       3       3       3       83      483     1483    4483    9483    166     167     TAAAAA  AHKAAA  OOOOxx
+8304   6943    0       0       4       4       4       304     304     3304    8304    8       9       KHAAAA  BHKAAA  VVVVxx
+296    6944    0       0       6       16      96      296     296     296     296     192     193     KLAAAA  CHKAAA  AAAAxx
+1176   6945    0       0       6       16      76      176     1176    1176    1176    152     153     GTAAAA  DHKAAA  HHHHxx
+2069   6946    1       1       9       9       69      69      69      2069    2069    138     139     PBAAAA  EHKAAA  OOOOxx
+1531   6947    1       3       1       11      31      531     1531    1531    1531    62      63      XGAAAA  FHKAAA  VVVVxx
+5329   6948    1       1       9       9       29      329     1329    329     5329    58      59      ZWAAAA  GHKAAA  AAAAxx
+3702   6949    0       2       2       2       2       702     1702    3702    3702    4       5       KMAAAA  HHKAAA  HHHHxx
+6520   6950    0       0       0       0       20      520     520     1520    6520    40      41      UQAAAA  IHKAAA  OOOOxx
+7310   6951    0       2       0       10      10      310     1310    2310    7310    20      21      EVAAAA  JHKAAA  VVVVxx
+1175   6952    1       3       5       15      75      175     1175    1175    1175    150     151     FTAAAA  KHKAAA  AAAAxx
+9107   6953    1       3       7       7       7       107     1107    4107    9107    14      15      HMAAAA  LHKAAA  HHHHxx
+2737   6954    1       1       7       17      37      737     737     2737    2737    74      75      HBAAAA  MHKAAA  OOOOxx
+3437   6955    1       1       7       17      37      437     1437    3437    3437    74      75      FCAAAA  NHKAAA  VVVVxx
+281    6956    1       1       1       1       81      281     281     281     281     162     163     VKAAAA  OHKAAA  AAAAxx
+6676   6957    0       0       6       16      76      676     676     1676    6676    152     153     UWAAAA  PHKAAA  HHHHxx
+145    6958    1       1       5       5       45      145     145     145     145     90      91      PFAAAA  QHKAAA  OOOOxx
+3172   6959    0       0       2       12      72      172     1172    3172    3172    144     145     ASAAAA  RHKAAA  VVVVxx
+4049   6960    1       1       9       9       49      49      49      4049    4049    98      99      TZAAAA  SHKAAA  AAAAxx
+6042   6961    0       2       2       2       42      42      42      1042    6042    84      85      KYAAAA  THKAAA  HHHHxx
+9122   6962    0       2       2       2       22      122     1122    4122    9122    44      45      WMAAAA  UHKAAA  OOOOxx
+7244   6963    0       0       4       4       44      244     1244    2244    7244    88      89      QSAAAA  VHKAAA  VVVVxx
+5361   6964    1       1       1       1       61      361     1361    361     5361    122     123     FYAAAA  WHKAAA  AAAAxx
+8647   6965    1       3       7       7       47      647     647     3647    8647    94      95      PUAAAA  XHKAAA  HHHHxx
+7956   6966    0       0       6       16      56      956     1956    2956    7956    112     113     AUAAAA  YHKAAA  OOOOxx
+7812   6967    0       0       2       12      12      812     1812    2812    7812    24      25      MOAAAA  ZHKAAA  VVVVxx
+570    6968    0       2       0       10      70      570     570     570     570     140     141     YVAAAA  AIKAAA  AAAAxx
+4115   6969    1       3       5       15      15      115     115     4115    4115    30      31      HCAAAA  BIKAAA  HHHHxx
+1856   6970    0       0       6       16      56      856     1856    1856    1856    112     113     KTAAAA  CIKAAA  OOOOxx
+9582   6971    0       2       2       2       82      582     1582    4582    9582    164     165     OEAAAA  DIKAAA  VVVVxx
+2025   6972    1       1       5       5       25      25      25      2025    2025    50      51      XZAAAA  EIKAAA  AAAAxx
+986    6973    0       2       6       6       86      986     986     986     986     172     173     YLAAAA  FIKAAA  HHHHxx
+8358   6974    0       2       8       18      58      358     358     3358    8358    116     117     MJAAAA  GIKAAA  OOOOxx
+510    6975    0       2       0       10      10      510     510     510     510     20      21      QTAAAA  HIKAAA  VVVVxx
+6101   6976    1       1       1       1       1       101     101     1101    6101    2       3       RAAAAA  IIKAAA  AAAAxx
+4167   6977    1       3       7       7       67      167     167     4167    4167    134     135     HEAAAA  JIKAAA  HHHHxx
+6139   6978    1       3       9       19      39      139     139     1139    6139    78      79      DCAAAA  KIKAAA  OOOOxx
+6912   6979    0       0       2       12      12      912     912     1912    6912    24      25      WFAAAA  LIKAAA  VVVVxx
+339    6980    1       3       9       19      39      339     339     339     339     78      79      BNAAAA  MIKAAA  AAAAxx
+8759   6981    1       3       9       19      59      759     759     3759    8759    118     119     XYAAAA  NIKAAA  HHHHxx
+246    6982    0       2       6       6       46      246     246     246     246     92      93      MJAAAA  OIKAAA  OOOOxx
+2831   6983    1       3       1       11      31      831     831     2831    2831    62      63      XEAAAA  PIKAAA  VVVVxx
+2327   6984    1       3       7       7       27      327     327     2327    2327    54      55      NLAAAA  QIKAAA  AAAAxx
+7001   6985    1       1       1       1       1       1       1001    2001    7001    2       3       HJAAAA  RIKAAA  HHHHxx
+4398   6986    0       2       8       18      98      398     398     4398    4398    196     197     ENAAAA  SIKAAA  OOOOxx
+1495   6987    1       3       5       15      95      495     1495    1495    1495    190     191     NFAAAA  TIKAAA  VVVVxx
+8522   6988    0       2       2       2       22      522     522     3522    8522    44      45      UPAAAA  UIKAAA  AAAAxx
+7090   6989    0       2       0       10      90      90      1090    2090    7090    180     181     SMAAAA  VIKAAA  HHHHxx
+8457   6990    1       1       7       17      57      457     457     3457    8457    114     115     HNAAAA  WIKAAA  OOOOxx
+4238   6991    0       2       8       18      38      238     238     4238    4238    76      77      AHAAAA  XIKAAA  VVVVxx
+6791   6992    1       3       1       11      91      791     791     1791    6791    182     183     FBAAAA  YIKAAA  AAAAxx
+1342   6993    0       2       2       2       42      342     1342    1342    1342    84      85      QZAAAA  ZIKAAA  HHHHxx
+4580   6994    0       0       0       0       80      580     580     4580    4580    160     161     EUAAAA  AJKAAA  OOOOxx
+1475   6995    1       3       5       15      75      475     1475    1475    1475    150     151     TEAAAA  BJKAAA  VVVVxx
+9184   6996    0       0       4       4       84      184     1184    4184    9184    168     169     GPAAAA  CJKAAA  AAAAxx
+1189   6997    1       1       9       9       89      189     1189    1189    1189    178     179     TTAAAA  DJKAAA  HHHHxx
+638    6998    0       2       8       18      38      638     638     638     638     76      77      OYAAAA  EJKAAA  OOOOxx
+5867   6999    1       3       7       7       67      867     1867    867     5867    134     135     RRAAAA  FJKAAA  VVVVxx
+9911   7000    1       3       1       11      11      911     1911    4911    9911    22      23      FRAAAA  GJKAAA  AAAAxx
+8147   7001    1       3       7       7       47      147     147     3147    8147    94      95      JBAAAA  HJKAAA  HHHHxx
+4492   7002    0       0       2       12      92      492     492     4492    4492    184     185     UQAAAA  IJKAAA  OOOOxx
+385    7003    1       1       5       5       85      385     385     385     385     170     171     VOAAAA  JJKAAA  VVVVxx
+5235   7004    1       3       5       15      35      235     1235    235     5235    70      71      JTAAAA  KJKAAA  AAAAxx
+4812   7005    0       0       2       12      12      812     812     4812    4812    24      25      CDAAAA  LJKAAA  HHHHxx
+9807   7006    1       3       7       7       7       807     1807    4807    9807    14      15      FNAAAA  MJKAAA  OOOOxx
+9588   7007    0       0       8       8       88      588     1588    4588    9588    176     177     UEAAAA  NJKAAA  VVVVxx
+9832   7008    0       0       2       12      32      832     1832    4832    9832    64      65      EOAAAA  OJKAAA  AAAAxx
+3757   7009    1       1       7       17      57      757     1757    3757    3757    114     115     NOAAAA  PJKAAA  HHHHxx
+9703   7010    1       3       3       3       3       703     1703    4703    9703    6       7       FJAAAA  QJKAAA  OOOOxx
+1022   7011    0       2       2       2       22      22      1022    1022    1022    44      45      INAAAA  RJKAAA  VVVVxx
+5165   7012    1       1       5       5       65      165     1165    165     5165    130     131     RQAAAA  SJKAAA  AAAAxx
+7129   7013    1       1       9       9       29      129     1129    2129    7129    58      59      FOAAAA  TJKAAA  HHHHxx
+4164   7014    0       0       4       4       64      164     164     4164    4164    128     129     EEAAAA  UJKAAA  OOOOxx
+7239   7015    1       3       9       19      39      239     1239    2239    7239    78      79      LSAAAA  VJKAAA  VVVVxx
+523    7016    1       3       3       3       23      523     523     523     523     46      47      DUAAAA  WJKAAA  AAAAxx
+4670   7017    0       2       0       10      70      670     670     4670    4670    140     141     QXAAAA  XJKAAA  HHHHxx
+8503   7018    1       3       3       3       3       503     503     3503    8503    6       7       BPAAAA  YJKAAA  OOOOxx
+714    7019    0       2       4       14      14      714     714     714     714     28      29      MBAAAA  ZJKAAA  VVVVxx
+1350   7020    0       2       0       10      50      350     1350    1350    1350    100     101     YZAAAA  AKKAAA  AAAAxx
+8318   7021    0       2       8       18      18      318     318     3318    8318    36      37      YHAAAA  BKKAAA  HHHHxx
+1834   7022    0       2       4       14      34      834     1834    1834    1834    68      69      OSAAAA  CKKAAA  OOOOxx
+4306   7023    0       2       6       6       6       306     306     4306    4306    12      13      QJAAAA  DKKAAA  VVVVxx
+8543   7024    1       3       3       3       43      543     543     3543    8543    86      87      PQAAAA  EKKAAA  AAAAxx
+9397   7025    1       1       7       17      97      397     1397    4397    9397    194     195     LXAAAA  FKKAAA  HHHHxx
+3145   7026    1       1       5       5       45      145     1145    3145    3145    90      91      ZQAAAA  GKKAAA  OOOOxx
+3942   7027    0       2       2       2       42      942     1942    3942    3942    84      85      QVAAAA  HKKAAA  VVVVxx
+8583   7028    1       3       3       3       83      583     583     3583    8583    166     167     DSAAAA  IKKAAA  AAAAxx
+8073   7029    1       1       3       13      73      73      73      3073    8073    146     147     NYAAAA  JKKAAA  HHHHxx
+4940   7030    0       0       0       0       40      940     940     4940    4940    80      81      AIAAAA  KKKAAA  OOOOxx
+9573   7031    1       1       3       13      73      573     1573    4573    9573    146     147     FEAAAA  LKKAAA  VVVVxx
+5325   7032    1       1       5       5       25      325     1325    325     5325    50      51      VWAAAA  MKKAAA  AAAAxx
+1833   7033    1       1       3       13      33      833     1833    1833    1833    66      67      NSAAAA  NKKAAA  HHHHxx
+1337   7034    1       1       7       17      37      337     1337    1337    1337    74      75      LZAAAA  OKKAAA  OOOOxx
+9749   7035    1       1       9       9       49      749     1749    4749    9749    98      99      ZKAAAA  PKKAAA  VVVVxx
+7505   7036    1       1       5       5       5       505     1505    2505    7505    10      11      RCAAAA  QKKAAA  AAAAxx
+9731   7037    1       3       1       11      31      731     1731    4731    9731    62      63      HKAAAA  RKKAAA  HHHHxx
+4098   7038    0       2       8       18      98      98      98      4098    4098    196     197     QBAAAA  SKKAAA  OOOOxx
+1418   7039    0       2       8       18      18      418     1418    1418    1418    36      37      OCAAAA  TKKAAA  VVVVxx
+63     7040    1       3       3       3       63      63      63      63      63      126     127     LCAAAA  UKKAAA  AAAAxx
+9889   7041    1       1       9       9       89      889     1889    4889    9889    178     179     JQAAAA  VKKAAA  HHHHxx
+2871   7042    1       3       1       11      71      871     871     2871    2871    142     143     LGAAAA  WKKAAA  OOOOxx
+1003   7043    1       3       3       3       3       3       1003    1003    1003    6       7       PMAAAA  XKKAAA  VVVVxx
+8796   7044    0       0       6       16      96      796     796     3796    8796    192     193     IAAAAA  YKKAAA  AAAAxx
+22     7045    0       2       2       2       22      22      22      22      22      44      45      WAAAAA  ZKKAAA  HHHHxx
+8244   7046    0       0       4       4       44      244     244     3244    8244    88      89      CFAAAA  ALKAAA  OOOOxx
+2282   7047    0       2       2       2       82      282     282     2282    2282    164     165     UJAAAA  BLKAAA  VVVVxx
+3487   7048    1       3       7       7       87      487     1487    3487    3487    174     175     DEAAAA  CLKAAA  AAAAxx
+8633   7049    1       1       3       13      33      633     633     3633    8633    66      67      BUAAAA  DLKAAA  HHHHxx
+6418   7050    0       2       8       18      18      418     418     1418    6418    36      37      WMAAAA  ELKAAA  OOOOxx
+4682   7051    0       2       2       2       82      682     682     4682    4682    164     165     CYAAAA  FLKAAA  VVVVxx
+4103   7052    1       3       3       3       3       103     103     4103    4103    6       7       VBAAAA  GLKAAA  AAAAxx
+6256   7053    0       0       6       16      56      256     256     1256    6256    112     113     QGAAAA  HLKAAA  HHHHxx
+4040   7054    0       0       0       0       40      40      40      4040    4040    80      81      KZAAAA  ILKAAA  OOOOxx
+9342   7055    0       2       2       2       42      342     1342    4342    9342    84      85      IVAAAA  JLKAAA  VVVVxx
+9969   7056    1       1       9       9       69      969     1969    4969    9969    138     139     LTAAAA  KLKAAA  AAAAxx
+223    7057    1       3       3       3       23      223     223     223     223     46      47      PIAAAA  LLKAAA  HHHHxx
+4593   7058    1       1       3       13      93      593     593     4593    4593    186     187     RUAAAA  MLKAAA  OOOOxx
+44     7059    0       0       4       4       44      44      44      44      44      88      89      SBAAAA  NLKAAA  VVVVxx
+3513   7060    1       1       3       13      13      513     1513    3513    3513    26      27      DFAAAA  OLKAAA  AAAAxx
+5771   7061    1       3       1       11      71      771     1771    771     5771    142     143     ZNAAAA  PLKAAA  HHHHxx
+5083   7062    1       3       3       3       83      83      1083    83      5083    166     167     NNAAAA  QLKAAA  OOOOxx
+3839   7063    1       3       9       19      39      839     1839    3839    3839    78      79      RRAAAA  RLKAAA  VVVVxx
+2986   7064    0       2       6       6       86      986     986     2986    2986    172     173     WKAAAA  SLKAAA  AAAAxx
+2200   7065    0       0       0       0       0       200     200     2200    2200    0       1       QGAAAA  TLKAAA  HHHHxx
+197    7066    1       1       7       17      97      197     197     197     197     194     195     PHAAAA  ULKAAA  OOOOxx
+7455   7067    1       3       5       15      55      455     1455    2455    7455    110     111     TAAAAA  VLKAAA  VVVVxx
+1379   7068    1       3       9       19      79      379     1379    1379    1379    158     159     BBAAAA  WLKAAA  AAAAxx
+4356   7069    0       0       6       16      56      356     356     4356    4356    112     113     OLAAAA  XLKAAA  HHHHxx
+6888   7070    0       0       8       8       88      888     888     1888    6888    176     177     YEAAAA  YLKAAA  OOOOxx
+9139   7071    1       3       9       19      39      139     1139    4139    9139    78      79      NNAAAA  ZLKAAA  VVVVxx
+7682   7072    0       2       2       2       82      682     1682    2682    7682    164     165     MJAAAA  AMKAAA  AAAAxx
+4873   7073    1       1       3       13      73      873     873     4873    4873    146     147     LFAAAA  BMKAAA  HHHHxx
+783    7074    1       3       3       3       83      783     783     783     783     166     167     DEAAAA  CMKAAA  OOOOxx
+6071   7075    1       3       1       11      71      71      71      1071    6071    142     143     NZAAAA  DMKAAA  VVVVxx
+5160   7076    0       0       0       0       60      160     1160    160     5160    120     121     MQAAAA  EMKAAA  AAAAxx
+2291   7077    1       3       1       11      91      291     291     2291    2291    182     183     DKAAAA  FMKAAA  HHHHxx
+187    7078    1       3       7       7       87      187     187     187     187     174     175     FHAAAA  GMKAAA  OOOOxx
+7786   7079    0       2       6       6       86      786     1786    2786    7786    172     173     MNAAAA  HMKAAA  VVVVxx
+3432   7080    0       0       2       12      32      432     1432    3432    3432    64      65      ACAAAA  IMKAAA  AAAAxx
+5450   7081    0       2       0       10      50      450     1450    450     5450    100     101     QBAAAA  JMKAAA  HHHHxx
+2699   7082    1       3       9       19      99      699     699     2699    2699    198     199     VZAAAA  KMKAAA  OOOOxx
+692    7083    0       0       2       12      92      692     692     692     692     184     185     QAAAAA  LMKAAA  VVVVxx
+6081   7084    1       1       1       1       81      81      81      1081    6081    162     163     XZAAAA  MMKAAA  AAAAxx
+4829   7085    1       1       9       9       29      829     829     4829    4829    58      59      TDAAAA  NMKAAA  HHHHxx
+238    7086    0       2       8       18      38      238     238     238     238     76      77      EJAAAA  OMKAAA  OOOOxx
+9100   7087    0       0       0       0       0       100     1100    4100    9100    0       1       AMAAAA  PMKAAA  VVVVxx
+1968   7088    0       0       8       8       68      968     1968    1968    1968    136     137     SXAAAA  QMKAAA  AAAAxx
+1872   7089    0       0       2       12      72      872     1872    1872    1872    144     145     AUAAAA  RMKAAA  HHHHxx
+7051   7090    1       3       1       11      51      51      1051    2051    7051    102     103     FLAAAA  SMKAAA  OOOOxx
+2743   7091    1       3       3       3       43      743     743     2743    2743    86      87      NBAAAA  TMKAAA  VVVVxx
+1237   7092    1       1       7       17      37      237     1237    1237    1237    74      75      PVAAAA  UMKAAA  AAAAxx
+3052   7093    0       0       2       12      52      52      1052    3052    3052    104     105     KNAAAA  VMKAAA  HHHHxx
+8021   7094    1       1       1       1       21      21      21      3021    8021    42      43      NWAAAA  WMKAAA  OOOOxx
+657    7095    1       1       7       17      57      657     657     657     657     114     115     HZAAAA  XMKAAA  VVVVxx
+2236   7096    0       0       6       16      36      236     236     2236    2236    72      73      AIAAAA  YMKAAA  AAAAxx
+7011   7097    1       3       1       11      11      11      1011    2011    7011    22      23      RJAAAA  ZMKAAA  HHHHxx
+4067   7098    1       3       7       7       67      67      67      4067    4067    134     135     LAAAAA  ANKAAA  OOOOxx
+9449   7099    1       1       9       9       49      449     1449    4449    9449    98      99      LZAAAA  BNKAAA  VVVVxx
+7428   7100    0       0       8       8       28      428     1428    2428    7428    56      57      SZAAAA  CNKAAA  AAAAxx
+1272   7101    0       0       2       12      72      272     1272    1272    1272    144     145     YWAAAA  DNKAAA  HHHHxx
+6897   7102    1       1       7       17      97      897     897     1897    6897    194     195     HFAAAA  ENKAAA  OOOOxx
+5839   7103    1       3       9       19      39      839     1839    839     5839    78      79      PQAAAA  FNKAAA  VVVVxx
+6835   7104    1       3       5       15      35      835     835     1835    6835    70      71      XCAAAA  GNKAAA  AAAAxx
+1887   7105    1       3       7       7       87      887     1887    1887    1887    174     175     PUAAAA  HNKAAA  HHHHxx
+1551   7106    1       3       1       11      51      551     1551    1551    1551    102     103     RHAAAA  INKAAA  OOOOxx
+4667   7107    1       3       7       7       67      667     667     4667    4667    134     135     NXAAAA  JNKAAA  VVVVxx
+9603   7108    1       3       3       3       3       603     1603    4603    9603    6       7       JFAAAA  KNKAAA  AAAAxx
+4332   7109    0       0       2       12      32      332     332     4332    4332    64      65      QKAAAA  LNKAAA  HHHHxx
+5681   7110    1       1       1       1       81      681     1681    681     5681    162     163     NKAAAA  MNKAAA  OOOOxx
+8062   7111    0       2       2       2       62      62      62      3062    8062    124     125     CYAAAA  NNKAAA  VVVVxx
+2302   7112    0       2       2       2       2       302     302     2302    2302    4       5       OKAAAA  ONKAAA  AAAAxx
+2825   7113    1       1       5       5       25      825     825     2825    2825    50      51      REAAAA  PNKAAA  HHHHxx
+4527   7114    1       3       7       7       27      527     527     4527    4527    54      55      DSAAAA  QNKAAA  OOOOxx
+4230   7115    0       2       0       10      30      230     230     4230    4230    60      61      SGAAAA  RNKAAA  VVVVxx
+3053   7116    1       1       3       13      53      53      1053    3053    3053    106     107     LNAAAA  SNKAAA  AAAAxx
+983    7117    1       3       3       3       83      983     983     983     983     166     167     VLAAAA  TNKAAA  HHHHxx
+9458   7118    0       2       8       18      58      458     1458    4458    9458    116     117     UZAAAA  UNKAAA  OOOOxx
+4128   7119    0       0       8       8       28      128     128     4128    4128    56      57      UCAAAA  VNKAAA  VVVVxx
+425    7120    1       1       5       5       25      425     425     425     425     50      51      JQAAAA  WNKAAA  AAAAxx
+3911   7121    1       3       1       11      11      911     1911    3911    3911    22      23      LUAAAA  XNKAAA  HHHHxx
+6607   7122    1       3       7       7       7       607     607     1607    6607    14      15      DUAAAA  YNKAAA  OOOOxx
+5431   7123    1       3       1       11      31      431     1431    431     5431    62      63      XAAAAA  ZNKAAA  VVVVxx
+6330   7124    0       2       0       10      30      330     330     1330    6330    60      61      MJAAAA  AOKAAA  AAAAxx
+3592   7125    0       0       2       12      92      592     1592    3592    3592    184     185     EIAAAA  BOKAAA  HHHHxx
+154    7126    0       2       4       14      54      154     154     154     154     108     109     YFAAAA  COKAAA  OOOOxx
+9879   7127    1       3       9       19      79      879     1879    4879    9879    158     159     ZPAAAA  DOKAAA  VVVVxx
+3202   7128    0       2       2       2       2       202     1202    3202    3202    4       5       ETAAAA  EOKAAA  AAAAxx
+3056   7129    0       0       6       16      56      56      1056    3056    3056    112     113     ONAAAA  FOKAAA  HHHHxx
+9890   7130    0       2       0       10      90      890     1890    4890    9890    180     181     KQAAAA  GOKAAA  OOOOxx
+5840   7131    0       0       0       0       40      840     1840    840     5840    80      81      QQAAAA  HOKAAA  VVVVxx
+9804   7132    0       0       4       4       4       804     1804    4804    9804    8       9       CNAAAA  IOKAAA  AAAAxx
+681    7133    1       1       1       1       81      681     681     681     681     162     163     FAAAAA  JOKAAA  HHHHxx
+3443   7134    1       3       3       3       43      443     1443    3443    3443    86      87      LCAAAA  KOKAAA  OOOOxx
+8088   7135    0       0       8       8       88      88      88      3088    8088    176     177     CZAAAA  LOKAAA  VVVVxx
+9447   7136    1       3       7       7       47      447     1447    4447    9447    94      95      JZAAAA  MOKAAA  AAAAxx
+1490   7137    0       2       0       10      90      490     1490    1490    1490    180     181     IFAAAA  NOKAAA  HHHHxx
+3684   7138    0       0       4       4       84      684     1684    3684    3684    168     169     SLAAAA  OOKAAA  OOOOxx
+3113   7139    1       1       3       13      13      113     1113    3113    3113    26      27      TPAAAA  POKAAA  VVVVxx
+9004   7140    0       0       4       4       4       4       1004    4004    9004    8       9       IIAAAA  QOKAAA  AAAAxx
+7147   7141    1       3       7       7       47      147     1147    2147    7147    94      95      XOAAAA  ROKAAA  HHHHxx
+7571   7142    1       3       1       11      71      571     1571    2571    7571    142     143     FFAAAA  SOKAAA  OOOOxx
+5545   7143    1       1       5       5       45      545     1545    545     5545    90      91      HFAAAA  TOKAAA  VVVVxx
+4558   7144    0       2       8       18      58      558     558     4558    4558    116     117     ITAAAA  UOKAAA  AAAAxx
+6206   7145    0       2       6       6       6       206     206     1206    6206    12      13      SEAAAA  VOKAAA  HHHHxx
+5695   7146    1       3       5       15      95      695     1695    695     5695    190     191     BLAAAA  WOKAAA  OOOOxx
+9600   7147    0       0       0       0       0       600     1600    4600    9600    0       1       GFAAAA  XOKAAA  VVVVxx
+5432   7148    0       0       2       12      32      432     1432    432     5432    64      65      YAAAAA  YOKAAA  AAAAxx
+9299   7149    1       3       9       19      99      299     1299    4299    9299    198     199     RTAAAA  ZOKAAA  HHHHxx
+2386   7150    0       2       6       6       86      386     386     2386    2386    172     173     UNAAAA  APKAAA  OOOOxx
+2046   7151    0       2       6       6       46      46      46      2046    2046    92      93      SAAAAA  BPKAAA  VVVVxx
+3293   7152    1       1       3       13      93      293     1293    3293    3293    186     187     RWAAAA  CPKAAA  AAAAxx
+3046   7153    0       2       6       6       46      46      1046    3046    3046    92      93      ENAAAA  DPKAAA  HHHHxx
+214    7154    0       2       4       14      14      214     214     214     214     28      29      GIAAAA  EPKAAA  OOOOxx
+7893   7155    1       1       3       13      93      893     1893    2893    7893    186     187     PRAAAA  FPKAAA  VVVVxx
+891    7156    1       3       1       11      91      891     891     891     891     182     183     HIAAAA  GPKAAA  AAAAxx
+6499   7157    1       3       9       19      99      499     499     1499    6499    198     199     ZPAAAA  HPKAAA  HHHHxx
+5003   7158    1       3       3       3       3       3       1003    3       5003    6       7       LKAAAA  IPKAAA  OOOOxx
+6487   7159    1       3       7       7       87      487     487     1487    6487    174     175     NPAAAA  JPKAAA  VVVVxx
+9403   7160    1       3       3       3       3       403     1403    4403    9403    6       7       RXAAAA  KPKAAA  AAAAxx
+945    7161    1       1       5       5       45      945     945     945     945     90      91      JKAAAA  LPKAAA  HHHHxx
+6713   7162    1       1       3       13      13      713     713     1713    6713    26      27      FYAAAA  MPKAAA  OOOOxx
+9928   7163    0       0       8       8       28      928     1928    4928    9928    56      57      WRAAAA  NPKAAA  VVVVxx
+8585   7164    1       1       5       5       85      585     585     3585    8585    170     171     FSAAAA  OPKAAA  AAAAxx
+4004   7165    0       0       4       4       4       4       4       4004    4004    8       9       AYAAAA  PPKAAA  HHHHxx
+2528   7166    0       0       8       8       28      528     528     2528    2528    56      57      GTAAAA  QPKAAA  OOOOxx
+3350   7167    0       2       0       10      50      350     1350    3350    3350    100     101     WYAAAA  RPKAAA  VVVVxx
+2160   7168    0       0       0       0       60      160     160     2160    2160    120     121     CFAAAA  SPKAAA  AAAAxx
+1521   7169    1       1       1       1       21      521     1521    1521    1521    42      43      NGAAAA  TPKAAA  HHHHxx
+5660   7170    0       0       0       0       60      660     1660    660     5660    120     121     SJAAAA  UPKAAA  OOOOxx
+5755   7171    1       3       5       15      55      755     1755    755     5755    110     111     JNAAAA  VPKAAA  VVVVxx
+7614   7172    0       2       4       14      14      614     1614    2614    7614    28      29      WGAAAA  WPKAAA  AAAAxx
+3121   7173    1       1       1       1       21      121     1121    3121    3121    42      43      BQAAAA  XPKAAA  HHHHxx
+2735   7174    1       3       5       15      35      735     735     2735    2735    70      71      FBAAAA  YPKAAA  OOOOxx
+7506   7175    0       2       6       6       6       506     1506    2506    7506    12      13      SCAAAA  ZPKAAA  VVVVxx
+2693   7176    1       1       3       13      93      693     693     2693    2693    186     187     PZAAAA  AQKAAA  AAAAxx
+2892   7177    0       0       2       12      92      892     892     2892    2892    184     185     GHAAAA  BQKAAA  HHHHxx
+3310   7178    0       2       0       10      10      310     1310    3310    3310    20      21      IXAAAA  CQKAAA  OOOOxx
+3484   7179    0       0       4       4       84      484     1484    3484    3484    168     169     AEAAAA  DQKAAA  VVVVxx
+9733   7180    1       1       3       13      33      733     1733    4733    9733    66      67      JKAAAA  EQKAAA  AAAAxx
+29     7181    1       1       9       9       29      29      29      29      29      58      59      DBAAAA  FQKAAA  HHHHxx
+9013   7182    1       1       3       13      13      13      1013    4013    9013    26      27      RIAAAA  GQKAAA  OOOOxx
+3847   7183    1       3       7       7       47      847     1847    3847    3847    94      95      ZRAAAA  HQKAAA  VVVVxx
+6724   7184    0       0       4       4       24      724     724     1724    6724    48      49      QYAAAA  IQKAAA  AAAAxx
+2559   7185    1       3       9       19      59      559     559     2559    2559    118     119     LUAAAA  JQKAAA  HHHHxx
+5326   7186    0       2       6       6       26      326     1326    326     5326    52      53      WWAAAA  KQKAAA  OOOOxx
+4802   7187    0       2       2       2       2       802     802     4802    4802    4       5       SCAAAA  LQKAAA  VVVVxx
+131    7188    1       3       1       11      31      131     131     131     131     62      63      BFAAAA  MQKAAA  AAAAxx
+1634   7189    0       2       4       14      34      634     1634    1634    1634    68      69      WKAAAA  NQKAAA  HHHHxx
+919    7190    1       3       9       19      19      919     919     919     919     38      39      JJAAAA  OQKAAA  OOOOxx
+9575   7191    1       3       5       15      75      575     1575    4575    9575    150     151     HEAAAA  PQKAAA  VVVVxx
+1256   7192    0       0       6       16      56      256     1256    1256    1256    112     113     IWAAAA  QQKAAA  AAAAxx
+9428   7193    0       0       8       8       28      428     1428    4428    9428    56      57      QYAAAA  RQKAAA  HHHHxx
+5121   7194    1       1       1       1       21      121     1121    121     5121    42      43      ZOAAAA  SQKAAA  OOOOxx
+6584   7195    0       0       4       4       84      584     584     1584    6584    168     169     GTAAAA  TQKAAA  VVVVxx
+7193   7196    1       1       3       13      93      193     1193    2193    7193    186     187     RQAAAA  UQKAAA  AAAAxx
+4047   7197    1       3       7       7       47      47      47      4047    4047    94      95      RZAAAA  VQKAAA  HHHHxx
+104    7198    0       0       4       4       4       104     104     104     104     8       9       AEAAAA  WQKAAA  OOOOxx
+1527   7199    1       3       7       7       27      527     1527    1527    1527    54      55      TGAAAA  XQKAAA  VVVVxx
+3460   7200    0       0       0       0       60      460     1460    3460    3460    120     121     CDAAAA  YQKAAA  AAAAxx
+8526   7201    0       2       6       6       26      526     526     3526    8526    52      53      YPAAAA  ZQKAAA  HHHHxx
+8959   7202    1       3       9       19      59      959     959     3959    8959    118     119     PGAAAA  ARKAAA  OOOOxx
+3633   7203    1       1       3       13      33      633     1633    3633    3633    66      67      TJAAAA  BRKAAA  VVVVxx
+1799   7204    1       3       9       19      99      799     1799    1799    1799    198     199     FRAAAA  CRKAAA  AAAAxx
+461    7205    1       1       1       1       61      461     461     461     461     122     123     TRAAAA  DRKAAA  HHHHxx
+718    7206    0       2       8       18      18      718     718     718     718     36      37      QBAAAA  ERKAAA  OOOOxx
+3219   7207    1       3       9       19      19      219     1219    3219    3219    38      39      VTAAAA  FRKAAA  VVVVxx
+3494   7208    0       2       4       14      94      494     1494    3494    3494    188     189     KEAAAA  GRKAAA  AAAAxx
+9402   7209    0       2       2       2       2       402     1402    4402    9402    4       5       QXAAAA  HRKAAA  HHHHxx
+7983   7210    1       3       3       3       83      983     1983    2983    7983    166     167     BVAAAA  IRKAAA  OOOOxx
+7919   7211    1       3       9       19      19      919     1919    2919    7919    38      39      PSAAAA  JRKAAA  VVVVxx
+8036   7212    0       0       6       16      36      36      36      3036    8036    72      73      CXAAAA  KRKAAA  AAAAxx
+5164   7213    0       0       4       4       64      164     1164    164     5164    128     129     QQAAAA  LRKAAA  HHHHxx
+4160   7214    0       0       0       0       60      160     160     4160    4160    120     121     AEAAAA  MRKAAA  OOOOxx
+5370   7215    0       2       0       10      70      370     1370    370     5370    140     141     OYAAAA  NRKAAA  VVVVxx
+5347   7216    1       3       7       7       47      347     1347    347     5347    94      95      RXAAAA  ORKAAA  AAAAxx
+7109   7217    1       1       9       9       9       109     1109    2109    7109    18      19      LNAAAA  PRKAAA  HHHHxx
+4826   7218    0       2       6       6       26      826     826     4826    4826    52      53      QDAAAA  QRKAAA  OOOOxx
+1338   7219    0       2       8       18      38      338     1338    1338    1338    76      77      MZAAAA  RRKAAA  VVVVxx
+2711   7220    1       3       1       11      11      711     711     2711    2711    22      23      HAAAAA  SRKAAA  AAAAxx
+6299   7221    1       3       9       19      99      299     299     1299    6299    198     199     HIAAAA  TRKAAA  HHHHxx
+1616   7222    0       0       6       16      16      616     1616    1616    1616    32      33      EKAAAA  URKAAA  OOOOxx
+7519   7223    1       3       9       19      19      519     1519    2519    7519    38      39      FDAAAA  VRKAAA  VVVVxx
+1262   7224    0       2       2       2       62      262     1262    1262    1262    124     125     OWAAAA  WRKAAA  AAAAxx
+7228   7225    0       0       8       8       28      228     1228    2228    7228    56      57      ASAAAA  XRKAAA  HHHHxx
+7892   7226    0       0       2       12      92      892     1892    2892    7892    184     185     ORAAAA  YRKAAA  OOOOxx
+7929   7227    1       1       9       9       29      929     1929    2929    7929    58      59      ZSAAAA  ZRKAAA  VVVVxx
+7705   7228    1       1       5       5       5       705     1705    2705    7705    10      11      JKAAAA  ASKAAA  AAAAxx
+3111   7229    1       3       1       11      11      111     1111    3111    3111    22      23      RPAAAA  BSKAAA  HHHHxx
+3066   7230    0       2       6       6       66      66      1066    3066    3066    132     133     YNAAAA  CSKAAA  OOOOxx
+9559   7231    1       3       9       19      59      559     1559    4559    9559    118     119     RDAAAA  DSKAAA  VVVVxx
+3787   7232    1       3       7       7       87      787     1787    3787    3787    174     175     RPAAAA  ESKAAA  AAAAxx
+8710   7233    0       2       0       10      10      710     710     3710    8710    20      21      AXAAAA  FSKAAA  HHHHxx
+4870   7234    0       2       0       10      70      870     870     4870    4870    140     141     IFAAAA  GSKAAA  OOOOxx
+1883   7235    1       3       3       3       83      883     1883    1883    1883    166     167     LUAAAA  HSKAAA  VVVVxx
+9689   7236    1       1       9       9       89      689     1689    4689    9689    178     179     RIAAAA  ISKAAA  AAAAxx
+9491   7237    1       3       1       11      91      491     1491    4491    9491    182     183     BBAAAA  JSKAAA  HHHHxx
+2035   7238    1       3       5       15      35      35      35      2035    2035    70      71      HAAAAA  KSKAAA  OOOOxx
+655    7239    1       3       5       15      55      655     655     655     655     110     111     FZAAAA  LSKAAA  VVVVxx
+6305   7240    1       1       5       5       5       305     305     1305    6305    10      11      NIAAAA  MSKAAA  AAAAxx
+9423   7241    1       3       3       3       23      423     1423    4423    9423    46      47      LYAAAA  NSKAAA  HHHHxx
+283    7242    1       3       3       3       83      283     283     283     283     166     167     XKAAAA  OSKAAA  OOOOxx
+2607   7243    1       3       7       7       7       607     607     2607    2607    14      15      HWAAAA  PSKAAA  VVVVxx
+7740   7244    0       0       0       0       40      740     1740    2740    7740    80      81      SLAAAA  QSKAAA  AAAAxx
+6956   7245    0       0       6       16      56      956     956     1956    6956    112     113     OHAAAA  RSKAAA  HHHHxx
+884    7246    0       0       4       4       84      884     884     884     884     168     169     AIAAAA  SSKAAA  OOOOxx
+5730   7247    0       2       0       10      30      730     1730    730     5730    60      61      KMAAAA  TSKAAA  VVVVxx
+3438   7248    0       2       8       18      38      438     1438    3438    3438    76      77      GCAAAA  USKAAA  AAAAxx
+3250   7249    0       2       0       10      50      250     1250    3250    3250    100     101     AVAAAA  VSKAAA  HHHHxx
+5470   7250    0       2       0       10      70      470     1470    470     5470    140     141     KCAAAA  WSKAAA  OOOOxx
+2037   7251    1       1       7       17      37      37      37      2037    2037    74      75      JAAAAA  XSKAAA  VVVVxx
+6593   7252    1       1       3       13      93      593     593     1593    6593    186     187     PTAAAA  YSKAAA  AAAAxx
+3893   7253    1       1       3       13      93      893     1893    3893    3893    186     187     TTAAAA  ZSKAAA  HHHHxx
+3200   7254    0       0       0       0       0       200     1200    3200    3200    0       1       CTAAAA  ATKAAA  OOOOxx
+7125   7255    1       1       5       5       25      125     1125    2125    7125    50      51      BOAAAA  BTKAAA  VVVVxx
+2295   7256    1       3       5       15      95      295     295     2295    2295    190     191     HKAAAA  CTKAAA  AAAAxx
+2056   7257    0       0       6       16      56      56      56      2056    2056    112     113     CBAAAA  DTKAAA  HHHHxx
+2962   7258    0       2       2       2       62      962     962     2962    2962    124     125     YJAAAA  ETKAAA  OOOOxx
+993    7259    1       1       3       13      93      993     993     993     993     186     187     FMAAAA  FTKAAA  VVVVxx
+9127   7260    1       3       7       7       27      127     1127    4127    9127    54      55      BNAAAA  GTKAAA  AAAAxx
+2075   7261    1       3       5       15      75      75      75      2075    2075    150     151     VBAAAA  HTKAAA  HHHHxx
+9338   7262    0       2       8       18      38      338     1338    4338    9338    76      77      EVAAAA  ITKAAA  OOOOxx
+8100   7263    0       0       0       0       0       100     100     3100    8100    0       1       OZAAAA  JTKAAA  VVVVxx
+5047   7264    1       3       7       7       47      47      1047    47      5047    94      95      DMAAAA  KTKAAA  AAAAxx
+7032   7265    0       0       2       12      32      32      1032    2032    7032    64      65      MKAAAA  LTKAAA  HHHHxx
+6374   7266    0       2       4       14      74      374     374     1374    6374    148     149     ELAAAA  MTKAAA  OOOOxx
+4137   7267    1       1       7       17      37      137     137     4137    4137    74      75      DDAAAA  NTKAAA  VVVVxx
+7132   7268    0       0       2       12      32      132     1132    2132    7132    64      65      IOAAAA  OTKAAA  AAAAxx
+3064   7269    0       0       4       4       64      64      1064    3064    3064    128     129     WNAAAA  PTKAAA  HHHHxx
+3621   7270    1       1       1       1       21      621     1621    3621    3621    42      43      HJAAAA  QTKAAA  OOOOxx
+6199   7271    1       3       9       19      99      199     199     1199    6199    198     199     LEAAAA  RTKAAA  VVVVxx
+4926   7272    0       2       6       6       26      926     926     4926    4926    52      53      MHAAAA  STKAAA  AAAAxx
+8035   7273    1       3       5       15      35      35      35      3035    8035    70      71      BXAAAA  TTKAAA  HHHHxx
+2195   7274    1       3       5       15      95      195     195     2195    2195    190     191     LGAAAA  UTKAAA  OOOOxx
+5366   7275    0       2       6       6       66      366     1366    366     5366    132     133     KYAAAA  VTKAAA  VVVVxx
+3478   7276    0       2       8       18      78      478     1478    3478    3478    156     157     UDAAAA  WTKAAA  AAAAxx
+1926   7277    0       2       6       6       26      926     1926    1926    1926    52      53      CWAAAA  XTKAAA  HHHHxx
+7265   7278    1       1       5       5       65      265     1265    2265    7265    130     131     LTAAAA  YTKAAA  OOOOxx
+7668   7279    0       0       8       8       68      668     1668    2668    7668    136     137     YIAAAA  ZTKAAA  VVVVxx
+3335   7280    1       3       5       15      35      335     1335    3335    3335    70      71      HYAAAA  AUKAAA  AAAAxx
+7660   7281    0       0       0       0       60      660     1660    2660    7660    120     121     QIAAAA  BUKAAA  HHHHxx
+9604   7282    0       0       4       4       4       604     1604    4604    9604    8       9       KFAAAA  CUKAAA  OOOOxx
+7301   7283    1       1       1       1       1       301     1301    2301    7301    2       3       VUAAAA  DUKAAA  VVVVxx
+4475   7284    1       3       5       15      75      475     475     4475    4475    150     151     DQAAAA  EUKAAA  AAAAxx
+9954   7285    0       2       4       14      54      954     1954    4954    9954    108     109     WSAAAA  FUKAAA  HHHHxx
+5723   7286    1       3       3       3       23      723     1723    723     5723    46      47      DMAAAA  GUKAAA  OOOOxx
+2669   7287    1       1       9       9       69      669     669     2669    2669    138     139     RYAAAA  HUKAAA  VVVVxx
+1685   7288    1       1       5       5       85      685     1685    1685    1685    170     171     VMAAAA  IUKAAA  AAAAxx
+2233   7289    1       1       3       13      33      233     233     2233    2233    66      67      XHAAAA  JUKAAA  HHHHxx
+8111   7290    1       3       1       11      11      111     111     3111    8111    22      23      ZZAAAA  KUKAAA  OOOOxx
+7685   7291    1       1       5       5       85      685     1685    2685    7685    170     171     PJAAAA  LUKAAA  VVVVxx
+3773   7292    1       1       3       13      73      773     1773    3773    3773    146     147     DPAAAA  MUKAAA  AAAAxx
+7172   7293    0       0       2       12      72      172     1172    2172    7172    144     145     WPAAAA  NUKAAA  HHHHxx
+1740   7294    0       0       0       0       40      740     1740    1740    1740    80      81      YOAAAA  OUKAAA  OOOOxx
+5416   7295    0       0       6       16      16      416     1416    416     5416    32      33      IAAAAA  PUKAAA  VVVVxx
+1823   7296    1       3       3       3       23      823     1823    1823    1823    46      47      DSAAAA  QUKAAA  AAAAxx
+1668   7297    0       0       8       8       68      668     1668    1668    1668    136     137     EMAAAA  RUKAAA  HHHHxx
+1795   7298    1       3       5       15      95      795     1795    1795    1795    190     191     BRAAAA  SUKAAA  OOOOxx
+8599   7299    1       3       9       19      99      599     599     3599    8599    198     199     TSAAAA  TUKAAA  VVVVxx
+5542   7300    0       2       2       2       42      542     1542    542     5542    84      85      EFAAAA  UUKAAA  AAAAxx
+5658   7301    0       2       8       18      58      658     1658    658     5658    116     117     QJAAAA  VUKAAA  HHHHxx
+9824   7302    0       0       4       4       24      824     1824    4824    9824    48      49      WNAAAA  WUKAAA  OOOOxx
+19     7303    1       3       9       19      19      19      19      19      19      38      39      TAAAAA  XUKAAA  VVVVxx
+9344   7304    0       0       4       4       44      344     1344    4344    9344    88      89      KVAAAA  YUKAAA  AAAAxx
+5900   7305    0       0       0       0       0       900     1900    900     5900    0       1       YSAAAA  ZUKAAA  HHHHxx
+7818   7306    0       2       8       18      18      818     1818    2818    7818    36      37      SOAAAA  AVKAAA  OOOOxx
+8377   7307    1       1       7       17      77      377     377     3377    8377    154     155     FKAAAA  BVKAAA  VVVVxx
+6886   7308    0       2       6       6       86      886     886     1886    6886    172     173     WEAAAA  CVKAAA  AAAAxx
+3201   7309    1       1       1       1       1       201     1201    3201    3201    2       3       DTAAAA  DVKAAA  HHHHxx
+87     7310    1       3       7       7       87      87      87      87      87      174     175     JDAAAA  EVKAAA  OOOOxx
+1089   7311    1       1       9       9       89      89      1089    1089    1089    178     179     XPAAAA  FVKAAA  VVVVxx
+3948   7312    0       0       8       8       48      948     1948    3948    3948    96      97      WVAAAA  GVKAAA  AAAAxx
+6383   7313    1       3       3       3       83      383     383     1383    6383    166     167     NLAAAA  HVKAAA  HHHHxx
+837    7314    1       1       7       17      37      837     837     837     837     74      75      FGAAAA  IVKAAA  OOOOxx
+6285   7315    1       1       5       5       85      285     285     1285    6285    170     171     THAAAA  JVKAAA  VVVVxx
+78     7316    0       2       8       18      78      78      78      78      78      156     157     ADAAAA  KVKAAA  AAAAxx
+4389   7317    1       1       9       9       89      389     389     4389    4389    178     179     VMAAAA  LVKAAA  HHHHxx
+4795   7318    1       3       5       15      95      795     795     4795    4795    190     191     LCAAAA  MVKAAA  OOOOxx
+9369   7319    1       1       9       9       69      369     1369    4369    9369    138     139     JWAAAA  NVKAAA  VVVVxx
+69     7320    1       1       9       9       69      69      69      69      69      138     139     RCAAAA  OVKAAA  AAAAxx
+7689   7321    1       1       9       9       89      689     1689    2689    7689    178     179     TJAAAA  PVKAAA  HHHHxx
+5642   7322    0       2       2       2       42      642     1642    642     5642    84      85      AJAAAA  QVKAAA  OOOOxx
+2348   7323    0       0       8       8       48      348     348     2348    2348    96      97      IMAAAA  RVKAAA  VVVVxx
+9308   7324    0       0       8       8       8       308     1308    4308    9308    16      17      AUAAAA  SVKAAA  AAAAxx
+9093   7325    1       1       3       13      93      93      1093    4093    9093    186     187     TLAAAA  TVKAAA  HHHHxx
+1199   7326    1       3       9       19      99      199     1199    1199    1199    198     199     DUAAAA  UVKAAA  OOOOxx
+307    7327    1       3       7       7       7       307     307     307     307     14      15      VLAAAA  VVKAAA  VVVVxx
+3814   7328    0       2       4       14      14      814     1814    3814    3814    28      29      SQAAAA  WVKAAA  AAAAxx
+8817   7329    1       1       7       17      17      817     817     3817    8817    34      35      DBAAAA  XVKAAA  HHHHxx
+2329   7330    1       1       9       9       29      329     329     2329    2329    58      59      PLAAAA  YVKAAA  OOOOxx
+2932   7331    0       0       2       12      32      932     932     2932    2932    64      65      UIAAAA  ZVKAAA  VVVVxx
+1986   7332    0       2       6       6       86      986     1986    1986    1986    172     173     KYAAAA  AWKAAA  AAAAxx
+5279   7333    1       3       9       19      79      279     1279    279     5279    158     159     BVAAAA  BWKAAA  HHHHxx
+5357   7334    1       1       7       17      57      357     1357    357     5357    114     115     BYAAAA  CWKAAA  OOOOxx
+6778   7335    0       2       8       18      78      778     778     1778    6778    156     157     SAAAAA  DWKAAA  VVVVxx
+2773   7336    1       1       3       13      73      773     773     2773    2773    146     147     RCAAAA  EWKAAA  AAAAxx
+244    7337    0       0       4       4       44      244     244     244     244     88      89      KJAAAA  FWKAAA  HHHHxx
+6900   7338    0       0       0       0       0       900     900     1900    6900    0       1       KFAAAA  GWKAAA  OOOOxx
+4739   7339    1       3       9       19      39      739     739     4739    4739    78      79      HAAAAA  HWKAAA  VVVVxx
+3217   7340    1       1       7       17      17      217     1217    3217    3217    34      35      TTAAAA  IWKAAA  AAAAxx
+7563   7341    1       3       3       3       63      563     1563    2563    7563    126     127     XEAAAA  JWKAAA  HHHHxx
+1807   7342    1       3       7       7       7       807     1807    1807    1807    14      15      NRAAAA  KWKAAA  OOOOxx
+4199   7343    1       3       9       19      99      199     199     4199    4199    198     199     NFAAAA  LWKAAA  VVVVxx
+1077   7344    1       1       7       17      77      77      1077    1077    1077    154     155     LPAAAA  MWKAAA  AAAAxx
+8348   7345    0       0       8       8       48      348     348     3348    8348    96      97      CJAAAA  NWKAAA  HHHHxx
+841    7346    1       1       1       1       41      841     841     841     841     82      83      JGAAAA  OWKAAA  OOOOxx
+8154   7347    0       2       4       14      54      154     154     3154    8154    108     109     QBAAAA  PWKAAA  VVVVxx
+5261   7348    1       1       1       1       61      261     1261    261     5261    122     123     JUAAAA  QWKAAA  AAAAxx
+1950   7349    0       2       0       10      50      950     1950    1950    1950    100     101     AXAAAA  RWKAAA  HHHHxx
+8472   7350    0       0       2       12      72      472     472     3472    8472    144     145     WNAAAA  SWKAAA  OOOOxx
+8745   7351    1       1       5       5       45      745     745     3745    8745    90      91      JYAAAA  TWKAAA  VVVVxx
+8715   7352    1       3       5       15      15      715     715     3715    8715    30      31      FXAAAA  UWKAAA  AAAAxx
+9708   7353    0       0       8       8       8       708     1708    4708    9708    16      17      KJAAAA  VWKAAA  HHHHxx
+5860   7354    0       0       0       0       60      860     1860    860     5860    120     121     KRAAAA  WWKAAA  OOOOxx
+9142   7355    0       2       2       2       42      142     1142    4142    9142    84      85      QNAAAA  XWKAAA  VVVVxx
+6582   7356    0       2       2       2       82      582     582     1582    6582    164     165     ETAAAA  YWKAAA  AAAAxx
+1255   7357    1       3       5       15      55      255     1255    1255    1255    110     111     HWAAAA  ZWKAAA  HHHHxx
+6459   7358    1       3       9       19      59      459     459     1459    6459    118     119     LOAAAA  AXKAAA  OOOOxx
+6327   7359    1       3       7       7       27      327     327     1327    6327    54      55      JJAAAA  BXKAAA  VVVVxx
+4692   7360    0       0       2       12      92      692     692     4692    4692    184     185     MYAAAA  CXKAAA  AAAAxx
+3772   7361    0       0       2       12      72      772     1772    3772    3772    144     145     CPAAAA  DXKAAA  HHHHxx
+4203   7362    1       3       3       3       3       203     203     4203    4203    6       7       RFAAAA  EXKAAA  OOOOxx
+2946   7363    0       2       6       6       46      946     946     2946    2946    92      93      IJAAAA  FXKAAA  VVVVxx
+3524   7364    0       0       4       4       24      524     1524    3524    3524    48      49      OFAAAA  GXKAAA  AAAAxx
+8409   7365    1       1       9       9       9       409     409     3409    8409    18      19      LLAAAA  HXKAAA  HHHHxx
+1824   7366    0       0       4       4       24      824     1824    1824    1824    48      49      ESAAAA  IXKAAA  OOOOxx
+4637   7367    1       1       7       17      37      637     637     4637    4637    74      75      JWAAAA  JXKAAA  VVVVxx
+589    7368    1       1       9       9       89      589     589     589     589     178     179     RWAAAA  KXKAAA  AAAAxx
+484    7369    0       0       4       4       84      484     484     484     484     168     169     QSAAAA  LXKAAA  HHHHxx
+8963   7370    1       3       3       3       63      963     963     3963    8963    126     127     TGAAAA  MXKAAA  OOOOxx
+5502   7371    0       2       2       2       2       502     1502    502     5502    4       5       QDAAAA  NXKAAA  VVVVxx
+6982   7372    0       2       2       2       82      982     982     1982    6982    164     165     OIAAAA  OXKAAA  AAAAxx
+8029   7373    1       1       9       9       29      29      29      3029    8029    58      59      VWAAAA  PXKAAA  HHHHxx
+4395   7374    1       3       5       15      95      395     395     4395    4395    190     191     BNAAAA  QXKAAA  OOOOxx
+2595   7375    1       3       5       15      95      595     595     2595    2595    190     191     VVAAAA  RXKAAA  VVVVxx
+2133   7376    1       1       3       13      33      133     133     2133    2133    66      67      BEAAAA  SXKAAA  AAAAxx
+1414   7377    0       2       4       14      14      414     1414    1414    1414    28      29      KCAAAA  TXKAAA  HHHHxx
+8201   7378    1       1       1       1       1       201     201     3201    8201    2       3       LDAAAA  UXKAAA  OOOOxx
+4706   7379    0       2       6       6       6       706     706     4706    4706    12      13      AZAAAA  VXKAAA  VVVVxx
+5310   7380    0       2       0       10      10      310     1310    310     5310    20      21      GWAAAA  WXKAAA  AAAAxx
+7333   7381    1       1       3       13      33      333     1333    2333    7333    66      67      BWAAAA  XXKAAA  HHHHxx
+9420   7382    0       0       0       0       20      420     1420    4420    9420    40      41      IYAAAA  YXKAAA  OOOOxx
+1383   7383    1       3       3       3       83      383     1383    1383    1383    166     167     FBAAAA  ZXKAAA  VVVVxx
+6225   7384    1       1       5       5       25      225     225     1225    6225    50      51      LFAAAA  AYKAAA  AAAAxx
+2064   7385    0       0       4       4       64      64      64      2064    2064    128     129     KBAAAA  BYKAAA  HHHHxx
+6700   7386    0       0       0       0       0       700     700     1700    6700    0       1       SXAAAA  CYKAAA  OOOOxx
+1352   7387    0       0       2       12      52      352     1352    1352    1352    104     105     AAAAAA  DYKAAA  VVVVxx
+4249   7388    1       1       9       9       49      249     249     4249    4249    98      99      LHAAAA  EYKAAA  AAAAxx
+9429   7389    1       1       9       9       29      429     1429    4429    9429    58      59      RYAAAA  FYKAAA  HHHHxx
+8090   7390    0       2       0       10      90      90      90      3090    8090    180     181     EZAAAA  GYKAAA  OOOOxx
+5378   7391    0       2       8       18      78      378     1378    378     5378    156     157     WYAAAA  HYKAAA  VVVVxx
+9085   7392    1       1       5       5       85      85      1085    4085    9085    170     171     LLAAAA  IYKAAA  AAAAxx
+7468   7393    0       0       8       8       68      468     1468    2468    7468    136     137     GBAAAA  JYKAAA  HHHHxx
+9955   7394    1       3       5       15      55      955     1955    4955    9955    110     111     XSAAAA  KYKAAA  OOOOxx
+8692   7395    0       0       2       12      92      692     692     3692    8692    184     185     IWAAAA  LYKAAA  VVVVxx
+1463   7396    1       3       3       3       63      463     1463    1463    1463    126     127     HEAAAA  MYKAAA  AAAAxx
+3577   7397    1       1       7       17      77      577     1577    3577    3577    154     155     PHAAAA  NYKAAA  HHHHxx
+5654   7398    0       2       4       14      54      654     1654    654     5654    108     109     MJAAAA  OYKAAA  OOOOxx
+7955   7399    1       3       5       15      55      955     1955    2955    7955    110     111     ZTAAAA  PYKAAA  VVVVxx
+4843   7400    1       3       3       3       43      843     843     4843    4843    86      87      HEAAAA  QYKAAA  AAAAxx
+1776   7401    0       0       6       16      76      776     1776    1776    1776    152     153     IQAAAA  RYKAAA  HHHHxx
+2223   7402    1       3       3       3       23      223     223     2223    2223    46      47      NHAAAA  SYKAAA  OOOOxx
+8442   7403    0       2       2       2       42      442     442     3442    8442    84      85      SMAAAA  TYKAAA  VVVVxx
+9738   7404    0       2       8       18      38      738     1738    4738    9738    76      77      OKAAAA  UYKAAA  AAAAxx
+4867   7405    1       3       7       7       67      867     867     4867    4867    134     135     FFAAAA  VYKAAA  HHHHxx
+2983   7406    1       3       3       3       83      983     983     2983    2983    166     167     TKAAAA  WYKAAA  OOOOxx
+3300   7407    0       0       0       0       0       300     1300    3300    3300    0       1       YWAAAA  XYKAAA  VVVVxx
+3815   7408    1       3       5       15      15      815     1815    3815    3815    30      31      TQAAAA  YYKAAA  AAAAxx
+1779   7409    1       3       9       19      79      779     1779    1779    1779    158     159     LQAAAA  ZYKAAA  HHHHxx
+1123   7410    1       3       3       3       23      123     1123    1123    1123    46      47      FRAAAA  AZKAAA  OOOOxx
+4824   7411    0       0       4       4       24      824     824     4824    4824    48      49      ODAAAA  BZKAAA  VVVVxx
+5407   7412    1       3       7       7       7       407     1407    407     5407    14      15      ZZAAAA  CZKAAA  AAAAxx
+5123   7413    1       3       3       3       23      123     1123    123     5123    46      47      BPAAAA  DZKAAA  HHHHxx
+2515   7414    1       3       5       15      15      515     515     2515    2515    30      31      TSAAAA  EZKAAA  OOOOxx
+4781   7415    1       1       1       1       81      781     781     4781    4781    162     163     XBAAAA  FZKAAA  VVVVxx
+7831   7416    1       3       1       11      31      831     1831    2831    7831    62      63      FPAAAA  GZKAAA  AAAAxx
+6946   7417    0       2       6       6       46      946     946     1946    6946    92      93      EHAAAA  HZKAAA  HHHHxx
+1215   7418    1       3       5       15      15      215     1215    1215    1215    30      31      TUAAAA  IZKAAA  OOOOxx
+7783   7419    1       3       3       3       83      783     1783    2783    7783    166     167     JNAAAA  JZKAAA  VVVVxx
+4532   7420    0       0       2       12      32      532     532     4532    4532    64      65      ISAAAA  KZKAAA  AAAAxx
+9068   7421    0       0       8       8       68      68      1068    4068    9068    136     137     UKAAAA  LZKAAA  HHHHxx
+7030   7422    0       2       0       10      30      30      1030    2030    7030    60      61      KKAAAA  MZKAAA  OOOOxx
+436    7423    0       0       6       16      36      436     436     436     436     72      73      UQAAAA  NZKAAA  VVVVxx
+6549   7424    1       1       9       9       49      549     549     1549    6549    98      99      XRAAAA  OZKAAA  AAAAxx
+3348   7425    0       0       8       8       48      348     1348    3348    3348    96      97      UYAAAA  PZKAAA  HHHHxx
+6229   7426    1       1       9       9       29      229     229     1229    6229    58      59      PFAAAA  QZKAAA  OOOOxx
+3933   7427    1       1       3       13      33      933     1933    3933    3933    66      67      HVAAAA  RZKAAA  VVVVxx
+1876   7428    0       0       6       16      76      876     1876    1876    1876    152     153     EUAAAA  SZKAAA  AAAAxx
+8920   7429    0       0       0       0       20      920     920     3920    8920    40      41      CFAAAA  TZKAAA  HHHHxx
+7926   7430    0       2       6       6       26      926     1926    2926    7926    52      53      WSAAAA  UZKAAA  OOOOxx
+8805   7431    1       1       5       5       5       805     805     3805    8805    10      11      RAAAAA  VZKAAA  VVVVxx
+6729   7432    1       1       9       9       29      729     729     1729    6729    58      59      VYAAAA  WZKAAA  AAAAxx
+7397   7433    1       1       7       17      97      397     1397    2397    7397    194     195     NYAAAA  XZKAAA  HHHHxx
+9303   7434    1       3       3       3       3       303     1303    4303    9303    6       7       VTAAAA  YZKAAA  OOOOxx
+4255   7435    1       3       5       15      55      255     255     4255    4255    110     111     RHAAAA  ZZKAAA  VVVVxx
+7229   7436    1       1       9       9       29      229     1229    2229    7229    58      59      BSAAAA  AALAAA  AAAAxx
+854    7437    0       2       4       14      54      854     854     854     854     108     109     WGAAAA  BALAAA  HHHHxx
+6723   7438    1       3       3       3       23      723     723     1723    6723    46      47      PYAAAA  CALAAA  OOOOxx
+9597   7439    1       1       7       17      97      597     1597    4597    9597    194     195     DFAAAA  DALAAA  VVVVxx
+6532   7440    0       0       2       12      32      532     532     1532    6532    64      65      GRAAAA  EALAAA  AAAAxx
+2910   7441    0       2       0       10      10      910     910     2910    2910    20      21      YHAAAA  FALAAA  HHHHxx
+6717   7442    1       1       7       17      17      717     717     1717    6717    34      35      JYAAAA  GALAAA  OOOOxx
+1790   7443    0       2       0       10      90      790     1790    1790    1790    180     181     WQAAAA  HALAAA  VVVVxx
+3761   7444    1       1       1       1       61      761     1761    3761    3761    122     123     ROAAAA  IALAAA  AAAAxx
+1565   7445    1       1       5       5       65      565     1565    1565    1565    130     131     FIAAAA  JALAAA  HHHHxx
+6205   7446    1       1       5       5       5       205     205     1205    6205    10      11      REAAAA  KALAAA  OOOOxx
+2726   7447    0       2       6       6       26      726     726     2726    2726    52      53      WAAAAA  LALAAA  VVVVxx
+799    7448    1       3       9       19      99      799     799     799     799     198     199     TEAAAA  MALAAA  AAAAxx
+3540   7449    0       0       0       0       40      540     1540    3540    3540    80      81      EGAAAA  NALAAA  HHHHxx
+5878   7450    0       2       8       18      78      878     1878    878     5878    156     157     CSAAAA  OALAAA  OOOOxx
+2542   7451    0       2       2       2       42      542     542     2542    2542    84      85      UTAAAA  PALAAA  VVVVxx
+4888   7452    0       0       8       8       88      888     888     4888    4888    176     177     AGAAAA  QALAAA  AAAAxx
+5290   7453    0       2       0       10      90      290     1290    290     5290    180     181     MVAAAA  RALAAA  HHHHxx
+7995   7454    1       3       5       15      95      995     1995    2995    7995    190     191     NVAAAA  SALAAA  OOOOxx
+3519   7455    1       3       9       19      19      519     1519    3519    3519    38      39      JFAAAA  TALAAA  VVVVxx
+3571   7456    1       3       1       11      71      571     1571    3571    3571    142     143     JHAAAA  UALAAA  AAAAxx
+7854   7457    0       2       4       14      54      854     1854    2854    7854    108     109     CQAAAA  VALAAA  HHHHxx
+5184   7458    0       0       4       4       84      184     1184    184     5184    168     169     KRAAAA  WALAAA  OOOOxx
+3498   7459    0       2       8       18      98      498     1498    3498    3498    196     197     OEAAAA  XALAAA  VVVVxx
+1264   7460    0       0       4       4       64      264     1264    1264    1264    128     129     QWAAAA  YALAAA  AAAAxx
+3159   7461    1       3       9       19      59      159     1159    3159    3159    118     119     NRAAAA  ZALAAA  HHHHxx
+5480   7462    0       0       0       0       80      480     1480    480     5480    160     161     UCAAAA  ABLAAA  OOOOxx
+1706   7463    0       2       6       6       6       706     1706    1706    1706    12      13      QNAAAA  BBLAAA  VVVVxx
+4540   7464    0       0       0       0       40      540     540     4540    4540    80      81      QSAAAA  CBLAAA  AAAAxx
+2799   7465    1       3       9       19      99      799     799     2799    2799    198     199     RDAAAA  DBLAAA  HHHHxx
+7389   7466    1       1       9       9       89      389     1389    2389    7389    178     179     FYAAAA  EBLAAA  OOOOxx
+5565   7467    1       1       5       5       65      565     1565    565     5565    130     131     BGAAAA  FBLAAA  VVVVxx
+3896   7468    0       0       6       16      96      896     1896    3896    3896    192     193     WTAAAA  GBLAAA  AAAAxx
+2100   7469    0       0       0       0       0       100     100     2100    2100    0       1       UCAAAA  HBLAAA  HHHHxx
+3507   7470    1       3       7       7       7       507     1507    3507    3507    14      15      XEAAAA  IBLAAA  OOOOxx
+7971   7471    1       3       1       11      71      971     1971    2971    7971    142     143     PUAAAA  JBLAAA  VVVVxx
+2312   7472    0       0       2       12      12      312     312     2312    2312    24      25      YKAAAA  KBLAAA  AAAAxx
+2494   7473    0       2       4       14      94      494     494     2494    2494    188     189     YRAAAA  LBLAAA  HHHHxx
+2474   7474    0       2       4       14      74      474     474     2474    2474    148     149     ERAAAA  MBLAAA  OOOOxx
+3136   7475    0       0       6       16      36      136     1136    3136    3136    72      73      QQAAAA  NBLAAA  VVVVxx
+7242   7476    0       2       2       2       42      242     1242    2242    7242    84      85      OSAAAA  OBLAAA  AAAAxx
+9430   7477    0       2       0       10      30      430     1430    4430    9430    60      61      SYAAAA  PBLAAA  HHHHxx
+1052   7478    0       0       2       12      52      52      1052    1052    1052    104     105     MOAAAA  QBLAAA  OOOOxx
+4172   7479    0       0       2       12      72      172     172     4172    4172    144     145     MEAAAA  RBLAAA  VVVVxx
+970    7480    0       2       0       10      70      970     970     970     970     140     141     ILAAAA  SBLAAA  AAAAxx
+882    7481    0       2       2       2       82      882     882     882     882     164     165     YHAAAA  TBLAAA  HHHHxx
+9799   7482    1       3       9       19      99      799     1799    4799    9799    198     199     XMAAAA  UBLAAA  OOOOxx
+5850   7483    0       2       0       10      50      850     1850    850     5850    100     101     ARAAAA  VBLAAA  VVVVxx
+9473   7484    1       1       3       13      73      473     1473    4473    9473    146     147     JAAAAA  WBLAAA  AAAAxx
+8635   7485    1       3       5       15      35      635     635     3635    8635    70      71      DUAAAA  XBLAAA  HHHHxx
+2349   7486    1       1       9       9       49      349     349     2349    2349    98      99      JMAAAA  YBLAAA  OOOOxx
+2270   7487    0       2       0       10      70      270     270     2270    2270    140     141     IJAAAA  ZBLAAA  VVVVxx
+7887   7488    1       3       7       7       87      887     1887    2887    7887    174     175     JRAAAA  ACLAAA  AAAAxx
+3091   7489    1       3       1       11      91      91      1091    3091    3091    182     183     XOAAAA  BCLAAA  HHHHxx
+3728   7490    0       0       8       8       28      728     1728    3728    3728    56      57      KNAAAA  CCLAAA  OOOOxx
+3658   7491    0       2       8       18      58      658     1658    3658    3658    116     117     SKAAAA  DCLAAA  VVVVxx
+5975   7492    1       3       5       15      75      975     1975    975     5975    150     151     VVAAAA  ECLAAA  AAAAxx
+332    7493    0       0       2       12      32      332     332     332     332     64      65      UMAAAA  FCLAAA  HHHHxx
+7990   7494    0       2       0       10      90      990     1990    2990    7990    180     181     IVAAAA  GCLAAA  OOOOxx
+8688   7495    0       0       8       8       88      688     688     3688    8688    176     177     EWAAAA  HCLAAA  VVVVxx
+9601   7496    1       1       1       1       1       601     1601    4601    9601    2       3       HFAAAA  ICLAAA  AAAAxx
+8401   7497    1       1       1       1       1       401     401     3401    8401    2       3       DLAAAA  JCLAAA  HHHHxx
+8093   7498    1       1       3       13      93      93      93      3093    8093    186     187     HZAAAA  KCLAAA  OOOOxx
+4278   7499    0       2       8       18      78      278     278     4278    4278    156     157     OIAAAA  LCLAAA  VVVVxx
+5467   7500    1       3       7       7       67      467     1467    467     5467    134     135     HCAAAA  MCLAAA  AAAAxx
+3137   7501    1       1       7       17      37      137     1137    3137    3137    74      75      RQAAAA  NCLAAA  HHHHxx
+204    7502    0       0       4       4       4       204     204     204     204     8       9       WHAAAA  OCLAAA  OOOOxx
+8224   7503    0       0       4       4       24      224     224     3224    8224    48      49      IEAAAA  PCLAAA  VVVVxx
+2944   7504    0       0       4       4       44      944     944     2944    2944    88      89      GJAAAA  QCLAAA  AAAAxx
+7593   7505    1       1       3       13      93      593     1593    2593    7593    186     187     BGAAAA  RCLAAA  HHHHxx
+814    7506    0       2       4       14      14      814     814     814     814     28      29      IFAAAA  SCLAAA  OOOOxx
+8047   7507    1       3       7       7       47      47      47      3047    8047    94      95      NXAAAA  TCLAAA  VVVVxx
+7802   7508    0       2       2       2       2       802     1802    2802    7802    4       5       COAAAA  UCLAAA  AAAAxx
+901    7509    1       1       1       1       1       901     901     901     901     2       3       RIAAAA  VCLAAA  HHHHxx
+6168   7510    0       0       8       8       68      168     168     1168    6168    136     137     GDAAAA  WCLAAA  OOOOxx
+2950   7511    0       2       0       10      50      950     950     2950    2950    100     101     MJAAAA  XCLAAA  VVVVxx
+5393   7512    1       1       3       13      93      393     1393    393     5393    186     187     LZAAAA  YCLAAA  AAAAxx
+3585   7513    1       1       5       5       85      585     1585    3585    3585    170     171     XHAAAA  ZCLAAA  HHHHxx
+9392   7514    0       0       2       12      92      392     1392    4392    9392    184     185     GXAAAA  ADLAAA  OOOOxx
+8314   7515    0       2       4       14      14      314     314     3314    8314    28      29      UHAAAA  BDLAAA  VVVVxx
+9972   7516    0       0       2       12      72      972     1972    4972    9972    144     145     OTAAAA  CDLAAA  AAAAxx
+9130   7517    0       2       0       10      30      130     1130    4130    9130    60      61      ENAAAA  DDLAAA  HHHHxx
+975    7518    1       3       5       15      75      975     975     975     975     150     151     NLAAAA  EDLAAA  OOOOxx
+5720   7519    0       0       0       0       20      720     1720    720     5720    40      41      AMAAAA  FDLAAA  VVVVxx
+3769   7520    1       1       9       9       69      769     1769    3769    3769    138     139     ZOAAAA  GDLAAA  AAAAxx
+5303   7521    1       3       3       3       3       303     1303    303     5303    6       7       ZVAAAA  HDLAAA  HHHHxx
+6564   7522    0       0       4       4       64      564     564     1564    6564    128     129     MSAAAA  IDLAAA  OOOOxx
+7855   7523    1       3       5       15      55      855     1855    2855    7855    110     111     DQAAAA  JDLAAA  VVVVxx
+8153   7524    1       1       3       13      53      153     153     3153    8153    106     107     PBAAAA  KDLAAA  AAAAxx
+2292   7525    0       0       2       12      92      292     292     2292    2292    184     185     EKAAAA  LDLAAA  HHHHxx
+3156   7526    0       0       6       16      56      156     1156    3156    3156    112     113     KRAAAA  MDLAAA  OOOOxx
+6580   7527    0       0       0       0       80      580     580     1580    6580    160     161     CTAAAA  NDLAAA  VVVVxx
+5324   7528    0       0       4       4       24      324     1324    324     5324    48      49      UWAAAA  ODLAAA  AAAAxx
+8871   7529    1       3       1       11      71      871     871     3871    8871    142     143     FDAAAA  PDLAAA  HHHHxx
+2543   7530    1       3       3       3       43      543     543     2543    2543    86      87      VTAAAA  QDLAAA  OOOOxx
+7857   7531    1       1       7       17      57      857     1857    2857    7857    114     115     FQAAAA  RDLAAA  VVVVxx
+4084   7532    0       0       4       4       84      84      84      4084    4084    168     169     CBAAAA  SDLAAA  AAAAxx
+9887   7533    1       3       7       7       87      887     1887    4887    9887    174     175     HQAAAA  TDLAAA  HHHHxx
+6940   7534    0       0       0       0       40      940     940     1940    6940    80      81      YGAAAA  UDLAAA  OOOOxx
+3415   7535    1       3       5       15      15      415     1415    3415    3415    30      31      JBAAAA  VDLAAA  VVVVxx
+5012   7536    0       0       2       12      12      12      1012    12      5012    24      25      UKAAAA  WDLAAA  AAAAxx
+3187   7537    1       3       7       7       87      187     1187    3187    3187    174     175     PSAAAA  XDLAAA  HHHHxx
+8556   7538    0       0       6       16      56      556     556     3556    8556    112     113     CRAAAA  YDLAAA  OOOOxx
+7966   7539    0       2       6       6       66      966     1966    2966    7966    132     133     KUAAAA  ZDLAAA  VVVVxx
+7481   7540    1       1       1       1       81      481     1481    2481    7481    162     163     TBAAAA  AELAAA  AAAAxx
+8524   7541    0       0       4       4       24      524     524     3524    8524    48      49      WPAAAA  BELAAA  HHHHxx
+3021   7542    1       1       1       1       21      21      1021    3021    3021    42      43      FMAAAA  CELAAA  OOOOxx
+6045   7543    1       1       5       5       45      45      45      1045    6045    90      91      NYAAAA  DELAAA  VVVVxx
+8022   7544    0       2       2       2       22      22      22      3022    8022    44      45      OWAAAA  EELAAA  AAAAxx
+3626   7545    0       2       6       6       26      626     1626    3626    3626    52      53      MJAAAA  FELAAA  HHHHxx
+1030   7546    0       2       0       10      30      30      1030    1030    1030    60      61      QNAAAA  GELAAA  OOOOxx
+8903   7547    1       3       3       3       3       903     903     3903    8903    6       7       LEAAAA  HELAAA  VVVVxx
+7488   7548    0       0       8       8       88      488     1488    2488    7488    176     177     ACAAAA  IELAAA  AAAAxx
+9293   7549    1       1       3       13      93      293     1293    4293    9293    186     187     LTAAAA  JELAAA  HHHHxx
+4586   7550    0       2       6       6       86      586     586     4586    4586    172     173     KUAAAA  KELAAA  OOOOxx
+9282   7551    0       2       2       2       82      282     1282    4282    9282    164     165     ATAAAA  LELAAA  VVVVxx
+1948   7552    0       0       8       8       48      948     1948    1948    1948    96      97      YWAAAA  MELAAA  AAAAxx
+2534   7553    0       2       4       14      34      534     534     2534    2534    68      69      MTAAAA  NELAAA  HHHHxx
+1150   7554    0       2       0       10      50      150     1150    1150    1150    100     101     GSAAAA  OELAAA  OOOOxx
+4931   7555    1       3       1       11      31      931     931     4931    4931    62      63      RHAAAA  PELAAA  VVVVxx
+2866   7556    0       2       6       6       66      866     866     2866    2866    132     133     GGAAAA  QELAAA  AAAAxx
+6172   7557    0       0       2       12      72      172     172     1172    6172    144     145     KDAAAA  RELAAA  HHHHxx
+4819   7558    1       3       9       19      19      819     819     4819    4819    38      39      JDAAAA  SELAAA  OOOOxx
+569    7559    1       1       9       9       69      569     569     569     569     138     139     XVAAAA  TELAAA  VVVVxx
+1146   7560    0       2       6       6       46      146     1146    1146    1146    92      93      CSAAAA  UELAAA  AAAAxx
+3062   7561    0       2       2       2       62      62      1062    3062    3062    124     125     UNAAAA  VELAAA  HHHHxx
+7690   7562    0       2       0       10      90      690     1690    2690    7690    180     181     UJAAAA  WELAAA  OOOOxx
+8611   7563    1       3       1       11      11      611     611     3611    8611    22      23      FTAAAA  XELAAA  VVVVxx
+1142   7564    0       2       2       2       42      142     1142    1142    1142    84      85      YRAAAA  YELAAA  AAAAxx
+1193   7565    1       1       3       13      93      193     1193    1193    1193    186     187     XTAAAA  ZELAAA  HHHHxx
+2507   7566    1       3       7       7       7       507     507     2507    2507    14      15      LSAAAA  AFLAAA  OOOOxx
+1043   7567    1       3       3       3       43      43      1043    1043    1043    86      87      DOAAAA  BFLAAA  VVVVxx
+7472   7568    0       0       2       12      72      472     1472    2472    7472    144     145     KBAAAA  CFLAAA  AAAAxx
+1817   7569    1       1       7       17      17      817     1817    1817    1817    34      35      XRAAAA  DFLAAA  HHHHxx
+3868   7570    0       0       8       8       68      868     1868    3868    3868    136     137     USAAAA  EFLAAA  OOOOxx
+9031   7571    1       3       1       11      31      31      1031    4031    9031    62      63      JJAAAA  FFLAAA  VVVVxx
+7254   7572    0       2       4       14      54      254     1254    2254    7254    108     109     ATAAAA  GFLAAA  AAAAxx
+5030   7573    0       2       0       10      30      30      1030    30      5030    60      61      MLAAAA  HFLAAA  HHHHxx
+6594   7574    0       2       4       14      94      594     594     1594    6594    188     189     QTAAAA  IFLAAA  OOOOxx
+6862   7575    0       2       2       2       62      862     862     1862    6862    124     125     YDAAAA  JFLAAA  VVVVxx
+1994   7576    0       2       4       14      94      994     1994    1994    1994    188     189     SYAAAA  KFLAAA  AAAAxx
+9017   7577    1       1       7       17      17      17      1017    4017    9017    34      35      VIAAAA  LFLAAA  HHHHxx
+5716   7578    0       0       6       16      16      716     1716    716     5716    32      33      WLAAAA  MFLAAA  OOOOxx
+1900   7579    0       0       0       0       0       900     1900    1900    1900    0       1       CVAAAA  NFLAAA  VVVVxx
+120    7580    0       0       0       0       20      120     120     120     120     40      41      QEAAAA  OFLAAA  AAAAxx
+9003   7581    1       3       3       3       3       3       1003    4003    9003    6       7       HIAAAA  PFLAAA  HHHHxx
+4178   7582    0       2       8       18      78      178     178     4178    4178    156     157     SEAAAA  QFLAAA  OOOOxx
+8777   7583    1       1       7       17      77      777     777     3777    8777    154     155     PZAAAA  RFLAAA  VVVVxx
+3653   7584    1       1       3       13      53      653     1653    3653    3653    106     107     NKAAAA  SFLAAA  AAAAxx
+1137   7585    1       1       7       17      37      137     1137    1137    1137    74      75      TRAAAA  TFLAAA  HHHHxx
+6362   7586    0       2       2       2       62      362     362     1362    6362    124     125     SKAAAA  UFLAAA  OOOOxx
+8537   7587    1       1       7       17      37      537     537     3537    8537    74      75      JQAAAA  VFLAAA  VVVVxx
+1590   7588    0       2       0       10      90      590     1590    1590    1590    180     181     EJAAAA  WFLAAA  AAAAxx
+374    7589    0       2       4       14      74      374     374     374     374     148     149     KOAAAA  XFLAAA  HHHHxx
+2597   7590    1       1       7       17      97      597     597     2597    2597    194     195     XVAAAA  YFLAAA  OOOOxx
+8071   7591    1       3       1       11      71      71      71      3071    8071    142     143     LYAAAA  ZFLAAA  VVVVxx
+9009   7592    1       1       9       9       9       9       1009    4009    9009    18      19      NIAAAA  AGLAAA  AAAAxx
+1978   7593    0       2       8       18      78      978     1978    1978    1978    156     157     CYAAAA  BGLAAA  HHHHxx
+1541   7594    1       1       1       1       41      541     1541    1541    1541    82      83      HHAAAA  CGLAAA  OOOOxx
+4998   7595    0       2       8       18      98      998     998     4998    4998    196     197     GKAAAA  DGLAAA  VVVVxx
+1649   7596    1       1       9       9       49      649     1649    1649    1649    98      99      LLAAAA  EGLAAA  AAAAxx
+5426   7597    0       2       6       6       26      426     1426    426     5426    52      53      SAAAAA  FGLAAA  HHHHxx
+1492   7598    0       0       2       12      92      492     1492    1492    1492    184     185     KFAAAA  GGLAAA  OOOOxx
+9622   7599    0       2       2       2       22      622     1622    4622    9622    44      45      CGAAAA  HGLAAA  VVVVxx
+701    7600    1       1       1       1       1       701     701     701     701     2       3       ZAAAAA  IGLAAA  AAAAxx
+2781   7601    1       1       1       1       81      781     781     2781    2781    162     163     ZCAAAA  JGLAAA  HHHHxx
+3982   7602    0       2       2       2       82      982     1982    3982    3982    164     165     EXAAAA  KGLAAA  OOOOxx
+7259   7603    1       3       9       19      59      259     1259    2259    7259    118     119     FTAAAA  LGLAAA  VVVVxx
+9868   7604    0       0       8       8       68      868     1868    4868    9868    136     137     OPAAAA  MGLAAA  AAAAxx
+564    7605    0       0       4       4       64      564     564     564     564     128     129     SVAAAA  NGLAAA  HHHHxx
+6315   7606    1       3       5       15      15      315     315     1315    6315    30      31      XIAAAA  OGLAAA  OOOOxx
+9092   7607    0       0       2       12      92      92      1092    4092    9092    184     185     SLAAAA  PGLAAA  VVVVxx
+8237   7608    1       1       7       17      37      237     237     3237    8237    74      75      VEAAAA  QGLAAA  AAAAxx
+1513   7609    1       1       3       13      13      513     1513    1513    1513    26      27      FGAAAA  RGLAAA  HHHHxx
+1922   7610    0       2       2       2       22      922     1922    1922    1922    44      45      YVAAAA  SGLAAA  OOOOxx
+5396   7611    0       0       6       16      96      396     1396    396     5396    192     193     OZAAAA  TGLAAA  VVVVxx
+2485   7612    1       1       5       5       85      485     485     2485    2485    170     171     PRAAAA  UGLAAA  AAAAxx
+5774   7613    0       2       4       14      74      774     1774    774     5774    148     149     COAAAA  VGLAAA  HHHHxx
+3983   7614    1       3       3       3       83      983     1983    3983    3983    166     167     FXAAAA  WGLAAA  OOOOxx
+221    7615    1       1       1       1       21      221     221     221     221     42      43      NIAAAA  XGLAAA  VVVVxx
+8662   7616    0       2       2       2       62      662     662     3662    8662    124     125     EVAAAA  YGLAAA  AAAAxx
+2456   7617    0       0       6       16      56      456     456     2456    2456    112     113     MQAAAA  ZGLAAA  HHHHxx
+9736   7618    0       0       6       16      36      736     1736    4736    9736    72      73      MKAAAA  AHLAAA  OOOOxx
+8936   7619    0       0       6       16      36      936     936     3936    8936    72      73      SFAAAA  BHLAAA  VVVVxx
+5395   7620    1       3       5       15      95      395     1395    395     5395    190     191     NZAAAA  CHLAAA  AAAAxx
+9523   7621    1       3       3       3       23      523     1523    4523    9523    46      47      HCAAAA  DHLAAA  HHHHxx
+6980   7622    0       0       0       0       80      980     980     1980    6980    160     161     MIAAAA  EHLAAA  OOOOxx
+2091   7623    1       3       1       11      91      91      91      2091    2091    182     183     LCAAAA  FHLAAA  VVVVxx
+6807   7624    1       3       7       7       7       807     807     1807    6807    14      15      VBAAAA  GHLAAA  AAAAxx
+8818   7625    0       2       8       18      18      818     818     3818    8818    36      37      EBAAAA  HHLAAA  HHHHxx
+5298   7626    0       2       8       18      98      298     1298    298     5298    196     197     UVAAAA  IHLAAA  OOOOxx
+1726   7627    0       2       6       6       26      726     1726    1726    1726    52      53      KOAAAA  JHLAAA  VVVVxx
+3878   7628    0       2       8       18      78      878     1878    3878    3878    156     157     ETAAAA  KHLAAA  AAAAxx
+8700   7629    0       0       0       0       0       700     700     3700    8700    0       1       QWAAAA  LHLAAA  HHHHxx
+5201   7630    1       1       1       1       1       201     1201    201     5201    2       3       BSAAAA  MHLAAA  OOOOxx
+3936   7631    0       0       6       16      36      936     1936    3936    3936    72      73      KVAAAA  NHLAAA  VVVVxx
+776    7632    0       0       6       16      76      776     776     776     776     152     153     WDAAAA  OHLAAA  AAAAxx
+5302   7633    0       2       2       2       2       302     1302    302     5302    4       5       YVAAAA  PHLAAA  HHHHxx
+3595   7634    1       3       5       15      95      595     1595    3595    3595    190     191     HIAAAA  QHLAAA  OOOOxx
+9061   7635    1       1       1       1       61      61      1061    4061    9061    122     123     NKAAAA  RHLAAA  VVVVxx
+6261   7636    1       1       1       1       61      261     261     1261    6261    122     123     VGAAAA  SHLAAA  AAAAxx
+8878   7637    0       2       8       18      78      878     878     3878    8878    156     157     MDAAAA  THLAAA  HHHHxx
+3312   7638    0       0       2       12      12      312     1312    3312    3312    24      25      KXAAAA  UHLAAA  OOOOxx
+9422   7639    0       2       2       2       22      422     1422    4422    9422    44      45      KYAAAA  VHLAAA  VVVVxx
+7321   7640    1       1       1       1       21      321     1321    2321    7321    42      43      PVAAAA  WHLAAA  AAAAxx
+3813   7641    1       1       3       13      13      813     1813    3813    3813    26      27      RQAAAA  XHLAAA  HHHHxx
+5848   7642    0       0       8       8       48      848     1848    848     5848    96      97      YQAAAA  YHLAAA  OOOOxx
+3535   7643    1       3       5       15      35      535     1535    3535    3535    70      71      ZFAAAA  ZHLAAA  VVVVxx
+1040   7644    0       0       0       0       40      40      1040    1040    1040    80      81      AOAAAA  AILAAA  AAAAxx
+8572   7645    0       0       2       12      72      572     572     3572    8572    144     145     SRAAAA  BILAAA  HHHHxx
+5435   7646    1       3       5       15      35      435     1435    435     5435    70      71      BBAAAA  CILAAA  OOOOxx
+8199   7647    1       3       9       19      99      199     199     3199    8199    198     199     JDAAAA  DILAAA  VVVVxx
+8775   7648    1       3       5       15      75      775     775     3775    8775    150     151     NZAAAA  EILAAA  AAAAxx
+7722   7649    0       2       2       2       22      722     1722    2722    7722    44      45      ALAAAA  FILAAA  HHHHxx
+3549   7650    1       1       9       9       49      549     1549    3549    3549    98      99      NGAAAA  GILAAA  OOOOxx
+2578   7651    0       2       8       18      78      578     578     2578    2578    156     157     EVAAAA  HILAAA  VVVVxx
+1695   7652    1       3       5       15      95      695     1695    1695    1695    190     191     FNAAAA  IILAAA  AAAAxx
+1902   7653    0       2       2       2       2       902     1902    1902    1902    4       5       EVAAAA  JILAAA  HHHHxx
+6058   7654    0       2       8       18      58      58      58      1058    6058    116     117     AZAAAA  KILAAA  OOOOxx
+6591   7655    1       3       1       11      91      591     591     1591    6591    182     183     NTAAAA  LILAAA  VVVVxx
+7962   7656    0       2       2       2       62      962     1962    2962    7962    124     125     GUAAAA  MILAAA  AAAAxx
+5612   7657    0       0       2       12      12      612     1612    612     5612    24      25      WHAAAA  NILAAA  HHHHxx
+3341   7658    1       1       1       1       41      341     1341    3341    3341    82      83      NYAAAA  OILAAA  OOOOxx
+5460   7659    0       0       0       0       60      460     1460    460     5460    120     121     ACAAAA  PILAAA  VVVVxx
+2368   7660    0       0       8       8       68      368     368     2368    2368    136     137     CNAAAA  QILAAA  AAAAxx
+8646   7661    0       2       6       6       46      646     646     3646    8646    92      93      OUAAAA  RILAAA  HHHHxx
+4987   7662    1       3       7       7       87      987     987     4987    4987    174     175     VJAAAA  SILAAA  OOOOxx
+9018   7663    0       2       8       18      18      18      1018    4018    9018    36      37      WIAAAA  TILAAA  VVVVxx
+8685   7664    1       1       5       5       85      685     685     3685    8685    170     171     BWAAAA  UILAAA  AAAAxx
+694    7665    0       2       4       14      94      694     694     694     694     188     189     SAAAAA  VILAAA  HHHHxx
+2012   7666    0       0       2       12      12      12      12      2012    2012    24      25      KZAAAA  WILAAA  OOOOxx
+2417   7667    1       1       7       17      17      417     417     2417    2417    34      35      ZOAAAA  XILAAA  VVVVxx
+4022   7668    0       2       2       2       22      22      22      4022    4022    44      45      SYAAAA  YILAAA  AAAAxx
+5935   7669    1       3       5       15      35      935     1935    935     5935    70      71      HUAAAA  ZILAAA  HHHHxx
+1656   7670    0       0       6       16      56      656     1656    1656    1656    112     113     SLAAAA  AJLAAA  OOOOxx
+6195   7671    1       3       5       15      95      195     195     1195    6195    190     191     HEAAAA  BJLAAA  VVVVxx
+3057   7672    1       1       7       17      57      57      1057    3057    3057    114     115     PNAAAA  CJLAAA  AAAAxx
+2852   7673    0       0       2       12      52      852     852     2852    2852    104     105     SFAAAA  DJLAAA  HHHHxx
+4634   7674    0       2       4       14      34      634     634     4634    4634    68      69      GWAAAA  EJLAAA  OOOOxx
+1689   7675    1       1       9       9       89      689     1689    1689    1689    178     179     ZMAAAA  FJLAAA  VVVVxx
+4102   7676    0       2       2       2       2       102     102     4102    4102    4       5       UBAAAA  GJLAAA  AAAAxx
+3287   7677    1       3       7       7       87      287     1287    3287    3287    174     175     LWAAAA  HJLAAA  HHHHxx
+5246   7678    0       2       6       6       46      246     1246    246     5246    92      93      UTAAAA  IJLAAA  OOOOxx
+7450   7679    0       2       0       10      50      450     1450    2450    7450    100     101     OAAAAA  JJLAAA  VVVVxx
+6548   7680    0       0       8       8       48      548     548     1548    6548    96      97      WRAAAA  KJLAAA  AAAAxx
+379    7681    1       3       9       19      79      379     379     379     379     158     159     POAAAA  LJLAAA  HHHHxx
+7435   7682    1       3       5       15      35      435     1435    2435    7435    70      71      ZZAAAA  MJLAAA  OOOOxx
+2041   7683    1       1       1       1       41      41      41      2041    2041    82      83      NAAAAA  NJLAAA  VVVVxx
+8462   7684    0       2       2       2       62      462     462     3462    8462    124     125     MNAAAA  OJLAAA  AAAAxx
+9076   7685    0       0       6       16      76      76      1076    4076    9076    152     153     CLAAAA  PJLAAA  HHHHxx
+761    7686    1       1       1       1       61      761     761     761     761     122     123     HDAAAA  QJLAAA  OOOOxx
+795    7687    1       3       5       15      95      795     795     795     795     190     191     PEAAAA  RJLAAA  VVVVxx
+1671   7688    1       3       1       11      71      671     1671    1671    1671    142     143     HMAAAA  SJLAAA  AAAAxx
+695    7689    1       3       5       15      95      695     695     695     695     190     191     TAAAAA  TJLAAA  HHHHxx
+4981   7690    1       1       1       1       81      981     981     4981    4981    162     163     PJAAAA  UJLAAA  OOOOxx
+1211   7691    1       3       1       11      11      211     1211    1211    1211    22      23      PUAAAA  VJLAAA  VVVVxx
+5914   7692    0       2       4       14      14      914     1914    914     5914    28      29      MTAAAA  WJLAAA  AAAAxx
+9356   7693    0       0       6       16      56      356     1356    4356    9356    112     113     WVAAAA  XJLAAA  HHHHxx
+1500   7694    0       0       0       0       0       500     1500    1500    1500    0       1       SFAAAA  YJLAAA  OOOOxx
+3353   7695    1       1       3       13      53      353     1353    3353    3353    106     107     ZYAAAA  ZJLAAA  VVVVxx
+1060   7696    0       0       0       0       60      60      1060    1060    1060    120     121     UOAAAA  AKLAAA  AAAAxx
+7910   7697    0       2       0       10      10      910     1910    2910    7910    20      21      GSAAAA  BKLAAA  HHHHxx
+1329   7698    1       1       9       9       29      329     1329    1329    1329    58      59      DZAAAA  CKLAAA  OOOOxx
+6011   7699    1       3       1       11      11      11      11      1011    6011    22      23      FXAAAA  DKLAAA  VVVVxx
+7146   7700    0       2       6       6       46      146     1146    2146    7146    92      93      WOAAAA  EKLAAA  AAAAxx
+4602   7701    0       2       2       2       2       602     602     4602    4602    4       5       AVAAAA  FKLAAA  HHHHxx
+6751   7702    1       3       1       11      51      751     751     1751    6751    102     103     RZAAAA  GKLAAA  OOOOxx
+2666   7703    0       2       6       6       66      666     666     2666    2666    132     133     OYAAAA  HKLAAA  VVVVxx
+2785   7704    1       1       5       5       85      785     785     2785    2785    170     171     DDAAAA  IKLAAA  AAAAxx
+5851   7705    1       3       1       11      51      851     1851    851     5851    102     103     BRAAAA  JKLAAA  HHHHxx
+2435   7706    1       3       5       15      35      435     435     2435    2435    70      71      RPAAAA  KKLAAA  OOOOxx
+7429   7707    1       1       9       9       29      429     1429    2429    7429    58      59      TZAAAA  LKLAAA  VVVVxx
+4241   7708    1       1       1       1       41      241     241     4241    4241    82      83      DHAAAA  MKLAAA  AAAAxx
+5691   7709    1       3       1       11      91      691     1691    691     5691    182     183     XKAAAA  NKLAAA  HHHHxx
+7731   7710    1       3       1       11      31      731     1731    2731    7731    62      63      JLAAAA  OKLAAA  OOOOxx
+249    7711    1       1       9       9       49      249     249     249     249     98      99      PJAAAA  PKLAAA  VVVVxx
+1731   7712    1       3       1       11      31      731     1731    1731    1731    62      63      POAAAA  QKLAAA  AAAAxx
+8716   7713    0       0       6       16      16      716     716     3716    8716    32      33      GXAAAA  RKLAAA  HHHHxx
+2670   7714    0       2       0       10      70      670     670     2670    2670    140     141     SYAAAA  SKLAAA  OOOOxx
+4654   7715    0       2       4       14      54      654     654     4654    4654    108     109     AXAAAA  TKLAAA  VVVVxx
+1027   7716    1       3       7       7       27      27      1027    1027    1027    54      55      NNAAAA  UKLAAA  AAAAxx
+1099   7717    1       3       9       19      99      99      1099    1099    1099    198     199     HQAAAA  VKLAAA  HHHHxx
+3617   7718    1       1       7       17      17      617     1617    3617    3617    34      35      DJAAAA  WKLAAA  OOOOxx
+4330   7719    0       2       0       10      30      330     330     4330    4330    60      61      OKAAAA  XKLAAA  VVVVxx
+9750   7720    0       2       0       10      50      750     1750    4750    9750    100     101     ALAAAA  YKLAAA  AAAAxx
+467    7721    1       3       7       7       67      467     467     467     467     134     135     ZRAAAA  ZKLAAA  HHHHxx
+8525   7722    1       1       5       5       25      525     525     3525    8525    50      51      XPAAAA  ALLAAA  OOOOxx
+5990   7723    0       2       0       10      90      990     1990    990     5990    180     181     KWAAAA  BLLAAA  VVVVxx
+4839   7724    1       3       9       19      39      839     839     4839    4839    78      79      DEAAAA  CLLAAA  AAAAxx
+9914   7725    0       2       4       14      14      914     1914    4914    9914    28      29      IRAAAA  DLLAAA  HHHHxx
+7047   7726    1       3       7       7       47      47      1047    2047    7047    94      95      BLAAAA  ELLAAA  OOOOxx
+874    7727    0       2       4       14      74      874     874     874     874     148     149     QHAAAA  FLLAAA  VVVVxx
+6061   7728    1       1       1       1       61      61      61      1061    6061    122     123     DZAAAA  GLLAAA  AAAAxx
+5491   7729    1       3       1       11      91      491     1491    491     5491    182     183     FDAAAA  HLLAAA  HHHHxx
+4344   7730    0       0       4       4       44      344     344     4344    4344    88      89      CLAAAA  ILLAAA  OOOOxx
+1281   7731    1       1       1       1       81      281     1281    1281    1281    162     163     HXAAAA  JLLAAA  VVVVxx
+3597   7732    1       1       7       17      97      597     1597    3597    3597    194     195     JIAAAA  KLLAAA  AAAAxx
+4992   7733    0       0       2       12      92      992     992     4992    4992    184     185     AKAAAA  LLLAAA  HHHHxx
+3849   7734    1       1       9       9       49      849     1849    3849    3849    98      99      BSAAAA  MLLAAA  OOOOxx
+2655   7735    1       3       5       15      55      655     655     2655    2655    110     111     DYAAAA  NLLAAA  VVVVxx
+147    7736    1       3       7       7       47      147     147     147     147     94      95      RFAAAA  OLLAAA  AAAAxx
+9110   7737    0       2       0       10      10      110     1110    4110    9110    20      21      KMAAAA  PLLAAA  HHHHxx
+1637   7738    1       1       7       17      37      637     1637    1637    1637    74      75      ZKAAAA  QLLAAA  OOOOxx
+9826   7739    0       2       6       6       26      826     1826    4826    9826    52      53      YNAAAA  RLLAAA  VVVVxx
+5957   7740    1       1       7       17      57      957     1957    957     5957    114     115     DVAAAA  SLLAAA  AAAAxx
+6932   7741    0       0       2       12      32      932     932     1932    6932    64      65      QGAAAA  TLLAAA  HHHHxx
+9684   7742    0       0       4       4       84      684     1684    4684    9684    168     169     MIAAAA  ULLAAA  OOOOxx
+4653   7743    1       1       3       13      53      653     653     4653    4653    106     107     ZWAAAA  VLLAAA  VVVVxx
+8065   7744    1       1       5       5       65      65      65      3065    8065    130     131     FYAAAA  WLLAAA  AAAAxx
+1202   7745    0       2       2       2       2       202     1202    1202    1202    4       5       GUAAAA  XLLAAA  HHHHxx
+9214   7746    0       2       4       14      14      214     1214    4214    9214    28      29      KQAAAA  YLLAAA  OOOOxx
+196    7747    0       0       6       16      96      196     196     196     196     192     193     OHAAAA  ZLLAAA  VVVVxx
+4486   7748    0       2       6       6       86      486     486     4486    4486    172     173     OQAAAA  AMLAAA  AAAAxx
+2585   7749    1       1       5       5       85      585     585     2585    2585    170     171     LVAAAA  BMLAAA  HHHHxx
+2464   7750    0       0       4       4       64      464     464     2464    2464    128     129     UQAAAA  CMLAAA  OOOOxx
+3467   7751    1       3       7       7       67      467     1467    3467    3467    134     135     JDAAAA  DMLAAA  VVVVxx
+9295   7752    1       3       5       15      95      295     1295    4295    9295    190     191     NTAAAA  EMLAAA  AAAAxx
+517    7753    1       1       7       17      17      517     517     517     517     34      35      XTAAAA  FMLAAA  HHHHxx
+6870   7754    0       2       0       10      70      870     870     1870    6870    140     141     GEAAAA  GMLAAA  OOOOxx
+5732   7755    0       0       2       12      32      732     1732    732     5732    64      65      MMAAAA  HMLAAA  VVVVxx
+9376   7756    0       0       6       16      76      376     1376    4376    9376    152     153     QWAAAA  IMLAAA  AAAAxx
+838    7757    0       2       8       18      38      838     838     838     838     76      77      GGAAAA  JMLAAA  HHHHxx
+9254   7758    0       2       4       14      54      254     1254    4254    9254    108     109     YRAAAA  KMLAAA  OOOOxx
+8879   7759    1       3       9       19      79      879     879     3879    8879    158     159     NDAAAA  LMLAAA  VVVVxx
+6281   7760    1       1       1       1       81      281     281     1281    6281    162     163     PHAAAA  MMLAAA  AAAAxx
+8216   7761    0       0       6       16      16      216     216     3216    8216    32      33      AEAAAA  NMLAAA  HHHHxx
+9213   7762    1       1       3       13      13      213     1213    4213    9213    26      27      JQAAAA  OMLAAA  OOOOxx
+7234   7763    0       2       4       14      34      234     1234    2234    7234    68      69      GSAAAA  PMLAAA  VVVVxx
+5692   7764    0       0       2       12      92      692     1692    692     5692    184     185     YKAAAA  QMLAAA  AAAAxx
+693    7765    1       1       3       13      93      693     693     693     693     186     187     RAAAAA  RMLAAA  HHHHxx
+9050   7766    0       2       0       10      50      50      1050    4050    9050    100     101     CKAAAA  SMLAAA  OOOOxx
+3623   7767    1       3       3       3       23      623     1623    3623    3623    46      47      JJAAAA  TMLAAA  VVVVxx
+2130   7768    0       2       0       10      30      130     130     2130    2130    60      61      YDAAAA  UMLAAA  AAAAxx
+2514   7769    0       2       4       14      14      514     514     2514    2514    28      29      SSAAAA  VMLAAA  HHHHxx
+1812   7770    0       0       2       12      12      812     1812    1812    1812    24      25      SRAAAA  WMLAAA  OOOOxx
+9037   7771    1       1       7       17      37      37      1037    4037    9037    74      75      PJAAAA  XMLAAA  VVVVxx
+5054   7772    0       2       4       14      54      54      1054    54      5054    108     109     KMAAAA  YMLAAA  AAAAxx
+7801   7773    1       1       1       1       1       801     1801    2801    7801    2       3       BOAAAA  ZMLAAA  HHHHxx
+7939   7774    1       3       9       19      39      939     1939    2939    7939    78      79      JTAAAA  ANLAAA  OOOOxx
+7374   7775    0       2       4       14      74      374     1374    2374    7374    148     149     QXAAAA  BNLAAA  VVVVxx
+1058   7776    0       2       8       18      58      58      1058    1058    1058    116     117     SOAAAA  CNLAAA  AAAAxx
+1972   7777    0       0       2       12      72      972     1972    1972    1972    144     145     WXAAAA  DNLAAA  HHHHxx
+3741   7778    1       1       1       1       41      741     1741    3741    3741    82      83      XNAAAA  ENLAAA  OOOOxx
+2227   7779    1       3       7       7       27      227     227     2227    2227    54      55      RHAAAA  FNLAAA  VVVVxx
+304    7780    0       0       4       4       4       304     304     304     304     8       9       SLAAAA  GNLAAA  AAAAxx
+4914   7781    0       2       4       14      14      914     914     4914    4914    28      29      AHAAAA  HNLAAA  HHHHxx
+2428   7782    0       0       8       8       28      428     428     2428    2428    56      57      KPAAAA  INLAAA  OOOOxx
+6660   7783    0       0       0       0       60      660     660     1660    6660    120     121     EWAAAA  JNLAAA  VVVVxx
+2676   7784    0       0       6       16      76      676     676     2676    2676    152     153     YYAAAA  KNLAAA  AAAAxx
+2454   7785    0       2       4       14      54      454     454     2454    2454    108     109     KQAAAA  LNLAAA  HHHHxx
+3798   7786    0       2       8       18      98      798     1798    3798    3798    196     197     CQAAAA  MNLAAA  OOOOxx
+1341   7787    1       1       1       1       41      341     1341    1341    1341    82      83      PZAAAA  NNLAAA  VVVVxx
+1611   7788    1       3       1       11      11      611     1611    1611    1611    22      23      ZJAAAA  ONLAAA  AAAAxx
+2681   7789    1       1       1       1       81      681     681     2681    2681    162     163     DZAAAA  PNLAAA  HHHHxx
+7292   7790    0       0       2       12      92      292     1292    2292    7292    184     185     MUAAAA  QNLAAA  OOOOxx
+7775   7791    1       3       5       15      75      775     1775    2775    7775    150     151     BNAAAA  RNLAAA  VVVVxx
+794    7792    0       2       4       14      94      794     794     794     794     188     189     OEAAAA  SNLAAA  AAAAxx
+8709   7793    1       1       9       9       9       709     709     3709    8709    18      19      ZWAAAA  TNLAAA  HHHHxx
+1901   7794    1       1       1       1       1       901     1901    1901    1901    2       3       DVAAAA  UNLAAA  OOOOxx
+3089   7795    1       1       9       9       89      89      1089    3089    3089    178     179     VOAAAA  VNLAAA  VVVVxx
+7797   7796    1       1       7       17      97      797     1797    2797    7797    194     195     XNAAAA  WNLAAA  AAAAxx
+6070   7797    0       2       0       10      70      70      70      1070    6070    140     141     MZAAAA  XNLAAA  HHHHxx
+2191   7798    1       3       1       11      91      191     191     2191    2191    182     183     HGAAAA  YNLAAA  OOOOxx
+3497   7799    1       1       7       17      97      497     1497    3497    3497    194     195     NEAAAA  ZNLAAA  VVVVxx
+8302   7800    0       2       2       2       2       302     302     3302    8302    4       5       IHAAAA  AOLAAA  AAAAxx
+4365   7801    1       1       5       5       65      365     365     4365    4365    130     131     XLAAAA  BOLAAA  HHHHxx
+3588   7802    0       0       8       8       88      588     1588    3588    3588    176     177     AIAAAA  COLAAA  OOOOxx
+8292   7803    0       0       2       12      92      292     292     3292    8292    184     185     YGAAAA  DOLAAA  VVVVxx
+4696   7804    0       0       6       16      96      696     696     4696    4696    192     193     QYAAAA  EOLAAA  AAAAxx
+5641   7805    1       1       1       1       41      641     1641    641     5641    82      83      ZIAAAA  FOLAAA  HHHHxx
+9386   7806    0       2       6       6       86      386     1386    4386    9386    172     173     AXAAAA  GOLAAA  OOOOxx
+507    7807    1       3       7       7       7       507     507     507     507     14      15      NTAAAA  HOLAAA  VVVVxx
+7201   7808    1       1       1       1       1       201     1201    2201    7201    2       3       ZQAAAA  IOLAAA  AAAAxx
+7785   7809    1       1       5       5       85      785     1785    2785    7785    170     171     LNAAAA  JOLAAA  HHHHxx
+463    7810    1       3       3       3       63      463     463     463     463     126     127     VRAAAA  KOLAAA  OOOOxx
+6656   7811    0       0       6       16      56      656     656     1656    6656    112     113     AWAAAA  LOLAAA  VVVVxx
+807    7812    1       3       7       7       7       807     807     807     807     14      15      BFAAAA  MOLAAA  AAAAxx
+7278   7813    0       2       8       18      78      278     1278    2278    7278    156     157     YTAAAA  NOLAAA  HHHHxx
+6237   7814    1       1       7       17      37      237     237     1237    6237    74      75      XFAAAA  OOLAAA  OOOOxx
+7671   7815    1       3       1       11      71      671     1671    2671    7671    142     143     BJAAAA  POLAAA  VVVVxx
+2235   7816    1       3       5       15      35      235     235     2235    2235    70      71      ZHAAAA  QOLAAA  AAAAxx
+4042   7817    0       2       2       2       42      42      42      4042    4042    84      85      MZAAAA  ROLAAA  HHHHxx
+5273   7818    1       1       3       13      73      273     1273    273     5273    146     147     VUAAAA  SOLAAA  OOOOxx
+7557   7819    1       1       7       17      57      557     1557    2557    7557    114     115     REAAAA  TOLAAA  VVVVxx
+4007   7820    1       3       7       7       7       7       7       4007    4007    14      15      DYAAAA  UOLAAA  AAAAxx
+1428   7821    0       0       8       8       28      428     1428    1428    1428    56      57      YCAAAA  VOLAAA  HHHHxx
+9739   7822    1       3       9       19      39      739     1739    4739    9739    78      79      PKAAAA  WOLAAA  OOOOxx
+7836   7823    0       0       6       16      36      836     1836    2836    7836    72      73      KPAAAA  XOLAAA  VVVVxx
+1777   7824    1       1       7       17      77      777     1777    1777    1777    154     155     JQAAAA  YOLAAA  AAAAxx
+5192   7825    0       0       2       12      92      192     1192    192     5192    184     185     SRAAAA  ZOLAAA  HHHHxx
+7236   7826    0       0       6       16      36      236     1236    2236    7236    72      73      ISAAAA  APLAAA  OOOOxx
+1623   7827    1       3       3       3       23      623     1623    1623    1623    46      47      LKAAAA  BPLAAA  VVVVxx
+8288   7828    0       0       8       8       88      288     288     3288    8288    176     177     UGAAAA  CPLAAA  AAAAxx
+2827   7829    1       3       7       7       27      827     827     2827    2827    54      55      TEAAAA  DPLAAA  HHHHxx
+458    7830    0       2       8       18      58      458     458     458     458     116     117     QRAAAA  EPLAAA  OOOOxx
+1818   7831    0       2       8       18      18      818     1818    1818    1818    36      37      YRAAAA  FPLAAA  VVVVxx
+6837   7832    1       1       7       17      37      837     837     1837    6837    74      75      ZCAAAA  GPLAAA  AAAAxx
+7825   7833    1       1       5       5       25      825     1825    2825    7825    50      51      ZOAAAA  HPLAAA  HHHHxx
+9146   7834    0       2       6       6       46      146     1146    4146    9146    92      93      UNAAAA  IPLAAA  OOOOxx
+8451   7835    1       3       1       11      51      451     451     3451    8451    102     103     BNAAAA  JPLAAA  VVVVxx
+6438   7836    0       2       8       18      38      438     438     1438    6438    76      77      QNAAAA  KPLAAA  AAAAxx
+4020   7837    0       0       0       0       20      20      20      4020    4020    40      41      QYAAAA  LPLAAA  HHHHxx
+4068   7838    0       0       8       8       68      68      68      4068    4068    136     137     MAAAAA  MPLAAA  OOOOxx
+2411   7839    1       3       1       11      11      411     411     2411    2411    22      23      TOAAAA  NPLAAA  VVVVxx
+6222   7840    0       2       2       2       22      222     222     1222    6222    44      45      IFAAAA  OPLAAA  AAAAxx
+3164   7841    0       0       4       4       64      164     1164    3164    3164    128     129     SRAAAA  PPLAAA  HHHHxx
+311    7842    1       3       1       11      11      311     311     311     311     22      23      ZLAAAA  QPLAAA  OOOOxx
+5683   7843    1       3       3       3       83      683     1683    683     5683    166     167     PKAAAA  RPLAAA  VVVVxx
+3993   7844    1       1       3       13      93      993     1993    3993    3993    186     187     PXAAAA  SPLAAA  AAAAxx
+9897   7845    1       1       7       17      97      897     1897    4897    9897    194     195     RQAAAA  TPLAAA  HHHHxx
+6609   7846    1       1       9       9       9       609     609     1609    6609    18      19      FUAAAA  UPLAAA  OOOOxx
+1362   7847    0       2       2       2       62      362     1362    1362    1362    124     125     KAAAAA  VPLAAA  VVVVxx
+3918   7848    0       2       8       18      18      918     1918    3918    3918    36      37      SUAAAA  WPLAAA  AAAAxx
+7376   7849    0       0       6       16      76      376     1376    2376    7376    152     153     SXAAAA  XPLAAA  HHHHxx
+6996   7850    0       0       6       16      96      996     996     1996    6996    192     193     CJAAAA  YPLAAA  OOOOxx
+9567   7851    1       3       7       7       67      567     1567    4567    9567    134     135     ZDAAAA  ZPLAAA  VVVVxx
+7525   7852    1       1       5       5       25      525     1525    2525    7525    50      51      LDAAAA  AQLAAA  AAAAxx
+9069   7853    1       1       9       9       69      69      1069    4069    9069    138     139     VKAAAA  BQLAAA  HHHHxx
+9999   7854    1       3       9       19      99      999     1999    4999    9999    198     199     PUAAAA  CQLAAA  OOOOxx
+9237   7855    1       1       7       17      37      237     1237    4237    9237    74      75      HRAAAA  DQLAAA  VVVVxx
+8441   7856    1       1       1       1       41      441     441     3441    8441    82      83      RMAAAA  EQLAAA  AAAAxx
+6769   7857    1       1       9       9       69      769     769     1769    6769    138     139     JAAAAA  FQLAAA  HHHHxx
+6073   7858    1       1       3       13      73      73      73      1073    6073    146     147     PZAAAA  GQLAAA  OOOOxx
+1091   7859    1       3       1       11      91      91      1091    1091    1091    182     183     ZPAAAA  HQLAAA  VVVVxx
+9886   7860    0       2       6       6       86      886     1886    4886    9886    172     173     GQAAAA  IQLAAA  AAAAxx
+3971   7861    1       3       1       11      71      971     1971    3971    3971    142     143     TWAAAA  JQLAAA  HHHHxx
+4621   7862    1       1       1       1       21      621     621     4621    4621    42      43      TVAAAA  KQLAAA  OOOOxx
+3120   7863    0       0       0       0       20      120     1120    3120    3120    40      41      AQAAAA  LQLAAA  VVVVxx
+9773   7864    1       1       3       13      73      773     1773    4773    9773    146     147     XLAAAA  MQLAAA  AAAAxx
+8712   7865    0       0       2       12      12      712     712     3712    8712    24      25      CXAAAA  NQLAAA  HHHHxx
+801    7866    1       1       1       1       1       801     801     801     801     2       3       VEAAAA  OQLAAA  OOOOxx
+9478   7867    0       2       8       18      78      478     1478    4478    9478    156     157     OAAAAA  PQLAAA  VVVVxx
+3466   7868    0       2       6       6       66      466     1466    3466    3466    132     133     IDAAAA  QQLAAA  AAAAxx
+6326   7869    0       2       6       6       26      326     326     1326    6326    52      53      IJAAAA  RQLAAA  HHHHxx
+1723   7870    1       3       3       3       23      723     1723    1723    1723    46      47      HOAAAA  SQLAAA  OOOOxx
+4978   7871    0       2       8       18      78      978     978     4978    4978    156     157     MJAAAA  TQLAAA  VVVVxx
+2311   7872    1       3       1       11      11      311     311     2311    2311    22      23      XKAAAA  UQLAAA  AAAAxx
+9532   7873    0       0       2       12      32      532     1532    4532    9532    64      65      QCAAAA  VQLAAA  HHHHxx
+3680   7874    0       0       0       0       80      680     1680    3680    3680    160     161     OLAAAA  WQLAAA  OOOOxx
+1244   7875    0       0       4       4       44      244     1244    1244    1244    88      89      WVAAAA  XQLAAA  VVVVxx
+3821   7876    1       1       1       1       21      821     1821    3821    3821    42      43      ZQAAAA  YQLAAA  AAAAxx
+9586   7877    0       2       6       6       86      586     1586    4586    9586    172     173     SEAAAA  ZQLAAA  HHHHxx
+3894   7878    0       2       4       14      94      894     1894    3894    3894    188     189     UTAAAA  ARLAAA  OOOOxx
+6169   7879    1       1       9       9       69      169     169     1169    6169    138     139     HDAAAA  BRLAAA  VVVVxx
+5919   7880    1       3       9       19      19      919     1919    919     5919    38      39      RTAAAA  CRLAAA  AAAAxx
+4187   7881    1       3       7       7       87      187     187     4187    4187    174     175     BFAAAA  DRLAAA  HHHHxx
+5477   7882    1       1       7       17      77      477     1477    477     5477    154     155     RCAAAA  ERLAAA  OOOOxx
+2806   7883    0       2       6       6       6       806     806     2806    2806    12      13      YDAAAA  FRLAAA  VVVVxx
+8158   7884    0       2       8       18      58      158     158     3158    8158    116     117     UBAAAA  GRLAAA  AAAAxx
+7130   7885    0       2       0       10      30      130     1130    2130    7130    60      61      GOAAAA  HRLAAA  HHHHxx
+7133   7886    1       1       3       13      33      133     1133    2133    7133    66      67      JOAAAA  IRLAAA  OOOOxx
+6033   7887    1       1       3       13      33      33      33      1033    6033    66      67      BYAAAA  JRLAAA  VVVVxx
+2415   7888    1       3       5       15      15      415     415     2415    2415    30      31      XOAAAA  KRLAAA  AAAAxx
+8091   7889    1       3       1       11      91      91      91      3091    8091    182     183     FZAAAA  LRLAAA  HHHHxx
+8347   7890    1       3       7       7       47      347     347     3347    8347    94      95      BJAAAA  MRLAAA  OOOOxx
+7879   7891    1       3       9       19      79      879     1879    2879    7879    158     159     BRAAAA  NRLAAA  VVVVxx
+9360   7892    0       0       0       0       60      360     1360    4360    9360    120     121     AWAAAA  ORLAAA  AAAAxx
+3369   7893    1       1       9       9       69      369     1369    3369    3369    138     139     PZAAAA  PRLAAA  HHHHxx
+8536   7894    0       0       6       16      36      536     536     3536    8536    72      73      IQAAAA  QRLAAA  OOOOxx
+8628   7895    0       0       8       8       28      628     628     3628    8628    56      57      WTAAAA  RRLAAA  VVVVxx
+1580   7896    0       0       0       0       80      580     1580    1580    1580    160     161     UIAAAA  SRLAAA  AAAAxx
+705    7897    1       1       5       5       5       705     705     705     705     10      11      DBAAAA  TRLAAA  HHHHxx
+4650   7898    0       2       0       10      50      650     650     4650    4650    100     101     WWAAAA  URLAAA  OOOOxx
+9165   7899    1       1       5       5       65      165     1165    4165    9165    130     131     NOAAAA  VRLAAA  VVVVxx
+4820   7900    0       0       0       0       20      820     820     4820    4820    40      41      KDAAAA  WRLAAA  AAAAxx
+3538   7901    0       2       8       18      38      538     1538    3538    3538    76      77      CGAAAA  XRLAAA  HHHHxx
+9947   7902    1       3       7       7       47      947     1947    4947    9947    94      95      PSAAAA  YRLAAA  OOOOxx
+4954   7903    0       2       4       14      54      954     954     4954    4954    108     109     OIAAAA  ZRLAAA  VVVVxx
+1104   7904    0       0       4       4       4       104     1104    1104    1104    8       9       MQAAAA  ASLAAA  AAAAxx
+8455   7905    1       3       5       15      55      455     455     3455    8455    110     111     FNAAAA  BSLAAA  HHHHxx
+8307   7906    1       3       7       7       7       307     307     3307    8307    14      15      NHAAAA  CSLAAA  OOOOxx
+9203   7907    1       3       3       3       3       203     1203    4203    9203    6       7       ZPAAAA  DSLAAA  VVVVxx
+7565   7908    1       1       5       5       65      565     1565    2565    7565    130     131     ZEAAAA  ESLAAA  AAAAxx
+7745   7909    1       1       5       5       45      745     1745    2745    7745    90      91      XLAAAA  FSLAAA  HHHHxx
+1787   7910    1       3       7       7       87      787     1787    1787    1787    174     175     TQAAAA  GSLAAA  OOOOxx
+4861   7911    1       1       1       1       61      861     861     4861    4861    122     123     ZEAAAA  HSLAAA  VVVVxx
+5183   7912    1       3       3       3       83      183     1183    183     5183    166     167     JRAAAA  ISLAAA  AAAAxx
+529    7913    1       1       9       9       29      529     529     529     529     58      59      JUAAAA  JSLAAA  HHHHxx
+2470   7914    0       2       0       10      70      470     470     2470    2470    140     141     ARAAAA  KSLAAA  OOOOxx
+1267   7915    1       3       7       7       67      267     1267    1267    1267    134     135     TWAAAA  LSLAAA  VVVVxx
+2059   7916    1       3       9       19      59      59      59      2059    2059    118     119     FBAAAA  MSLAAA  AAAAxx
+1862   7917    0       2       2       2       62      862     1862    1862    1862    124     125     QTAAAA  NSLAAA  HHHHxx
+7382   7918    0       2       2       2       82      382     1382    2382    7382    164     165     YXAAAA  OSLAAA  OOOOxx
+4796   7919    0       0       6       16      96      796     796     4796    4796    192     193     MCAAAA  PSLAAA  VVVVxx
+2331   7920    1       3       1       11      31      331     331     2331    2331    62      63      RLAAAA  QSLAAA  AAAAxx
+8870   7921    0       2       0       10      70      870     870     3870    8870    140     141     EDAAAA  RSLAAA  HHHHxx
+9581   7922    1       1       1       1       81      581     1581    4581    9581    162     163     NEAAAA  SSLAAA  OOOOxx
+9063   7923    1       3       3       3       63      63      1063    4063    9063    126     127     PKAAAA  TSLAAA  VVVVxx
+2192   7924    0       0       2       12      92      192     192     2192    2192    184     185     IGAAAA  USLAAA  AAAAxx
+6466   7925    0       2       6       6       66      466     466     1466    6466    132     133     SOAAAA  VSLAAA  HHHHxx
+7096   7926    0       0       6       16      96      96      1096    2096    7096    192     193     YMAAAA  WSLAAA  OOOOxx
+6257   7927    1       1       7       17      57      257     257     1257    6257    114     115     RGAAAA  XSLAAA  VVVVxx
+7009   7928    1       1       9       9       9       9       1009    2009    7009    18      19      PJAAAA  YSLAAA  AAAAxx
+8136   7929    0       0       6       16      36      136     136     3136    8136    72      73      YAAAAA  ZSLAAA  HHHHxx
+1854   7930    0       2       4       14      54      854     1854    1854    1854    108     109     ITAAAA  ATLAAA  OOOOxx
+3644   7931    0       0       4       4       44      644     1644    3644    3644    88      89      EKAAAA  BTLAAA  VVVVxx
+4437   7932    1       1       7       17      37      437     437     4437    4437    74      75      ROAAAA  CTLAAA  AAAAxx
+7209   7933    1       1       9       9       9       209     1209    2209    7209    18      19      HRAAAA  DTLAAA  HHHHxx
+1516   7934    0       0       6       16      16      516     1516    1516    1516    32      33      IGAAAA  ETLAAA  OOOOxx
+822    7935    0       2       2       2       22      822     822     822     822     44      45      QFAAAA  FTLAAA  VVVVxx
+1778   7936    0       2       8       18      78      778     1778    1778    1778    156     157     KQAAAA  GTLAAA  AAAAxx
+8161   7937    1       1       1       1       61      161     161     3161    8161    122     123     XBAAAA  HTLAAA  HHHHxx
+6030   7938    0       2       0       10      30      30      30      1030    6030    60      61      YXAAAA  ITLAAA  OOOOxx
+3515   7939    1       3       5       15      15      515     1515    3515    3515    30      31      FFAAAA  JTLAAA  VVVVxx
+1702   7940    0       2       2       2       2       702     1702    1702    1702    4       5       MNAAAA  KTLAAA  AAAAxx
+2671   7941    1       3       1       11      71      671     671     2671    2671    142     143     TYAAAA  LTLAAA  HHHHxx
+7623   7942    1       3       3       3       23      623     1623    2623    7623    46      47      FHAAAA  MTLAAA  OOOOxx
+9828   7943    0       0       8       8       28      828     1828    4828    9828    56      57      AOAAAA  NTLAAA  VVVVxx
+1888   7944    0       0       8       8       88      888     1888    1888    1888    176     177     QUAAAA  OTLAAA  AAAAxx
+4520   7945    0       0       0       0       20      520     520     4520    4520    40      41      WRAAAA  PTLAAA  HHHHxx
+3461   7946    1       1       1       1       61      461     1461    3461    3461    122     123     DDAAAA  QTLAAA  OOOOxx
+1488   7947    0       0       8       8       88      488     1488    1488    1488    176     177     GFAAAA  RTLAAA  VVVVxx
+7753   7948    1       1       3       13      53      753     1753    2753    7753    106     107     FMAAAA  STLAAA  AAAAxx
+5525   7949    1       1       5       5       25      525     1525    525     5525    50      51      NEAAAA  TTLAAA  HHHHxx
+5220   7950    0       0       0       0       20      220     1220    220     5220    40      41      USAAAA  UTLAAA  OOOOxx
+305    7951    1       1       5       5       5       305     305     305     305     10      11      TLAAAA  VTLAAA  VVVVxx
+7883   7952    1       3       3       3       83      883     1883    2883    7883    166     167     FRAAAA  WTLAAA  AAAAxx
+1222   7953    0       2       2       2       22      222     1222    1222    1222    44      45      AVAAAA  XTLAAA  HHHHxx
+8552   7954    0       0       2       12      52      552     552     3552    8552    104     105     YQAAAA  YTLAAA  OOOOxx
+6097   7955    1       1       7       17      97      97      97      1097    6097    194     195     NAAAAA  ZTLAAA  VVVVxx
+2298   7956    0       2       8       18      98      298     298     2298    2298    196     197     KKAAAA  AULAAA  AAAAxx
+956    7957    0       0       6       16      56      956     956     956     956     112     113     UKAAAA  BULAAA  HHHHxx
+9351   7958    1       3       1       11      51      351     1351    4351    9351    102     103     RVAAAA  CULAAA  OOOOxx
+6669   7959    1       1       9       9       69      669     669     1669    6669    138     139     NWAAAA  DULAAA  VVVVxx
+9383   7960    1       3       3       3       83      383     1383    4383    9383    166     167     XWAAAA  EULAAA  AAAAxx
+1607   7961    1       3       7       7       7       607     1607    1607    1607    14      15      VJAAAA  FULAAA  HHHHxx
+812    7962    0       0       2       12      12      812     812     812     812     24      25      GFAAAA  GULAAA  OOOOxx
+2109   7963    1       1       9       9       9       109     109     2109    2109    18      19      DDAAAA  HULAAA  VVVVxx
+207    7964    1       3       7       7       7       207     207     207     207     14      15      ZHAAAA  IULAAA  AAAAxx
+7124   7965    0       0       4       4       24      124     1124    2124    7124    48      49      AOAAAA  JULAAA  HHHHxx
+9333   7966    1       1       3       13      33      333     1333    4333    9333    66      67      ZUAAAA  KULAAA  OOOOxx
+3262   7967    0       2       2       2       62      262     1262    3262    3262    124     125     MVAAAA  LULAAA  VVVVxx
+1070   7968    0       2       0       10      70      70      1070    1070    1070    140     141     EPAAAA  MULAAA  AAAAxx
+7579   7969    1       3       9       19      79      579     1579    2579    7579    158     159     NFAAAA  NULAAA  HHHHxx
+9283   7970    1       3       3       3       83      283     1283    4283    9283    166     167     BTAAAA  OULAAA  OOOOxx
+4917   7971    1       1       7       17      17      917     917     4917    4917    34      35      DHAAAA  PULAAA  VVVVxx
+1328   7972    0       0       8       8       28      328     1328    1328    1328    56      57      CZAAAA  QULAAA  AAAAxx
+3042   7973    0       2       2       2       42      42      1042    3042    3042    84      85      ANAAAA  RULAAA  HHHHxx
+8352   7974    0       0       2       12      52      352     352     3352    8352    104     105     GJAAAA  SULAAA  OOOOxx
+2710   7975    0       2       0       10      10      710     710     2710    2710    20      21      GAAAAA  TULAAA  VVVVxx
+3330   7976    0       2       0       10      30      330     1330    3330    3330    60      61      CYAAAA  UULAAA  AAAAxx
+2822   7977    0       2       2       2       22      822     822     2822    2822    44      45      OEAAAA  VULAAA  HHHHxx
+5627   7978    1       3       7       7       27      627     1627    627     5627    54      55      LIAAAA  WULAAA  OOOOxx
+7848   7979    0       0       8       8       48      848     1848    2848    7848    96      97      WPAAAA  XULAAA  VVVVxx
+7384   7980    0       0       4       4       84      384     1384    2384    7384    168     169     AYAAAA  YULAAA  AAAAxx
+727    7981    1       3       7       7       27      727     727     727     727     54      55      ZBAAAA  ZULAAA  HHHHxx
+9926   7982    0       2       6       6       26      926     1926    4926    9926    52      53      URAAAA  AVLAAA  OOOOxx
+2647   7983    1       3       7       7       47      647     647     2647    2647    94      95      VXAAAA  BVLAAA  VVVVxx
+6416   7984    0       0       6       16      16      416     416     1416    6416    32      33      UMAAAA  CVLAAA  AAAAxx
+8751   7985    1       3       1       11      51      751     751     3751    8751    102     103     PYAAAA  DVLAAA  HHHHxx
+6515   7986    1       3       5       15      15      515     515     1515    6515    30      31      PQAAAA  EVLAAA  OOOOxx
+2472   7987    0       0       2       12      72      472     472     2472    2472    144     145     CRAAAA  FVLAAA  VVVVxx
+7205   7988    1       1       5       5       5       205     1205    2205    7205    10      11      DRAAAA  GVLAAA  AAAAxx
+9654   7989    0       2       4       14      54      654     1654    4654    9654    108     109     IHAAAA  HVLAAA  HHHHxx
+5646   7990    0       2       6       6       46      646     1646    646     5646    92      93      EJAAAA  IVLAAA  OOOOxx
+4217   7991    1       1       7       17      17      217     217     4217    4217    34      35      FGAAAA  JVLAAA  VVVVxx
+4484   7992    0       0       4       4       84      484     484     4484    4484    168     169     MQAAAA  KVLAAA  AAAAxx
+6654   7993    0       2       4       14      54      654     654     1654    6654    108     109     YVAAAA  LVLAAA  HHHHxx
+4876   7994    0       0       6       16      76      876     876     4876    4876    152     153     OFAAAA  MVLAAA  OOOOxx
+9690   7995    0       2       0       10      90      690     1690    4690    9690    180     181     SIAAAA  NVLAAA  VVVVxx
+2453   7996    1       1       3       13      53      453     453     2453    2453    106     107     JQAAAA  OVLAAA  AAAAxx
+829    7997    1       1       9       9       29      829     829     829     829     58      59      XFAAAA  PVLAAA  HHHHxx
+2547   7998    1       3       7       7       47      547     547     2547    2547    94      95      ZTAAAA  QVLAAA  OOOOxx
+9726   7999    0       2       6       6       26      726     1726    4726    9726    52      53      CKAAAA  RVLAAA  VVVVxx
+9267   8000    1       3       7       7       67      267     1267    4267    9267    134     135     LSAAAA  SVLAAA  AAAAxx
+7448   8001    0       0       8       8       48      448     1448    2448    7448    96      97      MAAAAA  TVLAAA  HHHHxx
+610    8002    0       2       0       10      10      610     610     610     610     20      21      MXAAAA  UVLAAA  OOOOxx
+2791   8003    1       3       1       11      91      791     791     2791    2791    182     183     JDAAAA  VVLAAA  VVVVxx
+3651   8004    1       3       1       11      51      651     1651    3651    3651    102     103     LKAAAA  WVLAAA  AAAAxx
+5206   8005    0       2       6       6       6       206     1206    206     5206    12      13      GSAAAA  XVLAAA  HHHHxx
+8774   8006    0       2       4       14      74      774     774     3774    8774    148     149     MZAAAA  YVLAAA  OOOOxx
+4753   8007    1       1       3       13      53      753     753     4753    4753    106     107     VAAAAA  ZVLAAA  VVVVxx
+4755   8008    1       3       5       15      55      755     755     4755    4755    110     111     XAAAAA  AWLAAA  AAAAxx
+686    8009    0       2       6       6       86      686     686     686     686     172     173     KAAAAA  BWLAAA  HHHHxx
+8281   8010    1       1       1       1       81      281     281     3281    8281    162     163     NGAAAA  CWLAAA  OOOOxx
+2058   8011    0       2       8       18      58      58      58      2058    2058    116     117     EBAAAA  DWLAAA  VVVVxx
+8900   8012    0       0       0       0       0       900     900     3900    8900    0       1       IEAAAA  EWLAAA  AAAAxx
+8588   8013    0       0       8       8       88      588     588     3588    8588    176     177     ISAAAA  FWLAAA  HHHHxx
+2904   8014    0       0       4       4       4       904     904     2904    2904    8       9       SHAAAA  GWLAAA  OOOOxx
+8917   8015    1       1       7       17      17      917     917     3917    8917    34      35      ZEAAAA  HWLAAA  VVVVxx
+9026   8016    0       2       6       6       26      26      1026    4026    9026    52      53      EJAAAA  IWLAAA  AAAAxx
+2416   8017    0       0       6       16      16      416     416     2416    2416    32      33      YOAAAA  JWLAAA  HHHHxx
+1053   8018    1       1       3       13      53      53      1053    1053    1053    106     107     NOAAAA  KWLAAA  OOOOxx
+7141   8019    1       1       1       1       41      141     1141    2141    7141    82      83      ROAAAA  LWLAAA  VVVVxx
+9771   8020    1       3       1       11      71      771     1771    4771    9771    142     143     VLAAAA  MWLAAA  AAAAxx
+2774   8021    0       2       4       14      74      774     774     2774    2774    148     149     SCAAAA  NWLAAA  HHHHxx
+3213   8022    1       1       3       13      13      213     1213    3213    3213    26      27      PTAAAA  OWLAAA  OOOOxx
+5694   8023    0       2       4       14      94      694     1694    694     5694    188     189     ALAAAA  PWLAAA  VVVVxx
+6631   8024    1       3       1       11      31      631     631     1631    6631    62      63      BVAAAA  QWLAAA  AAAAxx
+6638   8025    0       2       8       18      38      638     638     1638    6638    76      77      IVAAAA  RWLAAA  HHHHxx
+7407   8026    1       3       7       7       7       407     1407    2407    7407    14      15      XYAAAA  SWLAAA  OOOOxx
+8972   8027    0       0       2       12      72      972     972     3972    8972    144     145     CHAAAA  TWLAAA  VVVVxx
+2202   8028    0       2       2       2       2       202     202     2202    2202    4       5       SGAAAA  UWLAAA  AAAAxx
+6135   8029    1       3       5       15      35      135     135     1135    6135    70      71      ZBAAAA  VWLAAA  HHHHxx
+5043   8030    1       3       3       3       43      43      1043    43      5043    86      87      ZLAAAA  WWLAAA  OOOOxx
+5163   8031    1       3       3       3       63      163     1163    163     5163    126     127     PQAAAA  XWLAAA  VVVVxx
+1191   8032    1       3       1       11      91      191     1191    1191    1191    182     183     VTAAAA  YWLAAA  AAAAxx
+6576   8033    0       0       6       16      76      576     576     1576    6576    152     153     YSAAAA  ZWLAAA  HHHHxx
+3455   8034    1       3       5       15      55      455     1455    3455    3455    110     111     XCAAAA  AXLAAA  OOOOxx
+3688   8035    0       0       8       8       88      688     1688    3688    3688    176     177     WLAAAA  BXLAAA  VVVVxx
+4982   8036    0       2       2       2       82      982     982     4982    4982    164     165     QJAAAA  CXLAAA  AAAAxx
+4180   8037    0       0       0       0       80      180     180     4180    4180    160     161     UEAAAA  DXLAAA  HHHHxx
+4708   8038    0       0       8       8       8       708     708     4708    4708    16      17      CZAAAA  EXLAAA  OOOOxx
+1241   8039    1       1       1       1       41      241     1241    1241    1241    82      83      TVAAAA  FXLAAA  VVVVxx
+4921   8040    1       1       1       1       21      921     921     4921    4921    42      43      HHAAAA  GXLAAA  AAAAxx
+3197   8041    1       1       7       17      97      197     1197    3197    3197    194     195     ZSAAAA  HXLAAA  HHHHxx
+8225   8042    1       1       5       5       25      225     225     3225    8225    50      51      JEAAAA  IXLAAA  OOOOxx
+5913   8043    1       1       3       13      13      913     1913    913     5913    26      27      LTAAAA  JXLAAA  VVVVxx
+6387   8044    1       3       7       7       87      387     387     1387    6387    174     175     RLAAAA  KXLAAA  AAAAxx
+2706   8045    0       2       6       6       6       706     706     2706    2706    12      13      CAAAAA  LXLAAA  HHHHxx
+1461   8046    1       1       1       1       61      461     1461    1461    1461    122     123     FEAAAA  MXLAAA  OOOOxx
+7646   8047    0       2       6       6       46      646     1646    2646    7646    92      93      CIAAAA  NXLAAA  VVVVxx
+8066   8048    0       2       6       6       66      66      66      3066    8066    132     133     GYAAAA  OXLAAA  AAAAxx
+4171   8049    1       3       1       11      71      171     171     4171    4171    142     143     LEAAAA  PXLAAA  HHHHxx
+8008   8050    0       0       8       8       8       8       8       3008    8008    16      17      AWAAAA  QXLAAA  OOOOxx
+2088   8051    0       0       8       8       88      88      88      2088    2088    176     177     ICAAAA  RXLAAA  VVVVxx
+7907   8052    1       3       7       7       7       907     1907    2907    7907    14      15      DSAAAA  SXLAAA  AAAAxx
+2429   8053    1       1       9       9       29      429     429     2429    2429    58      59      LPAAAA  TXLAAA  HHHHxx
+9629   8054    1       1       9       9       29      629     1629    4629    9629    58      59      JGAAAA  UXLAAA  OOOOxx
+1470   8055    0       2       0       10      70      470     1470    1470    1470    140     141     OEAAAA  VXLAAA  VVVVxx
+4346   8056    0       2       6       6       46      346     346     4346    4346    92      93      ELAAAA  WXLAAA  AAAAxx
+7219   8057    1       3       9       19      19      219     1219    2219    7219    38      39      RRAAAA  XXLAAA  HHHHxx
+1185   8058    1       1       5       5       85      185     1185    1185    1185    170     171     PTAAAA  YXLAAA  OOOOxx
+8776   8059    0       0       6       16      76      776     776     3776    8776    152     153     OZAAAA  ZXLAAA  VVVVxx
+684    8060    0       0       4       4       84      684     684     684     684     168     169     IAAAAA  AYLAAA  AAAAxx
+2343   8061    1       3       3       3       43      343     343     2343    2343    86      87      DMAAAA  BYLAAA  HHHHxx
+4470   8062    0       2       0       10      70      470     470     4470    4470    140     141     YPAAAA  CYLAAA  OOOOxx
+5116   8063    0       0       6       16      16      116     1116    116     5116    32      33      UOAAAA  DYLAAA  VVVVxx
+1746   8064    0       2       6       6       46      746     1746    1746    1746    92      93      EPAAAA  EYLAAA  AAAAxx
+3216   8065    0       0       6       16      16      216     1216    3216    3216    32      33      STAAAA  FYLAAA  HHHHxx
+4594   8066    0       2       4       14      94      594     594     4594    4594    188     189     SUAAAA  GYLAAA  OOOOxx
+3013   8067    1       1       3       13      13      13      1013    3013    3013    26      27      XLAAAA  HYLAAA  VVVVxx
+2307   8068    1       3       7       7       7       307     307     2307    2307    14      15      TKAAAA  IYLAAA  AAAAxx
+7663   8069    1       3       3       3       63      663     1663    2663    7663    126     127     TIAAAA  JYLAAA  HHHHxx
+8504   8070    0       0       4       4       4       504     504     3504    8504    8       9       CPAAAA  KYLAAA  OOOOxx
+3683   8071    1       3       3       3       83      683     1683    3683    3683    166     167     RLAAAA  LYLAAA  VVVVxx
+144    8072    0       0       4       4       44      144     144     144     144     88      89      OFAAAA  MYLAAA  AAAAxx
+203    8073    1       3       3       3       3       203     203     203     203     6       7       VHAAAA  NYLAAA  HHHHxx
+5255   8074    1       3       5       15      55      255     1255    255     5255    110     111     DUAAAA  OYLAAA  OOOOxx
+4150   8075    0       2       0       10      50      150     150     4150    4150    100     101     QDAAAA  PYLAAA  VVVVxx
+5701   8076    1       1       1       1       1       701     1701    701     5701    2       3       HLAAAA  QYLAAA  AAAAxx
+7400   8077    0       0       0       0       0       400     1400    2400    7400    0       1       QYAAAA  RYLAAA  HHHHxx
+8203   8078    1       3       3       3       3       203     203     3203    8203    6       7       NDAAAA  SYLAAA  OOOOxx
+637    8079    1       1       7       17      37      637     637     637     637     74      75      NYAAAA  TYLAAA  VVVVxx
+2898   8080    0       2       8       18      98      898     898     2898    2898    196     197     MHAAAA  UYLAAA  AAAAxx
+1110   8081    0       2       0       10      10      110     1110    1110    1110    20      21      SQAAAA  VYLAAA  HHHHxx
+6255   8082    1       3       5       15      55      255     255     1255    6255    110     111     PGAAAA  WYLAAA  OOOOxx
+1071   8083    1       3       1       11      71      71      1071    1071    1071    142     143     FPAAAA  XYLAAA  VVVVxx
+541    8084    1       1       1       1       41      541     541     541     541     82      83      VUAAAA  YYLAAA  AAAAxx
+8077   8085    1       1       7       17      77      77      77      3077    8077    154     155     RYAAAA  ZYLAAA  HHHHxx
+6809   8086    1       1       9       9       9       809     809     1809    6809    18      19      XBAAAA  AZLAAA  OOOOxx
+4749   8087    1       1       9       9       49      749     749     4749    4749    98      99      RAAAAA  BZLAAA  VVVVxx
+2886   8088    0       2       6       6       86      886     886     2886    2886    172     173     AHAAAA  CZLAAA  AAAAxx
+5510   8089    0       2       0       10      10      510     1510    510     5510    20      21      YDAAAA  DZLAAA  HHHHxx
+713    8090    1       1       3       13      13      713     713     713     713     26      27      LBAAAA  EZLAAA  OOOOxx
+8388   8091    0       0       8       8       88      388     388     3388    8388    176     177     QKAAAA  FZLAAA  VVVVxx
+9524   8092    0       0       4       4       24      524     1524    4524    9524    48      49      ICAAAA  GZLAAA  AAAAxx
+9949   8093    1       1       9       9       49      949     1949    4949    9949    98      99      RSAAAA  HZLAAA  HHHHxx
+885    8094    1       1       5       5       85      885     885     885     885     170     171     BIAAAA  IZLAAA  OOOOxx
+8699   8095    1       3       9       19      99      699     699     3699    8699    198     199     PWAAAA  JZLAAA  VVVVxx
+2232   8096    0       0       2       12      32      232     232     2232    2232    64      65      WHAAAA  KZLAAA  AAAAxx
+5142   8097    0       2       2       2       42      142     1142    142     5142    84      85      UPAAAA  LZLAAA  HHHHxx
+8891   8098    1       3       1       11      91      891     891     3891    8891    182     183     ZDAAAA  MZLAAA  OOOOxx
+1881   8099    1       1       1       1       81      881     1881    1881    1881    162     163     JUAAAA  NZLAAA  VVVVxx
+3751   8100    1       3       1       11      51      751     1751    3751    3751    102     103     HOAAAA  OZLAAA  AAAAxx
+1896   8101    0       0       6       16      96      896     1896    1896    1896    192     193     YUAAAA  PZLAAA  HHHHxx
+8258   8102    0       2       8       18      58      258     258     3258    8258    116     117     QFAAAA  QZLAAA  OOOOxx
+3820   8103    0       0       0       0       20      820     1820    3820    3820    40      41      YQAAAA  RZLAAA  VVVVxx
+6617   8104    1       1       7       17      17      617     617     1617    6617    34      35      NUAAAA  SZLAAA  AAAAxx
+5100   8105    0       0       0       0       0       100     1100    100     5100    0       1       EOAAAA  TZLAAA  HHHHxx
+4277   8106    1       1       7       17      77      277     277     4277    4277    154     155     NIAAAA  UZLAAA  OOOOxx
+2498   8107    0       2       8       18      98      498     498     2498    2498    196     197     CSAAAA  VZLAAA  VVVVxx
+4343   8108    1       3       3       3       43      343     343     4343    4343    86      87      BLAAAA  WZLAAA  AAAAxx
+8319   8109    1       3       9       19      19      319     319     3319    8319    38      39      ZHAAAA  XZLAAA  HHHHxx
+4803   8110    1       3       3       3       3       803     803     4803    4803    6       7       TCAAAA  YZLAAA  OOOOxx
+3100   8111    0       0       0       0       0       100     1100    3100    3100    0       1       GPAAAA  ZZLAAA  VVVVxx
+428    8112    0       0       8       8       28      428     428     428     428     56      57      MQAAAA  AAMAAA  AAAAxx
+2811   8113    1       3       1       11      11      811     811     2811    2811    22      23      DEAAAA  BAMAAA  HHHHxx
+2989   8114    1       1       9       9       89      989     989     2989    2989    178     179     ZKAAAA  CAMAAA  OOOOxx
+1100   8115    0       0       0       0       0       100     1100    1100    1100    0       1       IQAAAA  DAMAAA  VVVVxx
+6586   8116    0       2       6       6       86      586     586     1586    6586    172     173     ITAAAA  EAMAAA  AAAAxx
+3124   8117    0       0       4       4       24      124     1124    3124    3124    48      49      EQAAAA  FAMAAA  HHHHxx
+1635   8118    1       3       5       15      35      635     1635    1635    1635    70      71      XKAAAA  GAMAAA  OOOOxx
+3888   8119    0       0       8       8       88      888     1888    3888    3888    176     177     OTAAAA  HAMAAA  VVVVxx
+8369   8120    1       1       9       9       69      369     369     3369    8369    138     139     XJAAAA  IAMAAA  AAAAxx
+3148   8121    0       0       8       8       48      148     1148    3148    3148    96      97      CRAAAA  JAMAAA  HHHHxx
+2842   8122    0       2       2       2       42      842     842     2842    2842    84      85      IFAAAA  KAMAAA  OOOOxx
+4965   8123    1       1       5       5       65      965     965     4965    4965    130     131     ZIAAAA  LAMAAA  VVVVxx
+3742   8124    0       2       2       2       42      742     1742    3742    3742    84      85      YNAAAA  MAMAAA  AAAAxx
+5196   8125    0       0       6       16      96      196     1196    196     5196    192     193     WRAAAA  NAMAAA  HHHHxx
+9105   8126    1       1       5       5       5       105     1105    4105    9105    10      11      FMAAAA  OAMAAA  OOOOxx
+6806   8127    0       2       6       6       6       806     806     1806    6806    12      13      UBAAAA  PAMAAA  VVVVxx
+5849   8128    1       1       9       9       49      849     1849    849     5849    98      99      ZQAAAA  QAMAAA  AAAAxx
+6504   8129    0       0       4       4       4       504     504     1504    6504    8       9       EQAAAA  RAMAAA  HHHHxx
+9841   8130    1       1       1       1       41      841     1841    4841    9841    82      83      NOAAAA  SAMAAA  OOOOxx
+457    8131    1       1       7       17      57      457     457     457     457     114     115     PRAAAA  TAMAAA  VVVVxx
+8856   8132    0       0       6       16      56      856     856     3856    8856    112     113     QCAAAA  UAMAAA  AAAAxx
+8043   8133    1       3       3       3       43      43      43      3043    8043    86      87      JXAAAA  VAMAAA  HHHHxx
+5933   8134    1       1       3       13      33      933     1933    933     5933    66      67      FUAAAA  WAMAAA  OOOOxx
+5725   8135    1       1       5       5       25      725     1725    725     5725    50      51      FMAAAA  XAMAAA  VVVVxx
+8607   8136    1       3       7       7       7       607     607     3607    8607    14      15      BTAAAA  YAMAAA  AAAAxx
+9280   8137    0       0       0       0       80      280     1280    4280    9280    160     161     YSAAAA  ZAMAAA  HHHHxx
+6017   8138    1       1       7       17      17      17      17      1017    6017    34      35      LXAAAA  ABMAAA  OOOOxx
+4946   8139    0       2       6       6       46      946     946     4946    4946    92      93      GIAAAA  BBMAAA  VVVVxx
+7373   8140    1       1       3       13      73      373     1373    2373    7373    146     147     PXAAAA  CBMAAA  AAAAxx
+8096   8141    0       0       6       16      96      96      96      3096    8096    192     193     KZAAAA  DBMAAA  HHHHxx
+3178   8142    0       2       8       18      78      178     1178    3178    3178    156     157     GSAAAA  EBMAAA  OOOOxx
+1849   8143    1       1       9       9       49      849     1849    1849    1849    98      99      DTAAAA  FBMAAA  VVVVxx
+8813   8144    1       1       3       13      13      813     813     3813    8813    26      27      ZAAAAA  GBMAAA  AAAAxx
+460    8145    0       0       0       0       60      460     460     460     460     120     121     SRAAAA  HBMAAA  HHHHxx
+7756   8146    0       0       6       16      56      756     1756    2756    7756    112     113     IMAAAA  IBMAAA  OOOOxx
+4425   8147    1       1       5       5       25      425     425     4425    4425    50      51      FOAAAA  JBMAAA  VVVVxx
+1602   8148    0       2       2       2       2       602     1602    1602    1602    4       5       QJAAAA  KBMAAA  AAAAxx
+5981   8149    1       1       1       1       81      981     1981    981     5981    162     163     BWAAAA  LBMAAA  HHHHxx
+8139   8150    1       3       9       19      39      139     139     3139    8139    78      79      BBAAAA  MBMAAA  OOOOxx
+754    8151    0       2       4       14      54      754     754     754     754     108     109     ADAAAA  NBMAAA  VVVVxx
+26     8152    0       2       6       6       26      26      26      26      26      52      53      ABAAAA  OBMAAA  AAAAxx
+106    8153    0       2       6       6       6       106     106     106     106     12      13      CEAAAA  PBMAAA  HHHHxx
+7465   8154    1       1       5       5       65      465     1465    2465    7465    130     131     DBAAAA  QBMAAA  OOOOxx
+1048   8155    0       0       8       8       48      48      1048    1048    1048    96      97      IOAAAA  RBMAAA  VVVVxx
+2303   8156    1       3       3       3       3       303     303     2303    2303    6       7       PKAAAA  SBMAAA  AAAAxx
+5794   8157    0       2       4       14      94      794     1794    794     5794    188     189     WOAAAA  TBMAAA  HHHHxx
+3321   8158    1       1       1       1       21      321     1321    3321    3321    42      43      TXAAAA  UBMAAA  OOOOxx
+6122   8159    0       2       2       2       22      122     122     1122    6122    44      45      MBAAAA  VBMAAA  VVVVxx
+6474   8160    0       2       4       14      74      474     474     1474    6474    148     149     APAAAA  WBMAAA  AAAAxx
+827    8161    1       3       7       7       27      827     827     827     827     54      55      VFAAAA  XBMAAA  HHHHxx
+6616   8162    0       0       6       16      16      616     616     1616    6616    32      33      MUAAAA  YBMAAA  OOOOxx
+2131   8163    1       3       1       11      31      131     131     2131    2131    62      63      ZDAAAA  ZBMAAA  VVVVxx
+5483   8164    1       3       3       3       83      483     1483    483     5483    166     167     XCAAAA  ACMAAA  AAAAxx
+606    8165    0       2       6       6       6       606     606     606     606     12      13      IXAAAA  BCMAAA  HHHHxx
+922    8166    0       2       2       2       22      922     922     922     922     44      45      MJAAAA  CCMAAA  OOOOxx
+8475   8167    1       3       5       15      75      475     475     3475    8475    150     151     ZNAAAA  DCMAAA  VVVVxx
+7645   8168    1       1       5       5       45      645     1645    2645    7645    90      91      BIAAAA  ECMAAA  AAAAxx
+5097   8169    1       1       7       17      97      97      1097    97      5097    194     195     BOAAAA  FCMAAA  HHHHxx
+5377   8170    1       1       7       17      77      377     1377    377     5377    154     155     VYAAAA  GCMAAA  OOOOxx
+6116   8171    0       0       6       16      16      116     116     1116    6116    32      33      GBAAAA  HCMAAA  VVVVxx
+8674   8172    0       2       4       14      74      674     674     3674    8674    148     149     QVAAAA  ICMAAA  AAAAxx
+8063   8173    1       3       3       3       63      63      63      3063    8063    126     127     DYAAAA  JCMAAA  HHHHxx
+5271   8174    1       3       1       11      71      271     1271    271     5271    142     143     TUAAAA  KCMAAA  OOOOxx
+1619   8175    1       3       9       19      19      619     1619    1619    1619    38      39      HKAAAA  LCMAAA  VVVVxx
+6419   8176    1       3       9       19      19      419     419     1419    6419    38      39      XMAAAA  MCMAAA  AAAAxx
+7651   8177    1       3       1       11      51      651     1651    2651    7651    102     103     HIAAAA  NCMAAA  HHHHxx
+2897   8178    1       1       7       17      97      897     897     2897    2897    194     195     LHAAAA  OCMAAA  OOOOxx
+8148   8179    0       0       8       8       48      148     148     3148    8148    96      97      KBAAAA  PCMAAA  VVVVxx
+7461   8180    1       1       1       1       61      461     1461    2461    7461    122     123     ZAAAAA  QCMAAA  AAAAxx
+9186   8181    0       2       6       6       86      186     1186    4186    9186    172     173     IPAAAA  RCMAAA  HHHHxx
+7127   8182    1       3       7       7       27      127     1127    2127    7127    54      55      DOAAAA  SCMAAA  OOOOxx
+8233   8183    1       1       3       13      33      233     233     3233    8233    66      67      REAAAA  TCMAAA  VVVVxx
+9651   8184    1       3       1       11      51      651     1651    4651    9651    102     103     FHAAAA  UCMAAA  AAAAxx
+6746   8185    0       2       6       6       46      746     746     1746    6746    92      93      MZAAAA  VCMAAA  HHHHxx
+7835   8186    1       3       5       15      35      835     1835    2835    7835    70      71      JPAAAA  WCMAAA  OOOOxx
+8815   8187    1       3       5       15      15      815     815     3815    8815    30      31      BBAAAA  XCMAAA  VVVVxx
+6398   8188    0       2       8       18      98      398     398     1398    6398    196     197     CMAAAA  YCMAAA  AAAAxx
+5344   8189    0       0       4       4       44      344     1344    344     5344    88      89      OXAAAA  ZCMAAA  HHHHxx
+8209   8190    1       1       9       9       9       209     209     3209    8209    18      19      TDAAAA  ADMAAA  OOOOxx
+8444   8191    0       0       4       4       44      444     444     3444    8444    88      89      UMAAAA  BDMAAA  VVVVxx
+5669   8192    1       1       9       9       69      669     1669    669     5669    138     139     BKAAAA  CDMAAA  AAAAxx
+2455   8193    1       3       5       15      55      455     455     2455    2455    110     111     LQAAAA  DDMAAA  HHHHxx
+6767   8194    1       3       7       7       67      767     767     1767    6767    134     135     HAAAAA  EDMAAA  OOOOxx
+135    8195    1       3       5       15      35      135     135     135     135     70      71      FFAAAA  FDMAAA  VVVVxx
+3503   8196    1       3       3       3       3       503     1503    3503    3503    6       7       TEAAAA  GDMAAA  AAAAxx
+6102   8197    0       2       2       2       2       102     102     1102    6102    4       5       SAAAAA  HDMAAA  HHHHxx
+7136   8198    0       0       6       16      36      136     1136    2136    7136    72      73      MOAAAA  IDMAAA  OOOOxx
+4933   8199    1       1       3       13      33      933     933     4933    4933    66      67      THAAAA  JDMAAA  VVVVxx
+8804   8200    0       0       4       4       4       804     804     3804    8804    8       9       QAAAAA  KDMAAA  AAAAxx
+3760   8201    0       0       0       0       60      760     1760    3760    3760    120     121     QOAAAA  LDMAAA  HHHHxx
+8603   8202    1       3       3       3       3       603     603     3603    8603    6       7       XSAAAA  MDMAAA  OOOOxx
+7411   8203    1       3       1       11      11      411     1411    2411    7411    22      23      BZAAAA  NDMAAA  VVVVxx
+834    8204    0       2       4       14      34      834     834     834     834     68      69      CGAAAA  ODMAAA  AAAAxx
+7385   8205    1       1       5       5       85      385     1385    2385    7385    170     171     BYAAAA  PDMAAA  HHHHxx
+3696   8206    0       0       6       16      96      696     1696    3696    3696    192     193     EMAAAA  QDMAAA  OOOOxx
+8720   8207    0       0       0       0       20      720     720     3720    8720    40      41      KXAAAA  RDMAAA  VVVVxx
+4539   8208    1       3       9       19      39      539     539     4539    4539    78      79      PSAAAA  SDMAAA  AAAAxx
+9837   8209    1       1       7       17      37      837     1837    4837    9837    74      75      JOAAAA  TDMAAA  HHHHxx
+8595   8210    1       3       5       15      95      595     595     3595    8595    190     191     PSAAAA  UDMAAA  OOOOxx
+3673   8211    1       1       3       13      73      673     1673    3673    3673    146     147     HLAAAA  VDMAAA  VVVVxx
+475    8212    1       3       5       15      75      475     475     475     475     150     151     HSAAAA  WDMAAA  AAAAxx
+2256   8213    0       0       6       16      56      256     256     2256    2256    112     113     UIAAAA  XDMAAA  HHHHxx
+6349   8214    1       1       9       9       49      349     349     1349    6349    98      99      FKAAAA  YDMAAA  OOOOxx
+9968   8215    0       0       8       8       68      968     1968    4968    9968    136     137     KTAAAA  ZDMAAA  VVVVxx
+7261   8216    1       1       1       1       61      261     1261    2261    7261    122     123     HTAAAA  AEMAAA  AAAAxx
+5799   8217    1       3       9       19      99      799     1799    799     5799    198     199     BPAAAA  BEMAAA  HHHHxx
+8159   8218    1       3       9       19      59      159     159     3159    8159    118     119     VBAAAA  CEMAAA  OOOOxx
+92     8219    0       0       2       12      92      92      92      92      92      184     185     ODAAAA  DEMAAA  VVVVxx
+5927   8220    1       3       7       7       27      927     1927    927     5927    54      55      ZTAAAA  EEMAAA  AAAAxx
+7925   8221    1       1       5       5       25      925     1925    2925    7925    50      51      VSAAAA  FEMAAA  HHHHxx
+5836   8222    0       0       6       16      36      836     1836    836     5836    72      73      MQAAAA  GEMAAA  OOOOxx
+7935   8223    1       3       5       15      35      935     1935    2935    7935    70      71      FTAAAA  HEMAAA  VVVVxx
+5505   8224    1       1       5       5       5       505     1505    505     5505    10      11      TDAAAA  IEMAAA  AAAAxx
+5882   8225    0       2       2       2       82      882     1882    882     5882    164     165     GSAAAA  JEMAAA  HHHHxx
+4411   8226    1       3       1       11      11      411     411     4411    4411    22      23      RNAAAA  KEMAAA  OOOOxx
+64     8227    0       0       4       4       64      64      64      64      64      128     129     MCAAAA  LEMAAA  VVVVxx
+2851   8228    1       3       1       11      51      851     851     2851    2851    102     103     RFAAAA  MEMAAA  AAAAxx
+1665   8229    1       1       5       5       65      665     1665    1665    1665    130     131     BMAAAA  NEMAAA  HHHHxx
+2895   8230    1       3       5       15      95      895     895     2895    2895    190     191     JHAAAA  OEMAAA  OOOOxx
+2210   8231    0       2       0       10      10      210     210     2210    2210    20      21      AHAAAA  PEMAAA  VVVVxx
+9873   8232    1       1       3       13      73      873     1873    4873    9873    146     147     TPAAAA  QEMAAA  AAAAxx
+5402   8233    0       2       2       2       2       402     1402    402     5402    4       5       UZAAAA  REMAAA  HHHHxx
+285    8234    1       1       5       5       85      285     285     285     285     170     171     ZKAAAA  SEMAAA  OOOOxx
+8545   8235    1       1       5       5       45      545     545     3545    8545    90      91      RQAAAA  TEMAAA  VVVVxx
+5328   8236    0       0       8       8       28      328     1328    328     5328    56      57      YWAAAA  UEMAAA  AAAAxx
+733    8237    1       1       3       13      33      733     733     733     733     66      67      FCAAAA  VEMAAA  HHHHxx
+7726   8238    0       2       6       6       26      726     1726    2726    7726    52      53      ELAAAA  WEMAAA  OOOOxx
+5418   8239    0       2       8       18      18      418     1418    418     5418    36      37      KAAAAA  XEMAAA  VVVVxx
+7761   8240    1       1       1       1       61      761     1761    2761    7761    122     123     NMAAAA  YEMAAA  AAAAxx
+9263   8241    1       3       3       3       63      263     1263    4263    9263    126     127     HSAAAA  ZEMAAA  HHHHxx
+5579   8242    1       3       9       19      79      579     1579    579     5579    158     159     PGAAAA  AFMAAA  OOOOxx
+5434   8243    0       2       4       14      34      434     1434    434     5434    68      69      ABAAAA  BFMAAA  VVVVxx
+5230   8244    0       2       0       10      30      230     1230    230     5230    60      61      ETAAAA  CFMAAA  AAAAxx
+9981   8245    1       1       1       1       81      981     1981    4981    9981    162     163     XTAAAA  DFMAAA  HHHHxx
+5830   8246    0       2       0       10      30      830     1830    830     5830    60      61      GQAAAA  EFMAAA  OOOOxx
+128    8247    0       0       8       8       28      128     128     128     128     56      57      YEAAAA  FFMAAA  VVVVxx
+2734   8248    0       2       4       14      34      734     734     2734    2734    68      69      EBAAAA  GFMAAA  AAAAxx
+4537   8249    1       1       7       17      37      537     537     4537    4537    74      75      NSAAAA  HFMAAA  HHHHxx
+3899   8250    1       3       9       19      99      899     1899    3899    3899    198     199     ZTAAAA  IFMAAA  OOOOxx
+1000   8251    0       0       0       0       0       0       1000    1000    1000    0       1       MMAAAA  JFMAAA  VVVVxx
+9896   8252    0       0       6       16      96      896     1896    4896    9896    192     193     QQAAAA  KFMAAA  AAAAxx
+3640   8253    0       0       0       0       40      640     1640    3640    3640    80      81      AKAAAA  LFMAAA  HHHHxx
+2568   8254    0       0       8       8       68      568     568     2568    2568    136     137     UUAAAA  MFMAAA  OOOOxx
+2026   8255    0       2       6       6       26      26      26      2026    2026    52      53      YZAAAA  NFMAAA  VVVVxx
+3955   8256    1       3       5       15      55      955     1955    3955    3955    110     111     DWAAAA  OFMAAA  AAAAxx
+7152   8257    0       0       2       12      52      152     1152    2152    7152    104     105     CPAAAA  PFMAAA  HHHHxx
+2402   8258    0       2       2       2       2       402     402     2402    2402    4       5       KOAAAA  QFMAAA  OOOOxx
+9522   8259    0       2       2       2       22      522     1522    4522    9522    44      45      GCAAAA  RFMAAA  VVVVxx
+4011   8260    1       3       1       11      11      11      11      4011    4011    22      23      HYAAAA  SFMAAA  AAAAxx
+3297   8261    1       1       7       17      97      297     1297    3297    3297    194     195     VWAAAA  TFMAAA  HHHHxx
+4915   8262    1       3       5       15      15      915     915     4915    4915    30      31      BHAAAA  UFMAAA  OOOOxx
+5397   8263    1       1       7       17      97      397     1397    397     5397    194     195     PZAAAA  VFMAAA  VVVVxx
+5454   8264    0       2       4       14      54      454     1454    454     5454    108     109     UBAAAA  WFMAAA  AAAAxx
+4568   8265    0       0       8       8       68      568     568     4568    4568    136     137     STAAAA  XFMAAA  HHHHxx
+5875   8266    1       3       5       15      75      875     1875    875     5875    150     151     ZRAAAA  YFMAAA  OOOOxx
+3642   8267    0       2       2       2       42      642     1642    3642    3642    84      85      CKAAAA  ZFMAAA  VVVVxx
+8506   8268    0       2       6       6       6       506     506     3506    8506    12      13      EPAAAA  AGMAAA  AAAAxx
+9621   8269    1       1       1       1       21      621     1621    4621    9621    42      43      BGAAAA  BGMAAA  HHHHxx
+7739   8270    1       3       9       19      39      739     1739    2739    7739    78      79      RLAAAA  CGMAAA  OOOOxx
+3987   8271    1       3       7       7       87      987     1987    3987    3987    174     175     JXAAAA  DGMAAA  VVVVxx
+2090   8272    0       2       0       10      90      90      90      2090    2090    180     181     KCAAAA  EGMAAA  AAAAxx
+3838   8273    0       2       8       18      38      838     1838    3838    3838    76      77      QRAAAA  FGMAAA  HHHHxx
+17     8274    1       1       7       17      17      17      17      17      17      34      35      RAAAAA  GGMAAA  OOOOxx
+3406   8275    0       2       6       6       6       406     1406    3406    3406    12      13      ABAAAA  HGMAAA  VVVVxx
+8312   8276    0       0       2       12      12      312     312     3312    8312    24      25      SHAAAA  IGMAAA  AAAAxx
+4034   8277    0       2       4       14      34      34      34      4034    4034    68      69      EZAAAA  JGMAAA  HHHHxx
+1535   8278    1       3       5       15      35      535     1535    1535    1535    70      71      BHAAAA  KGMAAA  OOOOxx
+7198   8279    0       2       8       18      98      198     1198    2198    7198    196     197     WQAAAA  LGMAAA  VVVVxx
+8885   8280    1       1       5       5       85      885     885     3885    8885    170     171     TDAAAA  MGMAAA  AAAAxx
+4081   8281    1       1       1       1       81      81      81      4081    4081    162     163     ZAAAAA  NGMAAA  HHHHxx
+980    8282    0       0       0       0       80      980     980     980     980     160     161     SLAAAA  OGMAAA  OOOOxx
+551    8283    1       3       1       11      51      551     551     551     551     102     103     FVAAAA  PGMAAA  VVVVxx
+7746   8284    0       2       6       6       46      746     1746    2746    7746    92      93      YLAAAA  QGMAAA  AAAAxx
+4756   8285    0       0       6       16      56      756     756     4756    4756    112     113     YAAAAA  RGMAAA  HHHHxx
+3655   8286    1       3       5       15      55      655     1655    3655    3655    110     111     PKAAAA  SGMAAA  OOOOxx
+7075   8287    1       3       5       15      75      75      1075    2075    7075    150     151     DMAAAA  TGMAAA  VVVVxx
+3950   8288    0       2       0       10      50      950     1950    3950    3950    100     101     YVAAAA  UGMAAA  AAAAxx
+2314   8289    0       2       4       14      14      314     314     2314    2314    28      29      ALAAAA  VGMAAA  HHHHxx
+8432   8290    0       0       2       12      32      432     432     3432    8432    64      65      IMAAAA  WGMAAA  OOOOxx
+62     8291    0       2       2       2       62      62      62      62      62      124     125     KCAAAA  XGMAAA  VVVVxx
+6920   8292    0       0       0       0       20      920     920     1920    6920    40      41      EGAAAA  YGMAAA  AAAAxx
+4077   8293    1       1       7       17      77      77      77      4077    4077    154     155     VAAAAA  ZGMAAA  HHHHxx
+9118   8294    0       2       8       18      18      118     1118    4118    9118    36      37      SMAAAA  AHMAAA  OOOOxx
+5375   8295    1       3       5       15      75      375     1375    375     5375    150     151     TYAAAA  BHMAAA  VVVVxx
+178    8296    0       2       8       18      78      178     178     178     178     156     157     WGAAAA  CHMAAA  AAAAxx
+1079   8297    1       3       9       19      79      79      1079    1079    1079    158     159     NPAAAA  DHMAAA  HHHHxx
+4279   8298    1       3       9       19      79      279     279     4279    4279    158     159     PIAAAA  EHMAAA  OOOOxx
+8436   8299    0       0       6       16      36      436     436     3436    8436    72      73      MMAAAA  FHMAAA  VVVVxx
+1931   8300    1       3       1       11      31      931     1931    1931    1931    62      63      HWAAAA  GHMAAA  AAAAxx
+2096   8301    0       0       6       16      96      96      96      2096    2096    192     193     QCAAAA  HHMAAA  HHHHxx
+1638   8302    0       2       8       18      38      638     1638    1638    1638    76      77      ALAAAA  IHMAAA  OOOOxx
+2788   8303    0       0       8       8       88      788     788     2788    2788    176     177     GDAAAA  JHMAAA  VVVVxx
+4751   8304    1       3       1       11      51      751     751     4751    4751    102     103     TAAAAA  KHMAAA  AAAAxx
+8824   8305    0       0       4       4       24      824     824     3824    8824    48      49      KBAAAA  LHMAAA  HHHHxx
+3098   8306    0       2       8       18      98      98      1098    3098    3098    196     197     EPAAAA  MHMAAA  OOOOxx
+4497   8307    1       1       7       17      97      497     497     4497    4497    194     195     ZQAAAA  NHMAAA  VVVVxx
+5223   8308    1       3       3       3       23      223     1223    223     5223    46      47      XSAAAA  OHMAAA  AAAAxx
+9212   8309    0       0       2       12      12      212     1212    4212    9212    24      25      IQAAAA  PHMAAA  HHHHxx
+4265   8310    1       1       5       5       65      265     265     4265    4265    130     131     BIAAAA  QHMAAA  OOOOxx
+6898   8311    0       2       8       18      98      898     898     1898    6898    196     197     IFAAAA  RHMAAA  VVVVxx
+8808   8312    0       0       8       8       8       808     808     3808    8808    16      17      UAAAAA  SHMAAA  AAAAxx
+5629   8313    1       1       9       9       29      629     1629    629     5629    58      59      NIAAAA  THMAAA  HHHHxx
+3779   8314    1       3       9       19      79      779     1779    3779    3779    158     159     JPAAAA  UHMAAA  OOOOxx
+4972   8315    0       0       2       12      72      972     972     4972    4972    144     145     GJAAAA  VHMAAA  VVVVxx
+4511   8316    1       3       1       11      11      511     511     4511    4511    22      23      NRAAAA  WHMAAA  AAAAxx
+6761   8317    1       1       1       1       61      761     761     1761    6761    122     123     BAAAAA  XHMAAA  HHHHxx
+2335   8318    1       3       5       15      35      335     335     2335    2335    70      71      VLAAAA  YHMAAA  OOOOxx
+732    8319    0       0       2       12      32      732     732     732     732     64      65      ECAAAA  ZHMAAA  VVVVxx
+4757   8320    1       1       7       17      57      757     757     4757    4757    114     115     ZAAAAA  AIMAAA  AAAAxx
+6624   8321    0       0       4       4       24      624     624     1624    6624    48      49      UUAAAA  BIMAAA  HHHHxx
+5869   8322    1       1       9       9       69      869     1869    869     5869    138     139     TRAAAA  CIMAAA  OOOOxx
+5842   8323    0       2       2       2       42      842     1842    842     5842    84      85      SQAAAA  DIMAAA  VVVVxx
+5735   8324    1       3       5       15      35      735     1735    735     5735    70      71      PMAAAA  EIMAAA  AAAAxx
+8276   8325    0       0       6       16      76      276     276     3276    8276    152     153     IGAAAA  FIMAAA  HHHHxx
+7227   8326    1       3       7       7       27      227     1227    2227    7227    54      55      ZRAAAA  GIMAAA  OOOOxx
+4923   8327    1       3       3       3       23      923     923     4923    4923    46      47      JHAAAA  HIMAAA  VVVVxx
+9135   8328    1       3       5       15      35      135     1135    4135    9135    70      71      JNAAAA  IIMAAA  AAAAxx
+5813   8329    1       1       3       13      13      813     1813    813     5813    26      27      PPAAAA  JIMAAA  HHHHxx
+9697   8330    1       1       7       17      97      697     1697    4697    9697    194     195     ZIAAAA  KIMAAA  OOOOxx
+3222   8331    0       2       2       2       22      222     1222    3222    3222    44      45      YTAAAA  LIMAAA  VVVVxx
+2394   8332    0       2       4       14      94      394     394     2394    2394    188     189     COAAAA  MIMAAA  AAAAxx
+5784   8333    0       0       4       4       84      784     1784    784     5784    168     169     MOAAAA  NIMAAA  HHHHxx
+3652   8334    0       0       2       12      52      652     1652    3652    3652    104     105     MKAAAA  OIMAAA  OOOOxx
+8175   8335    1       3       5       15      75      175     175     3175    8175    150     151     LCAAAA  PIMAAA  VVVVxx
+7568   8336    0       0       8       8       68      568     1568    2568    7568    136     137     CFAAAA  QIMAAA  AAAAxx
+6645   8337    1       1       5       5       45      645     645     1645    6645    90      91      PVAAAA  RIMAAA  HHHHxx
+8176   8338    0       0       6       16      76      176     176     3176    8176    152     153     MCAAAA  SIMAAA  OOOOxx
+530    8339    0       2       0       10      30      530     530     530     530     60      61      KUAAAA  TIMAAA  VVVVxx
+5439   8340    1       3       9       19      39      439     1439    439     5439    78      79      FBAAAA  UIMAAA  AAAAxx
+61     8341    1       1       1       1       61      61      61      61      61      122     123     JCAAAA  VIMAAA  HHHHxx
+3951   8342    1       3       1       11      51      951     1951    3951    3951    102     103     ZVAAAA  WIMAAA  OOOOxx
+5283   8343    1       3       3       3       83      283     1283    283     5283    166     167     FVAAAA  XIMAAA  VVVVxx
+7226   8344    0       2       6       6       26      226     1226    2226    7226    52      53      YRAAAA  YIMAAA  AAAAxx
+1954   8345    0       2       4       14      54      954     1954    1954    1954    108     109     EXAAAA  ZIMAAA  HHHHxx
+334    8346    0       2       4       14      34      334     334     334     334     68      69      WMAAAA  AJMAAA  OOOOxx
+3921   8347    1       1       1       1       21      921     1921    3921    3921    42      43      VUAAAA  BJMAAA  VVVVxx
+6276   8348    0       0       6       16      76      276     276     1276    6276    152     153     KHAAAA  CJMAAA  AAAAxx
+3378   8349    0       2       8       18      78      378     1378    3378    3378    156     157     YZAAAA  DJMAAA  HHHHxx
+5236   8350    0       0       6       16      36      236     1236    236     5236    72      73      KTAAAA  EJMAAA  OOOOxx
+7781   8351    1       1       1       1       81      781     1781    2781    7781    162     163     HNAAAA  FJMAAA  VVVVxx
+8601   8352    1       1       1       1       1       601     601     3601    8601    2       3       VSAAAA  GJMAAA  AAAAxx
+1473   8353    1       1       3       13      73      473     1473    1473    1473    146     147     REAAAA  HJMAAA  HHHHxx
+3246   8354    0       2       6       6       46      246     1246    3246    3246    92      93      WUAAAA  IJMAAA  OOOOxx
+3601   8355    1       1       1       1       1       601     1601    3601    3601    2       3       NIAAAA  JJMAAA  VVVVxx
+6861   8356    1       1       1       1       61      861     861     1861    6861    122     123     XDAAAA  KJMAAA  AAAAxx
+9032   8357    0       0       2       12      32      32      1032    4032    9032    64      65      KJAAAA  LJMAAA  HHHHxx
+216    8358    0       0       6       16      16      216     216     216     216     32      33      IIAAAA  MJMAAA  OOOOxx
+3824   8359    0       0       4       4       24      824     1824    3824    3824    48      49      CRAAAA  NJMAAA  VVVVxx
+8486   8360    0       2       6       6       86      486     486     3486    8486    172     173     KOAAAA  OJMAAA  AAAAxx
+276    8361    0       0       6       16      76      276     276     276     276     152     153     QKAAAA  PJMAAA  HHHHxx
+1838   8362    0       2       8       18      38      838     1838    1838    1838    76      77      SSAAAA  QJMAAA  OOOOxx
+6175   8363    1       3       5       15      75      175     175     1175    6175    150     151     NDAAAA  RJMAAA  VVVVxx
+3719   8364    1       3       9       19      19      719     1719    3719    3719    38      39      BNAAAA  SJMAAA  AAAAxx
+6958   8365    0       2       8       18      58      958     958     1958    6958    116     117     QHAAAA  TJMAAA  HHHHxx
+6822   8366    0       2       2       2       22      822     822     1822    6822    44      45      KCAAAA  UJMAAA  OOOOxx
+3318   8367    0       2       8       18      18      318     1318    3318    3318    36      37      QXAAAA  VJMAAA  VVVVxx
+7222   8368    0       2       2       2       22      222     1222    2222    7222    44      45      URAAAA  WJMAAA  AAAAxx
+85     8369    1       1       5       5       85      85      85      85      85      170     171     HDAAAA  XJMAAA  HHHHxx
+5158   8370    0       2       8       18      58      158     1158    158     5158    116     117     KQAAAA  YJMAAA  OOOOxx
+6360   8371    0       0       0       0       60      360     360     1360    6360    120     121     QKAAAA  ZJMAAA  VVVVxx
+2599   8372    1       3       9       19      99      599     599     2599    2599    198     199     ZVAAAA  AKMAAA  AAAAxx
+4002   8373    0       2       2       2       2       2       2       4002    4002    4       5       YXAAAA  BKMAAA  HHHHxx
+6597   8374    1       1       7       17      97      597     597     1597    6597    194     195     TTAAAA  CKMAAA  OOOOxx
+5762   8375    0       2       2       2       62      762     1762    762     5762    124     125     QNAAAA  DKMAAA  VVVVxx
+8383   8376    1       3       3       3       83      383     383     3383    8383    166     167     LKAAAA  EKMAAA  AAAAxx
+4686   8377    0       2       6       6       86      686     686     4686    4686    172     173     GYAAAA  FKMAAA  HHHHxx
+5972   8378    0       0       2       12      72      972     1972    972     5972    144     145     SVAAAA  GKMAAA  OOOOxx
+1432   8379    0       0       2       12      32      432     1432    1432    1432    64      65      CDAAAA  HKMAAA  VVVVxx
+1601   8380    1       1       1       1       1       601     1601    1601    1601    2       3       PJAAAA  IKMAAA  AAAAxx
+3012   8381    0       0       2       12      12      12      1012    3012    3012    24      25      WLAAAA  JKMAAA  HHHHxx
+9345   8382    1       1       5       5       45      345     1345    4345    9345    90      91      LVAAAA  KKMAAA  OOOOxx
+8869   8383    1       1       9       9       69      869     869     3869    8869    138     139     DDAAAA  LKMAAA  VVVVxx
+6612   8384    0       0       2       12      12      612     612     1612    6612    24      25      IUAAAA  MKMAAA  AAAAxx
+262    8385    0       2       2       2       62      262     262     262     262     124     125     CKAAAA  NKMAAA  HHHHxx
+300    8386    0       0       0       0       0       300     300     300     300     0       1       OLAAAA  OKMAAA  OOOOxx
+3045   8387    1       1       5       5       45      45      1045    3045    3045    90      91      DNAAAA  PKMAAA  VVVVxx
+7252   8388    0       0       2       12      52      252     1252    2252    7252    104     105     YSAAAA  QKMAAA  AAAAxx
+9099   8389    1       3       9       19      99      99      1099    4099    9099    198     199     ZLAAAA  RKMAAA  HHHHxx
+9006   8390    0       2       6       6       6       6       1006    4006    9006    12      13      KIAAAA  SKMAAA  OOOOxx
+3078   8391    0       2       8       18      78      78      1078    3078    3078    156     157     KOAAAA  TKMAAA  VVVVxx
+5159   8392    1       3       9       19      59      159     1159    159     5159    118     119     LQAAAA  UKMAAA  AAAAxx
+9329   8393    1       1       9       9       29      329     1329    4329    9329    58      59      VUAAAA  VKMAAA  HHHHxx
+1393   8394    1       1       3       13      93      393     1393    1393    1393    186     187     PBAAAA  WKMAAA  OOOOxx
+5894   8395    0       2       4       14      94      894     1894    894     5894    188     189     SSAAAA  XKMAAA  VVVVxx
+11     8396    1       3       1       11      11      11      11      11      11      22      23      LAAAAA  YKMAAA  AAAAxx
+5606   8397    0       2       6       6       6       606     1606    606     5606    12      13      QHAAAA  ZKMAAA  HHHHxx
+5541   8398    1       1       1       1       41      541     1541    541     5541    82      83      DFAAAA  ALMAAA  OOOOxx
+2689   8399    1       1       9       9       89      689     689     2689    2689    178     179     LZAAAA  BLMAAA  VVVVxx
+1023   8400    1       3       3       3       23      23      1023    1023    1023    46      47      JNAAAA  CLMAAA  AAAAxx
+8134   8401    0       2       4       14      34      134     134     3134    8134    68      69      WAAAAA  DLMAAA  HHHHxx
+5923   8402    1       3       3       3       23      923     1923    923     5923    46      47      VTAAAA  ELMAAA  OOOOxx
+6056   8403    0       0       6       16      56      56      56      1056    6056    112     113     YYAAAA  FLMAAA  VVVVxx
+653    8404    1       1       3       13      53      653     653     653     653     106     107     DZAAAA  GLMAAA  AAAAxx
+367    8405    1       3       7       7       67      367     367     367     367     134     135     DOAAAA  HLMAAA  HHHHxx
+1828   8406    0       0       8       8       28      828     1828    1828    1828    56      57      ISAAAA  ILMAAA  OOOOxx
+6506   8407    0       2       6       6       6       506     506     1506    6506    12      13      GQAAAA  JLMAAA  VVVVxx
+5772   8408    0       0       2       12      72      772     1772    772     5772    144     145     AOAAAA  KLMAAA  AAAAxx
+8052   8409    0       0       2       12      52      52      52      3052    8052    104     105     SXAAAA  LLMAAA  HHHHxx
+2633   8410    1       1       3       13      33      633     633     2633    2633    66      67      HXAAAA  MLMAAA  OOOOxx
+4878   8411    0       2       8       18      78      878     878     4878    4878    156     157     QFAAAA  NLMAAA  VVVVxx
+5621   8412    1       1       1       1       21      621     1621    621     5621    42      43      FIAAAA  OLMAAA  AAAAxx
+41     8413    1       1       1       1       41      41      41      41      41      82      83      PBAAAA  PLMAAA  HHHHxx
+4613   8414    1       1       3       13      13      613     613     4613    4613    26      27      LVAAAA  QLMAAA  OOOOxx
+9389   8415    1       1       9       9       89      389     1389    4389    9389    178     179     DXAAAA  RLMAAA  VVVVxx
+9414   8416    0       2       4       14      14      414     1414    4414    9414    28      29      CYAAAA  SLMAAA  AAAAxx
+3583   8417    1       3       3       3       83      583     1583    3583    3583    166     167     VHAAAA  TLMAAA  HHHHxx
+3454   8418    0       2       4       14      54      454     1454    3454    3454    108     109     WCAAAA  ULMAAA  OOOOxx
+719    8419    1       3       9       19      19      719     719     719     719     38      39      RBAAAA  VLMAAA  VVVVxx
+6188   8420    0       0       8       8       88      188     188     1188    6188    176     177     AEAAAA  WLMAAA  AAAAxx
+2288   8421    0       0       8       8       88      288     288     2288    2288    176     177     AKAAAA  XLMAAA  HHHHxx
+1287   8422    1       3       7       7       87      287     1287    1287    1287    174     175     NXAAAA  YLMAAA  OOOOxx
+1397   8423    1       1       7       17      97      397     1397    1397    1397    194     195     TBAAAA  ZLMAAA  VVVVxx
+7763   8424    1       3       3       3       63      763     1763    2763    7763    126     127     PMAAAA  AMMAAA  AAAAxx
+5194   8425    0       2       4       14      94      194     1194    194     5194    188     189     URAAAA  BMMAAA  HHHHxx
+3167   8426    1       3       7       7       67      167     1167    3167    3167    134     135     VRAAAA  CMMAAA  OOOOxx
+9218   8427    0       2       8       18      18      218     1218    4218    9218    36      37      OQAAAA  DMMAAA  VVVVxx
+2065   8428    1       1       5       5       65      65      65      2065    2065    130     131     LBAAAA  EMMAAA  AAAAxx
+9669   8429    1       1       9       9       69      669     1669    4669    9669    138     139     XHAAAA  FMMAAA  HHHHxx
+146    8430    0       2       6       6       46      146     146     146     146     92      93      QFAAAA  GMMAAA  OOOOxx
+6141   8431    1       1       1       1       41      141     141     1141    6141    82      83      FCAAAA  HMMAAA  VVVVxx
+2843   8432    1       3       3       3       43      843     843     2843    2843    86      87      JFAAAA  IMMAAA  AAAAxx
+7934   8433    0       2       4       14      34      934     1934    2934    7934    68      69      ETAAAA  JMMAAA  HHHHxx
+2536   8434    0       0       6       16      36      536     536     2536    2536    72      73      OTAAAA  KMMAAA  OOOOxx
+7088   8435    0       0       8       8       88      88      1088    2088    7088    176     177     QMAAAA  LMMAAA  VVVVxx
+2519   8436    1       3       9       19      19      519     519     2519    2519    38      39      XSAAAA  MMMAAA  AAAAxx
+6650   8437    0       2       0       10      50      650     650     1650    6650    100     101     UVAAAA  NMMAAA  HHHHxx
+3007   8438    1       3       7       7       7       7       1007    3007    3007    14      15      RLAAAA  OMMAAA  OOOOxx
+4507   8439    1       3       7       7       7       507     507     4507    4507    14      15      JRAAAA  PMMAAA  VVVVxx
+4892   8440    0       0       2       12      92      892     892     4892    4892    184     185     EGAAAA  QMMAAA  AAAAxx
+7159   8441    1       3       9       19      59      159     1159    2159    7159    118     119     JPAAAA  RMMAAA  HHHHxx
+3171   8442    1       3       1       11      71      171     1171    3171    3171    142     143     ZRAAAA  SMMAAA  OOOOxx
+1080   8443    0       0       0       0       80      80      1080    1080    1080    160     161     OPAAAA  TMMAAA  VVVVxx
+7248   8444    0       0       8       8       48      248     1248    2248    7248    96      97      USAAAA  UMMAAA  AAAAxx
+7230   8445    0       2       0       10      30      230     1230    2230    7230    60      61      CSAAAA  VMMAAA  HHHHxx
+3823   8446    1       3       3       3       23      823     1823    3823    3823    46      47      BRAAAA  WMMAAA  OOOOxx
+5517   8447    1       1       7       17      17      517     1517    517     5517    34      35      FEAAAA  XMMAAA  VVVVxx
+1482   8448    0       2       2       2       82      482     1482    1482    1482    164     165     AFAAAA  YMMAAA  AAAAxx
+9953   8449    1       1       3       13      53      953     1953    4953    9953    106     107     VSAAAA  ZMMAAA  HHHHxx
+2754   8450    0       2       4       14      54      754     754     2754    2754    108     109     YBAAAA  ANMAAA  OOOOxx
+3875   8451    1       3       5       15      75      875     1875    3875    3875    150     151     BTAAAA  BNMAAA  VVVVxx
+9800   8452    0       0       0       0       0       800     1800    4800    9800    0       1       YMAAAA  CNMAAA  AAAAxx
+8819   8453    1       3       9       19      19      819     819     3819    8819    38      39      FBAAAA  DNMAAA  HHHHxx
+8267   8454    1       3       7       7       67      267     267     3267    8267    134     135     ZFAAAA  ENMAAA  OOOOxx
+520    8455    0       0       0       0       20      520     520     520     520     40      41      AUAAAA  FNMAAA  VVVVxx
+5770   8456    0       2       0       10      70      770     1770    770     5770    140     141     YNAAAA  GNMAAA  AAAAxx
+2114   8457    0       2       4       14      14      114     114     2114    2114    28      29      IDAAAA  HNMAAA  HHHHxx
+5045   8458    1       1       5       5       45      45      1045    45      5045    90      91      BMAAAA  INMAAA  OOOOxx
+1094   8459    0       2       4       14      94      94      1094    1094    1094    188     189     CQAAAA  JNMAAA  VVVVxx
+8786   8460    0       2       6       6       86      786     786     3786    8786    172     173     YZAAAA  KNMAAA  AAAAxx
+353    8461    1       1       3       13      53      353     353     353     353     106     107     PNAAAA  LNMAAA  HHHHxx
+290    8462    0       2       0       10      90      290     290     290     290     180     181     ELAAAA  MNMAAA  OOOOxx
+3376   8463    0       0       6       16      76      376     1376    3376    3376    152     153     WZAAAA  NNMAAA  VVVVxx
+9305   8464    1       1       5       5       5       305     1305    4305    9305    10      11      XTAAAA  ONMAAA  AAAAxx
+186    8465    0       2       6       6       86      186     186     186     186     172     173     EHAAAA  PNMAAA  HHHHxx
+4817   8466    1       1       7       17      17      817     817     4817    4817    34      35      HDAAAA  QNMAAA  OOOOxx
+4638   8467    0       2       8       18      38      638     638     4638    4638    76      77      KWAAAA  RNMAAA  VVVVxx
+3558   8468    0       2       8       18      58      558     1558    3558    3558    116     117     WGAAAA  SNMAAA  AAAAxx
+9285   8469    1       1       5       5       85      285     1285    4285    9285    170     171     DTAAAA  TNMAAA  HHHHxx
+848    8470    0       0       8       8       48      848     848     848     848     96      97      QGAAAA  UNMAAA  OOOOxx
+8923   8471    1       3       3       3       23      923     923     3923    8923    46      47      FFAAAA  VNMAAA  VVVVxx
+6826   8472    0       2       6       6       26      826     826     1826    6826    52      53      OCAAAA  WNMAAA  AAAAxx
+5187   8473    1       3       7       7       87      187     1187    187     5187    174     175     NRAAAA  XNMAAA  HHHHxx
+2398   8474    0       2       8       18      98      398     398     2398    2398    196     197     GOAAAA  YNMAAA  OOOOxx
+7653   8475    1       1       3       13      53      653     1653    2653    7653    106     107     JIAAAA  ZNMAAA  VVVVxx
+8835   8476    1       3       5       15      35      835     835     3835    8835    70      71      VBAAAA  AOMAAA  AAAAxx
+5736   8477    0       0       6       16      36      736     1736    736     5736    72      73      QMAAAA  BOMAAA  HHHHxx
+1238   8478    0       2       8       18      38      238     1238    1238    1238    76      77      QVAAAA  COMAAA  OOOOxx
+6021   8479    1       1       1       1       21      21      21      1021    6021    42      43      PXAAAA  DOMAAA  VVVVxx
+6815   8480    1       3       5       15      15      815     815     1815    6815    30      31      DCAAAA  EOMAAA  AAAAxx
+2549   8481    1       1       9       9       49      549     549     2549    2549    98      99      BUAAAA  FOMAAA  HHHHxx
+5657   8482    1       1       7       17      57      657     1657    657     5657    114     115     PJAAAA  GOMAAA  OOOOxx
+6855   8483    1       3       5       15      55      855     855     1855    6855    110     111     RDAAAA  HOMAAA  VVVVxx
+1225   8484    1       1       5       5       25      225     1225    1225    1225    50      51      DVAAAA  IOMAAA  AAAAxx
+7452   8485    0       0       2       12      52      452     1452    2452    7452    104     105     QAAAAA  JOMAAA  HHHHxx
+2479   8486    1       3       9       19      79      479     479     2479    2479    158     159     JRAAAA  KOMAAA  OOOOxx
+7974   8487    0       2       4       14      74      974     1974    2974    7974    148     149     SUAAAA  LOMAAA  VVVVxx
+1212   8488    0       0       2       12      12      212     1212    1212    1212    24      25      QUAAAA  MOMAAA  AAAAxx
+8883   8489    1       3       3       3       83      883     883     3883    8883    166     167     RDAAAA  NOMAAA  HHHHxx
+8150   8490    0       2       0       10      50      150     150     3150    8150    100     101     MBAAAA  OOMAAA  OOOOxx
+3392   8491    0       0       2       12      92      392     1392    3392    3392    184     185     MAAAAA  POMAAA  VVVVxx
+6774   8492    0       2       4       14      74      774     774     1774    6774    148     149     OAAAAA  QOMAAA  AAAAxx
+904    8493    0       0       4       4       4       904     904     904     904     8       9       UIAAAA  ROMAAA  HHHHxx
+5068   8494    0       0       8       8       68      68      1068    68      5068    136     137     YMAAAA  SOMAAA  OOOOxx
+9339   8495    1       3       9       19      39      339     1339    4339    9339    78      79      FVAAAA  TOMAAA  VVVVxx
+1062   8496    0       2       2       2       62      62      1062    1062    1062    124     125     WOAAAA  UOMAAA  AAAAxx
+3841   8497    1       1       1       1       41      841     1841    3841    3841    82      83      TRAAAA  VOMAAA  HHHHxx
+8924   8498    0       0       4       4       24      924     924     3924    8924    48      49      GFAAAA  WOMAAA  OOOOxx
+9795   8499    1       3       5       15      95      795     1795    4795    9795    190     191     TMAAAA  XOMAAA  VVVVxx
+3981   8500    1       1       1       1       81      981     1981    3981    3981    162     163     DXAAAA  YOMAAA  AAAAxx
+4290   8501    0       2       0       10      90      290     290     4290    4290    180     181     AJAAAA  ZOMAAA  HHHHxx
+1067   8502    1       3       7       7       67      67      1067    1067    1067    134     135     BPAAAA  APMAAA  OOOOxx
+8679   8503    1       3       9       19      79      679     679     3679    8679    158     159     VVAAAA  BPMAAA  VVVVxx
+2894   8504    0       2       4       14      94      894     894     2894    2894    188     189     IHAAAA  CPMAAA  AAAAxx
+9248   8505    0       0       8       8       48      248     1248    4248    9248    96      97      SRAAAA  DPMAAA  HHHHxx
+1072   8506    0       0       2       12      72      72      1072    1072    1072    144     145     GPAAAA  EPMAAA  OOOOxx
+3510   8507    0       2       0       10      10      510     1510    3510    3510    20      21      AFAAAA  FPMAAA  VVVVxx
+6871   8508    1       3       1       11      71      871     871     1871    6871    142     143     HEAAAA  GPMAAA  AAAAxx
+8701   8509    1       1       1       1       1       701     701     3701    8701    2       3       RWAAAA  HPMAAA  HHHHxx
+8170   8510    0       2       0       10      70      170     170     3170    8170    140     141     GCAAAA  IPMAAA  OOOOxx
+2730   8511    0       2       0       10      30      730     730     2730    2730    60      61      ABAAAA  JPMAAA  VVVVxx
+2668   8512    0       0       8       8       68      668     668     2668    2668    136     137     QYAAAA  KPMAAA  AAAAxx
+8723   8513    1       3       3       3       23      723     723     3723    8723    46      47      NXAAAA  LPMAAA  HHHHxx
+3439   8514    1       3       9       19      39      439     1439    3439    3439    78      79      HCAAAA  MPMAAA  OOOOxx
+6219   8515    1       3       9       19      19      219     219     1219    6219    38      39      FFAAAA  NPMAAA  VVVVxx
+4264   8516    0       0       4       4       64      264     264     4264    4264    128     129     AIAAAA  OPMAAA  AAAAxx
+3929   8517    1       1       9       9       29      929     1929    3929    3929    58      59      DVAAAA  PPMAAA  HHHHxx
+7      8518    1       3       7       7       7       7       7       7       7       14      15      HAAAAA  QPMAAA  OOOOxx
+3737   8519    1       1       7       17      37      737     1737    3737    3737    74      75      TNAAAA  RPMAAA  VVVVxx
+358    8520    0       2       8       18      58      358     358     358     358     116     117     UNAAAA  SPMAAA  AAAAxx
+5128   8521    0       0       8       8       28      128     1128    128     5128    56      57      GPAAAA  TPMAAA  HHHHxx
+7353   8522    1       1       3       13      53      353     1353    2353    7353    106     107     VWAAAA  UPMAAA  OOOOxx
+8758   8523    0       2       8       18      58      758     758     3758    8758    116     117     WYAAAA  VPMAAA  VVVVxx
+7284   8524    0       0       4       4       84      284     1284    2284    7284    168     169     EUAAAA  WPMAAA  AAAAxx
+4037   8525    1       1       7       17      37      37      37      4037    4037    74      75      HZAAAA  XPMAAA  HHHHxx
+435    8526    1       3       5       15      35      435     435     435     435     70      71      TQAAAA  YPMAAA  OOOOxx
+3580   8527    0       0       0       0       80      580     1580    3580    3580    160     161     SHAAAA  ZPMAAA  VVVVxx
+4554   8528    0       2       4       14      54      554     554     4554    4554    108     109     ETAAAA  AQMAAA  AAAAxx
+4337   8529    1       1       7       17      37      337     337     4337    4337    74      75      VKAAAA  BQMAAA  HHHHxx
+512    8530    0       0       2       12      12      512     512     512     512     24      25      STAAAA  CQMAAA  OOOOxx
+2032   8531    0       0       2       12      32      32      32      2032    2032    64      65      EAAAAA  DQMAAA  VVVVxx
+1755   8532    1       3       5       15      55      755     1755    1755    1755    110     111     NPAAAA  EQMAAA  AAAAxx
+9923   8533    1       3       3       3       23      923     1923    4923    9923    46      47      RRAAAA  FQMAAA  HHHHxx
+3747   8534    1       3       7       7       47      747     1747    3747    3747    94      95      DOAAAA  GQMAAA  OOOOxx
+27     8535    1       3       7       7       27      27      27      27      27      54      55      BBAAAA  HQMAAA  VVVVxx
+3075   8536    1       3       5       15      75      75      1075    3075    3075    150     151     HOAAAA  IQMAAA  AAAAxx
+6259   8537    1       3       9       19      59      259     259     1259    6259    118     119     TGAAAA  JQMAAA  HHHHxx
+2940   8538    0       0       0       0       40      940     940     2940    2940    80      81      CJAAAA  KQMAAA  OOOOxx
+5724   8539    0       0       4       4       24      724     1724    724     5724    48      49      EMAAAA  LQMAAA  VVVVxx
+5638   8540    0       2       8       18      38      638     1638    638     5638    76      77      WIAAAA  MQMAAA  AAAAxx
+479    8541    1       3       9       19      79      479     479     479     479     158     159     LSAAAA  NQMAAA  HHHHxx
+4125   8542    1       1       5       5       25      125     125     4125    4125    50      51      RCAAAA  OQMAAA  OOOOxx
+1525   8543    1       1       5       5       25      525     1525    1525    1525    50      51      RGAAAA  PQMAAA  VVVVxx
+7529   8544    1       1       9       9       29      529     1529    2529    7529    58      59      PDAAAA  QQMAAA  AAAAxx
+931    8545    1       3       1       11      31      931     931     931     931     62      63      VJAAAA  RQMAAA  HHHHxx
+5175   8546    1       3       5       15      75      175     1175    175     5175    150     151     BRAAAA  SQMAAA  OOOOxx
+6798   8547    0       2       8       18      98      798     798     1798    6798    196     197     MBAAAA  TQMAAA  VVVVxx
+2111   8548    1       3       1       11      11      111     111     2111    2111    22      23      FDAAAA  UQMAAA  AAAAxx
+6145   8549    1       1       5       5       45      145     145     1145    6145    90      91      JCAAAA  VQMAAA  HHHHxx
+4712   8550    0       0       2       12      12      712     712     4712    4712    24      25      GZAAAA  WQMAAA  OOOOxx
+3110   8551    0       2       0       10      10      110     1110    3110    3110    20      21      QPAAAA  XQMAAA  VVVVxx
+97     8552    1       1       7       17      97      97      97      97      97      194     195     TDAAAA  YQMAAA  AAAAxx
+758    8553    0       2       8       18      58      758     758     758     758     116     117     EDAAAA  ZQMAAA  HHHHxx
+1895   8554    1       3       5       15      95      895     1895    1895    1895    190     191     XUAAAA  ARMAAA  OOOOxx
+5289   8555    1       1       9       9       89      289     1289    289     5289    178     179     LVAAAA  BRMAAA  VVVVxx
+5026   8556    0       2       6       6       26      26      1026    26      5026    52      53      ILAAAA  CRMAAA  AAAAxx
+4725   8557    1       1       5       5       25      725     725     4725    4725    50      51      TZAAAA  DRMAAA  HHHHxx
+1679   8558    1       3       9       19      79      679     1679    1679    1679    158     159     PMAAAA  ERMAAA  OOOOxx
+4433   8559    1       1       3       13      33      433     433     4433    4433    66      67      NOAAAA  FRMAAA  VVVVxx
+5340   8560    0       0       0       0       40      340     1340    340     5340    80      81      KXAAAA  GRMAAA  AAAAxx
+6340   8561    0       0       0       0       40      340     340     1340    6340    80      81      WJAAAA  HRMAAA  HHHHxx
+3261   8562    1       1       1       1       61      261     1261    3261    3261    122     123     LVAAAA  IRMAAA  OOOOxx
+8108   8563    0       0       8       8       8       108     108     3108    8108    16      17      WZAAAA  JRMAAA  VVVVxx
+8785   8564    1       1       5       5       85      785     785     3785    8785    170     171     XZAAAA  KRMAAA  AAAAxx
+7391   8565    1       3       1       11      91      391     1391    2391    7391    182     183     HYAAAA  LRMAAA  HHHHxx
+1496   8566    0       0       6       16      96      496     1496    1496    1496    192     193     OFAAAA  MRMAAA  OOOOxx
+1484   8567    0       0       4       4       84      484     1484    1484    1484    168     169     CFAAAA  NRMAAA  VVVVxx
+5884   8568    0       0       4       4       84      884     1884    884     5884    168     169     ISAAAA  ORMAAA  AAAAxx
+342    8569    0       2       2       2       42      342     342     342     342     84      85      ENAAAA  PRMAAA  HHHHxx
+7659   8570    1       3       9       19      59      659     1659    2659    7659    118     119     PIAAAA  QRMAAA  OOOOxx
+6635   8571    1       3       5       15      35      635     635     1635    6635    70      71      FVAAAA  RRMAAA  VVVVxx
+8507   8572    1       3       7       7       7       507     507     3507    8507    14      15      FPAAAA  SRMAAA  AAAAxx
+2583   8573    1       3       3       3       83      583     583     2583    2583    166     167     JVAAAA  TRMAAA  HHHHxx
+6533   8574    1       1       3       13      33      533     533     1533    6533    66      67      HRAAAA  URMAAA  OOOOxx
+5879   8575    1       3       9       19      79      879     1879    879     5879    158     159     DSAAAA  VRMAAA  VVVVxx
+5511   8576    1       3       1       11      11      511     1511    511     5511    22      23      ZDAAAA  WRMAAA  AAAAxx
+3682   8577    0       2       2       2       82      682     1682    3682    3682    164     165     QLAAAA  XRMAAA  HHHHxx
+7182   8578    0       2       2       2       82      182     1182    2182    7182    164     165     GQAAAA  YRMAAA  OOOOxx
+1409   8579    1       1       9       9       9       409     1409    1409    1409    18      19      FCAAAA  ZRMAAA  VVVVxx
+3363   8580    1       3       3       3       63      363     1363    3363    3363    126     127     JZAAAA  ASMAAA  AAAAxx
+729    8581    1       1       9       9       29      729     729     729     729     58      59      BCAAAA  BSMAAA  HHHHxx
+5857   8582    1       1       7       17      57      857     1857    857     5857    114     115     HRAAAA  CSMAAA  OOOOxx
+235    8583    1       3       5       15      35      235     235     235     235     70      71      BJAAAA  DSMAAA  VVVVxx
+193    8584    1       1       3       13      93      193     193     193     193     186     187     LHAAAA  ESMAAA  AAAAxx
+5586   8585    0       2       6       6       86      586     1586    586     5586    172     173     WGAAAA  FSMAAA  HHHHxx
+6203   8586    1       3       3       3       3       203     203     1203    6203    6       7       PEAAAA  GSMAAA  OOOOxx
+6795   8587    1       3       5       15      95      795     795     1795    6795    190     191     JBAAAA  HSMAAA  VVVVxx
+3211   8588    1       3       1       11      11      211     1211    3211    3211    22      23      NTAAAA  ISMAAA  AAAAxx
+9763   8589    1       3       3       3       63      763     1763    4763    9763    126     127     NLAAAA  JSMAAA  HHHHxx
+9043   8590    1       3       3       3       43      43      1043    4043    9043    86      87      VJAAAA  KSMAAA  OOOOxx
+2854   8591    0       2       4       14      54      854     854     2854    2854    108     109     UFAAAA  LSMAAA  VVVVxx
+565    8592    1       1       5       5       65      565     565     565     565     130     131     TVAAAA  MSMAAA  AAAAxx
+9284   8593    0       0       4       4       84      284     1284    4284    9284    168     169     CTAAAA  NSMAAA  HHHHxx
+7886   8594    0       2       6       6       86      886     1886    2886    7886    172     173     IRAAAA  OSMAAA  OOOOxx
+122    8595    0       2       2       2       22      122     122     122     122     44      45      SEAAAA  PSMAAA  VVVVxx
+4934   8596    0       2       4       14      34      934     934     4934    4934    68      69      UHAAAA  QSMAAA  AAAAxx
+1766   8597    0       2       6       6       66      766     1766    1766    1766    132     133     YPAAAA  RSMAAA  HHHHxx
+2554   8598    0       2       4       14      54      554     554     2554    2554    108     109     GUAAAA  SSMAAA  OOOOxx
+488    8599    0       0       8       8       88      488     488     488     488     176     177     USAAAA  TSMAAA  VVVVxx
+825    8600    1       1       5       5       25      825     825     825     825     50      51      TFAAAA  USMAAA  AAAAxx
+678    8601    0       2       8       18      78      678     678     678     678     156     157     CAAAAA  VSMAAA  HHHHxx
+4543   8602    1       3       3       3       43      543     543     4543    4543    86      87      TSAAAA  WSMAAA  OOOOxx
+1699   8603    1       3       9       19      99      699     1699    1699    1699    198     199     JNAAAA  XSMAAA  VVVVxx
+3771   8604    1       3       1       11      71      771     1771    3771    3771    142     143     BPAAAA  YSMAAA  AAAAxx
+1234   8605    0       2       4       14      34      234     1234    1234    1234    68      69      MVAAAA  ZSMAAA  HHHHxx
+4152   8606    0       0       2       12      52      152     152     4152    4152    104     105     SDAAAA  ATMAAA  OOOOxx
+1632   8607    0       0       2       12      32      632     1632    1632    1632    64      65      UKAAAA  BTMAAA  VVVVxx
+4988   8608    0       0       8       8       88      988     988     4988    4988    176     177     WJAAAA  CTMAAA  AAAAxx
+1980   8609    0       0       0       0       80      980     1980    1980    1980    160     161     EYAAAA  DTMAAA  HHHHxx
+7479   8610    1       3       9       19      79      479     1479    2479    7479    158     159     RBAAAA  ETMAAA  OOOOxx
+2586   8611    0       2       6       6       86      586     586     2586    2586    172     173     MVAAAA  FTMAAA  VVVVxx
+5433   8612    1       1       3       13      33      433     1433    433     5433    66      67      ZAAAAA  GTMAAA  AAAAxx
+2261   8613    1       1       1       1       61      261     261     2261    2261    122     123     ZIAAAA  HTMAAA  HHHHxx
+1180   8614    0       0       0       0       80      180     1180    1180    1180    160     161     KTAAAA  ITMAAA  OOOOxx
+3938   8615    0       2       8       18      38      938     1938    3938    3938    76      77      MVAAAA  JTMAAA  VVVVxx
+6714   8616    0       2       4       14      14      714     714     1714    6714    28      29      GYAAAA  KTMAAA  AAAAxx
+2890   8617    0       2       0       10      90      890     890     2890    2890    180     181     EHAAAA  LTMAAA  HHHHxx
+7379   8618    1       3       9       19      79      379     1379    2379    7379    158     159     VXAAAA  MTMAAA  OOOOxx
+5896   8619    0       0       6       16      96      896     1896    896     5896    192     193     USAAAA  NTMAAA  VVVVxx
+5949   8620    1       1       9       9       49      949     1949    949     5949    98      99      VUAAAA  OTMAAA  AAAAxx
+3194   8621    0       2       4       14      94      194     1194    3194    3194    188     189     WSAAAA  PTMAAA  HHHHxx
+9325   8622    1       1       5       5       25      325     1325    4325    9325    50      51      RUAAAA  QTMAAA  OOOOxx
+9531   8623    1       3       1       11      31      531     1531    4531    9531    62      63      PCAAAA  RTMAAA  VVVVxx
+711    8624    1       3       1       11      11      711     711     711     711     22      23      JBAAAA  STMAAA  AAAAxx
+2450   8625    0       2       0       10      50      450     450     2450    2450    100     101     GQAAAA  TTMAAA  HHHHxx
+1929   8626    1       1       9       9       29      929     1929    1929    1929    58      59      FWAAAA  UTMAAA  OOOOxx
+6165   8627    1       1       5       5       65      165     165     1165    6165    130     131     DDAAAA  VTMAAA  VVVVxx
+4050   8628    0       2       0       10      50      50      50      4050    4050    100     101     UZAAAA  WTMAAA  AAAAxx
+9011   8629    1       3       1       11      11      11      1011    4011    9011    22      23      PIAAAA  XTMAAA  HHHHxx
+7916   8630    0       0       6       16      16      916     1916    2916    7916    32      33      MSAAAA  YTMAAA  OOOOxx
+9136   8631    0       0       6       16      36      136     1136    4136    9136    72      73      KNAAAA  ZTMAAA  VVVVxx
+8782   8632    0       2       2       2       82      782     782     3782    8782    164     165     UZAAAA  AUMAAA  AAAAxx
+8491   8633    1       3       1       11      91      491     491     3491    8491    182     183     POAAAA  BUMAAA  HHHHxx
+5114   8634    0       2       4       14      14      114     1114    114     5114    28      29      SOAAAA  CUMAAA  OOOOxx
+5815   8635    1       3       5       15      15      815     1815    815     5815    30      31      RPAAAA  DUMAAA  VVVVxx
+5628   8636    0       0       8       8       28      628     1628    628     5628    56      57      MIAAAA  EUMAAA  AAAAxx
+810    8637    0       2       0       10      10      810     810     810     810     20      21      EFAAAA  FUMAAA  HHHHxx
+6178   8638    0       2       8       18      78      178     178     1178    6178    156     157     QDAAAA  GUMAAA  OOOOxx
+2619   8639    1       3       9       19      19      619     619     2619    2619    38      39      TWAAAA  HUMAAA  VVVVxx
+3340   8640    0       0       0       0       40      340     1340    3340    3340    80      81      MYAAAA  IUMAAA  AAAAxx
+2491   8641    1       3       1       11      91      491     491     2491    2491    182     183     VRAAAA  JUMAAA  HHHHxx
+3574   8642    0       2       4       14      74      574     1574    3574    3574    148     149     MHAAAA  KUMAAA  OOOOxx
+6754   8643    0       2       4       14      54      754     754     1754    6754    108     109     UZAAAA  LUMAAA  VVVVxx
+1566   8644    0       2       6       6       66      566     1566    1566    1566    132     133     GIAAAA  MUMAAA  AAAAxx
+9174   8645    0       2       4       14      74      174     1174    4174    9174    148     149     WOAAAA  NUMAAA  HHHHxx
+1520   8646    0       0       0       0       20      520     1520    1520    1520    40      41      MGAAAA  OUMAAA  OOOOxx
+2691   8647    1       3       1       11      91      691     691     2691    2691    182     183     NZAAAA  PUMAAA  VVVVxx
+6961   8648    1       1       1       1       61      961     961     1961    6961    122     123     THAAAA  QUMAAA  AAAAxx
+5722   8649    0       2       2       2       22      722     1722    722     5722    44      45      CMAAAA  RUMAAA  HHHHxx
+9707   8650    1       3       7       7       7       707     1707    4707    9707    14      15      JJAAAA  SUMAAA  OOOOxx
+2891   8651    1       3       1       11      91      891     891     2891    2891    182     183     FHAAAA  TUMAAA  VVVVxx
+341    8652    1       1       1       1       41      341     341     341     341     82      83      DNAAAA  UUMAAA  AAAAxx
+4690   8653    0       2       0       10      90      690     690     4690    4690    180     181     KYAAAA  VUMAAA  HHHHxx
+7841   8654    1       1       1       1       41      841     1841    2841    7841    82      83      PPAAAA  WUMAAA  OOOOxx
+6615   8655    1       3       5       15      15      615     615     1615    6615    30      31      LUAAAA  XUMAAA  VVVVxx
+9169   8656    1       1       9       9       69      169     1169    4169    9169    138     139     ROAAAA  YUMAAA  AAAAxx
+6689   8657    1       1       9       9       89      689     689     1689    6689    178     179     HXAAAA  ZUMAAA  HHHHxx
+8721   8658    1       1       1       1       21      721     721     3721    8721    42      43      LXAAAA  AVMAAA  OOOOxx
+7508   8659    0       0       8       8       8       508     1508    2508    7508    16      17      UCAAAA  BVMAAA  VVVVxx
+8631   8660    1       3       1       11      31      631     631     3631    8631    62      63      ZTAAAA  CVMAAA  AAAAxx
+480    8661    0       0       0       0       80      480     480     480     480     160     161     MSAAAA  DVMAAA  HHHHxx
+7094   8662    0       2       4       14      94      94      1094    2094    7094    188     189     WMAAAA  EVMAAA  OOOOxx
+319    8663    1       3       9       19      19      319     319     319     319     38      39      HMAAAA  FVMAAA  VVVVxx
+9421   8664    1       1       1       1       21      421     1421    4421    9421    42      43      JYAAAA  GVMAAA  AAAAxx
+4352   8665    0       0       2       12      52      352     352     4352    4352    104     105     KLAAAA  HVMAAA  HHHHxx
+5019   8666    1       3       9       19      19      19      1019    19      5019    38      39      BLAAAA  IVMAAA  OOOOxx
+3956   8667    0       0       6       16      56      956     1956    3956    3956    112     113     EWAAAA  JVMAAA  VVVVxx
+114    8668    0       2       4       14      14      114     114     114     114     28      29      KEAAAA  KVMAAA  AAAAxx
+1196   8669    0       0       6       16      96      196     1196    1196    1196    192     193     AUAAAA  LVMAAA  HHHHxx
+1407   8670    1       3       7       7       7       407     1407    1407    1407    14      15      DCAAAA  MVMAAA  OOOOxx
+7432   8671    0       0       2       12      32      432     1432    2432    7432    64      65      WZAAAA  NVMAAA  VVVVxx
+3141   8672    1       1       1       1       41      141     1141    3141    3141    82      83      VQAAAA  OVMAAA  AAAAxx
+2073   8673    1       1       3       13      73      73      73      2073    2073    146     147     TBAAAA  PVMAAA  HHHHxx
+3400   8674    0       0       0       0       0       400     1400    3400    3400    0       1       UAAAAA  QVMAAA  OOOOxx
+505    8675    1       1       5       5       5       505     505     505     505     10      11      LTAAAA  RVMAAA  VVVVxx
+1263   8676    1       3       3       3       63      263     1263    1263    1263    126     127     PWAAAA  SVMAAA  AAAAxx
+190    8677    0       2       0       10      90      190     190     190     190     180     181     IHAAAA  TVMAAA  HHHHxx
+6686   8678    0       2       6       6       86      686     686     1686    6686    172     173     EXAAAA  UVMAAA  OOOOxx
+9821   8679    1       1       1       1       21      821     1821    4821    9821    42      43      TNAAAA  VVMAAA  VVVVxx
+1119   8680    1       3       9       19      19      119     1119    1119    1119    38      39      BRAAAA  WVMAAA  AAAAxx
+2955   8681    1       3       5       15      55      955     955     2955    2955    110     111     RJAAAA  XVMAAA  HHHHxx
+224    8682    0       0       4       4       24      224     224     224     224     48      49      QIAAAA  YVMAAA  OOOOxx
+7562   8683    0       2       2       2       62      562     1562    2562    7562    124     125     WEAAAA  ZVMAAA  VVVVxx
+8845   8684    1       1       5       5       45      845     845     3845    8845    90      91      FCAAAA  AWMAAA  AAAAxx
+5405   8685    1       1       5       5       5       405     1405    405     5405    10      11      XZAAAA  BWMAAA  HHHHxx
+9192   8686    0       0       2       12      92      192     1192    4192    9192    184     185     OPAAAA  CWMAAA  OOOOxx
+4927   8687    1       3       7       7       27      927     927     4927    4927    54      55      NHAAAA  DWMAAA  VVVVxx
+997    8688    1       1       7       17      97      997     997     997     997     194     195     JMAAAA  EWMAAA  AAAAxx
+989    8689    1       1       9       9       89      989     989     989     989     178     179     BMAAAA  FWMAAA  HHHHxx
+7258   8690    0       2       8       18      58      258     1258    2258    7258    116     117     ETAAAA  GWMAAA  OOOOxx
+6899   8691    1       3       9       19      99      899     899     1899    6899    198     199     JFAAAA  HWMAAA  VVVVxx
+1770   8692    0       2       0       10      70      770     1770    1770    1770    140     141     CQAAAA  IWMAAA  AAAAxx
+4423   8693    1       3       3       3       23      423     423     4423    4423    46      47      DOAAAA  JWMAAA  HHHHxx
+5671   8694    1       3       1       11      71      671     1671    671     5671    142     143     DKAAAA  KWMAAA  OOOOxx
+8393   8695    1       1       3       13      93      393     393     3393    8393    186     187     VKAAAA  LWMAAA  VVVVxx
+4355   8696    1       3       5       15      55      355     355     4355    4355    110     111     NLAAAA  MWMAAA  AAAAxx
+3919   8697    1       3       9       19      19      919     1919    3919    3919    38      39      TUAAAA  NWMAAA  HHHHxx
+338    8698    0       2       8       18      38      338     338     338     338     76      77      ANAAAA  OWMAAA  OOOOxx
+5790   8699    0       2       0       10      90      790     1790    790     5790    180     181     SOAAAA  PWMAAA  VVVVxx
+1452   8700    0       0       2       12      52      452     1452    1452    1452    104     105     WDAAAA  QWMAAA  AAAAxx
+939    8701    1       3       9       19      39      939     939     939     939     78      79      DKAAAA  RWMAAA  HHHHxx
+8913   8702    1       1       3       13      13      913     913     3913    8913    26      27      VEAAAA  SWMAAA  OOOOxx
+7157   8703    1       1       7       17      57      157     1157    2157    7157    114     115     HPAAAA  TWMAAA  VVVVxx
+7240   8704    0       0       0       0       40      240     1240    2240    7240    80      81      MSAAAA  UWMAAA  AAAAxx
+3492   8705    0       0       2       12      92      492     1492    3492    3492    184     185     IEAAAA  VWMAAA  HHHHxx
+3464   8706    0       0       4       4       64      464     1464    3464    3464    128     129     GDAAAA  WWMAAA  OOOOxx
+388    8707    0       0       8       8       88      388     388     388     388     176     177     YOAAAA  XWMAAA  VVVVxx
+4135   8708    1       3       5       15      35      135     135     4135    4135    70      71      BDAAAA  YWMAAA  AAAAxx
+1194   8709    0       2       4       14      94      194     1194    1194    1194    188     189     YTAAAA  ZWMAAA  HHHHxx
+5476   8710    0       0       6       16      76      476     1476    476     5476    152     153     QCAAAA  AXMAAA  OOOOxx
+9844   8711    0       0       4       4       44      844     1844    4844    9844    88      89      QOAAAA  BXMAAA  VVVVxx
+9364   8712    0       0       4       4       64      364     1364    4364    9364    128     129     EWAAAA  CXMAAA  AAAAxx
+5238   8713    0       2       8       18      38      238     1238    238     5238    76      77      MTAAAA  DXMAAA  HHHHxx
+3712   8714    0       0       2       12      12      712     1712    3712    3712    24      25      UMAAAA  EXMAAA  OOOOxx
+6189   8715    1       1       9       9       89      189     189     1189    6189    178     179     BEAAAA  FXMAAA  VVVVxx
+5257   8716    1       1       7       17      57      257     1257    257     5257    114     115     FUAAAA  GXMAAA  AAAAxx
+81     8717    1       1       1       1       81      81      81      81      81      162     163     DDAAAA  HXMAAA  HHHHxx
+3289   8718    1       1       9       9       89      289     1289    3289    3289    178     179     NWAAAA  IXMAAA  OOOOxx
+1177   8719    1       1       7       17      77      177     1177    1177    1177    154     155     HTAAAA  JXMAAA  VVVVxx
+5038   8720    0       2       8       18      38      38      1038    38      5038    76      77      ULAAAA  KXMAAA  AAAAxx
+325    8721    1       1       5       5       25      325     325     325     325     50      51      NMAAAA  LXMAAA  HHHHxx
+7221   8722    1       1       1       1       21      221     1221    2221    7221    42      43      TRAAAA  MXMAAA  OOOOxx
+7123   8723    1       3       3       3       23      123     1123    2123    7123    46      47      ZNAAAA  NXMAAA  VVVVxx
+6364   8724    0       0       4       4       64      364     364     1364    6364    128     129     UKAAAA  OXMAAA  AAAAxx
+4468   8725    0       0       8       8       68      468     468     4468    4468    136     137     WPAAAA  PXMAAA  HHHHxx
+9185   8726    1       1       5       5       85      185     1185    4185    9185    170     171     HPAAAA  QXMAAA  OOOOxx
+4158   8727    0       2       8       18      58      158     158     4158    4158    116     117     YDAAAA  RXMAAA  VVVVxx
+9439   8728    1       3       9       19      39      439     1439    4439    9439    78      79      BZAAAA  SXMAAA  AAAAxx
+7759   8729    1       3       9       19      59      759     1759    2759    7759    118     119     LMAAAA  TXMAAA  HHHHxx
+3325   8730    1       1       5       5       25      325     1325    3325    3325    50      51      XXAAAA  UXMAAA  OOOOxx
+7991   8731    1       3       1       11      91      991     1991    2991    7991    182     183     JVAAAA  VXMAAA  VVVVxx
+1650   8732    0       2       0       10      50      650     1650    1650    1650    100     101     MLAAAA  WXMAAA  AAAAxx
+8395   8733    1       3       5       15      95      395     395     3395    8395    190     191     XKAAAA  XXMAAA  HHHHxx
+286    8734    0       2       6       6       86      286     286     286     286     172     173     ALAAAA  YXMAAA  OOOOxx
+1507   8735    1       3       7       7       7       507     1507    1507    1507    14      15      ZFAAAA  ZXMAAA  VVVVxx
+4122   8736    0       2       2       2       22      122     122     4122    4122    44      45      OCAAAA  AYMAAA  AAAAxx
+2625   8737    1       1       5       5       25      625     625     2625    2625    50      51      ZWAAAA  BYMAAA  HHHHxx
+1140   8738    0       0       0       0       40      140     1140    1140    1140    80      81      WRAAAA  CYMAAA  OOOOxx
+5262   8739    0       2       2       2       62      262     1262    262     5262    124     125     KUAAAA  DYMAAA  VVVVxx
+4919   8740    1       3       9       19      19      919     919     4919    4919    38      39      FHAAAA  EYMAAA  AAAAxx
+7266   8741    0       2       6       6       66      266     1266    2266    7266    132     133     MTAAAA  FYMAAA  HHHHxx
+630    8742    0       2       0       10      30      630     630     630     630     60      61      GYAAAA  GYMAAA  OOOOxx
+2129   8743    1       1       9       9       29      129     129     2129    2129    58      59      XDAAAA  HYMAAA  VVVVxx
+9552   8744    0       0       2       12      52      552     1552    4552    9552    104     105     KDAAAA  IYMAAA  AAAAxx
+3018   8745    0       2       8       18      18      18      1018    3018    3018    36      37      CMAAAA  JYMAAA  HHHHxx
+7145   8746    1       1       5       5       45      145     1145    2145    7145    90      91      VOAAAA  KYMAAA  OOOOxx
+1633   8747    1       1       3       13      33      633     1633    1633    1633    66      67      VKAAAA  LYMAAA  VVVVxx
+7957   8748    1       1       7       17      57      957     1957    2957    7957    114     115     BUAAAA  MYMAAA  AAAAxx
+774    8749    0       2       4       14      74      774     774     774     774     148     149     UDAAAA  NYMAAA  HHHHxx
+9371   8750    1       3       1       11      71      371     1371    4371    9371    142     143     LWAAAA  OYMAAA  OOOOxx
+6007   8751    1       3       7       7       7       7       7       1007    6007    14      15      BXAAAA  PYMAAA  VVVVxx
+5277   8752    1       1       7       17      77      277     1277    277     5277    154     155     ZUAAAA  QYMAAA  AAAAxx
+9426   8753    0       2       6       6       26      426     1426    4426    9426    52      53      OYAAAA  RYMAAA  HHHHxx
+9190   8754    0       2       0       10      90      190     1190    4190    9190    180     181     MPAAAA  SYMAAA  OOOOxx
+8996   8755    0       0       6       16      96      996     996     3996    8996    192     193     AIAAAA  TYMAAA  VVVVxx
+3409   8756    1       1       9       9       9       409     1409    3409    3409    18      19      DBAAAA  UYMAAA  AAAAxx
+7212   8757    0       0       2       12      12      212     1212    2212    7212    24      25      KRAAAA  VYMAAA  HHHHxx
+416    8758    0       0       6       16      16      416     416     416     416     32      33      AQAAAA  WYMAAA  OOOOxx
+7211   8759    1       3       1       11      11      211     1211    2211    7211    22      23      JRAAAA  XYMAAA  VVVVxx
+7454   8760    0       2       4       14      54      454     1454    2454    7454    108     109     SAAAAA  YYMAAA  AAAAxx
+8417   8761    1       1       7       17      17      417     417     3417    8417    34      35      TLAAAA  ZYMAAA  HHHHxx
+5562   8762    0       2       2       2       62      562     1562    562     5562    124     125     YFAAAA  AZMAAA  OOOOxx
+4996   8763    0       0       6       16      96      996     996     4996    4996    192     193     EKAAAA  BZMAAA  VVVVxx
+5718   8764    0       2       8       18      18      718     1718    718     5718    36      37      YLAAAA  CZMAAA  AAAAxx
+7838   8765    0       2       8       18      38      838     1838    2838    7838    76      77      MPAAAA  DZMAAA  HHHHxx
+7715   8766    1       3       5       15      15      715     1715    2715    7715    30      31      TKAAAA  EZMAAA  OOOOxx
+2780   8767    0       0       0       0       80      780     780     2780    2780    160     161     YCAAAA  FZMAAA  VVVVxx
+1013   8768    1       1       3       13      13      13      1013    1013    1013    26      27      ZMAAAA  GZMAAA  AAAAxx
+8465   8769    1       1       5       5       65      465     465     3465    8465    130     131     PNAAAA  HZMAAA  HHHHxx
+7976   8770    0       0       6       16      76      976     1976    2976    7976    152     153     UUAAAA  IZMAAA  OOOOxx
+7150   8771    0       2       0       10      50      150     1150    2150    7150    100     101     APAAAA  JZMAAA  VVVVxx
+6471   8772    1       3       1       11      71      471     471     1471    6471    142     143     XOAAAA  KZMAAA  AAAAxx
+1927   8773    1       3       7       7       27      927     1927    1927    1927    54      55      DWAAAA  LZMAAA  HHHHxx
+227    8774    1       3       7       7       27      227     227     227     227     54      55      TIAAAA  MZMAAA  OOOOxx
+6462   8775    0       2       2       2       62      462     462     1462    6462    124     125     OOAAAA  NZMAAA  VVVVxx
+5227   8776    1       3       7       7       27      227     1227    227     5227    54      55      BTAAAA  OZMAAA  AAAAxx
+1074   8777    0       2       4       14      74      74      1074    1074    1074    148     149     IPAAAA  PZMAAA  HHHHxx
+9448   8778    0       0       8       8       48      448     1448    4448    9448    96      97      KZAAAA  QZMAAA  OOOOxx
+4459   8779    1       3       9       19      59      459     459     4459    4459    118     119     NPAAAA  RZMAAA  VVVVxx
+2478   8780    0       2       8       18      78      478     478     2478    2478    156     157     IRAAAA  SZMAAA  AAAAxx
+5005   8781    1       1       5       5       5       5       1005    5       5005    10      11      NKAAAA  TZMAAA  HHHHxx
+2418   8782    0       2       8       18      18      418     418     2418    2418    36      37      APAAAA  UZMAAA  OOOOxx
+6991   8783    1       3       1       11      91      991     991     1991    6991    182     183     XIAAAA  VZMAAA  VVVVxx
+4729   8784    1       1       9       9       29      729     729     4729    4729    58      59      XZAAAA  WZMAAA  AAAAxx
+3548   8785    0       0       8       8       48      548     1548    3548    3548    96      97      MGAAAA  XZMAAA  HHHHxx
+9616   8786    0       0       6       16      16      616     1616    4616    9616    32      33      WFAAAA  YZMAAA  OOOOxx
+2901   8787    1       1       1       1       1       901     901     2901    2901    2       3       PHAAAA  ZZMAAA  VVVVxx
+10     8788    0       2       0       10      10      10      10      10      10      20      21      KAAAAA  AANAAA  AAAAxx
+2637   8789    1       1       7       17      37      637     637     2637    2637    74      75      LXAAAA  BANAAA  HHHHxx
+6747   8790    1       3       7       7       47      747     747     1747    6747    94      95      NZAAAA  CANAAA  OOOOxx
+797    8791    1       1       7       17      97      797     797     797     797     194     195     REAAAA  DANAAA  VVVVxx
+7609   8792    1       1       9       9       9       609     1609    2609    7609    18      19      RGAAAA  EANAAA  AAAAxx
+8290   8793    0       2       0       10      90      290     290     3290    8290    180     181     WGAAAA  FANAAA  HHHHxx
+8765   8794    1       1       5       5       65      765     765     3765    8765    130     131     DZAAAA  GANAAA  OOOOxx
+8053   8795    1       1       3       13      53      53      53      3053    8053    106     107     TXAAAA  HANAAA  VVVVxx
+5602   8796    0       2       2       2       2       602     1602    602     5602    4       5       MHAAAA  IANAAA  AAAAxx
+3672   8797    0       0       2       12      72      672     1672    3672    3672    144     145     GLAAAA  JANAAA  HHHHxx
+7513   8798    1       1       3       13      13      513     1513    2513    7513    26      27      ZCAAAA  KANAAA  OOOOxx
+3462   8799    0       2       2       2       62      462     1462    3462    3462    124     125     EDAAAA  LANAAA  VVVVxx
+4457   8800    1       1       7       17      57      457     457     4457    4457    114     115     LPAAAA  MANAAA  AAAAxx
+6547   8801    1       3       7       7       47      547     547     1547    6547    94      95      VRAAAA  NANAAA  HHHHxx
+7417   8802    1       1       7       17      17      417     1417    2417    7417    34      35      HZAAAA  OANAAA  OOOOxx
+8641   8803    1       1       1       1       41      641     641     3641    8641    82      83      JUAAAA  PANAAA  VVVVxx
+149    8804    1       1       9       9       49      149     149     149     149     98      99      TFAAAA  QANAAA  AAAAxx
+5041   8805    1       1       1       1       41      41      1041    41      5041    82      83      XLAAAA  RANAAA  HHHHxx
+9232   8806    0       0       2       12      32      232     1232    4232    9232    64      65      CRAAAA  SANAAA  OOOOxx
+3603   8807    1       3       3       3       3       603     1603    3603    3603    6       7       PIAAAA  TANAAA  VVVVxx
+2792   8808    0       0       2       12      92      792     792     2792    2792    184     185     KDAAAA  UANAAA  AAAAxx
+6620   8809    0       0       0       0       20      620     620     1620    6620    40      41      QUAAAA  VANAAA  HHHHxx
+4000   8810    0       0       0       0       0       0       0       4000    4000    0       1       WXAAAA  WANAAA  OOOOxx
+659    8811    1       3       9       19      59      659     659     659     659     118     119     JZAAAA  XANAAA  VVVVxx
+8174   8812    0       2       4       14      74      174     174     3174    8174    148     149     KCAAAA  YANAAA  AAAAxx
+4599   8813    1       3       9       19      99      599     599     4599    4599    198     199     XUAAAA  ZANAAA  HHHHxx
+7851   8814    1       3       1       11      51      851     1851    2851    7851    102     103     ZPAAAA  ABNAAA  OOOOxx
+6284   8815    0       0       4       4       84      284     284     1284    6284    168     169     SHAAAA  BBNAAA  VVVVxx
+7116   8816    0       0       6       16      16      116     1116    2116    7116    32      33      SNAAAA  CBNAAA  AAAAxx
+5595   8817    1       3       5       15      95      595     1595    595     5595    190     191     FHAAAA  DBNAAA  HHHHxx
+2903   8818    1       3       3       3       3       903     903     2903    2903    6       7       RHAAAA  EBNAAA  OOOOxx
+5948   8819    0       0       8       8       48      948     1948    948     5948    96      97      UUAAAA  FBNAAA  VVVVxx
+225    8820    1       1       5       5       25      225     225     225     225     50      51      RIAAAA  GBNAAA  AAAAxx
+524    8821    0       0       4       4       24      524     524     524     524     48      49      EUAAAA  HBNAAA  HHHHxx
+7639   8822    1       3       9       19      39      639     1639    2639    7639    78      79      VHAAAA  IBNAAA  OOOOxx
+7297   8823    1       1       7       17      97      297     1297    2297    7297    194     195     RUAAAA  JBNAAA  VVVVxx
+2606   8824    0       2       6       6       6       606     606     2606    2606    12      13      GWAAAA  KBNAAA  AAAAxx
+4771   8825    1       3       1       11      71      771     771     4771    4771    142     143     NBAAAA  LBNAAA  HHHHxx
+8162   8826    0       2       2       2       62      162     162     3162    8162    124     125     YBAAAA  MBNAAA  OOOOxx
+8999   8827    1       3       9       19      99      999     999     3999    8999    198     199     DIAAAA  NBNAAA  VVVVxx
+2309   8828    1       1       9       9       9       309     309     2309    2309    18      19      VKAAAA  OBNAAA  AAAAxx
+3594   8829    0       2       4       14      94      594     1594    3594    3594    188     189     GIAAAA  PBNAAA  HHHHxx
+6092   8830    0       0       2       12      92      92      92      1092    6092    184     185     IAAAAA  QBNAAA  OOOOxx
+7467   8831    1       3       7       7       67      467     1467    2467    7467    134     135     FBAAAA  RBNAAA  VVVVxx
+6986   8832    0       2       6       6       86      986     986     1986    6986    172     173     SIAAAA  SBNAAA  AAAAxx
+9898   8833    0       2       8       18      98      898     1898    4898    9898    196     197     SQAAAA  TBNAAA  HHHHxx
+9578   8834    0       2       8       18      78      578     1578    4578    9578    156     157     KEAAAA  UBNAAA  OOOOxx
+156    8835    0       0       6       16      56      156     156     156     156     112     113     AGAAAA  VBNAAA  VVVVxx
+5810   8836    0       2       0       10      10      810     1810    810     5810    20      21      MPAAAA  WBNAAA  AAAAxx
+790    8837    0       2       0       10      90      790     790     790     790     180     181     KEAAAA  XBNAAA  HHHHxx
+6840   8838    0       0       0       0       40      840     840     1840    6840    80      81      CDAAAA  YBNAAA  OOOOxx
+6725   8839    1       1       5       5       25      725     725     1725    6725    50      51      RYAAAA  ZBNAAA  VVVVxx
+5528   8840    0       0       8       8       28      528     1528    528     5528    56      57      QEAAAA  ACNAAA  AAAAxx
+4120   8841    0       0       0       0       20      120     120     4120    4120    40      41      MCAAAA  BCNAAA  HHHHxx
+6694   8842    0       2       4       14      94      694     694     1694    6694    188     189     MXAAAA  CCNAAA  OOOOxx
+3552   8843    0       0       2       12      52      552     1552    3552    3552    104     105     QGAAAA  DCNAAA  VVVVxx
+1478   8844    0       2       8       18      78      478     1478    1478    1478    156     157     WEAAAA  ECNAAA  AAAAxx
+8084   8845    0       0       4       4       84      84      84      3084    8084    168     169     YYAAAA  FCNAAA  HHHHxx
+7578   8846    0       2       8       18      78      578     1578    2578    7578    156     157     MFAAAA  GCNAAA  OOOOxx
+6314   8847    0       2       4       14      14      314     314     1314    6314    28      29      WIAAAA  HCNAAA  VVVVxx
+6123   8848    1       3       3       3       23      123     123     1123    6123    46      47      NBAAAA  ICNAAA  AAAAxx
+9443   8849    1       3       3       3       43      443     1443    4443    9443    86      87      FZAAAA  JCNAAA  HHHHxx
+9628   8850    0       0       8       8       28      628     1628    4628    9628    56      57      IGAAAA  KCNAAA  OOOOxx
+8508   8851    0       0       8       8       8       508     508     3508    8508    16      17      GPAAAA  LCNAAA  VVVVxx
+5552   8852    0       0       2       12      52      552     1552    552     5552    104     105     OFAAAA  MCNAAA  AAAAxx
+5327   8853    1       3       7       7       27      327     1327    327     5327    54      55      XWAAAA  NCNAAA  HHHHxx
+7771   8854    1       3       1       11      71      771     1771    2771    7771    142     143     XMAAAA  OCNAAA  OOOOxx
+8932   8855    0       0       2       12      32      932     932     3932    8932    64      65      OFAAAA  PCNAAA  VVVVxx
+3526   8856    0       2       6       6       26      526     1526    3526    3526    52      53      QFAAAA  QCNAAA  AAAAxx
+4340   8857    0       0       0       0       40      340     340     4340    4340    80      81      YKAAAA  RCNAAA  HHHHxx
+9419   8858    1       3       9       19      19      419     1419    4419    9419    38      39      HYAAAA  SCNAAA  OOOOxx
+8421   8859    1       1       1       1       21      421     421     3421    8421    42      43      XLAAAA  TCNAAA  VVVVxx
+7431   8860    1       3       1       11      31      431     1431    2431    7431    62      63      VZAAAA  UCNAAA  AAAAxx
+172    8861    0       0       2       12      72      172     172     172     172     144     145     QGAAAA  VCNAAA  HHHHxx
+3279   8862    1       3       9       19      79      279     1279    3279    3279    158     159     DWAAAA  WCNAAA  OOOOxx
+1508   8863    0       0       8       8       8       508     1508    1508    1508    16      17      AGAAAA  XCNAAA  VVVVxx
+7091   8864    1       3       1       11      91      91      1091    2091    7091    182     183     TMAAAA  YCNAAA  AAAAxx
+1419   8865    1       3       9       19      19      419     1419    1419    1419    38      39      PCAAAA  ZCNAAA  HHHHxx
+3032   8866    0       0       2       12      32      32      1032    3032    3032    64      65      QMAAAA  ADNAAA  OOOOxx
+8683   8867    1       3       3       3       83      683     683     3683    8683    166     167     ZVAAAA  BDNAAA  VVVVxx
+4763   8868    1       3       3       3       63      763     763     4763    4763    126     127     FBAAAA  CDNAAA  AAAAxx
+4424   8869    0       0       4       4       24      424     424     4424    4424    48      49      EOAAAA  DDNAAA  HHHHxx
+8640   8870    0       0       0       0       40      640     640     3640    8640    80      81      IUAAAA  EDNAAA  OOOOxx
+7187   8871    1       3       7       7       87      187     1187    2187    7187    174     175     LQAAAA  FDNAAA  VVVVxx
+6247   8872    1       3       7       7       47      247     247     1247    6247    94      95      HGAAAA  GDNAAA  AAAAxx
+7340   8873    0       0       0       0       40      340     1340    2340    7340    80      81      IWAAAA  HDNAAA  HHHHxx
+182    8874    0       2       2       2       82      182     182     182     182     164     165     AHAAAA  IDNAAA  OOOOxx
+2948   8875    0       0       8       8       48      948     948     2948    2948    96      97      KJAAAA  JDNAAA  VVVVxx
+9462   8876    0       2       2       2       62      462     1462    4462    9462    124     125     YZAAAA  KDNAAA  AAAAxx
+5997   8877    1       1       7       17      97      997     1997    997     5997    194     195     RWAAAA  LDNAAA  HHHHxx
+5608   8878    0       0       8       8       8       608     1608    608     5608    16      17      SHAAAA  MDNAAA  OOOOxx
+1472   8879    0       0       2       12      72      472     1472    1472    1472    144     145     QEAAAA  NDNAAA  VVVVxx
+277    8880    1       1       7       17      77      277     277     277     277     154     155     RKAAAA  ODNAAA  AAAAxx
+4807   8881    1       3       7       7       7       807     807     4807    4807    14      15      XCAAAA  PDNAAA  HHHHxx
+4969   8882    1       1       9       9       69      969     969     4969    4969    138     139     DJAAAA  QDNAAA  OOOOxx
+5611   8883    1       3       1       11      11      611     1611    611     5611    22      23      VHAAAA  RDNAAA  VVVVxx
+372    8884    0       0       2       12      72      372     372     372     372     144     145     IOAAAA  SDNAAA  AAAAxx
+6666   8885    0       2       6       6       66      666     666     1666    6666    132     133     KWAAAA  TDNAAA  HHHHxx
+476    8886    0       0       6       16      76      476     476     476     476     152     153     ISAAAA  UDNAAA  OOOOxx
+5225   8887    1       1       5       5       25      225     1225    225     5225    50      51      ZSAAAA  VDNAAA  VVVVxx
+5143   8888    1       3       3       3       43      143     1143    143     5143    86      87      VPAAAA  WDNAAA  AAAAxx
+1853   8889    1       1       3       13      53      853     1853    1853    1853    106     107     HTAAAA  XDNAAA  HHHHxx
+675    8890    1       3       5       15      75      675     675     675     675     150     151     ZZAAAA  YDNAAA  OOOOxx
+5643   8891    1       3       3       3       43      643     1643    643     5643    86      87      BJAAAA  ZDNAAA  VVVVxx
+5317   8892    1       1       7       17      17      317     1317    317     5317    34      35      NWAAAA  AENAAA  AAAAxx
+8102   8893    0       2       2       2       2       102     102     3102    8102    4       5       QZAAAA  BENAAA  HHHHxx
+978    8894    0       2       8       18      78      978     978     978     978     156     157     QLAAAA  CENAAA  OOOOxx
+4620   8895    0       0       0       0       20      620     620     4620    4620    40      41      SVAAAA  DENAAA  VVVVxx
+151    8896    1       3       1       11      51      151     151     151     151     102     103     VFAAAA  EENAAA  AAAAxx
+972    8897    0       0       2       12      72      972     972     972     972     144     145     KLAAAA  FENAAA  HHHHxx
+6820   8898    0       0       0       0       20      820     820     1820    6820    40      41      ICAAAA  GENAAA  OOOOxx
+7387   8899    1       3       7       7       87      387     1387    2387    7387    174     175     DYAAAA  HENAAA  VVVVxx
+9634   8900    0       2       4       14      34      634     1634    4634    9634    68      69      OGAAAA  IENAAA  AAAAxx
+6308   8901    0       0       8       8       8       308     308     1308    6308    16      17      QIAAAA  JENAAA  HHHHxx
+8323   8902    1       3       3       3       23      323     323     3323    8323    46      47      DIAAAA  KENAAA  OOOOxx
+6672   8903    0       0       2       12      72      672     672     1672    6672    144     145     QWAAAA  LENAAA  VVVVxx
+8283   8904    1       3       3       3       83      283     283     3283    8283    166     167     PGAAAA  MENAAA  AAAAxx
+7996   8905    0       0       6       16      96      996     1996    2996    7996    192     193     OVAAAA  NENAAA  HHHHxx
+6488   8906    0       0       8       8       88      488     488     1488    6488    176     177     OPAAAA  OENAAA  OOOOxx
+2365   8907    1       1       5       5       65      365     365     2365    2365    130     131     ZMAAAA  PENAAA  VVVVxx
+9746   8908    0       2       6       6       46      746     1746    4746    9746    92      93      WKAAAA  QENAAA  AAAAxx
+8605   8909    1       1       5       5       5       605     605     3605    8605    10      11      ZSAAAA  RENAAA  HHHHxx
+3342   8910    0       2       2       2       42      342     1342    3342    3342    84      85      OYAAAA  SENAAA  OOOOxx
+8429   8911    1       1       9       9       29      429     429     3429    8429    58      59      FMAAAA  TENAAA  VVVVxx
+1162   8912    0       2       2       2       62      162     1162    1162    1162    124     125     SSAAAA  UENAAA  AAAAxx
+531    8913    1       3       1       11      31      531     531     531     531     62      63      LUAAAA  VENAAA  HHHHxx
+8408   8914    0       0       8       8       8       408     408     3408    8408    16      17      KLAAAA  WENAAA  OOOOxx
+8862   8915    0       2       2       2       62      862     862     3862    8862    124     125     WCAAAA  XENAAA  VVVVxx
+5843   8916    1       3       3       3       43      843     1843    843     5843    86      87      TQAAAA  YENAAA  AAAAxx
+8704   8917    0       0       4       4       4       704     704     3704    8704    8       9       UWAAAA  ZENAAA  HHHHxx
+7070   8918    0       2       0       10      70      70      1070    2070    7070    140     141     YLAAAA  AFNAAA  OOOOxx
+9119   8919    1       3       9       19      19      119     1119    4119    9119    38      39      TMAAAA  BFNAAA  VVVVxx
+8344   8920    0       0       4       4       44      344     344     3344    8344    88      89      YIAAAA  CFNAAA  AAAAxx
+8979   8921    1       3       9       19      79      979     979     3979    8979    158     159     JHAAAA  DFNAAA  HHHHxx
+2971   8922    1       3       1       11      71      971     971     2971    2971    142     143     HKAAAA  EFNAAA  OOOOxx
+7700   8923    0       0       0       0       0       700     1700    2700    7700    0       1       EKAAAA  FFNAAA  VVVVxx
+8280   8924    0       0       0       0       80      280     280     3280    8280    160     161     MGAAAA  GFNAAA  AAAAxx
+9096   8925    0       0       6       16      96      96      1096    4096    9096    192     193     WLAAAA  HFNAAA  HHHHxx
+99     8926    1       3       9       19      99      99      99      99      99      198     199     VDAAAA  IFNAAA  OOOOxx
+6696   8927    0       0       6       16      96      696     696     1696    6696    192     193     OXAAAA  JFNAAA  VVVVxx
+9490   8928    0       2       0       10      90      490     1490    4490    9490    180     181     ABAAAA  KFNAAA  AAAAxx
+9073   8929    1       1       3       13      73      73      1073    4073    9073    146     147     ZKAAAA  LFNAAA  HHHHxx
+1861   8930    1       1       1       1       61      861     1861    1861    1861    122     123     PTAAAA  MFNAAA  OOOOxx
+4413   8931    1       1       3       13      13      413     413     4413    4413    26      27      TNAAAA  NFNAAA  VVVVxx
+6002   8932    0       2       2       2       2       2       2       1002    6002    4       5       WWAAAA  OFNAAA  AAAAxx
+439    8933    1       3       9       19      39      439     439     439     439     78      79      XQAAAA  PFNAAA  HHHHxx
+5449   8934    1       1       9       9       49      449     1449    449     5449    98      99      PBAAAA  QFNAAA  OOOOxx
+9737   8935    1       1       7       17      37      737     1737    4737    9737    74      75      NKAAAA  RFNAAA  VVVVxx
+1898   8936    0       2       8       18      98      898     1898    1898    1898    196     197     AVAAAA  SFNAAA  AAAAxx
+4189   8937    1       1       9       9       89      189     189     4189    4189    178     179     DFAAAA  TFNAAA  HHHHxx
+1408   8938    0       0       8       8       8       408     1408    1408    1408    16      17      ECAAAA  UFNAAA  OOOOxx
+394    8939    0       2       4       14      94      394     394     394     394     188     189     EPAAAA  VFNAAA  VVVVxx
+1935   8940    1       3       5       15      35      935     1935    1935    1935    70      71      LWAAAA  WFNAAA  AAAAxx
+3965   8941    1       1       5       5       65      965     1965    3965    3965    130     131     NWAAAA  XFNAAA  HHHHxx
+6821   8942    1       1       1       1       21      821     821     1821    6821    42      43      JCAAAA  YFNAAA  OOOOxx
+349    8943    1       1       9       9       49      349     349     349     349     98      99      LNAAAA  ZFNAAA  VVVVxx
+8428   8944    0       0       8       8       28      428     428     3428    8428    56      57      EMAAAA  AGNAAA  AAAAxx
+8200   8945    0       0       0       0       0       200     200     3200    8200    0       1       KDAAAA  BGNAAA  HHHHxx
+1737   8946    1       1       7       17      37      737     1737    1737    1737    74      75      VOAAAA  CGNAAA  OOOOxx
+6516   8947    0       0       6       16      16      516     516     1516    6516    32      33      QQAAAA  DGNAAA  VVVVxx
+5441   8948    1       1       1       1       41      441     1441    441     5441    82      83      HBAAAA  EGNAAA  AAAAxx
+5999   8949    1       3       9       19      99      999     1999    999     5999    198     199     TWAAAA  FGNAAA  HHHHxx
+1539   8950    1       3       9       19      39      539     1539    1539    1539    78      79      FHAAAA  GGNAAA  OOOOxx
+9067   8951    1       3       7       7       67      67      1067    4067    9067    134     135     TKAAAA  HGNAAA  VVVVxx
+4061   8952    1       1       1       1       61      61      61      4061    4061    122     123     FAAAAA  IGNAAA  AAAAxx
+1642   8953    0       2       2       2       42      642     1642    1642    1642    84      85      ELAAAA  JGNAAA  HHHHxx
+4657   8954    1       1       7       17      57      657     657     4657    4657    114     115     DXAAAA  KGNAAA  OOOOxx
+9934   8955    0       2       4       14      34      934     1934    4934    9934    68      69      CSAAAA  LGNAAA  VVVVxx
+6385   8956    1       1       5       5       85      385     385     1385    6385    170     171     PLAAAA  MGNAAA  AAAAxx
+6775   8957    1       3       5       15      75      775     775     1775    6775    150     151     PAAAAA  NGNAAA  HHHHxx
+3873   8958    1       1       3       13      73      873     1873    3873    3873    146     147     ZSAAAA  OGNAAA  OOOOxx
+3862   8959    0       2       2       2       62      862     1862    3862    3862    124     125     OSAAAA  PGNAAA  VVVVxx
+1224   8960    0       0       4       4       24      224     1224    1224    1224    48      49      CVAAAA  QGNAAA  AAAAxx
+4483   8961    1       3       3       3       83      483     483     4483    4483    166     167     LQAAAA  RGNAAA  HHHHxx
+3685   8962    1       1       5       5       85      685     1685    3685    3685    170     171     TLAAAA  SGNAAA  OOOOxx
+6082   8963    0       2       2       2       82      82      82      1082    6082    164     165     YZAAAA  TGNAAA  VVVVxx
+7798   8964    0       2       8       18      98      798     1798    2798    7798    196     197     YNAAAA  UGNAAA  AAAAxx
+9039   8965    1       3       9       19      39      39      1039    4039    9039    78      79      RJAAAA  VGNAAA  HHHHxx
+985    8966    1       1       5       5       85      985     985     985     985     170     171     XLAAAA  WGNAAA  OOOOxx
+5389   8967    1       1       9       9       89      389     1389    389     5389    178     179     HZAAAA  XGNAAA  VVVVxx
+1716   8968    0       0       6       16      16      716     1716    1716    1716    32      33      AOAAAA  YGNAAA  AAAAxx
+4209   8969    1       1       9       9       9       209     209     4209    4209    18      19      XFAAAA  ZGNAAA  HHHHxx
+746    8970    0       2       6       6       46      746     746     746     746     92      93      SCAAAA  AHNAAA  OOOOxx
+6295   8971    1       3       5       15      95      295     295     1295    6295    190     191     DIAAAA  BHNAAA  VVVVxx
+9754   8972    0       2       4       14      54      754     1754    4754    9754    108     109     ELAAAA  CHNAAA  AAAAxx
+2336   8973    0       0       6       16      36      336     336     2336    2336    72      73      WLAAAA  DHNAAA  HHHHxx
+3701   8974    1       1       1       1       1       701     1701    3701    3701    2       3       JMAAAA  EHNAAA  OOOOxx
+3551   8975    1       3       1       11      51      551     1551    3551    3551    102     103     PGAAAA  FHNAAA  VVVVxx
+8516   8976    0       0       6       16      16      516     516     3516    8516    32      33      OPAAAA  GHNAAA  AAAAxx
+9290   8977    0       2       0       10      90      290     1290    4290    9290    180     181     ITAAAA  HHNAAA  HHHHxx
+5686   8978    0       2       6       6       86      686     1686    686     5686    172     173     SKAAAA  IHNAAA  OOOOxx
+2893   8979    1       1       3       13      93      893     893     2893    2893    186     187     HHAAAA  JHNAAA  VVVVxx
+6279   8980    1       3       9       19      79      279     279     1279    6279    158     159     NHAAAA  KHNAAA  AAAAxx
+2278   8981    0       2       8       18      78      278     278     2278    2278    156     157     QJAAAA  LHNAAA  HHHHxx
+1618   8982    0       2       8       18      18      618     1618    1618    1618    36      37      GKAAAA  MHNAAA  OOOOxx
+3450   8983    0       2       0       10      50      450     1450    3450    3450    100     101     SCAAAA  NHNAAA  VVVVxx
+8857   8984    1       1       7       17      57      857     857     3857    8857    114     115     RCAAAA  OHNAAA  AAAAxx
+1005   8985    1       1       5       5       5       5       1005    1005    1005    10      11      RMAAAA  PHNAAA  HHHHxx
+4727   8986    1       3       7       7       27      727     727     4727    4727    54      55      VZAAAA  QHNAAA  OOOOxx
+7617   8987    1       1       7       17      17      617     1617    2617    7617    34      35      ZGAAAA  RHNAAA  VVVVxx
+2021   8988    1       1       1       1       21      21      21      2021    2021    42      43      TZAAAA  SHNAAA  AAAAxx
+9124   8989    0       0       4       4       24      124     1124    4124    9124    48      49      YMAAAA  THNAAA  HHHHxx
+3175   8990    1       3       5       15      75      175     1175    3175    3175    150     151     DSAAAA  UHNAAA  OOOOxx
+2949   8991    1       1       9       9       49      949     949     2949    2949    98      99      LJAAAA  VHNAAA  VVVVxx
+2424   8992    0       0       4       4       24      424     424     2424    2424    48      49      GPAAAA  WHNAAA  AAAAxx
+4791   8993    1       3       1       11      91      791     791     4791    4791    182     183     HCAAAA  XHNAAA  HHHHxx
+7500   8994    0       0       0       0       0       500     1500    2500    7500    0       1       MCAAAA  YHNAAA  OOOOxx
+4893   8995    1       1       3       13      93      893     893     4893    4893    186     187     FGAAAA  ZHNAAA  VVVVxx
+121    8996    1       1       1       1       21      121     121     121     121     42      43      REAAAA  AINAAA  AAAAxx
+1965   8997    1       1       5       5       65      965     1965    1965    1965    130     131     PXAAAA  BINAAA  HHHHxx
+2972   8998    0       0       2       12      72      972     972     2972    2972    144     145     IKAAAA  CINAAA  OOOOxx
+662    8999    0       2       2       2       62      662     662     662     662     124     125     MZAAAA  DINAAA  VVVVxx
+7074   9000    0       2       4       14      74      74      1074    2074    7074    148     149     CMAAAA  EINAAA  AAAAxx
+981    9001    1       1       1       1       81      981     981     981     981     162     163     TLAAAA  FINAAA  HHHHxx
+3520   9002    0       0       0       0       20      520     1520    3520    3520    40      41      KFAAAA  GINAAA  OOOOxx
+6540   9003    0       0       0       0       40      540     540     1540    6540    80      81      ORAAAA  HINAAA  VVVVxx
+6648   9004    0       0       8       8       48      648     648     1648    6648    96      97      SVAAAA  IINAAA  AAAAxx
+7076   9005    0       0       6       16      76      76      1076    2076    7076    152     153     EMAAAA  JINAAA  HHHHxx
+6919   9006    1       3       9       19      19      919     919     1919    6919    38      39      DGAAAA  KINAAA  OOOOxx
+1108   9007    0       0       8       8       8       108     1108    1108    1108    16      17      QQAAAA  LINAAA  VVVVxx
+317    9008    1       1       7       17      17      317     317     317     317     34      35      FMAAAA  MINAAA  AAAAxx
+3483   9009    1       3       3       3       83      483     1483    3483    3483    166     167     ZDAAAA  NINAAA  HHHHxx
+6764   9010    0       0       4       4       64      764     764     1764    6764    128     129     EAAAAA  OINAAA  OOOOxx
+1235   9011    1       3       5       15      35      235     1235    1235    1235    70      71      NVAAAA  PINAAA  VVVVxx
+7121   9012    1       1       1       1       21      121     1121    2121    7121    42      43      XNAAAA  QINAAA  AAAAxx
+426    9013    0       2       6       6       26      426     426     426     426     52      53      KQAAAA  RINAAA  HHHHxx
+6880   9014    0       0       0       0       80      880     880     1880    6880    160     161     QEAAAA  SINAAA  OOOOxx
+5401   9015    1       1       1       1       1       401     1401    401     5401    2       3       TZAAAA  TINAAA  VVVVxx
+7323   9016    1       3       3       3       23      323     1323    2323    7323    46      47      RVAAAA  UINAAA  AAAAxx
+9751   9017    1       3       1       11      51      751     1751    4751    9751    102     103     BLAAAA  VINAAA  HHHHxx
+3436   9018    0       0       6       16      36      436     1436    3436    3436    72      73      ECAAAA  WINAAA  OOOOxx
+7319   9019    1       3       9       19      19      319     1319    2319    7319    38      39      NVAAAA  XINAAA  VVVVxx
+7882   9020    0       2       2       2       82      882     1882    2882    7882    164     165     ERAAAA  YINAAA  AAAAxx
+8260   9021    0       0       0       0       60      260     260     3260    8260    120     121     SFAAAA  ZINAAA  HHHHxx
+9758   9022    0       2       8       18      58      758     1758    4758    9758    116     117     ILAAAA  AJNAAA  OOOOxx
+4205   9023    1       1       5       5       5       205     205     4205    4205    10      11      TFAAAA  BJNAAA  VVVVxx
+8884   9024    0       0       4       4       84      884     884     3884    8884    168     169     SDAAAA  CJNAAA  AAAAxx
+1112   9025    0       0       2       12      12      112     1112    1112    1112    24      25      UQAAAA  DJNAAA  HHHHxx
+2186   9026    0       2       6       6       86      186     186     2186    2186    172     173     CGAAAA  EJNAAA  OOOOxx
+8666   9027    0       2       6       6       66      666     666     3666    8666    132     133     IVAAAA  FJNAAA  VVVVxx
+4325   9028    1       1       5       5       25      325     325     4325    4325    50      51      JKAAAA  GJNAAA  AAAAxx
+4912   9029    0       0       2       12      12      912     912     4912    4912    24      25      YGAAAA  HJNAAA  HHHHxx
+6497   9030    1       1       7       17      97      497     497     1497    6497    194     195     XPAAAA  IJNAAA  OOOOxx
+9072   9031    0       0       2       12      72      72      1072    4072    9072    144     145     YKAAAA  JJNAAA  VVVVxx
+8899   9032    1       3       9       19      99      899     899     3899    8899    198     199     HEAAAA  KJNAAA  AAAAxx
+5619   9033    1       3       9       19      19      619     1619    619     5619    38      39      DIAAAA  LJNAAA  HHHHxx
+4110   9034    0       2       0       10      10      110     110     4110    4110    20      21      CCAAAA  MJNAAA  OOOOxx
+7025   9035    1       1       5       5       25      25      1025    2025    7025    50      51      FKAAAA  NJNAAA  VVVVxx
+5605   9036    1       1       5       5       5       605     1605    605     5605    10      11      PHAAAA  OJNAAA  AAAAxx
+2572   9037    0       0       2       12      72      572     572     2572    2572    144     145     YUAAAA  PJNAAA  HHHHxx
+3895   9038    1       3       5       15      95      895     1895    3895    3895    190     191     VTAAAA  QJNAAA  OOOOxx
+9138   9039    0       2       8       18      38      138     1138    4138    9138    76      77      MNAAAA  RJNAAA  VVVVxx
+4713   9040    1       1       3       13      13      713     713     4713    4713    26      27      HZAAAA  SJNAAA  AAAAxx
+6079   9041    1       3       9       19      79      79      79      1079    6079    158     159     VZAAAA  TJNAAA  HHHHxx
+8898   9042    0       2       8       18      98      898     898     3898    8898    196     197     GEAAAA  UJNAAA  OOOOxx
+2650   9043    0       2       0       10      50      650     650     2650    2650    100     101     YXAAAA  VJNAAA  VVVVxx
+5316   9044    0       0       6       16      16      316     1316    316     5316    32      33      MWAAAA  WJNAAA  AAAAxx
+5133   9045    1       1       3       13      33      133     1133    133     5133    66      67      LPAAAA  XJNAAA  HHHHxx
+2184   9046    0       0       4       4       84      184     184     2184    2184    168     169     AGAAAA  YJNAAA  OOOOxx
+2728   9047    0       0       8       8       28      728     728     2728    2728    56      57      YAAAAA  ZJNAAA  VVVVxx
+6737   9048    1       1       7       17      37      737     737     1737    6737    74      75      DZAAAA  AKNAAA  AAAAxx
+1128   9049    0       0       8       8       28      128     1128    1128    1128    56      57      KRAAAA  BKNAAA  HHHHxx
+9662   9050    0       2       2       2       62      662     1662    4662    9662    124     125     QHAAAA  CKNAAA  OOOOxx
+9384   9051    0       0       4       4       84      384     1384    4384    9384    168     169     YWAAAA  DKNAAA  VVVVxx
+4576   9052    0       0       6       16      76      576     576     4576    4576    152     153     AUAAAA  EKNAAA  AAAAxx
+9613   9053    1       1       3       13      13      613     1613    4613    9613    26      27      TFAAAA  FKNAAA  HHHHxx
+4001   9054    1       1       1       1       1       1       1       4001    4001    2       3       XXAAAA  GKNAAA  OOOOxx
+3628   9055    0       0       8       8       28      628     1628    3628    3628    56      57      OJAAAA  HKNAAA  VVVVxx
+6968   9056    0       0       8       8       68      968     968     1968    6968    136     137     AIAAAA  IKNAAA  AAAAxx
+6491   9057    1       3       1       11      91      491     491     1491    6491    182     183     RPAAAA  JKNAAA  HHHHxx
+1265   9058    1       1       5       5       65      265     1265    1265    1265    130     131     RWAAAA  KKNAAA  OOOOxx
+6128   9059    0       0       8       8       28      128     128     1128    6128    56      57      SBAAAA  LKNAAA  VVVVxx
+4274   9060    0       2       4       14      74      274     274     4274    4274    148     149     KIAAAA  MKNAAA  AAAAxx
+3598   9061    0       2       8       18      98      598     1598    3598    3598    196     197     KIAAAA  NKNAAA  HHHHxx
+7961   9062    1       1       1       1       61      961     1961    2961    7961    122     123     FUAAAA  OKNAAA  OOOOxx
+2643   9063    1       3       3       3       43      643     643     2643    2643    86      87      RXAAAA  PKNAAA  VVVVxx
+4547   9064    1       3       7       7       47      547     547     4547    4547    94      95      XSAAAA  QKNAAA  AAAAxx
+3568   9065    0       0       8       8       68      568     1568    3568    3568    136     137     GHAAAA  RKNAAA  HHHHxx
+8954   9066    0       2       4       14      54      954     954     3954    8954    108     109     KGAAAA  SKNAAA  OOOOxx
+8802   9067    0       2       2       2       2       802     802     3802    8802    4       5       OAAAAA  TKNAAA  VVVVxx
+7829   9068    1       1       9       9       29      829     1829    2829    7829    58      59      DPAAAA  UKNAAA  AAAAxx
+1008   9069    0       0       8       8       8       8       1008    1008    1008    16      17      UMAAAA  VKNAAA  HHHHxx
+3627   9070    1       3       7       7       27      627     1627    3627    3627    54      55      NJAAAA  WKNAAA  OOOOxx
+3999   9071    1       3       9       19      99      999     1999    3999    3999    198     199     VXAAAA  XKNAAA  VVVVxx
+7697   9072    1       1       7       17      97      697     1697    2697    7697    194     195     BKAAAA  YKNAAA  AAAAxx
+9380   9073    0       0       0       0       80      380     1380    4380    9380    160     161     UWAAAA  ZKNAAA  HHHHxx
+2707   9074    1       3       7       7       7       707     707     2707    2707    14      15      DAAAAA  ALNAAA  OOOOxx
+4430   9075    0       2       0       10      30      430     430     4430    4430    60      61      KOAAAA  BLNAAA  VVVVxx
+6440   9076    0       0       0       0       40      440     440     1440    6440    80      81      SNAAAA  CLNAAA  AAAAxx
+9958   9077    0       2       8       18      58      958     1958    4958    9958    116     117     ATAAAA  DLNAAA  HHHHxx
+7592   9078    0       0       2       12      92      592     1592    2592    7592    184     185     AGAAAA  ELNAAA  OOOOxx
+7852   9079    0       0       2       12      52      852     1852    2852    7852    104     105     AQAAAA  FLNAAA  VVVVxx
+9253   9080    1       1       3       13      53      253     1253    4253    9253    106     107     XRAAAA  GLNAAA  AAAAxx
+5910   9081    0       2       0       10      10      910     1910    910     5910    20      21      ITAAAA  HLNAAA  HHHHxx
+7487   9082    1       3       7       7       87      487     1487    2487    7487    174     175     ZBAAAA  ILNAAA  OOOOxx
+6324   9083    0       0       4       4       24      324     324     1324    6324    48      49      GJAAAA  JLNAAA  VVVVxx
+5792   9084    0       0       2       12      92      792     1792    792     5792    184     185     UOAAAA  KLNAAA  AAAAxx
+7390   9085    0       2       0       10      90      390     1390    2390    7390    180     181     GYAAAA  LLNAAA  HHHHxx
+8534   9086    0       2       4       14      34      534     534     3534    8534    68      69      GQAAAA  MLNAAA  OOOOxx
+2690   9087    0       2       0       10      90      690     690     2690    2690    180     181     MZAAAA  NLNAAA  VVVVxx
+3992   9088    0       0       2       12      92      992     1992    3992    3992    184     185     OXAAAA  OLNAAA  AAAAxx
+6928   9089    0       0       8       8       28      928     928     1928    6928    56      57      MGAAAA  PLNAAA  HHHHxx
+7815   9090    1       3       5       15      15      815     1815    2815    7815    30      31      POAAAA  QLNAAA  OOOOxx
+9477   9091    1       1       7       17      77      477     1477    4477    9477    154     155     NAAAAA  RLNAAA  VVVVxx
+497    9092    1       1       7       17      97      497     497     497     497     194     195     DTAAAA  SLNAAA  AAAAxx
+7532   9093    0       0       2       12      32      532     1532    2532    7532    64      65      SDAAAA  TLNAAA  HHHHxx
+9838   9094    0       2       8       18      38      838     1838    4838    9838    76      77      KOAAAA  ULNAAA  OOOOxx
+1557   9095    1       1       7       17      57      557     1557    1557    1557    114     115     XHAAAA  VLNAAA  VVVVxx
+2467   9096    1       3       7       7       67      467     467     2467    2467    134     135     XQAAAA  WLNAAA  AAAAxx
+2367   9097    1       3       7       7       67      367     367     2367    2367    134     135     BNAAAA  XLNAAA  HHHHxx
+5677   9098    1       1       7       17      77      677     1677    677     5677    154     155     JKAAAA  YLNAAA  OOOOxx
+6193   9099    1       1       3       13      93      193     193     1193    6193    186     187     FEAAAA  ZLNAAA  VVVVxx
+7126   9100    0       2       6       6       26      126     1126    2126    7126    52      53      COAAAA  AMNAAA  AAAAxx
+5264   9101    0       0       4       4       64      264     1264    264     5264    128     129     MUAAAA  BMNAAA  HHHHxx
+850    9102    0       2       0       10      50      850     850     850     850     100     101     SGAAAA  CMNAAA  OOOOxx
+4854   9103    0       2       4       14      54      854     854     4854    4854    108     109     SEAAAA  DMNAAA  VVVVxx
+4414   9104    0       2       4       14      14      414     414     4414    4414    28      29      UNAAAA  EMNAAA  AAAAxx
+8971   9105    1       3       1       11      71      971     971     3971    8971    142     143     BHAAAA  FMNAAA  HHHHxx
+9240   9106    0       0       0       0       40      240     1240    4240    9240    80      81      KRAAAA  GMNAAA  OOOOxx
+7341   9107    1       1       1       1       41      341     1341    2341    7341    82      83      JWAAAA  HMNAAA  VVVVxx
+3151   9108    1       3       1       11      51      151     1151    3151    3151    102     103     FRAAAA  IMNAAA  AAAAxx
+1742   9109    0       2       2       2       42      742     1742    1742    1742    84      85      APAAAA  JMNAAA  HHHHxx
+1347   9110    1       3       7       7       47      347     1347    1347    1347    94      95      VZAAAA  KMNAAA  OOOOxx
+9418   9111    0       2       8       18      18      418     1418    4418    9418    36      37      GYAAAA  LMNAAA  VVVVxx
+5452   9112    0       0       2       12      52      452     1452    452     5452    104     105     SBAAAA  MMNAAA  AAAAxx
+8637   9113    1       1       7       17      37      637     637     3637    8637    74      75      FUAAAA  NMNAAA  HHHHxx
+8287   9114    1       3       7       7       87      287     287     3287    8287    174     175     TGAAAA  OMNAAA  OOOOxx
+9865   9115    1       1       5       5       65      865     1865    4865    9865    130     131     LPAAAA  PMNAAA  VVVVxx
+1664   9116    0       0       4       4       64      664     1664    1664    1664    128     129     AMAAAA  QMNAAA  AAAAxx
+9933   9117    1       1       3       13      33      933     1933    4933    9933    66      67      BSAAAA  RMNAAA  HHHHxx
+3416   9118    0       0       6       16      16      416     1416    3416    3416    32      33      KBAAAA  SMNAAA  OOOOxx
+7981   9119    1       1       1       1       81      981     1981    2981    7981    162     163     ZUAAAA  TMNAAA  VVVVxx
+1981   9120    1       1       1       1       81      981     1981    1981    1981    162     163     FYAAAA  UMNAAA  AAAAxx
+441    9121    1       1       1       1       41      441     441     441     441     82      83      ZQAAAA  VMNAAA  HHHHxx
+1380   9122    0       0       0       0       80      380     1380    1380    1380    160     161     CBAAAA  WMNAAA  OOOOxx
+7325   9123    1       1       5       5       25      325     1325    2325    7325    50      51      TVAAAA  XMNAAA  VVVVxx
+5682   9124    0       2       2       2       82      682     1682    682     5682    164     165     OKAAAA  YMNAAA  AAAAxx
+1024   9125    0       0       4       4       24      24      1024    1024    1024    48      49      KNAAAA  ZMNAAA  HHHHxx
+1096   9126    0       0       6       16      96      96      1096    1096    1096    192     193     EQAAAA  ANNAAA  OOOOxx
+4717   9127    1       1       7       17      17      717     717     4717    4717    34      35      LZAAAA  BNNAAA  VVVVxx
+7948   9128    0       0       8       8       48      948     1948    2948    7948    96      97      STAAAA  CNNAAA  AAAAxx
+4074   9129    0       2       4       14      74      74      74      4074    4074    148     149     SAAAAA  DNNAAA  HHHHxx
+211    9130    1       3       1       11      11      211     211     211     211     22      23      DIAAAA  ENNAAA  OOOOxx
+8993   9131    1       1       3       13      93      993     993     3993    8993    186     187     XHAAAA  FNNAAA  VVVVxx
+4509   9132    1       1       9       9       9       509     509     4509    4509    18      19      LRAAAA  GNNAAA  AAAAxx
+823    9133    1       3       3       3       23      823     823     823     823     46      47      RFAAAA  HNNAAA  HHHHxx
+4747   9134    1       3       7       7       47      747     747     4747    4747    94      95      PAAAAA  INNAAA  OOOOxx
+6955   9135    1       3       5       15      55      955     955     1955    6955    110     111     NHAAAA  JNNAAA  VVVVxx
+7922   9136    0       2       2       2       22      922     1922    2922    7922    44      45      SSAAAA  KNNAAA  AAAAxx
+6936   9137    0       0       6       16      36      936     936     1936    6936    72      73      UGAAAA  LNNAAA  HHHHxx
+1546   9138    0       2       6       6       46      546     1546    1546    1546    92      93      MHAAAA  MNNAAA  OOOOxx
+9836   9139    0       0       6       16      36      836     1836    4836    9836    72      73      IOAAAA  NNNAAA  VVVVxx
+5626   9140    0       2       6       6       26      626     1626    626     5626    52      53      KIAAAA  ONNAAA  AAAAxx
+4879   9141    1       3       9       19      79      879     879     4879    4879    158     159     RFAAAA  PNNAAA  HHHHxx
+8590   9142    0       2       0       10      90      590     590     3590    8590    180     181     KSAAAA  QNNAAA  OOOOxx
+8842   9143    0       2       2       2       42      842     842     3842    8842    84      85      CCAAAA  RNNAAA  VVVVxx
+6505   9144    1       1       5       5       5       505     505     1505    6505    10      11      FQAAAA  SNNAAA  AAAAxx
+2803   9145    1       3       3       3       3       803     803     2803    2803    6       7       VDAAAA  TNNAAA  HHHHxx
+9258   9146    0       2       8       18      58      258     1258    4258    9258    116     117     CSAAAA  UNNAAA  OOOOxx
+741    9147    1       1       1       1       41      741     741     741     741     82      83      NCAAAA  VNNAAA  VVVVxx
+1457   9148    1       1       7       17      57      457     1457    1457    1457    114     115     BEAAAA  WNNAAA  AAAAxx
+5777   9149    1       1       7       17      77      777     1777    777     5777    154     155     FOAAAA  XNNAAA  HHHHxx
+2883   9150    1       3       3       3       83      883     883     2883    2883    166     167     XGAAAA  YNNAAA  OOOOxx
+6610   9151    0       2       0       10      10      610     610     1610    6610    20      21      GUAAAA  ZNNAAA  VVVVxx
+4331   9152    1       3       1       11      31      331     331     4331    4331    62      63      PKAAAA  AONAAA  AAAAxx
+2712   9153    0       0       2       12      12      712     712     2712    2712    24      25      IAAAAA  BONAAA  HHHHxx
+9268   9154    0       0       8       8       68      268     1268    4268    9268    136     137     MSAAAA  CONAAA  OOOOxx
+410    9155    0       2       0       10      10      410     410     410     410     20      21      UPAAAA  DONAAA  VVVVxx
+9411   9156    1       3       1       11      11      411     1411    4411    9411    22      23      ZXAAAA  EONAAA  AAAAxx
+4683   9157    1       3       3       3       83      683     683     4683    4683    166     167     DYAAAA  FONAAA  HHHHxx
+7072   9158    0       0       2       12      72      72      1072    2072    7072    144     145     AMAAAA  GONAAA  OOOOxx
+5050   9159    0       2       0       10      50      50      1050    50      5050    100     101     GMAAAA  HONAAA  VVVVxx
+5932   9160    0       0       2       12      32      932     1932    932     5932    64      65      EUAAAA  IONAAA  AAAAxx
+2756   9161    0       0       6       16      56      756     756     2756    2756    112     113     ACAAAA  JONAAA  HHHHxx
+9813   9162    1       1       3       13      13      813     1813    4813    9813    26      27      LNAAAA  KONAAA  OOOOxx
+7388   9163    0       0       8       8       88      388     1388    2388    7388    176     177     EYAAAA  LONAAA  VVVVxx
+2596   9164    0       0       6       16      96      596     596     2596    2596    192     193     WVAAAA  MONAAA  AAAAxx
+5102   9165    0       2       2       2       2       102     1102    102     5102    4       5       GOAAAA  NONAAA  HHHHxx
+208    9166    0       0       8       8       8       208     208     208     208     16      17      AIAAAA  OONAAA  OOOOxx
+86     9167    0       2       6       6       86      86      86      86      86      172     173     IDAAAA  PONAAA  VVVVxx
+8127   9168    1       3       7       7       27      127     127     3127    8127    54      55      PAAAAA  QONAAA  AAAAxx
+5154   9169    0       2       4       14      54      154     1154    154     5154    108     109     GQAAAA  RONAAA  HHHHxx
+4491   9170    1       3       1       11      91      491     491     4491    4491    182     183     TQAAAA  SONAAA  OOOOxx
+7423   9171    1       3       3       3       23      423     1423    2423    7423    46      47      NZAAAA  TONAAA  VVVVxx
+6441   9172    1       1       1       1       41      441     441     1441    6441    82      83      TNAAAA  UONAAA  AAAAxx
+2920   9173    0       0       0       0       20      920     920     2920    2920    40      41      IIAAAA  VONAAA  HHHHxx
+6386   9174    0       2       6       6       86      386     386     1386    6386    172     173     QLAAAA  WONAAA  OOOOxx
+9744   9175    0       0       4       4       44      744     1744    4744    9744    88      89      UKAAAA  XONAAA  VVVVxx
+2667   9176    1       3       7       7       67      667     667     2667    2667    134     135     PYAAAA  YONAAA  AAAAxx
+5754   9177    0       2       4       14      54      754     1754    754     5754    108     109     INAAAA  ZONAAA  HHHHxx
+4645   9178    1       1       5       5       45      645     645     4645    4645    90      91      RWAAAA  APNAAA  OOOOxx
+4327   9179    1       3       7       7       27      327     327     4327    4327    54      55      LKAAAA  BPNAAA  VVVVxx
+843    9180    1       3       3       3       43      843     843     843     843     86      87      LGAAAA  CPNAAA  AAAAxx
+4085   9181    1       1       5       5       85      85      85      4085    4085    170     171     DBAAAA  DPNAAA  HHHHxx
+2849   9182    1       1       9       9       49      849     849     2849    2849    98      99      PFAAAA  EPNAAA  OOOOxx
+5734   9183    0       2       4       14      34      734     1734    734     5734    68      69      OMAAAA  FPNAAA  VVVVxx
+5307   9184    1       3       7       7       7       307     1307    307     5307    14      15      DWAAAA  GPNAAA  AAAAxx
+8433   9185    1       1       3       13      33      433     433     3433    8433    66      67      JMAAAA  HPNAAA  HHHHxx
+3031   9186    1       3       1       11      31      31      1031    3031    3031    62      63      PMAAAA  IPNAAA  OOOOxx
+5714   9187    0       2       4       14      14      714     1714    714     5714    28      29      ULAAAA  JPNAAA  VVVVxx
+5969   9188    1       1       9       9       69      969     1969    969     5969    138     139     PVAAAA  KPNAAA  AAAAxx
+2532   9189    0       0       2       12      32      532     532     2532    2532    64      65      KTAAAA  LPNAAA  HHHHxx
+5219   9190    1       3       9       19      19      219     1219    219     5219    38      39      TSAAAA  MPNAAA  OOOOxx
+7343   9191    1       3       3       3       43      343     1343    2343    7343    86      87      LWAAAA  NPNAAA  VVVVxx
+9089   9192    1       1       9       9       89      89      1089    4089    9089    178     179     PLAAAA  OPNAAA  AAAAxx
+9337   9193    1       1       7       17      37      337     1337    4337    9337    74      75      DVAAAA  PPNAAA  HHHHxx
+5131   9194    1       3       1       11      31      131     1131    131     5131    62      63      JPAAAA  QPNAAA  OOOOxx
+6253   9195    1       1       3       13      53      253     253     1253    6253    106     107     NGAAAA  RPNAAA  VVVVxx
+5140   9196    0       0       0       0       40      140     1140    140     5140    80      81      SPAAAA  SPNAAA  AAAAxx
+2953   9197    1       1       3       13      53      953     953     2953    2953    106     107     PJAAAA  TPNAAA  HHHHxx
+4293   9198    1       1       3       13      93      293     293     4293    4293    186     187     DJAAAA  UPNAAA  OOOOxx
+9974   9199    0       2       4       14      74      974     1974    4974    9974    148     149     QTAAAA  VPNAAA  VVVVxx
+5061   9200    1       1       1       1       61      61      1061    61      5061    122     123     RMAAAA  WPNAAA  AAAAxx
+8570   9201    0       2       0       10      70      570     570     3570    8570    140     141     QRAAAA  XPNAAA  HHHHxx
+9504   9202    0       0       4       4       4       504     1504    4504    9504    8       9       OBAAAA  YPNAAA  OOOOxx
+604    9203    0       0       4       4       4       604     604     604     604     8       9       GXAAAA  ZPNAAA  VVVVxx
+4991   9204    1       3       1       11      91      991     991     4991    4991    182     183     ZJAAAA  AQNAAA  AAAAxx
+880    9205    0       0       0       0       80      880     880     880     880     160     161     WHAAAA  BQNAAA  HHHHxx
+3861   9206    1       1       1       1       61      861     1861    3861    3861    122     123     NSAAAA  CQNAAA  OOOOxx
+8262   9207    0       2       2       2       62      262     262     3262    8262    124     125     UFAAAA  DQNAAA  VVVVxx
+5689   9208    1       1       9       9       89      689     1689    689     5689    178     179     VKAAAA  EQNAAA  AAAAxx
+1793   9209    1       1       3       13      93      793     1793    1793    1793    186     187     ZQAAAA  FQNAAA  HHHHxx
+2661   9210    1       1       1       1       61      661     661     2661    2661    122     123     JYAAAA  GQNAAA  OOOOxx
+7954   9211    0       2       4       14      54      954     1954    2954    7954    108     109     YTAAAA  HQNAAA  VVVVxx
+1874   9212    0       2       4       14      74      874     1874    1874    1874    148     149     CUAAAA  IQNAAA  AAAAxx
+2982   9213    0       2       2       2       82      982     982     2982    2982    164     165     SKAAAA  JQNAAA  HHHHxx
+331    9214    1       3       1       11      31      331     331     331     331     62      63      TMAAAA  KQNAAA  OOOOxx
+5021   9215    1       1       1       1       21      21      1021    21      5021    42      43      DLAAAA  LQNAAA  VVVVxx
+9894   9216    0       2       4       14      94      894     1894    4894    9894    188     189     OQAAAA  MQNAAA  AAAAxx
+7709   9217    1       1       9       9       9       709     1709    2709    7709    18      19      NKAAAA  NQNAAA  HHHHxx
+4980   9218    0       0       0       0       80      980     980     4980    4980    160     161     OJAAAA  OQNAAA  OOOOxx
+8249   9219    1       1       9       9       49      249     249     3249    8249    98      99      HFAAAA  PQNAAA  VVVVxx
+7120   9220    0       0       0       0       20      120     1120    2120    7120    40      41      WNAAAA  QQNAAA  AAAAxx
+7464   9221    0       0       4       4       64      464     1464    2464    7464    128     129     CBAAAA  RQNAAA  HHHHxx
+8086   9222    0       2       6       6       86      86      86      3086    8086    172     173     AZAAAA  SQNAAA  OOOOxx
+3509   9223    1       1       9       9       9       509     1509    3509    3509    18      19      ZEAAAA  TQNAAA  VVVVxx
+3902   9224    0       2       2       2       2       902     1902    3902    3902    4       5       CUAAAA  UQNAAA  AAAAxx
+9907   9225    1       3       7       7       7       907     1907    4907    9907    14      15      BRAAAA  VQNAAA  HHHHxx
+6278   9226    0       2       8       18      78      278     278     1278    6278    156     157     MHAAAA  WQNAAA  OOOOxx
+9316   9227    0       0       6       16      16      316     1316    4316    9316    32      33      IUAAAA  XQNAAA  VVVVxx
+2824   9228    0       0       4       4       24      824     824     2824    2824    48      49      QEAAAA  YQNAAA  AAAAxx
+1558   9229    0       2       8       18      58      558     1558    1558    1558    116     117     YHAAAA  ZQNAAA  HHHHxx
+5436   9230    0       0       6       16      36      436     1436    436     5436    72      73      CBAAAA  ARNAAA  OOOOxx
+1161   9231    1       1       1       1       61      161     1161    1161    1161    122     123     RSAAAA  BRNAAA  VVVVxx
+7569   9232    1       1       9       9       69      569     1569    2569    7569    138     139     DFAAAA  CRNAAA  AAAAxx
+9614   9233    0       2       4       14      14      614     1614    4614    9614    28      29      UFAAAA  DRNAAA  HHHHxx
+6970   9234    0       2       0       10      70      970     970     1970    6970    140     141     CIAAAA  ERNAAA  OOOOxx
+2422   9235    0       2       2       2       22      422     422     2422    2422    44      45      EPAAAA  FRNAAA  VVVVxx
+8860   9236    0       0       0       0       60      860     860     3860    8860    120     121     UCAAAA  GRNAAA  AAAAxx
+9912   9237    0       0       2       12      12      912     1912    4912    9912    24      25      GRAAAA  HRNAAA  HHHHxx
+1109   9238    1       1       9       9       9       109     1109    1109    1109    18      19      RQAAAA  IRNAAA  OOOOxx
+3286   9239    0       2       6       6       86      286     1286    3286    3286    172     173     KWAAAA  JRNAAA  VVVVxx
+2277   9240    1       1       7       17      77      277     277     2277    2277    154     155     PJAAAA  KRNAAA  AAAAxx
+8656   9241    0       0       6       16      56      656     656     3656    8656    112     113     YUAAAA  LRNAAA  HHHHxx
+4656   9242    0       0       6       16      56      656     656     4656    4656    112     113     CXAAAA  MRNAAA  OOOOxx
+6965   9243    1       1       5       5       65      965     965     1965    6965    130     131     XHAAAA  NRNAAA  VVVVxx
+7591   9244    1       3       1       11      91      591     1591    2591    7591    182     183     ZFAAAA  ORNAAA  AAAAxx
+4883   9245    1       3       3       3       83      883     883     4883    4883    166     167     VFAAAA  PRNAAA  HHHHxx
+452    9246    0       0       2       12      52      452     452     452     452     104     105     KRAAAA  QRNAAA  OOOOxx
+4018   9247    0       2       8       18      18      18      18      4018    4018    36      37      OYAAAA  RRNAAA  VVVVxx
+4066   9248    0       2       6       6       66      66      66      4066    4066    132     133     KAAAAA  SRNAAA  AAAAxx
+6480   9249    0       0       0       0       80      480     480     1480    6480    160     161     GPAAAA  TRNAAA  HHHHxx
+8634   9250    0       2       4       14      34      634     634     3634    8634    68      69      CUAAAA  URNAAA  OOOOxx
+9387   9251    1       3       7       7       87      387     1387    4387    9387    174     175     BXAAAA  VRNAAA  VVVVxx
+3476   9252    0       0       6       16      76      476     1476    3476    3476    152     153     SDAAAA  WRNAAA  AAAAxx
+5995   9253    1       3       5       15      95      995     1995    995     5995    190     191     PWAAAA  XRNAAA  HHHHxx
+9677   9254    1       1       7       17      77      677     1677    4677    9677    154     155     FIAAAA  YRNAAA  OOOOxx
+3884   9255    0       0       4       4       84      884     1884    3884    3884    168     169     KTAAAA  ZRNAAA  VVVVxx
+6500   9256    0       0       0       0       0       500     500     1500    6500    0       1       AQAAAA  ASNAAA  AAAAxx
+7972   9257    0       0       2       12      72      972     1972    2972    7972    144     145     QUAAAA  BSNAAA  HHHHxx
+5281   9258    1       1       1       1       81      281     1281    281     5281    162     163     DVAAAA  CSNAAA  OOOOxx
+1288   9259    0       0       8       8       88      288     1288    1288    1288    176     177     OXAAAA  DSNAAA  VVVVxx
+4366   9260    0       2       6       6       66      366     366     4366    4366    132     133     YLAAAA  ESNAAA  AAAAxx
+6557   9261    1       1       7       17      57      557     557     1557    6557    114     115     FSAAAA  FSNAAA  HHHHxx
+7086   9262    0       2       6       6       86      86      1086    2086    7086    172     173     OMAAAA  GSNAAA  OOOOxx
+6588   9263    0       0       8       8       88      588     588     1588    6588    176     177     KTAAAA  HSNAAA  VVVVxx
+9062   9264    0       2       2       2       62      62      1062    4062    9062    124     125     OKAAAA  ISNAAA  AAAAxx
+9230   9265    0       2       0       10      30      230     1230    4230    9230    60      61      ARAAAA  JSNAAA  HHHHxx
+7672   9266    0       0       2       12      72      672     1672    2672    7672    144     145     CJAAAA  KSNAAA  OOOOxx
+5204   9267    0       0       4       4       4       204     1204    204     5204    8       9       ESAAAA  LSNAAA  VVVVxx
+2836   9268    0       0       6       16      36      836     836     2836    2836    72      73      CFAAAA  MSNAAA  AAAAxx
+7165   9269    1       1       5       5       65      165     1165    2165    7165    130     131     PPAAAA  NSNAAA  HHHHxx
+971    9270    1       3       1       11      71      971     971     971     971     142     143     JLAAAA  OSNAAA  OOOOxx
+3851   9271    1       3       1       11      51      851     1851    3851    3851    102     103     DSAAAA  PSNAAA  VVVVxx
+8593   9272    1       1       3       13      93      593     593     3593    8593    186     187     NSAAAA  QSNAAA  AAAAxx
+7742   9273    0       2       2       2       42      742     1742    2742    7742    84      85      ULAAAA  RSNAAA  HHHHxx
+2887   9274    1       3       7       7       87      887     887     2887    2887    174     175     BHAAAA  SSNAAA  OOOOxx
+8479   9275    1       3       9       19      79      479     479     3479    8479    158     159     DOAAAA  TSNAAA  VVVVxx
+9514   9276    0       2       4       14      14      514     1514    4514    9514    28      29      YBAAAA  USNAAA  AAAAxx
+273    9277    1       1       3       13      73      273     273     273     273     146     147     NKAAAA  VSNAAA  HHHHxx
+2938   9278    0       2       8       18      38      938     938     2938    2938    76      77      AJAAAA  WSNAAA  OOOOxx
+9793   9279    1       1       3       13      93      793     1793    4793    9793    186     187     RMAAAA  XSNAAA  VVVVxx
+8050   9280    0       2       0       10      50      50      50      3050    8050    100     101     QXAAAA  YSNAAA  AAAAxx
+6702   9281    0       2       2       2       2       702     702     1702    6702    4       5       UXAAAA  ZSNAAA  HHHHxx
+7290   9282    0       2       0       10      90      290     1290    2290    7290    180     181     KUAAAA  ATNAAA  OOOOxx
+1837   9283    1       1       7       17      37      837     1837    1837    1837    74      75      RSAAAA  BTNAAA  VVVVxx
+3206   9284    0       2       6       6       6       206     1206    3206    3206    12      13      ITAAAA  CTNAAA  AAAAxx
+4925   9285    1       1       5       5       25      925     925     4925    4925    50      51      LHAAAA  DTNAAA  HHHHxx
+5066   9286    0       2       6       6       66      66      1066    66      5066    132     133     WMAAAA  ETNAAA  OOOOxx
+3401   9287    1       1       1       1       1       401     1401    3401    3401    2       3       VAAAAA  FTNAAA  VVVVxx
+3474   9288    0       2       4       14      74      474     1474    3474    3474    148     149     QDAAAA  GTNAAA  AAAAxx
+57     9289    1       1       7       17      57      57      57      57      57      114     115     FCAAAA  HTNAAA  HHHHxx
+2082   9290    0       2       2       2       82      82      82      2082    2082    164     165     CCAAAA  ITNAAA  OOOOxx
+100    9291    0       0       0       0       0       100     100     100     100     0       1       WDAAAA  JTNAAA  VVVVxx
+9665   9292    1       1       5       5       65      665     1665    4665    9665    130     131     THAAAA  KTNAAA  AAAAxx
+8284   9293    0       0       4       4       84      284     284     3284    8284    168     169     QGAAAA  LTNAAA  HHHHxx
+958    9294    0       2       8       18      58      958     958     958     958     116     117     WKAAAA  MTNAAA  OOOOxx
+5282   9295    0       2       2       2       82      282     1282    282     5282    164     165     EVAAAA  NTNAAA  VVVVxx
+4257   9296    1       1       7       17      57      257     257     4257    4257    114     115     THAAAA  OTNAAA  AAAAxx
+3160   9297    0       0       0       0       60      160     1160    3160    3160    120     121     ORAAAA  PTNAAA  HHHHxx
+8449   9298    1       1       9       9       49      449     449     3449    8449    98      99      ZMAAAA  QTNAAA  OOOOxx
+500    9299    0       0       0       0       0       500     500     500     500     0       1       GTAAAA  RTNAAA  VVVVxx
+6432   9300    0       0       2       12      32      432     432     1432    6432    64      65      KNAAAA  STNAAA  AAAAxx
+6220   9301    0       0       0       0       20      220     220     1220    6220    40      41      GFAAAA  TTNAAA  HHHHxx
+7233   9302    1       1       3       13      33      233     1233    2233    7233    66      67      FSAAAA  UTNAAA  OOOOxx
+2723   9303    1       3       3       3       23      723     723     2723    2723    46      47      TAAAAA  VTNAAA  VVVVxx
+1899   9304    1       3       9       19      99      899     1899    1899    1899    198     199     BVAAAA  WTNAAA  AAAAxx
+7158   9305    0       2       8       18      58      158     1158    2158    7158    116     117     IPAAAA  XTNAAA  HHHHxx
+202    9306    0       2       2       2       2       202     202     202     202     4       5       UHAAAA  YTNAAA  OOOOxx
+2286   9307    0       2       6       6       86      286     286     2286    2286    172     173     YJAAAA  ZTNAAA  VVVVxx
+5356   9308    0       0       6       16      56      356     1356    356     5356    112     113     AYAAAA  AUNAAA  AAAAxx
+3809   9309    1       1       9       9       9       809     1809    3809    3809    18      19      NQAAAA  BUNAAA  HHHHxx
+3979   9310    1       3       9       19      79      979     1979    3979    3979    158     159     BXAAAA  CUNAAA  OOOOxx
+8359   9311    1       3       9       19      59      359     359     3359    8359    118     119     NJAAAA  DUNAAA  VVVVxx
+3479   9312    1       3       9       19      79      479     1479    3479    3479    158     159     VDAAAA  EUNAAA  AAAAxx
+4895   9313    1       3       5       15      95      895     895     4895    4895    190     191     HGAAAA  FUNAAA  HHHHxx
+6059   9314    1       3       9       19      59      59      59      1059    6059    118     119     BZAAAA  GUNAAA  OOOOxx
+9560   9315    0       0       0       0       60      560     1560    4560    9560    120     121     SDAAAA  HUNAAA  VVVVxx
+6756   9316    0       0       6       16      56      756     756     1756    6756    112     113     WZAAAA  IUNAAA  AAAAxx
+7504   9317    0       0       4       4       4       504     1504    2504    7504    8       9       QCAAAA  JUNAAA  HHHHxx
+6762   9318    0       2       2       2       62      762     762     1762    6762    124     125     CAAAAA  KUNAAA  OOOOxx
+5304   9319    0       0       4       4       4       304     1304    304     5304    8       9       AWAAAA  LUNAAA  VVVVxx
+9533   9320    1       1       3       13      33      533     1533    4533    9533    66      67      RCAAAA  MUNAAA  AAAAxx
+6649   9321    1       1       9       9       49      649     649     1649    6649    98      99      TVAAAA  NUNAAA  HHHHxx
+38     9322    0       2       8       18      38      38      38      38      38      76      77      MBAAAA  OUNAAA  OOOOxx
+5713   9323    1       1       3       13      13      713     1713    713     5713    26      27      TLAAAA  PUNAAA  VVVVxx
+3000   9324    0       0       0       0       0       0       1000    3000    3000    0       1       KLAAAA  QUNAAA  AAAAxx
+3738   9325    0       2       8       18      38      738     1738    3738    3738    76      77      UNAAAA  RUNAAA  HHHHxx
+3327   9326    1       3       7       7       27      327     1327    3327    3327    54      55      ZXAAAA  SUNAAA  OOOOxx
+3922   9327    0       2       2       2       22      922     1922    3922    3922    44      45      WUAAAA  TUNAAA  VVVVxx
+9245   9328    1       1       5       5       45      245     1245    4245    9245    90      91      PRAAAA  UUNAAA  AAAAxx
+2172   9329    0       0       2       12      72      172     172     2172    2172    144     145     OFAAAA  VUNAAA  HHHHxx
+7128   9330    0       0       8       8       28      128     1128    2128    7128    56      57      EOAAAA  WUNAAA  OOOOxx
+1195   9331    1       3       5       15      95      195     1195    1195    1195    190     191     ZTAAAA  XUNAAA  VVVVxx
+8445   9332    1       1       5       5       45      445     445     3445    8445    90      91      VMAAAA  YUNAAA  AAAAxx
+8638   9333    0       2       8       18      38      638     638     3638    8638    76      77      GUAAAA  ZUNAAA  HHHHxx
+1249   9334    1       1       9       9       49      249     1249    1249    1249    98      99      BWAAAA  AVNAAA  OOOOxx
+8659   9335    1       3       9       19      59      659     659     3659    8659    118     119     BVAAAA  BVNAAA  VVVVxx
+3556   9336    0       0       6       16      56      556     1556    3556    3556    112     113     UGAAAA  CVNAAA  AAAAxx
+3347   9337    1       3       7       7       47      347     1347    3347    3347    94      95      TYAAAA  DVNAAA  HHHHxx
+3260   9338    0       0       0       0       60      260     1260    3260    3260    120     121     KVAAAA  EVNAAA  OOOOxx
+5139   9339    1       3       9       19      39      139     1139    139     5139    78      79      RPAAAA  FVNAAA  VVVVxx
+9991   9340    1       3       1       11      91      991     1991    4991    9991    182     183     HUAAAA  GVNAAA  AAAAxx
+5499   9341    1       3       9       19      99      499     1499    499     5499    198     199     NDAAAA  HVNAAA  HHHHxx
+8082   9342    0       2       2       2       82      82      82      3082    8082    164     165     WYAAAA  IVNAAA  OOOOxx
+1640   9343    0       0       0       0       40      640     1640    1640    1640    80      81      CLAAAA  JVNAAA  VVVVxx
+8726   9344    0       2       6       6       26      726     726     3726    8726    52      53      QXAAAA  KVNAAA  AAAAxx
+2339   9345    1       3       9       19      39      339     339     2339    2339    78      79      ZLAAAA  LVNAAA  HHHHxx
+2601   9346    1       1       1       1       1       601     601     2601    2601    2       3       BWAAAA  MVNAAA  OOOOxx
+9940   9347    0       0       0       0       40      940     1940    4940    9940    80      81      ISAAAA  NVNAAA  VVVVxx
+4185   9348    1       1       5       5       85      185     185     4185    4185    170     171     ZEAAAA  OVNAAA  AAAAxx
+9546   9349    0       2       6       6       46      546     1546    4546    9546    92      93      EDAAAA  PVNAAA  HHHHxx
+5218   9350    0       2       8       18      18      218     1218    218     5218    36      37      SSAAAA  QVNAAA  OOOOxx
+4374   9351    0       2       4       14      74      374     374     4374    4374    148     149     GMAAAA  RVNAAA  VVVVxx
+288    9352    0       0       8       8       88      288     288     288     288     176     177     CLAAAA  SVNAAA  AAAAxx
+7445   9353    1       1       5       5       45      445     1445    2445    7445    90      91      JAAAAA  TVNAAA  HHHHxx
+1710   9354    0       2       0       10      10      710     1710    1710    1710    20      21      UNAAAA  UVNAAA  OOOOxx
+6409   9355    1       1       9       9       9       409     409     1409    6409    18      19      NMAAAA  VVNAAA  VVVVxx
+7982   9356    0       2       2       2       82      982     1982    2982    7982    164     165     AVAAAA  WVNAAA  AAAAxx
+4950   9357    0       2       0       10      50      950     950     4950    4950    100     101     KIAAAA  XVNAAA  HHHHxx
+9242   9358    0       2       2       2       42      242     1242    4242    9242    84      85      MRAAAA  YVNAAA  OOOOxx
+3272   9359    0       0       2       12      72      272     1272    3272    3272    144     145     WVAAAA  ZVNAAA  VVVVxx
+739    9360    1       3       9       19      39      739     739     739     739     78      79      LCAAAA  AWNAAA  AAAAxx
+5526   9361    0       2       6       6       26      526     1526    526     5526    52      53      OEAAAA  BWNAAA  HHHHxx
+8189   9362    1       1       9       9       89      189     189     3189    8189    178     179     ZCAAAA  CWNAAA  OOOOxx
+9106   9363    0       2       6       6       6       106     1106    4106    9106    12      13      GMAAAA  DWNAAA  VVVVxx
+9775   9364    1       3       5       15      75      775     1775    4775    9775    150     151     ZLAAAA  EWNAAA  AAAAxx
+4643   9365    1       3       3       3       43      643     643     4643    4643    86      87      PWAAAA  FWNAAA  HHHHxx
+8396   9366    0       0       6       16      96      396     396     3396    8396    192     193     YKAAAA  GWNAAA  OOOOxx
+3255   9367    1       3       5       15      55      255     1255    3255    3255    110     111     FVAAAA  HWNAAA  VVVVxx
+301    9368    1       1       1       1       1       301     301     301     301     2       3       PLAAAA  IWNAAA  AAAAxx
+6014   9369    0       2       4       14      14      14      14      1014    6014    28      29      IXAAAA  JWNAAA  HHHHxx
+6046   9370    0       2       6       6       46      46      46      1046    6046    92      93      OYAAAA  KWNAAA  OOOOxx
+984    9371    0       0       4       4       84      984     984     984     984     168     169     WLAAAA  LWNAAA  VVVVxx
+2420   9372    0       0       0       0       20      420     420     2420    2420    40      41      CPAAAA  MWNAAA  AAAAxx
+2922   9373    0       2       2       2       22      922     922     2922    2922    44      45      KIAAAA  NWNAAA  HHHHxx
+2317   9374    1       1       7       17      17      317     317     2317    2317    34      35      DLAAAA  OWNAAA  OOOOxx
+7332   9375    0       0       2       12      32      332     1332    2332    7332    64      65      AWAAAA  PWNAAA  VVVVxx
+6451   9376    1       3       1       11      51      451     451     1451    6451    102     103     DOAAAA  QWNAAA  AAAAxx
+2589   9377    1       1       9       9       89      589     589     2589    2589    178     179     PVAAAA  RWNAAA  HHHHxx
+4333   9378    1       1       3       13      33      333     333     4333    4333    66      67      RKAAAA  SWNAAA  OOOOxx
+8650   9379    0       2       0       10      50      650     650     3650    8650    100     101     SUAAAA  TWNAAA  VVVVxx
+6856   9380    0       0       6       16      56      856     856     1856    6856    112     113     SDAAAA  UWNAAA  AAAAxx
+4194   9381    0       2       4       14      94      194     194     4194    4194    188     189     IFAAAA  VWNAAA  HHHHxx
+6246   9382    0       2       6       6       46      246     246     1246    6246    92      93      GGAAAA  WWNAAA  OOOOxx
+4371   9383    1       3       1       11      71      371     371     4371    4371    142     143     DMAAAA  XWNAAA  VVVVxx
+1388   9384    0       0       8       8       88      388     1388    1388    1388    176     177     KBAAAA  YWNAAA  AAAAxx
+1056   9385    0       0       6       16      56      56      1056    1056    1056    112     113     QOAAAA  ZWNAAA  HHHHxx
+6041   9386    1       1       1       1       41      41      41      1041    6041    82      83      JYAAAA  AXNAAA  OOOOxx
+6153   9387    1       1       3       13      53      153     153     1153    6153    106     107     RCAAAA  BXNAAA  VVVVxx
+8450   9388    0       2       0       10      50      450     450     3450    8450    100     101     ANAAAA  CXNAAA  AAAAxx
+3469   9389    1       1       9       9       69      469     1469    3469    3469    138     139     LDAAAA  DXNAAA  HHHHxx
+5226   9390    0       2       6       6       26      226     1226    226     5226    52      53      ATAAAA  EXNAAA  OOOOxx
+8112   9391    0       0       2       12      12      112     112     3112    8112    24      25      AAAAAA  FXNAAA  VVVVxx
+647    9392    1       3       7       7       47      647     647     647     647     94      95      XYAAAA  GXNAAA  AAAAxx
+2567   9393    1       3       7       7       67      567     567     2567    2567    134     135     TUAAAA  HXNAAA  HHHHxx
+9064   9394    0       0       4       4       64      64      1064    4064    9064    128     129     QKAAAA  IXNAAA  OOOOxx
+5161   9395    1       1       1       1       61      161     1161    161     5161    122     123     NQAAAA  JXNAAA  VVVVxx
+5260   9396    0       0       0       0       60      260     1260    260     5260    120     121     IUAAAA  KXNAAA  AAAAxx
+8988   9397    0       0       8       8       88      988     988     3988    8988    176     177     SHAAAA  LXNAAA  HHHHxx
+9678   9398    0       2       8       18      78      678     1678    4678    9678    156     157     GIAAAA  MXNAAA  OOOOxx
+6853   9399    1       1       3       13      53      853     853     1853    6853    106     107     PDAAAA  NXNAAA  VVVVxx
+5294   9400    0       2       4       14      94      294     1294    294     5294    188     189     QVAAAA  OXNAAA  AAAAxx
+9864   9401    0       0       4       4       64      864     1864    4864    9864    128     129     KPAAAA  PXNAAA  HHHHxx
+8702   9402    0       2       2       2       2       702     702     3702    8702    4       5       SWAAAA  QXNAAA  OOOOxx
+1132   9403    0       0       2       12      32      132     1132    1132    1132    64      65      ORAAAA  RXNAAA  VVVVxx
+1524   9404    0       0       4       4       24      524     1524    1524    1524    48      49      QGAAAA  SXNAAA  AAAAxx
+4560   9405    0       0       0       0       60      560     560     4560    4560    120     121     KTAAAA  TXNAAA  HHHHxx
+2137   9406    1       1       7       17      37      137     137     2137    2137    74      75      FEAAAA  UXNAAA  OOOOxx
+3283   9407    1       3       3       3       83      283     1283    3283    3283    166     167     HWAAAA  VXNAAA  VVVVxx
+3377   9408    1       1       7       17      77      377     1377    3377    3377    154     155     XZAAAA  WXNAAA  AAAAxx
+2267   9409    1       3       7       7       67      267     267     2267    2267    134     135     FJAAAA  XXNAAA  HHHHxx
+8987   9410    1       3       7       7       87      987     987     3987    8987    174     175     RHAAAA  YXNAAA  OOOOxx
+6709   9411    1       1       9       9       9       709     709     1709    6709    18      19      BYAAAA  ZXNAAA  VVVVxx
+8059   9412    1       3       9       19      59      59      59      3059    8059    118     119     ZXAAAA  AYNAAA  AAAAxx
+3402   9413    0       2       2       2       2       402     1402    3402    3402    4       5       WAAAAA  BYNAAA  HHHHxx
+6443   9414    1       3       3       3       43      443     443     1443    6443    86      87      VNAAAA  CYNAAA  OOOOxx
+8858   9415    0       2       8       18      58      858     858     3858    8858    116     117     SCAAAA  DYNAAA  VVVVxx
+3974   9416    0       2       4       14      74      974     1974    3974    3974    148     149     WWAAAA  EYNAAA  AAAAxx
+3521   9417    1       1       1       1       21      521     1521    3521    3521    42      43      LFAAAA  FYNAAA  HHHHxx
+9509   9418    1       1       9       9       9       509     1509    4509    9509    18      19      TBAAAA  GYNAAA  OOOOxx
+5442   9419    0       2       2       2       42      442     1442    442     5442    84      85      IBAAAA  HYNAAA  VVVVxx
+8968   9420    0       0       8       8       68      968     968     3968    8968    136     137     YGAAAA  IYNAAA  AAAAxx
+333    9421    1       1       3       13      33      333     333     333     333     66      67      VMAAAA  JYNAAA  HHHHxx
+952    9422    0       0       2       12      52      952     952     952     952     104     105     QKAAAA  KYNAAA  OOOOxx
+7482   9423    0       2       2       2       82      482     1482    2482    7482    164     165     UBAAAA  LYNAAA  VVVVxx
+1486   9424    0       2       6       6       86      486     1486    1486    1486    172     173     EFAAAA  MYNAAA  AAAAxx
+1815   9425    1       3       5       15      15      815     1815    1815    1815    30      31      VRAAAA  NYNAAA  HHHHxx
+7937   9426    1       1       7       17      37      937     1937    2937    7937    74      75      HTAAAA  OYNAAA  OOOOxx
+1436   9427    0       0       6       16      36      436     1436    1436    1436    72      73      GDAAAA  PYNAAA  VVVVxx
+3470   9428    0       2       0       10      70      470     1470    3470    3470    140     141     MDAAAA  QYNAAA  AAAAxx
+8195   9429    1       3       5       15      95      195     195     3195    8195    190     191     FDAAAA  RYNAAA  HHHHxx
+6906   9430    0       2       6       6       6       906     906     1906    6906    12      13      QFAAAA  SYNAAA  OOOOxx
+2539   9431    1       3       9       19      39      539     539     2539    2539    78      79      RTAAAA  TYNAAA  VVVVxx
+5988   9432    0       0       8       8       88      988     1988    988     5988    176     177     IWAAAA  UYNAAA  AAAAxx
+8908   9433    0       0       8       8       8       908     908     3908    8908    16      17      QEAAAA  VYNAAA  HHHHxx
+2319   9434    1       3       9       19      19      319     319     2319    2319    38      39      FLAAAA  WYNAAA  OOOOxx
+3263   9435    1       3       3       3       63      263     1263    3263    3263    126     127     NVAAAA  XYNAAA  VVVVxx
+4039   9436    1       3       9       19      39      39      39      4039    4039    78      79      JZAAAA  YYNAAA  AAAAxx
+6373   9437    1       1       3       13      73      373     373     1373    6373    146     147     DLAAAA  ZYNAAA  HHHHxx
+1168   9438    0       0       8       8       68      168     1168    1168    1168    136     137     YSAAAA  AZNAAA  OOOOxx
+8338   9439    0       2       8       18      38      338     338     3338    8338    76      77      SIAAAA  BZNAAA  VVVVxx
+1172   9440    0       0       2       12      72      172     1172    1172    1172    144     145     CTAAAA  CZNAAA  AAAAxx
+200    9441    0       0       0       0       0       200     200     200     200     0       1       SHAAAA  DZNAAA  HHHHxx
+6355   9442    1       3       5       15      55      355     355     1355    6355    110     111     LKAAAA  EZNAAA  OOOOxx
+7768   9443    0       0       8       8       68      768     1768    2768    7768    136     137     UMAAAA  FZNAAA  VVVVxx
+25     9444    1       1       5       5       25      25      25      25      25      50      51      ZAAAAA  GZNAAA  AAAAxx
+7144   9445    0       0       4       4       44      144     1144    2144    7144    88      89      UOAAAA  HZNAAA  HHHHxx
+8671   9446    1       3       1       11      71      671     671     3671    8671    142     143     NVAAAA  IZNAAA  OOOOxx
+9163   9447    1       3       3       3       63      163     1163    4163    9163    126     127     LOAAAA  JZNAAA  VVVVxx
+8889   9448    1       1       9       9       89      889     889     3889    8889    178     179     XDAAAA  KZNAAA  AAAAxx
+5950   9449    0       2       0       10      50      950     1950    950     5950    100     101     WUAAAA  LZNAAA  HHHHxx
+6163   9450    1       3       3       3       63      163     163     1163    6163    126     127     BDAAAA  MZNAAA  OOOOxx
+8119   9451    1       3       9       19      19      119     119     3119    8119    38      39      HAAAAA  NZNAAA  VVVVxx
+1416   9452    0       0       6       16      16      416     1416    1416    1416    32      33      MCAAAA  OZNAAA  AAAAxx
+4132   9453    0       0       2       12      32      132     132     4132    4132    64      65      YCAAAA  PZNAAA  HHHHxx
+2294   9454    0       2       4       14      94      294     294     2294    2294    188     189     GKAAAA  QZNAAA  OOOOxx
+9094   9455    0       2       4       14      94      94      1094    4094    9094    188     189     ULAAAA  RZNAAA  VVVVxx
+4168   9456    0       0       8       8       68      168     168     4168    4168    136     137     IEAAAA  SZNAAA  AAAAxx
+9108   9457    0       0       8       8       8       108     1108    4108    9108    16      17      IMAAAA  TZNAAA  HHHHxx
+5706   9458    0       2       6       6       6       706     1706    706     5706    12      13      MLAAAA  UZNAAA  OOOOxx
+2231   9459    1       3       1       11      31      231     231     2231    2231    62      63      VHAAAA  VZNAAA  VVVVxx
+2173   9460    1       1       3       13      73      173     173     2173    2173    146     147     PFAAAA  WZNAAA  AAAAxx
+90     9461    0       2       0       10      90      90      90      90      90      180     181     MDAAAA  XZNAAA  HHHHxx
+9996   9462    0       0       6       16      96      996     1996    4996    9996    192     193     MUAAAA  YZNAAA  OOOOxx
+330    9463    0       2       0       10      30      330     330     330     330     60      61      SMAAAA  ZZNAAA  VVVVxx
+2052   9464    0       0       2       12      52      52      52      2052    2052    104     105     YAAAAA  AAOAAA  AAAAxx
+1093   9465    1       1       3       13      93      93      1093    1093    1093    186     187     BQAAAA  BAOAAA  HHHHxx
+5817   9466    1       1       7       17      17      817     1817    817     5817    34      35      TPAAAA  CAOAAA  OOOOxx
+1559   9467    1       3       9       19      59      559     1559    1559    1559    118     119     ZHAAAA  DAOAAA  VVVVxx
+8405   9468    1       1       5       5       5       405     405     3405    8405    10      11      HLAAAA  EAOAAA  AAAAxx
+9962   9469    0       2       2       2       62      962     1962    4962    9962    124     125     ETAAAA  FAOAAA  HHHHxx
+9461   9470    1       1       1       1       61      461     1461    4461    9461    122     123     XZAAAA  GAOAAA  OOOOxx
+3028   9471    0       0       8       8       28      28      1028    3028    3028    56      57      MMAAAA  HAOAAA  VVVVxx
+6814   9472    0       2       4       14      14      814     814     1814    6814    28      29      CCAAAA  IAOAAA  AAAAxx
+9587   9473    1       3       7       7       87      587     1587    4587    9587    174     175     TEAAAA  JAOAAA  HHHHxx
+6863   9474    1       3       3       3       63      863     863     1863    6863    126     127     ZDAAAA  KAOAAA  OOOOxx
+4963   9475    1       3       3       3       63      963     963     4963    4963    126     127     XIAAAA  LAOAAA  VVVVxx
+7811   9476    1       3       1       11      11      811     1811    2811    7811    22      23      LOAAAA  MAOAAA  AAAAxx
+7608   9477    0       0       8       8       8       608     1608    2608    7608    16      17      QGAAAA  NAOAAA  HHHHxx
+5321   9478    1       1       1       1       21      321     1321    321     5321    42      43      RWAAAA  OAOAAA  OOOOxx
+9971   9479    1       3       1       11      71      971     1971    4971    9971    142     143     NTAAAA  PAOAAA  VVVVxx
+6161   9480    1       1       1       1       61      161     161     1161    6161    122     123     ZCAAAA  QAOAAA  AAAAxx
+2181   9481    1       1       1       1       81      181     181     2181    2181    162     163     XFAAAA  RAOAAA  HHHHxx
+3828   9482    0       0       8       8       28      828     1828    3828    3828    56      57      GRAAAA  SAOAAA  OOOOxx
+348    9483    0       0       8       8       48      348     348     348     348     96      97      KNAAAA  TAOAAA  VVVVxx
+5459   9484    1       3       9       19      59      459     1459    459     5459    118     119     ZBAAAA  UAOAAA  AAAAxx
+9406   9485    0       2       6       6       6       406     1406    4406    9406    12      13      UXAAAA  VAOAAA  HHHHxx
+9852   9486    0       0       2       12      52      852     1852    4852    9852    104     105     YOAAAA  WAOAAA  OOOOxx
+3095   9487    1       3       5       15      95      95      1095    3095    3095    190     191     BPAAAA  XAOAAA  VVVVxx
+5597   9488    1       1       7       17      97      597     1597    597     5597    194     195     HHAAAA  YAOAAA  AAAAxx
+8841   9489    1       1       1       1       41      841     841     3841    8841    82      83      BCAAAA  ZAOAAA  HHHHxx
+3536   9490    0       0       6       16      36      536     1536    3536    3536    72      73      AGAAAA  ABOAAA  OOOOxx
+4009   9491    1       1       9       9       9       9       9       4009    4009    18      19      FYAAAA  BBOAAA  VVVVxx
+7366   9492    0       2       6       6       66      366     1366    2366    7366    132     133     IXAAAA  CBOAAA  AAAAxx
+7327   9493    1       3       7       7       27      327     1327    2327    7327    54      55      VVAAAA  DBOAAA  HHHHxx
+1613   9494    1       1       3       13      13      613     1613    1613    1613    26      27      BKAAAA  EBOAAA  OOOOxx
+8619   9495    1       3       9       19      19      619     619     3619    8619    38      39      NTAAAA  FBOAAA  VVVVxx
+4880   9496    0       0       0       0       80      880     880     4880    4880    160     161     SFAAAA  GBOAAA  AAAAxx
+1552   9497    0       0       2       12      52      552     1552    1552    1552    104     105     SHAAAA  HBOAAA  HHHHxx
+7636   9498    0       0       6       16      36      636     1636    2636    7636    72      73      SHAAAA  IBOAAA  OOOOxx
+8397   9499    1       1       7       17      97      397     397     3397    8397    194     195     ZKAAAA  JBOAAA  VVVVxx
+6224   9500    0       0       4       4       24      224     224     1224    6224    48      49      KFAAAA  KBOAAA  AAAAxx
+9102   9501    0       2       2       2       2       102     1102    4102    9102    4       5       CMAAAA  LBOAAA  HHHHxx
+7906   9502    0       2       6       6       6       906     1906    2906    7906    12      13      CSAAAA  MBOAAA  OOOOxx
+9467   9503    1       3       7       7       67      467     1467    4467    9467    134     135     DAAAAA  NBOAAA  VVVVxx
+828    9504    0       0       8       8       28      828     828     828     828     56      57      WFAAAA  OBOAAA  AAAAxx
+9585   9505    1       1       5       5       85      585     1585    4585    9585    170     171     REAAAA  PBOAAA  HHHHxx
+925    9506    1       1       5       5       25      925     925     925     925     50      51      PJAAAA  QBOAAA  OOOOxx
+7375   9507    1       3       5       15      75      375     1375    2375    7375    150     151     RXAAAA  RBOAAA  VVVVxx
+4027   9508    1       3       7       7       27      27      27      4027    4027    54      55      XYAAAA  SBOAAA  AAAAxx
+766    9509    0       2       6       6       66      766     766     766     766     132     133     MDAAAA  TBOAAA  HHHHxx
+5633   9510    1       1       3       13      33      633     1633    633     5633    66      67      RIAAAA  UBOAAA  OOOOxx
+5648   9511    0       0       8       8       48      648     1648    648     5648    96      97      GJAAAA  VBOAAA  VVVVxx
+148    9512    0       0       8       8       48      148     148     148     148     96      97      SFAAAA  WBOAAA  AAAAxx
+2072   9513    0       0       2       12      72      72      72      2072    2072    144     145     SBAAAA  XBOAAA  HHHHxx
+431    9514    1       3       1       11      31      431     431     431     431     62      63      PQAAAA  YBOAAA  OOOOxx
+1711   9515    1       3       1       11      11      711     1711    1711    1711    22      23      VNAAAA  ZBOAAA  VVVVxx
+9378   9516    0       2       8       18      78      378     1378    4378    9378    156     157     SWAAAA  ACOAAA  AAAAxx
+6776   9517    0       0       6       16      76      776     776     1776    6776    152     153     QAAAAA  BCOAAA  HHHHxx
+6842   9518    0       2       2       2       42      842     842     1842    6842    84      85      EDAAAA  CCOAAA  OOOOxx
+2656   9519    0       0       6       16      56      656     656     2656    2656    112     113     EYAAAA  DCOAAA  VVVVxx
+3116   9520    0       0       6       16      16      116     1116    3116    3116    32      33      WPAAAA  ECOAAA  AAAAxx
+7904   9521    0       0       4       4       4       904     1904    2904    7904    8       9       ASAAAA  FCOAAA  HHHHxx
+3529   9522    1       1       9       9       29      529     1529    3529    3529    58      59      TFAAAA  GCOAAA  OOOOxx
+3240   9523    0       0       0       0       40      240     1240    3240    3240    80      81      QUAAAA  HCOAAA  VVVVxx
+5801   9524    1       1       1       1       1       801     1801    801     5801    2       3       DPAAAA  ICOAAA  AAAAxx
+4090   9525    0       2       0       10      90      90      90      4090    4090    180     181     IBAAAA  JCOAAA  HHHHxx
+7687   9526    1       3       7       7       87      687     1687    2687    7687    174     175     RJAAAA  KCOAAA  OOOOxx
+9711   9527    1       3       1       11      11      711     1711    4711    9711    22      23      NJAAAA  LCOAAA  VVVVxx
+4760   9528    0       0       0       0       60      760     760     4760    4760    120     121     CBAAAA  MCOAAA  AAAAxx
+5524   9529    0       0       4       4       24      524     1524    524     5524    48      49      MEAAAA  NCOAAA  HHHHxx
+2251   9530    1       3       1       11      51      251     251     2251    2251    102     103     PIAAAA  OCOAAA  OOOOxx
+1511   9531    1       3       1       11      11      511     1511    1511    1511    22      23      DGAAAA  PCOAAA  VVVVxx
+5991   9532    1       3       1       11      91      991     1991    991     5991    182     183     LWAAAA  QCOAAA  AAAAxx
+7808   9533    0       0       8       8       8       808     1808    2808    7808    16      17      IOAAAA  RCOAAA  HHHHxx
+8708   9534    0       0       8       8       8       708     708     3708    8708    16      17      YWAAAA  SCOAAA  OOOOxx
+8939   9535    1       3       9       19      39      939     939     3939    8939    78      79      VFAAAA  TCOAAA  VVVVxx
+4295   9536    1       3       5       15      95      295     295     4295    4295    190     191     FJAAAA  UCOAAA  AAAAxx
+5905   9537    1       1       5       5       5       905     1905    905     5905    10      11      DTAAAA  VCOAAA  HHHHxx
+2649   9538    1       1       9       9       49      649     649     2649    2649    98      99      XXAAAA  WCOAAA  OOOOxx
+2347   9539    1       3       7       7       47      347     347     2347    2347    94      95      HMAAAA  XCOAAA  VVVVxx
+6339   9540    1       3       9       19      39      339     339     1339    6339    78      79      VJAAAA  YCOAAA  AAAAxx
+292    9541    0       0       2       12      92      292     292     292     292     184     185     GLAAAA  ZCOAAA  HHHHxx
+9314   9542    0       2       4       14      14      314     1314    4314    9314    28      29      GUAAAA  ADOAAA  OOOOxx
+6893   9543    1       1       3       13      93      893     893     1893    6893    186     187     DFAAAA  BDOAAA  VVVVxx
+3970   9544    0       2       0       10      70      970     1970    3970    3970    140     141     SWAAAA  CDOAAA  AAAAxx
+1652   9545    0       0       2       12      52      652     1652    1652    1652    104     105     OLAAAA  DDOAAA  HHHHxx
+4326   9546    0       2       6       6       26      326     326     4326    4326    52      53      KKAAAA  EDOAAA  OOOOxx
+7881   9547    1       1       1       1       81      881     1881    2881    7881    162     163     DRAAAA  FDOAAA  VVVVxx
+5291   9548    1       3       1       11      91      291     1291    291     5291    182     183     NVAAAA  GDOAAA  AAAAxx
+957    9549    1       1       7       17      57      957     957     957     957     114     115     VKAAAA  HDOAAA  HHHHxx
+2313   9550    1       1       3       13      13      313     313     2313    2313    26      27      ZKAAAA  IDOAAA  OOOOxx
+5463   9551    1       3       3       3       63      463     1463    463     5463    126     127     DCAAAA  JDOAAA  VVVVxx
+1268   9552    0       0       8       8       68      268     1268    1268    1268    136     137     UWAAAA  KDOAAA  AAAAxx
+5028   9553    0       0       8       8       28      28      1028    28      5028    56      57      KLAAAA  LDOAAA  HHHHxx
+656    9554    0       0       6       16      56      656     656     656     656     112     113     GZAAAA  MDOAAA  OOOOxx
+9274   9555    0       2       4       14      74      274     1274    4274    9274    148     149     SSAAAA  NDOAAA  VVVVxx
+8217   9556    1       1       7       17      17      217     217     3217    8217    34      35      BEAAAA  ODOAAA  AAAAxx
+2175   9557    1       3       5       15      75      175     175     2175    2175    150     151     RFAAAA  PDOAAA  HHHHxx
+6028   9558    0       0       8       8       28      28      28      1028    6028    56      57      WXAAAA  QDOAAA  OOOOxx
+7584   9559    0       0       4       4       84      584     1584    2584    7584    168     169     SFAAAA  RDOAAA  VVVVxx
+4114   9560    0       2       4       14      14      114     114     4114    4114    28      29      GCAAAA  SDOAAA  AAAAxx
+8894   9561    0       2       4       14      94      894     894     3894    8894    188     189     CEAAAA  TDOAAA  HHHHxx
+781    9562    1       1       1       1       81      781     781     781     781     162     163     BEAAAA  UDOAAA  OOOOxx
+133    9563    1       1       3       13      33      133     133     133     133     66      67      DFAAAA  VDOAAA  VVVVxx
+7572   9564    0       0       2       12      72      572     1572    2572    7572    144     145     GFAAAA  WDOAAA  AAAAxx
+8514   9565    0       2       4       14      14      514     514     3514    8514    28      29      MPAAAA  XDOAAA  HHHHxx
+3352   9566    0       0       2       12      52      352     1352    3352    3352    104     105     YYAAAA  YDOAAA  OOOOxx
+8098   9567    0       2       8       18      98      98      98      3098    8098    196     197     MZAAAA  ZDOAAA  VVVVxx
+9116   9568    0       0       6       16      16      116     1116    4116    9116    32      33      QMAAAA  AEOAAA  AAAAxx
+9444   9569    0       0       4       4       44      444     1444    4444    9444    88      89      GZAAAA  BEOAAA  HHHHxx
+2590   9570    0       2       0       10      90      590     590     2590    2590    180     181     QVAAAA  CEOAAA  OOOOxx
+7302   9571    0       2       2       2       2       302     1302    2302    7302    4       5       WUAAAA  DEOAAA  VVVVxx
+7444   9572    0       0       4       4       44      444     1444    2444    7444    88      89      IAAAAA  EEOAAA  AAAAxx
+8748   9573    0       0       8       8       48      748     748     3748    8748    96      97      MYAAAA  FEOAAA  HHHHxx
+7615   9574    1       3       5       15      15      615     1615    2615    7615    30      31      XGAAAA  GEOAAA  OOOOxx
+6090   9575    0       2       0       10      90      90      90      1090    6090    180     181     GAAAAA  HEOAAA  VVVVxx
+1529   9576    1       1       9       9       29      529     1529    1529    1529    58      59      VGAAAA  IEOAAA  AAAAxx
+9398   9577    0       2       8       18      98      398     1398    4398    9398    196     197     MXAAAA  JEOAAA  HHHHxx
+6114   9578    0       2       4       14      14      114     114     1114    6114    28      29      EBAAAA  KEOAAA  OOOOxx
+2736   9579    0       0       6       16      36      736     736     2736    2736    72      73      GBAAAA  LEOAAA  VVVVxx
+468    9580    0       0       8       8       68      468     468     468     468     136     137     ASAAAA  MEOAAA  AAAAxx
+1487   9581    1       3       7       7       87      487     1487    1487    1487    174     175     FFAAAA  NEOAAA  HHHHxx
+4784   9582    0       0       4       4       84      784     784     4784    4784    168     169     ACAAAA  OEOAAA  OOOOxx
+6731   9583    1       3       1       11      31      731     731     1731    6731    62      63      XYAAAA  PEOAAA  VVVVxx
+3328   9584    0       0       8       8       28      328     1328    3328    3328    56      57      AYAAAA  QEOAAA  AAAAxx
+6891   9585    1       3       1       11      91      891     891     1891    6891    182     183     BFAAAA  REOAAA  HHHHxx
+8039   9586    1       3       9       19      39      39      39      3039    8039    78      79      FXAAAA  SEOAAA  OOOOxx
+4064   9587    0       0       4       4       64      64      64      4064    4064    128     129     IAAAAA  TEOAAA  VVVVxx
+542    9588    0       2       2       2       42      542     542     542     542     84      85      WUAAAA  UEOAAA  AAAAxx
+1039   9589    1       3       9       19      39      39      1039    1039    1039    78      79      ZNAAAA  VEOAAA  HHHHxx
+5603   9590    1       3       3       3       3       603     1603    603     5603    6       7       NHAAAA  WEOAAA  OOOOxx
+6641   9591    1       1       1       1       41      641     641     1641    6641    82      83      LVAAAA  XEOAAA  VVVVxx
+6307   9592    1       3       7       7       7       307     307     1307    6307    14      15      PIAAAA  YEOAAA  AAAAxx
+5354   9593    0       2       4       14      54      354     1354    354     5354    108     109     YXAAAA  ZEOAAA  HHHHxx
+7878   9594    0       2       8       18      78      878     1878    2878    7878    156     157     ARAAAA  AFOAAA  OOOOxx
+6391   9595    1       3       1       11      91      391     391     1391    6391    182     183     VLAAAA  BFOAAA  VVVVxx
+4575   9596    1       3       5       15      75      575     575     4575    4575    150     151     ZTAAAA  CFOAAA  AAAAxx
+6644   9597    0       0       4       4       44      644     644     1644    6644    88      89      OVAAAA  DFOAAA  HHHHxx
+5207   9598    1       3       7       7       7       207     1207    207     5207    14      15      HSAAAA  EFOAAA  OOOOxx
+1736   9599    0       0       6       16      36      736     1736    1736    1736    72      73      UOAAAA  FFOAAA  VVVVxx
+3547   9600    1       3       7       7       47      547     1547    3547    3547    94      95      LGAAAA  GFOAAA  AAAAxx
+6647   9601    1       3       7       7       47      647     647     1647    6647    94      95      RVAAAA  HFOAAA  HHHHxx
+4107   9602    1       3       7       7       7       107     107     4107    4107    14      15      ZBAAAA  IFOAAA  OOOOxx
+8125   9603    1       1       5       5       25      125     125     3125    8125    50      51      NAAAAA  JFOAAA  VVVVxx
+9223   9604    1       3       3       3       23      223     1223    4223    9223    46      47      TQAAAA  KFOAAA  AAAAxx
+6903   9605    1       3       3       3       3       903     903     1903    6903    6       7       NFAAAA  LFOAAA  HHHHxx
+3639   9606    1       3       9       19      39      639     1639    3639    3639    78      79      ZJAAAA  MFOAAA  OOOOxx
+9606   9607    0       2       6       6       6       606     1606    4606    9606    12      13      MFAAAA  NFOAAA  VVVVxx
+3232   9608    0       0       2       12      32      232     1232    3232    3232    64      65      IUAAAA  OFOAAA  AAAAxx
+2063   9609    1       3       3       3       63      63      63      2063    2063    126     127     JBAAAA  PFOAAA  HHHHxx
+3731   9610    1       3       1       11      31      731     1731    3731    3731    62      63      NNAAAA  QFOAAA  OOOOxx
+2558   9611    0       2       8       18      58      558     558     2558    2558    116     117     KUAAAA  RFOAAA  VVVVxx
+2357   9612    1       1       7       17      57      357     357     2357    2357    114     115     RMAAAA  SFOAAA  AAAAxx
+6008   9613    0       0       8       8       8       8       8       1008    6008    16      17      CXAAAA  TFOAAA  HHHHxx
+8246   9614    0       2       6       6       46      246     246     3246    8246    92      93      EFAAAA  UFOAAA  OOOOxx
+8220   9615    0       0       0       0       20      220     220     3220    8220    40      41      EEAAAA  VFOAAA  VVVVxx
+1075   9616    1       3       5       15      75      75      1075    1075    1075    150     151     JPAAAA  WFOAAA  AAAAxx
+2410   9617    0       2       0       10      10      410     410     2410    2410    20      21      SOAAAA  XFOAAA  HHHHxx
+3253   9618    1       1       3       13      53      253     1253    3253    3253    106     107     DVAAAA  YFOAAA  OOOOxx
+4370   9619    0       2       0       10      70      370     370     4370    4370    140     141     CMAAAA  ZFOAAA  VVVVxx
+8426   9620    0       2       6       6       26      426     426     3426    8426    52      53      CMAAAA  AGOAAA  AAAAxx
+2262   9621    0       2       2       2       62      262     262     2262    2262    124     125     AJAAAA  BGOAAA  HHHHxx
+4149   9622    1       1       9       9       49      149     149     4149    4149    98      99      PDAAAA  CGOAAA  OOOOxx
+2732   9623    0       0       2       12      32      732     732     2732    2732    64      65      CBAAAA  DGOAAA  VVVVxx
+8606   9624    0       2       6       6       6       606     606     3606    8606    12      13      ATAAAA  EGOAAA  AAAAxx
+6311   9625    1       3       1       11      11      311     311     1311    6311    22      23      TIAAAA  FGOAAA  HHHHxx
+7223   9626    1       3       3       3       23      223     1223    2223    7223    46      47      VRAAAA  GGOAAA  OOOOxx
+3054   9627    0       2       4       14      54      54      1054    3054    3054    108     109     MNAAAA  HGOAAA  VVVVxx
+3952   9628    0       0       2       12      52      952     1952    3952    3952    104     105     AWAAAA  IGOAAA  AAAAxx
+8252   9629    0       0       2       12      52      252     252     3252    8252    104     105     KFAAAA  JGOAAA  HHHHxx
+6020   9630    0       0       0       0       20      20      20      1020    6020    40      41      OXAAAA  KGOAAA  OOOOxx
+3846   9631    0       2       6       6       46      846     1846    3846    3846    92      93      YRAAAA  LGOAAA  VVVVxx
+3755   9632    1       3       5       15      55      755     1755    3755    3755    110     111     LOAAAA  MGOAAA  AAAAxx
+3765   9633    1       1       5       5       65      765     1765    3765    3765    130     131     VOAAAA  NGOAAA  HHHHxx
+3434   9634    0       2       4       14      34      434     1434    3434    3434    68      69      CCAAAA  OGOAAA  OOOOxx
+1381   9635    1       1       1       1       81      381     1381    1381    1381    162     163     DBAAAA  PGOAAA  VVVVxx
+287    9636    1       3       7       7       87      287     287     287     287     174     175     BLAAAA  QGOAAA  AAAAxx
+4476   9637    0       0       6       16      76      476     476     4476    4476    152     153     EQAAAA  RGOAAA  HHHHxx
+2916   9638    0       0       6       16      16      916     916     2916    2916    32      33      EIAAAA  SGOAAA  OOOOxx
+4517   9639    1       1       7       17      17      517     517     4517    4517    34      35      TRAAAA  TGOAAA  VVVVxx
+4561   9640    1       1       1       1       61      561     561     4561    4561    122     123     LTAAAA  UGOAAA  AAAAxx
+5106   9641    0       2       6       6       6       106     1106    106     5106    12      13      KOAAAA  VGOAAA  HHHHxx
+2077   9642    1       1       7       17      77      77      77      2077    2077    154     155     XBAAAA  WGOAAA  OOOOxx
+5269   9643    1       1       9       9       69      269     1269    269     5269    138     139     RUAAAA  XGOAAA  VVVVxx
+5688   9644    0       0       8       8       88      688     1688    688     5688    176     177     UKAAAA  YGOAAA  AAAAxx
+8831   9645    1       3       1       11      31      831     831     3831    8831    62      63      RBAAAA  ZGOAAA  HHHHxx
+3867   9646    1       3       7       7       67      867     1867    3867    3867    134     135     TSAAAA  AHOAAA  OOOOxx
+6062   9647    0       2       2       2       62      62      62      1062    6062    124     125     EZAAAA  BHOAAA  VVVVxx
+8460   9648    0       0       0       0       60      460     460     3460    8460    120     121     KNAAAA  CHOAAA  AAAAxx
+3138   9649    0       2       8       18      38      138     1138    3138    3138    76      77      SQAAAA  DHOAAA  HHHHxx
+3173   9650    1       1       3       13      73      173     1173    3173    3173    146     147     BSAAAA  EHOAAA  OOOOxx
+7018   9651    0       2       8       18      18      18      1018    2018    7018    36      37      YJAAAA  FHOAAA  VVVVxx
+4836   9652    0       0       6       16      36      836     836     4836    4836    72      73      AEAAAA  GHOAAA  AAAAxx
+1007   9653    1       3       7       7       7       7       1007    1007    1007    14      15      TMAAAA  HHOAAA  HHHHxx
+658    9654    0       2       8       18      58      658     658     658     658     116     117     IZAAAA  IHOAAA  OOOOxx
+5205   9655    1       1       5       5       5       205     1205    205     5205    10      11      FSAAAA  JHOAAA  VVVVxx
+5805   9656    1       1       5       5       5       805     1805    805     5805    10      11      HPAAAA  KHOAAA  AAAAxx
+5959   9657    1       3       9       19      59      959     1959    959     5959    118     119     FVAAAA  LHOAAA  HHHHxx
+2863   9658    1       3       3       3       63      863     863     2863    2863    126     127     DGAAAA  MHOAAA  OOOOxx
+7272   9659    0       0       2       12      72      272     1272    2272    7272    144     145     STAAAA  NHOAAA  VVVVxx
+8437   9660    1       1       7       17      37      437     437     3437    8437    74      75      NMAAAA  OHOAAA  AAAAxx
+4900   9661    0       0       0       0       0       900     900     4900    4900    0       1       MGAAAA  PHOAAA  HHHHxx
+890    9662    0       2       0       10      90      890     890     890     890     180     181     GIAAAA  QHOAAA  OOOOxx
+3530   9663    0       2       0       10      30      530     1530    3530    3530    60      61      UFAAAA  RHOAAA  VVVVxx
+6209   9664    1       1       9       9       9       209     209     1209    6209    18      19      VEAAAA  SHOAAA  AAAAxx
+4595   9665    1       3       5       15      95      595     595     4595    4595    190     191     TUAAAA  THOAAA  HHHHxx
+5982   9666    0       2       2       2       82      982     1982    982     5982    164     165     CWAAAA  UHOAAA  OOOOxx
+1101   9667    1       1       1       1       1       101     1101    1101    1101    2       3       JQAAAA  VHOAAA  VVVVxx
+9555   9668    1       3       5       15      55      555     1555    4555    9555    110     111     NDAAAA  WHOAAA  AAAAxx
+1918   9669    0       2       8       18      18      918     1918    1918    1918    36      37      UVAAAA  XHOAAA  HHHHxx
+3527   9670    1       3       7       7       27      527     1527    3527    3527    54      55      RFAAAA  YHOAAA  OOOOxx
+7309   9671    1       1       9       9       9       309     1309    2309    7309    18      19      DVAAAA  ZHOAAA  VVVVxx
+8213   9672    1       1       3       13      13      213     213     3213    8213    26      27      XDAAAA  AIOAAA  AAAAxx
+306    9673    0       2       6       6       6       306     306     306     306     12      13      ULAAAA  BIOAAA  HHHHxx
+845    9674    1       1       5       5       45      845     845     845     845     90      91      NGAAAA  CIOAAA  OOOOxx
+16     9675    0       0       6       16      16      16      16      16      16      32      33      QAAAAA  DIOAAA  VVVVxx
+437    9676    1       1       7       17      37      437     437     437     437     74      75      VQAAAA  EIOAAA  AAAAxx
+9518   9677    0       2       8       18      18      518     1518    4518    9518    36      37      CCAAAA  FIOAAA  HHHHxx
+2142   9678    0       2       2       2       42      142     142     2142    2142    84      85      KEAAAA  GIOAAA  OOOOxx
+8121   9679    1       1       1       1       21      121     121     3121    8121    42      43      JAAAAA  HIOAAA  VVVVxx
+7354   9680    0       2       4       14      54      354     1354    2354    7354    108     109     WWAAAA  IIOAAA  AAAAxx
+1720   9681    0       0       0       0       20      720     1720    1720    1720    40      41      EOAAAA  JIOAAA  HHHHxx
+6078   9682    0       2       8       18      78      78      78      1078    6078    156     157     UZAAAA  KIOAAA  OOOOxx
+5929   9683    1       1       9       9       29      929     1929    929     5929    58      59      BUAAAA  LIOAAA  VVVVxx
+3856   9684    0       0       6       16      56      856     1856    3856    3856    112     113     ISAAAA  MIOAAA  AAAAxx
+3424   9685    0       0       4       4       24      424     1424    3424    3424    48      49      SBAAAA  NIOAAA  HHHHxx
+1712   9686    0       0       2       12      12      712     1712    1712    1712    24      25      WNAAAA  OIOAAA  OOOOxx
+2340   9687    0       0       0       0       40      340     340     2340    2340    80      81      AMAAAA  PIOAAA  VVVVxx
+5570   9688    0       2       0       10      70      570     1570    570     5570    140     141     GGAAAA  QIOAAA  AAAAxx
+8734   9689    0       2       4       14      34      734     734     3734    8734    68      69      YXAAAA  RIOAAA  HHHHxx
+6077   9690    1       1       7       17      77      77      77      1077    6077    154     155     TZAAAA  SIOAAA  OOOOxx
+2960   9691    0       0       0       0       60      960     960     2960    2960    120     121     WJAAAA  TIOAAA  VVVVxx
+5062   9692    0       2       2       2       62      62      1062    62      5062    124     125     SMAAAA  UIOAAA  AAAAxx
+1532   9693    0       0       2       12      32      532     1532    1532    1532    64      65      YGAAAA  VIOAAA  HHHHxx
+8298   9694    0       2       8       18      98      298     298     3298    8298    196     197     EHAAAA  WIOAAA  OOOOxx
+2496   9695    0       0       6       16      96      496     496     2496    2496    192     193     ASAAAA  XIOAAA  VVVVxx
+8412   9696    0       0       2       12      12      412     412     3412    8412    24      25      OLAAAA  YIOAAA  AAAAxx
+724    9697    0       0       4       4       24      724     724     724     724     48      49      WBAAAA  ZIOAAA  HHHHxx
+1019   9698    1       3       9       19      19      19      1019    1019    1019    38      39      FNAAAA  AJOAAA  OOOOxx
+6265   9699    1       1       5       5       65      265     265     1265    6265    130     131     ZGAAAA  BJOAAA  VVVVxx
+740    9700    0       0       0       0       40      740     740     740     740     80      81      MCAAAA  CJOAAA  AAAAxx
+8495   9701    1       3       5       15      95      495     495     3495    8495    190     191     TOAAAA  DJOAAA  HHHHxx
+6983   9702    1       3       3       3       83      983     983     1983    6983    166     167     PIAAAA  EJOAAA  OOOOxx
+991    9703    1       3       1       11      91      991     991     991     991     182     183     DMAAAA  FJOAAA  VVVVxx
+3189   9704    1       1       9       9       89      189     1189    3189    3189    178     179     RSAAAA  GJOAAA  AAAAxx
+4487   9705    1       3       7       7       87      487     487     4487    4487    174     175     PQAAAA  HJOAAA  HHHHxx
+5554   9706    0       2       4       14      54      554     1554    554     5554    108     109     QFAAAA  IJOAAA  OOOOxx
+1258   9707    0       2       8       18      58      258     1258    1258    1258    116     117     KWAAAA  JJOAAA  VVVVxx
+5359   9708    1       3       9       19      59      359     1359    359     5359    118     119     DYAAAA  KJOAAA  AAAAxx
+2709   9709    1       1       9       9       9       709     709     2709    2709    18      19      FAAAAA  LJOAAA  HHHHxx
+361    9710    1       1       1       1       61      361     361     361     361     122     123     XNAAAA  MJOAAA  OOOOxx
+4028   9711    0       0       8       8       28      28      28      4028    4028    56      57      YYAAAA  NJOAAA  VVVVxx
+3735   9712    1       3       5       15      35      735     1735    3735    3735    70      71      RNAAAA  OJOAAA  AAAAxx
+4427   9713    1       3       7       7       27      427     427     4427    4427    54      55      HOAAAA  PJOAAA  HHHHxx
+7540   9714    0       0       0       0       40      540     1540    2540    7540    80      81      AEAAAA  QJOAAA  OOOOxx
+3569   9715    1       1       9       9       69      569     1569    3569    3569    138     139     HHAAAA  RJOAAA  VVVVxx
+1916   9716    0       0       6       16      16      916     1916    1916    1916    32      33      SVAAAA  SJOAAA  AAAAxx
+7596   9717    0       0       6       16      96      596     1596    2596    7596    192     193     EGAAAA  TJOAAA  HHHHxx
+9721   9718    1       1       1       1       21      721     1721    4721    9721    42      43      XJAAAA  UJOAAA  OOOOxx
+4429   9719    1       1       9       9       29      429     429     4429    4429    58      59      JOAAAA  VJOAAA  VVVVxx
+3471   9720    1       3       1       11      71      471     1471    3471    3471    142     143     NDAAAA  WJOAAA  AAAAxx
+1157   9721    1       1       7       17      57      157     1157    1157    1157    114     115     NSAAAA  XJOAAA  HHHHxx
+5700   9722    0       0       0       0       0       700     1700    700     5700    0       1       GLAAAA  YJOAAA  OOOOxx
+4431   9723    1       3       1       11      31      431     431     4431    4431    62      63      LOAAAA  ZJOAAA  VVVVxx
+9409   9724    1       1       9       9       9       409     1409    4409    9409    18      19      XXAAAA  AKOAAA  AAAAxx
+8752   9725    0       0       2       12      52      752     752     3752    8752    104     105     QYAAAA  BKOAAA  HHHHxx
+9484   9726    0       0       4       4       84      484     1484    4484    9484    168     169     UAAAAA  CKOAAA  OOOOxx
+1266   9727    0       2       6       6       66      266     1266    1266    1266    132     133     SWAAAA  DKOAAA  VVVVxx
+9097   9728    1       1       7       17      97      97      1097    4097    9097    194     195     XLAAAA  EKOAAA  AAAAxx
+3068   9729    0       0       8       8       68      68      1068    3068    3068    136     137     AOAAAA  FKOAAA  HHHHxx
+5490   9730    0       2       0       10      90      490     1490    490     5490    180     181     EDAAAA  GKOAAA  OOOOxx
+1375   9731    1       3       5       15      75      375     1375    1375    1375    150     151     XAAAAA  HKOAAA  VVVVxx
+2487   9732    1       3       7       7       87      487     487     2487    2487    174     175     RRAAAA  IKOAAA  AAAAxx
+1705   9733    1       1       5       5       5       705     1705    1705    1705    10      11      PNAAAA  JKOAAA  HHHHxx
+1571   9734    1       3       1       11      71      571     1571    1571    1571    142     143     LIAAAA  KKOAAA  OOOOxx
+4005   9735    1       1       5       5       5       5       5       4005    4005    10      11      BYAAAA  LKOAAA  VVVVxx
+5497   9736    1       1       7       17      97      497     1497    497     5497    194     195     LDAAAA  MKOAAA  AAAAxx
+2144   9737    0       0       4       4       44      144     144     2144    2144    88      89      MEAAAA  NKOAAA  HHHHxx
+4052   9738    0       0       2       12      52      52      52      4052    4052    104     105     WZAAAA  OKOAAA  OOOOxx
+4942   9739    0       2       2       2       42      942     942     4942    4942    84      85      CIAAAA  PKOAAA  VVVVxx
+5504   9740    0       0       4       4       4       504     1504    504     5504    8       9       SDAAAA  QKOAAA  AAAAxx
+2913   9741    1       1       3       13      13      913     913     2913    2913    26      27      BIAAAA  RKOAAA  HHHHxx
+5617   9742    1       1       7       17      17      617     1617    617     5617    34      35      BIAAAA  SKOAAA  OOOOxx
+8179   9743    1       3       9       19      79      179     179     3179    8179    158     159     PCAAAA  TKOAAA  VVVVxx
+9437   9744    1       1       7       17      37      437     1437    4437    9437    74      75      ZYAAAA  UKOAAA  AAAAxx
+1821   9745    1       1       1       1       21      821     1821    1821    1821    42      43      BSAAAA  VKOAAA  HHHHxx
+5737   9746    1       1       7       17      37      737     1737    737     5737    74      75      RMAAAA  WKOAAA  OOOOxx
+4207   9747    1       3       7       7       7       207     207     4207    4207    14      15      VFAAAA  XKOAAA  VVVVxx
+4815   9748    1       3       5       15      15      815     815     4815    4815    30      31      FDAAAA  YKOAAA  AAAAxx
+8707   9749    1       3       7       7       7       707     707     3707    8707    14      15      XWAAAA  ZKOAAA  HHHHxx
+5970   9750    0       2       0       10      70      970     1970    970     5970    140     141     QVAAAA  ALOAAA  OOOOxx
+5501   9751    1       1       1       1       1       501     1501    501     5501    2       3       PDAAAA  BLOAAA  VVVVxx
+4013   9752    1       1       3       13      13      13      13      4013    4013    26      27      JYAAAA  CLOAAA  AAAAxx
+9235   9753    1       3       5       15      35      235     1235    4235    9235    70      71      FRAAAA  DLOAAA  HHHHxx
+2503   9754    1       3       3       3       3       503     503     2503    2503    6       7       HSAAAA  ELOAAA  OOOOxx
+9181   9755    1       1       1       1       81      181     1181    4181    9181    162     163     DPAAAA  FLOAAA  VVVVxx
+2289   9756    1       1       9       9       89      289     289     2289    2289    178     179     BKAAAA  GLOAAA  AAAAxx
+4256   9757    0       0       6       16      56      256     256     4256    4256    112     113     SHAAAA  HLOAAA  HHHHxx
+191    9758    1       3       1       11      91      191     191     191     191     182     183     JHAAAA  ILOAAA  OOOOxx
+9655   9759    1       3       5       15      55      655     1655    4655    9655    110     111     JHAAAA  JLOAAA  VVVVxx
+8615   9760    1       3       5       15      15      615     615     3615    8615    30      31      JTAAAA  KLOAAA  AAAAxx
+3011   9761    1       3       1       11      11      11      1011    3011    3011    22      23      VLAAAA  LLOAAA  HHHHxx
+6376   9762    0       0       6       16      76      376     376     1376    6376    152     153     GLAAAA  MLOAAA  OOOOxx
+68     9763    0       0       8       8       68      68      68      68      68      136     137     QCAAAA  NLOAAA  VVVVxx
+4720   9764    0       0       0       0       20      720     720     4720    4720    40      41      OZAAAA  OLOAAA  AAAAxx
+6848   9765    0       0       8       8       48      848     848     1848    6848    96      97      KDAAAA  PLOAAA  HHHHxx
+456    9766    0       0       6       16      56      456     456     456     456     112     113     ORAAAA  QLOAAA  OOOOxx
+5887   9767    1       3       7       7       87      887     1887    887     5887    174     175     LSAAAA  RLOAAA  VVVVxx
+9249   9768    1       1       9       9       49      249     1249    4249    9249    98      99      TRAAAA  SLOAAA  AAAAxx
+4041   9769    1       1       1       1       41      41      41      4041    4041    82      83      LZAAAA  TLOAAA  HHHHxx
+2304   9770    0       0       4       4       4       304     304     2304    2304    8       9       QKAAAA  ULOAAA  OOOOxx
+8763   9771    1       3       3       3       63      763     763     3763    8763    126     127     BZAAAA  VLOAAA  VVVVxx
+2115   9772    1       3       5       15      15      115     115     2115    2115    30      31      JDAAAA  WLOAAA  AAAAxx
+8014   9773    0       2       4       14      14      14      14      3014    8014    28      29      GWAAAA  XLOAAA  HHHHxx
+9895   9774    1       3       5       15      95      895     1895    4895    9895    190     191     PQAAAA  YLOAAA  OOOOxx
+671    9775    1       3       1       11      71      671     671     671     671     142     143     VZAAAA  ZLOAAA  VVVVxx
+3774   9776    0       2       4       14      74      774     1774    3774    3774    148     149     EPAAAA  AMOAAA  AAAAxx
+134    9777    0       2       4       14      34      134     134     134     134     68      69      EFAAAA  BMOAAA  HHHHxx
+534    9778    0       2       4       14      34      534     534     534     534     68      69      OUAAAA  CMOAAA  OOOOxx
+7308   9779    0       0       8       8       8       308     1308    2308    7308    16      17      CVAAAA  DMOAAA  VVVVxx
+5244   9780    0       0       4       4       44      244     1244    244     5244    88      89      STAAAA  EMOAAA  AAAAxx
+1512   9781    0       0       2       12      12      512     1512    1512    1512    24      25      EGAAAA  FMOAAA  HHHHxx
+8960   9782    0       0       0       0       60      960     960     3960    8960    120     121     QGAAAA  GMOAAA  OOOOxx
+6602   9783    0       2       2       2       2       602     602     1602    6602    4       5       YTAAAA  HMOAAA  VVVVxx
+593    9784    1       1       3       13      93      593     593     593     593     186     187     VWAAAA  IMOAAA  AAAAxx
+2353   9785    1       1       3       13      53      353     353     2353    2353    106     107     NMAAAA  JMOAAA  HHHHxx
+4139   9786    1       3       9       19      39      139     139     4139    4139    78      79      FDAAAA  KMOAAA  OOOOxx
+3063   9787    1       3       3       3       63      63      1063    3063    3063    126     127     VNAAAA  LMOAAA  VVVVxx
+652    9788    0       0       2       12      52      652     652     652     652     104     105     CZAAAA  MMOAAA  AAAAxx
+7405   9789    1       1       5       5       5       405     1405    2405    7405    10      11      VYAAAA  NMOAAA  HHHHxx
+3034   9790    0       2       4       14      34      34      1034    3034    3034    68      69      SMAAAA  OMOAAA  OOOOxx
+4614   9791    0       2       4       14      14      614     614     4614    4614    28      29      MVAAAA  PMOAAA  VVVVxx
+2351   9792    1       3       1       11      51      351     351     2351    2351    102     103     LMAAAA  QMOAAA  AAAAxx
+8208   9793    0       0       8       8       8       208     208     3208    8208    16      17      SDAAAA  RMOAAA  HHHHxx
+5475   9794    1       3       5       15      75      475     1475    475     5475    150     151     PCAAAA  SMOAAA  OOOOxx
+6875   9795    1       3       5       15      75      875     875     1875    6875    150     151     LEAAAA  TMOAAA  VVVVxx
+563    9796    1       3       3       3       63      563     563     563     563     126     127     RVAAAA  UMOAAA  AAAAxx
+3346   9797    0       2       6       6       46      346     1346    3346    3346    92      93      SYAAAA  VMOAAA  HHHHxx
+291    9798    1       3       1       11      91      291     291     291     291     182     183     FLAAAA  WMOAAA  OOOOxx
+6345   9799    1       1       5       5       45      345     345     1345    6345    90      91      BKAAAA  XMOAAA  VVVVxx
+8099   9800    1       3       9       19      99      99      99      3099    8099    198     199     NZAAAA  YMOAAA  AAAAxx
+2078   9801    0       2       8       18      78      78      78      2078    2078    156     157     YBAAAA  ZMOAAA  HHHHxx
+8238   9802    0       2       8       18      38      238     238     3238    8238    76      77      WEAAAA  ANOAAA  OOOOxx
+4482   9803    0       2       2       2       82      482     482     4482    4482    164     165     KQAAAA  BNOAAA  VVVVxx
+716    9804    0       0       6       16      16      716     716     716     716     32      33      OBAAAA  CNOAAA  AAAAxx
+7288   9805    0       0       8       8       88      288     1288    2288    7288    176     177     IUAAAA  DNOAAA  HHHHxx
+5906   9806    0       2       6       6       6       906     1906    906     5906    12      13      ETAAAA  ENOAAA  OOOOxx
+5618   9807    0       2       8       18      18      618     1618    618     5618    36      37      CIAAAA  FNOAAA  VVVVxx
+1141   9808    1       1       1       1       41      141     1141    1141    1141    82      83      XRAAAA  GNOAAA  AAAAxx
+8231   9809    1       3       1       11      31      231     231     3231    8231    62      63      PEAAAA  HNOAAA  HHHHxx
+3713   9810    1       1       3       13      13      713     1713    3713    3713    26      27      VMAAAA  INOAAA  OOOOxx
+9158   9811    0       2       8       18      58      158     1158    4158    9158    116     117     GOAAAA  JNOAAA  VVVVxx
+4051   9812    1       3       1       11      51      51      51      4051    4051    102     103     VZAAAA  KNOAAA  AAAAxx
+1973   9813    1       1       3       13      73      973     1973    1973    1973    146     147     XXAAAA  LNOAAA  HHHHxx
+6710   9814    0       2       0       10      10      710     710     1710    6710    20      21      CYAAAA  MNOAAA  OOOOxx
+1021   9815    1       1       1       1       21      21      1021    1021    1021    42      43      HNAAAA  NNOAAA  VVVVxx
+2196   9816    0       0       6       16      96      196     196     2196    2196    192     193     MGAAAA  ONOAAA  AAAAxx
+8335   9817    1       3       5       15      35      335     335     3335    8335    70      71      PIAAAA  PNOAAA  HHHHxx
+2272   9818    0       0       2       12      72      272     272     2272    2272    144     145     KJAAAA  QNOAAA  OOOOxx
+3818   9819    0       2       8       18      18      818     1818    3818    3818    36      37      WQAAAA  RNOAAA  VVVVxx
+679    9820    1       3       9       19      79      679     679     679     679     158     159     DAAAAA  SNOAAA  AAAAxx
+7512   9821    0       0       2       12      12      512     1512    2512    7512    24      25      YCAAAA  TNOAAA  HHHHxx
+493    9822    1       1       3       13      93      493     493     493     493     186     187     ZSAAAA  UNOAAA  OOOOxx
+5663   9823    1       3       3       3       63      663     1663    663     5663    126     127     VJAAAA  VNOAAA  VVVVxx
+4655   9824    1       3       5       15      55      655     655     4655    4655    110     111     BXAAAA  WNOAAA  AAAAxx
+3996   9825    0       0       6       16      96      996     1996    3996    3996    192     193     SXAAAA  XNOAAA  HHHHxx
+8797   9826    1       1       7       17      97      797     797     3797    8797    194     195     JAAAAA  YNOAAA  OOOOxx
+2991   9827    1       3       1       11      91      991     991     2991    2991    182     183     BLAAAA  ZNOAAA  VVVVxx
+7038   9828    0       2       8       18      38      38      1038    2038    7038    76      77      SKAAAA  AOOAAA  AAAAxx
+4174   9829    0       2       4       14      74      174     174     4174    4174    148     149     OEAAAA  BOOAAA  HHHHxx
+6908   9830    0       0       8       8       8       908     908     1908    6908    16      17      SFAAAA  COOAAA  OOOOxx
+8477   9831    1       1       7       17      77      477     477     3477    8477    154     155     BOAAAA  DOOAAA  VVVVxx
+3576   9832    0       0       6       16      76      576     1576    3576    3576    152     153     OHAAAA  EOOAAA  AAAAxx
+2685   9833    1       1       5       5       85      685     685     2685    2685    170     171     HZAAAA  FOOAAA  HHHHxx
+9161   9834    1       1       1       1       61      161     1161    4161    9161    122     123     JOAAAA  GOOAAA  OOOOxx
+2951   9835    1       3       1       11      51      951     951     2951    2951    102     103     NJAAAA  HOOAAA  VVVVxx
+8362   9836    0       2       2       2       62      362     362     3362    8362    124     125     QJAAAA  IOOAAA  AAAAxx
+2379   9837    1       3       9       19      79      379     379     2379    2379    158     159     NNAAAA  JOOAAA  HHHHxx
+1277   9838    1       1       7       17      77      277     1277    1277    1277    154     155     DXAAAA  KOOAAA  OOOOxx
+1728   9839    0       0       8       8       28      728     1728    1728    1728    56      57      MOAAAA  LOOAAA  VVVVxx
+9816   9840    0       0       6       16      16      816     1816    4816    9816    32      33      ONAAAA  MOOAAA  AAAAxx
+6288   9841    0       0       8       8       88      288     288     1288    6288    176     177     WHAAAA  NOOAAA  HHHHxx
+8985   9842    1       1       5       5       85      985     985     3985    8985    170     171     PHAAAA  OOOAAA  OOOOxx
+771    9843    1       3       1       11      71      771     771     771     771     142     143     RDAAAA  POOAAA  VVVVxx
+464    9844    0       0       4       4       64      464     464     464     464     128     129     WRAAAA  QOOAAA  AAAAxx
+9625   9845    1       1       5       5       25      625     1625    4625    9625    50      51      FGAAAA  ROOAAA  HHHHxx
+9608   9846    0       0       8       8       8       608     1608    4608    9608    16      17      OFAAAA  SOOAAA  OOOOxx
+9170   9847    0       2       0       10      70      170     1170    4170    9170    140     141     SOAAAA  TOOAAA  VVVVxx
+9658   9848    0       2       8       18      58      658     1658    4658    9658    116     117     MHAAAA  UOOAAA  AAAAxx
+7515   9849    1       3       5       15      15      515     1515    2515    7515    30      31      BDAAAA  VOOAAA  HHHHxx
+9400   9850    0       0       0       0       0       400     1400    4400    9400    0       1       OXAAAA  WOOAAA  OOOOxx
+2045   9851    1       1       5       5       45      45      45      2045    2045    90      91      RAAAAA  XOOAAA  VVVVxx
+324    9852    0       0       4       4       24      324     324     324     324     48      49      MMAAAA  YOOAAA  AAAAxx
+4252   9853    0       0       2       12      52      252     252     4252    4252    104     105     OHAAAA  ZOOAAA  HHHHxx
+8329   9854    1       1       9       9       29      329     329     3329    8329    58      59      JIAAAA  APOAAA  OOOOxx
+4472   9855    0       0       2       12      72      472     472     4472    4472    144     145     AQAAAA  BPOAAA  VVVVxx
+1047   9856    1       3       7       7       47      47      1047    1047    1047    94      95      HOAAAA  CPOAAA  AAAAxx
+9341   9857    1       1       1       1       41      341     1341    4341    9341    82      83      HVAAAA  DPOAAA  HHHHxx
+7000   9858    0       0       0       0       0       0       1000    2000    7000    0       1       GJAAAA  EPOAAA  OOOOxx
+1429   9859    1       1       9       9       29      429     1429    1429    1429    58      59      ZCAAAA  FPOAAA  VVVVxx
+2701   9860    1       1       1       1       1       701     701     2701    2701    2       3       XZAAAA  GPOAAA  AAAAxx
+6630   9861    0       2       0       10      30      630     630     1630    6630    60      61      AVAAAA  HPOAAA  HHHHxx
+3669   9862    1       1       9       9       69      669     1669    3669    3669    138     139     DLAAAA  IPOAAA  OOOOxx
+8613   9863    1       1       3       13      13      613     613     3613    8613    26      27      HTAAAA  JPOAAA  VVVVxx
+7080   9864    0       0       0       0       80      80      1080    2080    7080    160     161     IMAAAA  KPOAAA  AAAAxx
+8788   9865    0       0       8       8       88      788     788     3788    8788    176     177     AAAAAA  LPOAAA  HHHHxx
+6291   9866    1       3       1       11      91      291     291     1291    6291    182     183     ZHAAAA  MPOAAA  OOOOxx
+7885   9867    1       1       5       5       85      885     1885    2885    7885    170     171     HRAAAA  NPOAAA  VVVVxx
+7160   9868    0       0       0       0       60      160     1160    2160    7160    120     121     KPAAAA  OPOAAA  AAAAxx
+6140   9869    0       0       0       0       40      140     140     1140    6140    80      81      ECAAAA  PPOAAA  HHHHxx
+9881   9870    1       1       1       1       81      881     1881    4881    9881    162     163     BQAAAA  QPOAAA  OOOOxx
+9140   9871    0       0       0       0       40      140     1140    4140    9140    80      81      ONAAAA  RPOAAA  VVVVxx
+644    9872    0       0       4       4       44      644     644     644     644     88      89      UYAAAA  SPOAAA  AAAAxx
+3667   9873    1       3       7       7       67      667     1667    3667    3667    134     135     BLAAAA  TPOAAA  HHHHxx
+2675   9874    1       3       5       15      75      675     675     2675    2675    150     151     XYAAAA  UPOAAA  OOOOxx
+9492   9875    0       0       2       12      92      492     1492    4492    9492    184     185     CBAAAA  VPOAAA  VVVVxx
+5004   9876    0       0       4       4       4       4       1004    4       5004    8       9       MKAAAA  WPOAAA  AAAAxx
+9456   9877    0       0       6       16      56      456     1456    4456    9456    112     113     SZAAAA  XPOAAA  HHHHxx
+8197   9878    1       1       7       17      97      197     197     3197    8197    194     195     HDAAAA  YPOAAA  OOOOxx
+2837   9879    1       1       7       17      37      837     837     2837    2837    74      75      DFAAAA  ZPOAAA  VVVVxx
+127    9880    1       3       7       7       27      127     127     127     127     54      55      XEAAAA  AQOAAA  AAAAxx
+9772   9881    0       0       2       12      72      772     1772    4772    9772    144     145     WLAAAA  BQOAAA  HHHHxx
+5743   9882    1       3       3       3       43      743     1743    743     5743    86      87      XMAAAA  CQOAAA  OOOOxx
+2007   9883    1       3       7       7       7       7       7       2007    2007    14      15      FZAAAA  DQOAAA  VVVVxx
+7586   9884    0       2       6       6       86      586     1586    2586    7586    172     173     UFAAAA  EQOAAA  AAAAxx
+45     9885    1       1       5       5       45      45      45      45      45      90      91      TBAAAA  FQOAAA  HHHHxx
+6482   9886    0       2       2       2       82      482     482     1482    6482    164     165     IPAAAA  GQOAAA  OOOOxx
+4565   9887    1       1       5       5       65      565     565     4565    4565    130     131     PTAAAA  HQOAAA  VVVVxx
+6975   9888    1       3       5       15      75      975     975     1975    6975    150     151     HIAAAA  IQOAAA  AAAAxx
+7260   9889    0       0       0       0       60      260     1260    2260    7260    120     121     GTAAAA  JQOAAA  HHHHxx
+2830   9890    0       2       0       10      30      830     830     2830    2830    60      61      WEAAAA  KQOAAA  OOOOxx
+9365   9891    1       1       5       5       65      365     1365    4365    9365    130     131     FWAAAA  LQOAAA  VVVVxx
+8207   9892    1       3       7       7       7       207     207     3207    8207    14      15      RDAAAA  MQOAAA  AAAAxx
+2506   9893    0       2       6       6       6       506     506     2506    2506    12      13      KSAAAA  NQOAAA  HHHHxx
+8081   9894    1       1       1       1       81      81      81      3081    8081    162     163     VYAAAA  OQOAAA  OOOOxx
+8678   9895    0       2       8       18      78      678     678     3678    8678    156     157     UVAAAA  PQOAAA  VVVVxx
+9932   9896    0       0       2       12      32      932     1932    4932    9932    64      65      ASAAAA  QQOAAA  AAAAxx
+447    9897    1       3       7       7       47      447     447     447     447     94      95      FRAAAA  RQOAAA  HHHHxx
+9187   9898    1       3       7       7       87      187     1187    4187    9187    174     175     JPAAAA  SQOAAA  OOOOxx
+89     9899    1       1       9       9       89      89      89      89      89      178     179     LDAAAA  TQOAAA  VVVVxx
+7027   9900    1       3       7       7       27      27      1027    2027    7027    54      55      HKAAAA  UQOAAA  AAAAxx
+1536   9901    0       0       6       16      36      536     1536    1536    1536    72      73      CHAAAA  VQOAAA  HHHHxx
+160    9902    0       0       0       0       60      160     160     160     160     120     121     EGAAAA  WQOAAA  OOOOxx
+7679   9903    1       3       9       19      79      679     1679    2679    7679    158     159     JJAAAA  XQOAAA  VVVVxx
+5973   9904    1       1       3       13      73      973     1973    973     5973    146     147     TVAAAA  YQOAAA  AAAAxx
+4401   9905    1       1       1       1       1       401     401     4401    4401    2       3       HNAAAA  ZQOAAA  HHHHxx
+395    9906    1       3       5       15      95      395     395     395     395     190     191     FPAAAA  AROAAA  OOOOxx
+4904   9907    0       0       4       4       4       904     904     4904    4904    8       9       QGAAAA  BROAAA  VVVVxx
+2759   9908    1       3       9       19      59      759     759     2759    2759    118     119     DCAAAA  CROAAA  AAAAxx
+8713   9909    1       1       3       13      13      713     713     3713    8713    26      27      DXAAAA  DROAAA  HHHHxx
+3770   9910    0       2       0       10      70      770     1770    3770    3770    140     141     APAAAA  EROAAA  OOOOxx
+8272   9911    0       0       2       12      72      272     272     3272    8272    144     145     EGAAAA  FROAAA  VVVVxx
+5358   9912    0       2       8       18      58      358     1358    358     5358    116     117     CYAAAA  GROAAA  AAAAxx
+9747   9913    1       3       7       7       47      747     1747    4747    9747    94      95      XKAAAA  HROAAA  HHHHxx
+1567   9914    1       3       7       7       67      567     1567    1567    1567    134     135     HIAAAA  IROAAA  OOOOxx
+2136   9915    0       0       6       16      36      136     136     2136    2136    72      73      EEAAAA  JROAAA  VVVVxx
+314    9916    0       2       4       14      14      314     314     314     314     28      29      CMAAAA  KROAAA  AAAAxx
+4583   9917    1       3       3       3       83      583     583     4583    4583    166     167     HUAAAA  LROAAA  HHHHxx
+375    9918    1       3       5       15      75      375     375     375     375     150     151     LOAAAA  MROAAA  OOOOxx
+5566   9919    0       2       6       6       66      566     1566    566     5566    132     133     CGAAAA  NROAAA  VVVVxx
+6865   9920    1       1       5       5       65      865     865     1865    6865    130     131     BEAAAA  OROAAA  AAAAxx
+894    9921    0       2       4       14      94      894     894     894     894     188     189     KIAAAA  PROAAA  HHHHxx
+5399   9922    1       3       9       19      99      399     1399    399     5399    198     199     RZAAAA  QROAAA  OOOOxx
+1385   9923    1       1       5       5       85      385     1385    1385    1385    170     171     HBAAAA  RROAAA  VVVVxx
+2156   9924    0       0       6       16      56      156     156     2156    2156    112     113     YEAAAA  SROAAA  AAAAxx
+9659   9925    1       3       9       19      59      659     1659    4659    9659    118     119     NHAAAA  TROAAA  HHHHxx
+477    9926    1       1       7       17      77      477     477     477     477     154     155     JSAAAA  UROAAA  OOOOxx
+8194   9927    0       2       4       14      94      194     194     3194    8194    188     189     EDAAAA  VROAAA  VVVVxx
+3937   9928    1       1       7       17      37      937     1937    3937    3937    74      75      LVAAAA  WROAAA  AAAAxx
+3745   9929    1       1       5       5       45      745     1745    3745    3745    90      91      BOAAAA  XROAAA  HHHHxx
+4096   9930    0       0       6       16      96      96      96      4096    4096    192     193     OBAAAA  YROAAA  OOOOxx
+5487   9931    1       3       7       7       87      487     1487    487     5487    174     175     BDAAAA  ZROAAA  VVVVxx
+2475   9932    1       3       5       15      75      475     475     2475    2475    150     151     FRAAAA  ASOAAA  AAAAxx
+6105   9933    1       1       5       5       5       105     105     1105    6105    10      11      VAAAAA  BSOAAA  HHHHxx
+6036   9934    0       0       6       16      36      36      36      1036    6036    72      73      EYAAAA  CSOAAA  OOOOxx
+1315   9935    1       3       5       15      15      315     1315    1315    1315    30      31      PYAAAA  DSOAAA  VVVVxx
+4473   9936    1       1       3       13      73      473     473     4473    4473    146     147     BQAAAA  ESOAAA  AAAAxx
+4016   9937    0       0       6       16      16      16      16      4016    4016    32      33      MYAAAA  FSOAAA  HHHHxx
+8135   9938    1       3       5       15      35      135     135     3135    8135    70      71      XAAAAA  GSOAAA  OOOOxx
+8892   9939    0       0       2       12      92      892     892     3892    8892    184     185     AEAAAA  HSOAAA  VVVVxx
+4850   9940    0       2       0       10      50      850     850     4850    4850    100     101     OEAAAA  ISOAAA  AAAAxx
+2545   9941    1       1       5       5       45      545     545     2545    2545    90      91      XTAAAA  JSOAAA  HHHHxx
+3788   9942    0       0       8       8       88      788     1788    3788    3788    176     177     SPAAAA  KSOAAA  OOOOxx
+1672   9943    0       0       2       12      72      672     1672    1672    1672    144     145     IMAAAA  LSOAAA  VVVVxx
+3664   9944    0       0       4       4       64      664     1664    3664    3664    128     129     YKAAAA  MSOAAA  AAAAxx
+3775   9945    1       3       5       15      75      775     1775    3775    3775    150     151     FPAAAA  NSOAAA  HHHHxx
+3103   9946    1       3       3       3       3       103     1103    3103    3103    6       7       JPAAAA  OSOAAA  OOOOxx
+9335   9947    1       3       5       15      35      335     1335    4335    9335    70      71      BVAAAA  PSOAAA  VVVVxx
+9200   9948    0       0       0       0       0       200     1200    4200    9200    0       1       WPAAAA  QSOAAA  AAAAxx
+8665   9949    1       1       5       5       65      665     665     3665    8665    130     131     HVAAAA  RSOAAA  HHHHxx
+1356   9950    0       0       6       16      56      356     1356    1356    1356    112     113     EAAAAA  SSOAAA  OOOOxx
+6118   9951    0       2       8       18      18      118     118     1118    6118    36      37      IBAAAA  TSOAAA  VVVVxx
+4605   9952    1       1       5       5       5       605     605     4605    4605    10      11      DVAAAA  USOAAA  AAAAxx
+5651   9953    1       3       1       11      51      651     1651    651     5651    102     103     JJAAAA  VSOAAA  HHHHxx
+9055   9954    1       3       5       15      55      55      1055    4055    9055    110     111     HKAAAA  WSOAAA  OOOOxx
+8461   9955    1       1       1       1       61      461     461     3461    8461    122     123     LNAAAA  XSOAAA  VVVVxx
+6107   9956    1       3       7       7       7       107     107     1107    6107    14      15      XAAAAA  YSOAAA  AAAAxx
+1967   9957    1       3       7       7       67      967     1967    1967    1967    134     135     RXAAAA  ZSOAAA  HHHHxx
+8910   9958    0       2       0       10      10      910     910     3910    8910    20      21      SEAAAA  ATOAAA  OOOOxx
+8257   9959    1       1       7       17      57      257     257     3257    8257    114     115     PFAAAA  BTOAAA  VVVVxx
+851    9960    1       3       1       11      51      851     851     851     851     102     103     TGAAAA  CTOAAA  AAAAxx
+7823   9961    1       3       3       3       23      823     1823    2823    7823    46      47      XOAAAA  DTOAAA  HHHHxx
+3208   9962    0       0       8       8       8       208     1208    3208    3208    16      17      KTAAAA  ETOAAA  OOOOxx
+856    9963    0       0       6       16      56      856     856     856     856     112     113     YGAAAA  FTOAAA  VVVVxx
+2654   9964    0       2       4       14      54      654     654     2654    2654    108     109     CYAAAA  GTOAAA  AAAAxx
+7185   9965    1       1       5       5       85      185     1185    2185    7185    170     171     JQAAAA  HTOAAA  HHHHxx
+309    9966    1       1       9       9       9       309     309     309     309     18      19      XLAAAA  ITOAAA  OOOOxx
+9752   9967    0       0       2       12      52      752     1752    4752    9752    104     105     CLAAAA  JTOAAA  VVVVxx
+6405   9968    1       1       5       5       5       405     405     1405    6405    10      11      JMAAAA  KTOAAA  AAAAxx
+6113   9969    1       1       3       13      13      113     113     1113    6113    26      27      DBAAAA  LTOAAA  HHHHxx
+9774   9970    0       2       4       14      74      774     1774    4774    9774    148     149     YLAAAA  MTOAAA  OOOOxx
+1674   9971    0       2       4       14      74      674     1674    1674    1674    148     149     KMAAAA  NTOAAA  VVVVxx
+9602   9972    0       2       2       2       2       602     1602    4602    9602    4       5       IFAAAA  OTOAAA  AAAAxx
+1363   9973    1       3       3       3       63      363     1363    1363    1363    126     127     LAAAAA  PTOAAA  HHHHxx
+6887   9974    1       3       7       7       87      887     887     1887    6887    174     175     XEAAAA  QTOAAA  OOOOxx
+6170   9975    0       2       0       10      70      170     170     1170    6170    140     141     IDAAAA  RTOAAA  VVVVxx
+8888   9976    0       0       8       8       88      888     888     3888    8888    176     177     WDAAAA  STOAAA  AAAAxx
+2981   9977    1       1       1       1       81      981     981     2981    2981    162     163     RKAAAA  TTOAAA  HHHHxx
+7369   9978    1       1       9       9       69      369     1369    2369    7369    138     139     LXAAAA  UTOAAA  OOOOxx
+6227   9979    1       3       7       7       27      227     227     1227    6227    54      55      NFAAAA  VTOAAA  VVVVxx
+8002   9980    0       2       2       2       2       2       2       3002    8002    4       5       UVAAAA  WTOAAA  AAAAxx
+4288   9981    0       0       8       8       88      288     288     4288    4288    176     177     YIAAAA  XTOAAA  HHHHxx
+5136   9982    0       0       6       16      36      136     1136    136     5136    72      73      OPAAAA  YTOAAA  OOOOxx
+1084   9983    0       0       4       4       84      84      1084    1084    1084    168     169     SPAAAA  ZTOAAA  VVVVxx
+9117   9984    1       1       7       17      17      117     1117    4117    9117    34      35      RMAAAA  AUOAAA  AAAAxx
+2406   9985    0       2       6       6       6       406     406     2406    2406    12      13      OOAAAA  BUOAAA  HHHHxx
+1384   9986    0       0       4       4       84      384     1384    1384    1384    168     169     GBAAAA  CUOAAA  OOOOxx
+9194   9987    0       2       4       14      94      194     1194    4194    9194    188     189     QPAAAA  DUOAAA  VVVVxx
+858    9988    0       2       8       18      58      858     858     858     858     116     117     AHAAAA  EUOAAA  AAAAxx
+8592   9989    0       0       2       12      92      592     592     3592    8592    184     185     MSAAAA  FUOAAA  HHHHxx
+4773   9990    1       1       3       13      73      773     773     4773    4773    146     147     PBAAAA  GUOAAA  OOOOxx
+4093   9991    1       1       3       13      93      93      93      4093    4093    186     187     LBAAAA  HUOAAA  VVVVxx
+6587   9992    1       3       7       7       87      587     587     1587    6587    174     175     JTAAAA  IUOAAA  AAAAxx
+6093   9993    1       1       3       13      93      93      93      1093    6093    186     187     JAAAAA  JUOAAA  HHHHxx
+429    9994    1       1       9       9       29      429     429     429     429     58      59      NQAAAA  KUOAAA  OOOOxx
+5780   9995    0       0       0       0       80      780     1780    780     5780    160     161     IOAAAA  LUOAAA  VVVVxx
+1783   9996    1       3       3       3       83      783     1783    1783    1783    166     167     PQAAAA  MUOAAA  AAAAxx
+2992   9997    0       0       2       12      92      992     992     2992    2992    184     185     CLAAAA  NUOAAA  HHHHxx
+0      9998    0       0       0       0       0       0       0       0       0       0       1       AAAAAA  OUOAAA  OOOOxx
+2968   9999    0       0       8       8       68      968     968     2968    2968    136     137     EKAAAA  PUOAAA  VVVVxx
diff --git a/src/test/regress/destroy.source b/src/test/regress/destroy.source
new file mode 100644 (file)
index 0000000..3521ae1
--- /dev/null
@@ -0,0 +1,285 @@
+--
+-- destroy.source
+--
+-- $Header$
+--
+
+--
+-- this will fail if the user is not the postgres superuser.
+-- if it does, don't worry about it (you can turn usersuper
+-- back on as "postgres").  too many people don't follow 
+-- directions and run this as "postgres", though...
+--
+UPDATE pg_user
+   SET usesuper = 't'::bool
+   WHERE usename = '_USER_';
+
+
+--
+-- FUNCTION REMOVAL
+--
+DROP FUNCTION hobbies(person);
+
+DROP FUNCTION hobby_construct(text,text);
+
+DROP FUNCTION equipment(hobbies_r);
+
+DROP FUNCTION user_relns();
+
+DROP FUNCTION circle_in(opaque);
+
+DROP FUNCTION circle_out(opaque);
+
+DROP FUNCTION pt_in_circle(point,circle);
+
+DROP FUNCTION overpaid(emp);
+
+DROP FUNCTION boxarea(box);
+
+DROP FUNCTION interpt_pp(path,path);
+
+DROP FUNCTION reverse_c16(char16);
+
+
+--
+-- OPERATOR REMOVAL
+--
+DROP OPERATOR ## (path, path);
+
+DROP OPERATOR <% (point, circle);
+
+-- left unary 
+DROP OPERATOR @#@ (none, int4);
+
+-- right unary 
+DROP OPERATOR #@# (int4, none);        
+
+-- right unary 
+DROP OPERATOR #%# (int4, none);        
+
+
+--
+-- ABSTRACT DATA TYPE REMOVAL
+--
+DROP TYPE city_budget;
+
+DROP TYPE circle;
+
+
+--
+-- RULE REMOVAL
+--     (is also tested in queries.source)
+--
+
+--
+-- AGGREGATE REMOVAL
+--
+DROP AGGREGATE newavg;
+
+DROP AGGREGATE newsum;
+
+DROP AGGREGATE newcnt;
+
+
+--
+-- CLASS REMOVAL
+--     (inheritance hierarchies are deleted in reverse order)
+--
+
+--
+-- DROP ancillary data structures (i.e. indices)
+--
+DROP INDEX onek_unique1;
+
+DROP INDEX onek_unique2;
+
+DROP INDEX onek_hundred;
+
+DROP INDEX onek_stringu1;
+
+DROP INDEX tenk1_unique1;
+
+DROP INDEX tenk1_unique2;
+
+DROP INDEX tenk1_hundred;
+
+DROP INDEX tenk2_unique1;
+
+DROP INDEX tenk2_unique2;
+
+DROP INDEX tenk2_hundred;
+
+-- DROP INDEX onek2_u1_prtl;
+
+-- DROP INDEX onek2_u2_prtl;
+
+-- DROP INDEX onek2_stu1_prtl;
+
+DROP INDEX rect2ind;
+
+DROP INDEX rix;
+
+DROP INDEX iix;
+
+DROP INDEX six;
+
+DROP INDEX hash_i4_index;
+
+DROP INDEX hash_c16_index;
+
+DROP INDEX hash_txt_index;
+
+DROP INDEX hash_f8_index;
+
+-- DROP INDEX hash_ovfl_index;
+
+DROP INDEX bt_i4_index;
+
+DROP INDEX bt_c16_index;
+
+DROP INDEX bt_txt_index;
+
+DROP INDEX bt_f8_index;
+
+
+DROP TABLE  onek;
+
+DROP TABLE  onek2;
+
+DROP TABLE  tenk1;
+
+DROP TABLE  tenk2;
+
+DROP TABLE  Bprime;
+
+
+DROP TABLE  hobbies_r;
+
+DROP TABLE  equipment_r;
+
+
+DROP TABLE  aggtest;
+
+DROP TABLE  xacttest;
+
+DROP TABLE  arrtest;
+
+DROP TABLE  iportaltest;
+
+
+DROP TABLE  f_star;
+
+DROP TABLE  e_star;
+
+DROP TABLE  d_star;
+
+DROP TABLE  c_star;
+
+DROP TABLE  b_star;
+
+DROP TABLE  a_star;
+
+
+--
+-- must be in reverse inheritance order
+--
+DROP TABLE  stud_emp;
+
+DROP TABLE  student;
+
+DROP TABLE  slow_emp4000;
+
+DROP TABLE  fast_emp4000;
+
+DROP TABLE  emp;
+
+DROP TABLE  person;
+
+
+DROP TABLE  ramp;
+
+DROP TABLE  real_city;
+
+DROP TABLE  dept;
+
+DROP TABLE  ihighway;
+
+DROP TABLE  shighway;
+
+DROP TABLE  road;
+
+DROP TABLE  city;
+
+
+DROP TABLE  hash_i4_heap;
+
+DROP TABLE  hash_c16_heap;
+
+DROP TABLE  hash_txt_heap;
+
+DROP TABLE  hash_f8_heap;
+
+-- DROP TABLE  hash_ovfl_heap;
+
+DROP TABLE  bt_i4_heap;
+
+DROP TABLE  bt_c16_heap;
+
+DROP TABLE  bt_txt_heap;
+
+DROP TABLE  bt_f8_heap;
+
+
+DROP TABLE  BOOLTBL1;
+
+DROP TABLE  BOOLTBL2;
+
+DROP TABLE  ABSTIME_TBL;
+
+DROP TABLE  RELTIME_TBL;
+
+DROP TABLE  TINTERVAL_TBL;
+
+DROP TABLE  BOX_TBL;
+
+DROP TABLE  CHAR_TBL;
+
+DROP TABLE  CHAR2_TBL;
+
+DROP TABLE  CHAR4_TBL;
+
+DROP TABLE  CHAR8_TBL;
+
+DROP TABLE  CHAR16_TBL;
+
+DROP TABLE  FLOAT4_TBL;
+
+DROP TABLE  FLOAT8_TBL;
+
+DROP TABLE  INT2_TBL;
+
+DROP TABLE  INT4_TBL;
+
+DROP TABLE  OID_TBL;
+
+DROP TABLE  OIDNAME_TBL;
+
+DROP TABLE  OIDINT2_TBL;
+
+DROP TABLE  OIDINT4_TBL;
+
+DROP TABLE  POINT_TBL;
+
+DROP TABLE  POLYGON_TBL;
+
+
+--
+-- VIRTUAL CLASS REMOVAL
+--     (also tests removal of rewrite rules)
+--
+DROP VIEW street;
+
+DROP VIEW iexit;
+
+DROP VIEW toyemp;
+
diff --git a/src/test/regress/errors.source b/src/test/regress/errors.source
new file mode 100644 (file)
index 0000000..aa2b6b8
--- /dev/null
@@ -0,0 +1,275 @@
+--
+-- errors.source
+--
+-- $Header$
+
+-- bad in postquel, but ok in postsql
+select 1
+
+
+--
+-- UNSUPPORTED STUFF
+-- doesn't work 
+-- attachas nonesuch
+--
+-- doesn't work 
+-- notify pg_class
+--
+
+--
+-- RETRIEVE
+-- missing relation name 
+select
+
+-- no such relation 
+select * from nonesuch;
+
+-- bad name in target list
+select nonesuch from pg_database;
+-- bad attribute name on lhs of operator
+select * from pg_database where nonesuch = pg_database.datname;
+
+-- bad attribute name on rhs of operator
+select * from pg_database where pg_database.datname = nonesuch;
+
+
+-- bad select distinct on  syntax,  distinct attribute missing
+select distinct on foobar from pg_database;
+
+
+-- bad select distinct on syntax, distinct attribute not in target list
+select distinct on foobar * from pg_database;
+
+
+--
+-- DELETE
+-- missing relation name (this had better not wildcard!) 
+delete from;
+
+-- no such relation 
+delete from nonesuch;
+
+
+--
+-- DESTROY
+-- missing relation name (this had better not wildcard!) 
+drop table;
+
+-- no such relation 
+drop table nonesuch;
+
+
+--
+-- RENAME
+
+-- relation renaming 
+
+-- missing relation name 
+alter table rename;
+
+-- no such relation 
+alter table nonesuch rename to newnonesuch;
+
+-- no such relation 
+alter table nonesuch rename to stud_emp;
+
+-- system relation 
+alter table stud_emp rename to pg_stud_emp;
+
+-- conflict 
+alter table stud_emp rename to aggtest;
+
+-- self-conflict 
+alter table stud_emp rename to stud_emp;
+
+
+-- attribute renaming 
+
+-- no such relation 
+alter table nonesuchrel rename column nonesuchatt to newnonesuchatt;
+
+-- no such attribute 
+alter table emp rename column nonesuchatt to newnonesuchatt;
+
+-- conflict 
+alter table emp rename column salary to manager;
+
+-- conflict 
+alter table emp rename column salary to oid;
+
+
+--
+-- TRANSACTION STUFF
+-- not in a xact 
+abort;
+
+-- not in a xact 
+end;
+
+
+--
+-- DEFINE AGGREGATE
+-- left out finalfunc 
+create aggregate newavg1 (sfunc1 = int4pl,
+                         basetype = int4,
+                         stype1 = int4,
+                         sfunc2 = int4inc,
+                         stype2 = int4, 
+                         initcond1 = '0',
+                         initcond2 = '0');
+
+-- sfunc return type disagreement 
+create aggregate newavg2 (sfunc1 = int4pl,
+                         basetype = int4,
+                         stype1 = int4,
+                         sfunc2 = int2inc,
+                         stype2 = int2,
+                         finalfunc = int4div,
+                         initcond1 = '0',
+                         initcond2 = '0');
+
+-- sfunc/finalfunc type disagreement 
+create aggregate newavg3 (sfunc1 = int4pl,
+                         basetype = int4,
+                         stype1 = int4,
+                         sfunc2 = int4inc,
+                         stype2 = int4,
+                         finalfunc = int2div,
+                         initcond1 = '0',
+                         initcond2 = '0');
+
+-- left out basetype
+create aggregate newcnt1 (sfunc2 = int4inc,
+                         stype2 = int4,
+                       initcond2 = '0');
+
+-- left out initcond2 (for sfunc2) 
+create aggregate newcnt1 (sfunc2 = int4inc,
+                         basetype = int4,
+                         stype2 = int4);
+
+
+--
+-- REMOVE INDEX
+-- missing index name 
+drop index;
+
+-- bad index name 
+drop index 314159;
+
+-- no such index 
+drop index nonesuch;
+
+
+--
+-- REMOVE AGGREGATE
+-- missing aggregate name 
+drop aggregate;
+
+-- bad aggregate name 
+drop aggregate 314159;
+
+-- no such aggregate 
+drop aggregate nonesuch;
+
+
+--
+-- REMOVE FUNCTION
+-- missing function name 
+drop function ();
+
+-- bad function name 
+drop function 314159();
+
+-- no such function 
+drop function nonesuch();
+
+
+--
+-- REMOVE TYPE
+-- missing type name 
+drop type;
+
+-- bad type name 
+drop type 314159;
+
+-- no such type 
+drop type nonesuch;
+
+
+--
+-- DROP OPERATOR
+-- missing everything 
+drop operator;
+
+-- bad operator name 
+drop operator equals;
+
+-- missing type list 
+drop operator ===;
+
+-- missing parentheses 
+drop operator int4, int4;
+
+-- missing operator name 
+drop operator (int4, int4);
+
+-- missing type list contents 
+drop operator === ();
+
+-- no such operator 
+drop operator === (int4);
+
+-- no such operator by that name 
+drop operator === (int4, int4);
+
+-- no such type1 
+drop operator = (nonesuch);
+
+-- no such type1 
+drop operator = ( , int4);
+
+-- no such type1 
+drop operator = (nonesuch, int4);
+
+-- no such type2 
+drop operator = (int4, nonesuch);
+
+-- no such type2 
+drop operator = (int4, );
+
+
+--
+-- DROP RULE
+-- missing rule name 
+drop rule;
+
+-- bad rule name 
+drop rule 314159;
+
+-- no such rule 
+drop rule nonesuch;
+
+-- bad keyword 
+drop tuple rule nonesuch;
+
+-- no such rule 
+drop instance rule nonesuch;
+
+-- no such rule 
+drop rewrite rule nonesuch;
+
diff --git a/src/test/regress/queries.source b/src/test/regress/queries.source
new file mode 100644 (file)
index 0000000..ed176e0
--- /dev/null
@@ -0,0 +1,2614 @@
+--
+-- queries.source
+--
+-- $Header$
+--
+-- The comments that contain sequences of UNIX commands generate the 
+-- desired output for the POSTQUEL statement(s).
+--
+
+--
+-- --- operators and target lists ---
+--
+
+--
+-- sanity check - if this fails go insane!
+--
+SELECT 1 AS one;
+
+
+-- ******************testing built-in type bool********************
+
+-- check bool type-casting as well as and, or, not in qualifications--
+
+SELECT 't'::bool AS true;
+
+SELECT 'f'::bool AS false;
+
+SELECT 't'::bool or 'f'::bool AS true;
+
+SELECT 't'::bool and 'f'::bool AS false;
+
+SELECT not 'f'::bool AS true;
+
+SELECT 't'::bool = 'f'::bool AS false;
+
+SELECT 't'::bool <> 'f'::bool AS true;
+
+
+CREATE TABLE BOOLTBL1 (f1 bool);
+
+INSERT INTO BOOLTBL1 (f1) VALUES ('t'::bool);
+
+INSERT INTO BOOLTBL1 (f1) VALUES ('True'::bool);
+
+INSERT INTO BOOLTBL1 (f1) VALUES ('true'::bool);
+
+
+-- BOOLTBL1 should be full of true's at this point 
+SELECT '' AS t_3, BOOLTBL1.*;
+
+
+SELECT '' AS t_3, BOOLTBL1.*
+   FROM BOOLTBL1
+   WHERE f1 = 'true'::bool;
+
+
+SELECT '' AS t_3, BOOLTBL1.* 
+   FROM BOOLTBL1
+   WHERE f1 <> 'false'::bool;
+
+SELECT '' AS zero, BOOLTBL1.*
+   FROM BOOLTBL1
+   WHERE booleq('false'::bool, f1);
+
+INSERT INTO BOOLTBL1 (f1) VALUES ('f'::bool);
+
+SELECT '' AS f_1, BOOLTBL1.* 
+   FROM BOOLTBL1
+   WHERE f1 = 'false'::bool;
+
+
+CREATE TABLE BOOLTBL2 (f1 bool);
+
+INSERT INTO BOOLTBL2 (f1) VALUES ('f'::bool);
+
+INSERT INTO BOOLTBL2 (f1) VALUES ('false'::bool);
+
+INSERT INTO BOOLTBL2 (f1) VALUES ('False'::bool);
+
+-- this evaluates to a false value 
+INSERT INTO BOOLTBL2 (f1) 
+   VALUES ('XXX'::bool);  
+
+
+-- BOOLTBL2 should be full of false's at this point 
+SELECT '' AS f_4, BOOLTBL2.*;
+
+
+SELECT '' AS tf_12, BOOLTBL1.*, BOOLTBL2.*
+   WHERE BOOLTBL2.f1 <> BOOLTBL1.f1;
+
+
+SELECT '' AS tf_12, BOOLTBL1.*, BOOLTBL2.*
+   WHERE boolne(BOOLTBL2.f1,BOOLTBL1.f1);
+
+
+SELECT '' AS ff_4, BOOLTBL1.*, BOOLTBL2.*
+   WHERE BOOLTBL2.f1 = BOOLTBL1.f1 and BOOLTBL1.f1 = 'false'::bool;
+
+
+SELECT '' AS tf_12_ff_4, BOOLTBL1.*, BOOLTBL2.*
+   WHERE BOOLTBL2.f1 = BOOLTBL1.f1 or BOOLTBL1.f1 = 'true'::bool;
+
+
+-- **** testing built-in time types: abstime, reltime, and tinterval ****
+
+--
+-- timezones may vary based not only on location but the operating
+-- system.  the main correctness issue is that the OS may not get 
+-- DST right for times prior to unix epoch (jan 1 1970).
+--
+
+CREATE TABLE ABSTIME_TBL (f1 abstime);
+
+INSERT INTO ABSTIME_TBL (f1) VALUES ('Jan 14, 1973 03:14:21');
+
+-- was INSERT INTO ABSTIME_TBL (f1) VALUES ('now'::abstime):
+INSERT INTO ABSTIME_TBL (f1) VALUES ('Mon May  1 00:30:30 PDT 1995'::abstime);
+
+INSERT INTO ABSTIME_TBL (f1) VALUES ('epoch'::abstime);
+
+INSERT INTO ABSTIME_TBL (f1) VALUES ('current'::abstime);
+
+INSERT INTO ABSTIME_TBL (f1) VALUES ('infinity'::abstime);
+
+INSERT INTO ABSTIME_TBL (f1) VALUES ('-infinity'::abstime);
+
+INSERT INTO ABSTIME_TBL (f1) VALUES ('May 10, 1943 23:59:12');
+
+
+-- what happens if we specify slightly misformatted abstime? 
+INSERT INTO ABSTIME_TBL (f1) VALUES ('Feb 35, 1946 10:00:00');
+
+INSERT INTO ABSTIME_TBL (f1) VALUES ('Feb 28, 1984 25:08:10');
+
+
+-- badly formatted abstimes:  these should result in invalid abstimes 
+INSERT INTO ABSTIME_TBL (f1) VALUES ('bad date format');
+
+INSERT INTO ABSTIME_TBL (f1) VALUES ('Jun 10, 1843');
+
+
+CREATE TABLE RELTIME_TBL (f1 reltime);
+
+INSERT INTO RELTIME_TBL (f1) VALUES ('@ 1 minute');
+
+INSERT INTO RELTIME_TBL (f1) VALUES ('@ 5 hour');
+
+INSERT INTO RELTIME_TBL (f1) VALUES ('@ 10 day');
+
+INSERT INTO RELTIME_TBL (f1) VALUES ('@ 34 year');
+
+INSERT INTO RELTIME_TBL (f1) VALUES ('@ 3 months');
+
+INSERT INTO RELTIME_TBL (f1) VALUES ('@ 14 seconds ago');
+
+
+-- badly formatted reltimes:   
+INSERT INTO RELTIME_TBL (f1) VALUES ('badly formatted reltime');
+
+INSERT INTO RELTIME_TBL (f1) VALUES ('@ 30 eons ago');
+
+
+CREATE TABLE TINTERVAL_TBL (f1  tinterval);
+
+INSERT INTO TINTERVAL_TBL (f1)
+   VALUES ('["-infinity" "infinity"]');
+
+INSERT INTO TINTERVAL_TBL (f1)
+   VALUES ('["May 10, 1943 23:59:12" "Jan 14, 1973 03:14:21"]');
+
+INSERT INTO TINTERVAL_TBL (f1)
+   VALUES ('["Sep 4, 1983 23:59:12" "Oct 4, 1983 23:59:12"]');
+
+INSERT INTO TINTERVAL_TBL (f1)
+   VALUES ('["epoch" "Mon May  1 00:30:30 PDT 1995"]');
+
+INSERT INTO TINTERVAL_TBL (f1)
+   VALUES ('["Feb 15 1990 12:15:03" "current"]');
+
+
+-- badly formatted tintervals 
+INSERT INTO TINTERVAL_TBL (f1)
+   VALUES ('["bad time specifications" ""]');
+
+INSERT INTO TINTERVAL_TBL (f1)
+   VALUES ('["" "infinity"]');
+
+
+-- test abstime operators 
+
+SELECT '' AS eleven, ABSTIME_TBL.*;
+
+SELECT '' AS eight, ABSTIME_TBL.*
+   WHERE ABSTIME_TBL.f1 < 'Jun 30, 2001'::abstime;
+
+SELECT '' AS eight, ABSTIME_TBL.*
+   WHERE ABSTIME_TBL.f1 > '-infinity'::abstime;
+
+SELECT '' AS eight, ABSTIME_TBL.*
+   WHERE 'May 10, 1943 23:59:12'::abstime <> ABSTIME_TBL.f1;
+
+SELECT '' AS one, ABSTIME_TBL.*
+   WHERE 'current'::abstime = ABSTIME_TBL.f1;
+
+SELECT '' AS five, ABSTIME_TBL.*
+   WHERE 'epoch'::abstime >= ABSTIME_TBL.f1;
+
+SELECT '' AS six, ABSTIME_TBL.*
+   WHERE ABSTIME_TBL.f1 <= 'Jan 14, 1973 03:14:21'::abstime;
+
+SELECT '' AS six, ABSTIME_TBL.*
+  WHERE ABSTIME_TBL.f1 <?>
+       '["Apr 1 1945 00:00:00" "Dec 30 1999 23:00:00"]'::tinterval;
+
+
+-- these four queries should return the same answer 
+-- the "infinity" and "-infinity" tuples in ABSTIME_TBL cannot be added and 
+-- therefore, should not show up in the results. 
+SELECT '' AS five, ABSTIME_TBL.*
+  WHERE  (ABSTIME_TBL.f1 + '@ 3 year'::reltime)     -- +3 years 
+       < 'Jan 14 14:00:00 1977'::abstime; 
+
+SELECT '' AS five, ABSTIME_TBL.*
+   WHERE  (ABSTIME_TBL.f1 + '@ 3 year ago'::reltime) -- -3 years 
+       < 'Jan 14 14:00:00 1971'::abstime;
+
+SELECT '' AS five, ABSTIME_TBL.*
+   WHERE  (ABSTIME_TBL.f1 - '@ 3 year'::reltime)     -- -(+3) years 
+       < 'Jan 14 14:00:00 1971'::abstime; 
+
+SELECT '' AS five, ABSTIME_TBL.*
+   WHERE  (ABSTIME_TBL.f1 - '@ 3 year ago'::reltime) -- -(-3) years 
+       < 'Jan 14 14:00:00 1977'::abstime;
+
+
+SELECT '' AS twenty, ABSTIME_TBL.*, RELTIME_TBL.*
+   WHERE (ABSTIME_TBL.f1 + RELTIME_TBL.f1)
+       < 'Jan 14 14:00:00 1971'::abstime;
+
+
+-- test reltime operators 
+
+SELECT '' AS eight, RELTIME_TBL.*;
+
+SELECT '' AS five, RELTIME_TBL.*
+   WHERE RELTIME_TBL.f1 <> '@ 10 days'::reltime;
+
+SELECT '' AS three, RELTIME_TBL.*
+   WHERE RELTIME_TBL.f1 <= '@ 5 hours'::reltime;
+
+SELECT '' AS three, RELTIME_TBL.*
+   WHERE RELTIME_TBL.f1 < '@ 1 day'::reltime;
+
+SELECT '' AS one, RELTIME_TBL.*
+   WHERE RELTIME_TBL.f1 = '@ 34 years'::reltime;
+
+SELECT '' AS two, RELTIME_TBL.*
+   WHERE RELTIME_TBL.f1 >= '@ 1 month'::reltime;
+
+SELECT '' AS five, RELTIME_TBL.*
+   WHERE RELTIME_TBL.f1 > '@ 3 seconds ago'::reltime;
+
+SELECT '' AS fifteen, r1.*, r2.*
+   FROM RELTIME_TBL r1, RELTIME_TBL r2
+   WHERE r1.f1 > r2.f1;
+
+
+-- test tinterval operators 
+
+SELECT '' AS seven, TINTERVAL_TBL.*;
+
+-- length == 
+SELECT '' AS one, t.*
+   FROM TINTERVAL_TBL t
+   WHERE t.f1 #= '@ 1 months';
+
+-- length <> 
+SELECT '' AS three, t.*
+   FROM TINTERVAL_TBL t
+   WHERE t.f1 #<> '@ 1 months';
+
+-- length < 
+SELECT '' AS zero, t.*
+   FROM TINTERVAL_TBL t
+   WHERE t.f1 #< '@ 1 month';
+
+-- length <= 
+SELECT '' AS one, t.*
+   FROM TINTERVAL_TBL t
+   WHERE t.f1 #<= '@ 1 month';
+
+-- length > 
+SELECT '' AS three, t.*
+   FROM TINTERVAL_TBL t
+   WHERE t.f1 #> '@ 1 year';
+
+-- length >= 
+SELECT '' AS three, t.*
+   FROM TINTERVAL_TBL t
+   WHERE t.f1 #>= '@ 3 years';
+
+-- overlaps 
+SELECT '' AS three, t1.*
+   FROM TINTERVAL_TBL t1
+   WHERE t1.f1 &&
+       '["Aug 15 14:23:19 1983" "Sep 16 14:23:19 1983"]'::tinterval;
+
+SELECT '' AS five, t1.*, t2.*
+   FROM TINTERVAL_TBL t1, TINTERVAL_TBL t2
+   WHERE t1.f1 && t2.f1 and
+        t1.f1 = t2.f1;
+
+SELECT '' AS fourteen, t1.*, t2.*
+   FROM TINTERVAL_TBL t1, TINTERVAL_TBL t2
+   WHERE t1.f1 && t2.f1 and 
+       not t1.f1 = t2.f1;
+
+-- contains 
+SELECT '' AS five, t1.*
+   FROM TINTERVAL_TBL t1
+   WHERE not t1.f1 <<
+       '["Aug 15 14:23:19 1980" "Sep 16 14:23:19 1990"]'::tinterval;
+
+-- make time interval 
+SELECT '' AS three, t1.*
+   FROM TINTERVAL_TBL t1
+   WHERE t1.f1 && 
+       ('Aug 15 14:23:19 1983'::abstime <#> 
+        'Sep 16 14:23:19 1983'::abstime);
+
+
+--  ****************** test built-in type box ********************
+
+--
+-- box logic
+--          o
+-- 3     o--|X
+--       |  o|
+-- 2   +-+-+ |
+--     | | | |
+-- 1   | o-+-o
+--     |   |
+-- 0   +---+
+--
+--     0 1 2 3
+--
+
+-- boxes are specified by two points, given by four floats x1,y1,x2,y2
+
+
+CREATE TABLE BOX_TBL (f1 box);
+
+INSERT INTO BOX_TBL (f1) VALUES ('(2.0,2.0,0.0,0.0)');
+
+INSERT INTO BOX_TBL (f1) VALUES ('(1.0,1.0,3.0,3.0)');
+
+-- degenerate cases where the box is a line or a point 
+-- note that lines and points boxes all have zero area 
+INSERT INTO BOX_TBL (f1) VALUES ('(2.5, 2.5, 2.5,3.5)');
+
+INSERT INTO BOX_TBL (f1) VALUES ('(3.0, 3.0,3.0,3.0)');
+
+-- badly formatted box inputs 
+INSERT INTO BOX_TBL (f1) VALUES ('(2.3, 4.5)');
+
+INSERT INTO BOX_TBL (f1) VALUES ('asdfasdf(ad');
+
+
+SELECT '' AS four, BOX_TBL.*;
+
+SELECT '' AS four, b.*, box_area(b.f1) as barea
+   FROM BOX_TBL b;
+
+-- overlap 
+SELECT '' AS three, b.f1
+   FROM BOX_TBL b  
+   WHERE b.f1 && '(2.5,2.5,1.0,1.0)'::box;
+
+-- left-or-overlap (x only) 
+SELECT '' AS two, b1.*
+   FROM BOX_TBL b1
+   WHERE b1.f1 &< '(2.0,2.0,2.5,2.5)'::box;
+
+-- right-or-overlap (x only) 
+SELECT '' AS two, b1.*
+   FROM BOX_TBL b1
+   WHERE b1.f1 &> '(2.0,2.0,2.5,2.5)'::box;
+
+-- left of 
+SELECT '' AS two, b.f1
+   FROM BOX_TBL b
+   WHERE b.f1 << '(3.0,3.0,5.0,5.0)'::box;
+
+-- area <= 
+SELECT '' AS four, b.f1
+   FROM BOX_TBL b
+   WHERE b.f1 <= '(3.0,3.0,5.0,5.0)'::box;
+
+-- area < 
+SELECT '' AS two, b.f1
+   FROM BOX_TBL b
+   WHERE b.f1 < '(3.0,3.0,5.0,5.0)'::box;
+
+-- area = 
+SELECT '' AS two, b.f1
+   FROM BOX_TBL b
+   WHERE b.f1 = '(3.0,3.0,5.0,5.0)'::box;
+
+-- area > 
+SELECT '' AS two, b.f1
+   FROM BOX_TBL b                              -- zero area 
+   WHERE b.f1 > '(3.5,3.0,4.5,3.0)'::box;      
+
+-- area >= 
+SELECT '' AS four, b.f1
+   FROM BOX_TBL b                              -- zero area 
+   WHERE b.f1 >= '(3.5,3.0,4.5,3.0)'::box;
+
+-- right of 
+SELECT '' AS two, b.f1
+   FROM BOX_TBL b
+   WHERE '(3.0,3.0,5.0,5.0)'::box >> b.f1;
+
+-- contained in 
+SELECT '' AS three, b.f1
+   FROM BOX_TBL b
+   WHERE b.f1 @ '(0,0,3,3)'::box;
+
+-- contains 
+SELECT '' AS three, b.f1
+   FROM BOX_TBL b
+   WHERE '(0,0,3,3)'::box ~ b.f1;
+
+-- box equality 
+SELECT '' AS one, b.f1
+   FROM BOX_TBL b
+   WHERE '(1,1,3,3)'::box ~= b.f1;
+
+-- center of box, left unary operator 
+SELECT '' AS four, @@(b1.f1) AS p
+   FROM BOX_TBL b1;
+
+-- wholly-contained 
+SELECT '' AS one, b1.*, b2.*
+   FROM BOX_TBL b1, BOX_TBL b2 
+   WHERE b1.f1 ~ b2.f1 and not b1.f1 ~= b2.f1;
+
+
+--  ****************** test built-in type char **************
+--
+-- all inputs are SILENTLY truncated at 1 character
+--
+
+CREATE TABLE CHAR_TBL(f1 char);
+
+INSERT INTO CHAR_TBL (f1) VALUES ('a');
+
+INSERT INTO CHAR_TBL (f1) VALUES ('A');
+
+-- any of the following three input formats are acceptable 
+INSERT INTO CHAR_TBL (f1) VALUES ('1');
+
+INSERT INTO CHAR_TBL (f1) VALUES (2);
+
+INSERT INTO CHAR_TBL (f1) VALUES ('3');
+
+-- zero-length char 
+INSERT INTO CHAR_TBL (f1) VALUES ('');
+
+-- try char's of greater than 1 length 
+INSERT INTO CHAR_TBL (f1) VALUES ('cd');
+
+
+SELECT '' AS seven, CHAR_TBL.*;
+
+SELECT '' AS six, c.*
+   FROM CHAR_TBL c
+   WHERE c.f1 <> 'a';
+
+SELECT '' AS one, c.*
+   FROM CHAR_TBL c
+   WHERE c.f1 = 'a';
+
+SELECT '' AS five, c.*
+   FROM CHAR_TBL c
+   WHERE c.f1 < 'a';
+
+SELECT '' AS six, c.*
+   FROM CHAR_TBL c
+   WHERE c.f1 <= 'a';
+
+SELECT '' AS one, c.*
+   FROM CHAR_TBL c
+   WHERE c.f1 > 'a';
+
+SELECT '' AS two, c.*
+   FROM CHAR_TBL c
+   WHERE c.f1 >= 'a';
+
+
+--  **************** testing built-in type char2 **************
+--
+-- all inputs are silently truncated at 2 characters
+--
+
+CREATE TABLE CHAR2_TBL(f1 char2);
+
+INSERT INTO CHAR2_TBL (f1) VALUES ('AB');
+
+INSERT INTO CHAR2_TBL (f1) VALUES ('ab');
+
+INSERT INTO CHAR2_TBL (f1) VALUES ('ZY');
+
+INSERT INTO CHAR2_TBL (f1) VALUES ('34');
+
+INSERT INTO CHAR2_TBL (f1) VALUES ('d');
+
+INSERT INTO CHAR2_TBL (f1) VALUES ('');
+
+INSERT INTO CHAR2_TBL (f1) VALUES ('12345');
+
+
+SELECT '' AS seven, CHAR2_TBL.*;
+
+SELECT '' AS six, c.f1 FROM CHAR2_TBL c WHERE c.f1 <> 'AB';
+
+SELECT '' AS one, c.f1 FROM CHAR2_TBL c WHERE c.f1 = 'AB';
+
+SELECT '' AS three, c.f1 FROM CHAR2_TBL c WHERE c.f1 < 'AB';
+
+SELECT '' AS four, c.f1 FROM CHAR2_TBL c WHERE c.f1 <= 'AB';
+
+SELECT '' AS three, c.f1 FROM CHAR2_TBL c WHERE c.f1 > 'AB';
+
+SELECT '' AS four, c.f1 FROM CHAR2_TBL c WHERE c.f1 >= 'AB';
+
+SELECT '' AS seven, c.f1 FROM CHAR2_TBL c WHERE c.f1 ~ '.*';
+
+SELECT '' AS zero, c.f1 FROM CHAR2_TBL c WHERE c.f1 !~ '.*';
+
+SELECT '' AS one, c.f1 FROM CHAR2_TBL c WHERE c.f1 ~ '34';
+
+SELECT '' AS one, c.f1 FROM CHAR2_TBL c WHERE c.f1 ~ '3.*';
+
+
+
+--**************** testing built-in type char4 **************
+--
+-- all inputs are silently truncated at 4 characters
+--
+
+CREATE TABLE CHAR4_TBL (f1  char4);
+
+INSERT INTO CHAR4_TBL(f1) VALUES ('ABCD');
+
+INSERT INTO CHAR4_TBL(f1) VALUES ('abcd');
+
+INSERT INTO CHAR4_TBL(f1) VALUES ('ZYWZ');
+
+INSERT INTO CHAR4_TBL(f1) VALUES ('343f');
+
+INSERT INTO CHAR4_TBL(f1) VALUES ('d34a');
+
+INSERT INTO CHAR4_TBL(f1) VALUES ('');
+
+INSERT INTO CHAR4_TBL(f1) VALUES ('12345678');
+
+
+SELECT '' AS seven, CHAR4_TBL.*;
+
+SELECT '' AS six, c.f1 FROM CHAR4_TBL c WHERE c.f1 <> 'ABCD';
+
+SELECT '' AS one, c.f1 FROM CHAR4_TBL c WHERE c.f1 = 'ABCD';
+
+SELECT '' AS three, c.f1 FROM CHAR4_TBL c WHERE c.f1 < 'ABCD';
+
+SELECT '' AS four, c.f1 FROM CHAR4_TBL c WHERE c.f1 <= 'ABCD';
+
+SELECT '' AS three, c.f1 FROM CHAR4_TBL c WHERE c.f1 > 'ABCD';
+
+SELECT '' AS four, c.f1 FROM CHAR4_TBL c WHERE c.f1 >= 'ABCD';
+
+SELECT '' AS seven, c.f1 FROM CHAR4_TBL c WHERE c.f1 ~ '.*';
+
+SELECT '' AS zero, c.f1 FROM CHAR4_TBL c WHERE c.f1 !~ '.*';
+
+SELECT '' AS three, c.f1 FROM CHAR4_TBL c WHERE c.f1 ~ '.*34.*';
+
+
+--  **************** testing built-in type char8 **************
+--
+-- all inputs are silently truncated at 8 characters
+--
+
+CREATE TABLE CHAR8_TBL(f1 char8);
+
+INSERT INTO CHAR8_TBL(f1) VALUES ('ABCDEFGH');
+
+INSERT INTO CHAR8_TBL(f1) VALUES ('abcdefgh');
+
+INSERT INTO CHAR8_TBL(f1) VALUES ('ZYWZ410-');
+
+INSERT INTO CHAR8_TBL(f1) VALUES ('343f%2a');
+
+INSERT INTO CHAR8_TBL(f1) VALUES ('d34aas');
+
+INSERT INTO CHAR8_TBL(f1) VALUES ('');
+
+INSERT INTO CHAR8_TBL(f1) VALUES ('1234567890');
+
+
+SELECT '' AS seven, CHAR8_TBL.*;
+
+SELECT '' AS six, c.f1 FROM CHAR8_TBL c WHERE c.f1 <> 'ABCDEFGH';
+
+SELECT '' AS one, c.f1 FROM CHAR8_TBL c WHERE c.f1 = 'ABCDEFGH';
+
+SELECT '' AS three, c.f1 FROM CHAR8_TBL c WHERE c.f1 < 'ABCDEFGH';
+
+SELECT '' AS four, c.f1 FROM CHAR8_TBL c WHERE c.f1 <= 'ABCDEFGH';
+
+SELECT '' AS three, c.f1 FROM CHAR8_TBL c WHERE c.f1 > 'ABCDEFGH';
+
+SELECT '' AS four, c.f1 FROM CHAR8_TBL c WHERE c.f1 >= 'ABCDEFGH';
+
+SELECT '' AS seven, c.f1 FROM CHAR8_TBL c WHERE c.f1 ~ '.*';
+
+SELECT '' AS zero, c.f1 FROM CHAR8_TBL c WHERE c.f1 !~ '.*';
+
+SELECT '' AS four, c.f1 FROM CHAR8_TBL c WHERE c.f1 ~ '[0-9]';
+
+SELECT '' AS three, c.f1 FROM CHAR8_TBL c WHERE c.f1 ~ '.*34.*';
+
+
+
+--**************** testing built-in type char16 **************
+--
+-- all inputs are silently truncated at 16 characters
+--
+
+CREATE TABLE CHAR16_TBL(f1 char16);
+
+INSERT INTO CHAR16_TBL(f1) VALUES ('ABCDEFGHIJKLMNOP');
+
+INSERT INTO CHAR16_TBL(f1) VALUES ('abcdefghijklmnop');
+
+INSERT INTO CHAR16_TBL(f1) VALUES ('asdfghjkl;');
+
+INSERT INTO CHAR16_TBL(f1) VALUES ('343f%2a');
+
+INSERT INTO CHAR16_TBL(f1) VALUES ('d34aaasdf');
+
+INSERT INTO CHAR16_TBL(f1) VALUES ('');
+
+INSERT INTO CHAR16_TBL(f1) VALUES ('1234567890ABCDEFGHIJKLMNOPQRSTUV');
+
+
+SELECT '' AS seven, CHAR16_TBL.*;
+
+SELECT '' AS six, c.f1 FROM CHAR16_TBL c WHERE c.f1 <> 'ABCDEFGHIJKLMNOP';
+
+SELECT '' AS one, c.f1 FROM CHAR16_TBL c WHERE c.f1 = 'ABCDEFGHIJKLMNOP';
+
+SELECT '' AS three, c.f1 FROM CHAR16_TBL c WHERE c.f1 < 'ABCDEFGHIJKLMNOP';
+
+SELECT '' AS four, c.f1 FROM CHAR16_TBL c WHERE c.f1 <= 'ABCDEFGHIJKLMNOP';
+
+SELECT '' AS three, c.f1 FROM CHAR16_TBL c WHERE c.f1 > 'ABCDEFGHIJKLMNOP';
+
+SELECT '' AS four, c.f1 FROM CHAR16_TBL c WHERE c.f1 >= 'ABCDEFGHIJKLMNOP';
+
+SELECT '' AS seven, c.f1 FROM CHAR16_TBL c WHERE c.f1 ~ '.*';
+
+SELECT '' AS zero, c.f1 FROM CHAR16_TBL c WHERE c.f1 !~ '.*';
+
+SELECT '' AS three, c.f1 FROM CHAR16_TBL c WHERE c.f1 ~ '[0-9]';
+
+SELECT '' AS two, c.f1 FROM CHAR16_TBL c WHERE c.f1 ~ '.*asdf.*';
+
+
+--  *************testing built-in type float4 ****************
+
+CREATE TABLE FLOAT4_TBL (f1  float4);
+
+INSERT INTO FLOAT4_TBL(f1) VALUES ('0.0');
+
+INSERT INTO FLOAT4_TBL(f1) VALUES ('1004.30');
+
+INSERT INTO FLOAT4_TBL(f1) VALUES ('-34.84');
+
+INSERT INTO FLOAT4_TBL(f1) VALUES ('1.2345678901234e+20');
+
+INSERT INTO FLOAT4_TBL(f1) VALUES ('1.2345678901234e-20');
+
+-- test for over and under flow 
+INSERT INTO FLOAT4_TBL(f1) VALUES ('10e40');
+
+INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e40');
+
+INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-40');
+
+INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-40');
+
+
+SELECT '' AS five, FLOAT4_TBL.*;
+
+SELECT '' AS four, f.* FROM FLOAT4_TBL f WHERE f.f1 <> '1004.3';
+
+SELECT '' AS one, f.* FROM FLOAT4_TBL f WHERE f.f1 = '1004.3';
+
+SELECT '' AS three, f.* FROM FLOAT4_TBL f WHERE '1004.3' > f.f1;
+
+SELECT '' AS three, f.* FROM FLOAT4_TBL f WHERE  f.f1 < '1004.3';
+
+SELECT '' AS four, f.* FROM FLOAT4_TBL f WHERE '1004.3' >= f.f1;
+
+SELECT '' AS four, f.* FROM FLOAT4_TBL f WHERE  f.f1 <= '1004.3';
+
+SELECT '' AS three, f.f1, f.f1 * '-10' AS x FROM FLOAT4_TBL f
+   WHERE f.f1 > '0.0';
+
+SELECT '' AS three, f.f1, f.f1 + '-10' AS x FROM FLOAT4_TBL f
+   WHERE f.f1 > '0.0'
+
+SELECT '' AS three, f.f1, f.f1 / '-10' AS x FROM FLOAT4_TBL f
+   WHERE f.f1 > '0.0';
+
+SELECT '' AS three, f.f1, f.f1 - '-10' AS x FROM FLOAT4_TBL f
+   WHERE f.f1 > '0.0';
+
+-- test divide by zero
+SELECT '' AS bad, f.f1 / '0.0' from FLOAT4_TBL f;
+
+SELECT '' AS five, FLOAT4_TBL.*;
+
+-- test the unary float4abs operator 
+SELECT '' AS five, f.f1, @f.f1 AS abs_f1 FROM FLOAT4_TBL f;
+
+UPDATE FLOAT4_TBL
+   SET f1 = FLOAT4_TBL.f1 * '-1'
+   WHERE FLOAT4_TBL.f1 > '0.0';
+
+SELECT '' AS five, FLOAT4_TBL.*;
+
+
+--  *************testing built-in type float8 ****************
+
+CREATE TABLE FLOAT8_TBL(f1 float8);
+
+INSERT INTO FLOAT8_TBL(f1) VALUES ('0.0');
+
+INSERT INTO FLOAT8_TBL(f1) VALUES ('1004.30');
+
+INSERT INTO FLOAT8_TBL(f1) VALUES ('-34.84');
+
+INSERT INTO FLOAT8_TBL(f1) VALUES ('1.2345678901234e+200');
+
+INSERT INTO FLOAT8_TBL(f1) VALUES ('1.2345678901234e-200');
+
+-- test for over and under flow 
+INSERT INTO FLOAT8_TBL(f1) VALUES ('10e400');
+
+INSERT INTO FLOAT8_TBL(f1) VALUES ('-10e400');
+
+INSERT INTO FLOAT8_TBL(f1) VALUES ('10e-400');
+
+INSERT INTO FLOAT8_TBL(f1) VALUES ('-10e-400');
+
+
+SELECT '' AS five, FLOAT8_TBL.*;
+
+SELECT '' AS four, f.* FROM FLOAT8_TBL f WHERE f.f1 <> '1004.3';
+
+SELECT '' AS one, f.* FROM FLOAT8_TBL f WHERE f.f1 = '1004.3';
+
+SELECT '' AS three, f.* FROM FLOAT8_TBL f WHERE '1004.3' > f.f1;
+
+SELECT '' AS three, f.* FROM FLOAT8_TBL f WHERE  f.f1 < '1004.3';
+
+SELECT '' AS four, f.* FROM FLOAT8_TBL f WHERE '1004.3' >= f.f1;
+
+SELECT '' AS four, f.* FROM FLOAT8_TBL f WHERE  f.f1 <= '1004.3';
+
+SELECT '' AS three, f.f1, f.f1 * '-10' AS x 
+   FROM FLOAT8_TBL f
+   WHERE f.f1 > '0.0';
+
+SELECT '' AS three, f.f1, f.f1 + '-10' AS x
+   FROM FLOAT8_TBL f
+   WHERE f.f1 > '0.0';
+
+SELECT '' AS three, f.f1, f.f1 / '-10' AS x
+   FROM FLOAT8_TBL f
+   WHERE f.f1 > '0.0';
+
+SELECT '' AS three, f.f1, f.f1 - '-10' AS x
+   FROM FLOAT8_TBL f
+   WHERE f.f1 > '0.0';
+
+SELECT '' AS one, f.f1 ^ '2.0' AS square_f1
+   FROM FLOAT8_TBL f where f.f1 = '1004.3';
+
+-- absolute value 
+SELECT '' AS five, f.f1, @f.f1 AS abs_f1 
+   FROM FLOAT8_TBL f;
+
+-- truncate 
+SELECT '' AS five, f.f1, %f.f1 AS trunc_f1
+   FROM FLOAT8_TBL f;
+
+-- round 
+SELECT '' AS five, f.f1, f.f1 % AS round_f1
+   FROM FLOAT8_TBL f;
+
+-- square root 
+SELECT '' AS three, f.f1, |/f.f1 AS sqrt_f1
+   FROM FLOAT8_TBL f
+   WHERE f.f1 > '0.0';
+
+-- take exp of ln(f.f1) 
+SELECT '' AS three, f.f1, : ( ; f.f1) AS exp_ln_f1
+   FROM FLOAT8_TBL f
+   WHERE f.f1 > '0.0';
+
+-- cube root 
+SELECT '' AS five, f.f1, ||/f.f1 AS cbrt_f1 FROM FLOAT8_TBL f;
+
+
+SELECT '' AS five, FLOAT8_TBL.*;
+
+UPDATE FLOAT8_TBL
+   SET f1 = FLOAT8_TBL.f1 * '-1'
+   WHERE FLOAT8_TBL.f1 > '0.0';
+
+SELECT '' AS bad, f.f1 * '1e200' from FLOAT8_TBL f;
+
+SELECT '' AS bad, f.f1 ^ '1e200' from FLOAT8_TBL f;
+
+SELECT '' AS bad, ; (f.f1) from FLOAT8_TBL f where f.f1 = '0.0' ;
+
+SELECT '' AS bad, ; (f.f1) from FLOAT8_TBL f where f.f1 < '0.0' ;
+
+SELECT '' AS bad, : (f.f1) from FLOAT8_TBL f;
+
+SELECT '' AS bad, f.f1 / '0.0' from FLOAT8_TBL f;
+
+SELECT '' AS five, FLOAT8_TBL.*;
+
+
+-- *************testing built-in type int2 ****************
+--
+-- NOTE: int2 operators never check for over/underflow!
+-- Some of these answers are consequently numerically incorrect.
+--
+
+CREATE TABLE INT2_TBL(f1 int2);
+
+INSERT INTO INT2_TBL(f1) VALUES ('0');
+
+INSERT INTO INT2_TBL(f1) VALUES ('1234');
+
+INSERT INTO INT2_TBL(f1) VALUES ('-1234');
+
+INSERT INTO INT2_TBL(f1) VALUES ('34.5');
+
+-- largest and smallest values 
+INSERT INTO INT2_TBL(f1) VALUES ('32767');
+
+INSERT INTO INT2_TBL(f1) VALUES ('-32767');
+
+-- bad input values -- should give warnings 
+INSERT INTO INT2_TBL(f1) VALUES ('100000');
+
+INSERT INTO INT2_TBL(f1) VALUES ('asdf');
+
+
+SELECT '' AS five, INT2_TBL.*;
+
+SELECT '' AS four, i.* FROM INT2_TBL i WHERE i.f1 <> '0'::int2;
+
+SELECT '' AS four, i.* FROM INT2_TBL i WHERE i.f1 <> '0'::int4;
+
+SELECT '' AS one, i.* FROM INT2_TBL i WHERE i.f1 = '0'::int2;
+
+SELECT '' AS one, i.* FROM INT2_TBL i WHERE i.f1 = '0'::int4;
+
+SELECT '' AS two, i.* FROM INT2_TBL i WHERE i.f1 < '0'::int2;
+
+SELECT '' AS two, i.* FROM INT2_TBL i WHERE i.f1 < '0'::int4;
+
+SELECT '' AS three, i.* FROM INT2_TBL i WHERE i.f1 <= '0'::int2;
+
+SELECT '' AS three, i.* FROM INT2_TBL i WHERE i.f1 <= '0'::int4;
+
+SELECT '' AS two, i.* FROM INT2_TBL i WHERE i.f1 > '0'::int2;
+
+SELECT '' AS two, i.* FROM INT2_TBL i WHERE i.f1 > '0'::int4;
+
+SELECT '' AS three, i.* FROM INT2_TBL i WHERE i.f1 >= '0'::int2;
+
+SELECT '' AS three, i.* FROM INT2_TBL i WHERE i.f1 >= '0'::int4;
+
+-- positive odds 
+SELECT '' AS one, i.* FROM INT2_TBL i WHERE (i.f1 % '2'::int2) = '1'::int2;
+
+-- any evens 
+SELECT '' AS three, i.* FROM INT2_TBL i WHERE (i.f1 % '2'::int4) = '0'::int2;
+
+SELECT '' AS five, i.f1, i.f1 * '2'::int2 AS x FROM INT2_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 * '2'::int4 AS x FROM INT2_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 + '2'::int2 AS x FROM INT2_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 + '2'::int4 AS x FROM INT2_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 - '2'::int2 AS x FROM INT2_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 - '2'::int4 AS x FROM INT2_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 / '2'::int2 AS x FROM INT2_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 / '2'::int4 AS x FROM INT2_TBL i;
+
+
+
+--  *************testing built-in type int4 ****************
+--
+-- WARNING: int4 operators never check for over/underflow!
+-- Some of these answers are consequently numerically incorrect.
+--
+
+CREATE TABLE INT4_TBL(f1 int4);
+
+INSERT INTO INT4_TBL(f1) VALUES ('0');
+
+INSERT INTO INT4_TBL(f1) VALUES ('123456');
+
+INSERT INTO INT4_TBL(f1) VALUES ('-123456');
+
+INSERT INTO INT4_TBL(f1) VALUES ('34.5');
+
+-- largest and smallest values 
+INSERT INTO INT4_TBL(f1) VALUES ('2147483647');
+
+INSERT INTO INT4_TBL(f1) VALUES ('-2147483647');
+
+-- bad input values -- should give warnings 
+INSERT INTO INT4_TBL(f1) VALUES ('1000000000000');
+
+INSERT INTO INT4_TBL(f1) VALUES ('asdf');
+
+
+SELECT '' AS five, INT4_TBL.*;
+
+SELECT '' AS four, i.* FROM INT4_TBL i WHERE i.f1 <> '0'::int2;
+
+SELECT '' AS four, i.* FROM INT4_TBL i WHERE i.f1 <> '0'::int4;
+
+SELECT '' AS one, i.* FROM INT4_TBL i WHERE i.f1 = '0'::int2;
+
+SELECT '' AS one, i.* FROM INT4_TBL i WHERE i.f1 = '0'::int4;
+
+SELECT '' AS two, i.* FROM INT4_TBL i WHERE i.f1 < '0'::int2;
+
+SELECT '' AS two, i.* FROM INT4_TBL i WHERE i.f1 < '0'::int4;
+
+SELECT '' AS three, i.* FROM INT4_TBL i WHERE i.f1 <= '0'::int2;
+
+SELECT '' AS three, i.* FROM INT4_TBL i WHERE i.f1 <= '0'::int4;
+
+SELECT '' AS two, i.* FROM INT4_TBL i WHERE i.f1 > '0'::int2;
+
+SELECT '' AS two, i.* FROM INT4_TBL i WHERE i.f1 > '0'::int4;
+
+SELECT '' AS three, i.* FROM INT4_TBL i WHERE i.f1 >= '0'::int2;
+
+SELECT '' AS three, i.* FROM INT4_TBL i WHERE i.f1 >= '0'::int4;
+
+-- positive odds 
+SELECT '' AS one, i.* FROM INT4_TBL i WHERE (i.f1 % '2'::int2) = '1'::int2;
+
+-- any evens 
+SELECT '' AS three, i.* FROM INT4_TBL i WHERE (i.f1 % '2'::int4) = '0'::int2;
+
+SELECT '' AS five, i.f1, i.f1 * '2'::int2 AS x FROM INT4_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 * '2'::int4 AS x FROM INT4_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 + '2'::int2 AS x FROM INT4_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 + '2'::int4 AS x FROM INT4_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 - '2'::int2 AS x FROM INT4_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 - '2'::int4 AS x FROM INT4_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 / '2'::int2 AS x FROM INT4_TBL i;
+
+SELECT '' AS five, i.f1, i.f1 / '2'::int4 AS x FROM INT4_TBL i;
+
+
+--
+-- more complex expressions
+--
+SELECT '2'::int2 * '2'::int2 = '16'::int2 / '4'::int2 AS true;
+
+SELECT '2'::int4 * '2'::int2 = '16'::int2 / '4'::int4 AS true;
+
+SELECT '2'::int2 * '2'::int4 = '16'::int4 / '4'::int2 AS true;
+
+SELECT '1000'::int4 < '999'::int4 AS false;
+
+SELECT 4! AS twenty_four;
+
+SELECT !!3 AS six;
+
+SELECT 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 AS ten;
+
+SELECT 2 + 2 / 2 AS three;
+
+SELECT (2 + 2) / 2 AS two;
+
+SELECT dsqrt('64'::float8) AS eight;
+
+SELECT |/'64'::float8 AS eight;
+
+SELECT ||/'27'::float8 AS three;
+
+
+
+-- *************testing built-in type oid ****************
+CREATE TABLE OID_TBL(f1 oid);
+
+INSERT INTO OID_TBL(f1) VALUES ('1234');
+
+INSERT INTO OID_TBL(f1) VALUES ('1235');
+
+INSERT INTO OID_TBL(f1) VALUES ('987');
+
+INSERT INTO OID_TBL(f1) VALUES ('-1040');
+
+INSERT INTO OID_TBL(f1) VALUES ('');
+
+-- bad inputs 
+INSERT INTO OID_TBL(f1) VALUES ('asdfasd');
+
+SELECT '' AS five, OID_TBL.*;
+
+
+SELECT '' AS one, o.* FROM OID_TBL o WHERE o.f1 = '1234'::oid;
+
+SELECT '' AS four, o.* FROM OID_TBL o WHERE o.f1 <> '1234';
+
+SELECT '' AS four, o.* FROM OID_TBL o WHERE o.f1 <= '1234';
+
+SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 < '1234';
+
+SELECT '' AS two, o.* FROM OID_TBL o WHERE o.f1 >= '1234';
+
+SELECT '' AS one, o.* FROM OID_TBL o WHERE o.f1 > '1234';
+
+
+-- *************testing built-in type oidname ****************
+-- oidname is a an adt for multiple key indices involving oid and name
+-- probably will not be used directly by most users 
+
+CREATE TABLE OIDNAME_TBL(f1 oidname);
+
+INSERT INTO OIDNAME_TBL(f1) VALUES ('1234,abcd');
+
+INSERT INTO OIDNAME_TBL(f1) VALUES ('1235,efgh');
+
+INSERT INTO OIDNAME_TBL(f1) VALUES ('987,XXXX');
+
+-- no char16 component 
+INSERT INTO OIDNAME_TBL(f1) VALUES ('123456');
+
+-- char16 component too long 
+INSERT INTO OIDNAME_TBL(f1) VALUES ('123456,abcdefghijklmnopqrsutvwyz');
+
+-- bad inputs 
+INSERT INTO OIDNAME_TBL(f1) VALUES ('');
+
+INSERT INTO OIDNAME_TBL(f1) VALUES ('asdfasd');
+
+
+SELECT '' AS four, OIDNAME_TBL.*;
+
+SELECT '' AS one, o.* FROM OIDNAME_TBL o WHERE o.f1 = '1234,abcd';
+
+SELECT '' AS three, o.* FROM OIDNAME_TBL o WHERE o.f1 <> '1234,abcd';
+
+SELECT '' AS two, o.* FROM OIDNAME_TBL o WHERE o.f1 <= '1234,abcd';
+
+SELECT '' AS one, o.* FROM OIDNAME_TBL o WHERE o.f1 < '1234,abcd';
+
+SELECT '' AS three, o.* FROM OIDNAME_TBL o WHERE o.f1 >= '1234,abcd';
+
+SELECT '' AS two, o.* FROM OIDNAME_TBL o WHERE o.f1 > '1234,abcd';
+
+
+-- *************testing built-in type oidint2 ****************
+-- oidint2 is a an adt for multiple key indices involving oid and int2 
+-- probably will not be used directly by most users 
+
+CREATE TABLE OIDINT2_TBL(f1 oidint2);
+
+INSERT INTO OIDINT2_TBL(f1) VALUES ('1234/9873');
+
+INSERT INTO OIDINT2_TBL(f1) VALUES ('1235/9873');
+
+INSERT INTO OIDINT2_TBL(f1) VALUES ('987/-1234');
+
+-- no int2 component 
+--
+-- this is defined as good in the code -- I don't know what will break
+-- if we disallow it.
+--
+INSERT INTO OIDINT2_TBL(f1) VALUES ('123456');
+
+-- int2 component too large 
+INSERT INTO OIDINT2_TBL(f1) VALUES ('123456/123456');
+
+--
+-- this is defined as good in the code -- I don't know what will break
+-- if we disallow it.
+--
+INSERT INTO OIDINT2_TBL(f1) VALUES ('');
+
+-- bad inputs 
+INSERT INTO OIDINT2_TBL(f1) VALUES ('asdfasd');
+
+
+SELECT '' AS five, OIDINT2_TBL.*;
+
+SELECT '' AS one, o.* FROM OIDINT2_TBL o WHERE o.f1 = '1235/9873';
+
+SELECT '' AS four, o.* FROM OIDINT2_TBL o WHERE o.f1 <> '1235/9873';
+
+SELECT '' AS four, o.* FROM OIDINT2_TBL o WHERE o.f1 <= '1235/9873';
+
+SELECT '' AS three, o.* FROM OIDINT2_TBL o WHERE o.f1 < '1235/9873';
+
+SELECT '' AS two, o.* FROM OIDINT2_TBL o WHERE o.f1 >= '1235/9873';
+
+SELECT '' AS one, o.* FROM OIDINT2_TBL o WHERE o.f1 > '1235/9873';
+
+
+--*************testing built-in type oidint4 ****************
+-- oidint4 is a an adt for multiple key indices involving oid and int4 
+-- probably will not be used directly by most users 
+
+CREATE TABLE OIDINT4_TBL(f1 oidint4);
+
+INSERT INTO OIDINT4_TBL(f1) VALUES ('1234/9873');
+
+INSERT INTO OIDINT4_TBL(f1) VALUES ('1235/9873');
+
+INSERT INTO OIDINT4_TBL(f1) VALUES ('987/-1234');
+
+-- no int4 component 
+--
+-- this is defined as good in the code -- I don't know what will break
+-- if we disallow it.
+--
+INSERT INTO OIDINT4_TBL(f1) VALUES ('123456');
+
+-- int4 component too large 
+INSERT INTO OIDINT4_TBL(f1) VALUES ('123456/1234568901234567890');
+
+--
+-- this is defined as good in the code -- I don't know what will break
+-- if we disallow it.
+--
+INSERT INTO OIDINT4_TBL(f1) VALUES ('');
+
+-- bad inputs 
+INSERT INTO OIDINT4_TBL(f1) VALUES ('asdfasd');
+
+SELECT '' AS five, OIDINT4_TBL.*;
+
+SELECT '' AS one, o.* FROM OIDINT4_TBL o WHERE o.f1 = '1235/9873';
+
+SELECT '' AS four, o.* FROM OIDINT4_TBL o WHERE o.f1 <> '1235/9873';
+
+SELECT '' AS four, o.* FROM OIDINT4_TBL o WHERE o.f1 <= '1235/9873';
+
+SELECT '' AS three, o.* FROM OIDINT4_TBL o WHERE o.f1 < '1235/9873';
+
+SELECT '' AS two, o.* FROM OIDINT4_TBL o WHERE o.f1 >= '1235/9873';
+
+SELECT '' AS one, o.* FROM OIDINT4_TBL o WHERE o.f1 > '1235/9873';
+
+
+--  ************testing built-in type point ****************
+
+CREATE TABLE POINT_TBL(f1 point);
+
+INSERT INTO POINT_TBL(f1) VALUES ('(0.0,0.0)');
+
+INSERT INTO POINT_TBL(f1) VALUES ('(-10.0,0.0)');
+
+INSERT INTO POINT_TBL(f1) VALUES ('(-3.0,4.0)');
+
+INSERT INTO POINT_TBL(f1) VALUES ('(5.1, 34.5)');
+
+INSERT INTO POINT_TBL(f1) VALUES ('(-5.0,-12.0)');
+
+-- bad format points 
+INSERT INTO POINT_TBL(f1) VALUES ('asdfasdf');
+
+INSERT INTO POINT_TBL(f1) VALUES ('10.0,10.0');
+
+INSERT INTO POINT_TBL(f1) VALUES ('(10.0 10.0)');
+
+INSERT INTO POINT_TBL(f1) VALUES ('(10.0,10.0');
+
+
+SELECT '' AS five, POINT_TBL.*;
+
+-- left of 
+SELECT '' AS three, p.* FROM POINT_TBL p WHERE p.f1 !< '(0.0, 0.0)';
+
+-- right of 
+SELECT '' AS three, p.* FROM POINT_TBL p WHERE '(0.0,0.0)' !> p.f1;
+
+-- above 
+SELECT '' AS one, p.* FROM POINT_TBL p WHERE '(0.0,0.0)' !^ p.f1;
+
+-- below 
+SELECT '' AS one, p.* FROM POINT_TBL p WHERE p.f1 !| '(0.0, 0.0)';
+
+-- equal 
+SELECT '' AS one, p.* FROM POINT_TBL p WHERE p.f1 =|= '(5.1, 34.5)';
+
+-- point in box 
+SELECT '' AS two, p.* FROM POINT_TBL p
+   WHERE p.f1 ===> '(0,0,100,100)';
+
+SELECT '' AS three, p.* FROM POINT_TBL p
+   WHERE not on_pb(p.f1,'(0,0,100,100)'::box);
+
+SELECT '' AS two, p.* FROM POINT_TBL p
+   WHERE on_ppath(p.f1,'(0,3,0,0,-10,0,-10,10)'::path);
+
+SELECT '' AS five, p.f1, p.f1 <===> '(0,0)' AS dist FROM POINT_TBL p;
+
+SELECT '' AS twentyfive, p1.f1, p2.f1, p1.f1 <===> p2.f1 AS dist
+   FROM POINT_TBL p1, POINT_TBL p2;
+
+SELECT '' AS twenty, p1.f1, p2.f1
+   FROM POINT_TBL p1, POINT_TBL p2
+   WHERE (p1.f1 <===> p2.f1) > 3;
+
+SELECT '' AS ten, p1.f1, p2.f1
+   FROM POINT_TBL p1, POINT_TBL p2
+   WHERE (p1.f1 <===> p2.f1) > 3 and 
+       p1.f1 !< p2.f1;
+
+SELECT '' AS two, p1.f1, p2.f1 
+   FROM POINT_TBL p1, POINT_TBL p2 
+   WHERE (p1.f1 <===> p2.f1) > 3 and 
+       p1.f1 !< p2.f1 and
+       p1.f1 !^ p2.f1;
+
+
+-- *************testing built-in type polygon ****************
+--
+-- polygon logic
+--
+-- 3         o
+--           |
+-- 2       + |
+--        /  |
+-- 1     # o +
+--       /    |
+-- 0   #-----o-+
+--
+--     0 1 2 3 4
+--
+
+CREATE TABLE POLYGON_TBL(f1 polygon);
+
+
+INSERT INTO POLYGON_TBL(f1) VALUES ('(2.0,2.0,0.0,0.0,4.0,0.0)');
+
+INSERT INTO POLYGON_TBL(f1) VALUES ('(3.0,3.0,1.0,1.0,3.0,0.0)');
+
+-- degenerate polygons 
+INSERT INTO POLYGON_TBL(f1) VALUES ('(0.0,0.0)');
+
+INSERT INTO POLYGON_TBL(f1) VALUES ('(0.0,0.0,1.0,1.0)');
+-- bad polygon input strings 
+INSERT INTO POLYGON_TBL(f1) VALUES ('0.0');
+
+INSERT INTO POLYGON_TBL(f1) VALUES ('(0.0 0.0');
+
+INSERT INTO POLYGON_TBL(f1) VALUES ('(0,1,2)');
+
+INSERT INTO POLYGON_TBL(f1) VALUES ('(0,1,2,3');
+
+INSERT INTO POLYGON_TBL(f1) VALUES ('asdf');
+
+
+SELECT '' AS four, POLYGON_TBL.*;
+
+-- overlap 
+SELECT '' AS three, p.*
+   FROM POLYGON_TBL p
+   WHERE p.f1 && '(3.0,3.0,1.0,1.0,3.0,0.0)';
+
+-- left overlap 
+SELECT '' AS four, p.* 
+   FROM POLYGON_TBL p
+   WHERE p.f1 &< '(3.0,3.0,1.0,1.0,3.0,0.0)';
+
+-- right overlap 
+SELECT '' AS two, p.* 
+   FROM POLYGON_TBL p
+   WHERE p.f1 &> '(3.0,3.0,1.0,1.0,3.0,0.0)';
+
+-- left of 
+SELECT '' AS one, p.*
+   FROM POLYGON_TBL p
+   WHERE p.f1 << '(3.0,3.0,1.0,1.0,3.0,0.0)';
+
+-- right of 
+SELECT '' AS zero, p.*
+   FROM POLYGON_TBL p
+   WHERE p.f1 >> '(3.0,3.0,1.0,1.0,3.0,0.0)';
+
+-- contained 
+SELECT '' AS one, p.* 
+   FROM POLYGON_TBL p
+   WHERE p.f1 @ '(3.0,3.0,1.0,1.0,3.0,0.0)';
+
+-- same 
+SELECT '' AS one, p.*
+   FROM POLYGON_TBL p
+   WHERE p.f1 ~= '(3.0,3.0,1.0,1.0,3.0,0.0)';
+
+-- contains 
+SELECT '' AS one, p.*
+   FROM POLYGON_TBL p
+   WHERE p.f1 ~ '(3.0,3.0,1.0,1.0,3.0,0.0)';
+
+
+-- *************testing built-in type text  ****************
+
+--
+-- adt operators in the target list
+--
+-- fixed-length by reference 
+SELECT 'char 16 string'::char16 = 'char 16 string '::char16 AS false;
+
+-- fixed-length by value 
+SELECT 'c'::char = 'c'::char AS true;
+
+-- variable-length 
+SELECT 'this is a text string'::text = 'this is a text string'::text AS true;
+
+SELECT 'this is a text string'::text = 'this is a text strin'::text AS false;
+
+--
+-- polygon logic
+--
+-- 3         o
+--           |
+-- 2       + |
+--        /  |
+-- 1     / o +
+--       /    |
+-- 0   +-----o-+
+--
+--     0 1 2 3 4
+--
+-- left of 
+SELECT '(2.0,2.0,0.0,0.0,4.0,0.0)'::polygon << '(3.0,3.0,1.0,1.0,3.0,0.0)'::polygon AS false;
+
+-- left overlap 
+SELECT '(2.0,2.0,0.0,0.0,4.0,0.0)'::polygon &< '(3.0,3.0,1.0,1.0,3.0,0.0)'::polygon AS true;
+
+-- right overlap 
+SELECT '(2.0,2.0,0.0,0.0,4.0,0.0)'::polygon &> '(3.0,3.0,1.0,1.0,3.0,0.0)'::polygon AS true;
+
+-- right of 
+SELECT '(2.0,2.0,0.0,0.0,4.0,0.0)'::polygon >> '(3.0,3.0,1.0,1.0,3.0,0.0)'::polygon AS false;
+
+-- contained in 
+SELECT '(2.0,2.0,0.0,0.0,4.0,0.0)'::polygon @ '(3.0,3.0,1.0,1.0,3.0,0.0)'::polygon AS false;
+
+-- contains 
+SELECT '(2.0,2.0,0.0,0.0,4.0,0.0)'::polygon ~ '(3.0,3.0,1.0,1.0,3.0,0.0)'::polygon AS false;
+
+-- same 
+SELECT '(2.0,2.0,0.0,0.0,4.0,0.0)'::polygon ~= '(3.0,3.0,1.0,1.0,3.0,0.0)'::polygon AS false;
+
+-- overlap 
+SELECT '(2.0,2.0,0.0,0.0,4.0,0.0)'::polygon && '(3.0,3.0,1.0,1.0,3.0,0.0)'::polygon AS true;
+
+
+
+--
+-- qualifications
+--
+
+--
+-- from clauses
+--
+
+--
+-- retrieve
+--
+
+--
+-- btree index
+-- awk '{if($1<10){print;}else{next;}}' onek.data | sort +0n -1
+--
+SELECT onek.* WHERE onek.unique1 < 10;
+
+--
+-- awk '{if($1<20){print $1,$14;}else{next;}}' onek.data | sort +0nr -1
+--
+SELECT onek.unique1, onek.stringu1
+   WHERE onek.unique1 < 20 
+   ORDER BY unique1 using >;
+
+--
+-- awk '{if($1>980){print $1,$14;}else{next;}}' onek.data | sort +1d -2
+--
+SELECT onek.unique1, onek.stringu1
+   WHERE onek.unique1 > 980 
+   ORDER BY stringu1 using <;
+       
+--
+-- awk '{if($1>980){print $1,$16;}else{next;}}' onek.data |
+-- sort +1d -2 +0nr -1
+--
+SELECT onek.unique1, onek.string4
+   WHERE onek.unique1 > 980 
+   ORDER BY string4 using <, unique1 using >;
+       
+--
+-- awk '{if($1>980){print $1,$16;}else{next;}}' onek.data |
+-- sort +1dr -2 +0n -1
+--
+SELECT onek.unique1, onek.string4
+   WHERE onek.unique1 > 980
+   ORDER BY string4 using >, unique1 using <;
+       
+--
+-- awk '{if($1<20){print $1,$16;}else{next;}}' onek.data |
+-- sort +0nr -1 +1d -2
+--
+SELECT onek.unique1, onek.string4
+   WHERE onek.unique1 < 20
+   ORDER BY unique1 using >, string4 using <;
+
+--
+-- awk '{if($1<20){print $1,$16;}else{next;}}' onek.data |
+-- sort +0n -1 +1dr -2
+--
+SELECT onek.unique1, onek.string4
+   WHERE onek.unique1 < 20 
+   ORDER BY unique1 using <, string4 using >;
+
+--
+-- partial btree index
+-- awk '{if($1<10){print $0;}else{next;}}' onek.data | sort +0n -1
+--
+-- SELECT onek2.* WHERE onek2.unique1 < 10;
+
+--
+-- partial btree index
+-- awk '{if($1<20){print $1,$14;}else{next;}}' onek.data | sort +0nr -1
+--
+-- SELECT onek2.unique1, onek2.stringu1
+    WHERE onek2.unique1 < 20 
+    ORDER BY unique1 using >;
+
+--
+-- awk '{if($1>980){print $1,$14;}else{next;}}' onek.data | sort +1d -2
+--
+--SELECT onek2.unique1, onek2.stringu1
+--   WHERE onek2.unique1 > 980
+--   ORDER BY stringu1 using <;
+       
+SELECT two, stringu1, ten, string4
+   INTO TABLE temp
+   FROM onek;
+
+--
+-- awk '{print $3;}' onek.data | sort -n | uniq
+--
+SELECT DISTINCT two FROM temp;
+
+--
+-- awk '{print $5;}' onek.data | sort -n | uniq
+--
+SELECT DISTINCT ten FROM temp;
+
+--
+-- awk '{print $16;}' onek.data | sort -d | uniq
+--
+SELECT DISTINCT string4 FROM temp;
+
+--
+-- awk '{print $3,$16,$5;}' onek.data | sort -d | uniq |
+-- sort +0n -1 +1d -2 +2n -3
+--
+SELECT DISTINCT two, string4, ten
+   FROM temp
+   ORDER BY two using <, string4 using <, ten using <;
+
+
+--
+-- test select distinct on
+--
+SELECT DISTINCT ON string4 two, string4, ten
+          FROM temp
+   ORDER BY two using <, string4 using <, ten using <;
+
+
+SELECT *
+   INTO TABLE temp1
+   FROM temp
+   WHERE onek.unique1 < 2;
+
+DROP TABLE temp1;
+
+SELECT *
+   INTO TABLE temp1
+   FROM temp
+   WHERE onek2.unique1 < 2;
+
+DROP TABLE temp1;
+
+--
+-- awk '{print $1,$2;}' person.data |
+-- awk '{if(NF!=2){print $3,$2;}else{print;}}' - emp.data |
+-- awk '{if(NF!=2){print $3,$2;}else{print;}}' - student.data |
+-- awk 'BEGIN{FS="     ";}{if(NF!=2){print $4,$5;}else{print;}}' - stud_emp.data
+--
+-- SELECT name, age FROM person*; ??? check if different
+SELECT p.name, p.age FROM person* p;
+
+--
+-- awk '{print $1,$2;}' person.data |
+-- awk '{if(NF!=2){print $3,$2;}else{print;}}' - emp.data |
+-- awk '{if(NF!=2){print $3,$2;}else{print;}}' - student.data |
+-- awk 'BEGIN{FS="     ";}{if(NF!=1){print $4,$5;}else{print;}}' - stud_emp.data |
+-- sort +1nr -2
+--
+SELECT p.name, p.age FROM person* p ORDER BY age using >;
+
+--
+-- awk '{print $2;}' person.data |
+-- awk '{if(NF!=1){print $2;}else{print;}}' - emp.data |
+-- awk '{if(NF!=1){print $2;}else{print;}}' - student.data |
+-- awk 'BEGIN{FS="     ";}{if(NF!=1){print $5;}else{print;}}' - stud_emp.data |
+-- sort -n -r | uniq
+--
+SELECT DISTINCT p.age FROM person* p ORDER BY age using >;
+
+--
+-- hash index
+-- grep 843938989 hash.data
+--
+SELECT hash_i4_heap.* 
+   WHERE hash_i4_heap.random = 843938989;
+
+--
+-- hash index
+-- grep 66766766 hash.data
+--
+SELECT hash_i4_heap.*
+   WHERE hash_i4_heap.random = 66766766;
+
+--
+-- hash index
+-- grep 1505703298 hash.data
+--
+SELECT hash_c16_heap.*
+   WHERE hash_c16_heap.random = '1505703298'::char16;
+
+--
+-- hash index
+-- grep 7777777 hash.data
+--
+SELECT hash_c16_heap.*
+   WHERE hash_c16_heap.random = '7777777'::char16;
+
+--
+-- hash index
+-- grep 1351610853 hash.data
+--
+SELECT hash_txt_heap.*
+   WHERE hash_txt_heap.random = '1351610853'::text;
+
+--
+-- hash index
+-- grep 111111112222222233333333 hash.data
+--
+SELECT hash_txt_heap.*
+   WHERE hash_txt_heap.random = '111111112222222233333333'::text;
+
+--
+-- hash index
+-- grep 444705537 hash.data
+--
+SELECT hash_f8_heap.*
+   WHERE hash_f8_heap.random = '444705537'::float8;
+
+--
+-- hash index
+-- grep 88888888 hash.data
+--
+SELECT hash_f8_heap.*
+   WHERE hash_f8_heap.random = '88888888'::float8;
+
+--
+-- hash index
+-- grep '^90[^0-9]' hashovfl.data
+--
+-- SELECT count(*) AS i988 FROM hash_ovfl_heap
+--    WHERE x = 90;
+
+--
+-- hash index
+-- grep '^1000[^0-9]' hashovfl.data
+--
+-- SELECT count(*) AS i0 FROM hash_ovfl_heap
+--    WHERE x = 1000;
+
+
+--
+-- btree index
+-- test retrieval of min/max keys for each
+--
+
+SELECT b.*
+   FROM bt_i4_heap b
+   WHERE b.seqno < 1;
+
+SELECT b.*
+   FROM bt_i4_heap b
+   WHERE b.seqno >= 9999;
+
+SELECT b.*
+   FROM bt_i4_heap b
+   WHERE b.seqno = 4500;
+
+SELECT b.*
+   FROM bt_c16_heap b
+   WHERE b.seqno < '1'::char16;
+
+SELECT b.*
+   FROM bt_c16_heap b
+   WHERE b.seqno >= '9999'::char16;
+
+SELECT b.*
+   FROM bt_c16_heap b
+   WHERE b.seqno = '4500'::char16;
+
+SELECT b.*
+   FROM bt_txt_heap b
+   WHERE b.seqno < '1'::text;
+
+SELECT b.*
+   FROM bt_txt_heap b
+   WHERE b.seqno >= '9999'::text;
+
+SELECT b.*
+   FROM bt_txt_heap b
+   WHERE b.seqno = '4500'::text;
+
+SELECT b.*
+   FROM bt_f8_heap b
+   WHERE b.seqno < '1'::float8;
+
+SELECT b.*
+   FROM bt_f8_heap b
+   WHERE b.seqno >= '9999'::float8;
+
+SELECT b.*
+   FROM bt_f8_heap b
+   WHERE b.seqno = '4500'::float8;
+
+
+
+--
+-- replace
+--
+--
+-- BTREE
+--
+UPDATE onek
+   SET unique1 = onek.unique1 + 1;
+
+UPDATE onek
+   SET unique1 = onek.unique1 - 1;
+
+--
+-- BTREE partial
+--
+-- UPDATE onek2
+--   SET unique1 = onek2.unique1 + 1;
+
+--UPDATE onek2 
+--   SET unique1 = onek2.unique1 - 1;
+
+--
+-- BTREE shutting out non-functional updates
+--
+-- the following two tests seem to take a long time on some 
+-- systems.    This non-func update stuff needs to be examined
+-- more closely.                       - jolly (2/22/96)
+-- 
+UPDATE temp
+   SET stringu1 = reverse_c16(onek.stringu1)
+   WHERE onek.stringu1 = 'JBAAAA' and
+         onek.stringu1 = temp.stringu1;
+
+UPDATE temp
+   SET stringu1 = reverse_c16(onek2.stringu1)
+   WHERE onek2.stringu1 = 'JCAAAA' and
+         onek2.stringu1 = temp.stringu1;
+
+DROP TABLE temp;
+
+--UPDATE person*
+--   SET age = age + 1;
+
+--UPDATE person*
+--   SET age = age + 3
+--   WHERE name = 'linda';
+
+
+--
+-- HASH
+--
+UPDATE hash_i4_heap
+   SET random = 1
+   WHERE hash_i4_heap.seqno = 1492;
+
+SELECT h.seqno AS i1492, h.random AS i1
+   FROM hash_i4_heap h
+   WHERE h.random = 1;
+
+UPDATE hash_i4_heap 
+   SET seqno = 20000 
+   WHERE hash_i4_heap.random = 1492795354;
+
+SELECT h.seqno AS i20000 
+   FROM hash_i4_heap h
+   WHERE h.random = 1492795354;
+
+UPDATE hash_c16_heap 
+   SET random = '0123456789abcdef'::char16
+   WHERE hash_c16_heap.seqno = 6543;
+
+SELECT h.seqno AS i6543, h.random AS c0_to_f
+   FROM hash_c16_heap h
+   WHERE h.random = '0123456789abcdef'::char16;
+
+UPDATE hash_c16_heap
+   SET seqno = 20000
+   WHERE hash_c16_heap.random = '76652222'::char16;
+
+--
+-- this is the row we just replaced; index scan should return zero rows 
+--
+SELECT h.seqno AS emptyset
+   FROM hash_c16_heap h
+   WHERE h.random = '76652222'::char16;
+
+UPDATE hash_txt_heap 
+   SET random = '0123456789abcdefghijklmnop'::text
+   WHERE hash_txt_heap.seqno = 4002;
+
+SELECT h.seqno AS i4002, h.random AS c0_to_p
+   FROM hash_txt_heap h
+   WHERE h.random = '0123456789abcdefghijklmnop'::text;
+
+UPDATE hash_txt_heap
+   SET seqno = 20000
+   WHERE hash_txt_heap.random = '959363399'::text;
+
+SELECT h.seqno AS t20000
+   FROM hash_txt_heap h
+   WHERE h.random = '959363399'::text;
+
+UPDATE hash_f8_heap
+   SET random = '-1234.1234'::float8
+   WHERE hash_f8_heap.seqno = 8906;
+
+SELECT h.seqno AS i8096, h.random AS f1234_1234 
+   FROM hash_f8_heap h
+   WHERE h.random = '-1234.1234'::float8;
+
+UPDATE hash_f8_heap 
+   SET seqno = 20000
+   WHERE hash_f8_heap.random = '488912369'::float8;
+
+SELECT h.seqno AS f20000
+   FROM hash_f8_heap h
+   WHERE h.random = '488912369'::float8;
+
+-- UPDATE hash_ovfl_heap
+--    SET x = 1000
+--   WHERE x = 90;
+
+-- this vacuums the index as well
+-- VACUUM hash_ovfl_heap;
+
+-- SELECT count(*) AS i0 FROM hash_ovfl_heap
+--   WHERE x = 90;
+
+-- SELECT count(*) AS i988 FROM hash_ovfl_heap
+--  WHERE x = 1000;
+
+--
+-- append
+--     (is tested in create.source)
+--
+
+--
+-- queries to plan and execute each plannode and execnode we have
+--
+
+--
+-- builtin functions
+--
+
+--
+-- copy
+--
+COPY onek TO '_OBJWD_/onek.data';
+
+DELETE FROM onek;
+
+COPY onek FROM '_OBJWD_/onek.data';
+
+SELECT unique1 FROM onek WHERE unique1 < 2;
+
+DELETE FROM onek2;
+
+COPY onek2 FROM '_OBJWD_/onek.data';
+
+SELECT unique1 FROM onek2 WHERE unique1 < 2;
+
+COPY BINARY stud_emp TO '_OBJWD_/stud_emp.data';
+
+DELETE FROM stud_emp;
+
+COPY BINARY stud_emp FROM '_OBJWD_/stud_emp.data';
+
+SELECT * FROM stud_emp;
+
+-- COPY aggtest FROM stdin;
+-- 56  7.8
+-- 100 99.097
+-- 0   0.09561
+-- 42  324.78
+-- .
+-- COPY aggtest TO stdout;
+
+
+--
+-- test the random function
+--
+-- count the number of tuples originally
+SELECT count(*) FROM onek;
+
+-- select roughly 1/10 of the tuples
+SELECT count(*) FROM onek where oidrand(onek.oid, 10);
+
+-- select again, the count should be different
+SELECT count(*) FROM onek where oidrand(onek.oid, 10);
+
+
+--
+-- transaction blocks
+--
+BEGIN;
+
+SELECT * 
+   INTO TABLE xacttest
+   FROM aggtest;
+
+INSERT INTO xacttest (a, b) VALUES (777, 777.777);
+
+END;
+
+-- should retrieve one value--
+SELECT a FROM xacttest WHERE a > 100;
+
+
+BEGIN;
+
+CREATE TABLE disappear (a int4);
+
+DELETE FROM aggtest;
+
+-- should be empty
+SELECT * FROM aggtest;
+
+ABORT;
+
+-- should not exist 
+SELECT oid FROM pg_class WHERE relname = 'disappear';
+
+-- should have members again 
+SELECT * FROM aggtest;
+
+
+--
+-- portal manipulation
+--
+BEGIN;
+
+DECLARE foo1 CURSOR FOR SELECT * FROM tenk1;
+
+DECLARE foo2 CURSOR FOR SELECT * FROM tenk2;
+
+DECLARE foo3 CURSOR FOR SELECT * FROM tenk1;
+
+DECLARE foo4 CURSOR FOR SELECT * FROM tenk2;
+
+DECLARE foo5 CURSOR FOR SELECT * FROM tenk1;
+
+DECLARE foo6 CURSOR FOR SELECT * FROM tenk2;
+
+DECLARE foo7 CURSOR FOR SELECT * FROM tenk1;
+
+DECLARE foo8 CURSOR FOR SELECT * FROM tenk2;
+
+DECLARE foo9 CURSOR FOR SELECT * FROM tenk1;
+
+DECLARE foo10 CURSOR FOR SELECT * FROM tenk2;
+
+DECLARE foo11 CURSOR FOR SELECT * FROM tenk1;
+
+DECLARE foo12 CURSOR FOR SELECT * FROM tenk2;
+
+DECLARE foo13 CURSOR FOR SELECT * FROM tenk1;
+
+DECLARE foo14 CURSOR FOR SELECT * FROM tenk2;
+
+DECLARE foo15 CURSOR FOR SELECT * FROM tenk1;
+
+DECLARE foo16 CURSOR FOR SELECT * FROM tenk2;
+
+DECLARE foo17 CURSOR FOR SELECT * FROM tenk1;
+
+DECLARE foo18 CURSOR FOR SELECT * FROM tenk2;
+
+DECLARE foo19 CURSOR FOR SELECT * FROM tenk1;
+
+DECLARE foo20 CURSOR FOR SELECT * FROM tenk2;
+
+DECLARE foo21 CURSOR FOR SELECT * FROM tenk1;
+
+DECLARE foo22 CURSOR FOR SELECT * FROM tenk2;
+
+DECLARE foo23 CURSOR FOR SELECT * FROM tenk1;
+
+FETCH 1 in foo1;
+
+FETCH 2 in foo2;
+
+FETCH 3 in foo3;
+
+FETCH 4 in foo4;
+
+FETCH 5 in foo5;
+
+FETCH 6 in foo6;
+
+FETCH 7 in foo7;
+
+FETCH 8 in foo8;
+
+FETCH 9 in foo9;
+
+FETCH 10 in foo10;
+
+FETCH 11 in foo11;
+
+FETCH 12 in foo12;
+
+FETCH 13 in foo13;
+
+FETCH 14 in foo14;
+
+FETCH 15 in foo15;
+
+FETCH 16 in foo16;
+
+FETCH 17 in foo17;
+
+FETCH 18 in foo18;
+
+FETCH 19 in foo19;
+
+FETCH 20 in foo20;
+
+FETCH 21 in foo21;
+
+FETCH 22 in foo22;
+
+FETCH 23 in foo23;
+
+FETCH backward 1 in foo23;
+
+FETCH backward 2 in foo22;
+
+FETCH backward 3 in foo21;
+
+FETCH backward 4 in foo20;
+
+FETCH backward 5 in foo19;
+
+FETCH backward 6 in foo18;
+
+FETCH backward 7 in foo17;
+
+FETCH backward 8 in foo16;
+
+FETCH backward 9 in foo15;
+
+FETCH backward 10 in foo14;
+
+FETCH backward 11 in foo13;
+
+FETCH backward 12 in foo12;
+
+FETCH backward 13 in foo11;
+
+FETCH backward 14 in foo10;
+
+FETCH backward 15 in foo9;
+
+FETCH backward 16 in foo8;
+
+FETCH backward 17 in foo7;
+
+FETCH backward 18 in foo6;
+
+FETCH backward 19 in foo5;
+
+FETCH backward 20 in foo4;
+
+FETCH backward 21 in foo3;
+
+FETCH backward 22 in foo2;
+
+FETCH backward 23 in foo1;
+
+CLOSE foo1;
+
+CLOSE foo2;
+
+CLOSE foo3;
+
+CLOSE foo4;
+
+CLOSE foo5;
+
+CLOSE foo6;
+
+CLOSE foo7;
+
+CLOSE foo8;
+
+CLOSE foo9;
+
+CLOSE foo10;
+
+CLOSE foo11;
+
+CLOSE foo12;
+
+end;
+
+EXTEND INDEX onek2_u1_prtl WHERE onek2.unique1 <= 60;
+
+BEGIN;
+
+DECLARE foo13 CURSOR FOR 
+   SELECT * FROM onek WHERE unique1 = 50;
+
+DECLARE foo14 CURSOR FOR 
+   SELECT * FROM onek WHERE unique1 = 51;
+
+DECLARE foo15 CURSOR FOR 
+   SELECT * FROM onek WHERE unique1 = 52;
+
+DECLARE foo16 CURSOR FOR 
+   SELECT * FROM onek WHERE unique1 = 53;
+
+DECLARE foo17 CURSOR FOR 
+   SELECT * FROM onek WHERE unique1 = 54;
+
+DECLARE foo18 CURSOR FOR 
+   SELECT * FROM onek WHERE unique1 = 55;
+
+DECLARE foo19 CURSOR FOR 
+   SELECT * FROM onek WHERE unique1 = 56;
+
+DECLARE foo20 CURSOR FOR 
+   SELECT * FROM onek WHERE unique1 = 57;
+
+DECLARE foo21 CURSOR FOR 
+   SELECT * FROM onek WHERE unique1 = 58;
+
+DECLARE foo22 CURSOR FOR 
+   SELECT * FROM onek WHERE unique1 = 59;
+
+DECLARE foo23 CURSOR FOR 
+   SELECT * FROM onek WHERE unique1 = 60;
+
+DECLARE foo24 CURSOR FOR 
+   SELECT * FROM onek2 WHERE unique1 = 50;
+
+DECLARE foo25 CURSOR FOR 
+   SELECT * FROM onek2 WHERE unique1 = 60;
+
+FETCH all in foo13;
+
+FETCH all in foo14;
+
+FETCH all in foo15;
+
+FETCH all in foo16;
+
+FETCH all in foo17;
+
+FETCH all in foo18;
+
+FETCH all in foo19;
+
+FETCH all in foo20;
+
+FETCH all in foo21;
+
+FETCH all in foo22;
+
+FETCH all in foo23;
+
+FETCH all in foo24;
+
+FETCH all in foo25;
+
+CLOSE foo13;
+
+CLOSE foo14;
+
+CLOSE foo15;
+
+CLOSE foo16;
+
+CLOSE foo17;
+
+CLOSE foo18;
+
+CLOSE foo19;
+
+CLOSE foo20;
+
+CLOSE foo21;
+
+CLOSE foo22;
+
+CLOSE foo23;
+
+CLOSE foo24;
+
+CLOSE foo25;
+
+END;
+
+
+--
+-- PURGE
+--
+-- we did two updates on each of these 10K tables up above.  we should
+-- therefore go from 10002 tuples (two of which are not visible without
+-- using a time qual) to 10000.
+--
+-- vacuuming here also tests whether or not the hash index compaction
+-- code works; this used to be commented out because the hash AM would
+-- miss deleting a bunch of index tuples, which caused big problems when
+-- you dereferenced the tids and found garbage..
+--
+PURGE hash_f8_heap BEFORE 'now';               -- absolute time
+
+SELECT count(*) AS has_10002 FROM hash_f8_heap[,] h;
+
+VACUUM hash_f8_heap;
+
+SELECT count(*) AS has_10000 FROM hash_f8_heap[,] h;
+
+PURGE hash_i4_heap AFTER '@ 1 second ago';     -- relative time
+
+SELECT count(*) AS has_10002 FROM hash_i4_heap[,] h;
+
+VACUUM hash_i4_heap;
+
+SELECT count(*) AS has_10000 FROM hash_i4_heap[,] h;
+
+
+--
+-- add attribute
+--
+CREATE TABLE temp (initial int4);
+
+ALTER TABLE temp ADD COLUMN a int4;
+
+ALTER TABLE temp ADD COLUMN b char16;
+
+ALTER TABLE temp ADD COLUMN c text;
+
+ALTER TABLE temp ADD COLUMN d float8;
+
+ALTER TABLE temp ADD COLUMN e float4;
+
+ALTER TABLE temp ADD COLUMN f int2;
+
+ALTER TABLE temp ADD COLUMN g polygon;
+
+ALTER TABLE temp ADD COLUMN h abstime;
+
+ALTER TABLE temp ADD COLUMN i char;
+
+ALTER TABLE temp ADD COLUMN j abstime[];
+
+ALTER TABLE temp ADD COLUMN k dt;
+
+ALTER TABLE temp ADD COLUMN l tid;
+
+ALTER TABLE temp ADD COLUMN m xid;
+
+ALTER TABLE temp ADD COLUMN n oid8;
+
+--ALTER TABLE temp ADD COLUMN o lock;
+ALTER TABLE temp ADD COLUMN p smgr;
+
+ALTER TABLE temp ADD COLUMN q point;
+
+ALTER TABLE temp ADD COLUMN r lseg;
+
+ALTER TABLE temp ADD COLUMN s path;
+
+ALTER TABLE temp ADD COLUMN t box;
+
+ALTER TABLE temp ADD COLUMN u tinterval;
+
+ALTER TABLE temp ADD COLUMN v oidint4;
+
+ALTER TABLE temp ADD COLUMN w oidname;
+
+ALTER TABLE temp ADD COLUMN x float8[];
+
+ALTER TABLE temp ADD COLUMN y float4[];
+
+ALTER TABLE temp ADD COLUMN z int2[];
+
+INSERT INTO temp (a, b, c, d, e, f, g, h, i, j, k, l, m, n, p, q, r, s, t, u,
+       v, w, x, y, z)
+   VALUES (4, 'char16', 'text', 4.1, 4.1, 2, '(4.1,4.1,3.1,3.1)', 
+        'Mon May  1 00:30:30 PDT 1995', 'c', '{Mon May  1 00:30:30 PDT 1995, Monday Aug 24 14:43:07 1992 PDT, epoch}', 
+       314159, '(1,1)', 512,
+       '1 2 3 4 5 6 7 8', 'magnetic disk', '(1.1,1.1)', '(4.1,4.1,3.1,3.1)',
+       '(0,2,4.1,4.1,3.1,3.1)', '(4.1,4.1,3.1,3.1)', '["current" "infinity"]',
+       '1/3', '1,char16', '{1.0,2.0,3.0,4.0}', '{1.0,2.0,3.0,4.0}', '{1,2,3,4}');
+
+SELECT * FROM temp;
+
+DROP TABLE temp;
+
+-- the wolf bug - schema mods caused inconsistent row descriptors 
+CREATE TABLE temp (
+       initial         int4
+) ARCHIVE = light;
+
+ALTER TABLE temp ADD COLUMN a int4;
+
+ALTER TABLE temp ADD COLUMN b char16;
+
+ALTER TABLE temp ADD COLUMN c text;
+
+ALTER TABLE temp ADD COLUMN d float8;
+
+ALTER TABLE temp ADD COLUMN e float4;
+
+ALTER TABLE temp ADD COLUMN f int2;
+
+ALTER TABLE temp ADD COLUMN g polygon;
+
+ALTER TABLE temp ADD COLUMN h abstime;
+
+ALTER TABLE temp ADD COLUMN i char;
+
+ALTER TABLE temp ADD COLUMN j abstime[];
+
+ALTER TABLE temp ADD COLUMN k dt;
+
+ALTER TABLE temp ADD COLUMN l tid;
+
+ALTER TABLE temp ADD COLUMN m xid;
+
+ALTER TABLE temp ADD COLUMN n oid8;
+
+--ALTER TABLE temp ADD COLUMN o lock;
+ALTER TABLE temp ADD COLUMN p smgr;
+
+ALTER TABLE temp ADD COLUMN q point;
+
+ALTER TABLE temp ADD COLUMN r lseg;
+
+ALTER TABLE temp ADD COLUMN s path;
+
+ALTER TABLE temp ADD COLUMN t box;
+
+ALTER TABLE temp ADD COLUMN u tinterval;
+
+ALTER TABLE temp ADD COLUMN v oidint4;
+
+ALTER TABLE temp ADD COLUMN w oidname;
+
+ALTER TABLE temp ADD COLUMN x float8[];
+
+ALTER TABLE temp ADD COLUMN y float4[];
+
+ALTER TABLE temp ADD COLUMN z int2[];
+
+INSERT INTO temp (a, b, c, d, e, f, g, h, i, j, k, l, m, n, p, q, r, s, t, u,
+       v, w, x, y, z)
+   VALUES (4, 'char16', 'text', 4.1, 4.1, 2, '(4.1,4.1,3.1,3.1)', 
+       'Mon May  1 00:30:30 PDT 1995', 'c', '{Mon May  1 00:30:30 PDT 1995, Monday Aug 24 14:43:07 1992 PDT, epoch}',
+        314159, '(1,1)', 512,
+       '1 2 3 4 5 6 7 8', 'magnetic disk', '(1.1,1.1)', '(4.1,4.1,3.1,3.1)',
+       '(0,2,4.1,4.1,3.1,3.1)', '(4.1,4.1,3.1,3.1)', '["current" "infinity"]',
+       '1/3', '1,char16', '{1.0,2.0,3.0,4.0}', '{1.0,2.0,3.0,4.0}', '{1,2,3,4}');
+
+SELECT * FROM temp[,];
+
+DROP TABLE temp;
+
+
+--
+-- rename -
+--   should preserve indices
+--
+ALTER TABLE tenk1 RENAME TO ten_k;
+
+-- 20 values, sorted 
+SELECT unique1 FROM ten_k WHERE unique1 < 20;
+
+-- 20 values, sorted 
+SELECT unique2 FROM ten_k WHERE unique2 < 20;
+
+-- 100 values, sorted 
+SELECT hundred FROM ten_k WHERE hundred = 50;
+
+ALTER TABLE ten_k RENAME TO tenk1;
+
+-- 5 values, sorted 
+SELECT unique1 FROM tenk1 WHERE unique1 < 5;
+
+
+--
+-- VIEW queries
+--
+-- test the views defined in create.source
+--
+SELECT * from street;
+
+SELECT * from iexit;
+
+SELECT * from toyemp where name='sharon';
+
+
+
+
+--
+-- AGGREGATES
+--
+SELECT avg(four) AS avg_1 FROM onek;
+
+SELECT avg(a) AS avg_49 FROM aggtest WHERE a < 100;
+
+SELECT avg(b) AS avg_107_943 FROM aggtest;
+
+SELECT avg(gpa) AS avg_3_4 FROM student;
+
+
+SELECT sum(four) AS sum_1500 FROM onek;
+
+SELECT sum(a) AS sum_198 FROM aggtest;
+
+SELECT sum(b) AS avg_431_773 FROM aggtest;
+
+SELECT sum(gpa) AS avg_6_8 FROM student;
+
+
+SELECT max(four) AS max_3 FROM onek;
+
+SELECT max(a) AS max_100 FROM aggtest;
+
+SELECT max(aggtest.b) AS max_324_78 FROM aggtest;
+
+SELECT max(student.gpa) AS max_3_7 FROM student;
+
+
+SELECT count(four) AS cnt_1000 FROM onek;
+
+
+SELECT newavg(four) AS avg_1 FROM onek;
+
+SELECT newsum(four) AS sum_1500 FROM onek;
+
+SELECT newcnt(four) AS cnt_1000 FROM onek;
+
+
+--
+-- inheritance stress test
+--
+SELECT * FROM a_star*;
+
+SELECT * 
+   FROM b_star* x
+   WHERE x.b = 'bumble'::text or x.a < 3;
+
+SELECT class, a 
+   FROM c_star* x 
+   WHERE x.c ~ 'hi'::text;
+
+SELECT class, b, c
+   FROM d_star* x
+   WHERE x.a < 100;
+
+SELECT class, c FROM e_star* x WHERE x.c NOTNULL;
+
+SELECT * FROM f_star* x WHERE x.c ISNULL;
+
+ALTER TABLE f_star RENAME COLUMN f TO ff;
+
+ALTER TABLE e_star* RENAME COLUMN e TO ee;
+
+ALTER TABLE d_star* RENAME COLUMN d TO dd;
+
+ALTER TABLE c_star* RENAME COLUMN c TO cc;
+
+ALTER TABLE b_star* RENAME COLUMN b TO bb;
+
+ALTER TABLE a_star* RENAME COLUMN a TO aa;
+
+SELECT class, aa
+   FROM a_star* x
+   WHERE aa ISNULL;
+
+ALTER TABLE a_star RENAME COLUMN aa TO foo;
+
+SELECT class, foo
+   FROM a_star x
+   WHERE x.foo >= 2;
+
+ALTER TABLE a_star RENAME COLUMN foo TO aa;
+
+SELECT * 
+   from a_star*
+   WHERE aa < 1000;
+
+ALTER TABLE f_star ADD COLUMN f int4;
+
+UPDATE f_star SET f = 10;
+
+ALTER TABLE e_star* ADD COLUMN e int4;
+
+UPDATE e_star* SET e = 42;
+
+SELECT * FROM e_star*;
+
+ALTER TABLE a_star* ADD COLUMN a text;
+
+UPDATE b_star*
+   SET a = 'gazpacho'::text
+   WHERE aa > 4;
+
+SELECT class, aa, a FROM a_star*;
+
+
+--
+-- versions
+--
+
+--
+-- postquel functions
+--
+--
+-- mike does post_hacking,
+-- joe and sally play basketball, and
+-- everyone else does nothing.
+--
+SELECT p.name, p.hobbies.name FROM person p;
+
+--
+-- as above, but jeff also does post_hacking.
+--
+SELECT p.name, p.hobbies.name FROM person* p;
+
+--
+-- the next two queries demonstrate how functions generate bogus duplicates.
+-- this is a "feature" ..
+--
+SELECT DISTINCT hobbies_r.name, hobbies_r.equipment.name FROM hobbies_r;
+
+SELECT hobbies_r.name, hobbies_r.equipment.name FROM hobbies_r;
+
+--
+-- mike needs advil and peet's coffee,
+-- joe and sally need hightops, and
+-- everyone else is fine.
+--
+SELECT p.name, p.hobbies.name, p.hobbies.equipment.name FROM person p;
+
+--
+-- as above, but jeff needs advil and peet's coffee as well.
+--
+SELECT p.name, p.hobbies.name, p.hobbies.equipment.name FROM person* p;
+
+--
+-- just like the last two, but make sure that the target list fixup and
+-- unflattening is being done correctly.
+--
+SELECT p.hobbies.equipment.name, p.name, p.hobbies.name FROM person p;
+
+SELECT p.hobbies.equipment.name, p.name, p.hobbies.name FROM person* p;
+
+SELECT p.hobbies.equipment.name, p.hobbies.name, p.name FROM person p;
+
+SELECT p.hobbies.equipment.name, p.hobbies.name, p.name FROM person* p;
+
+SELECT user_relns() AS user_relns
+   ORDER BY user_relns;
+
+--SELECT name(equipment(hobby_construct('skywalking'::text, 'mer'::text))) AS equip_name;
+
+
+--
+-- functional joins
+--
+
+--
+-- instance rules
+--
+
+--
+-- rewrite rules
+--
+
+--
+-- ARRAYS
+--
+SELECT * FROM arrtest;
+
+SELECT arrtest.a[1],
+          arrtest.b[1][1][1],
+          arrtest.c[1],
+          arrtest.d[1][1], 
+          arrtest.e[0]
+   FROM arrtest;
+-- ??? what about
+-- SELECT a[1], b[1][1][1], c[1], d[1][1], e[0]
+--    FROM arrtest;
+
+SELECT arrtest.a[1:3],
+          arrtest.b[1:1][1:2][1:2],
+          arrtest.c[1:2], 
+          arrtest.d[1:1][1:2]
+   FROM arrtest;
+
+-- returns three different results--
+SELECT array_dims(arrtest.b) AS x;
+
+-- returns nothing 
+SELECT *
+   FROM arrtest
+   WHERE arrtest.a[1] < 5 and 
+         arrtest.c = '{"foobar"}'::_char16;
+
+-- updating array subranges seems to be broken
+-- 
+-- UPDATE arrtest
+--   SET a[1:2] = '{16,25}',
+--       b[1:1][1:1][1:2] = '{113, 117}', 
+--       c[1:1] = '{"new_word"}';
+
+SELECT arrtest.a[1:3],
+          arrtest.b[1:1][1:2][1:2],
+          arrtest.c[1:2], 
+          arrtest.d[1:1][1:2]
+   FROM arrtest;
+
+
+--
+-- expensive functions
+--
+
+
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
new file mode 100644 (file)
index 0000000..999a8d8
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ * $Header$
+ */
+
+#include <float.h>             /* faked on sunos */
+#include <stdio.h>
+
+#include "utils/geo-decls.h"   /* includes <math.h> */
+#include "libpq-fe.h"
+
+#define P_MAXDIG 12
+#define LDELIM         '('
+#define RDELIM         ')'
+#define        DELIM           ','
+
+extern double *regress_dist_ptpath (Point *pt, PATH *path);
+extern double *regress_path_dist (PATH *p1, PATH *p2);
+extern PATH *poly2path (POLYGON *poly);
+extern Point *interpt_pp (PATH *p1, PATH *p2);
+extern void regress_lseg_construct (LSEG *lseg, Point *pt1, Point *pt2);
+extern char overpaid (TUPLE tuple);
+extern int boxarea (BOX *box);
+extern char *reverse_c16 (char *string);
+
+/*
+** Distance from a point to a path 
+*/
+double *
+regress_dist_ptpath(pt, path)
+    Point *pt;
+    PATH *path;
+{
+    double *result;
+    double *tmp;
+    int i;
+    LSEG lseg;
+
+    switch (path->npts) {
+    case 0:
+       result = PALLOCTYPE(double);
+       *result = Abs((double) DBL_MAX);        /* +infinity */
+       break;
+    case 1:
+       result = point_distance(pt, &path->p[0]);
+       break;
+    default:
+       /*
+        * the distance from a point to a path is the smallest distance
+        * from the point to any of its constituent segments.
+        */
+       Assert(path->npts > 1);
+       result = PALLOCTYPE(double);
+       for (i = 0; i < path->npts - 1; ++i) {
+           regress_lseg_construct(&lseg, &path->p[i], &path->p[i+1]);
+           tmp = dist_ps(pt, &lseg);
+           if (i == 0 || *tmp < *result)
+               *result = *tmp;
+           PFREE(tmp);
+
+       }
+       break;
+    }
+    return(result);
+}
+
+/* this essentially does a cartesian product of the lsegs in the
+   two paths, and finds the min distance between any two lsegs */
+double *
+regress_path_dist(p1, p2)
+    PATH *p1;
+    PATH *p2;
+{
+    double *min, *tmp;
+    int i,j;
+    LSEG seg1, seg2;
+
+    regress_lseg_construct(&seg1, &p1->p[0], &p1->p[1]);
+    regress_lseg_construct(&seg2, &p2->p[0], &p2->p[1]);
+    min = lseg_distance(&seg1, &seg2);
+
+    for (i = 0; i < p1->npts - 1; i++)
+      for (j = 0; j < p2->npts - 1; j++)
+       {
+          regress_lseg_construct(&seg1, &p1->p[i], &p1->p[i+1]);
+          regress_lseg_construct(&seg2, &p2->p[j], &p2->p[j+1]);
+
+          if (*min < *(tmp = lseg_distance(&seg1, &seg2)))
+            *min = *tmp;
+          PFREE(tmp);
+       }
+
+    return(min);
+}
+
+PATH *
+poly2path(poly)
+    POLYGON *poly;
+{
+    int i;
+    char *output = (char *)PALLOC(2*(P_MAXDIG + 1)*poly->npts + 64);
+    char *outptr = output;
+    double *xp, *yp;
+
+    sprintf(outptr, "(1, %*d", P_MAXDIG, poly->npts);
+    xp = (double *) poly->pts;
+    yp = (double *) (poly->pts + (poly->npts * sizeof(double *)));
+
+    for (i=1; i<poly->npts; i++,xp++,yp++)
+     {
+        sprintf(outptr, ",%*g,%*g", P_MAXDIG, *xp, P_MAXDIG, *yp);
+        outptr += 2*(P_MAXDIG + 1);
+     }
+
+    *outptr++ = RDELIM;
+    *outptr = '\0';
+    return(path_in(outptr));
+}
+
+/* return the point where two paths intersect.  Assumes that they do. */
+Point *
+interpt_pp(p1,p2)
+    PATH *p1;
+    PATH *p2;
+{
+        
+    Point *retval;
+    int i,j;
+    LSEG seg1, seg2;
+    LINE *ln;
+
+    for (i = 0; i < p1->npts - 1; i++)
+      for (j = 0; j < p2->npts - 1; j++)
+       {
+          regress_lseg_construct(&seg1, &p1->p[i], &p1->p[i+1]);
+          regress_lseg_construct(&seg2, &p2->p[j], &p2->p[j+1]);
+          if (lseg_intersect(&seg1, &seg2))
+           {
+               ln = line_construct_pp(&seg2.p[0], &seg2.p[1]);
+               retval = interpt_sl(&seg1, ln);
+               goto exit;
+           }
+       }
+
+  exit:
+    return(retval);
+}
+
+
+/* like lseg_construct, but assume space already allocated */
+void
+regress_lseg_construct(lseg, pt1, pt2)
+    LSEG *lseg;
+    Point *pt1;
+    Point *pt2;
+{
+    lseg->p[0].x = pt1->x;
+    lseg->p[0].y = pt1->y;
+    lseg->p[1].x = pt2->x;
+    lseg->p[1].y = pt2->y;
+    lseg->m = point_sl(pt1, pt2);
+}
+
+
+char overpaid(tuple)
+    TUPLE tuple;
+{
+    bool isnull;
+    long salary;
+
+    salary = (long)GetAttributeByName(tuple, "salary", &isnull);
+    return(salary > 699);
+}
+
+typedef struct {
+       Point   center;
+       double  radius;
+} CIRCLE;
+
+extern CIRCLE *circle_in (char *str);
+extern char *circle_out (CIRCLE *circle);
+extern int pt_in_circle (Point *point, CIRCLE *circle);
+
+#define NARGS  3
+
+CIRCLE *
+circle_in(str)
+char   *str;
+{
+       char    *p, *coord[NARGS], buf2[1000];
+       int     i;
+       CIRCLE  *result;
+
+       if (str == NULL)
+               return(NULL);
+       for (i = 0, p = str; *p && i < NARGS && *p != RDELIM; p++)
+               if (*p == ',' || (*p == LDELIM && !i))
+                       coord[i++] = p + 1;
+       if (i < NARGS - 1)
+               return(NULL);
+       result = (CIRCLE *) palloc(sizeof(CIRCLE));
+       result->center.x = atof(coord[0]);
+       result->center.y = atof(coord[1]);
+       result->radius = atof(coord[2]);
+
+       sprintf(buf2, "circle_in: read (%f, %f, %f)\n", result->center.x,
+       result->center.y,result->radius);
+       return(result);
+}
+
+char *
+circle_out(circle)
+    CIRCLE     *circle;
+{
+    char       *result;
+
+    if (circle == NULL)
+       return(NULL);
+
+    result = (char *) palloc(60);
+    (void) sprintf(result, "(%g,%g,%g)",
+                  circle->center.x, circle->center.y, circle->radius);
+    return(result);
+}
+
+int
+pt_in_circle(point, circle)
+       Point   *point;
+       CIRCLE  *circle;
+{
+       extern double   point_dt();
+
+       return( point_dt(point, &circle->center) < circle->radius );
+}
+
+#define ABS(X) ((X) > 0 ? (X) : -(X))
+
+int
+boxarea(box)
+
+BOX *box;
+
+{
+       int width, height;
+
+       width  = ABS(box->xh - box->xl);
+       height = ABS(box->yh - box->yl);
+       return (width * height);
+}
+
+char *
+reverse_c16(string)
+    char *string;
+{
+    register i;
+    int len;
+    char *new_string;
+
+    if (!(new_string = palloc(16))) {
+       fprintf(stderr, "reverse_c16: palloc failed\n");
+       return(NULL);
+    }
+    memset(new_string, 0, 16);
+    for (i = 0; i < 16 && string[i]; ++i)
+       ;
+    if (i == 16 || !string[i])
+       --i;
+    len = i;
+    for (; i >= 0; --i)
+       new_string[len-i] = string[i];
+    return(new_string);
+}
diff --git a/src/test/regress/regress.sh b/src/test/regress/regress.sh
new file mode 100755 (executable)
index 0000000..d9a8978
--- /dev/null
@@ -0,0 +1,64 @@
+#!/bin/sh
+# $Header$
+#
+if [ -d ./obj ]; then
+       cd ./obj
+fi
+
+#FRONTEND=monitor
+FRONTEND="psql -n -e -q"
+
+echo =============== destroying old regression database... =================
+destroydb regression
+
+echo =============== creating new regression database... =================
+createdb regression
+if [ $? -ne 0 ]; then
+     echo createdb failed
+     exit 1
+fi
+
+$FRONTEND regression < create.sql
+if [ $? -ne 0 ]; then
+     echo the creation script has an error
+     exit 1
+fi
+
+echo =============== running regression queries ... =================
+$FRONTEND regression < queries.sql
+if [ $? -ne 0 ]; then
+     echo the queries script causes an error
+     exit 1
+fi
+
+echo =============== running error queries ... =================
+$FRONTEND regression < errors.sql
+if [ $? -ne 0 ]; then
+     echo the errors script has an unanticipated problem
+     exit 1
+fi
+
+#set this to 1 to avoid clearing the database
+debug=0
+
+if test "$debug" -eq 1
+then
+echo Skipping clearing and deletion of the regression database
+else
+echo =============== clearing regression database... =================
+$FRONTEND regression < destroy.sql
+if [ $? -ne 0 ]; then
+     echo the destroy script has an error
+     exit 1
+fi
+
+exit 0
+echo =============== destroying regression database... =================
+destroydb regression
+if [ $? -ne 0 ]; then
+     echo destroydb failed
+     exit 1
+fi
+
+exit 0
+fi
diff --git a/src/test/regress/sample.regress.out b/src/test/regress/sample.regress.out
new file mode 100644 (file)
index 0000000..1caf4c7
--- /dev/null
@@ -0,0 +1,6362 @@
+/bin/sh ./regress.sh 2>&1 | tee obj/regress.out
+=============== destroying old regression database... =================
+WARN:destroydb: database regression does not exist.
+destroydb: database destroy failed on regression.
+=============== creating new regression database... =================
+QUERY: CREATE FUNCTION circle_in(opaque)
+   RETURNS circle
+   AS '/home2/jolly/pg95-1.01/src/test/regress/obj/regress.so'
+   LANGUAGE 'c';
+NOTICE:ProcedureCreate: type 'circle' is not yet defined
+QUERY: CREATE FUNCTION circle_out(opaque)
+   RETURNS opaque
+   AS '/home2/jolly/pg95-1.01/src/test/regress/obj/regress.so'
+   LANGUAGE 'c';
+QUERY: CREATE TYPE circle (
+   internallength = 24,
+   input = circle_in,
+   output = circle_out,
+   alignment = double
+);
+QUERY: CREATE TYPE city_budget (
+   internallength = 16,
+   input = int44in,
+   output = int44out,
+   element = int4
+);
+QUERY: CREATE TABLE hobbies_r (
+       name            text,
+       person          text
+);
+QUERY: CREATE TABLE equipment_r (
+       name            text,
+       hobby           text
+);
+QUERY: CREATE TABLE onek (
+       unique1         int4,
+       unique2         int4,
+       two             int4,
+       four            int4,
+       ten             int4,
+       twenty          int4,
+       hundred         int4,
+       thousand        int4,
+       twothousand     int4,
+       fivethous       int4,
+       tenthous        int4,
+       odd             int4,
+       even            int4,
+       stringu1        char16,
+       stringu2        char16,
+       string4         char16
+);
+QUERY: CREATE TABLE tenk1 (
+       unique1         int4,
+       unique2         int4,
+       two             int4,
+       four            int4,
+       ten             int4,
+       twenty          int4,
+       hundred         int4,
+       thousand        int4,
+       twothousand     int4,
+       fivethous       int4,
+       tenthous        int4,
+       odd             int4,
+       even            int4,
+       stringu1        char16,
+       stringu2        char16,
+       string4         char16
+);
+QUERY: CREATE TABLE tenk2 (
+       unique1         int4,
+       unique2         int4,
+       two             int4,
+       four            int4,
+       ten             int4,
+       twenty          int4,
+       hundred         int4,
+       thousand        int4,
+       twothousand     int4,
+       fivethous       int4,
+       tenthous        int4,
+       odd             int4,
+       even            int4,
+       stringu1        char16,
+       stringu2        char16,
+       string4         char16
+);
+QUERY: CREATE TABLE person (
+       name            text,
+       age             int4,
+       location        point
+);
+QUERY: CREATE TABLE emp (
+       salary          int4,
+       manager         char16
+) INHERITS (person);
+QUERY: CREATE TABLE student (
+       gpa             float8
+) INHERITS (person);
+QUERY: CREATE TABLE stud_emp (
+       percent         int4
+) INHERITS (emp, student);
+QUERY: CREATE TABLE city (
+       name            char16,
+       location        box,
+       budget          city_budget
+);
+QUERY: CREATE TABLE dept (
+       dname           char16,
+       mgrname         text
+);
+QUERY: CREATE TABLE slow_emp4000 (
+       home_base        box
+);
+QUERY: CREATE TABLE fast_emp4000 (
+       home_base        box
+);
+QUERY: CREATE TABLE road (
+       name            text,
+       thepath         path
+);
+QUERY: CREATE TABLE ihighway () INHERITS (road);
+QUERY: CREATE TABLE shighway (
+       surface         text
+) INHERITS (road);
+QUERY: CREATE TABLE real_city (
+       pop             int4,
+       cname           text,
+       outline         path
+);
+QUERY: CREATE TABLE a_star (
+       class           char,
+       a               int4
+);
+QUERY: CREATE TABLE b_star (
+       b               text
+) INHERITS (a_star);
+QUERY: CREATE TABLE c_star (
+       c               char16
+) INHERITS (a_star);
+QUERY: CREATE TABLE d_star (
+       d               float8
+) INHERITS (b_star, c_star);
+QUERY: CREATE TABLE e_star (
+       e               int2
+) INHERITS (c_star);
+QUERY: CREATE TABLE f_star (
+       f               polygon
+) INHERITS (e_star);
+QUERY: CREATE TABLE aggtest (
+       a               int2,
+       b               float4
+);
+QUERY: CREATE TABLE arrtest (
+       a               int2[],
+       b               int4[][][],
+       c               char16[],
+       d               text[][],
+       e               float8[]
+);
+QUERY: CREATE TABLE hash_i4_heap (
+       seqno           int4,
+       random          int4
+);
+QUERY: CREATE TABLE hash_c16_heap (
+       seqno           int4,
+       random          char16
+);
+QUERY: CREATE TABLE hash_txt_heap (
+       seqno           int4,
+       random          text
+);
+QUERY: CREATE TABLE hash_f8_heap (
+       seqno           int4,
+       random          float8
+);
+QUERY: CREATE TABLE bt_i4_heap (
+       seqno           int4,
+       random          int4
+);
+QUERY: CREATE TABLE bt_c16_heap (
+       seqno           char16,
+       random          int4
+);
+QUERY: CREATE TABLE bt_txt_heap (
+       seqno           text,
+       random          int4
+);
+QUERY: CREATE TABLE bt_f8_heap (
+       seqno           float8,
+       random          int4
+);
+QUERY: CREATE FUNCTION hobbies(person)
+   RETURNS setof hobbies_r
+   AS 'select * from hobbies_r where person = $1.name'
+   LANGUAGE 'sql';
+QUERY: CREATE FUNCTION hobby_construct(text, text)
+   RETURNS hobbies_r
+   AS 'select $1 as name, $2 as hobby'
+   LANGUAGE 'sql';
+QUERY: CREATE FUNCTION equipment(hobbies_r)
+   RETURNS setof equipment_r
+   AS 'select * from equipment_r where hobby = $1.name'
+   LANGUAGE 'sql';
+QUERY: CREATE FUNCTION user_relns()
+   RETURNS setof name
+   AS 'select relname
+       from pg_class
+       where relname !~ ''pg_.*'' and
+             relkind <> ''i'' '
+   LANGUAGE 'sql';
+QUERY: CREATE FUNCTION pt_in_circle(point, circle)
+   RETURNS int4
+   AS '/home2/jolly/pg95-1.01/src/test/regress/obj/regress.so'
+   LANGUAGE 'c';
+QUERY: CREATE FUNCTION overpaid(emp)
+   RETURNS bool
+   AS '/home2/jolly/pg95-1.01/src/test/regress/obj/regress.so'
+   LANGUAGE 'c';
+QUERY: CREATE FUNCTION boxarea(box)
+   RETURNS int4
+   AS '/home2/jolly/pg95-1.01/src/test/regress/obj/regress.so'
+   LANGUAGE 'c';
+QUERY: CREATE FUNCTION interpt_pp(path, path)
+   RETURNS point
+   AS '/home2/jolly/pg95-1.01/src/test/regress/obj/regress.so'
+   LANGUAGE 'c';
+QUERY: CREATE FUNCTION reverse_c16(char16)
+   RETURNS char16
+   AS '/home2/jolly/pg95-1.01/src/test/regress/obj/regress.so'
+   LANGUAGE 'c';
+QUERY: LOAD '/home2/jolly/pg95-1.01/src/test/regress/obj/regress.so'
+COPY onek FROM '/home2/jolly/pg95-1.01/src/test/regress/data/onek.data';
+QUERY: COPY tenk1 FROM '/home2/jolly/pg95-1.01/src/test/regress/data/tenk.data';
+QUERY: INSERT INTO tenk2 VALUES (tenk1.*);
+QUERY: SELECT * INTO TABLE onek2 FROM onek;
+QUERY: COPY slow_emp4000 FROM '/home2/jolly/pg95-1.01/src/test/regress/data/rect.data';
+QUERY: INSERT INTO fast_emp4000 VALUES (slow_emp4000.*);
+QUERY: COPY person FROM '/home2/jolly/pg95-1.01/src/test/regress/data/person.data';
+QUERY: COPY emp FROM '/home2/jolly/pg95-1.01/src/test/regress/data/emp.data';
+QUERY: COPY student FROM '/home2/jolly/pg95-1.01/src/test/regress/data/student.data';
+QUERY: COPY stud_emp FROM '/home2/jolly/pg95-1.01/src/test/regress/data/stud_emp.data';
+QUERY: SELECT *
+   INTO TABLE Bprime
+   FROM tenk1
+   WHERE unique2 < 1000;
+QUERY: INSERT INTO hobbies_r (name, person)
+   SELECT 'posthacking', p.name
+   FROM person* p
+   WHERE p.name = 'mike' or p.name = 'jeff';
+QUERY: INSERT INTO hobbies_r (name, person)
+   SELECT 'basketball', p.name
+   FROM person p
+   WHERE p.name = 'joe' or p.name = 'sally';
+QUERY: INSERT INTO hobbies_r (name) VALUES ('skywalking');
+QUERY: INSERT INTO equipment_r (name, hobby) VALUES ('advil', 'posthacking');
+QUERY: INSERT INTO equipment_r (name, hobby) VALUES ('peet''s coffee', 'posthacking');
+QUERY: INSERT INTO equipment_r (name, hobby) VALUES ('hightops', 'basketball');
+QUERY: INSERT INTO equipment_r (name, hobby) VALUES ('guts', 'skywalking');
+QUERY: COPY road FROM '/home2/jolly/pg95-1.01/src/test/regress/data/streets.data';
+QUERY: COPY real_city FROM '/home2/jolly/pg95-1.01/src/test/regress/data/real_city.data';
+QUERY: SELECT *
+   INTO TABLE ramp
+   FROM road
+   WHERE name ~ '.*Ramp';
+QUERY: INSERT INTO ihighway
+   SELECT *
+   FROM road
+   WHERE name ~ 'I- .*';
+QUERY: INSERT INTO shighway
+   SELECT *
+   FROM road
+   WHERE name ~ 'State Hwy.*';
+QUERY: UPDATE shighway
+   SET surface = 'asphalt'
+INSERT INTO a_star (class, a) VALUES ('a', 1);
+QUERY: INSERT INTO a_star (class, a) VALUES ('a', 2);
+QUERY: INSERT INTO a_star (class) VALUES ('a');
+QUERY: INSERT INTO b_star (class, a, b) VALUES ('b', 3, 'mumble'::text);
+QUERY: INSERT INTO b_star (class, a) VALUES ('b', 4);
+QUERY: INSERT INTO b_star (class, b) VALUES ('b', 'bumble'::text);
+QUERY: INSERT INTO b_star (class) VALUES ('b');
+QUERY: INSERT INTO c_star (class, a, c) VALUES ('c', 5, 'hi mom'::char16);
+QUERY: INSERT INTO c_star (class, a) VALUES ('c', 6);
+QUERY: INSERT INTO c_star (class, c) VALUES ('c', 'hi paul'::char16);
+QUERY: INSERT INTO c_star (class) VALUES ('c');
+QUERY: INSERT INTO d_star (class, a, b, c, d)
+   VALUES ('d', 7, 'grumble'::text, 'hi sunita'::char16, '0.0'::float8);
+QUERY: INSERT INTO d_star (class, a, b, c)
+   VALUES ('d', 8, 'stumble'::text, 'hi koko'::char16);
+QUERY: INSERT INTO d_star (class, a, b, d)
+   VALUES ('d', 9, 'rumble'::text, '1.1'::float8);
+QUERY: INSERT INTO d_star (class, a, c, d)
+   VALUES ('d', 10, 'hi kristin'::char16, '10.01'::float8);
+QUERY: INSERT INTO d_star (class, b, c, d)
+   VALUES ('d', 'crumble'::text, 'hi boris'::char16, '100.001'::float8);
+QUERY: INSERT INTO d_star (class, a, b)
+   VALUES ('d', 11, 'fumble'::text)
+INSERT INTO d_star (class, a, c)
+   VALUES ('d', 12, 'hi avi'::char16);
+QUERY: INSERT INTO d_star (class, a, d)
+   VALUES ('d', 13, '1000.0001'::float8);
+QUERY: INSERT INTO d_star (class, b, c)
+   VALUES ('d', 'tumble'::text, 'hi andrew'::char16);
+QUERY: INSERT INTO d_star (class, b, d)
+   VALUES ('d', 'humble'::text, '10000.00001'::float8);
+QUERY: INSERT INTO d_star (class, c, d)
+   VALUES ('d', 'hi ginger'::char16, '100000.000001'::float8);
+QUERY: INSERT INTO d_star (class, a) VALUES ('d', 14);
+QUERY: INSERT INTO d_star (class, b) VALUES ('d', 'jumble'::text);
+QUERY: INSERT INTO d_star (class, c) VALUES ('d', 'hi jolly'::char16);
+QUERY: INSERT INTO d_star (class, d) VALUES ('d', '1000000.0000001'::float8);
+QUERY: INSERT INTO d_star (class) VALUES ('d');
+QUERY: INSERT INTO e_star (class, a, c, e)
+   VALUES ('e', 15, 'hi carol'::char16, '-1'::int2);
+QUERY: INSERT INTO e_star (class, a, c)
+   VALUES ('e', 16, 'hi bob'::char16);
+QUERY: INSERT INTO e_star (class, a, e)
+   VALUES ('e', 17, '-2'::int2);
+QUERY: INSERT INTO e_star (class, c, e)
+   VALUES ('e', 'hi michelle'::char16, '-3'::int2);
+QUERY: INSERT INTO e_star (class, a)
+   VALUES ('e', 18);
+QUERY: INSERT INTO e_star (class, c)
+   VALUES ('e', 'hi elisa'::char16);
+QUERY: INSERT INTO e_star (class, e)
+   VALUES ('e', '-4'::int2);
+QUERY: INSERT INTO f_star (class, a, c, e, f)
+   VALUES ('f', 19, 'hi claire'::char16, '-5'::int2, '(1,2,3,4)'::polygon);
+QUERY: INSERT INTO f_star (class, a, c, e)
+   VALUES ('f', 20, 'hi mike'::char16, '-6'::int2);
+QUERY: INSERT INTO f_star (class, a, c, f)
+   VALUES ('f', 21, 'hi marcel'::char16, '(11,22,33,44,55,66)'::polygon);
+QUERY: INSERT INTO f_star (class, a, e, f)
+   VALUES ('f', 22, '-7'::int2, '(111,222,333,444,555,666,777,888)'::polygon);
+QUERY: INSERT INTO f_star (class, c, e, f)
+   VALUES ('f', 'hi keith'::char16, '-8'::int2,
+          '(1111,2222,3333,4444)'::polygon);
+QUERY: INSERT INTO f_star (class, a, c)
+   VALUES ('f', 24, 'hi marc'::char16);
+QUERY: INSERT INTO f_star (class, a, e)
+   VALUES ('f', 25, '-9'::int2);
+QUERY: INSERT INTO f_star (class, a, f)
+   VALUES ('f', 26, '(11111,22222,33333,44444)'::polygon);
+QUERY: INSERT INTO f_star (class, c, e)
+   VALUES ('f', 'hi allison'::char16, '-10'::int2);
+QUERY: INSERT INTO f_star (class, c, f)
+   VALUES ('f', 'hi jeff'::char16,
+           '(111111,222222,333333,444444)'::polygon);
+QUERY: INSERT INTO f_star (class, e, f)
+   VALUES ('f', '-11'::int2, '(1111111,2222222,3333333,4444444)'::polygon);
+QUERY: INSERT INTO f_star (class, a) VALUES ('f', 27);
+QUERY: INSERT INTO f_star (class, c) VALUES ('f', 'hi carl'::char16);
+QUERY: INSERT INTO f_star (class, e) VALUES ('f', '-12'::int2);
+QUERY: INSERT INTO f_star (class, f)
+   VALUES ('f', '(11111111,22222222,33333333,44444444)'::polygon);
+QUERY: INSERT INTO f_star (class) VALUES ('f');
+QUERY: COPY hash_i4_heap FROM '/home2/jolly/pg95-1.01/src/test/regress/data/hash.data';
+QUERY: COPY hash_c16_heap FROM '/home2/jolly/pg95-1.01/src/test/regress/data/hash.data';
+QUERY: COPY hash_txt_heap FROM '/home2/jolly/pg95-1.01/src/test/regress/data/hash.data';
+QUERY: COPY hash_f8_heap FROM '/home2/jolly/pg95-1.01/src/test/regress/data/hash.data';
+QUERY: COPY bt_i4_heap FROM '/home2/jolly/pg95-1.01/src/test/regress/data/desc.data';
+QUERY: COPY bt_c16_heap FROM '/home2/jolly/pg95-1.01/src/test/regress/data/hash.data';
+QUERY: COPY bt_txt_heap FROM '/home2/jolly/pg95-1.01/src/test/regress/data/desc.data';
+QUERY: COPY bt_f8_heap FROM '/home2/jolly/pg95-1.01/src/test/regress/data/hash.data';
+QUERY: INSERT INTO arrtest (a[5], b[2][1][2], c, d)
+   VALUES ('{1,2,3,4,5}', '{{{},{1,2}}}', '{}', '{}');
+QUERY: INSERT INTO arrtest (a, b[2][2][1], c, d, e)
+   VALUES ('{11,12,23}', '{{3,4},{4,5}}', '{"foobar"}',
+           '{{"elt1", "elt2"}}', '{"3.4", "6.7"}');
+QUERY: INSERT INTO arrtest (a, b[1][2][2], c, d[2][1])
+   VALUES ('{}', '{3,4}', '{foo,bar}', '{bar,foo}');
+QUERY: CREATE TABLE iportaltest (
+       i               int4,
+       d               float4,
+       p               polygon
+);
+QUERY: INSERT INTO iportaltest (i, d, p)
+   VALUES (1, 3.567, '(3.0,4.0,1.0,2.0)'::polygon);
+QUERY: INSERT INTO iportaltest (i, d, p)
+   VALUES (2, 89.05, '(4.0,3.0,2.0,1.0)'::polygon);
+QUERY: CREATE INDEX onek_unique1 ON onek USING btree(unique1 int4_ops);
+QUERY: CREATE INDEX onek_unique2 ON onek USING btree(unique2 int4_ops);
+QUERY: CREATE INDEX onek_hundred ON onek USING btree(hundred int4_ops);
+QUERY: CREATE INDEX onek_stringu1 ON onek USING btree(stringu1 char16_ops);
+QUERY: CREATE INDEX tenk1_unique1 ON tenk1 USING btree(unique1 int4_ops);
+QUERY: CREATE INDEX tenk1_unique2 ON tenk1 USING btree(unique2 int4_ops);
+QUERY: CREATE INDEX tenk1_hundred ON tenk1 USING btree(hundred int4_ops);
+QUERY: CREATE INDEX tenk2_unique1 ON tenk2 USING btree(unique1 int4_ops);
+QUERY: CREATE INDEX tenk2_unique2 ON tenk2 USING btree(unique2 int4_ops);
+QUERY: CREATE INDEX tenk2_hundred ON tenk2 USING btree(hundred int4_ops);
+QUERY: CREATE INDEX rix ON road USING btree (name text_ops);
+QUERY: CREATE INDEX iix ON ihighway USING btree (name text_ops);
+QUERY: CREATE INDEX six ON shighway USING btree (name text_ops);
+QUERY: CREATE INDEX bt_i4_index ON bt_i4_heap USING btree (seqno int4_ops);
+QUERY: CREATE INDEX bt_c16_index ON bt_c16_heap USING btree (seqno char16_ops);
+QUERY: CREATE INDEX bt_txt_index ON bt_txt_heap USING btree (seqno text_ops);
+QUERY: CREATE INDEX bt_f8_index ON bt_f8_heap USING btree (seqno float8_ops);
+QUERY: CREATE INDEX rect2ind ON fast_emp4000 USING rtree (home_base bigbox_ops);
+QUERY: CREATE INDEX hash_i4_index ON hash_i4_heap USING hash (random int4_ops);
+QUERY: CREATE INDEX hash_c16_index ON hash_c16_heap USING hash (random char16_ops);
+QUERY: CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
+QUERY: CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+QUERY: CREATE OPERATOR ## (
+   leftarg = path,
+   rightarg = path,
+   procedure = path_inter,
+   commutator = ##
+);
+QUERY: CREATE OPERATOR <% (
+   leftarg = point,
+   rightarg = circle,
+   procedure = pt_in_circle,
+   commutator = >=%
+);
+QUERY: CREATE OPERATOR @#@ (
+   rightarg = int4,            -- left unary
+   procedure = int4fac
+);
+QUERY: CREATE OPERATOR #@# (
+   leftarg = int4,             -- right unary
+   procedure = int4fac
+);
+QUERY: CREATE OPERATOR #%# (
+   leftarg = int4,             -- right unary
+   procedure = int4fac
+);
+QUERY: CREATE VIEW street AS
+   SELECT r.name, r.thepath, c.cname AS cname
+   FROM road r, real_city c
+   WHERE c.outline ## r.thepath;
+QUERY: CREATE VIEW iexit AS
+   SELECT ih.name, ih.thepath,
+       interpt_pp(ih.thepath, r.thepath) AS exit
+   FROM ihighway ih, ramp r
+   WHERE ih.thepath ## r.thepath;
+QUERY: CREATE VIEW toyemp AS
+   SELECT name, age, location, 12*salary AS annualsal
+   FROM emp;
+QUERY: CREATE AGGREGATE newavg (
+   sfunc1 = int4pl, basetype = int4, stype1 = int4,
+   sfunc2 = int4inc, stype2 = int4,
+   finalfunc = int4div,
+   initcond1 = '0', initcond2 = '0'
+);
+QUERY: CREATE AGGREGATE newsum (
+   sfunc1 = int4pl, basetype = int4, stype1 = int4,
+   initcond1 = '0'
+);
+QUERY: CREATE AGGREGATE newcnt (
+   sfunc2 = int4inc, basetype = int4, stype2 = int4,
+   initcond2 = '0'
+);
+QUERY: VACUUM;
+QUERY: SELECT relname, relhasindex
+   FROM pg_class
+   WHERE relhasindex
+   ORDER BY relname;
+relname        relhasindex  
+-------------- ------------ 
+bt_c16_heap    t            
+bt_f8_heap     t            
+bt_i4_heap     t            
+bt_txt_heap    t            
+fast_emp4000   t            
+hash_c16_heap  t            
+hash_f8_heap   t            
+hash_i4_heap   t            
+hash_txt_heap  t            
+ihighway       t            
+onek           t            
+pg_attribute   t            
+pg_class       t            
+pg_proc        t            
+pg_type        t            
+road           t            
+shighway       t            
+tenk1          t            
+tenk2          t            
+=============== running regression queries ... =================
+QUERY: SELECT 1 AS one;
+one  
+---- 
+1    
+QUERY: SELECT 't'::bool AS true;
+true  
+----- 
+t     
+QUERY: SELECT 'f'::bool AS false;
+false  
+------ 
+f      
+QUERY: SELECT 't'::bool or 'f'::bool AS true;
+true  
+----- 
+t     
+QUERY: SELECT 't'::bool and 'f'::bool AS false;
+false  
+------ 
+f      
+QUERY: SELECT not 'f'::bool AS true;
+true  
+----- 
+t     
+QUERY: SELECT 't'::bool = 'f'::bool AS false;
+false  
+------ 
+f      
+QUERY: SELECT 't'::bool <> 'f'::bool AS true;
+true  
+----- 
+t     
+QUERY: CREATE TABLE BOOLTBL1 (f1 bool);
+QUERY: INSERT INTO BOOLTBL1 (f1) VALUES ('t'::bool);
+QUERY: INSERT INTO BOOLTBL1 (f1) VALUES ('True'::bool);
+QUERY: INSERT INTO BOOLTBL1 (f1) VALUES ('true'::bool);
+QUERY: SELECT '' AS t_3, BOOLTBL1.*;
+t_3  f1  
+---- --- 
+     t   
+     t   
+     t   
+QUERY: SELECT '' AS t_3, BOOLTBL1.*
+   FROM BOOLTBL1
+   WHERE f1 = 'true'::bool;
+t_3  f1  
+---- --- 
+     t   
+     t   
+     t   
+QUERY: SELECT '' AS t_3, BOOLTBL1.*
+   FROM BOOLTBL1
+   WHERE f1 <> 'false'::bool;
+t_3  f1  
+---- --- 
+     t   
+     t   
+     t   
+QUERY: SELECT '' AS zero, BOOLTBL1.*
+   FROM BOOLTBL1
+   WHERE booleq('false'::bool, f1);
+zero  f1  
+----- --- 
+QUERY: INSERT INTO BOOLTBL1 (f1) VALUES ('f'::bool);
+QUERY: SELECT '' AS f_1, BOOLTBL1.*
+   FROM BOOLTBL1
+   WHERE f1 = 'false'::bool;
+f_1  f1  
+---- --- 
+     f   
+QUERY: CREATE TABLE BOOLTBL2 (f1 bool);
+QUERY: INSERT INTO BOOLTBL2 (f1) VALUES ('f'::bool);
+QUERY: INSERT INTO BOOLTBL2 (f1) VALUES ('false'::bool);
+QUERY: INSERT INTO BOOLTBL2 (f1) VALUES ('False'::bool);
+QUERY: INSERT INTO BOOLTBL2 (f1)
+   VALUES ('XXX'::bool);
+QUERY: SELECT '' AS f_4, BOOLTBL2.*;
+f_4  f1  
+---- --- 
+     f   
+     f   
+     f   
+     f   
+QUERY: SELECT '' AS tf_12, BOOLTBL1.*, BOOLTBL2.*
+   WHERE BOOLTBL2.f1 <> BOOLTBL1.f1;
+tf_12  f1  f1  
+------ --- --- 
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+QUERY: SELECT '' AS tf_12, BOOLTBL1.*, BOOLTBL2.*
+   WHERE boolne(BOOLTBL2.f1,BOOLTBL1.f1);
+tf_12  f1  f1  
+------ --- --- 
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+       t   f   
+QUERY: SELECT '' AS ff_4, BOOLTBL1.*, BOOLTBL2.*
+   WHERE BOOLTBL2.f1 = BOOLTBL1.f1 and BOOLTBL1.f1 = 'false'::bool;
+ff_4  f1  f1  
+----- --- --- 
+      f   f   
+      f   f   
+      f   f   
+      f   f   
+QUERY: SELECT '' AS tf_12_ff_4, BOOLTBL1.*, BOOLTBL2.*
+   WHERE BOOLTBL2.f1 = BOOLTBL1.f1 or BOOLTBL1.f1 = 'true'::bool;
+tf_12_ff_4  f1  f1  
+----------- --- --- 
+            t   f   
+            t   f   
+            t   f   
+            f   f   
+            t   f   
+            t   f   
+            t   f   
+            f   f   
+            t   f   
+            t   f   
+            t   f   
+            f   f   
+            t   f   
+            t   f   
+            t   f   
+            f   f   
+QUERY: CREATE TABLE ABSTIME_TBL (f1 abstime);
+QUERY: INSERT INTO ABSTIME_TBL (f1) VALUES ('Jan 14, 1973 03:14:21');
+QUERY: INSERT INTO ABSTIME_TBL (f1) VALUES ('Mon May  1 00:30:30 PDT 1995'::abstime);
+QUERY: INSERT INTO ABSTIME_TBL (f1) VALUES ('epoch'::abstime);
+QUERY: INSERT INTO ABSTIME_TBL (f1) VALUES ('current'::abstime);
+QUERY: INSERT INTO ABSTIME_TBL (f1) VALUES ('infinity'::abstime);
+QUERY: INSERT INTO ABSTIME_TBL (f1) VALUES ('-infinity'::abstime);
+QUERY: INSERT INTO ABSTIME_TBL (f1) VALUES ('May 10, 1943 23:59:12');
+QUERY: INSERT INTO ABSTIME_TBL (f1) VALUES ('Feb 35, 1946 10:00:00');
+QUERY: INSERT INTO ABSTIME_TBL (f1) VALUES ('Feb 28, 1984 25:08:10');
+QUERY: INSERT INTO ABSTIME_TBL (f1) VALUES ('bad date format');
+QUERY: INSERT INTO ABSTIME_TBL (f1) VALUES ('Jun 10, 1843');
+QUERY: CREATE TABLE RELTIME_TBL (f1 reltime);
+QUERY: INSERT INTO RELTIME_TBL (f1) VALUES ('@ 1 minute');
+QUERY: INSERT INTO RELTIME_TBL (f1) VALUES ('@ 5 hour');
+QUERY: INSERT INTO RELTIME_TBL (f1) VALUES ('@ 10 day');
+QUERY: INSERT INTO RELTIME_TBL (f1) VALUES ('@ 34 year');
+QUERY: INSERT INTO RELTIME_TBL (f1) VALUES ('@ 3 months');
+QUERY: INSERT INTO RELTIME_TBL (f1) VALUES ('@ 14 seconds ago');
+QUERY: INSERT INTO RELTIME_TBL (f1) VALUES ('badly formatted reltime');
+QUERY: INSERT INTO RELTIME_TBL (f1) VALUES ('@ 30 eons ago');
+QUERY: CREATE TABLE TINTERVAL_TBL (f1  tinterval);
+QUERY: INSERT INTO TINTERVAL_TBL (f1)
+   VALUES ('["-infinity" "infinity"]');
+QUERY: INSERT INTO TINTERVAL_TBL (f1)
+   VALUES ('["May 10, 1943 23:59:12" "Jan 14, 1973 03:14:21"]');
+QUERY: INSERT INTO TINTERVAL_TBL (f1)
+   VALUES ('["Sep 4, 1983 23:59:12" "Oct 4, 1983 23:59:12"]');
+QUERY: INSERT INTO TINTERVAL_TBL (f1)
+   VALUES ('["epoch" "Mon May  1 00:30:30 PDT 1995"]');
+QUERY: INSERT INTO TINTERVAL_TBL (f1)
+   VALUES ('["Feb 15 1990 12:15:03" "current"]');
+QUERY: INSERT INTO TINTERVAL_TBL (f1)
+   VALUES ('["bad time specifications" ""]');
+QUERY: INSERT INTO TINTERVAL_TBL (f1)
+   VALUES ('["" "infinity"]');
+QUERY: SELECT '' AS eleven, ABSTIME_TBL.*;
+eleven  f1                            
+------- ----------------------------- 
+        Sun Jan 14 03:14:21 1973 PST  
+        Mon May 01 00:30:30 1995 PDT  
+        epoch                         
+        current                       
+        infinity                      
+        -infinity                     
+        Mon May 10 23:59:12 1943 PWT  
+        Thu Mar 07 10:00:00 1946 PST  
+        Wed Dec 31 15:59:59 1969 PST  
+        Invalid Abstime               
+        Invalid Abstime               
+QUERY: SELECT '' AS eight, ABSTIME_TBL.*
+   WHERE ABSTIME_TBL.f1 < 'Jun 30, 2001'::abstime;
+eight  f1                            
+------ ----------------------------- 
+       Sun Jan 14 03:14:21 1973 PST  
+       Mon May 01 00:30:30 1995 PDT  
+       epoch                         
+       current                       
+       -infinity                     
+       Mon May 10 23:59:12 1943 PWT  
+       Thu Mar 07 10:00:00 1946 PST  
+       Wed Dec 31 15:59:59 1969 PST  
+QUERY: SELECT '' AS eight, ABSTIME_TBL.*
+   WHERE ABSTIME_TBL.f1 > '-infinity'::abstime;
+eight  f1                            
+------ ----------------------------- 
+       Sun Jan 14 03:14:21 1973 PST  
+       Mon May 01 00:30:30 1995 PDT  
+       epoch                         
+       current                       
+       infinity                      
+       Mon May 10 23:59:12 1943 PWT  
+       Thu Mar 07 10:00:00 1946 PST  
+       Wed Dec 31 15:59:59 1969 PST  
+QUERY: SELECT '' AS eight, ABSTIME_TBL.*
+   WHERE 'May 10, 1943 23:59:12'::abstime <> ABSTIME_TBL.f1;
+eight  f1                            
+------ ----------------------------- 
+       Sun Jan 14 03:14:21 1973 PST  
+       Mon May 01 00:30:30 1995 PDT  
+       epoch                         
+       current                       
+       infinity                      
+       -infinity                     
+       Thu Mar 07 10:00:00 1946 PST  
+       Wed Dec 31 15:59:59 1969 PST  
+QUERY: SELECT '' AS one, ABSTIME_TBL.*
+   WHERE 'current'::abstime = ABSTIME_TBL.f1;
+one  f1       
+---- -------- 
+     current  
+QUERY: SELECT '' AS five, ABSTIME_TBL.*
+   WHERE 'epoch'::abstime >= ABSTIME_TBL.f1;
+five  f1                            
+----- ----------------------------- 
+      epoch                         
+      -infinity                     
+      Mon May 10 23:59:12 1943 PWT  
+      Thu Mar 07 10:00:00 1946 PST  
+      Wed Dec 31 15:59:59 1969 PST  
+QUERY: SELECT '' AS six, ABSTIME_TBL.*
+   WHERE ABSTIME_TBL.f1 <= 'Jan 14, 1973 03:14:21'::abstime;
+six  f1                            
+---- ----------------------------- 
+     Sun Jan 14 03:14:21 1973 PST  
+     epoch                         
+     -infinity                     
+     Mon May 10 23:59:12 1943 PWT  
+     Thu Mar 07 10:00:00 1946 PST  
+     Wed Dec 31 15:59:59 1969 PST  
+QUERY: SELECT '' AS six, ABSTIME_TBL.*
+  WHERE ABSTIME_TBL.f1 <?>
+       '["Apr 1 1945 00:00:00" "Dec 30 1999 23:00:00"]'::tinterval;
+six  f1                            
+---- ----------------------------- 
+     Sun Jan 14 03:14:21 1973 PST  
+     Mon May 01 00:30:30 1995 PDT  
+     epoch                         
+     current                       
+     Thu Mar 07 10:00:00 1946 PST  
+     Wed Dec 31 15:59:59 1969 PST  
+QUERY: SELECT '' AS five, ABSTIME_TBL.*
+  WHERE  (ABSTIME_TBL.f1 + '@ 3 year'::reltime)     -- +3 years
+       < 'Jan 14 14:00:00 1977'::abstime;
+five  f1                            
+----- ----------------------------- 
+      Sun Jan 14 03:14:21 1973 PST  
+      epoch                         
+      Mon May 10 23:59:12 1943 PWT  
+      Thu Mar 07 10:00:00 1946 PST  
+      Wed Dec 31 15:59:59 1969 PST  
+QUERY: SELECT '' AS five, ABSTIME_TBL.*
+   WHERE  (ABSTIME_TBL.f1 + '@ 3 year ago'::reltime) -- -3 years
+       < 'Jan 14 14:00:00 1971'::abstime;
+five  f1                            
+----- ----------------------------- 
+      Sun Jan 14 03:14:21 1973 PST  
+      epoch                         
+      Mon May 10 23:59:12 1943 PWT  
+      Thu Mar 07 10:00:00 1946 PST  
+      Wed Dec 31 15:59:59 1969 PST  
+QUERY: SELECT '' AS five, ABSTIME_TBL.*
+   WHERE  (ABSTIME_TBL.f1 - '@ 3 year'::reltime)     -- -(+3) years
+       < 'Jan 14 14:00:00 1971'::abstime;
+five  f1                            
+----- ----------------------------- 
+      Sun Jan 14 03:14:21 1973 PST  
+      epoch                         
+      Mon May 10 23:59:12 1943 PWT  
+      Thu Mar 07 10:00:00 1946 PST  
+      Wed Dec 31 15:59:59 1969 PST  
+QUERY: SELECT '' AS five, ABSTIME_TBL.*
+   WHERE  (ABSTIME_TBL.f1 - '@ 3 year ago'::reltime) -- -(-3) years
+       < 'Jan 14 14:00:00 1977'::abstime;
+five  f1                            
+----- ----------------------------- 
+      Sun Jan 14 03:14:21 1973 PST  
+      epoch                         
+      Mon May 10 23:59:12 1943 PWT  
+      Thu Mar 07 10:00:00 1946 PST  
+      Wed Dec 31 15:59:59 1969 PST  
+QUERY: SELECT '' AS twenty, ABSTIME_TBL.*, RELTIME_TBL.*
+   WHERE (ABSTIME_TBL.f1 + RELTIME_TBL.f1)
+       < 'Jan 14 14:00:00 1971'::abstime;
+twenty  f1                            f1                
+------- ----------------------------- ----------------- 
+        epoch                         @ 1 minute        
+        Mon May 10 23:59:12 1943 PWT  @ 1 minute        
+        Thu Mar 07 10:00:00 1946 PST  @ 1 minute        
+        Wed Dec 31 15:59:59 1969 PST  @ 1 minute        
+        epoch                         @ 5 hours         
+        Mon May 10 23:59:12 1943 PWT  @ 5 hours         
+        Thu Mar 07 10:00:00 1946 PST  @ 5 hours         
+        Wed Dec 31 15:59:59 1969 PST  @ 5 hours         
+        epoch                         @ 10 days         
+        Mon May 10 23:59:12 1943 PWT  @ 10 days         
+        Thu Mar 07 10:00:00 1946 PST  @ 10 days         
+        Wed Dec 31 15:59:59 1969 PST  @ 10 days         
+        epoch                         @ 3 months        
+        Mon May 10 23:59:12 1943 PWT  @ 3 months        
+        Thu Mar 07 10:00:00 1946 PST  @ 3 months        
+        Wed Dec 31 15:59:59 1969 PST  @ 3 months        
+        epoch                         @ 14 seconds ago  
+        Mon May 10 23:59:12 1943 PWT  @ 14 seconds ago  
+        Thu Mar 07 10:00:00 1946 PST  @ 14 seconds ago  
+        Wed Dec 31 15:59:59 1969 PST  @ 14 seconds ago  
+QUERY: SELECT '' AS eight, RELTIME_TBL.*;
+eight  f1                 
+------ ------------------ 
+       @ 1 minute         
+       @ 5 hours          
+       @ 10 days          
+       @ 34 years         
+       @ 3 months         
+       @ 14 seconds ago   
+       Undefined RelTime  
+       Undefined RelTime  
+QUERY: SELECT '' AS five, RELTIME_TBL.*
+   WHERE RELTIME_TBL.f1 <> '@ 10 days'::reltime;
+five  f1                
+----- ----------------- 
+      @ 1 minute        
+      @ 5 hours         
+      @ 34 years        
+      @ 3 months        
+      @ 14 seconds ago  
+QUERY: SELECT '' AS three, RELTIME_TBL.*
+   WHERE RELTIME_TBL.f1 <= '@ 5 hours'::reltime;
+three  f1                
+------ ----------------- 
+       @ 1 minute        
+       @ 5 hours         
+       @ 14 seconds ago  
+QUERY: SELECT '' AS three, RELTIME_TBL.*
+   WHERE RELTIME_TBL.f1 < '@ 1 day'::reltime;
+three  f1                
+------ ----------------- 
+       @ 1 minute        
+       @ 5 hours         
+       @ 14 seconds ago  
+QUERY: SELECT '' AS one, RELTIME_TBL.*
+   WHERE RELTIME_TBL.f1 = '@ 34 years'::reltime;
+one  f1          
+---- ----------- 
+     @ 34 years  
+QUERY: SELECT '' AS two, RELTIME_TBL.*
+   WHERE RELTIME_TBL.f1 >= '@ 1 month'::reltime;
+two  f1          
+---- ----------- 
+     @ 34 years  
+     @ 3 months  
+QUERY: SELECT '' AS five, RELTIME_TBL.*
+   WHERE RELTIME_TBL.f1 > '@ 3 seconds ago'::reltime;
+five  f1          
+----- ----------- 
+      @ 1 minute  
+      @ 5 hours   
+      @ 10 days   
+      @ 34 years  
+      @ 3 months  
+QUERY: SELECT '' AS fifteen, r1.*, r2.*
+   FROM RELTIME_TBL r1, RELTIME_TBL r2
+   WHERE r1.f1 > r2.f1;
+fifteen  f1          f1                
+-------- ----------- ----------------- 
+         @ 5 hours   @ 1 minute        
+         @ 10 days   @ 1 minute        
+         @ 34 years  @ 1 minute        
+         @ 3 months  @ 1 minute        
+         @ 10 days   @ 5 hours         
+         @ 34 years  @ 5 hours         
+         @ 3 months  @ 5 hours         
+         @ 34 years  @ 10 days         
+         @ 3 months  @ 10 days         
+         @ 34 years  @ 3 months        
+         @ 1 minute  @ 14 seconds ago  
+         @ 5 hours   @ 14 seconds ago  
+         @ 10 days   @ 14 seconds ago  
+         @ 34 years  @ 14 seconds ago  
+         @ 3 months  @ 14 seconds ago  
+QUERY: SELECT '' AS seven, TINTERVAL_TBL.*;
+seven  f1                                                               
+------ ---------------------------------------------------------------- 
+       ['-infinity' 'infinity']                                         
+       ['Mon May 10 23:59:12 1943 PWT' 'Sun Jan 14 03:14:21 1973 PST']  
+       ['Sun Sep 04 23:59:12 1983 PDT' 'Tue Oct 04 23:59:12 1983 PDT']  
+       ['epoch' 'Mon May 01 00:30:30 1995 PDT']                         
+       ['Thu Feb 15 12:15:03 1990 PST' 'current']                       
+       ['Undefined Range']                                              
+       ['Undefined Range']                                              
+QUERY: SELECT '' AS one, t.*
+   FROM TINTERVAL_TBL t
+   WHERE t.f1 #= '@ 1 months';
+one  f1                                                               
+---- ---------------------------------------------------------------- 
+     ['Sun Sep 04 23:59:12 1983 PDT' 'Tue Oct 04 23:59:12 1983 PDT']  
+QUERY: SELECT '' AS three, t.*
+   FROM TINTERVAL_TBL t
+   WHERE t.f1 #<> '@ 1 months';
+three  f1                                                               
+------ ---------------------------------------------------------------- 
+       ['Mon May 10 23:59:12 1943 PWT' 'Sun Jan 14 03:14:21 1973 PST']  
+       ['epoch' 'Mon May 01 00:30:30 1995 PDT']                         
+       ['Thu Feb 15 12:15:03 1990 PST' 'current']                       
+QUERY: SELECT '' AS zero, t.*
+   FROM TINTERVAL_TBL t
+   WHERE t.f1 #< '@ 1 month';
+zero  f1  
+----- --- 
+QUERY: SELECT '' AS one, t.*
+   FROM TINTERVAL_TBL t
+   WHERE t.f1 #<= '@ 1 month';
+one  f1                                                               
+---- ---------------------------------------------------------------- 
+     ['Sun Sep 04 23:59:12 1983 PDT' 'Tue Oct 04 23:59:12 1983 PDT']  
+QUERY: SELECT '' AS three, t.*
+   FROM TINTERVAL_TBL t
+   WHERE t.f1 #> '@ 1 year';
+three  f1                                                               
+------ ---------------------------------------------------------------- 
+       ['Mon May 10 23:59:12 1943 PWT' 'Sun Jan 14 03:14:21 1973 PST']  
+       ['epoch' 'Mon May 01 00:30:30 1995 PDT']                         
+       ['Thu Feb 15 12:15:03 1990 PST' 'current']                       
+QUERY: SELECT '' AS three, t.*
+   FROM TINTERVAL_TBL t
+   WHERE t.f1 #>= '@ 3 years';
+three  f1                                                               
+------ ---------------------------------------------------------------- 
+       ['Mon May 10 23:59:12 1943 PWT' 'Sun Jan 14 03:14:21 1973 PST']  
+       ['epoch' 'Mon May 01 00:30:30 1995 PDT']                         
+       ['Thu Feb 15 12:15:03 1990 PST' 'current']                       
+QUERY: SELECT '' AS three, t1.*
+   FROM TINTERVAL_TBL t1
+   WHERE t1.f1 &&
+       '["Aug 15 14:23:19 1983" "Sep 16 14:23:19 1983"]'::tinterval;
+three  f1                                                               
+------ ---------------------------------------------------------------- 
+       ['-infinity' 'infinity']                                         
+       ['Sun Sep 04 23:59:12 1983 PDT' 'Tue Oct 04 23:59:12 1983 PDT']  
+       ['epoch' 'Mon May 01 00:30:30 1995 PDT']                         
+QUERY: SELECT '' AS five, t1.*, t2.*
+   FROM TINTERVAL_TBL t1, TINTERVAL_TBL t2
+   WHERE t1.f1 && t2.f1 and
+        t1.f1 = t2.f1;
+five  f1                                                               f1                                                               
+----- ---------------------------------------------------------------- ---------------------------------------------------------------- 
+      ['-infinity' 'infinity']                                         ['-infinity' 'infinity']                                         
+      ['Mon May 10 23:59:12 1943 PWT' 'Sun Jan 14 03:14:21 1973 PST']  ['Mon May 10 23:59:12 1943 PWT' 'Sun Jan 14 03:14:21 1973 PST']  
+      ['Sun Sep 04 23:59:12 1983 PDT' 'Tue Oct 04 23:59:12 1983 PDT']  ['Sun Sep 04 23:59:12 1983 PDT' 'Tue Oct 04 23:59:12 1983 PDT']  
+      ['epoch' 'Mon May 01 00:30:30 1995 PDT']                         ['epoch' 'Mon May 01 00:30:30 1995 PDT']                         
+      ['Thu Feb 15 12:15:03 1990 PST' 'current']                       ['Thu Feb 15 12:15:03 1990 PST' 'current']                       
+QUERY: SELECT '' AS fourteen, t1.*, t2.*
+   FROM TINTERVAL_TBL t1, TINTERVAL_TBL t2
+   WHERE t1.f1 && t2.f1 and
+       not t1.f1 = t2.f1;
+fourteen  f1                                                               f1                                                               
+--------- ---------------------------------------------------------------- ---------------------------------------------------------------- 
+          ['Mon May 10 23:59:12 1943 PWT' 'Sun Jan 14 03:14:21 1973 PST']  ['-infinity' 'infinity']                                         
+          ['Sun Sep 04 23:59:12 1983 PDT' 'Tue Oct 04 23:59:12 1983 PDT']  ['-infinity' 'infinity']                                         
+          ['epoch' 'Mon May 01 00:30:30 1995 PDT']                         ['-infinity' 'infinity']                                         
+          ['Thu Feb 15 12:15:03 1990 PST' 'current']                       ['-infinity' 'infinity']                                         
+          ['-infinity' 'infinity']                                         ['Mon May 10 23:59:12 1943 PWT' 'Sun Jan 14 03:14:21 1973 PST']  
+          ['epoch' 'Mon May 01 00:30:30 1995 PDT']                         ['Mon May 10 23:59:12 1943 PWT' 'Sun Jan 14 03:14:21 1973 PST']  
+          ['-infinity' 'infinity']                                         ['Sun Sep 04 23:59:12 1983 PDT' 'Tue Oct 04 23:59:12 1983 PDT']  
+          ['epoch' 'Mon May 01 00:30:30 1995 PDT']                         ['Sun Sep 04 23:59:12 1983 PDT' 'Tue Oct 04 23:59:12 1983 PDT']  
+          ['-infinity' 'infinity']                                         ['epoch' 'Mon May 01 00:30:30 1995 PDT']                         
+          ['Mon May 10 23:59:12 1943 PWT' 'Sun Jan 14 03:14:21 1973 PST']  ['epoch' 'Mon May 01 00:30:30 1995 PDT']                         
+          ['Sun Sep 04 23:59:12 1983 PDT' 'Tue Oct 04 23:59:12 1983 PDT']  ['epoch' 'Mon May 01 00:30:30 1995 PDT']                         
+          ['Thu Feb 15 12:15:03 1990 PST' 'current']                       ['epoch' 'Mon May 01 00:30:30 1995 PDT']                         
+          ['-infinity' 'infinity']                                         ['Thu Feb 15 12:15:03 1990 PST' 'current']                       
+          ['epoch' 'Mon May 01 00:30:30 1995 PDT']                         ['Thu Feb 15 12:15:03 1990 PST' 'current']                       
+QUERY: SELECT '' AS five, t1.*
+   FROM TINTERVAL_TBL t1
+   WHERE not t1.f1 <<
+       '["Aug 15 14:23:19 1980" "Sep 16 14:23:19 1990"]'::tinterval;
+five  f1                                                               
+----- ---------------------------------------------------------------- 
+      ['Mon May 10 23:59:12 1943 PWT' 'Sun Jan 14 03:14:21 1973 PST']  
+      ['Sun Sep 04 23:59:12 1983 PDT' 'Tue Oct 04 23:59:12 1983 PDT']  
+      ['Thu Feb 15 12:15:03 1990 PST' 'current']                       
+      ['Undefined Range']                                              
+      ['Undefined Range']                                              
+QUERY: SELECT '' AS three, t1.*
+   FROM TINTERVAL_TBL t1
+   WHERE t1.f1 &&
+       ('Aug 15 14:23:19 1983'::abstime <#>
+        'Sep 16 14:23:19 1983'::abstime);
+three  f1                                                               
+------ ---------------------------------------------------------------- 
+       ['-infinity' 'infinity']                                         
+       ['Sun Sep 04 23:59:12 1983 PDT' 'Tue Oct 04 23:59:12 1983 PDT']  
+       ['epoch' 'Mon May 01 00:30:30 1995 PDT']                         
+QUERY: CREATE TABLE BOX_TBL (f1 box);
+QUERY: INSERT INTO BOX_TBL (f1) VALUES ('(2.0,2.0,0.0,0.0)');
+QUERY: INSERT INTO BOX_TBL (f1) VALUES ('(1.0,1.0,3.0,3.0)');
+QUERY: INSERT INTO BOX_TBL (f1) VALUES ('(2.5, 2.5, 2.5,3.5)');
+QUERY: INSERT INTO BOX_TBL (f1) VALUES ('(3.0, 3.0,3.0,3.0)');
+QUERY: INSERT INTO BOX_TBL (f1) VALUES ('(2.3, 4.5)');
+WARN:Bad box external representation '(2.3, 4.5)'
+QUERY: INSERT INTO BOX_TBL (f1) VALUES ('asdfasdf(ad');
+WARN:Bad box external representation 'asdfasdf(ad'
+QUERY: SELECT '' AS four, BOX_TBL.*;
+four  f1                 
+----- ------------------ 
+      (2,2,0,0)          
+      (3,3,1,1)          
+      (2.5,3.5,2.5,2.5)  
+      (3,3,3,3)          
+QUERY: SELECT '' AS four, b.*, box_area(b.f1) as barea
+   FROM BOX_TBL b;
+four  f1                 barea  
+----- ------------------ ------ 
+      (2,2,0,0)          4      
+      (3,3,1,1)          4      
+      (2.5,3.5,2.5,2.5)  0      
+      (3,3,3,3)          0      
+QUERY: SELECT '' AS three, b.f1
+   FROM BOX_TBL b
+   WHERE b.f1 && '(2.5,2.5,1.0,1.0)'::box;
+three  f1                 
+------ ------------------ 
+       (2,2,0,0)          
+       (3,3,1,1)          
+       (2.5,3.5,2.5,2.5)  
+QUERY: SELECT '' AS two, b1.*
+   FROM BOX_TBL b1
+   WHERE b1.f1 &< '(2.0,2.0,2.5,2.5)'::box;
+two  f1                 
+---- ------------------ 
+     (2,2,0,0)          
+     (2.5,3.5,2.5,2.5)  
+QUERY: SELECT '' AS two, b1.*
+   FROM BOX_TBL b1
+   WHERE b1.f1 &> '(2.0,2.0,2.5,2.5)'::box;
+two  f1                 
+---- ------------------ 
+     (2.5,3.5,2.5,2.5)  
+     (3,3,3,3)          
+QUERY: SELECT '' AS two, b.f1
+   FROM BOX_TBL b
+   WHERE b.f1 << '(3.0,3.0,5.0,5.0)'::box;
+two  f1                 
+---- ------------------ 
+     (2,2,0,0)          
+     (2.5,3.5,2.5,2.5)  
+QUERY: SELECT '' AS four, b.f1
+   FROM BOX_TBL b
+   WHERE b.f1 <= '(3.0,3.0,5.0,5.0)'::box;
+four  f1                 
+----- ------------------ 
+      (2,2,0,0)          
+      (3,3,1,1)          
+      (2.5,3.5,2.5,2.5)  
+      (3,3,3,3)          
+QUERY: SELECT '' AS two, b.f1
+   FROM BOX_TBL b
+   WHERE b.f1 < '(3.0,3.0,5.0,5.0)'::box;
+two  f1                 
+---- ------------------ 
+     (2.5,3.5,2.5,2.5)  
+     (3,3,3,3)          
+QUERY: SELECT '' AS two, b.f1
+   FROM BOX_TBL b
+   WHERE b.f1 = '(3.0,3.0,5.0,5.0)'::box;
+two  f1         
+---- ---------- 
+     (2,2,0,0)  
+     (3,3,1,1)  
+QUERY: SELECT '' AS two, b.f1
+   FROM BOX_TBL b                              -- zero area
+   WHERE b.f1 > '(3.5,3.0,4.5,3.0)'::box;
+two  f1         
+---- ---------- 
+     (2,2,0,0)  
+     (3,3,1,1)  
+QUERY: SELECT '' AS four, b.f1
+   FROM BOX_TBL b                              -- zero area
+   WHERE b.f1 >= '(3.5,3.0,4.5,3.0)'::box;
+four  f1                 
+----- ------------------ 
+      (2,2,0,0)          
+      (3,3,1,1)          
+      (2.5,3.5,2.5,2.5)  
+      (3,3,3,3)          
+QUERY: SELECT '' AS two, b.f1
+   FROM BOX_TBL b
+   WHERE '(3.0,3.0,5.0,5.0)'::box >> b.f1;
+two  f1                 
+---- ------------------ 
+     (2,2,0,0)          
+     (2.5,3.5,2.5,2.5)  
+QUERY: SELECT '' AS three, b.f1
+   FROM BOX_TBL b
+   WHERE b.f1 @ '(0,0,3,3)'::box;
+three  f1         
+------ ---------- 
+       (2,2,0,0)  
+       (3,3,1,1)  
+       (3,3,3,3)  
+QUERY: SELECT '' AS three, b.f1
+   FROM BOX_TBL b
+   WHERE '(0,0,3,3)'::box ~ b.f1;
+three  f1         
+------ ---------- 
+       (2,2,0,0)  
+       (3,3,1,1)  
+       (3,3,3,3)  
+QUERY: SELECT '' AS one, b.f1
+   FROM BOX_TBL b
+   WHERE '(1,1,3,3)'::box ~= b.f1;
+one  f1         
+---- ---------- 
+     (3,3,1,1)  
+QUERY: SELECT '' AS four, @@(b1.f1) AS p
+   FROM BOX_TBL b1;
+four  p        
+----- -------- 
+      (1,1)    
+      (2,2)    
+      (2.5,3)  
+      (3,3)    
+QUERY: SELECT '' AS one, b1.*, b2.*
+   FROM BOX_TBL b1, BOX_TBL b2
+   WHERE b1.f1 ~ b2.f1 and not b1.f1 ~= b2.f1;
+one  f1         f1         
+---- ---------- ---------- 
+     (3,3,1,1)  (3,3,3,3)  
+QUERY: CREATE TABLE CHAR_TBL(f1 char);
+QUERY: INSERT INTO CHAR_TBL (f1) VALUES ('a');
+QUERY: INSERT INTO CHAR_TBL (f1) VALUES ('A');
+QUERY: INSERT INTO CHAR_TBL (f1) VALUES ('1');
+QUERY: INSERT INTO CHAR_TBL (f1) VALUES (2);
+QUERY: INSERT INTO CHAR_TBL (f1) VALUES ('3');
+QUERY: INSERT INTO CHAR_TBL (f1) VALUES ('');
+QUERY: INSERT INTO CHAR_TBL (f1) VALUES ('cd');
+QUERY: SELECT '' AS seven, CHAR_TBL.*;
+seven  f1  
+------ --- 
+       a   
+       A   
+       1   
+       2   
+       3   
+           
+       c   
+QUERY: SELECT '' AS six, c.*
+   FROM CHAR_TBL c
+   WHERE c.f1 <> 'a';
+six  f1  
+---- --- 
+     A   
+     1   
+     2   
+     3   
+         
+     c   
+QUERY: SELECT '' AS one, c.*
+   FROM CHAR_TBL c
+   WHERE c.f1 = 'a';
+one  f1  
+---- --- 
+     a   
+QUERY: SELECT '' AS five, c.*
+   FROM CHAR_TBL c
+   WHERE c.f1 < 'a';
+five  f1  
+----- --- 
+      A   
+      1   
+      2   
+      3   
+          
+QUERY: SELECT '' AS six, c.*
+   FROM CHAR_TBL c
+   WHERE c.f1 <= 'a';
+six  f1  
+---- --- 
+     a   
+     A   
+     1   
+     2   
+     3   
+         
+QUERY: SELECT '' AS one, c.*
+   FROM CHAR_TBL c
+   WHERE c.f1 > 'a';
+one  f1  
+---- --- 
+     c   
+QUERY: SELECT '' AS two, c.*
+   FROM CHAR_TBL c
+   WHERE c.f1 >= 'a';
+two  f1  
+---- --- 
+     a   
+     c   
+QUERY: CREATE TABLE CHAR2_TBL(f1 char2);
+QUERY: INSERT INTO CHAR2_TBL (f1) VALUES ('AB');
+QUERY: INSERT INTO CHAR2_TBL (f1) VALUES ('ab');
+QUERY: INSERT INTO CHAR2_TBL (f1) VALUES ('ZY');
+QUERY: INSERT INTO CHAR2_TBL (f1) VALUES ('34');
+QUERY: INSERT INTO CHAR2_TBL (f1) VALUES ('d');
+QUERY: INSERT INTO CHAR2_TBL (f1) VALUES ('');
+QUERY: INSERT INTO CHAR2_TBL (f1) VALUES ('12345');
+QUERY: SELECT '' AS seven, CHAR2_TBL.*;
+seven  f1  
+------ --- 
+       AB  
+       ab  
+       ZY  
+       34  
+       d   
+           
+       12  
+QUERY: SELECT '' AS six, c.f1 FROM CHAR2_TBL c WHERE c.f1 <> 'AB';
+six  f1  
+---- --- 
+     ab  
+     ZY  
+     34  
+     d   
+         
+     12  
+QUERY: SELECT '' AS one, c.f1 FROM CHAR2_TBL c WHERE c.f1 = 'AB';
+one  f1  
+---- --- 
+     AB  
+QUERY: SELECT '' AS three, c.f1 FROM CHAR2_TBL c WHERE c.f1 < 'AB';
+three  f1  
+------ --- 
+       34  
+           
+       12  
+QUERY: SELECT '' AS four, c.f1 FROM CHAR2_TBL c WHERE c.f1 <= 'AB';
+four  f1  
+----- --- 
+      AB  
+      34  
+          
+      12  
+QUERY: SELECT '' AS three, c.f1 FROM CHAR2_TBL c WHERE c.f1 > 'AB';
+three  f1  
+------ --- 
+       ab  
+       ZY  
+       d   
+QUERY: SELECT '' AS four, c.f1 FROM CHAR2_TBL c WHERE c.f1 >= 'AB';
+four  f1  
+----- --- 
+      AB  
+      ab  
+      ZY  
+      d   
+QUERY: SELECT '' AS seven, c.f1 FROM CHAR2_TBL c WHERE c.f1 ~ '.*';
+seven  f1  
+------ --- 
+       AB  
+       ab  
+       ZY  
+       34  
+       d   
+           
+       12  
+QUERY: SELECT '' AS zero, c.f1 FROM CHAR2_TBL c WHERE c.f1 !~ '.*';
+zero  f1  
+----- --- 
+QUERY: SELECT '' AS one, c.f1 FROM CHAR2_TBL c WHERE c.f1 ~ '34';
+one  f1  
+---- --- 
+     34  
+QUERY: SELECT '' AS one, c.f1 FROM CHAR2_TBL c WHERE c.f1 ~ '3.*';
+one  f1  
+---- --- 
+     34  
+QUERY: CREATE TABLE CHAR4_TBL (f1  char4);
+QUERY: INSERT INTO CHAR4_TBL(f1) VALUES ('ABCD');
+QUERY: INSERT INTO CHAR4_TBL(f1) VALUES ('abcd');
+QUERY: INSERT INTO CHAR4_TBL(f1) VALUES ('ZYWZ');
+QUERY: INSERT INTO CHAR4_TBL(f1) VALUES ('343f');
+QUERY: INSERT INTO CHAR4_TBL(f1) VALUES ('d34a');
+QUERY: INSERT INTO CHAR4_TBL(f1) VALUES ('');
+QUERY: INSERT INTO CHAR4_TBL(f1) VALUES ('12345678');
+QUERY: SELECT '' AS seven, CHAR4_TBL.*;
+seven  f1    
+------ ----- 
+       ABCD  
+       abcd  
+       ZYWZ  
+       343f  
+       d34a  
+             
+       1234  
+QUERY: SELECT '' AS six, c.f1 FROM CHAR4_TBL c WHERE c.f1 <> 'ABCD';
+six  f1    
+---- ----- 
+     abcd  
+     ZYWZ  
+     343f  
+     d34a  
+           
+     1234  
+QUERY: SELECT '' AS one, c.f1 FROM CHAR4_TBL c WHERE c.f1 = 'ABCD';
+one  f1    
+---- ----- 
+     ABCD  
+QUERY: SELECT '' AS three, c.f1 FROM CHAR4_TBL c WHERE c.f1 < 'ABCD';
+three  f1    
+------ ----- 
+       343f  
+             
+       1234  
+QUERY: SELECT '' AS four, c.f1 FROM CHAR4_TBL c WHERE c.f1 <= 'ABCD';
+four  f1    
+----- ----- 
+      ABCD  
+      343f  
+            
+      1234  
+QUERY: SELECT '' AS three, c.f1 FROM CHAR4_TBL c WHERE c.f1 > 'ABCD';
+three  f1    
+------ ----- 
+       abcd  
+       ZYWZ  
+       d34a  
+QUERY: SELECT '' AS four, c.f1 FROM CHAR4_TBL c WHERE c.f1 >= 'ABCD';
+four  f1    
+----- ----- 
+      ABCD  
+      abcd  
+      ZYWZ  
+      d34a  
+QUERY: SELECT '' AS seven, c.f1 FROM CHAR4_TBL c WHERE c.f1 ~ '.*';
+seven  f1    
+------ ----- 
+       ABCD  
+       abcd  
+       ZYWZ  
+       343f  
+       d34a  
+             
+       1234  
+QUERY: SELECT '' AS zero, c.f1 FROM CHAR4_TBL c WHERE c.f1 !~ '.*';
+zero  f1  
+----- --- 
+QUERY: SELECT '' AS three, c.f1 FROM CHAR4_TBL c WHERE c.f1 ~ '.*34.*';
+three  f1    
+------ ----- 
+       343f  
+       d34a  
+       1234  
+QUERY: CREATE TABLE CHAR8_TBL(f1 char8);
+QUERY: INSERT INTO CHAR8_TBL(f1) VALUES ('ABCDEFGH');
+QUERY: INSERT INTO CHAR8_TBL(f1) VALUES ('abcdefgh');
+QUERY: INSERT INTO CHAR8_TBL(f1) VALUES ('ZYWZ410-');
+QUERY: INSERT INTO CHAR8_TBL(f1) VALUES ('343f%2a');
+QUERY: INSERT INTO CHAR8_TBL(f1) VALUES ('d34aas');
+QUERY: INSERT INTO CHAR8_TBL(f1) VALUES ('');
+QUERY: INSERT INTO CHAR8_TBL(f1) VALUES ('1234567890');
+QUERY: SELECT '' AS seven, CHAR8_TBL.*;
+seven  f1        
+------ --------- 
+       ABCDEFGH  
+       abcdefgh  
+       ZYWZ410-  
+       343f%2a   
+       d34aas    
+                 
+       12345678  
+QUERY: SELECT '' AS six, c.f1 FROM CHAR8_TBL c WHERE c.f1 <> 'ABCDEFGH';
+six  f1        
+---- --------- 
+     abcdefgh  
+     ZYWZ410-  
+     343f%2a   
+     d34aas    
+               
+     12345678  
+QUERY: SELECT '' AS one, c.f1 FROM CHAR8_TBL c WHERE c.f1 = 'ABCDEFGH';
+one  f1        
+---- --------- 
+     ABCDEFGH  
+QUERY: SELECT '' AS three, c.f1 FROM CHAR8_TBL c WHERE c.f1 < 'ABCDEFGH';
+three  f1        
+------ --------- 
+       343f%2a   
+                 
+       12345678  
+QUERY: SELECT '' AS four, c.f1 FROM CHAR8_TBL c WHERE c.f1 <= 'ABCDEFGH';
+four  f1        
+----- --------- 
+      ABCDEFGH  
+      343f%2a   
+                
+      12345678  
+QUERY: SELECT '' AS three, c.f1 FROM CHAR8_TBL c WHERE c.f1 > 'ABCDEFGH';
+three  f1        
+------ --------- 
+       abcdefgh  
+       ZYWZ410-  
+       d34aas    
+QUERY: SELECT '' AS four, c.f1 FROM CHAR8_TBL c WHERE c.f1 >= 'ABCDEFGH';
+four  f1        
+----- --------- 
+      ABCDEFGH  
+      abcdefgh  
+      ZYWZ410-  
+      d34aas    
+QUERY: SELECT '' AS seven, c.f1 FROM CHAR8_TBL c WHERE c.f1 ~ '.*';
+seven  f1        
+------ --------- 
+       ABCDEFGH  
+       abcdefgh  
+       ZYWZ410-  
+       343f%2a   
+       d34aas    
+                 
+       12345678  
+QUERY: SELECT '' AS zero, c.f1 FROM CHAR8_TBL c WHERE c.f1 !~ '.*';
+zero  f1  
+----- --- 
+QUERY: SELECT '' AS four, c.f1 FROM CHAR8_TBL c WHERE c.f1 ~ '[0-9]';
+four  f1        
+----- --------- 
+      ZYWZ410-  
+      343f%2a   
+      d34aas    
+      12345678  
+QUERY: SELECT '' AS three, c.f1 FROM CHAR8_TBL c WHERE c.f1 ~ '.*34.*';
+three  f1        
+------ --------- 
+       343f%2a   
+       d34aas    
+       12345678  
+QUERY: CREATE TABLE CHAR16_TBL(f1 char16);
+QUERY: INSERT INTO CHAR16_TBL(f1) VALUES ('ABCDEFGHIJKLMNOP');
+QUERY: INSERT INTO CHAR16_TBL(f1) VALUES ('abcdefghijklmnop');
+QUERY: INSERT INTO CHAR16_TBL(f1) VALUES ('asdfghjkl;');
+QUERY: INSERT INTO CHAR16_TBL(f1) VALUES ('343f%2a');
+QUERY: INSERT INTO CHAR16_TBL(f1) VALUES ('d34aaasdf');
+QUERY: INSERT INTO CHAR16_TBL(f1) VALUES ('');
+QUERY: INSERT INTO CHAR16_TBL(f1) VALUES ('1234567890ABCDEFGHIJKLMNOPQRSTUV');
+QUERY: SELECT '' AS seven, CHAR16_TBL.*;
+seven  f1                
+------ ----------------- 
+       ABCDEFGHIJKLMNOP  
+       abcdefghijklmnop  
+       asdfghjkl;        
+       343f%2a           
+       d34aaasdf         
+                         
+       1234567890ABCDEF  
+QUERY: SELECT '' AS six, c.f1 FROM CHAR16_TBL c WHERE c.f1 <> 'ABCDEFGHIJKLMNOP';
+six  f1                
+---- ----------------- 
+     abcdefghijklmnop  
+     asdfghjkl;        
+     343f%2a           
+     d34aaasdf         
+                       
+     1234567890ABCDEF  
+QUERY: SELECT '' AS one, c.f1 FROM CHAR16_TBL c WHERE c.f1 = 'ABCDEFGHIJKLMNOP';
+one  f1                
+---- ----------------- 
+     ABCDEFGHIJKLMNOP  
+QUERY: SELECT '' AS three, c.f1 FROM CHAR16_TBL c WHERE c.f1 < 'ABCDEFGHIJKLMNOP';
+three  f1                
+------ ----------------- 
+       343f%2a           
+                         
+       1234567890ABCDEF  
+QUERY: SELECT '' AS four, c.f1 FROM CHAR16_TBL c WHERE c.f1 <= 'ABCDEFGHIJKLMNOP';
+four  f1                
+----- ----------------- 
+      ABCDEFGHIJKLMNOP  
+      343f%2a           
+                        
+      1234567890ABCDEF  
+QUERY: SELECT '' AS three, c.f1 FROM CHAR16_TBL c WHERE c.f1 > 'ABCDEFGHIJKLMNOP';
+three  f1                
+------ ----------------- 
+       abcdefghijklmnop  
+       asdfghjkl;        
+       d34aaasdf         
+QUERY: SELECT '' AS four, c.f1 FROM CHAR16_TBL c WHERE c.f1 >= 'ABCDEFGHIJKLMNOP';
+four  f1                
+----- ----------------- 
+      ABCDEFGHIJKLMNOP  
+      abcdefghijklmnop  
+      asdfghjkl;        
+      d34aaasdf         
+QUERY: SELECT '' AS seven, c.f1 FROM CHAR16_TBL c WHERE c.f1 ~ '.*';
+seven  f1                
+------ ----------------- 
+       ABCDEFGHIJKLMNOP  
+       abcdefghijklmnop  
+       asdfghjkl;        
+       343f%2a           
+       d34aaasdf         
+                         
+       1234567890ABCDEF  
+QUERY: SELECT '' AS zero, c.f1 FROM CHAR16_TBL c WHERE c.f1 !~ '.*';
+zero  f1  
+----- --- 
+QUERY: SELECT '' AS three, c.f1 FROM CHAR16_TBL c WHERE c.f1 ~ '[0-9]';
+three  f1                
+------ ----------------- 
+       343f%2a           
+       d34aaasdf         
+       1234567890ABCDEF  
+QUERY: SELECT '' AS two, c.f1 FROM CHAR16_TBL c WHERE c.f1 ~ '.*asdf.*';
+two  f1          
+---- ----------- 
+     asdfghjkl;  
+     d34aaasdf   
+QUERY: CREATE TABLE FLOAT4_TBL (f1  float4);
+QUERY: INSERT INTO FLOAT4_TBL(f1) VALUES ('0.0');
+QUERY: INSERT INTO FLOAT4_TBL(f1) VALUES ('1004.30');
+QUERY: INSERT INTO FLOAT4_TBL(f1) VALUES ('-34.84');
+QUERY: INSERT INTO FLOAT4_TBL(f1) VALUES ('1.2345678901234e+20');
+QUERY: INSERT INTO FLOAT4_TBL(f1) VALUES ('1.2345678901234e-20');
+QUERY: INSERT INTO FLOAT4_TBL(f1) VALUES ('10e40');
+WARN:  Bad float4 input format -- overflow
+
+QUERY: INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e40');
+WARN:  Bad float4 input format -- overflow
+
+QUERY: INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-40');
+WARN:  Bad float4 input format -- underflow
+
+QUERY: INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-40');
+WARN:  Bad float4 input format -- underflow
+
+QUERY: SELECT '' AS five, FLOAT4_TBL.*;
+five  f1           
+----- ------------ 
+      0            
+      1004.3       
+      -34.84       
+      1.23457e+20  
+      1.23457e-20  
+QUERY: SELECT '' AS four, f.* FROM FLOAT4_TBL f WHERE f.f1 <> '1004.3';
+four  f1           
+----- ------------ 
+      0            
+      -34.84       
+      1.23457e+20  
+      1.23457e-20  
+QUERY: SELECT '' AS one, f.* FROM FLOAT4_TBL f WHERE f.f1 = '1004.3';
+one  f1      
+---- ------- 
+     1004.3  
+QUERY: SELECT '' AS three, f.* FROM FLOAT4_TBL f WHERE '1004.3' > f.f1;
+three  f1           
+------ ------------ 
+       0            
+       -34.84       
+       1.23457e-20  
+QUERY: SELECT '' AS three, f.* FROM FLOAT4_TBL f WHERE  f.f1 < '1004.3';
+three  f1           
+------ ------------ 
+       0            
+       -34.84       
+       1.23457e-20  
+QUERY: SELECT '' AS four, f.* FROM FLOAT4_TBL f WHERE '1004.3' >= f.f1;
+four  f1           
+----- ------------ 
+      0            
+      1004.3       
+      -34.84       
+      1.23457e-20  
+QUERY: SELECT '' AS four, f.* FROM FLOAT4_TBL f WHERE  f.f1 <= '1004.3';
+four  f1           
+----- ------------ 
+      0            
+      1004.3       
+      -34.84       
+      1.23457e-20  
+QUERY: SELECT '' AS three, f.f1, f.f1 * '-10' AS x FROM FLOAT4_TBL f
+   WHERE f.f1 > '0.0';
+three  f1           x             
+------ ------------ ------------- 
+       1004.3       -10043        
+       1.23457e+20  -1.23457e+21  
+       1.23457e-20  -1.23457e-19  
+QUERY: SELECT '' AS three, f.f1, f.f1 + '-10' AS x FROM FLOAT4_TBL f
+   WHERE f.f1 > '0.0'
+SELECT '' AS three, f.f1, f.f1 / '-10' AS x FROM FLOAT4_TBL f
+   WHERE f.f1 > '0.0';
+three  f1           x            
+------ ------------ ------------ 
+       1004.3       994.3        
+       1.23457e+20  1.23457e+20  
+       1.23457e-20  -10          
+QUERY: SELECT '' AS three, f.f1, f.f1 - '-10' AS x FROM FLOAT4_TBL f
+   WHERE f.f1 > '0.0';
+three  f1           x             
+------ ------------ ------------- 
+       1004.3       -100.43       
+       1.23457e+20  -1.23457e+19  
+       1.23457e-20  -1.23457e-21  
+QUERY: SELECT '' AS bad, f.f1 / '0.0' from FLOAT4_TBL f;
+three  f1           x            
+------ ------------ ------------ 
+       1004.3       1014.3       
+       1.23457e+20  1.23457e+20  
+       1.23457e-20  10           
+QUERY: SELECT '' AS five, FLOAT4_TBL.*;
+WARN:float4div:  divide by 0.0 error
+QUERY: SELECT '' AS five, f.f1, @f.f1 AS abs_f1 FROM FLOAT4_TBL f;
+five  f1           
+----- ------------ 
+      0            
+      1004.3       
+      -34.84       
+      1.23457e+20  
+      1.23457e-20  
+QUERY: UPDATE FLOAT4_TBL
+   SET f1 = FLOAT4_TBL.f1 * '-1'
+   WHERE FLOAT4_TBL.f1 > '0.0';
+five  f1           abs_f1       
+----- ------------ ------------ 
+      0            0            
+      1004.3       1004.3       
+      -34.84       34.84        
+      1.23457e+20  1.23457e+20  
+      1.23457e-20  1.23457e-20  
+QUERY: SELECT '' AS five, FLOAT4_TBL.*;
+QUERY: CREATE TABLE FLOAT8_TBL(f1 float8);
+QUERY: INSERT INTO FLOAT8_TBL(f1) VALUES ('0.0');
+QUERY: INSERT INTO FLOAT8_TBL(f1) VALUES ('1004.30');
+QUERY: INSERT INTO FLOAT8_TBL(f1) VALUES ('-34.84');
+QUERY: INSERT INTO FLOAT8_TBL(f1) VALUES ('1.2345678901234e+200');
+QUERY: INSERT INTO FLOAT8_TBL(f1) VALUES ('1.2345678901234e-200');
+QUERY: INSERT INTO FLOAT8_TBL(f1) VALUES ('10e400');
+WARN:  Bad float8 input format
+
+QUERY: INSERT INTO FLOAT8_TBL(f1) VALUES ('-10e400');
+WARN:  Bad float8 input format
+
+QUERY: INSERT INTO FLOAT8_TBL(f1) VALUES ('10e-400');
+WARN:  Bad float8 input format
+
+QUERY: INSERT INTO FLOAT8_TBL(f1) VALUES ('-10e-400');
+WARN:  Bad float8 input format
+
+QUERY: SELECT '' AS five, FLOAT8_TBL.*;
+five  f1                    
+----- --------------------- 
+      0                     
+      1004.3                
+      -34.84                
+      1.2345678901234e+200  
+      1.2345678901234e-200  
+QUERY: SELECT '' AS four, f.* FROM FLOAT8_TBL f WHERE f.f1 <> '1004.3';
+four  f1                    
+----- --------------------- 
+      0                     
+      -34.84                
+      1.2345678901234e+200  
+      1.2345678901234e-200  
+QUERY: SELECT '' AS one, f.* FROM FLOAT8_TBL f WHERE f.f1 = '1004.3';
+one  f1      
+---- ------- 
+     1004.3  
+QUERY: SELECT '' AS three, f.* FROM FLOAT8_TBL f WHERE '1004.3' > f.f1;
+three  f1                    
+------ --------------------- 
+       0                     
+       -34.84                
+       1.2345678901234e-200  
+QUERY: SELECT '' AS three, f.* FROM FLOAT8_TBL f WHERE  f.f1 < '1004.3';
+three  f1                    
+------ --------------------- 
+       0                     
+       -34.84                
+       1.2345678901234e-200  
+QUERY: SELECT '' AS four, f.* FROM FLOAT8_TBL f WHERE '1004.3' >= f.f1;
+four  f1                    
+----- --------------------- 
+      0                     
+      1004.3                
+      -34.84                
+      1.2345678901234e-200  
+QUERY: SELECT '' AS four, f.* FROM FLOAT8_TBL f WHERE  f.f1 <= '1004.3';
+four  f1                    
+----- --------------------- 
+      0                     
+      1004.3                
+      -34.84                
+      1.2345678901234e-200  
+QUERY: SELECT '' AS three, f.f1, f.f1 * '-10' AS x
+   FROM FLOAT8_TBL f
+   WHERE f.f1 > '0.0';
+three  f1                    x                      
+------ --------------------- ---------------------- 
+       1004.3                -10043                 
+       1.2345678901234e+200  -1.2345678901234e+201  
+       1.2345678901234e-200  -1.2345678901234e-199  
+QUERY: SELECT '' AS three, f.f1, f.f1 + '-10' AS x
+   FROM FLOAT8_TBL f
+   WHERE f.f1 > '0.0';
+three  f1                    x                     
+------ --------------------- --------------------- 
+       1004.3                994.3                 
+       1.2345678901234e+200  1.2345678901234e+200  
+       1.2345678901234e-200  -10                   
+QUERY: SELECT '' AS three, f.f1, f.f1 / '-10' AS x
+   FROM FLOAT8_TBL f
+   WHERE f.f1 > '0.0';
+three  f1                    x                      
+------ --------------------- ---------------------- 
+       1004.3                -100.43                
+       1.2345678901234e+200  -1.2345678901234e+199  
+       1.2345678901234e-200  -1.2345678901234e-201  
+QUERY: SELECT '' AS three, f.f1, f.f1 - '-10' AS x
+   FROM FLOAT8_TBL f
+   WHERE f.f1 > '0.0';
+three  f1                    x                     
+------ --------------------- --------------------- 
+       1004.3                1014.3                
+       1.2345678901234e+200  1.2345678901234e+200  
+       1.2345678901234e-200  10                    
+QUERY: SELECT '' AS one, f.f1 ^ '2.0' AS square_f1
+   FROM FLOAT8_TBL f where f.f1 = '1004.3';
+one  square_f1   
+---- ----------- 
+     1008618.49  
+QUERY: SELECT '' AS five, f.f1, @f.f1 AS abs_f1
+   FROM FLOAT8_TBL f;
+five  f1                    abs_f1                
+----- --------------------- --------------------- 
+      0                     0                     
+      1004.3                1004.3                
+      -34.84                34.84                 
+      1.2345678901234e+200  1.2345678901234e+200  
+      1.2345678901234e-200  1.2345678901234e-200  
+QUERY: SELECT '' AS five, f.f1, %f.f1 AS trunc_f1
+   FROM FLOAT8_TBL f;
+five  f1                    trunc_f1              
+----- --------------------- --------------------- 
+      0                     0                     
+      1004.3                1004                  
+      -34.84                -34                   
+      1.2345678901234e+200  1.2345678901234e+200  
+      1.2345678901234e-200  0                     
+QUERY: SELECT '' AS five, f.f1, f.f1 % AS round_f1
+   FROM FLOAT8_TBL f;
+five  f1                    round_f1              
+----- --------------------- --------------------- 
+      0                     0                     
+      1004.3                1004                  
+      -34.84                -35                   
+      1.2345678901234e+200  1.2345678901234e+200  
+      1.2345678901234e-200  0                     
+QUERY: SELECT '' AS three, f.f1, |/f.f1 AS sqrt_f1
+   FROM FLOAT8_TBL f
+   WHERE f.f1 > '0.0';
+three  f1                    sqrt_f1                
+------ --------------------- ---------------------- 
+       1004.3                31.6906926399535       
+       1.2345678901234e+200  1.11111110611109e+100  
+       1.2345678901234e-200  1.11111110611109e-100  
+QUERY: SELECT '' AS three, f.f1, : ( ; f.f1) AS exp_ln_f1
+   FROM FLOAT8_TBL f
+   WHERE f.f1 > '0.0';
+three  f1                    exp_ln_f1              
+------ --------------------- ---------------------- 
+       1004.3                1004.3                 
+       1.2345678901234e+200  1.23456789012338e+200  
+       1.2345678901234e-200  1.23456789012339e-200  
+QUERY: SELECT '' AS five, f.f1, ||/f.f1 AS cbrt_f1 FROM FLOAT8_TBL f;
+five  f1                    cbrt_f1               
+----- --------------------- --------------------- 
+      0                     0                     
+      1004.3                10.014312837827       
+      -34.84                -3.26607421344208     
+      1.2345678901234e+200  4.97933859234765e+66  
+      1.2345678901234e-200  2.3112042409018e-67   
+QUERY: SELECT '' AS five, FLOAT8_TBL.*;
+five  f1                    
+----- --------------------- 
+      0                     
+      1004.3                
+      -34.84                
+      1.2345678901234e+200  
+      1.2345678901234e-200  
+QUERY: UPDATE FLOAT8_TBL
+   SET f1 = FLOAT8_TBL.f1 * '-1'
+   WHERE FLOAT8_TBL.f1 > '0.0';
+QUERY: SELECT '' AS bad, f.f1 * '1e200' from FLOAT8_TBL f;
+WARN:floating point exception! the last floating point operation either exceeded legal ranges or was a divide by zero
+QUERY: SELECT '' AS bad, f.f1 ^ '1e200' from FLOAT8_TBL f;
+WARN:pow() returned a floating point out of the range
+
+QUERY: SELECT '' AS bad, ; (f.f1) from FLOAT8_TBL f where f.f1 = '0.0' ;
+WARN:can't take log of 0!
+QUERY: SELECT '' AS bad, ; (f.f1) from FLOAT8_TBL f where f.f1 < '0.0' ;
+WARN:can't take log of a negative number
+QUERY: SELECT '' AS bad, : (f.f1) from FLOAT8_TBL f;
+WARN:exp() returned a floating point out of range
+
+QUERY: SELECT '' AS bad, f.f1 / '0.0' from FLOAT8_TBL f;
+WARN:float8div:  divide by 0.0 error
+QUERY: SELECT '' AS five, FLOAT8_TBL.*;
+five  f1                     
+----- ---------------------- 
+      0                      
+      -34.84                 
+      -1004.3                
+      -1.2345678901234e+200  
+      -1.2345678901234e-200  
+QUERY: CREATE TABLE INT2_TBL(f1 int2);
+QUERY: INSERT INTO INT2_TBL(f1) VALUES ('0');
+QUERY: INSERT INTO INT2_TBL(f1) VALUES ('1234');
+QUERY: INSERT INTO INT2_TBL(f1) VALUES ('-1234');
+QUERY: INSERT INTO INT2_TBL(f1) VALUES ('34.5');
+WARN:pg_atoi: error in "34.5": can't parse ".5"
+QUERY: INSERT INTO INT2_TBL(f1) VALUES ('32767');
+QUERY: INSERT INTO INT2_TBL(f1) VALUES ('-32767');
+QUERY: INSERT INTO INT2_TBL(f1) VALUES ('100000');
+WARN:pg_atoi: error reading "100000": Result too large
+QUERY: INSERT INTO INT2_TBL(f1) VALUES ('asdf');
+WARN:pg_atoi: error in "asdf": can't parse "asdf"
+QUERY: SELECT '' AS five, INT2_TBL.*;
+five  f1      
+----- ------- 
+      0       
+      1234    
+      -1234   
+      32767   
+      -32767  
+QUERY: SELECT '' AS four, i.* FROM INT2_TBL i WHERE i.f1 <> '0'::int2;
+four  f1      
+----- ------- 
+      1234    
+      -1234   
+      32767   
+      -32767  
+QUERY: SELECT '' AS four, i.* FROM INT2_TBL i WHERE i.f1 <> '0'::int4;
+four  f1      
+----- ------- 
+      1234    
+      -1234   
+      32767   
+      -32767  
+QUERY: SELECT '' AS one, i.* FROM INT2_TBL i WHERE i.f1 = '0'::int2;
+one  f1  
+---- --- 
+     0   
+QUERY: SELECT '' AS one, i.* FROM INT2_TBL i WHERE i.f1 = '0'::int4;
+one  f1  
+---- --- 
+     0   
+QUERY: SELECT '' AS two, i.* FROM INT2_TBL i WHERE i.f1 < '0'::int2;
+two  f1      
+---- ------- 
+     -1234   
+     -32767  
+QUERY: SELECT '' AS two, i.* FROM INT2_TBL i WHERE i.f1 < '0'::int4;
+two  f1      
+---- ------- 
+     -1234   
+     -32767  
+QUERY: SELECT '' AS three, i.* FROM INT2_TBL i WHERE i.f1 <= '0'::int2;
+three  f1      
+------ ------- 
+       0       
+       -1234   
+       -32767  
+QUERY: SELECT '' AS three, i.* FROM INT2_TBL i WHERE i.f1 <= '0'::int4;
+three  f1      
+------ ------- 
+       0       
+       -1234   
+       -32767  
+QUERY: SELECT '' AS two, i.* FROM INT2_TBL i WHERE i.f1 > '0'::int2;
+two  f1     
+---- ------ 
+     1234   
+     32767  
+QUERY: SELECT '' AS two, i.* FROM INT2_TBL i WHERE i.f1 > '0'::int4;
+two  f1     
+---- ------ 
+     1234   
+     32767  
+QUERY: SELECT '' AS three, i.* FROM INT2_TBL i WHERE i.f1 >= '0'::int2;
+three  f1     
+------ ------ 
+       0      
+       1234   
+       32767  
+QUERY: SELECT '' AS three, i.* FROM INT2_TBL i WHERE i.f1 >= '0'::int4;
+three  f1     
+------ ------ 
+       0      
+       1234   
+       32767  
+QUERY: SELECT '' AS one, i.* FROM INT2_TBL i WHERE (i.f1 % '2'::int2) = '1'::int2;
+one  f1     
+---- ------ 
+     32767  
+QUERY: SELECT '' AS three, i.* FROM INT2_TBL i WHERE (i.f1 % '2'::int4) = '0'::int2;
+three  f1     
+------ ------ 
+       0      
+       1234   
+       -1234  
+QUERY: SELECT '' AS five, i.f1, i.f1 * '2'::int2 AS x FROM INT2_TBL i;
+five  f1      x      
+----- ------- ------ 
+      0       0      
+      1234    2468   
+      -1234   -2468  
+      32767   -2     
+      -32767  2      
+QUERY: SELECT '' AS five, i.f1, i.f1 * '2'::int4 AS x FROM INT2_TBL i;
+five  f1      x       
+----- ------- ------- 
+      0       0       
+      1234    2468    
+      -1234   -2468   
+      32767   65534   
+      -32767  -65534  
+QUERY: SELECT '' AS five, i.f1, i.f1 + '2'::int2 AS x FROM INT2_TBL i;
+five  f1      x       
+----- ------- ------- 
+      0       2       
+      1234    1236    
+      -1234   -1232   
+      32767   -32767  
+      -32767  -32765  
+QUERY: SELECT '' AS five, i.f1, i.f1 + '2'::int4 AS x FROM INT2_TBL i;
+five  f1      x       
+----- ------- ------- 
+      0       2       
+      1234    1236    
+      -1234   -1232   
+      32767   32769   
+      -32767  -32765  
+QUERY: SELECT '' AS five, i.f1, i.f1 - '2'::int2 AS x FROM INT2_TBL i;
+five  f1      x      
+----- ------- ------ 
+      0       -2     
+      1234    1232   
+      -1234   -1236  
+      32767   32765  
+      -32767  32767  
+QUERY: SELECT '' AS five, i.f1, i.f1 - '2'::int4 AS x FROM INT2_TBL i;
+five  f1      x       
+----- ------- ------- 
+      0       -2      
+      1234    1232    
+      -1234   -1236   
+      32767   32765   
+      -32767  -32769  
+QUERY: SELECT '' AS five, i.f1, i.f1 / '2'::int2 AS x FROM INT2_TBL i;
+five  f1      x       
+----- ------- ------- 
+      0       0       
+      1234    617     
+      -1234   -617    
+      32767   16383   
+      -32767  -16383  
+QUERY: SELECT '' AS five, i.f1, i.f1 / '2'::int4 AS x FROM INT2_TBL i;
+five  f1      x       
+----- ------- ------- 
+      0       0       
+      1234    617     
+      -1234   -617    
+      32767   16383   
+      -32767  -16383  
+QUERY: CREATE TABLE INT4_TBL(f1 int4);
+QUERY: INSERT INTO INT4_TBL(f1) VALUES ('0');
+QUERY: INSERT INTO INT4_TBL(f1) VALUES ('123456');
+QUERY: INSERT INTO INT4_TBL(f1) VALUES ('-123456');
+QUERY: INSERT INTO INT4_TBL(f1) VALUES ('34.5');
+WARN:pg_atoi: error in "34.5": can't parse ".5"
+QUERY: INSERT INTO INT4_TBL(f1) VALUES ('2147483647');
+QUERY: INSERT INTO INT4_TBL(f1) VALUES ('-2147483647');
+QUERY: INSERT INTO INT4_TBL(f1) VALUES ('1000000000000');
+WARN:pg_atoi: error reading "1000000000000": Result too large
+QUERY: INSERT INTO INT4_TBL(f1) VALUES ('asdf');
+WARN:pg_atoi: error in "asdf": can't parse "asdf"
+QUERY: SELECT '' AS five, INT4_TBL.*;
+five  f1           
+----- ------------ 
+      0            
+      123456       
+      -123456      
+      2147483647   
+      -2147483647  
+QUERY: SELECT '' AS four, i.* FROM INT4_TBL i WHERE i.f1 <> '0'::int2;
+four  f1           
+----- ------------ 
+      123456       
+      -123456      
+      2147483647   
+      -2147483647  
+QUERY: SELECT '' AS four, i.* FROM INT4_TBL i WHERE i.f1 <> '0'::int4;
+four  f1           
+----- ------------ 
+      123456       
+      -123456      
+      2147483647   
+      -2147483647  
+QUERY: SELECT '' AS one, i.* FROM INT4_TBL i WHERE i.f1 = '0'::int2;
+one  f1  
+---- --- 
+     0   
+QUERY: SELECT '' AS one, i.* FROM INT4_TBL i WHERE i.f1 = '0'::int4;
+one  f1  
+---- --- 
+     0   
+QUERY: SELECT '' AS two, i.* FROM INT4_TBL i WHERE i.f1 < '0'::int2;
+two  f1           
+---- ------------ 
+     -123456      
+     -2147483647  
+QUERY: SELECT '' AS two, i.* FROM INT4_TBL i WHERE i.f1 < '0'::int4;
+two  f1           
+---- ------------ 
+     -123456      
+     -2147483647  
+QUERY: SELECT '' AS three, i.* FROM INT4_TBL i WHERE i.f1 <= '0'::int2;
+three  f1           
+------ ------------ 
+       0            
+       -123456      
+       -2147483647  
+QUERY: SELECT '' AS three, i.* FROM INT4_TBL i WHERE i.f1 <= '0'::int4;
+three  f1           
+------ ------------ 
+       0            
+       -123456      
+       -2147483647  
+QUERY: SELECT '' AS two, i.* FROM INT4_TBL i WHERE i.f1 > '0'::int2;
+two  f1          
+---- ----------- 
+     123456      
+     2147483647  
+QUERY: SELECT '' AS two, i.* FROM INT4_TBL i WHERE i.f1 > '0'::int4;
+two  f1          
+---- ----------- 
+     123456      
+     2147483647  
+QUERY: SELECT '' AS three, i.* FROM INT4_TBL i WHERE i.f1 >= '0'::int2;
+three  f1          
+------ ----------- 
+       0           
+       123456      
+       2147483647  
+QUERY: SELECT '' AS three, i.* FROM INT4_TBL i WHERE i.f1 >= '0'::int4;
+three  f1          
+------ ----------- 
+       0           
+       123456      
+       2147483647  
+QUERY: SELECT '' AS one, i.* FROM INT4_TBL i WHERE (i.f1 % '2'::int2) = '1'::int2;
+one  f1          
+---- ----------- 
+     2147483647  
+QUERY: SELECT '' AS three, i.* FROM INT4_TBL i WHERE (i.f1 % '2'::int4) = '0'::int2;
+three  f1       
+------ -------- 
+       0        
+       123456   
+       -123456  
+QUERY: SELECT '' AS five, i.f1, i.f1 * '2'::int2 AS x FROM INT4_TBL i;
+five  f1           x        
+----- ------------ -------- 
+      0            0        
+      123456       246912   
+      -123456      -246912  
+      2147483647   -2       
+      -2147483647  2        
+QUERY: SELECT '' AS five, i.f1, i.f1 * '2'::int4 AS x FROM INT4_TBL i;
+five  f1           x        
+----- ------------ -------- 
+      0            0        
+      123456       246912   
+      -123456      -246912  
+      2147483647   -2       
+      -2147483647  2        
+QUERY: SELECT '' AS five, i.f1, i.f1 + '2'::int2 AS x FROM INT4_TBL i;
+five  f1           x            
+----- ------------ ------------ 
+      0            2            
+      123456       123458       
+      -123456      -123454      
+      2147483647   -2147483647  
+      -2147483647  -2147483645  
+QUERY: SELECT '' AS five, i.f1, i.f1 + '2'::int4 AS x FROM INT4_TBL i;
+five  f1           x            
+----- ------------ ------------ 
+      0            2            
+      123456       123458       
+      -123456      -123454      
+      2147483647   -2147483647  
+      -2147483647  -2147483645  
+QUERY: SELECT '' AS five, i.f1, i.f1 - '2'::int2 AS x FROM INT4_TBL i;
+five  f1           x           
+----- ------------ ----------- 
+      0            -2          
+      123456       123454      
+      -123456      -123458     
+      2147483647   2147483645  
+      -2147483647  2147483647  
+QUERY: SELECT '' AS five, i.f1, i.f1 - '2'::int4 AS x FROM INT4_TBL i;
+five  f1           x           
+----- ------------ ----------- 
+      0            -2          
+      123456       123454      
+      -123456      -123458     
+      2147483647   2147483645  
+      -2147483647  2147483647  
+QUERY: SELECT '' AS five, i.f1, i.f1 / '2'::int2 AS x FROM INT4_TBL i;
+five  f1           x            
+----- ------------ ------------ 
+      0            0            
+      123456       61728        
+      -123456      -61728       
+      2147483647   1073741823   
+      -2147483647  -1073741823  
+QUERY: SELECT '' AS five, i.f1, i.f1 / '2'::int4 AS x FROM INT4_TBL i;
+five  f1           x            
+----- ------------ ------------ 
+      0            0            
+      123456       61728        
+      -123456      -61728       
+      2147483647   1073741823   
+      -2147483647  -1073741823  
+QUERY: SELECT '2'::int2 * '2'::int2 = '16'::int2 / '4'::int2 AS true;
+true  
+----- 
+t     
+QUERY: SELECT '2'::int4 * '2'::int2 = '16'::int2 / '4'::int4 AS true;
+true  
+----- 
+t     
+QUERY: SELECT '2'::int2 * '2'::int4 = '16'::int4 / '4'::int2 AS true;
+true  
+----- 
+t     
+QUERY: SELECT '1000'::int4 < '999'::int4 AS false;
+false  
+------ 
+f      
+QUERY: SELECT 4! AS twenty_four;
+twenty_four  
+------------ 
+24           
+QUERY: SELECT !!3 AS six;
+six  
+---- 
+6    
+QUERY: SELECT 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 AS ten;
+ten  
+---- 
+10   
+QUERY: SELECT 2 + 2 / 2 AS three;
+three  
+------ 
+3      
+QUERY: SELECT (2 + 2) / 2 AS two;
+two  
+---- 
+2    
+QUERY: SELECT dsqrt('64'::float8) AS eight;
+eight  
+------ 
+8      
+QUERY: SELECT |/'64'::float8 AS eight;
+eight  
+------ 
+8      
+QUERY: SELECT ||/'27'::float8 AS three;
+three  
+------ 
+3      
+QUERY: CREATE TABLE OID_TBL(f1 oid);
+QUERY: INSERT INTO OID_TBL(f1) VALUES ('1234');
+QUERY: INSERT INTO OID_TBL(f1) VALUES ('1235');
+QUERY: INSERT INTO OID_TBL(f1) VALUES ('987');
+QUERY: INSERT INTO OID_TBL(f1) VALUES ('-1040');
+QUERY: INSERT INTO OID_TBL(f1) VALUES ('');
+QUERY: INSERT INTO OID_TBL(f1) VALUES ('asdfasd');
+WARN:pg_atoi: error in "asdfasd": can't parse "asdfasd"
+QUERY: SELECT '' AS five, OID_TBL.*;
+five  f1     
+----- ------ 
+      1234   
+      1235   
+      987    
+      -1040  
+      0      
+QUERY: SELECT '' AS one, o.* FROM OID_TBL o WHERE o.f1 = '1234'::oid;
+one  f1    
+---- ----- 
+     1234  
+QUERY: SELECT '' AS four, o.* FROM OID_TBL o WHERE o.f1 <> '1234';
+four  f1     
+----- ------ 
+      1235   
+      987    
+      -1040  
+      0      
+QUERY: SELECT '' AS four, o.* FROM OID_TBL o WHERE o.f1 <= '1234';
+four  f1     
+----- ------ 
+      1234   
+      987    
+      -1040  
+      0      
+QUERY: SELECT '' AS three, o.* FROM OID_TBL o WHERE o.f1 < '1234';
+three  f1     
+------ ------ 
+       987    
+       -1040  
+       0      
+QUERY: SELECT '' AS two, o.* FROM OID_TBL o WHERE o.f1 >= '1234';
+two  f1    
+---- ----- 
+     1234  
+     1235  
+QUERY: SELECT '' AS one, o.* FROM OID_TBL o WHERE o.f1 > '1234';
+one  f1    
+---- ----- 
+     1235  
+QUERY: CREATE TABLE OIDNAME_TBL(f1 oidname);
+QUERY: INSERT INTO OIDNAME_TBL(f1) VALUES ('1234,abcd');
+QUERY: INSERT INTO OIDNAME_TBL(f1) VALUES ('1235,efgh');
+QUERY: INSERT INTO OIDNAME_TBL(f1) VALUES ('987,XXXX');
+QUERY: INSERT INTO OIDNAME_TBL(f1) VALUES ('123456');
+WARN:Bad input data for type oidname
+QUERY: INSERT INTO OIDNAME_TBL(f1) VALUES ('123456,abcdefghijklmnopqrsutvwyz');
+QUERY: INSERT INTO OIDNAME_TBL(f1) VALUES ('');
+WARN:Bad input data for type oidname
+QUERY: INSERT INTO OIDNAME_TBL(f1) VALUES ('asdfasd');
+WARN:Bad input data for type oidname
+QUERY: SELECT '' AS four, OIDNAME_TBL.*;
+four  f1                                
+----- --------------------------------- 
+      1234,abcd                         
+      1235,efgh                         
+      987,XXXX                          
+      123456,abcdefghijklmnopqrsutvwyz  
+QUERY: SELECT '' AS one, o.* FROM OIDNAME_TBL o WHERE o.f1 = '1234,abcd';
+one  f1         
+---- ---------- 
+     1234,abcd  
+QUERY: SELECT '' AS three, o.* FROM OIDNAME_TBL o WHERE o.f1 <> '1234,abcd';
+three  f1                                
+------ --------------------------------- 
+       1235,efgh                         
+       987,XXXX                          
+       123456,abcdefghijklmnopqrsutvwyz  
+QUERY: SELECT '' AS two, o.* FROM OIDNAME_TBL o WHERE o.f1 <= '1234,abcd';
+two  f1         
+---- ---------- 
+     1234,abcd  
+     987,XXXX   
+QUERY: SELECT '' AS one, o.* FROM OIDNAME_TBL o WHERE o.f1 < '1234,abcd';
+one  f1        
+---- --------- 
+     987,XXXX  
+QUERY: SELECT '' AS three, o.* FROM OIDNAME_TBL o WHERE o.f1 >= '1234,abcd';
+three  f1                                
+------ --------------------------------- 
+       1234,abcd                         
+       1235,efgh                         
+       123456,abcdefghijklmnopqrsutvwyz  
+QUERY: SELECT '' AS two, o.* FROM OIDNAME_TBL o WHERE o.f1 > '1234,abcd';
+two  f1                                
+---- --------------------------------- 
+     1235,efgh                         
+     123456,abcdefghijklmnopqrsutvwyz  
+QUERY: CREATE TABLE OIDINT2_TBL(f1 oidint2);
+QUERY: INSERT INTO OIDINT2_TBL(f1) VALUES ('1234/9873');
+QUERY: INSERT INTO OIDINT2_TBL(f1) VALUES ('1235/9873');
+QUERY: INSERT INTO OIDINT2_TBL(f1) VALUES ('987/-1234');
+QUERY: INSERT INTO OIDINT2_TBL(f1) VALUES ('123456');
+QUERY: INSERT INTO OIDINT2_TBL(f1) VALUES ('123456/123456');
+WARN:pg_atoi: error reading "123456": Result too large
+QUERY: INSERT INTO OIDINT2_TBL(f1) VALUES ('');
+QUERY: INSERT INTO OIDINT2_TBL(f1) VALUES ('asdfasd');
+WARN:pg_atoi: error in "asdfasd": can't parse "asdfasd"
+QUERY: SELECT '' AS five, OIDINT2_TBL.*;
+five  f1         
+----- ---------- 
+      1234/9873  
+      1235/9873  
+      987/-1234  
+      123456/0   
+      0/0        
+QUERY: SELECT '' AS one, o.* FROM OIDINT2_TBL o WHERE o.f1 = '1235/9873';
+one  f1         
+---- ---------- 
+     1235/9873  
+QUERY: SELECT '' AS four, o.* FROM OIDINT2_TBL o WHERE o.f1 <> '1235/9873';
+four  f1         
+----- ---------- 
+      1234/9873  
+      987/-1234  
+      123456/0   
+      0/0        
+QUERY: SELECT '' AS four, o.* FROM OIDINT2_TBL o WHERE o.f1 <= '1235/9873';
+four  f1         
+----- ---------- 
+      1234/9873  
+      1235/9873  
+      987/-1234  
+      0/0        
+QUERY: SELECT '' AS three, o.* FROM OIDINT2_TBL o WHERE o.f1 < '1235/9873';
+three  f1         
+------ ---------- 
+       1234/9873  
+       987/-1234  
+       0/0        
+QUERY: SELECT '' AS two, o.* FROM OIDINT2_TBL o WHERE o.f1 >= '1235/9873';
+two  f1         
+---- ---------- 
+     1235/9873  
+     123456/0   
+QUERY: SELECT '' AS one, o.* FROM OIDINT2_TBL o WHERE o.f1 > '1235/9873';
+one  f1        
+---- --------- 
+     123456/0  
+QUERY: CREATE TABLE OIDINT4_TBL(f1 oidint4);
+QUERY: INSERT INTO OIDINT4_TBL(f1) VALUES ('1234/9873');
+QUERY: INSERT INTO OIDINT4_TBL(f1) VALUES ('1235/9873');
+QUERY: INSERT INTO OIDINT4_TBL(f1) VALUES ('987/-1234');
+QUERY: INSERT INTO OIDINT4_TBL(f1) VALUES ('123456');
+QUERY: INSERT INTO OIDINT4_TBL(f1) VALUES ('123456/1234568901234567890');
+WARN:pg_atoi: error reading "1234568901234567890": Result too large
+QUERY: INSERT INTO OIDINT4_TBL(f1) VALUES ('');
+QUERY: INSERT INTO OIDINT4_TBL(f1) VALUES ('asdfasd');
+WARN:pg_atoi: error in "asdfasd": can't parse "asdfasd"
+QUERY: SELECT '' AS five, OIDINT4_TBL.*;
+five  f1         
+----- ---------- 
+      1234/9873  
+      1235/9873  
+      987/-1234  
+      123456/0   
+      0/0        
+QUERY: SELECT '' AS one, o.* FROM OIDINT4_TBL o WHERE o.f1 = '1235/9873';
+one  f1         
+---- ---------- 
+     1235/9873  
+QUERY: SELECT '' AS four, o.* FROM OIDINT4_TBL o WHERE o.f1 <> '1235/9873';
+four  f1         
+----- ---------- 
+      1234/9873  
+      987/-1234  
+      123456/0   
+      0/0        
+QUERY: SELECT '' AS four, o.* FROM OIDINT4_TBL o WHERE o.f1 <= '1235/9873';
+four  f1         
+----- ---------- 
+      1234/9873  
+      1235/9873  
+      987/-1234  
+      0/0        
+QUERY: SELECT '' AS three, o.* FROM OIDINT4_TBL o WHERE o.f1 < '1235/9873';
+three  f1         
+------ ---------- 
+       1234/9873  
+       987/-1234  
+       0/0        
+QUERY: SELECT '' AS two, o.* FROM OIDINT4_TBL o WHERE o.f1 >= '1235/9873';
+two  f1         
+---- ---------- 
+     1235/9873  
+     123456/0   
+QUERY: SELECT '' AS one, o.* FROM OIDINT4_TBL o WHERE o.f1 > '1235/9873';
+one  f1        
+---- --------- 
+     123456/0  
+QUERY: CREATE TABLE POINT_TBL(f1 point);
+QUERY: INSERT INTO POINT_TBL(f1) VALUES ('(0.0,0.0)');
+QUERY: INSERT INTO POINT_TBL(f1) VALUES ('(-10.0,0.0)');
+QUERY: INSERT INTO POINT_TBL(f1) VALUES ('(-3.0,4.0)');
+QUERY: INSERT INTO POINT_TBL(f1) VALUES ('(5.1, 34.5)');
+QUERY: INSERT INTO POINT_TBL(f1) VALUES ('(-5.0,-12.0)');
+QUERY: INSERT INTO POINT_TBL(f1) VALUES ('asdfasdf');
+WARN:Bad point external representation 'asdfasdf'
+QUERY: INSERT INTO POINT_TBL(f1) VALUES ('10.0,10.0');
+WARN:Bad point external representation '10.0,10.0'
+QUERY: INSERT INTO POINT_TBL(f1) VALUES ('(10.0 10.0)');
+WARN:Bad point external representation '(10.0 10.0)'
+QUERY: INSERT INTO POINT_TBL(f1) VALUES ('(10.0,10.0');
+WARN:Bad point external representation '(10.0,10.0'
+QUERY: SELECT '' AS five, POINT_TBL.*;
+five  f1          
+----- ----------- 
+      (0,0)       
+      (-10,0)     
+      (-3,4)      
+      (5.1,34.5)  
+      (-5,-12)    
+QUERY: SELECT '' AS three, p.* FROM POINT_TBL p WHERE p.f1 !< '(0.0, 0.0)';
+three  f1        
+------ --------- 
+       (-10,0)   
+       (-3,4)    
+       (-5,-12)  
+QUERY: SELECT '' AS three, p.* FROM POINT_TBL p WHERE '(0.0,0.0)' !> p.f1;
+three  f1        
+------ --------- 
+       (-10,0)   
+       (-3,4)    
+       (-5,-12)  
+QUERY: SELECT '' AS one, p.* FROM POINT_TBL p WHERE '(0.0,0.0)' !^ p.f1;
+one  f1        
+---- --------- 
+     (-5,-12)  
+QUERY: SELECT '' AS one, p.* FROM POINT_TBL p WHERE p.f1 !| '(0.0, 0.0)';
+one  f1        
+---- --------- 
+     (-5,-12)  
+QUERY: SELECT '' AS one, p.* FROM POINT_TBL p WHERE p.f1 =|= '(5.1, 34.5)';
+one  f1          
+---- ----------- 
+     (5.1,34.5)  
+QUERY: SELECT '' AS two, p.* FROM POINT_TBL p
+   WHERE p.f1 ===> '(0,0,100,100)';
+two  f1          
+---- ----------- 
+     (0,0)       
+     (5.1,34.5)  
+QUERY: SELECT '' AS three, p.* FROM POINT_TBL p
+   WHERE not on_pb(p.f1,'(0,0,100,100)'::box);
+three  f1        
+------ --------- 
+       (-10,0)   
+       (-3,4)    
+       (-5,-12)  
+QUERY: SELECT '' AS two, p.* FROM POINT_TBL p
+   WHERE on_ppath(p.f1,'(0,3,0,0,-10,0,-10,10)'::path);
+two  f1       
+---- -------- 
+     (0,0)    
+     (-10,0)  
+QUERY: SELECT '' AS five, p.f1, p.f1 <===> '(0,0)' AS dist FROM POINT_TBL p;
+five  f1          dist  
+----- ----------- ----- 
+      (0,0)       0     
+      (-10,0)     10    
+      (-3,4)      5     
+      (5.1,34.5)  34    
+      (-5,-12)    13    
+QUERY: SELECT '' AS twentyfive, p1.f1, p2.f1, p1.f1 <===> p2.f1 AS dist
+   FROM POINT_TBL p1, POINT_TBL p2;
+twentyfive  f1          f1          dist  
+----------- ----------- ----------- ----- 
+            (0,0)       (0,0)       0     
+            (-10,0)     (0,0)       10    
+            (-3,4)      (0,0)       5     
+            (5.1,34.5)  (0,0)       34    
+            (-5,-12)    (0,0)       13    
+            (0,0)       (-10,0)     10    
+            (-10,0)     (-10,0)     0     
+            (-3,4)      (-10,0)     8     
+            (5.1,34.5)  (-10,0)     37    
+            (-5,-12)    (-10,0)     13    
+            (0,0)       (-3,4)      5     
+            (-10,0)     (-3,4)      8     
+            (-3,4)      (-3,4)      0     
+            (5.1,34.5)  (-3,4)      31    
+            (-5,-12)    (-3,4)      16    
+            (0,0)       (5.1,34.5)  34    
+            (-10,0)     (5.1,34.5)  37    
+            (-3,4)      (5.1,34.5)  31    
+            (5.1,34.5)  (5.1,34.5)  0     
+            (-5,-12)    (5.1,34.5)  47    
+            (0,0)       (-5,-12)    13    
+            (-10,0)     (-5,-12)    13    
+            (-3,4)      (-5,-12)    16    
+            (5.1,34.5)  (-5,-12)    47    
+            (-5,-12)    (-5,-12)    0     
+QUERY: SELECT '' AS twenty, p1.f1, p2.f1
+   FROM POINT_TBL p1, POINT_TBL p2
+   WHERE (p1.f1 <===> p2.f1) > 3;
+twenty  f1          f1          
+------- ----------- ----------- 
+        (-10,0)     (0,0)       
+        (-3,4)      (0,0)       
+        (5.1,34.5)  (0,0)       
+        (-5,-12)    (0,0)       
+        (0,0)       (-10,0)     
+        (-3,4)      (-10,0)     
+        (5.1,34.5)  (-10,0)     
+        (-5,-12)    (-10,0)     
+        (0,0)       (-3,4)      
+        (-10,0)     (-3,4)      
+        (5.1,34.5)  (-3,4)      
+        (-5,-12)    (-3,4)      
+        (0,0)       (5.1,34.5)  
+        (-10,0)     (5.1,34.5)  
+        (-3,4)      (5.1,34.5)  
+        (-5,-12)    (5.1,34.5)  
+        (0,0)       (-5,-12)    
+        (-10,0)     (-5,-12)    
+        (-3,4)      (-5,-12)    
+        (5.1,34.5)  (-5,-12)    
+QUERY: SELECT '' AS ten, p1.f1, p2.f1
+   FROM POINT_TBL p1, POINT_TBL p2
+   WHERE (p1.f1 <===> p2.f1) > 3 and
+       p1.f1 !< p2.f1;
+ten  f1        f1          
+---- --------- ----------- 
+     (-10,0)   (0,0)       
+     (-3,4)    (0,0)       
+     (-5,-12)  (0,0)       
+     (-10,0)   (-3,4)      
+     (-5,-12)  (-3,4)      
+     (0,0)     (5.1,34.5)  
+     (-10,0)   (5.1,34.5)  
+     (-3,4)    (5.1,34.5)  
+     (-5,-12)  (5.1,34.5)  
+     (-10,0)   (-5,-12)    
+QUERY: SELECT '' AS two, p1.f1, p2.f1
+   FROM POINT_TBL p1, POINT_TBL p2
+   WHERE (p1.f1 <===> p2.f1) > 3 and
+       p1.f1 !< p2.f1 and
+       p1.f1 !^ p2.f1;
+two  f1       f1        
+---- -------- --------- 
+     (-3,4)   (0,0)     
+     (-10,0)  (-5,-12)  
+QUERY: CREATE TABLE POLYGON_TBL(f1 polygon);
+QUERY: INSERT INTO POLYGON_TBL(f1) VALUES ('(2.0,2.0,0.0,0.0,4.0,0.0)');
+QUERY: INSERT INTO POLYGON_TBL(f1) VALUES ('(3.0,3.0,1.0,1.0,3.0,0.0)');
+QUERY: INSERT INTO POLYGON_TBL(f1) VALUES ('(0.0,0.0)');
+QUERY: INSERT INTO POLYGON_TBL(f1) VALUES ('(0.0,0.0,1.0,1.0)');
+QUERY: INSERT INTO POLYGON_TBL(f1) VALUES ('0.0');
+WARN:Bad polygon external representation '0.0'
+QUERY: INSERT INTO POLYGON_TBL(f1) VALUES ('(0.0 0.0');
+WARN:Bad polygon external representation '(0.0 0.0'
+QUERY: INSERT INTO POLYGON_TBL(f1) VALUES ('(0,1,2)');
+WARN:Bad polygon external representation '(0,1,2)'
+QUERY: INSERT INTO POLYGON_TBL(f1) VALUES ('(0,1,2,3');
+WARN:Bad polygon external representation '(0,1,2,3'
+QUERY: INSERT INTO POLYGON_TBL(f1) VALUES ('asdf');
+WARN:Bad polygon external representation 'asdf'
+QUERY: SELECT '' AS four, POLYGON_TBL.*;
+four  f1                                                                               
+----- -------------------------------------------------------------------------------- 
+      (           2,           2,           0,           0,           4,           0)  
+      (           3,           3,           1,           1,           3,           0)  
+      (           0,           0)                                                      
+      (           0,           0,           1,           1)                            
+QUERY: SELECT '' AS three, p.*
+   FROM POLYGON_TBL p
+   WHERE p.f1 && '(3.0,3.0,1.0,1.0,3.0,0.0)';
+three  f1                                                                               
+------ -------------------------------------------------------------------------------- 
+       (           2,           2,           0,           0,           4,           0)  
+       (           3,           3,           1,           1,           3,           0)  
+       (           0,           0,           1,           1)                            
+QUERY: SELECT '' AS four, p.*
+   FROM POLYGON_TBL p
+   WHERE p.f1 &< '(3.0,3.0,1.0,1.0,3.0,0.0)';
+four  f1                                                                               
+----- -------------------------------------------------------------------------------- 
+      (           2,           2,           0,           0,           4,           0)  
+      (           3,           3,           1,           1,           3,           0)  
+      (           0,           0)                                                      
+      (           0,           0,           1,           1)                            
+QUERY: SELECT '' AS two, p.*
+   FROM POLYGON_TBL p
+   WHERE p.f1 &> '(3.0,3.0,1.0,1.0,3.0,0.0)';
+two  f1                                                                               
+---- -------------------------------------------------------------------------------- 
+     (           2,           2,           0,           0,           4,           0)  
+     (           3,           3,           1,           1,           3,           0)  
+QUERY: SELECT '' AS one, p.*
+   FROM POLYGON_TBL p
+   WHERE p.f1 << '(3.0,3.0,1.0,1.0,3.0,0.0)';
+one  f1                           
+---- ---------------------------- 
+     (           0,           0)  
+QUERY: SELECT '' AS zero, p.*
+   FROM POLYGON_TBL p
+   WHERE p.f1 >> '(3.0,3.0,1.0,1.0,3.0,0.0)';
+zero  f1  
+----- --- 
+QUERY: SELECT '' AS one, p.*
+   FROM POLYGON_TBL p
+   WHERE p.f1 @ '(3.0,3.0,1.0,1.0,3.0,0.0)';
+one  f1                                                                               
+---- -------------------------------------------------------------------------------- 
+     (           3,           3,           1,           1,           3,           0)  
+QUERY: SELECT '' AS one, p.*
+   FROM POLYGON_TBL p
+   WHERE p.f1 ~= '(3.0,3.0,1.0,1.0,3.0,0.0)';
+one  f1                                                                               
+---- -------------------------------------------------------------------------------- 
+     (           3,           3,           1,           1,           3,           0)  
+QUERY: SELECT '' AS one, p.*
+   FROM POLYGON_TBL p
+   WHERE p.f1 ~ '(3.0,3.0,1.0,1.0,3.0,0.0)';
+one  f1                                                                               
+---- -------------------------------------------------------------------------------- 
+     (           3,           3,           1,           1,           3,           0)  
+QUERY: SELECT 'char 16 string'::char16 = 'char 16 string '::char16 AS false;
+false  
+------ 
+f      
+QUERY: SELECT 'c'::char = 'c'::char AS true;
+true  
+----- 
+t     
+QUERY: SELECT 'this is a text string'::text = 'this is a text string'::text AS true;
+true  
+----- 
+t     
+QUERY: SELECT 'this is a text string'::text = 'this is a text strin'::text AS false;
+false  
+------ 
+f      
+QUERY: SELECT '(2.0,2.0,0.0,0.0,4.0,0.0)'::polygon << '(3.0,3.0,1.0,1.0,3.0,0.0)'::polygon AS false;
+false  
+------ 
+f      
+QUERY: SELECT '(2.0,2.0,0.0,0.0,4.0,0.0)'::polygon &< '(3.0,3.0,1.0,1.0,3.0,0.0)'::polygon AS true;
+true  
+----- 
+t     
+QUERY: SELECT '(2.0,2.0,0.0,0.0,4.0,0.0)'::polygon &> '(3.0,3.0,1.0,1.0,3.0,0.0)'::polygon AS true;
+true  
+----- 
+t     
+QUERY: SELECT '(2.0,2.0,0.0,0.0,4.0,0.0)'::polygon >> '(3.0,3.0,1.0,1.0,3.0,0.0)'::polygon AS false;
+false  
+------ 
+f      
+QUERY: SELECT '(2.0,2.0,0.0,0.0,4.0,0.0)'::polygon @ '(3.0,3.0,1.0,1.0,3.0,0.0)'::polygon AS false;
+false  
+------ 
+f      
+QUERY: SELECT '(2.0,2.0,0.0,0.0,4.0,0.0)'::polygon ~ '(3.0,3.0,1.0,1.0,3.0,0.0)'::polygon AS false;
+false  
+------ 
+f      
+QUERY: SELECT '(2.0,2.0,0.0,0.0,4.0,0.0)'::polygon ~= '(3.0,3.0,1.0,1.0,3.0,0.0)'::polygon AS false;
+false  
+------ 
+f      
+QUERY: SELECT '(2.0,2.0,0.0,0.0,4.0,0.0)'::polygon && '(3.0,3.0,1.0,1.0,3.0,0.0)'::polygon AS true;
+true  
+----- 
+t     
+QUERY: SELECT onek.* WHERE onek.unique1 < 10;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+0        998      0    0     0    0       0        0         0            0          0         0    1     AAAAAA    KMBAAA    OOOOxx   
+1        214      1    1     1    1       1        1         1            1          1         2    3     BAAAAA    GIAAAA    OOOOxx   
+2        326      0    2     2    2       2        2         2            2          2         4    5     CAAAAA    OMAAAA    OOOOxx   
+3        431      1    3     3    3       3        3         3            3          3         6    7     DAAAAA    PQAAAA    VVVVxx   
+4        833      0    0     4    4       4        4         4            4          4         8    9     EAAAAA    BGBAAA    HHHHxx   
+5        541      1    1     5    5       5        5         5            5          5         10   11    FAAAAA    VUAAAA    HHHHxx   
+6        978      0    2     6    6       6        6         6            6          6         12   13    GAAAAA    QLBAAA    OOOOxx   
+7        647      1    3     7    7       7        7         7            7          7         14   15    HAAAAA    XYAAAA    VVVVxx   
+8        653      0    0     8    8       8        8         8            8          8         16   17    IAAAAA    DZAAAA    HHHHxx   
+9        49       1    1     9    9       9        9         9            9          9         18   19    JAAAAA    XBAAAA    HHHHxx   
+QUERY: SELECT onek.unique1, onek.stringu1
+   WHERE onek.unique1 < 20
+   ORDER BY unique1 using >;
+unique1  stringu1  
+-------- --------- 
+19       TAAAAA    
+18       SAAAAA    
+17       RAAAAA    
+16       QAAAAA    
+15       PAAAAA    
+14       OAAAAA    
+13       NAAAAA    
+12       MAAAAA    
+11       LAAAAA    
+10       KAAAAA    
+9        JAAAAA    
+8        IAAAAA    
+7        HAAAAA    
+6        GAAAAA    
+5        FAAAAA    
+4        EAAAAA    
+3        DAAAAA    
+2        CAAAAA    
+1        BAAAAA    
+0        AAAAAA    
+QUERY: SELECT onek.unique1, onek.stringu1
+   WHERE onek.unique1 > 980
+   ORDER BY stringu1 using <;
+unique1  stringu1  
+-------- --------- 
+988      AMAAAA    
+989      BMAAAA    
+990      CMAAAA    
+991      DMAAAA    
+992      EMAAAA    
+993      FMAAAA    
+994      GMAAAA    
+995      HMAAAA    
+996      IMAAAA    
+997      JMAAAA    
+998      KMAAAA    
+999      LMAAAA    
+981      TLAAAA    
+982      ULAAAA    
+983      VLAAAA    
+984      WLAAAA    
+985      XLAAAA    
+986      YLAAAA    
+987      ZLAAAA    
+QUERY: SELECT onek.unique1, onek.string4
+   WHERE onek.unique1 > 980
+   ORDER BY string4 using <, unique1 using >;
+unique1  string4  
+-------- -------- 
+999      AAAAxx   
+995      AAAAxx   
+983      AAAAxx   
+982      AAAAxx   
+981      AAAAxx   
+998      HHHHxx   
+997      HHHHxx   
+993      HHHHxx   
+990      HHHHxx   
+986      HHHHxx   
+996      OOOOxx   
+991      OOOOxx   
+988      OOOOxx   
+987      OOOOxx   
+985      OOOOxx   
+994      VVVVxx   
+992      VVVVxx   
+989      VVVVxx   
+984      VVVVxx   
+QUERY: SELECT onek.unique1, onek.string4
+   WHERE onek.unique1 > 980
+   ORDER BY string4 using >, unique1 using <;
+unique1  string4  
+-------- -------- 
+984      VVVVxx   
+989      VVVVxx   
+992      VVVVxx   
+994      VVVVxx   
+985      OOOOxx   
+987      OOOOxx   
+988      OOOOxx   
+991      OOOOxx   
+996      OOOOxx   
+986      HHHHxx   
+990      HHHHxx   
+993      HHHHxx   
+997      HHHHxx   
+998      HHHHxx   
+981      AAAAxx   
+982      AAAAxx   
+983      AAAAxx   
+995      AAAAxx   
+999      AAAAxx   
+QUERY: SELECT onek.unique1, onek.string4
+   WHERE onek.unique1 < 20
+   ORDER BY unique1 using >, string4 using <;
+unique1  string4  
+-------- -------- 
+19       OOOOxx   
+18       VVVVxx   
+17       HHHHxx   
+16       OOOOxx   
+15       VVVVxx   
+14       AAAAxx   
+13       OOOOxx   
+12       AAAAxx   
+11       OOOOxx   
+10       AAAAxx   
+9        HHHHxx   
+8        HHHHxx   
+7        VVVVxx   
+6        OOOOxx   
+5        HHHHxx   
+4        HHHHxx   
+3        VVVVxx   
+2        OOOOxx   
+1        OOOOxx   
+0        OOOOxx   
+QUERY: SELECT onek.unique1, onek.string4
+   WHERE onek.unique1 < 20
+   ORDER BY unique1 using <, string4 using >;
+unique1  string4  
+-------- -------- 
+0        OOOOxx   
+1        OOOOxx   
+2        OOOOxx   
+3        VVVVxx   
+4        HHHHxx   
+5        HHHHxx   
+6        OOOOxx   
+7        VVVVxx   
+8        HHHHxx   
+9        HHHHxx   
+10       AAAAxx   
+11       OOOOxx   
+12       AAAAxx   
+13       OOOOxx   
+14       AAAAxx   
+15       VVVVxx   
+16       OOOOxx   
+17       HHHHxx   
+18       VVVVxx   
+19       OOOOxx   
+QUERY:     WHERE onek2.unique1 < 20
+    ORDER BY unique1 using >;
+WARN:parser: syntax error at or near "WHERE"
+
+QUERY: SELECT two, stringu1, ten, string4
+   INTO TABLE temp
+   FROM onek;
+QUERY: SELECT DISTINCT two FROM temp;
+two  
+---- 
+0    
+1    
+QUERY: SELECT DISTINCT ten FROM temp;
+ten  
+---- 
+0    
+1    
+2    
+3    
+4    
+5    
+6    
+7    
+8    
+9    
+QUERY: SELECT DISTINCT string4 FROM temp;
+string4  
+-------- 
+AAAAxx   
+HHHHxx   
+OOOOxx   
+VVVVxx   
+QUERY: SELECT DISTINCT two, string4, ten
+   FROM temp
+   ORDER BY two using <, string4 using <, ten using <;
+two  string4  ten  
+---- -------- ---- 
+0    AAAAxx   0    
+0    AAAAxx   2    
+0    AAAAxx   4    
+0    AAAAxx   6    
+0    AAAAxx   8    
+0    HHHHxx   0    
+0    HHHHxx   2    
+0    HHHHxx   4    
+0    HHHHxx   6    
+0    HHHHxx   8    
+0    OOOOxx   0    
+0    OOOOxx   2    
+0    OOOOxx   4    
+0    OOOOxx   6    
+0    OOOOxx   8    
+0    VVVVxx   0    
+0    VVVVxx   2    
+0    VVVVxx   4    
+0    VVVVxx   6    
+0    VVVVxx   8    
+1    AAAAxx   1    
+1    AAAAxx   3    
+1    AAAAxx   5    
+1    AAAAxx   7    
+1    AAAAxx   9    
+1    HHHHxx   1    
+1    HHHHxx   3    
+1    HHHHxx   5    
+1    HHHHxx   7    
+1    HHHHxx   9    
+1    OOOOxx   1    
+1    OOOOxx   3    
+1    OOOOxx   5    
+1    OOOOxx   7    
+1    OOOOxx   9    
+1    VVVVxx   1    
+1    VVVVxx   3    
+1    VVVVxx   5    
+1    VVVVxx   7    
+1    VVVVxx   9    
+QUERY: SELECT DISTINCT ON string4 two, string4, ten
+          FROM temp
+   ORDER BY two using <, string4 using <, ten using <;
+two  string4  ten  
+---- -------- ---- 
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   0    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   2    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   4    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   6    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    AAAAxx   8    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   0    
+0    HHHHxx   2    
+0    HHHHxx   2    
+0    HHHHxx   2    
+0    HHHHxx   2    
+0    HHHHxx   2    
+0    HHHHxx   2    
+0    HHHHxx   2    
+0    HHHHxx   2    
+0    HHHHxx   2    
+0    HHHHxx   2    
+0    HHHHxx   2    
+0    HHHHxx   2    
+0    HHHHxx   2    
+0    HHHHxx   2    
+0    HHHHxx   2    
+0    HHHHxx   2    
+0    HHHHxx   2    
+0    HHHHxx   2    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   4    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   6    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    HHHHxx   8    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   0    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   2    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   4    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   6    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    OOOOxx   8    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   0    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   2    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   4    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   6    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+0    VVVVxx   8    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   1    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   3    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   5    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   7    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    AAAAxx   9    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   1    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   3    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   5    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   7    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    HHHHxx   9    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   1    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   3    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   5    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   7    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    OOOOxx   9    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   1    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   3    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   5    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   7    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+1    VVVVxx   9    
+QUERY: SELECT *
+   INTO TABLE temp1
+   FROM temp
+   WHERE onek.unique1 < 2;
+QUERY: DROP TABLE temp1;
+QUERY: SELECT *
+   INTO TABLE temp1
+   FROM temp
+   WHERE onek2.unique1 < 2;
+QUERY: DROP TABLE temp1;
+QUERY: SELECT p.name, p.age FROM person* p;
+name     age  
+-------- ---- 
+mike     40   
+joe      20   
+sally    34   
+sandra   19   
+alex     30   
+sue      50   
+denise   24   
+sarah    88   
+teresa   38   
+nan      28   
+leah     68   
+wendy    78   
+melissa  28   
+joan     18   
+mary     8    
+jane     58   
+liza     38   
+jean     28   
+jenifer  38   
+juanita  58   
+susan    78   
+zena     98   
+martie   88   
+chris    78   
+pat      18   
+zola     58   
+louise   98   
+edna     18   
+bertha   88   
+sumi     38   
+koko     88   
+gina     18   
+rean     48   
+sharon   78   
+paula    68   
+julie    68   
+belinda  38   
+karen    48   
+carina   58   
+diane    18   
+esther   98   
+trudy    88   
+fanny    8    
+carmen   78   
+lita     25   
+pamela   48   
+sandy    38   
+trisha   88   
+vera     78   
+velma    68   
+sharon   25   
+sam      30   
+bill     20   
+fred     28   
+larry    60   
+jeff     23   
+cim      30   
+linda    19   
+QUERY: SELECT p.name, p.age FROM person* p ORDER BY age using >;
+name     age  
+-------- ---- 
+esther   98   
+louise   98   
+zena     98   
+martie   88   
+bertha   88   
+trisha   88   
+koko     88   
+sarah    88   
+trudy    88   
+vera     78   
+carmen   78   
+chris    78   
+sharon   78   
+susan    78   
+wendy    78   
+velma    68   
+leah     68   
+julie    68   
+paula    68   
+larry    60   
+carina   58   
+juanita  58   
+jane     58   
+zola     58   
+sue      50   
+karen    48   
+rean     48   
+pamela   48   
+mike     40   
+jenifer  38   
+sandy    38   
+teresa   38   
+liza     38   
+belinda  38   
+sumi     38   
+sally    34   
+alex     30   
+cim      30   
+sam      30   
+fred     28   
+nan      28   
+jean     28   
+melissa  28   
+sharon   25   
+lita     25   
+denise   24   
+jeff     23   
+joe      20   
+bill     20   
+linda    19   
+sandra   19   
+diane    18   
+edna     18   
+gina     18   
+joan     18   
+pat      18   
+fanny    8    
+mary     8    
+QUERY: SELECT DISTINCT p.age FROM person* p ORDER BY age using >;
+age  
+---- 
+98   
+88   
+78   
+68   
+60   
+58   
+50   
+48   
+40   
+38   
+34   
+30   
+28   
+25   
+24   
+23   
+20   
+19   
+18   
+8    
+QUERY: SELECT hash_i4_heap.*
+   WHERE hash_i4_heap.random = 843938989;
+seqno  random     
+------ ---------- 
+15     843938989  
+QUERY: SELECT hash_i4_heap.*
+   WHERE hash_i4_heap.random = 66766766;
+seqno  random  
+------ ------- 
+QUERY: SELECT hash_c16_heap.*
+   WHERE hash_c16_heap.random = '1505703298'::char16;
+seqno  random      
+------ ----------- 
+9838   1505703298  
+QUERY: SELECT hash_c16_heap.*
+   WHERE hash_c16_heap.random = '7777777'::char16;
+seqno  random  
+------ ------- 
+QUERY: SELECT hash_txt_heap.*
+   WHERE hash_txt_heap.random = '1351610853'::text;
+seqno  random      
+------ ----------- 
+5677   1351610853  
+QUERY: SELECT hash_txt_heap.*
+   WHERE hash_txt_heap.random = '111111112222222233333333'::text;
+seqno  random  
+------ ------- 
+QUERY: SELECT hash_f8_heap.*
+   WHERE hash_f8_heap.random = '444705537'::float8;
+seqno  random     
+------ ---------- 
+7853   444705537  
+QUERY: SELECT hash_f8_heap.*
+   WHERE hash_f8_heap.random = '88888888'::float8;
+seqno  random  
+------ ------- 
+QUERY: SELECT b.*
+   FROM bt_i4_heap b
+   WHERE b.seqno < 1;
+seqno  random      
+------ ----------- 
+0      1935401906  
+QUERY: SELECT b.*
+   FROM bt_i4_heap b
+   WHERE b.seqno >= 9999;
+seqno  random      
+------ ----------- 
+9999   1227676208  
+QUERY: SELECT b.*
+   FROM bt_i4_heap b
+   WHERE b.seqno = 4500;
+seqno  random      
+------ ----------- 
+4500   2080851358  
+QUERY: SELECT b.*
+   FROM bt_c16_heap b
+   WHERE b.seqno < '1'::char16;
+seqno  random      
+------ ----------- 
+0      1935401906  
+QUERY: SELECT b.*
+   FROM bt_c16_heap b
+   WHERE b.seqno >= '9999'::char16;
+seqno  random      
+------ ----------- 
+9999   1227676208  
+QUERY: SELECT b.*
+   FROM bt_c16_heap b
+   WHERE b.seqno = '4500'::char16;
+seqno  random      
+------ ----------- 
+4500   2080851358  
+QUERY: SELECT b.*
+   FROM bt_txt_heap b
+   WHERE b.seqno < '1'::text;
+seqno  random      
+------ ----------- 
+0      1935401906  
+QUERY: SELECT b.*
+   FROM bt_txt_heap b
+   WHERE b.seqno >= '9999'::text;
+seqno  random      
+------ ----------- 
+9999   1227676208  
+QUERY: SELECT b.*
+   FROM bt_txt_heap b
+   WHERE b.seqno = '4500'::text;
+seqno  random      
+------ ----------- 
+4500   2080851358  
+QUERY: SELECT b.*
+   FROM bt_f8_heap b
+   WHERE b.seqno < '1'::float8;
+seqno  random      
+------ ----------- 
+0      1935401906  
+QUERY: SELECT b.*
+   FROM bt_f8_heap b
+   WHERE b.seqno >= '9999'::float8;
+seqno  random      
+------ ----------- 
+9999   1227676208  
+QUERY: SELECT b.*
+   FROM bt_f8_heap b
+   WHERE b.seqno = '4500'::float8;
+seqno  random      
+------ ----------- 
+4500   2080851358  
+QUERY: UPDATE onek
+   SET unique1 = onek.unique1 + 1;
+QUERY: UPDATE onek
+   SET unique1 = onek.unique1 - 1;
+QUERY: UPDATE temp
+   SET stringu1 = reverse_c16(onek.stringu1)
+   WHERE onek.stringu1 = 'JBAAAA' and
+         onek.stringu1 = temp.stringu1;
+NOTICE:Non-functional update, only first update is performed
+NOTICE:Non-functional update, only first update is performed
+QUERY: UPDATE temp
+   SET stringu1 = reverse_c16(onek2.stringu1)
+   WHERE onek2.stringu1 = 'JCAAAA' and
+         onek2.stringu1 = temp.stringu1;
+NOTICE:Non-functional update, only first update is performed
+NOTICE:Non-functional update, only first update is performed
+QUERY: DROP TABLE temp;
+QUERY: UPDATE hash_i4_heap
+   SET random = 1
+   WHERE hash_i4_heap.seqno = 1492;
+QUERY: SELECT h.seqno AS i1492, h.random AS i1
+   FROM hash_i4_heap h
+   WHERE h.random = 1;
+i1492  i1  
+------ --- 
+1492   1   
+QUERY: UPDATE hash_i4_heap
+   SET seqno = 20000
+   WHERE hash_i4_heap.random = 1492795354;
+QUERY: SELECT h.seqno AS i20000
+   FROM hash_i4_heap h
+   WHERE h.random = 1492795354;
+i20000  
+------- 
+20000   
+QUERY: UPDATE hash_c16_heap
+   SET random = '0123456789abcdef'::char16
+   WHERE hash_c16_heap.seqno = 6543;
+QUERY: SELECT h.seqno AS i6543, h.random AS c0_to_f
+   FROM hash_c16_heap h
+   WHERE h.random = '0123456789abcdef'::char16;
+i6543  c0_to_f           
+------ ----------------- 
+6543   0123456789abcdef  
+QUERY: UPDATE hash_c16_heap
+   SET seqno = 20000
+   WHERE hash_c16_heap.random = '76652222'::char16;
+QUERY: SELECT h.seqno AS emptyset
+   FROM hash_c16_heap h
+   WHERE h.random = '76652222'::char16;
+emptyset  
+--------- 
+QUERY: UPDATE hash_txt_heap
+   SET random = '0123456789abcdefghijklmnop'::text
+   WHERE hash_txt_heap.seqno = 4002;
+QUERY: SELECT h.seqno AS i4002, h.random AS c0_to_p
+   FROM hash_txt_heap h
+   WHERE h.random = '0123456789abcdefghijklmnop'::text;
+i4002  c0_to_p                     
+------ --------------------------- 
+4002   0123456789abcdefghijklmnop  
+QUERY: UPDATE hash_txt_heap
+   SET seqno = 20000
+   WHERE hash_txt_heap.random = '959363399'::text;
+QUERY: SELECT h.seqno AS t20000
+   FROM hash_txt_heap h
+   WHERE h.random = '959363399'::text;
+t20000  
+------- 
+20000   
+QUERY: UPDATE hash_f8_heap
+   SET random = '-1234.1234'::float8
+   WHERE hash_f8_heap.seqno = 8906;
+QUERY: SELECT h.seqno AS i8096, h.random AS f1234_1234
+   FROM hash_f8_heap h
+   WHERE h.random = '-1234.1234'::float8;
+i8096  f1234_1234  
+------ ----------- 
+8906   -1234.1234  
+QUERY: UPDATE hash_f8_heap
+   SET seqno = 20000
+   WHERE hash_f8_heap.random = '488912369'::float8;
+QUERY: SELECT h.seqno AS f20000
+   FROM hash_f8_heap h
+   WHERE h.random = '488912369'::float8;
+f20000  
+------- 
+20000   
+QUERY: COPY onek TO '/home2/jolly/pg95-1.01/src/test/regress/obj/onek.data';
+QUERY: DELETE FROM onek;
+QUERY: COPY onek FROM '/home2/jolly/pg95-1.01/src/test/regress/obj/onek.data';
+QUERY: SELECT unique1 FROM onek WHERE unique1 < 2;
+unique1  
+-------- 
+0        
+1        
+QUERY: DELETE FROM onek2;
+QUERY: COPY onek2 FROM '/home2/jolly/pg95-1.01/src/test/regress/obj/onek.data';
+QUERY: SELECT unique1 FROM onek2 WHERE unique1 < 2;
+unique1  
+-------- 
+1        
+0        
+QUERY: COPY BINARY stud_emp TO '/home2/jolly/pg95-1.01/src/test/regress/obj/stud_emp.data';
+QUERY: DELETE FROM stud_emp;
+QUERY: COPY BINARY stud_emp FROM '/home2/jolly/pg95-1.01/src/test/regress/obj/stud_emp.data';
+QUERY: SELECT * FROM stud_emp;
+name   age  location    salary  manager  gpa  percent  
+------ ---- ----------- ------- -------- ---- -------- 
+jeff   23   (8,7.7)     600     sharon   3.5           
+cim    30   (10.5,4.7)  400              3.4           
+linda  19   (0.9,6.1)   100              2.9           
+QUERY: SELECT count(*) FROM onek;
+count  
+------ 
+1000   
+QUERY: SELECT count(*) FROM onek where oidrand(onek.oid, 10);
+count  
+------ 
+95     
+QUERY: SELECT count(*) FROM onek where oidrand(onek.oid, 10);
+count  
+------ 
+88     
+QUERY: BEGIN;
+QUERY: SELECT *
+   INTO TABLE xacttest
+   FROM aggtest;
+QUERY: INSERT INTO xacttest (a, b) VALUES (777, 777.777);
+QUERY: END;
+QUERY: SELECT a FROM xacttest WHERE a > 100;
+a    
+---- 
+777  
+QUERY: BEGIN;
+QUERY: CREATE TABLE disappear (a int4);
+QUERY: DELETE FROM aggtest;
+QUERY: SELECT * FROM aggtest;
+a  b  
+-- -- 
+QUERY: ABORT;
+QUERY: SELECT oid FROM pg_class WHERE relname = 'disappear';
+oid  
+---- 
+QUERY: SELECT * FROM aggtest;
+a  b  
+-- -- 
+QUERY: BEGIN;
+QUERY: DECLARE foo1 CURSOR FOR SELECT * FROM tenk1;
+QUERY: DECLARE foo2 CURSOR FOR SELECT * FROM tenk2;
+QUERY: DECLARE foo3 CURSOR FOR SELECT * FROM tenk1;
+QUERY: DECLARE foo4 CURSOR FOR SELECT * FROM tenk2;
+QUERY: DECLARE foo5 CURSOR FOR SELECT * FROM tenk1;
+QUERY: DECLARE foo6 CURSOR FOR SELECT * FROM tenk2;
+QUERY: DECLARE foo7 CURSOR FOR SELECT * FROM tenk1;
+QUERY: DECLARE foo8 CURSOR FOR SELECT * FROM tenk2;
+QUERY: DECLARE foo9 CURSOR FOR SELECT * FROM tenk1;
+QUERY: DECLARE foo10 CURSOR FOR SELECT * FROM tenk2;
+QUERY: DECLARE foo11 CURSOR FOR SELECT * FROM tenk1;
+QUERY: DECLARE foo12 CURSOR FOR SELECT * FROM tenk2;
+QUERY: DECLARE foo13 CURSOR FOR SELECT * FROM tenk1;
+QUERY: DECLARE foo14 CURSOR FOR SELECT * FROM tenk2;
+QUERY: DECLARE foo15 CURSOR FOR SELECT * FROM tenk1;
+QUERY: DECLARE foo16 CURSOR FOR SELECT * FROM tenk2;
+QUERY: DECLARE foo17 CURSOR FOR SELECT * FROM tenk1;
+QUERY: DECLARE foo18 CURSOR FOR SELECT * FROM tenk2;
+QUERY: DECLARE foo19 CURSOR FOR SELECT * FROM tenk1;
+QUERY: DECLARE foo20 CURSOR FOR SELECT * FROM tenk2;
+QUERY: DECLARE foo21 CURSOR FOR SELECT * FROM tenk1;
+QUERY: DECLARE foo22 CURSOR FOR SELECT * FROM tenk2;
+QUERY: DECLARE foo23 CURSOR FOR SELECT * FROM tenk1;
+QUERY: FETCH 1 in foo1;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+QUERY: FETCH 2 in foo2;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+QUERY: FETCH 3 in foo3;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+QUERY: FETCH 4 in foo4;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+QUERY: FETCH 5 in foo5;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+QUERY: FETCH 6 in foo6;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+QUERY: FETCH 7 in foo7;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+QUERY: FETCH 8 in foo8;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+QUERY: FETCH 9 in foo9;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+QUERY: FETCH 10 in foo10;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+QUERY: FETCH 11 in foo11;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+QUERY: FETCH 12 in foo12;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+QUERY: FETCH 13 in foo13;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+5222     12       0    2     2    2       22       222       1222         222        5222      44   45    WSAAAA    MAAAAA    AAAAxx   
+QUERY: FETCH 14 in foo14;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+5222     12       0    2     2    2       22       222       1222         222        5222      44   45    WSAAAA    MAAAAA    AAAAxx   
+6243     13       1    3     3    3       43       243       243          1243       6243      86   87    DGAAAA    NAAAAA    HHHHxx   
+QUERY: FETCH 15 in foo15;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+5222     12       0    2     2    2       22       222       1222         222        5222      44   45    WSAAAA    MAAAAA    AAAAxx   
+6243     13       1    3     3    3       43       243       243          1243       6243      86   87    DGAAAA    NAAAAA    HHHHxx   
+5471     14       1    3     1    11      71       471       1471         471        5471      142  143   LCAAAA    OAAAAA    OOOOxx   
+QUERY: FETCH 16 in foo16;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+5222     12       0    2     2    2       22       222       1222         222        5222      44   45    WSAAAA    MAAAAA    AAAAxx   
+6243     13       1    3     3    3       43       243       243          1243       6243      86   87    DGAAAA    NAAAAA    HHHHxx   
+5471     14       1    3     1    11      71       471       1471         471        5471      142  143   LCAAAA    OAAAAA    OOOOxx   
+5006     15       0    2     6    6       6        6         1006         6          5006      12   13    OKAAAA    PAAAAA    VVVVxx   
+QUERY: FETCH 17 in foo17;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+5222     12       0    2     2    2       22       222       1222         222        5222      44   45    WSAAAA    MAAAAA    AAAAxx   
+6243     13       1    3     3    3       43       243       243          1243       6243      86   87    DGAAAA    NAAAAA    HHHHxx   
+5471     14       1    3     1    11      71       471       1471         471        5471      142  143   LCAAAA    OAAAAA    OOOOxx   
+5006     15       0    2     6    6       6        6         1006         6          5006      12   13    OKAAAA    PAAAAA    VVVVxx   
+5387     16       1    3     7    7       87       387       1387         387        5387      174  175   FZAAAA    QAAAAA    AAAAxx   
+QUERY: FETCH 18 in foo18;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+5222     12       0    2     2    2       22       222       1222         222        5222      44   45    WSAAAA    MAAAAA    AAAAxx   
+6243     13       1    3     3    3       43       243       243          1243       6243      86   87    DGAAAA    NAAAAA    HHHHxx   
+5471     14       1    3     1    11      71       471       1471         471        5471      142  143   LCAAAA    OAAAAA    OOOOxx   
+5006     15       0    2     6    6       6        6         1006         6          5006      12   13    OKAAAA    PAAAAA    VVVVxx   
+5387     16       1    3     7    7       87       387       1387         387        5387      174  175   FZAAAA    QAAAAA    AAAAxx   
+5785     17       1    1     5    5       85       785       1785         785        5785      170  171   NOAAAA    RAAAAA    HHHHxx   
+QUERY: FETCH 19 in foo19;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+5222     12       0    2     2    2       22       222       1222         222        5222      44   45    WSAAAA    MAAAAA    AAAAxx   
+6243     13       1    3     3    3       43       243       243          1243       6243      86   87    DGAAAA    NAAAAA    HHHHxx   
+5471     14       1    3     1    11      71       471       1471         471        5471      142  143   LCAAAA    OAAAAA    OOOOxx   
+5006     15       0    2     6    6       6        6         1006         6          5006      12   13    OKAAAA    PAAAAA    VVVVxx   
+5387     16       1    3     7    7       87       387       1387         387        5387      174  175   FZAAAA    QAAAAA    AAAAxx   
+5785     17       1    1     5    5       85       785       1785         785        5785      170  171   NOAAAA    RAAAAA    HHHHxx   
+6621     18       1    1     1    1       21       621       621          1621       6621      42   43    RUAAAA    SAAAAA    OOOOxx   
+QUERY: FETCH 20 in foo20;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+5222     12       0    2     2    2       22       222       1222         222        5222      44   45    WSAAAA    MAAAAA    AAAAxx   
+6243     13       1    3     3    3       43       243       243          1243       6243      86   87    DGAAAA    NAAAAA    HHHHxx   
+5471     14       1    3     1    11      71       471       1471         471        5471      142  143   LCAAAA    OAAAAA    OOOOxx   
+5006     15       0    2     6    6       6        6         1006         6          5006      12   13    OKAAAA    PAAAAA    VVVVxx   
+5387     16       1    3     7    7       87       387       1387         387        5387      174  175   FZAAAA    QAAAAA    AAAAxx   
+5785     17       1    1     5    5       85       785       1785         785        5785      170  171   NOAAAA    RAAAAA    HHHHxx   
+6621     18       1    1     1    1       21       621       621          1621       6621      42   43    RUAAAA    SAAAAA    OOOOxx   
+6969     19       1    1     9    9       69       969       969          1969       6969      138  139   BIAAAA    TAAAAA    VVVVxx   
+QUERY: FETCH 21 in foo21;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+5222     12       0    2     2    2       22       222       1222         222        5222      44   45    WSAAAA    MAAAAA    AAAAxx   
+6243     13       1    3     3    3       43       243       243          1243       6243      86   87    DGAAAA    NAAAAA    HHHHxx   
+5471     14       1    3     1    11      71       471       1471         471        5471      142  143   LCAAAA    OAAAAA    OOOOxx   
+5006     15       0    2     6    6       6        6         1006         6          5006      12   13    OKAAAA    PAAAAA    VVVVxx   
+5387     16       1    3     7    7       87       387       1387         387        5387      174  175   FZAAAA    QAAAAA    AAAAxx   
+5785     17       1    1     5    5       85       785       1785         785        5785      170  171   NOAAAA    RAAAAA    HHHHxx   
+6621     18       1    1     1    1       21       621       621          1621       6621      42   43    RUAAAA    SAAAAA    OOOOxx   
+6969     19       1    1     9    9       69       969       969          1969       6969      138  139   BIAAAA    TAAAAA    VVVVxx   
+9460     20       0    0     0    0       60       460       1460         4460       9460      120  121   WZAAAA    UAAAAA    AAAAxx   
+QUERY: FETCH 22 in foo22;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+5222     12       0    2     2    2       22       222       1222         222        5222      44   45    WSAAAA    MAAAAA    AAAAxx   
+6243     13       1    3     3    3       43       243       243          1243       6243      86   87    DGAAAA    NAAAAA    HHHHxx   
+5471     14       1    3     1    11      71       471       1471         471        5471      142  143   LCAAAA    OAAAAA    OOOOxx   
+5006     15       0    2     6    6       6        6         1006         6          5006      12   13    OKAAAA    PAAAAA    VVVVxx   
+5387     16       1    3     7    7       87       387       1387         387        5387      174  175   FZAAAA    QAAAAA    AAAAxx   
+5785     17       1    1     5    5       85       785       1785         785        5785      170  171   NOAAAA    RAAAAA    HHHHxx   
+6621     18       1    1     1    1       21       621       621          1621       6621      42   43    RUAAAA    SAAAAA    OOOOxx   
+6969     19       1    1     9    9       69       969       969          1969       6969      138  139   BIAAAA    TAAAAA    VVVVxx   
+9460     20       0    0     0    0       60       460       1460         4460       9460      120  121   WZAAAA    UAAAAA    AAAAxx   
+59       21       1    3     9    19      59       59        59           59         59        118  119   HCAAAA    VAAAAA    HHHHxx   
+QUERY: FETCH 23 in foo23;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+5222     12       0    2     2    2       22       222       1222         222        5222      44   45    WSAAAA    MAAAAA    AAAAxx   
+6243     13       1    3     3    3       43       243       243          1243       6243      86   87    DGAAAA    NAAAAA    HHHHxx   
+5471     14       1    3     1    11      71       471       1471         471        5471      142  143   LCAAAA    OAAAAA    OOOOxx   
+5006     15       0    2     6    6       6        6         1006         6          5006      12   13    OKAAAA    PAAAAA    VVVVxx   
+5387     16       1    3     7    7       87       387       1387         387        5387      174  175   FZAAAA    QAAAAA    AAAAxx   
+5785     17       1    1     5    5       85       785       1785         785        5785      170  171   NOAAAA    RAAAAA    HHHHxx   
+6621     18       1    1     1    1       21       621       621          1621       6621      42   43    RUAAAA    SAAAAA    OOOOxx   
+6969     19       1    1     9    9       69       969       969          1969       6969      138  139   BIAAAA    TAAAAA    VVVVxx   
+9460     20       0    0     0    0       60       460       1460         4460       9460      120  121   WZAAAA    UAAAAA    AAAAxx   
+59       21       1    3     9    19      59       59        59           59         59        118  119   HCAAAA    VAAAAA    HHHHxx   
+8020     22       0    0     0    0       20       20        20           3020       8020      40   41    MWAAAA    WAAAAA    OOOOxx   
+QUERY: FETCH backward 1 in foo23;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+59       21       1    3     9    19      59       59        59           59         59        118  119   HCAAAA    VAAAAA    HHHHxx   
+QUERY: FETCH backward 2 in foo22;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+9460     20       0    0     0    0       60       460       1460         4460       9460      120  121   WZAAAA    UAAAAA    AAAAxx   
+6969     19       1    1     9    9       69       969       969          1969       6969      138  139   BIAAAA    TAAAAA    VVVVxx   
+QUERY: FETCH backward 3 in foo21;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+6969     19       1    1     9    9       69       969       969          1969       6969      138  139   BIAAAA    TAAAAA    VVVVxx   
+6621     18       1    1     1    1       21       621       621          1621       6621      42   43    RUAAAA    SAAAAA    OOOOxx   
+5785     17       1    1     5    5       85       785       1785         785        5785      170  171   NOAAAA    RAAAAA    HHHHxx   
+QUERY: FETCH backward 4 in foo20;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+6621     18       1    1     1    1       21       621       621          1621       6621      42   43    RUAAAA    SAAAAA    OOOOxx   
+5785     17       1    1     5    5       85       785       1785         785        5785      170  171   NOAAAA    RAAAAA    HHHHxx   
+5387     16       1    3     7    7       87       387       1387         387        5387      174  175   FZAAAA    QAAAAA    AAAAxx   
+5006     15       0    2     6    6       6        6         1006         6          5006      12   13    OKAAAA    PAAAAA    VVVVxx   
+QUERY: FETCH backward 5 in foo19;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+5785     17       1    1     5    5       85       785       1785         785        5785      170  171   NOAAAA    RAAAAA    HHHHxx   
+5387     16       1    3     7    7       87       387       1387         387        5387      174  175   FZAAAA    QAAAAA    AAAAxx   
+5006     15       0    2     6    6       6        6         1006         6          5006      12   13    OKAAAA    PAAAAA    VVVVxx   
+5471     14       1    3     1    11      71       471       1471         471        5471      142  143   LCAAAA    OAAAAA    OOOOxx   
+6243     13       1    3     3    3       43       243       243          1243       6243      86   87    DGAAAA    NAAAAA    HHHHxx   
+QUERY: FETCH backward 6 in foo18;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+5387     16       1    3     7    7       87       387       1387         387        5387      174  175   FZAAAA    QAAAAA    AAAAxx   
+5006     15       0    2     6    6       6        6         1006         6          5006      12   13    OKAAAA    PAAAAA    VVVVxx   
+5471     14       1    3     1    11      71       471       1471         471        5471      142  143   LCAAAA    OAAAAA    OOOOxx   
+6243     13       1    3     3    3       43       243       243          1243       6243      86   87    DGAAAA    NAAAAA    HHHHxx   
+5222     12       0    2     2    2       22       222       1222         222        5222      44   45    WSAAAA    MAAAAA    AAAAxx   
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+QUERY: FETCH backward 7 in foo17;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+5006     15       0    2     6    6       6        6         1006         6          5006      12   13    OKAAAA    PAAAAA    VVVVxx   
+5471     14       1    3     1    11      71       471       1471         471        5471      142  143   LCAAAA    OAAAAA    OOOOxx   
+6243     13       1    3     3    3       43       243       243          1243       6243      86   87    DGAAAA    NAAAAA    HHHHxx   
+5222     12       0    2     2    2       22       222       1222         222        5222      44   45    WSAAAA    MAAAAA    AAAAxx   
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+QUERY: FETCH backward 8 in foo16;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+5471     14       1    3     1    11      71       471       1471         471        5471      142  143   LCAAAA    OAAAAA    OOOOxx   
+6243     13       1    3     3    3       43       243       243          1243       6243      86   87    DGAAAA    NAAAAA    HHHHxx   
+5222     12       0    2     2    2       22       222       1222         222        5222      44   45    WSAAAA    MAAAAA    AAAAxx   
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+QUERY: FETCH backward 9 in foo15;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+6243     13       1    3     3    3       43       243       243          1243       6243      86   87    DGAAAA    NAAAAA    HHHHxx   
+5222     12       0    2     2    2       22       222       1222         222        5222      44   45    WSAAAA    MAAAAA    AAAAxx   
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+QUERY: FETCH backward 10 in foo14;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+5222     12       0    2     2    2       22       222       1222         222        5222      44   45    WSAAAA    MAAAAA    AAAAxx   
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+QUERY: FETCH backward 11 in foo13;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+1504     11       0    0     4    4       4        504       1504         1504       1504      8    9     WFAAAA    LAAAAA    VVVVxx   
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+QUERY: FETCH backward 12 in foo12;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+1314     10       0    2     4    14      14       314       1314         1314       1314      28   29    OYAAAA    KAAAAA    OOOOxx   
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+QUERY: FETCH backward 13 in foo11;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+3043     9        1    3     3    3       43       43        1043         3043       3043      86   87    BNAAAA    JAAAAA    HHHHxx   
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+QUERY: FETCH backward 14 in foo10;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+4321     8        1    1     1    1       21       321       321          4321       4321      42   43    FKAAAA    IAAAAA    AAAAxx   
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+QUERY: FETCH backward 15 in foo9;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+6701     7        1    1     1    1       1        701       701          1701       6701      2    3     TXAAAA    HAAAAA    VVVVxx   
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+QUERY: FETCH backward 16 in foo8;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+5057     6        1    1     7    17      57       57        1057         57         5057      114  115   NMAAAA    GAAAAA    OOOOxx   
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+QUERY: FETCH backward 17 in foo7;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8009     5        1    1     9    9       9        9         9            3009       8009      18   19    BWAAAA    FAAAAA    HHHHxx   
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+QUERY: FETCH backward 18 in foo6;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+7164     4        0    0     4    4       64       164       1164         2164       7164      128  129   OPAAAA    EAAAAA    AAAAxx   
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+QUERY: FETCH backward 19 in foo5;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+9850     3        0    2     0    10      50       850       1850         4850       9850      100  101   WOAAAA    DAAAAA    VVVVxx   
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+QUERY: FETCH backward 20 in foo4;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+3420     2        0    0     0    0       20       420       1420         3420       3420      40   41    OBAAAA    CAAAAA    OOOOxx   
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+QUERY: FETCH backward 21 in foo3;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+1891     1        1    3     1    11      91       891       1891         1891       1891      182  183   TUAAAA    BAAAAA    HHHHxx   
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+QUERY: FETCH backward 22 in foo2;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+8800     0        0    0     0    0       0        800       800          3800       8800      0    1     MAAAAA    AAAAAA    AAAAxx   
+QUERY: FETCH backward 23 in foo1;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+QUERY: CLOSE foo1;
+QUERY: CLOSE foo2;
+QUERY: CLOSE foo3;
+QUERY: CLOSE foo4;
+QUERY: CLOSE foo5;
+QUERY: CLOSE foo6;
+QUERY: CLOSE foo7;
+QUERY: CLOSE foo8;
+QUERY: CLOSE foo9;
+QUERY: CLOSE foo10;
+QUERY: CLOSE foo11;
+QUERY: CLOSE foo12;
+QUERY: end;
+QUERY: EXTEND INDEX onek2_u1_prtl WHERE onek2.unique1 <= 60;
+WARN:ExtendIndex: onek2_u1_prtl index not found
+QUERY: BEGIN;
+QUERY: DECLARE foo13 CURSOR FOR
+   SELECT * FROM onek WHERE unique1 = 50;
+QUERY: DECLARE foo14 CURSOR FOR
+   SELECT * FROM onek WHERE unique1 = 51;
+QUERY: DECLARE foo15 CURSOR FOR
+   SELECT * FROM onek WHERE unique1 = 52;
+QUERY: DECLARE foo16 CURSOR FOR
+   SELECT * FROM onek WHERE unique1 = 53;
+QUERY: DECLARE foo17 CURSOR FOR
+   SELECT * FROM onek WHERE unique1 = 54;
+QUERY: DECLARE foo18 CURSOR FOR
+   SELECT * FROM onek WHERE unique1 = 55;
+QUERY: DECLARE foo19 CURSOR FOR
+   SELECT * FROM onek WHERE unique1 = 56;
+QUERY: DECLARE foo20 CURSOR FOR
+   SELECT * FROM onek WHERE unique1 = 57;
+QUERY: DECLARE foo21 CURSOR FOR
+   SELECT * FROM onek WHERE unique1 = 58;
+QUERY: DECLARE foo22 CURSOR FOR
+   SELECT * FROM onek WHERE unique1 = 59;
+QUERY: DECLARE foo23 CURSOR FOR
+   SELECT * FROM onek WHERE unique1 = 60;
+QUERY: DECLARE foo24 CURSOR FOR
+   SELECT * FROM onek2 WHERE unique1 = 50;
+QUERY: DECLARE foo25 CURSOR FOR
+   SELECT * FROM onek2 WHERE unique1 = 60;
+QUERY: FETCH all in foo13;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+50       253      0    2     0    10      0        50        50           50         50        0    1     YBAAAA    TJAAAA    HHHHxx   
+QUERY: FETCH all in foo14;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+51       76       1    3     1    11      1        51        51           51         51        2    3     ZBAAAA    YCAAAA    AAAAxx   
+QUERY: FETCH all in foo15;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+52       985      0    0     2    12      2        52        52           52         52        4    5     ACAAAA    XLBAAA    HHHHxx   
+QUERY: FETCH all in foo16;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+53       196      1    1     3    13      3        53        53           53         53        6    7     BCAAAA    OHAAAA    AAAAxx   
+QUERY: FETCH all in foo17;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+54       356      0    2     4    14      4        54        54           54         54        8    9     CCAAAA    SNAAAA    AAAAxx   
+QUERY: FETCH all in foo18;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+55       627      1    3     5    15      5        55        55           55         55        10   11    DCAAAA    DYAAAA    VVVVxx   
+QUERY: FETCH all in foo19;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+56       54       0    0     6    16      6        56        56           56         56        12   13    ECAAAA    CCAAAA    OOOOxx   
+QUERY: FETCH all in foo20;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+57       942      1    1     7    17      7        57        57           57         57        14   15    FCAAAA    GKBAAA    OOOOxx   
+QUERY: FETCH all in foo21;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+58       114      0    2     8    18      8        58        58           58         58        16   17    GCAAAA    KEAAAA    OOOOxx   
+QUERY: FETCH all in foo22;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+59       593      1    3     9    19      9        59        59           59         59        18   19    HCAAAA    VWAAAA    HHHHxx   
+QUERY: FETCH all in foo23;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+60       483      0    0     0    0       0        60        60           60         60        0    1     ICAAAA    PSAAAA    VVVVxx   
+QUERY: FETCH all in foo24;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+50       253      0    2     0    10      0        50        50           50         50        0    1     YBAAAA    TJAAAA    HHHHxx   
+QUERY: FETCH all in foo25;
+unique1  unique2  two  four  ten  twenty  hundred  thousand  twothousand  fivethous  tenthous  odd  even  stringu1  stringu2  string4  
+-------- -------- ---- ----- ---- ------- -------- --------- ------------ ---------- --------- ---- ----- --------- --------- -------- 
+60       483      0    0     0    0       0        60        60           60         60        0    1     ICAAAA    PSAAAA    VVVVxx   
+QUERY: CLOSE foo13;
+QUERY: CLOSE foo14;
+QUERY: CLOSE foo15;
+QUERY: CLOSE foo16;
+QUERY: CLOSE foo17;
+QUERY: CLOSE foo18;
+QUERY: CLOSE foo19;
+QUERY: CLOSE foo20;
+QUERY: CLOSE foo21;
+QUERY: CLOSE foo22;
+QUERY: CLOSE foo23;
+QUERY: CLOSE foo24;
+QUERY: CLOSE foo25;
+QUERY: END;
+QUERY: PURGE hash_f8_heap BEFORE 'now';                -- absolute time
+SELECT count(*) AS has_10002 FROM hash_f8_heap[,] h;
+QUERY: VACUUM hash_f8_heap;
+QUERY: SELECT count(*) AS has_10000 FROM hash_f8_heap[,] h;
+has_10000  
+---------- 
+10002      
+QUERY: PURGE hash_i4_heap AFTER '@ 1 second ago';      -- relative time
+SELECT count(*) AS has_10002 FROM hash_i4_heap[,] h;
+QUERY: VACUUM hash_i4_heap;
+QUERY: SELECT count(*) AS has_10000 FROM hash_i4_heap[,] h;
+has_10000  
+---------- 
+10002      
+QUERY: CREATE TABLE temp (initial int4);
+QUERY: ALTER TABLE temp ADD COLUMN a int4;
+QUERY: ALTER TABLE temp ADD COLUMN b char16;
+QUERY: ALTER TABLE temp ADD COLUMN c text;
+QUERY: ALTER TABLE temp ADD COLUMN d float8;
+QUERY: ALTER TABLE temp ADD COLUMN e float4;
+QUERY: ALTER TABLE temp ADD COLUMN f int2;
+QUERY: ALTER TABLE temp ADD COLUMN g polygon;
+QUERY: ALTER TABLE temp ADD COLUMN h abstime;
+QUERY: ALTER TABLE temp ADD COLUMN i char;
+QUERY: ALTER TABLE temp ADD COLUMN j abstime[];
+QUERY: ALTER TABLE temp ADD COLUMN k dt;
+WARN:type name lookup of dt failed
+QUERY: ALTER TABLE temp ADD COLUMN l tid;
+QUERY: ALTER TABLE temp ADD COLUMN m xid;
+QUERY: ALTER TABLE temp ADD COLUMN n oid8;
+QUERY: ALTER TABLE temp ADD COLUMN p smgr;
+QUERY: ALTER TABLE temp ADD COLUMN q point;
+QUERY: ALTER TABLE temp ADD COLUMN r lseg;
+QUERY: ALTER TABLE temp ADD COLUMN s path;
+QUERY: ALTER TABLE temp ADD COLUMN t box;
+QUERY: ALTER TABLE temp ADD COLUMN u tinterval;
+QUERY: ALTER TABLE temp ADD COLUMN v oidint4;
+QUERY: ALTER TABLE temp ADD COLUMN w oidname;
+QUERY: ALTER TABLE temp ADD COLUMN x float8[];
+QUERY: ALTER TABLE temp ADD COLUMN y float4[];
+QUERY: ALTER TABLE temp ADD COLUMN z int2[];
+QUERY: INSERT INTO temp (a, b, c, d, e, f, g, h, i, j, k, l, m, n, p, q, r, s, t, u,
+       v, w, x, y, z)
+   VALUES (4, 'char16', 'text', 4.1, 4.1, 2, '(4.1,4.1,3.1,3.1)',
+        'Mon May  1 00:30:30 PDT 1995', 'c', '{Mon May  1 00:30:30 PDT 1995, Monday Aug 24 14:43:07 1992 PDT, epoch}',
+       314159, '(1,1)', 512,
+       '1 2 3 4 5 6 7 8', 'magnetic disk', '(1.1,1.1)', '(4.1,4.1,3.1,3.1)',
+       '(0,2,4.1,4.1,3.1,3.1)', '(4.1,4.1,3.1,3.1)', '["current" "infinity"]',
+       '1/3', '1,char16', '{1.0,2.0,3.0,4.0}', '{1.0,2.0,3.0,4.0}', '{1,2,3,4}');
+WARN:Relation temp does not have attribute k
+
+QUERY: SELECT * FROM temp;
+initial  a  b  c  d  e  f  g  h  i  j  l  m  n  p  q  r  s  t  u  v  w  x  y  z  
+-------- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
+QUERY: DROP TABLE temp;
+QUERY: CREATE TABLE temp (
+       initial         int4
+) ARCHIVE = light;
+QUERY: ALTER TABLE temp ADD COLUMN a int4;
+QUERY: ALTER TABLE temp ADD COLUMN b char16;
+QUERY: ALTER TABLE temp ADD COLUMN c text;
+QUERY: ALTER TABLE temp ADD COLUMN d float8;
+QUERY: ALTER TABLE temp ADD COLUMN e float4;
+QUERY: ALTER TABLE temp ADD COLUMN f int2;
+QUERY: ALTER TABLE temp ADD COLUMN g polygon;
+QUERY: ALTER TABLE temp ADD COLUMN h abstime;
+QUERY: ALTER TABLE temp ADD COLUMN i char;
+QUERY: ALTER TABLE temp ADD COLUMN j abstime[];
+QUERY: ALTER TABLE temp ADD COLUMN k dt;
+WARN:type name lookup of dt failed
+QUERY: ALTER TABLE temp ADD COLUMN l tid;
+QUERY: ALTER TABLE temp ADD COLUMN m xid;
+QUERY: ALTER TABLE temp ADD COLUMN n oid8;
+QUERY: ALTER TABLE temp ADD COLUMN p smgr;
+QUERY: ALTER TABLE temp ADD COLUMN q point;
+QUERY: ALTER TABLE temp ADD COLUMN r lseg;
+QUERY: ALTER TABLE temp ADD COLUMN s path;
+QUERY: ALTER TABLE temp ADD COLUMN t box;
+QUERY: ALTER TABLE temp ADD COLUMN u tinterval;
+QUERY: ALTER TABLE temp ADD COLUMN v oidint4;
+QUERY: ALTER TABLE temp ADD COLUMN w oidname;
+QUERY: ALTER TABLE temp ADD COLUMN x float8[];
+QUERY: ALTER TABLE temp ADD COLUMN y float4[];
+QUERY: ALTER TABLE temp ADD COLUMN z int2[];
+QUERY: INSERT INTO temp (a, b, c, d, e, f, g, h, i, j, k, l, m, n, p, q, r, s, t, u,
+       v, w, x, y, z)
+   VALUES (4, 'char16', 'text', 4.1, 4.1, 2, '(4.1,4.1,3.1,3.1)',
+       'Mon May  1 00:30:30 PDT 1995', 'c', '{Mon May  1 00:30:30 PDT 1995, Monday Aug 24 14:43:07 1992 PDT, epoch}',
+        314159, '(1,1)', 512,
+       '1 2 3 4 5 6 7 8', 'magnetic disk', '(1.1,1.1)', '(4.1,4.1,3.1,3.1)',
+       '(0,2,4.1,4.1,3.1,3.1)', '(4.1,4.1,3.1,3.1)', '["current" "infinity"]',
+       '1/3', '1,char16', '{1.0,2.0,3.0,4.0}', '{1.0,2.0,3.0,4.0}', '{1,2,3,4}');
+WARN:Relation temp does not have attribute k
+
+QUERY: SELECT * FROM temp[,];
+initial  a  b  c  d  e  f  g  h  i  j  l  m  n  p  q  r  s  t  u  v  w  x  y  z  
+-------- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
+QUERY: DROP TABLE temp;
+QUERY: ALTER TABLE tenk1 RENAME TO ten_k;
+QUERY: SELECT unique1 FROM ten_k WHERE unique1 < 20;
+unique1  
+-------- 
+0        
+1        
+2        
+3        
+4        
+5        
+6        
+7        
+8        
+9        
+10       
+11       
+12       
+13       
+14       
+15       
+16       
+17       
+18       
+19       
+QUERY: SELECT unique2 FROM ten_k WHERE unique2 < 20;
+unique2  
+-------- 
+0        
+1        
+2        
+3        
+4        
+5        
+6        
+7        
+8        
+9        
+10       
+11       
+12       
+13       
+14       
+15       
+16       
+17       
+18       
+19       
+QUERY: SELECT hundred FROM ten_k WHERE hundred = 50;
+hundred  
+-------- 
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+50       
+QUERY: ALTER TABLE ten_k RENAME TO tenk1;
+QUERY: SELECT unique1 FROM tenk1 WHERE unique1 < 5;
+unique1  
+-------- 
+0        
+1        
+2        
+3        
+4        
+QUERY: SELECT * from street;
+name                                thepath                                                                                                                                                                                                                                                                                                                cname      
+----------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------- 
+Whitlock Creek                      (0,2,-121.747,37.9128,-121.733,37)                                                                                                                                                                                                                                                                                     Oakland    
+Warm Springs                  Blvd  (0,2,-121.934,37,-121.934,37.97)                                                                                                                                                                                                                                                                                       Oakland    
+Tissiack                      Way   (0,2,-121.92,37,-121.921,37.995)                                                                                                                                                                                                                                                                                       Oakland    
+Mission                       Blvd  (0,3,-121.919,37,-121.919,37.976,-121.92,37.975)                                                                                                                                                                                                                                                                       Oakland    
+Theresa                       Way   (0,2,-121.729,37.906,-121.728,37.899)                                                                                                                                                                                                                                                                                  Oakland    
+Cowing                        Road  (0,2,-122,37.934,-121.977,37.782)                                                                                                                                                                                                                                                                                      Oakland    
+Rosedale                      Ct    (0,2,-121.923,37.9,-121.924,37.897)                                                                                                                                                                                                                                                                                    Oakland    
+Saginaw                       Ct    (0,2,-121.88,37.898,-121.881,37.901)                                                                                                                                                                                                                                                                                   Oakland    
+Pimlico                       Dr    (0,2,-121.862,37.998,-121.862,37.008)                                                                                                                                                                                                                                                                                  Oakland    
+Livermore                     Ave   (0,2,-121.769,37.448,-121.769,37.375)                                                                                                                                                                                                                                                                                  Oakland    
+Arroyo Las Positas                  (0,2,-121.797,37.997,-121.796,37.005)                                                                                                                                                                                                                                                                                  Oakland    
+Arlington                     Road  (0,2,-121.796,37.898,-121.796,37.906)                                                                                                                                                                                                                                                                                  Oakland    
+Juniper                       St    (0,2,-121.782,37.897,-121.781,37.9)                                                                                                                                                                                                                                                                                    Oakland    
+Fairview                      Ave   (0,2,-121.999,37.428,-121.986,37.351)                                                                                                                                                                                                                                                                                  Oakland    
+Sunol Ridge                   Trl   (0,2,-121.942,37.455,-121.934,37.38)                                                                                                                                                                                                                                                                                   Oakland    
+Vallecitos                    Road  (0,2,-121.87,37.916,-121.87,37.891)                                                                                                                                                                                                                                                                                    Oakland    
+Driscoll                      Road  (0,2,-121.948,37.403,-121.948,37.3999)                                                                                                                                                                                                                                                                                 Oakland    
+Apricot                       Lane  (0,2,-121.947,37.401,-121.946,37.392)                                                                                                                                                                                                                                                                                  Oakland    
+Calaveras Creek                     (0,2,-121.82,37.035,-121.821,37.931)                                                                                                                                                                                                                                                                                   Oakland    
+Livermore                     Ave   (0,2,-121.773,37.9909,-121.773,37.001)                                                                                                                                                                                                                                                                                 Oakland    
+Sp Railroad                         (0,2,-121.894,37.9901,-121.897,37.016)                                                                                                                                                                                                                                                                                 Oakland    
+Tassajara Creek                     (0,2,-121.879,37.989,-121.878,37.015)                                                                                                                                                                                                                                                                                  Oakland    
+Andrea                        Cir   (0,2,-121.733,37.8864,-121.733,37.9062)                                                                                                                                                                                                                                                                                Oakland    
+I- 580                              (0,5,-122.018,37.019,-122.001,37.032,-121.979,37.983,-121.958,37.984,-121.957,37.986)                                                                                                                                                                                                                                  Oakland    
+I- 580                        Ramp  (0,8,-121.937,37.986,-121.936,37.9883,-121.935,37.997,-121.935,37.0003,-121.935,37.006,-121.934,37.0003,-121.933,37.997,-121.932,37.989)                                                                                                                                                                               Oakland    
+I- 580                              (0,3,-121.932,37.989,-121.924,37.006,-121.922,37.014)                                                                                                                                                                                                                                                                  Oakland    
+I- 580/I-680                  Ramp  (1,2,-121.921,37.988,-121.919,37.016)                                                                                                                                                                                                                                                                                  Oakland    
+I- 580                        Ramp  (0,4,-121.904,37.998,-121.904,37.013,-121.903,37.0174,-121.903,37.018)                                                                                                                                                                                                                                                 Oakland    
+I- 580                        Ramp  (0,3,-121.874,37.014,-121.872,37.999,-121.871,37.999)                                                                                                                                                                                                                                                                  Oakland    
+I- 580                        Ramp  (0,5,-121.852,37.011,-121.848,37.999,-121.848,37.999,-121.846,37.01,-121.846,37.011)                                                                                                                                                                                                                                   Oakland    
+I- 680                              (0,7,-121.91,37.715,-121.911,37.7468,-121.912,37.764,-121.912,37.776,-121.917,37.905,-121.919,37.957,-121.921,37.988)                                                                                                                                                                                                  Oakland    
+I- 680                        Ramp  (0,5,-121.883,37.376,-121.883,37.392,-121.883,37.4,-121.883,37.402,-121.885,37.422)                                                                                                                                                                                                                                    Oakland    
+I- 680                        Ramp  (0,4,-121.92,37.438,-121.922,37.424,-121.924,37.408,-121.925,37.392)                                                                                                                                                                                                                                                   Oakland    
+I- 680                        Ramp  (0,3,-121.924,37.402,-121.923,37.395,-121.923,37.399)                                                                                                                                                                                                                                                                  Oakland    
+I- 680                              (1,16,-121.939,37.15,-121.939,37.145,-121.937,37.125,-121.934,37.0764,-121.934,37.0709,-121.934,37.068,-121.933,37.0614,-121.933,37.057,-121.932,37.0511,-121.932,37.0468,-121.93,37.027,-121.927,37,-121.927,37.998,-121.922,37.96,-121.92,37.949,-121.918,37.934)                                                    Oakland    
+State Hwy 84                        (0,5,-121.957,37.898,-121.957,37.8991,-121.957,37.903,-121.956,37.91,-121.955,37.919)                                                                                                                                                                                                                                  Oakland    
+Whitlock Creek                      (0,2,-121.747,37.9128,-121.733,37)                                                                                                                                                                                                                                                                                     Oakland    
+Warm Springs                  Blvd  (0,2,-121.934,37,-121.934,37.97)                                                                                                                                                                                                                                                                                       Oakland    
+Tissiack                      Way   (0,2,-121.92,37,-121.921,37.995)                                                                                                                                                                                                                                                                                       Oakland    
+Mission                       Blvd  (0,3,-121.919,37,-121.919,37.976,-121.92,37.975)                                                                                                                                                                                                                                                                       Oakland    
+Kildare                       Road  (0,2,-122.097,37.016,-122.096,37)                                                                                                                                                                                                                                                                                      Oakland    
+Ranspot                       Dr    (0,2,-122.097,37.999,-122.096,37)                                                                                                                                                                                                                                                                                      Oakland    
+Butterfield                   Dr    (0,2,-122.084,37.002,-122.083,37.987)                                                                                                                                                                                                                                                                                  Oakland    
+Hesperian                     Blvd  (0,3,-122.097,37.333,-122.096,37.31,-122.095,37.293)                                                                                                                                                                                                                                                                   Oakland    
+Thackeray                     Ave   (0,2,-122.072,37.305,-122.072,37.298)                                                                                                                                                                                                                                                                                  Oakland    
+Celia                         St    (0,2,-122.061,37.3,-122.062,37.299)                                                                                                                                                                                                                                                                                    Oakland    
+Periwinkle                    Road  (0,2,-122.045,37.301,-122.045,37.2984)                                                                                                                                                                                                                                                                                 Oakland    
+Bridgepointe                  Dr    (0,2,-122.051,37.305,-122.051,37.299)                                                                                                                                                                                                                                                                                  Oakland    
+Crystaline                    Dr    (0,2,-121.926,37,-121.926,37.0053)                                                                                                                                                                                                                                                                                     Oakland    
+Paseo Padre                   Pkwy  (0,2,-121.914,37.005,-121.914,37)                                                                                                                                                                                                                                                                                      Oakland    
+Oakridge                      Road  (0,2,-121.832,37.049,-121.828,37)                                                                                                                                                                                                                                                                                      Oakland    
+Railroad                      Ave   (0,3,-122.025,37.013,-122.023,37.003,-122.022,37.993)                                                                                                                                                                                                                                                                  Oakland    
+Eden Creek                          (0,2,-122.022,37.0067,-122.022,37.998)                                                                                                                                                                                                                                                                                 Oakland    
+I- 880                              (0,10,-122.083,37.312,-122.082,37.296,-122.081,37.285,-122.079,37.248,-122.078,37.24,-122.078,37.235,-122.077,37.2257,-122.077,37.2203,-122.076,37.215,-122.076,37.209)                                                                                                                                                Oakland    
+I- 880                        Ramp  (0,5,-122.004,37.313,-122.004,37.308,-122.004,37.284,-122.001,37.287,-121.999,37.289)                                                                                                                                                                                                                                  Oakland    
+I- 880                        Ramp  (0,2,-122.002,37.301,-122.002,37.293)                                                                                                                                                                                                                                                                                  Oakland    
+I- 880                              (1,6,-121.967,37.075,-121.966,37.071,-121.966,37.065,-121.962,37.037,-121.957,37,-121.948,37.933)                                                                                                                                                                                                                      Oakland    
+I- 680                              (1,16,-121.939,37.15,-121.939,37.145,-121.937,37.125,-121.934,37.0764,-121.934,37.0709,-121.934,37.068,-121.933,37.0614,-121.933,37.057,-121.932,37.0511,-121.932,37.0468,-121.93,37.027,-121.927,37,-121.927,37.998,-121.922,37.96,-121.92,37.949,-121.918,37.934)                                                    Oakland    
+Wisconsin                     St    (0,3,-122.199,37.017,-122.198,37.998,-122.197,37.994)                                                                                                                                                                                                                                                                  Oakland    
+Herrier                       St    (0,2,-122.194,37.006,-122.194,37.998)                                                                                                                                                                                                                                                                                  Oakland    
+Skyline                       Blvd  (0,2,-122.174,37.01,-122.171,37.996)                                                                                                                                                                                                                                                                                   Oakland    
+Coliseum                      Way   (0,2,-122.2,37.47,-122.198,37.516)                                                                                                                                                                                                                                                                                     Oakland    
+Hegenberger                   Exwy  (0,2,-122.195,37.52,-122.195,37.497)                                                                                                                                                                                                                                                                                   Oakland    
+Sp Railroad                         (0,2,-122.195,37.497,-122.193,37.4848)                                                                                                                                                                                                                                                                                 Oakland    
+85th                          Ave   (0,2,-122.188,37.466,-122.186,37.476)                                                                                                                                                                                                                                                                                  Oakland    
+E                             St    (0,3,-122.183,37.505,-122.183,37.498,-122.182,37.49)                                                                                                                                                                                                                                                                   Oakland    
+D                             St    (0,2,-122.181,37.505,-122.18,37.497)                                                                                                                                                                                                                                                                                   Oakland    
+Birch                         St    (0,2,-122.167,37.509,-122.166,37.492)                                                                                                                                                                                                                                                                                  Oakland    
+Bancroft                      Ave   (0,3,-122.164,37.523,-122.163,37.508,-122.162,37.493)                                                                                                                                                                                                                                                                  Oakland    
+Avenue 140th                        (0,2,-122.166,37.003,-122.169,37.988)                                                                                                                                                                                                                                                                                  Oakland    
+Redwood                       Road  (0,2,-122.149,37.98,-122.144,37.001)                                                                                                                                                                                                                                                                                   Oakland    
+98th                          Ave   (0,2,-122.157,37.498,-122.156,37.502)                                                                                                                                                                                                                                                                                  Oakland    
+Cameron                       Ave   (0,2,-122.132,37.502,-122.133,37.481)                                                                                                                                                                                                                                                                                  Oakland    
+Locust                        St    (0,2,-122.161,37.007,-122.159,37.987)                                                                                                                                                                                                                                                                                  Oakland    
+McClure                       Ave   (0,2,-122.143,37.001,-122.144,37.998)                                                                                                                                                                                                                                                                                  Oakland    
+Maubert                       Ave   (0,2,-122.111,37.009,-122.11,37.995)                                                                                                                                                                                                                                                                                   Oakland    
+Ranspot                       Dr    (0,2,-122.097,37.999,-122.096,37)                                                                                                                                                                                                                                                                                      Oakland    
+Butterfield                   Dr    (0,2,-122.084,37.002,-122.083,37.987)                                                                                                                                                                                                                                                                                  Oakland    
+National                      Ave   (0,2,-122.119,37.5,-122.128,37.489)                                                                                                                                                                                                                                                                                    Oakland    
+Broadmore                     Ave   (0,2,-122.095,37.522,-122.094,37.497)                                                                                                                                                                                                                                                                                  Oakland    
+Skyline                       Dr    (0,2,-122.028,37.5,-122.028,37.498)                                                                                                                                                                                                                                                                                    Oakland    
+Decoto                        Road  (0,3,-122.016,37.006,-122.016,37.002,-122.016,37.993)                                                                                                                                                                                                                                                                  Oakland    
+Chapman                       Dr    (0,2,-122.042,37.504,-122.041,37.498)                                                                                                                                                                                                                                                                                  Oakland    
+Charles                       St    (0,2,-122.025,37.505,-122.025,37.499)                                                                                                                                                                                                                                                                                  Oakland    
+Mattos                        Dr    (0,2,-122.001,37.502,-122.001,37.4968)                                                                                                                                                                                                                                                                                 Oakland    
+Sp Railroad                         (0,3,-122.138,37.003,-122.136,37.992,-122.131,37.9461)                                                                                                                                                                                                                                                                 Oakland    
+Railroad                      Ave   (0,3,-122.025,37.013,-122.023,37.003,-122.022,37.993)                                                                                                                                                                                                                                                                  Oakland    
+Eden Creek                          (0,2,-122.022,37.0067,-122.022,37.998)                                                                                                                                                                                                                                                                                 Oakland    
+I- 880                              (0,19,-122.175,37.185,-122.175,37.178,-122.174,37.173,-122.169,37.126,-122.168,37.1159,-122.168,37.1144,-122.167,37.111,-122.165,37.1,-122.165,37.0981,-122.164,37.092,-122.16,37.061,-122.158,37.0528,-122.156,37.0366,-122.153,37.017,-122.148,37.98,-122.141,37.932,-122.139,37.924,-122.139,37.92,-122.138,37.91)  Oakland    
+I- 580                              (0,14,-122.111,37.023,-122.11,37.02,-122.108,37.0076,-122.108,37.007,-122.107,37.998,-122.106,37.994,-122.105,37.982,-122.105,37.977,-122.103,37.958,-122.103,37.953,-122.101,37.938,-122.099,37.911,-122.098,37.91,-122.098,37.908)                                                                                   Oakland    
+I- 880                              (0,7,-122.098,37.528,-122.096,37.496,-122.093,37.453,-122.093,37.4496,-122.09,37.4144,-122.09,37.405,-122.085,37.34)                                                                                                                                                                                                   Oakland    
+I- 880                        Ramp  (0,3,-122.062,37.011,-122.063,37.982,-122.058,37.967)                                                                                                                                                                                                                                                                  Oakland    
+I- 880                              (0,12,-122.061,37.003,-122.06,37.991,-122.06,37.982,-122.058,37.967,-122.058,37.961,-122.055,37.918,-122.054,37.8948,-122.051,37.8546,-122.05,37.844,-122.049,37.817,-122.048,37.813,-122.048,37.811)                                                                                                                  Oakland    
+I- 880                              (0,12,-122.037,37.632,-122.036,37.619,-122.036,37.616,-122.035,37.6041,-122.032,37.5797,-122.031,37.5733,-122.03,37.5637,-122.029,37.557,-122.029,37.5493,-122.028,37.5391,-122.026,37.517,-122.024,37.491)                                                                                                            Oakland    
+Cornell                       Ave   (0,3,-122.296,37.925,-122.295,37.906,-122.294,37.875)                                                                                                                                                                                                                                                                  Berkeley   
+Euclid                        Ave   (0,2,-122.267,37.009,-122.267,37.987)                                                                                                                                                                                                                                                                                  Berkeley   
+Marin                         Ave   (0,2,-122.274,37.894,-122.272,37.901)                                                                                                                                                                                                                                                                                  Berkeley   
+Sacramento                    St    (0,2,-122.28,37.606,-122.28,37.597)                                                                                                                                                                                                                                                                                    Berkeley   
+Martin Luther King Jr         Way   (0,2,-122.271,37.608,-122.271,37.599)                                                                                                                                                                                                                                                                                  Berkeley   
+Shoreline                     Dr    (0,2,-122.266,37.603,-122.265,37.6)                                                                                                                                                                                                                                                                                    Berkeley   
+Creston                       Road  (0,4,-122.264,37.002,-122.261,37.986,-122.26,37.978,-122.26,37.973)                                                                                                                                                                                                                                                    Berkeley   
+Keeler                        Ave   (0,2,-122.258,37.906,-122.258,37.899)                                                                                                                                                                                                                                                                                  Berkeley   
+Lakeshore                     Ave   (0,2,-122.259,37.99,-122.256,37.006)                                                                                                                                                                                                                                                                                   Berkeley   
+Oakland Inner Harbor                (0,2,-122.263,37.913,-122.26,37.8948)                                                                                                                                                                                                                                                                                  Berkeley   
+Wp Railroad                         (0,2,-122.254,37.902,-122.251,37.891)                                                                                                                                                                                                                                                                                  Berkeley   
+Foothill                      Blvd  (0,2,-122.241,37.9,-122.24,37.893)                                                                                                                                                                                                                                                                                     Berkeley   
+19th                          Ave   (0,2,-122.237,37.897,-122.236,37.905)                                                                                                                                                                                                                                                                                  Berkeley   
+Dimond                        Ave   (0,2,-122.217,37.994,-122.216,37.006)                                                                                                                                                                                                                                                                                  Berkeley   
+Champion                      St    (0,2,-122.214,37.991,-122.215,37.002)                                                                                                                                                                                                                                                                                  Berkeley   
+Laguna                        Ave   (0,2,-122.21,37.989,-122.209,37)                                                                                                                                                                                                                                                                                       Berkeley   
+Wisconsin                     St    (0,3,-122.199,37.017,-122.198,37.998,-122.197,37.994)                                                                                                                                                                                                                                                                  Berkeley   
+Herrier                       St    (0,2,-122.194,37.006,-122.194,37.998)                                                                                                                                                                                                                                                                                  Berkeley   
+Redding                       St    (0,2,-122.198,37.901,-122.198,37.895)                                                                                                                                                                                                                                                                                  Berkeley   
+Carson                        St    (0,2,-122.185,37.9,-122.184,37.901)                                                                                                                                                                                                                                                                                    Berkeley   
+Skyline                       Blvd  (0,2,-122.174,37.01,-122.171,37.996)                                                                                                                                                                                                                                                                                   Berkeley   
+Campus                        Dr    (0,3,-122.17,37.905,-122.168,37.868,-122.167,37.865)                                                                                                                                                                                                                                                                   Berkeley   
+Broadway                            (0,2,-122.241,37.586,-122.24,37.601)                                                                                                                                                                                                                                                                                   Berkeley   
+Coliseum                      Way   (0,3,-122.211,37.626,-122.209,37.592,-122.206,37.568)                                                                                                                                                                                                                                                                  Berkeley   
+82nd                          Ave   (0,2,-122.169,37.596,-122.168,37.603)                                                                                                                                                                                                                                                                                  Berkeley   
+Avenue 140th                        (0,2,-122.166,37.003,-122.169,37.988)                                                                                                                                                                                                                                                                                  Berkeley   
+Parkridge                     Dr    (0,2,-122.144,37.884,-122.143,37.9)                                                                                                                                                                                                                                                                                    Berkeley   
+Cull Creek                          (0,2,-122.062,37.875,-122.058,37.527)                                                                                                                                                                                                                                                                                  Berkeley   
+Locust                        St    (0,2,-122.161,37.007,-122.159,37.987)                                                                                                                                                                                                                                                                                  Berkeley   
+McClure                       Ave   (0,2,-122.143,37.001,-122.144,37.998)                                                                                                                                                                                                                                                                                  Berkeley   
+Maubert                       Ave   (0,2,-122.111,37.009,-122.11,37.995)                                                                                                                                                                                                                                                                                   Berkeley   
+Ranspot                       Dr    (0,2,-122.097,37.999,-122.096,37)                                                                                                                                                                                                                                                                                      Berkeley   
+Butterfield                   Dr    (0,2,-122.084,37.002,-122.083,37.987)                                                                                                                                                                                                                                                                                  Berkeley   
+Crow Canyon Creek                   (0,2,-122.043,37.905,-122.037,37.71)                                                                                                                                                                                                                                                                                   Berkeley   
+Skywest                       Dr    (0,2,-122.116,37.62,-122.112,37.586)                                                                                                                                                                                                                                                                                   Berkeley   
+Hesperian                     Blvd  (0,2,-122.113,37.6,-122.112,37.586)                                                                                                                                                                                                                                                                                    Berkeley   
+Sp Railroad                         (0,3,-122.091,37.601,-122.087,37.56,-122.086,37.5551)                                                                                                                                                                                                                                                                  Berkeley   
+Jackson                       St    (0,2,-122.085,37.6,-122.084,37.606)                                                                                                                                                                                                                                                                                    Berkeley   
+Joyce                         St    (0,2,-122.079,37.604,-122.077,37.581)                                                                                                                                                                                                                                                                                  Berkeley   
+San Andreas                   Dr    (0,2,-122.061,37.9,-122.061,37.895)                                                                                                                                                                                                                                                                                    Berkeley   
+West Loop                     Road  (0,2,-122.058,37.604,-122.06,37.586)                                                                                                                                                                                                                                                                                   Berkeley   
+Parkside                      Dr    (0,2,-122.047,37.603,-122.044,37.596)                                                                                                                                                                                                                                                                                  Berkeley   
+Arizona                       St    (0,2,-122.038,37.901,-122.037,37.898)                                                                                                                                                                                                                                                                                  Berkeley   
+Decoto                        Road  (0,3,-122.016,37.006,-122.016,37.002,-122.016,37.993)                                                                                                                                                                                                                                                                  Berkeley   
+Oneil                         Ave   (0,2,-122.077,37.6248,-122.075,37.595)                                                                                                                                                                                                                                                                                 Berkeley   
+Sp Railroad                         (0,3,-122.138,37.003,-122.136,37.992,-122.131,37.9461)                                                                                                                                                                                                                                                                 Berkeley   
+Railroad                      Ave   (0,3,-122.025,37.013,-122.023,37.003,-122.022,37.993)                                                                                                                                                                                                                                                                  Berkeley   
+Lakehurst                     Cir   (0,2,-122.285,37.8903,-122.286,37.9036)                                                                                                                                                                                                                                                                                Berkeley   
+Eden Creek                          (0,2,-122.022,37.0067,-122.022,37.998)                                                                                                                                                                                                                                                                                 Berkeley   
+I- 880                              (0,17,-122.271,37.975,-122.269,37.972,-122.268,37.966,-122.267,37.962,-122.266,37.957,-122.265,37.952,-122.264,37.946,-122.263,37.935,-122.262,37.927,-122.261,37.921,-122.259,37.916,-122.258,37.911,-122.254,37.898,-122.243,37.858,-122.241,37.845,-122.239,37.827,-122.237,37.811)                                 Berkeley   
+I- 880                        Ramp  (0,2,-122.254,37.898,-122.254,37.902)                                                                                                                                                                                                                                                                                  Berkeley   
+I- 580                              (0,12,-122.22,37.99,-122.22,37.99,-122.222,37.9952,-122.223,37.998,-122.224,37.9996,-122.226,37.003,-122.228,37.007,-122.23,37.026,-122.232,37.043,-122.234,37.059,-122.235,37.0643,-122.237,37.07)                                                                                                                    Berkeley   
+I- 880                              (0,13,-122.221,37.711,-122.22,37.699,-122.22,37.695,-122.219,37.682,-122.218,37.672,-122.217,37.652,-122.216,37.638,-122.214,37.616,-122.214,37.612,-122.213,37.609,-122.212,37.592,-122.212,37.586,-122.211,37.581)                                                                                                   Berkeley   
+I- 880                              (0,19,-122.175,37.185,-122.175,37.178,-122.174,37.173,-122.169,37.126,-122.168,37.1159,-122.168,37.1144,-122.167,37.111,-122.165,37.1,-122.165,37.0981,-122.164,37.092,-122.16,37.061,-122.158,37.0528,-122.156,37.0366,-122.153,37.017,-122.148,37.98,-122.141,37.932,-122.139,37.924,-122.139,37.92,-122.138,37.91)  Berkeley   
+I- 880                        Ramp  (0,8,-122.138,37.931,-122.138,37.9274,-122.137,37.925,-122.137,37.924,-122.137,37.914,-122.136,37.905,-122.136,37.908,-122.136,37.898)                                                                                                                                                                                 Berkeley   
+I- 880                              (0,17,-122.136,37.902,-122.136,37.898,-122.133,37.881,-122.132,37.874,-122.131,37.866,-122.131,37.865,-122.131,37.864,-122.129,37.851,-122.128,37.843,-122.126,37.834,-122.123,37.812,-122.117,37.766,-122.11,37.72,-122.11,37.7109,-122.109,37.702,-122.108,37.6917,-122.108,37.681)                                  Berkeley   
+I- 580                              (0,14,-122.111,37.023,-122.11,37.02,-122.108,37.0076,-122.108,37.007,-122.107,37.998,-122.106,37.994,-122.105,37.982,-122.105,37.977,-122.103,37.958,-122.103,37.953,-122.101,37.938,-122.099,37.911,-122.098,37.91,-122.098,37.908)                                                                                   Berkeley   
+I- 580                        Ramp  (0,4,-122.109,37.003,-122.107,37.993,-122.107,37.992,-122.105,37.982)                                                                                                                                                                                                                                                  Berkeley   
+I- 580                        Ramp  (0,3,-122.101,37.898,-122.1,37.902,-122.099,37.911)                                                                                                                                                                                                                                                                    Berkeley   
+I- 580                        Ramp  (0,3,-122.096,37.888,-122.096,37.891,-122.096,37.9)                                                                                                                                                                                                                                                                    Berkeley   
+I- 880                        Ramp  (0,3,-122.103,37.61,-122.101,37.587,-122.1,37.569)                                                                                                                                                                                                                                                                     Berkeley   
+I- 880                              (0,12,-122.061,37.003,-122.06,37.991,-122.06,37.982,-122.058,37.967,-122.058,37.961,-122.055,37.918,-122.054,37.8948,-122.051,37.8546,-122.05,37.844,-122.049,37.817,-122.048,37.813,-122.048,37.811)                                                                                                                  Berkeley   
+I- 880                        Ramp  (0,3,-122.059,37.982,-122.058,37.984,-122.061,37.003)                                                                                                                                                                                                                                                                  Berkeley   
+I- 880                              (0,12,-122.037,37.632,-122.036,37.619,-122.036,37.616,-122.035,37.6041,-122.032,37.5797,-122.031,37.5733,-122.03,37.5637,-122.029,37.557,-122.029,37.5493,-122.028,37.5391,-122.026,37.517,-122.024,37.491)                                                                                                            Berkeley   
+I- 580                        Ramp  (0,3,-122.093,37.9035,-122.094,37.8963,-122.094,37.8921)                                                                                                                                                                                                                                                               Berkeley   
+State Hwy 13                        (0,9,-122.18,37.943,-122.18,37.9185,-122.18,37.9,-122.179,37.8661,-122.179,37.862,-122.178,37.851,-122.178,37.845,-122.177,37.839,-122.177,37.833)                                                                                                                                                                     Berkeley   
+State Hwy 238                       (1,8,-122.098,37.908,-122.098,37.907,-122.099,37.905,-122.101,37.898,-122.102,37.8971,-122.103,37.8944,-122.105,37.892,-122.106,37.89)                                                                                                                                                                                 Berkeley   
+Euclid                        Ave   (0,2,-122.267,37.009,-122.267,37.987)                                                                                                                                                                                                                                                                                  Lafayette  
+Hollis                        St    (0,2,-122.288,37.397,-122.289,37.414)                                                                                                                                                                                                                                                                                  Lafayette  
+5th                           St    (0,3,-122.278,37,-122.279,37.005,-122.28,37.009)                                                                                                                                                                                                                                                                       Lafayette  
+Creston                       Road  (0,4,-122.264,37.002,-122.261,37.986,-122.26,37.978,-122.26,37.973)                                                                                                                                                                                                                                                    Lafayette  
+Ada                           St    (0,2,-122.249,37.398,-122.25,37.401)                                                                                                                                                                                                                                                                                   Lafayette  
+Sheridan                      Road  (0,3,-122.228,37.425,-122.225,37.411,-122.222,37.377)                                                                                                                                                                                                                                                                  Lafayette  
+Proctor                       Ave   (0,2,-122.227,37.406,-122.225,37.386)                                                                                                                                                                                                                                                                                  Lafayette  
+Capricorn                     Ave   (0,2,-122.218,37.404,-122.216,37.384)                                                                                                                                                                                                                                                                                  Lafayette  
+Taurus                        Ave   (0,2,-122.216,37.416,-122.213,37.389)                                                                                                                                                                                                                                                                                  Lafayette  
+Lakeshore                     Ave   (0,2,-122.259,37.99,-122.256,37.006)                                                                                                                                                                                                                                                                                   Lafayette  
+Dimond                        Ave   (0,2,-122.217,37.994,-122.216,37.006)                                                                                                                                                                                                                                                                                  Lafayette  
+Indian                        Way   (0,2,-122.207,37.398,-122.204,37.411)                                                                                                                                                                                                                                                                                  Lafayette  
+Champion                      St    (0,2,-122.214,37.991,-122.215,37.002)                                                                                                                                                                                                                                                                                  Lafayette  
+Laguna                        Ave   (0,2,-122.21,37.989,-122.209,37)                                                                                                                                                                                                                                                                                       Lafayette  
+California                    St    (0,2,-122.203,37.005,-122.202,37.996)                                                                                                                                                                                                                                                                                  Lafayette  
+Edgewater                     Dr    (0,2,-122.201,37.379,-122.204,37.41)                                                                                                                                                                                                                                                                                   Lafayette  
+I- 880                        Ramp  (0,2,-122.277,37.002,-122.278,37)                                                                                                                                                                                                                                                                                      Lafayette  
+I- 580                              (0,12,-122.22,37.99,-122.22,37.99,-122.222,37.9952,-122.223,37.998,-122.224,37.9996,-122.226,37.003,-122.228,37.007,-122.23,37.026,-122.232,37.043,-122.234,37.059,-122.235,37.0643,-122.237,37.07)                                                                                                                    Lafayette  
+State Hwy 13                  Ramp  (0,4,-122.224,37.427,-122.223,37.414,-122.221,37.396,-122.221,37.388)                                                                                                                                                                                                                                                  Lafayette  
+QUERY: SELECT * from iexit;
+name                                thepath                                                                                                                                                                                                                                                                                                                exit                
+----------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------- 
+I- 880                        Ramp  (1,3,-121.948,37.91,-121.947,37.911,-121.946,37.911)                                                                                                                                                                                                                                                                   (-121.946,37.911)   
+I- 880                        Ramp  (1,3,-121.948,37.91,-121.947,37.911,-121.946,37.911)                                                                                                                                                                                                                                                                   (-121.947,37.911)   
+I- 580                        Ramp  (0,8,-121.937,37.986,-121.936,37.9883,-121.935,37.997,-121.935,37.0003,-121.935,37.006,-121.934,37.0003,-121.933,37.997,-121.932,37.989)                                                                                                                                                                               (-121.935,37.8507)  
+I- 580                        Ramp  (0,8,-121.937,37.986,-121.936,37.9883,-121.935,37.997,-121.935,37.0003,-121.935,37.006,-121.934,37.0003,-121.933,37.997,-121.932,37.989)                                                                                                                                                                               (-121.935,37.8337)  
+I- 580                        Ramp  (0,8,-121.937,37.986,-121.936,37.9883,-121.935,37.997,-121.935,37.0003,-121.935,37.006,-121.934,37.0003,-121.933,37.997,-121.932,37.989)                                                                                                                                                                               (-121.935,37.8354)  
+I- 680                              (0,7,-121.91,37.715,-121.911,37.7468,-121.912,37.764,-121.912,37.776,-121.917,37.905,-121.919,37.957,-121.921,37.988)                                                                                                                                                                                                  (-121.919,37.9349)  
+I- 680                        Ramp  (0,7,-121.927,37.998,-121.924,37.983,-121.922,37.9786,-121.92,37.975,-121.919,37.954,-121.919,37.941,-121.918,37.934)                                                                                                                                                                                                  (-121.922,37.9786)  
+I- 680                              (0,7,-121.91,37.715,-121.911,37.7468,-121.912,37.764,-121.912,37.776,-121.917,37.905,-121.919,37.957,-121.921,37.988)                                                                                                                                                                                                  (-121.92,37.9755)   
+I- 680                        Ramp  (1,3,-121.925,37.932,-121.921,37.944,-121.92,37.944)                                                                                                                                                                                                                                                                   (-121.921,37.944)   
+I- 580                        Ramp  (0,4,-121.904,37.998,-121.904,37.013,-121.903,37.0174,-121.903,37.018)                                                                                                                                                                                                                                                 (-121.904,37.6629)  
+I- 580                        Ramp  (0,6,-121.658,37.2009,-121.658,37.2009,-121.66,37.1972,-121.66,37.1965,-121.662,37.19,-121.662,37.1869)                                                                                                                                                                                                                (-121.658,37.2009)  
+I- 580                              (0,18,-121.77,37.013,-121.769,37.015,-121.758,37.03,-121.756,37.03,-121.755,37.0297,-121.751,37.0281,-121.75,37.0274,-121.749,37.026,-121.745,37.024,-121.744,37.024,-121.741,37.024,-121.74,37.0251,-121.739,37.0272,-121.738,37.028,-121.736,37.0328,-121.736,37.033,-121.736,37.034,-121.733,37.046)                (-121.744,37.024)   
+I- 580                        Ramp  (0,5,-121.74,37.034,-121.741,37.034,-121.74,37.029,-121.738,37.032,-121.736,37.034)                                                                                                                                                                                                                                    (-121.741,37.034)   
+I- 580                        Ramp  (1,3,-121.74,37.036,-121.739,37.033,-121.738,37.032)                                                                                                                                                                                                                                                                   (-121.738,37.032)   
+I- 580                              (0,18,-121.77,37.013,-121.769,37.015,-121.758,37.03,-121.756,37.03,-121.755,37.0297,-121.751,37.0281,-121.75,37.0274,-121.749,37.026,-121.745,37.024,-121.744,37.024,-121.741,37.024,-121.74,37.0251,-121.739,37.0272,-121.738,37.028,-121.736,37.0328,-121.736,37.033,-121.736,37.034,-121.733,37.046)                (-121.741,37.024)   
+I- 580                        Ramp  (1,3,-121.74,37.036,-121.739,37.033,-121.738,37.032)                                                                                                                                                                                                                                                                   (-121.739,37.033)   
+I- 580                        Ramp  (0,3,-121.723,37.103,-121.722,37.103,-121.722,37.0986)                                                                                                                                                                                                                                                                 (-121.722,37.103)   
+I- 80                         Ramp  (0,5,-122.299,37.518,-122.299,37.5,-122.299,37.488,-122.299,37.477,-122.297,37.452)                                                                                                                                                                                                                                    (-122.299,37.5)     
+I- 80                         Ramp  (0,5,-122.299,37.518,-122.299,37.5,-122.299,37.488,-122.299,37.477,-122.297,37.452)                                                                                                                                                                                                                                    (-122.297,37.452)   
+I- 80                         Ramp  (0,2,-122.304,37.25,-122.308,37.249)                                                                                                                                                                                                                                                                                   (-122.304,37.25)    
+I- 80                         Ramp  (0,2,-122.304,37.25,-122.305,37.254)                                                                                                                                                                                                                                                                                   (-122.304,37.25)    
+I- 580                              (1,9,-122.274,37.262,-122.275,37.263,-122.277,37.27,-122.278,37.271,-122.279,37.274,-122.281,37.275,-122.282,37.276,-122.283,37.276,-122.284,37.276)                                                                                                                                                                   (-122.284,37.276)   
+I- 580                              (0,6,-122.274,37.262,-122.273,37.259,-122.269,37.247,-122.269,37.2449,-122.268,37.2443,-122.268,37.244)                                                                                                                                                                                                                (-122.268,37.244)   
+I- 580                        Ramp  (0,2,-122.268,37.243,-122.269,37.243)                                                                                                                                                                                                                                                                                  (-122.268,37.243)   
+I- 580                        Ramp  (1,3,-122.265,37.242,-122.266,37.245,-122.267,37.245)                                                                                                                                                                                                                                                                  (-122.267,37.245)   
+I- 580                              (0,3,-122.267,37.243,-122.268,37.243,-122.268,37.244)                                                                                                                                                                                                                                                                  (-122.267,37.243)   
+I- 580                        Ramp  (1,3,-122.265,37.242,-122.266,37.245,-122.267,37.245)                                                                                                                                                                                                                                                                  (-122.267,37.245)   
+I- 580                        Ramp  (1,3,-122.265,37.242,-122.266,37.245,-122.267,37.245)                                                                                                                                                                                                                                                                  (-122.266,37.245)   
+I- 580                              (0,6,-122.261,37.23,-122.26,37.2283,-122.261,37.231,-122.264,37.238,-122.265,37.241,-122.265,37.242)                                                                                                                                                                                                                   (-122.264,37.238)   
+I- 580                        Ramp  (0,5,-122.264,37.238,-122.266,37.239,-122.266,37.238,-122.268,37.231,-122.268,37.227)                                                                                                                                                                                                                                  (-122.266,37.239)   
+I- 580                              (0,5,-122.261,37.23,-122.263,37.2331,-122.265,37.2349,-122.266,37.236,-122.266,37.238)                                                                                                                                                                                                                                 (-122.266,37.238)   
+I- 580                        Ramp  (1,3,-122.265,37.242,-122.266,37.245,-122.267,37.245)                                                                                                                                                                                                                                                                  (-122.266,37.245)   
+I- 580                        Ramp  (1,3,-122.265,37.241,-122.265,37.244,-122.266,37.245)                                                                                                                                                                                                                                                                  (-122.265,37.244)   
+I- 580                        Ramp  (0,4,-122.255,37.205,-122.254,37.205,-122.254,37.202,-122.254,37.196)                                                                                                                                                                                                                                                  (-122.254,37.205)   
+I- 580                        Ramp  (0,4,-122.255,37.205,-122.254,37.205,-122.254,37.202,-122.254,37.196)                                                                                                                                                                                                                                                  (-122.254,37.205)   
+I- 880                              (0,17,-122.271,37.975,-122.269,37.972,-122.268,37.966,-122.267,37.962,-122.266,37.957,-122.265,37.952,-122.264,37.946,-122.263,37.935,-122.262,37.927,-122.261,37.921,-122.259,37.916,-122.258,37.911,-122.254,37.898,-122.243,37.858,-122.241,37.845,-122.239,37.827,-122.237,37.811)                                 (-122.258,37.911)   
+I- 580                              (0,12,-122.22,37.99,-122.22,37.99,-122.222,37.9952,-122.223,37.998,-122.224,37.9996,-122.226,37.003,-122.228,37.007,-122.23,37.026,-122.232,37.043,-122.234,37.059,-122.235,37.0643,-122.237,37.07)                                                                                                                    (-122.22,37.99)     
+I- 580                              (0,12,-122.22,37.99,-122.22,37.99,-122.222,37.9952,-122.223,37.998,-122.224,37.9996,-122.226,37.003,-122.228,37.007,-122.23,37.026,-122.232,37.043,-122.234,37.059,-122.235,37.0643,-122.237,37.07)                                                                                                                    (-122.22,37.99)     
+I- 580                        Ramp  (0,6,-122.216,37.985,-122.217,37.989,-122.218,37.991,-122.218,37.9907,-122.219,37.9902,-122.22,37.99)                                                                                                                                                                                                                  (-122.22,37.99)     
+I- 880                        Ramp  (1,3,-122.235,37.767,-122.236,37.768,-122.236,37.768)                                                                                                                                                                                                                                                                  (-122.236,37.768)   
+I- 880                        Ramp  (0,4,-122.232,37.746,-122.232,37.751,-122.232,37.752,-122.233,37.752)                                                                                                                                                                                                                                                  (-122.232,37.752)   
+I- 880                        Ramp  (0,4,-122.22,37.695,-122.219,37.697,-122.22,37.7,-122.22,37.699)                                                                                                                                                                                                                                                       (-122.219,37.697)   
+I- 880                        Ramp  (1,3,-122.209,37.532,-122.209,37.535,-122.207,37.535)                                                                                                                                                                                                                                                                  (-122.207,37.535)   
+I- 880                        Ramp  (1,3,-122.209,37.532,-122.209,37.535,-122.207,37.535)                                                                                                                                                                                                                                                                  (-122.209,37.535)   
+I- 880                        Ramp  (0,4,-122.196,37.407,-122.196,37.396,-122.195,37.396,-122.195,37.394)                                                                                                                                                                                                                                                  (-122.195,37.396)   
+I- 880                        Ramp  (0,3,-122.195,37.405,-122.194,37.411,-122.195,37.411)                                                                                                                                                                                                                                                                  (-122.194,37.411)   
+I- 580                        Ramp  (0,3,-122.174,37.817,-122.175,37.822,-122.177,37.833)                                                                                                                                                                                                                                                                  (-122.175,37.822)   
+I- 880                        Ramp  (0,5,-122.187,37.32,-122.187,37.322,-122.187,37.321,-122.188,37.319,-122.188,37.317)                                                                                                                                                                                                                                   (-122.187,37.322)   
+I- 880                        Ramp  (1,2,-122.186,37.322,-122.187,37.322)                                                                                                                                                                                                                                                                                  (-122.187,37.322)   
+I- 880                        Ramp  (0,5,-122.187,37.32,-122.187,37.322,-122.187,37.321,-122.188,37.319,-122.188,37.317)                                                                                                                                                                                                                                   (-122.187,37.322)   
+I- 880                        Ramp  (0,6,-122.176,37.193,-122.175,37.191,-122.174,37.194,-122.174,37.192,-122.174,37.196,-122.172,37.198)                                                                                                                                                                                                                  (-122.174,37.196)   
+I- 880                        Ramp  (0,5,-122.168,37.09,-122.167,37.089,-122.166,37.0897,-122.165,37.09,-122.164,37.092)                                                                                                                                                                                                                                   (-122.167,37.089)   
+I- 580                        Ramp  (0,3,-122.152,37.529,-122.151,37.524,-122.151,37.509)                                                                                                                                                                                                                                                                  (-122.151,37.524)   
+I- 580                        Ramp  (1,2,-122.152,37.526,-122.151,37.524)                                                                                                                                                                                                                                                                                  (-122.151,37.524)   
+I- 580                        Ramp  (0,3,-122.152,37.529,-122.151,37.524,-122.151,37.509)                                                                                                                                                                                                                                                                  (-122.151,37.524)   
+I- 880                              (0,19,-122.175,37.185,-122.175,37.178,-122.174,37.173,-122.169,37.126,-122.168,37.1159,-122.168,37.1144,-122.167,37.111,-122.165,37.1,-122.165,37.0981,-122.164,37.092,-122.16,37.061,-122.158,37.0528,-122.156,37.0366,-122.153,37.017,-122.148,37.98,-122.141,37.932,-122.139,37.924,-122.139,37.92,-122.138,37.91)  (-122.15,37.5392)   
+I- 880                        Ramp  (1,2,-122.133,37.901,-122.136,37.905)                                                                                                                                                                                                                                                                                  (-122.136,37.905)   
+I- 880                        Ramp  (0,3,-122.129,37.842,-122.128,37.839,-122.126,37.834)                                                                                                                                                                                                                                                                  (-122.128,37.839)   
+I- 580                              (0,14,-122.111,37.023,-122.11,37.02,-122.108,37.0076,-122.108,37.007,-122.107,37.998,-122.106,37.994,-122.105,37.982,-122.105,37.977,-122.103,37.958,-122.103,37.953,-122.101,37.938,-122.099,37.911,-122.098,37.91,-122.098,37.908)                                                                                   (-122.107,37.844)   
+I- 580                        Ramp  (0,2,-122.108,37.007,-122.109,37.02)                                                                                                                                                                                                                                                                                   (-122.109,37.0128)  
+I- 580                              (0,14,-122.111,37.023,-122.11,37.02,-122.108,37.0076,-122.108,37.007,-122.107,37.998,-122.106,37.994,-122.105,37.982,-122.105,37.977,-122.103,37.958,-122.103,37.953,-122.101,37.938,-122.099,37.911,-122.098,37.91,-122.098,37.908)                                                                                   (-122.099,37.911)   
+I- 580                              (0,7,-122.098,37.908,-122.097,37.904,-122.096,37.903,-122.095,37.903,-122.094,37.902,-122.094,37.903,-122.093,37.9035)                                                                                                                                                                                                 (-122.095,37.903)   
+I- 580                              (0,9,-122.091,37.906,-122.09,37.908,-122.088,37.908,-122.086,37.909,-122.078,37.909,-122.073,37.909,-122.071,37.91,-122.068,37.9114,-122.065,37.914)                                                                                                                                                                   (-122.065,37.914)   
+I- 580                        Ramp  (0,3,-122.065,37.914,-122.062,37.916,-122.06,37.92)                                                                                                                                                                                                                                                                    (-122.062,37.916)   
+I- 580                        Ramp  (0,3,-122.019,37.012,-122.018,37.009,-122.018,37.019)                                                                                                                                                                                                                                                                  (-122.019,37.012)   
+I- 580                        Ramp  (0,3,-122.019,37.012,-122.02,37.015,-122.021,37.02)                                                                                                                                                                                                                                                                    (-122.019,37.012)   
+I- 580                        Ramp  (0,8,-121.937,37.986,-121.936,37.9883,-121.935,37.997,-121.935,37.0003,-121.935,37.006,-121.934,37.0003,-121.933,37.997,-121.932,37.989)                                                                                                                                                                               (-121.935,37.9796)  
+I- 880                        Ramp  (0,6,-121.934,37.85,-121.937,37.852,-121.937,37.836,-121.936,37.835,-121.936,37.826,-121.935,37.813)                                                                                                                                                                                                                   (-121.935,37.8507)  
+I- 880                              (0,10,-121.936,37.83,-121.936,37.826,-121.935,37.819,-121.935,37.813,-121.934,37.788,-121.933,37.767,-121.923,37.57,-121.923,37.563,-121.923,37.561,-121.922,37.5541)                                                                                                                                                  (-121.933,37.7817)  
+I- 580                        Ramp  (0,4,-121.936,37.986,-121.934,37.971,-121.933,37.979,-121.932,37.989)                                                                                                                                                                                                                                                  (-121.935,37.9796)  
+I- 680                              (0,10,-121.923,37.039,-121.924,37.057,-121.929,37.106,-121.929,37.1133,-121.93,37.119,-121.932,37.148,-121.934,37.1711,-121.936,37.193,-121.936,37.2019,-121.938,37.219)                                                                                                                                               (-121.934,37.1683)  
+I- 680                              (1,16,-121.939,37.15,-121.939,37.145,-121.937,37.125,-121.934,37.0764,-121.934,37.0709,-121.934,37.068,-121.933,37.0614,-121.933,37.057,-121.932,37.0511,-121.932,37.0468,-121.93,37.027,-121.927,37,-121.927,37.998,-121.922,37.96,-121.92,37.949,-121.918,37.934)                                                    (-121.935,37.0895)  
+I- 580                              (0,3,-121.932,37.989,-121.924,37.006,-121.922,37.014)                                                                                                                                                                                                                                                                  (-121.924,37.0087)  
+I- 580/I-680                  Ramp  (0,4,-121.924,37.006,-121.924,37.005,-121.922,37.008,-121.922,37.0104)                                                                                                                                                                                                                                                 (-121.924,37.006)   
+I- 680                        Ramp  (1,5,-121.921,37.965,-121.92,37.96,-121.921,37.957,-121.92,37.951,-121.919,37.941)                                                                                                                                                                                                                                     (-121.921,37.9574)  
+I- 680                              (1,16,-121.939,37.15,-121.939,37.145,-121.937,37.125,-121.934,37.0764,-121.934,37.0709,-121.934,37.068,-121.933,37.0614,-121.933,37.057,-121.932,37.0511,-121.932,37.0468,-121.93,37.027,-121.927,37,-121.927,37.998,-121.922,37.96,-121.92,37.949,-121.918,37.934)                                                    (-121.921,37.9517)  
+I- 580                              (0,6,-121.921,37.015,-121.919,37.02,-121.918,37.02,-121.909,37.017,-121.906,37.018,-121.906,37.018)                                                                                                                                                                                                                    (-121.909,37.017)   
+I- 680                        Ramp  (0,3,-121.905,37.702,-121.905,37.667,-121.903,37.6588)                                                                                                                                                                                                                                                                 (-121.904,37.6629)  
+I- 580                        Ramp  (1,3,-121.903,37.018,-121.904,37.022,-121.906,37.029)                                                                                                                                                                                                                                                                  (-121.904,37.0217)  
+I- 580                        Ramp  (0,4,-121.904,37.998,-121.904,37.013,-121.903,37.0174,-121.903,37.018)                                                                                                                                                                                                                                                 (-121.904,37.0217)  
+I- 580                        Ramp  (0,3,-121.874,37.014,-121.872,37.999,-121.871,37.999)                                                                                                                                                                                                                                                                  (-121.872,37.999)   
+I- 680                        Ramp  (0,2,-121.87,37.01,-121.871,37.038)                                                                                                                                                                                                                                                                                    (-121.87,37.0126)   
+I- 580                              (0,12,-121.859,37.013,-121.852,37.011,-121.849,37.011,-121.846,37.011,-121.846,37.011,-121.842,37.011,-121.841,37.011,-121.834,37.0104,-121.829,37.01,-121.829,37.009,-121.823,37.0083,-121.821,37.008)                                                                                                                (-121.852,37.011)   
+I- 580                        Ramp  (0,5,-121.852,37.011,-121.848,37.999,-121.848,37.999,-121.846,37.01,-121.846,37.011)                                                                                                                                                                                                                                   (-121.848,37.999)   
+I- 580                              (0,12,-121.859,37.013,-121.852,37.011,-121.849,37.011,-121.846,37.011,-121.846,37.011,-121.842,37.011,-121.841,37.011,-121.834,37.0104,-121.829,37.01,-121.829,37.009,-121.823,37.0083,-121.821,37.008)                                                                                                                (-121.852,37.011)   
+I- 580                              (0,12,-121.859,37.013,-121.852,37.011,-121.849,37.011,-121.846,37.011,-121.846,37.011,-121.842,37.011,-121.841,37.011,-121.834,37.0104,-121.829,37.01,-121.829,37.009,-121.823,37.0083,-121.821,37.008)                                                                                                                (-121.846,37.011)   
+I- 580                              (0,12,-121.859,37.013,-121.852,37.011,-121.849,37.011,-121.846,37.011,-121.846,37.011,-121.842,37.011,-121.841,37.011,-121.834,37.0104,-121.829,37.01,-121.829,37.009,-121.823,37.0083,-121.821,37.008)                                                                                                                (-121.821,37.008)   
+I- 580                        Ramp  (0,3,-121.774,37.006,-121.773,37.013,-121.77,37.013)                                                                                                                                                                                                                                                                   (-121.773,37.013)   
+I- 580                        Ramp  (0,3,-121.774,37.006,-121.773,37.013,-121.77,37.013)                                                                                                                                                                                                                                                                   (-121.77,37.013)    
+I- 580                              (0,14,-122.111,37.023,-122.11,37.02,-122.108,37.0076,-122.108,37.007,-122.107,37.998,-122.106,37.994,-122.105,37.982,-122.105,37.977,-122.103,37.958,-122.103,37.953,-122.101,37.938,-122.099,37.911,-122.098,37.91,-122.098,37.908)                                                                                   (-122.107,37.6771)  
+I- 880                        Ramp  (0,3,-122.103,37.557,-122.1,37.548,-122.098,37.528)                                                                                                                                                                                                                                                                    (-122.1,37.548)     
+I- 880                              (0,12,-122.061,37.003,-122.06,37.991,-122.06,37.982,-122.058,37.967,-122.058,37.961,-122.055,37.918,-122.054,37.8948,-122.051,37.8546,-122.05,37.844,-122.049,37.817,-122.048,37.813,-122.048,37.811)                                                                                                                  (-122.058,37.9539)  
+I- 880                        Ramp  (1,5,-122.058,37.967,-122.058,37.974,-122.055,37.966,-122.055,37.9683,-122.058,37.984)                                                                                                                                                                                                                                 (-122.058,37.9737)  
+I- 880                        Ramp  (0,5,-122.039,37.65,-122.039,37.625,-122.039,37.617,-122.036,37.6161,-122.036,37.616)                                                                                                                                                                                                                                  (-122.039,37.625)   
+I- 880                        Ramp  (0,5,-122.024,37.488,-122.023,37.458,-122.023,37.458,-122.022,37.452,-122.02,37.447)                                                                                                                                                                                                                                   (-122.02,37.447)    
+I- 880                        Ramp  (0,5,-122.024,37.488,-122.023,37.458,-122.023,37.458,-122.022,37.452,-122.02,37.447)                                                                                                                                                                                                                                   (-122.023,37.458)   
+I- 880                        Ramp  (0,3,-122.023,37.474,-122.021,37.473,-122.022,37.466)                                                                                                                                                                                                                                                                  (-122.021,37.473)   
+I- 880                              (0,17,-121.999,37.289,-121.999,37.2856,-121.998,37.282,-121.997,37.2761,-121.993,37.255,-121.992,37.252,-121.991,37.248,-121.99,37.2437,-121.99,37.2402,-121.988,37.233,-121.987,37.229,-121.987,37.226,-121.985,37.216,-121.982,37.196,-121.981,37.186,-121.976,37.1472,-121.971,37.107)                              (-121.987,37.226)   
+I- 680                        Ramp  (0,5,-121.898,37.545,-121.9,37.565,-121.9,37.571,-121.901,37.572,-121.903,37.586)                                                                                                                                                                                                                                      (-121.9,37.571)     
+I- 580                        Ramp  (0,4,-121.87,37.013,-121.871,37.011,-121.872,37.001,-121.871,37.001)                                                                                                                                                                                                                                                   (-121.871,37.011)   
+I- 580                        Ramp  (0,2,-121.867,37.0138,-121.871,37.0261)                                                                                                                                                                                                                                                                                (-121.871,37.0252)  
+I- 580                        Ramp  (0,4,-121.87,37.013,-121.871,37.011,-121.872,37.001,-121.871,37.001)                                                                                                                                                                                                                                                   (-121.87,37.0126)   
+I- 680                        Ramp  (0,3,-121.923,37.394,-121.923,37.392,-121.925,37.392)                                                                                                                                                                                                                                                                  (-121.923,37.392)   
+I- 680                              (1,16,-121.939,37.15,-121.939,37.145,-121.937,37.125,-121.934,37.0764,-121.934,37.0709,-121.934,37.068,-121.933,37.0614,-121.933,37.057,-121.932,37.0511,-121.932,37.0468,-121.93,37.027,-121.927,37,-121.927,37.998,-121.922,37.96,-121.92,37.949,-121.918,37.934)                                                    (-121.937,37.125)   
+I- 580                        Ramp  (0,3,-121.906,37.018,-121.906,37.0239,-121.906,37.023)                                                                                                                                                                                                                                                                 (-121.906,37.0233)  
+I- 680                        Ramp  (0,2,-121.871,37.01,-121.871,37.047)                                                                                                                                                                                                                                                                                   (-121.871,37.0252)  
+I- 580                              (0,14,-122.111,37.023,-122.11,37.02,-122.108,37.0076,-122.108,37.007,-122.107,37.998,-122.106,37.994,-122.105,37.982,-122.105,37.977,-122.103,37.958,-122.103,37.953,-122.101,37.938,-122.099,37.911,-122.098,37.91,-122.098,37.908)                                                                                   (-122.107,37.8937)  
+I- 580                        Ramp  (0,4,-122.109,37.003,-122.107,37.993,-122.107,37.992,-122.105,37.982)                                                                                                                                                                                                                                                  (-122.107,37.8943)  
+I- 580                              (0,14,-122.111,37.023,-122.11,37.02,-122.108,37.0076,-122.108,37.007,-122.107,37.998,-122.106,37.994,-122.105,37.982,-122.105,37.977,-122.103,37.958,-122.103,37.953,-122.101,37.938,-122.099,37.911,-122.098,37.91,-122.098,37.908)                                                                                   (-122.107,37.8846)  
+I- 580                        Ramp  (0,4,-122.109,37.003,-122.107,37.993,-122.107,37.992,-122.105,37.982)                                                                                                                                                                                                                                                  (-122.107,37.8842)  
+QUERY: SELECT * from toyemp where name='sharon';
+name    age  location  annualsal  
+------- ---- --------- ---------- 
+sharon  25   (15,12)   12000      
+QUERY: SELECT avg(four) AS avg_1 FROM onek;
+avg_1  
+------ 
+1      
+QUERY: SELECT avg(a) AS avg_49 FROM aggtest WHERE a < 100;
+avg_49  
+------- 
+0       
+QUERY: SELECT avg(b) AS avg_107_943 FROM aggtest;
+avg_107_943  
+------------ 
+0            
+QUERY: SELECT avg(gpa) AS avg_3_4 FROM student;
+avg_3_4  
+-------- 
+3.4      
+QUERY: SELECT sum(four) AS sum_1500 FROM onek;
+sum_1500  
+--------- 
+1500      
+QUERY: SELECT sum(a) AS sum_198 FROM aggtest;
+sum_198  
+-------- 
+0        
+QUERY: SELECT sum(b) AS avg_431_773 FROM aggtest;
+avg_431_773  
+------------ 
+0            
+QUERY: SELECT sum(gpa) AS avg_6_8 FROM student;
+avg_6_8  
+-------- 
+6.8      
+QUERY: SELECT max(four) AS max_3 FROM onek;
+max_3  
+------ 
+3      
+QUERY: SELECT max(a) AS max_100 FROM aggtest;
+max_100  
+-------- 
+         
+QUERY: SELECT max(aggtest.b) AS max_324_78 FROM aggtest;
+max_324_78  
+----------- 
+            
+QUERY: SELECT max(student.gpa) AS max_3_7 FROM student;
+max_3_7  
+-------- 
+3.7      
+QUERY: SELECT count(four) AS cnt_1000 FROM onek;
+cnt_1000  
+--------- 
+1000      
+QUERY: SELECT newavg(four) AS avg_1 FROM onek;
+avg_1  
+------ 
+1      
+QUERY: SELECT newsum(four) AS sum_1500 FROM onek;
+sum_1500  
+--------- 
+1500      
+QUERY: SELECT newcnt(four) AS cnt_1000 FROM onek;
+cnt_1000  
+--------- 
+1000      
+QUERY: SELECT * FROM a_star*;
+class  a   
+------ --- 
+a      1   
+a      2   
+a          
+b      3   
+b      4   
+b          
+b          
+c      5   
+c      6   
+c          
+c          
+e      15  
+e      16  
+e      17  
+e          
+e      18  
+e          
+e          
+d      7   
+d      8   
+d      9   
+d      10  
+d          
+d      11  
+d      12  
+d      13  
+d          
+d          
+d          
+d      14  
+d          
+d          
+d          
+d          
+f      19  
+f      20  
+f      21  
+f      22  
+f          
+f      24  
+f      25  
+f      26  
+f          
+f          
+f          
+f      27  
+f          
+f          
+f          
+f          
+QUERY: SELECT *
+   FROM b_star* x
+   WHERE x.b = 'bumble'::text or x.a < 3;
+class  a  b       
+------ -- ------- 
+b         bumble  
+QUERY: SELECT class, a
+   FROM c_star* x
+   WHERE x.c ~ 'hi'::text;
+class  a   
+------ --- 
+c      5   
+c          
+d      7   
+d      8   
+d      10  
+d          
+d      12  
+d          
+d          
+d          
+e      15  
+e      16  
+e          
+e          
+f      19  
+f      20  
+f      21  
+f          
+f      24  
+f          
+f          
+f          
+QUERY: SELECT class, b, c
+   FROM d_star* x
+   WHERE x.a < 100;
+class  b        c           
+------ -------- ----------- 
+d      grumble  hi sunita   
+d      stumble  hi koko     
+d      rumble               
+d               hi kristin  
+d      fumble               
+d               hi avi      
+d                           
+d                           
+QUERY: SELECT class, c FROM e_star* x WHERE x.c NOTNULL;
+class  c            
+------ ------------ 
+e      hi carol     
+e      hi bob       
+e      hi michelle  
+e      hi elisa     
+f      hi claire    
+f      hi mike      
+f      hi marcel    
+f      hi keith     
+f      hi marc      
+f      hi allison   
+f      hi jeff      
+f      hi carl      
+QUERY: SELECT * FROM f_star* x WHERE x.c ISNULL;
+class  a   c  e    f                                                                                                          
+------ --- -- ---- ---------------------------------------------------------------------------------------------------------- 
+f      22     -7   (         111,         222,         333,         444,         555,         666,         777,         888)  
+f      25     -9                                                                                                              
+f      26          (       11111,       22222,       33333,       44444)                                                      
+f             -11  ( 1.11111e+06, 2.22222e+06, 3.33333e+06, 4.44444e+06)                                                      
+f      27                                                                                                                     
+f             -12                                                                                                             
+f                  ( 1.11111e+07, 2.22222e+07, 3.33333e+07, 4.44444e+07)                                                      
+f                                                                                                                             
+QUERY: ALTER TABLE f_star RENAME COLUMN f TO ff;
+QUERY: ALTER TABLE e_star* RENAME COLUMN e TO ee;
+QUERY: ALTER TABLE d_star* RENAME COLUMN d TO dd;
+QUERY: ALTER TABLE c_star* RENAME COLUMN c TO cc;
+QUERY: ALTER TABLE b_star* RENAME COLUMN b TO bb;
+QUERY: ALTER TABLE a_star* RENAME COLUMN a TO aa;
+QUERY: SELECT class, aa
+   FROM a_star* x
+   WHERE aa ISNULL;
+class  aa  
+------ --- 
+a          
+b          
+b          
+c          
+c          
+e          
+e          
+e          
+d          
+d          
+d          
+d          
+d          
+d          
+d          
+d          
+f          
+f          
+f          
+f          
+f          
+f          
+f          
+f          
+QUERY: ALTER TABLE a_star RENAME COLUMN aa TO foo;
+QUERY: SELECT class, foo
+   FROM a_star x
+   WHERE x.foo >= 2;
+class  foo  
+------ ---- 
+a      2    
+QUERY: ALTER TABLE a_star RENAME COLUMN foo TO aa;
+QUERY: SELECT *
+   from a_star*
+   WHERE aa < 1000;
+class  aa  
+------ --- 
+a      1   
+a      2   
+b      3   
+b      4   
+c      5   
+c      6   
+e      15  
+e      16  
+e      17  
+e      18  
+d      7   
+d      8   
+d      9   
+d      10  
+d      11  
+d      12  
+d      13  
+d      14  
+f      19  
+f      20  
+f      21  
+f      22  
+f      24  
+f      25  
+f      26  
+f      27  
+QUERY: ALTER TABLE f_star ADD COLUMN f int4;
+QUERY: UPDATE f_star SET f = 10;
+QUERY: ALTER TABLE e_star* ADD COLUMN e int4;
+QUERY: UPDATE e_star* SET e = 42;
+WARN:parser: syntax error at or near "*"
+
+QUERY: SELECT * FROM e_star*;
+class  aa  cc           ee   e  
+------ --- ------------ ---- -- 
+e      15  hi carol     -1      
+e      16  hi bob               
+e      17               -2      
+e          hi michelle  -3      
+e      18                       
+e          hi elisa             
+e                       -4      
+f      19  hi claire    -5      
+f      20  hi mike      -6      
+f      21  hi marcel            
+f      22               -7      
+f          hi keith     -8      
+f      24  hi marc              
+f      25               -9      
+f      26                       
+f          hi allison   -10     
+f          hi jeff              
+f                       -11     
+f      27                       
+f          hi carl              
+f                       -12     
+f                               
+f                               
+QUERY: ALTER TABLE a_star* ADD COLUMN a text;
+QUERY: UPDATE b_star*
+   SET a = 'gazpacho'::text
+   WHERE aa > 4;
+WARN:parser: syntax error at or near "*"
+
+QUERY: SELECT class, aa, a FROM a_star*;
+class  aa  a  
+------ --- -- 
+a      1      
+a      2      
+a             
+b      3      
+b      4      
+b             
+b             
+c      5      
+c      6      
+c             
+c             
+e      15     
+e      16     
+e      17     
+e             
+e      18     
+e             
+e             
+d      7      
+d      8      
+d      9      
+d      10     
+d             
+d      11     
+d      12     
+d      13     
+d             
+d             
+d             
+d      14     
+d             
+d             
+d             
+d             
+f      19     
+f      20     
+f      21     
+f      22     
+f             
+f      24     
+f      25     
+f      26     
+f             
+f             
+f             
+f      27     
+f             
+f             
+f             
+f             
+QUERY: SELECT p.name, p.hobbies.name FROM person p;
+name   name         
+------ ------------ 
+mike   posthacking  
+joe    basketball   
+sally  basketball   
+QUERY: SELECT p.name, p.hobbies.name FROM person* p;
+name   name         
+------ ------------ 
+mike   posthacking  
+joe    basketball   
+sally  basketball   
+jeff   posthacking  
+QUERY: SELECT DISTINCT hobbies_r.name, hobbies_r.equipment.name FROM hobbies_r;
+name         name           
+------------ -------------- 
+basketball   hightops       
+posthacking  advil          
+posthacking  peet's coffee  
+skywalking   guts           
+QUERY: SELECT hobbies_r.name, hobbies_r.equipment.name FROM hobbies_r;
+name         name           
+------------ -------------- 
+posthacking  advil          
+posthacking  peet's coffee  
+posthacking  advil          
+posthacking  peet's coffee  
+basketball   hightops       
+basketball   hightops       
+skywalking   guts           
+QUERY: SELECT p.name, p.hobbies.name, p.hobbies.equipment.name FROM person p;
+name   name         name           
+------ ------------ -------------- 
+mike   posthacking  advil          
+joe    basketball   peet's coffee  
+sally  basketball   hightops       
+QUERY: SELECT p.name, p.hobbies.name, p.hobbies.equipment.name FROM person* p;
+name   name         name           
+------ ------------ -------------- 
+mike   posthacking  advil          
+joe    basketball   peet's coffee  
+sally  basketball   hightops       
+jeff   posthacking  advil          
+QUERY: SELECT p.hobbies.equipment.name, p.name, p.hobbies.name FROM person p;
+name      name   name         
+--------- ------ ------------ 
+advil     mike   posthacking  
+hightops  joe    basketball   
+hightops  sally  basketball   
+QUERY: SELECT p.hobbies.equipment.name, p.name, p.hobbies.name FROM person* p;
+name      name   name         
+--------- ------ ------------ 
+advil     mike   posthacking  
+hightops  joe    basketball   
+hightops  sally  basketball   
+advil     jeff   posthacking  
+QUERY: SELECT p.hobbies.equipment.name, p.hobbies.name, p.name FROM person p;
+name           name         name   
+-------------- ------------ ------ 
+advil          posthacking  mike   
+peet's coffee  basketball   joe    
+hightops       basketball   sally  
+QUERY: SELECT p.hobbies.equipment.name, p.hobbies.name, p.name FROM person* p;
+name           name         name   
+-------------- ------------ ------ 
+advil          posthacking  mike   
+peet's coffee  basketball   joe    
+hightops       basketball   sally  
+advil          posthacking  jeff   
+QUERY: SELECT user_relns() AS user_relns
+   ORDER BY user_relns;
+user_relns     
+-------------- 
+ABSTIME_TBL    
+BOOLTBL1       
+BOOLTBL2       
+BOX_TBL        
+Bprime         
+CHAR16_TBL     
+CHAR2_TBL      
+CHAR4_TBL      
+CHAR8_TBL      
+CHAR_TBL       
+FLOAT4_TBL     
+FLOAT8_TBL     
+INT2_TBL       
+INT4_TBL       
+OIDINT2_TBL    
+OIDINT4_TBL    
+OIDNAME_TBL    
+OID_TBL        
+POINT_TBL      
+POLYGON_TBL    
+RELTIME_TBL    
+TINTERVAL_TBL  
+a,276956       
+a_star         
+aggtest        
+arrtest        
+b_star         
+bt_c16_heap    
+bt_f8_heap     
+bt_i4_heap     
+bt_txt_heap    
+c_star         
+city           
+d_star         
+dept           
+e_star         
+emp            
+equipment_r    
+f_star         
+fast_emp4000   
+hash_c16_heap  
+hash_f8_heap   
+hash_i4_heap   
+hash_txt_heap  
+hobbies_r      
+iexit          
+ihighway       
+iportaltest    
+onek           
+onek2          
+person         
+ramp           
+real_city      
+road           
+shighway       
+slow_emp4000   
+street         
+stud_emp       
+student        
+tenk1          
+tenk2          
+toyemp         
+xacttest       
+QUERY: SELECT * FROM arrtest;
+a            b                      c              d                  e              
+------------ ---------------------- -------------- ------------------ -------------- 
+{1,2,3,4,5}  {{{0,0}},{{1,2}}}      {}             {}                                
+{11,12,23}   {{{3},{4}},{{4},{5}}}  {"foobar"}     {{"elt1","elt2"}}  {"3.4","6.7"}  
+{}           {{{3,4},{0,0}}}        {"foo","bar"}  {{"bar"},{"foo"}}                 
+QUERY: SELECT arrtest.a[1],
+          arrtest.b[1][1][1],
+          arrtest.c[1],
+          arrtest.d[1][1],
+          arrtest.e[0]
+   FROM arrtest;
+a   b  c       d     e  
+--- -- ------- ----- -- 
+1   0                   
+11  3  foobar  elt1     
+    3  foo     bar      
+QUERY: SELECT arrtest.a[1:3],
+          arrtest.b[1:1][1:2][1:2],
+          arrtest.c[1:2],
+          arrtest.d[1:1][1:2]
+   FROM arrtest;
+a           b                c              d                  
+----------- ---------------- -------------- ------------------ 
+{1,2,3}                                                        
+{11,12,23}                                  {{"elt1","elt2"}}  
+            {{{3,4},{0,0}}}  {"foo","bar"}                     
+QUERY: SELECT array_dims(arrtest.b) AS x;
+x                
+---------------- 
+[1:2][1:1][1:2]  
+[1:2][1:2][1:1]  
+[1:1][1:2][1:2]  
+QUERY: SELECT *
+   FROM arrtest
+   WHERE arrtest.a[1] < 5 and
+         arrtest.c = '{"foobar"}'::_char16;
+a  b  c  d  e  
+-- -- -- -- -- 
+QUERY: SELECT arrtest.a[1:3],
+          arrtest.b[1:1][1:2][1:2],
+          arrtest.c[1:2],
+          arrtest.d[1:1][1:2]
+   FROM arrtest;
+a           b                c              d                  
+----------- ---------------- -------------- ------------------ 
+{1,2,3}                                                        
+{11,12,23}                                  {{"elt1","elt2"}}  
+            {{{3,4},{0,0}}}  {"foo","bar"}                     
+=============== running error queries ... =================
+QUERY: select 1
+select
+select * from nonesuch;
+WARN:parser: syntax error at or near "select"
+
+QUERY: select nonesuch from pg_database;
+WARN:attribute "nonesuch" not found
+QUERY: select * from pg_database where nonesuch = pg_database.datname;
+WARN:attribute "nonesuch" not found
+QUERY: select * from pg_database where pg_database.datname = nonesuch;
+WARN:attribute "nonesuch" not found
+QUERY: select distinct on foobar from pg_database;
+WARN:parser: syntax error at or near "from"
+
+QUERY: select distinct on foobar * from pg_database;
+WARN:The field specified in the UNIQUE ON clause is not in the targetlist
+QUERY: delete from;
+WARN:parser: syntax error at or near ";"
+
+QUERY: delete from nonesuch;
+WARN:nonesuch: Either no such class or insufficient privilege
+QUERY: drop table;
+WARN:parser: syntax error at or near ";"
+
+QUERY: drop table nonesuch;
+WARN:Relation nonesuch Does Not Exist!
+QUERY: alter table rename;
+WARN:parser: syntax error at or near "rename"
+
+QUERY: alter table nonesuch rename to newnonesuch;
+WARN:renamerel: relation "nonesuch" does not exist
+QUERY: alter table nonesuch rename to stud_emp;
+WARN:renamerel: relation "nonesuch" does not exist
+QUERY: alter table stud_emp rename to pg_stud_emp;
+WARN:renamerel: Illegal class name: "pg_stud_emp" -- pg_ is reserved for system catalogs
+QUERY: alter table stud_emp rename to aggtest;
+WARN:renamerel: relation "aggtest" exists
+QUERY: alter table stud_emp rename to stud_emp;
+WARN:renamerel: relation "stud_emp" exists
+QUERY: alter table nonesuchrel rename column nonesuchatt to newnonesuchatt;
+WARN:renameatt: relation "nonesuchrel" nonexistent
+QUERY: alter table emp rename column nonesuchatt to newnonesuchatt;
+WARN:renameatt: attribute "nonesuchatt" nonexistent
+QUERY: alter table emp rename column salary to manager;
+WARN:renameatt: attribute "manager" exists
+QUERY: alter table emp rename column salary to oid;
+WARN:renameatt: attribute "oid" exists
+QUERY: abort;
+NOTICE:UserAbortTransactionBlock and not inprogress state
+QUERY: end;
+NOTICE:EndTransactionBlock and not inprogress/abort state 
+QUERY: create aggregate newavg1 (sfunc1 = int4pl,
+                         basetype = int4,
+                         stype1 = int4,
+                         sfunc2 = int4inc,
+                         stype2 = int4,
+                         initcond1 = '0',
+                         initcond2 = '0');
+WARN:AggregateCreate: Aggregate must have final function with both transition functions
+QUERY: create aggregate newavg2 (sfunc1 = int4pl,
+                         basetype = int4,
+                         stype1 = int4,
+                         sfunc2 = int2inc,
+                         stype2 = int2,
+                         finalfunc = int4div,
+                         initcond1 = '0',
+                         initcond2 = '0');
+WARN:AggregateCreate: 'int4div'('int4','int2') does not exist
+QUERY: create aggregate newavg3 (sfunc1 = int4pl,
+                         basetype = int4,
+                         stype1 = int4,
+                         sfunc2 = int4inc,
+                         stype2 = int4,
+                         finalfunc = int2div,
+                         initcond1 = '0',
+                         initcond2 = '0');
+WARN:AggregateCreate: 'int2div'('int4','int4') does not exist
+QUERY: create aggregate newcnt1 (sfunc2 = int4inc,
+                         stype2 = int4,
+                       initcond2 = '0');
+WARN:Define: "basetype" unspecified
+QUERY: create aggregate newcnt1 (sfunc2 = int4inc,
+                         basetype = int4,
+                         stype2 = int4);
+WARN:AggregateCreate: transition function 2 MUST have an initial value
+QUERY: drop index;
+WARN:parser: syntax error at or near ";"
+
+QUERY: drop index 314159;
+WARN:parser: syntax error at or near "314159"
+
+QUERY: drop index nonesuch;
+WARN:index "nonesuch" nonexistant
+QUERY: drop aggregate;
+WARN:parser: syntax error at or near ";"
+
+QUERY: drop aggregate 314159;
+WARN:parser: syntax error at or near "314159"
+
+QUERY: drop aggregate nonesuch;
+WARN:RemoveAggregate: aggregate 'nonesuch' does not exist
+QUERY: drop function ();
+WARN:parser: syntax error at or near "("
+
+QUERY: drop function 314159();
+WARN:parser: syntax error at or near "314159"
+
+QUERY: drop function nonesuch();
+WARN:RemoveFunction: function nonesuch() does not exist
+QUERY: drop type;
+WARN:parser: syntax error at or near ";"
+
+QUERY: drop type 314159;
+WARN:parser: syntax error at or near "314159"
+
+QUERY: drop type nonesuch;
+WARN:RemoveType: type 'nonesuch' does not exist
+QUERY: drop operator;
+WARN:parser: syntax error at or near ";"
+
+QUERY: drop operator equals;
+WARN:parser: syntax error at or near "equals"
+
+QUERY: drop operator ===;
+WARN:parser: syntax error at or near ";"
+
+QUERY: drop operator int4, int4;
+WARN:parser: syntax error at or near "int4"
+
+QUERY: drop operator (int4, int4);
+WARN:parser: syntax error at or near "("
+
+QUERY: drop operator === ();
+WARN:parser: syntax error at or near ")"
+
+QUERY: drop operator === (int4);
+WARN:parser: argument type missing (use NONE for unary operators)
+QUERY: drop operator === (int4, int4);
+WARN:RemoveOperator: binary operator '===' taking 'int4' and 'int4' does not exist
+QUERY: drop operator = (nonesuch);
+WARN:parser: argument type missing (use NONE for unary operators)
+QUERY: drop operator = ( , int4);
+WARN:parser: syntax error at or near ","
+
+QUERY: drop operator = (nonesuch, int4);
+WARN:RemoveOperator: type 'nonesuch' does not exist
+QUERY: drop operator = (int4, nonesuch);
+WARN:RemoveOperator: type 'nonesuch' does not exist
+QUERY: drop operator = (int4, );
+WARN:parser: syntax error at or near ")"
+
+QUERY: drop rule;
+WARN:parser: syntax error at or near ";"
+
+QUERY: drop rule 314159;
+WARN:parser: syntax error at or near "314159"
+
+QUERY: drop rule nonesuch;
+WARN:RewriteGetRuleEventRel: rule "nonesuch" not found
+QUERY: drop tuple rule nonesuch;
+WARN:parser: syntax error at or near "tuple"
+
+QUERY: drop instance rule nonesuch;
+WARN:parser: syntax error at or near "instance"
+
+QUERY: drop rewrite rule nonesuch;
+WARN:parser: syntax error at or near "rewrite"
+
+=============== clearing regression database... =================
+QUERY: UPDATE pg_user
+   SET usesuper = 't'::bool
+   WHERE usename = 'jolly';
+QUERY: DROP FUNCTION hobbies(person);
+QUERY: DROP FUNCTION hobby_construct(text,text);
+QUERY: DROP FUNCTION equipment(hobbies_r);
+QUERY: DROP FUNCTION user_relns();
+QUERY: DROP FUNCTION circle_in(opaque);
+QUERY: DROP FUNCTION circle_out(opaque);
+QUERY: DROP FUNCTION pt_in_circle(point,circle);
+QUERY: DROP FUNCTION overpaid(emp);
+QUERY: DROP FUNCTION boxarea(box);
+QUERY: DROP FUNCTION interpt_pp(path,path);
+QUERY: DROP FUNCTION reverse_c16(char16);
+QUERY: DROP OPERATOR ## (path, path);
+QUERY: DROP OPERATOR <% (point, circle);
+QUERY: DROP OPERATOR @#@ (none, int4);
+QUERY: DROP OPERATOR #@# (int4, none);
+QUERY: DROP OPERATOR #%# (int4, none);
+QUERY: DROP TYPE city_budget;
+QUERY: DROP TYPE circle;
+QUERY: DROP AGGREGATE newavg;
+QUERY: DROP AGGREGATE newsum;
+QUERY: DROP AGGREGATE newcnt;
+QUERY: DROP INDEX onek_unique1;
+QUERY: DROP INDEX onek_unique2;
+QUERY: DROP INDEX onek_hundred;
+QUERY: DROP INDEX onek_stringu1;
+QUERY: DROP INDEX tenk1_unique1;
+QUERY: DROP INDEX tenk1_unique2;
+QUERY: DROP INDEX tenk1_hundred;
+QUERY: DROP INDEX tenk2_unique1;
+QUERY: DROP INDEX tenk2_unique2;
+QUERY: DROP INDEX tenk2_hundred;
+QUERY: DROP INDEX rect2ind;
+QUERY: DROP INDEX rix;
+QUERY: DROP INDEX iix;
+QUERY: DROP INDEX six;
+QUERY: DROP INDEX hash_i4_index;
+QUERY: DROP INDEX hash_c16_index;
+QUERY: DROP INDEX hash_txt_index;
+QUERY: DROP INDEX hash_f8_index;
+QUERY: DROP INDEX bt_i4_index;
+QUERY: DROP INDEX bt_c16_index;
+QUERY: DROP INDEX bt_txt_index;
+QUERY: DROP INDEX bt_f8_index;
+QUERY: DROP TABLE  onek;
+QUERY: DROP TABLE  onek2;
+QUERY: DROP TABLE  tenk1;
+QUERY: DROP TABLE  tenk2;
+QUERY: DROP TABLE  Bprime;
+QUERY: DROP TABLE  hobbies_r;
+QUERY: DROP TABLE  equipment_r;
+QUERY: DROP TABLE  aggtest;
+QUERY: DROP TABLE  xacttest;
+QUERY: DROP TABLE  arrtest;
+QUERY: DROP TABLE  iportaltest;
+QUERY: DROP TABLE  f_star;
+QUERY: DROP TABLE  e_star;
+QUERY: DROP TABLE  d_star;
+QUERY: DROP TABLE  c_star;
+QUERY: DROP TABLE  b_star;
+QUERY: DROP TABLE  a_star;
+QUERY: DROP TABLE  stud_emp;
+QUERY: DROP TABLE  student;
+QUERY: DROP TABLE  slow_emp4000;
+QUERY: DROP TABLE  fast_emp4000;
+QUERY: DROP TABLE  emp;
+QUERY: DROP TABLE  person;
+QUERY: DROP TABLE  ramp;
+QUERY: DROP TABLE  real_city;
+QUERY: DROP TABLE  dept;
+QUERY: DROP TABLE  ihighway;
+QUERY: DROP TABLE  shighway;
+QUERY: DROP TABLE  road;
+QUERY: DROP TABLE  city;
+QUERY: DROP TABLE  hash_i4_heap;
+QUERY: DROP TABLE  hash_c16_heap;
+QUERY: DROP TABLE  hash_txt_heap;
+QUERY: DROP TABLE  hash_f8_heap;
+QUERY: DROP TABLE  bt_i4_heap;
+QUERY: DROP TABLE  bt_c16_heap;
+QUERY: DROP TABLE  bt_txt_heap;
+QUERY: DROP TABLE  bt_f8_heap;
+QUERY: DROP TABLE  BOOLTBL1;
+QUERY: DROP TABLE  BOOLTBL2;
+QUERY: DROP TABLE  ABSTIME_TBL;
+QUERY: DROP TABLE  RELTIME_TBL;
+QUERY: DROP TABLE  TINTERVAL_TBL;
+QUERY: DROP TABLE  BOX_TBL;
+QUERY: DROP TABLE  CHAR_TBL;
+QUERY: DROP TABLE  CHAR2_TBL;
+QUERY: DROP TABLE  CHAR4_TBL;
+QUERY: DROP TABLE  CHAR8_TBL;
+QUERY: DROP TABLE  CHAR16_TBL;
+QUERY: DROP TABLE  FLOAT4_TBL;
+QUERY: DROP TABLE  FLOAT8_TBL;
+QUERY: DROP TABLE  INT2_TBL;
+QUERY: DROP TABLE  INT4_TBL;
+QUERY: DROP TABLE  OID_TBL;
+QUERY: DROP TABLE  OIDNAME_TBL;
+QUERY: DROP TABLE  OIDINT2_TBL;
+QUERY: DROP TABLE  OIDINT4_TBL;
+QUERY: DROP TABLE  POINT_TBL;
+QUERY: DROP TABLE  POLYGON_TBL;
+QUERY: DROP VIEW street;
+QUERY: DROP VIEW iexit;
+QUERY: DROP VIEW toyemp;
+RESULTS OF REGRESSION ARE SAVED IN obj/regress.out
diff --git a/src/test/regress/security.source b/src/test/regress/security.source
new file mode 100644 (file)
index 0000000..8164574
--- /dev/null
@@ -0,0 +1,64 @@
+
+-- test this file separately. Be careful the second update statement turns off
+-- super user permission for _USER_.
+
+--
+-- SECURITY CRUFT
+--
+UPDATE pg_class
+   SET relacl='{}'
+   WHERE relname !~ 'pg_*'::text;
+
+UPDATE pg_user
+   SET usesuper='f'::bool
+   WHERE usename = '_USER_';
+
+
+CREATE TABLE myclass0 (a int4);
+
+
+-- these should all succeed 
+INSERT INTO myclass0 (a) VALUES (5);
+
+SELECT a FROM myclass0;
+
+UPDATE myclass0 SET a=6;
+
+INSERT INTO myclass0 (a) VALUES (10);
+
+INSERT INTO myclass0 (a) VALUES (20);
+
+UPDATE myclass0 SET a=10 WHERE myclass0.a < 10;
+
+UPDATE myclass0 SET a=myclass0.a+1;
+
+DELETE FROM myclass0 WHERE myclass0.a > 15;
+
+CREATE RULE foo AS ON SELECT TO myclass0 DO INSTEAD NOTHING;
+
+DROP RULE foo;
+
+
+CHANGE ACL _USER_-arR myclass0;
+
+
+-- succeeds 
+UPDATE myclass0 SET a=1;
+
+-- succeeds (we still have write permission) 
+INSERT INTO myclass0 (a) VALUES (100);
+
+-- fails 
+select a from myclass0;
+
+-- fails due to read in qualification 
+update myclass0 set a = 10 where myclass0.a < 15;
+
+-- fails due to read in target list 
+update myclass0 set a = myclass0.a + 1;
+
+-- fails due to read in qualification 
+delete from myclass0 where myclass0.a >= 100;
+
+-- fails 
+create rule foo as on retrieve to myclass0 do instead nothing;
diff --git a/src/test/suite/README b/src/test/suite/README
new file mode 100644 (file)
index 0000000..3be4047
--- /dev/null
@@ -0,0 +1,5 @@
+
+This directory contains our feeble collection of tests. To test foo.sql do
+       psql -q < foo.sql >! foo.out
+       diff foo.out results/foo.sql.out
+
diff --git a/src/test/suite/agg.sql b/src/test/suite/agg.sql
new file mode 100644 (file)
index 0000000..a25282b
--- /dev/null
@@ -0,0 +1,76 @@
+---------------------------------------------------------------------------
+--
+-- agg.sql-
+--    test aggregates
+--
+--
+-- Copyright (c) 1994-5, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+create table agga (a integer);
+create table aggb (b smallint);
+create table aggc (c float);
+create table aggd (d float8);
+insert into agga values (1);
+insert into agga values (1);
+insert into agga values (4);
+insert into agga values (3);
+select * from agga;
+insert into aggb values (10);
+insert into aggb values (45);
+insert into aggb values (10);
+insert into aggb values (30);
+select * from aggb;
+insert into aggc values (210.3);
+insert into aggc values (4.45);
+insert into aggc values (310);
+insert into aggc values (310);
+select * from aggc;
+insert into aggd values ('-210.3'::float8);
+insert into aggd values ('210.3'::float8);
+insert into aggd values ('4.45'::float8);
+insert into aggd values ('10310.33336'::float8);
+insert into aggd values ('10310.33335'::float8);
+select * from aggd;
+
+select count(*) from agga;
+select count(*), avg(a) from agga;
+select avg(a), max(a) from agga;
+select sum(a), max(a) from agga;
+
+select avg(c) from aggc;
+select sum(c) from aggc;
+select max(c) from aggc;
+select min(c) from aggc;
+
+select count(*), avg(a), sum(a), max(a), min(a) from agga;
+select count(*), avg(b), sum(b), max(b), min(b) from aggb;
+select count(*), avg(c), sum(c), max(c), min(c) from aggc;
+select count(*), avg(d), sum(d), max(d), min(d) from aggd;
+
+create table agge (e integer);
+-- aggregates on an empty table
+select count(*) from agge;
+select avg(e) from agge;
+select sum(e) from agge;
+select sum(e) from agge;
+select min(e) from agge;
+
+create table aggf (x int, y int);
+insert into aggf (x) values (1);
+insert into aggf (y) values (2);
+insert into aggf values (10, 20);
+select * from aggf;
+select count(*) from aggf;
+select count(x), count(y) from aggf;
+select avg(x), avg(y) from aggf;
+
+drop table agga;
+drop table aggb;
+drop table aggc;
+drop table aggd;
+drop table agge;
+drop table aggf;
diff --git a/src/test/suite/date.sql b/src/test/suite/date.sql
new file mode 100644 (file)
index 0000000..657e4b1
--- /dev/null
@@ -0,0 +1,30 @@
+---------------------------------------------------------------------------
+--
+-- date.sql-
+--    test DATE adt
+--
+--
+-- Copyright (c) 1994-5, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+create table dd (d date);
+insert into dd values ('06-22-1995');
+insert into dd values ('05-31-1994');
+insert into dd values ('02-29-1996');
+insert into dd values ('12-02-1993');
+insert into dd values ('05-31-1994');
+insert into dd values ('10-20-1970');
+select * from dd;
+select * from dd order by d;
+select * from dd order by d using >;
+select * from dd where d = '05-31-1994';
+select * from dd where d <> '05-31-1994';
+select * from dd where d < '05-31-1994';
+select * from dd where d <= '05-31-1994';
+select * from dd where d > '05-31-1994';
+select * from dd where d >= '05-31-1994';
+create index dd_ind on dd using btree (d date_ops);
+drop table dd;
diff --git a/src/test/suite/float.sql b/src/test/suite/float.sql
new file mode 100644 (file)
index 0000000..bb13042
--- /dev/null
@@ -0,0 +1,113 @@
+---------------------------------------------------------------------------
+--
+-- float.sql-
+--    test float4, float8 adt
+--
+--
+-- Copyright (c) 1994-5, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+--
+-- float4
+--
+create table fl (x float4);
+insert into fl values ( 3.14 );
+insert into fl values ( 147.0 );
+insert into fl values ( 3.14 );
+insert into fl values ( -3.14 );
+select * from fl;
+-- float literals
+select * from fl where x = 3.14;
+select * from fl where x <> 3.14;
+select * from fl where x < 3.14;
+select * from fl where x <= 3.14;
+select * from fl where x > 3.14;
+select * from fl where x >= 3.14;
+-- adt constant without cast (test coercion)
+select * from fl where x = '3.14';
+select * from fl where x <> '3.14';
+select * from fl where x < '3.14';
+select * from fl where x <= '3.14';
+select * from fl where x > '3.14';
+select * from fl where x >= '3.14';
+-- adt constant with float4 cast (test float4 opers)
+select * from fl where x = '3.14'::float4;
+select * from fl where x <> '3.14'::float4;
+select * from fl where x < '3.14'::float4;
+select * from fl where x <= '3.14'::float4;
+select * from fl where x > '3.14'::float4;
+select * from fl where x >= '3.14'::float4;
+-- adt constant with float8 cast (test float48 opers)
+select * from fl where x = '3.14'::float8;
+select * from fl where x <> '3.14'::float8;
+select * from fl where x < '3.14'::float8;
+select * from fl where x <= '3.14'::float8;
+select * from fl where x > '3.14'::float8;
+select * from fl where x >= '3.14'::float8;
+
+-- try other operators
+update fl set x = x + 2.2;
+select * from fl;
+update fl set x = x - 2.2;
+select * from fl;
+update fl set x = x * 2.2;
+select * from fl;
+update fl set x = x / 2.2;
+select * from fl;
+
+--
+-- float8
+--
+create table fl8 (y float8);
+insert into fl8 values ( '3.14'::float8 );
+insert into fl8 values ( '147.0'::float8 );
+insert into fl8 values ( '3.140000001'::float8 );
+insert into fl8 values ( '-3.14'::float8);
+select * from fl8;
+-- float literals
+select * from fl8 where y = 3.14;
+select * from fl8 where y <> 3.14;
+select * from fl8 where y < 3.14;
+select * from fl8 where y <= 3.14;
+select * from fl8 where y > 3.14;
+select * from fl8 where y >= 3.14;
+-- adt constant without cast (test coercion)
+select * from fl8 where y = '3.14';
+select * from fl8 where y <> '3.14';
+select * from fl8 where y < '3.14';
+select * from fl8 where y <= '3.14';
+select * from fl8 where y > '3.14';
+select * from fl8 where y >= '3.14';
+-- adt constant with float4 cast (test float84 opers)
+select * from fl8 where y = '3.14'::float4;
+select * from fl8 where y <> '3.14'::float4;
+select * from fl8 where y < '3.14'::float4;
+select * from fl8 where y <= '3.14'::float4;
+select * from fl8 where y > '3.14'::float4;
+select * from fl8 where y >= '3.14'::float4;
+-- adt constant with float8 cast (test float8 opers)
+select * from fl8 where y = '3.14'::float8;
+select * from fl8 where y <> '3.14'::float8;
+select * from fl8 where y < '3.14'::float8;
+select * from fl8 where y <= '3.14'::float8;
+select * from fl8 where y > '3.14'::float8;
+select * from fl8 where y >= '3.14'::float8;
+
+-- try other operators
+update fl8 set y = y + '2.2'::float8;
+select * from fl8;
+update fl8 set y = y - '2.2'::float8;
+select * from fl8;
+update fl8 set y = y * '2.2'::float8;
+select * from fl8;
+update fl8 set y = y / '2.2'::float8;
+select * from fl8;
+
+-- drop tables
+
+drop table fl;
+drop table fl8;
+
diff --git a/src/test/suite/group.sql b/src/test/suite/group.sql
new file mode 100644 (file)
index 0000000..1e67cbc
--- /dev/null
@@ -0,0 +1,100 @@
+---------------------------------------------------------------------------
+--
+-- group.sql-
+--    test GROUP BY (with aggregates)
+--
+--
+-- Copyright (c) 1994-5, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+create table G (x int4, y int4, z int4);
+insert into G values (1, 2, 6);
+insert into G values (1, 3, 7);
+insert into G values (1, 3, 8);
+insert into G values (1, 4, 9);
+insert into G values (1, 4, 10);
+insert into G values (1, 4, 11);
+insert into G values (1, 5, 12);
+insert into G values (1, 5, 13);
+
+select x from G group by x;
+select y from G group by y;
+select z from G group by z;
+select x, y from G group by x, y;
+select x, y from G group by y, x;
+select x, y, z from G group by x, y, z;
+
+-- mixed target list (aggregates and group columns)
+select count(y) from G group by y;
+select x, count(x) from G group by x;
+select y, count(y), sum(G.z) from G group by y;
+select sum(G.x), sum(G.y), z from G group by z;
+select y, avg(z) from G group by y;
+
+-- group attr not in target list
+select sum(x) from G group by y;
+select sum(x), sum(z) from G group by y;
+select sum(z) from G group by y;
+
+-- aggregates in expressions
+select sum(G.z)/count(G.z), avg(G.z) from G group by y;
+
+-- with qualifications
+select y, count(y) from G where z < 11 group by y;
+select y, count(y) from G where z > 9 group by y;
+select y, count(y) from G where z > 8 and z < 12 group by y;
+select y, count(y) from G where y = 4 group by y;
+select y, count(y) from G where y > 10 group by y;
+
+-- with order by
+select y, count(y) as c from G group by y order by c;
+select y, count(y) as c from G group by y order by c, y;
+select y, count(y) as c from G where z > 20 group by y order by c;
+-- just to make sure we didn't screw up order by
+select x, y from G order by y, x;
+
+-- with having
+-- HAVING clause is not implemented yet
+--select count(y) from G having count(y) > 1
+--select count(y) from G group by y having y > 3
+--select y from G group by y having y > 3
+--select y from G where z > 10 group by y having y > 3
+--select y from G group by y having y > 10
+--select count(G.y) from G group by y having y > 10
+--select y from G where z > 20 group by y having y > 3
+
+create table H (a int4, b int4);
+insert into H values (3, 9)
+insert into H values (4, 13);
+create table F (p int4);
+insert into F values (7)
+insert into F values (11);
+
+-- joins
+select y from G, H where G.y = H.a group by y;
+select sum(b) from G, H where G.y = H.a group by y;
+select y, count(y), sum(b) from G, H where G.y = H.a group by y;
+select a, sum(x), sum(b) from G, H where G.y = H.a group by a;
+select y, count(*) from G, H where G.z = H.b group by y;
+select z, sum(y) from G, H, F where G.y = H.a and G.z = F.p group by z;
+select a, avg(p) from G, H, F where G.y = H.a and G.z = F.p group by a;
+
+-- just aggregates
+select sum(x) from G, H where G.y = H.a;
+select sum(y) from G, H where G.y = H.a;
+select sum(a) from G, H where G.y = H.a;
+select sum(b) from G, H where G.y = H.a;
+select count(*) from G group by y;
+
+insert into G (y, z) values (6, 14);
+insert into G (x, z) values (2, 14);
+select count(*) from G;
+select count(x), count(y), count(z) from G;
+select x from G group by x;
+select y, count(*) from G group by y;
+
+-- 
+drop table G, H, F;
diff --git a/src/test/suite/group_err.sql b/src/test/suite/group_err.sql
new file mode 100644 (file)
index 0000000..a9eab55
--- /dev/null
@@ -0,0 +1,29 @@
+---------------------------------------------------------------------------
+--
+-- group_err.sql-
+--    test illegal use of GROUP BY (with aggregates)
+--
+--
+-- Copyright (c) 1994-5, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+create table G_ERR (x int4, y int4, z int4);
+
+select x from G_ERR group by y;
+select x, sum(z) from G_ERR group by y;
+select x, count(x) from G_ERR;
+
+select max(count(x)) from G_ERR;
+
+select x from G_ERR where count(x) = 1;
+
+create table H_ERR (a int4, b int4);
+
+select y, a, count(y), sum(b) 
+from G_ERR, H_ERR 
+where G_ERR.y = H_ERR.a group by y;
+
+drop table G_ERR, H_ERR;
diff --git a/src/test/suite/inh.sql b/src/test/suite/inh.sql
new file mode 100644 (file)
index 0000000..6c73ef8
--- /dev/null
@@ -0,0 +1,73 @@
+---------------------------------------------------------------------------
+--
+-- inh.sql-
+--    checks inheritance 
+--
+--
+-- Copyright (c) 1994, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+create table person (name text, age int4, location point);
+create table man () inherits(person);
+create table emp (salary int4, manager char16) inherits(person);
+create table student (gpa float8) inherits (person);
+create table stud_emp (percent int4) inherits (emp, student);
+create table female_stud_emp () inherits(stud_emp);
+
+-- attr order: name, age, location
+select * from person;
+select * from man;
+-- attr order: name, age, location, salary, manager
+select * from emp;
+-- attr order: name, age, location, gpa
+select * from student;
+-- attr order: name, age, location, salary, manager, gpa, percent
+select * from stud_emp;
+select * from female_stud_emp;
+
+insert into person values ('andy', 14, '(1,1)');
+insert into emp values ('betty', 20, '(2, 1)', 1000, 'mandy');
+insert into student values ('cy', 45, '(3, 2)', 1.9);
+insert into stud_emp values ('danny', 19, '(3.3, 4.55)', 400, 'mandy', 3.9);
+insert into man values ('fred', 2, '(0, 0)');
+insert into female_stud_emp values ('gina', 16, '(10, 10)', 500, 'mandy', 3.0);
+
+-- andy
+select * from person;
+
+-- betty
+select * from emp;
+
+-- cy
+select * from student;
+
+-- danny
+select * from stud_emp;
+
+-- fred
+select * from man;
+
+-- gina
+select * from female_stud_emp;
+
+-- andy, betty, cy, danny, fred, gina
+select * from person*;
+
+-- betty, danny, gina
+select * from emp*;
+
+-- cy, danny, gina
+select * from student*;
+
+-- danny, gina
+select * from stud_emp*;
+
+drop table female_stud_emp;
+drop table stud_emp;
+drop table student;
+drop table emp;
+drop table man;
+drop table person;
diff --git a/src/test/suite/join.sql b/src/test/suite/join.sql
new file mode 100644 (file)
index 0000000..e381970
--- /dev/null
@@ -0,0 +1,40 @@
+---------------------------------------------------------------------------
+--
+-- joins.sql-
+--    test joins
+--
+--
+-- Copyright (c) 1994, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+create table foo (x int4, y int4);
+create table bar (p int4, q int4);
+create table baz (a int4, b int4);
+
+insert into foo values (1, 1);
+insert into foo values (2, 2);
+insert into bar values (1, 1);
+insert into baz values (1, 1);
+insert into baz values (2, 2);
+
+select * from foo,bar,baz 
+where foo.x=bar.p and bar.p=baz.a and baz.b=foo.y;
+
+select * from foo,bar,baz 
+where foo.y=bar.p and bar.p=baz.a and baz.b=foo.x and foo.y=bar.q;
+
+select * from foo,bar,baz 
+where foo.x=bar.q and bar.p=baz.b and baz.b=foo.y and foo.y=bar.q 
+  and bar.p=baz.a;
+
+select * from foo,bar,baz 
+where foo.y=bar.p and bar.q=baz.b and baz.b=foo.x and foo.x=bar.q 
+  and bar.p=baz.a and bar.p=baz.a;
+
+select bar.p from foo, bar;
+select foo.x from foo, bar where foo.x = bar.p;
+
+drop table foo, bar, baz;
diff --git a/src/test/suite/oper.sql b/src/test/suite/oper.sql
new file mode 100644 (file)
index 0000000..8e7ec9f
--- /dev/null
@@ -0,0 +1,27 @@
+---------------------------------------------------------------------------
+--
+-- oper.sql-
+--    test operators
+--
+--
+-- Copyright (c) 1994, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+-- test creation
+create operator ##+ (leftarg=int4, rightarg=int4, procedure = int4pl);\g
+create operator ##+ (rightarg=int4, procedure=int4fac);\g
+create operator ##+ (leftarg=int4, procedure=int4inc);\g
+
+select 4 ##+ 4;\g
+select ##+ 4;\g
+
+-- why "select 4 ##+" does not work?
+select (4 ##+);\g
+
+drop operator ##+(int4,int4);\g
+drop operator ##+(none, int4);\g
+drop operator ##+(int4, none);\g
+
diff --git a/src/test/suite/parse.sql b/src/test/suite/parse.sql
new file mode 100644 (file)
index 0000000..1c9b311
--- /dev/null
@@ -0,0 +1,45 @@
+---------------------------------------------------------------------------
+--
+-- parse.sql-
+--    checks the parser
+--
+--
+-- Copyright (c) 1994, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+create table foo (x int4, y int4, z int4);
+create table bar (x int4, y int4, z int4);
+create table baz (a int4, b int4);
+
+insert into foo values (1, 2, 3);
+insert into foo values (4, 5, 6);
+insert into foo values (7, 8, 9);
+insert into bar values (11, 12, 13);
+insert into bar values (14, 15, 16);
+insert into bar values (17, 18, 19);
+insert into baz values (99, 88);
+insert into baz values (77, 66);
+
+-- once upon a time, this becomes a join of foo and f:
+select * from foo f where f.x = 4;
+select * from foo f, foo where f.x > foo.x;
+select * from foo f, foo where f.x = 1 and foo.z > f.z;
+
+-- not standard SQL, POSTQUEL semantics
+-- update foo set x = f.x from foo f where foo.x = 1 and f.x = 7
+-- select * from foo
+
+-- fix error message:
+--select foo.x from foo,bar,baz where foo.x=bar.x and bar.y=baz.x and baz.x=foo.x
+
+-- see if renaming the column works
+select y as a, z as b from foo order by a;
+select foo.y as a, foo.z as b from foo order by b;
+
+-- column expansion
+select foo.*, bar.z, baz.* from foo, bar, baz;
+
+drop table foo, bar, baz;
diff --git a/src/test/suite/quote.sql b/src/test/suite/quote.sql
new file mode 100644 (file)
index 0000000..0040beb
--- /dev/null
@@ -0,0 +1,18 @@
+create table quoteTBL (f text);
+
+insert into quoteTBL values ('hello world');
+insert into quoteTBL values ('hello '' world');
+insert into quoteTBL values ('hello \' world');
+insert into quoteTBL values ('hello \\ world');
+insert into quoteTBL values ('hello \t world');
+insert into quoteTBL values ('hello
+world
+with 
+newlines
+');
+insert into quoteTBL values ('hello " world');
+insert into quoteTBL values ('');
+  -- bad escape sequence 
+insert into quoteTBL values ('hello \y world');  
+select * from quoteTBL;
+drop table quoteTBL;
diff --git a/src/test/suite/results/agg.sql.out b/src/test/suite/results/agg.sql.out
new file mode 100644 (file)
index 0000000..a3b41ee
--- /dev/null
@@ -0,0 +1,147 @@
+QUERY: create table agga (a integer);
+QUERY: create table aggb (b smallint);
+QUERY: create table aggc (c float);
+QUERY: create table aggd (d float8);
+QUERY: insert into agga values (1);
+QUERY: insert into agga values (1);
+QUERY: insert into agga values (4);
+QUERY: insert into agga values (3);
+QUERY: select * from agga;
+a  
+-- 
+1  
+1  
+4  
+3  
+QUERY: insert into aggb values (10);
+QUERY: insert into aggb values (45);
+QUERY: insert into aggb values (10);
+QUERY: insert into aggb values (30);
+QUERY: select * from aggb;
+b   
+--- 
+10  
+45  
+10  
+30  
+QUERY: insert into aggc values (210.3);
+QUERY: insert into aggc values (4.45);
+QUERY: insert into aggc values (310);
+QUERY: insert into aggc values (310);
+QUERY: select * from aggc;
+c      
+------ 
+210.3  
+4.45   
+310    
+310    
+QUERY: insert into aggd values ('-210.3'::float8);
+QUERY: insert into aggd values ('210.3'::float8);
+QUERY: insert into aggd values ('4.45'::float8);
+QUERY: insert into aggd values ('10310.33336'::float8);
+QUERY: insert into aggd values ('10310.33335'::float8);
+QUERY: select * from aggd;
+d            
+------------ 
+-210.3       
+210.3        
+4.45         
+10310.33336  
+10310.33335  
+QUERY: select count(*) from agga;
+count  
+------ 
+4      
+QUERY: select count(*), avg(a) from agga;
+count  avg  
+------ ---- 
+4      2    
+QUERY: select avg(a), max(a) from agga;
+avg  max  
+---- ---- 
+2    4    
+QUERY: select sum(a), max(a) from agga;
+sum  max  
+---- ---- 
+9    4    
+QUERY: select avg(c) from aggc;
+avg      
+-------- 
+208.687  
+QUERY: select sum(c) from aggc;
+sum     
+------- 
+834.75  
+QUERY: select max(c) from aggc;
+max  
+---- 
+310  
+QUERY: select min(c) from aggc;
+min   
+----- 
+4.45  
+QUERY: select count(*), avg(a), sum(a), max(a), min(a) from agga;
+count  avg  sum  max  min  
+------ ---- ---- ---- ---- 
+4      2    9    4    1    
+QUERY: select count(*), avg(b), sum(b), max(b), min(b) from aggb;
+count  avg  sum  max  min  
+------ ---- ---- ---- ---- 
+4      23   95   45   10   
+QUERY: select count(*), avg(c), sum(c), max(c), min(c) from aggc;
+count  avg      sum     max  min   
+------ -------- ------- ---- ----- 
+4      208.687  834.75  310  4.45  
+QUERY: select count(*), avg(d), sum(d), max(d), min(d) from aggd;
+count  avg          sum          max          min     
+------ ------------ ------------ ------------ ------- 
+5      4125.023342  20625.11671  10310.33336  -210.3  
+QUERY: create table agge (e integer);
+QUERY: select count(*) from agge;
+count  
+------ 
+0      
+QUERY: select avg(e) from agge;
+avg  
+---- 
+0    
+QUERY: select sum(e) from agge;
+sum  
+---- 
+0    
+QUERY: select sum(e) from agge;
+sum  
+---- 
+0    
+QUERY: select min(e) from agge;
+min  
+---- 
+     
+QUERY: create table aggf (x int, y int);
+QUERY: insert into aggf (x) values (1);
+QUERY: insert into aggf (y) values (2);
+QUERY: insert into aggf values (10, 20);
+QUERY: select * from aggf;
+x   y   
+--- --- 
+1       
+    2   
+10  20  
+QUERY: select count(*) from aggf;
+count  
+------ 
+3      
+QUERY: select count(x), count(y) from aggf;
+count  count  
+------ ------ 
+2      2      
+QUERY: select avg(x), avg(y) from aggf;
+avg  avg  
+---- ---- 
+5    11   
+QUERY: drop table agga;
+QUERY: drop table aggb;
+QUERY: drop table aggc;
+QUERY: drop table aggd;
+QUERY: drop table agge;
+QUERY: drop table aggf;
diff --git a/src/test/suite/results/date.sql.out b/src/test/suite/results/date.sql.out
new file mode 100644 (file)
index 0000000..ffd785f
--- /dev/null
@@ -0,0 +1,72 @@
+QUERY: create table dd (d date);
+QUERY: insert into dd values ('06-22-1995');
+QUERY: insert into dd values ('05-31-1994');
+QUERY: insert into dd values ('02-29-1996');
+QUERY: insert into dd values ('12-02-1993');
+QUERY: insert into dd values ('05-31-1994');
+QUERY: insert into dd values ('10-20-1970');
+QUERY: select * from dd;
+d           
+----------- 
+06-22-1995  
+05-31-1994  
+02-29-1996  
+12-02-1993  
+05-31-1994  
+10-20-1970  
+QUERY: select * from dd order by d;
+d           
+----------- 
+10-20-1970  
+12-02-1993  
+05-31-1994  
+05-31-1994  
+06-22-1995  
+02-29-1996  
+QUERY: select * from dd order by d using >;
+d           
+----------- 
+02-29-1996  
+06-22-1995  
+05-31-1994  
+05-31-1994  
+12-02-1993  
+10-20-1970  
+QUERY: select * from dd where d = '05-31-1994';
+d           
+----------- 
+05-31-1994  
+05-31-1994  
+QUERY: select * from dd where d <> '05-31-1994';
+d           
+----------- 
+06-22-1995  
+02-29-1996  
+12-02-1993  
+10-20-1970  
+QUERY: select * from dd where d < '05-31-1994';
+d           
+----------- 
+12-02-1993  
+10-20-1970  
+QUERY: select * from dd where d <= '05-31-1994';
+d           
+----------- 
+05-31-1994  
+12-02-1993  
+05-31-1994  
+10-20-1970  
+QUERY: select * from dd where d > '05-31-1994';
+d           
+----------- 
+06-22-1995  
+02-29-1996  
+QUERY: select * from dd where d >= '05-31-1994';
+d           
+----------- 
+06-22-1995  
+05-31-1994  
+02-29-1996  
+05-31-1994  
+QUERY: create index dd_ind on dd using btree (d date_ops);
+QUERY: drop table dd;
diff --git a/src/test/suite/results/float.sql.out b/src/test/suite/results/float.sql.out
new file mode 100644 (file)
index 0000000..49bce67
--- /dev/null
@@ -0,0 +1,330 @@
+QUERY: create table fl (x float4);
+QUERY: insert into fl values ( 3.14 );
+QUERY: insert into fl values ( 147.0 );
+QUERY: insert into fl values ( 3.14 );
+QUERY: insert into fl values ( -3.14 );
+QUERY: select * from fl;
+x      
+------ 
+3.14   
+147    
+3.14   
+-3.14  
+QUERY: select * from fl where x = 3.14;
+x     
+----- 
+3.14  
+3.14  
+QUERY: select * from fl where x <> 3.14;
+x      
+------ 
+147    
+-3.14  
+QUERY: select * from fl where x < 3.14;
+x      
+------ 
+-3.14  
+QUERY: select * from fl where x <= 3.14;
+x      
+------ 
+3.14   
+3.14   
+-3.14  
+QUERY: select * from fl where x > 3.14;
+x    
+---- 
+147  
+QUERY: select * from fl where x >= 3.14;
+x     
+----- 
+3.14  
+147   
+3.14  
+QUERY: select * from fl where x = '3.14';
+x     
+----- 
+3.14  
+3.14  
+QUERY: select * from fl where x <> '3.14';
+x      
+------ 
+147    
+-3.14  
+QUERY: select * from fl where x < '3.14';
+x      
+------ 
+-3.14  
+QUERY: select * from fl where x <= '3.14';
+x      
+------ 
+3.14   
+3.14   
+-3.14  
+QUERY: select * from fl where x > '3.14';
+x    
+---- 
+147  
+QUERY: select * from fl where x >= '3.14';
+x     
+----- 
+3.14  
+147   
+3.14  
+QUERY: select * from fl where x = '3.14'::float4;
+x     
+----- 
+3.14  
+3.14  
+QUERY: select * from fl where x <> '3.14'::float4;
+x      
+------ 
+147    
+-3.14  
+QUERY: select * from fl where x < '3.14'::float4;
+x      
+------ 
+-3.14  
+QUERY: select * from fl where x <= '3.14'::float4;
+x      
+------ 
+3.14   
+3.14   
+-3.14  
+QUERY: select * from fl where x > '3.14'::float4;
+x    
+---- 
+147  
+QUERY: select * from fl where x >= '3.14'::float4;
+x     
+----- 
+3.14  
+147   
+3.14  
+QUERY: select * from fl where x = '3.14'::float8;
+x     
+----- 
+3.14  
+3.14  
+QUERY: select * from fl where x <> '3.14'::float8;
+x      
+------ 
+147    
+-3.14  
+QUERY: select * from fl where x < '3.14'::float8;
+x      
+------ 
+-3.14  
+QUERY: select * from fl where x <= '3.14'::float8;
+x      
+------ 
+3.14   
+3.14   
+-3.14  
+QUERY: select * from fl where x > '3.14'::float8;
+x    
+---- 
+147  
+QUERY: select * from fl where x >= '3.14'::float8;
+x     
+----- 
+3.14  
+147   
+3.14  
+QUERY: update fl set x = x + 2.2;
+QUERY: select * from fl;
+x      
+------ 
+5.34   
+149.2  
+5.34   
+-0.94  
+QUERY: update fl set x = x - 2.2;
+QUERY: select * from fl;
+x      
+------ 
+3.14   
+147    
+3.14   
+-3.14  
+QUERY: update fl set x = x * 2.2;
+QUERY: select * from fl;
+x       
+------- 
+6.908   
+323.4   
+6.908   
+-6.908  
+QUERY: update fl set x = x / 2.2;
+QUERY: select * from fl;
+x      
+------ 
+3.14   
+147    
+3.14   
+-3.14  
+QUERY: create table fl8 (y float8);
+QUERY: insert into fl8 values ( '3.14'::float8 );
+QUERY: insert into fl8 values ( '147.0'::float8 );
+QUERY: insert into fl8 values ( '3.140000001'::float8 );
+QUERY: insert into fl8 values ( '-3.14'::float8);
+QUERY: select * from fl8;
+y            
+------------ 
+3.14         
+147          
+3.140000001  
+-3.14        
+QUERY: select * from fl8 where y = 3.14;
+y            
+------------ 
+3.14         
+3.140000001  
+QUERY: select * from fl8 where y <> 3.14;
+y      
+------ 
+147    
+-3.14  
+QUERY: select * from fl8 where y < 3.14;
+y      
+------ 
+-3.14  
+QUERY: select * from fl8 where y <= 3.14;
+y            
+------------ 
+3.14         
+3.140000001  
+-3.14        
+QUERY: select * from fl8 where y > 3.14;
+y    
+---- 
+147  
+QUERY: select * from fl8 where y >= 3.14;
+y            
+------------ 
+3.14         
+147          
+3.140000001  
+QUERY: select * from fl8 where y = '3.14';
+y     
+----- 
+3.14  
+QUERY: select * from fl8 where y <> '3.14';
+y            
+------------ 
+147          
+3.140000001  
+-3.14        
+QUERY: select * from fl8 where y < '3.14';
+y      
+------ 
+-3.14  
+QUERY: select * from fl8 where y <= '3.14';
+y      
+------ 
+3.14   
+-3.14  
+QUERY: select * from fl8 where y > '3.14';
+y            
+------------ 
+147          
+3.140000001  
+QUERY: select * from fl8 where y >= '3.14';
+y            
+------------ 
+3.14         
+147          
+3.140000001  
+QUERY: select * from fl8 where y = '3.14'::float4;
+y            
+------------ 
+3.14         
+3.140000001  
+QUERY: select * from fl8 where y <> '3.14'::float4;
+y      
+------ 
+147    
+-3.14  
+QUERY: select * from fl8 where y < '3.14'::float4;
+y      
+------ 
+-3.14  
+QUERY: select * from fl8 where y <= '3.14'::float4;
+y            
+------------ 
+3.14         
+3.140000001  
+-3.14        
+QUERY: select * from fl8 where y > '3.14'::float4;
+y    
+---- 
+147  
+QUERY: select * from fl8 where y >= '3.14'::float4;
+y            
+------------ 
+3.14         
+147          
+3.140000001  
+QUERY: select * from fl8 where y = '3.14'::float8;
+y     
+----- 
+3.14  
+QUERY: select * from fl8 where y <> '3.14'::float8;
+y            
+------------ 
+147          
+3.140000001  
+-3.14        
+QUERY: select * from fl8 where y < '3.14'::float8;
+y      
+------ 
+-3.14  
+QUERY: select * from fl8 where y <= '3.14'::float8;
+y      
+------ 
+3.14   
+-3.14  
+QUERY: select * from fl8 where y > '3.14'::float8;
+y            
+------------ 
+147          
+3.140000001  
+QUERY: select * from fl8 where y >= '3.14'::float8;
+y            
+------------ 
+3.14         
+147          
+3.140000001  
+QUERY: update fl8 set y = y + '2.2'::float8;
+QUERY: select * from fl8;
+y            
+------------ 
+5.34         
+149.2        
+5.340000001  
+-0.94        
+QUERY: update fl8 set y = y - '2.2'::float8;
+QUERY: select * from fl8;
+y            
+------------ 
+3.14         
+147          
+3.140000001  
+-3.14        
+QUERY: update fl8 set y = y * '2.2'::float8;
+QUERY: select * from fl8;
+y             
+------------- 
+6.908         
+323.4         
+6.9080000022  
+-6.908        
+QUERY: update fl8 set y = y / '2.2'::float8;
+QUERY: select * from fl8;
+y            
+------------ 
+3.14         
+147          
+3.140000001  
+-3.14        
+QUERY: drop table fl;
+QUERY: drop table fl8;
diff --git a/src/test/suite/results/group.sql.out b/src/test/suite/results/group.sql.out
new file mode 100644 (file)
index 0000000..5730a6e
--- /dev/null
@@ -0,0 +1,262 @@
+QUERY: create table G (x int4, y int4, z int4);
+QUERY: insert into G values (1, 2, 6);
+QUERY: insert into G values (1, 3, 7);
+QUERY: insert into G values (1, 3, 8);
+QUERY: insert into G values (1, 4, 9);
+QUERY: insert into G values (1, 4, 10);
+QUERY: insert into G values (1, 4, 11);
+QUERY: insert into G values (1, 5, 12);
+QUERY: insert into G values (1, 5, 13);
+QUERY: select x from G group by x;
+x  
+-- 
+1  
+QUERY: select y from G group by y;
+y  
+-- 
+2  
+3  
+4  
+5  
+QUERY: select z from G group by z;
+z   
+--- 
+6   
+7   
+8   
+9   
+10  
+11  
+12  
+13  
+QUERY: select x, y from G group by x, y;
+x  y  
+-- -- 
+1  2  
+1  3  
+1  4  
+1  5  
+QUERY: select x, y from G group by y, x;
+x  y  
+-- -- 
+1  2  
+1  3  
+1  4  
+1  5  
+QUERY: select x, y, z from G group by x, y, z;
+x  y  z   
+-- -- --- 
+1  2  6   
+1  3  7   
+1  3  8   
+1  4  9   
+1  4  10  
+1  4  11  
+1  5  12  
+1  5  13  
+QUERY: select count(y) from G group by y;
+count  
+------ 
+1      
+2      
+3      
+2      
+QUERY: select x, count(x) from G group by x;
+x  count  
+-- ------ 
+1  8      
+QUERY: select y, count(y), sum(G.z) from G group by y;
+y  count  sum  
+-- ------ ---- 
+2  1      6    
+3  2      15   
+4  3      30   
+5  2      25   
+QUERY: select sum(G.x), sum(G.y), z from G group by z;
+sum  sum  z   
+---- ---- --- 
+1    2    6   
+1    3    7   
+1    3    8   
+1    4    9   
+1    4    10  
+1    4    11  
+1    5    12  
+1    5    13  
+QUERY: select y, avg(z) from G group by y;
+y  avg  
+-- ---- 
+2  6    
+3  7    
+4  10   
+5  12   
+QUERY: select sum(x) from G group by y;
+sum  
+---- 
+1    
+2    
+3    
+2    
+QUERY: select sum(x), sum(z) from G group by y;
+sum  sum  
+---- ---- 
+1    6    
+2    15   
+3    30   
+2    25   
+QUERY: select sum(z) from G group by y;
+sum  
+---- 
+6    
+15   
+30   
+25   
+QUERY: select sum(G.z)/count(G.z), avg(G.z) from G group by y;
+?column?  avg  
+--------- ---- 
+6         6    
+7         7    
+10        10   
+12        12   
+QUERY: select y, count(y) from G where z < 11 group by y;
+y  count  
+-- ------ 
+2  1      
+3  2      
+4  2      
+QUERY: select y, count(y) from G where z > 9 group by y;
+y  count  
+-- ------ 
+4  2      
+5  2      
+QUERY: select y, count(y) from G where z > 8 and z < 12 group by y;
+y  count  
+-- ------ 
+4  3      
+QUERY: select y, count(y) from G where y = 4 group by y;
+y  count  
+-- ------ 
+4  3      
+QUERY: select y, count(y) from G where y > 10 group by y;
+y  count  
+-- ------ 
+   0      
+QUERY: select y, count(y) as c from G group by y order by c;
+y  c  
+-- -- 
+2  1  
+5  2  
+3  2  
+4  3  
+QUERY: select y, count(y) as c from G group by y order by c, y;
+y  c  
+-- -- 
+2  1  
+3  2  
+5  2  
+4  3  
+QUERY: select y, count(y) as c from G where z > 20 group by y order by c;
+y  c  
+-- -- 
+   0  
+QUERY: select x, y from G order by y, x;
+x  y  
+-- -- 
+1  2  
+1  3  
+1  3  
+1  4  
+1  4  
+1  4  
+1  5  
+1  5  
+QUERY: create table H (a int4, b int4);
+QUERY: insert into H values (3, 9)
+insert into H values (4, 13);
+QUERY: create table F (p int4);
+QUERY: insert into F values (7)
+insert into F values (11);
+QUERY: select y from G, H where G.y = H.a group by y;
+y  
+-- 
+3  
+4  
+QUERY: select sum(b) from G, H where G.y = H.a group by y;
+sum  
+---- 
+18   
+39   
+QUERY: select y, count(y), sum(b) from G, H where G.y = H.a group by y;
+y  count  sum  
+-- ------ ---- 
+3  2      18   
+4  3      39   
+QUERY: select a, sum(x), sum(b) from G, H where G.y = H.a group by a;
+a  sum  sum  
+-- ---- ---- 
+3  2    18   
+4  3    39   
+QUERY: select y, count(*) from G, H where G.z = H.b group by y;
+y  count  
+-- ------ 
+4  1      
+5  1      
+QUERY: select z, sum(y) from G, H, F where G.y = H.a and G.z = F.p group by z;
+z   sum  
+--- ---- 
+7   3    
+11  4    
+QUERY: select a, avg(p) from G, H, F where G.y = H.a and G.z = F.p group by a;
+a  avg  
+-- ---- 
+3  7    
+4  11   
+QUERY: select sum(x) from G, H where G.y = H.a;
+sum  
+---- 
+5    
+QUERY: select sum(y) from G, H where G.y = H.a;
+sum  
+---- 
+18   
+QUERY: select sum(a) from G, H where G.y = H.a;
+sum  
+---- 
+18   
+QUERY: select sum(b) from G, H where G.y = H.a;
+sum  
+---- 
+57   
+QUERY: select count(*) from G group by y;
+count  
+------ 
+1      
+2      
+3      
+2      
+QUERY: insert into G (y, z) values (6, 14);
+QUERY: insert into G (x, z) values (2, 14);
+QUERY: select count(*) from G;
+count  
+------ 
+10     
+QUERY: select count(x), count(y), count(z) from G;
+count  count  count  
+------ ------ ------ 
+9      9      10     
+QUERY: select x from G group by x;
+x  
+-- 
+1  
+2  
+   
+QUERY: select y, count(*) from G group by y;
+y  count  
+-- ------ 
+2  1      
+3  2      
+4  3      
+5  2      
+6  1      
+   1      
+QUERY: drop table G, H, F;
diff --git a/src/test/suite/results/group_err.sql.out b/src/test/suite/results/group_err.sql.out
new file mode 100644 (file)
index 0000000..ea0f15c
--- /dev/null
@@ -0,0 +1,18 @@
+QUERY: create table G_ERR (x int4, y int4, z int4);
+QUERY: select x from G_ERR group by y;
+x  
+-- 
+QUERY: select x, sum(z) from G_ERR group by y;
+WARN:parser: illegal use of aggregates or non-group column in target list
+QUERY: select x, count(x) from G_ERR;
+WARN:parser: illegal use of aggregates or non-group column in target list
+QUERY: select max(count(x)) from G_ERR;
+WARN:parser: aggregate can only be applied on an attribute
+QUERY: select x from G_ERR where count(x) = 1;
+WARN:parser: aggregates not allowed in WHERE clause
+QUERY: create table H_ERR (a int4, b int4);
+QUERY: select y, a, count(y), sum(b)
+from G_ERR, H_ERR
+where G_ERR.y = H_ERR.a group by y;
+WARN:parser: illegal use of aggregates or non-group column in target list
+QUERY: drop table G_ERR, H_ERR;
diff --git a/src/test/suite/results/inh.sql.out b/src/test/suite/results/inh.sql.out
new file mode 100644 (file)
index 0000000..658028c
--- /dev/null
@@ -0,0 +1,86 @@
+QUERY: create table person (name text, age int4, location point);
+QUERY: create table man () inherits(person);
+QUERY: create table emp (salary int4, manager char16) inherits(person);
+QUERY: create table student (gpa float8) inherits (person);
+QUERY: create table stud_emp (percent int4) inherits (emp, student);
+QUERY: create table female_stud_emp () inherits(stud_emp);
+QUERY: select * from person;
+name  age  location  
+----- ---- --------- 
+QUERY: select * from man;
+name  age  location  
+----- ---- --------- 
+QUERY: select * from emp;
+name  age  location  salary  manager  
+----- ---- --------- ------- -------- 
+QUERY: select * from student;
+name  age  location  gpa  
+----- ---- --------- ---- 
+QUERY: select * from stud_emp;
+name  age  location  salary  manager  gpa  percent  
+----- ---- --------- ------- -------- ---- -------- 
+QUERY: select * from female_stud_emp;
+name  age  location  salary  manager  gpa  percent  
+----- ---- --------- ------- -------- ---- -------- 
+QUERY: insert into person values ('andy', 14, '(1,1)');
+QUERY: insert into emp values ('betty', 20, '(2, 1)', 1000, 'mandy');
+QUERY: insert into student values ('cy', 45, '(3, 2)', 1.9);
+QUERY: insert into stud_emp values ('danny', 19, '(3.3, 4.55)', 400, 'mandy', 3.9);
+QUERY: insert into man values ('fred', 2, '(0, 0)');
+QUERY: insert into female_stud_emp values ('gina', 16, '(10, 10)', 500, 'mandy', 3.0);
+QUERY: select * from person;
+name  age  location  
+----- ---- --------- 
+andy  14   (1,1)     
+QUERY: select * from emp;
+name   age  location  salary  manager  
+------ ---- --------- ------- -------- 
+betty  20   (2,1)     1000    mandy    
+QUERY: select * from student;
+name  age  location  gpa  
+----- ---- --------- ---- 
+cy    45   (3,2)     1.9  
+QUERY: select * from stud_emp;
+name   age  location    salary  manager  gpa  percent  
+------ ---- ----------- ------- -------- ---- -------- 
+danny  19   (3.3,4.55)  400     mandy    3.9           
+QUERY: select * from man;
+name  age  location  
+----- ---- --------- 
+fred  2    (0,0)     
+QUERY: select * from female_stud_emp;
+name  age  location  salary  manager  gpa  percent  
+----- ---- --------- ------- -------- ---- -------- 
+gina  16   (10,10)   500     mandy    3             
+QUERY: select * from person*;
+name   age  location    
+------ ---- ----------- 
+andy   14   (1,1)       
+fred   2    (0,0)       
+betty  20   (2,1)       
+cy     45   (3,2)       
+danny  19   (3.3,4.55)  
+gina   16   (10,10)     
+QUERY: select * from emp*;
+name   age  location    salary  manager  
+------ ---- ----------- ------- -------- 
+betty  20   (2,1)       1000    mandy    
+danny  19   (3.3,4.55)  400     mandy    
+gina   16   (10,10)     500     mandy    
+QUERY: select * from student*;
+name   age  location    gpa  
+------ ---- ----------- ---- 
+cy     45   (3,2)       1.9  
+danny  19   (3.3,4.55)  3.9  
+gina   16   (10,10)     3    
+QUERY: select * from stud_emp*;
+name   age  location    salary  manager  gpa  percent  
+------ ---- ----------- ------- -------- ---- -------- 
+danny  19   (3.3,4.55)  400     mandy    3.9           
+gina   16   (10,10)     500     mandy    3             
+QUERY: drop table female_stud_emp;
+QUERY: drop table stud_emp;
+QUERY: drop table student;
+QUERY: drop table emp;
+QUERY: drop table man;
+QUERY: drop table person;
diff --git a/src/test/suite/results/join.sql.out b/src/test/suite/results/join.sql.out
new file mode 100644 (file)
index 0000000..423aa3d
--- /dev/null
@@ -0,0 +1,40 @@
+QUERY: create table foo (x int4, y int4);
+QUERY: create table bar (p int4, q int4);
+QUERY: create table baz (a int4, b int4);
+QUERY: insert into foo values (1, 1);
+QUERY: insert into foo values (2, 2);
+QUERY: insert into bar values (1, 1);
+QUERY: insert into baz values (1, 1);
+QUERY: insert into baz values (2, 2);
+QUERY: select * from foo,bar,baz
+where foo.x=bar.p and bar.p=baz.a and baz.b=foo.y;
+x  y  p  q  a  b  
+-- -- -- -- -- -- 
+1  1  1  1  1  1  
+QUERY: select * from foo,bar,baz
+where foo.y=bar.p and bar.p=baz.a and baz.b=foo.x and foo.y=bar.q;
+x  y  p  q  a  b  
+-- -- -- -- -- -- 
+1  1  1  1  1  1  
+QUERY: select * from foo,bar,baz
+where foo.x=bar.q and bar.p=baz.b and baz.b=foo.y and foo.y=bar.q
+  and bar.p=baz.a;
+x  y  p  q  a  b  
+-- -- -- -- -- -- 
+1  1  1  1  1  1  
+QUERY: select * from foo,bar,baz
+where foo.y=bar.p and bar.q=baz.b and baz.b=foo.x and foo.x=bar.q
+  and bar.p=baz.a and bar.p=baz.a;
+x  y  p  q  a  b  
+-- -- -- -- -- -- 
+1  1  1  1  1  1  
+QUERY: select bar.p from foo, bar;
+p  
+-- 
+1  
+1  
+QUERY: select foo.x from foo, bar where foo.x = bar.p;
+x  
+-- 
+1  
+QUERY: drop table foo, bar, baz;
diff --git a/src/test/suite/results/oper.sql.out b/src/test/suite/results/oper.sql.out
new file mode 100644 (file)
index 0000000..d7b76be
--- /dev/null
@@ -0,0 +1,18 @@
+QUERY: create operator ##+ (leftarg=int4, rightarg=int4, procedure = int4pl);
+QUERY: create operator ##+ (rightarg=int4, procedure=int4fac);
+QUERY: create operator ##+ (leftarg=int4, procedure=int4inc);
+QUERY: select 4 ##+ 4;
+?column?  
+--------- 
+8         
+QUERY: select ##+ 4;
+?column?  
+--------- 
+24        
+QUERY: select (4 ##+);
+?column?  
+--------- 
+5         
+QUERY: drop operator ##+(int4,int4);
+QUERY: drop operator ##+(none, int4);
+QUERY: drop operator ##+(int4, none);
diff --git a/src/test/suite/results/parse.sql.out b/src/test/suite/results/parse.sql.out
new file mode 100644 (file)
index 0000000..0a97f30
--- /dev/null
@@ -0,0 +1,60 @@
+QUERY: create table foo (x int4, y int4, z int4);
+QUERY: create table bar (x int4, y int4, z int4);
+QUERY: create table baz (a int4, b int4);
+QUERY: insert into foo values (1, 2, 3);
+QUERY: insert into foo values (4, 5, 6);
+QUERY: insert into foo values (7, 8, 9);
+QUERY: insert into bar values (11, 12, 13);
+QUERY: insert into bar values (14, 15, 16);
+QUERY: insert into bar values (17, 18, 19);
+QUERY: insert into baz values (99, 88);
+QUERY: insert into baz values (77, 66);
+QUERY: select * from foo f where f.x = 4;
+x  y  z  
+-- -- -- 
+4  5  6  
+QUERY: select * from foo f, foo where f.x > foo.x;
+x  y  z  x  y  z  
+-- -- -- -- -- -- 
+4  5  6  1  2  3  
+7  8  9  1  2  3  
+7  8  9  4  5  6  
+QUERY: select * from foo f, foo where f.x = 1 and foo.z > f.z;
+x  y  z  x  y  z  
+-- -- -- -- -- -- 
+1  2  3  4  5  6  
+1  2  3  7  8  9  
+QUERY: select y as a, z as b from foo order by a;
+a  b  
+-- -- 
+2  3  
+5  6  
+8  9  
+QUERY: select foo.y as a, foo.z as b from foo order by b;
+a  b  
+-- -- 
+2  3  
+5  6  
+8  9  
+QUERY: select foo.*, bar.z, baz.* from foo, bar, baz;
+x  y  z  z   a   b   
+-- -- -- --- --- --- 
+1  2  3  13  99  88  
+4  5  6  13  99  88  
+7  8  9  13  99  88  
+1  2  3  16  99  88  
+4  5  6  16  99  88  
+7  8  9  16  99  88  
+1  2  3  19  99  88  
+4  5  6  19  99  88  
+7  8  9  19  99  88  
+1  2  3  13  77  66  
+4  5  6  13  77  66  
+7  8  9  13  77  66  
+1  2  3  16  77  66  
+4  5  6  16  77  66  
+7  8  9  16  77  66  
+1  2  3  19  77  66  
+4  5  6  19  77  66  
+7  8  9  19  77  66  
+QUERY: drop table foo, bar, baz;
diff --git a/src/test/suite/results/quote.sql.out b/src/test/suite/results/quote.sql.out
new file mode 100644 (file)
index 0000000..cba9580
--- /dev/null
@@ -0,0 +1,32 @@
+QUERY: create table quoteTBL (f text);
+QUERY: insert into quoteTBL values ('hello world');
+QUERY: insert into quoteTBL values ('hello '' world');
+QUERY: insert into quoteTBL values ('hello \' world');
+QUERY: insert into quoteTBL values ('hello \\ world');
+QUERY: insert into quoteTBL values ('hello \t world');
+QUERY: insert into quoteTBL values ('hello
+world
+with
+newlines
+');
+QUERY: insert into quoteTBL values ('hello " world');
+QUERY: insert into quoteTBL values ('');
+QUERY:   -- bad escape sequence
+insert into quoteTBL values ('hello \y world');
+WARN:Bad escape sequence, s[i] = 121
+QUERY: select * from quoteTBL;
+f                           
+--------------------------- 
+hello world                 
+hello ' world               
+hello ' world               
+hello \ world               
+hello   world               
+hello
+world
+with
+newlines
+  
+hello " world               
+                            
+QUERY: drop table quoteTBL;
diff --git a/src/test/suite/results/rules.sql.out b/src/test/suite/results/rules.sql.out
new file mode 100644 (file)
index 0000000..b39e7af
--- /dev/null
@@ -0,0 +1,22 @@
+QUERY: create table foo (x int4);
+QUERY: select * from foo;
+x  
+-- 
+QUERY: create table bar (x int4, y float4);
+QUERY: create rule rule1 as on insert to bar do insert into foo (x) values (new.x);
+QUERY: insert into bar (x,y) values (10, -10.0);
+QUERY: insert into bar (x,y) values (20, -20.0);
+QUERY: insert into bar (x,y) values (30, 3.14159);
+QUERY: select * from bar;
+x   y        
+--- -------- 
+10  -10      
+20  -20      
+30  3.14159  
+QUERY: select * from foo;
+x   
+--- 
+10  
+20  
+30  
+QUERY: drop table foo, bar;
diff --git a/src/test/suite/results/select.sql.out b/src/test/suite/results/select.sql.out
new file mode 100644 (file)
index 0000000..ba4c75d
--- /dev/null
@@ -0,0 +1,31 @@
+QUERY: select 1 as X;
+X  
+-- 
+1  
+QUERY: create table foo (name char16, salary int4);
+QUERY: insert into foo values ('mike', 15000);
+QUERY: select * from foo where 2 > 1;
+name  salary  
+----- ------- 
+mike  15000   
+QUERY: select * from pg_class where 1=0;
+relname  reltype  relowner  relam  relpages  reltuples  relexpires  relpreserved  relhasindex  relisshared  relkind  relarch  relnatts  relsmgr  relkey  relkeyop  relhasrules  relacl  
+-------- -------- --------- ------ --------- ---------- ----------- ------------- ------------ ------------ -------- -------- --------- -------- ------- --------- ------------ ------- 
+QUERY: create table bar (x int4);
+QUERY: insert into bar values (1);
+QUERY: insert into bar values (2);
+QUERY: insert into bar values (1);
+QUERY: select distinct * from bar;
+x  
+-- 
+1  
+2  
+QUERY: select distinct * into table bar2 from bar;
+QUERY: select distinct * from bar2;
+x  
+-- 
+1  
+2  
+QUERY: drop table foo;
+QUERY: drop table bar;
+QUERY: drop table bar2;
diff --git a/src/test/suite/results/sort.sql.out b/src/test/suite/results/sort.sql.out
new file mode 100644 (file)
index 0000000..322ce59
--- /dev/null
@@ -0,0 +1,229 @@
+QUERY: create table s1 (x int4, y int4);
+QUERY: create table s2 (a int4, b int4, c int4);
+QUERY: insert into s1 values (1, 3);
+QUERY: insert into s1 values (2, 3);
+QUERY: insert into s1 values (2, 1);
+QUERY: insert into s2 values (1, 3, 9);
+QUERY: insert into s2 values (1, 4, 9);
+QUERY: insert into s2 values (3, 4, 7);
+QUERY: insert into s2 values (3, 5, 8);
+QUERY: select distinct y from s1;
+y  
+-- 
+1  
+3  
+QUERY: select a, c from s2;
+a  c  
+-- -- 
+1  9  
+1  9  
+3  7  
+3  8  
+QUERY: select distinct a, c from s2;
+a  c  
+-- -- 
+1  9  
+3  7  
+3  8  
+QUERY: select distinct a, c from s2 order by c;
+a  c  
+-- -- 
+3  7  
+3  8  
+1  9  
+QUERY: select b, c from s2 order by c, b;
+b  c  
+-- -- 
+4  7  
+5  8  
+3  9  
+4  9  
+QUERY: select x, b, c from s1, s2 order by b;
+x  b  c  
+-- -- -- 
+2  3  9  
+2  3  9  
+1  3  9  
+2  4  7  
+2  4  7  
+1  4  7  
+2  4  9  
+2  4  9  
+1  4  9  
+2  5  8  
+2  5  8  
+1  5  8  
+QUERY: select distinct a, x, c from s1, s2 order by c, x;
+a  x  c  
+-- -- -- 
+3  1  7  
+3  2  7  
+3  1  8  
+3  2  8  
+1  1  9  
+1  2  9  
+QUERY: select x AS p, b AS q, c AS r from s1, s2 order by p;
+p  q  r  
+-- -- -- 
+1  5  8  
+1  4  7  
+1  4  9  
+1  3  9  
+2  3  9  
+2  3  9  
+2  5  8  
+2  5  8  
+2  4  9  
+2  4  7  
+2  4  9  
+2  4  7  
+QUERY: select x AS p, b AS q, c AS r from s1, s2 order by q;
+p  q  r  
+-- -- -- 
+2  3  9  
+2  3  9  
+1  3  9  
+2  4  7  
+2  4  7  
+1  4  7  
+2  4  9  
+2  4  9  
+1  4  9  
+2  5  8  
+2  5  8  
+1  5  8  
+QUERY: select x AS p, b AS q, c AS r from s1, s2 order by r;
+p  q  r  
+-- -- -- 
+2  4  7  
+2  4  7  
+1  4  7  
+2  5  8  
+2  5  8  
+1  5  8  
+2  4  9  
+2  4  9  
+1  4  9  
+2  3  9  
+2  3  9  
+1  3  9  
+QUERY: select x AS p, b AS q, c AS r from s1, s2 order by p, r;
+p  q  r  
+-- -- -- 
+1  4  7  
+1  5  8  
+1  4  9  
+1  3  9  
+2  4  7  
+2  4  7  
+2  5  8  
+2  5  8  
+2  3  9  
+2  4  9  
+2  3  9  
+2  4  9  
+QUERY: select x AS p, b AS q, c AS r from s1, s2 order by q, r;
+p  q  r  
+-- -- -- 
+2  3  9  
+2  3  9  
+1  3  9  
+2  4  7  
+2  4  7  
+1  4  7  
+2  4  9  
+2  4  9  
+1  4  9  
+2  5  8  
+2  5  8  
+1  5  8  
+QUERY: select x AS p, b AS q, c AS r from s1, s2 order by q, p;
+p  q  r  
+-- -- -- 
+1  3  9  
+2  3  9  
+2  3  9  
+1  4  9  
+1  4  7  
+2  4  7  
+2  4  7  
+2  4  9  
+2  4  9  
+1  5  8  
+2  5  8  
+2  5  8  
+QUERY: create table s3 (x int4);
+QUERY: insert into s3 values (3);
+QUERY: insert into s3 values (4);
+QUERY: select * from s1, s3 order by x;
+x  y  x  
+-- -- -- 
+1  3  4  
+1  3  3  
+2  1  3  
+2  3  3  
+2  1  4  
+2  3  4  
+QUERY: select * from s3, s1 order by x;
+x  x  y  
+-- -- -- 
+3  2  1  
+3  2  3  
+3  1  3  
+4  2  3  
+4  1  3  
+4  2  1  
+QUERY: create table s4 (a int4, b int4, c int4, d int4, e int4, f int4, g int4, h int4, i int4);
+QUERY: insert into s4 values (1, 1, 1, 1, 1, 1, 1, 1, 2);
+QUERY: insert into s4 values (1, 1, 1, 1, 1, 1, 1, 1, 1);
+QUERY: insert into s4 values (1, 1, 1, 1, 1, 1, 1, 1, 3);
+QUERY: select * from s4 order by a, b, c, d, e, f, g, h;
+a  b  c  d  e  f  g  h  i  
+-- -- -- -- -- -- -- -- -- 
+1  1  1  1  1  1  1  1  3  
+1  1  1  1  1  1  1  1  1  
+1  1  1  1  1  1  1  1  2  
+QUERY: create table s5 (a int4, b int4);
+QUERY: insert into s5 values (1, 2);
+QUERY: insert into s5 values (1, 3);
+QUERY: insert into s5 values (1, 1);
+QUERY: insert into s5 values (2, 1);
+QUERY: insert into s5 values (2, 4);
+QUERY: insert into s5 values (2, 2);
+QUERY: select * from s5 order by a using <;
+a  b  
+-- -- 
+1  1  
+1  3  
+1  2  
+2  2  
+2  4  
+2  1  
+QUERY: select * from s5 order by a using >;
+a  b  
+-- -- 
+2  2  
+2  4  
+2  1  
+1  1  
+1  3  
+1  2  
+QUERY: select * from s5 order by a using >, b using <;
+a  b  
+-- -- 
+2  1  
+2  2  
+2  4  
+1  1  
+1  2  
+1  3  
+QUERY: select * from s5 order by a using >, b using >;
+a  b  
+-- -- 
+2  4  
+2  2  
+2  1  
+1  3  
+1  2  
+1  1  
+QUERY: drop table s1, s2, s3, s4, s5;
diff --git a/src/test/suite/results/sqlcompat.sql.out b/src/test/suite/results/sqlcompat.sql.out
new file mode 100644 (file)
index 0000000..76aee79
--- /dev/null
@@ -0,0 +1,100 @@
+QUERY: create table st1 (x int, y integer, z int4);
+QUERY: insert into st1 values (1);
+QUERY: insert into st1 values (10);
+QUERY: select * from st1;
+x   y  z  
+--- -- -- 
+1         
+10        
+QUERY: create table st2 (x smallint, y int2);
+QUERY: insert into st2 values (1);
+QUERY: insert into st2 values (10);
+QUERY: select * from st2;
+x   y  
+--- -- 
+1      
+10     
+QUERY: create table st3 (x float, y real, z float4);
+QUERY: insert into st3 values (1);
+QUERY: insert into st3 values (10);
+QUERY: select * from st3;
+x   y  z  
+--- -- -- 
+1         
+10        
+QUERY: create table st4 (x float8);
+QUERY: insert into st4 values (1);
+QUERY: insert into st4 values (10);
+QUERY: select * from st4;
+x   
+--- 
+1   
+10  
+QUERY: select max(x) from st1;
+max  
+---- 
+10   
+QUERY: select min(x) from st1;
+min  
+---- 
+1    
+QUERY: select sum(x) from st1;
+sum  
+---- 
+11   
+QUERY: select avg(x) from st1;
+avg  
+---- 
+5    
+QUERY: select max(x) from st2;
+max  
+---- 
+10   
+QUERY: select min(x) from st2;
+min  
+---- 
+1    
+QUERY: select sum(x) from st2;
+sum  
+---- 
+11   
+QUERY: select avg(x) from st2;
+avg  
+---- 
+5    
+QUERY: select max(x) from st3;
+max  
+---- 
+10   
+QUERY: select min(x) from st3;
+min  
+---- 
+1    
+QUERY: select sum(x) from st3;
+sum  
+---- 
+11   
+QUERY: select avg(x) from st3;
+avg  
+---- 
+5.5  
+QUERY: select max(x) from st4;
+max  
+---- 
+10   
+QUERY: select min(x) from st4;
+min  
+---- 
+1    
+QUERY: select sum(x) from st4;
+sum  
+---- 
+11   
+QUERY: select avg(x) from st4;
+avg  
+---- 
+5.5  
+QUERY: drop table st1;
+QUERY: drop table st2;
+QUERY: drop table st3;
+QUERY: drop table st4;
diff --git a/src/test/suite/results/time.sql.out b/src/test/suite/results/time.sql.out
new file mode 100644 (file)
index 0000000..9cc3a27
--- /dev/null
@@ -0,0 +1,72 @@
+QUERY: create table tt (t time);
+QUERY: insert into tt values ('6:22:19.95');
+QUERY: insert into tt values ('5:31:19.94');
+QUERY: insert into tt values ('2:29:1.996');
+QUERY: insert into tt values ('23:59:59.93');
+QUERY: insert into tt values ('0:0:0.0');
+QUERY: insert into tt values ('2:29:1.996');
+QUERY: select * from tt;
+t                
+---------------- 
+06:22:19.950001  
+05:31:19.940001  
+02:29:01.996000  
+23:59:59.930000  
+00:00:00.000000  
+02:29:01.996000  
+QUERY: select * from tt order by t;
+t                
+---------------- 
+00:00:00.000000  
+02:29:01.996000  
+02:29:01.996000  
+05:31:19.940001  
+06:22:19.950001  
+23:59:59.930000  
+QUERY: select * from tt order by t using >;
+t                
+---------------- 
+23:59:59.930000  
+06:22:19.950001  
+05:31:19.940001  
+02:29:01.996000  
+02:29:01.996000  
+00:00:00.000000  
+QUERY: select * from tt where t = '2:29:1.996';
+t                
+---------------- 
+02:29:01.996000  
+02:29:01.996000  
+QUERY: select * from tt where t <> '2:29:1.996';
+t                
+---------------- 
+06:22:19.950001  
+05:31:19.940001  
+23:59:59.930000  
+00:00:00.000000  
+QUERY: select * from tt where t < '2:29:1.996';
+t                
+---------------- 
+00:00:00.000000  
+QUERY: select * from tt where t <= '2:29:1.996';
+t                
+---------------- 
+02:29:01.996000  
+00:00:00.000000  
+02:29:01.996000  
+QUERY: select * from tt where t > '2:29:1.996';
+t                
+---------------- 
+06:22:19.950001  
+05:31:19.940001  
+23:59:59.930000  
+QUERY: select * from tt where t >= '2:29:1.996';
+t                
+---------------- 
+06:22:19.950001  
+05:31:19.940001  
+02:29:01.996000  
+23:59:59.930000  
+02:29:01.996000  
+QUERY: create index tt_ind on tt using btree (t time_ops);
+QUERY: drop table tt;
diff --git a/src/test/suite/results/varchar.sql.out b/src/test/suite/results/varchar.sql.out
new file mode 100644 (file)
index 0000000..535ef8f
--- /dev/null
@@ -0,0 +1,226 @@
+QUERY: create table f (x char(5));
+QUERY: insert into f values ('zoo');
+QUERY: insert into f values ('a');
+QUERY: insert into f values ('jet');
+QUERY: insert into f values ('abc');
+QUERY: insert into f values ('');
+QUERY: insert into f values ('a c');
+QUERY: insert into f values ('abxyzxyz');
+QUERY: select * from f;
+x      
+------ 
+zoo    
+a      
+jet    
+abc    
+       
+a c    
+abxyz  
+QUERY: select * from f where x = 'jet';
+x      
+------ 
+jet    
+QUERY: select * from f where x <> 'jet';
+x      
+------ 
+zoo    
+a      
+abc    
+       
+a c    
+abxyz  
+QUERY: select * from f where x < 'jet';
+x      
+------ 
+a      
+abc    
+       
+a c    
+abxyz  
+QUERY: select * from f where x <= 'jet';
+x      
+------ 
+a      
+jet    
+abc    
+       
+a c    
+abxyz  
+QUERY: select * from f where x > 'jet';
+x      
+------ 
+zoo    
+QUERY: select * from f where x >= 'jet';
+x      
+------ 
+zoo    
+jet    
+       
+QUERY: select * from f where x = 'ab';
+x  
+-- 
+QUERY: select * from f where x <> 'ab';
+x      
+------ 
+zoo    
+a      
+jet    
+abc    
+       
+a c    
+abxyz  
+QUERY: select * from f where x < 'ab';
+x      
+------ 
+a      
+       
+a c    
+QUERY: select * from f where x <= 'ab';
+x      
+------ 
+a      
+abc    
+       
+a c    
+abxyz  
+QUERY: select * from f where x > 'ab';
+x      
+------ 
+zoo    
+jet    
+abc    
+abxyz  
+QUERY: select * from f where x >= 'ab';
+x      
+------ 
+zoo    
+a      
+jet    
+abc    
+       
+abxyz  
+QUERY: select * from f order by x;
+x      
+------ 
+       
+a      
+a c    
+abc    
+abxyz  
+jet    
+zoo    
+QUERY: create table ff (x varchar(5));
+QUERY: insert into ff values ('a');
+QUERY: insert into ff values ('zoo');
+QUERY: insert into ff values ('jet');
+QUERY: insert into ff values ('abc');
+QUERY: insert into ff values ('');
+QUERY: insert into ff values ('a c');
+QUERY: insert into ff values ('abxyzxyz');
+QUERY: select * from ff;
+x      
+------ 
+a      
+zoo    
+jet    
+abc    
+       
+a c    
+abxyz  
+QUERY: select * from ff where x = 'jet';
+x    
+---- 
+jet  
+QUERY: select * from ff where x <> 'jet';
+x      
+------ 
+a      
+zoo    
+abc    
+       
+a c    
+abxyz  
+QUERY: select * from ff where x < 'jet';
+x      
+------ 
+a      
+abc    
+       
+a c    
+abxyz  
+QUERY: select * from ff where x <= 'jet';
+x      
+------ 
+a      
+jet    
+abc    
+       
+a c    
+abxyz  
+QUERY: select * from ff where x > 'jet';
+x    
+---- 
+zoo  
+QUERY: select * from ff where x >= 'jet';
+x    
+---- 
+zoo  
+jet  
+     
+QUERY: select * from ff where x = 'ab';
+x  
+-- 
+QUERY: select * from ff where x <> 'ab';
+x      
+------ 
+a      
+zoo    
+jet    
+abc    
+       
+a c    
+abxyz  
+QUERY: select * from ff where x < 'ab';
+x    
+---- 
+a    
+     
+a c  
+QUERY: select * from ff where x <= 'ab';
+x      
+------ 
+a      
+abc    
+       
+a c    
+abxyz  
+QUERY: select * from ff where x > 'ab';
+x      
+------ 
+zoo    
+jet    
+abc    
+abxyz  
+QUERY: select * from ff where x >= 'ab';
+x      
+------ 
+a      
+zoo    
+jet    
+abc    
+       
+abxyz  
+QUERY: select * from ff order by x using >;
+x      
+------ 
+zoo    
+jet    
+abxyz  
+abc    
+a c    
+a      
+       
+QUERY: create index f_ind on f using btree (x bpchar_ops);
+QUERY: create index ff_ind on ff using btree (x varchar_ops);
+QUERY: drop table f;
+QUERY: drop table ff;
diff --git a/src/test/suite/results/views.sql.out b/src/test/suite/results/views.sql.out
new file mode 100644 (file)
index 0000000..42b45b2
--- /dev/null
@@ -0,0 +1,125 @@
+QUERY: create table v1 (x int4, y int4, z int4);
+QUERY: insert into v1 values (1, 2, 3);
+QUERY: insert into v1 values (1, 3, 4);
+QUERY: insert into v1 values (1, 4, 5);
+QUERY: insert into v1 values (1, 2, 6);
+QUERY: create view vv1 as select x from v1;
+QUERY: create view vv2 as select y from v1;
+QUERY: create view vv3 as select z from v1;
+QUERY: select * from vv1;
+x  
+-- 
+1  
+1  
+1  
+1  
+QUERY: select * from vv2;
+y  
+-- 
+2  
+3  
+4  
+2  
+QUERY: select * from vv3;
+z  
+-- 
+3  
+4  
+5  
+6  
+QUERY: drop view vv2;
+QUERY: drop view vv3;
+QUERY: create view vv as select * from vv1;
+QUERY: select * from vv;
+x  
+-- 
+1  
+1  
+1  
+1  
+QUERY: create view vv2 as select x from vv;
+QUERY: select * from vv2;
+x  
+-- 
+1  
+1  
+1  
+1  
+QUERY: drop view vv;
+QUERY: drop view vv1;
+QUERY: drop view vv2;
+QUERY: create view vv1 as select x, z from v1;
+QUERY: create view vv2 as select y, z from v1;
+QUERY: create view vv3 as select y, z, x from v1;
+QUERY: select * from vv1;
+x  z  
+-- -- 
+1  3  
+1  4  
+1  5  
+1  6  
+QUERY: select * from vv2;
+y  z  
+-- -- 
+2  3  
+3  4  
+4  5  
+2  6  
+QUERY: select * from vv3;
+y  z  x  
+-- -- -- 
+2  3  1  
+3  4  1  
+4  5  1  
+2  6  1  
+QUERY: drop view vv1;
+QUERY: drop view vv2;
+QUERY: drop view vv3;
+QUERY: create view vv1 as select x as a, z as b, y as c from v1;
+QUERY: select * from vv1;
+a  b  c  
+-- -- -- 
+1  3  2  
+1  4  3  
+1  5  4  
+1  6  2  
+QUERY: drop view vv1;
+QUERY: create view vv1 as select z, 100 as p, x as q from v1;
+QUERY: select * from vv1;
+z  p    q  
+-- ---- -- 
+3  100  1  
+4  100  1  
+5  100  1  
+6  100  1  
+QUERY: drop view vv1;
+QUERY: create view vv1 as select x + y as xy, z from v1;
+QUERY: select * from vv1;
+xy  z  
+--- -- 
+3   3  
+4   4  
+5   5  
+3   6  
+QUERY: drop view vv1;
+QUERY: create table v2 (a int4);
+QUERY: insert into v2 values (2);
+QUERY: insert into v2 values (3);
+QUERY: create view vv1 as select y, z from v1, v2 where y = a;
+QUERY: select * from vv1;
+y  z  
+-- -- 
+2  6  
+2  3  
+3  4  
+QUERY: drop view vv1;
+QUERY: create view vv1 as select y - x as yx, z, a from v1, v2 where (x + y) > 3;
+QUERY: select * from vv1;
+yx  z  a  
+--- -- -- 
+2   4  2  
+3   5  2  
+2   4  3  
+3   5  3  
+QUERY: drop view vv1;
+QUERY: drop table v1, v2;
diff --git a/src/test/suite/rules.sql b/src/test/suite/rules.sql
new file mode 100644 (file)
index 0000000..b018fdf
--- /dev/null
@@ -0,0 +1,29 @@
+---------------------------------------------------------------------------
+--
+-- rules.sql-
+--    test rules
+--
+--
+-- Copyright (c) 1994-5, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+-- test rules creation
+create table foo (x int4);
+-- instead rules are not working right now
+-- create rule rule1 as on select to foo.x do instead update foo set x = 2;
+-- select rulename, ev_class, ev_type from pg_rewrite;
+select * from foo;
+
+create table bar (x int4, y float4);
+create rule rule1 as on insert to bar do insert into foo (x) values (new.x);
+insert into bar (x,y) values (10, -10.0);
+insert into bar (x,y) values (20, -20.0);
+insert into bar (x,y) values (30, 3.14159);
+
+select * from bar;
+select * from foo;
+drop table foo, bar;
+
diff --git a/src/test/suite/runall b/src/test/suite/runall
new file mode 100755 (executable)
index 0000000..d657e89
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/csh
+
+foreach s (*.sql)
+       echo "===> $s";
+       psql -q -e -n $USER < $s >& $s.out;
+       diff $s.out results/$s.out;
+end
+
diff --git a/src/test/suite/select.sql b/src/test/suite/select.sql
new file mode 100644 (file)
index 0000000..664cc88
--- /dev/null
@@ -0,0 +1,31 @@
+---------------------------------------------------------------------------
+--
+-- select.sql-
+--    test select
+--
+--
+-- Copyright (c) 1994-5, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+-- test Result nodes (constant target list/quals)
+select 1 as X;
+create table foo (name char16, salary int4);
+insert into foo values ('mike', 15000);
+select * from foo where 2 > 1;
+select * from pg_class where 1=0;
+
+-- test select distinct
+create table bar (x int4);
+insert into bar values (1);
+insert into bar values (2);
+insert into bar values (1);
+select distinct * from bar;
+select distinct * into table bar2 from bar;
+select distinct * from bar2;
+
+drop table foo;
+drop table bar;
+drop table bar2;
diff --git a/src/test/suite/sort.sql b/src/test/suite/sort.sql
new file mode 100644 (file)
index 0000000..ae2487d
--- /dev/null
@@ -0,0 +1,57 @@
+---------------------------------------------------------------------------
+--
+-- sort.sql-
+--    test sorting
+--
+--
+-- Copyright (c) 1994-5, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+create table s1 (x int4, y int4);
+create table s2 (a int4, b int4, c int4);
+insert into s1 values (1, 3);
+insert into s1 values (2, 3);
+insert into s1 values (2, 1);
+insert into s2 values (1, 3, 9);
+insert into s2 values (1, 4, 9);
+insert into s2 values (3, 4, 7);
+insert into s2 values (3, 5, 8);
+select distinct y from s1;
+select a, c from s2;
+select distinct a, c from s2;
+select distinct a, c from s2 order by c;
+select b, c from s2 order by c, b;
+select x, b, c from s1, s2 order by b;
+select distinct a, x, c from s1, s2 order by c, x;
+select x AS p, b AS q, c AS r from s1, s2 order by p;
+select x AS p, b AS q, c AS r from s1, s2 order by q;
+select x AS p, b AS q, c AS r from s1, s2 order by r;
+select x AS p, b AS q, c AS r from s1, s2 order by p, r;
+select x AS p, b AS q, c AS r from s1, s2 order by q, r;
+select x AS p, b AS q, c AS r from s1, s2 order by q, p;
+create table s3 (x int4);
+insert into s3 values (3);
+insert into s3 values (4);
+select * from s1, s3 order by x;
+select * from s3, s1 order by x;
+create table s4 (a int4, b int4, c int4, d int4, e int4, f int4, g int4, h int4, i int4);
+insert into s4 values (1, 1, 1, 1, 1, 1, 1, 1, 2);
+insert into s4 values (1, 1, 1, 1, 1, 1, 1, 1, 1);
+insert into s4 values (1, 1, 1, 1, 1, 1, 1, 1, 3);
+select * from s4 order by a, b, c, d, e, f, g, h;
+create table s5 (a int4, b int4);
+insert into s5 values (1, 2);
+insert into s5 values (1, 3);
+insert into s5 values (1, 1);
+insert into s5 values (2, 1);
+insert into s5 values (2, 4);
+insert into s5 values (2, 2);
+select * from s5 order by a using <;
+select * from s5 order by a using >;
+select * from s5 order by a using >, b using <;
+select * from s5 order by a using >, b using >;
+
+drop table s1, s2, s3, s4, s5;
diff --git a/src/test/suite/sqlcompat.sql b/src/test/suite/sqlcompat.sql
new file mode 100644 (file)
index 0000000..82a5d69
--- /dev/null
@@ -0,0 +1,57 @@
+---------------------------------------------------------------------------
+--
+-- sqlcompat.sql-
+--    test aliases for SQL basic types and aggregates
+--
+--
+-- Copyright (c) 1994-5, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+-- check aliases for data types
+create table st1 (x int, y integer, z int4);
+insert into st1 values (1);
+insert into st1 values (10);
+select * from st1;
+create table st2 (x smallint, y int2);
+insert into st2 values (1);
+insert into st2 values (10);
+select * from st2;
+create table st3 (x float, y real, z float4);
+insert into st3 values (1);
+insert into st3 values (10);
+select * from st3;
+
+create table st4 (x float8);
+insert into st4 values (1);
+insert into st4 values (10);
+select * from st4;
+
+-- check aliases for aggregate names
+select max(x) from st1;
+select min(x) from st1;
+select sum(x) from st1;
+select avg(x) from st1;
+
+select max(x) from st2;
+select min(x) from st2;
+select sum(x) from st2;
+select avg(x) from st2;
+
+select max(x) from st3;
+select min(x) from st3;
+select sum(x) from st3;
+select avg(x) from st3;
+
+select max(x) from st4;
+select min(x) from st4;
+select sum(x) from st4;
+select avg(x) from st4;
+
+drop table st1;
+drop table st2;
+drop table st3;
+drop table st4;
+
diff --git a/src/test/suite/time.sql b/src/test/suite/time.sql
new file mode 100644 (file)
index 0000000..d46fa95
--- /dev/null
@@ -0,0 +1,30 @@
+---------------------------------------------------------------------------
+--
+-- time.sql-
+--    test TIME adt
+--
+--
+-- Copyright (c) 1994-5, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+create table tt (t time);
+insert into tt values ('6:22:19.95');
+insert into tt values ('5:31:19.94');
+insert into tt values ('2:29:1.996');
+insert into tt values ('23:59:59.93');
+insert into tt values ('0:0:0.0');
+insert into tt values ('2:29:1.996');
+select * from tt;
+select * from tt order by t;
+select * from tt order by t using >;
+select * from tt where t = '2:29:1.996';
+select * from tt where t <> '2:29:1.996';
+select * from tt where t < '2:29:1.996';
+select * from tt where t <= '2:29:1.996';
+select * from tt where t > '2:29:1.996';
+select * from tt where t >= '2:29:1.996';
+create index tt_ind on tt using btree (t time_ops);
+drop table tt;
diff --git a/src/test/suite/varchar.sql b/src/test/suite/varchar.sql
new file mode 100644 (file)
index 0000000..8e35714
--- /dev/null
@@ -0,0 +1,64 @@
+---------------------------------------------------------------------------
+--
+-- varchar.sql-
+--    test CHAR() and VARCHAR() adts
+--
+--
+-- Copyright (c) 1994-5, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+-- test char(): insert w/ boundary cases
+create table f (x char(5));
+insert into f values ('zoo');
+insert into f values ('a');
+insert into f values ('jet');
+insert into f values ('abc');
+insert into f values ('');
+insert into f values ('a c');
+insert into f values ('abxyzxyz');
+select * from f;
+select * from f where x = 'jet';
+select * from f where x <> 'jet';
+select * from f where x < 'jet';
+select * from f where x <= 'jet';
+select * from f where x > 'jet';
+select * from f where x >= 'jet';
+select * from f where x = 'ab';
+select * from f where x <> 'ab';
+select * from f where x < 'ab';
+select * from f where x <= 'ab';
+select * from f where x > 'ab';
+select * from f where x >= 'ab';
+select * from f order by x;
+-- test varchar(): insert w/ boundary cases
+create table ff (x varchar(5));
+insert into ff values ('a');
+insert into ff values ('zoo');
+insert into ff values ('jet');
+insert into ff values ('abc');
+insert into ff values ('');
+insert into ff values ('a c');
+insert into ff values ('abxyzxyz');
+select * from ff;
+select * from ff where x = 'jet';
+select * from ff where x <> 'jet';
+select * from ff where x < 'jet';
+select * from ff where x <= 'jet';
+select * from ff where x > 'jet';
+select * from ff where x >= 'jet';
+select * from ff where x = 'ab';
+select * from ff where x <> 'ab';
+select * from ff where x < 'ab';
+select * from ff where x <= 'ab';
+select * from ff where x > 'ab';
+select * from ff where x >= 'ab';
+select * from ff order by x using >;
+
+create index f_ind on f using btree (x bpchar_ops);
+create index ff_ind on ff using btree (x varchar_ops);
+
+drop table f;
+drop table ff;
diff --git a/src/test/suite/views.sql b/src/test/suite/views.sql
new file mode 100644 (file)
index 0000000..a318aea
--- /dev/null
@@ -0,0 +1,77 @@
+---------------------------------------------------------------------------
+--
+-- views.sql-
+--    test views queries
+--
+--
+-- Copyright (c) 1994-5, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+-- create a real table first
+create table v1 (x int4, y int4, z int4);
+insert into v1 values (1, 2, 3);
+insert into v1 values (1, 3, 4);
+insert into v1 values (1, 4, 5);
+insert into v1 values (1, 2, 6);
+
+-- create views for selecting single column
+create view vv1 as select x from v1;
+create view vv2 as select y from v1;
+create view vv3 as select z from v1;
+select * from vv1;
+select * from vv2;
+select * from vv3;
+drop view vv2;
+drop view vv3;
+
+-- create views for selecting column(s) from another view
+create view vv as select * from vv1;
+select * from vv;
+
+create view vv2 as select x from vv;
+select * from vv2;
+drop view vv;
+drop view vv1;
+drop view vv2;
+
+-- create views for selecting multiple columns 
+create view vv1 as select x, z from v1;
+create view vv2 as select y, z from v1;
+create view vv3 as select y, z, x from v1;
+select * from vv1;
+select * from vv2;
+select * from vv3;
+drop view vv1;
+drop view vv2;
+drop view vv3;
+
+-- create views with expressions
+create view vv1 as select x as a, z as b, y as c from v1;
+select * from vv1;
+drop view vv1;
+
+create view vv1 as select z, 100 as p, x as q from v1;
+select * from vv1;
+drop view vv1;
+
+create view vv1 as select x + y as xy, z from v1;
+select * from vv1;
+drop view vv1;
+
+-- create views of joins
+create table v2 (a int4);
+insert into v2 values (2);
+insert into v2 values (3);
+
+create view vv1 as select y, z from v1, v2 where y = a;
+select * from vv1;
+drop view vv1;
+
+create view vv1 as select y - x as yx, z, a from v1, v2 where (x + y) > 3;
+select * from vv1;
+drop view vv1;
+
+drop table v1, v2;
diff --git a/src/tools/mkldexport/Makefile b/src/tools/mkldexport/Makefile
new file mode 100644 (file)
index 0000000..1171e37
--- /dev/null
@@ -0,0 +1,20 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+#    Makefile for tools/mkldexport
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+SHPROG=mkldexport
+
+MKDIR= ../../mk
+include ../../Makefile.global
+include $(MKDIR)/postgres.mk
+
+include $(MKDIR)/postgres.shell.mk
diff --git a/src/tools/mkldexport/README b/src/tools/mkldexport/README
new file mode 100644 (file)
index 0000000..83415d7
--- /dev/null
@@ -0,0 +1,12 @@
+mkldexport is a script for creating an AIX exports from an object file.
+       
+Usage:
+       mkldexport objectfile [location]
+ where
+       objectfile is the current location of the object file.
+       location is the eventual (installed) location of the 
+       object file (if different from the current
+       working directory).
+
+Written originally by Paul Aoki for postgres v4r2.
+
diff --git a/src/tools/mkldexport/mkldexport.sh b/src/tools/mkldexport/mkldexport.sh
new file mode 100644 (file)
index 0000000..cd07a45
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# mkldexport
+#      create an AIX exports file from an object file
+#
+# Usage:
+#      mkldexport objectfile [location]
+# where
+#      objectfile is the current location of the object file.
+#      location is the eventual (installed) location of the 
+#              object file (if different from the current
+#              working directory).
+#
+# /usr/local/devel/postgres-v4r2/src/tools/mkldexport/RCS/mkldexport.sh,v 1.2 1994/03/13 04:59:12 aoki Exp
+#
+CMDNAME=`basename $0`
+if [ -z "$1" ]; then
+       echo "Usage: $CMDNAME object [location]"
+       exit 1
+fi
+OBJNAME=`basename $1`
+if [ "`basename $OBJNAME`" != "`basename $OBJNAME .o`" ]; then
+       OBJNAME=`basename $OBJNAME .o`.so
+fi
+if [ -z "$2" ]; then
+       echo '#!'
+else
+       echo '#!' $2/$OBJNAME
+fi
+/usr/ucb/nm -g $1 | \
+       egrep ' [TD] ' | \
+       sed -e 's/.* //' | \
+       egrep -v '\$' | \
+       sed -e 's/^[.]//' | \
+       sort | \
+       uniq
diff --git a/src/tutorial/C-code/beard.c b/src/tutorial/C-code/beard.c
new file mode 100644 (file)
index 0000000..24b5083
--- /dev/null
@@ -0,0 +1,64 @@
+/*-------------------------------------------------------------------------
+ *
+ * beard.c--
+ *    sample routines to use large objects
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *    $Header$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+typedef struct ImageHdr {
+    int                size;
+} ImageHdr;
+
+#define BUFSIZE 10
+
+/*
+ * beard -
+ *   clips lower 1/3 of picture and return as large object
+ */
+Oid
+beard(Oid picture)
+{
+    Oid beard;
+    int pic_fd, beard_fd;
+    ImageHdr ihdr;
+    char buf[BUFSIZE];
+    int cc;
+
+    if ((pic_fd = lo_open(picture, INV_READ)) == -1)
+       elog(WARN, "Cannot access picture large object");
+
+    if (lo_read(pic_fd, (char*)&ihdr, sizeof(ihdr)) != sizeof(ihdr))
+       elog(WARN, "Picture large object corrupted");
+
+    beardOffset = (ihdr.size / 3) * 2;
+
+    /*
+     * new large object
+     */
+    if ((beard = lo_creat(INV_MD)) == 0)  /* ?? is this right? */
+       elog(WARN, "Cannot create new large object");
+
+    if ((beard_fd = lo_open(beard, INV_WRITE)) == -1)
+       elog(WARN, "Cannot access beard large object");
+
+    lo_lseek(pic_fd, beardOffset, SET_CUR);
+    while ((cc = lo_read(pic_fd, buf, BUFSIZE)) > 0) {
+       if (lo_write(beard_fd, buf, cc) != cc)
+           elog(WARN, "error while writing large object");
+    }
+
+    lo_close(pic_fd);
+    lo_close(beard_fd);
+
+    return beard;
+}
+
+
+
diff --git a/src/tutorial/C-code/complex.c b/src/tutorial/C-code/complex.c
new file mode 100644 (file)
index 0000000..bebdd51
--- /dev/null
@@ -0,0 +1,150 @@
+#include <stdio.h>
+/* do not include libpq-fe.h for backend-loaded functions*/
+/* #include "libpq-fe.h"  */
+#include "postgres.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/mcxt.h"
+
+typedef struct Complex {
+    double     x;
+    double     y;
+} Complex;
+
+/*****************************************************************************
+ * Input/Output functions
+ *****************************************************************************/
+
+Complex *
+complex_in(char *str)
+{
+    double x, y;
+    Complex *result;
+    
+    if (sscanf(str, " ( %lf , %lf )", &x, &y) != 2) {
+       elog(WARN, "complex_in: error in parsing \"%s\"", str);
+       return NULL;
+    }
+    result = (Complex *)palloc(sizeof(Complex));
+    result->x = x;
+    result->y = y;
+    return (result);
+}
+
+/*
+ * You might have noticed a slight inconsistency between the following
+ * declaration and the SQL definition:
+ *     CREATE FUNCTION complex_out(opaque) RETURNS opaque ...
+ * The reason is that the argument pass into complex_out is really just a
+ * pointer. POSTGRES thinks all output functions are:
+ *     char *out_func(char *);
+ */
+char *
+complex_out(Complex *complex)
+{
+    char *result;
+
+    if (complex == NULL)
+       return(NULL);
+
+    result = (char *) palloc(60);
+    sprintf(result, "(%lg,%lg)", complex->x, complex->y);
+    return(result);
+}
+
+/*****************************************************************************
+ * New Operators
+ *****************************************************************************/
+
+Complex *
+complex_add(Complex *a, Complex *b)
+{
+    Complex *result;
+    
+    result = (Complex *)palloc(sizeof(Complex));
+    result->x = a->x + b->x;
+    result->y = a->y + b->y;
+    return (result);
+}
+
+
+/*****************************************************************************
+ * Operator class for defining B-tree index
+ *****************************************************************************/
+
+#define Mag(c) ((c)->x*(c)->x + (c)->y*(c)->y)
+
+bool
+complex_abs_lt(Complex *a, Complex *b)
+{
+    double amag = Mag(a), bmag = Mag(b);
+    return (amag<bmag);
+}
+
+bool
+complex_abs_le(Complex *a, Complex *b)
+{
+    double amag = Mag(a), bmag = Mag(b);
+    return (amag<=bmag);
+}
+
+bool
+complex_abs_eq(Complex *a, Complex *b)
+{
+    double amag = Mag(a), bmag = Mag(b);
+    return (amag==bmag);
+}
+
+bool
+complex_abs_ge(Complex *a, Complex *b)
+{
+    double amag = Mag(a), bmag = Mag(b);
+    return (amag>=bmag);
+}
+
+bool
+complex_abs_gt(Complex *a, Complex *b)
+{
+    double amag = Mag(a), bmag = Mag(b);
+    return (amag>bmag);
+}
+
+int4
+complex_abs_cmp(Complex *a, Complex *b)
+{
+    double amag = Mag(a), bmag = Mag(b);
+    if (a < b)
+       return -1;
+    else if (a > b)
+       return 1;
+    else
+       return 0;
+}
+
+/*****************************************************************************
+ * test code
+ *****************************************************************************/
+
+/*
+ * You should always test your code separately. Trust me, using POSTGRES to
+ * debug your C function will be very painful and unproductive. In case of
+ * POSTGRES crashing, it is impossible to tell whether the bug is in your
+ * code or POSTGRES's.
+ */
+void
+test_main()
+{
+    Complex *a;
+    Complex *b;
+
+    a = complex_in("(4.01, 3.77 )");
+    printf("a = %s\n", complex_out(a));
+    b = complex_in("(1.0,2.0)");
+    printf("b = %s\n", complex_out(b));
+    printf("a +  b = %s\n", complex_out(complex_add(a,b)));
+    printf("a <  b = %d\n", complex_abs_lt(a,b));
+    printf("a <= b = %d\n", complex_abs_le(a,b));
+    printf("a =  b = %d\n", complex_abs_eq(a,b));
+    printf("a >= b = %d\n", complex_abs_ge(a,b));
+    printf("a >  b = %d\n", complex_abs_gt(a,b));
+}
diff --git a/src/tutorial/C-code/funcs.c b/src/tutorial/C-code/funcs.c
new file mode 100644 (file)
index 0000000..f91b4d6
--- /dev/null
@@ -0,0 +1,56 @@
+#include <string.h>
+#include <stdio.h>
+#include "postgres.h"    /* for char16, etc. */
+#include "utils/palloc.h" /* for palloc */
+#include "libpq-fe.h" /* for TUPLE */
+
+int
+add_one(int arg)
+{
+    return(arg + 1);
+}
+
+char16 *
+concat16(char16 *arg1, char16 *arg2)
+{
+    char16 *new_c16 = (char16 *) palloc(sizeof(char16));
+
+    memset(new_c16, 0, sizeof(char16));
+    (void) strncpy((char*)new_c16, (char*)arg1, 16);
+    return (char16 *)(strncat((char*)new_c16, (char*)arg2, 16));
+}
+
+text *
+copytext(text *t)
+{
+    /*
+     * VARSIZE is the total size of the struct in bytes.
+     */
+    text *new_t = (text *) palloc(VARSIZE(t));
+    
+    memset(new_t, 0, VARSIZE(t));
+
+    VARSIZE(new_t) = VARSIZE(t);
+    /*
+     * VARDATA is a pointer to the data region of the struct.
+     */
+    memcpy((void *) VARDATA(new_t), /* destination */
+           (void *) VARDATA(t),     /* source */
+           VARSIZE(t)-VARHDRSZ);              /* how many bytes */
+
+    return(new_t);
+}
+
+bool
+c_overpaid(TUPLE t,    /* the current instance of EMP */
+          int4 limit) 
+{
+    bool isnull = false;
+    int4 salary;
+
+    salary = (int4) GetAttributeByName(t, "salary", &isnull);
+
+    if (isnull)
+       return (false);
+    return(salary > limit);
+}
diff --git a/src/tutorial/Makefile b/src/tutorial/Makefile
new file mode 100644 (file)
index 0000000..e52b78b
--- /dev/null
@@ -0,0 +1,39 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for tutorial/C-code
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+#    $Header$
+#
+#-------------------------------------------------------------------------
+
+MKDIR= ../mk
+include $(MKDIR)/postgres.mk
+
+VPATH:= $(VPATH):C-code
+
+#
+# build dynamically-loaded object files
+#
+DLOBJS= complex$(SLSUFF) funcs$(SLSUFF) 
+
+#
+# ... plus test query inputs
+#
+CREATEFILES= $(DLOBJS:%=$(objdir)/%) \
+       advanced.sql basics.sql complex.sql funcs.sql syscat.sql
+
+include $(MKDIR)/postgres.user.mk
+
+CFLAGS+= -I$(srcdir)/backend
+
+CLEANFILES+= $(notdir $(CREATEFILES))
+
+all:: $(CREATEFILES)
+
+
+
diff --git a/src/tutorial/README b/src/tutorial/README
new file mode 100644 (file)
index 0000000..b35f7b2
--- /dev/null
@@ -0,0 +1,24 @@
+This directory contains SQL tutorial scripts.  To look at them, first do a
+       % make
+to compile all the scripts and C files for the user-defined functions
+and types.  (make needs to be GNU make and may be named something
+different on your system)
+
+Then, change to the object directory
+       % cd obj
+
+and run psql with the -s flag:
+       % psql -s
+
+Welcome to the POSTGRES95 interactive sql monitor:
+
+   type \? for help on slash commands
+   type \q to quit
+   type \g or terminate with semicolon to execute query
+ You are currently connected to the database: jolly
+
+jolly==>
+
+From within psql, you can try each individual script file by using
+the \i <filename> psql command.
+
diff --git a/src/tutorial/advanced.source b/src/tutorial/advanced.source
new file mode 100644 (file)
index 0000000..163eedc
--- /dev/null
@@ -0,0 +1,125 @@
+---------------------------------------------------------------------------
+--
+-- advanced.sql-
+--    more POSTGRES SQL features. (These are not part of the SQL-92
+--    standard.)
+--
+--
+-- Copyright (c) 1994, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+-----------------------------
+-- Inheritance:
+--     a table can inherit from zero or more tables. A query can reference
+--     either all rows of a table or all rows of a table plus all of its
+--     descendants.
+-----------------------------
+
+-- For example, the capitals table inherits from cities table. (It inherits 
+-- all data fields from cities.)
+
+CREATE TABLE cities (
+       name            text,
+       population      float8,
+       altitude        int             -- (in ft)
+)
+
+CREATE TABLE capitals (
+       state           char2
+) INHERITS (cities);
+
+-- now, let's populate the tables
+INSERT INTO cities VALUES ('San Francisco', 7.24E+5, 63)
+INSERT INTO cities VALUES ('Las Vegas', 2.583E+5, 2174)
+INSERT INTO cities VALUES ('Mariposa', 1200, 1953)
+
+INSERT INTO capitals VALUES ('Sacramento', 3.694E+5, 30, 'CA')
+INSERT INTO capitals VALUES ('Madison', 1.913E+5, 845, 'WI')
+
+SELECT * FROM cities
+SELECT * FROM capitals;
+
+-- like before, a regular query references rows of the base table only
+
+SELECT name, altitude
+FROM cities
+WHERE altitude > 500;
+
+-- on the other hand, you can find all cities, including capitals, that
+-- are located at an altitude of 500 'ft or higher by:
+
+SELECT c.name, c.altitude
+FROM cities* c
+WHERE c.altitude > 500;
+
+
+-----------------------------
+-- Time Travel:
+--     this feature allows you to run historical queries. 
+-----------------------------
+
+-- first, let's make some changes to the cities table (suppose Mariposa's
+-- population grows 10% this year)
+
+UPDATE cities
+SET population = population * 1.1
+WHERE name = 'Mariposa';
+
+-- the default time is the current time ('now'):
+
+SELECT * FROM cities WHERE name = 'Mariposa';
+
+-- we can also retrieve the population of Mariposa ever has. ('epoch' is the
+-- earliest time representable by the system)
+
+SELECT name, population
+FROM cities['epoch', 'now']    -- can be abbreviated to cities[,]
+WHERE name = 'Mariposa';
+
+
+----------------------
+-- Arrays:
+--      attributes can be arrays of base types or user-defined types
+----------------------
+
+CREATE TABLE sal_emp (
+       name    text,
+       pay_by_quarter  int4[],
+       schedule        char16[][]
+);
+
+-- insert instances with array attributes.  Note the use of braces
+
+INSERT INTO sal_emp VALUES (
+       'Bill',
+       '{10000,10000,10000,10000}',
+       '{{"meeting", "lunch"}, {}}')
+
+INSERT INTO sal_emp VALUES (
+       'Carol',
+       '{20000,25000,25000,25000}',
+       '{{"talk", "consult"}, {"meeting"}}');
+
+----------------------
+-- queries on array attributes
+----------------------
+SELECT name FROM sal_emp WHERE
+       sal_emp.pay_by_quarter[1] <> sal_emp.pay_by_quarter[2];
+
+-- retrieve third quarter pay of all employees
+
+SELECT sal_emp.pay_by_quarter[3] FROM sal_emp;
+
+-- select subarrays
+
+SELECT sal_emp.schedule[1:2][1:1] FROM sal_emp WHERE   
+       sal_emp.name = 'Bill';
+
+
+-- clean up (you must remove the children first)
+DROP TABLE sal_emp
+DROP TABLE capitals
+DROP TABLE cities;
diff --git a/src/tutorial/basics.source b/src/tutorial/basics.source
new file mode 100644 (file)
index 0000000..20f9e42
--- /dev/null
@@ -0,0 +1,188 @@
+---------------------------------------------------------------------------
+--
+-- basics.sql-
+--    Tutorial on the basics (table creation and data manipulation)
+--
+--
+-- Copyright (c) 1994, Andrew Yu, University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+-----------------------------
+-- Creating a table:
+--     a CREATE TABLE is used to create base tables. POSTGRES SQL has
+--     its own set of built-in types. (Note that keywords are case-
+--     insensitive but identifiers are case-sensitive.)
+-----------------------------
+
+CREATE TABLE weather (
+       city            varchar(80),
+       temp_lo         int,            -- low temperature
+       temp_hi         int,            -- high temperature
+       prcp            float8,         -- precipitation
+       date            date
+)
+
+CREATE TABLE cities (
+       name            varchar(80),
+       location        point
+);
+
+-----------------------------
+-- Inserting data:
+--     an INSERT statement is used to insert a new row into a table. There 
+--     are several ways you can specify what columns the data should go to.
+-----------------------------
+
+-- 1. the simplest case is when the list of value correspond to the order of
+--    the columns specified in CREATE TABLE.
+
+INSERT INTO weather 
+   VALUES ('San Francisco', 46, 50, 0.25, '11/27/1994')
+
+INSERT INTO cities 
+   VALUES ('San Francisco', '(-194.0, 53.0)');
+
+-- 2. you can also specify what column the values correspond to. (The columns
+--    can be specified in any order. You may also omit any number of columns.
+--    eg. unknown precipitation below)
+
+INSERT INTO weather (city, temp_lo, temp_hi, prcp, date)
+   VALUES ('San Francisco', 43, 57, 0.0, '11/29/1994')
+
+INSERT INTO weather (date, city, temp_hi, temp_lo)
+   VALUES ('11/29/1994', 'Hayward', 54, 37);
+
+
+-----------------------------
+-- Retrieving data:
+--     a SELECT statement is used for retrieving data. The basic syntax is
+--             SELECT columns FROM tables WHERE predicates
+-----------------------------
+
+-- a simple one would be
+
+SELECT * FROM weather;
+
+-- you may also specify expressions in the target list (the 'AS column'
+-- specifies the column name of the result. It is optional.)
+
+SELECT city, (temp_hi+temp_lo)/2 AS temp_avg, date FROM weather;
+
+-- if you want to retrieve rows that satisfy certain condition (ie. a
+-- restriction), specify the condition in WHERE. The following retrieves
+-- the weather of San Francisco on rainy days.
+
+SELECT *
+FROM weather
+WHERE city = 'San Francisco' 
+   and prcp > 0.0;
+
+-- here is a more complicated one. Duplicates are removed when DISTINCT is
+-- specified. ORDER BY specifies the column to sort on. (Just to make sure the
+-- following won't confuse you, DISTINCT and ORDER BY can be used separately.)
+
+SELECT DISTINCT city
+FROM weather
+ORDER BY city;
+
+-----------------------------
+-- Retrieving data into other classes:
+--     a SELECT ... INTO statement can be used to retrieve data into
+--     another class.
+-----------------------------
+
+SELECT * INTO TABLE temp 
+FROM weather
+WHERE city = 'San Francisco' 
+   and prcp > 0.0;
+
+SELECT * from temp;
+
+-----------------------------
+-- Aggregates
+-----------------------------
+
+SELECT max(temp_lo)
+FROM weather;
+
+-- Aggregate with GROUP BY
+SELECT city, max(temp_lo)
+FROM weather 
+GROUP BY city;
+
+-----------------------------
+-- Joining tables:
+--     queries can access multiple tables at once or access the same table
+--     in such a way that multiple instances of the table are being processed
+--     at the same time.
+-----------------------------
+
+-- suppose we want to find all the records that are in the temperature range
+-- of other records. W1 and W2 are aliases for weather.
+
+SELECT W1.city, W1.temp_lo, W1.temp_hi, 
+       W2.city, W2.temp_lo, W2.temp_hi
+FROM weather W1, weather W2
+WHERE W1.temp_lo < W2.temp_lo 
+   and W1.temp_hi > W2.temp_hi;
+
+-- let's join two tables. The following joins the weather table
+-- and the cities table.
+
+SELECT city, location, prcp, date
+FROM weather, cities
+WHERE name = city;
+
+-- since the column names are all different, we don't have to specify the
+-- table name. If you want to be clear, you can do the following. They give
+-- identical results, of course.
+
+SELECT w.city, c.location, w.prcp, w.date
+FROM weather w, cities c
+WHERE c.name = w.city;
+
+-----------------------------
+-- Updating data:
+--     an UPDATE statement is used for updating data. 
+-----------------------------
+
+-- suppose you discover the temperature readings are all off by 2 degrees as
+-- of Nov 28, you may update the data as follow:
+
+UPDATE weather
+  SET temp_hi = temp_hi - 2,  temp_lo = temp_lo - 2
+  WHERE date > '11/28/1994';
+
+SELECT * from weather;
+
+
+-----------------------------
+-- Deleting data:
+--     a DELETE statement is used for deleting rows from a table.
+-----------------------------
+
+-- suppose you are no longer interested in the weather of Hayward, you can
+-- do the following to delete those rows from the table
+
+DELETE FROM weather WHERE city = 'Hayward';
+
+SELECT * from weather;
+
+-- you can also delete all the rows in a table by doing the following. (This
+-- is different from DROP TABLE which removes the table in addition to the 
+-- removing the rows.)
+
+DELETE FROM weather;
+
+SELECT * from weather;
+
+-----------------------------
+-- Removing the tables:
+--     DROP TABLE is used to remove tables. After you have done this, you
+--      can no longer use those tables.
+-----------------------------
+
+DROP TABLE weather, cities, temp;
diff --git a/src/tutorial/complex.source b/src/tutorial/complex.source
new file mode 100644 (file)
index 0000000..2899509
--- /dev/null
@@ -0,0 +1,251 @@
+---------------------------------------------------------------------------
+--
+-- complex.sql-
+--    This file shows how to create a new user-defined type and how to
+--    use them.
+-- 
+--
+-- Copyright (c) 1994, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+-----------------------------
+-- Creating a new type:
+--     a user-defined type must have an input and an output function. They
+--     are user-defined C functions. We are going to create a new type 
+--     called 'complex' which represents complex numbers.
+-----------------------------
+
+-- Assume the user defined functions are in _OBJWD_/complex.so
+-- Look at $PWD/C-code/complex.c for the source.
+
+-- the input function 'complex_in' takes a null-terminated string (the 
+-- textual representation of the type) and turns it into the internal
+-- (in memory) representation. You will get a message telling you 'complex'
+-- does not exist yet but that's okay.
+
+CREATE FUNCTION complex_in(opaque)
+   RETURNS complex
+   AS '_OBJWD_/complex.so'
+   LANGUAGE 'c';
+
+-- the output function 'complex_out' takes the internal representation and
+-- converts it into the textual representation.
+
+CREATE FUNCTION complex_out(opaque)
+   RETURNS opaque
+   AS '_OBJWD_/complex.so'
+   LANGUAGE 'c';
+
+-- now, we can create the type. The internallength specifies the size of the
+-- memory block required to hold the type (we need two 8-byte doubles).
+
+CREATE TYPE complex (
+   internallength = 16, 
+   input = complex_in,
+   output = complex_out
+);
+
+
+-----------------------------
+-- Using the new type:
+--     user-defined types can be use like ordinary built-in types.
+-----------------------------
+
+-- eg. we can use it in a schema
+
+CREATE TABLE test_complex (
+       a       complex,
+       b       complex
+);
+
+-- data for user-defined type are just strings in the proper textual
+-- representation. 
+
+INSERT INTO test_complex VALUES ('(1.0, 2.5)', '(4.2, 3.55 )')
+INSERT INTO test_complex VALUES ('(33.0, 51.4)', '(100.42, 93.55)')
+
+SELECT * FROM test_complex;
+
+-----------------------------
+-- Creating an operator for the new type:
+--     Let's define an add operator for complex types. Since POSTGRES
+--     supports function overloading, we'll use + as the add operator.
+--     (Operators can be reused with different number and types of 
+--     arguments.)
+-----------------------------
+
+-- first, define a function complex_add (also in C-code/complex.c)
+CREATE FUNCTION complex_add(complex, complex)
+   RETURNS complex
+   AS '_OBJWD_/complex.so'
+   LANGUAGE 'c';
+
+-- we can now define the operator. We show a binary operator here but you
+-- can also define unary operators by omitting either of leftarg or rightarg.
+CREATE OPERATOR + ( 
+   leftarg = complex,
+   rightarg = complex,
+   procedure = complex_add,
+   commutator = +
+);
+
+
+SELECT (a + b) AS c FROM test_complex;
+
+-- Occasionally, you may find it useful to cast the string to the desired
+-- type explicitly. :: denotes a type cast.
+
+SELECT  a + '(1.0,1.0)'::complex AS aa,
+        b + '(1.0,1.0)'::complex AS bb
+   FROM test_complex;
+
+
+-----------------------------
+-- Creating aggregate functions
+--     you can also define aggregate functions. The syntax is some what
+--     cryptic but the idea is to express the aggregate in terms of state
+--     transition functions.
+-----------------------------
+
+CREATE AGGREGATE complex_sum (
+   sfunc1 = complex_add,
+   basetype = complex,
+   stype1 = complex,
+   initcond1 = '(0,0)'
+);
+
+SELECT complex_sum(a) FROM test_complex;
+
+
+-------------------------------------------------------------------------------
+--             ATTENTION!      ATTENTION!      ATTENTION!                    --
+--  YOU MAY SKIP THE SECTION BELOW ON INTERFACING WITH INDICIES. YOU DON'T   --
+--  NEED THE FOLLOWING IF YOU DON'T USE INDICIES WITH NEW DATA TYPES.        --
+-------------------------------------------------------------------------------
+
+SELECT 'READ ABOVE!' AS STOP;
+
+-----------------------------
+-- Interfacing New Types with Indices:
+--     We cannot define a secondary index (eg. a B-tree) over the new type
+--     yet. We need to modify a few system catalogs to show POSTGRES how
+--     to use the new type. Unfortunately, there is no simple command to
+--     do this. Please bear with me.
+-----------------------------
+
+-- first, define the required operators
+CREATE FUNCTION complex_abs_lt(complex, complex) RETURNS bool
+   AS '_OBJWD_/complex.so' LANGUAGE 'c'
+CREATE FUNCTION complex_abs_le(complex, complex) RETURNS bool
+   AS '_OBJWD_/complex.so' LANGUAGE 'c'
+CREATE FUNCTION complex_abs_eq(complex, complex) RETURNS bool
+   AS '_OBJWD_/complex.so' LANGUAGE 'c'
+CREATE FUNCTION complex_abs_ge(complex, complex) RETURNS bool
+   AS '_OBJWD_/complex.so' LANGUAGE 'c'
+CREATE FUNCTION complex_abs_gt(complex, complex) RETURNS bool
+   AS '_OBJWD_/complex.so' LANGUAGE 'c';
+
+-- the restrict and join selectivity functions are bogus (notice we only
+-- have intltsel, eqsel and intgtsel)
+CREATE OPERATOR < (
+   leftarg = complex, rightarg = complex, procedure = complex_abs_lt,
+   restrict = intltsel, join = intltjoinsel
+)
+CREATE OPERATOR <= (
+   leftarg = complex, rightarg = complex, procedure = complex_abs_le,
+   restrict = intltsel, join = intltjoinsel
+)
+CREATE OPERATOR = (
+   leftarg = complex, rightarg = complex, procedure = complex_abs_eq,
+   restrict = eqsel, join = eqjoinsel
+)
+CREATE OPERATOR >= (
+   leftarg = complex, rightarg = complex, procedure = complex_abs_ge,
+   restrict = intgtsel, join = intgtjoinsel
+)
+CREATE OPERATOR > (
+   leftarg = complex, rightarg = complex, procedure = complex_abs_gt,
+   restrict = intgtsel, join = intgtjoinsel
+);
+
+INSERT INTO pg_opclass VALUES ('complex_abs_ops')
+
+SELECT oid, opcname FROM pg_opclass WHERE opcname = 'complex_abs_ops';
+
+SELECT o.oid AS opoid, o.oprname
+INTO TABLE complex_ops_tmp
+FROM pg_operator o, pg_type t
+WHERE o.oprleft = t.oid and o.oprright = t.oid
+   and t.typname = 'complex';
+
+-- make sure we have the right operators
+SELECT * from complex_ops_tmp;
+
+INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy, 
+                     amopselect, amopnpages)
+   SELECT am.oid, opcl.oid, c.opoid, 1,
+       'btreesel'::regproc, 'btreenpage'::regproc
+   FROM pg_am am, pg_opclass opcl, complex_ops_tmp c
+   WHERE amname = 'btree' and opcname = 'complex_abs_ops' 
+      and c.oprname = '<';
+
+INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy, 
+                     amopselect, amopnpages)
+   SELECT am.oid, opcl.oid, c.opoid, 2,
+       'btreesel'::regproc, 'btreenpage'::regproc
+   FROM pg_am am, pg_opclass opcl, complex_ops_tmp c
+   WHERE amname = 'btree' and opcname = 'complex_abs_ops' 
+      and c.oprname = '<=';
+
+INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy, 
+                     amopselect, amopnpages)
+   SELECT am.oid, opcl.oid, c.opoid, 3,
+       'btreesel'::regproc, 'btreenpage'::regproc
+   FROM pg_am am, pg_opclass opcl, complex_ops_tmp c
+   WHERE amname = 'btree' and opcname = 'complex_abs_ops' 
+      and c.oprname = '=';
+
+INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy, 
+                     amopselect, amopnpages)
+   SELECT am.oid, opcl.oid, c.opoid, 4,
+       'btreesel'::regproc, 'btreenpage'::regproc
+   FROM pg_am am, pg_opclass opcl, complex_ops_tmp c
+   WHERE amname = 'btree' and opcname = 'complex_abs_ops' 
+      and c.oprname = '>=';
+
+INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy, 
+                     amopselect, amopnpages)
+   SELECT am.oid, opcl.oid, c.opoid, 5,
+       'btreesel'::regproc, 'btreenpage'::regproc
+   FROM pg_am am, pg_opclass opcl, complex_ops_tmp c
+   WHERE amname = 'btree' and opcname = 'complex_abs_ops' 
+      and c.oprname = '>';
+
+DROP table complex_ops_tmp;
+
+--
+CREATE FUNCTION complex_abs_cmp(complex, complex) RETURNS int4
+   AS '_OBJWD_/complex.so' LANGUAGE 'c';
+
+SELECT oid, proname FROM pg_proc WHERE proname = 'complex_abs_cmp';
+
+INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum)
+   SELECT am.oid, opcl.oid, pro.oid, 1
+   FROM pg_am am, pg_opclass opcl, pg_proc pro
+   WHERE  amname = 'btree' and opcname = 'complex_abs_ops'
+      and proname = 'complex_abs_cmp';
+
+-- now, we can define a btree index on complex types. First, let's populate
+-- the table (THIS DOESN'T ACTUALLY WORK. YOU NEED MANY MORE TUPLES.)
+INSERT INTO test_complex VALUES ('(56.0,-22.5)', '(-43.2,-0.07)')
+INSERT INTO test_complex VALUES ('(-91.9,33.6)', '(8.6,3.0)');
+
+CREATE INDEX test_cplx_ind ON test_complex
+   USING btree(a complex_abs_ops);
+
+SELECT * from test_complex where a = '(56.0,-22.5)';
+SELECT * from test_complex where a < '(56.0,-22.5)';
+SELECT * from test_complex where a > '(56.0,-22.5)';
\ No newline at end of file
diff --git a/src/tutorial/funcs.source b/src/tutorial/funcs.source
new file mode 100644 (file)
index 0000000..6baaad1
--- /dev/null
@@ -0,0 +1,158 @@
+---------------------------------------------------------------------------
+--
+-- funcs.sql-
+--    Tutorial on using functions in POSTGRES.
+--
+--
+-- Copyright (c) 1994-5, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+-----------------------------
+-- Creating SQL Functions on Base Types
+--     a CREATE FUNCTION statement lets you create a new function that
+--     can be used in expressions (in SELECT, INSERT, etc.). We will start
+--     with functions that return values of base types.
+-----------------------------
+
+--
+-- let's create a simple SQL function that takes no arguments and 
+-- returns 1
+
+CREATE FUNCTION one() RETURNS int4
+   AS 'SELECT 1 as ONE' LANGUAGE 'sql';
+
+--
+-- functions can be used in any expressions (eg. in the target list or 
+-- qualifications)
+
+SELECT one() AS answer;
+
+--
+-- here's how you create a function that takes arguments. The following
+-- function returns the sum of its two arguments:
+
+CREATE FUNCTION add_em(int4, int4) RETURNS int4
+   AS 'SELECT $1 + $2' LANGUAGE 'sql';
+
+SELECT add_em(1, 2) AS answer;
+
+-----------------------------
+-- Creating SQL Functions on Composite Types
+--     it is also possible to create functions that return values of
+--     composite types.
+-----------------------------
+
+-- before we create more sophisticated functions, let's populate an EMP
+-- table
+
+CREATE TABLE EMP (
+       name            text,
+       salary          int4,
+       age             int4,
+       dept            char16
+);
+
+INSERT INTO EMP VALUES ('Sam', 1200, 16, 'toy')
+INSERT INTO EMP VALUES ('Claire', 5000, 32, 'shoe')
+INSERT INTO EMP VALUES ('Andy', -1000, 2, 'candy')
+INSERT INTO EMP VALUES ('Bill', 4200, 36, 'shoe')
+INSERT INTO EMP VALUES ('Ginger', 4800, 30, 'candy');
+
+-- the argument of a function can also be a tuple. For instance, 
+-- double_salary takes a tuple of the EMP table
+
+CREATE FUNCTION double_salary(EMP) RETURNS int4
+   AS 'SELECT $1.salary * 2 AS salary' LANGUAGE 'sql';
+
+SELECT name, double_salary(EMP) AS dream
+FROM EMP
+WHERE EMP.dept = 'toy';
+
+-- the return value of a function can also be a tuple. However, make sure 
+-- that the expressions in the target list is in the same order as the 
+-- columns of EMP.
+
+CREATE FUNCTION new_emp() RETURNS EMP
+   AS 'SELECT \'None\'::text AS name,
+              1000 AS salary,
+              25 AS age,
+              \'none\'::char16 AS dept'
+   LANGUAGE 'sql';
+
+-- you can then project a column out of resulting the tuple by using the
+-- "function notation" for projection columns. (ie. bar(foo) is equivalent
+-- to foo.bar) Note that we don't support new_emp().name at this moment.
+
+SELECT name(new_emp()) AS nobody;
+
+-- let's try one more function that returns tuples
+CREATE FUNCTION high_pay() RETURNS setof EMP
+   AS 'SELECT * FROM EMP where salary > 1500'
+   LANGUAGE 'sql';
+
+SELECT name(high_pay()) AS overpaid;
+
+
+-----------------------------
+-- Creating SQL Functions with multiple SQL statements
+--     you can also create functions that do more than just a SELECT.
+-----------------------------
+
+-- you may have noticed that Andy has a negative salary. We'll create a
+-- function that removes employees with negative salaries.
+
+SELECT * FROM EMP;
+
+CREATE FUNCTION clean_EMP () RETURNS int4
+   AS 'DELETE FROM EMP WHERE EMP.salary <= 0
+       SELECT 1 AS ignore_this'
+   LANGUAGE 'sql';
+
+SELECT clean_EMP();
+
+SELECT * FROM EMP;
+
+
+-----------------------------
+-- Creating C Functions
+--     in addition to SQL functions, you can also create C functions. 
+--     See C-code/funcs.c for the definition of the C functions.
+-----------------------------
+
+CREATE FUNCTION add_one(int4) RETURNS int4
+   AS '_OBJWD_/funcs.so' LANGUAGE 'c';
+
+CREATE FUNCTION concat16(char16, char16) RETURNS char16
+   AS '_OBJWD_/funcs.so' LANGUAGE 'c';
+
+CREATE FUNCTION copytext(text) RETURNS text
+   AS '_OBJWD_/funcs.so' LANGUAGE 'c';
+
+CREATE FUNCTION c_overpaid(EMP, int4) RETURNS bool
+   AS '_OBJWD_/funcs.so' LANGUAGE 'c';
+
+SELECT add_one(3) AS four;
+
+SELECT concat16('abc', 'xyz') AS newchar16;
+
+SELECT copytext('hello world!');
+
+SELECT name, c_overpaid(EMP, 1500) AS overpaid
+FROM EMP 
+WHERE name = 'Bill' or name = 'Sam';
+
+-- remove functions that were created in this file
+
+DROP FUNCTION c_overpaid(EMP, int4)
+DROP FUNCTION copytext(text)
+DROP FUNCTION concat16(char16,char16)
+DROP FUNCTION add_one(int4)
+DROP FUNCTION clean_EMP()
+DROP FUNCTION new_emp()
+DROP FUNCTION add_em(int4, int4)
+DROP FUNCTION one();
+
+DROP TABLE EMP;
diff --git a/src/tutorial/syscat.source b/src/tutorial/syscat.source
new file mode 100644 (file)
index 0000000..e2923c2
--- /dev/null
@@ -0,0 +1,151 @@
+---------------------------------------------------------------------------
+--
+-- syscat.sql-
+--    sample queries to the system catalogs
+--
+--
+-- Copyright (c) 1994, Regents of the University of California
+--
+-- $Id$
+--
+---------------------------------------------------------------------------
+
+--
+-- lists the name of all database adminstrators and the name of their
+-- database(s)
+--
+SELECT usename, datname
+  FROM pg_user, pg_database
+  WHERE usesysid = int2in(int4out(datdba))
+  ORDER BY usename, datname;
+
+--
+-- lists all user-defined classes
+--
+SELECT relname
+  FROM pg_class
+  WHERE relkind = 'r'           -- not indices
+    and relname !~ '^pg_'       -- not catalogs
+    and relname !~ '^Inv'       -- not large objects
+  ORDER BY relname;
+
+
+--
+-- lists all simple indicies (ie. those that are not defined over a function
+-- of several attributes)
+--
+SELECT bc.relname AS class_name, 
+       ic.relname AS index_name, 
+       a.attname
+  FROM pg_class bc,             -- base class
+       pg_class ic,             -- index class
+       pg_index i,
+       pg_attribute a           -- att in base
+  WHERE i.indrelid = bc.oid
+     and i.indexrelid = ic.oid
+     and i.indkey[0] = a.attnum
+     and a.attrelid = bc.oid
+     and i.indproc = '0'::oid   -- no functional indices
+  ORDER BY class_name, index_name, attname;
+
+
+--
+-- lists the user-defined attributes and their types for all user-defined
+-- classes
+--
+SELECT c.relname, a.attname, t.typname
+  FROM pg_class c, pg_attribute a, pg_type t
+  WHERE c.relkind = 'r'     -- no indices
+    and c.relname !~ '^pg_' -- no catalogs
+    and c.relname !~ '^Inv' -- no large objects
+    and a.attnum > 0        -- no system att's
+    and a.attrelid = c.oid
+    and a.atttypid = t.oid
+  ORDER BY relname, attname;
+
+
+--
+-- lists all user-defined base types (not includeing array types)
+--
+SELECT u.usename, t.typname
+  FROM pg_type t, pg_user u
+  WHERE u.usesysid = int2in(int4out(t.typowner))
+    and t.typrelid = '0'::oid   -- no complex types
+    and t.typelem = '0'::oid    -- no arrays
+    and u.usename <> 'postgres'
+  ORDER BY usename, typname;
+
+
+--
+-- lists all left unary operators
+--
+SELECT o.oprname AS left_unary, 
+       right.typname AS operand,
+       result.typname AS return_type
+  FROM pg_operator o, pg_type right, pg_type result
+  WHERE o.oprkind = 'l'           -- left unary
+    and o.oprright = right.oid
+    and o.oprresult = result.oid
+  ORDER BY operand;
+
+
+--
+-- lists all right unary operators
+--
+SELECT o.oprname AS right_unary,
+       left.typname AS operand,
+       result.typname AS return_type
+  FROM pg_operator o, pg_type left, pg_type result
+  WHERE o.oprkind = 'r'          -- right unary
+    and o.oprleft = left.oid
+    and o.oprresult = result.oid
+  ORDER BY operand;
+
+--
+-- lists all binary operators
+--
+SELECT o.oprname AS binary_op,
+       left.typname AS left_opr,
+       right.typname AS right_opr,
+       result.typname AS return_type
+  FROM pg_operator o, pg_type left, pg_type right, pg_type result
+  WHERE o.oprkind = 'b'         -- binary
+    and o.oprleft = left.oid
+    and o.oprright = right.oid
+    and o.oprresult = result.oid
+  ORDER BY left_opr, right_opr;
+
+
+--
+-- lists the name, number of arguments and the return type of all user-defined
+-- C functions
+--
+SELECT p.proname, p.pronargs, t.typname
+  FROM pg_proc p, pg_language l, pg_type t
+  WHERE p.prolang = l.oid 
+    and p.prorettype = t.oid
+    and l.lanname = 'c'
+  ORDER BY proname;
+
+--
+-- lists all aggregate functions and the types to which they can be applied
+--
+SELECT a.aggname, t.typname
+  FROM pg_aggregate a, pg_type t
+  WHERE a.aggbasetype = t.oid
+  ORDER BY aggname, typname;
+
+
+--
+-- lists all the operator classes that can be used with each access method
+-- as well as the operators that cn be used with the respective operator
+-- classes
+--
+SELECT am.amname, opc.opcname, opr.oprname
+  FROM pg_am am, pg_amop amop, pg_opclass opc, pg_operator opr
+  WHERE amop.amopid = am.oid
+    and amop.amopclaid = opc.oid
+    and amop.amopopr = opr.oid
+  ORDER BY amname, opcname, oprname;
+
+